├── apps ├── native │ ├── .nvmrc │ ├── .example.env │ ├── tsconfig.json │ ├── assets │ │ ├── icon.png │ │ ├── favicon.png │ │ ├── splash.png │ │ └── adaptive-icon.png │ ├── babel.config.js │ ├── src │ │ ├── assets │ │ │ ├── icons │ │ │ │ ├── logo.png │ │ │ │ ├── avatar.png │ │ │ │ ├── google.png │ │ │ │ ├── saveIcon.png │ │ │ │ ├── arrow-back.png │ │ │ │ ├── logo2small.png │ │ │ │ ├── OrignalIcon.png │ │ │ │ └── summaryIcon.png │ │ │ └── fonts │ │ │ │ ├── Inter-Bold.ttf │ │ │ │ ├── Inter-Medium.ttf │ │ │ │ ├── Inter-Regular.ttf │ │ │ │ ├── Inter-SemiBold.ttf │ │ │ │ ├── Montserrat-Bold.ttf │ │ │ │ ├── Montserrat-Light.ttf │ │ │ │ ├── Montserrat-Medium.ttf │ │ │ │ ├── Montserrat-Regular.ttf │ │ │ │ └── Montserrat-SemiBold.ttf │ │ ├── navigation │ │ │ └── Navigation.tsx │ │ └── screens │ │ │ ├── LoginScreen.tsx │ │ │ ├── InsideNoteScreen.tsx │ │ │ ├── NotesDashboardScreen.tsx │ │ │ └── CreateNoteScreen.tsx │ ├── .gitignore │ ├── index.tsx │ ├── ConvexClientProvider.tsx │ ├── app.json │ ├── metro.config.js │ ├── App.tsx │ └── package.json └── web │ ├── .eslintrc.json │ ├── .example.env │ ├── postcss.config.js │ ├── src │ ├── app │ │ ├── favicon.ico │ │ ├── notes │ │ │ ├── page.tsx │ │ │ └── [slug] │ │ │ │ └── page.tsx │ │ ├── page.tsx │ │ ├── layout.tsx │ │ ├── ConvexClientProvider.tsx │ │ ├── globals.css │ │ └── ErrorBoundary.tsx │ ├── lib │ │ └── utils.ts │ ├── middleware.ts │ └── components │ │ ├── common │ │ ├── Logo.tsx │ │ ├── Menu.tsx │ │ ├── avatar.tsx │ │ ├── button.tsx │ │ ├── UserNav.tsx │ │ ├── TestTimonialCard.tsx │ │ └── dropdown-menu.tsx │ │ ├── notes │ │ ├── NoteItem.tsx │ │ ├── NoteDetails.tsx │ │ ├── Checkbox.tsx │ │ ├── Notes.tsx │ │ ├── DeleteNote.tsx │ │ └── CreateNote.tsx │ │ ├── home │ │ ├── FooterHero.tsx │ │ ├── ComplexToggle.tsx │ │ ├── Hero.tsx │ │ ├── Footer.tsx │ │ ├── Testimonials.tsx │ │ └── Benefits.tsx │ │ └── Header.tsx │ ├── public │ ├── images │ │ ├── Add.png │ │ ├── bot.png │ │ ├── hero.png │ │ ├── logo.png │ │ ├── menu.png │ │ ├── github.png │ │ ├── Fantasy.png │ │ ├── cloudSync.png │ │ ├── goodNews.png │ │ ├── monitor.png │ │ ├── profile.png │ │ ├── Moe-Partuj.jpeg │ │ ├── background.png │ │ ├── left-arrow.png │ │ ├── googleCalander.png │ │ ├── star.svg │ │ ├── blue-circle-right.svg │ │ ├── blue-circle.svg │ │ ├── cricle.svg │ │ ├── hero_image_bg.svg │ │ ├── message-right.svg │ │ ├── message-left.svg │ │ ├── delete.svg │ │ └── search.svg │ ├── vercel.svg │ └── next.svg │ ├── vercel.json │ ├── .gitignore │ ├── tsconfig.json │ ├── package.json │ └── README.md ├── .prettierrc ├── packages └── backend │ ├── .gitignore │ ├── convex │ ├── auth.config.js │ ├── schema.ts │ ├── _generated │ │ ├── api.js │ │ ├── api.d.ts │ │ ├── dataModel.d.ts │ │ ├── server.js │ │ └── server.d.ts │ ├── utils.ts │ ├── tsconfig.json │ ├── notes.ts │ ├── openai.ts │ └── README.md │ └── package.json ├── renovate.json ├── turbo.json ├── .gitignore ├── package.json ├── README.md └── LICENSE /apps/native/.nvmrc: -------------------------------------------------------------------------------- 1 | node-linker=hoisted 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always" 3 | } 4 | -------------------------------------------------------------------------------- /packages/backend/.gitignore: -------------------------------------------------------------------------------- 1 | .env.local 2 | .env 3 | -------------------------------------------------------------------------------- /apps/web/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /apps/native/.example.env: -------------------------------------------------------------------------------- 1 | EXPO_PUBLIC_CONVEX_URL= 2 | EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY= 3 | -------------------------------------------------------------------------------- /apps/native/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": {}, 3 | "extends": "expo/tsconfig.base" 4 | } 5 | -------------------------------------------------------------------------------- /apps/web/.example.env: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_CONVEX_URL= 2 | NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= 3 | CLERK_SECRET_KEY= 4 | -------------------------------------------------------------------------------- /apps/web/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /apps/native/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/assets/icon.png -------------------------------------------------------------------------------- /apps/web/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/src/app/favicon.ico -------------------------------------------------------------------------------- /apps/native/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/assets/favicon.png -------------------------------------------------------------------------------- /apps/native/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/assets/splash.png -------------------------------------------------------------------------------- /apps/web/public/images/Add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/Add.png -------------------------------------------------------------------------------- /apps/web/public/images/bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/bot.png -------------------------------------------------------------------------------- /apps/web/public/images/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/hero.png -------------------------------------------------------------------------------- /apps/web/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/logo.png -------------------------------------------------------------------------------- /apps/web/public/images/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/menu.png -------------------------------------------------------------------------------- /apps/web/public/images/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/github.png -------------------------------------------------------------------------------- /apps/native/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/assets/adaptive-icon.png -------------------------------------------------------------------------------- /apps/web/public/images/Fantasy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/Fantasy.png -------------------------------------------------------------------------------- /apps/web/public/images/cloudSync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/cloudSync.png -------------------------------------------------------------------------------- /apps/web/public/images/goodNews.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/goodNews.png -------------------------------------------------------------------------------- /apps/web/public/images/monitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/monitor.png -------------------------------------------------------------------------------- /apps/web/public/images/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/profile.png -------------------------------------------------------------------------------- /apps/native/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: ["babel-preset-expo"], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /apps/native/src/assets/icons/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/icons/logo.png -------------------------------------------------------------------------------- /apps/web/public/images/Moe-Partuj.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/Moe-Partuj.jpeg -------------------------------------------------------------------------------- /apps/web/public/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/background.png -------------------------------------------------------------------------------- /apps/web/public/images/left-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/left-arrow.png -------------------------------------------------------------------------------- /apps/native/src/assets/icons/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/icons/avatar.png -------------------------------------------------------------------------------- /apps/native/src/assets/icons/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/icons/google.png -------------------------------------------------------------------------------- /apps/native/src/assets/icons/saveIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/icons/saveIcon.png -------------------------------------------------------------------------------- /apps/web/public/images/googleCalander.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/web/public/images/googleCalander.png -------------------------------------------------------------------------------- /apps/native/src/assets/fonts/Inter-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/fonts/Inter-Bold.ttf -------------------------------------------------------------------------------- /apps/native/src/assets/icons/arrow-back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/icons/arrow-back.png -------------------------------------------------------------------------------- /apps/native/src/assets/icons/logo2small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/icons/logo2small.png -------------------------------------------------------------------------------- /apps/native/src/assets/fonts/Inter-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/fonts/Inter-Medium.ttf -------------------------------------------------------------------------------- /apps/native/src/assets/fonts/Inter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/fonts/Inter-Regular.ttf -------------------------------------------------------------------------------- /apps/native/src/assets/icons/OrignalIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/icons/OrignalIcon.png -------------------------------------------------------------------------------- /apps/native/src/assets/icons/summaryIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/icons/summaryIcon.png -------------------------------------------------------------------------------- /apps/native/src/assets/fonts/Inter-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/fonts/Inter-SemiBold.ttf -------------------------------------------------------------------------------- /apps/native/src/assets/fonts/Montserrat-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/fonts/Montserrat-Bold.ttf -------------------------------------------------------------------------------- /apps/native/src/assets/fonts/Montserrat-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/fonts/Montserrat-Light.ttf -------------------------------------------------------------------------------- /apps/native/src/assets/fonts/Montserrat-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/fonts/Montserrat-Medium.ttf -------------------------------------------------------------------------------- /apps/native/src/assets/fonts/Montserrat-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/fonts/Montserrat-Regular.ttf -------------------------------------------------------------------------------- /apps/native/src/assets/fonts/Montserrat-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/get-convex/turbo-expo-nextjs-clerk-convex-monorepo/HEAD/apps/native/src/assets/fonts/Montserrat-SemiBold.ttf -------------------------------------------------------------------------------- /apps/web/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "buildCommand": "cd ../../packages/backend && npx convex deploy --cmd 'cd ../../apps/web && turbo run build' --cmd-url-env-var-name NEXT_PUBLIC_CONVEX_URL" 3 | } 4 | -------------------------------------------------------------------------------- /packages/backend/convex/auth.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | providers: [ 3 | { 4 | domain: process.env.CLERK_ISSUER_URL, 5 | applicationID: "convex", 6 | }, 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /apps/web/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /apps/web/src/app/notes/page.tsx: -------------------------------------------------------------------------------- 1 | import Header from "@/components/Header"; 2 | import Notes from "@/components/notes/Notes"; 3 | 4 | export default function Home() { 5 | return ( 6 |
7 |
8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /apps/native/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | 16 | # Temporary files created by Metro to check the health of the file watcher 17 | .metro-health-check* 18 | -------------------------------------------------------------------------------- /apps/web/public/images/star.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/backend/convex/schema.ts: -------------------------------------------------------------------------------- 1 | import { defineSchema, defineTable } from "convex/server"; 2 | import { v } from "convex/values"; 3 | 4 | export default defineSchema({ 5 | notes: defineTable({ 6 | userId: v.string(), 7 | title: v.string(), 8 | content: v.string(), 9 | summary: v.optional(v.string()), 10 | }), 11 | }); 12 | -------------------------------------------------------------------------------- /apps/native/index.tsx: -------------------------------------------------------------------------------- 1 | import { registerRootComponent } from "expo"; 2 | 3 | import App from "./App"; 4 | 5 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App); 6 | // It also ensures that whether you load the app in the Expo client or in a native build, 7 | // the environment is set up appropriately 8 | registerRootComponent(App); 9 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "packageRules": [ 4 | { 5 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"], 6 | "automerge": true 7 | }, 8 | { 9 | "matchDepTypes": ["devDependencies"], 10 | "automerge": true 11 | } 12 | ], 13 | "extends": ["config:best-practices"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@packages/backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "dev": "convex dev", 7 | "setup": "convex dev --until-success", 8 | "typecheck": "tsc --noEmit -p convex" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "convex": "^1.29.3", 14 | "openai": "^6.9.1" 15 | }, 16 | "devDependencies": { 17 | "typescript": "5.9.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "globalDependencies": ["**/.env.*local"], 4 | "ui": "tui", 5 | "tasks": { 6 | "build": { 7 | "outputs": ["dist/**", ".next/**", "!.next/cache/**"], 8 | "dependsOn": ["^build"] 9 | }, 10 | "dev": { 11 | "cache": false, 12 | "persistent": true 13 | }, 14 | "lint": {}, 15 | "typecheck": {}, 16 | "clean": { 17 | "cache": false 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apps/web/src/app/notes/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | import Header from "@/components/Header"; 2 | import NoteDetails from "@/components/notes/NoteDetails"; 3 | import { Id } from "@packages/backend/convex/_generated/dataModel"; 4 | 5 | export default async function Page({ params }: { params: Promise<{ slug: string }> }) { 6 | const { slug } = await params; 7 | return ( 8 |
9 |
10 | } /> 11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /apps/web/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server"; 2 | import { NextResponse } from "next/server"; 3 | 4 | const isProtectedRoute = createRouteMatcher(["/notes(.*)"]); 5 | 6 | export default clerkMiddleware(async (auth, request) => { 7 | if (isProtectedRoute(request)) { 8 | await auth.protect(); 9 | } 10 | 11 | return NextResponse.next(); 12 | }); 13 | 14 | export const config = { 15 | matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"], 16 | }; 17 | -------------------------------------------------------------------------------- /apps/web/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | # next.js 12 | .next/ 13 | .swc/ 14 | out/ 15 | build 16 | 17 | # expo 18 | .expo 19 | 20 | # misc 21 | .DS_Store 22 | *.pem 23 | dist 24 | 25 | # debug 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | 30 | # local env files 31 | .env.local 32 | .env.development.local 33 | .env.test.local 34 | .env.production.local 35 | .env 36 | 37 | # turbo 38 | .turbo 39 | -------------------------------------------------------------------------------- /apps/web/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Header from "@/components/Header"; 4 | import Benefits from "@/components/home/Benefits"; 5 | import Footer from "@/components/home/Footer"; 6 | import FooterHero from "@/components/home/FooterHero"; 7 | import Hero from "@/components/home/Hero"; 8 | import Testimonials from "@/components/home/Testimonials"; 9 | 10 | export default function Home() { 11 | return ( 12 |
13 |
14 | 15 | 16 | 17 | 18 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /packages/backend/convex/_generated/api.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * Generated `api` utility. 4 | * 5 | * THIS CODE IS AUTOMATICALLY GENERATED. 6 | * 7 | * To regenerate, run `npx convex dev`. 8 | * @module 9 | */ 10 | 11 | import { anyApi, componentsGeneric } from "convex/server"; 12 | 13 | /** 14 | * A utility for referencing Convex functions in your app's API. 15 | * 16 | * Usage: 17 | * ```js 18 | * const myFunctionReference = api.myModule.myFunction; 19 | * ``` 20 | */ 21 | export const api = anyApi; 22 | export const internal = anyApi; 23 | export const components = componentsGeneric(); 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "convex-monorepo", 3 | "private": true, 4 | "scripts": { 5 | "dev": "turbo run dev", 6 | "build": "turbo run build", 7 | "typecheck": "turbo run typecheck", 8 | "clean": "turbo run clean && rm -rf node_modules", 9 | "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\" --ignore-path .gitignore" 10 | }, 11 | "devDependencies": { 12 | "prettier": "3.7.4", 13 | "turbo": "2.6.2" 14 | }, 15 | "engines": { 16 | "node": ">=20.19.4" 17 | }, 18 | "workspaces": [ 19 | "apps/*", 20 | "packages/*" 21 | ], 22 | "packageManager": "yarn@1.22.22" 23 | } 24 | -------------------------------------------------------------------------------- /apps/web/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/native/ConvexClientProvider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ConvexReactClient } from "convex/react"; 4 | import { ConvexProviderWithClerk } from "convex/react-clerk"; 5 | import { ClerkProvider, useAuth } from "@clerk/clerk-expo"; 6 | 7 | const convex = new ConvexReactClient(process.env.EXPO_PUBLIC_CONVEX_URL); 8 | 9 | export default function ConvexClientProvider({ children }) { 10 | return ( 11 | 14 | 15 | {children} 16 | 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/web/public/images/blue-circle-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /apps/web/public/images/blue-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /apps/web/public/images/cricle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /apps/web/public/images/hero_image_bg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/backend/convex/utils.ts: -------------------------------------------------------------------------------- 1 | export function missingEnvVariableUrl(envVarName: string, whereToGet: string) { 2 | const deployment = deploymentName(); 3 | if (!deployment) return `Missing ${envVarName} in environment variables.`; 4 | return ( 5 | `\n Missing ${envVarName} in environment variables.\n\n` + 6 | ` Get it from ${whereToGet} .\n Paste it on the Convex dashboard:\n` + 7 | ` https://dashboard.convex.dev/d/${deployment}/settings?var=${envVarName}` 8 | ); 9 | } 10 | 11 | export function deploymentName() { 12 | const url = process.env.CONVEX_CLOUD_URL; 13 | if (!url) return undefined; 14 | const regex = new RegExp("https://(.+).convex.cloud"); 15 | return regex.exec(url)?.[1]; 16 | } 17 | -------------------------------------------------------------------------------- /apps/web/src/components/common/Logo.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import Link from "next/link"; 3 | import React from "react"; 4 | 5 | interface Props { 6 | isMobile?: boolean; 7 | } 8 | 9 | const Logo = ({ isMobile }: Props) => { 10 | return ( 11 | 12 |
13 | logo 14 | {!isMobile ? ( 15 |

16 | UseNotes 17 |

18 | ) : null} 19 |
20 | 21 | ); 22 | }; 23 | 24 | export default Logo; 25 | -------------------------------------------------------------------------------- /apps/web/src/components/common/Menu.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | interface Props { 4 | menuItems: { 5 | title: string; 6 | url: string; 7 | }[]; 8 | } 9 | 10 | const Menu = ({ menuItems }: Props) => { 11 | return ( 12 | 24 | ); 25 | }; 26 | 27 | export default Menu; 28 | -------------------------------------------------------------------------------- /apps/native/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "newArchEnabled": true, 4 | "name": "NotesContract", 5 | "slug": "NotesContract", 6 | "version": "1.0.0", 7 | "orientation": "portrait", 8 | "icon": "./assets/icon.png", 9 | "userInterfaceStyle": "light", 10 | "splash": { 11 | "image": "./assets/splash.png", 12 | "resizeMode": "contain", 13 | "backgroundColor": "#0D87E1" 14 | }, 15 | "assetBundlePatterns": ["**/*"], 16 | "ios": { 17 | "supportsTablet": true 18 | }, 19 | "android": { 20 | "adaptiveIcon": { 21 | "foregroundImage": "./assets/adaptive-icon.png", 22 | "backgroundColor": "#ffffff" 23 | } 24 | }, 25 | "web": { 26 | "favicon": "./assets/favicon.png" 27 | }, 28 | "plugins": ["expo-font"] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/backend/convex/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | /* This TypeScript project config describes the environment that 3 | * Convex functions run in and is used to typecheck them. 4 | * You can modify it, but some settings required to use Convex. 5 | */ 6 | "compilerOptions": { 7 | /* These settings are not required by Convex and can be modified. */ 8 | "allowJs": true, 9 | "strict": true, 10 | 11 | /* These compiler options are required by Convex */ 12 | "target": "ESNext", 13 | "lib": ["ES2021", "dom"], 14 | "forceConsistentCasingInFileNames": true, 15 | "allowSyntheticDefaultImports": true, 16 | "module": "ESNext", 17 | "moduleResolution": "Bundler", 18 | "isolatedModules": true, 19 | "skipLibCheck": true, 20 | "noEmit": true 21 | }, 22 | "include": ["./**/*"], 23 | "exclude": ["./_generated"] 24 | } 25 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": true, 12 | "noEmit": true, 13 | "esModuleInterop": true, 14 | "module": "esnext", 15 | "moduleResolution": "bundler", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "jsx": "react-jsx", 19 | "incremental": true, 20 | "plugins": [ 21 | { 22 | "name": "next" 23 | } 24 | ], 25 | "paths": { 26 | "@/*": [ 27 | "./src/*" 28 | ] 29 | } 30 | }, 31 | "include": [ 32 | "next-env.d.ts", 33 | "**/*.ts", 34 | "**/*.tsx", 35 | ".next/types/**/*.ts", 36 | ".next/dev/types/**/*.ts" 37 | ], 38 | "exclude": [ 39 | "node_modules" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /apps/web/public/images/message-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /apps/web/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter, Montserrat, Lato } from "next/font/google"; 3 | import { cn } from "@/lib/utils"; 4 | import "./globals.css"; 5 | import ConvexClientProvider from "./ConvexClientProvider"; 6 | 7 | const inter = Inter({ subsets: ["latin"] }); 8 | const montserrat = Montserrat({ subsets: ["latin"] }); 9 | const lato = Lato({ weight: "400", subsets: ["latin"] }); 10 | 11 | export const metadata: Metadata = { 12 | title: "Notes App", 13 | description: "This is an app to take notes.", 14 | }; 15 | 16 | export default function RootLayout({ 17 | children, 18 | }: { 19 | children: React.ReactNode; 20 | }) { 21 | return ( 22 | 23 | 24 | {children} 25 | 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /apps/web/public/images/message-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /apps/web/public/images/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /apps/web/src/app/ConvexClientProvider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ReactNode } from "react"; 4 | import { ConvexReactClient } from "convex/react"; 5 | import { ConvexProviderWithClerk } from "convex/react-clerk"; 6 | import { ClerkProvider, useAuth } from "@clerk/clerk-react"; 7 | import { ErrorBoundary } from "./ErrorBoundary"; 8 | 9 | const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!); 10 | 11 | export default function ConvexClientProvider({ 12 | children, 13 | }: { 14 | children: ReactNode; 15 | }) { 16 | return ( 17 | // NOTE: Once you get Clerk working you can remove this error boundary 18 | 19 | 22 | 23 | {children} 24 | 25 | 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /apps/native/metro.config.js: -------------------------------------------------------------------------------- 1 | // Learn more https://docs.expo.dev/guides/monorepos 2 | 3 | const { getDefaultConfig } = require("expo/metro-config"); 4 | const { FileStore } = require("metro-cache"); 5 | const path = require("path"); 6 | 7 | const projectRoot = __dirname; 8 | const workspaceRoot = path.resolve(projectRoot, "../.."); 9 | 10 | const config = getDefaultConfig(projectRoot); 11 | 12 | // #1 - Watch all files in the monorepo 13 | config.watchFolders = [workspaceRoot]; 14 | // #3 - Force resolving nested modules to the folders below 15 | config.resolver.disableHierarchicalLookup = true; 16 | // #2 - Try resolving with project modules first, then workspace modules 17 | config.resolver.nodeModulesPaths = [ 18 | path.resolve(projectRoot, "node_modules"), 19 | path.resolve(workspaceRoot, "node_modules"), 20 | ]; 21 | 22 | // Use turborepo to restore the cache when possible 23 | config.cacheStores = [ 24 | new FileStore({ 25 | root: path.join(projectRoot, "node_modules", ".cache", "metro"), 26 | }), 27 | ]; 28 | 29 | module.exports = config; 30 | -------------------------------------------------------------------------------- /apps/web/src/components/notes/NoteItem.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import DeleteNote from "./DeleteNote"; 3 | 4 | export interface NoteProps { 5 | note: { 6 | title: string; 7 | _id: string; 8 | _creationTime: number; 9 | }; 10 | deleteNote: any; 11 | } 12 | 13 | const NoteItem = ({ note, deleteNote }: NoteProps) => { 14 | return ( 15 |
16 | 17 |

18 | {note.title} 19 |

20 | 21 |

22 | {new Date(Number(note._creationTime)).toLocaleDateString()} 23 |

24 | deleteNote({ noteId: note._id })} /> 25 |
26 | ); 27 | }; 28 | 29 | export default NoteItem; 30 | -------------------------------------------------------------------------------- /apps/native/src/navigation/Navigation.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { NavigationContainer } from "@react-navigation/native"; 3 | import { createNativeStackNavigator } from "@react-navigation/native-stack"; 4 | import LoginScreen from "../screens/LoginScreen"; 5 | import NotesDashboardScreen from "../screens/NotesDashboardScreen"; 6 | import InsideNoteScreen from "../screens/InsideNoteScreen"; 7 | import CreateNoteScreen from "../screens/CreateNoteScreen"; 8 | 9 | const Stack = createNativeStackNavigator(); 10 | 11 | const Navigation = () => { 12 | return ( 13 | 14 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | ); 29 | }; 30 | 31 | export default Navigation; 32 | -------------------------------------------------------------------------------- /apps/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "typecheck": "tsc --noEmit" 11 | }, 12 | "dependencies": { 13 | "@clerk/clerk-react": "^5.57.1", 14 | "@clerk/nextjs": "^6.35.6", 15 | "@headlessui/react": "^2.2.9", 16 | "@heroicons/react": "^2.2.0", 17 | "@packages/backend": "*", 18 | "@radix-ui/react-avatar": "^1.1.11", 19 | "@radix-ui/react-dropdown-menu": "^2.1.16", 20 | "@radix-ui/react-slot": "^1.2.4", 21 | "class-variance-authority": "^0.7.0", 22 | "convex": "^1.29.3", 23 | "lucide-react": "^0.555.0", 24 | "next": "^16.0.9", 25 | "react": "^19.2.2", 26 | "react-dom": "^19.2.1", 27 | "tailwind-merge": "^3.4.0" 28 | }, 29 | "devDependencies": { 30 | "@tailwindcss/forms": "0.5.10", 31 | "@tailwindcss/postcss": "4.1.17", 32 | "@types/node": "^24.10.1", 33 | "@types/react": "^19.2.7", 34 | "@types/react-dom": "^19.2.3", 35 | "eslint": "^9.39.1", 36 | "eslint-config-next": "16.0.7", 37 | "postcss": "8.5.6", 38 | "tailwindcss": "4.1.17", 39 | "typescript": "5.9.3" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/backend/convex/_generated/api.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * Generated `api` utility. 4 | * 5 | * THIS CODE IS AUTOMATICALLY GENERATED. 6 | * 7 | * To regenerate, run `npx convex dev`. 8 | * @module 9 | */ 10 | 11 | import type * as notes from "../notes.js"; 12 | import type * as openai from "../openai.js"; 13 | import type * as utils from "../utils.js"; 14 | 15 | import type { 16 | ApiFromModules, 17 | FilterApi, 18 | FunctionReference, 19 | } from "convex/server"; 20 | 21 | declare const fullApi: ApiFromModules<{ 22 | notes: typeof notes; 23 | openai: typeof openai; 24 | utils: typeof utils; 25 | }>; 26 | 27 | /** 28 | * A utility for referencing Convex functions in your app's public API. 29 | * 30 | * Usage: 31 | * ```js 32 | * const myFunctionReference = api.myModule.myFunction; 33 | * ``` 34 | */ 35 | export declare const api: FilterApi< 36 | typeof fullApi, 37 | FunctionReference 38 | >; 39 | 40 | /** 41 | * A utility for referencing Convex functions in your app's internal API. 42 | * 43 | * Usage: 44 | * ```js 45 | * const myFunctionReference = internal.myModule.myFunction; 46 | * ``` 47 | */ 48 | export declare const internal: FilterApi< 49 | typeof fullApi, 50 | FunctionReference 51 | >; 52 | 53 | export declare const components: {}; 54 | -------------------------------------------------------------------------------- /apps/web/public/images/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /apps/web/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/web/src/components/notes/NoteDetails.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { api } from "@packages/backend/convex/_generated/api"; 4 | import { Id } from "@packages/backend/convex/_generated/dataModel"; 5 | import { useQuery } from "convex/react"; 6 | import ComplexToggle from "../home/ComplexToggle"; 7 | import { useState } from "react"; 8 | 9 | interface NoteDetailsProps { 10 | noteId: Id<"notes">; 11 | } 12 | 13 | const NoteDetails = ({ noteId }: NoteDetailsProps) => { 14 | const [isSummary, setIsSummary] = useState(false); 15 | const currentNote = useQuery(api.notes.getNote, { id: noteId }); 16 | 17 | return ( 18 |
19 |
20 | 21 |
22 |

23 | {currentNote?.title} 24 |

25 |

26 | {!isSummary 27 | ? currentNote?.content 28 | : currentNote?.summary 29 | ? currentNote?.summary 30 | : "No Summary available"} 31 |

32 |
33 | ); 34 | }; 35 | 36 | export default NoteDetails; 37 | -------------------------------------------------------------------------------- /apps/web/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 37 | -------------------------------------------------------------------------------- /apps/native/App.tsx: -------------------------------------------------------------------------------- 1 | import { View, StatusBar, Platform } from "react-native"; 2 | import { useFonts } from "expo-font"; 3 | import { LogBox } from "react-native"; 4 | import Navigation from "./src/navigation/Navigation"; 5 | import ConvexClientProvider from "./ConvexClientProvider"; 6 | 7 | export default function App() { 8 | LogBox.ignoreLogs(["Warning: ..."]); 9 | LogBox.ignoreAllLogs(); 10 | 11 | const [loaded] = useFonts({ 12 | Bold: require("./src/assets/fonts/Inter-Bold.ttf"), 13 | SemiBold: require("./src/assets/fonts/Inter-SemiBold.ttf"), 14 | Medium: require("./src/assets/fonts/Inter-Medium.ttf"), 15 | Regular: require("./src/assets/fonts/Inter-Regular.ttf"), 16 | 17 | MBold: require("./src/assets/fonts/Montserrat-Bold.ttf"), 18 | MSemiBold: require("./src/assets/fonts/Montserrat-SemiBold.ttf"), 19 | MMedium: require("./src/assets/fonts/Montserrat-Medium.ttf"), 20 | MRegular: require("./src/assets/fonts/Montserrat-Regular.ttf"), 21 | MLight: require("./src/assets/fonts/Montserrat-Light.ttf"), 22 | }); 23 | if (!loaded) { 24 | return false; 25 | } 26 | 27 | const STATUS_BAR_HEIGHT = 28 | Platform.OS === "ios" ? 50 : StatusBar.currentHeight; 29 | 30 | return ( 31 | 32 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /apps/web/src/components/common/avatar.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as AvatarPrimitive from "@radix-ui/react-avatar"; 3 | import { cn } from "@/lib/utils"; 4 | 5 | const Avatar = React.forwardRef< 6 | React.ComponentRef, 7 | React.ComponentPropsWithoutRef 8 | >(({ className, ...props }, ref) => ( 9 | 17 | )); 18 | Avatar.displayName = AvatarPrimitive.Root.displayName; 19 | 20 | const AvatarImage = React.forwardRef< 21 | React.ComponentRef, 22 | React.ComponentPropsWithoutRef 23 | >(({ className, ...props }, ref) => ( 24 | 29 | )); 30 | AvatarImage.displayName = AvatarPrimitive.Image.displayName; 31 | 32 | const AvatarFallback = React.forwardRef< 33 | React.ComponentRef, 34 | React.ComponentPropsWithoutRef 35 | >(({ className, ...props }, ref) => ( 36 | 44 | )); 45 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; 46 | 47 | export { Avatar, AvatarImage, AvatarFallback }; 48 | -------------------------------------------------------------------------------- /apps/web/src/components/home/FooterHero.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import Link from "next/link"; 3 | import React from "react"; 4 | 5 | const FooterHero = () => { 6 | return ( 7 |
8 |
9 |
10 |

11 | Start Your Intelligent Note-Taking Journey 12 |

13 |

14 | Sign up now and experience the power of AI-enhanced note-taking with 15 | UseNotes 16 |

17 | 18 | 21 | 22 |
23 |
24 | hero 30 |
31 |
32 |
33 | ); 34 | }; 35 | 36 | export default FooterHero; 37 | -------------------------------------------------------------------------------- /apps/native/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "native-app", 3 | "version": "1.0.0", 4 | "main": "index.tsx", 5 | "scripts": { 6 | "dev": "expo start", 7 | "start": "expo start", 8 | "android": "expo start --android", 9 | "ios": "expo start --ios", 10 | "web": "expo start --web", 11 | "typecheck": "tsc --noEmit" 12 | }, 13 | "dependencies": { 14 | "@clerk/clerk-expo": "^2.19.6", 15 | "@expo/vector-icons": "^15.0.3", 16 | "@packages/backend": "*", 17 | "@react-native/virtualized-lists": "^0.82.1", 18 | "@react-navigation/bottom-tabs": "^7.8.11", 19 | "@react-navigation/native": "^7.1.24", 20 | "@react-navigation/native-stack": "^7.8.5", 21 | "expo": "~54.0.25", 22 | "expo-asset": "~12.0.10", 23 | "expo-auth-session": "~7.0.9", 24 | "expo-font": "~14.0.9", 25 | "expo-system-ui": "~6.0.8", 26 | "expo-web-browser": "~15.0.9", 27 | "react": "19.2.2", 28 | "react-dom": "19.2.1", 29 | "react-native": "0.82.1", 30 | "react-native-gesture-handler": "~2.29.1", 31 | "react-native-keyboard-aware-scroll-view": "^0.9.5", 32 | "react-native-reanimated": "~4.1.5", 33 | "react-native-responsive-fontsize": "^0.5.1", 34 | "react-native-safe-area-context": "5.6.2", 35 | "react-native-screens": "~4.18.0", 36 | "react-native-web": "~0.21.2", 37 | "react-native-webview": "13.16.0", 38 | "use-sync-external-store": "^1.6.0" 39 | }, 40 | "devDependencies": { 41 | "@babel/core": "7.28.5", 42 | "@types/jest": "30.0.0", 43 | "@types/react": "^19.2.7", 44 | "@types/react-test-renderer": "19.1.0", 45 | "jest": "30.2.0", 46 | "jest-expo": "54.0.13", 47 | "react-test-renderer": "19.2.1", 48 | "typescript": "5.9.3" 49 | }, 50 | "private": true 51 | } 52 | -------------------------------------------------------------------------------- /apps/web/src/components/notes/Checkbox.tsx: -------------------------------------------------------------------------------- 1 | const Checkbox = ({ 2 | isChecked, 3 | checkHandler, 4 | openaiKeySet, 5 | }: { 6 | isChecked: boolean; 7 | checkHandler: () => void; 8 | openaiKeySet: boolean; 9 | }) => { 10 | return ( 11 |
12 |
13 | 22 |
23 |
24 | 30 | {openaiKeySet ? ( 31 |

32 | Check this box if you want to generate summaries using AI 33 |

34 | ) : ( 35 |

36 | 41 | Set an OPENAI_API_KEY in your environment variables here. 42 | 43 |

44 | )} 45 |
46 |
47 | ); 48 | }; 49 | 50 | export default Checkbox; 51 | -------------------------------------------------------------------------------- /apps/web/src/components/home/ComplexToggle.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Switch } from "@headlessui/react"; 3 | 4 | function classNames(...classes: string[]) { 5 | return classes.filter(Boolean).join(" "); 6 | } 7 | 8 | export default function ComplexToggle({ 9 | setIsSummary, 10 | isSummary, 11 | }: { 12 | setIsSummary: (value: boolean) => void; 13 | isSummary: boolean; 14 | }) { 15 | return ( 16 | 17 | 18 | 23 | Original Note 24 | {" "} 25 | 26 | 34 | 42 | 43 | 46 | AI Summary 47 | {" "} 48 | 49 | 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /packages/backend/convex/notes.ts: -------------------------------------------------------------------------------- 1 | import { mutation, query } from "./_generated/server"; 2 | import { v } from "convex/values"; 3 | import { internal } from "../convex/_generated/api"; 4 | import { Auth } from "convex/server"; 5 | 6 | export const getUserId = async (ctx: { auth: Auth }) => { 7 | return (await ctx.auth.getUserIdentity())?.subject; 8 | }; 9 | 10 | // Get all notes for a specific user 11 | export const getNotes = query({ 12 | args: {}, 13 | handler: async (ctx) => { 14 | const userId = await getUserId(ctx); 15 | if (!userId) return null; 16 | 17 | const notes = await ctx.db 18 | .query("notes") 19 | .filter((q) => q.eq(q.field("userId"), userId)) 20 | .collect(); 21 | 22 | return notes; 23 | }, 24 | }); 25 | 26 | // Get note for a specific note 27 | export const getNote = query({ 28 | args: { 29 | id: v.optional(v.id("notes")), 30 | }, 31 | handler: async (ctx, args) => { 32 | const { id } = args; 33 | if (!id) return null; 34 | const note = await ctx.db.get(id); 35 | return note; 36 | }, 37 | }); 38 | 39 | // Create a new note for a user 40 | export const createNote = mutation({ 41 | args: { 42 | title: v.string(), 43 | content: v.string(), 44 | isSummary: v.boolean(), 45 | }, 46 | handler: async (ctx, { title, content, isSummary }) => { 47 | const userId = await getUserId(ctx); 48 | if (!userId) throw new Error("User not found"); 49 | const noteId = await ctx.db.insert("notes", { userId, title, content }); 50 | 51 | if (isSummary) { 52 | await ctx.scheduler.runAfter(0, internal.openai.summary, { 53 | id: noteId, 54 | title, 55 | content, 56 | }); 57 | } 58 | 59 | return noteId; 60 | }, 61 | }); 62 | 63 | export const deleteNote = mutation({ 64 | args: { 65 | noteId: v.id("notes"), 66 | }, 67 | handler: async (ctx, args) => { 68 | await ctx.db.delete(args.noteId); 69 | }, 70 | }); 71 | -------------------------------------------------------------------------------- /packages/backend/convex/_generated/dataModel.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * Generated data model types. 4 | * 5 | * THIS CODE IS AUTOMATICALLY GENERATED. 6 | * 7 | * To regenerate, run `npx convex dev`. 8 | * @module 9 | */ 10 | 11 | import type { 12 | DataModelFromSchemaDefinition, 13 | DocumentByName, 14 | TableNamesInDataModel, 15 | SystemTableNames, 16 | } from "convex/server"; 17 | import type { GenericId } from "convex/values"; 18 | import schema from "../schema.js"; 19 | 20 | /** 21 | * The names of all of your Convex tables. 22 | */ 23 | export type TableNames = TableNamesInDataModel; 24 | 25 | /** 26 | * The type of a document stored in Convex. 27 | * 28 | * @typeParam TableName - A string literal type of the table name (like "users"). 29 | */ 30 | export type Doc = DocumentByName< 31 | DataModel, 32 | TableName 33 | >; 34 | 35 | /** 36 | * An identifier for a document in Convex. 37 | * 38 | * Convex documents are uniquely identified by their `Id`, which is accessible 39 | * on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids). 40 | * 41 | * Documents can be loaded using `db.get(id)` in query and mutation functions. 42 | * 43 | * IDs are just strings at runtime, but this type can be used to distinguish them from other 44 | * strings when type checking. 45 | * 46 | * @typeParam TableName - A string literal type of the table name (like "users"). 47 | */ 48 | export type Id = 49 | GenericId; 50 | 51 | /** 52 | * A type describing your Convex data model. 53 | * 54 | * This type includes information about what tables you have, the type of 55 | * documents stored in those tables, and the indexes defined on them. 56 | * 57 | * This type is used to parameterize methods like `queryGeneric` and 58 | * `mutationGeneric` to make them type-safe. 59 | */ 60 | export type DataModel = DataModelFromSchemaDefinition; 61 | -------------------------------------------------------------------------------- /apps/web/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | 3 | @theme { 4 | --breakpoint-*: initial; 5 | --breakpoint-sm: 640px; 6 | --breakpoint-md: 768px; 7 | --breakpoint-lg: 1024px; 8 | --breakpoint-xl: 1440px; 9 | 10 | --color-primary: #0d87e1; 11 | 12 | --font-inter: Inter, sans-serif; 13 | --font-lato: Lato, sans-serif; 14 | --font-montserrat: Montserrat, sans-serif; 15 | 16 | --background-image-gradient-radial: radial-gradient(var(--tw-gradient-stops)); 17 | --background-image-gradient-conic: conic-gradient( 18 | from 180deg at 50% 50%, 19 | var(--tw-gradient-stops) 20 | ); 21 | } 22 | 23 | @utility container { 24 | margin-inline: auto; 25 | } 26 | 27 | /* 28 | The default border color has changed to `currentcolor` in Tailwind CSS v4, 29 | so we've added these compatibility styles to make sure everything still 30 | looks the same as it did with Tailwind CSS v3. 31 | 32 | If we ever want to remove these styles, we need to add an explicit border 33 | color utility to any element that depends on these defaults. 34 | */ 35 | @layer base { 36 | *, 37 | ::after, 38 | ::before, 39 | ::backdrop, 40 | ::file-selector-button { 41 | border-color: var(--color-gray-200, currentcolor); 42 | } 43 | } 44 | 45 | .linear_gradient { 46 | background: linear-gradient( 47 | 267deg, 48 | #fff -9.43%, 49 | #040218 -9.42%, 50 | #fff 4.63%, 51 | #d2e4f2 127.55% 52 | ); 53 | } 54 | 55 | .bg_image { 56 | background: 57 | linear-gradient( 58 | 181deg, 59 | rgba(255, 255, 255, 0) 57.92%, 60 | #fff 97.09%, 61 | rgba(255, 255, 255, 0) 127.09% 62 | ), 63 | url("/images/background.png"), 64 | lightgray 0% 0% / 261.2499952316284px 261.2499952316284px repeat; 65 | } 66 | .button { 67 | border-radius: 8px; 68 | background: linear-gradient( 69 | 267deg, 70 | #0983df -9.43%, 71 | #040218 -9.42%, 72 | #0d87e1 4.63%, 73 | #0983df 127.55% 74 | ); 75 | box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); 76 | } 77 | -------------------------------------------------------------------------------- /apps/web/src/components/common/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Slot } from "@radix-ui/react-slot"; 3 | import { cva, type VariantProps } from "class-variance-authority"; 4 | 5 | import { cn } from "@/lib/utils"; 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90", 14 | destructive: 15 | "bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90", 16 | outline: 17 | "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground", 18 | secondary: 19 | "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", 20 | ghost: "hover:bg-accent hover:text-accent-foreground", 21 | link: "text-primary underline-offset-4 hover:underline", 22 | }, 23 | size: { 24 | default: "h-9 px-4 py-2", 25 | sm: "h-8 rounded-md px-3 text-xs", 26 | lg: "h-10 rounded-md px-8", 27 | icon: "h-9 w-9", 28 | }, 29 | }, 30 | defaultVariants: { 31 | variant: "default", 32 | size: "default", 33 | }, 34 | }, 35 | ); 36 | 37 | export interface ButtonProps 38 | extends React.ButtonHTMLAttributes, 39 | VariantProps { 40 | asChild?: boolean; 41 | } 42 | 43 | const Button = React.forwardRef( 44 | ({ className, variant, size, asChild = false, ...props }, ref) => { 45 | const Comp = asChild ? Slot : "button"; 46 | return ( 47 | 52 | ); 53 | }, 54 | ); 55 | Button.displayName = "Button"; 56 | 57 | export { Button, buttonVariants }; 58 | -------------------------------------------------------------------------------- /apps/web/src/components/common/UserNav.tsx: -------------------------------------------------------------------------------- 1 | import { useClerk } from "@clerk/clerk-react"; 2 | import { LogOut, Paintbrush2 } from "lucide-react"; 3 | import Link from "next/link"; 4 | import { Avatar, AvatarFallback, AvatarImage } from "./avatar"; 5 | import { Button } from "./button"; 6 | import { 7 | DropdownMenu, 8 | DropdownMenuContent, 9 | DropdownMenuItem, 10 | DropdownMenuLabel, 11 | DropdownMenuSeparator, 12 | DropdownMenuTrigger, 13 | } from "./dropdown-menu"; 14 | 15 | export function UserNav({ 16 | image, 17 | name, 18 | email, 19 | }: { 20 | image: string; 21 | name: string; 22 | email: string; 23 | }) { 24 | const { signOut } = useClerk(); 25 | 26 | return ( 27 | 28 | 29 | 37 | 38 | 39 | 40 |
41 |

42 | {name} 43 |

44 |

{email}

45 |
46 |
47 | 48 | 49 | 50 | 51 | Dashboard 52 | 53 | 54 | signOut()} 56 | className="hover:cursor-pointer hover:bg-gray-200" 57 | > 58 | 59 | Log out 60 | 61 |
62 |
63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /apps/web/src/components/notes/Notes.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { api } from "@packages/backend/convex/_generated/api"; 4 | import { useMutation, useQuery } from "convex/react"; 5 | import Image from "next/image"; 6 | import { useState } from "react"; 7 | import CreateNote from "./CreateNote"; 8 | import NoteItem from "./NoteItem"; 9 | 10 | const Notes = () => { 11 | const [search, setSearch] = useState(""); 12 | 13 | const allNotes = useQuery(api.notes.getNotes); 14 | const deleteNote = useMutation(api.notes.deleteNote); 15 | 16 | const finalNotes = search 17 | ? allNotes?.filter( 18 | (note) => 19 | note.title.toLowerCase().includes(search.toLowerCase()) || 20 | note.content.toLowerCase().includes(search.toLowerCase()), 21 | ) 22 | : allNotes; 23 | 24 | return ( 25 |
26 |

27 | Your Notes 28 |

29 |
30 |
31 | search 38 | setSearch(e.target.value)} 43 | className="flex-1 text-[#2D2D2D] text-[17px] sm:text-2xl not-italic font-light leading-[114.3%] tracking-[-0.6px] focus:outline-0 focus:ring-0 focus:border-0 border-0" 44 | /> 45 |
46 |
47 | 48 |
49 | {finalNotes && 50 | finalNotes.map((note, index) => ( 51 | 52 | ))} 53 |
54 | 55 | 56 |
57 | ); 58 | }; 59 | 60 | export default Notes; 61 | -------------------------------------------------------------------------------- /apps/web/src/components/common/TestTimonialCard.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | interface Props { 3 | data: { 4 | feature?: boolean; 5 | review: string; 6 | profile: string; 7 | name: string; 8 | designation: string; 9 | }; 10 | } 11 | 12 | const TestTimonialCard = ({ data }: Props) => { 13 | return ( 14 |
19 |
20 | {Array(5) 21 | .fill(0) 22 | .map((data, index) => ( 23 | star 30 | ))} 31 |
32 |
37 | 38 | {data.review} 39 | 40 |
41 |
42 |
43 | ryan 50 |
51 |
52 |

57 | {data.name} 58 |

59 |

64 | {data.designation} 65 |

66 |
67 |
68 |
69 | ); 70 | }; 71 | 72 | export default TestTimonialCard; 73 | -------------------------------------------------------------------------------- /packages/backend/convex/openai.ts: -------------------------------------------------------------------------------- 1 | import OpenAI from "openai"; 2 | import { internalAction, internalMutation, query } from "./_generated/server"; 3 | import { v } from "convex/values"; 4 | import { internal } from "./_generated/api"; 5 | import { missingEnvVariableUrl } from "./utils"; 6 | 7 | export const openaiKeySet = query({ 8 | args: {}, 9 | handler: async () => { 10 | return !!process.env.OPENAI_API_KEY; 11 | }, 12 | }); 13 | 14 | export const summary = internalAction({ 15 | args: { 16 | id: v.id("notes"), 17 | title: v.string(), 18 | content: v.string(), 19 | }, 20 | handler: async (ctx, { id, title, content }) => { 21 | const prompt = `Take in the following note and return a summary: Title: ${title}, Note content: ${content}`; 22 | 23 | const apiKey = process.env.OPENAI_API_KEY; 24 | if (!apiKey) { 25 | const error = missingEnvVariableUrl( 26 | "OPENAI_API_KEY", 27 | "https://platform.openai.com/account/api-keys", 28 | ); 29 | console.error(error); 30 | await ctx.runMutation(internal.openai.saveSummary, { 31 | id: id, 32 | summary: error, 33 | }); 34 | return; 35 | } 36 | const openai = new OpenAI({ apiKey }); 37 | const output = await openai.chat.completions.create({ 38 | messages: [ 39 | { 40 | role: "system", 41 | content: 42 | "You are a helpful assistant designed to output JSON in this format: {summary: string}", 43 | }, 44 | { role: "user", content: prompt }, 45 | ], 46 | model: "gpt-4-1106-preview", 47 | response_format: { type: "json_object" }, 48 | }); 49 | 50 | // Pull the message content out of the response 51 | const messageContent = output.choices[0]?.message.content; 52 | 53 | console.log({ messageContent }); 54 | 55 | const parsedOutput = JSON.parse(messageContent!); 56 | console.log({ parsedOutput }); 57 | 58 | await ctx.runMutation(internal.openai.saveSummary, { 59 | id: id, 60 | summary: parsedOutput.summary, 61 | }); 62 | }, 63 | }); 64 | 65 | export const saveSummary = internalMutation({ 66 | args: { 67 | id: v.id("notes"), 68 | summary: v.string(), 69 | }, 70 | handler: async (ctx, { id, summary }) => { 71 | await ctx.db.patch(id, { 72 | summary: summary, 73 | }); 74 | }, 75 | }); 76 | -------------------------------------------------------------------------------- /apps/web/src/components/home/Hero.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import Link from "next/link"; 3 | 4 | const Hero = () => { 5 | return ( 6 |
7 |
8 |
9 |
10 |

11 | The Ultimate
Note-Taking Experience 12 |

13 |

14 | UseNotes harnesses the power of artificial intelligence to 15 | revolutionize the way you capture, organize, and recall your 16 | thoughts 17 |

18 | 19 | 22 | 23 |
24 |
25 |
26 |
27 | hero 34 |
35 |
36 | hero 43 |
44 |
45 |
46 |
47 |
48 |
49 | ); 50 | }; 51 | 52 | export default Hero; 53 | -------------------------------------------------------------------------------- /apps/web/src/app/ErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import { Component, ReactNode } from "react"; 2 | import NextLink from "next/link"; 3 | 4 | // NOTE: Once you get Clerk working you can remove this error boundary 5 | export class ErrorBoundary extends Component< 6 | { children: ReactNode }, 7 | { error: ReactNode | null } 8 | > { 9 | constructor(props: { children: ReactNode }) { 10 | super(props); 11 | this.state = { error: null }; 12 | } 13 | 14 | static getDerivedStateFromError(error: unknown) { 15 | const errorText = "" + (error as any).toString(); 16 | if ( 17 | errorText.includes("@clerk/clerk-react") && 18 | errorText.includes("publishableKey") 19 | ) { 20 | const [clerkDashboardUrl] = errorText.match(/https:\S+/) ?? []; 21 | return { 22 | error: ( 23 | <> 24 |

25 | Add{" "} 26 | 27 | NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY={'""'} 28 | {" "} 29 | to the{" "} 30 | 31 | .env 32 | {" "} 33 | file 34 |

35 | {clerkDashboardUrl ? ( 36 |

37 | You can find it at{" "} 38 | 43 | {clerkDashboardUrl} 44 | 45 |

46 | ) : null} 47 |

Raw error: {errorText}

48 | 49 | ), 50 | }; 51 | } 52 | 53 | // propagate error to Next.js provided error boundary 54 | throw error; 55 | } 56 | 57 | componentDidCatch() {} 58 | 59 | render() { 60 | if (this.state.error !== null) { 61 | return ( 62 |
63 |

