From 06baf0f1547fb64d7c5813e4da332be620a1282f Mon Sep 17 00:00:00 2001 From: Shusui MOYATANI Date: Tue, 7 Mar 2023 20:50:34 +0900 Subject: [PATCH] feat: support inline image --- src/clients/useEvent.ts | 6 +-- src/clients/usePubkey.ts | 1 + src/clients/useReactions.ts | 6 +-- src/components/textNote/ImageDisplay.tsx | 33 ++++++++++++++ .../textNote/TextNoteContentDisplay.tsx | 18 +++++++- src/core/parseTextNote.ts | 43 +++++++++++++------ src/pages/Hello.tsx | 5 +++ 7 files changed, 91 insertions(+), 21 deletions(-) create mode 100644 src/components/textNote/ImageDisplay.tsx diff --git a/src/clients/useEvent.ts b/src/clients/useEvent.ts index 1fe891e..f28eeba 100644 --- a/src/clients/useEvent.ts +++ b/src/clients/useEvent.ts @@ -35,9 +35,9 @@ const useEvent = (propsProvider: () => UseEventProps | null): UseEvent => { return timeout(15000, `useEvent: ${currentProps.eventId}`)(exec(currentProps, signal)); }, { - // 5 minutes - staleTime: 5 * 60 * 1000, - cacheTime: 15 * 60 * 1000, + // a hour + staleTime: 60 * 60 * 1000, + cacheTime: 60 * 60 * 1000, }, ); diff --git a/src/clients/usePubkey.ts b/src/clients/usePubkey.ts index 965b5d4..186a810 100644 --- a/src/clients/usePubkey.ts +++ b/src/clients/usePubkey.ts @@ -4,6 +4,7 @@ import { createSignal, onMount, type Accessor } from 'solid-js'; let asking = false; const [pubkey, setPubkey] = createSignal(undefined); +// TODO 失敗したときに通知等を表示したい const usePubkey = (): Accessor => { onMount(() => { let count = 0; diff --git a/src/clients/useReactions.ts b/src/clients/useReactions.ts index cbc3ee1..a601370 100644 --- a/src/clients/useReactions.ts +++ b/src/clients/useReactions.ts @@ -19,7 +19,7 @@ export type UseReactions = { }; const { exec } = useBatchedEvents(() => ({ - interval: 5000, + interval: 3400, generateKey: ({ eventId }) => eventId, mergeFilters: (args) => { const eventIds = args.map((arg) => arg.eventId); @@ -43,9 +43,9 @@ const useReactions = (propsProvider: () => UseReactionsProps | null): UseReactio return timeout(15000, `useReactions: ${currentProps.eventId}`)(exec(currentProps, signal)); }, { - // 1 minutes + // 3 minutes staleTime: 1 * 60 * 1000, - cacheTime: 1 * 60 * 1000, + cacheTime: 3 * 60 * 1000, }, ); diff --git a/src/components/textNote/ImageDisplay.tsx b/src/components/textNote/ImageDisplay.tsx new file mode 100644 index 0000000..d8d6264 --- /dev/null +++ b/src/components/textNote/ImageDisplay.tsx @@ -0,0 +1,33 @@ +import { Component } from 'solid-js'; + +type ImageDisplayProps = { + url: string; +}; + +const fixUrl = (url: URL): string => { + const result = new URL(url); + if (url.host === 'i.imgur.com') { + const match = url.pathname.match(/^\/([a-zA-Z0-9]+)\.(jpg|jpeg|png|gif)/); + if (match != null) { + const imageId = match[1]; + result.pathname = `${imageId}l.webp`; + } + } + return result.toString(); +}; + +const ImageDisplay: Component = (props) => { + const url = () => new URL(props.url); + + return ( + + {props.url} + + ); +}; + +export default ImageDisplay; diff --git a/src/components/textNote/TextNoteContentDisplay.tsx b/src/components/textNote/TextNoteContentDisplay.tsx index da164f0..f9c3eec 100644 --- a/src/components/textNote/TextNoteContentDisplay.tsx +++ b/src/components/textNote/TextNoteContentDisplay.tsx @@ -4,6 +4,7 @@ import type { Event as NostrEvent } from 'nostr-tools'; import PlainTextDisplay from '@/components/textNote/PlainTextDisplay'; import MentionedUserDisplay from '@/components/textNote/MentionedUserDisplay'; import MentionedEventDisplay from '@/components/textNote/MentionedEventDisplay'; +import ImageDisplay from '@/components/textNote/ImageDisplay'; export type TextNoteContentDisplayProps = { event: NostrEvent; @@ -24,7 +25,22 @@ const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => { return ; } if (item.type === 'HashTag') { - return {item.content}; + return {item.content}; + } + if (item.type === 'URL') { + if (item.content.match(/\.(jpeg|jpg|png|gif|webp)$/i)) { + return ; + } + return ( + + {item.content} + + ); } return null; }} diff --git a/src/core/parseTextNote.ts b/src/core/parseTextNote.ts index 413076d..1eca683 100644 --- a/src/core/parseTextNote.ts +++ b/src/core/parseTextNote.ts @@ -26,25 +26,38 @@ export type HashTag = { tagName: string; }; -export type ParsedTextNoteNode = PlainText | MentionedEvent | MentionedUser | HashTag; +export type UrlText = { + type: 'URL'; + content: string; +}; + +export type ParsedTextNoteNode = PlainText | MentionedEvent | MentionedUser | HashTag | UrlText; export type ParsedTextNote = ParsedTextNoteNode[]; export const parseTextNote = (event: NostrEvent): ParsedTextNote => { - const matches = Array.from( - event.content.matchAll(/(?:#\[(?\d+)\]|#(?[^\[\]\(\)\s]+))/g), - ); + const matches = [ + ...event.content.matchAll(/(?:#\[(?\d+)\])/g), + ...event.content.matchAll(/#(?[^[]\(\)\s]+)/g), + ...event.content.matchAll( + /(?https?:\/\/[-a-zA-Z0-9.]+(?:\/[-\w.%]+|\/)*(?:\?[-\w=&]*)?(?:#[-\w]*)?)/g, + ), + ].sort((a, b) => a?.index - b?.index); let pos = 0; const result: ParsedTextNote = []; + const pushPlainText = (index: number | undefined) => { + if (index != null && pos !== index) { + const content = event.content.slice(pos, index); + const plainText: PlainText = { type: 'PlainText', content }; + result.push(plainText); + } + }; + matches.forEach((match) => { if (match.groups?.hashtag) { + pushPlainText(match.index); const tagName = match.groups?.hashtag; - if (pos !== match.index) { - const content = event.content.slice(pos, match.index); - const plainText: PlainText = { type: 'PlainText', content }; - result.push(plainText); - } const hashtag: HashTag = { type: 'HashTag', content: match[0], @@ -55,11 +68,9 @@ export const parseTextNote = (event: NostrEvent): ParsedTextNote => { const tagIndex = parseInt(match.groups.idx, 10); const tag = event.tags[tagIndex]; if (tag == null) return; - if (pos !== match.index) { - const content = event.content.slice(pos, match.index); - const plainText: PlainText = { type: 'PlainText', content }; - result.push(plainText); - } + + pushPlainText(match.index); + const tagName = tag[0]; if (tagName === 'p') { const mentionedUser: MentionedUser = { @@ -79,6 +90,10 @@ export const parseTextNote = (event: NostrEvent): ParsedTextNote => { }; result.push(mentionedEvent); } + } else if (match.groups?.url) { + pushPlainText(match.index); + const url: UrlText = { type: 'URL', content: match.groups?.url }; + result.push(url); } pos = match.index + match[0].length; }); diff --git a/src/pages/Hello.tsx b/src/pages/Hello.tsx index 991ad32..8f1a4ef 100644 --- a/src/pages/Hello.tsx +++ b/src/pages/Hello.tsx @@ -49,6 +49,11 @@ const Hello: Component = () => {
🐰

Rabbit

Rabbit is a Web client for Nostr.
+

+ 注意: 現在ベータ版です。 +
+ 未実装の機能やバグがあることを承知の上でご利用ください。 +