diff --git a/package.json b/package.json index b216fd64..a48ff465 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@codemirror/lang-javascript": "^6.2.3", "@codemirror/lang-json": "^6.0.1", "@convex-dev/auth": "^0.0.88", - "@convex-dev/better-auth": "0.9", + "@convex-dev/better-auth": "0.9.7-alpha.0", "@convex-dev/react-query": "0.0.0-alpha.11", "@daytonaio/sdk": "^0.25.6", "@erquhart/convex-oss-stats": "^0.8.1", @@ -49,12 +49,12 @@ "@radix-ui/react-select": "^2.2.2", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.2.3", - "@radix-ui/react-tooltip": "^1.1.5", "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-toast": "^1.2.2", "@radix-ui/react-toggle": "^1.1.10", "@radix-ui/react-toggle-group": "^1.1.11", + "@radix-ui/react-tooltip": "^1.1.5", "@remix-run/node": "^2.8.1", "@sentry/react": "^8.35.0", "@sentry/vite-plugin": "^2.22.6", @@ -89,8 +89,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", - "convex": "^1.25.4", - "convex-oss-stats": "link:../../../erquhart/convex-oss-stats", + "convex": "^1.28.0", "cors": "^2.8.5", "d3": "^7.9.0", "date-fns": "^2.30.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5f04cdf0..cfa8d2e8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,19 +40,19 @@ importers: version: 6.0.2 '@convex-dev/auth': specifier: ^0.0.88 - version: 0.0.88(@auth/core@0.37.0)(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0) + version: 0.0.88(@auth/core@0.37.0)(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0) '@convex-dev/better-auth': - specifier: '0.9' - version: 0.9.6(@standard-schema/spec@1.0.0)(better-auth@1.3.27(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(solid-js@1.9.9))(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.2) + specifier: 0.9.7-alpha.0 + version: 0.9.7-alpha.0(@standard-schema/spec@1.0.0)(better-auth@1.3.27(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(solid-js@1.9.9))(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.2) '@convex-dev/react-query': specifier: 0.0.0-alpha.11 - version: 0.0.0-alpha.11(@standard-schema/spec@1.0.0)(@tanstack/react-query@5.90.2(react@19.0.0))(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17) + version: 0.0.0-alpha.11(@standard-schema/spec@1.0.0)(@tanstack/react-query@5.90.2(react@19.0.0))(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17) '@daytonaio/sdk': specifier: ^0.25.6 version: 0.25.6 '@erquhart/convex-oss-stats': specifier: ^0.8.1 - version: 0.8.1(@standard-schema/spec@1.0.0)(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17) + version: 0.8.1(@standard-schema/spec@1.0.0)(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17) '@floating-ui/react': specifier: ^0.27.8 version: 0.27.8(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -225,11 +225,8 @@ importers: specifier: ^1.1.1 version: 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) convex: - specifier: ^1.25.4 - version: 1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) - convex-oss-stats: - specifier: link:../../../erquhart/convex-oss-stats - version: link:../../../erquhart/convex-oss-stats + specifier: ^1.28.0 + version: 1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) cors: specifier: ^2.8.5 version: 2.8.5 @@ -1615,11 +1612,11 @@ packages: react: optional: true - '@convex-dev/better-auth@0.9.6': - resolution: {integrity: sha512-wqwGnvjJmy5WZeRK3nO+o0P95brdIfBbCFzIlJeRoXOP4CgYPaDBZNFZY+W5Zx6Zvnai8WZ2wjTr+jvd9bzJ2A==} + '@convex-dev/better-auth@0.9.7-alpha.0': + resolution: {integrity: sha512-EtEHscNUK4NeEAsn+oAYLoI9xxq7emfhaPlbhmKQlHHQ+jm7EMA25vcAm+Z3k4PicxQzUkn72Kcw29zB5RYiRA==} peerDependencies: better-auth: 1.3.27 - convex: ^1.26.2 + convex: ^1.28.0 react: ^18.3.1 || ^19.0.0 react-dom: ^18.3.1 || ^19.0.0 @@ -5742,8 +5739,8 @@ packages: zod: optional: true - convex@1.27.0: - resolution: {integrity: sha512-IHkqZX3GtY4nKFPTAR4mvWHHhDiQX9PM7EjpEv0pJWoMoq0On6oOL3iZ7Xz4Ls96dF7WJd4AjfitJsg2hUnLSQ==} + convex@1.28.0: + resolution: {integrity: sha512-40FgeJ/LxP9TxnkDDztU/A5gcGTdq1klcTT5mM0Ak+kSlQiDktMpjNX1TfkWLxXaE3lI4qvawKH95v2RiYgFxA==} engines: {node: '>=18.0.0', npm: '>=7.0.0'} hasBin: true peerDependencies: @@ -11969,10 +11966,10 @@ snapshots: '@content-collections/integrations': 0.2.1(@content-collections/core@0.8.2(typescript@5.9.2)) vite: 7.1.7(@types/node@24.3.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1) - '@convex-dev/auth@0.0.83(@auth/core@0.37.0)(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)': + '@convex-dev/auth@0.0.83(@auth/core@0.37.0)(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)': dependencies: '@auth/core': 0.37.0 - convex: 1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex: 1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) cookie: 1.0.2 is-network-error: 1.1.0 jose: 5.10.0 @@ -11985,12 +11982,12 @@ snapshots: optionalDependencies: react: 19.0.0 - '@convex-dev/auth@0.0.88(@auth/core@0.37.0)(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)': + '@convex-dev/auth@0.0.88(@auth/core@0.37.0)(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)': dependencies: '@auth/core': 0.37.0 '@oslojs/crypto': 1.0.1 '@oslojs/encoding': 1.1.0 - convex: 1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex: 1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) cookie: 1.0.2 is-network-error: 1.1.0 jose: 5.10.0 @@ -12002,12 +11999,13 @@ snapshots: optionalDependencies: react: 19.0.0 - '@convex-dev/better-auth@0.9.6(@standard-schema/spec@1.0.0)(better-auth@1.3.27(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(solid-js@1.9.9))(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.2)': + '@convex-dev/better-auth@0.9.7-alpha.0(@standard-schema/spec@1.0.0)(better-auth@1.3.27(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(solid-js@1.9.9))(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.2)': dependencies: better-auth: 1.3.27(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(solid-js@1.9.9) common-tags: 1.8.2 - convex: 1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) - convex-helpers: 0.1.104(@standard-schema/spec@1.0.0)(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@3.25.76) + convex: 1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex-helpers: 0.1.104(@standard-schema/spec@1.0.0)(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@3.25.76) + jose: 6.1.0 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) remeda: 2.32.0 @@ -12019,18 +12017,18 @@ snapshots: - hono - typescript - '@convex-dev/crons@0.1.9(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))': + '@convex-dev/crons@0.1.9(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))': dependencies: - convex: 1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex: 1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) cron-parser: 4.9.0 - '@convex-dev/react-query@0.0.0-alpha.11(@standard-schema/spec@1.0.0)(@tanstack/react-query@5.90.2(react@19.0.0))(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17)': + '@convex-dev/react-query@0.0.0-alpha.11(@standard-schema/spec@1.0.0)(@tanstack/react-query@5.90.2(react@19.0.0))(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17)': dependencies: '@auth/core': 0.37.0 - '@convex-dev/auth': 0.0.83(@auth/core@0.37.0)(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0) + '@convex-dev/auth': 0.0.83(@auth/core@0.37.0)(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0) '@tanstack/react-query': 5.90.2(react@19.0.0) - convex: 1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) - convex-helpers: 0.1.104(@standard-schema/spec@1.0.0)(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17) + convex: 1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex-helpers: 0.1.104(@standard-schema/spec@1.0.0)(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17) transitivePeerDependencies: - '@simplewebauthn/browser' - '@simplewebauthn/server' @@ -12096,14 +12094,14 @@ snapshots: '@whatwg-node/promise-helpers': 1.3.2 tslib: 2.8.1 - '@erquhart/convex-oss-stats@0.8.1(@standard-schema/spec@1.0.0)(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17)': + '@erquhart/convex-oss-stats@0.8.1(@standard-schema/spec@1.0.0)(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17)': dependencies: - '@convex-dev/crons': 0.1.9(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)) + '@convex-dev/crons': 0.1.9(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)) '@octokit/graphql': 8.2.2 '@octokit/graphql-schema': 15.26.0 cheerio: 1.1.2 - convex: 1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) - convex-helpers: 0.1.99(@standard-schema/spec@1.0.0)(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17) + convex: 1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex-helpers: 0.1.99(@standard-schema/spec@1.0.0)(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17) date-fns: 4.1.0 framer-motion: 11.18.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) nano: 10.1.4 @@ -12671,7 +12669,7 @@ snapshots: https-proxy-agent: 7.0.6 node-fetch: 2.7.0 nopt: 8.1.0 - semver: 7.7.2 + semver: 7.7.3 tar: 7.4.3 transitivePeerDependencies: - encoding @@ -15689,7 +15687,7 @@ snapshots: fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.7.2 + semver: 7.7.3 ts-api-utils: 2.1.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: @@ -16638,37 +16636,36 @@ snapshots: convert-source-map@2.0.0: {} - convex-helpers@0.1.104(@standard-schema/spec@1.0.0)(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@3.25.76): + convex-helpers@0.1.104(@standard-schema/spec@1.0.0)(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@3.25.76): dependencies: - convex: 1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex: 1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) optionalDependencies: '@standard-schema/spec': 1.0.0 react: 19.0.0 typescript: 5.9.2 zod: 3.25.76 - convex-helpers@0.1.104(@standard-schema/spec@1.0.0)(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17): + convex-helpers@0.1.104(@standard-schema/spec@1.0.0)(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17): dependencies: - convex: 1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex: 1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) optionalDependencies: '@standard-schema/spec': 1.0.0 react: 19.0.0 typescript: 5.9.2 zod: 4.0.17 - convex-helpers@0.1.99(@standard-schema/spec@1.0.0)(convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17): + convex-helpers@0.1.99(@standard-schema/spec@1.0.0)(convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)(zod@4.0.17): dependencies: - convex: 1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + convex: 1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) optionalDependencies: '@standard-schema/spec': 1.0.0 react: 19.0.0 typescript: 5.9.2 zod: 4.0.17 - convex@1.27.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0): + convex@1.28.0(@clerk/clerk-react@5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0): dependencies: esbuild: 0.25.4 - jwt-decode: 4.0.0 prettier: 3.6.2 optionalDependencies: '@clerk/clerk-react': 5.48.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -19295,7 +19292,7 @@ snapshots: normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 - semver: 7.7.2 + semver: 7.7.3 validate-npm-package-license: 3.0.4 normalize-path@2.1.1: @@ -20251,7 +20248,7 @@ snapshots: dependencies: '@img/colour': 1.0.0 detect-libc: 2.1.2 - semver: 7.7.2 + semver: 7.7.3 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.4 '@img/sharp-darwin-x64': 0.34.4 diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index a0a99571..7efe46f6 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -16,8 +16,7 @@ import { } from 'react-icons/fa' import { ThemeToggle } from './ThemeToggle' import { SearchButton } from './SearchButton' -import { Authenticated, Unauthenticated, useQuery } from 'convex/react' -import { AuthLoading } from 'convex/react' +import { Authenticated, useQuery } from 'convex/react' import { api } from 'convex/_generated/api' import { MdLibraryBooks, MdLineAxis, MdPerson, MdSupport } from 'react-icons/md' import { CgClose, CgMenuLeft, CgMusicSpeaker } from 'react-icons/cg' @@ -25,9 +24,13 @@ import { BiSolidCheckShield } from 'react-icons/bi' import { PiHammerFill, PiSparkleFill } from 'react-icons/pi' import { libraries } from '~/libraries' import { sortBy } from '~/utils/utils' +import { convexQuery } from '@convex-dev/react-query' +import { useSuspenseQuery } from '@tanstack/react-query' export function Navbar({ children }: { children: React.ReactNode }) { - const user = useQuery(api.auth.getCurrentUser) + const { data: user } = useSuspenseQuery( + convexQuery(api.auth.getCurrentUser, {}) + ) const matches = useMatches() const Title = @@ -65,48 +68,40 @@ export function Navbar({ children }: { children: React.ReactNode }) { const loginButton = ( <> - {(() => { - const loginEl = ( - - -
Log In
- - ) - - return ( - <> - {loginEl} - {loginEl} - - ) - })()} - - -
- - - My Account - -
- {canAdmin ? ( + > + +
Log In
+ + )} + {user && ( + <>
- + - Admin + My Account
- ) : null} -
+ {canAdmin && ( +
+ + + Admin + +
+ )} + + )} ) diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index f7c2f647..822842d3 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -26,6 +26,11 @@ import { ToastProvider } from '~/components/ToastProvider' import { ThemeProvider } from '~/components/ThemeProvider' import { ConvexQueryClient } from '@convex-dev/react-query' import { ConvexReactClient } from 'convex/react' +import { + getCookieNames, + fetchAuth, + getAuthFromCookie, +} from '@convex-dev/better-auth/react-start' import { Navbar } from '~/components/Navbar' import { ConvexBetterAuthProvider } from '@convex-dev/better-auth/react' @@ -33,6 +38,20 @@ import { authClient } from '../utils/auth.client' import { LibrariesLayout } from './_libraries/route' import { TanStackUser } from 'convex/auth' +import { getCookie, getRequest } from '@tanstack/react-start/server' +import { createServerFn } from '@tanstack/react-start' + +const getAuth = createServerFn({ method: 'GET' }).handler(async () => { + const { createAuth } = await import('../../convex/auth') + const cookieNames = getCookieNames(createAuth) + const cookieAuth = getAuthFromCookie(getCookie(cookieNames.convexJwt)) + if (cookieAuth) { + return cookieAuth + } + if (getCookie(cookieNames.sessionToken)) { + return await fetchAuth(getRequest()) + } +}) export const Route = createRootRouteWithContext<{ queryClient: QueryClient @@ -137,11 +156,20 @@ export const Route = createRootRouteWithContext<{ }) } - // // During SSR only (the only time serverHttpClient exists), - // // set the auth token for Convex to make HTTP queries with. - // if (token) { - // ctx.context.convexQueryClient.serverHttpClient?.setAuth(token) - // } + const authData = await getAuth() + + // all queries, mutations and actions through TanStack Query will be + // authenticated during SSR if we have a valid token + if (authData?.token) { + // During SSR only (the only time serverHttpClient exists), + // set the auth token to make HTTP queries with. + ctx.context.convexQueryClient.serverHttpClient?.setAuth(authData.token) + } + + return { + userId: authData?.userId, + token: authData?.token, + } }, staleTime: Infinity, errorComponent: (props) => { diff --git a/src/routes/_libraries/account.tsx b/src/routes/_libraries/account.tsx index 439c4bd7..a488e03d 100644 --- a/src/routes/_libraries/account.tsx +++ b/src/routes/_libraries/account.tsx @@ -7,22 +7,30 @@ import { FaToggleOn, FaToggleOff, } from 'react-icons/fa' -import { - Authenticated, - Unauthenticated, - useMutation, - useQuery, -} from 'convex/react' -import { Link, redirect, createFileRoute } from '@tanstack/react-router' +import { useMutation } from 'convex/react' +import { createFileRoute, useNavigate } from '@tanstack/react-router' import { authClient } from '~/utils/auth.client' -import { useCurrentUserQuery } from '~/hooks/useCurrentUser' import { api } from 'convex/_generated/api' import { Id } from 'convex/_generated/dataModel' import * as React from 'react' import { useToast } from '~/components/ToastProvider' +import { convexQuery } from '@convex-dev/react-query' +import { useSuspenseQuery } from '@tanstack/react-query' +import { requireAuth } from '~/utils/utils' export const Route = createFileRoute('/_libraries/account')({ component: AccountPage, + beforeLoad: async (ctx) => { + requireAuth(ctx) + }, + loader: async (ctx) => { + await ctx.context.queryClient.ensureQueryData( + convexQuery(api.auth.getCurrentUser, {}) + ) + await ctx.context.queryClient.ensureQueryData( + convexQuery(api.llmKeys.listMyLLMKeysForDisplay, {}) + ) + }, }) interface LLMKeyForm { @@ -44,7 +52,9 @@ function LLMKeysSection() { }) // Queries - const llmKeys = useQuery(api.llmKeys.listMyLLMKeysForDisplay) + const { data: llmKeys } = useSuspenseQuery( + convexQuery(api.llmKeys.listMyLLMKeysForDisplay, {}) + ) // Mutations const createLLMKey = useMutation(api.llmKeys.createMyLLMKey) @@ -91,7 +101,7 @@ function LLMKeysSection() { } const handleDeleteKey = async (keyId: Id<'llm_keys'>) => { - if (!confirm('Are you sure you want to delete this LLM key?')) return + if (!window.confirm('Are you sure you want to delete this LLM key?')) return try { await deleteLLMKey({ keyId }) @@ -416,8 +426,9 @@ function LLMKeysSection() { } function UserSettings() { - const userQuery = useCurrentUserQuery() + const userQuery = useSuspenseQuery(convexQuery(api.auth.getCurrentUser, {})) const { notify } = useToast() + const navigate = useNavigate() // Use current user query directly instead of separate ad preference query const updateAdPreferenceMutation = useMutation( api.users.updateAdPreference @@ -453,7 +464,6 @@ function UserSettings() { const signOut = async () => { await authClient.signOut() - redirect({ to: '/login' }) notify(
Signed out
@@ -462,6 +472,7 @@ function UserSettings() {
) + navigate({ to: '/login' }) } return ( @@ -532,24 +543,7 @@ function UserSettings() { function AccountPage() { return (
- - - - -
-

- Sign In Required -

-

- Please sign in to access your account settings. -

- - - -
-
+
) } diff --git a/src/routes/_libraries/login.tsx b/src/routes/_libraries/login.tsx index ddb2e054..a3b527b4 100644 --- a/src/routes/_libraries/login.tsx +++ b/src/routes/_libraries/login.tsx @@ -6,17 +6,22 @@ import { BrandContextMenu } from '~/components/BrandContextMenu' import { redirect, createFileRoute } from '@tanstack/react-router' import { api } from 'convex/_generated/api' import { convexQuery } from '@convex-dev/react-query' +import { z } from 'zod' export const Route = createFileRoute('/_libraries/login')({ component: LoginPage, + validateSearch: z.object({ + redirectTo: z.string().optional(), + }), + beforeLoad: async ({ context }) => { + if (context.userId) { + throw redirect({ to: '/account' }) + } + }, loader: async ({ context }) => { - const user = await context.queryClient.ensureQueryData( + await context.queryClient.ensureQueryData( convexQuery(api.auth.getCurrentUser, {}) ) - - if (user) { - throw redirect({ to: '/account' }) - } }, }) @@ -41,6 +46,7 @@ function SplashImage() { } export function SignInForm() { + const { redirectTo = '/account' } = Route.useSearch() return (
@@ -51,6 +57,7 @@ export function SignInForm() { onClick={() => authClient.signIn.social({ provider: 'github', + callbackURL: redirectTo, }) } className="w-full bg-black/80 hover:bg-black text-white dark:text-black dark:bg-white/95 dark:hover:bg-white font-semibold py-2 px-4 rounded-md transition-colors" @@ -61,6 +68,7 @@ export function SignInForm() { onClick={() => authClient.signIn.social({ provider: 'google', + callbackURL: redirectTo, }) } className="w-full bg-[#DB4437]/95 hover:bg-[#DB4437] text-white font-semibold py-2 px-4 rounded-md transition-colors mt-4" diff --git a/src/routes/builder.tsx b/src/routes/builder.tsx index 049ee538..ceae2fa4 100644 --- a/src/routes/builder.tsx +++ b/src/routes/builder.tsx @@ -4,6 +4,7 @@ import { seo } from '~/utils/seo' import BuilderRoot from '~/cta/components/cta-ui' import { useCurrentUserQuery } from '~/hooks/useCurrentUser' import { Authenticated } from 'convex/react' +import { requireAuth } from '~/utils/utils' export const Route = createFileRoute('/builder')({ ssr: true, @@ -20,16 +21,19 @@ export const Route = createFileRoute('/builder')({ description: 'Build amazing applications with TanStack', }), }), + beforeLoad: async (ctx) => { + requireAuth(ctx) + }, + loader: async (opts) => { + const user = await opts.context.ensureUser() + return { user } + }, headers(ctx) { return { 'Cross-Origin-Embedder-Policy': 'require-corp', 'Cross-Origin-Opener-Policy': 'same-origin', } }, - loader: async (opts) => { - const user = await opts.context.ensureUser() - return { user } - }, }) function BuilderComponent() { diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 525866fe..2fc4bb7b 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,3 +1,5 @@ +import { redirect, RouteContext } from '@tanstack/react-router' + /** * Uppercases the first character of a string and returns the result. */ @@ -175,3 +177,14 @@ export async function logTime( console.log(`${lable}: ${(end - start).toLocaleString()} ms`) return result as any } + +export function requireAuth(ctx: { + context: { userId?: string } + location: { href: string } +}) { + if (!ctx.context.userId) { + throw redirect({ + to: '/login?redirectTo=' + encodeURIComponent(ctx.location.href), + }) + } +}