mirror of
https://github.com/dergigi/boris.git
synced 2025-12-18 23:24:22 +01:00
feat: reorganize bookmarks UI - add explore button and move refresh
- Move refresh button from top bar to end of bookmarks list - Show relative time of last fetch next to refresh button - Add 'Explore' button (fa-newspaper icon) to top bar that links to /explore - Track lastFetchTime in useBookmarksData hook - Better UX with explore more prominent and refresh less intrusive
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faChevronLeft, faBookmark, faSpinner, faList, faThLarge, faImage } from '@fortawesome/free-solid-svg-icons'
|
import { faChevronLeft, faBookmark, faSpinner, faList, faThLarge, faImage, faRotate } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { formatDistanceToNow } from 'date-fns'
|
||||||
import { RelayPool } from 'applesauce-relay'
|
import { RelayPool } from 'applesauce-relay'
|
||||||
import { Bookmark, IndividualBookmark } from '../types/bookmarks'
|
import { Bookmark, IndividualBookmark } from '../types/bookmarks'
|
||||||
import { BookmarkItem } from './BookmarkItem'
|
import { BookmarkItem } from './BookmarkItem'
|
||||||
@@ -22,6 +23,7 @@ interface BookmarkListProps {
|
|||||||
onOpenSettings: () => void
|
onOpenSettings: () => void
|
||||||
onRefresh?: () => void
|
onRefresh?: () => void
|
||||||
isRefreshing?: boolean
|
isRefreshing?: boolean
|
||||||
|
lastFetchTime?: number | null
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
relayPool: RelayPool | null
|
relayPool: RelayPool | null
|
||||||
settings?: UserSettings
|
settings?: UserSettings
|
||||||
@@ -39,6 +41,7 @@ export const BookmarkList: React.FC<BookmarkListProps> = ({
|
|||||||
onOpenSettings,
|
onOpenSettings,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
isRefreshing,
|
isRefreshing,
|
||||||
|
lastFetchTime,
|
||||||
loading = false,
|
loading = false,
|
||||||
relayPool,
|
relayPool,
|
||||||
settings
|
settings
|
||||||
@@ -102,8 +105,6 @@ export const BookmarkList: React.FC<BookmarkListProps> = ({
|
|||||||
onToggleCollapse={onToggleCollapse}
|
onToggleCollapse={onToggleCollapse}
|
||||||
onLogout={onLogout}
|
onLogout={onLogout}
|
||||||
onOpenSettings={onOpenSettings}
|
onOpenSettings={onOpenSettings}
|
||||||
onRefresh={onRefresh}
|
|
||||||
isRefreshing={isRefreshing}
|
|
||||||
relayPool={relayPool}
|
relayPool={relayPool}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -130,6 +131,34 @@ export const BookmarkList: React.FC<BookmarkListProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{onRefresh && (
|
||||||
|
<div className="refresh-section" style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: '0.5rem',
|
||||||
|
padding: '1rem',
|
||||||
|
marginTop: '1rem',
|
||||||
|
borderTop: '1px solid var(--border-color)',
|
||||||
|
fontSize: '0.85rem',
|
||||||
|
color: 'var(--text-secondary)'
|
||||||
|
}}>
|
||||||
|
<IconButton
|
||||||
|
icon={faRotate}
|
||||||
|
onClick={onRefresh}
|
||||||
|
title="Refresh bookmarks"
|
||||||
|
ariaLabel="Refresh bookmarks"
|
||||||
|
variant="ghost"
|
||||||
|
disabled={isRefreshing}
|
||||||
|
spin={isRefreshing}
|
||||||
|
/>
|
||||||
|
{lastFetchTime && (
|
||||||
|
<span>
|
||||||
|
Updated {formatDistanceToNow(lastFetchTime, { addSuffix: true })}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="view-mode-controls">
|
<div className="view-mode-controls">
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
|
|||||||
setHighlightsLoading,
|
setHighlightsLoading,
|
||||||
followedPubkeys,
|
followedPubkeys,
|
||||||
isRefreshing,
|
isRefreshing,
|
||||||
|
lastFetchTime,
|
||||||
handleFetchHighlights,
|
handleFetchHighlights,
|
||||||
handleRefreshAll
|
handleRefreshAll
|
||||||
} = useBookmarksData({
|
} = useBookmarksData({
|
||||||
@@ -182,6 +183,7 @@ const Bookmarks: React.FC<BookmarksProps> = ({ relayPool, onLogout }) => {
|
|||||||
bookmarksLoading={bookmarksLoading}
|
bookmarksLoading={bookmarksLoading}
|
||||||
viewMode={viewMode}
|
viewMode={viewMode}
|
||||||
isRefreshing={isRefreshing}
|
isRefreshing={isRefreshing}
|
||||||
|
lastFetchTime={lastFetchTime}
|
||||||
onToggleSidebar={() => setIsCollapsed(!isCollapsed)}
|
onToggleSidebar={() => setIsCollapsed(!isCollapsed)}
|
||||||
onLogout={onLogout}
|
onLogout={onLogout}
|
||||||
onViewModeChange={setViewMode}
|
onViewModeChange={setViewMode}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { faChevronRight, faRightFromBracket, faRightToBracket, faUserCircle, faGear, faRotate, faHome, faPlus } from '@fortawesome/free-solid-svg-icons'
|
import { faChevronRight, faRightFromBracket, faRightToBracket, faUserCircle, faGear, faHome, faPlus, faNewspaper } from '@fortawesome/free-solid-svg-icons'
|
||||||
import { Hooks } from 'applesauce-react'
|
import { Hooks } from 'applesauce-react'
|
||||||
import { useEventModel } from 'applesauce-react/hooks'
|
import { useEventModel } from 'applesauce-react/hooks'
|
||||||
import { Models } from 'applesauce-core'
|
import { Models } from 'applesauce-core'
|
||||||
@@ -16,12 +16,10 @@ interface SidebarHeaderProps {
|
|||||||
onToggleCollapse: () => void
|
onToggleCollapse: () => void
|
||||||
onLogout: () => void
|
onLogout: () => void
|
||||||
onOpenSettings: () => void
|
onOpenSettings: () => void
|
||||||
onRefresh?: () => void
|
|
||||||
isRefreshing?: boolean
|
|
||||||
relayPool: RelayPool | null
|
relayPool: RelayPool | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const SidebarHeader: React.FC<SidebarHeaderProps> = ({ onToggleCollapse, onLogout, onOpenSettings, onRefresh, isRefreshing, relayPool }) => {
|
const SidebarHeader: React.FC<SidebarHeaderProps> = ({ onToggleCollapse, onLogout, onOpenSettings, relayPool }) => {
|
||||||
const [isConnecting, setIsConnecting] = useState(false)
|
const [isConnecting, setIsConnecting] = useState(false)
|
||||||
const [showAddModal, setShowAddModal] = useState(false)
|
const [showAddModal, setShowAddModal] = useState(false)
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
@@ -61,11 +59,6 @@ const SidebarHeader: React.FC<SidebarHeaderProps> = ({ onToggleCollapse, onLogou
|
|||||||
}
|
}
|
||||||
|
|
||||||
await createWebBookmark(url, title, description, tags, activeAccount, relayPool, RELAYS)
|
await createWebBookmark(url, title, description, tags, activeAccount, relayPool, RELAYS)
|
||||||
|
|
||||||
// Refresh bookmarks after creating
|
|
||||||
if (onRefresh) {
|
|
||||||
onRefresh()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const profileImage = getProfileImage()
|
const profileImage = getProfileImage()
|
||||||
@@ -101,6 +94,13 @@ const SidebarHeader: React.FC<SidebarHeaderProps> = ({ onToggleCollapse, onLogou
|
|||||||
ariaLabel="Home"
|
ariaLabel="Home"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
/>
|
/>
|
||||||
|
<IconButton
|
||||||
|
icon={faNewspaper}
|
||||||
|
onClick={() => navigate('/explore')}
|
||||||
|
title="Explore"
|
||||||
|
ariaLabel="Explore"
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={faGear}
|
icon={faGear}
|
||||||
onClick={onOpenSettings}
|
onClick={onOpenSettings}
|
||||||
@@ -108,17 +108,6 @@ const SidebarHeader: React.FC<SidebarHeaderProps> = ({ onToggleCollapse, onLogou
|
|||||||
ariaLabel="Settings"
|
ariaLabel="Settings"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
/>
|
/>
|
||||||
{onRefresh && (
|
|
||||||
<IconButton
|
|
||||||
icon={faRotate}
|
|
||||||
onClick={onRefresh}
|
|
||||||
title="Refresh bookmarks"
|
|
||||||
ariaLabel="Refresh bookmarks"
|
|
||||||
variant="ghost"
|
|
||||||
disabled={isRefreshing}
|
|
||||||
spin={isRefreshing}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{activeAccount && (
|
{activeAccount && (
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={faPlus}
|
icon={faPlus}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ interface ThreePaneLayoutProps {
|
|||||||
bookmarksLoading: boolean
|
bookmarksLoading: boolean
|
||||||
viewMode: ViewMode
|
viewMode: ViewMode
|
||||||
isRefreshing: boolean
|
isRefreshing: boolean
|
||||||
|
lastFetchTime?: number | null
|
||||||
onToggleSidebar: () => void
|
onToggleSidebar: () => void
|
||||||
onLogout: () => void
|
onLogout: () => void
|
||||||
onViewModeChange: (mode: ViewMode) => void
|
onViewModeChange: (mode: ViewMode) => void
|
||||||
@@ -90,6 +91,7 @@ const ThreePaneLayout: React.FC<ThreePaneLayoutProps> = (props) => {
|
|||||||
onOpenSettings={props.onOpenSettings}
|
onOpenSettings={props.onOpenSettings}
|
||||||
onRefresh={props.onRefresh}
|
onRefresh={props.onRefresh}
|
||||||
isRefreshing={props.isRefreshing}
|
isRefreshing={props.isRefreshing}
|
||||||
|
lastFetchTime={props.lastFetchTime}
|
||||||
loading={props.bookmarksLoading}
|
loading={props.bookmarksLoading}
|
||||||
relayPool={props.relayPool}
|
relayPool={props.relayPool}
|
||||||
settings={props.settings}
|
settings={props.settings}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export const useBookmarksData = ({
|
|||||||
const [highlightsLoading, setHighlightsLoading] = useState(true)
|
const [highlightsLoading, setHighlightsLoading] = useState(true)
|
||||||
const [followedPubkeys, setFollowedPubkeys] = useState<Set<string>>(new Set())
|
const [followedPubkeys, setFollowedPubkeys] = useState<Set<string>>(new Set())
|
||||||
const [isRefreshing, setIsRefreshing] = useState(false)
|
const [isRefreshing, setIsRefreshing] = useState(false)
|
||||||
|
const [lastFetchTime, setLastFetchTime] = useState<number | null>(null)
|
||||||
|
|
||||||
const handleFetchContacts = useCallback(async () => {
|
const handleFetchContacts = useCallback(async () => {
|
||||||
if (!relayPool || !activeAccount) return
|
if (!relayPool || !activeAccount) return
|
||||||
@@ -93,6 +94,7 @@ export const useBookmarksData = ({
|
|||||||
await handleFetchBookmarks()
|
await handleFetchBookmarks()
|
||||||
await handleFetchHighlights()
|
await handleFetchHighlights()
|
||||||
await handleFetchContacts()
|
await handleFetchContacts()
|
||||||
|
setLastFetchTime(Date.now())
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to refresh data:', err)
|
console.error('Failed to refresh data:', err)
|
||||||
} finally {
|
} finally {
|
||||||
@@ -119,6 +121,7 @@ export const useBookmarksData = ({
|
|||||||
setHighlightsLoading,
|
setHighlightsLoading,
|
||||||
followedPubkeys,
|
followedPubkeys,
|
||||||
isRefreshing,
|
isRefreshing,
|
||||||
|
lastFetchTime,
|
||||||
handleFetchBookmarks,
|
handleFetchBookmarks,
|
||||||
handleFetchHighlights,
|
handleFetchHighlights,
|
||||||
handleRefreshAll
|
handleRefreshAll
|
||||||
|
|||||||
Reference in New Issue
Block a user