refactor: simplify mobile board layouts
This commit is contained in:
1 parent
7bbca3bd40
commit
02fe99d5aa
30 files changed
+75
-1430
No files matched your search
@@ -5,10 +5,10 @@ import { and, eq, inArray } from "@homarr/db";
|
||||
import {
|
||||
getServerSettingByKeyAsync,
|
||||
getServerSettingsAsync,
|
||||
mergeServerSettingByKeyAsync,
|
||||
insertServerSettingByKeyAsync,
|
||||
updateServerSettingByKeyAsync,
|
||||
} from "@homarr/db/queries";
|
||||
import { boards } from "@homarr/db/schema";
|
||||
import { boards, serverSettings } from "@homarr/db/schema";
|
||||
import type { ServerSettings } from "@homarr/server-settings";
|
||||
import { defaultServerSettingsKeys } from "@homarr/server-settings";
|
||||
import { settingsInitSchema } from "@homarr/validation/settings";
|
||||
@@ -19,7 +19,6 @@ import { nextOnboardingStepAsync } from "./onboard/onboard-queries";
|
||||
const boardServerSettingsSchema = z.object({
|
||||
homeBoardId: z.string().nullable(),
|
||||
mobileHomeBoardId: z.string().nullable(),
|
||||
enableAutomaticMobileLayout: z.boolean(),
|
||||
enableStatusByDefault: z.boolean(),
|
||||
forceDisableStatus: z.boolean(),
|
||||
}) satisfies z.ZodType<ServerSettings["board"]>;
|
||||
@@ -40,7 +39,7 @@ export const serverSettingsRouter = createTRPCRouter({
|
||||
mcp: {
|
||||
enabled: true,
|
||||
description:
|
||||
"Get global board defaults, including desktop/mobile home board IDs, automatic mobile layout mode, and status behavior. Requires admin permission",
|
||||
"Get global board defaults, including desktop/mobile home board IDs and status behavior. Requires admin permission",
|
||||
},
|
||||
})
|
||||
.input(z.void())
|
||||
@@ -55,7 +54,7 @@ export const serverSettingsRouter = createTRPCRouter({
|
||||
mcp: {
|
||||
enabled: true,
|
||||
description:
|
||||
"Update global board defaults. Requires admin permission. Optional fields: homeBoardId, mobileHomeBoardId, enableAutomaticMobileLayout, enableStatusByDefault, forceDisableStatus. Home board IDs must reference public boards returned by the public-board listing tool or API, using each board's id field, or be null",
|
||||
"Update global board defaults. Requires admin permission. Optional fields: homeBoardId, mobileHomeBoardId, enableStatusByDefault, forceDisableStatus. Home board IDs must reference public boards or be null",
|
||||
},
|
||||
})
|
||||
.input(boardServerSettingsUpdateSchema)
|
||||
@@ -82,9 +81,17 @@ export const serverSettingsRouter = createTRPCRouter({
|
||||
|
||||
const current = await getServerSettingByKeyAsync(ctx.db, "board");
|
||||
const next = { ...current, ...input };
|
||||
await mergeServerSettingByKeyAsync(ctx.db, "board", next, input);
|
||||
const existing = await ctx.db.query.serverSettings.findFirst({
|
||||
where: eq(serverSettings.settingKey, "board"),
|
||||
});
|
||||
|
||||
return await getServerSettingByKeyAsync(ctx.db, "board");
|
||||
if (existing) {
|
||||
await updateServerSettingByKeyAsync(ctx.db, "board", next);
|
||||
} else {
|
||||
await insertServerSettingByKeyAsync(ctx.db, "board", next);
|
||||
}
|
||||
|
||||
return next;
|
||||
}),
|
||||
saveSettings: permissionRequiredProcedure
|
||||
.requiresPermission("admin")
|
||||
@@ -96,11 +103,10 @@ export const serverSettingsRouter = createTRPCRouter({
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const current = await getServerSettingByKeyAsync(ctx.db, input.settingsKey);
|
||||
const next = {
|
||||
await updateServerSettingByKeyAsync(ctx.db, input.settingsKey, {
|
||||
...current,
|
||||
...input.value,
|
||||
} as ServerSettings[keyof ServerSettings];
|
||||
await mergeServerSettingByKeyAsync(ctx.db, input.settingsKey, next, input.value);
|
||||
} as ServerSettings[keyof ServerSettings]);
|
||||
}),
|
||||
initSettings: onboardingProcedure
|
||||
.requiresStep("settings")
|
||||
|
||||
@@ -3,7 +3,7 @@ import { describe, expect, test, vi } from "vitest";
|
||||
|
||||
import type { Session } from "@homarr/auth";
|
||||
import { createId } from "@homarr/common";
|
||||
import { boards, layouts, serverSettings } from "@homarr/db/schema";
|
||||
import { boards, serverSettings } from "@homarr/db/schema";
|
||||
import { createDb } from "@homarr/db/test";
|
||||
import { defaultServerSettings, defaultServerSettingsKeys } from "@homarr/server-settings";
|
||||
|
||||
@@ -56,33 +56,6 @@ describe("getAll server settings", () => {
|
||||
});
|
||||
|
||||
describe("saveSettings", () => {
|
||||
test("saveSettings should persist settings when defaults are not seeded", async () => {
|
||||
const db = createDb();
|
||||
const caller = serverSettingsRouter.createCaller({
|
||||
db,
|
||||
deviceType: undefined,
|
||||
session: defaultSession,
|
||||
});
|
||||
|
||||
await caller.saveSettings({
|
||||
settingsKey: "board",
|
||||
value: {
|
||||
enableAutomaticMobileLayout: false,
|
||||
},
|
||||
});
|
||||
|
||||
const dbSettings = await db.select().from(serverSettings);
|
||||
expect(dbSettings).toStrictEqual([
|
||||
{
|
||||
settingKey: "board",
|
||||
value: stringify({
|
||||
...defaultServerSettings.board,
|
||||
enableAutomaticMobileLayout: false,
|
||||
}),
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("saveSettings should update settings and return true when it updated only one", async () => {
|
||||
const db = createDb();
|
||||
const caller = serverSettingsRouter.createCaller({
|
||||
@@ -116,33 +89,6 @@ describe("saveSettings", () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("saveSettings should preserve concurrent partial updates", async () => {
|
||||
const db = createDb();
|
||||
const caller = serverSettingsRouter.createCaller({
|
||||
db,
|
||||
deviceType: undefined,
|
||||
session: defaultSession,
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
caller.saveSettings({
|
||||
settingsKey: "analytics",
|
||||
value: { enableGeneral: false },
|
||||
}),
|
||||
caller.saveSettings({
|
||||
settingsKey: "analytics",
|
||||
value: { instanceId: "concurrent-instance" },
|
||||
}),
|
||||
]);
|
||||
|
||||
await expect(caller.getAll()).resolves.toMatchObject({
|
||||
analytics: {
|
||||
enableGeneral: false,
|
||||
instanceId: "concurrent-instance",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("board settings API", () => {
|
||||
@@ -157,36 +103,6 @@ describe("board settings API", () => {
|
||||
await expect(caller.getBoardSettings()).resolves.toStrictEqual(defaultServerSettings.board);
|
||||
});
|
||||
|
||||
test("getBoardSettings should backfill the automatic layout default for legacy values", async () => {
|
||||
const db = createDb();
|
||||
const caller = serverSettingsRouter.createCaller({
|
||||
db,
|
||||
deviceType: undefined,
|
||||
session: defaultSession,
|
||||
});
|
||||
const boardId = createId();
|
||||
await db.insert(boards).values({ id: boardId, name: "legacy", isPublic: false });
|
||||
await db.insert(layouts).values([
|
||||
{ id: createId(), boardId, name: "Base", columnCount: 12, breakpoint: 0 },
|
||||
{ id: createId(), boardId, name: "Mobile", columnCount: 4, breakpoint: 480 },
|
||||
]);
|
||||
const legacyBoardSettings = {
|
||||
homeBoardId: defaultServerSettings.board.homeBoardId,
|
||||
mobileHomeBoardId: defaultServerSettings.board.mobileHomeBoardId,
|
||||
enableStatusByDefault: defaultServerSettings.board.enableStatusByDefault,
|
||||
forceDisableStatus: defaultServerSettings.board.forceDisableStatus,
|
||||
};
|
||||
await db.insert(serverSettings).values({
|
||||
settingKey: "board",
|
||||
value: stringify(legacyBoardSettings),
|
||||
});
|
||||
|
||||
await expect(caller.getBoardSettings()).resolves.toStrictEqual({
|
||||
...defaultServerSettings.board,
|
||||
enableAutomaticMobileLayout: false,
|
||||
});
|
||||
});
|
||||
|
||||
test("updateBoardSettings should insert settings when defaults are not persisted", async () => {
|
||||
const db = createDb();
|
||||
const caller = serverSettingsRouter.createCaller({
|
||||
@@ -229,7 +145,6 @@ describe("board settings API", () => {
|
||||
await caller.updateBoardSettings({ homeBoardId: boardId });
|
||||
const result = await caller.updateBoardSettings({
|
||||
mobileHomeBoardId: boardId,
|
||||
enableAutomaticMobileLayout: false,
|
||||
enableStatusByDefault: false,
|
||||
});
|
||||
|
||||
@@ -237,7 +152,6 @@ describe("board settings API", () => {
|
||||
...defaultServerSettings.board,
|
||||
homeBoardId: boardId,
|
||||
mobileHomeBoardId: boardId,
|
||||
enableAutomaticMobileLayout: false,
|
||||
enableStatusByDefault: false,
|
||||
});
|
||||
const dbSettings = await db.select().from(serverSettings);
|
||||
@@ -249,25 +163,6 @@ describe("board settings API", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
test("updateBoardSettings should preserve concurrent partial updates", async () => {
|
||||
const db = createDb();
|
||||
const caller = serverSettingsRouter.createCaller({
|
||||
db,
|
||||
deviceType: undefined,
|
||||
session: defaultSession,
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
caller.updateBoardSettings({ forceDisableStatus: true }),
|
||||
caller.updateBoardSettings({ enableStatusByDefault: false }),
|
||||
]);
|
||||
|
||||
await expect(caller.getBoardSettings()).resolves.toMatchObject({
|
||||
forceDisableStatus: true,
|
||||
enableStatusByDefault: false,
|
||||
});
|
||||
});
|
||||
|
||||
test("updateBoardSettings should reject private home boards", async () => {
|
||||
const db = createDb();
|
||||
const caller = serverSettingsRouter.createCaller({
|
||||
|
||||
Reference in new issue
Block a user