feat: community mode (#738)
Co-authored-by: CXPLAY <62034099+cxplay@users.noreply.github.com>
This commit is contained in:
parent
686b1f9998
commit
ed8a22d5bc
21 changed files with 303 additions and 101 deletions
32
src/pages/primary/FollowingPage/index.tsx
Normal file
32
src/pages/primary/FollowingPage/index.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import FollowingFeed from '@/components/FollowingFeed'
|
||||
import PrimaryPageLayout from '@/layouts/PrimaryPageLayout'
|
||||
import { TPageRef } from '@/types'
|
||||
import { UsersRound } from 'lucide-react'
|
||||
import { forwardRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const FollowingPage = forwardRef<TPageRef>((_, ref) => {
|
||||
return (
|
||||
<PrimaryPageLayout
|
||||
pageName="following"
|
||||
titlebar={<FollowingPageTitlebar />}
|
||||
displayScrollToTopButton
|
||||
ref={ref}
|
||||
>
|
||||
<FollowingFeed />
|
||||
</PrimaryPageLayout>
|
||||
)
|
||||
})
|
||||
FollowingPage.displayName = 'FollowingPage'
|
||||
export default FollowingPage
|
||||
|
||||
function FollowingPageTitlebar() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className="flex h-full items-center gap-2 pl-3">
|
||||
<UsersRound />
|
||||
<div className="text-lg font-semibold">{t('Following')}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ import FeedSwitcher from '@/components/FeedSwitcher'
|
|||
import RelayIcon from '@/components/RelayIcon'
|
||||
import { Drawer, DrawerContent } from '@/components/ui/drawer'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import { IS_COMMUNITY_MODE, COMMUNITY_RELAY_SETS, COMMUNITY_RELAYS } from '@/constants'
|
||||
import { simplifyUrl } from '@/lib/url'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
|
||||
|
|
@ -15,6 +16,10 @@ export default function FeedButton({ className }: { className?: string }) {
|
|||
const { isSmallScreen } = useScreenSize()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
if (IS_COMMUNITY_MODE && COMMUNITY_RELAY_SETS.length + COMMUNITY_RELAYS.length <= 1) {
|
||||
return <FeedSwitcherTrigger className={className} />
|
||||
}
|
||||
|
||||
if (isSmallScreen) {
|
||||
return (
|
||||
<>
|
||||
|
|
@ -61,7 +66,8 @@ const FeedSwitcherTrigger = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivEle
|
|||
const { relaySets } = useFavoriteRelays()
|
||||
const activeRelaySet = useMemo(() => {
|
||||
return feedInfo?.feedType === 'relays' && feedInfo.id
|
||||
? relaySets.find((set) => set.id === feedInfo.id)
|
||||
? (relaySets.find((set) => set.id === feedInfo.id) ??
|
||||
COMMUNITY_RELAY_SETS.find((set) => set.id === feedInfo.id))
|
||||
: undefined
|
||||
}, [feedInfo, relaySets])
|
||||
const title = useMemo(() => {
|
||||
|
|
@ -78,7 +84,7 @@ const FeedSwitcherTrigger = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivEle
|
|||
return simplifyUrl(feedInfo?.id ?? '')
|
||||
}
|
||||
if (feedInfo?.feedType === 'relays') {
|
||||
return activeRelaySet?.name ?? activeRelaySet?.id
|
||||
return feedInfo.name ?? activeRelaySet?.name ?? activeRelaySet?.id
|
||||
}
|
||||
}, [feedInfo, activeRelaySet])
|
||||
|
||||
|
|
@ -92,15 +98,22 @@ const FeedSwitcherTrigger = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivEle
|
|||
return <Server />
|
||||
}, [feedInfo])
|
||||
|
||||
const clickable =
|
||||
!IS_COMMUNITY_MODE || COMMUNITY_RELAY_SETS.length + COMMUNITY_RELAYS.length > 1
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('clickable flex h-full items-center gap-2 rounded-xl px-3', className)}
|
||||
className={cn(
|
||||
'flex h-full items-center gap-2 rounded-xl px-3',
|
||||
clickable && 'clickable',
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
{icon}
|
||||
<div className="truncate text-lg font-semibold">{title}</div>
|
||||
<ChevronDown />
|
||||
{clickable && <ChevronDown />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
import NormalFeed from '@/components/NormalFeed'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { usePrimaryPage } from '@/PageManager'
|
||||
import { useFollowList } from '@/providers/FollowListProvider'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import client from '@/services/client.service'
|
||||
import { TFeedSubRequest } from '@/types'
|
||||
import { Compass, Search, UserPlus } from 'lucide-react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export default function FollowingFeed() {
|
||||
const { t } = useTranslation()
|
||||
const { pubkey } = useNostr()
|
||||
const { followingSet } = useFollowList()
|
||||
const { navigate } = usePrimaryPage()
|
||||
const [subRequests, setSubRequests] = useState<TFeedSubRequest[]>([])
|
||||
const [hasFollowings, setHasFollowings] = useState<boolean | null>(null)
|
||||
const [refreshCount, setRefreshCount] = useState(0)
|
||||
const initializedRef = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (initializedRef.current) return
|
||||
|
||||
async function init() {
|
||||
if (!pubkey) {
|
||||
setSubRequests([])
|
||||
setHasFollowings(null)
|
||||
return
|
||||
}
|
||||
|
||||
const followings = await client.fetchFollowings(pubkey)
|
||||
setHasFollowings(followings.length > 0)
|
||||
setSubRequests(await client.generateSubRequestsForPubkeys([pubkey, ...followings], pubkey))
|
||||
|
||||
if (followings.length) {
|
||||
initializedRef.current = true
|
||||
}
|
||||
}
|
||||
|
||||
init()
|
||||
}, [pubkey, followingSet, refreshCount])
|
||||
|
||||
// Show empty state when user has no followings
|
||||
if (hasFollowings === false && subRequests.length > 0) {
|
||||
return (
|
||||
<div className="flex min-h-[60vh] flex-col items-center justify-center px-6 text-center">
|
||||
<UserPlus size={64} className="mb-4 text-muted-foreground" strokeWidth={1.5} />
|
||||
<h2 className="mb-2 text-2xl font-semibold">{t('Welcome to Jumble!')}</h2>
|
||||
<p className="mb-6 max-w-md text-muted-foreground">
|
||||
{t(
|
||||
'Your feed is empty because you are not following anyone yet. Start by exploring interesting content and following users you like!'
|
||||
)}
|
||||
</p>
|
||||
<div className="flex w-full max-w-md flex-col gap-3 sm:flex-row">
|
||||
<Button size="lg" onClick={() => navigate('explore')} className="w-full">
|
||||
<Compass className="size-5" />
|
||||
{t('Explore')}
|
||||
</Button>
|
||||
<Button size="lg" variant="outline" onClick={() => navigate('search')} className="w-full">
|
||||
<Search className="size-5" />
|
||||
{t('Search Users')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<NormalFeed
|
||||
subRequests={subRequests}
|
||||
onRefresh={() => {
|
||||
initializedRef.current = false
|
||||
setRefreshCount((count) => count + 1)
|
||||
}}
|
||||
isMainFeed
|
||||
isPubkeyFeed
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { usePrimaryPage, useSecondaryPage } from '@/PageManager'
|
||||
import FollowingFeed from '@/components/FollowingFeed'
|
||||
import PostEditor from '@/components/PostEditor'
|
||||
import RelayInfo from '@/components/RelayInfo'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
|
@ -21,7 +22,6 @@ import {
|
|||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import FeedButton from './FeedButton'
|
||||
import FollowingFeed from './FollowingFeed'
|
||||
import PinnedFeed from './PinnedFeed'
|
||||
import RelaysFeed from './RelaysFeed'
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue