diff --git a/src/components/Column.tsx b/src/components/Column.tsx index 5949e6c..e84074e 100644 --- a/src/components/Column.tsx +++ b/src/components/Column.tsx @@ -42,7 +42,7 @@ const Column: Component = (props) => {
= (props) => { 'sm:w-[270px]': width() === 'narrow', }} > -
- {/* 🏠 */} - {props.name} -
-
{props.children}
- + +
+ {/* 🏠 */} + {props.name} +
+
{props.children}
+ + } + > {(timeline) => ( -
+
+ + ); +}; + +const MenuDisplay: Component = (props) => { + return ( +
    + + {(item) => } + +
+ ); +}; + +const ContextMenu: Component = (props) => { + let menuRef: HTMLDivElement | undefined; + + const [isOpen, setIsOpen] = createSignal(false); + + const handleClickOutside = (ev: MouseEvent) => { + const target = ev.target as HTMLElement; + if (target != null && !menuRef?.contains(target)) { + setIsOpen(false); + } + }; + const addClickOutsideHandler = () => { + document.addEventListener('mousedown', handleClickOutside); + }; + const removeClickOutsideHandler = () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + + const handleClick: JSX.EventHandler = (ev) => { + if (menuRef == null) return; + + const buttonRect = ev.target.getBoundingClientRect(); + menuRef.style.left = `${buttonRect.left}px`; + menuRef.style.top = `${buttonRect.top + buttonRect.height}px`; + + setIsOpen(true); + }; + + createEffect(() => { + if (isOpen()) { + addClickOutsideHandler(); + } else { + removeClickOutsideHandler(); + } + }); + + onCleanup(() => removeClickOutsideHandler()); + + return ( +
+ +
+ setIsOpen(false)} /> +
+
+ ); +}; + +export default ContextMenu; diff --git a/src/components/textNote/TextNoteDisplay.tsx b/src/components/textNote/TextNoteDisplay.tsx index 20234c0..81397ea 100644 --- a/src/components/textNote/TextNoteDisplay.tsx +++ b/src/components/textNote/TextNoteDisplay.tsx @@ -1,5 +1,5 @@ import { Show, For, createSignal, createMemo, onMount, type JSX, type Component } from 'solid-js'; -import type { Event as NostrEvent } from 'nostr-tools'; +import { nip19, type Event as NostrEvent } from 'nostr-tools'; import { createMutation } from '@tanstack/solid-query'; import HeartOutlined from 'heroicons/24/outline/heart.svg'; @@ -31,6 +31,7 @@ import NotePostForm from '@/components/NotePostForm'; import ensureNonNull from '@/utils/ensureNonNull'; import npubEncodeFallback from '@/utils/npubEncodeFallback'; import useSubscription from '@/nostr/useSubscription'; +import ContextMenu, { MenuItem } from '../ContextMenu'; export type TextNoteDisplayProps = { event: NostrEvent; @@ -38,9 +39,26 @@ export type TextNoteDisplayProps = { actions?: boolean; }; +const { noteEncode } = nip19; + const TextNoteDisplay: Component = (props) => { let contentRef: HTMLDivElement | undefined; + const menu: MenuItem[] = [ + { + content: () => 'IDをコピー', + onSelect: () => { + navigator.clipboard.writeText(noteEncode(props.event.id)).catch((err) => window.alert(err)); + }, + }, + { + content: () => 'JSONとしてコピー', + onSelect: () => { + navigator.clipboard.writeText(JSON.stringify(props.event)).catch((err) => window.alert(err)); + }, + }, + ]; + const { config } = useConfig(); const formatDate = useFormatDate(); const pubkey = usePubkey(); @@ -51,7 +69,6 @@ const TextNoteDisplay: Component = (props) => { const closeReplyForm = () => setShowReplyForm(false); const [showOverflow, setShowOverflow] = createSignal(false); const [overflow, setOverflow] = createSignal(false); - const [showMenu, setShowMenu] = createSignal(false); const event = createMemo(() => eventWrapper(props.event)); @@ -88,13 +105,11 @@ const TextNoteDisplay: Component = (props) => { mutationKey: ['publishDeprecatedRepost', event().id], mutationFn: commands.publishDeprecatedRepost.bind(commands), onSuccess: () => { - console.log('succeeded to publish deprecated reposts'); - invalidateDeprecatedReposts().catch((err) => - console.error('failed to refetch deprecated reposts', err), - ); + console.log('succeeded to publish reposts'); + invalidateDeprecatedReposts().catch((err) => console.error('failed to refetch reposts', err)); }, onError: (err) => { - console.error('failed to publish deprecated repost: ', err); + console.error('failed to publish repost: ', err); }, }); @@ -205,10 +220,12 @@ const TextNoteDisplay: Component = (props) => {
- +
= (props) => {
- + + + + +
diff --git a/src/core/event.ts b/src/core/event.ts index e5ca4b1..6db662c 100644 --- a/src/core/event.ts +++ b/src/core/event.ts @@ -20,7 +20,7 @@ const eventWrapper = (event: NostrEvent) => { get rawEvent(): NostrEvent { return event; }, - get id(): string | undefined { + get id(): string { return event.id; }, get pubkey(): string {