Merge pull request #149 from aryasaatvik/has-onboarded

fix(web): users should be onboarded once
This commit is contained in:
Dhravya Shah 2024-07-23 18:19:19 -05:00 committed by GitHub
commit a134b99506
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 984 additions and 21 deletions

View file

@ -1,6 +1,5 @@
"use client";
import Link from "next/link";
import {
ChevronLeftIcon,
ChevronRightIcon,
@ -11,7 +10,7 @@ import { CheckIcon, PlusCircleIcon } from "@heroicons/react/24/outline";
import { motion } from "framer-motion";
import { useEffect, useState } from "react";
import { toast } from "sonner";
import { createMemory } from "@repo/web/app/actions/doers";
import { completeOnboarding, createMemory } from "@repo/web/app/actions/doers";
import { useRouter } from "next/navigation";
import Logo from "../../../public/logo.svg";
import Image from "next/image";
@ -23,8 +22,13 @@ export default function Home() {
const { push } = useRouter();
useEffect(() => {
const updateDb = async () => {
await completeOnboarding();
}
if (currStep > 3) {
push("/home?q=what%20is%20supermemory");
updateDb().then(() => {
push("/home?q=what%20is%20supermemory");
});
}
}, [currStep]);
@ -182,7 +186,7 @@ function StepIndicator({
/>
<p>Step: {currStep}/3</p>
<ChevronRightIcon
className="h-6"
className="h-6 cursor-pointer"
onClick={() => currStep <= 3 && setCurrStep(currStep + 1)}
/>
</div>
@ -381,6 +385,12 @@ function StepTwo({ currStep }: { currStep: number }) {
}
function Navbar() {
const router = useRouter();
const handleSkip = async () => {
await completeOnboarding();
router.push("/home?q=what%20is%20supermemory");
}
return (
<div className="flex items-center justify-between p-4 fixed top-0 left-0 w-full">
<Image
@ -389,9 +399,7 @@ function Navbar() {
className="hover:brightness-125 duration-200 size-12"
/>
<Link href="/home">
<button className="text-sm">Skip</button>
</Link>
<button className="text-sm" onClick={handleSkip}>Skip</button>
</div>
);
}

View file

@ -18,7 +18,7 @@ async function Signin({
const user = await auth();
if (user) {
await redirect("/home");
redirect("/home");
}
return (
@ -64,7 +64,7 @@ async function Signin({
action={async () => {
"use server";
await signIn("google", {
redirectTo: "/home?firstTime=true",
redirectTo: "/home",
});
}}
>

View file

@ -3,13 +3,12 @@
import React, { useEffect, useState } from "react";
import QueryInput from "./queryinput";
import { getSessionAuthToken, getSpaces } from "@/app/actions/fetchers";
import { redirect, useRouter } from "next/navigation";
import { useRouter } from "next/navigation";
import { createChatThread, linkTelegramToUser } from "@/app/actions/doers";
import { toast } from "sonner";
import { motion } from "framer-motion";
import { ChromeIcon, GithubIcon, TwitterIcon } from "lucide-react";
import Link from "next/link";
import { homeSearchParamsCache } from "@/lib/searchParams";
import History from "./history";
const slap = {
@ -26,15 +25,7 @@ const slap = {
};
function Page({ searchParams }: { searchParams: Record<string, string> }) {
// TODO: use this to show a welcome page/modal
const firstTime = searchParams.firstTime === "true";
const query = searchParams.q || "";
if (firstTime) {
redirect("/onboarding");
}
const [queryPresent, setQueryPresent] = useState<boolean>(false);
const [telegramUser, setTelegramUser] = useState<string | undefined>(

View file

@ -4,6 +4,7 @@ import { redirect } from "next/navigation";
import { auth } from "../../server/auth";
import { Toaster } from "@repo/ui/shadcn/sonner";
import BackgroundPlus from "../(landing)/GridPatterns/PlusGrid";
import { getUser } from "../actions/fetchers";
async function Layout({ children }: { children: React.ReactNode }) {
const info = await auth();
@ -12,6 +13,13 @@ async function Layout({ children }: { children: React.ReactNode }) {
return redirect("/signin");
}
const user = await getUser();
const hasOnboarded = user.data?.hasOnboarded;
if (!hasOnboarded) {
redirect("/onboarding");
}
return (
<main className="h-screen flex flex-col">
<div className="fixed top-0 left-0 w-full z-40">

View file

@ -23,6 +23,33 @@ import { decipher } from "@/server/encrypt";
import { redirect } from "next/navigation";
import { tweetToMd } from "@repo/shared-types/utils";
export const completeOnboarding = async (): ServerActionReturnType<boolean> => {
const data = await auth();
if (!data || !data.user || !data.user.id) {
redirect("/signin");
return { error: "Not authenticated", success: false };
}
try {
const res = await db
.update(users)
.set({ hasOnboarded: true })
.where(eq(users.id, data.user.id))
.returning({ hasOnboarded: users.hasOnboarded });
if (res.length === 0 || !res[0]?.hasOnboarded) {
return { success: false, data: false, error: "Failed to update user" };
}
return { success: true, data: res[0].hasOnboarded };
} catch (e) {
return { success: false, data: false, error: (e as Error).message };
}
}
export const createSpace = async (
input: string | FormData,
): ServerActionReturnType<number> => {

View file

@ -1,6 +1,6 @@
"use server";
import { and, asc, eq, exists, inArray, not, or, sql } from "drizzle-orm";
import { and, asc, eq, exists, not, or } from "drizzle-orm";
import { db } from "../../server/db";
import {
canvas,
@ -13,15 +13,32 @@ import {
spacesAccess,
storedContent,
StoredSpace,
User,
users,
} from "../../server/db/schema";
import { ServerActionReturnType, Space } from "./types";
import { ServerActionReturnType } from "./types";
import { auth } from "../../server/auth";
import { ChatHistory, SourceZod } from "@repo/shared-types";
import { z } from "zod";
import { redirect } from "next/navigation";
import { cookies, headers } from "next/headers";
export const getUser = async (): ServerActionReturnType<User> => {
const data = await auth();
if (!data || !data.user || !data.user.id) {
redirect("/signin");
return { error: "Not authenticated", success: false };
}
console.log("data.user.id", data.user.id);
const user = await db.query.users.findFirst({
where: eq(users.id, data.user.id),
});
return { success: true, data: user };
};
export const getSpaces = async (): ServerActionReturnType<StoredSpace[]> => {
const data = await auth();

View file

@ -0,0 +1 @@
ALTER TABLE `user` ADD `hasOnboarded` integer DEFAULT false;

View file

@ -0,0 +1,903 @@
{
"version": "6",
"dialect": "sqlite",
"id": "1f43694b-f42b-4074-876e-8501fc18bf38",
"prevId": "e8646bed-105d-4f69-b385-b8b6fee8a6a9",
"tables": {
"account": {
"name": "account",
"columns": {
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"provider": {
"name": "provider",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"providerAccountId": {
"name": "providerAccountId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"refresh_token": {
"name": "refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"access_token": {
"name": "access_token",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"expires_at": {
"name": "expires_at",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"token_type": {
"name": "token_type",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"scope": {
"name": "scope",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"id_token": {
"name": "id_token",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"session_state": {
"name": "session_state",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"account_userId_user_id_fk": {
"name": "account_userId_user_id_fk",
"tableFrom": "account",
"tableTo": "user",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"account_provider_providerAccountId_pk": {
"columns": [
"provider",
"providerAccountId"
],
"name": "account_provider_providerAccountId_pk"
}
},
"uniqueConstraints": {}
},
"authenticator": {
"name": "authenticator",
"columns": {
"credentialID": {
"name": "credentialID",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"providerAccountId": {
"name": "providerAccountId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"credentialPublicKey": {
"name": "credentialPublicKey",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"counter": {
"name": "counter",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"credentialDeviceType": {
"name": "credentialDeviceType",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"credentialBackedUp": {
"name": "credentialBackedUp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"transports": {
"name": "transports",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"authenticator_credentialID_unique": {
"name": "authenticator_credentialID_unique",
"columns": [
"credentialID"
],
"isUnique": true
}
},
"foreignKeys": {
"authenticator_userId_user_id_fk": {
"name": "authenticator_userId_user_id_fk",
"tableFrom": "authenticator",
"tableTo": "user",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"authenticator_userId_credentialID_pk": {
"columns": [
"credentialID",
"userId"
],
"name": "authenticator_userId_credentialID_pk"
}
},
"uniqueConstraints": {}
},
"canvas": {
"name": "canvas",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'Untitled'"
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'Untitled'"
},
"url": {
"name": "url",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "''"
},
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"canvas_user_userId": {
"name": "canvas_user_userId",
"columns": [
"userId"
],
"isUnique": false
}
},
"foreignKeys": {
"canvas_userId_user_id_fk": {
"name": "canvas_userId_user_id_fk",
"tableFrom": "canvas",
"tableTo": "user",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"chatHistory": {
"name": "chatHistory",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"threadId": {
"name": "threadId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"question": {
"name": "question",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"answerParts": {
"name": "answerParts",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"answerSources": {
"name": "answerSources",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"answerJustification": {
"name": "answerJustification",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"chatHistory_thread_idx": {
"name": "chatHistory_thread_idx",
"columns": [
"threadId"
],
"isUnique": false
}
},
"foreignKeys": {
"chatHistory_threadId_chatThread_id_fk": {
"name": "chatHistory_threadId_chatThread_id_fk",
"tableFrom": "chatHistory",
"tableTo": "chatThread",
"columnsFrom": [
"threadId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"chatThread": {
"name": "chatThread",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"firstMessage": {
"name": "firstMessage",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"chatThread_user_idx": {
"name": "chatThread_user_idx",
"columns": [
"userId"
],
"isUnique": false
}
},
"foreignKeys": {
"chatThread_userId_user_id_fk": {
"name": "chatThread_userId_user_id_fk",
"tableFrom": "chatThread",
"tableTo": "user",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"contentToSpace": {
"name": "contentToSpace",
"columns": {
"contentId": {
"name": "contentId",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"spaceId": {
"name": "spaceId",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"contentToSpace_contentId_storedContent_id_fk": {
"name": "contentToSpace_contentId_storedContent_id_fk",
"tableFrom": "contentToSpace",
"tableTo": "storedContent",
"columnsFrom": [
"contentId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"contentToSpace_spaceId_space_id_fk": {
"name": "contentToSpace_spaceId_space_id_fk",
"tableFrom": "contentToSpace",
"tableTo": "space",
"columnsFrom": [
"spaceId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"contentToSpace_contentId_spaceId_pk": {
"columns": [
"contentId",
"spaceId"
],
"name": "contentToSpace_contentId_spaceId_pk"
}
},
"uniqueConstraints": {}
},
"session": {
"name": "session",
"columns": {
"sessionToken": {
"name": "sessionToken",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"expires": {
"name": "expires",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"session_userId_user_id_fk": {
"name": "session_userId_user_id_fk",
"tableFrom": "session",
"tableTo": "user",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"space": {
"name": "space",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'none'"
},
"user": {
"name": "user",
"type": "text(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"numItems": {
"name": "numItems",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
}
},
"indexes": {
"space_name_unique": {
"name": "space_name_unique",
"columns": [
"name"
],
"isUnique": true
},
"spaces_name_idx": {
"name": "spaces_name_idx",
"columns": [
"name"
],
"isUnique": false
},
"spaces_user_idx": {
"name": "spaces_user_idx",
"columns": [
"user"
],
"isUnique": false
}
},
"foreignKeys": {
"space_user_user_id_fk": {
"name": "space_user_user_id_fk",
"tableFrom": "space",
"tableTo": "user",
"columnsFrom": [
"user"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"spacesAccess": {
"name": "spacesAccess",
"columns": {
"spaceId": {
"name": "spaceId",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"userEmail": {
"name": "userEmail",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"spacesAccess_spaceId_space_id_fk": {
"name": "spacesAccess_spaceId_space_id_fk",
"tableFrom": "spacesAccess",
"tableTo": "space",
"columnsFrom": [
"spaceId"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"spacesAccess_spaceId_userEmail_pk": {
"columns": [
"spaceId",
"userEmail"
],
"name": "spacesAccess_spaceId_userEmail_pk"
}
},
"uniqueConstraints": {}
},
"storedContent": {
"name": "storedContent",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"title": {
"name": "title",
"type": "text(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"url": {
"name": "url",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"savedAt": {
"name": "savedAt",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"baseUrl": {
"name": "baseUrl",
"type": "text(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"ogImage": {
"name": "ogImage",
"type": "text(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'page'"
},
"image": {
"name": "image",
"type": "text(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"user": {
"name": "user",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"noteId": {
"name": "noteId",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"storedContent_baseUrl_unique": {
"name": "storedContent_baseUrl_unique",
"columns": [
"baseUrl"
],
"isUnique": true
},
"storedContent_url_idx": {
"name": "storedContent_url_idx",
"columns": [
"url"
],
"isUnique": false
},
"storedContent_savedAt_idx": {
"name": "storedContent_savedAt_idx",
"columns": [
"savedAt"
],
"isUnique": false
},
"storedContent_title_idx": {
"name": "storedContent_title_idx",
"columns": [
"title"
],
"isUnique": false
},
"storedContent_user_idx": {
"name": "storedContent_user_idx",
"columns": [
"user"
],
"isUnique": false
}
},
"foreignKeys": {
"storedContent_user_user_id_fk": {
"name": "storedContent_user_user_id_fk",
"tableFrom": "storedContent",
"tableTo": "user",
"columnsFrom": [
"user"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"user": {
"name": "user",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"emailVerified": {
"name": "emailVerified",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"image": {
"name": "image",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"telegramId": {
"name": "telegramId",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"hasOnboarded": {
"name": "hasOnboarded",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": false
}
},
"indexes": {
"users_email_idx": {
"name": "users_email_idx",
"columns": [
"email"
],
"isUnique": false
},
"users_telegram_idx": {
"name": "users_telegram_idx",
"columns": [
"telegramId"
],
"isUnique": false
},
"users_id_idx": {
"name": "users_id_idx",
"columns": [
"id"
],
"isUnique": false
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"verificationToken": {
"name": "verificationToken",
"columns": {
"identifier": {
"name": "identifier",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"token": {
"name": "token",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"expires": {
"name": "expires",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"verificationToken_identifier_token_pk": {
"columns": [
"identifier",
"token"
],
"name": "verificationToken_identifier_token_pk"
}
},
"uniqueConstraints": {}
}
},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

View file

@ -8,6 +8,13 @@
"when": 1721746132570,
"tag": "0000_silky_havok",
"breakpoints": true
},
{
"idx": 1,
"version": "6",
"when": 1721775651258,
"tag": "0001_dear_sally_floyd",
"breakpoints": true
}
]
}

View file

@ -22,6 +22,7 @@ export const users = createTable(
emailVerified: integer("emailVerified", { mode: "timestamp_ms" }),
image: text("image"),
telegramId: text("telegramId"),
hasOnboarded: integer("hasOnboarded", { mode: "boolean" }).default(false),
},
(user) => ({
emailIdx: index("users_email_idx").on(user.email),