From 09652176f97f11bc06f4c9b57a448e14744eac12 Mon Sep 17 00:00:00 2001 From: Mohamed Bassem Date: Sat, 24 May 2025 12:59:43 +0000 Subject: feat: Allow defaulting to reader mode when clicking on bookmarks. Fixes #662 --- packages/trpc/routers/users.test.ts | 35 +++++++++++++++++++++++++++++++ packages/trpc/routers/users.ts | 41 +++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) (limited to 'packages/trpc/routers') diff --git a/packages/trpc/routers/users.test.ts b/packages/trpc/routers/users.test.ts index ea342d33..3d2d164d 100644 --- a/packages/trpc/routers/users.test.ts +++ b/packages/trpc/routers/users.test.ts @@ -94,4 +94,39 @@ describe("User Routes", () => { // A normal user can't list all users await expect(() => user2Caller.users.list()).rejects.toThrow(/FORBIDDEN/); }); + + test("get/update user settings", async ({ + db, + unauthedAPICaller, + }) => { + const user = await unauthedAPICaller.users.create({ + name: "Test User", + email: "testupdate@test.com", + password: "pass1234", + confirmPassword: "pass1234", + }); + const caller = getApiCaller(db, user.id); + + const settings = await caller.users.settings(); + // The default settings + expect(settings).toEqual({ + bookmarkClickAction: "open_original_link", + }); + + // Update settings + await caller.users.updateSettings({ + bookmarkClickAction: "expand_bookmark_preview", + }); + + // Verify updated settings + const updatedSettings = await caller.users.settings(); + expect(updatedSettings).toEqual({ + bookmarkClickAction: "expand_bookmark_preview", + }); + + // Test invalid update (e.g., empty input, if schema enforces it) + await expect(() => caller.users.updateSettings({})).rejects.toThrow( + /No settings provided/, + ); + }); }); diff --git a/packages/trpc/routers/users.ts b/packages/trpc/routers/users.ts index c56daaee..6f1f1145 100644 --- a/packages/trpc/routers/users.ts +++ b/packages/trpc/routers/users.ts @@ -10,11 +10,14 @@ import { bookmarkTags, highlights, users, + userSettings, } from "@karakeep/db/schema"; import { deleteUserAssets } from "@karakeep/shared/assetdb"; import serverConfig from "@karakeep/shared/config"; import { zSignUpSchema, + zUpdateUserSettingsSchema, + zUserSettingsSchema, zUserStatsResponseSchema, zWhoAmIResponseSchema, } from "@karakeep/shared/types/users"; @@ -59,6 +62,12 @@ export async function createUser( email: users.email, role: users.role, }); + + // Insert user settings for the new user + await trx.insert(userSettings).values({ + userId: result[0].id, + }); + return result[0]; } catch (e) { if (e instanceof SqliteError) { @@ -242,4 +251,36 @@ export const usersAppRouter = router({ numHighlights, }; }), + settings: authedProcedure + .output(zUserSettingsSchema) + .query(async ({ ctx }) => { + const settings = await ctx.db.query.userSettings.findFirst({ + where: eq(userSettings.userId, ctx.user.id), + }); + if (!settings) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "User settings not found", + }); + } + return { + bookmarkClickAction: settings.bookmarkClickAction, + }; + }), + updateSettings: authedProcedure + .input(zUpdateUserSettingsSchema) + .mutation(async ({ input, ctx }) => { + if (Object.keys(input).length === 0) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "No settings provided", + }); + } + await ctx.db + .update(userSettings) + .set({ + bookmarkClickAction: input.bookmarkClickAction, + }) + .where(eq(userSettings.userId, ctx.user.id)); + }), }); -- cgit v1.2.3-70-g09d2