64 | Caught an error while rendering: 65 |

66 | {this.state.error} 67 |
68 | ); 69 | } 70 | 71 | return this.props.children; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /apps/web/src/components/home/Footer.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Logo from "../common/Logo"; 3 | import Menu from "../common/Menu"; 4 | 5 | const menuItems = [ 6 | { 7 | title: "Home", 8 | url: "/", 9 | }, 10 | { 11 | title: "Benefits", 12 | url: "#Benefits", 13 | }, 14 | { 15 | title: "Get Started", 16 | url: "/notes", 17 | }, 18 | { 19 | title: "Reviews", 20 | url: "#reviews", 21 | }, 22 | ]; 23 | 24 | const Footer = () => { 25 | return ( 26 | <> 27 |
28 |
29 | 30 | 31 |
32 |
33 |

34 | Take more efficient notes with UseNotes 35 |

36 |
37 |

38 | Save countless hours of note-taking and organize your notes 39 | easier. 40 |

41 |

42 | © 2023 UseNotes. All rights reserved. 43 |

44 |
45 |
46 |
47 | 48 |
49 |
50 |
51 | 52 |

53 | Take more efficient notes with UseNotes 54 |

55 |

56 | Save countless hours of note-taking and organize your notes 57 | easier. 58 |

59 |
60 |
61 | 62 |
63 |
64 |

65 | © 2023 UseNotes. All rights reserved.
66 | Icons by Icons8 67 |

68 |
69 | 70 | ); 71 | }; 72 | 73 | export default Footer; 74 | -------------------------------------------------------------------------------- /apps/web/src/components/home/Testimonials.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import TestTimonialCard from "../common/TestTimonialCard"; 3 | 4 | const TestimonialsData = [ 5 | { 6 | rating: 5, 7 | review: 8 | "Great note-taking application! The AI features make note-taking a breeze", 9 | name: "Ryan Lowry", 10 | designation: "Engineer & Author", 11 | profile: "/images/profile.png", 12 | feature: false, 13 | }, 14 | { 15 | rating: 5, 16 | review: 17 | "Really like the clean design of UseNotes. The AI-driven search is impressively accurate, adding a personal dimension to my notes. Fast and very easy to use.", 18 | name: "John Collins", 19 | designation: "Engineer & Author", 20 | profile: "/images/profile.png", 21 | feature: true, 22 | }, 23 | { 24 | rating: 5, 25 | review: "Simply brilliant! UseNotes has elevated my productivity.", 26 | name: "Moe Partuj", 27 | designation: "Student", 28 | profile: "/images/Moe-Partuj.jpeg", 29 | feature: false, 30 | }, 31 | ]; 32 | 33 | const Testimonials = () => { 34 | return ( 35 |
39 | 46 |
47 |

48 | Reviews 49 |

50 |

51 | User Testimonials 52 |

53 | 54 |
55 | 62 | 69 | {TestimonialsData.map((item, index) => ( 70 | 71 | ))} 72 |
73 |
74 |
75 | ); 76 | }; 77 | 78 | export default Testimonials; 79 | -------------------------------------------------------------------------------- /packages/backend/convex/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your Convex functions directory! 2 | 3 | Write your Convex functions here. See 4 | https://docs.convex.dev/using/writing-convex-functions for more. 5 | 6 | A query function that takes two arguments looks like: 7 | 8 | ```ts 9 | // functions.js 10 | import { query } from "./_generated/server"; 11 | import { v } from "convex/values"; 12 | 13 | export const myQueryFunction = query({ 14 | // Validators for arguments. 15 | args: { 16 | first: v.number(), 17 | second: v.string(), 18 | }, 19 | 20 | // Function implementation. 21 | handler: async (ctx, args) => { 22 | // Read the database as many times as you need here. 23 | // See https://docs.convex.dev/database/reading-data. 24 | const documents = await ctx.db.query("tablename").collect(); 25 | 26 | // Arguments passed from the client are properties of the args object. 27 | console.log(args.first, args.second); 28 | 29 | // Write arbitrary JavaScript here: filter, aggregate, build derived data, 30 | // remove non-public properties, or create new objects. 31 | return documents; 32 | }, 33 | }); 34 | ``` 35 | 36 | Using this query function in a React component looks like: 37 | 38 | ```ts 39 | const data = useQuery(api.functions.myQueryFunction, { 40 | first: 10, 41 | second: "hello", 42 | }); 43 | ``` 44 | 45 | A mutation function looks like: 46 | 47 | ```ts 48 | // functions.js 49 | import { mutation } from "./_generated/server"; 50 | import { v } from "convex/values"; 51 | 52 | export const myMutationFunction = mutation({ 53 | // Validators for arguments. 54 | args: { 55 | first: v.string(), 56 | second: v.string(), 57 | }, 58 | 59 | // Function implementation. 60 | handler: async (ctx, args) => { 61 | // Insert or modify documents in the database here. 62 | // Mutations can also read from the database like queries. 63 | // See https://docs.convex.dev/database/writing-data. 64 | const message = { body: args.first, author: args.second }; 65 | const id = await ctx.db.insert("messages", message); 66 | 67 | // Optionally, return a value from your mutation. 68 | return await ctx.db.get(id); 69 | }, 70 | }); 71 | ``` 72 | 73 | Using this mutation function in a React component looks like: 74 | 75 | ```ts 76 | const mutation = useMutation(api.functions.myMutationFunction); 77 | function handleButtonPress() { 78 | // fire and forget, the most common way to use mutations 79 | mutation({ first: "Hello!", second: "me" }); 80 | // OR 81 | // use the result once the mutation has completed 82 | mutation({ first: "Hello!", second: "me" }).then((result) => 83 | console.log(result), 84 | ); 85 | } 86 | ``` 87 | 88 | Use the Convex CLI to push your functions to a deployment. See everything 89 | the Convex CLI can do by running `npx convex -h` in your project root 90 | directory. To learn more, launch the docs with `npx convex docs`. 91 | -------------------------------------------------------------------------------- /apps/web/src/components/home/Benefits.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | const benefits = [ 4 | { 5 | title: "Effortless Note-Taking", 6 | description: "Capture thoughts effortlessly with our intuitive interface", 7 | image: "/images/goodNews.png", 8 | }, 9 | { 10 | title: "Seamless Sync", 11 | description: 12 | "Access your notes anytime, anywhere, with seamless cloud synchronization.", 13 | image: "/images/cloudSync.png", 14 | }, 15 | { 16 | title: "Enhanced Productivity", 17 | description: 18 | "Let AI handle organization, so you can focus on what matters most.", 19 | image: "/images/googleCalander.png", 20 | }, 21 | { 22 | title: "AI-Powered Insights", 23 | description: 24 | "Gain valuable insights with smart analytics based on your note patterns.", 25 | image: "/images/bot.png", 26 | }, 27 | ]; 28 | 29 | const Benefits = () => { 30 | return ( 31 |
32 | 39 |
40 |

