summaryrefslogtreecommitdiff
path: root/apps/web/lib/stores
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-07 03:26:15 -0800
committerFuwn <[email protected]>2026-02-07 03:26:15 -0800
commitf2a5d1c04b9787bbd9f41af699345be6c0345ca8 (patch)
treeffbbacd807f0d3d30efb7110058bd70d6404681e /apps/web/lib/stores
parentstyle: lowercase all user-facing strings and add custom eslint rule (diff)
downloadasa.news-f2a5d1c04b9787bbd9f41af699345be6c0345ca8.tar.xz
asa.news-f2a5d1c04b9787bbd9f41af699345be6c0345ca8.zip
feat: pre-ship polish — UI improvements, keyboard shortcuts, appearance settings
- Rename "muted keywords" to "muted phrases" throughout settings UI - Add header with navigation to auth pages (sign-in, sign-up, etc.) - Merge security tab (TOTP setup) into account settings tab - Fix TOTP name input truncation on Safari (w-64 → flex-1 min-w-0) - Add appearance settings: font size, time display format, entry images toggle, reading time toggle - Add keyboard shortcuts dialog (? key) with all keybindings documented - Add extended vim shortcuts: gg, G, n/N (next/prev unread), Ctrl+h/l (panel focus) - Add command palette shortcut (⌘K) to shortcuts dialog - Add icon URL fields for folders and custom feeds (DB + queries + settings UI) - Add data-has-unreads attribute for sidebar keyboard navigation - Fix SSR prerendering crash from Zustand persist and react-resizable-panels localStorage access - Add detail panel layout persistence via useDefaultLayout - Update marketing copy to advertise vim-like keyboard navigation
Diffstat (limited to 'apps/web/lib/stores')
-rw-r--r--apps/web/lib/stores/user-interface-store.ts55
1 files changed, 52 insertions, 3 deletions
diff --git a/apps/web/lib/stores/user-interface-store.ts b/apps/web/lib/stores/user-interface-store.ts
index 468542d..5890167 100644
--- a/apps/web/lib/stores/user-interface-store.ts
+++ b/apps/web/lib/stores/user-interface-store.ts
@@ -1,21 +1,24 @@
import { create } from "zustand"
-import { persist } from "zustand/middleware"
+import { persist, createJSONStorage } from "zustand/middleware"
type EntryListViewMode = "compact" | "comfortable" | "expanded"
type DisplayDensity = "compact" | "default" | "spacious"
+type FontSize = "small" | "default" | "large"
+
+type TimeDisplayFormat = "relative" | "absolute"
+
type FocusedPanel = "sidebar" | "entryList" | "detailPanel"
type SettingsTab =
| "subscriptions"
| "folders"
- | "muted-keywords"
+ | "muted-phrases"
| "custom-feeds"
| "import-export"
| "appearance"
| "account"
- | "security"
| "billing"
| "api"
| "danger"
@@ -34,6 +37,11 @@ interface UserInterfaceState {
activeSettingsTab: SettingsTab
showFeedFavicons: boolean
focusFollowsInteraction: boolean
+ fontSize: FontSize
+ timeDisplayFormat: TimeDisplayFormat
+ showEntryImages: boolean
+ showReadingTime: boolean
+ isShortcutsDialogOpen: boolean
expandedFolderIdentifiers: string[]
navigableEntryIdentifiers: string[]
@@ -51,6 +59,12 @@ interface UserInterfaceState {
setActiveSettingsTab: (tab: SettingsTab) => void
setShowFeedFavicons: (show: boolean) => void
setFocusFollowsInteraction: (enabled: boolean) => void
+ setFontSize: (size: FontSize) => void
+ setTimeDisplayFormat: (format: TimeDisplayFormat) => void
+ setShowEntryImages: (show: boolean) => void
+ setShowReadingTime: (show: boolean) => void
+ setShortcutsDialogOpen: (isOpen: boolean) => void
+ toggleShortcutsDialog: () => void
toggleFolderExpansion: (folderIdentifier: string) => void
setNavigableEntryIdentifiers: (identifiers: string[]) => void
}
@@ -71,6 +85,11 @@ export const useUserInterfaceStore = create<UserInterfaceState>()(
activeSettingsTab: "subscriptions",
showFeedFavicons: true,
focusFollowsInteraction: false,
+ fontSize: "default",
+ timeDisplayFormat: "relative",
+ showEntryImages: true,
+ showReadingTime: true,
+ isShortcutsDialogOpen: false,
expandedFolderIdentifiers: [],
navigableEntryIdentifiers: [],
@@ -107,6 +126,22 @@ export const useUserInterfaceStore = create<UserInterfaceState>()(
setFocusFollowsInteraction: (enabled) =>
set({ focusFollowsInteraction: enabled }),
+ setFontSize: (size) => set({ fontSize: size }),
+
+ setTimeDisplayFormat: (format) => set({ timeDisplayFormat: format }),
+
+ setShowEntryImages: (show) => set({ showEntryImages: show }),
+
+ setShowReadingTime: (show) => set({ showReadingTime: show }),
+
+ setShortcutsDialogOpen: (isOpen) =>
+ set({ isShortcutsDialogOpen: isOpen }),
+
+ toggleShortcutsDialog: () =>
+ set((state) => ({
+ isShortcutsDialogOpen: !state.isShortcutsDialogOpen,
+ })),
+
toggleFolderExpansion: (folderIdentifier) =>
set((state) => {
const current = state.expandedFolderIdentifiers
@@ -123,12 +158,26 @@ export const useUserInterfaceStore = create<UserInterfaceState>()(
}),
{
name: "asa-news-ui-preferences",
+ storage: createJSONStorage(() => {
+ if (typeof window === "undefined") {
+ return {
+ getItem: () => null,
+ setItem: () => {},
+ removeItem: () => {},
+ }
+ }
+ return localStorage
+ }),
partialize: (state) => ({
entryListViewMode: state.entryListViewMode,
displayDensity: state.displayDensity,
showFeedFavicons: state.showFeedFavicons,
focusFollowsInteraction: state.focusFollowsInteraction,
expandedFolderIdentifiers: state.expandedFolderIdentifiers,
+ fontSize: state.fontSize,
+ timeDisplayFormat: state.timeDisplayFormat,
+ showEntryImages: state.showEntryImages,
+ showReadingTime: state.showReadingTime,
}),
}
)