mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-18 14:34:25 +01:00
update
This commit is contained in:
@@ -1,11 +1,7 @@
|
||||
# nostRabbit 🐰
|
||||
# Rabbit 🐰
|
||||
|
||||
A nostr client like TweetDeck powered by SolidJS.
|
||||
|
||||
## Supported features
|
||||
|
||||
- [ ] Posting a new text note. (NIP-01)
|
||||
|
||||
## 使い方
|
||||
|
||||
1. NIP-07に対応したブラウザ拡張機能のインストールが事前に必要です
|
||||
|
||||
58
package-lock.json
generated
58
package-lock.json
generated
@@ -19,7 +19,7 @@
|
||||
"@types/lodash": "^4.14.191",
|
||||
"heroicons": "^2.0.15",
|
||||
"lodash": "^4.17.21",
|
||||
"nostr-tools": "^1.3.2",
|
||||
"nostr-tools": "^1.7.4",
|
||||
"solid-js": "^1.6.9",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"zod": "^3.20.6"
|
||||
@@ -1187,9 +1187,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-0.5.9.tgz",
|
||||
"integrity": "sha512-7lN1Qh6d8DUGmfN36XRsbN/WcGIPNtTGhkw26vWId/DlCIGsYJJootTtPGghTLcn/AaXPx2Q0b3cacrwXa7OVw=="
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.0.0.tgz",
|
||||
"integrity": "sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg=="
|
||||
},
|
||||
"node_modules/@noble/secp256k1": {
|
||||
"version": "1.7.1",
|
||||
@@ -5889,15 +5889,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nostr-tools": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.3.2.tgz",
|
||||
"integrity": "sha512-LKVfGDkTSzNFSIUHhCDKHogfFaToKrqzUAXJOJhnYKvBws1/XW52xOYZlfYnY9jc/7C+ylq8njFIL5c5e6ObjA==",
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.7.4.tgz",
|
||||
"integrity": "sha512-YowDJ+S3UW9KCYPDZfZXXMITrJSMjiCmFOK5HohyKkg+w6EipFUTkFRBPRA2BTLXO/qw8gukKXfL0B7Dv3jtcQ==",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "^0.5.7",
|
||||
"@noble/secp256k1": "^1.7.0",
|
||||
"@noble/hashes": "1.0.0",
|
||||
"@noble/secp256k1": "^1.7.1",
|
||||
"@scure/base": "^1.1.1",
|
||||
"@scure/bip32": "^1.1.1",
|
||||
"@scure/bip39": "^1.1.0"
|
||||
"@scure/bip32": "^1.1.5",
|
||||
"@scure/bip39": "^1.1.1",
|
||||
"prettier": "^2.8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-normalize-package-bin": {
|
||||
@@ -6910,10 +6911,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
|
||||
"integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
|
||||
"dev": true,
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
|
||||
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
},
|
||||
@@ -9337,9 +9337,9 @@
|
||||
}
|
||||
},
|
||||
"@noble/hashes": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-0.5.9.tgz",
|
||||
"integrity": "sha512-7lN1Qh6d8DUGmfN36XRsbN/WcGIPNtTGhkw26vWId/DlCIGsYJJootTtPGghTLcn/AaXPx2Q0b3cacrwXa7OVw=="
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.0.0.tgz",
|
||||
"integrity": "sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg=="
|
||||
},
|
||||
"@noble/secp256k1": {
|
||||
"version": "1.7.1",
|
||||
@@ -12732,15 +12732,16 @@
|
||||
"dev": true
|
||||
},
|
||||
"nostr-tools": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.3.2.tgz",
|
||||
"integrity": "sha512-LKVfGDkTSzNFSIUHhCDKHogfFaToKrqzUAXJOJhnYKvBws1/XW52xOYZlfYnY9jc/7C+ylq8njFIL5c5e6ObjA==",
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.7.4.tgz",
|
||||
"integrity": "sha512-YowDJ+S3UW9KCYPDZfZXXMITrJSMjiCmFOK5HohyKkg+w6EipFUTkFRBPRA2BTLXO/qw8gukKXfL0B7Dv3jtcQ==",
|
||||
"requires": {
|
||||
"@noble/hashes": "^0.5.7",
|
||||
"@noble/secp256k1": "^1.7.0",
|
||||
"@noble/hashes": "1.0.0",
|
||||
"@noble/secp256k1": "^1.7.1",
|
||||
"@scure/base": "^1.1.1",
|
||||
"@scure/bip32": "^1.1.1",
|
||||
"@scure/bip39": "^1.1.0"
|
||||
"@scure/bip32": "^1.1.5",
|
||||
"@scure/bip39": "^1.1.1",
|
||||
"prettier": "^2.8.4"
|
||||
}
|
||||
},
|
||||
"npm-normalize-package-bin": {
|
||||
@@ -13483,10 +13484,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"prettier": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
|
||||
"integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==",
|
||||
"dev": true
|
||||
"version": "2.8.4",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
|
||||
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw=="
|
||||
},
|
||||
"prettier-linter-helpers": {
|
||||
"version": "1.0.0",
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"@types/lodash": "^4.14.191",
|
||||
"heroicons": "^2.0.15",
|
||||
"lodash": "^4.17.21",
|
||||
"nostr-tools": "^1.3.2",
|
||||
"nostr-tools": "^1.7.4",
|
||||
"solid-js": "^1.6.9",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"zod": "^3.20.6"
|
||||
|
||||
@@ -6,6 +6,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/solid-query';
|
||||
|
||||
import Home from '@/pages/Home';
|
||||
import NotFound from '@/pages/NotFound';
|
||||
import Hello from '@/pages/Hello';
|
||||
|
||||
const queryClient = new QueryClient({});
|
||||
|
||||
@@ -20,6 +21,7 @@ const App: Component = () => (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Routes>
|
||||
<Route path="/" element={() => <Home />} />
|
||||
<Route path="/hello" element={() => <Hello />} />
|
||||
<Route path="/*" element={() => <NotFound />} />
|
||||
</Routes>
|
||||
</QueryClientProvider>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createMemo } from 'solid-js';
|
||||
import { type Event as NostrEvent } from 'nostr-tools/event';
|
||||
import { type Filter } from 'nostr-tools/filter';
|
||||
import { type Event as NostrEvent, type Filter } from 'nostr-tools';
|
||||
|
||||
import useConfig from '@/clients/useConfig';
|
||||
import useBatch, { type Task } from '@/clients/useBatch';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createSignal, createMemo, type Signal, type Accessor } from 'solid-js';
|
||||
import { type Event as NostrEvent } from 'nostr-tools/event';
|
||||
import { type Filter } from 'nostr-tools/filter';
|
||||
import { type Event as NostrEvent, type Filter } from 'nostr-tools';
|
||||
|
||||
import useConfig from '@/clients/useConfig';
|
||||
import useBatch, { type Task } from '@/clients/useBatch';
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { createQuery } from '@tanstack/solid-query';
|
||||
import { type UseSubscriptionProps } from '@/clients/useSubscription';
|
||||
import type { Event as NostrEvent } from 'nostr-tools/event';
|
||||
import type { Filter } from 'nostr-tools/filter';
|
||||
import type { SimplePool } from 'nostr-tools/pool';
|
||||
import type { SubscriptionOptions } from 'nostr-tools/relay';
|
||||
import type { Event as NostrEvent, Filter, SimplePool, SubscriptionOptions } from 'nostr-tools';
|
||||
import usePool from './usePool';
|
||||
|
||||
type GetEventsArgs = {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { getEventHash } from 'nostr-tools/event';
|
||||
import type { Event as NostrEvent } from 'nostr-tools/event';
|
||||
import type { Pub } from 'nostr-tools/relay';
|
||||
import { getEventHash, type Event as NostrEvent, type Pub } from 'nostr-tools';
|
||||
|
||||
import '@/types/nostr.d';
|
||||
import usePool from '@/clients/usePool';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createMemo, type Accessor } from 'solid-js';
|
||||
import { type Event as NostrEvent } from 'nostr-tools/event';
|
||||
import { type Event as NostrEvent } from 'nostr-tools';
|
||||
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
|
||||
|
||||
import useBatchedEvents, { type BatchedEvents } from '@/clients/useBatchedEvents';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createMemo, type Accessor } from 'solid-js';
|
||||
import { type Event as NostrEvent } from 'nostr-tools/event';
|
||||
import { type Event as NostrEvent } from 'nostr-tools';
|
||||
import { createQuery, type CreateQueryResult } from '@tanstack/solid-query';
|
||||
import timeout from '@/utils/timeout';
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createMemo, type Accessor } from 'solid-js';
|
||||
import { type Event as NostrEvent } from 'nostr-tools/event';
|
||||
import { type Filter } from 'nostr-tools/filter';
|
||||
import { type Event as NostrEvent, type Filter } from 'nostr-tools';
|
||||
import { createQuery, type CreateQueryResult } from '@tanstack/solid-query';
|
||||
|
||||
import useBatchedEvent from '@/clients/useBatchedEvent';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createMemo, type Accessor } from 'solid-js';
|
||||
import { type Event as NostrEvent } from 'nostr-tools/event';
|
||||
import { type Event as NostrEvent } from 'nostr-tools';
|
||||
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
|
||||
|
||||
import useBatchedEvents, { type BatchedEvents } from '@/clients/useBatchedEvents';
|
||||
@@ -19,6 +19,7 @@ export type UseReactions = {
|
||||
};
|
||||
|
||||
const { exec } = useBatchedEvents<UseReactionsProps>(() => ({
|
||||
interval: 5000,
|
||||
generateKey: ({ eventId }) => eventId,
|
||||
mergeFilters: (args) => {
|
||||
const eventIds = args.map((arg) => arg.eventId);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { createSignal, createEffect, onCleanup } from 'solid-js';
|
||||
import type { Event as NostrEvent } from 'nostr-tools/event';
|
||||
import type { Filter } from 'nostr-tools/filter';
|
||||
import type { SubscriptionOptions } from 'nostr-tools/relay';
|
||||
import type { Event as NostrEvent, Filter, SubscriptionOptions } from 'nostr-tools';
|
||||
import usePool from '@/clients/usePool';
|
||||
|
||||
export type UseSubscriptionProps = {
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// NORMAL (implicit AND)
|
||||
// A filter 'hello world' should match 'hello world'
|
||||
// A filter "Lorem amet" should match "Lorem ipsum dolor sit amet"
|
||||
//
|
||||
// AND
|
||||
//
|
||||
// A filter "Lorem AND amet" should match "Lorem ipsum dolor sit amet"
|
||||
//
|
||||
// CASE
|
||||
// A filter 'hello world' should match 'HELLO WORLD'
|
||||
// A filter 'HELLO WORLD' should match 'hello world'
|
||||
//
|
||||
// DOUBLEQUOTE
|
||||
// A filter '"HELLO WORLD"' should match 'HELLO WORLD'
|
||||
// A filter '"HELLO WORLD"' should not match 'hello world'
|
||||
@@ -29,3 +29,20 @@ const applyContentFilter = (contentFilter: ContentFilter): boolean => {
|
||||
// TODO implement
|
||||
throw new Error('NotImplemented');
|
||||
};
|
||||
|
||||
// NORMAL (implicit AND)
|
||||
// A filter 'hello world' should match 'hello world'
|
||||
// A filter "Lorem amet" should match "Lorem ipsum dolor sit amet"
|
||||
//
|
||||
// AND
|
||||
//
|
||||
// A filter "Lorem AND amet" should match "Lorem ipsum dolor sit amet"
|
||||
//
|
||||
// CASE
|
||||
// A filter 'hello world' should match 'HELLO WORLD'
|
||||
// A filter 'HELLO WORLD' should match 'hello world'
|
||||
//
|
||||
// DOUBLEQUOTE
|
||||
// A filter '"HELLO WORLD"' should match 'HELLO WORLD'
|
||||
// A filter '"HELLO WORLD"' should not match 'hello world'
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ const useShortcutKeys = ({ shortcuts = defaultShortcut, onShortcut }: UseShortcu
|
||||
|
||||
onMount(() => {
|
||||
const handleKeydown: JSX.EventHandler<Window, KeyboardEvent> = (ev) => {
|
||||
console.log(ev);
|
||||
if (ev.type !== 'keydown') return;
|
||||
if (ev.target instanceof HTMLTextAreaElement || ev.target instanceof HTMLInputElement) return;
|
||||
|
||||
|
||||
64
src/pages/Hello.tsx
Normal file
64
src/pages/Hello.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { createSignal, onMount, Switch, Match, type Component } from 'solid-js';
|
||||
|
||||
type SignerStatus = 'checking' | 'available' | 'unavailable';
|
||||
|
||||
const Hello: Component = () => {
|
||||
const [signerStatus, setSignerStatus] = createSignal<SignerStatus>('checking');
|
||||
|
||||
const checkStatus = () => {
|
||||
if (window.nostr != null) {
|
||||
setSignerStatus('available');
|
||||
} else {
|
||||
setSignerStatus('unavailable');
|
||||
}
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
let count = 0;
|
||||
const intervalId = setInterval(() => {
|
||||
checkStatus();
|
||||
if (count >= 10) clearInterval(intervalId);
|
||||
count += 1;
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="mx-auto flex max-w-[640px] flex-col items-center p-4 text-stone-600">
|
||||
<div class="flex flex-col items-center gap-4 rounded bg-white p-4">
|
||||
<div class="text-7xl">🐰</div>
|
||||
<h1 class="text-5xl font-bold text-rose-300">Rabbit</h1>
|
||||
<div>Rabbit is a Web client for Nostr.</div>
|
||||
</div>
|
||||
<div class="p-8 shadow-md">
|
||||
<Switch>
|
||||
<Match when={signerStatus() === 'checking'}>
|
||||
拡張機能のインストール状況を確認中です...
|
||||
</Match>
|
||||
<Match when={signerStatus() === 'unavailable'}>
|
||||
<div class="pb-1 text-lg font-bold">拡張機能がインストールされていません!</div>
|
||||
<p>
|
||||
利用にはNIP-07に対応した拡張機能が必要です。
|
||||
<br />
|
||||
<a
|
||||
class="text-blue-500 underline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://scrapbox.io/nostr/NIP-07#63e1c10c8b8fcb00000584fc"
|
||||
>
|
||||
こちらを参考
|
||||
</a>
|
||||
に拡張機能をインストールしてください
|
||||
</p>
|
||||
</Match>
|
||||
<Match when={signerStatus() === 'available'}>
|
||||
<button class="rounded bg-rose-400 p-4 text-lg font-bold text-white hover:shadow-md">
|
||||
NIP-07 拡張機能でログイン
|
||||
</button>
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Hello;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createSignal, Show, For } from 'solid-js';
|
||||
import { createSignal, Show, For, createEffect } from 'solid-js';
|
||||
import type { Component } from 'solid-js';
|
||||
|
||||
import Column from '@/components/Column';
|
||||
@@ -7,6 +7,7 @@ import SideBar from '@/components/SideBar';
|
||||
import Timeline from '@/components/Timeline';
|
||||
import Notification from '@/components/Notification';
|
||||
import TextNote from '@/components/TextNote';
|
||||
import usePool from '@/clients/usePool';
|
||||
import useCommands from '@/clients/useCommands';
|
||||
import useConfig from '@/clients/useConfig';
|
||||
import useSubscription from '@/clients/useSubscription';
|
||||
@@ -20,9 +21,20 @@ useShortcutKeys({
|
||||
});
|
||||
|
||||
const Home: Component = () => {
|
||||
const pool = usePool();
|
||||
const [config] = useConfig();
|
||||
const pubkey = usePubkey();
|
||||
const commands = useCommands();
|
||||
|
||||
createEffect(() => {
|
||||
config().relayUrls.map(async (relayUrl) => {
|
||||
const relay = await pool().ensureRelay(relayUrl);
|
||||
relay.on('notice', (msg) => {
|
||||
console.error(`NOTICE: ${relayUrl}: ${msg}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const { followings } = useFollowings(() => ({
|
||||
relayUrls: config().relayUrls,
|
||||
pubkey: pubkey(),
|
||||
|
||||
Reference in New Issue
Block a user