diff --git a/.eslintrc.js b/.eslintrc.js
index 38699fe..12f0922 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -25,6 +25,8 @@ module.exports = {
},
plugins: ['import', 'solid', 'jsx-a11y', 'prettier', '@typescript-eslint', 'tailwindcss'],
rules: {
+ 'no-alert': ['off'],
+ 'no-console': ['off'],
'import/extensions': [
'error',
'ignorePackages',
@@ -33,9 +35,6 @@ module.exports = {
tsx: 'never',
},
],
- 'prettier/prettier': 'error',
- 'no-console': ['off'],
- 'no-alert': ['off'],
'import/order': [
'warn',
{
@@ -49,6 +48,16 @@ module.exports = {
],
},
],
+ 'jsx-a11y/label-has-associated-control': [
+ 'error',
+ {
+ labelComponents: ['label'],
+ labelAttributes: ['inputLabel'],
+ assert: 'both',
+ depth: 3,
+ },
+ ],
+ 'prettier/prettier': 'error',
},
settings: {
linkComponents: ['Link'],
diff --git a/src/components/Config.tsx b/src/components/Config.tsx
index 1de4434..b4ed8f2 100644
--- a/src/components/Config.tsx
+++ b/src/components/Config.tsx
@@ -3,7 +3,7 @@ import { createSignal, For, type JSX } from 'solid-js';
import XMark from 'heroicons/24/outline/x-mark.svg';
import Modal from '@/components/Modal';
-import useConfig, { type Config } from '@/nostr/useConfig';
+import useConfig, { type Config } from '@/core/useConfig';
import UserNameDisplay from './UserDisplayName';
@@ -214,6 +214,13 @@ const OtherConfig = () => {
}));
};
+ const toggleHideCount = () => {
+ setConfig((current) => ({
+ ...current,
+ hideCount: !(current.hideCount ?? false),
+ }));
+ };
+
return (
その他
@@ -229,6 +236,10 @@ const OtherConfig = () => {
画像をデフォルトで表示する
toggleShowImage()} />
+
+
いいねやリポスト、フォロワーなどの数を隠す
+
toggleHideCount()} />
+
{/*
リアクションのデフォルト
diff --git a/src/components/DeprecatedRepost.tsx b/src/components/DeprecatedRepost.tsx
index db298a1..4512ff0 100644
--- a/src/components/DeprecatedRepost.tsx
+++ b/src/components/DeprecatedRepost.tsx
@@ -6,9 +6,9 @@ import { Event as NostrEvent } from 'nostr-tools';
import ColumnItem from '@/components/ColumnItem';
import UserDisplayName from '@/components/UserDisplayName';
-import eventWrapper from '@/core/event';
import useFormatDate from '@/hooks/useFormatDate';
import useModalState from '@/hooks/useModalState';
+import eventWrapper from '@/nostr/event';
import TextNoteDisplayById from './textNote/TextNoteDisplayById';
diff --git a/src/components/NotePostForm.tsx b/src/components/NotePostForm.tsx
index 9fc8fb9..671d9b7 100644
--- a/src/components/NotePostForm.tsx
+++ b/src/components/NotePostForm.tsx
@@ -17,11 +17,11 @@ import uniq from 'lodash/uniq';
import { Event as NostrEvent } from 'nostr-tools';
import UserNameDisplay from '@/components/UserDisplayName';
-import eventWrapper from '@/core/event';
-import parseTextNote from '@/core/parseTextNote';
+import useConfig from '@/core/useConfig';
import { useHandleCommand } from '@/hooks/useCommandBus';
+import eventWrapper from '@/nostr/event';
+import parseTextNote, { ParsedTextNote } from '@/nostr/parseTextNote';
import useCommands, { PublishTextNoteParams } from '@/nostr/useCommands';
-import useConfig from '@/nostr/useConfig';
import usePubkey from '@/nostr/usePubkey';
import { uploadNostrBuild, uploadFiles } from '@/utils/imageUpload';
@@ -43,9 +43,7 @@ const placeholder = (mode: NotePostFormProps['mode']) => {
}
};
-const parseAndExtract = (content: string) => {
- const parsed = parseTextNote(content);
-
+const extract = (parsed: ParsedTextNote) => {
const hashtags: string[] = [];
const pubkeyReferences: string[] = [];
const eventReferences: string[] = [];
@@ -73,6 +71,18 @@ const parseAndExtract = (content: string) => {
};
};
+const format = (parsed: ParsedTextNote) => {
+ const content = [];
+ parsed.forEach((node) => {
+ if (node.type === 'Bech32Entity' && !node.isNIP19) {
+ content.push(`nostr:${node.content}`);
+ } else {
+ content.push(node.content);
+ }
+ });
+ return content.join('');
+};
+
const NotePostForm: Component
= (props) => {
let textAreaRef: HTMLTextAreaElement | undefined;
let fileInputRef: HTMLInputElement | undefined;
@@ -166,12 +176,14 @@ const NotePostForm: Component = (props) => {
return;
}
- const { hashtags, pubkeyReferences, eventReferences, urlReferences } = parseAndExtract(text());
+ const parsed = parseTextNote(text());
+ const { hashtags, pubkeyReferences, eventReferences, urlReferences } = extract(parsed);
+ const formattedContent = format(parsed);
let textNote: PublishTextNoteParams = {
relayUrls: config().relayUrls,
pubkey,
- content: text(),
+ content: formattedContent,
notifyPubkeys: pubkeyReferences,
mentionEventIds: eventReferences,
hashtags,
diff --git a/src/components/ProfileDisplay.tsx b/src/components/ProfileDisplay.tsx
index 44e0747..a1230a7 100644
--- a/src/components/ProfileDisplay.tsx
+++ b/src/components/ProfileDisplay.tsx
@@ -7,13 +7,14 @@ import GlobeAlt from 'heroicons/24/outline/globe-alt.svg';
import XMark from 'heroicons/24/outline/x-mark.svg';
import CheckCircle from 'heroicons/24/solid/check-circle.svg';
import ExclamationCircle from 'heroicons/24/solid/exclamation-circle.svg';
+import uniq from 'lodash/uniq';
import Modal from '@/components/Modal';
import Timeline from '@/components/Timeline';
import Copy from '@/components/utils/Copy';
import SafeLink from '@/components/utils/SafeLink';
+import useConfig from '@/core/useConfig';
import useCommands from '@/nostr/useCommands';
-import useConfig from '@/nostr/useConfig';
import useFollowers from '@/nostr/useFollowers';
import useFollowings from '@/nostr/useFollowings';
import useProfile from '@/nostr/useProfile';
@@ -125,7 +126,7 @@ const ProfileDisplay: Component = (props) => {
relayUrls: config().relayUrls,
pubkey: p,
content: myFollowingQuery.data?.content ?? '',
- followingPubkeys: [...myFollowingPubkeys(), props.pubkey],
+ followingPubkeys: uniq([...myFollowingPubkeys(), props.pubkey]),
});
};
@@ -325,25 +326,27 @@ const ProfileDisplay: Component = (props) => {
-
-
フォロワー
-
-
setShowFollowers(true)}
- >
- 読み込む
-
- }
- keyed
- >
-
-
+
+
+
フォロワー
+
+ setShowFollowers(true)}
+ >
+ 読み込む
+
+ }
+ keyed
+ >
+
+
+
-
+
0}>
diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx
index 890f2e4..61ca4d4 100644
--- a/src/components/SideBar.tsx
+++ b/src/components/SideBar.tsx
@@ -6,8 +6,8 @@ import PencilSquare from 'heroicons/24/solid/pencil-square.svg';
import Config from '@/components/Config';
import NotePostForm from '@/components/NotePostForm';
+import useConfig from '@/core/useConfig';
import { useHandleCommand } from '@/hooks/useCommandBus';
-import useConfig from '@/nostr/useConfig';
const SideBar: Component = () => {
let textAreaRef: HTMLTextAreaElement | undefined;
diff --git a/src/components/TextNote.tsx b/src/components/TextNote.tsx
index 25e8eca..3cb2f93 100644
--- a/src/components/TextNote.tsx
+++ b/src/components/TextNote.tsx
@@ -1,7 +1,7 @@
import { Show, type Component } from 'solid-js';
import ColumnItem from '@/components/ColumnItem';
-import useConfig from '@/nostr/useConfig';
+import useConfig from '@/core/useConfig';
import TextNoteDisplay, { TextNoteDisplayProps } from './textNote/TextNoteDisplay';
diff --git a/src/components/TimelineContentDisplay.tsx b/src/components/TimelineContentDisplay.tsx
index 3c82cf2..9c264b3 100644
--- a/src/components/TimelineContentDisplay.tsx
+++ b/src/components/TimelineContentDisplay.tsx
@@ -5,8 +5,8 @@ import { Filter, Event as NostrEvent } from 'nostr-tools';
import Timeline from '@/components/Timeline';
import { type TimelineContent } from '@/components/TimelineContext';
-import eventWrapper from '@/core/event';
-import useConfig from '@/nostr/useConfig';
+import useConfig from '@/core/useConfig';
+import eventWrapper from '@/nostr/event';
import useSubscription from '@/nostr/useSubscription';
const relatedEvents = (rawEvent: NostrEvent) => {
diff --git a/src/components/notification/Reaction.tsx b/src/components/notification/Reaction.tsx
index 5828ce1..7aff5f0 100644
--- a/src/components/notification/Reaction.tsx
+++ b/src/components/notification/Reaction.tsx
@@ -6,8 +6,8 @@ import { type Event as NostrEvent } from 'nostr-tools';
import ColumnItem from '@/components/ColumnItem';
import TextNoteDisplay from '@/components/textNote/TextNoteDisplay';
import UserDisplayName from '@/components/UserDisplayName';
-import eventWrapper from '@/core/event';
import useModalState from '@/hooks/useModalState';
+import eventWrapper from '@/nostr/event';
import useEvent from '@/nostr/useEvent';
import useProfile from '@/nostr/useProfile';
diff --git a/src/components/textNote/ContentWarningDisplay.tsx b/src/components/textNote/ContentWarningDisplay.tsx
index df184f3..0a798c0 100644
--- a/src/components/textNote/ContentWarningDisplay.tsx
+++ b/src/components/textNote/ContentWarningDisplay.tsx
@@ -1,6 +1,6 @@
import { createSignal, type Component, type JSX, Show } from 'solid-js';
-import { ContentWarning } from '@/core/event';
+import { ContentWarning } from '@/nostr/event';
export type ContentWarningDisplayProps = {
contentWarning: ContentWarning;
diff --git a/src/components/textNote/MentionedEventDisplay.tsx b/src/components/textNote/MentionedEventDisplay.tsx
index 1be5c7b..2dc8181 100644
--- a/src/components/textNote/MentionedEventDisplay.tsx
+++ b/src/components/textNote/MentionedEventDisplay.tsx
@@ -2,7 +2,7 @@ import { Show } from 'solid-js';
// eslint-disable-next-line import/no-cycle
import TextNoteDisplayById from '@/components/textNote/TextNoteDisplayById';
-import { type MentionedEvent } from '@/core/parseTextNote';
+import { type MentionedEvent } from '@/nostr/parseTextNote';
import EventLink from '../EventLink';
diff --git a/src/components/textNote/MentionedUserDisplay.tsx b/src/components/textNote/MentionedUserDisplay.tsx
index 2a715ec..928838a 100644
--- a/src/components/textNote/MentionedUserDisplay.tsx
+++ b/src/components/textNote/MentionedUserDisplay.tsx
@@ -1,7 +1,7 @@
import GeneralUserMentionDisplay from '@/components/textNote/GeneralUserMentionDisplay';
import useModalState from '@/hooks/useModalState';
-import type { MentionedUser } from '@/core/parseTextNote';
+import type { MentionedUser } from '@/nostr/parseTextNote';
export type MentionedUserDisplayProps = {
pubkey: string;
diff --git a/src/components/textNote/PlainTextDisplay.tsx b/src/components/textNote/PlainTextDisplay.tsx
index a43b3f9..7b6e6d9 100644
--- a/src/components/textNote/PlainTextDisplay.tsx
+++ b/src/components/textNote/PlainTextDisplay.tsx
@@ -1,4 +1,4 @@
-import type { PlainText } from '@/core/parseTextNote';
+import type { PlainText } from '@/nostr/parseTextNote';
export type PlainTextDisplayProps = {
plainText: PlainText;
diff --git a/src/components/textNote/TextNoteContentDisplay.tsx b/src/components/textNote/TextNoteContentDisplay.tsx
index dee25ab..db5e348 100644
--- a/src/components/textNote/TextNoteContentDisplay.tsx
+++ b/src/components/textNote/TextNoteContentDisplay.tsx
@@ -8,12 +8,12 @@ import MentionedUserDisplay from '@/components/textNote/MentionedUserDisplay';
import PlainTextDisplay from '@/components/textNote/PlainTextDisplay';
import TextNoteDisplayById from '@/components/textNote/TextNoteDisplayById';
import SafeLink from '@/components/utils/SafeLink';
-import eventWrapper from '@/core/event';
-import parseTextNote, { resolveTagReference, type ParsedTextNoteNode } from '@/core/parseTextNote';
+import useConfig from '@/core/useConfig';
+import eventWrapper from '@/nostr/event';
+import parseTextNote, { resolveTagReference, type ParsedTextNoteNode } from '@/nostr/parseTextNote';
import type { Event as NostrEvent } from 'nostr-tools';
-import useConfig from '@/nostr/useConfig';
import { isImageUrl } from '@/utils/imageUrl';
export type TextNoteContentDisplayProps = {
diff --git a/src/components/textNote/TextNoteDisplay.tsx b/src/components/textNote/TextNoteDisplay.tsx
index ebd2f49..5e2e9ac 100644
--- a/src/components/textNote/TextNoteDisplay.tsx
+++ b/src/components/textNote/TextNoteDisplay.tsx
@@ -15,11 +15,11 @@ import GeneralUserMentionDisplay from '@/components/textNote/GeneralUserMentionD
import TextNoteContentDisplay from '@/components/textNote/TextNoteContentDisplay';
import TextNoteDisplayById from '@/components/textNote/TextNoteDisplayById';
import { useTimelineContext } from '@/components/TimelineContext';
-import eventWrapper from '@/core/event';
+import useConfig from '@/core/useConfig';
import useFormatDate from '@/hooks/useFormatDate';
import useModalState from '@/hooks/useModalState';
+import eventWrapper from '@/nostr/event';
import useCommands from '@/nostr/useCommands';
-import useConfig from '@/nostr/useConfig';
import useProfile from '@/nostr/useProfile';
import usePubkey from '@/nostr/usePubkey';
import useReactions from '@/nostr/useReactions';
@@ -27,6 +27,7 @@ import useReposts from '@/nostr/useReposts';
import useSubscription from '@/nostr/useSubscription';
import ensureNonNull from '@/utils/ensureNonNull';
import npubEncodeFallback from '@/utils/npubEncodeFallback';
+import timeout from '@/utils/timeout';
import ContextMenu, { MenuItem } from '../ContextMenu';
@@ -41,23 +42,6 @@ const { noteEncode } = nip19;
const TextNoteDisplay: Component = (props) => {
let contentRef: HTMLDivElement | undefined;
- const menu: MenuItem[] = [
- {
- content: () => 'IDをコピー',
- onSelect: () => {
- navigator.clipboard.writeText(noteEncode(props.event.id)).catch((err) => window.alert(err));
- },
- },
- {
- content: () => 'JSONとしてコピー',
- onSelect: () => {
- navigator.clipboard
- .writeText(JSON.stringify(props.event))
- .catch((err) => window.alert(err));
- },
- },
- ];
-
const { config } = useConfig();
const formatDate = useFormatDate();
const pubkey = usePubkey();
@@ -116,6 +100,68 @@ const TextNoteDisplay: Component = (props) => {
},
});
+ const deleteMutation = createMutation({
+ mutationKey: ['delete', event().id],
+ mutationFn: (...params: Parameters) =>
+ commands
+ .delete(...params)
+ .then((promeses) => Promise.allSettled(promeses.map(timeout(10000)))),
+ onSuccess: (results) => {
+ // TODO タイムラインから削除する
+ const succeeded = results.filter((res) => res.status === 'fulfilled').length;
+ const failed = results.length - succeeded;
+ if (succeeded === results.length) {
+ window.alert('削除しました(画面の反映にはリロード)');
+ } else if (succeeded > 0) {
+ window.alert('一部のリレーで削除に失敗しました');
+ } else {
+ window.alert('すべてのリレーで削除に失敗しました');
+ }
+ },
+ onError: (err) => {
+ console.error('failed to delete', err);
+ },
+ });
+
+ const myPostMenu = (): MenuItem[] => {
+ if (event().pubkey !== pubkey()) return [];
+
+ return [
+ {
+ content: () => '削除',
+ onSelect: () => {
+ const p = pubkey();
+ if (p == null) return;
+
+ if (!window.confirm('本当に削除しますか?')) return;
+ deleteMutation.mutate({
+ relayUrls: config().relayUrls,
+ pubkey: p,
+ eventId: event().id,
+ });
+ },
+ },
+ ];
+ };
+
+ const menu: MenuItem[] = [
+ ...myPostMenu(),
+ {
+ content: () => 'IDをコピー',
+ onSelect: () => {
+ navigator.clipboard.writeText(noteEncode(props.event.id)).catch((err) => window.alert(err));
+ },
+ },
+ {
+ content: () => 'JSONとしてコピー',
+ onSelect: () => {
+ navigator.clipboard
+ .writeText(JSON.stringify(props.event))
+ .catch((err) => window.alert(err));
+ },
+ },
+ ];
+
const isReactedByMe = createMemo(() => {
const p = pubkey();
return p != null && isReactedBy(p);
@@ -313,7 +359,7 @@ const TextNoteDisplay: Component = (props) => {
>
- 0}>
+ 0}>
{reposts().length}
@@ -333,7 +379,7 @@ const TextNoteDisplay: Component = (props) => {
- 0}>
+ 0}>
{reactions().length}
diff --git a/src/components/textNote/TextNoteDisplayById.tsx b/src/components/textNote/TextNoteDisplayById.tsx
index bf6914f..571db71 100644
--- a/src/components/textNote/TextNoteDisplayById.tsx
+++ b/src/components/textNote/TextNoteDisplayById.tsx
@@ -2,7 +2,7 @@ import { Switch, Match, type Component } from 'solid-js';
// eslint-disable-next-line import/no-cycle
import TextNoteDisplay, { type TextNoteDisplayProps } from '@/components/textNote/TextNoteDisplay';
-import useConfig from '@/nostr/useConfig';
+import useConfig from '@/core/useConfig';
import useEvent from '@/nostr/useEvent';
import ensureNonNull from '@/utils/ensureNonNull';
diff --git a/src/components/utils/Popup.tsx b/src/components/utils/Popup.tsx
new file mode 100644
index 0000000..1975534
--- /dev/null
+++ b/src/components/utils/Popup.tsx
@@ -0,0 +1,20 @@
+import { createSignal, createEffect, type Component, type JSX } from 'solid-js';
+
+export type PopupProps = {
+ baseElemRef: HTMLElement | null;
+ children: JSX.Element;
+};
+
+const Popup: Component = (props) => {
+ let popupRef: HTMLDivElement | undefined;
+
+ const [style, setStyle] = createSignal({});
+
+ createEffect(() => {
+ if (props.baseElemRef == null || popupRef == null) return;
+
+ const baseElemRect = props.baseElemRef;
+ });
+
+ return null;
+};
diff --git a/src/nostr/useConfig.ts b/src/core/useConfig.ts
similarity index 98%
rename from src/nostr/useConfig.ts
rename to src/core/useConfig.ts
index 614bd72..fa3eb8b 100644
--- a/src/nostr/useConfig.ts
+++ b/src/core/useConfig.ts
@@ -15,6 +15,7 @@ export type Config = {
dateFormat: 'relative' | 'absolute-long' | 'absolute-short';
keepOpenPostForm: boolean;
showImage: boolean;
+ hideCount: boolean;
mutedPubkeys: string[];
mutedKeywords: string[];
};
@@ -64,6 +65,7 @@ const InitialConfig = (): Config => {
dateFormat: 'relative',
keepOpenPostForm: false,
showImage: true,
+ hideCount: false,
mutedPubkeys: [],
mutedKeywords: [],
};
diff --git a/src/hooks/useFormatDate.ts b/src/hooks/useFormatDate.ts
index 150df75..aa0c37e 100644
--- a/src/hooks/useFormatDate.ts
+++ b/src/hooks/useFormatDate.ts
@@ -1,5 +1,5 @@
+import useConfig from '@/core/useConfig';
import useDatePulser from '@/hooks/useDatePulser';
-import useConfig from '@/nostr/useConfig';
import { formatRelative, formatAbsoluteLong, formatAbsoluteShort } from '@/utils/formatDate';
// 7 seconds is used here so that the last digit of relative time is changed.
diff --git a/src/core/event.ts b/src/nostr/event.ts
similarity index 100%
rename from src/core/event.ts
rename to src/nostr/event.ts
diff --git a/src/core/parseTextNote.test.ts b/src/nostr/parseTextNote.test.ts
similarity index 99%
rename from src/core/parseTextNote.test.ts
rename to src/nostr/parseTextNote.test.ts
index bcae40d..7e5692f 100644
--- a/src/core/parseTextNote.test.ts
+++ b/src/nostr/parseTextNote.test.ts
@@ -162,6 +162,7 @@ describe('parseTextNote', () => {
{
type: 'Bech32Entity',
content: 'npub1srf6g8v2qpnecqg9l2kzehmkg0ym5f5rtnlsj6lhl8r6pmhger7q5mtt3q',
+ isNIP19: false,
data: {
type: 'npub',
data: '80d3a41d8a00679c0105faac2cdf7643c9ba26835cff096bf7f9c7a0eee8c8fc',
diff --git a/src/core/parseTextNote.ts b/src/nostr/parseTextNote.ts
similarity index 94%
rename from src/core/parseTextNote.ts
rename to src/nostr/parseTextNote.ts
index 0a9c127..b10ad6f 100644
--- a/src/core/parseTextNote.ts
+++ b/src/nostr/parseTextNote.ts
@@ -25,6 +25,7 @@ export type Bech32Entity = {
| { type: 'npub' | 'note'; data: string }
| { type: 'nprofile'; data: ProfilePointer }
| { type: 'nevent'; data: EventPointer };
+ isNIP19: boolean;
};
export type HashTag = {
@@ -61,7 +62,8 @@ const tagRefRegex = /(?:#\[(?\d+)\])/g;
const hashTagRegex = /#(?[\p{Letter}\p{Number}_]+)/gu;
// raw NIP-19 codes, NIP-21 links (NIP-27)
// nrelay and naddr is not supported by nostr-tools
-const mentionRegex = /(?:nostr:)?(?(npub|note|nprofile|nevent)1[ac-hj-np-z02-9]+)/gi;
+const mentionRegex =
+ /(?(?nostr:)?(?(?:npub|note|nprofile|nevent)1[ac-hj-np-z02-9]+))/gi;
const urlRegex =
/(?(?:https?|wss?):\/\/[-a-zA-Z0-9.]+(:\d{1,5})?(?:\/[-[\]~!$&'()*+.,:;@&=%\w]+|\/)*(?:\?[-[\]~!$&'()*+.,/:;%@&=\w?]+)?(?:#[-[\]~!$&'()*+.,/:;%@\w&=?#]+)?)/g;
@@ -105,11 +107,12 @@ const parseTextNote = (textNoteContent: string) => {
} else if (match.groups?.mention) {
pushPlainText(index);
try {
- const decoded = decode(match[1]);
+ const decoded = decode(match.groups.bech32);
const bech32Entity: Bech32Entity = {
type: 'Bech32Entity',
content: match[0],
data: decoded as Bech32Entity['data'],
+ isNIP19: match.groups.nip19 === 'nostr:',
};
result.push(bech32Entity);
} catch (e) {
diff --git a/src/nostr/useBatchedEvents.ts b/src/nostr/useBatchedEvents.ts
index 6a7e435..5d9a869 100644
--- a/src/nostr/useBatchedEvents.ts
+++ b/src/nostr/useBatchedEvents.ts
@@ -10,9 +10,9 @@ import {
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
import { type Event as NostrEvent, type Filter, Kind } from 'nostr-tools';
-import eventWrapper from '@/core/event';
+import useConfig from '@/core/useConfig';
+import eventWrapper from '@/nostr/event';
import useBatch, { type Task } from '@/nostr/useBatch';
-import useConfig from '@/nostr/useConfig';
import usePool from '@/nostr/usePool';
import useStats from '@/nostr/useStats';
import timeout from '@/utils/timeout';
diff --git a/src/nostr/useCommands.ts b/src/nostr/useCommands.ts
index f0b70bf..f71968f 100644
--- a/src/nostr/useCommands.ts
+++ b/src/nostr/useCommands.ts
@@ -192,6 +192,24 @@ const useCommands = () => {
};
return publishEvent(relayUrls, preSignedEvent);
},
+ async delete({
+ relayUrls,
+ pubkey,
+ eventId,
+ }: {
+ relayUrls: string[];
+ pubkey: string;
+ eventId: string;
+ }): Promise[]> {
+ const preSignedEvent: UnsignedEvent = {
+ kind: Kind.EventDeletion,
+ pubkey,
+ created_at: epoch(),
+ tags: [['e', eventId, '']],
+ content: '',
+ };
+ return publishEvent(relayUrls, preSignedEvent);
+ },
};
};
diff --git a/src/nostr/useFollowers.ts b/src/nostr/useFollowers.ts
index 39fb119..b2d56e2 100644
--- a/src/nostr/useFollowers.ts
+++ b/src/nostr/useFollowers.ts
@@ -3,7 +3,7 @@ import { createMemo, createSignal } from 'solid-js';
import uniq from 'lodash/uniq';
import { Kind } from 'nostr-tools';
-import useConfig from '@/nostr/useConfig';
+import useConfig from '@/core/useConfig';
import useSubscription from '@/nostr/useSubscription';
export type UseFollowersProps = {
diff --git a/src/nostr/useSubscription.ts b/src/nostr/useSubscription.ts
index d835ea6..a73a1ab 100644
--- a/src/nostr/useSubscription.ts
+++ b/src/nostr/useSubscription.ts
@@ -2,10 +2,9 @@ import { createSignal, createEffect, onCleanup, on } from 'solid-js';
import uniqBy from 'lodash/uniqBy';
+import useConfig from '@/core/useConfig';
import usePool from '@/nostr/usePool';
-
-import useConfig from './useConfig';
-import useStats from './useStats';
+import useStats from '@/nostr/useStats';
import type { Event as NostrEvent, Filter, SubscriptionOptions } from 'nostr-tools';
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx
index 20c4152..bb7d1e7 100644
--- a/src/pages/Home.tsx
+++ b/src/pages/Home.tsx
@@ -18,10 +18,10 @@ import Notification from '@/components/Notification';
import ProfileDisplay from '@/components/ProfileDisplay';
import SideBar from '@/components/SideBar';
import Timeline from '@/components/Timeline';
+import useConfig from '@/core/useConfig';
import useModalState from '@/hooks/useModalState';
import usePersistStatus from '@/hooks/usePersistStatus';
import { useMountShortcutKeys } from '@/hooks/useShortcutKeys';
-import useConfig from '@/nostr/useConfig';
import useFollowings from '@/nostr/useFollowings';
import usePool from '@/nostr/usePool';
import usePubkey from '@/nostr/usePubkey';
diff --git a/src/utils/epoch.ts b/src/utils/epoch.ts
index 3e681f0..dbcbcb7 100644
--- a/src/utils/epoch.ts
+++ b/src/utils/epoch.ts
@@ -1,3 +1,5 @@
-const epoch = (): number => Math.floor(Date.now() / 1000);
+export const toEpoch = (date: Date) => Math.floor(+date / 1000);
+
+const epoch = (): number => toEpoch(new Date());
export default epoch;