summaryrefslogtreecommitdiff
path: root/apps/web/app/reader/_components/sidebar-content.tsx
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/app/reader/_components/sidebar-content.tsx
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/app/reader/_components/sidebar-content.tsx')
-rw-r--r--apps/web/app/reader/_components/sidebar-content.tsx22
1 files changed, 15 insertions, 7 deletions
diff --git a/apps/web/app/reader/_components/sidebar-content.tsx b/apps/web/app/reader/_components/sidebar-content.tsx
index ee5c873..be59390 100644
--- a/apps/web/app/reader/_components/sidebar-content.tsx
+++ b/apps/web/app/reader/_components/sidebar-content.tsx
@@ -96,7 +96,6 @@ export function SidebarContent() {
const focusedSidebarIndex = useUserInterfaceStore(
(state) => state.focusedSidebarIndex
)
-
function closeSidebarOnMobile() {
if (typeof window !== "undefined" && window.innerWidth < 768) {
toggleSidebar()
@@ -138,6 +137,7 @@ export function SidebarContent() {
<Link
href="/reader"
data-sidebar-nav-item
+ {...(totalUnreadCount > 0 ? { "data-has-unreads": "" } : {})}
onClick={closeSidebarOnMobile}
className={classNames(
NAVIGATION_LINK_CLASS,
@@ -189,7 +189,6 @@ export function SidebarContent() {
>
shares
</Link>
-
{customFeedsData && customFeedsData.length > 0 && (
<div className="mt-3 space-y-0.5">
{customFeedsData.map((customFeed) => (
@@ -200,13 +199,16 @@ export function SidebarContent() {
onClick={closeSidebarOnMobile}
className={classNames(
NAVIGATION_LINK_CLASS,
- "truncate pl-4 text-[0.85em]",
+ "flex items-center gap-2 truncate pl-4 text-[0.85em]",
activeCustomFeedIdentifier === customFeed.identifier &&
ACTIVE_LINK_CLASS,
sidebarFocusClass(focusedPanel, focusedSidebarIndex, navIndex++)
)}
>
- {customFeed.name}
+ {customFeed.iconUrl && (
+ <img src={customFeed.iconUrl} alt="" width={16} height={16} className="shrink-0" loading="lazy" />
+ )}
+ <span className="truncate">{customFeed.name}</span>
</Link>
))}
</div>
@@ -219,6 +221,7 @@ export function SidebarContent() {
key={subscription.subscriptionIdentifier}
href={`/reader?feed=${subscription.feedIdentifier}`}
data-sidebar-nav-item
+ {...((unreadCounts?.[subscription.feedIdentifier] ?? 0) > 0 ? { "data-has-unreads": "" } : {})}
onClick={closeSidebarOnMobile}
className={classNames(
NAVIGATION_LINK_CLASS,
@@ -268,6 +271,7 @@ export function SidebarContent() {
<div key={folder.folderIdentifier} className="mt-2">
<div
data-sidebar-nav-item
+ {...(folderUnreadCount > 0 ? { "data-has-unreads": "" } : {})}
className={classNames(
"flex w-full items-center gap-1 px-2 py-1",
sidebarFocusClass(focusedPanel, focusedSidebarIndex, folderNavIndex)
@@ -286,12 +290,15 @@ export function SidebarContent() {
href={`/reader?folder=${folder.folderIdentifier}`}
onClick={closeSidebarOnMobile}
className={classNames(
- "flex-1 truncate text-text-secondary transition-colors hover:text-text-primary",
+ "flex flex-1 items-center gap-2 truncate text-text-secondary transition-colors hover:text-text-primary",
activeFolderIdentifier === folder.folderIdentifier &&
"text-text-primary"
)}
>
- {folder.name}
+ {folder.iconUrl && (
+ <img src={folder.iconUrl} alt="" width={16} height={16} className="shrink-0" loading="lazy" />
+ )}
+ <span className="truncate">{folder.name}</span>
</Link>
<UnreadBadge count={folderUnreadCount} />
</div>
@@ -302,6 +309,7 @@ export function SidebarContent() {
key={subscription.subscriptionIdentifier}
href={`/reader?feed=${subscription.feedIdentifier}`}
data-sidebar-nav-item
+ {...((unreadCounts?.[subscription.feedIdentifier] ?? 0) > 0 ? { "data-has-unreads": "" } : {})}
onClick={closeSidebarOnMobile}
className={classNames(
NAVIGATION_LINK_CLASS,
@@ -338,7 +346,7 @@ export function SidebarContent() {
)
})}
- <div className="mt-3">
+ <div className="mt-3 space-y-0.5">
<button
type="button"
data-sidebar-nav-item