From d4fa40900b399d3025f05eefa46acf230c7c42e3 Mon Sep 17 00:00:00 2001 From: codytseng Date: Thu, 27 Feb 2025 14:36:18 +0800 Subject: [PATCH] fix: close mentions dropdown on mobile --- src/components/PostEditor/Mentions.tsx | 78 ++++++++++++++------------ src/lib/event.ts | 78 +++++++++++++++----------- 2 files changed, 87 insertions(+), 69 deletions(-) diff --git a/src/components/PostEditor/Mentions.tsx b/src/components/PostEditor/Mentions.tsx index e8b9af34..726538e3 100644 --- a/src/components/PostEditor/Mentions.tsx +++ b/src/components/PostEditor/Mentions.tsx @@ -1,17 +1,10 @@ import { Button } from '@/components/ui/button' -import { - DropdownMenu, - DropdownMenuCheckboxItem, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger -} from '@/components/ui/dropdown-menu' +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' import { extractMentions } from '@/lib/event' import { useNostr } from '@/providers/NostrProvider' +import { Check } from 'lucide-react' import { Event } from 'nostr-tools' -import { useEffect, useState } from 'react' +import { HTMLAttributes, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { SimpleUserAvatar } from '../UserAvatar' import { SimpleUsername } from '../Username' @@ -70,8 +63,8 @@ export default function Mentions({ }, [pubkeys, relatedPubkeys, parentEventPubkey, addedPubkeys, removedPubkeys]) return ( - - + + - - -
- {t('Mentions')}: + + +
{parentEventPubkey && ( - + - + )} - {(pubkeys.length > 0 || relatedPubkeys.length > 0) && } {pubkeys.concat(relatedPubkeys).map((pubkey, index) => ( - { if (checked) { @@ -116,23 +106,37 @@ export default function Mentions({ className="font-semibold text-sm truncate" skeletonClassName="h-3" /> - + ))} - {(relatedPubkeys.length > 0 || pubkeys.length > 0) && ( - <> - - { - setAddedPubkeys([...relatedPubkeys]) - setRemovedPubkeys([]) - }} - > - {t('Select all')} - - - )}
- - +
+ + ) +} + +function PopoverCheckboxItem({ + children, + checked, + onCheckedChange, + disabled, + ...props +}: HTMLAttributes & { + disabled?: boolean + checked: boolean + onCheckedChange?: (checked: boolean) => void +}) { + return ( +
+ +
) } diff --git a/src/lib/event.ts b/src/lib/event.ts index b04fa7eb..1a213d70 100644 --- a/src/lib/event.ts +++ b/src/lib/event.ts @@ -171,25 +171,30 @@ export function getProfileFromProfileEvent(event: Event) { } export async function extractMentions(content: string, parentEvent?: Event) { - let parentEventPubkey: string | undefined - const pubkeySet = new Set() - const relatedPubkeySet = new Set() + const parentEventPubkey = parentEvent ? parentEvent.pubkey : undefined + const pubkeys: string[] = [] + const relatedPubkeys: string[] = [] const matches = content.match( /nostr:(npub1[a-z0-9]{58}|nprofile1[a-z0-9]+|note1[a-z0-9]{58}|nevent1[a-z0-9]+)/g ) + const addToSet = (arr: string[], pubkey: string) => { + if (pubkey === parentEventPubkey) return + if (!arr.includes(pubkey)) arr.push(pubkey) + } + for (const m of matches || []) { try { const id = m.split(':')[1] const { type, data } = nip19.decode(id) if (type === 'nprofile') { - pubkeySet.add(data.pubkey) + addToSet(pubkeys, data.pubkey) } else if (type === 'npub') { - pubkeySet.add(data) - } else if (['nevent', 'note', 'naddr'].includes(type)) { + addToSet(pubkeys, data) + } else if (['nevent', 'note'].includes(type)) { const event = await client.fetchEvent(id) if (event) { - pubkeySet.add(event.pubkey) + addToSet(pubkeys, event.pubkey) } } } catch (e) { @@ -198,41 +203,44 @@ export async function extractMentions(content: string, parentEvent?: Event) { } if (parentEvent) { - parentEventPubkey = parentEvent.pubkey parentEvent.tags.forEach(([tagName, tagValue]) => { if (['p', 'P'].includes(tagName) && !!tagValue) { - relatedPubkeySet.add(tagValue) + addToSet(relatedPubkeys, tagValue) } }) } - if (parentEventPubkey) { - pubkeySet.delete(parentEventPubkey) - relatedPubkeySet.delete(parentEventPubkey) - } - return { - pubkeys: Array.from(pubkeySet), - relatedPubkeys: Array.from(relatedPubkeySet).filter((p) => !pubkeySet.has(p)), + pubkeys, + relatedPubkeys: relatedPubkeys.filter((p) => !pubkeys.includes(p)), parentEventPubkey } } export async function extractRelatedEventIds(content: string, parentEvent?: Event) { - const relatedEventIdSet = new Set() - const quoteEventIdSet = new Set() + const relatedEventIds: string[] = [] + const quoteEventIds: string[] = [] let rootEventId: string | undefined let parentEventId: string | undefined const matches = content.match(/nostr:(note1[a-z0-9]{58}|nevent1[a-z0-9]+)/g) + const addToSet = (arr: string[], item: string) => { + if (!arr.includes(item)) arr.push(item) + } + + const removeFromSet = (arr: string[], item: string) => { + const index = arr.indexOf(item) + if (index !== -1) arr.splice(index, 1) + } + for (const m of matches || []) { try { const id = m.split(':')[1] const { type, data } = nip19.decode(id) if (type === 'nevent') { - quoteEventIdSet.add(data.id) + addToSet(quoteEventIds, data.id) } else if (type === 'note') { - quoteEventIdSet.add(data) + addToSet(quoteEventIds, data) } } catch (e) { console.error(e) @@ -240,12 +248,12 @@ export async function extractRelatedEventIds(content: string, parentEvent?: Even } if (parentEvent) { - relatedEventIdSet.add(parentEvent.id) + addToSet(relatedEventIds, parentEvent.id) parentEvent.tags.forEach((tag) => { if (isRootETag(tag)) { rootEventId = tag[1] } else if (tagNameEquals('e')(tag)) { - relatedEventIdSet.add(tag[1]) + addToSet(relatedEventIds, tag[1]) } }) if (rootEventId || isReplyNoteEvent(parentEvent)) { @@ -255,19 +263,22 @@ export async function extractRelatedEventIds(content: string, parentEvent?: Even } } - if (rootEventId) relatedEventIdSet.delete(rootEventId) - if (parentEventId) relatedEventIdSet.delete(parentEventId) - + if (rootEventId) { + removeFromSet(relatedEventIds, rootEventId) + } + if (parentEventId) { + removeFromSet(relatedEventIds, parentEventId) + } return { - otherRelatedEventIds: Array.from(relatedEventIdSet), - quoteEventIds: Array.from(quoteEventIdSet), + otherRelatedEventIds: relatedEventIds, + quoteEventIds, rootEventId, parentEventId } } export async function extractCommentMentions(content: string, parentEvent: Event) { - const quoteEventIdSet = new Set() + const quoteEventIds: string[] = [] const rootEventId = parentEvent.tags.find(tagNameEquals('E'))?.[1] ?? parentEvent.id const rootEventKind = parentEvent.tags.find(tagNameEquals('K'))?.[1] ?? parentEvent.kind const rootEventPubkey = parentEvent.tags.find(tagNameEquals('P'))?.[1] ?? parentEvent.pubkey @@ -275,16 +286,19 @@ export async function extractCommentMentions(content: string, parentEvent: Event const parentEventKind = parentEvent.kind const parentEventPubkey = parentEvent.pubkey - const matches = content.match(/nostr:(note1[a-z0-9]{58}|nevent1[a-z0-9]+)/g) + const addToSet = (arr: string[], item: string) => { + if (!arr.includes(item)) arr.push(item) + } + const matches = content.match(/nostr:(note1[a-z0-9]{58}|nevent1[a-z0-9]+)/g) for (const m of matches || []) { try { const id = m.split(':')[1] const { type, data } = nip19.decode(id) if (type === 'nevent') { - quoteEventIdSet.add(data.id) + addToSet(quoteEventIds, data.id) } else if (type === 'note') { - quoteEventIdSet.add(data) + addToSet(quoteEventIds, data) } } catch (e) { console.error(e) @@ -292,7 +306,7 @@ export async function extractCommentMentions(content: string, parentEvent: Event } return { - quoteEventIds: Array.from(quoteEventIdSet), + quoteEventIds, rootEventId, rootEventKind, rootEventPubkey,