feat(category): save collapse state for signed in users (#2134)
This commit is contained in:
@@ -15,6 +15,7 @@ import { logRouter } from "./router/log";
|
||||
import { mediaRouter } from "./router/medias/media-router";
|
||||
import { onboardRouter } from "./router/onboard/onboard-router";
|
||||
import { searchEngineRouter } from "./router/search-engine/search-engine-router";
|
||||
import { sectionRouter } from "./router/section/section-router";
|
||||
import { serverSettingsRouter } from "./router/serverSettings";
|
||||
import { updateCheckerRouter } from "./router/update-checker";
|
||||
import { userRouter } from "./router/user";
|
||||
@@ -27,6 +28,7 @@ export const appRouter = createTRPCRouter({
|
||||
invite: inviteRouter,
|
||||
integration: integrationRouter,
|
||||
board: boardRouter,
|
||||
section: sectionRouter,
|
||||
app: innerAppRouter,
|
||||
searchEngine: searchEngineRouter,
|
||||
widget: widgetRouter,
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
integrationItems,
|
||||
integrationUserPermissions,
|
||||
items,
|
||||
sectionCollapseStates,
|
||||
sections,
|
||||
users,
|
||||
} from "@homarr/db/schema";
|
||||
@@ -1025,6 +1026,9 @@ const getFullBoardWithWhereAsync = async (db: Database, where: SQL<unknown>, use
|
||||
},
|
||||
sections: {
|
||||
with: {
|
||||
collapseStates: {
|
||||
where: eq(sectionCollapseStates.userId, userId ?? ""),
|
||||
},
|
||||
items: {
|
||||
with: {
|
||||
integrations: {
|
||||
@@ -1059,9 +1063,10 @@ const getFullBoardWithWhereAsync = async (db: Database, where: SQL<unknown>, use
|
||||
|
||||
return {
|
||||
...otherBoardProperties,
|
||||
sections: sections.map((section) =>
|
||||
sections: sections.map(({ collapseStates, ...section }) =>
|
||||
parseSection({
|
||||
...section,
|
||||
collapsed: collapseStates.at(0)?.collapsed ?? false,
|
||||
items: section.items.map(({ integrations: itemIntegrations, ...item }) => ({
|
||||
...item,
|
||||
integrationIds: itemIntegrations.map((item) => item.integration.id),
|
||||
|
||||
52
packages/api/src/router/section/section-router.ts
Normal file
52
packages/api/src/router/section/section-router.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { z } from "zod";
|
||||
|
||||
import { and, eq } from "@homarr/db";
|
||||
import { sectionCollapseStates, sections } from "@homarr/db/schema";
|
||||
|
||||
import { createTRPCRouter, protectedProcedure } from "../../trpc";
|
||||
|
||||
export const sectionRouter = createTRPCRouter({
|
||||
changeCollapsed: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
sectionId: z.string(),
|
||||
collapsed: z.boolean(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const section = await ctx.db.query.sections.findFirst({
|
||||
where: and(eq(sections.id, input.sectionId), eq(sections.kind, "category")),
|
||||
with: {
|
||||
collapseStates: {
|
||||
where: eq(sectionCollapseStates.userId, ctx.session.user.id),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!section) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: `Section not found id=${input.sectionId}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (section.collapseStates.length === 0) {
|
||||
await ctx.db.insert(sectionCollapseStates).values({
|
||||
sectionId: section.id,
|
||||
userId: ctx.session.user.id,
|
||||
collapsed: input.collapsed,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.db
|
||||
.update(sectionCollapseStates)
|
||||
.set({
|
||||
collapsed: input.collapsed,
|
||||
})
|
||||
.where(
|
||||
and(eq(sectionCollapseStates.sectionId, section.id), eq(sectionCollapseStates.userId, ctx.session.user.id)),
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -812,7 +812,7 @@ describe("saveBoard should save full board", () => {
|
||||
expect(integration).toBeUndefined();
|
||||
expect(spy).toHaveBeenCalledWith(expect.anything(), expect.anything(), "modify");
|
||||
});
|
||||
it.each([[{ kind: "empty" as const }], [{ kind: "category" as const, name: "My first category" }]])(
|
||||
it.each([[{ kind: "empty" as const }], [{ kind: "category" as const, collapsed: false, name: "My first category" }]])(
|
||||
"should add section when present in input",
|
||||
async (partialSection) => {
|
||||
const spy = vi.spyOn(boardAccess, "throwIfActionForbiddenAsync");
|
||||
@@ -1023,6 +1023,7 @@ describe("saveBoard should save full board", () => {
|
||||
yOffset: 1,
|
||||
xOffset: 0,
|
||||
name: "Test",
|
||||
collapsed: true,
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
@@ -1031,6 +1032,7 @@ describe("saveBoard should save full board", () => {
|
||||
name: "After",
|
||||
yOffset: 0,
|
||||
xOffset: 0,
|
||||
collapsed: false,
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user