diff --git a/commafeed-client/src/app/entries/thunks.ts b/commafeed-client/src/app/entries/thunks.ts
index 88c5abd1..3a85f601 100644
--- a/commafeed-client/src/app/entries/thunks.ts
+++ b/commafeed-client/src/app/entries/thunks.ts
@@ -3,6 +3,7 @@ import { client } from "app/client"
import { Constants } from "app/constants"
import { type EntrySource, type EntrySourceType, entriesSlice, setMarkAllAsReadConfirmationDialogOpen, setSearch } from "app/entries/slice"
import type { RootState } from "app/store"
+import { setHasNewEntries } from "app/tree/slice"
import { reloadTree } from "app/tree/thunks"
import type { Entry, MarkRequest, TagRequest } from "app/types"
import { reloadTags } from "app/user/thunks"
@@ -26,6 +27,9 @@ export const loadEntries = createAppAsyncThunk(
const state = thunkApi.getState()
const endpoint = getEndpoint(arg.source.type)
const result = await endpoint(buildGetEntriesPaginatedRequest(state, arg.source, 0))
+ if (arg.source.type === "feed") {
+ thunkApi.dispatch(setHasNewEntries({ feedId: +arg.source.id, value: false }))
+ }
return result.data
}
)
diff --git a/commafeed-client/src/app/tree/slice.ts b/commafeed-client/src/app/tree/slice.ts
index fd863350..4cf942ef 100644
--- a/commafeed-client/src/app/tree/slice.ts
+++ b/commafeed-client/src/app/tree/slice.ts
@@ -40,6 +40,15 @@ export const treeSlice = createSlice({
}
})
},
+ setHasNewEntries: (state, action: PayloadAction<{ feedId: number; value: boolean }>) => {
+ if (!state.rootCategory) return
+
+ visitCategoryTree(state.rootCategory, category => {
+ category.feeds = category.feeds.map(feed =>
+ feed.id === action.payload.feedId ? { ...feed, hasNewEntries: action.payload.value } : feed
+ )
+ })
+ },
},
extraReducers: builder => {
builder.addCase(reloadTree.fulfilled, (state, action) => {
@@ -65,4 +74,4 @@ export const treeSlice = createSlice({
},
})
-export const { setMobileMenuOpen, toggleSidebar, incrementUnreadCount } = treeSlice.actions
+export const { setMobileMenuOpen, toggleSidebar, incrementUnreadCount, setHasNewEntries } = treeSlice.actions
diff --git a/commafeed-client/src/app/tree/thunks.ts b/commafeed-client/src/app/tree/thunks.ts
index ac0d7a33..22ef4b8d 100644
--- a/commafeed-client/src/app/tree/thunks.ts
+++ b/commafeed-client/src/app/tree/thunks.ts
@@ -1,7 +1,7 @@
import { createAppAsyncThunk } from "app/async-thunk"
import { client } from "app/client"
import { redirectToCategory, redirectToFeed } from "app/redirect/thunks"
-import { incrementUnreadCount } from "app/tree/slice"
+import { incrementUnreadCount, setHasNewEntries } from "app/tree/slice"
import type { CollapseRequest, Subscription } from "app/types"
import { flattenCategoryTree, visitCategoryTree } from "app/utils"
@@ -11,7 +11,6 @@ export const collapseTreeCategory = createAppAsyncThunk(
"tree/category/collapse",
async (req: CollapseRequest) => await client.category.collapse(req).then(r => r.data)
)
-
export const selectNextUnreadTreeItem = createAppAsyncThunk(
"tree/selectNextUnreadItem",
(
@@ -75,6 +74,7 @@ export const newFeedEntriesDiscovered = createAppAsyncThunk(
amount,
})
)
+ thunkApi.dispatch(setHasNewEntries({ feedId, value: true }))
}
}
)
diff --git a/commafeed-client/src/app/types.ts b/commafeed-client/src/app/types.ts
index 68a2218d..db57202b 100644
--- a/commafeed-client/src/app/types.ts
+++ b/commafeed-client/src/app/types.ts
@@ -30,13 +30,17 @@ export interface Subscription {
filter?: string
}
+export interface TreeSubscription extends Subscription {
+ hasNewEntries?: boolean
+}
+
export interface Category {
id: string
parentId?: string
parentName?: string
name: string
children: Category[]
- feeds: Subscription[]
+ feeds: TreeSubscription[]
expanded: boolean
position: number
}
diff --git a/commafeed-client/src/components/sidebar/Tree.tsx b/commafeed-client/src/components/sidebar/Tree.tsx
index ccfa8dca..0d805737 100644
--- a/commafeed-client/src/components/sidebar/Tree.tsx
+++ b/commafeed-client/src/components/sidebar/Tree.tsx
@@ -11,7 +11,7 @@ import {
} from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { collapseTreeCategory } from "app/tree/thunks"
-import type { Category, Subscription } from "app/types"
+import type { Category, Subscription, TreeSubscription } from "app/types"
import { categoryUnreadCount, flattenCategoryTree } from "app/utils"
import { Loader } from "components/Loader"
import { OnDesktop } from "components/responsive/OnDesktop"
@@ -133,7 +133,7 @@ export function Tree() {
)
}
- const feedNode = (feed: Subscription, level = 0) => {
+ const feedNode = (feed: TreeSubscription, level = 0) => {
if (!isFeedDisplayed(feed)) return null
return (
@@ -148,6 +148,7 @@ export function Tree() {
hasError={feed.errorCount > errorThreshold}
onClick={feedClicked}
key={feed.id}
+ newMessages={feed.hasNewEntries}
/>
)
}
diff --git a/commafeed-client/src/components/sidebar/TreeNode.tsx b/commafeed-client/src/components/sidebar/TreeNode.tsx
index 26d55777..f61b6eb1 100644
--- a/commafeed-client/src/components/sidebar/TreeNode.tsx
+++ b/commafeed-client/src/components/sidebar/TreeNode.tsx
@@ -15,6 +15,7 @@ interface TreeNodeProps {
expanded?: boolean
level: number
hasError: boolean
+ newMessages?: boolean
onClick: (e: React.MouseEvent, id: string) => void
onIconClick?: (e: React.MouseEvent, id: string) => void
}
@@ -64,12 +65,12 @@ export function TreeNode(props: TreeNodeProps) {
hasError: props.hasError,
hasUnread: props.unread > 0,
})
+
return (
props.onClick(e, props.id)}
data-id={props.id}
data-type={props.type}
data-unread-count={props.unread}
@@ -80,7 +81,7 @@ export function TreeNode(props: TreeNodeProps) {
{props.name}
{!props.expanded && (
-
+
)}
diff --git a/commafeed-client/src/components/sidebar/UnreadCount.tsx b/commafeed-client/src/components/sidebar/UnreadCount.tsx
index 63e39235..a1e6e154 100644
--- a/commafeed-client/src/components/sidebar/UnreadCount.tsx
+++ b/commafeed-client/src/components/sidebar/UnreadCount.tsx
@@ -1,26 +1,28 @@
-import { Badge, Tooltip } from "@mantine/core"
+import { Badge, Indicator, Tooltip } from "@mantine/core"
import { Constants } from "app/constants"
import { tss } from "tss"
const useStyles = tss.create(() => ({
badge: {
width: "3.2rem",
- // for some reason, mantine Badge has "cursor: 'default'"
cursor: "pointer",
},
}))
-export function UnreadCount(props: { unreadCount: number }) {
+export function UnreadCount(props: { unreadCount: number; newMessages: boolean | undefined }) {
const { classes } = useStyles()
if (props.unreadCount <= 0) return null
const count = props.unreadCount >= 10000 ? "10k+" : props.unreadCount
+
return (
-
- {count}
-
+
+
+ {count}
+
+
)
}