Fix team custom colors reset when updating team profile

Closes #2956
This commit is contained in:
Kalle
2026-04-09 21:09:15 +03:00
parent 8cd32d5761
commit 19f80e15a8
4 changed files with 89 additions and 54 deletions

View File

@@ -311,10 +311,7 @@ export async function update({
bio,
bsky,
tag,
customTheme,
}: Pick<Insertable<Tables["Team"]>, "id" | "name" | "bio" | "bsky" | "tag"> & {
customTheme: CustomTheme | null;
}) {
}: Pick<Insertable<Tables["Team"]>, "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()

View File

@@ -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<typeof editTeamSchema>({
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");
});
});

View File

@@ -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));

View File

@@ -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(),
),
}),
]);