Must-fix: - CHANGELOG: add [Unreleased]/Added entry for temporary notes (#201). - temporary-note-cleanup: re-check temporary_expires_at at deletion time so a concurrent "Make permanent" (sets it NULL) between the batch SELECT and the per-row removePage wins the race and the note is not trashed. Add unit tests for the make-permanent and already-trashed race windows. Non-blocking review items: - temporary-note-cleanup: cap the sweep batch (LIMIT 500) so a large backlog is not loaded into memory; remainder drains on the next hourly run. - client: extract duplicated post-toggle cache sync into syncTemporaryExpiresInCache() shared by the header menu and the banner. - Remove the tautological migration spec that mocked the whole Kysely builder. - Tests: cover create() frozen temporaryExpiresAt (workspace override + NULL default fallback + non-temporary skips lookup) and restorePage disarming the timer (temporaryExpiresAt: null). Deferred (forward-looking, non-blocking): extract PageService.computeTemporaryExpiresAt() to dedupe the deadline formula and drop the @InjectKysely from PageTemplateController; replace migration unit test with a real Postgres up/down integration test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
71 lines
2.0 KiB
TypeScript
71 lines
2.0 KiB
TypeScript
import { useMutation } from "@tanstack/react-query";
|
|
import { notifications } from "@mantine/notifications";
|
|
import {
|
|
toggleTemplate,
|
|
toggleTemporary,
|
|
} from "@/features/page-embed/services/page-embed-api";
|
|
import type {
|
|
ToggleTemplateResponse,
|
|
ToggleTemporaryResponse,
|
|
} from "@/features/page-embed/types/page-embed.types";
|
|
import { queryClient } from "@/main.tsx";
|
|
|
|
/**
|
|
* After toggling a note's temporary state, mirror the new deadline into the
|
|
* shared page cache (keyed by both slugId and id) and refresh the sidebar so the
|
|
* menu label, the in-page banner, and the tree icon all reflect the change.
|
|
* Centralised here so the header menu and the banner can't drift apart on the
|
|
* cache-key plumbing.
|
|
*/
|
|
export function syncTemporaryExpiresInCache(
|
|
page: { id: string; slugId: string },
|
|
temporaryExpiresAt: string | null,
|
|
) {
|
|
for (const key of [page.slugId, page.id]) {
|
|
const cached = queryClient.getQueryData<any>(["pages", key]);
|
|
if (cached) {
|
|
queryClient.setQueryData(["pages", key], {
|
|
...cached,
|
|
temporaryExpiresAt,
|
|
});
|
|
}
|
|
}
|
|
queryClient.invalidateQueries({
|
|
predicate: (item) =>
|
|
["sidebar-pages"].includes(item.queryKey[0] as string),
|
|
});
|
|
}
|
|
|
|
export function useToggleTemplateMutation() {
|
|
return useMutation<
|
|
ToggleTemplateResponse,
|
|
Error,
|
|
{ pageId: string; isTemplate?: boolean }
|
|
>({
|
|
mutationFn: (data) => toggleTemplate(data),
|
|
onError: (err: any) => {
|
|
notifications.show({
|
|
message: err?.response?.data?.message || "Failed to update template",
|
|
color: "red",
|
|
});
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useToggleTemporaryMutation() {
|
|
return useMutation<
|
|
ToggleTemporaryResponse,
|
|
Error,
|
|
{ pageId: string; temporary?: boolean }
|
|
>({
|
|
mutationFn: (data) => toggleTemporary(data),
|
|
onError: (err: any) => {
|
|
notifications.show({
|
|
message:
|
|
err?.response?.data?.message || "Failed to update temporary note",
|
|
color: "red",
|
|
});
|
|
},
|
|
});
|
|
}
|