mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
the "new-feed-entries" websocket event no longer needs to reload the entire tree
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public class FeedRefreshUpdater {
|
||||
|
||||
public boolean update(Feed feed, List<Entry> entries) {
|
||||
boolean processed = true;
|
||||
boolean insertedAtLeastOneEntry = false;
|
||||
long inserted = 0;
|
||||
|
||||
if (!entries.isEmpty()) {
|
||||
Set<String> 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<User> 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<FeedSubscription> subscriptions, long inserted) {
|
||||
subscriptions.forEach(sub -> webSocketSessions.sendMessage(sub.getUser(), WebSocketMessageBuilder.newFeedEntries(sub, inserted)));
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
private static class AddEntryResult {
|
||||
private final boolean processed;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user