From b0aa6ae52466ac56c1a90acf8524ab8e9b97218b Mon Sep 17 00:00:00 2001 From: Athou Date: Sat, 13 Jan 2024 09:03:14 +0100 Subject: [PATCH] the "new-feed-entries" websocket event no longer needs to reload the entire tree --- commafeed-client/src/app/tree/slice.ts | 18 +++++++++++++++++- commafeed-client/src/hooks/useWebSocket.ts | 19 ++++++++++++++++--- .../backend/feed/FeedRefreshUpdater.java | 15 +++++++++------ .../frontend/ws/WebSocketMessageBuilder.java | 4 ++-- .../commafeed/integration/WebSocketIT.java | 2 +- 5 files changed, 45 insertions(+), 13 deletions(-) diff --git a/commafeed-client/src/app/tree/slice.ts b/commafeed-client/src/app/tree/slice.ts index 4b20eec6..0c386525 100644 --- a/commafeed-client/src/app/tree/slice.ts +++ b/commafeed-client/src/app/tree/slice.ts @@ -26,6 +26,22 @@ export const treeSlice = createSlice({ toggleSidebar: state => { state.sidebarVisible = !state.sidebarVisible }, + incrementUnreadCount: ( + state, + action: PayloadAction<{ + feedId: number + amount: number + }> + ) => { + if (!state.rootCategory) return + visitCategoryTree(state.rootCategory, c => + c.feeds + .filter(f => f.id === action.payload.feedId) + .forEach(f => { + f.unread += action.payload.amount + }) + ) + }, }, extraReducers: builder => { builder.addCase(reloadTree.fulfilled, (state, action) => { @@ -53,4 +69,4 @@ export const treeSlice = createSlice({ }, }) -export const { setMobileMenuOpen, toggleSidebar } = treeSlice.actions +export const { setMobileMenuOpen, toggleSidebar, incrementUnreadCount } = treeSlice.actions diff --git a/commafeed-client/src/hooks/useWebSocket.ts b/commafeed-client/src/hooks/useWebSocket.ts index 40e490d4..01506111 100644 --- a/commafeed-client/src/hooks/useWebSocket.ts +++ b/commafeed-client/src/hooks/useWebSocket.ts @@ -1,9 +1,22 @@ import { setWebSocketConnected } from "app/server/slice" -import { useAppDispatch, useAppSelector } from "app/store" -import { reloadTree } from "app/tree/thunks" +import { type AppDispatch, useAppDispatch, useAppSelector } from "app/store" +import { incrementUnreadCount } from "app/tree/slice" import { useEffect } from "react" import WebsocketHeartbeatJs from "websocket-heartbeat-js" +const handleMessage = (dispatch: AppDispatch, message: string) => { + const parts = message.split(":") + const type = parts[0] + if (type === "new-feed-entries") { + dispatch( + incrementUnreadCount({ + feedId: +parts[1], + amount: +parts[2], + }) + ) + } +} + export const useWebSocket = () => { const websocketEnabled = useAppSelector(state => state.server.serverInfos?.websocketEnabled) const websocketPingInterval = useAppSelector(state => state.server.serverInfos?.websocketPingInterval) @@ -27,7 +40,7 @@ export const useWebSocket = () => { ws.onmessage = event => { const { data } = event if (typeof data === "string") { - if (data.startsWith("new-feed-entries:")) dispatch(reloadTree()) + handleMessage(dispatch, data) } } } diff --git a/commafeed-server/src/main/java/com/commafeed/backend/feed/FeedRefreshUpdater.java b/commafeed-server/src/main/java/com/commafeed/backend/feed/FeedRefreshUpdater.java index 1d311559..bacb65a7 100644 --- a/commafeed-server/src/main/java/com/commafeed/backend/feed/FeedRefreshUpdater.java +++ b/commafeed-server/src/main/java/com/commafeed/backend/feed/FeedRefreshUpdater.java @@ -118,7 +118,7 @@ public class FeedRefreshUpdater { public boolean update(Feed feed, List entries) { boolean processed = true; - boolean insertedAtLeastOneEntry = false; + long inserted = 0; if (!entries.isEmpty()) { Set lastEntries = cache.getLastEntries(feed); @@ -134,7 +134,7 @@ public class FeedRefreshUpdater { } AddEntryResult addEntryResult = addEntry(feed, entry, subscriptions); processed &= addEntryResult.processed; - insertedAtLeastOneEntry |= addEntryResult.inserted; + inserted += addEntryResult.inserted ? 1 : 0; entryCacheMiss.mark(); } else { @@ -148,13 +148,12 @@ public class FeedRefreshUpdater { if (subscriptions == null) { feed.setMessage("No new entries found"); - } else if (insertedAtLeastOneEntry) { + } else if (inserted > 0) { List users = subscriptions.stream().map(FeedSubscription::getUser).toList(); cache.invalidateUnreadCount(subscriptions.toArray(new FeedSubscription[0])); cache.invalidateUserRootCategory(users.toArray(new User[0])); - // notify over websocket - subscriptions.forEach(sub -> webSocketSessions.sendMessage(sub.getUser(), WebSocketMessageBuilder.newFeedEntries(sub))); + notifyOverWebsocket(subscriptions, inserted); } } @@ -163,7 +162,7 @@ public class FeedRefreshUpdater { feed.setDisabledUntil(Instant.EPOCH); } - if (insertedAtLeastOneEntry) { + if (inserted > 0) { feedUpdated.mark(); } @@ -172,6 +171,10 @@ public class FeedRefreshUpdater { return processed; } + private void notifyOverWebsocket(List subscriptions, long inserted) { + subscriptions.forEach(sub -> webSocketSessions.sendMessage(sub.getUser(), WebSocketMessageBuilder.newFeedEntries(sub, inserted))); + } + @AllArgsConstructor private static class AddEntryResult { private final boolean processed; diff --git a/commafeed-server/src/main/java/com/commafeed/frontend/ws/WebSocketMessageBuilder.java b/commafeed-server/src/main/java/com/commafeed/frontend/ws/WebSocketMessageBuilder.java index b15b92ff..f0212533 100644 --- a/commafeed-server/src/main/java/com/commafeed/frontend/ws/WebSocketMessageBuilder.java +++ b/commafeed-server/src/main/java/com/commafeed/frontend/ws/WebSocketMessageBuilder.java @@ -7,8 +7,8 @@ import lombok.experimental.UtilityClass; @UtilityClass public class WebSocketMessageBuilder { - public static String newFeedEntries(FeedSubscription subscription) { - return String.format("%s:%s", "new-feed-entries", subscription.getId()); + public static String newFeedEntries(FeedSubscription subscription, long count) { + return String.format("%s:%s:%s", "new-feed-entries", subscription.getId(), count); } } diff --git a/commafeed-server/src/test/java/com/commafeed/integration/WebSocketIT.java b/commafeed-server/src/test/java/com/commafeed/integration/WebSocketIT.java index f2657934..30e3cb6a 100644 --- a/commafeed-server/src/test/java/com/commafeed/integration/WebSocketIT.java +++ b/commafeed-server/src/test/java/com/commafeed/integration/WebSocketIT.java @@ -66,7 +66,7 @@ class WebSocketIT extends BaseIT { Long subscriptionId = subscribe(getFeedUrl()); Awaitility.await().atMost(15, TimeUnit.SECONDS).until(() -> messageRef.get() != null); - Assertions.assertEquals("new-feed-entries:" + subscriptionId, messageRef.get()); + Assertions.assertEquals("new-feed-entries:" + subscriptionId + ":2", messageRef.get()); } }