diff --git a/app/features/team/TeamRepository.server.ts b/app/features/team/TeamRepository.server.ts index f7af186b5..0e97cb7a2 100644 --- a/app/features/team/TeamRepository.server.ts +++ b/app/features/team/TeamRepository.server.ts @@ -311,10 +311,7 @@ export async function update({ bio, bsky, tag, - customTheme, -}: Pick, "id" | "name" | "bio" | "bsky" | "tag"> & { - customTheme: CustomTheme | null; -}) { +}: Pick, "id" | "name" | "bio" | "bsky" | "tag">) { const customUrl = mySlugify(name); const team = await db @@ -325,7 +322,6 @@ export async function update({ bio, bsky, tag, - customTheme: customTheme ? JSON.stringify(customTheme) : null, }) .where("id", "=", id) .returningAll() diff --git a/app/features/team/actions/t.$customUrl.edit.server.test.ts b/app/features/team/actions/t.$customUrl.edit.server.test.ts index 6e1888b2f..3cb64917f 100644 --- a/app/features/team/actions/t.$customUrl.edit.server.test.ts +++ b/app/features/team/actions/t.$customUrl.edit.server.test.ts @@ -1,4 +1,8 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { REGULAR_USER_TEST_ID } from "~/db/seed/constants"; +import { db } from "~/db/sql"; +import * as TeamRepository from "~/features/team/TeamRepository.server"; +import { clampThemeToGamut } from "~/utils/oklch-gamut"; import { assertResponseErrored, dbInsertUsers, @@ -20,7 +24,7 @@ const editTeamProfileAction = wrappedAction({ isJsonSubmission: true, }); -const DEFAULT_FIELDS = { +const DEFAULT_EDIT_FIELDS = { _action: "EDIT", name: "Team 1", bio: "", @@ -28,6 +32,31 @@ const DEFAULT_FIELDS = { tag: "", } as const; +const VALID_CUSTOM_THEME = { + baseHue: 180, + baseChroma: 0.05, + accentHue: 200, + accentChroma: 0.1, + chatHue: null, + radiusBox: 3, + radiusField: 2, + radiusSelector: 2, + borderWidth: 2, + sizeField: 1, + sizeSelector: 1, + sizeSpacing: 1, +} as const; + +const expectedStoredTheme = () => + JSON.parse(JSON.stringify(clampThemeToGamut(VALID_CUSTOM_THEME))); + +const makeUserPatron = () => + db + .updateTable("User") + .set({ patronTier: 2 }) + .where("id", "=", REGULAR_USER_TEST_ID) + .execute(); + describe("team page editing", () => { beforeEach(async () => { await dbInsertUsers(); @@ -37,65 +66,85 @@ describe("team page editing", () => { dbReset(); }); - it("adds valid custom theme", async () => { + it("sets a custom theme via UPDATE_CUSTOM_THEME", async () => { + await makeUserPatron(); + const response = await editTeamProfileAction( { - customTheme: { - baseHue: 180, - baseChroma: 0.05, - accentHue: 200, - accentChroma: 0.1, - chatHue: null, - radiusBox: 3, - radiusField: 2, - radiusSelector: 2, - borderWidth: 2, - sizeField: 1, - sizeSelector: 1, - sizeSpacing: 1, - }, - ...DEFAULT_FIELDS, + _action: "UPDATE_CUSTOM_THEME", + newValue: VALID_CUSTOM_THEME, }, { user: "regular", params: { customUrl: "team-1" } }, ); - expect(response.status).toBe(302); + expect(response).toEqual({ ok: true }); + + const team = await TeamRepository.findByCustomUrl("team-1"); + expect(team?.customTheme).toEqual(expectedStoredTheme()); }); - it("allows null custom theme", async () => { - const response = await editTeamProfileAction( + it("clears a custom theme via UPDATE_CUSTOM_THEME with null", async () => { + await makeUserPatron(); + + await editTeamProfileAction( { - customTheme: null, - ...DEFAULT_FIELDS, + _action: "UPDATE_CUSTOM_THEME", + newValue: VALID_CUSTOM_THEME, }, { user: "regular", params: { customUrl: "team-1" } }, ); - expect(response.status).toBe(302); - }); - - it("prevents adding custom theme with invalid values", async () => { const response = await editTeamProfileAction( { - customTheme: { + _action: "UPDATE_CUSTOM_THEME", + newValue: null, + }, + { user: "regular", params: { customUrl: "team-1" } }, + ); + + expect(response).toEqual({ ok: true }); + + const team = await TeamRepository.findByCustomUrl("team-1"); + expect(team?.customTheme).toBeNull(); + }); + + it("prevents setting an invalid custom theme", async () => { + await makeUserPatron(); + + const response = await editTeamProfileAction( + { + _action: "UPDATE_CUSTOM_THEME", + newValue: { + ...VALID_CUSTOM_THEME, baseHue: 500, // Invalid: max is 360 - baseChroma: 0.05, - accentHue: 200, - accentChroma: 0.1, - chatHue: null, - radiusBox: 3, - radiusField: 2, - radiusSelector: 2, - borderWidth: 2, - sizeField: 1, - sizeSelector: 1, - sizeSpacing: 1, }, - ...DEFAULT_FIELDS, }, { user: "regular", params: { customUrl: "team-1" } }, ); assertResponseErrored(response); }); + + it("preserves an existing custom theme when editing the team profile", async () => { + await makeUserPatron(); + + await editTeamProfileAction( + { + _action: "UPDATE_CUSTOM_THEME", + newValue: VALID_CUSTOM_THEME, + }, + { user: "regular", params: { customUrl: "team-1" } }, + ); + + const response = await editTeamProfileAction( + { ...DEFAULT_EDIT_FIELDS, bio: "Updated bio" }, + { user: "regular", params: { customUrl: "team-1" } }, + ); + + expect(response.status).toBe(302); + + const team = await TeamRepository.findByCustomUrl("team-1"); + expect(team?.customTheme).toEqual(expectedStoredTheme()); + expect(team?.bio).toBe("Updated bio"); + }); }); diff --git a/app/features/team/actions/t.$customUrl.edit.server.ts b/app/features/team/actions/t.$customUrl.edit.server.ts index 44cb20e87..f05856b49 100644 --- a/app/features/team/actions/t.$customUrl.edit.server.ts +++ b/app/features/team/actions/t.$customUrl.edit.server.ts @@ -87,15 +87,9 @@ export const action: ActionFunction = async ({ request, params }) => { return { errors: ["forms:errors.duplicateName"] }; } - const customTheme = - canAddCustomizedColors(team) && data.customTheme - ? clampThemeToGamut(data.customTheme) - : null; - const updatedTeam = await TeamRepository.update({ id: team.id, ...data, - customTheme, }); throw redirect(teamPage(updatedTeam.customUrl)); diff --git a/app/features/team/team-schemas.server.ts b/app/features/team/team-schemas.server.ts index f6ec1db1f..1aa2f26ac 100644 --- a/app/features/team/team-schemas.server.ts +++ b/app/features/team/team-schemas.server.ts @@ -61,10 +61,6 @@ export const editTeamSchema = z.union([ falsyToNull, z.string().max(TEAM.TAG_MAX_LENGTH).nullable(), ), - customTheme: z.preprocess( - (val) => (!val || val === "null" ? null : val), - themeInputSchema.nullable(), - ), }), ]);