5 changed files with 200 additions and 76 deletions
@ -1,57 +1,138 @@ |
|||||||
import { Button } from '@/components/ui/button' |
import { Button } from '@/components/ui/button' |
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' |
import { |
||||||
|
DropdownMenu, |
||||||
|
DropdownMenuCheckboxItem, |
||||||
|
DropdownMenuContent, |
||||||
|
DropdownMenuItem, |
||||||
|
DropdownMenuLabel, |
||||||
|
DropdownMenuSeparator, |
||||||
|
DropdownMenuTrigger |
||||||
|
} from '@/components/ui/dropdown-menu' |
||||||
import { extractMentions } from '@/lib/event' |
import { extractMentions } from '@/lib/event' |
||||||
import { useNostr } from '@/providers/NostrProvider' |
import { useNostr } from '@/providers/NostrProvider' |
||||||
import { Event } from 'nostr-tools' |
import { Event } from 'nostr-tools' |
||||||
import { useEffect, useState } from 'react' |
import { useEffect, useState } from 'react' |
||||||
import UserAvatar from '../UserAvatar' |
|
||||||
import Username from '../Username' |
|
||||||
import { useTranslation } from 'react-i18next' |
import { useTranslation } from 'react-i18next' |
||||||
|
import { SimpleUserAvatar } from '../UserAvatar' |
||||||
|
import { SimpleUsername } from '../Username' |
||||||
|
|
||||||
export default function Mentions({ |
export default function Mentions({ |
||||||
content, |
content, |
||||||
|
mentions, |
||||||
|
setMentions, |
||||||
parentEvent |
parentEvent |
||||||
}: { |
}: { |
||||||
content: string |
content: string |
||||||
|
mentions: string[] |
||||||
|
setMentions: (mentions: string[]) => void |
||||||
parentEvent?: Event |
parentEvent?: Event |
||||||
}) { |
}) { |
||||||
const { t } = useTranslation() |
const { t } = useTranslation() |
||||||
const { pubkey } = useNostr() |
const { pubkey } = useNostr() |
||||||
const [pubkeys, setPubkeys] = useState<string[]>([]) |
const [pubkeys, setPubkeys] = useState<string[]>([]) |
||||||
|
const [relatedPubkeys, setRelatedPubkeys] = useState<string[]>([]) |
||||||
|
const [parentEventPubkey, setParentEventPubkey] = useState<string | undefined>() |
||||||
|
const [addedPubkeys, setAddedPubkeys] = useState<string[]>([]) |
||||||
|
const [removedPubkeys, setRemovedPubkeys] = useState<string[]>([]) |
||||||
|
|
||||||
useEffect(() => { |
useEffect(() => { |
||||||
extractMentions(content, parentEvent).then(({ pubkeys }) => |
extractMentions(content, parentEvent).then(({ pubkeys, relatedPubkeys, parentEventPubkey }) => { |
||||||
setPubkeys(pubkeys.filter((p) => p !== pubkey)) |
setPubkeys(pubkeys.filter((p) => p !== pubkey)) |
||||||
) |
setRelatedPubkeys(relatedPubkeys.filter((p) => p !== pubkey)) |
||||||
|
setParentEventPubkey(parentEventPubkey !== pubkey ? parentEventPubkey : undefined) |
||||||
|
const potentialMentions = [...pubkeys, ...relatedPubkeys] |
||||||
|
setAddedPubkeys((pubkeys) => { |
||||||
|
return pubkeys.filter((p) => potentialMentions.includes(p)) |
||||||
|
}) |
||||||
|
setRemovedPubkeys((pubkeys) => { |
||||||
|
return pubkeys.filter((p) => potentialMentions.includes(p)) |
||||||
|
}) |
||||||
|
}) |
||||||
}, [content, parentEvent, pubkey]) |
}, [content, parentEvent, pubkey]) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
const newMentions = [...pubkeys] |
||||||
|
addedPubkeys.forEach((pubkey) => { |
||||||
|
if (!newMentions.includes(pubkey) && pubkey !== parentEventPubkey) { |
||||||
|
newMentions.push(pubkey) |
||||||
|
} |
||||||
|
}) |
||||||
|
removedPubkeys.forEach((pubkey) => { |
||||||
|
const index = newMentions.indexOf(pubkey) |
||||||
|
if (index !== -1) { |
||||||
|
newMentions.splice(index, 1) |
||||||
|
} |
||||||
|
}) |
||||||
|
if (parentEventPubkey) { |
||||||
|
newMentions.push(parentEventPubkey) |
||||||
|
} |
||||||
|
setMentions(newMentions) |
||||||
|
}, [pubkeys, relatedPubkeys, parentEventPubkey, addedPubkeys, removedPubkeys]) |
||||||
|
|
||||||
return ( |
return ( |
||||||
<Popover> |
<DropdownMenu> |
||||||
<PopoverTrigger asChild> |
<DropdownMenuTrigger asChild> |
||||||
<Button |
<Button |
||||||
className="px-3" |
className="px-3" |
||||||
variant="ghost" |
variant="ghost" |
||||||
disabled={pubkeys.length === 0} |
disabled={pubkeys.length === 0 && relatedPubkeys.length === 0 && !parentEventPubkey} |
||||||
onClick={(e) => e.stopPropagation()} |
onClick={(e) => e.stopPropagation()} |
||||||
> |
> |
||||||
{t('Mentions')} {pubkeys.length > 0 && `(${pubkeys.length})`} |
{t('Mentions')} {mentions.length > 0 && `(${mentions.length})`} |
||||||
</Button> |
</Button> |
||||||
</PopoverTrigger> |
</DropdownMenuTrigger> |
||||||
<PopoverContent className="w-48"> |
<DropdownMenuContent className="w-48"> |
||||||
<div className="space-y-2"> |
<div className="space-y-2"> |
||||||
<div className="text-sm font-semibold">{t('Mentions')}:</div> |
<DropdownMenuLabel>{t('Mentions')}:</DropdownMenuLabel> |
||||||
{pubkeys.map((pubkey, index) => ( |
{parentEventPubkey && ( |
||||||
<div key={`${pubkey}-${index}`} className="flex gap-1 items-center"> |
<DropdownMenuCheckboxItem className="flex gap-1 items-center" checked disabled> |
||||||
<UserAvatar userId={pubkey} size="small" /> |
<SimpleUserAvatar userId={parentEventPubkey} size="small" /> |
||||||
<Username |
<SimpleUsername |
||||||
|
userId={parentEventPubkey} |
||||||
|
className="font-semibold text-sm truncate" |
||||||
|
skeletonClassName="h-3" |
||||||
|
/> |
||||||
|
</DropdownMenuCheckboxItem> |
||||||
|
)} |
||||||
|
{(pubkeys.length > 0 || relatedPubkeys.length > 0) && <DropdownMenuSeparator />} |
||||||
|
{pubkeys.concat(relatedPubkeys).map((pubkey, index) => ( |
||||||
|
<DropdownMenuCheckboxItem |
||||||
|
key={`${pubkey}-${index}`} |
||||||
|
className="flex gap-1 items-center cursor-pointer" |
||||||
|
checked={mentions.includes(pubkey)} |
||||||
|
onCheckedChange={(checked) => { |
||||||
|
if (checked) { |
||||||
|
setAddedPubkeys((pubkeys) => [...pubkeys, pubkey]) |
||||||
|
setRemovedPubkeys((pubkeys) => pubkeys.filter((p) => p !== pubkey)) |
||||||
|
} else { |
||||||
|
setRemovedPubkeys((pubkeys) => [...pubkeys, pubkey]) |
||||||
|
setAddedPubkeys((pubkeys) => pubkeys.filter((p) => p !== pubkey)) |
||||||
|
} |
||||||
|
}} |
||||||
|
> |
||||||
|
<SimpleUserAvatar userId={pubkey} size="small" /> |
||||||
|
<SimpleUsername |
||||||
userId={pubkey} |
userId={pubkey} |
||||||
className="font-semibold text-sm truncate" |
className="font-semibold text-sm truncate" |
||||||
skeletonClassName="h-3" |
skeletonClassName="h-3" |
||||||
/> |
/> |
||||||
</div> |
</DropdownMenuCheckboxItem> |
||||||
))} |
))} |
||||||
|
{(relatedPubkeys.length > 0 || pubkeys.length > 0) && ( |
||||||
|
<> |
||||||
|
<DropdownMenuSeparator /> |
||||||
|
<DropdownMenuItem |
||||||
|
onClick={() => { |
||||||
|
setAddedPubkeys([...relatedPubkeys]) |
||||||
|
setRemovedPubkeys([]) |
||||||
|
}} |
||||||
|
> |
||||||
|
{t('Select all')} |
||||||
|
</DropdownMenuItem> |
||||||
|
</> |
||||||
|
)} |
||||||
</div> |
</div> |
||||||
</PopoverContent> |
</DropdownMenuContent> |
||||||
</Popover> |
</DropdownMenu> |
||||||
) |
) |
||||||
} |
} |
||||||
|
|||||||
Loading…
Reference in new issue