You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
4.0 KiB
117 lines
4.0 KiB
import RelaySimpleInfo, { RelaySimpleInfoSkeleton } from '@/components/RelaySimpleInfo' |
|
import { Button } from '@/components/ui/button' |
|
import { DEFAULT_FAVORITE_RELAYS } from '@/constants' |
|
import { useFetchRelayInfo } from '@/hooks' |
|
import { toRelay } from '@/lib/link' |
|
import { normalizeUrl, simplifyUrl } from '@/lib/url' |
|
import { usePrimaryPage, useSmartRelayNavigation } from '@/PageManager' |
|
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider' |
|
import { cn } from '@/lib/utils' |
|
import { Newspaper } from 'lucide-react' |
|
import { useMemo } from 'react' |
|
import { useTranslation } from 'react-i18next' |
|
|
|
function FavoriteRelayCard({ url }: { url: string }) { |
|
const { navigateToRelay } = useSmartRelayNavigation() |
|
const { relayInfo, isFetching } = useFetchRelayInfo(url) |
|
|
|
if (isFetching) { |
|
return ( |
|
<RelaySimpleInfoSkeleton className="h-full min-h-[5.5rem] rounded-lg border bg-card p-3 shadow-sm" /> |
|
) |
|
} |
|
|
|
if (!relayInfo) { |
|
return ( |
|
<button |
|
type="button" |
|
className={cn( |
|
'clickable flex h-full min-h-[5.5rem] min-w-[220px] max-w-[280px] shrink-0 flex-col justify-center rounded-lg border bg-card p-3 text-left shadow-sm', |
|
'transition-colors hover:bg-accent/40' |
|
)} |
|
onClick={() => navigateToRelay(toRelay(url))} |
|
> |
|
<div className="truncate font-mono text-sm font-semibold">{simplifyUrl(url)}</div> |
|
<div className="mt-1 line-clamp-2 text-xs text-muted-foreground">{url}</div> |
|
</button> |
|
) |
|
} |
|
|
|
return ( |
|
<RelaySimpleInfo |
|
relayInfo={relayInfo} |
|
className={cn( |
|
'clickable h-full min-h-[5.5rem] min-w-[220px] max-w-[280px] shrink-0 rounded-lg border bg-card p-3 shadow-sm', |
|
'transition-colors hover:bg-accent/40' |
|
)} |
|
onClick={(e) => { |
|
e.stopPropagation() |
|
navigateToRelay(toRelay(relayInfo.url)) |
|
}} |
|
/> |
|
) |
|
} |
|
|
|
/** |
|
* Horizontal strip of favorite relays (non-blocked), or {@link DEFAULT_FAVORITE_RELAYS} when none. |
|
*/ |
|
export default function ExploreFavoriteRelays() { |
|
const { t } = useTranslation() |
|
const { navigate } = usePrimaryPage() |
|
const { favoriteRelays, blockedRelays } = useFavoriteRelays() |
|
|
|
const blockedSet = useMemo( |
|
() => new Set(blockedRelays.map((b) => normalizeUrl(b) || b)), |
|
[blockedRelays] |
|
) |
|
|
|
const { urls, usingDefaults } = useMemo(() => { |
|
const visible = favoriteRelays.filter((r) => { |
|
const k = normalizeUrl(r) || r |
|
return k && !blockedSet.has(k) |
|
}) |
|
if (visible.length > 0) { |
|
return { urls: visible, usingDefaults: false } |
|
} |
|
const defaultsFiltered = DEFAULT_FAVORITE_RELAYS.filter((r) => { |
|
const k = normalizeUrl(r) || r |
|
return k && !blockedSet.has(k) |
|
}) |
|
return { |
|
urls: defaultsFiltered.length > 0 ? defaultsFiltered : DEFAULT_FAVORITE_RELAYS, |
|
usingDefaults: true |
|
} |
|
}, [favoriteRelays, blockedSet]) |
|
|
|
if (urls.length === 0) return null |
|
|
|
return ( |
|
<section className="min-w-0 px-2 pb-4 pt-1" aria-label={t('Favorite Relays')}> |
|
<div className="mb-2 flex flex-wrap items-center justify-between gap-2 px-2"> |
|
<div className="flex min-w-0 flex-wrap items-center gap-2"> |
|
<h2 className="text-base font-semibold tracking-tight">{t('Favorite Relays')}</h2> |
|
<Button |
|
type="button" |
|
variant="outline" |
|
size="sm" |
|
className="h-8 gap-1.5 px-2.5 font-medium" |
|
onClick={() => navigate('feed')} |
|
> |
|
<Newspaper className="size-4 shrink-0" strokeWidth={2.5} /> |
|
<span>{t('Favorites Feed')}</span> |
|
</Button> |
|
</div> |
|
{usingDefaults ? ( |
|
<span className="text-xs text-muted-foreground">{t('Using app default relays')}</span> |
|
) : null} |
|
</div> |
|
<div className="flex gap-3 overflow-x-auto overflow-y-hidden pb-4 pt-0.5 snap-x snap-mandatory [scrollbar-gutter:stable]"> |
|
{urls.map((url) => ( |
|
<div key={url} className="snap-start"> |
|
<FavoriteRelayCard url={url} /> |
|
</div> |
|
))} |
|
</div> |
|
</section> |
|
) |
|
}
|
|
|