From ab61bfb2cdf06965e740a6bea663eb36bf88770d Mon Sep 17 00:00:00 2001
From: Ren Amamiya <123083837+reyamir@users.noreply.github.com>
Date: Wed, 16 Aug 2023 20:52:09 +0700
Subject: [PATCH] wip: clean up & refactor
---
package.json | 3 -
pnpm-lock.yaml | 78 ----
.../20230814083543_add_events_table.sql | 2 +-
.../20230816090508_clean_up_tables.sql | 8 +
src-tauri/src/main.rs | 7 +-
src/app/auth/create/step-1.tsx | 10 +-
src/app/auth/onboarding/step-2.tsx | 4 +-
src/app/auth/reset.tsx | 2 -
src/app/auth/unlock.tsx | 5 +-
src/app/auth/welcome.tsx | 4 +-
src/app/channel/components/blacklist.tsx | 58 ---
src/app/channel/components/createModal.tsx | 269 -----------
src/app/channel/components/item.tsx | 34 --
src/app/channel/components/list.tsx | 52 ---
src/app/channel/components/member.tsx | 24 -
src/app/channel/components/members.tsx | 28 --
src/app/channel/components/messages/form.tsx | 115 -----
.../components/messages/hideButton.tsx | 132 ------
src/app/channel/components/messages/item.tsx | 56 ---
.../components/messages/muteButton.tsx | 132 ------
.../components/messages/replyButton.tsx | 29 --
.../channel/components/messages/userMute.tsx | 40 --
.../channel/components/messages/userReply.tsx | 35 --
src/app/channel/components/metadata.tsx | 44 --
src/app/channel/components/mutedItem.tsx | 85 ----
src/app/channel/hooks/useChannelProfile.tsx | 36 --
src/app/channel/index.tsx | 149 ------
src/app/chats/components/list.tsx | 5 +-
src/app/chats/index.tsx | 33 +-
src/app/settings/account.tsx | 11 +-
src/app/settings/components/autoStart.tsx | 8 +-
src/app/settings/components/cacheTime.tsx | 9 +-
src/app/space/components/modals/feed.tsx | 4 +-
src/app/space/components/modals/hashtag.tsx | 4 +-
src/app/space/components/modals/image.tsx | 4 +-
src/app/space/components/widgets/feed.tsx | 8 +-
src/app/space/components/widgets/image.tsx | 36 --
src/app/space/components/widgets/network.tsx | 26 +-
src/app/space/hooks/useLiveThread.tsx | 48 --
src/app/space/hooks/useNewsfeed.tsx | 45 --
src/app/space/index.tsx | 10 +-
src/app/trending/components/trendingNotes.tsx | 10 +-
.../trending/components/trendingProfiles.tsx | 10 +-
src/libs/ndk/cache.ts | 6 +-
src/libs/ndk/instance.ts | 1 +
src/libs/storage.ts | 435 ------------------
src/libs/storage/instance.ts | 32 +-
src/shared/accounts/active.tsx | 77 ----
src/shared/composer/mention/suggestion.tsx | 4 +-
src/shared/composer/modal.tsx | 4 -
src/shared/editProfileModal.tsx | 11 +-
src/shared/logout.tsx | 15 +-
src/shared/notes/actions.tsx | 9 +-
src/shared/notes/hashtag.tsx | 4 +-
src/shared/notes/kinds/kind1.tsx | 4 +-
src/shared/notes/kinds/kind1063.tsx | 4 +-
src/shared/notes/kinds/sub.tsx | 2 +-
src/shared/notes/kinds/thread.tsx | 4 +-
src/shared/notes/kinds/unsupport.tsx | 4 +-
src/shared/notes/mentions/note.tsx | 4 +-
src/shared/notes/mentions/user.tsx | 4 +-
src/shared/notes/metadata.tsx | 20 +-
src/shared/notes/replies/item.tsx | 6 +-
src/shared/notes/replies/sub.tsx | 2 +-
src/shared/protected.tsx | 35 --
src/shared/titleBar.tsx | 5 +-
src/shared/user.tsx | 4 +-
src/stores/channels.tsx | 92 ----
src/stores/constants.tsx | 66 +--
src/stores/shortcuts.tsx | 4 -
src/stores/widgets.tsx | 9 +
src/utils/date.tsx | 23 -
src/utils/hooks/useAccount.tsx | 31 --
src/utils/hooks/useEvent.tsx | 2 +-
src/utils/hooks/useNostr.tsx | 32 +-
src/utils/hooks/useOpenGraph.tsx | 3 +-
src/utils/hooks/useSecureStorage.tsx | 38 --
src/utils/transform.tsx | 81 +---
vite.config.ts | 12 +-
79 files changed, 183 insertions(+), 2618 deletions(-)
create mode 100644 src-tauri/migrations/20230816090508_clean_up_tables.sql
delete mode 100644 src/app/channel/components/blacklist.tsx
delete mode 100644 src/app/channel/components/createModal.tsx
delete mode 100644 src/app/channel/components/item.tsx
delete mode 100644 src/app/channel/components/list.tsx
delete mode 100644 src/app/channel/components/member.tsx
delete mode 100644 src/app/channel/components/members.tsx
delete mode 100644 src/app/channel/components/messages/form.tsx
delete mode 100644 src/app/channel/components/messages/hideButton.tsx
delete mode 100644 src/app/channel/components/messages/item.tsx
delete mode 100644 src/app/channel/components/messages/muteButton.tsx
delete mode 100644 src/app/channel/components/messages/replyButton.tsx
delete mode 100644 src/app/channel/components/messages/userMute.tsx
delete mode 100644 src/app/channel/components/messages/userReply.tsx
delete mode 100644 src/app/channel/components/metadata.tsx
delete mode 100644 src/app/channel/components/mutedItem.tsx
delete mode 100644 src/app/channel/hooks/useChannelProfile.tsx
delete mode 100644 src/app/channel/index.tsx
delete mode 100644 src/app/space/components/widgets/image.tsx
delete mode 100644 src/app/space/hooks/useLiveThread.tsx
delete mode 100644 src/app/space/hooks/useNewsfeed.tsx
delete mode 100644 src/libs/storage.ts
delete mode 100644 src/shared/protected.tsx
delete mode 100644 src/stores/channels.tsx
delete mode 100644 src/stores/shortcuts.tsx
delete mode 100644 src/utils/hooks/useAccount.tsx
delete mode 100644 src/utils/hooks/useSecureStorage.tsx
diff --git a/package.json b/package.json
index 5e32c0aa..1564f7cd 100644
--- a/package.json
+++ b/package.json
@@ -20,14 +20,12 @@
"@ctrl/magnet-link": "^3.1.2",
"@headlessui/react": "^1.7.16",
"@nostr-dev-kit/ndk": "^0.8.17",
- "@nostr-fetch/adapter-ndk": "^0.12.2",
"@radix-ui/react-alert-dialog": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-popover": "^1.0.6",
"@radix-ui/react-tooltip": "^1.0.6",
"@tanstack/react-query": "^4.32.6",
- "@tanstack/react-query-devtools": "^4.32.6",
"@tanstack/react-virtual": "3.0.0-beta.54",
"@tauri-apps/api": "2.0.0-alpha.6",
"@tauri-apps/cli": "2.0.0-alpha.11",
@@ -61,7 +59,6 @@
"immer": "^10.0.2",
"light-bolt11-decoder": "^3.0.0",
"lru-cache": "^10.0.1",
- "nostr-fetch": "^0.12.2",
"nostr-tools": "^1.14.0",
"qrcode.react": "^3.1.0",
"react": "^18.2.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b3056977..02c3dbdf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -10,9 +10,6 @@ dependencies:
'@nostr-dev-kit/ndk':
specifier: ^0.8.17
version: 0.8.17(typescript@5.1.6)
- '@nostr-fetch/adapter-ndk':
- specifier: ^0.12.2
- version: 0.12.2(@nostr-dev-kit/ndk@0.8.17)(nostr-fetch@0.12.2)
'@radix-ui/react-alert-dialog':
specifier: ^1.0.4
version: 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.20)(react-dom@18.2.0)(react@18.2.0)
@@ -31,9 +28,6 @@ dependencies:
'@tanstack/react-query':
specifier: ^4.32.6
version: 4.32.6(react-dom@18.2.0)(react@18.2.0)
- '@tanstack/react-query-devtools':
- specifier: ^4.32.6
- version: 4.32.6(@tanstack/react-query@4.32.6)(react-dom@18.2.0)(react@18.2.0)
'@tanstack/react-virtual':
specifier: 3.0.0-beta.54
version: 3.0.0-beta.54(react@18.2.0)
@@ -133,9 +127,6 @@ dependencies:
lru-cache:
specifier: ^10.0.1
version: 10.0.1
- nostr-fetch:
- specifier: ^0.12.2
- version: 0.12.2
nostr-tools:
specifier: ^1.14.0
version: 1.14.0
@@ -1003,24 +994,6 @@ packages:
- typescript
dev: false
- /@nostr-fetch/adapter-ndk@0.12.2(@nostr-dev-kit/ndk@0.8.17)(nostr-fetch@0.12.2):
- resolution: {integrity: sha512-+7EVuxS5DDZvNo6qbfFp7xRHwIyjyi36hYkiQFDjbQ4gX5LKo9RIPB1P+1XGkOSDFshypTbovZCaFunscJ/zhQ==}
- peerDependencies:
- '@nostr-dev-kit/ndk': ^0.7.5
- nostr-fetch: ^0.12.2
- dependencies:
- '@nostr-dev-kit/ndk': 0.8.17(typescript@5.1.6)
- '@nostr-fetch/kernel': 0.12.2
- nostr-fetch: 0.12.2
- dev: false
-
- /@nostr-fetch/kernel@0.12.2:
- resolution: {integrity: sha512-ja7StOV33NmdtAMGfQIS0/R0dAkLRm3QxN6u/YAQdp5mXER4BYxiQKxUS/dCoTCSX986MH2zp9Fm0f76u4VaNQ==}
- dependencies:
- '@noble/curves': 1.1.0
- '@noble/hashes': 1.3.1
- dev: false
-
/@popperjs/core@2.11.8:
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
dev: false
@@ -1733,32 +1706,10 @@ packages:
tailwindcss: 3.3.3
dev: true
- /@tanstack/match-sorter-utils@8.8.4:
- resolution: {integrity: sha512-rKH8LjZiszWEvmi01NR72QWZ8m4xmXre0OOwlRGnjU01Eqz/QnN+cqpty2PJ0efHblq09+KilvyR7lsbzmXVEw==}
- engines: {node: '>=12'}
- dependencies:
- remove-accents: 0.4.2
- dev: false
-
/@tanstack/query-core@4.32.6:
resolution: {integrity: sha512-YVB+mVWENQwPyv+40qO7flMgKZ0uI41Ph7qXC2Zf1ft5AIGfnXnMZyifB2ghhZ27u+5wm5mlzO4Y6lwwadzxCA==}
dev: false
- /@tanstack/react-query-devtools@4.32.6(@tanstack/react-query@4.32.6)(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-Gd9pBkm2sbeze9P5Yp8R7y0rZVUdoIOhduomDjz138WdJuVbRS4Y8p6gX2uMJFsUFVe7jA6fX/D6NfQ9o5OS/A==}
- peerDependencies:
- '@tanstack/react-query': ^4.32.6
- react: ^16.8.0 || ^17.0.0 || ^18.0.0
- react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
- dependencies:
- '@tanstack/match-sorter-utils': 8.8.4
- '@tanstack/react-query': 4.32.6(react-dom@18.2.0)(react@18.2.0)
- react: 18.2.0
- react-dom: 18.2.0(react@18.2.0)
- superjson: 1.13.1
- use-sync-external-store: 1.2.0(react@18.2.0)
- dev: false
-
/@tanstack/react-query@4.32.6(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-AITu/IKJJJXsHHeXNBy5bclu12t08usMCY0vFC2dh9SP/w6JAk5U9GwfjOIPj3p+ATADZvxQPe8UiCtMLNeQbg==}
peerDependencies:
@@ -2998,13 +2949,6 @@ packages:
engines: {node: '>=12'}
dev: false
- /copy-anything@3.0.5:
- resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
- engines: {node: '>=12.13'}
- dependencies:
- is-what: 4.1.15
- dev: false
-
/create-esm-loader@0.2.3:
resolution: {integrity: sha512-cllzD6IU/mzXBs5OdQVWL3+ne5Elpu3Wdm7h5OldMbGXk76yr9XzHlQXWJ4zfs0ZAibe26rkbs4KvMAJm7fIZA==}
engines: {node: '>=14.x'}
@@ -4280,11 +4224,6 @@ packages:
dependencies:
call-bind: 1.0.2
- /is-what@4.1.15:
- resolution: {integrity: sha512-uKua1wfy3Yt+YqsD6mTUEa2zSi3G1oPlqTflgaPJ7z63vUGN5pxFpnQfeSLMFnJDEsdvOtkp1rUWkYjB4YfhgA==}
- engines: {node: '>=12.13'}
- dev: false
-
/isarray@2.0.5:
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
@@ -5124,12 +5063,6 @@ packages:
engines: {node: '>=14.16'}
dev: false
- /nostr-fetch@0.12.2:
- resolution: {integrity: sha512-0WH0LlaPcIvG5gOIwrGtzRwHpaZ+JQxH0XG7EjQcKpviePVmVKWK7UAGuzuWJj/V0iSqnDGOLSQ+HSEBjGVCEQ==}
- dependencies:
- '@nostr-fetch/kernel': 0.12.2
- dev: false
-
/nostr-tools@1.14.0:
resolution: {integrity: sha512-hwq2i1z5/DneXRE5Zu/TzQuKzVLcB+gOdfT9CeoiScvNw/2dWRGJvyTXIdF92d7NQ7nMcEwqVJPDytLpEpiiKw==}
dependencies:
@@ -6016,10 +5949,6 @@ packages:
unified: 10.1.2
dev: false
- /remove-accents@0.4.2:
- resolution: {integrity: sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==}
- dev: false
-
/resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
@@ -6354,13 +6283,6 @@ packages:
time-span: 5.1.0
dev: false
- /superjson@1.13.1:
- resolution: {integrity: sha512-AVH2eknm9DEd3qvxM4Sq+LTCkSXE2ssfh1t11MHMXyYXFQyQ1HLgVvV+guLTsaQnJU3gnaVo34TohHPulY/wLg==}
- engines: {node: '>=10'}
- dependencies:
- copy-anything: 3.0.5
- dev: false
-
/supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
diff --git a/src-tauri/migrations/20230814083543_add_events_table.sql b/src-tauri/migrations/20230814083543_add_events_table.sql
index 31049a6c..deb8ec52 100644
--- a/src-tauri/migrations/20230814083543_add_events_table.sql
+++ b/src-tauri/migrations/20230814083543_add_events_table.sql
@@ -2,7 +2,7 @@
CREATE TABLE
events (
id INTEGER NOT NULL PRIMARY KEY,
- cache_key TEXT NOT NULL UNIQUE,
+ cache_key TEXT NOT NULL,
event_id TEXT NOT NULL UNIQUE,
event_kind INTEGER NOT NULL DEFAULT 1,
event TEXT NOT NULL
diff --git a/src-tauri/migrations/20230816090508_clean_up_tables.sql b/src-tauri/migrations/20230816090508_clean_up_tables.sql
new file mode 100644
index 00000000..0814840b
--- /dev/null
+++ b/src-tauri/migrations/20230816090508_clean_up_tables.sql
@@ -0,0 +1,8 @@
+-- Add migration script here
+DROP TABLE IF EXISTS notes;
+
+DROP TABLE IF EXISTS chats;
+
+DROP TABLE IF EXISTS metadata;
+
+DROP TABLE IF EXISTS replies;
\ No newline at end of file
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index 0ba3c818..2bb51659 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -123,6 +123,12 @@ fn main() {
sql: include_str!("../migrations/20230814083543_add_events_table.sql"),
kind: MigrationKind::Up,
},
+ Migration {
+ version: 20230816090508,
+ description: "clean up tables",
+ sql: include_str!("../migrations/20230816090508_clean_up_tables.sql"),
+ kind: MigrationKind::Up,
+ },
],
)
.build(),
@@ -138,7 +144,6 @@ fn main() {
..Default::default()
};
- // let salt = Alphanumeric.sample_string(&mut rand::thread_rng(), 12);
let key = argon2::hash_raw(
password.as_ref(),
b"LUME_NEED_RUST_DEVELOPER_HELP_MAKE_SALT_RANDOM",
diff --git a/src/app/auth/create/step-1.tsx b/src/app/auth/create/step-1.tsx
index a75a52e4..bc6dce7c 100644
--- a/src/app/auth/create/step-1.tsx
+++ b/src/app/auth/create/step-1.tsx
@@ -42,9 +42,13 @@ export function CreateStep1Screen() {
};
const download = async () => {
- await writeTextFile('lume-keys.txt', `Public key: ${npub}\nPrivate key: ${nsec}`, {
- dir: BaseDirectory.Download,
- });
+ await writeTextFile(
+ `nostr_keys_${new Date().toISOString().slice(0, 10)}.txt`,
+ `Generated by Lume (lume.nu)\nPublic key: ${npub}\nPrivate key: ${nsec}`,
+ {
+ dir: BaseDirectory.Download,
+ }
+ );
setDownloaded(true);
};
diff --git a/src/app/auth/onboarding/step-2.tsx b/src/app/auth/onboarding/step-2.tsx
index 5f9ebd51..f530e509 100644
--- a/src/app/auth/onboarding/step-2.tsx
+++ b/src/app/auth/onboarding/step-2.tsx
@@ -5,7 +5,7 @@ import { useStorage } from '@libs/storage/provider';
import { ArrowRightCircleIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons';
-import { BLOCK_KINDS } from '@stores/constants';
+import { widgetKinds } from '@stores/constants';
import { useOnboarding } from '@stores/onboarding';
const data = [
@@ -52,7 +52,7 @@ export function OnboardStep2Screen() {
setLoading(true);
for (const tag of tags) {
- await db.createWidget(BLOCK_KINDS.hashtag, tag, tag.replace('#', ''));
+ await db.createWidget(widgetKinds.hashtag, tag, tag.replace('#', ''));
}
navigate('/auth/onboarding/step-3', { replace: true });
diff --git a/src/app/auth/reset.tsx b/src/app/auth/reset.tsx
index 77aaca65..52d0be9b 100644
--- a/src/app/auth/reset.tsx
+++ b/src/app/auth/reset.tsx
@@ -11,8 +11,6 @@ import { EyeOffIcon, EyeOnIcon, LoaderIcon } from '@shared/icons';
import { useStronghold } from '@stores/stronghold';
-import { useAccount } from '@utils/hooks/useAccount';
-
type FormValues = {
password: string;
privkey: string;
diff --git a/src/app/auth/unlock.tsx b/src/app/auth/unlock.tsx
index b0a7d343..bbbfc44d 100644
--- a/src/app/auth/unlock.tsx
+++ b/src/app/auth/unlock.tsx
@@ -60,18 +60,19 @@ export function UnlockScreen() {
// redirect to home
navigate('/', { replace: true });
} catch (e) {
+ setLoading(false);
setError('password', {
type: 'custom',
message: e,
});
}
} else {
+ setLoading(false);
setError('password', {
type: 'custom',
message: 'Password is required and must be greater than 3',
});
}
- setLoading(false);
};
return (
@@ -118,7 +119,7 @@ export function UnlockScreen() {
<>
Decryting...
-
+
>
) : (
<>
diff --git a/src/app/auth/welcome.tsx b/src/app/auth/welcome.tsx
index c623c628..d66bbd3c 100644
--- a/src/app/auth/welcome.tsx
+++ b/src/app/auth/welcome.tsx
@@ -1,10 +1,12 @@
-import { LogicalSize, appWindow } from '@tauri-apps/plugin-window';
+import { LogicalSize, getCurrent } from '@tauri-apps/plugin-window';
import { useEffect } from 'react';
import { Link } from 'react-router-dom';
import { ArrowRightCircleIcon } from '@shared/icons/arrowRightCircle';
export function WelcomeScreen() {
+ const appWindow = getCurrent();
+
async function setWindow() {
await appWindow.setSize(new LogicalSize(400, 500));
await appWindow.setResizable(false);
diff --git a/src/app/channel/components/blacklist.tsx b/src/app/channel/components/blacklist.tsx
deleted file mode 100644
index 798d898e..00000000
--- a/src/app/channel/components/blacklist.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { Popover, Transition } from '@headlessui/react';
-import { Fragment } from 'react';
-
-import { MutedItem } from '@app/channel/components/mutedItem';
-
-import { MuteIcon } from '@shared/icons';
-
-export function ChannelBlackList({ blacklist }: { blacklist: any }) {
- return (
-
- {({ open }) => (
- <>
-
-
-
-
-
-
-
-
-
- Your muted list
-
-
- Currently, unmute only affect locally, when you move to new client,
- muted list will loaded again
-
-
-
-
- {blacklist.map((item: any) => (
-
- ))}
-
-
-
-
- >
- )}
-
- );
-}
diff --git a/src/app/channel/components/createModal.tsx b/src/app/channel/components/createModal.tsx
deleted file mode 100644
index 62dec65e..00000000
--- a/src/app/channel/components/createModal.tsx
+++ /dev/null
@@ -1,269 +0,0 @@
-import { Dialog, Transition } from '@headlessui/react';
-import { NDKEvent, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
-import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { Fragment, useContext, useEffect, useState } from 'react';
-import { useForm } from 'react-hook-form';
-import { useNavigate } from 'react-router-dom';
-
-import { useNDK } from '@libs/ndk/provider';
-import { createChannel } from '@libs/storage';
-
-import { AvatarUploader } from '@shared/avatarUploader';
-import { CancelIcon, LoaderIcon, PlusIcon } from '@shared/icons';
-import { Image } from '@shared/image';
-
-import { DEFAULT_AVATAR } from '@stores/constants';
-
-import { dateToUnix } from '@utils/date';
-import { useAccount } from '@utils/hooks/useAccount';
-
-export function ChannelCreateModal() {
- const { ndk } = useNDK();
- const queryClient = useQueryClient();
- const navigate = useNavigate();
-
- const [isOpen, setIsOpen] = useState(false);
- const [loading, setLoading] = useState(false);
- const [image, setImage] = useState(DEFAULT_AVATAR);
-
- const { account } = useAccount();
-
- const closeModal = () => {
- setIsOpen(false);
- };
-
- const openModal = () => {
- setIsOpen(true);
- };
-
- const {
- register,
- handleSubmit,
- reset,
- setValue,
- formState: { isDirty, isValid },
- } = useForm();
-
- const addChannel = useMutation({
- mutationFn: (event: any) => {
- return createChannel(
- event.id,
- event.pubkey,
- event.name,
- event.picture,
- event.about,
- event.created_at
- );
- },
- onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ['channels'] });
- },
- });
-
- const onSubmit = (data: any) => {
- setLoading(true);
-
- try {
- const signer = new NDKPrivateKeySigner(account.privkey);
- ndk.signer = signer;
-
- const event = new NDKEvent(ndk);
- // build event
- event.content = JSON.stringify(data);
- event.kind = 40;
- event.created_at = dateToUnix();
- event.pubkey = account.pubkey;
- event.tags = [];
-
- // publish event
- event.publish();
-
- // insert to database
- addChannel.mutate({
- ...event,
- name: data.name,
- picture: data.picture,
- about: data.about,
- });
-
- // reset form
- reset();
-
- setTimeout(() => {
- // close modal
- setIsOpen(false);
- // redirect to channel page
- navigate(`/channel/${event.id}`);
- }, 1000);
- } catch (e) {
- console.log('error: ', e);
- }
- };
-
- useEffect(() => {
- setValue('picture', image);
- }, [setValue, image]);
-
- return (
- <>
-
-
-
-
-
- >
- );
-}
diff --git a/src/app/channel/components/item.tsx b/src/app/channel/components/item.tsx
deleted file mode 100644
index f75f1a09..00000000
--- a/src/app/channel/components/item.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { NavLink } from 'react-router-dom';
-import { twMerge } from 'tailwind-merge';
-
-import { useChannelProfile } from '@app/channel/hooks/useChannelProfile';
-
-export function ChannelsListItem({ data }: { data: any }) {
- const channel = useChannelProfile(data.event_id);
- return (
-
- twMerge(
- 'inline-flex h-9 items-center gap-2.5 rounded-md px-2.5',
- isActive ? 'bg-zinc-900/50 text-white' : ''
- )
- }
- >
-
- #
-
-
-
{channel?.name}
-
- {data.new_messages && (
-
- {data.new_messages}
-
- )}
-
-
-
- );
-}
diff --git a/src/app/channel/components/list.tsx b/src/app/channel/components/list.tsx
deleted file mode 100644
index d3f9246a..00000000
--- a/src/app/channel/components/list.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { useQuery } from '@tanstack/react-query';
-
-import { ChannelCreateModal } from '@app/channel/components/createModal';
-import { ChannelsListItem } from '@app/channel/components/item';
-
-import { getChannels } from '@libs/storage';
-
-export function ChannelsList() {
- const {
- status,
- data: channels,
- isFetching,
- } = useQuery(
- ['channels'],
- async () => {
- return await getChannels();
- },
- {
- refetchOnMount: false,
- refetchOnReconnect: false,
- refetchOnWindowFocus: false,
- }
- );
-
- return (
-
- {status === 'loading' ? (
- <>
-
-
- >
- ) : (
- channels.map((item: { event_id: string }) => (
-
- ))
- )}
- {isFetching && (
-
- )}
-
-
- );
-}
diff --git a/src/app/channel/components/member.tsx b/src/app/channel/components/member.tsx
deleted file mode 100644
index 22fa6ba8..00000000
--- a/src/app/channel/components/member.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Image } from '@shared/image';
-
-import { DEFAULT_AVATAR } from '@stores/constants';
-
-import { useProfile } from '@utils/hooks/useProfile';
-
-export function Member({ pubkey }: { pubkey: string }) {
- const { user, isError, isLoading } = useProfile(pubkey);
-
- return (
- <>
- {isError || isLoading ? (
-
- ) : (
-
- )}
- >
- );
-}
diff --git a/src/app/channel/components/members.tsx b/src/app/channel/components/members.tsx
deleted file mode 100644
index b1fdf40f..00000000
--- a/src/app/channel/components/members.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { useQuery } from '@tanstack/react-query';
-
-import { Member } from '@app/channel/components/member';
-
-import { getChannelUsers } from '@libs/storage';
-
-export function ChannelMembers({ id }: { id: string }) {
- const { status, data, isFetching } = useQuery(['channel-members', id], async () => {
- return await getChannelUsers(id);
- });
-
- return (
-
-
- Members
-
-
- {status === 'loading' || isFetching ? (
-
Loading...
- ) : (
- data.map((member: { pubkey: string }) => (
-
- ))
- )}
-
-
- );
-}
diff --git a/src/app/channel/components/messages/form.tsx b/src/app/channel/components/messages/form.tsx
deleted file mode 100644
index 234067f1..00000000
--- a/src/app/channel/components/messages/form.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { NDKEvent, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
-import { useContext, useState } from 'react';
-
-import { UserReply } from '@app/channel/components/messages/userReply';
-
-import { useNDK } from '@libs/ndk/provider';
-
-import { CancelIcon, EnterIcon } from '@shared/icons';
-import { MediaUploader } from '@shared/mediaUploader';
-
-import { useChannelMessages } from '@stores/channels';
-
-import { dateToUnix } from '@utils/date';
-import { useAccount } from '@utils/hooks/useAccount';
-
-export function ChannelMessageForm({ channelID }: { channelID: string }) {
- const { ndk } = useNDK();
-
- const [value, setValue] = useState('');
- const [replyTo, closeReply] = useChannelMessages((state: any) => [
- state.replyTo,
- state.closeReply,
- ]);
-
- const { account } = useAccount();
-
- const submit = () => {
- let tags: string[][];
-
- if (replyTo.id !== null) {
- tags = [
- ['e', channelID, '', 'root'],
- ['e', replyTo.id, '', 'reply'],
- ['p', replyTo.pubkey, ''],
- ];
- } else {
- tags = [['e', channelID, '', 'root']];
- }
-
- const signer = new NDKPrivateKeySigner(account.privkey);
- ndk.signer = signer;
-
- const event = new NDKEvent(ndk);
- // build event
- event.content = value;
- event.kind = 42;
- event.created_at = dateToUnix();
- event.pubkey = account.pubkey;
- event.tags = tags;
-
- // publish event
- event.publish();
-
- // reset state
- setValue('');
- };
-
- const handleEnterPress = (e) => {
- if (e.key === 'Enter' && !e.shiftKey) {
- e.preventDefault();
- submit();
- }
- };
-
- const stopReply = () => {
- closeReply();
- };
-
- return (
-
- {replyTo.id && (
-
-
-
-
-
-
- )}
-
- );
-}
diff --git a/src/app/channel/components/messages/hideButton.tsx b/src/app/channel/components/messages/hideButton.tsx
deleted file mode 100644
index 9477b41b..00000000
--- a/src/app/channel/components/messages/hideButton.tsx
+++ /dev/null
@@ -1,132 +0,0 @@
-import { Dialog, Transition } from '@headlessui/react';
-import { NDKEvent, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
-import { Fragment, useState } from 'react';
-
-import { useNDK } from '@libs/ndk/provider';
-
-import { CancelIcon, HideIcon } from '@shared/icons';
-
-import { useChannelMessages } from '@stores/channels';
-
-import { dateToUnix } from '@utils/date';
-import { useAccount } from '@utils/hooks/useAccount';
-
-export function MessageHideButton({ id }: { id: string }) {
- const { ndk } = useNDK();
- const hide = useChannelMessages((state: any) => state.hideMessage);
-
- const [isOpen, setIsOpen] = useState(false);
-
- const { account } = useAccount();
-
- const closeModal = () => {
- setIsOpen(false);
- };
-
- const openModal = () => {
- setIsOpen(true);
- };
-
- const hideMessage = () => {
- const signer = new NDKPrivateKeySigner(account.privkey);
- ndk.signer = signer;
-
- const event = new NDKEvent(ndk);
- // build event
- event.content = '';
- event.kind = 43;
- event.created_at = dateToUnix();
- event.pubkey = account.pubkey;
- event.tags = [['e', id]];
-
- // publish event
- event.publish();
-
- // update state
- hide(id);
-
- // close modal
- closeModal();
- };
-
- return (
- <>
-
-
-
-
- >
- );
-}
diff --git a/src/app/channel/components/messages/item.tsx b/src/app/channel/components/messages/item.tsx
deleted file mode 100644
index 0d61b904..00000000
--- a/src/app/channel/components/messages/item.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { MessageHideButton } from '@app/channel/components/messages/hideButton';
-import { MessageMuteButton } from '@app/channel/components/messages/muteButton';
-import { MessageReplyButton } from '@app/channel/components/messages/replyButton';
-
-import { MentionNote } from '@shared/notes/mentions/note';
-import { ImagePreview } from '@shared/notes/preview/image';
-import { LinkPreview } from '@shared/notes/preview/link';
-import { VideoPreview } from '@shared/notes/preview/video';
-import { User } from '@shared/user';
-
-import { parser } from '@utils/parser';
-import { LumeEvent } from '@utils/types';
-
-export function ChannelMessageItem({ data }: { data: LumeEvent }) {
- const content = parser(data);
-
- return (
-
-
-
-
-
- {content.parsed}
-
- {Array.isArray(content.images) && content.images.length ? (
-
- ) : (
- <>>
- )}
- {Array.isArray(content.videos) && content.videos.length ? (
-
- ) : (
- <>>
- )}
- {Array.isArray(content.links) && content.links.length ? (
-
- ) : (
- <>>
- )}
- {Array.isArray(content.notes) && content.notes.length ? (
- content.notes.map((note: string) =>
)
- ) : (
- <>>
- )}
-
-
-
-
- );
-}
diff --git a/src/app/channel/components/messages/muteButton.tsx b/src/app/channel/components/messages/muteButton.tsx
deleted file mode 100644
index fb6ebf7a..00000000
--- a/src/app/channel/components/messages/muteButton.tsx
+++ /dev/null
@@ -1,132 +0,0 @@
-import { Dialog, Transition } from '@headlessui/react';
-import { NDKEvent, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
-import { Fragment, useContext, useState } from 'react';
-
-import { useNDK } from '@libs/ndk/provider';
-
-import { CancelIcon, MuteIcon } from '@shared/icons';
-
-import { useChannelMessages } from '@stores/channels';
-
-import { dateToUnix } from '@utils/date';
-import { useAccount } from '@utils/hooks/useAccount';
-
-export function MessageMuteButton({ pubkey }: { pubkey: string }) {
- const { ndk } = useNDK();
- const mute = useChannelMessages((state: any) => state.muteUser);
-
- const [isOpen, setIsOpen] = useState(false);
-
- const { account } = useAccount();
-
- const closeModal = () => {
- setIsOpen(false);
- };
-
- const openModal = () => {
- setIsOpen(true);
- };
-
- const muteUser = () => {
- const signer = new NDKPrivateKeySigner(account.privkey);
- ndk.signer = signer;
-
- const event = new NDKEvent(ndk);
- // build event
- event.content = '';
- event.kind = 44;
- event.created_at = dateToUnix();
- event.pubkey = account.pubkey;
- event.tags = [['p', pubkey]];
-
- // publish event
- event.publish();
-
- // update state
- mute(pubkey);
-
- // close modal
- closeModal();
- };
-
- return (
- <>
-
-
-
-
- >
- );
-}
diff --git a/src/app/channel/components/messages/replyButton.tsx b/src/app/channel/components/messages/replyButton.tsx
deleted file mode 100644
index 535b0427..00000000
--- a/src/app/channel/components/messages/replyButton.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { ReplyMessageIcon } from '@shared/icons';
-
-import { useChannelMessages } from '@stores/channels';
-
-export function MessageReplyButton({
- id,
- pubkey,
- content,
-}: {
- id: string;
- pubkey: string;
- content: string;
-}) {
- const openReply = useChannelMessages((state: any) => state.openReply);
-
- const createReply = () => {
- openReply(id, pubkey, content);
- };
-
- return (
-
- );
-}
diff --git a/src/app/channel/components/messages/userMute.tsx b/src/app/channel/components/messages/userMute.tsx
deleted file mode 100644
index 08147b04..00000000
--- a/src/app/channel/components/messages/userMute.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Image } from '@shared/image';
-
-import { DEFAULT_AVATAR } from '@stores/constants';
-
-import { useProfile } from '@utils/hooks/useProfile';
-
-export function ChannelMessageUserMute({ pubkey }: { pubkey: string }) {
- const { user, isError, isLoading } = useProfile(pubkey);
-
- return (
-
- {isError || isLoading ? (
- <>
-
-
- >
- ) : (
- <>
-
-
-
-
-
- You has been muted this user
-
-
- >
- )}
-
- );
-}
diff --git a/src/app/channel/components/messages/userReply.tsx b/src/app/channel/components/messages/userReply.tsx
deleted file mode 100644
index 2ef3b200..00000000
--- a/src/app/channel/components/messages/userReply.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Image } from '@shared/image';
-
-import { DEFAULT_AVATAR } from '@stores/constants';
-
-import { useProfile } from '@utils/hooks/useProfile';
-import { shortenKey } from '@utils/shortenKey';
-
-export function UserReply({ pubkey }: { pubkey: string }) {
- const { user, isError, isLoading } = useProfile(pubkey);
-
- return (
-
- {isError || isLoading ? (
- <>
-
-
- >
- ) : (
- <>
-
-
-
-
- Replying to {user?.name || shortenKey(pubkey)}
-
- >
- )}
-
- );
-}
diff --git a/src/app/channel/components/metadata.tsx b/src/app/channel/components/metadata.tsx
deleted file mode 100644
index 01bce991..00000000
--- a/src/app/channel/components/metadata.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { nip19 } from 'nostr-tools';
-
-import { useChannelProfile } from '@app/channel/hooks/useChannelProfile';
-
-import { CopyIcon } from '@shared/icons';
-import { Image } from '@shared/image';
-
-import { DEFAULT_AVATAR } from '@stores/constants';
-
-export function ChannelMetadata({ id }: { id: string }) {
- const metadata = useChannelProfile(id);
- const noteID = id ? nip19.noteEncode(id) : null;
-
- const copyNoteID = async () => {
- const { writeText } = await import('@tauri-apps/plugin-clipboard-manager');
- if (noteID) {
- await writeText(noteID);
- }
- };
-
- return (
-
-
-
-
-
-
-
{metadata?.name}
-
-
-
- {metadata?.about || (noteID && `${noteID.substring(0, 24)}...`)}
-
-
-
- );
-}
diff --git a/src/app/channel/components/mutedItem.tsx b/src/app/channel/components/mutedItem.tsx
deleted file mode 100644
index 0ae95075..00000000
--- a/src/app/channel/components/mutedItem.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import { useState } from 'react';
-
-import { Image } from '@shared/image';
-
-import { DEFAULT_AVATAR } from '@stores/constants';
-
-import { useProfile } from '@utils/hooks/useProfile';
-import { shortenKey } from '@utils/shortenKey';
-
-export function MutedItem({ data }: { data: any }) {
- const { user, isError, isLoading } = useProfile(data.content);
- const [status, setStatus] = useState(data.status);
-
- const unmute = async () => {
- const { updateItemInBlacklist } = await import('@libs/storage');
- const res = await updateItemInBlacklist(data.content, 0);
- if (res) {
- setStatus(0);
- }
- };
-
- const mute = async () => {
- const { updateItemInBlacklist } = await import('@libs/storage');
- const res = await updateItemInBlacklist(data.content, 1);
- if (res) {
- setStatus(1);
- }
- };
-
- return (
-
- {isError || isLoading ? (
- <>
-
- >
- ) : (
- <>
-
-
-
-
-
-
- {user?.displayName || user?.name || 'Pleb'}
-
-
- {shortenKey(data.content)}
-
-
-
-
- {status === 1 ? (
-
- ) : (
-
- )}
-
- >
- )}
-
- );
-}
diff --git a/src/app/channel/hooks/useChannelProfile.tsx b/src/app/channel/hooks/useChannelProfile.tsx
deleted file mode 100644
index e2533d53..00000000
--- a/src/app/channel/hooks/useChannelProfile.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import { useQuery } from '@tanstack/react-query';
-import { useEffect } from 'react';
-
-import { useNDK } from '@libs/ndk/provider';
-import { getChannel, updateChannelMetadata } from '@libs/storage';
-
-export function useChannelProfile(id: string) {
- const { ndk } = useNDK();
- const { data } = useQuery(['channel-metadata', id], async () => {
- return await getChannel(id);
- });
-
- useEffect(() => {
- // subscribe to channel
- const sub = ndk.subscribe(
- {
- '#e': [id],
- kinds: [41],
- },
- {
- closeOnEose: true,
- }
- );
-
- sub.addListener('event', (event: { content: string }) => {
- // update in local database
- updateChannelMetadata(id, event.content);
- });
-
- return () => {
- sub.stop();
- };
- }, []);
-
- return data;
-}
diff --git a/src/app/channel/index.tsx b/src/app/channel/index.tsx
deleted file mode 100644
index 6ffe9298..00000000
--- a/src/app/channel/index.tsx
+++ /dev/null
@@ -1,149 +0,0 @@
-import { useCallback, useContext, useEffect, useLayoutEffect, useRef } from 'react';
-import { useParams } from 'react-router-dom';
-import { Virtuoso } from 'react-virtuoso';
-
-import { ChannelMembers } from '@app/channel/components/members';
-import { ChannelMessageForm } from '@app/channel/components/messages/form';
-import { ChannelMetadata } from '@app/channel/components/metadata';
-
-import { useNDK } from '@libs/ndk/provider';
-
-import { useChannelMessages } from '@stores/channels';
-
-import { dateToUnix, getHourAgo } from '@utils/date';
-import { LumeEvent } from '@utils/types';
-
-import { ChannelMessageItem } from './components/messages/item';
-
-const now = new Date();
-
-const Header = (
-
-
-
-
- {getHourAgo(24, now).toLocaleDateString('en-US', {
- weekday: 'long',
- year: 'numeric',
- month: 'long',
- day: 'numeric',
- })}
-
-
-
-);
-
-const Empty = (
-
-
- Nothing to see here yet
-
-
- Be the first to share a message in this channel.
-
-
-);
-
-export function ChannelScreen() {
- const { ndk } = useNDK();
- const virtuosoRef = useRef(null);
-
- const { id } = useParams();
-
- const [messages, fetchMessages, addMessage, clearMessages] = useChannelMessages(
- (state: any) => [state.messages, state.fetch, state.add, state.clear]
- );
-
- useLayoutEffect(() => {
- fetchMessages(id);
- }, [fetchMessages]);
-
- useEffect(() => {
- // subscribe to channel
- const sub = ndk.subscribe(
- {
- '#e': [id],
- kinds: [42],
- since: dateToUnix(),
- },
- { closeOnEose: false }
- );
-
- sub.addListener('event', (event: LumeEvent) => {
- addMessage(id, event);
- });
-
- return () => {
- clearMessages();
- sub.stop();
- };
- }, []);
-
- const itemContent: any = useCallback(
- (index: string | number) => {
- return ;
- },
- [messages]
- );
-
- const computeItemKey = useCallback(
- (index: string | number) => {
- return messages[index].event_id;
- },
- [messages]
- );
-
- return (
-
-
-
-
Public Channel
-
-
-
-
- {!messages ? (
-
Loading...
- ) : (
-
Header,
- EmptyPlaceholder: () => Empty,
- }}
- />
- )}
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/app/chats/components/list.tsx b/src/app/chats/components/list.tsx
index bcd7c010..ff227079 100644
--- a/src/app/chats/components/list.tsx
+++ b/src/app/chats/components/list.tsx
@@ -6,17 +6,14 @@ import { NewMessageModal } from '@app/chats/components/modal';
import { ChatsListSelfItem } from '@app/chats/components/self';
import { UnknownsModal } from '@app/chats/components/unknowns';
-import { useNDK } from '@libs/ndk/provider';
-import { getChats } from '@libs/storage';
import { useStorage } from '@libs/storage/provider';
import { Chats } from '@utils/types';
export function ChatsList() {
const { db } = useStorage();
- const { ndk } = useNDK();
const { status, data: chats } = useQuery(['chats'], async () => {
- return await getChats();
+ return { follows: [], unknowns: [] };
});
const renderItem = useCallback(
diff --git a/src/app/chats/index.tsx b/src/app/chats/index.tsx
index 2eebcedb..ef6fa6cc 100644
--- a/src/app/chats/index.tsx
+++ b/src/app/chats/index.tsx
@@ -1,5 +1,5 @@
import { NDKSubscription } from '@nostr-dev-kit/ndk';
-import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
+import { useQuery } from '@tanstack/react-query';
import { useCallback, useEffect, useRef } from 'react';
import { useParams } from 'react-router-dom';
import { Virtuoso } from 'react-virtuoso';
@@ -9,22 +9,18 @@ import { ChatMessageItem } from '@app/chats/components/messages/item';
import { ChatSidebar } from '@app/chats/components/sidebar';
import { useNDK } from '@libs/ndk/provider';
-import { createChat, getChatMessages } from '@libs/storage';
import { useStorage } from '@libs/storage/provider';
import { useStronghold } from '@stores/stronghold';
-import { Chats } from '@utils/types';
-
export function ChatScreen() {
- const queryClient = useQueryClient();
const virtuosoRef = useRef(null);
const { ndk } = useNDK();
const { db } = useStorage();
const { pubkey } = useParams();
const { status, data } = useQuery(['chat', pubkey], async () => {
- return await getChatMessages(db.account.pubkey, pubkey);
+ return [];
});
const userPrivkey = useStronghold((state) => state.privkey);
@@ -49,22 +45,6 @@ export function ChatScreen() {
[data]
);
- const chat = useMutation({
- mutationFn: (data: Chats) => {
- return createChat(
- data.id,
- data.receiver_pubkey,
- data.sender_pubkey,
- data.content,
- data.tags,
- data.created_at
- );
- },
- onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ['chat', pubkey] });
- },
- });
-
useEffect(() => {
const sub: NDKSubscription = ndk.subscribe(
{
@@ -79,14 +59,7 @@ export function ChatScreen() {
);
sub.addListener('event', (event) => {
- chat.mutate({
- id: event.id,
- receiver_pubkey: pubkey,
- sender_pubkey: event.pubkey,
- content: event.content,
- tags: event.tags,
- created_at: event.created_at,
- });
+ console.log(event);
});
return () => {
diff --git a/src/app/settings/account.tsx b/src/app/settings/account.tsx
index d91af1e7..63860833 100644
--- a/src/app/settings/account.tsx
+++ b/src/app/settings/account.tsx
@@ -1,16 +1,15 @@
import { useState } from 'react';
+import { useStorage } from '@libs/storage/provider';
+
import { EyeOffIcon, EyeOnIcon } from '@shared/icons';
import { useStronghold } from '@stores/stronghold';
-import { useAccount } from '@utils/hooks/useAccount';
-
export function AccountSettingsScreen() {
- const { status, account } = useAccount();
const [type, setType] = useState('password');
-
const privkey = useStronghold((state) => state.privkey);
+ const { db } = useStorage();
const showPrivateKey = () => {
if (type === 'password') {
@@ -35,7 +34,7 @@ export function AccountSettingsScreen() {
@@ -45,7 +44,7 @@ export function AccountSettingsScreen() {
diff --git a/src/app/settings/components/autoStart.tsx b/src/app/settings/components/autoStart.tsx
index 38f87e77..36877cc5 100644
--- a/src/app/settings/components/autoStart.tsx
+++ b/src/app/settings/components/autoStart.tsx
@@ -3,26 +3,24 @@ import { disable, enable, isEnabled } from '@tauri-apps/plugin-autostart';
import { useEffect, useState } from 'react';
import { twMerge } from 'tailwind-merge';
-import { getSetting, updateSetting } from '@libs/storage';
-
export function AutoStartSetting() {
const [enabled, setEnabled] = useState(false);
const toggle = async () => {
if (!enabled) {
await enable();
- await updateSetting('auto_start', 1);
+ // await updateSetting('auto_start', 1);
console.log(`registered for autostart? ${await isEnabled()}`);
} else {
await disable();
- await updateSetting('auto_start', 0);
+ // await updateSetting('auto_start', 0);
}
setEnabled(!enabled);
};
useEffect(() => {
async function getAppSetting() {
- const setting = await getSetting('auto_start');
+ const setting = '0';
if (parseInt(setting) === 0) {
setEnabled(false);
} else {
diff --git a/src/app/settings/components/cacheTime.tsx b/src/app/settings/components/cacheTime.tsx
index 1a612a21..56ac971b 100644
--- a/src/app/settings/components/cacheTime.tsx
+++ b/src/app/settings/components/cacheTime.tsx
@@ -1,17 +1,12 @@
import { useState } from 'react';
-import { getSetting, updateSetting } from '@libs/storage';
-
import { CheckCircleIcon } from '@shared/icons';
-const setting = await getSetting('cache_time');
-const cacheTime = setting;
-
export function CacheTimeSetting() {
- const [time, setTime] = useState(cacheTime);
+ const [time, setTime] = useState('0');
const update = async () => {
- await updateSetting('cache_time', time);
+ // await updateSetting('cache_time', time);
};
return (
diff --git a/src/app/space/components/modals/feed.tsx b/src/app/space/components/modals/feed.tsx
index 34152235..b7e42b5b 100644
--- a/src/app/space/components/modals/feed.tsx
+++ b/src/app/space/components/modals/feed.tsx
@@ -10,7 +10,7 @@ import { useStorage } from '@libs/storage/provider';
import { CancelIcon, CheckCircleIcon, CommandIcon, LoaderIcon } from '@shared/icons';
-import { BLOCK_KINDS, DEFAULT_AVATAR } from '@stores/constants';
+import { DEFAULT_AVATAR, widgetKinds } from '@stores/constants';
import { useWidgets } from '@stores/widgets';
export function FeedModal() {
@@ -40,7 +40,7 @@ export function FeedModal() {
// update state
setWidget(db, {
- kind: BLOCK_KINDS.feed,
+ kind: widgetKinds.feed,
title: data.title,
content: JSON.stringify(selected),
});
diff --git a/src/app/space/components/modals/hashtag.tsx b/src/app/space/components/modals/hashtag.tsx
index cf2c7d01..17e3a129 100644
--- a/src/app/space/components/modals/hashtag.tsx
+++ b/src/app/space/components/modals/hashtag.tsx
@@ -6,7 +6,7 @@ import { useStorage } from '@libs/storage/provider';
import { CancelIcon, CommandIcon, LoaderIcon } from '@shared/icons';
-import { BLOCK_KINDS } from '@stores/constants';
+import { widgetKinds } from '@stores/constants';
import { useWidgets } from '@stores/widgets';
export function HashtagModal() {
@@ -28,7 +28,7 @@ export function HashtagModal() {
// update state
setWidget(db, {
- kind: BLOCK_KINDS.hashtag,
+ kind: widgetKinds.hashtag,
title: data.hashtag,
content: data.hashtag.replace('#', ''),
});
diff --git a/src/app/space/components/modals/image.tsx b/src/app/space/components/modals/image.tsx
index 7b25adac..170df528 100644
--- a/src/app/space/components/modals/image.tsx
+++ b/src/app/space/components/modals/image.tsx
@@ -7,7 +7,7 @@ import { useStorage } from '@libs/storage/provider';
import { CancelIcon, CommandIcon, LoaderIcon } from '@shared/icons';
import { Image } from '@shared/image';
-import { BLOCK_KINDS, DEFAULT_AVATAR } from '@stores/constants';
+import { DEFAULT_AVATAR, widgetKinds } from '@stores/constants';
import { useWidgets } from '@stores/widgets';
import { useImageUploader } from '@utils/hooks/useUploader';
@@ -40,7 +40,7 @@ export function ImageModal() {
setLoading(true);
// mutate
- setWidget(db, { kind: BLOCK_KINDS.image, title: data.title, content: data.content });
+ setWidget(db, { kind: widgetKinds.image, title: data.title, content: data.content });
setLoading(false);
// reset form
diff --git a/src/app/space/components/widgets/feed.tsx b/src/app/space/components/widgets/feed.tsx
index 50b91ebf..df17179a 100644
--- a/src/app/space/components/widgets/feed.tsx
+++ b/src/app/space/components/widgets/feed.tsx
@@ -2,8 +2,6 @@ import { useInfiniteQuery } from '@tanstack/react-query';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useCallback, useEffect, useRef } from 'react';
-import { getNotesByAuthors } from '@libs/storage';
-
import { NoteKind_1, NoteKind_1063, NoteThread, Repost } from '@shared/notes';
import { NoteKindUnsupport } from '@shared/notes/kinds/unsupport';
import { NoteSkeleton } from '@shared/notes/skeleton';
@@ -11,14 +9,12 @@ import { TitleBar } from '@shared/titleBar';
import { LumeEvent, Widget } from '@utils/types';
-const ITEM_PER_PAGE = 10;
-
export function FeedBlock({ params }: { params: Widget }) {
const { status, data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery({
queryKey: ['newsfeed', params.content],
- queryFn: async ({ pageParam = 0 }) => {
- return await getNotesByAuthors(params.content, ITEM_PER_PAGE, pageParam);
+ queryFn: async () => {
+ return { data: [], nextCursor: 0 };
},
getNextPageParam: (lastPage) => lastPage.nextCursor,
});
diff --git a/src/app/space/components/widgets/image.tsx b/src/app/space/components/widgets/image.tsx
deleted file mode 100644
index a1c9ddcd..00000000
--- a/src/app/space/components/widgets/image.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import { CancelIcon } from '@shared/icons';
-import { Image } from '@shared/image';
-
-import { DEFAULT_AVATAR } from '@stores/constants';
-import { useWidgets } from '@stores/widgets';
-
-import { Widget } from '@utils/types';
-
-export function ImageBlock({ params }: { params: Widget }) {
- const remove = useWidgets((state) => state.removeWidget);
-
- return (
-
-
-
-
-
{params.title}
-
-
-
-
-
-
- );
-}
diff --git a/src/app/space/components/widgets/network.tsx b/src/app/space/components/widgets/network.tsx
index 0c8bd0f4..a67a1118 100644
--- a/src/app/space/components/widgets/network.tsx
+++ b/src/app/space/components/widgets/network.tsx
@@ -1,9 +1,11 @@
+import { NDKFilter } from '@nostr-dev-kit/ndk';
import { useInfiniteQuery } from '@tanstack/react-query';
import { useVirtualizer } from '@tanstack/react-virtual';
-import { NostrEvent } from 'nostr-fetch';
-import { useCallback, useMemo, useRef } from 'react';
+import { useCallback, useEffect, useMemo, useRef } from 'react';
import { Link } from 'react-router-dom';
+import { useStorage } from '@libs/storage/provider';
+
import { NoteKind_1, NoteKind_1063, NoteThread, Repost } from '@shared/notes';
import { NoteKindUnsupport } from '@shared/notes/kinds/unsupport';
import { NoteSkeleton } from '@shared/notes/skeleton';
@@ -13,11 +15,12 @@ import { useNostr } from '@utils/hooks/useNostr';
import { LumeEvent } from '@utils/types';
export function NetworkBlock() {
- const { fetchNotes } = useNostr();
+ const { db } = useStorage();
+ const { sub, fetchNotes } = useNostr();
const { status, data, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
queryKey: ['network-widget'],
queryFn: async ({ pageParam = 24 }) => {
- return await fetchNotes(pageParam);
+ return { data: [], nextCursor: 0 };
},
getNextPageParam: (lastPage) => lastPage.nextCursor,
refetchOnWindowFocus: false,
@@ -26,8 +29,7 @@ export function NetworkBlock() {
const parentRef = useRef();
const notes = useMemo(
- // @ts-expect-error, todo
- () => (data ? data.pages.flatMap((d: { data: NostrEvent[] }) => d.data) : []),
+ () => (data ? data.pages.flatMap((d: { data: LumeEvent[] }) => d.data) : []),
[data]
);
@@ -37,10 +39,20 @@ export function NetworkBlock() {
estimateSize: () => 500,
overscan: 2,
});
-
const itemsVirtualizer = rowVirtualizer.getVirtualItems();
const totalSize = rowVirtualizer.getTotalSize();
+ useEffect(() => {
+ const since = Math.floor(Date.now() / 1000);
+ const filter: NDKFilter = {
+ kinds: [1, 6],
+ authors: db.account.network,
+ since: since,
+ };
+
+ sub(filter, (event) => console.log('[network] event received: ', event));
+ }, []);
+
const renderItem = useCallback(
(index: string | number) => {
const note: LumeEvent = notes[index];
diff --git a/src/app/space/hooks/useLiveThread.tsx b/src/app/space/hooks/useLiveThread.tsx
deleted file mode 100644
index 4d7ee39f..00000000
--- a/src/app/space/hooks/useLiveThread.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk';
-import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { useEffect, useRef } from 'react';
-
-import { useNDK } from '@libs/ndk/provider';
-import { createReplyNote } from '@libs/storage';
-
-export function useLiveThread(id: string) {
- const queryClient = useQueryClient();
- const now = useRef(Math.floor(Date.now() / 1000));
-
- const { ndk } = useNDK();
-
- const thread = useMutation({
- mutationFn: (data: NDKEvent) => {
- return createReplyNote(
- id,
- data.id,
- data.pubkey,
- data.kind,
- data.tags,
- data.content,
- data.created_at
- );
- },
- onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ['replies', id] });
- },
- });
-
- useEffect(() => {
- const filter: NDKFilter = {
- kinds: [1],
- '#e': [id],
- since: now.current,
- };
-
- const sub = ndk.subscribe(filter, { closeOnEose: false });
-
- sub.addListener('event', (event: NDKEvent) => {
- thread.mutate(event);
- });
-
- return () => {
- sub.stop();
- };
- }, []);
-}
diff --git a/src/app/space/hooks/useNewsfeed.tsx b/src/app/space/hooks/useNewsfeed.tsx
deleted file mode 100644
index d8e37a23..00000000
--- a/src/app/space/hooks/useNewsfeed.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk';
-import { useEffect, useRef } from 'react';
-
-import { useNDK } from '@libs/ndk/provider';
-import { createNote } from '@libs/storage';
-
-import { useAccount } from '@utils/hooks/useAccount';
-
-export function useNewsfeed() {
- const sub = useRef(null);
- const now = useRef(Math.floor(Date.now() / 1000));
-
- const { ndk } = useNDK();
- const { status, account } = useAccount();
-
- useEffect(() => {
- if (status === 'success' && account) {
- const filter: NDKFilter = {
- kinds: [1, 6],
- authors: account.follows,
- since: now.current,
- };
-
- sub.current = ndk.subscribe(filter, { closeOnEose: false });
-
- sub.current.addListener('event', (event: NDKEvent) => {
- // add to db
- createNote(
- event.id,
- event.pubkey,
- event.kind,
- event.tags,
- event.content,
- event.created_at
- );
- });
- }
-
- return () => {
- if (sub.current) {
- sub.current.stop();
- }
- };
- }, [status]);
-}
diff --git a/src/app/space/index.tsx b/src/app/space/index.tsx
index d7d3f2bb..b05824a1 100644
--- a/src/app/space/index.tsx
+++ b/src/app/space/index.tsx
@@ -5,7 +5,6 @@ import { HashtagModal } from '@app/space/components/modals/hashtag';
import { ImageModal } from '@app/space/components/modals/image';
import { FeedBlock } from '@app/space/components/widgets/feed';
import { HashtagBlock } from '@app/space/components/widgets/hashtag';
-import { ImageBlock } from '@app/space/components/widgets/image';
import { NetworkBlock } from '@app/space/components/widgets/network';
import { ThreadBlock } from '@app/space/components/widgets/thread';
import { UserBlock } from '@app/space/components/widgets/user';
@@ -19,18 +18,16 @@ import { useWidgets } from '@stores/widgets';
import { Widget } from '@utils/types';
export function SpaceScreen() {
+ const { db } = useStorage();
+
const [widgets, fetchWidgets] = useWidgets((state) => [
state.widgets,
state.fetchWidgets,
]);
- const { db } = useStorage();
-
const renderItem = useCallback(
(widget: Widget) => {
switch (widget.kind) {
- case 0:
- return ;
case 1:
return ;
case 2:
@@ -39,6 +36,8 @@ export function SpaceScreen() {
return ;
case 5:
return ;
+ case 9999:
+ return ;
default:
break;
}
@@ -52,7 +51,6 @@ export function SpaceScreen() {
return (
-
{!widgets ? (
diff --git a/src/app/trending/components/trendingNotes.tsx b/src/app/trending/components/trendingNotes.tsx
index ef258d73..9da75c42 100644
--- a/src/app/trending/components/trendingNotes.tsx
+++ b/src/app/trending/components/trendingNotes.tsx
@@ -11,14 +11,15 @@ interface Response {
}
export function TrendingNotes() {
- const { status, data, error } = useQuery(
+ const { status, data } = useQuery(
['trending-notes'],
async () => {
const res = await fetch('https://api.nostr.band/v0/trending/notes');
if (!res.ok) {
- throw new Error('Error');
+ throw new Error('failed to fecht trending notes');
}
const json: Response = await res.json();
+ if (!json.notes) return null;
return json.notes;
},
{
@@ -29,19 +30,18 @@ export function TrendingNotes() {
}
);
- console.log('notes: ', data);
-
return (
- {error &&
Failed to fetch
}
{status === 'loading' ? (
+ ) : status === 'error' ? (
+
Failed to fetch
) : (
{data.map((item) => (
diff --git a/src/app/trending/components/trendingProfiles.tsx b/src/app/trending/components/trendingProfiles.tsx
index 002ced7f..439e058e 100644
--- a/src/app/trending/components/trendingProfiles.tsx
+++ b/src/app/trending/components/trendingProfiles.tsx
@@ -10,7 +10,7 @@ interface Response {
}
export function TrendingProfiles() {
- const { status, data, error } = useQuery(
+ const { status, data } = useQuery(
['trending-profiles'],
async () => {
const res = await fetch('https://api.nostr.band/v0/trending/profiles');
@@ -18,6 +18,7 @@ export function TrendingProfiles() {
throw new Error('Error');
}
const json: Response = await res.json();
+ if (!json.profiles) return null;
return json.profiles;
},
{
@@ -28,22 +29,21 @@ export function TrendingProfiles() {
}
);
- console.log('profiles: ', data);
-
return (
- {error &&
Failed to fetch
}
{status === 'loading' ? (
+ ) : status === 'error' ? (
+
Failed to fetch
) : (
- {data?.map((item) => (
+ {data.map((item) => (
))}
diff --git a/src/libs/ndk/cache.ts b/src/libs/ndk/cache.ts
index b011bd22..55406a21 100644
--- a/src/libs/ndk/cache.ts
+++ b/src/libs/ndk/cache.ts
@@ -21,15 +21,15 @@ export default class TauriAdapter implements NDKCacheAdapter {
for (const author of filter.authors) {
for (const kind of filter.kinds) {
const key = `${author}:${kind}`;
- promises.push(this.store.getEventByKey(key));
+ promises.concat(this.store.getALlEventByKey(key));
}
}
const results = await Promise.all(promises);
for (const result of results) {
- if (result) {
- console.log('cache hit: ', result);
+ if (result && result.event) {
+ console.log('cache hit: ', result.event);
const ndkEvent = new NDKEvent(
subscription.ndk,
JSON.parse(result.event as string)
diff --git a/src/libs/ndk/instance.ts b/src/libs/ndk/instance.ts
index 8f2ee5ec..5e7f04b9 100644
--- a/src/libs/ndk/instance.ts
+++ b/src/libs/ndk/instance.ts
@@ -56,6 +56,7 @@ export const NDKInstance = () => {
async function initNDK() {
let explicitRelayUrls: string[];
const explicitRelayUrlsFromDB = await db.getExplicitRelayUrls();
+ console.log('relays in db: ', explicitRelayUrlsFromDB);
if (explicitRelayUrlsFromDB) {
explicitRelayUrls = await verifyRelays(explicitRelayUrlsFromDB);
diff --git a/src/libs/storage.ts b/src/libs/storage.ts
deleted file mode 100644
index ec835f3f..00000000
--- a/src/libs/storage.ts
+++ /dev/null
@@ -1,435 +0,0 @@
-import Database from '@tauri-apps/plugin-sql';
-import { destr } from 'destr';
-
-import { parser } from '@utils/parser';
-import { getParentID } from '@utils/transform';
-import {
- Account,
- Chats,
- LumeEvent,
- Profile,
- Relays,
- Settings,
- Widget,
-} from '@utils/types';
-
-let db: null | Database = null;
-
-// connect database (sqlite)
-// path: tauri::api::path::BaseDirectory::App
-export async function connect(): Promise
{
- if (db) {
- return db;
- }
- try {
- db = await Database.load('sqlite:lume.db');
- } catch (e) {
- throw new Error('Failed to connect to database, error: ', e);
- }
- return db;
-}
-
-// get active account
-export async function getActiveAccount() {
- const db = await connect();
- const result: Array = await db.select(
- 'SELECT * FROM accounts WHERE is_active = 1;'
- );
- if (result.length > 0) {
- return result[0];
- } else {
- return null;
- }
-}
-
-// create account
-export async function createAccount(npub: string, pubkey: string, follows?: string[][]) {
- const db = await connect();
- const res = await db.execute(
- 'INSERT OR IGNORE INTO accounts (npub, pubkey, privkey, follows, is_active) VALUES (?, ?, ?, ?, ?);',
- [npub, pubkey, 'privkey is stored in secure storage', follows || '', 1]
- );
- if (res) {
- await createWidget(
- 0,
- 'Have fun together!',
- 'https://void.cat/d/N5KUHEQCVg7SywXUPiJ7yq.jpg'
- );
- }
- const getAccount = await getActiveAccount();
- return getAccount;
-}
-
-// update account
-export async function updateAccount(column: string, value: string | string[]) {
- const db = await connect();
- const account = await getActiveAccount();
- return await db.execute(`UPDATE accounts SET ${column} = ? WHERE id = ?;`, [
- value,
- account.id,
- ]);
-}
-
-// count total notes
-export async function countTotalNotes() {
- const db = await connect();
- const result: Array<{ total: string }> = await db.select(
- 'SELECT COUNT(*) AS "total" FROM notes WHERE kind IN (1, 6);'
- );
- return parseInt(result[0].total);
-}
-
-// get all notes
-export async function getNotes(limit: number, offset: number) {
- const db = await connect();
- const totalNotes = await countTotalNotes();
- const nextCursor = offset + limit;
-
- const notes: { data: LumeEvent[] | null; nextCursor: number } = {
- data: null,
- nextCursor: 0,
- };
-
- const query: LumeEvent[] = await db.select(
- `SELECT * FROM notes WHERE kind IN (1, 6, 1063) GROUP BY parent_id ORDER BY created_at DESC LIMIT "${limit}" OFFSET "${offset}";`
- );
-
- query.forEach(
- (el) => (el.tags = typeof el.tags === 'string' ? destr(el.tags) : el.tags)
- );
-
- notes['data'] = query;
- notes['nextCursor'] = Math.round(totalNotes / nextCursor) > 1 ? nextCursor : undefined;
-
- return notes;
-}
-
-// get all notes by authors
-export async function getNotesByAuthors(authors: string, limit: number, offset: number) {
- const db = await connect();
- const totalNotes = await countTotalNotes();
- const nextCursor = offset + limit;
- const array = JSON.parse(authors);
- const finalArray = `'${array.join("','")}'`;
-
- const notes: { data: LumeEvent[] | null; nextCursor: number } = {
- data: null,
- nextCursor: 0,
- };
-
- const query: LumeEvent[] = await db.select(
- `SELECT * FROM notes WHERE pubkey IN (${finalArray}) AND kind IN (1, 6, 1063) GROUP BY parent_id ORDER BY created_at DESC LIMIT "${limit}" OFFSET "${offset}";`
- );
-
- query.forEach(
- (el) => (el.tags = typeof el.tags === 'string' ? destr(el.tags) : el.tags)
- );
-
- notes['data'] = query;
- notes['nextCursor'] = Math.round(totalNotes / nextCursor) > 1 ? nextCursor : undefined;
-
- return notes;
-}
-
-// get note by id
-export async function getNoteByID(event_id: string) {
- const db = await connect();
- const result: LumeEvent[] = await db.select(
- `SELECT * FROM notes WHERE event_id = "${event_id}";`
- );
- if (result[0]) {
- if (result[0].kind === 1) result[0]['content'] = parser(result[0]);
- return result[0];
- } else {
- return null;
- }
-}
-
-// create note
-export async function createNote(
- event_id: string,
- pubkey: string,
- kind: number,
- tags: string[][],
- content: string,
- created_at: number
-) {
- const db = await connect();
- const account = await getActiveAccount();
- const parentID = getParentID(tags, event_id);
-
- return await db.execute(
- 'INSERT OR IGNORE INTO notes (event_id, account_id, pubkey, kind, tags, content, created_at, parent_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?);',
- [event_id, account.id, pubkey, kind, tags, content, created_at, parentID]
- );
-}
-
-// get note replies
-export async function getReplies(parent_id: string) {
- const db = await connect();
- const result: Array = await db.select(
- `SELECT * FROM replies WHERE parent_id = "${parent_id}" ORDER BY created_at DESC;`
- );
- return result;
-}
-
-// create reply note
-export async function createReplyNote(
- parent_id: string,
- event_id: string,
- pubkey: string,
- kind: number,
- tags: string[][],
- content: string,
- created_at: number
-) {
- const db = await connect();
- return await db.execute(
- 'INSERT OR IGNORE INTO replies (event_id, parent_id, pubkey, kind, tags, content, created_at) VALUES (?, ?, ?, ?, ?, ?, ?);',
- [event_id, parent_id, pubkey, kind, tags, content, created_at]
- );
-}
-
-// get all chats by pubkey
-export async function getChats() {
- const db = await connect();
- const account = await getActiveAccount();
-
- const chats: { follows: Array | null; unknowns: Array | null } = {
- follows: [],
- unknowns: [],
- };
-
- let result: Array = await db.select(
- `SELECT DISTINCT sender_pubkey FROM chats WHERE receiver_pubkey = "${account.pubkey}" ORDER BY created_at DESC;`
- );
-
- result = result.map((v) => ({ ...v, new_messages: 0 }));
- result = result.sort((a, b) => a.new_messages - b.new_messages);
-
- chats.follows = result.filter((el) => {
- return account.follows.some((i) => {
- return i === el.sender_pubkey;
- });
- });
-
- chats.unknowns = result.filter(
- (el) => !chats.follows.includes(el) && el.sender_pubkey !== account.pubkey
- );
-
- return chats;
-}
-
-// get chat messages
-export async function getChatMessages(receiver_pubkey: string, sender_pubkey: string) {
- const db = await connect();
- let receiver = [];
-
- const sender: Array = await db.select(
- `SELECT * FROM chats WHERE sender_pubkey = "${sender_pubkey}" AND receiver_pubkey = "${receiver_pubkey}";`
- );
-
- if (receiver_pubkey !== sender_pubkey) {
- receiver = await db.select(
- `SELECT * FROM chats WHERE sender_pubkey = "${receiver_pubkey}" AND receiver_pubkey = "${sender_pubkey}";`
- );
- }
-
- const result = [...sender, ...receiver].sort(
- (x: { created_at: number }, y: { created_at: number }) => x.created_at - y.created_at
- );
-
- return result;
-}
-
-// create chat
-export async function createChat(
- event_id: string,
- receiver_pubkey: string,
- sender_pubkey: string,
- content: string,
- tags: string[][],
- created_at: number
-) {
- const db = await connect();
- await db.execute(
- 'INSERT OR IGNORE INTO chats (event_id, receiver_pubkey, sender_pubkey, content, tags, created_at) VALUES (?, ?, ?, ?, ?, ?);',
- [event_id, receiver_pubkey, sender_pubkey, content, tags, created_at]
- );
- return sender_pubkey;
-}
-
-// get setting
-export async function getSetting(key: string) {
- const db = await connect();
- const result: Array = await db.select(
- `SELECT value FROM settings WHERE key = "${key}";`
- );
- return result[0]?.value;
-}
-
-// update setting
-export async function updateSetting(key: string, value: string | number) {
- const db = await connect();
- return await db.execute(`UPDATE settings SET value = "${value}" WHERE key = "${key}";`);
-}
-
-// get last login
-export async function getLastLogin() {
- const db = await connect();
- const result: Array = await db.select(
- `SELECT value FROM settings WHERE key = "last_login";`
- );
- if (result[0]) {
- return parseInt(result[0].value);
- } else {
- return 0;
- }
-}
-
-// update last login
-export async function updateLastLogin(value: number) {
- const db = await connect();
- return await db.execute(
- `UPDATE settings SET value = ${value} WHERE key = "last_login";`
- );
-}
-
-// get all widgets
-export async function getWidgets() {
- const db = await connect();
- const account = await getActiveAccount();
- const result: Array = await db.select(
- `SELECT * FROM widgets WHERE account_id = "${account.id}" ORDER BY created_at DESC;`
- );
- return result;
-}
-
-// create block
-export async function createWidget(
- kind: number,
- title: string,
- content: string | string[]
-) {
- const db = await connect();
- const activeAccount = await getActiveAccount();
- const insert = await db.execute(
- 'INSERT OR IGNORE INTO widgets (account_id, kind, title, content) VALUES (?, ?, ?, ?);',
- [activeAccount.id, kind, title, content]
- );
-
- if (insert) {
- const record: Widget = await db.select(
- 'SELECT * FROM widgets ORDER BY id DESC LIMIT 1;'
- );
- return record[0];
- } else {
- return null;
- }
-}
-
-// remove block
-export async function removeWidget(id: string) {
- const db = await connect();
- return await db.execute(`DELETE FROM widgets WHERE id = "${id}";`);
-}
-
-// logout
-export async function removeAll() {
- const db = await connect();
- await db.execute(`UPDATE settings SET value = "0" WHERE key = "last_login";`);
- await db.execute('DELETE FROM replies;');
- await db.execute('DELETE FROM notes;');
- await db.execute('DELETE FROM widgets;');
- await db.execute('DELETE FROM chats;');
- await db.execute('DELETE FROM accounts;');
- return true;
-}
-
-// create metadata
-export async function createMetadata(id: string, pubkey: string, content: string) {
- const db = await connect();
- const now = Math.floor(Date.now() / 1000);
- return await db.execute(
- 'INSERT OR REPLACE INTO metadata (id, pubkey, content, created_at) VALUES (?, ?, ?, ?);',
- [id, pubkey, content, now]
- );
-}
-
-export async function getAllMetadata() {
- const db = await connect();
- const result: LumeEvent[] = await db.select(`SELECT * FROM metadata;`);
- const users: Profile[] = result.map((el) => {
- const profile: Profile = destr(el.content);
- return {
- pubkey: el.pubkey,
- ident: profile.name || profile.display_name || profile.username || 'anon',
- picture:
- profile.picture ||
- profile.image ||
- 'https://void.cat/d/5VKmKyuHyxrNMf9bWSVPih.jpg',
- };
- });
- return users;
-}
-
-// get user metadata
-export async function getUserMetadata(pubkey: string) {
- const db = await connect();
- const result = await db.select(`SELECT * FROM metadata WHERE pubkey = "${pubkey}";`);
- if (result[0]) {
- return { ...result[0], ...JSON.parse(result[0].content) } as Profile;
- } else {
- return null;
- }
-}
-
-// delete privkey
-export async function removePrivkey() {
- const db = await connect();
- const activeAccount = await getActiveAccount();
- return await db.execute(
- `UPDATE accounts SET privkey = "privkey is stored in secure storage" WHERE id = "${activeAccount.id}";`
- );
-}
-
-// get relays
-export async function getRelays() {
- const db = await connect();
- const activeAccount = await getActiveAccount();
- return (await db.select(
- `SELECT * FROM relays WHERE account_id = "${activeAccount.id}";`
- )) as Relays[];
-}
-
-// get relays
-export async function getExplicitRelayUrls() {
- const db = await connect();
- const activeAccount = await getActiveAccount();
-
- if (!activeAccount) return null;
-
- const result: Relays[] = await db.select(
- `SELECT * FROM relays WHERE account_id = "${activeAccount.id}";`
- );
-
- if (result.length > 0) return result.map((el) => el.relay);
-
- return null;
-}
-
-// create relay
-export async function createRelay(relay: string, purpose?: string) {
- const db = await connect();
- const activeAccount = await getActiveAccount();
- return await db.execute(
- 'INSERT OR IGNORE INTO relays (account_id, relay, purpose) VALUES (?, ?, ?);',
- [activeAccount.id, relay, purpose || '']
- );
-}
-
-// remove relay
-export async function removeRelay(relay: string) {
- const db = await connect();
- return await db.execute(`DELETE FROM relays WHERE relay = "${relay}";`);
-}
diff --git a/src/libs/storage/instance.ts b/src/libs/storage/instance.ts
index 46087546..960aa2d2 100644
--- a/src/libs/storage/instance.ts
+++ b/src/libs/storage/instance.ts
@@ -2,7 +2,7 @@ import { BaseDirectory, removeFile } from '@tauri-apps/plugin-fs';
import Database from '@tauri-apps/plugin-sql';
import { Stronghold } from '@tauri-apps/plugin-stronghold';
-import { Account, Relays, Widget } from '@utils/types';
+import { Account, LumeEvent, Relays, Widget } from '@utils/types';
export class LumeStorage {
public db: Database;
@@ -133,16 +133,14 @@ export class LumeStorage {
);
}
- public async getEventByKey(cacheKey: string) {
- const event = await this.db.select(
- 'SELECT * FROM events WHERE cache_key = $1 ORDER BY id DESC LIMIT 1;',
+ public async getALlEventByKey(cacheKey: string) {
+ const events: LumeEvent[] = await this.db.select(
+ 'SELECT * FROM events WHERE cache_key = $1 ORDER BY id DESC;',
[cacheKey]
- )?.[0];
- if (!event) {
- // console.error('failed to get event by cache_key: ', cacheKey);
- return null;
- }
- return event;
+ );
+
+ if (events.length < 1) return null;
+ return events;
}
public async getEventByID(id: string) {
@@ -150,20 +148,20 @@ export class LumeStorage {
'SELECT * FROM events WHERE event_id = $1 ORDER BY id DESC LIMIT 1;',
[id]
)?.[0];
- if (!event) {
- // console.error('failed to get event by id: ', id);
- return null;
- }
+
+ if (!event) return null;
return event;
}
public async getExplicitRelayUrls() {
+ if (!this.account) return null;
+
const result: Relays[] = await this.db.select(
- `SELECT * FROM relays WHERE account_id = "${this.account.id}";`
+ `SELECT * FROM relays WHERE account_id = "${this.account.id}" ORDER BY id DESC LIMIT 50;`
);
- if (result.length > 0) return result.map((el) => el.relay);
- return null;
+ if (result.length < 1) return null;
+ return result.map((el) => el.relay);
}
public async createRelay(relay: string, purpose?: string) {
diff --git a/src/shared/accounts/active.tsx b/src/shared/accounts/active.tsx
index c2e459a7..c15d4184 100644
--- a/src/shared/accounts/active.tsx
+++ b/src/shared/accounts/active.tsx
@@ -1,92 +1,15 @@
-import { useMutation, useQueryClient } from '@tanstack/react-query';
-import { produce } from 'immer';
-import { useEffect } from 'react';
import { Link } from 'react-router-dom';
-import { useNDK } from '@libs/ndk/provider';
-import { createChat, getLastLogin } from '@libs/storage';
-
import { Image } from '@shared/image';
import { NetworkStatusIndicator } from '@shared/networkStatusIndicator';
import { DEFAULT_AVATAR } from '@stores/constants';
import { useProfile } from '@utils/hooks/useProfile';
-import { sendNativeNotification } from '@utils/notification';
-
-const lastLogin = await getLastLogin();
export function ActiveAccount({ data }: { data: { pubkey: string; npub: string } }) {
- const queryClient = useQueryClient();
-
- const { ndk } = useNDK();
const { status, user } = useProfile(data.pubkey);
- const chat = useMutation({
- mutationFn: (data: any) => {
- return createChat(
- data.id,
- data.receiver_pubkey,
- data.sender_pubkey,
- data.content,
- data.tags,
- data.created_at
- );
- },
- onSuccess: (data: any) => {
- const prev = queryClient.getQueryData(['chats']);
- const next = produce(prev, (draft: any) => {
- const target = draft.findIndex(
- (m: { sender_pubkey: string }) => m.sender_pubkey === data
- );
- if (target !== -1) {
- draft[target]['new_messages'] = draft[target]['new_messages'] + 1 || 1;
- } else {
- draft.push({ sender_pubkey: data, new_messages: 1 });
- }
- });
- queryClient.setQueryData(['chats'], next);
- },
- });
-
- useEffect(() => {
- const since = lastLogin > 0 ? lastLogin : Math.floor(Date.now() / 1000);
- const sub = ndk.subscribe(
- {
- kinds: [4],
- '#p': [data.pubkey],
- since: since,
- },
- {
- closeOnEose: false,
- }
- );
-
- sub.addListener('event', (event) => {
- switch (event.kind) {
- case 4:
- // update state
- chat.mutate({
- id: event.id,
- receiver_pubkey: data.pubkey,
- sender_pubkey: event.pubkey,
- content: event.content,
- tags: event.tags,
- created_at: event.created_at,
- });
- // send native notifiation
- sendNativeNotification("You've received new message");
- break;
- default:
- break;
- }
- });
-
- return () => {
- sub.stop();
- };
- }, []);
-
if (status === 'loading') {
return ;
}
diff --git a/src/shared/composer/mention/suggestion.tsx b/src/shared/composer/mention/suggestion.tsx
index 57c48ed0..9eb62391 100644
--- a/src/shared/composer/mention/suggestion.tsx
+++ b/src/shared/composer/mention/suggestion.tsx
@@ -1,13 +1,11 @@
import { ReactRenderer } from '@tiptap/react';
import tippy from 'tippy.js';
-import { getAllMetadata } from '@libs/storage';
-
import { MentionList } from '@shared/composer';
export const Suggestion = {
items: async ({ query }) => {
- const users = await getAllMetadata();
+ const users = [];
return users
.filter((item) => item.ident.toLowerCase().startsWith(query.toLowerCase()))
.slice(0, 5);
diff --git a/src/shared/composer/modal.tsx b/src/shared/composer/modal.tsx
index 067d0219..72cfc9c7 100644
--- a/src/shared/composer/modal.tsx
+++ b/src/shared/composer/modal.tsx
@@ -1,5 +1,4 @@
import * as Dialog from '@radix-ui/react-dialog';
-import { useHotkeys } from 'react-hotkeys-hook';
import { useStorage } from '@libs/storage/provider';
@@ -12,14 +11,11 @@ import {
} from '@shared/icons';
import { useComposer } from '@stores/composer';
-import { COMPOSE_SHORTCUT } from '@stores/shortcuts';
export function ComposerModal() {
const { db } = useStorage();
const [toggle, open] = useComposer((state) => [state.toggleModal, state.open]);
- useHotkeys(COMPOSE_SHORTCUT, () => toggle(true));
-
return (
diff --git a/src/shared/editProfileModal.tsx b/src/shared/editProfileModal.tsx
index b04a6686..e37e2c54 100644
--- a/src/shared/editProfileModal.tsx
+++ b/src/shared/editProfileModal.tsx
@@ -4,6 +4,8 @@ import { useQueryClient } from '@tanstack/react-query';
import { Fragment, useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
+import { useStorage } from '@libs/storage/provider';
+
import { AvatarUploader } from '@shared/avatarUploader';
import { BannerUploader } from '@shared/bannerUploader';
import { CancelIcon, CheckCircleIcon, LoaderIcon, UnverifiedIcon } from '@shared/icons';
@@ -11,7 +13,6 @@ import { Image } from '@shared/image';
import { DEFAULT_AVATAR } from '@stores/constants';
-import { useAccount } from '@utils/hooks/useAccount';
import { useNostr } from '@utils/hooks/useNostr';
export function EditProfileModal() {
@@ -23,8 +24,8 @@ export function EditProfileModal() {
const [banner, setBanner] = useState('');
const [nip05, setNIP05] = useState({ verified: false, text: '' });
+ const { db } = useStorage();
const { publish } = useNostr();
- const { account } = useAccount();
const {
register,
handleSubmit,
@@ -33,7 +34,7 @@ export function EditProfileModal() {
formState: { isValid, errors },
} = useForm({
defaultValues: async () => {
- const res: any = queryClient.getQueryData(['user', account.pubkey]);
+ const res: any = queryClient.getQueryData(['user', db.account.pubkey]);
if (res.image) {
setPicture(res.image);
}
@@ -70,7 +71,7 @@ export function EditProfileModal() {
});
if (!res.ok) return false;
- if (res.data.names[username] === account.pubkey) {
+ if (res.data.names[username] === db.account.pubkey) {
setNIP05((prev) => ({ ...prev, verified: true }));
return true;
} else {
@@ -119,7 +120,7 @@ export function EditProfileModal() {
if (event.id) {
setTimeout(() => {
// invalid cache
- queryClient.invalidateQueries(['user', account.pubkey]);
+ queryClient.invalidateQueries(['user', db.account.pubkey]);
// reset form
reset();
// reset state
diff --git a/src/shared/logout.tsx b/src/shared/logout.tsx
index a1056189..193266c0 100644
--- a/src/shared/logout.tsx
+++ b/src/shared/logout.tsx
@@ -1,27 +1,15 @@
import * as Dialog from '@radix-ui/react-dialog';
import { useQueryClient } from '@tanstack/react-query';
import { relaunch } from '@tauri-apps/plugin-process';
-import { Fragment, useState } from 'react';
-
-import { removeAll } from '@libs/storage';
import { CancelIcon, LogoutIcon } from '@shared/icons';
export function Logout() {
const queryClient = useQueryClient();
- const [isOpen, setIsOpen] = useState(false);
-
- const closeModal = () => {
- setIsOpen(false);
- };
-
- const openModal = () => {
- setIsOpen(true);
- };
const logout = async () => {
// reset database
- await removeAll();
+ // await removeAll();
// reset react query
queryClient.clear();
// navigate
@@ -69,7 +57,6 @@ export function Logout() {
{!skipMetadata ? (
-
+
) : (
)}
diff --git a/src/shared/notes/kinds/kind1063.tsx b/src/shared/notes/kinds/kind1063.tsx
index 8136bdeb..ca8dd6ca 100644
--- a/src/shared/notes/kinds/kind1063.tsx
+++ b/src/shared/notes/kinds/kind1063.tsx
@@ -27,10 +27,10 @@ export function NoteKind_1063({ event }: { event: LumeEvent }) {
className="h-auto w-full rounded-lg object-cover"
/>
)}
-
+
-
+
diff --git a/src/shared/notes/kinds/sub.tsx b/src/shared/notes/kinds/sub.tsx
index 82f3c707..588a6140 100644
--- a/src/shared/notes/kinds/sub.tsx
+++ b/src/shared/notes/kinds/sub.tsx
@@ -31,7 +31,7 @@ export function SubNote({ id, root }: { id: string; root?: string }) {
-
+
diff --git a/src/shared/notes/kinds/thread.tsx b/src/shared/notes/kinds/thread.tsx
index ed291028..3961bffe 100644
--- a/src/shared/notes/kinds/thread.tsx
+++ b/src/shared/notes/kinds/thread.tsx
@@ -28,10 +28,10 @@ export function NoteThread({
-
+
-
+
diff --git a/src/shared/notes/kinds/unsupport.tsx b/src/shared/notes/kinds/unsupport.tsx
index 2fa297a0..6f604b8d 100644
--- a/src/shared/notes/kinds/unsupport.tsx
+++ b/src/shared/notes/kinds/unsupport.tsx
@@ -25,10 +25,10 @@ export function NoteKindUnsupport({ event }: { event: LumeEvent }) {
{event.content.toString()}
-
+
-
+
diff --git a/src/shared/notes/mentions/note.tsx b/src/shared/notes/mentions/note.tsx
index 9bdf8fa7..c18cc30f 100644
--- a/src/shared/notes/mentions/note.tsx
+++ b/src/shared/notes/mentions/note.tsx
@@ -5,7 +5,7 @@ import remarkGfm from 'remark-gfm';
import { MentionUser, NoteSkeleton } from '@shared/notes';
import { User } from '@shared/user';
-import { BLOCK_KINDS } from '@stores/constants';
+import { widgetKinds } from '@stores/constants';
import { useWidgets } from '@stores/widgets';
import { useEvent } from '@utils/hooks/useEvent';
@@ -17,7 +17,7 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
const openThread = (event, thread: string) => {
const selection = window.getSelection();
if (selection.toString().length === 0) {
- setWidget({ kind: BLOCK_KINDS.thread, title: 'Thread', content: thread });
+ setWidget({ kind: widgetKinds.thread, title: 'Thread', content: thread });
} else {
event.stopPropagation();
}
diff --git a/src/shared/notes/mentions/user.tsx b/src/shared/notes/mentions/user.tsx
index 36d947b8..6014a470 100644
--- a/src/shared/notes/mentions/user.tsx
+++ b/src/shared/notes/mentions/user.tsx
@@ -1,4 +1,4 @@
-import { BLOCK_KINDS } from '@stores/constants';
+import { widgetKinds } from '@stores/constants';
import { useWidgets } from '@stores/widgets';
import { useProfile } from '@utils/hooks/useProfile';
@@ -13,7 +13,7 @@ export function MentionUser({ pubkey }: { pubkey: string }) {
type="button"
onClick={() =>
setWidget({
- kind: BLOCK_KINDS.user,
+ kind: widgetKinds.user,
title: user?.nip05 || user?.name || user?.display_name,
content: pubkey,
})
diff --git a/src/shared/notes/metadata.tsx b/src/shared/notes/metadata.tsx
index 832c23e0..d850236f 100644
--- a/src/shared/notes/metadata.tsx
+++ b/src/shared/notes/metadata.tsx
@@ -3,12 +3,12 @@ import { useQuery } from '@tanstack/react-query';
import { decode } from 'light-bolt11-decoder';
import { useNDK } from '@libs/ndk/provider';
-import { createReplyNote } from '@libs/storage';
+import { useStorage } from '@libs/storage/provider';
import { LoaderIcon } from '@shared/icons';
import { MiniUser } from '@shared/notes/users/mini';
-import { BLOCK_KINDS } from '@stores/constants';
+import { widgetKinds } from '@stores/constants';
import { useWidgets } from '@stores/widgets';
import { compactNumber } from '@utils/number';
@@ -16,6 +16,7 @@ import { compactNumber } from '@utils/number';
export function NoteMetadata({ id }: { id: string }) {
const setWidget = useWidgets((state) => state.setWidget);
+ const { db } = useStorage();
const { ndk } = useNDK();
const { status, data } = useQuery(
['note-metadata', id],
@@ -35,15 +36,6 @@ export function NoteMetadata({ id }: { id: string }) {
case 1:
replies += 1;
if (users.length < 3) users.push(event.pubkey);
- createReplyNote(
- id,
- event.id,
- event.pubkey,
- event.kind,
- event.tags,
- event.content,
- event.created_at
- );
break;
case 9735: {
const bolt11 = event.tags.find((tag) => tag[0] === 'bolt11')[1];
@@ -93,7 +85,11 @@ export function NoteMetadata({ id }: { id: string }) {
- setWidget({ kind: BLOCK_KINDS.thread, title: 'Thread', content: id })
+ setWidget(db, {
+ kind: widgetKinds.thread,
+ title: 'Thread',
+ content: id,
+ })
}
className="text-white/50"
>
diff --git a/src/shared/notes/replies/item.tsx b/src/shared/notes/replies/item.tsx
index 4a014b6a..fbeef981 100644
--- a/src/shared/notes/replies/item.tsx
+++ b/src/shared/notes/replies/item.tsx
@@ -18,11 +18,7 @@ export function Reply({ event, root }: { event: LumeEvent; root?: string }) {
-
+
diff --git a/src/shared/notes/replies/sub.tsx b/src/shared/notes/replies/sub.tsx
index 57b3ad38..0e680d3b 100644
--- a/src/shared/notes/replies/sub.tsx
+++ b/src/shared/notes/replies/sub.tsx
@@ -16,7 +16,7 @@ export function SubReply({ event }: { event: LumeEvent }) {
-
+
diff --git a/src/shared/protected.tsx b/src/shared/protected.tsx
deleted file mode 100644
index b52a4e26..00000000
--- a/src/shared/protected.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { ReactNode } from 'react';
-import { Navigate } from 'react-router-dom';
-
-import { LoaderIcon } from '@shared/icons';
-
-import { useStronghold } from '@stores/stronghold';
-
-import { useAccount } from '@utils/hooks/useAccount';
-
-export function Protected({ children }: { children: ReactNode }) {
- const privkey = useStronghold((state) => state.privkey);
- const { status, account } = useAccount();
-
- if (status === 'loading') {
- return (
-
-
-
- );
- }
-
- if (!account) {
- return ;
- }
-
- if (account && account.privkey.length > 35) {
- return ;
- }
-
- if (account && !privkey) {
- return ;
- }
-
- return children;
-}
diff --git a/src/shared/titleBar.tsx b/src/shared/titleBar.tsx
index 50d3d09a..cc790bcf 100644
--- a/src/shared/titleBar.tsx
+++ b/src/shared/titleBar.tsx
@@ -1,8 +1,11 @@
+import { useStorage } from '@libs/storage/provider';
+
import { CancelIcon } from '@shared/icons';
import { useWidgets } from '@stores/widgets';
export function TitleBar({ id, title }: { id?: string; title: string }) {
+ const { db } = useStorage();
const remove = useWidgets((state) => state.removeWidget);
return (
@@ -15,7 +18,7 @@ export function TitleBar({ id, title }: { id?: string; title: string }) {
{id ? (
remove(id)}
+ onClick={() => remove(db, id)}
className="inline-flex h-6 w-6 shrink translate-y-8 transform items-center justify-center rounded transition-transform duration-150 ease-in-out hover:bg-white/10 group-hover:translate-y-0"
>
diff --git a/src/shared/user.tsx b/src/shared/user.tsx
index 20cc0bc1..eca23ae0 100644
--- a/src/shared/user.tsx
+++ b/src/shared/user.tsx
@@ -8,7 +8,7 @@ import { DEFAULT_AVATAR } from '@stores/constants';
import { formatCreatedAt } from '@utils/createdAt';
import { useProfile } from '@utils/hooks/useProfile';
-import { displayNpub, shortenKey } from '@utils/shortenKey';
+import { displayNpub } from '@utils/shortenKey';
export function User({
pubkey,
@@ -85,7 +85,7 @@ export function User({
{user?.nip05?.toLowerCase() ||
user?.name ||
user?.display_name ||
- shortenKey(pubkey)}
+ displayNpub(pubkey, 16)}
·
{createdAt}
diff --git a/src/stores/channels.tsx b/src/stores/channels.tsx
deleted file mode 100644
index 3a9f0f9c..00000000
--- a/src/stores/channels.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import { create } from 'zustand';
-import { immer } from 'zustand/middleware/immer';
-
-import { createChannelMessage, getChannelMessages, getChannels } from '@libs/storage';
-
-import { LumeEvent } from '@utils/types';
-
-export const useChannels = create(
- immer((set) => ({
- channels: [],
- fetch: async () => {
- const response = await getChannels();
- set({ channels: response });
- },
- add: (event) => {
- set((state) => {
- const target = state.channels.findIndex(
- (m: { event_id: string }) => m.event_id === event.id
- );
- if (target !== -1) {
- state.channels[target]['new_messages'] =
- state.channels[target]['new_messages'] + 1 || 1;
- } else {
- state.channels.push({ event_id: event.id, ...event });
- }
- });
- },
- clearBubble: (id: string) => {
- set((state) => {
- const target = state.channels.findIndex(
- (m: { event_id: string }) => m.event_id === id
- );
- state.channels[target]['new_messages'] = 0;
- });
- },
- }))
-);
-
-export const useChannelMessages = create(
- immer((set) => ({
- messages: [],
- replyTo: { id: null, pubkey: null, content: null },
- fetch: async (id: string) => {
- const events = await getChannelMessages(id);
- set({ messages: events });
- },
- add: (id, event: LumeEvent) => {
- set((state: any) => {
- createChannelMessage(
- id,
- event.id,
- event.pubkey,
- event.kind,
- event.content,
- event.tags,
- event.created_at
- );
- state.messages.push({
- event_id: event.id,
- channel_id: id,
- hide: 0,
- mute: 0,
- ...event,
- });
- });
- },
- openReply: (id: string, pubkey: string, content: string) => {
- set(() => ({ replyTo: { id, pubkey, content } }));
- },
- closeReply: () => {
- set(() => ({ replyTo: { id: null, pubkey: null, content: null } }));
- },
- hideMessage: (id: string) => {
- set((state) => {
- const target = state.messages.findIndex((m) => m.id === id);
- state.messages[target]['hide'] = true;
- });
- },
- muteUser: (pubkey: string) => {
- set((state) => {
- const target = state.messages.findIndex((m) => m.pubkey === pubkey);
- state.messages[target]['mute'] = true;
- });
- },
- clear: () => {
- set(() => ({
- messages: [],
- replyTo: { id: null, pubkey: null, content: null },
- }));
- },
- }))
-);
diff --git a/src/stores/constants.tsx b/src/stores/constants.tsx
index a7d3a198..f57780d5 100644
--- a/src/stores/constants.tsx
+++ b/src/stores/constants.tsx
@@ -1,76 +1,12 @@
-export const APP_VERSION = '1.2.0';
-
export const DEFAULT_AVATAR = 'https://void.cat/d/5VKmKyuHyxrNMf9bWSVPih';
-export const OPENGRAPH = {
- REGEX_VALID_URL: new RegExp(
- '^' +
- // protocol identifier
- '(?:(?:https?|ftp)://)' +
- // user:pass authentication
- '(?:\\S+(?::\\S*)?@)?' +
- '(?:' +
- // IP address exclusion
- // private & local networks
- '(?!(?:10|127)(?:\\.\\d{1,3}){3})' +
- '(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})' +
- '(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})' +
- // IP address dotted notation octets
- // excludes loopback network 0.0.0.0
- // excludes reserved space >= 224.0.0.0
- // excludes network & broacast addresses
- // (first & last IP address of each class)
- '(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])' +
- '(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}' +
- '(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))' +
- '|' +
- // host name
- '(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)' +
- // domain name
- '(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*' +
- // TLD identifier
- '(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))' +
- // TLD may end with dot
- '\\.?' +
- ')' +
- // port number
- '(?::\\d{2,5})?' +
- // resource path
- '(?:[/?#]\\S*)?' +
- '$',
- 'i'
- ),
-
- REGEX_LOOPBACK: new RegExp(
- '^' +
- '(?:(?:10|127)(?:\\.\\d{1,3}){3})' +
- '|' +
- '(?:(?:169\\.254|192\\.168|192\\.0)(?:\\.\\d{1,3}){2})' +
- '|' +
- '(?:172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})' +
- '$',
- 'i'
- ),
-
- REGEX_CONTENT_TYPE_IMAGE: new RegExp('image/.*', 'i'),
-
- REGEX_CONTENT_TYPE_AUDIO: new RegExp('audio/.*', 'i'),
-
- REGEX_CONTENT_TYPE_VIDEO: new RegExp('video/.*', 'i'),
-
- REGEX_CONTENT_TYPE_TEXT: new RegExp('text/.*', 'i'),
-
- REGEX_CONTENT_TYPE_APPLICATION: new RegExp('application/.*', 'i'),
-};
-
export const FULL_RELAYS = [
- 'wss://relayable.org',
'wss://relay.damus.io',
'wss://relay.nostr.band/all',
'wss://nostr.mutinywallet.com',
];
-export const BLOCK_KINDS = {
+export const widgetKinds = {
image: 0,
feed: 1,
thread: 2,
diff --git a/src/stores/shortcuts.tsx b/src/stores/shortcuts.tsx
deleted file mode 100644
index 4b81ec9b..00000000
--- a/src/stores/shortcuts.tsx
+++ /dev/null
@@ -1,4 +0,0 @@
-export const COMPOSE_SHORTCUT = 'meta+n';
-export const ADD_IMAGEBLOCK_SHORTCUT = 'meta+i';
-export const ADD_FEEDBLOCK_SHORTCUT = 'meta+f';
-export const ADD_HASHTAGBLOCK_SHORTCUT = 'meta+t';
diff --git a/src/stores/widgets.tsx b/src/stores/widgets.tsx
index 3657f0f5..3852f802 100644
--- a/src/stores/widgets.tsx
+++ b/src/stores/widgets.tsx
@@ -18,6 +18,15 @@ export const useWidgets = create()(
widgets: null,
fetchWidgets: async (db: LumeStorage) => {
const widgets = await db.getWidgets();
+
+ // default: add network widget
+ widgets.unshift({
+ id: String(widgets.length + 1),
+ title: 'Network',
+ content: '',
+ kind: 9999,
+ });
+
set({ widgets: widgets });
},
setWidget: async (db: LumeStorage, { kind, title, content }: Widget) => {
diff --git a/src/utils/date.tsx b/src/utils/date.tsx
index e289a359..77657a17 100644
--- a/src/utils/date.tsx
+++ b/src/utils/date.tsx
@@ -1,25 +1,2 @@
-// get X days ago with user provided date
-export function getDayAgo(numOfDays, date = new Date()) {
- const days = new Date(date.getTime());
- days.setDate(date.getDate() - numOfDays);
-
- return days;
-}
-
-// get X hours ago with user provided date
-export function getHourAgo(numOfHours, date = new Date()) {
- const hours = new Date(date.getTime());
- hours.setHours(date.getHours() - numOfHours);
-
- return hours;
-}
-
-// convert date to unix timestamp
-export function dateToUnix(_date?: Date) {
- const date = _date || new Date();
-
- return Math.floor(date.getTime() / 1000);
-}
-
export const nHoursAgo = (hrs: number): number =>
Math.floor((Date.now() - hrs * 60 * 60 * 1000) / 1000);
diff --git a/src/utils/hooks/useAccount.tsx b/src/utils/hooks/useAccount.tsx
deleted file mode 100644
index 74e13a2b..00000000
--- a/src/utils/hooks/useAccount.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { useQuery } from '@tanstack/react-query';
-
-import { useNDK } from '@libs/ndk/provider';
-import { useStorage } from '@libs/storage/provider';
-
-export function useAccount() {
- const { db } = useStorage();
- const { ndk } = useNDK();
- const { status, data: account } = useQuery(
- ['account'],
- async () => {
- const account = await db.getActiveAccount();
- console.log('account: ', account);
- if (account?.pubkey) {
- const user = ndk.getUser({ hexpubkey: account?.pubkey });
- await user.fetchProfile();
- return { ...account, ...user.profile };
- }
- return account;
- },
- {
- enabled: !!ndk,
- staleTime: Infinity,
- refetchOnMount: false,
- refetchOnWindowFocus: false,
- refetchOnReconnect: false,
- }
- );
-
- return { status, account };
-}
diff --git a/src/utils/hooks/useEvent.tsx b/src/utils/hooks/useEvent.tsx
index 6ea7c661..f67348a2 100644
--- a/src/utils/hooks/useEvent.tsx
+++ b/src/utils/hooks/useEvent.tsx
@@ -16,7 +16,7 @@ export function useEvent(id: string, embed?: string) {
return embed as unknown as LumeEvent;
} else {
const event = (await ndk.fetchEvent(id)) as LumeEvent;
- if (!event) return null;
+ if (!event) throw new Error('event not found');
if (event.kind === 1) event['content'] = parser(event) as unknown as string;
return event as LumeEvent;
}
diff --git a/src/utils/hooks/useNostr.tsx b/src/utils/hooks/useNostr.tsx
index eabadbbb..20c21ea2 100644
--- a/src/utils/hooks/useNostr.tsx
+++ b/src/utils/hooks/useNostr.tsx
@@ -6,7 +6,6 @@ import {
NDKSubscription,
NDKUser,
} from '@nostr-dev-kit/ndk';
-import { destr } from 'destr';
import { LRUCache } from 'lru-cache';
import { nip19 } from 'nostr-tools';
import { useMemo } from 'react';
@@ -17,6 +16,14 @@ import { useStorage } from '@libs/storage/provider';
import { useStronghold } from '@stores/stronghold';
import { nHoursAgo } from '@utils/date';
+import { LumeEvent } from '@utils/types';
+
+interface NotesResponse {
+ status: string;
+ data: LumeEvent[];
+ nextCursor?: number;
+ message?: string;
+}
export function useNostr() {
const { ndk } = useNDK();
@@ -37,6 +44,8 @@ export function useNostr() {
callback: (event: NDKEvent) => void,
closeOnEose?: boolean
) => {
+ if (!ndk) throw new Error('NDK instance not found');
+
const subEvent = ndk.subscribe(filter, { closeOnEose: closeOnEose ?? true });
subManager.set(JSON.stringify(filter), subEvent);
@@ -78,17 +87,22 @@ export function useNostr() {
}
};
- const fetchNotes = async (since: number) => {
+ const fetchNotes = async (since: number): Promise => {
try {
- if (!ndk) return { status: 'failed', message: 'NDK instance not found' };
+ if (!ndk) return { status: 'failed', data: [], message: 'NDK instance not found' };
+ console.log('fetch all events since: ', since);
const events = await ndk.fetchEvents({
kinds: [1],
authors: db.account.network ?? db.account.follows,
since: nHoursAgo(since),
});
- return { status: 'ok', data: [...events], nextCursor: since * 2 };
+ const sorted = [...events].sort(
+ (a, b) => b.created_at - a.created_at
+ ) as unknown as LumeEvent[];
+
+ return { status: 'ok', data: sorted, nextCursor: since * 2 };
} catch (e) {
console.error('failed get notes, error: ', e);
return { status: 'failed', data: [], message: e };
@@ -122,14 +136,6 @@ export function useNostr() {
};
const createZap = async (event: NDKEvent, amount: number, message?: string) => {
- // @ts-expect-error, LumeEvent to NDKEvent
- event.id = event.event_id;
-
- // @ts-expect-error, LumeEvent to NDKEvent
- if (typeof event.content !== 'string') event.content = event.content.original;
-
- if (typeof event.tags === 'string') event.tags = destr(event.tags);
-
if (!privkey) throw new Error('Private key not found');
if (!ndk.signer) {
@@ -137,7 +143,7 @@ export function useNostr() {
ndk.signer = signer;
}
- // @ts-expect-error, LumeEvent to NDKEvent
+ // @ts-expect-error, NostrEvent to NDKEvent
const ndkEvent = new NDKEvent(ndk, event);
const res = await ndkEvent.zap(amount, message ?? 'zap from lume');
diff --git a/src/utils/hooks/useOpenGraph.tsx b/src/utils/hooks/useOpenGraph.tsx
index bbdc527f..2dda8901 100644
--- a/src/utils/hooks/useOpenGraph.tsx
+++ b/src/utils/hooks/useOpenGraph.tsx
@@ -4,7 +4,7 @@ import { invoke } from '@tauri-apps/api/tauri';
import { Opengraph } from '@utils/types';
export function useOpenGraph(url: string) {
- const { status, data, error, isFetching } = useQuery(
+ const { status, data, error } = useQuery(
['preview', url],
async () => {
const res: Opengraph = await invoke('opengraph', { url });
@@ -25,6 +25,5 @@ export function useOpenGraph(url: string) {
status,
data,
error,
- isFetching,
};
}
diff --git a/src/utils/hooks/useSecureStorage.tsx b/src/utils/hooks/useSecureStorage.tsx
deleted file mode 100644
index 91057a8f..00000000
--- a/src/utils/hooks/useSecureStorage.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { BaseDirectory, appConfigDir } from '@tauri-apps/api/path';
-import { removeFile } from '@tauri-apps/plugin-fs';
-import { Stronghold } from '@tauri-apps/plugin-stronghold';
-
-const dir = await appConfigDir();
-
-export function useSecureStorage() {
- async function getClient(stronghold: Stronghold) {
- try {
- return await stronghold.loadClient('lume');
- } catch {
- return await stronghold.createClient('lume');
- }
- }
-
- const save = async (key: string, value: string, password: string) => {
- const stronghold = await Stronghold.load(`${dir}/lume.stronghold`, password);
- const client = await getClient(stronghold);
- const store = client.getStore();
- await store.insert(key, Array.from(new TextEncoder().encode(value)));
- return await stronghold.save();
- };
-
- const load = async (key: string, password: string) => {
- const stronghold = await Stronghold.load(`${dir}/lume.stronghold`, password);
- const client = await getClient(stronghold);
- const store = client.getStore();
- const value = await store.get(key);
- const decoded = new TextDecoder().decode(new Uint8Array(value));
- return decoded;
- };
-
- const reset = async () => {
- return await removeFile('lume.stronghold', { dir: BaseDirectory.AppConfig });
- };
-
- return { save, load, reset };
-}
diff --git a/src/utils/transform.tsx b/src/utils/transform.tsx
index bd30adb1..fb67a921 100644
--- a/src/utils/transform.tsx
+++ b/src/utils/transform.tsx
@@ -1,30 +1,5 @@
import { NDKTag } from '@nostr-dev-kit/ndk';
import { destr } from 'destr';
-import { nip19 } from 'nostr-tools';
-
-export function truncateContent(str, n) {
- return str.length > n ? `${str.slice(0, n - 1)}...` : str;
-}
-
-export function setToArray(tags: any) {
- const newArray = [];
- tags.forEach((item) => {
- const hexpubkey = nip19.decode(item.npub).data;
- newArray.push(hexpubkey);
- });
-
- return newArray;
-}
-
-// convert NIP-02 to array of pubkey
-export function nip02ToArray(tags: any) {
- const arr = [];
- tags.forEach((item) => {
- arr.push(item[1]);
- });
-
- return arr;
-}
// convert array to NIP-02 tag list
export function arrayToNIP02(arr: string[]) {
@@ -36,52 +11,9 @@ export function arrayToNIP02(arr: string[]) {
return nip02_arr;
}
-// convert array object to pure array
-export function arrayObjToPureArr(arr: any) {
- const pure_arr = [];
- arr.forEach((item) => {
- pure_arr.push(item.content);
- });
-
- return pure_arr;
-}
-
-// get parent id from event tags
-export function getParentID(arr: string[][], fallback: string) {
- const tags = destr(arr);
- let parentID = fallback;
-
- if (tags.length > 0) {
- if (tags[0][0] === 'e') {
- parentID = tags[0][1];
- } else {
- tags.forEach((tag) => {
- if (tag[0] === 'e' && (tag[2] === 'root' || tag[3] === 'root')) {
- parentID = tag[1];
- }
- });
- }
- }
-
- return parentID;
-}
-
-// check id present in event tags
-export function isTagsIncludeID(id: string, arr: NDKTag[]) {
- const tags = destr(arr);
-
- if (tags.length > 0) {
- if (tags[0][1] === id) {
- return true;
- }
- } else {
- return false;
- }
-}
-
-// get parent id from event tags
+// get repost id from event tags
export function getRepostID(arr: NDKTag[]) {
- const tags = destr(arr);
+ const tags = destr(arr) as string[];
let quoteID = null;
if (tags.length > 0) {
@@ -94,12 +26,3 @@ export function getRepostID(arr: NDKTag[]) {
return quoteID;
}
-
-// sort events by timestamp
-export function sortEvents(arr: any) {
- arr.sort((a, b) => {
- return a.created_at - b.created_at;
- });
-
- return arr;
-}
diff --git a/vite.config.ts b/vite.config.ts
index 663eb07a..94e36316 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,19 +1,9 @@
import react from '@vitejs/plugin-react-swc';
-// import million from 'million/compiler';
import { defineConfig } from 'vite';
-import topLevelAwait from 'vite-plugin-top-level-await';
import viteTsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
- plugins: [
- // million.vite(),
- react(),
- viteTsconfigPaths(),
- topLevelAwait({
- promiseExportName: '__tla',
- promiseImportName: (i) => `__tla_${i}`,
- }),
- ],
+ plugins: [react(), viteTsconfigPaths()],
envPrefix: ['VITE_', 'TAURI_'],
build: {
target: process.env.TAURI_PLATFORM === 'windows' ? 'chrome105' : 'safari13',