41 | Benefits 42 |

43 |

44 | Why Choose UseNotes 45 |

46 | 47 |
48 |
49 | {Array(3) 50 | .fill(0) 51 | .map((_, index) => ( 52 | line 59 | ))} 60 |
61 | 62 |
63 | {benefits.map((benefit, index) => ( 64 |
68 |
69 | benefit 76 |
77 |
78 |

79 | {benefit.title} 80 |

81 |

82 | {benefit.description} 83 |

84 |
85 |
86 | ))} 87 |
88 |
89 |
90 |
91 | ); 92 | }; 93 | 94 | export default Benefits; 95 | -------------------------------------------------------------------------------- /packages/backend/convex/_generated/server.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * Generated utilities for implementing server-side Convex query and mutation functions. 4 | * 5 | * THIS CODE IS AUTOMATICALLY GENERATED. 6 | * 7 | * To regenerate, run `npx convex dev`. 8 | * @module 9 | */ 10 | 11 | import { 12 | actionGeneric, 13 | httpActionGeneric, 14 | queryGeneric, 15 | mutationGeneric, 16 | internalActionGeneric, 17 | internalMutationGeneric, 18 | internalQueryGeneric, 19 | } from "convex/server"; 20 | 21 | /** 22 | * Define a query in this Convex app's public API. 23 | * 24 | * This function will be allowed to read your Convex database and will be accessible from the client. 25 | * 26 | * @param func - The query function. It receives a {@link QueryCtx} as its first argument. 27 | * @returns The wrapped query. Include this as an `export` to name it and make it accessible. 28 | */ 29 | export const query = queryGeneric; 30 | 31 | /** 32 | * Define a query that is only accessible from other Convex functions (but not from the client). 33 | * 34 | * This function will be allowed to read from your Convex database. It will not be accessible from the client. 35 | * 36 | * @param func - The query function. It receives a {@link QueryCtx} as its first argument. 37 | * @returns The wrapped query. Include this as an `export` to name it and make it accessible. 38 | */ 39 | export const internalQuery = internalQueryGeneric; 40 | 41 | /** 42 | * Define a mutation in this Convex app's public API. 43 | * 44 | * This function will be allowed to modify your Convex database and will be accessible from the client. 45 | * 46 | * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. 47 | * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. 48 | */ 49 | export const mutation = mutationGeneric; 50 | 51 | /** 52 | * Define a mutation that is only accessible from other Convex functions (but not from the client). 53 | * 54 | * This function will be allowed to modify your Convex database. It will not be accessible from the client. 55 | * 56 | * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. 57 | * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. 58 | */ 59 | export const internalMutation = internalMutationGeneric; 60 | 61 | /** 62 | * Define an action in this Convex app's public API. 63 | * 64 | * An action is a function which can execute any JavaScript code, including non-deterministic 65 | * code and code with side-effects, like calling third-party services. 66 | * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive. 67 | * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}. 68 | * 69 | * @param func - The action. It receives an {@link ActionCtx} as its first argument. 70 | * @returns The wrapped action. Include this as an `export` to name it and make it accessible. 71 | */ 72 | export const action = actionGeneric; 73 | 74 | /** 75 | * Define an action that is only accessible from other Convex functions (but not from the client). 76 | * 77 | * @param func - The function. It receives an {@link ActionCtx} as its first argument. 78 | * @returns The wrapped function. Include this as an `export` to name it and make it accessible. 79 | */ 80 | export const internalAction = internalActionGeneric; 81 | 82 | /** 83 | * Define an HTTP action. 84 | * 85 | * The wrapped function will be used to respond to HTTP requests received 86 | * by a Convex deployment if the requests matches the path and method where 87 | * this action is routed. Be sure to route your httpAction in `convex/http.js`. 88 | * 89 | * @param func - The function. It receives an {@link ActionCtx} as its first argument 90 | * and a Fetch API `Request` object as its second. 91 | * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. 92 | */ 93 | export const httpAction = httpActionGeneric; 94 | -------------------------------------------------------------------------------- /apps/web/src/components/notes/DeleteNote.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Fragment, useRef, useState } from "react"; 3 | import { Dialog, Transition } from "@headlessui/react"; 4 | import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; 5 | import Image from "next/image"; 6 | 7 | export default function DeleteNote({ deleteAction }: any) { 8 | const [open, setOpen] = useState(false); 9 | 10 | const cancelButtonRef = useRef(null); 11 | 12 | return ( 13 | <> 14 | setOpen(true)} 16 | src={"/images/delete.svg"} 17 | width={20} 18 | height={20} 19 | alt="search" 20 | className="cursor-pointer" 21 | /> 22 | 23 | 24 | 30 | 39 |
40 | 41 | 42 |
43 |
44 | 53 | 54 |
55 |
56 |
57 |
62 |
63 | 67 | Delete Note 68 | 69 |
70 |

71 | Are you sure you want to delete this note? All of 72 | your data will be permanently removed. This action 73 | cannot be undone. 74 |

75 |
76 |
77 |
78 |
79 |
80 | 90 | 98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | 106 | ); 107 | } 108 | -------------------------------------------------------------------------------- /apps/native/src/screens/LoginScreen.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { StyleSheet, View, Text, TouchableOpacity, Image } from "react-native"; 3 | import { RFValue } from "react-native-responsive-fontsize"; 4 | import { useOAuth } from "@clerk/clerk-expo"; 5 | import { AntDesign } from "@expo/vector-icons"; 6 | 7 | const LoginScreen = ({ navigation }) => { 8 | const { startOAuthFlow: startGoogleAuthFlow } = useOAuth({ 9 | strategy: "oauth_google", 10 | }); 11 | const { startOAuthFlow: startAppleAuthFlow } = useOAuth({ 12 | strategy: "oauth_apple", 13 | }); 14 | 15 | const onPress = async (authType: string) => { 16 | try { 17 | if (authType === "google") { 18 | const { createdSessionId, setActive } = await startGoogleAuthFlow(); 19 | if (createdSessionId) { 20 | setActive({ session: createdSessionId }); 21 | navigation.navigate("NotesDashboardScreen"); 22 | } 23 | } else if (authType === "apple") { 24 | const { createdSessionId, setActive } = await startAppleAuthFlow(); 25 | if (createdSessionId) { 26 | setActive({ session: createdSessionId }); 27 | navigation.navigate("NotesDashboardScreen"); 28 | } 29 | } 30 | } catch (err) { 31 | console.error("OAuth error", err); 32 | } 33 | }; 34 | 35 | return ( 36 | 37 | 38 | 42 | Log in to your account 43 | Welcome! Please login below. 44 | onPress("google")} 47 | > 48 | 52 | 53 | Continue with Google 54 | 55 | 56 | 57 | onPress("apple")} 60 | > 61 | 62 | 65 | Continue with Apple 66 | 67 | 68 | 69 | 70 | Don’t have an account? 71 | Sign up above. 72 | 73 | 74 | 75 | ); 76 | }; 77 | 78 | const styles = StyleSheet.create({ 79 | container: { 80 | flex: 1, 81 | backgroundColor: "#fff", 82 | }, 83 | card: { 84 | backgroundColor: "#fff", 85 | padding: 10, 86 | alignItems: "center", 87 | width: "98%", 88 | }, 89 | logo: { 90 | width: 74, 91 | height: 74, 92 | marginTop: 20, 93 | }, 94 | title: { 95 | marginTop: 49, 96 | fontSize: RFValue(21), 97 | fontFamily: "SemiBold", 98 | }, 99 | subtitle: { 100 | marginTop: 8, 101 | fontSize: RFValue(14), 102 | color: "#000", 103 | fontFamily: "Regular", 104 | marginBottom: 32, 105 | textAlign: "center", 106 | }, 107 | input: { 108 | width: "100%", 109 | borderWidth: 1, 110 | borderColor: "#D0D5DD", 111 | borderRadius: 10, 112 | padding: 14, 113 | marginBottom: 16, 114 | fontFamily: "Regular", 115 | fontSize: RFValue(14), 116 | }, 117 | buttonEmail: { 118 | backgroundColor: "#0D87E1", 119 | padding: 15, 120 | borderRadius: 10, 121 | width: "100%", 122 | marginBottom: 24, 123 | minHeight: 44, 124 | }, 125 | buttonText: { 126 | textAlign: "center", 127 | color: "#FFF", 128 | fontFamily: "SemiBold", 129 | fontSize: RFValue(14), 130 | }, 131 | buttonTextWithIcon: { 132 | marginLeft: 10, 133 | }, 134 | dividerContainer: { 135 | flexDirection: "row", 136 | alignItems: "center", 137 | width: "100%", 138 | marginBottom: 24, 139 | }, 140 | divider: { 141 | flex: 1, 142 | height: 1, 143 | backgroundColor: "#000", 144 | }, 145 | dividerText: { 146 | marginHorizontal: 10, 147 | color: "#000", 148 | fontFamily: "Medium", 149 | }, 150 | buttonGoogle: { 151 | flexDirection: "row", 152 | alignItems: "center", 153 | justifyContent: "center", 154 | backgroundColor: "#FFF", 155 | borderRadius: 10, 156 | borderWidth: 1, 157 | borderColor: "#D0D5DD", 158 | width: "100%", 159 | marginBottom: 12, 160 | height: 44, 161 | }, 162 | buttonApple: { 163 | flexDirection: "row", 164 | justifyContent: "center", 165 | alignItems: "center", 166 | backgroundColor: "#FFF", 167 | padding: 15, 168 | borderRadius: 10, 169 | borderWidth: 1, 170 | borderColor: "#D0D5DD", 171 | width: "100%", 172 | marginBottom: 32, 173 | }, 174 | signupContainer: { 175 | flexDirection: "row", 176 | }, 177 | signupText: { 178 | color: "#4D9DE0", 179 | fontFamily: "SemiBold", 180 | }, 181 | googleIcon: { 182 | width: 24, 183 | height: 24, 184 | marginRight: 12, 185 | }, 186 | errorText: { 187 | fontSize: RFValue(14), 188 | color: "tomato", 189 | fontFamily: "Medium", 190 | alignSelf: "flex-start", 191 | marginBottom: 8, 192 | marginLeft: 4, 193 | }, 194 | }); 195 | 196 | export default LoginScreen; 197 | -------------------------------------------------------------------------------- /apps/native/src/screens/InsideNoteScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { 3 | StyleSheet, 4 | Text, 5 | View, 6 | Image, 7 | Dimensions, 8 | TouchableOpacity, 9 | ScrollView, 10 | } from "react-native"; 11 | import { RFValue } from "react-native-responsive-fontsize"; 12 | 13 | const { width } = Dimensions.get("window"); 14 | 15 | export default function InsideNoteScreen({ route, navigation }) { 16 | const { item } = route.params; 17 | console.log({ item }); 18 | const [activeTab, setActiveTab] = useState("original"); // State to manage active tab 19 | 20 | return ( 21 | 22 | 23 | 27 | 28 | 29 | 30 | navigation.goBack()}> 31 | 35 | 36 | 37 | {item.title} 38 | 39 | 40 | 41 | 45 | 46 | 47 | {activeTab === "original" 48 | ? item.content 49 | : item.summary 50 | ? item.summary 51 | : "No summary available"} 52 | 53 | 54 | 55 | 56 | {/* Sticky footer */} 57 | 58 | setActiveTab("original")} 64 | > 65 | 74 | 82 | Original 83 | 84 | 85 | setActiveTab("summary")} 91 | > 92 | 99 | 107 | Summary 108 | 109 | 110 | 111 | 112 | ); 113 | } 114 | 115 | const styles = StyleSheet.create({ 116 | container: { 117 | flex: 1, 118 | backgroundColor: "#F5F7FE", 119 | }, 120 | header: { 121 | backgroundColor: "#0D87E1", 122 | height: 67, 123 | justifyContent: "center", 124 | alignItems: "center", 125 | }, 126 | logo: { 127 | width: 46, 128 | height: 46, 129 | borderRadius: 20, 130 | resizeMode: "contain", 131 | }, 132 | underHeaderContainer: { 133 | width: width, 134 | height: 62, 135 | backgroundColor: "#fff", 136 | borderBottomWidth: 2, 137 | borderBottomColor: "#D9D9D9", 138 | flexDirection: "row", 139 | alignItems: "center", 140 | justifyContent: "space-between", 141 | paddingHorizontal: 16, 142 | }, 143 | arrowBack: { 144 | width: 20, 145 | height: 20, 146 | resizeMode: "contain", 147 | }, 148 | title: { 149 | fontSize: RFValue(17.5), 150 | fontFamily: "MMedium", 151 | color: "#2D2D2D", 152 | }, 153 | contentContainer: { 154 | // Add styles for contentContainer if needed 155 | }, 156 | contentTitle: { 157 | fontSize: RFValue(17.5), 158 | fontFamily: "MMedium", 159 | color: "#000", 160 | textAlign: "center", 161 | marginTop: 28, 162 | }, 163 | contentDescription: { 164 | fontSize: RFValue(17.5), 165 | fontFamily: "MRegular", 166 | alignSelf: "center", 167 | textAlign: "justify", 168 | paddingLeft: 29, 169 | paddingRight: 21, 170 | marginTop: 30, 171 | }, 172 | footer: { 173 | flexDirection: "row", 174 | position: "absolute", 175 | bottom: 0, 176 | left: 0, 177 | right: 0, 178 | backgroundColor: "#fff", 179 | borderTopWidth: 1, 180 | borderTopColor: "#D9D9D9", 181 | }, 182 | footerTab: { 183 | flex: 1, 184 | padding: 12, 185 | justifyContent: "center", 186 | alignItems: "center", 187 | }, 188 | footerIcon: { 189 | width: 25, 190 | height: 25, 191 | resizeMode: "contain", 192 | }, 193 | activeTab: { 194 | backgroundColor: "#0D87E1", 195 | }, 196 | activeIcon: { 197 | tintColor: "#fff", 198 | }, 199 | inactiveIcon: { 200 | tintColor: "#000", 201 | }, 202 | footerText: { 203 | fontSize: RFValue(12.5), 204 | fontFamily: "MRegular", 205 | }, 206 | activeTabText: { 207 | color: "#fff", 208 | }, 209 | inactiveTabText: { 210 | color: "#000", 211 | }, 212 | }); 213 | -------------------------------------------------------------------------------- /packages/backend/convex/_generated/server.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * Generated utilities for implementing server-side Convex query and mutation functions. 4 | * 5 | * THIS CODE IS AUTOMATICALLY GENERATED. 6 | * 7 | * To regenerate, run `npx convex dev`. 8 | * @module 9 | */ 10 | 11 | import { 12 | ActionBuilder, 13 | HttpActionBuilder, 14 | MutationBuilder, 15 | QueryBuilder, 16 | GenericActionCtx, 17 | GenericMutationCtx, 18 | GenericQueryCtx, 19 | GenericDatabaseReader, 20 | GenericDatabaseWriter, 21 | } from "convex/server"; 22 | import type { DataModel } from "./dataModel.js"; 23 | 24 | /** 25 | * Define a query in this Convex app's public API. 26 | * 27 | * This function will be allowed to read your Convex database and will be accessible from the client. 28 | * 29 | * @param func - The query function. It receives a {@link QueryCtx} as its first argument. 30 | * @returns The wrapped query. Include this as an `export` to name it and make it accessible. 31 | */ 32 | export declare const query: QueryBuilder; 33 | 34 | /** 35 | * Define a query that is only accessible from other Convex functions (but not from the client). 36 | * 37 | * This function will be allowed to read from your Convex database. It will not be accessible from the client. 38 | * 39 | * @param func - The query function. It receives a {@link QueryCtx} as its first argument. 40 | * @returns The wrapped query. Include this as an `export` to name it and make it accessible. 41 | */ 42 | export declare const internalQuery: QueryBuilder; 43 | 44 | /** 45 | * Define a mutation in this Convex app's public API. 46 | * 47 | * This function will be allowed to modify your Convex database and will be accessible from the client. 48 | * 49 | * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. 50 | * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. 51 | */ 52 | export declare const mutation: MutationBuilder; 53 | 54 | /** 55 | * Define a mutation that is only accessible from other Convex functions (but not from the client). 56 | * 57 | * This function will be allowed to modify your Convex database. It will not be accessible from the client. 58 | * 59 | * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. 60 | * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. 61 | */ 62 | export declare const internalMutation: MutationBuilder; 63 | 64 | /** 65 | * Define an action in this Convex app's public API. 66 | * 67 | * An action is a function which can execute any JavaScript code, including non-deterministic 68 | * code and code with side-effects, like calling third-party services. 69 | * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive. 70 | * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}. 71 | * 72 | * @param func - The action. It receives an {@link ActionCtx} as its first argument. 73 | * @returns The wrapped action. Include this as an `export` to name it and make it accessible. 74 | */ 75 | export declare const action: ActionBuilder; 76 | 77 | /** 78 | * Define an action that is only accessible from other Convex functions (but not from the client). 79 | * 80 | * @param func - The function. It receives an {@link ActionCtx} as its first argument. 81 | * @returns The wrapped function. Include this as an `export` to name it and make it accessible. 82 | */ 83 | export declare const internalAction: ActionBuilder; 84 | 85 | /** 86 | * Define an HTTP action. 87 | * 88 | * The wrapped function will be used to respond to HTTP requests received 89 | * by a Convex deployment if the requests matches the path and method where 90 | * this action is routed. Be sure to route your httpAction in `convex/http.js`. 91 | * 92 | * @param func - The function. It receives an {@link ActionCtx} as its first argument 93 | * and a Fetch API `Request` object as its second. 94 | * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. 95 | */ 96 | export declare const httpAction: HttpActionBuilder; 97 | 98 | /** 99 | * A set of services for use within Convex query functions. 100 | * 101 | * The query context is passed as the first argument to any Convex query 102 | * function run on the server. 103 | * 104 | * This differs from the {@link MutationCtx} because all of the services are 105 | * read-only. 106 | */ 107 | export type QueryCtx = GenericQueryCtx; 108 | 109 | /** 110 | * A set of services for use within Convex mutation functions. 111 | * 112 | * The mutation context is passed as the first argument to any Convex mutation 113 | * function run on the server. 114 | */ 115 | export type MutationCtx = GenericMutationCtx; 116 | 117 | /** 118 | * A set of services for use within Convex action functions. 119 | * 120 | * The action context is passed as the first argument to any Convex action 121 | * function run on the server. 122 | */ 123 | export type ActionCtx = GenericActionCtx; 124 | 125 | /** 126 | * An interface to read from the database within Convex query functions. 127 | * 128 | * The two entry points are {@link DatabaseReader.get}, which fetches a single 129 | * document by its {@link Id}, or {@link DatabaseReader.query}, which starts 130 | * building a query. 131 | */ 132 | export type DatabaseReader = GenericDatabaseReader; 133 | 134 | /** 135 | * An interface to read from and write to the database within Convex mutation 136 | * functions. 137 | * 138 | * Convex guarantees that all writes within a single mutation are 139 | * executed atomically, so you never have to worry about partial writes leaving 140 | * your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control) 141 | * for the guarantees Convex provides your functions. 142 | */ 143 | export type DatabaseWriter = GenericDatabaseWriter; 144 | -------------------------------------------------------------------------------- /apps/web/src/components/Header.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Disclosure, DisclosureButton, DisclosurePanel } from "@headlessui/react"; 4 | import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline"; 5 | import Logo from "./common/Logo"; 6 | import Link from "next/link"; 7 | import { useUser } from "@clerk/clerk-react"; 8 | import { UserNav } from "./common/UserNav"; 9 | import { usePathname } from "next/navigation"; 10 | 11 | type NavigationItem = { 12 | name: string; 13 | href: string; 14 | current: boolean; 15 | }; 16 | 17 | const navigation: NavigationItem[] = [ 18 | { name: "Benefits", href: "#Benefits", current: true }, 19 | { name: "Reviews", href: "#reviews", current: false }, 20 | ]; 21 | 22 | export default function Header() { 23 | const { user } = useUser(); 24 | const pathname = usePathname(); 25 | 26 | return ( 27 | 28 | {({ open }) => ( 29 | <> 30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 | 38 |
39 | {pathname === "/" && ( 40 |
41 |
42 |
    43 | {navigation.map((item) => ( 44 |
  • 45 | 50 | {item.name} 51 | 52 |
  • 53 | ))} 54 |
55 |
56 |
57 | )} 58 | {user ? ( 59 |
60 | 61 | 67 | 68 | 73 |
74 | ) : ( 75 |
76 | 80 | Sign in 81 | 82 | 86 | Get Started 87 | 88 |
89 | )} 90 |
91 | {/* Mobile menu button*/} 92 | 93 | 94 | Open main menu 95 | {open ? ( 96 | 101 |
102 |
103 |
104 |
105 | 106 | 107 |
108 | {navigation.map((item) => ( 109 | 116 | {item.name} 117 | 118 | ))} 119 |
120 | 124 | Sign in 125 | 126 | 130 | Get Started 131 | 132 |
133 |
134 |
135 | 136 | )} 137 |
138 | ); 139 | } 140 | -------------------------------------------------------------------------------- /apps/native/src/screens/NotesDashboardScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { 3 | StyleSheet, 4 | View, 5 | Text, 6 | TextInput, 7 | TouchableOpacity, 8 | Image, 9 | FlatList, 10 | Dimensions, 11 | } from "react-native"; 12 | import { Feather, AntDesign } from "@expo/vector-icons"; 13 | import { RFValue } from "react-native-responsive-fontsize"; 14 | import { useAuth, useUser } from "@clerk/clerk-expo"; 15 | import { api } from "@packages/backend/convex/_generated/api"; 16 | import { useQuery } from "convex/react"; 17 | 18 | const NotesDashboardScreen = ({ navigation }) => { 19 | const user = useUser(); 20 | const imageUrl = user?.user?.imageUrl; 21 | const firstName = user?.user?.firstName; 22 | 23 | const allNotes = useQuery(api.notes.getNotes); 24 | const [search, setSearch] = useState(""); 25 | 26 | const finalNotes = search 27 | ? allNotes.filter( 28 | (note) => 29 | note.title.toLowerCase().includes(search.toLowerCase()) || 30 | note.content.toLowerCase().includes(search.toLowerCase()), 31 | ) 32 | : allNotes; 33 | 34 | const renderItem = ({ item }) => ( 35 | 37 | navigation.navigate("InsideNoteScreen", { 38 | item: item, 39 | }) 40 | } 41 | activeOpacity={0.5} 42 | style={styles.noteItem} 43 | > 44 | {item.title} 45 | 46 | ); 47 | 48 | return ( 49 | 50 | 51 | 55 | 56 | 57 | 58 | {/* @ts-ignore, for css purposes */} 59 | 60 | Your Notes 61 | {imageUrl ? ( 62 | 63 | ) : ( 64 | {firstName ? firstName : ""} 65 | )} 66 | 67 | 68 | 74 | 80 | 81 | {!finalNotes || finalNotes.length === 0 ? ( 82 | 83 | 84 | Create your first note to{"\n"}get started 85 | 86 | 87 | ) : ( 88 | item._id} 92 | style={styles.notesList} 93 | contentContainerStyle={{ 94 | marginTop: 19, 95 | borderTopWidth: 0.5, 96 | borderTopColor: "rgba(0, 0, 0, 0.59)", 97 | }} 98 | /> 99 | )} 100 | 101 | navigation.navigate("CreateNoteScreen")} 103 | style={styles.newNoteButton} 104 | > 105 | 106 | Create a New Note 107 | 108 | 109 | ); 110 | }; 111 | 112 | const styles = StyleSheet.create({ 113 | container: { 114 | flex: 1, 115 | backgroundColor: "white", 116 | }, 117 | header: { 118 | backgroundColor: "#0D87E1", 119 | height: 67, 120 | justifyContent: "center", 121 | alignItems: "center", 122 | }, 123 | logo: { 124 | width: 46, 125 | height: 46, 126 | borderRadius: 20, 127 | resizeMode: "contain", 128 | }, 129 | title: { 130 | fontSize: RFValue(17.5), 131 | fontFamily: "MMedium", 132 | alignSelf: "center", 133 | }, 134 | yourNotesContainer: { 135 | flexDirection: "row", 136 | alignItems: "center", 137 | justifyContent: "space-between", 138 | paddingHorizontal: 13, 139 | marginTop: 19, 140 | }, 141 | avatarSmall: { 142 | width: 28, 143 | height: 28, 144 | borderRadius: 10, 145 | }, 146 | searchContainer: { 147 | flexDirection: "row", 148 | alignItems: "center", 149 | borderWidth: 1, 150 | borderColor: "grey", 151 | borderRadius: 10, 152 | padding: 10, 153 | marginHorizontal: 15, 154 | marginTop: 30, 155 | }, 156 | searchIcon: { 157 | marginRight: 10, 158 | }, 159 | searchInput: { 160 | flex: 1, 161 | fontSize: RFValue(15), 162 | fontFamily: "MRegular", 163 | color: "#2D2D2D", 164 | }, 165 | notesList: { 166 | flex: 1, 167 | }, 168 | noteItem: { 169 | padding: 20, 170 | borderBottomWidth: 0.5, 171 | borderBottomColor: "rgba(0, 0, 0, 0.59)", 172 | 173 | backgroundColor: "#F9FAFB", 174 | }, 175 | noteText: { 176 | fontSize: 16, 177 | fontFamily: "MLight", 178 | color: "#2D2D2D", 179 | }, 180 | 181 | newNoteButton: { 182 | flexDirection: "row", 183 | backgroundColor: "#0D87E1", 184 | borderRadius: 7, 185 | width: Dimensions.get("window").width / 1.6, 186 | alignSelf: "center", 187 | alignItems: "center", 188 | justifyContent: "center", 189 | minHeight: 44, 190 | position: "absolute", 191 | bottom: 35, 192 | shadowColor: "#000", 193 | shadowOffset: { 194 | width: 0, 195 | height: 3, 196 | }, 197 | shadowOpacity: 0.27, 198 | shadowRadius: 4.65, 199 | 200 | elevation: 6, 201 | }, 202 | newNoteButtonText: { 203 | color: "white", 204 | fontSize: RFValue(15), 205 | fontFamily: "MMedium", 206 | marginLeft: 10, 207 | }, 208 | switchContainer: { 209 | position: "absolute", 210 | top: 20, 211 | right: 20, 212 | }, 213 | emptyStateText: { 214 | textAlign: "center", 215 | alignSelf: "center", 216 | fontSize: RFValue(15), 217 | color: "grey", 218 | fontFamily: "MLight", 219 | }, 220 | emptyState: { 221 | width: "100%", 222 | height: "35%", 223 | marginTop: 19, 224 | backgroundColor: "#F9FAFB", 225 | justifyContent: "center", 226 | alignItems: "center", 227 | borderWidth: 0.5, 228 | borderColor: "rgba(0, 0, 0, 0.59)", 229 | }, 230 | }); 231 | 232 | export default NotesDashboardScreen; 233 | -------------------------------------------------------------------------------- /apps/web/src/components/notes/CreateNote.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Fragment, useRef, useState } from "react"; 4 | import { Dialog, Transition } from "@headlessui/react"; 5 | import Image from "next/image"; 6 | import Checkbox from "./Checkbox"; 7 | import { api } from "@packages/backend/convex/_generated/api"; 8 | import { useMutation, useQuery } from "convex/react"; 9 | 10 | export default function CreateNote() { 11 | const [open, setOpen] = useState(false); 12 | const [title, setTitle] = useState(""); 13 | const [content, setContent] = useState(""); 14 | const [isChecked, setIsChecked] = useState(false); 15 | 16 | const cancelButtonRef = useRef(null); 17 | 18 | const createNote = useMutation(api.notes.createNote); 19 | const openaiKeySet = useQuery(api.openai.openaiKeySet) ?? true; 20 | 21 | const createUserNote = async () => { 22 | await createNote({ 23 | title, 24 | content, 25 | isSummary: isChecked, 26 | }); 27 | setOpen(false); 28 | }; 29 | 30 | return ( 31 | <> 32 |
33 | 49 |
50 | 51 | 52 | 58 | 67 |
68 | 69 | 70 |
71 |
72 | 81 | 82 |
83 | <> 84 |
85 | 89 | Create New Note 90 | 91 |
92 |
93 | 99 |
100 | setTitle(e.target.value)} 108 | className="border shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)] rounded-lg border-solid border-[#D0D5DD] bg-white w-full py-2.5 px-[14px] text-black text-[17px] not-italic font-light leading-[90.3%] tracking-[-0.425px] sm:text-2xl" 109 | /> 110 |
111 |
112 | 113 |
114 | 120 |
121 |