From cade8c8b4cb5dd4cb9d4060015fa463ca43f1d3a Mon Sep 17 00:00:00 2001 From: reya Date: Sat, 21 Oct 2023 15:58:39 +0700 Subject: [PATCH] wip: multi-type composer --- package.json | 1 + pnpm-lock.yaml | 34 +++++++++++ src/app.tsx | 7 +++ src/app/auth/create.tsx | 6 +- src/app/auth/onboarding/enrich.tsx | 16 +++-- src/app/auth/onboarding/relays.tsx | 12 +++- src/app/notes/components/editor/post.tsx | 40 +++++++++++++ src/app/notes/components/index.ts | 1 + src/app/notes/new.tsx | 74 ++++++++++++++++++++++++ src/libs/ndk/instance.ts | 6 +- src/libs/storage/instance.ts | 10 +++- src/shared/accounts/more.tsx | 4 +- src/shared/logout.tsx | 28 ++++++--- src/shared/navigation.tsx | 25 +++++--- src/shared/user.tsx | 2 +- vite.config.ts | 4 +- 16 files changed, 234 insertions(+), 36 deletions(-) create mode 100644 src/app/notes/components/editor/post.tsx create mode 100644 src/app/notes/components/index.ts create mode 100644 src/app/notes/new.tsx diff --git a/package.json b/package.json index 79d3620a..d6aa0445 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "tailwind-scrollbar": "^3.0.5", "tauri-controls": "^0.2.0", "tippy.js": "^6.3.7", + "tiptap-markdown": "^0.8.2", "virtua": "^0.14.0", "zustand": "^4.4.3" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 06a1b85a..13e6c655 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -200,6 +200,9 @@ dependencies: tippy.js: specifier: ^6.3.7 version: 6.3.7 + tiptap-markdown: + specifier: ^0.8.2 + version: 0.8.2(@tiptap/core@2.1.12) virtua: specifier: ^0.14.0 version: 0.14.0(react-dom@18.2.0)(react@18.2.0) @@ -2745,12 +2748,27 @@ packages: resolution: {integrity: sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==} dev: true + /@types/linkify-it@3.0.4: + resolution: {integrity: sha512-hPpIeeHb/2UuCw06kSNAOVWgehBLXEo0/fUs0mw3W2qhqX89PI2yvok83MnuctYGCPrabGIoi0fFso4DQ+sNUQ==} + dev: false + + /@types/markdown-it@12.2.3: + resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==} + dependencies: + '@types/linkify-it': 3.0.4 + '@types/mdurl': 1.0.4 + dev: false + /@types/mdast@3.0.14: resolution: {integrity: sha512-gVZ04PGgw1qLZKsnWnyFv4ORnaJ+DXLdHTVSFbU8yX6xZ34Bjg4Q32yPkmveUP1yItXReKfB0Aknlh/3zxTKAw==} dependencies: '@types/unist': 2.0.9 dev: false + /@types/mdurl@1.0.4: + resolution: {integrity: sha512-ARVxjAEX5TARFRzpDRVC6cEk0hUIXCCwaMhz8y7S1/PxU6zZS1UMjyobz7q4w/D/R552r4++EhwmXK1N2rAy0A==} + dev: false + /@types/ms@0.7.33: resolution: {integrity: sha512-AuHIyzR5Hea7ij0P9q7vx7xu4z0C28ucwjAZC0ja7JhINyCnOw8/DnvAPQQ9TfOlCtZAmCERKQX9+o1mgQhuOQ==} dev: false @@ -4735,6 +4753,10 @@ packages: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: false + /markdown-it-task-lists@2.1.1: + resolution: {integrity: sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==} + dev: false + /markdown-it@13.0.2: resolution: {integrity: sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==} hasBin: true @@ -6434,6 +6456,18 @@ packages: '@popperjs/core': 2.11.8 dev: false + /tiptap-markdown@0.8.2(@tiptap/core@2.1.12): + resolution: {integrity: sha512-RyfpnH475+FYVh1fCiWF9+wBvA8T6X3PqxZR1MApEewxkoQ5kREQFiVwjZiez9qUQk/Bxu3RerFSJot65V6cbQ==} + peerDependencies: + '@tiptap/core': ^2.0.3 + dependencies: + '@tiptap/core': 2.1.12(@tiptap/pm@2.1.12) + '@types/markdown-it': 12.2.3 + markdown-it: 13.0.2 + markdown-it-task-lists: 2.1.1 + prosemirror-markdown: 1.11.2 + dev: false + /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} diff --git a/src/app.tsx b/src/app.tsx index 65360885..009d7157 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -123,6 +123,13 @@ export default function App() { element: , errorElement: , children: [ + { + path: 'new', + async lazy() { + const { NewNoteScreen } = await import('@app/notes/new'); + return { Component: NewNoteScreen }; + }, + }, { path: 'text/:id', async lazy() { diff --git a/src/app/auth/create.tsx b/src/app/auth/create.tsx index c360e5ca..caf0c2d2 100644 --- a/src/app/auth/create.tsx +++ b/src/app/auth/create.tsx @@ -59,16 +59,16 @@ export function CreateAccountScreen() { const userNpub = nip19.npubEncode(userPubkey); const userNsec = nip19.nsecEncode(userPrivkey); - const event = new NDKEvent(ndk); const signer = new NDKPrivateKeySigner(userPrivkey); + ndk.signer = signer; + const event = new NDKEvent(ndk); event.content = JSON.stringify(profile); event.kind = NDKKind.Metadata; event.created_at = Math.floor(Date.now() / 1000); event.pubkey = userPubkey; event.tags = []; - await event.sign(signer); const publish = await event.publish(); if (publish) { @@ -217,7 +217,7 @@ export function CreateAccountScreen() {

Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place.{' '} - + There is no way to reset your private key.

diff --git a/src/app/auth/onboarding/enrich.tsx b/src/app/auth/onboarding/enrich.tsx index ec69c273..350a2698 100644 --- a/src/app/auth/onboarding/enrich.tsx +++ b/src/app/auth/onboarding/enrich.tsx @@ -1,8 +1,10 @@ +import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk'; import { useQuery } from '@tanstack/react-query'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; +import { useNDK } from '@libs/ndk/provider'; import { useStorage } from '@libs/storage/provider'; import { ArrowLeftIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons'; @@ -10,10 +12,10 @@ import { User } from '@shared/user'; import { useOnboarding } from '@stores/onboarding'; -import { useNostr } from '@utils/hooks/useNostr'; import { arrayToNIP02 } from '@utils/transform'; export function OnboardEnrichScreen() { + const { ndk } = useNDK(); const { db } = useStorage(); const { status, data } = useQuery(['trending-profiles-widget'], async () => { const res = await fetch('https://api.nostr.band/v0/trending/profiles'); @@ -22,7 +24,6 @@ export function OnboardEnrichScreen() { } return res.json(); }); - const { publish } = useNostr(); const [loading, setLoading] = useState(false); const [follows, setFollows] = useState([]); @@ -43,10 +44,17 @@ export function OnboardEnrichScreen() { setLoading(true); const tags = arrayToNIP02(follows); - const event = await publish({ content: '', kind: 3, tags: tags }); + + const event = new NDKEvent(ndk); + event.content = ''; + event.kind = NDKKind.Contacts; + event.created_at = Math.floor(Date.now() / 1000); + event.tags = tags; + + const publish = await event.publish(); // redirect to next step - if (event) { + if (publish) { db.account.follows = follows; await db.updateAccount('follows', JSON.stringify(follows)); diff --git a/src/app/auth/onboarding/relays.tsx b/src/app/auth/onboarding/relays.tsx index c77dc270..3c940fae 100644 --- a/src/app/auth/onboarding/relays.tsx +++ b/src/app/auth/onboarding/relays.tsx @@ -1,8 +1,10 @@ +import { NDKEvent } from '@nostr-dev-kit/ndk'; import { useQuery } from '@tanstack/react-query'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; +import { useNDK } from '@libs/ndk/provider'; import { useStorage } from '@libs/storage/provider'; import { ArrowLeftIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons'; @@ -19,8 +21,8 @@ export function OnboardRelaysScreen() { const [loading, setLoading] = useState(false); const [relays, setRelays] = useState(new Set()); - const { publish } = useNostr(); const { db } = useStorage(); + const { ndk } = useNDK(); const { getAllRelaysByUsers } = useNostr(); const { status, data } = useQuery( ['relays'], @@ -52,7 +54,13 @@ export function OnboardRelaysScreen() { } const tags = Array.from(relays).map((relay) => ['r', relay.replace(/\/+$/, '')]); - await publish({ content: '', kind: 10002, tags: tags }); + const event = new NDKEvent(ndk); + event.content = ''; + event.kind = 10002; + event.created_at = Math.floor(Date.now() / 1000); + event.tags = tags; + + await event.publish(); toggleRelays(); navigate(-1); diff --git a/src/app/notes/components/editor/post.tsx b/src/app/notes/components/editor/post.tsx new file mode 100644 index 00000000..de650e81 --- /dev/null +++ b/src/app/notes/components/editor/post.tsx @@ -0,0 +1,40 @@ +import Image from '@tiptap/extension-image'; +import Placeholder from '@tiptap/extension-placeholder'; +import { EditorContent, useEditor } from '@tiptap/react'; +import StarterKit from '@tiptap/starter-kit'; + +export function PostEditor() { + const editor = useEditor({ + extensions: [ + StarterKit.configure(), + Placeholder.configure({ placeholder: 'Type something...' }), + Image.configure({ + HTMLAttributes: { + class: + 'rounded-lg w-full h-auto border border-white/10 outline outline-2 outline-offset-0 outline-white/20 ml-1', + }, + }), + ], + content: JSON.parse(localStorage.getItem('editor-post') || '{}'), + editorProps: { + attributes: { + class: + 'h-full outline-none prose prose-lg prose-neutral max-w-none select-text whitespace-pre-line break-words dark:prose-invert hover:prose-a:text-blue-500', + }, + }, + onUpdate: ({ editor }) => { + const jsonContent = JSON.stringify(editor.getJSON()); + localStorage.setItem('editor-post', jsonContent); + }, + }); + + return ( + + ); +} diff --git a/src/app/notes/components/index.ts b/src/app/notes/components/index.ts new file mode 100644 index 00000000..bfd163e0 --- /dev/null +++ b/src/app/notes/components/index.ts @@ -0,0 +1 @@ +export * from './editor/post'; diff --git a/src/app/notes/new.tsx b/src/app/notes/new.tsx new file mode 100644 index 00000000..b7c95886 --- /dev/null +++ b/src/app/notes/new.tsx @@ -0,0 +1,74 @@ +import { useState } from 'react'; +import { Link } from 'react-router-dom'; +import { twMerge } from 'tailwind-merge'; + +import { PostEditor } from '@app/notes/components'; + +import { ArrowLeftIcon } from '@shared/icons'; + +export function NewNoteScreen() { + const [type, setType] = useState<'post' | 'article' | 'file' | 'raw'>('post'); + + return ( +
+
+
+ + + +
+
+
+
+ + + + +
+
+ {type === 'post' ? : null} +
+
+
+
+ ); +} diff --git a/src/libs/ndk/instance.ts b/src/libs/ndk/instance.ts index 946a4bc1..e4bc6ba6 100644 --- a/src/libs/ndk/instance.ts +++ b/src/libs/ndk/instance.ts @@ -68,8 +68,6 @@ export const NDKInstance = () => { // Privkey Signer const userPrivkey = await db.secureLoad(db.account.pubkey); if (userPrivkey) return new NDKPrivateKeySigner(userPrivkey); - - return null; } async function initNDK() { @@ -87,9 +85,13 @@ export const NDKInstance = () => { try { // connect await instance.connect(2000); + // add signer const signer = await getSigner(instance); instance.signer = signer; + + // update account's metadata + // todo } catch (error) { await message(`NDK instance init failed: ${error}`, { title: 'Lume', diff --git a/src/libs/storage/instance.ts b/src/libs/storage/instance.ts index 6f342db9..5ac8499d 100644 --- a/src/libs/storage/instance.ts +++ b/src/libs/storage/instance.ts @@ -23,9 +23,13 @@ export class LumeStorage { } public async secureLoad(key: string) { - const value: string = await invoke('secure_load', { key }); - if (!value) return null; - return value; + try { + const value: string = await invoke('secure_load', { key }); + if (!value) return null; + return value; + } catch { + return null; + } } public async secureRemove(key: string) { diff --git a/src/shared/accounts/more.tsx b/src/shared/accounts/more.tsx index 6fbbc80d..bbc4f82a 100644 --- a/src/shared/accounts/more.tsx +++ b/src/shared/accounts/more.tsx @@ -44,9 +44,7 @@ export function AccountMoreActions({ pubkey }: { pubkey: string }) { Settings - - - + diff --git a/src/shared/logout.tsx b/src/shared/logout.tsx index 31da1c1d..54eb8d1d 100644 --- a/src/shared/logout.tsx +++ b/src/shared/logout.tsx @@ -1,16 +1,23 @@ import * as AlertDialog from '@radix-ui/react-alert-dialog'; import { useNavigate } from 'react-router-dom'; +import { useNDK } from '@libs/ndk/provider'; import { useStorage } from '@libs/storage/provider'; export function Logout() { - const { db } = useStorage(); - const navigate = useNavigate(); + const { db } = useStorage(); + const { ndk } = useNDK(); + const logout = async () => { + ndk.signer = null; + // remove account - db.accountLogout(); + await db.accountLogout(); + await db.secureRemove(db.account.pubkey); + await db.secureRemove(db.account.pubkey + '-bunker'); + // redirect to welcome screen navigate('/auth/welcome'); }; @@ -26,11 +33,11 @@ export function Logout() { - + -
-
- +
+
+ Are you sure! @@ -40,14 +47,17 @@ export function Logout() {
- diff --git a/src/shared/navigation.tsx b/src/shared/navigation.tsx index c95e37f3..4b85149e 100644 --- a/src/shared/navigation.tsx +++ b/src/shared/navigation.tsx @@ -2,8 +2,14 @@ import { Link, NavLink } from 'react-router-dom'; import { twMerge } from 'tailwind-merge'; import { ActiveAccount } from '@shared/accounts/active'; -import { ComposerModal } from '@shared/composer'; -import { ChatsIcon, ExploreIcon, HomeIcon, NwcIcon, RelayIcon } from '@shared/icons'; +import { + ChatsIcon, + ComposeIcon, + ExploreIcon, + HomeIcon, + NwcIcon, + RelayIcon, +} from '@shared/icons'; export function Navigation() { return ( @@ -21,7 +27,7 @@ export function Navigation() { 'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg', isActive ? 'bg-black/10 text-black dark:bg-white/10 dark:text-white' - : 'text-black/50 dark:text-neutral-400 dark:text-neutral-600' + : 'text-black/50 dark:text-neutral-400' )} > @@ -42,7 +48,7 @@ export function Navigation() { 'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg', isActive ? 'bg-black/10 text-black dark:bg-white/10 dark:text-white' - : 'text-black/50 dark:text-neutral-400 dark:text-neutral-600' + : 'text-black/50 dark:text-neutral-400' )} > @@ -63,7 +69,7 @@ export function Navigation() { 'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg', isActive ? 'bg-black/10 text-black dark:bg-white/10 dark:text-white' - : 'text-black/50 dark:text-neutral-400 dark:text-neutral-600' + : 'text-black/50 dark:text-neutral-400' )} > @@ -84,7 +90,7 @@ export function Navigation() { 'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg', isActive ? 'bg-black/10 text-black dark:bg-white/10 dark:text-white' - : 'text-black/50 dark:text-neutral-400 dark:text-neutral-600' + : 'text-black/50 dark:text-neutral-400' )} > @@ -97,7 +103,12 @@ export function Navigation() {
- + + + - +