This commit is contained in:
Shusui MOYATANI
2023-06-03 20:44:22 +09:00
parent 26700c0cae
commit 5db35d7028
33 changed files with 1227 additions and 717 deletions

View File

@@ -11,9 +11,8 @@ import { Event as NostrEvent } from 'nostr-tools';
import EmojiPicker from '@/components/EmojiPicker';
import UserNameDisplay from '@/components/UserDisplayName';
import useConfig from '@/core/useConfig';
import { useHandleCommand } from '@/hooks/useCommandBus';
import usePersistStatus from '@/hooks/usePersistStatus';
import eventWrapper from '@/nostr/event';
import { textNote } from '@/nostr/event';
import parseTextNote, { ParsedTextNote } from '@/nostr/parseTextNote';
import useCommands, { PublishTextNoteParams } from '@/nostr/useCommands';
import usePubkey from '@/nostr/usePubkey';
@@ -112,7 +111,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
const getPubkey = usePubkey();
const commands = useCommands();
const replyTo = () => props.replyTo && eventWrapper(props.replyTo);
const replyTo = () => props.replyTo && textNote(props.replyTo);
const mode = () => props.mode ?? 'normal';
const publishTextNoteMutation = createMutation({
@@ -156,11 +155,11 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
},
});
const mentionedPubkeysWithoutMe = createMemo(() => {
const taggedPubkeysWithoutMe = createMemo(() => {
const p = getPubkey();
return (
replyTo()
?.mentionedPubkeys()
?.taggedPubkeys()
?.filter((pubkey) => pubkey !== p) ?? []
);
});
@@ -171,7 +170,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
// 返信先を先頭に
props.replyTo.pubkey,
// その他の返信先
...mentionedPubkeysWithoutMe(),
...taggedPubkeysWithoutMe(),
]);
});
@@ -203,7 +202,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
const formattedContent = format(parsed);
const emojiTags = buildEmojiTags(emojis);
let textNote: PublishTextNoteParams = {
let textNoteParams: PublishTextNoteParams = {
relayUrls: config().relayUrls,
pubkey,
content: formattedContent,
@@ -215,8 +214,8 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
};
if (replyTo() != null) {
textNote = {
...textNote,
textNoteParams = {
...textNoteParams,
notifyPubkeys: uniq([
...notifyPubkeys(),
...pubkeyReferences, // 本文中の公開鍵npub)
@@ -226,12 +225,12 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
};
}
if (contentWarning()) {
textNote = {
...textNote,
textNoteParams = {
...textNoteParams,
contentWarning: contentWarningReason(),
};
}
publishTextNoteMutation.mutate(textNote);
publishTextNoteMutation.mutate(textNoteParams);
close();
};

View File

@@ -10,7 +10,7 @@ import Timeline from '@/components/timeline/Timeline';
import { FollowingColumnType } from '@/core/column';
import { applyContentFilter } from '@/core/contentFilter';
import useConfig from '@/core/useConfig';
import { useFollowings } from '@/nostr/useBatchedEvents';
import useFollowings from '@/nostr/useFollowings';
import useSubscription from '@/nostr/useSubscription';
import epoch from '@/utils/epoch';

View File

@@ -2,7 +2,7 @@ import { Switch, Match, Component } from 'solid-js';
import { Kind, type Event as NostrEvent } from 'nostr-tools';
import ChannelInfo from '@/components/event/ChannelInfo';
// 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

View File

@@ -3,24 +3,14 @@ import { Component } from 'solid-js';
import DocumentText from 'heroicons/24/outline/document-text.svg';
import { Kind, Event as NostrEvent } from 'nostr-tools';
import eventWrapper from '@/nostr/event';
import { genericEvent } from '@/nostr/event';
export type LongFormContentProps = {
event: NostrEvent;
};
const LongFormContent: Component<LongFormContentProps> = (props) => {
const event = () => eventWrapper(props.event);
const getMeta = (name: string) => {
const tags = event().findTagsByName(name);
if (tags.length === 0) return null;
const [, lastTagValue] = tags[tags.length - 1];
return lastTagValue;
};
const title = () => getMeta('title');
// const imageUrl = () => getMeta('image');
// const summary = () => getMeta('summary');
// const publishdAt = () => getMeta('published_at');
// const event = () => genericEvent(props.event);
return (
<button class="flex flex-col gap-1 px-1">
@@ -28,7 +18,7 @@ const LongFormContent: Component<LongFormContentProps> = (props) => {
<span class="inline-block h-4 w-4 text-purple-400">
<DocumentText />
</span>
<span>{title()}</span>
<span>TODO</span>
</div>
</button>
);

View File

@@ -7,7 +7,7 @@ import TextNoteDisplay from '@/components/event/textNote/TextNoteDisplay';
import UserDisplayName from '@/components/UserDisplayName';
import useConfig from '@/core/useConfig';
import useModalState from '@/hooks/useModalState';
import eventWrapper from '@/nostr/event';
import { genericEvent } from '@/nostr/event';
import useEvent from '@/nostr/useEvent';
import useProfile from '@/nostr/useProfile';
import ensureNonNull from '@/utils/ensureNonNull';
@@ -19,7 +19,7 @@ type ReactionProps = {
const Reaction: Component<ReactionProps> = (props) => {
const { shouldMuteEvent } = useConfig();
const { showProfile } = useModalState();
const event = () => eventWrapper(props.event);
const event = () => genericEvent(props.event);
const eventId = () => event().lastTaggedEventId();
const { profile } = useProfile(() => ({

View File

@@ -9,7 +9,7 @@ import EventDisplayById from '@/components/event/EventDisplayById';
import UserDisplayName from '@/components/UserDisplayName';
import useFormatDate from '@/hooks/useFormatDate';
import useModalState from '@/hooks/useModalState';
import eventWrapper from '@/nostr/event';
import { genericEvent } from '@/nostr/event';
export type RepostProps = {
event: NostrEvent;
@@ -18,7 +18,7 @@ export type RepostProps = {
const Repost: Component<RepostProps> = (props) => {
const { showProfile } = useModalState();
const formatDate = useFormatDate();
const event = createMemo(() => eventWrapper(props.event));
const event = createMemo(() => genericEvent(props.event));
const eventId = () => event().lastTaggedEventId();
return (

View File

@@ -1,6 +1,6 @@
import { createSignal, type Component, type JSX, Show } from 'solid-js';
import { ContentWarning } from '@/nostr/event';
import { ContentWarning } from '@/nostr/event/TextNote';
export type ContentWarningDisplayProps = {
contentWarning: ContentWarning;

View File

@@ -4,6 +4,7 @@ import { Kind, Event as NostrEvent } from 'nostr-tools';
// eslint-disable-next-line import/no-cycle
import EventDisplayById from '@/components/event/EventDisplayById';
// import ParameterizedReplaceableEventDisplayById from '@/components/event/ParameterizedReplaceableEventDisplayById';
import ImageDisplay from '@/components/event/textNote/ImageDisplay';
import MentionedEventDisplay from '@/components/event/textNote/MentionedEventDisplay';
import MentionedUserDisplay from '@/components/event/textNote/MentionedUserDisplay';
@@ -12,8 +13,8 @@ import SafeLink from '@/components/utils/SafeLink';
import { createSearchColumn } from '@/core/column';
import useConfig from '@/core/useConfig';
import { useRequestCommand } from '@/hooks/useCommandBus';
import eventWrapper from '@/nostr/event';
import parseTextNote, { resolveTagReference, type ParsedTextNoteNode } from '@/nostr/parseTextNote';
import { textNote } from '@/nostr/event';
import parseTextNote, { type ParsedTextNoteNode } from '@/nostr/parseTextNote';
import { isImageUrl } from '@/utils/imageUrl';
export type TextNoteContentDisplayProps = {
@@ -26,7 +27,7 @@ const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => {
const request = useRequestCommand();
const event = () => eventWrapper(props.event);
const event = () => textNote(props.event);
const addHashTagColumn = (query: string) => {
saveColumn(createSearchColumn({ query }));
@@ -34,7 +35,7 @@ const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => {
};
return (
<For each={parseTextNote(props.event.content)}>
<For each={event().parsed()}>
{(item: ParsedTextNoteNode) => {
if (item.type === 'PlainText') {
return <span>{item.content}</span>;
@@ -53,7 +54,7 @@ const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => {
return <SafeLink class="text-blue-500 underline" href={item.content} />;
}
if (item.type === 'TagReference') {
const resolved = resolveTagReference(item, props.event);
const resolved = event().resolveTagReference(item);
if (resolved == null) {
return <span>{item.content}</span>;
}

View File

@@ -21,7 +21,7 @@ import { useTimelineContext } from '@/components/timeline/TimelineContext';
import useConfig from '@/core/useConfig';
import useFormatDate from '@/hooks/useFormatDate';
import useModalState from '@/hooks/useModalState';
import eventWrapper from '@/nostr/event';
import { textNote } from '@/nostr/event';
import useCommands from '@/nostr/useCommands';
import useProfile from '@/nostr/useProfile';
import usePubkey from '@/nostr/usePubkey';
@@ -100,7 +100,7 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
const [showOverflow, setShowOverflow] = createSignal(false);
const [overflow, setOverflow] = createSignal(false);
const event = createMemo(() => eventWrapper(props.event));
const event = createMemo(() => textNote(props.event));
const embedding = () => props.embedding ?? true;
const actions = () => props.actions ?? true;
@@ -197,7 +197,7 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
content: () => 'JSONとしてコピー',
onSelect: () => {
navigator.clipboard
.writeText(JSON.stringify(props.event))
.writeText(JSON.stringify(props.event, null, 2))
.catch((err) => window.alert(err));
},
},
@@ -232,13 +232,22 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
});
const showReplyEvent = (): string | undefined => {
const replyingToEvent = event().replyingToEvent();
if (
embedding() &&
replyingToEvent != null &&
!event().containsEventMentionIndex(replyingToEvent.index)
) {
return replyingToEvent.id;
if (embedding()) {
const replyingToEvent = event().replyingToEvent();
if (replyingToEvent != null && !event().containsEventMention(replyingToEvent.id)) {
return replyingToEvent.id;
}
const rootEvent = event().rootEvent();
if (
replyingToEvent == null &&
rootEvent != null &&
!event().containsEventMention(rootEvent.id)
) {
return rootEvent.id;
}
}
return undefined;
};
@@ -360,9 +369,9 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
</div>
)}
</Show>
<Show when={event().mentionedPubkeys().length > 0}>
<Show when={event().taggedPubkeys().length > 0}>
<div class="text-xs">
<For each={event().mentionedPubkeys()}>
<For each={event().taggedPubkeys()}>
{(replyToPubkey: string) => (
<button
class="pr-1 text-blue-500 hover:underline"

View File

@@ -7,8 +7,9 @@ import omitBy from 'lodash/omitBy';
import BasicModal from '@/components/modal/BasicModal';
import useConfig from '@/core/useConfig';
import { Profile, useProfile } from '@/nostr/useBatchedEvents';
import { Profile } from '@/nostr/event/Profile';
import useCommands from '@/nostr/useCommands';
import { useProfile } from '@/nostr/useProfile';
import usePubkey from '@/nostr/usePubkey';
import ensureNonNull from '@/utils/ensureNonNull';
import timeout from '@/utils/timeout';

View File

@@ -6,17 +6,17 @@ import { Filter, Event as NostrEvent } from 'nostr-tools';
import Timeline from '@/components/timeline/Timeline';
import { type TimelineContent } from '@/components/timeline/TimelineContext';
import useConfig from '@/core/useConfig';
import eventWrapper from '@/nostr/event';
import { textNote } from '@/nostr/event';
import TextNote from '@/nostr/event/TextNote';
import useSubscription from '@/nostr/useSubscription';
const relatedEvents = (rawEvent: NostrEvent) => {
const event = () => eventWrapper(rawEvent);
const ids = [rawEvent.id];
const relatedEvents = (event: TextNote) => {
const ids = [event.id];
const rootId = event().rootEvent()?.id;
const rootId = event.rootEvent()?.id;
if (rootId != null) ids.push(rootId);
const replyId = event().replyingToEvent()?.id;
const replyId = event.replyingToEvent()?.id;
if (replyId != null) ids.push(replyId);
return uniq(ids);
@@ -25,10 +25,12 @@ const relatedEvents = (rawEvent: NostrEvent) => {
const RepliesDisplay: Component<{ event: NostrEvent }> = (props) => {
const { config } = useConfig();
const event = () => textNote(props.event);
const { events } = useSubscription(() => ({
relayUrls: config().relayUrls,
filters: [
{ kinds: [1], ids: relatedEvents(props.event), limit: 25 },
{ kinds: [1], ids: relatedEvents(event()), limit: 25 },
{ kinds: [1], '#e': [props.event.id], limit: 25 } as Filter,
],
limit: 200,