diff --git a/src/components/event/ChannelInfo.tsx b/src/components/event/ChannelInfo.tsx new file mode 100644 index 0000000..3b200e3 --- /dev/null +++ b/src/components/event/ChannelInfo.tsx @@ -0,0 +1,54 @@ +import { Component, Show } from 'solid-js'; + +import ChatBubbleLeftRight from 'heroicons/24/outline/chat-bubble-left-right.svg'; +import { Event as NostrEvent } from 'nostr-tools'; +import { z } from 'zod'; + +import EventLink from '@/components/EventLink'; +import { isImageUrl } from '@/utils/imageUrl'; + +export type ChannelInfoProps = { + event: NostrEvent; +}; + +const ChannelMetaSchema = z.object({ + name: z.string(), + about: z.string().optional(), + picture: z + .string() + .url() + .refine((url) => isImageUrl(url), { message: 'not an image url' }) + .optional(), +}); + +export type ChannelMeta = z.infer; + +const parseContent = (content: string): ChannelMeta | null => { + try { + return ChannelMetaSchema.parse(JSON.parse(content)); + } catch (err) { + console.warn('failed to parse chat channel schema: ', err); + return null; + } +}; + +const ChannelInfo: Component = (props) => { + const parsedContent = () => parseContent(props.event.content); + + return ( + + {(meta) => ( + + )} + + ); +}; + +export default ChannelInfo; diff --git a/src/components/event/EventDisplay.tsx b/src/components/event/EventDisplay.tsx index c18380a..8ada45d 100644 --- a/src/components/event/EventDisplay.tsx +++ b/src/components/event/EventDisplay.tsx @@ -2,6 +2,7 @@ import { Switch, Match, Component } from 'solid-js'; import { Kind, type Event as NostrEvent } from 'nostr-tools'; +import ChannelInfo from '@/components/event/ChannelInfo'; // eslint-disable-next-line import/no-cycle import Repost from '@/components/event/Repost'; // eslint-disable-next-line import/no-cycle @@ -12,12 +13,16 @@ export type EventDisplayProps = { event: NostrEvent; embedding?: boolean; actions?: boolean; - kinds?: Kind[]; + ensureKinds?: Kind[]; }; const EventDisplay: Component = (props) => { + // noteの場合は kind:1 であることを保証するために利用できる + // タイムラインで表示されるべきでないイベントが表示されてしまうのを防ぐ const isAllowedKind = () => - props.kinds == null || props.kinds.length === 0 || props.kinds.includes(props.event.kind); + props.ensureKinds == null || + props.ensureKinds.length === 0 || + props.ensureKinds.includes(props.event.kind); return ( = (props) => { } > - {null} + + + 予期しないイベント種別({props.event.kind}) + + + @@ -37,6 +47,11 @@ const EventDisplay: Component = (props) => { ); + /* + + + + */ }; export default EventDisplay; diff --git a/src/components/event/textNote/TextNoteContentDisplay.tsx b/src/components/event/textNote/TextNoteContentDisplay.tsx index 206f61d..e88d421 100644 --- a/src/components/event/textNote/TextNoteContentDisplay.tsx +++ b/src/components/event/textNote/TextNoteContentDisplay.tsx @@ -1,5 +1,7 @@ import { For } from 'solid-js'; +import { Kind, Event as NostrEvent } from 'nostr-tools'; + // eslint-disable-next-line import/no-cycle import EventDisplayById from '@/components/event/EventDisplayById'; import ImageDisplay from '@/components/event/textNote/ImageDisplay'; @@ -15,8 +17,6 @@ import eventWrapper from '@/nostr/event'; import parseTextNote, { resolveTagReference, type ParsedTextNoteNode } from '@/nostr/parseTextNote'; import { isImageUrl } from '@/utils/imageUrl'; -import type { Event as NostrEvent } from 'nostr-tools'; - export type TextNoteContentDisplayProps = { event: NostrEvent; embedding: boolean; @@ -57,7 +57,12 @@ const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => { if (item.data.type === 'note' && props.embedding) { return (
- +
); } diff --git a/src/components/event/textNote/TextNoteDisplay.tsx b/src/components/event/textNote/TextNoteDisplay.tsx index 8089e51..8588dee 100644 --- a/src/components/event/textNote/TextNoteDisplay.tsx +++ b/src/components/event/textNote/TextNoteDisplay.tsx @@ -49,7 +49,7 @@ const EmojiReactions: Component = (props) => { const pubkey = usePubkey(); return ( -
+
{([content, events]) => { const isReactedByMeWithThisContent = @@ -57,9 +57,10 @@ const EmojiReactions: Component = (props) => { return (