diff options
| author | Fuwn <[email protected]> | 2026-02-07 03:26:15 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-07 03:26:15 -0800 |
| commit | f2a5d1c04b9787bbd9f41af699345be6c0345ca8 (patch) | |
| tree | ffbbacd807f0d3d30efb7110058bd70d6404681e /apps/web/lib/stores | |
| parent | style: lowercase all user-facing strings and add custom eslint rule (diff) | |
| download | asa.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.ts | 55 |
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, }), } ) |