the "new-feed-entries" websocket event no longer needs to reload the entire tree

This commit is contained in:
Athou
2024-01-13 09:03:14 +01:00
parent 11dd151a3b
commit b0aa6ae524
5 changed files with 45 additions and 13 deletions

View File

@@ -26,6 +26,22 @@ export const treeSlice = createSlice({
toggleSidebar: state => { toggleSidebar: state => {
state.sidebarVisible = !state.sidebarVisible 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 => { extraReducers: builder => {
builder.addCase(reloadTree.fulfilled, (state, action) => { 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

View File

@@ -1,9 +1,22 @@
import { setWebSocketConnected } from "app/server/slice" import { setWebSocketConnected } from "app/server/slice"
import { useAppDispatch, useAppSelector } from "app/store" import { type AppDispatch, useAppDispatch, useAppSelector } from "app/store"
import { reloadTree } from "app/tree/thunks" import { incrementUnreadCount } from "app/tree/slice"
import { useEffect } from "react" import { useEffect } from "react"
import WebsocketHeartbeatJs from "websocket-heartbeat-js" 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 = () => { export const useWebSocket = () => {
const websocketEnabled = useAppSelector(state => state.server.serverInfos?.websocketEnabled) const websocketEnabled = useAppSelector(state => state.server.serverInfos?.websocketEnabled)
const websocketPingInterval = useAppSelector(state => state.server.serverInfos?.websocketPingInterval) const websocketPingInterval = useAppSelector(state => state.server.serverInfos?.websocketPingInterval)
@@ -27,7 +40,7 @@ export const useWebSocket = () => {
ws.onmessage = event => { ws.onmessage = event => {
const { data } = event const { data } = event
if (typeof data === "string") { if (typeof data === "string") {
if (data.startsWith("new-feed-entries:")) dispatch(reloadTree()) handleMessage(dispatch, data)
} }
} }
} }

View File

@@ -118,7 +118,7 @@ public class FeedRefreshUpdater {
public boolean update(Feed feed, List<Entry> entries) { public boolean update(Feed feed, List<Entry> entries) {
boolean processed = true; boolean processed = true;
boolean insertedAtLeastOneEntry = false; long inserted = 0;
if (!entries.isEmpty()) { if (!entries.isEmpty()) {
Set<String> lastEntries = cache.getLastEntries(feed); Set<String> lastEntries = cache.getLastEntries(feed);
@@ -134,7 +134,7 @@ public class FeedRefreshUpdater {
} }
AddEntryResult addEntryResult = addEntry(feed, entry, subscriptions); AddEntryResult addEntryResult = addEntry(feed, entry, subscriptions);
processed &= addEntryResult.processed; processed &= addEntryResult.processed;
insertedAtLeastOneEntry |= addEntryResult.inserted; inserted += addEntryResult.inserted ? 1 : 0;
entryCacheMiss.mark(); entryCacheMiss.mark();
} else { } else {
@@ -148,13 +148,12 @@ public class FeedRefreshUpdater {
if (subscriptions == null) { if (subscriptions == null) {
feed.setMessage("No new entries found"); feed.setMessage("No new entries found");
} else if (insertedAtLeastOneEntry) { } else if (inserted > 0) {
List<User> users = subscriptions.stream().map(FeedSubscription::getUser).toList(); List<User> users = subscriptions.stream().map(FeedSubscription::getUser).toList();
cache.invalidateUnreadCount(subscriptions.toArray(new FeedSubscription[0])); cache.invalidateUnreadCount(subscriptions.toArray(new FeedSubscription[0]));
cache.invalidateUserRootCategory(users.toArray(new User[0])); cache.invalidateUserRootCategory(users.toArray(new User[0]));
// notify over websocket notifyOverWebsocket(subscriptions, inserted);
subscriptions.forEach(sub -> webSocketSessions.sendMessage(sub.getUser(), WebSocketMessageBuilder.newFeedEntries(sub)));
} }
} }
@@ -163,7 +162,7 @@ public class FeedRefreshUpdater {
feed.setDisabledUntil(Instant.EPOCH); feed.setDisabledUntil(Instant.EPOCH);
} }
if (insertedAtLeastOneEntry) { if (inserted > 0) {
feedUpdated.mark(); feedUpdated.mark();
} }
@@ -172,6 +171,10 @@ public class FeedRefreshUpdater {
return processed; return processed;
} }
private void notifyOverWebsocket(List<FeedSubscription> subscriptions, long inserted) {
subscriptions.forEach(sub -> webSocketSessions.sendMessage(sub.getUser(), WebSocketMessageBuilder.newFeedEntries(sub, inserted)));
}
@AllArgsConstructor @AllArgsConstructor
private static class AddEntryResult { private static class AddEntryResult {
private final boolean processed; private final boolean processed;

View File

@@ -7,8 +7,8 @@ import lombok.experimental.UtilityClass;
@UtilityClass @UtilityClass
public class WebSocketMessageBuilder { public class WebSocketMessageBuilder {
public static String newFeedEntries(FeedSubscription subscription) { public static String newFeedEntries(FeedSubscription subscription, long count) {
return String.format("%s:%s", "new-feed-entries", subscription.getId()); return String.format("%s:%s:%s", "new-feed-entries", subscription.getId(), count);
} }
} }

View File

@@ -66,7 +66,7 @@ class WebSocketIT extends BaseIT {
Long subscriptionId = subscribe(getFeedUrl()); Long subscriptionId = subscribe(getFeedUrl());
Awaitility.await().atMost(15, TimeUnit.SECONDS).until(() -> messageRef.get() != null); 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());
} }
} }