From 0b14c529d828cf43b49feec07457532e39f4be56 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Thu, 28 May 2026 16:05:06 +0100 Subject: [PATCH] fix page updated time object --- .../src/features/editor/page-editor.tsx | 21 ++++++++++++++++++- .../components/page-details-aside.tsx | 9 ++++---- .../extensions/persistence.extension.ts | 15 +++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/apps/client/src/features/editor/page-editor.tsx b/apps/client/src/features/editor/page-editor.tsx index a703561f5..9d1316943 100644 --- a/apps/client/src/features/editor/page-editor.tsx +++ b/apps/client/src/features/editor/page-editor.tsx @@ -14,6 +14,7 @@ import { WebSocketStatus, HocuspocusProviderWebsocket, onSyncedParameters, + onStatelessParameters, } from "@hocuspocus/provider"; import { Editor, @@ -145,6 +146,24 @@ export default function PageEditor({ const onSyncedHandler = (event: onSyncedParameters) => { setIsRemoteSynced(event.state); }; + const onStatelessHandler = ({ payload }: onStatelessParameters) => { + try { + const message = JSON.parse(payload); + if (message?.type !== "page.updated" || !message.updatedAt) return; + const pageData = queryClient.getQueryData(["pages", slugId]); + if (pageData) { + queryClient.setQueryData(["pages", slugId], { + ...pageData, + updatedAt: message.updatedAt, + ...(message.lastUpdatedBy && { + lastUpdatedBy: message.lastUpdatedBy, + }), + }); + } + } catch { + // ignore unrelated stateless messages + } + }; const onAuthenticationFailedHandler = () => { const payload = jwtDecode(collabQuery?.token); const now = Date.now().valueOf() / 1000; @@ -169,6 +188,7 @@ export default function PageEditor({ onAuthenticationFailed: onAuthenticationFailedHandler, onStatus: onStatusHandler, onSynced: onSyncedHandler, + onStateless: onStatelessHandler, }); local.on("synced", onLocalSyncedHandler); @@ -318,7 +338,6 @@ export default function PageEditor({ queryClient.setQueryData(["pages", slugId], { ...pageData, content: newContent, - updatedAt: new Date(), }); } }, 3000); diff --git a/apps/client/src/features/page-details/components/page-details-aside.tsx b/apps/client/src/features/page-details/components/page-details-aside.tsx index 84209d7a6..89c51027c 100644 --- a/apps/client/src/features/page-details/components/page-details-aside.tsx +++ b/apps/client/src/features/page-details/components/page-details-aside.tsx @@ -16,7 +16,8 @@ import { usePageQuery } from "@/features/page/queries/page-query.ts"; import { pageEditorAtom } from "@/features/editor/atoms/editor-atoms.ts"; import { useBacklinksCountQuery } from "@/features/page-details/queries/backlinks-query.ts"; import { BacklinksModal } from "./backlinks-modal"; -import { formattedDate, timeAgo } from "@/lib/time.ts"; +import { formattedDate } from "@/lib/time.ts"; +import { useTimeAgo } from "@/hooks/use-time-ago.tsx"; import { CustomAvatar } from "@/components/ui/custom-avatar.tsx"; import { LabelsSection } from "@/features/label/components/labels-section.tsx"; @@ -139,6 +140,7 @@ function StatsSection({ updatedAt: Date | string; }) { const { t } = useTranslation(); + const lastUpdated = useTimeAgo(updatedAt); return ( @@ -150,10 +152,7 @@ function StatsSection({ label={t("Created")} value={formattedDate(new Date(createdAt))} /> - + ); } diff --git a/apps/server/src/collaboration/extensions/persistence.extension.ts b/apps/server/src/collaboration/extensions/persistence.extension.ts index 53bc8b334..3a4df24a7 100644 --- a/apps/server/src/collaboration/extensions/persistence.extension.ts +++ b/apps/server/src/collaboration/extensions/persistence.extension.ts @@ -165,6 +165,21 @@ export class PersistenceExtension implements Extension { } if (page) { + document.broadcastStateless( + JSON.stringify({ + type: 'page.updated', + updatedAt: new Date().toISOString(), + lastUpdatedById: context?.user?.id, + lastUpdatedBy: context?.user + ? { + id: context.user?.id, + name: context.user?.name, + avatarUrl: context.user?.avatarUrl, + } + : undefined, + }), + ); + await this.syncTransclusion(pageId, page.workspaceId, tiptapJson); }