summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-10 02:11:41 -0800
committerFuwn <[email protected]>2026-02-10 02:11:41 -0800
commit566aff236740d82def2a51c73642fdcbaf382931 (patch)
tree2d8c5e20071f4e25bb10ecb0d0c637ba0a1b6d9e /apps
parentfeat: add Go worker tests and include in CI (diff)
downloadasa.news-566aff236740d82def2a51c73642fdcbaf382931.tar.xz
asa.news-566aff236740d82def2a51c73642fdcbaf382931.zip
feat: add unread priority option to push unread entries to top
Adds a persisted appearance setting (disabled by default) that partitions the entry list into unread-first, preserving original order within each group.
Diffstat (limited to 'apps')
-rw-r--r--apps/web/app/reader/_components/entry-list.tsx15
-rw-r--r--apps/web/app/reader/settings/_components/appearance-settings.tsx21
-rw-r--r--apps/web/lib/stores/user-interface-store.ts7
3 files changed, 42 insertions, 1 deletions
diff --git a/apps/web/app/reader/_components/entry-list.tsx b/apps/web/app/reader/_components/entry-list.tsx
index cf75620..7786e55 100644
--- a/apps/web/app/reader/_components/entry-list.tsx
+++ b/apps/web/app/reader/_components/entry-list.tsx
@@ -72,7 +72,20 @@ export function EntryList({
(state) => state.setNavigableEntryIdentifiers
)
- const allEntries = data?.pages.flatMap((page) => page) ?? []
+ const prioritiseUnreadEntries = useUserInterfaceStore(
+ (state) => state.prioritiseUnreadEntries
+ )
+
+ const allEntries = useMemo(() => {
+ const flatEntries = data?.pages.flatMap((page) => page) ?? []
+
+ if (!prioritiseUnreadEntries) return flatEntries
+
+ const unreadEntries = flatEntries.filter((entry) => !entry.isRead)
+ const readEntries = flatEntries.filter((entry) => entry.isRead)
+
+ return [...unreadEntries, ...readEntries]
+ }, [data, prioritiseUnreadEntries])
const scrollContainerReference = useRef<HTMLDivElement>(null)
const firstEntryIdentifier = allEntries[0]?.entryIdentifier
diff --git a/apps/web/app/reader/settings/_components/appearance-settings.tsx b/apps/web/app/reader/settings/_components/appearance-settings.tsx
index 9d2d146..9e03702 100644
--- a/apps/web/app/reader/settings/_components/appearance-settings.tsx
+++ b/apps/web/app/reader/settings/_components/appearance-settings.tsx
@@ -80,6 +80,12 @@ export function AppearanceSettings() {
const setShowFoldersAboveFeeds = useUserInterfaceStore(
(state) => state.setShowFoldersAboveFeeds
)
+ const prioritiseUnreadEntries = useUserInterfaceStore(
+ (state) => state.prioritiseUnreadEntries
+ )
+ const setPrioritiseUnreadEntries = useUserInterfaceStore(
+ (state) => state.setPrioritiseUnreadEntries
+ )
const autoRefreshTimeline = useUserInterfaceStore(
(state) => state.autoRefreshTimeline
)
@@ -249,6 +255,21 @@ export function AppearanceSettings() {
<option value="absolute">absolute</option>
</select>
</div>
+ <div>
+ <h3 className="mb-2 text-text-primary">unread priority</h3>
+ <p className="mb-3 text-text-dim">
+ push unread articles to the top of the entry list
+ </p>
+ <label className="flex cursor-pointer items-center gap-2 text-text-primary">
+ <input
+ type="checkbox"
+ checked={prioritiseUnreadEntries}
+ onChange={(event) => setPrioritiseUnreadEntries(event.target.checked)}
+ className="accent-text-primary"
+ />
+ <span>prioritise unread entries</span>
+ </label>
+ </div>
</SettingsSection>
<SettingsSection title="sidebar">
diff --git a/apps/web/lib/stores/user-interface-store.ts b/apps/web/lib/stores/user-interface-store.ts
index 814b0d6..808273c 100644
--- a/apps/web/lib/stores/user-interface-store.ts
+++ b/apps/web/lib/stores/user-interface-store.ts
@@ -46,6 +46,7 @@ interface UserInterfaceState {
showFoldersAboveFeeds: boolean
showEntryFavicons: boolean
autoRefreshTimeline: boolean
+ prioritiseUnreadEntries: boolean
toolbarPosition: ToolbarPosition
isEntryListAtTop: boolean
isShortcutsDialogOpen: boolean
@@ -79,6 +80,7 @@ interface UserInterfaceState {
setShowFoldersAboveFeeds: (show: boolean) => void
setShowEntryFavicons: (show: boolean) => void
setAutoRefreshTimeline: (enabled: boolean) => void
+ setPrioritiseUnreadEntries: (enabled: boolean) => void
setToolbarPosition: (position: ToolbarPosition) => void
setIsEntryListAtTop: (isAtTop: boolean) => void
setShortcutsDialogOpen: (isOpen: boolean) => void
@@ -112,6 +114,7 @@ export const useUserInterfaceStore = create<UserInterfaceState>()(
showFoldersAboveFeeds: false,
showEntryFavicons: false,
autoRefreshTimeline: false,
+ prioritiseUnreadEntries: false,
toolbarPosition: "top",
isEntryListAtTop: true,
isShortcutsDialogOpen: false,
@@ -176,6 +179,9 @@ export const useUserInterfaceStore = create<UserInterfaceState>()(
setAutoRefreshTimeline: (enabled) =>
set({ autoRefreshTimeline: enabled }),
+ setPrioritiseUnreadEntries: (enabled) =>
+ set({ prioritiseUnreadEntries: enabled }),
+
setToolbarPosition: (position) => set({ toolbarPosition: position }),
setIsEntryListAtTop: (isAtTop) => set({ isEntryListAtTop: isAtTop }),
@@ -233,6 +239,7 @@ export const useUserInterfaceStore = create<UserInterfaceState>()(
showFoldersAboveFeeds: state.showFoldersAboveFeeds,
showEntryFavicons: state.showEntryFavicons,
autoRefreshTimeline: state.autoRefreshTimeline,
+ prioritiseUnreadEntries: state.prioritiseUnreadEntries,
toolbarPosition: state.toolbarPosition,
}),
}