diff --git a/src/components/FollowButton/index.tsx b/src/components/FollowButton/index.tsx index d48dd324..9f63676f 100644 --- a/src/components/FollowButton/index.tsx +++ b/src/components/FollowButton/index.tsx @@ -20,10 +20,10 @@ import { toast } from 'sonner' export default function FollowButton({ pubkey }: { pubkey: string }) { const { t } = useTranslation() const { pubkey: accountPubkey, checkLogin } = useNostr() - const { followings, follow, unfollow } = useFollowList() + const { followingSet, follow, unfollow } = useFollowList() const [updating, setUpdating] = useState(false) const [hover, setHover] = useState(false) - const isFollowing = useMemo(() => followings.includes(pubkey), [followings, pubkey]) + const isFollowing = useMemo(() => followingSet.has(pubkey), [followingSet, pubkey]) if (!accountPubkey || (pubkey && pubkey === accountPubkey)) return null diff --git a/src/components/FollowingBadge/index.tsx b/src/components/FollowingBadge/index.tsx new file mode 100644 index 00000000..d6d00c93 --- /dev/null +++ b/src/components/FollowingBadge/index.tsx @@ -0,0 +1,23 @@ +import { userIdToPubkey } from '@/lib/pubkey' +import { useFollowList } from '@/providers/FollowListProvider' +import { UserRoundCheck } from 'lucide-react' +import { useMemo } from 'react' +import { useTranslation } from 'react-i18next' + +export default function FollowingBadge({ pubkey, userId }: { pubkey?: string; userId?: string }) { + const { t } = useTranslation() + const { followingSet } = useFollowList() + const isFollowing = useMemo(() => { + if (pubkey) return followingSet.has(pubkey) + + return userId ? followingSet.has(userIdToPubkey(userId)) : false + }, [followingSet, pubkey, userId]) + + if (!isFollowing) return null + + return ( +
+ +
+ ) +} diff --git a/src/components/Note/index.tsx b/src/components/Note/index.tsx index b094ae50..56086259 100644 --- a/src/components/Note/index.tsx +++ b/src/components/Note/index.tsx @@ -10,6 +10,7 @@ import { useMemo, useState } from 'react' import AudioPlayer from '../AudioPlayer' import ClientTag from '../ClientTag' import Content from '../Content' +import FollowingBadge from '../FollowingBadge' import { FormattedTimestamp } from '../FormattedTimestamp' import Nip05 from '../Nip05' import NoteOptions from '../NoteOptions' @@ -28,9 +29,9 @@ import MutedNote from './MutedNote' import NsfwNote from './NsfwNote' import PictureNote from './PictureNote' import Poll from './Poll' +import RelayReview from './RelayReview' import UnknownNote from './UnknownNote' import VideoNote from './VideoNote' -import RelayReview from './RelayReview' export default function Note({ event, @@ -117,6 +118,7 @@ export default function Note({ className={`font-semibold flex truncate ${size === 'small' ? 'text-sm' : ''}`} skeletonClassName={size === 'small' ? 'h-3' : 'h-4'} /> +
diff --git a/src/components/PostEditor/PostTextarea/Mention/MentionList.tsx b/src/components/PostEditor/PostTextarea/Mention/MentionList.tsx index 536429fa..b0ed0d96 100644 --- a/src/components/PostEditor/PostTextarea/Mention/MentionList.tsx +++ b/src/components/PostEditor/PostTextarea/Mention/MentionList.tsx @@ -1,3 +1,4 @@ +import FollowingBadge from '@/components/FollowingBadge' import { ScrollArea } from '@/components/ui/scroll-area' import { formatNpub, userIdToPubkey } from '@/lib/pubkey' import { cn } from '@/lib/utils' @@ -87,7 +88,10 @@ const MentionList = forwardRef((props, ref)
- +
+ + +
diff --git a/src/components/Profile/Followings.tsx b/src/components/Profile/Followings.tsx index 07dca48b..95382017 100644 --- a/src/components/Profile/Followings.tsx +++ b/src/components/Profile/Followings.tsx @@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next' export default function Followings({ pubkey }: { pubkey: string }) { const { t } = useTranslation() const { pubkey: accountPubkey } = useNostr() - const { followings: selfFollowings } = useFollowList() + const { followingSet: selfFollowingSet } = useFollowList() const { followings, isFetching } = useFetchFollowings(pubkey) return ( @@ -18,7 +18,7 @@ export default function Followings({ pubkey }: { pubkey: string }) { className="flex gap-1 hover:underline w-fit items-center" > {accountPubkey === pubkey ? ( - selfFollowings.length + selfFollowingSet.size ) : isFetching ? ( ) : ( diff --git a/src/components/ProfileList/index.tsx b/src/components/ProfileList/index.tsx index 2d288893..6a255c9a 100644 --- a/src/components/ProfileList/index.tsx +++ b/src/components/ProfileList/index.tsx @@ -37,7 +37,7 @@ export default function ProfileList({ pubkeys }: { pubkeys: string[] }) { return (
{visiblePubkeys.map((pubkey, index) => ( - + ))} {pubkeys.length > visiblePubkeys.length &&
}
diff --git a/src/components/ProfileListBySearch/index.tsx b/src/components/ProfileListBySearch/index.tsx index 9c74bd2c..9f84a027 100644 --- a/src/components/ProfileListBySearch/index.tsx +++ b/src/components/ProfileListBySearch/index.tsx @@ -67,7 +67,7 @@ export function ProfileListBySearch({ search }: { search: string }) { return (
{Array.from(pubkeySet).map((pubkey, index) => ( - + ))} {hasMore && } {hasMore &&
} diff --git a/src/components/SearchBar/index.tsx b/src/components/SearchBar/index.tsx index e14e6568..dde72a5a 100644 --- a/src/components/SearchBar/index.tsx +++ b/src/components/SearchBar/index.tsx @@ -378,7 +378,12 @@ function ProfileItem({ className={cn('px-2 hover:bg-accent rounded-md cursor-pointer', selected && 'bg-accent')} onClick={onClick} > - +
) } diff --git a/src/components/UserItem/index.tsx b/src/components/UserItem/index.tsx index 785655ff..7dfc6280 100644 --- a/src/components/UserItem/index.tsx +++ b/src/components/UserItem/index.tsx @@ -3,29 +3,39 @@ import Nip05 from '@/components/Nip05' import UserAvatar from '@/components/UserAvatar' import Username from '@/components/Username' import { Skeleton } from '@/components/ui/skeleton' +import { userIdToPubkey } from '@/lib/pubkey' import { cn } from '@/lib/utils' +import { useMemo } from 'react' +import FollowingBadge from '../FollowingBadge' export default function UserItem({ - pubkey, + userId, hideFollowButton, + showFollowingBadge = false, className }: { - pubkey: string + userId: string hideFollowButton?: boolean + showFollowingBadge?: boolean className?: string }) { + const pubkey = useMemo(() => userIdToPubkey(userId), [userId]) + return (
- +
- - +
+ + {showFollowingBadge && } +
+
- {!hideFollowButton && } + {!hideFollowButton && }
) } diff --git a/src/providers/FollowListProvider.tsx b/src/providers/FollowListProvider.tsx index cb8c7053..b321098a 100644 --- a/src/providers/FollowListProvider.tsx +++ b/src/providers/FollowListProvider.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next' import { useNostr } from './NostrProvider' type TFollowListContext = { - followings: string[] + followingSet: Set follow: (pubkey: string) => Promise unfollow: (pubkey: string) => Promise } @@ -24,8 +24,8 @@ export const useFollowList = () => { export function FollowListProvider({ children }: { children: React.ReactNode }) { const { t } = useTranslation() const { pubkey: accountPubkey, followListEvent, publish, updateFollowListEvent } = useNostr() - const followings = useMemo( - () => (followListEvent ? getPubkeysFromPTags(followListEvent.tags) : []), + const followingSet = useMemo( + () => new Set(followListEvent ? getPubkeysFromPTags(followListEvent.tags) : []), [followListEvent] ) @@ -65,7 +65,7 @@ export function FollowListProvider({ children }: { children: React.ReactNode }) return (