feat: mute
This commit is contained in:
87
src/pages/secondary/MuteListPage/index.tsx
Normal file
87
src/pages/secondary/MuteListPage/index.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import MuteButton from '@/components/MuteButton'
|
||||
import Nip05 from '@/components/Nip05'
|
||||
import UserAvatar from '@/components/UserAvatar'
|
||||
import Username from '@/components/Username'
|
||||
import { useFetchProfile } from '@/hooks'
|
||||
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
|
||||
import { useMuteList } from '@/providers/MuteListProvider'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import NotFoundPage from '../NotFoundPage'
|
||||
|
||||
export default function MuteListPage({ index }: { index?: number }) {
|
||||
const { t } = useTranslation()
|
||||
const { profile } = useNostr()
|
||||
const { mutePubkeys } = useMuteList()
|
||||
const [visibleMutePubkeys, setVisibleMutePubkeys] = useState<string[]>([])
|
||||
const bottomRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
setVisibleMutePubkeys(mutePubkeys.slice(0, 10))
|
||||
}, [mutePubkeys])
|
||||
|
||||
useEffect(() => {
|
||||
const options = {
|
||||
root: null,
|
||||
rootMargin: '10px',
|
||||
threshold: 1
|
||||
}
|
||||
|
||||
const observerInstance = new IntersectionObserver((entries) => {
|
||||
if (entries[0].isIntersecting && mutePubkeys.length > visibleMutePubkeys.length) {
|
||||
setVisibleMutePubkeys((prev) => [
|
||||
...prev,
|
||||
...mutePubkeys.slice(prev.length, prev.length + 10)
|
||||
])
|
||||
}
|
||||
}, options)
|
||||
|
||||
const currentBottomRef = bottomRef.current
|
||||
if (currentBottomRef) {
|
||||
observerInstance.observe(currentBottomRef)
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (observerInstance && currentBottomRef) {
|
||||
observerInstance.unobserve(currentBottomRef)
|
||||
}
|
||||
}
|
||||
}, [visibleMutePubkeys, mutePubkeys])
|
||||
|
||||
if (!profile) {
|
||||
return <NotFoundPage />
|
||||
}
|
||||
|
||||
return (
|
||||
<SecondaryPageLayout
|
||||
index={index}
|
||||
title={t("username's muted", { username: profile.username })}
|
||||
displayScrollToTopButton
|
||||
>
|
||||
<div className="space-y-2 px-4">
|
||||
{visibleMutePubkeys.map((pubkey, index) => (
|
||||
<UserItem key={`${index}-${pubkey}`} pubkey={pubkey} />
|
||||
))}
|
||||
{mutePubkeys.length > visibleMutePubkeys.length && <div ref={bottomRef} />}
|
||||
</div>
|
||||
</SecondaryPageLayout>
|
||||
)
|
||||
}
|
||||
|
||||
function UserItem({ pubkey }: { pubkey: string }) {
|
||||
const { profile } = useFetchProfile(pubkey)
|
||||
const { nip05, about } = profile || {}
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 items-start">
|
||||
<UserAvatar userId={pubkey} className="shrink-0" />
|
||||
<div className="w-full overflow-hidden">
|
||||
<Username userId={pubkey} className="font-semibold truncate" skeletonClassName="h-4" />
|
||||
<Nip05 nip05={nip05} pubkey={pubkey} />
|
||||
<div className="truncate text-muted-foreground text-sm">{about}</div>
|
||||
</div>
|
||||
<MuteButton pubkey={pubkey} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import { useFetchRelayList } from '@/hooks/useFetchRelayList'
|
||||
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
|
||||
import {
|
||||
toFollowingList,
|
||||
toMuteList,
|
||||
toOthersRelaySettings,
|
||||
toProfileEditor,
|
||||
toRelaySettings
|
||||
@@ -21,10 +22,12 @@ import { generateImageByPubkey } from '@/lib/pubkey'
|
||||
import { SecondaryPageLink, useSecondaryPage } from '@/PageManager'
|
||||
import { useFeed } from '@/providers/FeedProvider'
|
||||
import { useFollowList } from '@/providers/FollowListProvider'
|
||||
import { useMuteList } from '@/providers/MuteListProvider'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import NotFoundPage from '../NotFoundPage'
|
||||
import ProfileOptions from '@/components/ProfileOptions'
|
||||
|
||||
export default function ProfilePage({ id, index }: { id?: string; index?: number }) {
|
||||
const { t } = useTranslation()
|
||||
@@ -41,6 +44,7 @@ export default function ProfilePage({ id, index }: { id?: string; index?: number
|
||||
)
|
||||
const { pubkey: accountPubkey } = useNostr()
|
||||
const { followings: selfFollowings } = useFollowList()
|
||||
const { mutePubkeys } = useMuteList()
|
||||
const { followings } = useFetchFollowings(profile?.pubkey)
|
||||
const isFollowingYou = useMemo(() => {
|
||||
return (
|
||||
@@ -103,6 +107,7 @@ export default function ProfilePage({ id, index }: { id?: string; index?: number
|
||||
) : (
|
||||
<FollowButton pubkey={pubkey} />
|
||||
)}
|
||||
<ProfileOptions pubkey={pubkey} />
|
||||
</div>
|
||||
<div className="pt-2">
|
||||
<div className="text-xl font-semibold">{username}</div>
|
||||
@@ -127,11 +132,22 @@ export default function ProfilePage({ id, index }: { id?: string; index?: number
|
||||
{relayList.originalRelays.length}
|
||||
<div className="text-muted-foreground">{t('Relays')}</div>
|
||||
</SecondaryPageLink>
|
||||
{isSelf && (
|
||||
<SecondaryPageLink to={toMuteList()} className="flex gap-1 hover:underline w-fit">
|
||||
{mutePubkeys.length}
|
||||
<div className="text-muted-foreground">{t('Muted')}</div>
|
||||
</SecondaryPageLink>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!isFetchingRelayInfo && (
|
||||
<NoteList filter={{ authors: [pubkey] }} relayUrls={relayUrls} className="mt-2" />
|
||||
<NoteList
|
||||
filter={{ authors: [pubkey] }}
|
||||
relayUrls={relayUrls}
|
||||
className="mt-2"
|
||||
filterMutedNotes={false}
|
||||
/>
|
||||
)}
|
||||
</SecondaryPageLayout>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user