Skip to content

Commit 1f3ac3b

Browse files
committed
entry invalidation
1 parent 89dd1a6 commit 1f3ac3b

File tree

7 files changed

+71
-26
lines changed

7 files changed

+71
-26
lines changed

ui/src/components/entry/entry-item.tsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import { A, type AnchorProps, useMatch } from '@solidjs/router';
2-
import { createQuery } from '@tanstack/solid-query';
32
import { cx } from 'class-variance-authority';
43
import { type Component, Show, splitProps } from 'solid-js';
5-
import { getEntry } from '~/api/entries';
64
import { DATA_ATTRIBUTES } from '~/constants/attributes';
7-
import { QUERY_KEYS } from '~/constants/query';
85
import { useQueryState } from '~/contexts/query-state-context';
6+
import { useEntry } from '~/hooks/queries/use-entry';
97
import { useFeeds } from '~/hooks/queries/use-feeds';
108
import type { Entry } from '~/types/bindings';
119
import { formatDate } from '~/utils/date';
@@ -21,17 +19,13 @@ export const EntryItem: Component<EntryItemProps> = props => {
2119
const [local, rest] = splitProps(props, ['entry', 'class']);
2220

2321
// Get the data for an entry to check if user marked it as read
24-
const entryData = createQuery(() => ({
22+
const entryData = useEntry(() => ({
23+
entry_uuid: local.entry.uuid,
2524
enabled: state.params.entry_uuid === local.entry.uuid,
26-
queryKey: [QUERY_KEYS.ENTRIES_VIEW, local.entry.uuid],
27-
queryFn: () => getEntry(local.entry.uuid),
28-
refetchOnWindowFocus: false,
29-
refetchOnMount: false,
3025
}));
3126

3227
const feed = () => feeds.findFeed(local.entry.feed_uuid);
3328
const isRead = () => !!local.entry.read_at || !!entryData.data?.read_at;
34-
3529
const getDate = () => local.entry.published_at || local.entry.updated_at;
3630

3731
const entryRouteMatch = useMatch(() => state.getEntryUrl(local.entry.uuid, false));

ui/src/components/entry/entry-list.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const EntryList: Component<EntryListProps> = props => {
2323
// Handle arrow navigation
2424
useListNav(() => ({
2525
enabled: !!props.containsActiveElement,
26-
entries: entries.getAllEntries(),
26+
entries: entries.allEntries(),
2727
}));
2828

2929
createEffect(() => {
@@ -51,15 +51,15 @@ export const EntryList: Component<EntryListProps> = props => {
5151

5252
<Match when={entries.query.isSuccess && feeds.data}>
5353
<Show
54-
when={entries.getAllEntries().length}
54+
when={entries.allEntries().length}
5555
fallback={
5656
<div class="size-full flex-1 p-4">
5757
<Empty icon={HiOutlineInbox} text="No items to display" />
5858
</div>
5959
}
6060
>
6161
<div class="flex flex-col gap-2 px-4 py-2">
62-
<For each={entries.getAllEntries()}>
62+
<For each={entries.allEntries()}>
6363
{(entry, index) => (
6464
<EntryItem
6565
tabIndex={index() === 0 ? 0 : -1} // Disable tabindex so we can override it with arrow keys

ui/src/components/panels/entry-panel.tsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { createQuery, useQueryClient } from '@tanstack/solid-query';
1+
import { useQueryClient } from '@tanstack/solid-query';
22
import { createMutation } from '@tanstack/solid-query';
33
import { Match, Show, Switch, createEffect } from 'solid-js';
4-
import { getEntry } from '~/api/entries';
54
import { updateEntryAsRead } from '~/api/entries';
65
import { EntryView } from '~/components/entry/entry-view';
76
import { Panel } from '~/components/layout/panel';
87
import { QUERY_KEYS } from '~/constants/query';
98
import { useQueryState } from '~/contexts/query-state-context';
9+
import { useEntry } from '~/hooks/queries/use-entry';
1010
import { useInvalidateStats } from '~/hooks/queries/use-invalidate-stats';
1111
import { useViewport } from '~/hooks/use-viewport';
1212
import { Empty } from '../ui/empty';
@@ -18,13 +18,7 @@ export const EntryPanel = () => {
1818
const queryClient = useQueryClient();
1919
const invalidateStats = useInvalidateStats();
2020

21-
const entry = createQuery(() => ({
22-
enabled: !!state.params.entry_uuid,
23-
queryKey: [QUERY_KEYS.ENTRIES_VIEW, state.params.entry_uuid],
24-
queryFn: () => getEntry(state.params.entry_uuid!),
25-
refetchOnWindowFocus: false,
26-
refetchOnMount: false,
27-
}));
21+
const entry = useEntry(() => ({ entry_uuid: state.params.entry_uuid }));
2822

2923
const markAsRead = createMutation(() => ({
3024
mutationKey: [QUERY_KEYS.ENTRIES_VIEW_READ],

ui/src/contexts/notifications-context.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { WebSocket } from 'partysocket';
22
import { createContext, createSignal, useContext } from 'solid-js';
3+
import { useEntry } from '~/hooks/queries/use-entry';
4+
import { useInvalidateEntry } from '~/hooks/queries/use-invalidate-entry';
35
import type { Notification } from '~/types/bindings';
46
import { wsUrl } from '~/utils/url';
57
import { useInvalidateFeed } from '../hooks/queries/use-invalidate-feed';
8+
import { useQueryState } from './query-state-context';
69

710
type NotificationsContextType = ReturnType<typeof makeNotificationsContext>;
811
export const NotificationsContext = createContext<NotificationsContextType>();
@@ -15,7 +18,12 @@ export const useNotifications = () => {
1518
};
1619

1720
export const makeNotificationsContext = () => {
21+
const state = useQueryState();
22+
23+
const currentEntry = useEntry(() => ({ entry_uuid: state.params.entry_uuid }));
24+
1825
const invalidateFeed = useInvalidateFeed();
26+
const invalidateEntry = useInvalidateEntry();
1927

2028
const [feedsRefreshing, setFeedsRefreshing] = createSignal<string[]>([]);
2129

@@ -24,6 +32,13 @@ export const makeNotificationsContext = () => {
2432
maxRetries: 20,
2533
});
2634

35+
const maybeInvalidateCurrentEntry = (feed_uuid: string) => {
36+
if (!state.params.entry_uuid || currentEntry.data?.feed_uuid !== feed_uuid) return;
37+
38+
// Invalidate the entry being viewed if it belongs to the same feed
39+
invalidateEntry(state.params.entry_uuid);
40+
};
41+
2742
socket.addEventListener('open', () => console.info('[ws] connection established'));
2843
socket.addEventListener('close', () => console.info('[ws] connection terminated'));
2944
socket.addEventListener('error', event => console.info('[ws] error:', event.error));
@@ -34,19 +49,24 @@ export const makeNotificationsContext = () => {
3449
console.info('[ws] received message:', notif);
3550

3651
switch (notif.type) {
37-
case 'StartedFeedRefresh':
52+
case 'StartedFeedRefresh': {
3853
setFeedsRefreshing(uuids => [...uuids, notif.data.feed_uuid]);
3954
break;
55+
}
4056

41-
case 'FinishedFeedRefresh':
57+
case 'FinishedFeedRefresh': {
4258
setFeedsRefreshing(uuids => uuids.filter(uuid => uuid !== notif.data.feed_uuid));
4359
invalidateFeed(notif.data.feed_uuid);
60+
maybeInvalidateCurrentEntry(notif.data.feed_uuid);
4461
break;
62+
}
4563

4664
case 'FinishedScrapingEntries':
47-
case 'FinishedFetchingFeedFavicon':
65+
case 'FinishedFetchingFeedFavicon': {
4866
invalidateFeed(notif.data.feed_uuid);
67+
maybeInvalidateCurrentEntry(notif.data.feed_uuid);
4968
break;
69+
}
5070
}
5171
});
5272

ui/src/hooks/queries/use-entry.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { createQuery } from '@tanstack/solid-query';
2+
import { getEntry } from '~/api/entries';
3+
import { QUERY_KEYS } from '~/constants/query';
4+
5+
type UseEntryParams = {
6+
entry_uuid?: string;
7+
enabled?: boolean;
8+
};
9+
10+
export const useEntry = (params: () => UseEntryParams) =>
11+
createQuery(() => ({
12+
enabled: (params().enabled ?? true) && !!params().entry_uuid,
13+
queryKey: [QUERY_KEYS.ENTRIES_VIEW, params().entry_uuid],
14+
queryFn: () => getEntry(params().entry_uuid!),
15+
refetchOnWindowFocus: false,
16+
refetchOnMount: false,
17+
}));

ui/src/hooks/queries/use-infinite-entries.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const useInfiniteEntries = () => {
2424
refetchOnMount: false,
2525
}));
2626

27-
const getAllEntries = () => query.data?.pages.flatMap(page => page.data) || [];
27+
const allEntries = () => query.data?.pages.flatMap(page => page.data) || [];
2828

2929
const fetchMore = leading(
3030
debounce,
@@ -39,7 +39,7 @@ export const useInfiniteEntries = () => {
3939

4040
return {
4141
query,
42-
getAllEntries,
4342
fetchMore,
43+
allEntries,
4444
};
4545
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { debounce, leadingAndTrailing } from '@solid-primitives/scheduled';
2+
import { useQueryClient } from '@tanstack/solid-query';
3+
import { QUERY_KEYS } from '~/constants/query';
4+
5+
const DEBOUNCE_MS = 500;
6+
7+
/**
8+
* Invalidate an entry by its UUID, debounced.
9+
*/
10+
export const useInvalidateEntry = () => {
11+
const queryClient = useQueryClient();
12+
13+
return leadingAndTrailing(
14+
debounce,
15+
(entry_uuid: string) => {
16+
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.ENTRIES_VIEW, entry_uuid] });
17+
},
18+
DEBOUNCE_MS,
19+
);
20+
};

0 commit comments

Comments
 (0)