diff options
| author | Fuwn <[email protected]> | 2026-02-10 20:33:11 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-10 20:33:11 -0800 |
| commit | 40592fe37310837ad4d6e6f9be76de17e9a33027 (patch) | |
| tree | 58bfafe8de39a7555e9160ad1224e275b4a8ec71 /apps/web/app/reader/_components | |
| parent | fix: let display density apply to main content panel (diff) | |
| download | asa.news-40592fe37310837ad4d6e6f9be76de17e9a33027.tar.xz asa.news-40592fe37310837ad4d6e6f9be76de17e9a33027.zip | |
fix: query entry state directly instead of relying on unfiltered timeline
The detail panel called useTimeline() with no args, creating a separate
cache from the entry list's filtered query. Entries not in the first 50
of the global timeline had isSaved/isRead stuck at false.
Diffstat (limited to 'apps/web/app/reader/_components')
| -rw-r--r-- | apps/web/app/reader/_components/entry-detail-panel.tsx | 35 |
1 files changed, 26 insertions, 9 deletions
diff --git a/apps/web/app/reader/_components/entry-detail-panel.tsx b/apps/web/app/reader/_components/entry-detail-panel.tsx index 7901100..6760f52 100644 --- a/apps/web/app/reader/_components/entry-detail-panel.tsx +++ b/apps/web/app/reader/_components/entry-detail-panel.tsx @@ -10,7 +10,6 @@ import { } from "@/lib/queries/use-entry-state-mutations" import { queryKeys } from "@/lib/queries/query-keys" import { useUserInterfaceStore } from "@/lib/stores/user-interface-store" -import { useTimeline } from "@/lib/queries/use-timeline" import { useEntryShare } from "@/lib/queries/use-entry-share" import { useEntryHighlights } from "@/lib/queries/use-entry-highlights" import { @@ -90,10 +89,28 @@ export function EntryDetailPanel({ const [shareNoteText, setShareNoteText] = useState("") const shareNoteTextareaReference = useRef<HTMLTextAreaElement>(null) - const { data: timelineData } = useTimeline() - const currentEntry = timelineData?.pages - .flatMap((page) => page) - .find((entry) => entry.entryIdentifier === entryIdentifier) + const { data: entryState } = useQuery({ + queryKey: queryKeys.entryState.single(entryIdentifier), + queryFn: async () => { + const { + data: { user }, + } = await supabaseClient.auth.getUser() + + if (!user) return { isRead: false, isSaved: false } + + const { data } = await supabaseClient + .from("user_entry_states") + .select("read, saved") + .eq("user_id", user.id) + .eq("entry_id", entryIdentifier) + .maybeSingle() + + return { + isRead: data?.read ?? false, + isSaved: data?.saved ?? false, + } + }, + }) const { data: entryDetail, isLoading } = useQuery({ queryKey: queryKeys.entryDetail.single(entryIdentifier), @@ -140,7 +157,7 @@ export function EntryDetailPanel({ }) useEffect(() => { - if (!currentEntry || currentEntry.isRead) return + if (!entryState || entryState.isRead) return const autoReadTimeout = setTimeout(() => { toggleReadState.mutate({ @@ -151,7 +168,7 @@ export function EntryDetailPanel({ return () => clearTimeout(autoReadTimeout) // eslint-disable-next-line react-hooks/exhaustive-deps - }, [entryIdentifier, currentEntry?.isRead]) + }, [entryIdentifier, entryState?.isRead]) const contentHtml = entryDetail?.content_html || entryDetail?.summary || "" @@ -439,8 +456,8 @@ export function EntryDetailPanel({ } const readingTimeMinutes = estimateReadingTimeMinutes(contentHtml) - const isRead = currentEntry?.isRead ?? false - const isSaved = currentEntry?.isSaved ?? false + const isRead = entryState?.isRead ?? false + const isSaved = entryState?.isSaved ?? false const actionBarAtBottom = isMobile && toolbarPosition === "bottom" |