diff --git a/server/router/api/v1/connect_interceptors.go b/server/router/api/v1/connect_interceptors.go index 03eb35de4..348c89279 100644 --- a/server/router/api/v1/connect_interceptors.go +++ b/server/router/api/v1/connect_interceptors.go @@ -50,7 +50,19 @@ func (*MetadataInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc // Set metadata in context so services can use metadata.FromIncomingContext() ctx = metadata.NewIncomingContext(ctx, md) - return next(ctx, req) + + // Execute the request + resp, err := next(ctx, req) + + // Prevent browser caching of API responses to avoid stale data issues + // See: https://github.com/usememos/memos/issues/5470 + if resp != nil { + resp.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") + resp.Header().Set("Pragma", "no-cache") + resp.Header().Set("Expires", "0") + } + + return resp, err } } diff --git a/web/src/components/MemoEditor/hooks/useMemoInit.ts b/web/src/components/MemoEditor/hooks/useMemoInit.ts index 7586c0e62..6cdc0f8f6 100644 --- a/web/src/components/MemoEditor/hooks/useMemoInit.ts +++ b/web/src/components/MemoEditor/hooks/useMemoInit.ts @@ -1,4 +1,6 @@ +import { useQueryClient } from "@tanstack/react-query"; import { useEffect, useRef } from "react"; +import { memoKeys } from "@/hooks/useMemoQueries"; import type { Visibility } from "@/types/proto/api/v1/memo_service_pb"; import type { EditorRefActions } from "../Editor"; import { cacheService, memoService } from "../services"; @@ -13,6 +15,7 @@ export const useMemoInit = ( defaultVisibility?: Visibility, ) => { const { actions, dispatch } = useEditorContext(); + const queryClient = useQueryClient(); const initializedRef = useRef(false); useEffect(() => { @@ -24,6 +27,10 @@ export const useMemoInit = ( try { if (memoName) { + // Force refetch from server to prevent stale data issues + // See: https://github.com/usememos/memos/issues/5470 + await queryClient.invalidateQueries({ queryKey: memoKeys.detail(memoName) }); + // Load existing memo const loadedState = await memoService.load(memoName); dispatch( @@ -58,5 +65,5 @@ export const useMemoInit = ( }; init(); - }, [memoName, cacheKey, username, autoFocus, defaultVisibility, actions, dispatch, editorRef]); + }, [memoName, cacheKey, username, autoFocus, defaultVisibility, actions, dispatch, editorRef, queryClient]); }; diff --git a/web/src/hooks/useMemoQueries.ts b/web/src/hooks/useMemoQueries.ts index bb61426ed..6cb107e5a 100644 --- a/web/src/hooks/useMemoQueries.ts +++ b/web/src/hooks/useMemoQueries.ts @@ -53,7 +53,7 @@ export function useMemo(name: string, options?: { enabled?: boolean }) { return memo; }, enabled: options?.enabled ?? true, - staleTime: 1000 * 60, // 1 minute - memos can be edited frequently + staleTime: 1000 * 10, // 10 seconds - reduced to prevent stale data in collaborative editing }); }