This commit is contained in:
Shusui MOYATANI
2023-03-27 00:53:19 +09:00
parent 567e1f8df8
commit 3e52a24f87
7 changed files with 60 additions and 25 deletions

View File

@@ -1,4 +1,6 @@
import { Show, type JSX, type Component } from 'solid-js';
import ArrowLeft from 'heroicons/24/outline/arrow-left.svg';
import { useHandleCommand } from '@/hooks/useCommandBus';
import { ColumnContext, useColumnState } from '@/components/ColumnContext';
import ColumnContentDisplay from '@/components/ColumnContentDisplay';
@@ -57,8 +59,14 @@ const Column: Component<ColumnProps> = (props) => {
{(columnContent) => (
<div class="absolute h-full w-full bg-white">
<div class="flex h-8 shrink-0 items-center border-b bg-white px-2">
<button class="w-full text-left" onClick={() => columnState?.clearColumnContext()}>
<button
class="flex w-full items-center gap-1"
onClick={() => columnState?.clearColumnContext()}
>
<div class="inline-block h-4 w-4">
<ArrowLeft />
</div>
<div></div>
</button>
</div>
<ul class="flex h-full flex-col overflow-y-scroll scroll-smooth">

View File

@@ -7,8 +7,17 @@ type EventLinkProps = {
eventId: string;
};
const EventLink: Component<EventLinkProps> = (props) => (
<span class="text-blue-500 underline">{noteEncode(props.eventId)}</span>
);
const tryEncode = (eventId: string) => {
try {
return noteEncode(eventId);
} catch (err) {
console.error('failed to encode event id into Bech32 entity (NIP-19) but ignore', eventId, err);
return eventId;
}
};
const EventLink: Component<EventLinkProps> = (props) => {
return <button class="text-blue-500 underline">{tryEncode(props.eventId)}</button>;
};
export default EventLink;

View File

@@ -3,18 +3,18 @@ import GeneralUserMentionDisplay from '@/components/textNote/GeneralUserMentionD
import useModalState from '@/hooks/useModalState';
export type MentionedUserDisplayProps = {
mentionedUser: MentionedUser;
pubkey: string;
};
const MentionedUserDisplay = (props: MentionedUserDisplayProps) => {
const { showProfile } = useModalState();
const handleClick = () => {
showProfile(props.mentionedUser.pubkey);
showProfile(props.pubkey);
};
return (
<button class="inline text-blue-500 underline" onClick={handleClick}>
<GeneralUserMentionDisplay pubkey={props.mentionedUser.pubkey} />
<GeneralUserMentionDisplay pubkey={props.pubkey} />
</button>
);
};

View File

@@ -27,7 +27,7 @@ const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => {
return <PlainTextDisplay plainText={item} />;
}
if (item.type === 'MentionedUser') {
return <MentionedUserDisplay mentionedUser={item} />;
return <MentionedUserDisplay pubkey={item.pubkey} />;
}
if (item.type === 'MentionedEvent') {
if (props.embedding) {
@@ -43,6 +43,12 @@ const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => {
</div>
);
}
if (item.data.type === 'npub' && props.embedding) {
return <MentionedUserDisplay pubkey={item.data.data} />;
}
if (item.data.type === 'nprofile' && props.embedding) {
return <MentionedUserDisplay pubkey={item.data.data.pubkey} />;
}
return <span class="text-blue-500 underline">{item.content}</span>;
}
if (item.type === 'HashTag') {
@@ -61,6 +67,7 @@ const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => {
}
return <SafeLink class="text-blue-500 underline" href={item.content} />;
}
console.error('Not all ParsedTextNoteNodes are covered', item);
return null;
}}
</For>

View File

@@ -165,16 +165,7 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
});
return (
<div
class="nostr-textnote flex flex-col"
onClick={() => {
// FIXME
// columnContext?.setColumnContent({
// type: 'Replies',
// eventId: event().rootEvent()?.id ?? props.event.id,
// });
}}
>
<div class="nostr-textnote flex flex-col">
<div class="flex w-full gap-1">
<button
class="author-icon h-10 w-10 shrink-0 overflow-hidden"
@@ -213,7 +204,20 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
{/* TODO <Match when={author()?.nip05 != null}>@{author()?.nip05}</Match> */}
</div>
</button>
<div class="created-at shrink-0">{createdAt()}</div>
<div class="created-at shrink-0">
<button
type="button"
class="hover:underline"
onClick={() => {
columnContext?.setColumnContent({
type: 'Replies',
eventId: event().rootEvent()?.id ?? props.event.id,
});
}}
>
{createdAt()}
</button>
</div>
</div>
<div
ref={contentRef}

View File

@@ -60,8 +60,11 @@ const parseTextNote = (event: NostrEvent): ParsedTextNote => {
const matches = [
...event.content.matchAll(/(?:#\[(?<idx>\d+)\])/g),
...event.content.matchAll(/#(?<hashtag>[^[-^`:-@!-/{-~\d\s][^[-^`:-@!-/{-~\s]+)/g),
// raw NIP-19 codes, NIP-21 links (NIP-27)
// nrelay and naddr is not supported by nostr-tools
...event.content.matchAll(/(?<nip19>(npub|note|nprofile|nevent)1[ac-hj-np-z02-9]+)/gi),
...event.content.matchAll(
/(?:nostr:)?(?<mention>(npub|note|nprofile|nevent)1[ac-hj-np-z02-9]+)/gi,
),
...event.content.matchAll(
/(?<url>(?:https?|wss?):\/\/[-a-zA-Z0-9.]+(?:\/[-[\]~!$&'()*+.,:;@%\w]+|\/)*(?:\?[-[\]~!$&'()*+.,/:;%@\w&=]+)?(?:#[-[\]~!$&'()*+.,/:;%@\w?&=#]+)?)/g,
),
@@ -117,10 +120,10 @@ const parseTextNote = (event: NostrEvent): ParsedTextNote => {
};
result.push(mentionedEvent);
}
} else if (match.groups?.nip19) {
} else if (match.groups?.mention) {
pushPlainText(index);
try {
const decoded = decode(match[0]);
const decoded = decode(match[1]);
const bech32Entity: Bech32Entity = {
type: 'Bech32Entity',
content: match[0],
@@ -129,6 +132,8 @@ const parseTextNote = (event: NostrEvent): ParsedTextNote => {
result.push(bech32Entity);
} catch (e) {
console.error(`failed to parse Bech32 entity (NIP-19) but ignore this: ${match[0]}`);
pushPlainText(index + match[0].length);
return;
}
} else if (match.groups?.hashtag) {
pushPlainText(index);

View File

@@ -286,6 +286,7 @@ export const useProfile = (propsProvider: () => UseProfileProps | null): UseProf
// cacheTime is long so that the user see profiles instantly.
staleTime: 5 * 60 * 1000, // 5 min
cacheTime: 24 * 60 * 60 * 1000, // 1 day
refetchInterval: 5 * 60 * 1000, // 5 min
},
);
@@ -331,7 +332,7 @@ export const useTextNote = (propsProvider: () => UseTextNoteProps | null): UseTe
},
);
const event = () => query.data;
const event = () => query.data ?? null;
return { event, query };
};
@@ -362,6 +363,7 @@ export const useReactions = (propsProvider: () => UseReactionsProps | null): Use
{
staleTime: 1 * 60 * 1000, // 1 min
cacheTime: 4 * 60 * 60 * 1000, // 4 hour
refetchInterval: 1 * 60 * 1000, // 1 min
},
);
@@ -412,6 +414,7 @@ export const useDeprecatedReposts = (
{
staleTime: 1 * 60 * 1000, // 1 min
cacheTime: 4 * 60 * 60 * 1000, // 4 hour
refetchInterval: 1 * 60 * 1000, // 1 min
},
);
@@ -458,7 +461,6 @@ export const useFollowings = (propsProvider: () => UseFollowingsProps | null): U
staleTime: 5 * 60 * 1000, // 5 min
cacheTime: 24 * 60 * 60 * 1000, // 24 hour
refetchOnWindowFocus: false,
refetchInterval: 5 * 60 * 1000, // 5 min
},
);