From 60347be4f7c6c637ad6481ed94d764fab9107fc3 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Fri, 19 Sep 2025 14:14:30 +1000 Subject: [PATCH 01/51] add users table --- src/convex/schema.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/convex/schema.ts b/src/convex/schema.ts index 5b2393db..286e2e09 100644 --- a/src/convex/schema.ts +++ b/src/convex/schema.ts @@ -11,5 +11,13 @@ export default defineSchema({ name: v.string(), data: v.string(), user: v.string() + }), + users: defineTable({ + email: v.string(), + firstName: v.string(), + lastName: v.string(), + avatar: v.string(), + username: v.string(), + verified: v.boolean() }) }); From 477aab49e9f1f4495596704a54cf4ca66226143b Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Fri, 19 Sep 2025 14:15:47 +1000 Subject: [PATCH 02/51] move to another file --- src/convex/_generated/api.d.ts | 2 ++ src/convex/backups.ts | 18 +----------------- src/convex/utils.ts | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 17 deletions(-) create mode 100644 src/convex/utils.ts diff --git a/src/convex/_generated/api.d.ts b/src/convex/_generated/api.d.ts index bf758f5d..ad051305 100644 --- a/src/convex/_generated/api.d.ts +++ b/src/convex/_generated/api.d.ts @@ -14,6 +14,7 @@ import type { FunctionReference, } from "convex/server"; import type * as backups from "../backups.js"; +import type * as utils from "../utils.js"; /** * A utility for referencing Convex functions in your app's API. @@ -25,6 +26,7 @@ import type * as backups from "../backups.js"; */ declare const fullApi: ApiFromModules<{ backups: typeof backups; + utils: typeof utils; }>; export declare const api: FilterApi< typeof fullApi, diff --git a/src/convex/backups.ts b/src/convex/backups.ts index 63280174..9600a2a5 100644 --- a/src/convex/backups.ts +++ b/src/convex/backups.ts @@ -1,22 +1,6 @@ import { v } from 'convex/values'; import { mutation, query } from './_generated/server'; -import * as jose from 'jose'; - -// Shared helper function to verify JWT and return payload -async function verifyJwtAndGetPayload(jwt: string) { - if (!process.env.CLERK_JWT_KEY) { - throw new Error('Missing CLERK_JWT_KEY environment variable'); - } - const publicKey = await jose.importSPKI(process.env.CLERK_JWT_KEY, 'RS256'); - if (jwt.length === 0) { - throw new Error('Missing JWT'); - } - const { payload } = await jose.jwtVerify(jwt, publicKey, {}); - if (!payload.sub) { - throw new Error('Invalid JWT'); - } - return payload; -} +import { verifyJwtAndGetPayload } from './utils'; export const get = query({ args: { diff --git a/src/convex/utils.ts b/src/convex/utils.ts new file mode 100644 index 00000000..8c4eff64 --- /dev/null +++ b/src/convex/utils.ts @@ -0,0 +1,17 @@ +import * as jose from 'jose'; + +// Shared helper function to verify JWT and return payload +export async function verifyJwtAndGetPayload(jwt: string) { + if (!process.env.CLERK_JWT_KEY) { + throw new Error('Missing CLERK_JWT_KEY environment variable'); + } + const publicKey = await jose.importSPKI(process.env.CLERK_JWT_KEY, 'RS256'); + if (jwt.length === 0) { + throw new Error('Missing JWT'); + } + const { payload } = await jose.jwtVerify(jwt, publicKey, {}); + if (!payload.sub) { + throw new Error('Invalid JWT'); + } + return payload; +} From 6a3b907b7fa34f1c019f6b980d7e305fc88d7cdb Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Fri, 19 Sep 2025 14:20:34 +1000 Subject: [PATCH 03/51] add jwt payload type --- src/convex/types.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/convex/types.ts diff --git a/src/convex/types.ts b/src/convex/types.ts new file mode 100644 index 00000000..b6a5f8e1 --- /dev/null +++ b/src/convex/types.ts @@ -0,0 +1,18 @@ +export interface JWTPayload { + email: string; + avatar: string; + lastname: string; + username: string; + verified: boolean; + firstname: string; + azp: string; + exp: number; + fva: number[]; + iat: number; + iss: string; + nbf: number; + sid: string; + sub: string; + v: string; + fea: string; +} From 85ccbf45379b688eb66d0b88cc3d85f40bd5fb65 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Fri, 19 Sep 2025 14:24:34 +1000 Subject: [PATCH 04/51] add clerkid to schema --- src/convex/schema.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/convex/schema.ts b/src/convex/schema.ts index 286e2e09..d5852449 100644 --- a/src/convex/schema.ts +++ b/src/convex/schema.ts @@ -18,6 +18,7 @@ export default defineSchema({ lastName: v.string(), avatar: v.string(), username: v.string(), - verified: v.boolean() + verified: v.boolean(), + clerkId: v.string() }) }); From 69e555badeaa6c32c0ed13633de204f9bc332869 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Fri, 19 Sep 2025 14:25:56 +1000 Subject: [PATCH 05/51] add indexing by clerk id --- src/convex/schema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/convex/schema.ts b/src/convex/schema.ts index d5852449..3d2f0867 100644 --- a/src/convex/schema.ts +++ b/src/convex/schema.ts @@ -20,5 +20,5 @@ export default defineSchema({ username: v.string(), verified: v.boolean(), clerkId: v.string() - }) + }).index('clerkid', ['clerkId']) }); From 6a73f04b6bf508c1534e71cae4272c4f39ad62c2 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Fri, 19 Sep 2025 14:35:27 +1000 Subject: [PATCH 06/51] add code to get and update user --- src/convex/_generated/api.d.ts | 2 ++ src/convex/utils.ts | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/convex/_generated/api.d.ts b/src/convex/_generated/api.d.ts index ad051305..f0d8658f 100644 --- a/src/convex/_generated/api.d.ts +++ b/src/convex/_generated/api.d.ts @@ -14,6 +14,7 @@ import type { FunctionReference, } from "convex/server"; import type * as backups from "../backups.js"; +import type * as types from "../types.js"; import type * as utils from "../utils.js"; /** @@ -26,6 +27,7 @@ import type * as utils from "../utils.js"; */ declare const fullApi: ApiFromModules<{ backups: typeof backups; + types: typeof types; utils: typeof utils; }>; export declare const api: FilterApi< diff --git a/src/convex/utils.ts b/src/convex/utils.ts index 8c4eff64..6a23c5af 100644 --- a/src/convex/utils.ts +++ b/src/convex/utils.ts @@ -1,4 +1,6 @@ import * as jose from 'jose'; +import type { MutationCtx } from './_generated/server'; +import type { JWTPayload } from './types'; // Shared helper function to verify JWT and return payload export async function verifyJwtAndGetPayload(jwt: string) { @@ -15,3 +17,34 @@ export async function verifyJwtAndGetPayload(jwt: string) { } return payload; } + +export async function getAndUpdateUser(ctx: MutationCtx, payload: JWTPayload) { + let user = await ctx.db + .query('users') + .withIndex('clerkid', (q) => q.eq('clerkId', payload.sub)) + .first(); + if (user) { + await ctx.db.patch(user._id, { + avatar: payload.avatar, + email: payload.email, + firstName: payload.firstname, + lastName: payload.firstname, + username: payload.username, + verified: payload.verified, + clerkId: payload.sub + }); + user = await ctx.db.get(user._id); + } else { + const userId = await ctx.db.insert('users', { + avatar: payload.avatar, + email: payload.email, + firstName: payload.firstname, + lastName: payload.firstname, + username: payload.username, + verified: payload.verified, + clerkId: payload.sub + }); + user = await ctx.db.get(userId); + } + return user; +} From 9a7663d908bd66f61fded0959e780e7d4df16062 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Fri, 19 Sep 2025 14:36:30 +1000 Subject: [PATCH 07/51] make user an id --- src/convex/schema.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/convex/schema.ts b/src/convex/schema.ts index 3d2f0867..9a8cf2f6 100644 --- a/src/convex/schema.ts +++ b/src/convex/schema.ts @@ -5,12 +5,12 @@ export default defineSchema({ comments: defineTable({ body: v.string(), gmaeid: v.string(), - user: v.string() + user: v.id('users') }), backup: defineTable({ name: v.string(), data: v.string(), - user: v.string() + user: v.id('users') }), users: defineTable({ email: v.string(), From 31452da823ffcea5f383c7e57e77c387c3146881 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Fri, 19 Sep 2025 14:39:19 +1000 Subject: [PATCH 08/51] change some stuff --- src/convex/utils.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/convex/utils.ts b/src/convex/utils.ts index 6a23c5af..b6565681 100644 --- a/src/convex/utils.ts +++ b/src/convex/utils.ts @@ -1,6 +1,6 @@ import * as jose from 'jose'; import type { MutationCtx } from './_generated/server'; -import type { JWTPayload } from './types'; +import type { JwtPayload } from 'jsonwebtoken'; // Shared helper function to verify JWT and return payload export async function verifyJwtAndGetPayload(jwt: string) { @@ -18,10 +18,13 @@ export async function verifyJwtAndGetPayload(jwt: string) { return payload; } -export async function getAndUpdateUser(ctx: MutationCtx, payload: JWTPayload) { +export async function getAndUpdateUser(ctx: MutationCtx, payload: JwtPayload) { + if (!payload.sub) { + throw new Error('Invalid JWT'); + } let user = await ctx.db .query('users') - .withIndex('clerkid', (q) => q.eq('clerkId', payload.sub)) + .withIndex('clerkid', (q) => q.eq('clerkId', payload.sub || '')) .first(); if (user) { await ctx.db.patch(user._id, { From af0e4747feb098a696bff079ecc926677ae6377f Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Fri, 19 Sep 2025 14:40:20 +1000 Subject: [PATCH 09/51] use userid --- src/convex/backups.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/convex/backups.ts b/src/convex/backups.ts index 9600a2a5..18bf2534 100644 --- a/src/convex/backups.ts +++ b/src/convex/backups.ts @@ -1,6 +1,6 @@ import { v } from 'convex/values'; import { mutation, query } from './_generated/server'; -import { verifyJwtAndGetPayload } from './utils'; +import { getAndUpdateUser, verifyJwtAndGetPayload } from './utils'; export const get = query({ args: { @@ -33,8 +33,12 @@ export const create = mutation({ if (!payload.sub) { throw new Error('Invalid JWT: missing subject'); } + const userInfo = await getAndUpdateUser(ctx, payload); + if (!userInfo?._id) { + throw new Error('Something went wrong'); + } await ctx.db.insert('backup', { - user: payload.sub, + user: userInfo?._id, name: args.name, data: args.data }); From 71337a587fd24892e1d323b94ea2ee07567f00aa Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Fri, 19 Sep 2025 14:42:40 +1000 Subject: [PATCH 10/51] add get user info function --- src/convex/utils.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/convex/utils.ts b/src/convex/utils.ts index b6565681..bb43c34c 100644 --- a/src/convex/utils.ts +++ b/src/convex/utils.ts @@ -1,5 +1,5 @@ import * as jose from 'jose'; -import type { MutationCtx } from './_generated/server'; +import type { MutationCtx, QueryCtx } from './_generated/server'; import type { JwtPayload } from 'jsonwebtoken'; // Shared helper function to verify JWT and return payload @@ -18,6 +18,22 @@ export async function verifyJwtAndGetPayload(jwt: string) { return payload; } +export async function getUser(ctx: QueryCtx, payload: JwtPayload) { + if (!payload.sub) { + throw new Error('Invalid JWT'); + } + let user = await ctx.db + .query('users') + .withIndex('clerkid', (q) => q.eq('clerkId', payload.sub || '')) + .first(); + + if (!user) { + throw new Error('User not found'); + } + + return user; +} + export async function getAndUpdateUser(ctx: MutationCtx, payload: JwtPayload) { if (!payload.sub) { throw new Error('Invalid JWT'); From a2996e93f963a9df7cd3301b352c41dd52e3ec20 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Fri, 19 Sep 2025 14:43:14 +1000 Subject: [PATCH 11/51] use new function and stuff --- src/convex/backups.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/convex/backups.ts b/src/convex/backups.ts index 18bf2534..f59d23ef 100644 --- a/src/convex/backups.ts +++ b/src/convex/backups.ts @@ -1,6 +1,6 @@ import { v } from 'convex/values'; import { mutation, query } from './_generated/server'; -import { getAndUpdateUser, verifyJwtAndGetPayload } from './utils'; +import { getAndUpdateUser, getUser, verifyJwtAndGetPayload } from './utils'; export const get = query({ args: { @@ -8,11 +8,12 @@ export const get = query({ }, handler: async (ctx, args) => { const payload = await verifyJwtAndGetPayload(args.jwt); + const userInfo = await getUser(ctx, payload); const backups = await ctx.db .query('backup') .order('desc') - .filter((q) => q.eq(q.field('user'), payload.sub)) - .take(100); + .filter((q) => q.eq(q.field('user'), userInfo._id)) + .collect(); return backups.map((backup) => ({ name: backup.name, data: backup.data, @@ -56,6 +57,7 @@ export const remove = mutation({ if (backup?.user !== payload.sub) { throw new Error('Unauthorized'); } + await getAndUpdateUser(ctx, payload); await ctx.db.delete(args.id); } }); From 372da0ed0db0a71e9e891b150109f7c2f04a633c Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Fri, 19 Sep 2025 14:45:41 +1000 Subject: [PATCH 12/51] add optional fields --- src/convex/schema.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/convex/schema.ts b/src/convex/schema.ts index 9a8cf2f6..4dd7f5d3 100644 --- a/src/convex/schema.ts +++ b/src/convex/schema.ts @@ -14,9 +14,9 @@ export default defineSchema({ }), users: defineTable({ email: v.string(), - firstName: v.string(), - lastName: v.string(), - avatar: v.string(), + firstName: v.optional(v.string()), + lastName: v.optional(v.string()), + avatar: v.optional(v.string()), username: v.string(), verified: v.boolean(), clerkId: v.string() From caf45126d469944172d1a1ec64e79fe880fd9f62 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 16:37:02 +1000 Subject: [PATCH 13/51] change some settings for clerk --- src/lib/components/providers.svelte | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/components/providers.svelte b/src/lib/components/providers.svelte index 6fec6c5a..69858b81 100644 --- a/src/lib/components/providers.svelte +++ b/src/lib/components/providers.svelte @@ -2,6 +2,8 @@ import { ClerkProvider, GoogleOneTap } from 'svelte-clerk/client'; import { ModeWatcher } from 'mode-watcher'; import { setupConvex } from 'convex-svelte'; + import { dark } from '@clerk/themes'; + import { mode } from 'mode-watcher'; // Props let { children } = $props(); @@ -19,7 +21,10 @@ } - + {@render children()} From 7c5e6431ae45e195d5091ee6f06520909d521a3e Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 16:50:10 +1000 Subject: [PATCH 14/51] create account route --- src/routes/account/[...slug]/+page.svelte | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/routes/account/[...slug]/+page.svelte diff --git a/src/routes/account/[...slug]/+page.svelte b/src/routes/account/[...slug]/+page.svelte new file mode 100644 index 00000000..a0588038 --- /dev/null +++ b/src/routes/account/[...slug]/+page.svelte @@ -0,0 +1,36 @@ + + +
+ +
From 7989196d8e3f69ed334ca47804e533e7d721c750 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 16:52:47 +1000 Subject: [PATCH 15/51] remove dialog --- src/lib/components/sidebar-auth.svelte | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/lib/components/sidebar-auth.svelte b/src/lib/components/sidebar-auth.svelte index 7553fb1f..3a476153 100644 --- a/src/lib/components/sidebar-auth.svelte +++ b/src/lib/components/sidebar-auth.svelte @@ -47,9 +47,9 @@ - - - + + {#snippet child({ props })} + @@ -57,13 +57,10 @@ - {ctx.user?.username} - - - - - - + {ctx.user?.username} + {/snippet} + { From a455402be7e215f5cc8410493e4ed8ace030721f Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 16:54:14 +1000 Subject: [PATCH 16/51] add active state for profile button --- src/lib/components/sidebar-auth.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/sidebar-auth.svelte b/src/lib/components/sidebar-auth.svelte index 3a476153..bfa71177 100644 --- a/src/lib/components/sidebar-auth.svelte +++ b/src/lib/components/sidebar-auth.svelte @@ -47,7 +47,7 @@ - + {#snippet child({ props })} From a1b827f81036ebabb7a0cd2f8465f7e15af11e22 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:06:19 +1000 Subject: [PATCH 17/51] add sync settings store --- src/lib/stores.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/stores.ts b/src/lib/stores.ts index 9112d074..cdc8ebb4 100644 --- a/src/lib/stores.ts +++ b/src/lib/stores.ts @@ -21,3 +21,7 @@ export const preferencesStore = persisted('preferences', { export const favoritesStore = persisted('favorites', [] as string[]); export const historyStore = persisted('history', [] as string[]); + +export const syncSettingsStore = persisted('syncSettings', { + enabled: false +}); From 78a0f32ff5a86afa35b04f0d85024f397f9626b5 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:09:49 +1000 Subject: [PATCH 18/51] add sync item --- src/lib/components/sidebar-auth.svelte | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/lib/components/sidebar-auth.svelte b/src/lib/components/sidebar-auth.svelte index bfa71177..7d4d36b2 100644 --- a/src/lib/components/sidebar-auth.svelte +++ b/src/lib/components/sidebar-auth.svelte @@ -5,6 +5,8 @@ import * as Avatar from '$lib/components/ui/avatar/index.js'; const sidebar = useSidebar(); import * as Dialog from '$lib/components/ui/dialog/index.js'; + import * as Popover from '$lib/components/ui/popover/index.js'; + import { Checkbox } from '$lib/components/ui/checkbox/index.js'; // Lucide icons import Login from '@lucide/svelte/icons/log-in'; @@ -35,6 +37,9 @@ const ctx = useClerkContext(); import posthog from 'posthog-js'; + import { RefreshCw } from '@lucide/svelte'; + import Switch from './ui/switch/switch.svelte'; + import { syncSettingsStore } from '$lib/stores'; {#if page.url.hostname == 'edutools.ingo.au' || page.url.hostname == 'localhost'} @@ -46,6 +51,31 @@ + + + + {#snippet child({ props })} + + + Sync +
+
+ {$syncSettingsStore.enabled} +
+
+ {/snippet} +
+ +
+
Sync
+
+ +
+
+
+
{#snippet child({ props })} From 9534846a3183ded73ad18acf903755f7b27eae06 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:11:03 +1000 Subject: [PATCH 19/51] grey out icon when disabled --- src/lib/components/sidebar-auth.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/components/sidebar-auth.svelte b/src/lib/components/sidebar-auth.svelte index 7d4d36b2..ac0883b1 100644 --- a/src/lib/components/sidebar-auth.svelte +++ b/src/lib/components/sidebar-auth.svelte @@ -40,6 +40,7 @@ import { RefreshCw } from '@lucide/svelte'; import Switch from './ui/switch/switch.svelte'; import { syncSettingsStore } from '$lib/stores'; + import clsx from 'clsx'; {#if page.url.hostname == 'edutools.ingo.au' || page.url.hostname == 'localhost'} @@ -56,7 +57,7 @@ {#snippet child({ props })} - + Sync
Date: Sat, 20 Sep 2025 17:12:57 +1000 Subject: [PATCH 20/51] add syncing state --- src/lib/state.svelte.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/state.svelte.ts b/src/lib/state.svelte.ts index dbbf2e09..4bec3075 100644 --- a/src/lib/state.svelte.ts +++ b/src/lib/state.svelte.ts @@ -1 +1,2 @@ export let settingsOpen = $state({ current: false }); +export let syncState: { current: '' | 'uploading' | 'downloading' } = $state({ current: '' }); From 1302f57fee493bd869304b3313d8c70cef813640 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:15:33 +1000 Subject: [PATCH 21/51] add sync state to sidebar --- src/lib/components/sidebar-auth.svelte | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib/components/sidebar-auth.svelte b/src/lib/components/sidebar-auth.svelte index ac0883b1..a2c1ae74 100644 --- a/src/lib/components/sidebar-auth.svelte +++ b/src/lib/components/sidebar-auth.svelte @@ -41,6 +41,7 @@ import Switch from './ui/switch/switch.svelte'; import { syncSettingsStore } from '$lib/stores'; import clsx from 'clsx'; + import { syncState } from '$lib/state.svelte'; {#if page.url.hostname == 'edutools.ingo.au' || page.url.hostname == 'localhost'} @@ -63,7 +64,13 @@
- {$syncSettingsStore.enabled} + {$syncSettingsStore.enabled + ? syncState.current == 'uploading' + ? 'Uploading' + : syncState.current == 'downloading' + ? 'Downloading' + : 'Synced' + : 'Disabled'}
{/snippet} From dd67dc4e551dbcbda8060603bcd8148893636010 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:17:17 +1000 Subject: [PATCH 22/51] add animation when syncing --- src/lib/components/sidebar-auth.svelte | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/components/sidebar-auth.svelte b/src/lib/components/sidebar-auth.svelte index a2c1ae74..e123d398 100644 --- a/src/lib/components/sidebar-auth.svelte +++ b/src/lib/components/sidebar-auth.svelte @@ -58,7 +58,15 @@ {#snippet child({ props })} - + Sync
Date: Sat, 20 Sep 2025 17:19:36 +1000 Subject: [PATCH 23/51] add checkboxes --- src/lib/components/sidebar-auth.svelte | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/lib/components/sidebar-auth.svelte b/src/lib/components/sidebar-auth.svelte index e123d398..caa72f55 100644 --- a/src/lib/components/sidebar-auth.svelte +++ b/src/lib/components/sidebar-auth.svelte @@ -42,6 +42,7 @@ import { syncSettingsStore } from '$lib/stores'; import clsx from 'clsx'; import { syncState } from '$lib/state.svelte'; + import Label from './ui/label/label.svelte'; {#if page.url.hostname == 'edutools.ingo.au' || page.url.hostname == 'localhost'} @@ -84,10 +85,24 @@ {/snippet} -
-
Sync
-
- +
+
+
Sync
+
+ +
+
+ + +
+
+ + +
+
+ + +
From 9a20581654fa1ed1abe28d94187dd28de1111ab4 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:28:31 +1000 Subject: [PATCH 24/51] add more sync settings --- src/lib/components/sidebar-auth.svelte | 41 ++++++++++++++++++-------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/lib/components/sidebar-auth.svelte b/src/lib/components/sidebar-auth.svelte index caa72f55..132ace85 100644 --- a/src/lib/components/sidebar-auth.svelte +++ b/src/lib/components/sidebar-auth.svelte @@ -7,6 +7,7 @@ import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Popover from '$lib/components/ui/popover/index.js'; import { Checkbox } from '$lib/components/ui/checkbox/index.js'; + import * as Accordion from '$lib/components/ui/accordion/index.js'; // Lucide icons import Login from '@lucide/svelte/icons/log-in'; @@ -84,24 +85,40 @@ {/snippet} - -
+ +
Sync
-
- - -
-
- - +
+
+ + +
+
+ + +
+
+ + +
-
- - +
+ + + Advanced + +
+ + + +
+
+
+
From 2ab1a0cffd019c66c549fc24560bcde5f8296827 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:29:17 +1000 Subject: [PATCH 25/51] add more sync settings --- src/lib/stores.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/stores.ts b/src/lib/stores.ts index cdc8ebb4..4a595926 100644 --- a/src/lib/stores.ts +++ b/src/lib/stores.ts @@ -23,5 +23,8 @@ export const favoritesStore = persisted('favorites', [] as string[]); export const historyStore = persisted('history', [] as string[]); export const syncSettingsStore = persisted('syncSettings', { - enabled: false + enabled: false, + settings: true, + favorites: true, + history: true }); From a36b95b1583321f809137bfce1efe1139e5e20ae Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:30:40 +1000 Subject: [PATCH 26/51] bind settings to local storage --- src/lib/components/sidebar-auth.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/components/sidebar-auth.svelte b/src/lib/components/sidebar-auth.svelte index 132ace85..3cf30b3c 100644 --- a/src/lib/components/sidebar-auth.svelte +++ b/src/lib/components/sidebar-auth.svelte @@ -94,15 +94,15 @@
- +
- +
- +
From be88fa94a90e28135c204c409c6f6a16cedf1d76 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:31:17 +1000 Subject: [PATCH 27/51] transition no matter the state --- src/lib/components/sidebar-auth.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/components/sidebar-auth.svelte b/src/lib/components/sidebar-auth.svelte index 3cf30b3c..3fdba059 100644 --- a/src/lib/components/sidebar-auth.svelte +++ b/src/lib/components/sidebar-auth.svelte @@ -66,7 +66,8 @@ ? syncState.current !== '' ? 'animate-spin' : '' - : 'text-gray-500 transition-all duration-300' + : 'text-gray-500 duration-300', + 'transition-all' )} /> Sync From abf8599699406a7b39f3bf56acccc2ad8d019108 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:32:43 +1000 Subject: [PATCH 28/51] disabled settings when sync is disabled --- src/lib/components/sidebar-auth.svelte | 34 ++++++++++++++++++++------ 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/lib/components/sidebar-auth.svelte b/src/lib/components/sidebar-auth.svelte index 3fdba059..533fba22 100644 --- a/src/lib/components/sidebar-auth.svelte +++ b/src/lib/components/sidebar-auth.svelte @@ -95,27 +95,47 @@
- +
- +
- +
- Advanced + Advanced
- - - + + +
From 52f81b4735994346fbf7f9d1c4a5b9a1d50a881b Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:39:00 +1000 Subject: [PATCH 29/51] fix non existent user handling --- src/convex/backups.ts | 3 +++ src/convex/utils.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/convex/backups.ts b/src/convex/backups.ts index f59d23ef..2bce7c77 100644 --- a/src/convex/backups.ts +++ b/src/convex/backups.ts @@ -9,6 +9,9 @@ export const get = query({ handler: async (ctx, args) => { const payload = await verifyJwtAndGetPayload(args.jwt); const userInfo = await getUser(ctx, payload); + if (!userInfo) { + return []; + } const backups = await ctx.db .query('backup') .order('desc') diff --git a/src/convex/utils.ts b/src/convex/utils.ts index bb43c34c..3badbccf 100644 --- a/src/convex/utils.ts +++ b/src/convex/utils.ts @@ -28,7 +28,7 @@ export async function getUser(ctx: QueryCtx, payload: JwtPayload) { .first(); if (!user) { - throw new Error('User not found'); + return null; } return user; From 74477750d08d7429f2b9ed47d6c69fa4537997c5 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:41:15 +1000 Subject: [PATCH 30/51] fix verification --- src/convex/backups.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/convex/backups.ts b/src/convex/backups.ts index 2bce7c77..de1c060e 100644 --- a/src/convex/backups.ts +++ b/src/convex/backups.ts @@ -57,7 +57,9 @@ export const remove = mutation({ handler: async (ctx, args) => { const payload = await verifyJwtAndGetPayload(args.jwt); const backup = await ctx.db.get(args.id); - if (backup?.user !== payload.sub) { + const userInfo = await getAndUpdateUser(ctx, payload); + + if (backup?.user !== userInfo?._id) { throw new Error('Unauthorized'); } await getAndUpdateUser(ctx, payload); From 231edf8cd6ef180b03737f74136f377edd2bd8d8 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:42:37 +1000 Subject: [PATCH 31/51] remove unused analytics setting --- src/lib/stores.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/stores.ts b/src/lib/stores.ts index 4a595926..67091cd5 100644 --- a/src/lib/stores.ts +++ b/src/lib/stores.ts @@ -15,7 +15,6 @@ export const preferencesStore = persisted('preferences', { name: 'Home', icon: 'https://ssl.gstatic.com/classroom/favicon.png' }, - analytics: true, history: true }); From 54adb8c7530a8d217078a1dd3ee64c87547c07ee Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:43:53 +1000 Subject: [PATCH 32/51] add schema for settings --- src/convex/schema.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/convex/schema.ts b/src/convex/schema.ts index 4dd7f5d3..d1a26db9 100644 --- a/src/convex/schema.ts +++ b/src/convex/schema.ts @@ -19,6 +19,25 @@ export default defineSchema({ avatar: v.optional(v.string()), username: v.string(), verified: v.boolean(), - clerkId: v.string() + clerkId: v.string(), + settings: v.optional( + v.object({ + experimentalFeatures: v.boolean(), + open: v.string(), + theme: v.string(), + panic: v.object({ + enabled: v.boolean(), + key: v.string(), + url: v.string(), + disableExperimentalMode: v.boolean() + }), + cloak: v.object({ + mode: v.string(), + name: v.string(), + icon: v.string() + }), + history: v.boolean() + }) + ) }).index('clerkid', ['clerkId']) }); From 449944a3a69e78fb2a5640450715e40ceaa0b04a Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:44:48 +1000 Subject: [PATCH 33/51] add favourites and history --- src/convex/schema.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/convex/schema.ts b/src/convex/schema.ts index d1a26db9..d4aaefe8 100644 --- a/src/convex/schema.ts +++ b/src/convex/schema.ts @@ -38,6 +38,8 @@ export default defineSchema({ }), history: v.boolean() }) - ) + ), + favourites: v.array(v.string()), + history: v.array(v.string()) }).index('clerkid', ['clerkId']) }); From 969fb2206b53b75e9752973a569908e78455d885 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:48:10 +1000 Subject: [PATCH 34/51] make sync things optional --- src/convex/schema.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/convex/schema.ts b/src/convex/schema.ts index d4aaefe8..b689eac1 100644 --- a/src/convex/schema.ts +++ b/src/convex/schema.ts @@ -39,7 +39,7 @@ export default defineSchema({ history: v.boolean() }) ), - favourites: v.array(v.string()), - history: v.array(v.string()) + favourites: v.optional(v.array(v.string())), + history: v.optional(v.array(v.string())) }).index('clerkid', ['clerkId']) }); From 289447b324b28fe19a7cfff41055e0dbc0704be6 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:48:41 +1000 Subject: [PATCH 35/51] add sync get query --- src/convex/_generated/api.d.ts | 2 ++ src/convex/sync.ts | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 src/convex/sync.ts diff --git a/src/convex/_generated/api.d.ts b/src/convex/_generated/api.d.ts index f0d8658f..19187b5f 100644 --- a/src/convex/_generated/api.d.ts +++ b/src/convex/_generated/api.d.ts @@ -14,6 +14,7 @@ import type { FunctionReference, } from "convex/server"; import type * as backups from "../backups.js"; +import type * as sync from "../sync.js"; import type * as types from "../types.js"; import type * as utils from "../utils.js"; @@ -27,6 +28,7 @@ import type * as utils from "../utils.js"; */ declare const fullApi: ApiFromModules<{ backups: typeof backups; + sync: typeof sync; types: typeof types; utils: typeof utils; }>; diff --git a/src/convex/sync.ts b/src/convex/sync.ts new file mode 100644 index 00000000..086f403d --- /dev/null +++ b/src/convex/sync.ts @@ -0,0 +1,21 @@ +import { v } from 'convex/values'; +import { query } from './_generated/server'; +import { getUser, verifyJwtAndGetPayload } from './utils'; + +export const get = query({ + args: { + jwt: v.string() + }, + handler: async (ctx, args) => { + const payload = await verifyJwtAndGetPayload(args.jwt); + const userInfo = await getUser(ctx, payload); + if (!userInfo) { + return null; + } + return { + settings: userInfo.settings, + favourites: userInfo.favourites, + history: userInfo.history + }; + } +}); From ec2a7bcf6be4beca64fe367db70505313ba664e4 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:53:16 +1000 Subject: [PATCH 36/51] add mutation for sync update --- src/convex/sync.ts | 57 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/src/convex/sync.ts b/src/convex/sync.ts index 086f403d..52aa9cba 100644 --- a/src/convex/sync.ts +++ b/src/convex/sync.ts @@ -1,6 +1,6 @@ import { v } from 'convex/values'; -import { query } from './_generated/server'; -import { getUser, verifyJwtAndGetPayload } from './utils'; +import { mutation, query } from './_generated/server'; +import { getAndUpdateUser, getUser, verifyJwtAndGetPayload } from './utils'; export const get = query({ args: { @@ -19,3 +19,56 @@ export const get = query({ }; } }); + +export const update = mutation({ + args: { + jwt: v.string(), + settings: v.optional( + v.object({ + experimentalFeatures: v.boolean(), + open: v.string(), + theme: v.string(), + panic: v.object({ + enabled: v.boolean(), + key: v.string(), + url: v.string(), + disableExperimentalMode: v.boolean() + }), + cloak: v.object({ + mode: v.string(), + name: v.string(), + icon: v.string() + }), + history: v.boolean() + }) + ), + favourites: v.optional(v.array(v.string())), + history: v.optional(v.array(v.string())) + }, + handler: async (ctx, args) => { + const payload = await verifyJwtAndGetPayload(args.jwt); + if (!payload.sub) { + throw new Error('Invalid JWT: missing subject'); + } + const userInfo = await getAndUpdateUser(ctx, payload); + if (!userInfo?._id) { + throw new Error('Something went wrong'); + } + + if (args.favourites) { + await ctx.db.patch(userInfo._id, { + favourites: args.favourites + }); + } + if (args.history) { + await ctx.db.patch(userInfo._id, { + history: args.history + }); + } + if (args.settings) { + await ctx.db.patch(userInfo._id, { + settings: args.settings + }); + } + } +}); From b01637fdc6b4e2304db939aaa65c98f785424574 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sat, 20 Sep 2025 17:56:40 +1000 Subject: [PATCH 37/51] change branch name thing --- src/lib/components/app-sidebar.svelte | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/components/app-sidebar.svelte b/src/lib/components/app-sidebar.svelte index e0d8b34e..c0fc34ed 100644 --- a/src/lib/components/app-sidebar.svelte +++ b/src/lib/components/app-sidebar.svelte @@ -253,12 +253,14 @@ {...props} > - EducationalTools/src +
EducationalTools/src
- - - {process.env.BRANCH_NAME} + + {process.env.BRANCH_NAME} +
{/snippet} From 726860375daa2dc85f66f7e9739775cd2fc49a59 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sun, 21 Sep 2025 13:57:27 +1000 Subject: [PATCH 38/51] add functions for sync --- src/lib/sync.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/lib/sync.ts diff --git a/src/lib/sync.ts b/src/lib/sync.ts new file mode 100644 index 00000000..19b10162 --- /dev/null +++ b/src/lib/sync.ts @@ -0,0 +1,37 @@ +import { api } from '../convex/_generated/api'; +import { syncState } from './state.svelte'; +import { syncSettingsStore, preferencesStore } from './stores'; +import { useConvexClient } from 'convex-svelte'; + +const client = useConvexClient(); + +export function saveSettings( + jwt: string, + settings: { + experimentalFeatures: boolean; + open: string; + theme: string; + panic: { + enabled: boolean; + key: string; + url: string; + disableExperimentalMode: boolean; + }; + cloak: { + mode: string; + name: string; + icon: string; + }; + history: boolean; + } +) { + client.mutation(api.sync.update, { settings, jwt }); +} + +export function saveHistory(jwt: string, history: string[]) { + client.mutation(api.sync.update, { history, jwt }); +} + +export function saveFavourites(jwt: string, favourites: string[]) { + client.mutation(api.sync.update, { favourites, jwt }); +} From 25b7685563a1a222e11109d7795d2d7146b64aad Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sun, 21 Sep 2025 13:59:46 +1000 Subject: [PATCH 39/51] make functions async and change syncstate --- src/lib/sync.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/lib/sync.ts b/src/lib/sync.ts index 19b10162..2419a429 100644 --- a/src/lib/sync.ts +++ b/src/lib/sync.ts @@ -5,7 +5,7 @@ import { useConvexClient } from 'convex-svelte'; const client = useConvexClient(); -export function saveSettings( +export async function saveSettings( jwt: string, settings: { experimentalFeatures: boolean; @@ -25,13 +25,19 @@ export function saveSettings( history: boolean; } ) { - client.mutation(api.sync.update, { settings, jwt }); + syncState.current = 'uploading'; + await client.mutation(api.sync.update, { settings, jwt }); + syncState.current = ''; } -export function saveHistory(jwt: string, history: string[]) { - client.mutation(api.sync.update, { history, jwt }); +export async function saveHistory(jwt: string, history: string[]) { + syncState.current = 'uploading'; + await client.mutation(api.sync.update, { history, jwt }); + syncState.current = ''; } -export function saveFavourites(jwt: string, favourites: string[]) { - client.mutation(api.sync.update, { favourites, jwt }); +export async function saveFavourites(jwt: string, favourites: string[]) { + syncState.current = 'uploading'; + await client.mutation(api.sync.update, { favourites, jwt }); + syncState.current = ''; } From f0b393c16780ba2f923ecd88448b819845b68407 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sun, 21 Sep 2025 14:01:59 +1000 Subject: [PATCH 40/51] change some stuff --- src/lib/sync.ts | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/src/lib/sync.ts b/src/lib/sync.ts index 2419a429..c70d9e66 100644 --- a/src/lib/sync.ts +++ b/src/lib/sync.ts @@ -1,43 +1,25 @@ +import { get } from 'svelte/store'; import { api } from '../convex/_generated/api'; import { syncState } from './state.svelte'; -import { syncSettingsStore, preferencesStore } from './stores'; +import { syncSettingsStore, preferencesStore, favoritesStore, historyStore } from './stores'; import { useConvexClient } from 'convex-svelte'; const client = useConvexClient(); -export async function saveSettings( - jwt: string, - settings: { - experimentalFeatures: boolean; - open: string; - theme: string; - panic: { - enabled: boolean; - key: string; - url: string; - disableExperimentalMode: boolean; - }; - cloak: { - mode: string; - name: string; - icon: string; - }; - history: boolean; - } -) { +export async function saveSettings(jwt: string) { syncState.current = 'uploading'; - await client.mutation(api.sync.update, { settings, jwt }); + await client.mutation(api.sync.update, { settings: get(preferencesStore), jwt }); syncState.current = ''; } -export async function saveHistory(jwt: string, history: string[]) { +export async function saveHistory(jwt: string) { syncState.current = 'uploading'; - await client.mutation(api.sync.update, { history, jwt }); + await client.mutation(api.sync.update, { history: get(historyStore), jwt }); syncState.current = ''; } -export async function saveFavourites(jwt: string, favourites: string[]) { +export async function saveFavourites(jwt: string) { syncState.current = 'uploading'; - await client.mutation(api.sync.update, { favourites, jwt }); + await client.mutation(api.sync.update, { favourites: get(favoritesStore), jwt }); syncState.current = ''; } From ec742fa9530fc5c67bfad0b74e7fcbdff664c86f Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sun, 21 Sep 2025 14:08:04 +1000 Subject: [PATCH 41/51] move everything to one sync function --- src/lib/sync.ts | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/lib/sync.ts b/src/lib/sync.ts index c70d9e66..03b301c0 100644 --- a/src/lib/sync.ts +++ b/src/lib/sync.ts @@ -6,20 +6,21 @@ import { useConvexClient } from 'convex-svelte'; const client = useConvexClient(); -export async function saveSettings(jwt: string) { +export async function save( + jwt: string, + { + settings = false, + history = false, + favourites = false + }: { settings?: boolean; history?: boolean; favourites?: boolean } +) { syncState.current = 'uploading'; - await client.mutation(api.sync.update, { settings: get(preferencesStore), jwt }); - syncState.current = ''; -} + let mutationData: { settings?: any; history?: any; favourites?: any } = {}; -export async function saveHistory(jwt: string) { - syncState.current = 'uploading'; - await client.mutation(api.sync.update, { history: get(historyStore), jwt }); - syncState.current = ''; -} + if (settings) mutationData.settings = get(preferencesStore); + if (history) mutationData.history = get(historyStore); + if (favourites) mutationData.favourites = get(favoritesStore); -export async function saveFavourites(jwt: string) { - syncState.current = 'uploading'; - await client.mutation(api.sync.update, { favourites: get(favoritesStore), jwt }); + await client.mutation(api.sync.update, { ...mutationData, jwt }); syncState.current = ''; } From 21ddbf62e9acd8731a622e807ca8d3602e180671 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sun, 21 Sep 2025 14:11:26 +1000 Subject: [PATCH 42/51] fix issue with svelte --- src/lib/sync.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/sync.ts b/src/lib/sync.ts index 03b301c0..2b850efd 100644 --- a/src/lib/sync.ts +++ b/src/lib/sync.ts @@ -4,8 +4,6 @@ import { syncState } from './state.svelte'; import { syncSettingsStore, preferencesStore, favoritesStore, historyStore } from './stores'; import { useConvexClient } from 'convex-svelte'; -const client = useConvexClient(); - export async function save( jwt: string, { @@ -14,6 +12,8 @@ export async function save( favourites = false }: { settings?: boolean; history?: boolean; favourites?: boolean } ) { + const client = useConvexClient(); + syncState.current = 'uploading'; let mutationData: { settings?: any; history?: any; favourites?: any } = {}; From 135a72e66a767be482124e1104e6a9d61e2e4b78 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sun, 21 Sep 2025 14:14:27 +1000 Subject: [PATCH 43/51] fix issue with users that still have analytics key in local stoarge --- src/lib/sync.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/lib/sync.ts b/src/lib/sync.ts index 2b850efd..dd9d796f 100644 --- a/src/lib/sync.ts +++ b/src/lib/sync.ts @@ -17,7 +17,26 @@ export async function save( syncState.current = 'uploading'; let mutationData: { settings?: any; history?: any; favourites?: any } = {}; - if (settings) mutationData.settings = get(preferencesStore); + if (settings) { + let preferences = get(preferencesStore); + mutationData.settings = { + experimentalFeatures: preferences.experimentalFeatures, + open: preferences.open, + theme: preferences.theme, + panic: { + enabled: preferences.panic.enabled, + key: preferences.panic.key, + url: preferences.panic.url, + disableExperimentalMode: preferences.panic.disableExperimentalMode + }, + cloak: { + mode: preferences.cloak.mode, + name: preferences.cloak.name, + icon: preferences.cloak.icon + }, + history: preferences.history + }; + } if (history) mutationData.history = get(historyStore); if (favourites) mutationData.favourites = get(favoritesStore); From 82355887742bde686d92248e381d5a90f4404476 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sun, 21 Sep 2025 14:15:16 +1000 Subject: [PATCH 44/51] add slightly better error handling --- src/lib/sync.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/sync.ts b/src/lib/sync.ts index dd9d796f..63df68a0 100644 --- a/src/lib/sync.ts +++ b/src/lib/sync.ts @@ -40,6 +40,10 @@ export async function save( if (history) mutationData.history = get(historyStore); if (favourites) mutationData.favourites = get(favoritesStore); - await client.mutation(api.sync.update, { ...mutationData, jwt }); + try { + await client.mutation(api.sync.update, { ...mutationData, jwt }); + } catch { + syncState.current = ''; + } syncState.current = ''; } From c5c6a882546d349d85fc5baf0e82f8d12cec6303 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sun, 21 Sep 2025 14:18:46 +1000 Subject: [PATCH 45/51] add code to sync settings --- src/lib/components/settings.svelte | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/lib/components/settings.svelte b/src/lib/components/settings.svelte index eaf2ff8e..66c01b3b 100644 --- a/src/lib/components/settings.svelte +++ b/src/lib/components/settings.svelte @@ -23,9 +23,14 @@ import Button from './ui/button/button.svelte'; import { toast } from 'svelte-sonner'; import { onMount } from 'svelte'; + import { save } from '$lib/sync'; + import { useClerkContext } from 'svelte-clerk'; + const ctx = useClerkContext(); let distinct_id = $state('Not available') as string; + let sessionToken = $state(''); + onMount(() => { setTimeout(() => { distinct_id = posthog.get_distinct_id(); @@ -34,8 +39,30 @@ $effect(() => { posthog.capture('settingschange', $preferencesStore); + if (sessionToken && sessionToken !== '') { + save(sessionToken, { settings: true }); + } }); + $effect(() => { + if (ctx.session) { + getToken().then((token) => { + sessionToken = token; + }); + } + }); + + async function getToken() { + const token = await ctx.session?.getToken(); + if (!token) { + if (ctx.session) { + toast.error('Something went wrong'); + } + return ''; + } + return token; + } + const themeTriggerContent = $derived( themes.find((theme) => theme.value === $preferencesStore.theme)?.label ?? 'No theme :D' ); From 450215da14e18df75075e1810053df7ce713d2f9 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sun, 21 Sep 2025 14:52:59 +1000 Subject: [PATCH 46/51] add handler for settings change --- src/lib/components/settings.svelte | 52 ++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/src/lib/components/settings.svelte b/src/lib/components/settings.svelte index 66c01b3b..ec36b2d5 100644 --- a/src/lib/components/settings.svelte +++ b/src/lib/components/settings.svelte @@ -37,6 +37,10 @@ }, 1000); }); + function handleSettingsChange() { + console.log('Settings changed'); + } + $effect(() => { posthog.capture('settingschange', $preferencesStore); if (sessionToken && sessionToken !== '') { @@ -75,7 +79,7 @@ Settings Open in - +
@@ -97,7 +101,10 @@
- + {#each themes as theme}
@@ -120,22 +127,37 @@
Panic key (requires refresh to apply)
- +
- - + +
Cloak - + Off When not focused @@ -143,12 +165,24 @@
- - + +
Privacy
- +
From bd342425486586327338fa6df96831a09ec7b9dc Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sun, 21 Sep 2025 14:54:33 +1000 Subject: [PATCH 47/51] move analytics and sync to settings change function --- src/lib/components/settings.svelte | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/lib/components/settings.svelte b/src/lib/components/settings.svelte index ec36b2d5..e1ceb8a0 100644 --- a/src/lib/components/settings.svelte +++ b/src/lib/components/settings.svelte @@ -38,15 +38,11 @@ }); function handleSettingsChange() { - console.log('Settings changed'); - } - - $effect(() => { posthog.capture('settingschange', $preferencesStore); if (sessionToken && sessionToken !== '') { save(sessionToken, { settings: true }); } - }); + } $effect(() => { if (ctx.session) { From 74f308e67792c4d5ba139c141c7f9a3d59cf4257 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Sun, 21 Sep 2025 14:58:51 +1000 Subject: [PATCH 48/51] pass convex client down --- src/lib/components/settings.svelte | 4 +++- src/lib/sync.ts | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/components/settings.svelte b/src/lib/components/settings.svelte index e1ceb8a0..2c4f60d0 100644 --- a/src/lib/components/settings.svelte +++ b/src/lib/components/settings.svelte @@ -25,7 +25,9 @@ import { onMount } from 'svelte'; import { save } from '$lib/sync'; import { useClerkContext } from 'svelte-clerk'; + import { useConvexClient } from 'convex-svelte'; const ctx = useClerkContext(); + const client = useConvexClient(); let distinct_id = $state('Not available') as string; @@ -40,7 +42,7 @@ function handleSettingsChange() { posthog.capture('settingschange', $preferencesStore); if (sessionToken && sessionToken !== '') { - save(sessionToken, { settings: true }); + save(sessionToken, client, { settings: true }); } } diff --git a/src/lib/sync.ts b/src/lib/sync.ts index 63df68a0..7f9945d4 100644 --- a/src/lib/sync.ts +++ b/src/lib/sync.ts @@ -6,14 +6,13 @@ import { useConvexClient } from 'convex-svelte'; export async function save( jwt: string, + client: ReturnType, { settings = false, history = false, favourites = false }: { settings?: boolean; history?: boolean; favourites?: boolean } ) { - const client = useConvexClient(); - syncState.current = 'uploading'; let mutationData: { settings?: any; history?: any; favourites?: any } = {}; From 8a84cdea72d7927b92c6401b9da803eb84213321 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Tue, 23 Sep 2025 10:55:40 +1000 Subject: [PATCH 49/51] disable sync for now --- src/lib/components/settings.svelte | 6 +++--- src/lib/components/sidebar-auth.svelte | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/components/settings.svelte b/src/lib/components/settings.svelte index 2c4f60d0..8e16e15a 100644 --- a/src/lib/components/settings.svelte +++ b/src/lib/components/settings.svelte @@ -41,9 +41,9 @@ function handleSettingsChange() { posthog.capture('settingschange', $preferencesStore); - if (sessionToken && sessionToken !== '') { - save(sessionToken, client, { settings: true }); - } + // if (sessionToken && sessionToken !== '') { + // save(sessionToken, client, { settings: true }); + // } } $effect(() => { diff --git a/src/lib/components/sidebar-auth.svelte b/src/lib/components/sidebar-auth.svelte index 533fba22..3fd7f397 100644 --- a/src/lib/components/sidebar-auth.svelte +++ b/src/lib/components/sidebar-auth.svelte @@ -55,7 +55,7 @@ - + {#snippet child({ props })} From da14aa98069adeef8cf877126217e5715978ef41 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Tue, 23 Sep 2025 12:47:39 +1000 Subject: [PATCH 50/51] fix issue --- src/convex/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/convex/utils.ts b/src/convex/utils.ts index 3badbccf..77170ae2 100644 --- a/src/convex/utils.ts +++ b/src/convex/utils.ts @@ -47,7 +47,7 @@ export async function getAndUpdateUser(ctx: MutationCtx, payload: JwtPayload) { avatar: payload.avatar, email: payload.email, firstName: payload.firstname, - lastName: payload.firstname, + lastName: payload.lastname, username: payload.username, verified: payload.verified, clerkId: payload.sub From 829fd21072448f7f891847a28a61d694f7104c31 Mon Sep 17 00:00:00 2001 From: Ingo Wolf Date: Tue, 23 Sep 2025 12:50:17 +1000 Subject: [PATCH 51/51] use the same workflow for build --- .github/workflows/build.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1d334744..0add09a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,16 +6,15 @@ on: jobs: build: runs-on: ubuntu-latest - container: - image: 'archlinux:latest' permissions: write-all steps: - name: Checkout code uses: actions/checkout@v2 - - name: Install packages - run: pacman -Sy pnpm nodejs npm chromium icu --noconfirm + - uses: pnpm/action-setup@v4 + with: + version: 9 - name: Install dependencies run: |