feat(bookmarks): add refresh button to sidebar header with loading state

This commit is contained in:
Gigi
2025-10-05 22:00:18 +01:00
parent 008c14c14a
commit aac4adeda6
4 changed files with 47 additions and 5 deletions

View File

@@ -17,6 +17,8 @@ interface BookmarkListProps {
onViewModeChange: (mode: ViewMode) => void
selectedUrl?: string
onOpenSettings: () => void
onRefresh?: () => void
isRefreshing?: boolean
}
export const BookmarkList: React.FC<BookmarkListProps> = ({
@@ -28,7 +30,9 @@ export const BookmarkList: React.FC<BookmarkListProps> = ({
viewMode,
onViewModeChange,
selectedUrl,
onOpenSettings
onOpenSettings,
onRefresh,
isRefreshing
}) => {
if (isCollapsed) {
// Check if the selected URL is in bookmarks
@@ -60,6 +64,8 @@ export const BookmarkList: React.FC<BookmarkListProps> = ({
viewMode={viewMode}
onViewModeChange={onViewModeChange}
onOpenSettings={onOpenSettings}
onRefresh={onRefresh}
isRefreshing={isRefreshing}
/>
{bookmarks.length === 0 ? (

View File

@@ -47,6 +47,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
mine: true
})
const [followedPubkeys, setFollowedPubkeys] = useState<Set<string>>(new Set())
const [isRefreshing, setIsRefreshing] = useState(false)
const activeAccount = Hooks.useActiveAccount()
const accountManager = Hooks.useAccountManager()
const eventStore = useEventStore()
@@ -132,6 +133,21 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
}
}
const handleRefreshBookmarks = async () => {
if (!relayPool || !activeAccount || isRefreshing) return
setIsRefreshing(true)
try {
await handleFetchBookmarks()
await handleFetchHighlights()
await handleFetchContacts()
} catch (err) {
console.error('Failed to refresh bookmarks:', err)
} finally {
setIsRefreshing(false)
}
}
// Classify highlights with levels based on user context
const classifiedHighlights = useMemo(() => {
return highlights.map(h => {
@@ -182,6 +198,8 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
setIsCollapsed(true)
setIsHighlightsCollapsed(true)
}}
onRefresh={handleRefreshBookmarks}
isRefreshing={isRefreshing}
/>
</div>
<div className="pane main">

View File

@@ -9,6 +9,8 @@ interface IconButtonProps {
ariaLabel?: string
variant?: 'primary' | 'success' | 'ghost'
size?: number
disabled?: boolean
spin?: boolean
}
const IconButton: React.FC<IconButtonProps> = ({
@@ -17,7 +19,9 @@ const IconButton: React.FC<IconButtonProps> = ({
title,
ariaLabel,
variant = 'ghost',
size = 33
size = 33,
disabled = false,
spin = false
}) => {
return (
<button
@@ -26,8 +30,9 @@ const IconButton: React.FC<IconButtonProps> = ({
title={title}
aria-label={ariaLabel || title}
style={{ width: size, height: size }}
disabled={disabled}
>
<FontAwesomeIcon icon={icon} />
<FontAwesomeIcon icon={icon} spin={spin} />
</button>
)
}

View File

@@ -1,6 +1,6 @@
import React, { useState } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faChevronRight, faRightFromBracket, faRightToBracket, faUser, faList, faThLarge, faImage, faGear } from '@fortawesome/free-solid-svg-icons'
import { faChevronRight, faRightFromBracket, faRightToBracket, faUser, faList, faThLarge, faImage, faGear, faRotate } from '@fortawesome/free-solid-svg-icons'
import { Hooks } from 'applesauce-react'
import { useEventModel } from 'applesauce-react/hooks'
import { Models } from 'applesauce-core'
@@ -14,9 +14,11 @@ interface SidebarHeaderProps {
viewMode: ViewMode
onViewModeChange: (mode: ViewMode) => void
onOpenSettings: () => void
onRefresh?: () => void
isRefreshing?: boolean
}
const SidebarHeader: React.FC<SidebarHeaderProps> = ({ onToggleCollapse, onLogout, viewMode, onViewModeChange, onOpenSettings }) => {
const SidebarHeader: React.FC<SidebarHeaderProps> = ({ onToggleCollapse, onLogout, viewMode, onViewModeChange, onOpenSettings, onRefresh, isRefreshing }) => {
const [isConnecting, setIsConnecting] = useState(false)
const activeAccount = Hooks.useActiveAccount()
const accountManager = Hooks.useAccountManager()
@@ -68,6 +70,17 @@ const SidebarHeader: React.FC<SidebarHeaderProps> = ({ onToggleCollapse, onLogou
<FontAwesomeIcon icon={faUser} />
)}
</div>
{onRefresh && (
<IconButton
icon={faRotate}
onClick={onRefresh}
title="Refresh bookmarks"
ariaLabel="Refresh bookmarks"
variant="ghost"
disabled={isRefreshing}
spin={isRefreshing}
/>
)}
<IconButton
icon={faGear}
onClick={onOpenSettings}