Browse Source

expand home feed refresh functionality

make top bar fully visible on mobile
imwald
Silberengel 4 months ago
parent
commit
fb681b2c0e
  1. 2
      src/components/HideUntrustedContentButton/index.tsx
  2. 8
      src/components/KindFilter/index.tsx
  3. 34
      src/components/NormalFeed/index.tsx
  4. 18
      src/components/NoteInteractions/ReplySort.tsx
  5. 9
      src/components/NoteInteractions/Tabs.tsx
  6. 4
      src/components/NoteInteractions/index.tsx
  7. 2
      src/components/RefreshButton/index.tsx
  8. 8
      src/components/Tabs/index.tsx

2
src/components/HideUntrustedContentButton/index.tsx

@ -40,7 +40,7 @@ export default function HideUntrustedContentButton({ @@ -40,7 +40,7 @@ export default function HideUntrustedContentButton({
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="ghost" size={size}>
<Button variant="ghost" size={size} className="[&_svg]:size-3.5">
{enabled ? (
<ShieldCheck className="text-green-400" />
) : (

8
src/components/KindFilter/index.tsx

@ -79,7 +79,7 @@ export default function KindFilter({ @@ -79,7 +79,7 @@ export default function KindFilter({
variant="ghost"
size="titlebar-icon"
className={cn(
'relative w-fit px-3 focus:text-foreground',
'relative w-fit px-2 h-8 text-xs focus:text-foreground',
!isDifferentFromSaved && 'text-muted-foreground'
)}
onClick={() => {
@ -88,10 +88,10 @@ export default function KindFilter({ @@ -88,10 +88,10 @@ export default function KindFilter({
}
}}
>
<ListFilter size={16} />
{t('Filter')}
<ListFilter className="size-2.5" />
<span className="ml-1 text-xs">{t('Filter')}</span>
{isDifferentFromSaved && (
<div className="absolute size-2 rounded-full bg-primary left-7 top-2 ring-2 ring-background" />
<div className="absolute size-1.5 rounded-full bg-primary left-6 top-1.5 ring-1 ring-background" />
)}
</Button>
)

34
src/components/NormalFeed/index.tsx

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
import NoteList, { TNoteListRef } from '@/components/NoteList'
import Tabs from '@/components/Tabs'
import logger from '@/lib/logger'
import { isTouchDevice } from '@/lib/utils'
import { useKindFilter } from '@/providers/KindFilterProvider'
import { useUserTrust } from '@/providers/UserTrustProvider'
import storage from '@/services/local-storage.service'
@ -38,7 +37,6 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -38,7 +37,6 @@ const NormalFeed = forwardRef<TNoteListRef, {
const storedMode = storage.getNoteListMode()
return storedMode || 'posts'
})
const supportTouch = useMemo(() => isTouchDevice(), [])
const internalNoteListRef = useRef<TNoteListRef>(null)
const noteListRef = ref || internalNoteListRef
const [showRssFeed, setShowRssFeed] = useState(() => storage.getShowRssFeed())
@ -117,18 +115,10 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -117,18 +115,10 @@ const NormalFeed = forwardRef<TNoteListRef, {
handleListModeChange(tab)
}}
options={
activeTab !== 'rss' ? (
<>
{!supportTouch && <RefreshButton onClick={() => {
if (noteListRef && typeof noteListRef !== 'function') {
noteListRef.current?.refresh()
}
}} />}
<KindFilter showKinds={temporaryShowKinds} onShowKindsChange={handleShowKindsChange} />
</>
) : (
<>
{!supportTouch && <RefreshButton onClick={() => {
<>
<RefreshButton onClick={() => {
if (activeTab === 'rss') {
// Refresh RSS feeds
// Get feed URLs from event or use default
let feedUrls: string[] = DEFAULT_RSS_FEEDS
if (pubkey && rssFeedListEvent) {
@ -150,7 +140,7 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -150,7 +140,7 @@ const NormalFeed = forwardRef<TNoteListRef, {
}
// Trigger background refresh and UI update
logger.info('[NormalFeed] Manual refresh: triggering background refresh', { feedCount: feedUrls.length })
logger.info('[NormalFeed] Manual refresh: triggering RSS background refresh', { feedCount: feedUrls.length })
// Start background refresh (don't wait for it)
rssFeedService.backgroundRefreshFeeds(feedUrls).catch(err => {
logger.error('[NormalFeed] Manual refresh: background refresh failed', { error: err })
@ -163,9 +153,17 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -163,9 +153,17 @@ const NormalFeed = forwardRef<TNoteListRef, {
}
// Also force re-render by updating key
setRssRefreshKey(prev => prev + 1)
}} />}
</>
)
} else {
// Refresh Notes/Replies
if (noteListRef && typeof noteListRef !== 'function') {
noteListRef.current?.refresh()
}
}
}} />
{activeTab !== 'rss' && (
<KindFilter showKinds={temporaryShowKinds} onShowKindsChange={handleShowKindsChange} />
)}
</>
}
/>
{activeTab === 'rss' ? (

18
src/components/NoteInteractions/ReplySort.tsx

@ -21,23 +21,23 @@ export default function ReplySort({ selectedSort, onSortChange }: { selectedSort @@ -21,23 +21,23 @@ export default function ReplySort({ selectedSort, onSortChange }: { selectedSort
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="flex items-center gap-1 h-8 px-2">
<selectedOption.icon className="w-4 h-4" />
<span className="text-sm">{selectedOption.label}</span>
<ChevronDown className="w-4 h-4" />
<Button variant="outline" className="flex items-center gap-1 h-8 px-2 text-xs [&_svg]:size-2">
<selectedOption.icon className="size-2" />
<span className="text-xs">{selectedOption.label}</span>
<ChevronDown className="size-2" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuContent align="start" className="min-w-32 p-0.5">
{sortOptions.map(option => (
<DropdownMenuItem
key={option.id}
onClick={() => onSortChange(option.id)}
className="flex items-center gap-2"
className="flex items-center gap-1.5 text-xs py-0.5 px-1.5 [&_svg]:size-2.5"
>
<option.icon className="w-4 h-4" />
<span>{option.label}</span>
<option.icon className="size-2.5" />
<span className="text-xs">{option.label}</span>
{option.id === selectedSort && (
<span className="ml-auto text-primary"></span>
<span className="ml-auto text-primary text-xs"></span>
)}
</DropdownMenuItem>
))}

9
src/components/NoteInteractions/Tabs.tsx

@ -45,7 +45,8 @@ export function Tabs({ @@ -45,7 +45,8 @@ export function Tabs({
// Calculate the indicator's top position relative to the container
// Position it at the bottom of the active tab's row
const relativeTop = tabTop - containerTop + offsetHeight
const padding = 32 // 16px padding on each side
// Responsive padding: smaller on mobile, larger on desktop
const padding = window.innerWidth < 640 ? 16 : window.innerWidth < 768 ? 32 : 48
setIndicatorStyle({
width: offsetWidth - padding,
@ -57,14 +58,14 @@ export function Tabs({ @@ -57,14 +58,14 @@ export function Tabs({
}, [selectedTab, visibleTabs])
return (
<div className="w-full">
<div ref={containerRef} className="flex flex-wrap relative gap-1">
<div className="w-full min-w-0">
<div ref={containerRef} className="flex relative gap-1 overflow-x-auto scrollbar-hide">
{visibleTabs.map((tab, index) => (
<div
key={tab.value}
ref={(el) => (tabRefs.current[index] = el)}
className={cn(
`text-center px-4 py-2 font-semibold whitespace-nowrap clickable cursor-pointer rounded-lg`,
`text-center py-2 px-2 sm:px-4 md:px-6 font-semibold whitespace-nowrap clickable cursor-pointer rounded-lg text-xs sm:text-sm md:text-base shrink-0`,
selectedTab === tab.value ? '' : 'text-muted-foreground'
)}
onClick={() => onTabChange(tab.value)}

4
src/components/NoteInteractions/index.tsx

@ -64,8 +64,8 @@ export default function NoteInteractions({ @@ -64,8 +64,8 @@ export default function NoteInteractions({
<Separator orientation="vertical" className="h-6" />
</>
)}
<div className="size-10 flex items-center justify-center">
<HideUntrustedContentButton type="interactions" />
<div className="size-8 flex items-center justify-center shrink-0">
<HideUntrustedContentButton type="interactions" size="icon" />
</div>
</div>
<Separator />

2
src/components/RefreshButton/index.tsx

@ -16,7 +16,7 @@ export function RefreshButton({ onClick }: { onClick: () => void }) { @@ -16,7 +16,7 @@ export function RefreshButton({ onClick }: { onClick: () => void }) {
onClick()
setTimeout(() => setRefreshing(false), 500)
}}
className="text-muted-foreground focus:text-foreground [&_svg]:size-4"
className="text-muted-foreground focus:text-foreground [&_svg]:size-3 h-8 px-2 text-xs"
>
<RefreshCcw className={cn(refreshing ? 'animate-spin' : '')} />
</Button>

8
src/components/Tabs/index.tsx

@ -127,14 +127,14 @@ export default function Tabs({ @@ -127,14 +127,14 @@ export default function Tabs({
deepBrowsing && lastScrollTop > threshold ? '-translate-y-[calc(100%+12rem)]' : ''
)}
>
<div className="flex-1 w-0">
<div ref={tabsContainerRef} className="flex flex-wrap relative gap-1">
<div className="flex-1 w-0 min-w-0">
<div ref={tabsContainerRef} className="flex relative gap-1 overflow-x-auto scrollbar-hide">
{tabs.map((tab, index) => (
<div
key={tab.value}
ref={(el) => (tabRefs.current[index] = el)}
className={cn(
`text-center py-2 px-6 font-semibold whitespace-nowrap clickable cursor-pointer rounded-lg`,
`text-center py-2 px-2 sm:px-4 md:px-6 font-semibold whitespace-nowrap clickable cursor-pointer rounded-lg text-xs sm:text-sm md:text-base shrink-0`,
value === tab.value ? '' : 'text-muted-foreground'
)}
onClick={() => {
@ -154,7 +154,7 @@ export default function Tabs({ @@ -154,7 +154,7 @@ export default function Tabs({
/>
</div>
</div>
{options && <div className="py-1 flex items-center">{options}</div>}
{options && <div className="py-1 flex items-center shrink-0 gap-1">{options}</div>}
</div>
)
}

Loading…
Cancel
Save