Browse Source

fix following and add mute display on following list

imwald
Silberengel 4 months ago
parent
commit
9e05ea1c09
  1. 51
      src/components/FollowButton/index.tsx
  2. 4
      src/components/MuteButton/index.tsx
  3. 6
      src/components/OthersRelayList/index.tsx
  4. 39
      src/pages/secondary/FollowPacksPage/index.tsx
  5. 8
      src/pages/secondary/FollowingListPage/index.tsx
  6. 2
      src/pages/secondary/MuteListPage/index.tsx
  7. 2
      src/pages/secondary/OthersRelaySettingsPage/index.tsx

51
src/components/FollowButton/index.tsx

@ -11,6 +11,7 @@ import { @@ -11,6 +11,7 @@ import {
} from '@/components/ui/alert-dialog'
import { Button } from '@/components/ui/button'
import { useFollowList } from '@/providers/FollowListProvider'
import { useMuteList } from '@/providers/MuteListProvider'
import { useNostr } from '@/providers/NostrProvider'
import { Loader } from 'lucide-react'
import { useMemo, useState } from 'react'
@ -21,9 +22,11 @@ export default function FollowButton({ pubkey }: { pubkey: string }) { @@ -21,9 +22,11 @@ export default function FollowButton({ pubkey }: { pubkey: string }) {
const { t } = useTranslation()
const { pubkey: accountPubkey, checkLogin } = useNostr()
const { followings, follow, unfollow } = useFollowList()
const { mutePubkeySet, unmutePubkey } = useMuteList()
const [updating, setUpdating] = useState(false)
const [hover, setHover] = useState(false)
const isFollowing = useMemo(() => followings.includes(pubkey), [followings, pubkey])
const isMuted = useMemo(() => mutePubkeySet.has(pubkey), [mutePubkeySet, pubkey])
if (!accountPubkey || (pubkey && pubkey === accountPubkey)) return null
@ -59,6 +62,54 @@ export default function FollowButton({ pubkey }: { pubkey: string }) { @@ -59,6 +62,54 @@ export default function FollowButton({ pubkey }: { pubkey: string }) {
})
}
const handleUnmute = async (e: React.MouseEvent) => {
e.stopPropagation()
checkLogin(async () => {
if (!isMuted) return
setUpdating(true)
try {
await unmutePubkey(pubkey)
toast.success(t('User unmuted'))
} catch (error) {
toast.error(t('Unmute failed') + ': ' + (error as Error).message)
} finally {
setUpdating(false)
}
})
}
// If following and muted, show "Muted" button instead of "Following"
if (isFollowing && isMuted) {
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
className="rounded-full min-w-28 max-w-full text-destructive whitespace-normal break-words px-3"
variant="secondary"
disabled={updating}
>
{updating ? <Loader className="animate-spin" /> : <span className="text-destructive text-center">{t('Muted')}</span>}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{t('Unmute user')}?</AlertDialogTitle>
<AlertDialogDescription>
{t('Are you sure you want to unmute this user? This will restore the follow button.')}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>{t('Cancel')}</AlertDialogCancel>
<AlertDialogAction onClick={handleUnmute}>
{t('Unmute')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}
return isFollowing ? (
<AlertDialog>
<AlertDialogTrigger asChild>

4
src/components/MuteButton/index.tsx

@ -64,12 +64,12 @@ export default function MuteButton({ pubkey }: { pubkey: string }) { @@ -64,12 +64,12 @@ export default function MuteButton({ pubkey }: { pubkey: string }) {
if (isMuted) {
return (
<Button
className="w-20 min-w-20 rounded-full"
className="rounded-full min-w-20 max-w-full text-destructive whitespace-normal break-words px-3"
variant="secondary"
onClick={handleUnmute}
disabled={updating || changing}
>
{updating ? <Loader className="animate-spin" /> : t('Unmute')}
{updating ? <Loader className="animate-spin" /> : <span className="text-destructive text-center">{t('Unmute')}</span>}
</Button>
)
}

6
src/components/OthersRelayList/index.tsx

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { useSecondaryPage } from '@/PageManager'
import { useSmartRelayNavigation } from '@/PageManager'
import { Badge } from '@/components/ui/badge'
import { useFetchRelayInfo, useFetchRelayList } from '@/hooks'
import { toRelay } from '@/lib/link'
@ -28,12 +28,12 @@ export default function OthersRelayList({ userId }: { userId: string }) { @@ -28,12 +28,12 @@ export default function OthersRelayList({ userId }: { userId: string }) {
function RelayItem({ relay }: { relay: TMailboxRelay }) {
const { t } = useTranslation()
const { push } = useSecondaryPage()
const { navigateToRelay } = useSmartRelayNavigation()
const { relayInfo } = useFetchRelayInfo(relay.url)
const { url, scope } = relay
return (
<div className="p-4 rounded-lg border clickable space-y-1" onClick={() => push(toRelay(url))}>
<div className="p-4 rounded-lg border clickable space-y-1" onClick={() => navigateToRelay(toRelay(url))}>
<RelaySimpleInfo relayInfo={relayInfo} />
<div className="flex gap-2">
{['both', 'read'].includes(scope) && (

39
src/pages/secondary/FollowPacksPage/index.tsx

@ -2,6 +2,7 @@ import { Button } from '@/components/ui/button' @@ -2,6 +2,7 @@ import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Skeleton } from '@/components/ui/skeleton'
import { useFollowList } from '@/providers/FollowListProvider'
import { useMuteList } from '@/providers/MuteListProvider'
import { useNostr } from '@/providers/NostrProvider'
import { getPubkeysFromPTags } from '@/lib/tag'
import { Event } from 'nostr-tools'
@ -22,6 +23,7 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba @@ -22,6 +23,7 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba
const { t } = useTranslation()
const { pubkey } = useNostr()
const { followings, follow } = useFollowList()
const { mutePubkeySet } = useMuteList()
const [packs, setPacks] = useState<Event[]>([])
const [isLoading, setIsLoading] = useState(true)
const [_followingPacks, setFollowingPacks] = useState<Set<string>>(new Set())
@ -77,22 +79,29 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba @@ -77,22 +79,29 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba
const packPubkeys = getPubkeysFromPTags(pack.tags)
const followingSet = new Set(followings)
const toFollow = packPubkeys.filter(p => !followingSet.has(p))
// Filter out users that are already followed OR muted
const toFollow = packPubkeys.filter(p => !followingSet.has(p) && !mutePubkeySet.has(p))
if (toFollow.length === 0) {
toast.info(t('You are already following all members of this pack'))
const mutedCount = packPubkeys.filter(p => mutePubkeySet.has(p) && !followingSet.has(p)).length
if (mutedCount > 0) {
toast.info(t('All available members are already followed or muted'))
} else {
toast.info(t('You are already following all members of this pack'))
}
return
}
try {
// Follow all pubkeys in the pack
// Follow all pubkeys in the pack (excluding muted users)
for (const pubkeyToFollow of toFollow) {
await follow(pubkeyToFollow)
}
toast.success(t('Followed {{count}} users', { count: toFollow.length }))
// Update followingPacks if all members are now followed
if (packPubkeys.every(p => followingSet.has(p) || toFollow.includes(p))) {
// Update followingPacks if all non-muted members are now followed
const nonMutedPackPubkeys = packPubkeys.filter(p => !mutePubkeySet.has(p))
if (nonMutedPackPubkeys.length > 0 && nonMutedPackPubkeys.every(p => followingSet.has(p) || toFollow.includes(p))) {
setFollowingPacks(prev => new Set([...prev, pack.id]))
}
} catch (error) {
@ -127,7 +136,7 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba @@ -127,7 +136,7 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba
if (!pubkey) {
return (
<SecondaryPageLayout ref={ref} index={index} title={t('Browse Follow Packs')} hideBackButton={hideTitlebar}>
<SecondaryPageLayout ref={ref} index={index} title={hideTitlebar ? undefined : t('Browse Follow Packs')} hideBackButton={hideTitlebar}>
<div className="flex flex-col items-center justify-center py-16">
<div className="text-lg font-semibold mb-2">{t('Please log in')}</div>
<div className="text-sm text-muted-foreground">{t('You need to be logged in to browse follow packs')}</div>
@ -137,7 +146,7 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba @@ -137,7 +146,7 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba
}
return (
<SecondaryPageLayout ref={ref} index={index} title={t('Browse Follow Packs')} hideBackButton={hideTitlebar} displayScrollToTopButton>
<SecondaryPageLayout ref={ref} index={index} title={hideTitlebar ? undefined : t('Browse Follow Packs')} hideBackButton={hideTitlebar} displayScrollToTopButton>
<div className="space-y-4 p-4">
{!isLoading && packs.length > 0 && (
<div className="flex items-center gap-2">
@ -178,8 +187,10 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba @@ -178,8 +187,10 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba
{filteredPacks.map((pack) => {
const packPubkeys = getPubkeysFromPTags(pack.tags)
const followingSet = new Set(followings)
const alreadyFollowingAll = packPubkeys.length > 0 && packPubkeys.every(p => followingSet.has(p))
const toFollowCount = packPubkeys.filter(p => !followingSet.has(p)).length
// Exclude muted users from calculations
const availablePubkeys = packPubkeys.filter(p => !mutePubkeySet.has(p))
const alreadyFollowingAll = availablePubkeys.length > 0 && availablePubkeys.every(p => followingSet.has(p))
const toFollowCount = availablePubkeys.filter(p => !followingSet.has(p)).length
return (
<Card key={pack.id}>
@ -192,12 +203,12 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba @@ -192,12 +203,12 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba
<CardContent className="space-y-4">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Users className="size-4" />
<span>{t('{{count}} profiles', { count: packPubkeys.length })}</span>
<span>{t('{{count}} profiles', { count: availablePubkeys.length })}</span>
</div>
{packPubkeys.length > 0 && (
{availablePubkeys.length > 0 && (
<div className="flex -space-x-2">
{packPubkeys.slice(0, 5).map((pubkey) => (
{availablePubkeys.slice(0, 5).map((pubkey) => (
<SimpleUserAvatar
key={pubkey}
userId={pubkey}
@ -205,9 +216,9 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba @@ -205,9 +216,9 @@ const FollowPacksPage = forwardRef<HTMLDivElement, { index?: number; hideTitleba
className="border-2 border-background"
/>
))}
{packPubkeys.length > 5 && (
{availablePubkeys.length > 5 && (
<div className="size-8 rounded-full border-2 border-background bg-muted flex items-center justify-center text-xs">
+{packPubkeys.length - 5}
+{availablePubkeys.length - 5}
</div>
)}
</div>

8
src/pages/secondary/FollowingListPage/index.tsx

@ -14,9 +14,11 @@ const FollowingListPage = forwardRef(({ id, index, hideTitlebar = false }: { id? @@ -14,9 +14,11 @@ const FollowingListPage = forwardRef(({ id, index, hideTitlebar = false }: { id?
ref={ref}
index={index}
title={
profile?.username
? t("username's following", { username: profile.username })
: t('Following')
hideTitlebar
? undefined
: profile?.username
? t("username's following", { username: profile.username })
: t('Following')
}
hideBackButton={hideTitlebar}
displayScrollToTopButton

2
src/pages/secondary/MuteListPage/index.tsx

@ -60,7 +60,7 @@ const MuteListPage = forwardRef(({ index, hideTitlebar = false }: { index?: numb @@ -60,7 +60,7 @@ const MuteListPage = forwardRef(({ index, hideTitlebar = false }: { index?: numb
<SecondaryPageLayout
ref={ref}
index={index}
title={t("username's muted", { username: profile.username })}
title={hideTitlebar ? undefined : t("username's muted", { username: profile.username })}
hideBackButton={hideTitlebar}
displayScrollToTopButton
>

2
src/pages/secondary/OthersRelaySettingsPage/index.tsx

@ -16,7 +16,7 @@ const RelaySettingsPage = forwardRef(({ id, index, hideTitlebar = false }: { id? @@ -16,7 +16,7 @@ const RelaySettingsPage = forwardRef(({ id, index, hideTitlebar = false }: { id?
<SecondaryPageLayout
ref={ref}
index={index}
title={t("username's used relays", { username: profile.username })}
title={hideTitlebar ? undefined : t("username's used relays", { username: profile.username })}
hideBackButton={hideTitlebar}
>
<div className="px-4 pt-3">

Loading…
Cancel
Save