update nostr-tools

This commit is contained in:
Shusui MOYATANI
2023-12-21 02:51:51 +09:00
parent 4df42cea0a
commit 47eb2fc57d
48 changed files with 216 additions and 162 deletions

77
package-lock.json generated
View File

@@ -26,7 +26,7 @@
"i18next-browser-languagedetector": "^7.2.0",
"idb-keyval": "^6.2.1",
"lodash": "^4.17.21",
"nostr-tools": "^1.16.0",
"nostr-tools": "^2.0.3",
"solid-js": "^1.8.7",
"tailwindcss": "^3.4.0",
"zod": "^3.22.4"
@@ -5494,16 +5494,17 @@
}
},
"node_modules/nostr-tools": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.16.0.tgz",
"integrity": "sha512-sx/aOl0gmkeHVoIVbyOhEQhzF88NsrBXMC8bsjhPASqA6oZ8uSOAyEGgRLMfC3SKgzQD5Gr6KvDoAahaD6xKcg==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.0.3.tgz",
"integrity": "sha512-A7/sBaeBA7Vmi3q1Or62FYbU4OuNBOfQmSxn4/sEK5Z6L4Ql9dwSq8H/mrxsiCQHfbLaVHBOmxLOOd3070/xCA==",
"dependencies": {
"@noble/ciphers": "^0.2.0",
"@noble/curves": "1.1.0",
"@noble/ciphers": "0.2.0",
"@noble/curves": "1.2.0",
"@noble/hashes": "1.3.1",
"@scure/base": "1.1.1",
"@scure/bip32": "1.3.1",
"@scure/bip39": "1.2.1"
"@scure/bip39": "1.2.1",
"nostr-wasm": "v0.0.3"
},
"peerDependencies": {
"typescript": ">=5.0.0"
@@ -5514,6 +5515,33 @@
}
}
},
"node_modules/nostr-tools/node_modules/@noble/curves": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
"dependencies": {
"@noble/hashes": "1.3.2"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/nostr-tools/node_modules/@noble/curves/node_modules/@noble/hashes": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/nostr-wasm": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.0.3.tgz",
"integrity": "sha512-zz5INXiuya10s+zsNmW2k4xH47j+Ikgxn5SL8xt/quGBNWn/A6+blVqT3J6yjkZim/gyVC/9GXd0jH4w3w1HZA=="
},
"node_modules/npm-normalize-package-bin": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
@@ -12599,17 +12627,40 @@
"dev": true
},
"nostr-tools": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.16.0.tgz",
"integrity": "sha512-sx/aOl0gmkeHVoIVbyOhEQhzF88NsrBXMC8bsjhPASqA6oZ8uSOAyEGgRLMfC3SKgzQD5Gr6KvDoAahaD6xKcg==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.0.3.tgz",
"integrity": "sha512-A7/sBaeBA7Vmi3q1Or62FYbU4OuNBOfQmSxn4/sEK5Z6L4Ql9dwSq8H/mrxsiCQHfbLaVHBOmxLOOd3070/xCA==",
"requires": {
"@noble/ciphers": "^0.2.0",
"@noble/curves": "1.1.0",
"@noble/ciphers": "0.2.0",
"@noble/curves": "1.2.0",
"@noble/hashes": "1.3.1",
"@scure/base": "1.1.1",
"@scure/bip32": "1.3.1",
"@scure/bip39": "1.2.1"
"@scure/bip39": "1.2.1",
"nostr-wasm": "v0.0.3"
},
"dependencies": {
"@noble/curves": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
"requires": {
"@noble/hashes": "1.3.2"
},
"dependencies": {
"@noble/hashes": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="
}
}
}
}
},
"nostr-wasm": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.0.3.tgz",
"integrity": "sha512-zz5INXiuya10s+zsNmW2k4xH47j+Ikgxn5SL8xt/quGBNWn/A6+blVqT3J6yjkZim/gyVC/9GXd0jH4w3w1HZA=="
},
"npm-normalize-package-bin": {
"version": "1.0.1",

View File

@@ -68,7 +68,7 @@
"i18next-browser-languagedetector": "^7.2.0",
"idb-keyval": "^6.2.1",
"lodash": "^4.17.21",
"nostr-tools": "^1.16.0",
"nostr-tools": "^2.0.3",
"solid-js": "^1.8.7",
"tailwindcss": "^3.4.0",
"zod": "^3.22.4"

View File

@@ -16,7 +16,8 @@ import EllipsisHorizontal from 'heroicons/24/outline/ellipsis-horizontal.svg';
import HeartOutlined from 'heroicons/24/outline/heart.svg';
import Plus from 'heroicons/24/outline/plus.svg';
import HeartSolid from 'heroicons/24/solid/heart.svg';
import { type Event as NostrEvent, nip19 } from 'nostr-tools';
import { noteEncode } from 'nostr-tools/nip19';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import ContextMenu, { MenuItem } from '@/components/ContextMenu';
import EmojiDisplay from '@/components/EmojiDisplay';
@@ -41,8 +42,6 @@ export type ActionProps = {
onClickReply: () => void;
};
const { noteEncode } = nip19;
const emojiDataToReactionTypes = (emoji: EmojiData): ReactionTypes => {
if (emoji.native != null) {
return { type: 'Emoji', content: emoji.native };

View File

@@ -1,12 +1,10 @@
import { Show, type Component } from 'solid-js';
import { Kind, nip19 } from 'nostr-tools';
const { noteEncode, neventEncode } = nip19;
import { noteEncode, neventEncode } from 'nostr-tools/nip19';
type EventLinkProps = {
eventId: string;
kind?: Kind;
kind?: number;
};
const tryEncodeNote = (eventId: string) => {
@@ -29,10 +27,7 @@ const tryEncodeNevent = (eventId: string) => {
const EventLink: Component<EventLinkProps> = (props) => (
<button class="text-blue-500 underline">
<Show
when={props.kind == null || props.kind === Kind.Text}
fallback={tryEncodeNevent(props.eventId)}
>
<Show when={props.kind == null || props.kind === 1} fallback={tryEncodeNevent(props.eventId)}>
{tryEncodeNote(props.eventId)}
</Show>
</button>

View File

@@ -6,7 +6,7 @@ import Photo from 'heroicons/24/outline/photo.svg';
import XMark from 'heroicons/24/outline/x-mark.svg';
import PaperAirplane from 'heroicons/24/solid/paper-airplane.svg';
import uniq from 'lodash/uniq';
import { Event as NostrEvent } from 'nostr-tools';
import { Event as NostrEvent } from 'nostr-tools/pure';
import EmojiPicker, { EmojiData } from '@/components/EmojiPicker';
import UserNameDisplay from '@/components/UserDisplayName';

View File

@@ -1,7 +1,7 @@
import { Component } from 'solid-js';
import ChatBubbleLeftRight from 'heroicons/24/outline/chat-bubble-left-right.svg';
import { Kind } from 'nostr-tools';
import * as Kind from 'nostr-tools/kinds';
import BasicColumnHeader from '@/components/column/BasicColumnHeader';
import Column from '@/components/column/Column';

View File

@@ -1,7 +1,7 @@
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 { Event as NostrEvent } from 'nostr-tools/pure';
import { parseChannelMeta } from '@/nostr/event/channel';

View File

@@ -1,6 +1,7 @@
import { Switch, Match, Component } from 'solid-js';
import { Kind, type Event as NostrEvent } from 'nostr-tools';
import * as Kind from 'nostr-tools/kinds';
import { type Event as NostrEvent } from 'nostr-tools/pure';
// import ChannelInfo from '@/components/event/ChannelInfo';
// eslint-disable-next-line import/no-cycle
@@ -13,7 +14,7 @@ export type EventDisplayProps = {
event: NostrEvent;
embedding?: boolean;
actions?: boolean;
ensureKinds?: Kind[];
ensureKinds?: number[];
};
const EventDisplay: Component<EventDisplayProps> = (props) => {
@@ -39,10 +40,10 @@ const EventDisplay: Component<EventDisplayProps> = (props) => {
<EventLink eventId={props.event.id} kind={props.event.kind} />
</span>
</Match>
<Match when={props.event.kind === (Kind.Text as number)}>
<Match when={props.event.kind === Kind.ShortTextNote}>
<TextNote event={props.event} embedding={props.actions} actions={props.actions} />
</Match>
<Match when={props.event.kind === (Kind.Repost as number)}>
<Match when={props.event.kind === Kind.Repost}>
<Repost event={props.event} />
</Match>
</Switch>

View File

@@ -1,6 +1,6 @@
import { type Component, Show } from 'solid-js';
import { type Event as NostrEvent } from 'nostr-tools';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import EmojiDisplay from '@/components/EmojiDisplay';
import TextNote from '@/components/event/TextNote';

View File

@@ -2,7 +2,7 @@
import { type Component, createMemo } from 'solid-js';
import ArrowPathRoundedSquare from 'heroicons/24/outline/arrow-path-rounded-square.svg';
import { Event as NostrEvent } from 'nostr-tools';
import { type Event as NostrEvent } from 'nostr-tools/pure';
// eslint-disable-next-line import/no-cycle
import EventDisplayById from '@/components/event/EventDisplayById';

View File

@@ -1,6 +1,6 @@
import { Show, For, createSignal, createMemo, type Component } from 'solid-js';
import { type Event as NostrEvent } from 'nostr-tools';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import Actions from '@/components/Actions';
// eslint-disable-next-line import/no-cycle

View File

@@ -1,6 +1,6 @@
import { Show } from 'solid-js';
import { Kind } from 'nostr-tools';
import * as Kind from 'nostr-tools/kinds';
// eslint-disable-next-line import/no-cycle
import EventDisplayById from '@/components/event/EventDisplayById';
@@ -24,7 +24,7 @@ const MentionedEventDisplay = (props: MentionedEventDisplayProps) => (
eventId={props.mentionedEvent.eventId}
embedding={false}
actions={false}
ensureKinds={[Kind.Text]}
ensureKinds={[Kind.ShortTextNote]}
/>
)}
</LazyLoad>

View File

@@ -1,6 +1,6 @@
import { For } from 'solid-js';
import { Kind } from 'nostr-tools';
import * as Kind from 'nostr-tools/kinds';
// eslint-disable-next-line import/no-cycle
import EventDisplayById from '@/components/event/EventDisplayById';
@@ -88,7 +88,7 @@ const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => {
eventId={item.data.data}
actions={false}
embedding={false}
ensureKinds={[Kind.Text]}
ensureKinds={[Kind.ShortTextNote]}
/>
</div>
);

View File

@@ -1,6 +1,6 @@
import { Component, createMemo } from 'solid-js';
import { type Event as NostrEvent } from 'nostr-tools';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import BasicModal from '@/components/modal/BasicModal';
import Copy from '@/components/utils/Copy';

View File

@@ -1,6 +1,7 @@
import { For, type Component, createMemo } from 'solid-js';
import { Kind, type Event as NostrEvent } from 'nostr-tools';
import * as Kind from 'nostr-tools/kinds';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import ColumnItem from '@/components/ColumnItem';
import EventDisplayById from '@/components/event/EventDisplayById';
@@ -40,7 +41,7 @@ const Bookmark: Component<BookmarkProps> = (props) => {
<For each={[...bookmarkedEventIds(), ...bookmarkedEventIdsPrivate()]}>
{(eventId) => (
<ColumnItem>
<EventDisplayById eventId={eventId} ensureKinds={[Kind.Text]} />
<EventDisplayById eventId={eventId} ensureKinds={[Kind.ShortTextNote]} />
</ColumnItem>
)}
</For>

View File

@@ -1,6 +1,7 @@
import { For, Switch, Match, type Component, Show } from 'solid-js';
import { Kind, type Event as NostrEvent } from 'nostr-tools';
import * as Kind from 'nostr-tools/kinds';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import ColumnItem from '@/components/ColumnItem';
import Reaction from '@/components/event/Reaction';
@@ -20,18 +21,18 @@ const Notification: Component<NotificationProps> = (props) => {
{(event) => (
<Show when={!shouldMuteEvent(event)}>
<Switch fallback={<div>unknown event</div>}>
<Match when={event.kind === (Kind.Text as number)}>
<Match when={event.kind === Kind.ShortTextNote}>
<ColumnItem>
<TextNote event={event} />
</ColumnItem>
</Match>
<Match when={event.kind === (Kind.Reaction as number)}>
<Match when={event.kind === Kind.Reaction}>
<ColumnItem>
<Reaction event={event} />
</ColumnItem>
</Match>
{/* TODO ちゃんとnotification用のコンポーネント使う */}
<Match when={event.kind === (Kind.Repost as number)}>
<Match when={event.kind === Kind.Repost}>
<ColumnItem>
<Repost event={event} />
</ColumnItem>

View File

@@ -1,6 +1,6 @@
import { For, type Component, Show } from 'solid-js';
import { type Event as NostrEvent } from 'nostr-tools';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import ColumnItem from '@/components/ColumnItem';
import EventDisplay from '@/components/event/EventDisplay';

View File

@@ -1,7 +1,8 @@
import { Switch, Match, type Component } from 'solid-js';
import uniq from 'lodash/uniq';
import { Filter, Event as NostrEvent } from 'nostr-tools';
import { type Filter } from 'nostr-tools/filter';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import Timeline from '@/components/timeline/Timeline';
import { type TimelineContent } from '@/components/timeline/TimelineContext';

View File

@@ -1,6 +1,6 @@
import { createContext, useContext } from 'solid-js';
import { Event as NostrEvent } from 'nostr-tools';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import { createStore } from 'solid-js/store';
export type TimelineContent = {

View File

@@ -1,5 +1,5 @@
// import { z } from 'zod';
import { type Filter } from 'nostr-tools';
import { type Filter } from 'nostr-tools/filter';
import { type ColumnProps } from '@/components/column/Column';
import { ContentFilter } from '@/core/contentFilter';

View File

@@ -2,7 +2,8 @@ import { type Accessor, type Setter } from 'solid-js';
import { sortBy } from 'lodash';
import uniq from 'lodash/uniq';
import { Kind, type Event as NostrEvent } from 'nostr-tools';
import * as Kind from 'nostr-tools/kinds';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import {
ColumnType,
@@ -194,7 +195,7 @@ const useConfig = (): UseConfig => {
const isPubkeyMuted = (pubkey: string) => config.mutedPubkeys.includes(pubkey);
const hasMutedKeyword = (event: NostrEvent) => {
if (event.kind === (Kind.Text as number)) {
if (event.kind === Kind.ShortTextNote) {
return config.mutedKeywords.some((keyword) => event.content.includes(keyword));
}
return false;
@@ -205,7 +206,7 @@ const useConfig = (): UseConfig => {
return (
isPubkeyMuted(event.pubkey) ||
ev.taggedPubkeys().some(isPubkeyMuted) ||
(event.kind === (Kind.Text as number) && hasMutedKeyword(event))
(event.kind === Kind.ShortTextNote && hasMutedKeyword(event))
);
};

View File

@@ -1,4 +1,4 @@
import { Event as NostrEvent } from 'nostr-tools';
import { Event as NostrEvent } from 'nostr-tools/pure';
import GenericEvent from '@/nostr/event/GenericEvent';
import Reaction from '@/nostr/event/Reaction';

View File

@@ -1,4 +1,4 @@
import { Kind, Event as NostrEvent } from 'nostr-tools';
import { Event as NostrEvent } from 'nostr-tools/pure';
import TagsBase from '@/nostr/event/TagsBase';
@@ -15,7 +15,7 @@ export default class GenericEvent extends TagsBase {
return this.rawEvent.sig;
}
get kind(): Kind {
get kind(): number {
return this.rawEvent.kind;
}

View File

@@ -1,4 +1,5 @@
import { Event as NostrEvent, Kind } from 'nostr-tools';
import * as Kind from 'nostr-tools/kinds';
import { Event as NostrEvent } from 'nostr-tools/pure';
import GenericEvent from '@/nostr/event/GenericEvent';
@@ -30,7 +31,7 @@ const reactionToReactionTypes = (event: Reaction): ReactionTypes => {
export default class Reaction extends GenericEvent {
constructor(rawEvent: NostrEvent) {
if (rawEvent.kind !== (Kind.Reaction as number)) {
if (rawEvent.kind !== Kind.Reaction) {
throw new TypeError('kind should be 7');
}
super(rawEvent);

View File

@@ -1,6 +1,6 @@
import assert from 'assert';
import { Kind } from 'nostr-tools';
import * as Kind from 'nostr-tools/kinds';
import { describe, it } from 'vitest';
import TextNote from '@/nostr/event/TextNote';
@@ -142,7 +142,7 @@ describe('TextNote', () => {
it('should return true if content of the event includes a NIP-19-styled note ID', () => {
const textnote = new TextNote({
id: '',
kind: Kind.Text,
kind: Kind.ShortTextNote,
content: 'nostr:note1qpwq08jv0sgrz68qev6eyu9vj6n2gmjl7n8g73jruzp37mguy3gq8d7asa',
created_at: 0,
pubkey: '',
@@ -159,7 +159,7 @@ describe('TextNote', () => {
it('should return true if content of the event includes a NIP-19-styled nevent ID', () => {
const textnote = new TextNote({
id: '',
kind: Kind.Text,
kind: Kind.ShortTextNote,
content: 'nostr:nevent1qqsttdc4rmxn8fqmk9s6823fqvpcmlm8ry6dmtu4m2snv0cm9sv70kq6urpy5',
created_at: 0,
pubkey: '',
@@ -178,7 +178,7 @@ describe('TextNote', () => {
it('should return a reply MarkedEventTag if the event has a reply tag', () => {
const textnote = new TextNote({
id: '',
kind: Kind.Text,
kind: Kind.ShortTextNote,
content: '',
created_at: 0,
pubkey: '',

View File

@@ -1,11 +1,12 @@
import { type Event as NostrEvent, Kind } from 'nostr-tools';
import * as Kind from 'nostr-tools/kinds';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import TextNoteLike from '@/nostr/event/TextNoteLike';
export default class TextNote extends TextNoteLike {
constructor(rawEvent: NostrEvent) {
if (rawEvent.kind !== (Kind.Text as number)) {
throw new TypeError('kind should be 1');
if (rawEvent.kind !== Kind.ShortTextNote) {
throw new TypeError(`kind should be 1 but it was ${rawEvent.kind}`);
}
super(rawEvent);
}

View File

@@ -1,6 +1,6 @@
import assert from 'assert';
import { Event as NostrEvent } from 'nostr-tools';
import { Event as NostrEvent } from 'nostr-tools/pure';
import { describe, it } from 'vitest';
import { compareEvents, pickLatestEvent } from '@/nostr/event/comparator';

View File

@@ -1,4 +1,4 @@
import { Event as NostrEvent } from 'nostr-tools';
import { Event as NostrEvent } from 'nostr-tools/pure';
/**
* compareEvents compares events by created_at and id.

View File

@@ -1,11 +1,8 @@
import { nip19 } from 'nostr-tools';
import { DecodeResult } from 'nostr-tools/lib/nip19';
import { decode, type DecodeResult } from 'nostr-tools/nip19';
import isValidId from '@/nostr/event/isValidId';
import TagsBase from '@/nostr/event/TagsBase';
const { decode } = nip19;
export type PlainText = {
type: 'PlainText';
content: string;

View File

@@ -1,6 +1,6 @@
import { QueryClient, QueryKey } from '@tanstack/solid-query';
import { uniqBy } from 'lodash';
import { Event as NostrEvent } from 'nostr-tools';
import { Event as NostrEvent } from 'nostr-tools/pure';
import { compareEvents, pickLatestEvent, sortEvents } from '@/nostr/event/comparator';
import { BatchedEventsTask, registerTask } from '@/nostr/useBatchedEvents';

View File

@@ -1,4 +1,7 @@
import { type Event as NostrEvent, type Filter, Kind, utils } from 'nostr-tools';
import { type Filter } from 'nostr-tools/filter';
import * as Kind from 'nostr-tools/kinds';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import { insertEventIntoDescendingList } from 'nostr-tools/utils';
import useConfig from '@/core/useConfig';
import { genericEvent } from '@/nostr/event';
@@ -35,7 +38,7 @@ type TaskArg = TaskArgs[number];
export class BatchedEventsTask<T = TaskArg> extends ObservableTask<T, NostrEvent[]> {
addEvent(event: NostrEvent) {
this.updateWith((current) => utils.insertEventIntoDescendingList(current ?? [], event));
this.updateWith((current) => insertEventIntoDescendingList(current ?? [], event));
}
firstEventPromise(): Promise<NostrEvent> {
@@ -64,9 +67,6 @@ setInterval(() => {
setActiveBatchSubscriptions(count);
}, 1000);
const isParameterizedReplaceableEvent = (event: NostrEvent) =>
event.kind >= 30000 && event.kind < 40000;
const keyForParameterizedReplaceableEvent = ({
kind,
author,
@@ -220,22 +220,22 @@ export const tasksRequestBuilder = (tasks: BatchedEventsTask[]) => {
];
const resolve = (event: NostrEvent) => {
if (event.kind === (Kind.Metadata as number)) {
if (event.kind === Kind.Metadata) {
if (profileTasks.resolve(event)) return;
}
if (event.kind === (Kind.Contacts as number)) {
if (event.kind === Kind.Contacts) {
if (followingsTasks.resolve(event)) return;
}
if (event.kind === (Kind.Repost as number)) {
if (event.kind === Kind.Repost) {
if (repostsTasks.resolve(event)) return;
}
if (event.kind === (Kind.Reaction as number)) {
if (event.kind === Kind.Reaction) {
if (reactionsTasks.resolve(event)) return;
}
if (event.kind === (Kind.Zap as number)) {
if (event.kind === Kind.Zap) {
if (zapReceiptsTasks.resolve(event)) return;
}
if (isParameterizedReplaceableEvent(event)) {
if (Kind.isParameterizedReplaceableKind(event.kind)) {
if (parameterizedReplaceableEventsTasks.resolve(event)) return;
}
eventTasks.resolve(event);
@@ -279,18 +279,17 @@ const { addTask, removeTask } = useBatch<BatchedEventsTask>(() => ({
const { config } = useConfig();
const pool = usePool();
const sub = pool().sub(config().relayUrls, filters, {});
count += 1;
sub.on('event', (event: NostrEvent & { id: string }) => {
const sub = pool().subscribeMany(config().relayUrls, filters, {
eoseTimeout: 15000,
onevent: (event: NostrEvent) => {
builder.resolve(event);
});
sub.on('eose', () => {
},
oneose: () => {
finalizeTasks();
sub.unsub();
sub.close();
count -= 1;
},
});
},
}));

View File

@@ -1,7 +1,5 @@
import { createMemo } from 'solid-js';
import { Kind } from 'nostr-tools';
import useConfig from '@/core/useConfig';
import useSubscription from '@/nostr/useSubscription';
@@ -15,7 +13,7 @@ export default function useBookmarks(propsProvider: () => UseBookmarksProps) {
const { events } = useSubscription(() => ({
relayUrls: config().relayUrls,
filters: [{ kinds: [30001 as Kind], authors: [props().pubkey] }],
filters: [{ kinds: [30001], authors: [props().pubkey] }],
continuous: true,
}));

View File

@@ -1,6 +1,6 @@
import { getEventHash, Kind, verifySignature, type UnsignedEvent } from 'nostr-tools';
import * as Kind from 'nostr-tools/kinds';
import { verifyEvent, getEventHash, type UnsignedEvent } from 'nostr-tools/pure';
// import '@/types/nostr.d';
import { ProfileWithOtherProperties, Profile } from '@/nostr/event/Profile';
import { ReactionTypes } from '@/nostr/event/Reaction';
import usePool from '@/nostr/usePool';
@@ -87,7 +87,7 @@ const useCommands = () => {
throw new Error('NIP-07 implementation not found');
}
const signedEvent = await window.nostr.signEvent(preSignedEvent);
if (!verifySignature({ ...signedEvent, id })) {
if (!verifyEvent({ ...signedEvent, id })) {
throw new Error('nostr.signEvent returned invalid data');
}
@@ -109,7 +109,7 @@ const useCommands = () => {
const tags = buildTags(params);
const preSignedEvent: UnsignedEvent = {
kind: 1,
kind: Kind.ShortTextNote,
pubkey,
created_at: epoch(),
tags,
@@ -145,7 +145,7 @@ const useCommands = () => {
}
const preSignedEvent: UnsignedEvent = {
kind: 7,
kind: Kind.Reaction,
pubkey,
created_at: epoch(),
tags,

View File

@@ -1,7 +1,7 @@
import { createMemo } from 'solid-js';
import { createQuery, type CreateQueryResult } from '@tanstack/solid-query';
import { Event as NostrEvent } from 'nostr-tools';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import { registerTask, BatchedEventsTask, EventTask } from '@/nostr/useBatchedEvents';
import timeout from '@/utils/timeout';

View File

@@ -1,7 +1,7 @@
import { createMemo } from 'solid-js';
import uniq from 'lodash/uniq';
import { Kind } from 'nostr-tools';
import { Metadata } from 'nostr-tools/kinds';
import useConfig from '@/core/useConfig';
import useSubscription from '@/nostr/useSubscription';
@@ -16,7 +16,7 @@ export default function useFollowers(propsProvider: () => UseFollowersProps) {
const { events } = useSubscription(() => ({
relayUrls: config().relayUrls,
filters: [{ kinds: [Kind.Contacts], '#p': [props().pubkey] }],
filters: [{ kinds: [Metadata], '#p': [props().pubkey] }],
limit: Number.MAX_SAFE_INTEGER,
continuous: true,
}));

View File

@@ -1,7 +1,7 @@
import { createMemo } from 'solid-js';
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
import { Event as NostrEvent } from 'nostr-tools';
import { Event as NostrEvent } from 'nostr-tools/pure';
import { genericEvent } from '@/nostr/event';
import { latestEventQuery } from '@/nostr/query';

View File

@@ -1,7 +1,7 @@
import { createMemo } from 'solid-js';
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
import { Event as NostrEvent } from 'nostr-tools';
import { Event as NostrEvent } from 'nostr-tools/pure';
import { pickLatestEvent } from '@/nostr/event/comparator';
import {

View File

@@ -1,8 +1,8 @@
import { createSignal } from 'solid-js';
import { SimplePool } from 'nostr-tools';
import { SimplePool } from 'nostr-tools/pool';
const [pool] = createSignal<SimplePool>(new SimplePool({ eoseSubTimeout: 12000 }));
const [pool] = createSignal<SimplePool>(new SimplePool());
const usePool = () => pool;

View File

@@ -1,7 +1,7 @@
import { createMemo } from 'solid-js';
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
import { Event as NostrEvent } from 'nostr-tools';
import { Event as NostrEvent } from 'nostr-tools/pure';
import { Profile, ProfileWithOtherProperties, safeParseProfile } from '@/nostr/event/Profile';
import { latestEventQuery } from '@/nostr/query';

View File

@@ -1,7 +1,7 @@
import { createMemo } from 'solid-js';
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
import { Event as NostrEvent } from 'nostr-tools';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import useConfig from '@/core/useConfig';
import { reaction } from '@/nostr/event';

View File

@@ -1,7 +1,7 @@
import { createMemo } from 'solid-js';
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
import { Event as NostrEvent } from 'nostr-tools';
import { Event as NostrEvent } from 'nostr-tools/pure';
import useConfig from '@/core/useConfig';
import { eventsQuery } from '@/nostr/query';

View File

@@ -1,7 +1,10 @@
import { createSignal, createEffect, onMount, onCleanup, on } from 'solid-js';
import uniqBy from 'lodash/uniqBy';
import { utils } from 'nostr-tools';
import { type Filter } from 'nostr-tools/filter';
import { type SubscribeManyParams } from 'nostr-tools/pool';
import { type Event as NostrEvent } from 'nostr-tools/pure';
import { insertEventIntoDescendingList } from 'nostr-tools/utils';
import useConfig from '@/core/useConfig';
import { sortEvents } from '@/nostr/event/comparator';
@@ -9,12 +12,10 @@ import usePool from '@/nostr/usePool';
import useStats from '@/nostr/useStats';
import epoch from '@/utils/epoch';
import type { Event as NostrEvent, Filter, SubscriptionOptions } from 'nostr-tools';
export type UseSubscriptionProps = {
relayUrls: string[];
filters: Filter[];
options?: SubscriptionOptions;
options?: SubscribeManyParams;
/**
* subscribe not only stored events but also new events published after the subscription
* default is true
@@ -72,7 +73,7 @@ const useSubscription = (propsProvider: () => UseSubscriptionProps | null) => {
}
setEvents((current) => {
const sorted = utils.insertEventIntoDescendingList(current, event).slice(0, limit);
const sorted = insertEventIntoDescendingList(current, event).slice(0, limit);
// FIXME なぜか重複して取得される問題があるが一旦uniqByで対処
// https://github.com/syusui-s/rabbit/issues/5
const deduped = uniqBy(sorted, (e) => e.id);
@@ -89,8 +90,6 @@ const useSubscription = (propsProvider: () => UseSubscriptionProps | null) => {
const props = propsProvider();
if (props == null) return;
const { relayUrls, filters, options, onEvent, onEOSE, continuous = true } = props;
const sub = pool().sub(relayUrls, filters, options);
let subscribing = true;
count += 1;
@@ -98,7 +97,13 @@ const useSubscription = (propsProvider: () => UseSubscriptionProps | null) => {
let eose = false;
const storedEvents: NostrEvent[] = [];
sub.on('event', (event: NostrEvent) => {
const sub = pool().subscribeMany(
relayUrls,
filters,
options ?? {
eoseTimeout: 12000,
maxWait: 6000,
onevent: (event: NostrEvent) => {
if (onEvent != null) {
onEvent(event as NostrEvent & { id: string });
}
@@ -112,9 +117,8 @@ const useSubscription = (propsProvider: () => UseSubscriptionProps | null) => {
} else {
addEvent(event);
}
});
sub.on('eose', () => {
},
oneose: () => {
if (onEOSE != null) {
onEOSE();
}
@@ -123,13 +127,15 @@ const useSubscription = (propsProvider: () => UseSubscriptionProps | null) => {
setEvents(sortEvents(storedEvents));
if (!continuous) {
sub.unsub();
sub.close();
if (subscribing) {
subscribing = false;
count -= 1;
}
}
});
},
},
);
// avoid updating an array too rapidly while this is fetching stored events
let updating = false;
@@ -150,7 +156,7 @@ const useSubscription = (propsProvider: () => UseSubscriptionProps | null) => {
onCleanup(() => {
console.debug('startSubscription: end');
sub.unsub();
sub.close();
if (subscribing) {
subscribing = false;
count -= 1;

View File

@@ -1,15 +1,16 @@
import { createMemo, type Accessor } from 'solid-js';
import { createQuery, type CreateQueryResult } from '@tanstack/solid-query';
import { nip05, nip19 } from 'nostr-tools';
import { queryProfile } from 'nostr-tools/nip05';
import { type ProfilePointer } from 'nostr-tools/nip19';
export type UseVerificationProps = {
nip05: string;
};
export type UseVerification = {
verification: Accessor<nip19.ProfilePointer | null>;
query: CreateQueryResult<nip19.ProfilePointer | null>;
verification: Accessor<ProfilePointer | null>;
query: CreateQueryResult<ProfilePointer | null>;
};
const useVerification = (propsProvider: () => UseVerificationProps | null): UseVerification => {
@@ -22,7 +23,7 @@ const useVerification = (propsProvider: () => UseVerificationProps | null): UseV
const [, currentProps] = queryKey;
if (currentProps == null) return Promise.resolve(null);
const { nip05: nip05string } = currentProps;
return nip05.queryProfile(nip05string);
return queryProfile(nip05string);
},
staleTime: 30 * 60 * 1000, // 30 min
cacheTime: 24 * 60 * 60 * 1000, // 24 hour

View File

@@ -25,9 +25,12 @@ const Home: Component = () => {
config().relayUrls.map(async (relayUrl) => {
try {
const relay = await pool().ensureRelay(relayUrl);
relay.on('notice', (msg: string) => {
relay.onnotice = (msg: string) => {
console.error(`NOTICE: ${relayUrl}: ${msg}`);
});
};
relay.onclose = () => {
console.warn(`CLOSE: ${relayUrl}`);
};
} catch (err) {
console.error('ensureRelay failed', err);
}

View File

@@ -1,7 +1,7 @@
import { createEffect, onMount } from 'solid-js';
import { useNavigate, useParams } from '@solidjs/router';
import { nip19 } from 'nostr-tools';
import { decode } from 'nostr-tools/nip19';
import GlobalModal from '@/components/modal/GlobalModal';
import SideBar from '@/components/SideBar';
@@ -25,7 +25,7 @@ const Permalink = () => {
onMount(() => {
if (params.id != null) {
try {
const decoded = nip19.decode(params.id);
const decoded = decode(params.id);
if (decoded.type === 'npub') {
showProfile(decoded.data);
}

View File

@@ -1,6 +1,6 @@
// The original code was published under the public domain license (CC0-1.0).
// https://gist.github.com/syusui-s/cd5482ddfc83792b54a756759acbda55
import { type UnsignedEvent, type Event as NostrEvent } from 'nostr-tools';
import { type UnsignedEvent, type Event as NostrEvent } from 'nostr-tools/pure';
type NostrAPI = {
/** returns a public key as hex */

View File

@@ -1,6 +1,4 @@
import { nip19 } from 'nostr-tools';
const { npubEncode } = nip19;
import { npubEncode } from 'nostr-tools/nip19';
const npubEncodeFallback = (pubkey: string): string => {
try {

View File

@@ -3,7 +3,7 @@
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"lib": ["dom", "dom.iterable", "esnext"],
"allowSyntheticDefaultImports": true,