wip: multi-type composer

This commit is contained in:
reya 2023-10-21 15:58:39 +07:00
parent de88ca51fe
commit cade8c8b4c
16 changed files with 234 additions and 36 deletions

View File

@ -83,6 +83,7 @@
"tailwind-scrollbar": "^3.0.5", "tailwind-scrollbar": "^3.0.5",
"tauri-controls": "^0.2.0", "tauri-controls": "^0.2.0",
"tippy.js": "^6.3.7", "tippy.js": "^6.3.7",
"tiptap-markdown": "^0.8.2",
"virtua": "^0.14.0", "virtua": "^0.14.0",
"zustand": "^4.4.3" "zustand": "^4.4.3"
}, },

View File

@ -200,6 +200,9 @@ dependencies:
tippy.js: tippy.js:
specifier: ^6.3.7 specifier: ^6.3.7
version: 6.3.7 version: 6.3.7
tiptap-markdown:
specifier: ^0.8.2
version: 0.8.2(@tiptap/core@2.1.12)
virtua: virtua:
specifier: ^0.14.0 specifier: ^0.14.0
version: 0.14.0(react-dom@18.2.0)(react@18.2.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==} resolution: {integrity: sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==}
dev: true 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: /@types/mdast@3.0.14:
resolution: {integrity: sha512-gVZ04PGgw1qLZKsnWnyFv4ORnaJ+DXLdHTVSFbU8yX6xZ34Bjg4Q32yPkmveUP1yItXReKfB0Aknlh/3zxTKAw==} resolution: {integrity: sha512-gVZ04PGgw1qLZKsnWnyFv4ORnaJ+DXLdHTVSFbU8yX6xZ34Bjg4Q32yPkmveUP1yItXReKfB0Aknlh/3zxTKAw==}
dependencies: dependencies:
'@types/unist': 2.0.9 '@types/unist': 2.0.9
dev: false dev: false
/@types/mdurl@1.0.4:
resolution: {integrity: sha512-ARVxjAEX5TARFRzpDRVC6cEk0hUIXCCwaMhz8y7S1/PxU6zZS1UMjyobz7q4w/D/R552r4++EhwmXK1N2rAy0A==}
dev: false
/@types/ms@0.7.33: /@types/ms@0.7.33:
resolution: {integrity: sha512-AuHIyzR5Hea7ij0P9q7vx7xu4z0C28ucwjAZC0ja7JhINyCnOw8/DnvAPQQ9TfOlCtZAmCERKQX9+o1mgQhuOQ==} resolution: {integrity: sha512-AuHIyzR5Hea7ij0P9q7vx7xu4z0C28ucwjAZC0ja7JhINyCnOw8/DnvAPQQ9TfOlCtZAmCERKQX9+o1mgQhuOQ==}
dev: false dev: false
@ -4735,6 +4753,10 @@ packages:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
dev: false dev: false
/markdown-it-task-lists@2.1.1:
resolution: {integrity: sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==}
dev: false
/markdown-it@13.0.2: /markdown-it@13.0.2:
resolution: {integrity: sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==} resolution: {integrity: sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==}
hasBin: true hasBin: true
@ -6434,6 +6456,18 @@ packages:
'@popperjs/core': 2.11.8 '@popperjs/core': 2.11.8
dev: false 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: /to-fast-properties@2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'} engines: {node: '>=4'}

View File

@ -123,6 +123,13 @@ export default function App() {
element: <NoteLayout />, element: <NoteLayout />,
errorElement: <ErrorScreen />, errorElement: <ErrorScreen />,
children: [ children: [
{
path: 'new',
async lazy() {
const { NewNoteScreen } = await import('@app/notes/new');
return { Component: NewNoteScreen };
},
},
{ {
path: 'text/:id', path: 'text/:id',
async lazy() { async lazy() {

View File

@ -59,16 +59,16 @@ export function CreateAccountScreen() {
const userNpub = nip19.npubEncode(userPubkey); const userNpub = nip19.npubEncode(userPubkey);
const userNsec = nip19.nsecEncode(userPrivkey); const userNsec = nip19.nsecEncode(userPrivkey);
const event = new NDKEvent(ndk);
const signer = new NDKPrivateKeySigner(userPrivkey); const signer = new NDKPrivateKeySigner(userPrivkey);
ndk.signer = signer;
const event = new NDKEvent(ndk);
event.content = JSON.stringify(profile); event.content = JSON.stringify(profile);
event.kind = NDKKind.Metadata; event.kind = NDKKind.Metadata;
event.created_at = Math.floor(Date.now() / 1000); event.created_at = Math.floor(Date.now() / 1000);
event.pubkey = userPubkey; event.pubkey = userPubkey;
event.tags = []; event.tags = [];
await event.sign(signer);
const publish = await event.publish(); const publish = await event.publish();
if (publish) { if (publish) {
@ -217,7 +217,7 @@ export function CreateAccountScreen() {
<p className="mb-2 select-text text-sm text-neutral-800 dark:text-neutral-200"> <p className="mb-2 select-text text-sm text-neutral-800 dark:text-neutral-200">
Your private key is your password. If you lose this key, you will 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.{' '} lose access to your account! Copy it and keep it in a safe place.{' '}
<span className="text-red-500"> <span className="text-red-600">
There is no way to reset your private key. There is no way to reset your private key.
</span> </span>
</p> </p>

View File

@ -1,8 +1,10 @@
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { useState } from 'react'; import { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { useNDK } from '@libs/ndk/provider';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
import { ArrowLeftIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons'; import { ArrowLeftIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons';
@ -10,10 +12,10 @@ import { User } from '@shared/user';
import { useOnboarding } from '@stores/onboarding'; import { useOnboarding } from '@stores/onboarding';
import { useNostr } from '@utils/hooks/useNostr';
import { arrayToNIP02 } from '@utils/transform'; import { arrayToNIP02 } from '@utils/transform';
export function OnboardEnrichScreen() { export function OnboardEnrichScreen() {
const { ndk } = useNDK();
const { db } = useStorage(); const { db } = useStorage();
const { status, data } = useQuery(['trending-profiles-widget'], async () => { const { status, data } = useQuery(['trending-profiles-widget'], async () => {
const res = await fetch('https://api.nostr.band/v0/trending/profiles'); const res = await fetch('https://api.nostr.band/v0/trending/profiles');
@ -22,7 +24,6 @@ export function OnboardEnrichScreen() {
} }
return res.json(); return res.json();
}); });
const { publish } = useNostr();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [follows, setFollows] = useState([]); const [follows, setFollows] = useState([]);
@ -43,10 +44,17 @@ export function OnboardEnrichScreen() {
setLoading(true); setLoading(true);
const tags = arrayToNIP02(follows); 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 // redirect to next step
if (event) { if (publish) {
db.account.follows = follows; db.account.follows = follows;
await db.updateAccount('follows', JSON.stringify(follows)); await db.updateAccount('follows', JSON.stringify(follows));

View File

@ -1,8 +1,10 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { useState } from 'react'; import { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { useNDK } from '@libs/ndk/provider';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
import { ArrowLeftIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons'; import { ArrowLeftIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons';
@ -19,8 +21,8 @@ export function OnboardRelaysScreen() {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [relays, setRelays] = useState(new Set<string>()); const [relays, setRelays] = useState(new Set<string>());
const { publish } = useNostr();
const { db } = useStorage(); const { db } = useStorage();
const { ndk } = useNDK();
const { getAllRelaysByUsers } = useNostr(); const { getAllRelaysByUsers } = useNostr();
const { status, data } = useQuery( const { status, data } = useQuery(
['relays'], ['relays'],
@ -52,7 +54,13 @@ export function OnboardRelaysScreen() {
} }
const tags = Array.from(relays).map((relay) => ['r', relay.replace(/\/+$/, '')]); 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(); toggleRelays();
navigate(-1); navigate(-1);

View File

@ -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 (
<EditorContent
editor={editor}
spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
/>
);
}

View File

@ -0,0 +1 @@
export * from './editor/post';

74
src/app/notes/new.tsx Normal file
View File

@ -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 (
<div className="h-full w-full pt-4">
<div className="container mx-auto grid grid-cols-8 px-4">
<div className="col-span-1">
<Link
to="/"
className="inline-flex h-10 w-10 items-center justify-center rounded-lg bg-neutral-100 hover:bg-neutral-200 dark:bg-neutral-900"
>
<ArrowLeftIcon className="h-5 w-5" />
</Link>
</div>
<div className="col-span-6">
<div className="mb-8 flex items-center gap-3">
<div className="flex h-10 items-center gap-2 rounded-lg bg-neutral-100 px-0.5 dark:bg-neutral-800">
<button
type="button"
onClick={() => setType('post')}
className={twMerge(
'inline-flex h-9 w-20 items-center justify-center rounded-lg text-sm font-medium',
type === 'post' ? 'bg-white shadow' : 'bg-transparent'
)}
>
Post
</button>
<button
type="button"
onClick={() => setType('article')}
className={twMerge(
'inline-flex h-9 w-20 items-center justify-center rounded-lg text-sm font-medium',
type === 'article' ? 'bg-white shadow' : 'bg-transparent'
)}
>
Article
</button>
<button
type="button"
onClick={() => setType('file')}
className={twMerge(
'inline-flex h-9 w-20 items-center justify-center rounded-lg text-sm font-medium',
type === 'file' ? 'bg-white shadow' : 'bg-transparent'
)}
>
File
</button>
<button
type="button"
onClick={() => setType('raw')}
className={twMerge(
'inline-flex h-9 w-20 items-center justify-center rounded-lg text-sm font-medium',
type === 'file' ? 'bg-white shadow' : 'bg-transparent'
)}
>
Raw (advance)
</button>
</div>
</div>
{type === 'post' ? <PostEditor /> : null}
</div>
<div className="col-span-1" />
</div>
</div>
);
}

View File

@ -68,8 +68,6 @@ export const NDKInstance = () => {
// Privkey Signer // Privkey Signer
const userPrivkey = await db.secureLoad(db.account.pubkey); const userPrivkey = await db.secureLoad(db.account.pubkey);
if (userPrivkey) return new NDKPrivateKeySigner(userPrivkey); if (userPrivkey) return new NDKPrivateKeySigner(userPrivkey);
return null;
} }
async function initNDK() { async function initNDK() {
@ -87,9 +85,13 @@ export const NDKInstance = () => {
try { try {
// connect // connect
await instance.connect(2000); await instance.connect(2000);
// add signer // add signer
const signer = await getSigner(instance); const signer = await getSigner(instance);
instance.signer = signer; instance.signer = signer;
// update account's metadata
// todo
} catch (error) { } catch (error) {
await message(`NDK instance init failed: ${error}`, { await message(`NDK instance init failed: ${error}`, {
title: 'Lume', title: 'Lume',

View File

@ -23,9 +23,13 @@ export class LumeStorage {
} }
public async secureLoad(key: string) { public async secureLoad(key: string) {
const value: string = await invoke('secure_load', { key }); try {
if (!value) return null; const value: string = await invoke('secure_load', { key });
return value; if (!value) return null;
return value;
} catch {
return null;
}
} }
public async secureRemove(key: string) { public async secureRemove(key: string) {

View File

@ -44,9 +44,7 @@ export function AccountMoreActions({ pubkey }: { pubkey: string }) {
Settings Settings
</Link> </Link>
</DropdownMenu.Item> </DropdownMenu.Item>
<DropdownMenu.Item asChild> <Logout />
<Logout />
</DropdownMenu.Item>
</DropdownMenu.Content> </DropdownMenu.Content>
</DropdownMenu.Portal> </DropdownMenu.Portal>
</DropdownMenu.Root> </DropdownMenu.Root>

View File

@ -1,16 +1,23 @@
import * as AlertDialog from '@radix-ui/react-alert-dialog'; import * as AlertDialog from '@radix-ui/react-alert-dialog';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useNDK } from '@libs/ndk/provider';
import { useStorage } from '@libs/storage/provider'; import { useStorage } from '@libs/storage/provider';
export function Logout() { export function Logout() {
const { db } = useStorage();
const navigate = useNavigate(); const navigate = useNavigate();
const { db } = useStorage();
const { ndk } = useNDK();
const logout = async () => { const logout = async () => {
ndk.signer = null;
// remove account // remove account
db.accountLogout(); await db.accountLogout();
await db.secureRemove(db.account.pubkey);
await db.secureRemove(db.account.pubkey + '-bunker');
// redirect to welcome screen // redirect to welcome screen
navigate('/auth/welcome'); navigate('/auth/welcome');
}; };
@ -26,11 +33,11 @@ export function Logout() {
</button> </button>
</AlertDialog.Trigger> </AlertDialog.Trigger>
<AlertDialog.Portal> <AlertDialog.Portal>
<AlertDialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-2xl" /> <AlertDialog.Overlay className="fixed inset-0 z-50 bg-black/50 backdrop-blur-2xl dark:bg-white/50" />
<AlertDialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center"> <AlertDialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<div className="relative h-min w-full max-w-md rounded-xl bg-neutral-400 dark:bg-neutral-600"> <div className="relative h-min w-full max-w-md rounded-xl bg-neutral-100 dark:bg-neutral-900">
<div className="flex flex-col gap-2 border-b border-white/5 px-5 py-4"> <div className="flex flex-col gap-1 border-b border-white/5 px-5 py-4">
<AlertDialog.Title className="text-lg font-semibold leading-none text-white"> <AlertDialog.Title className="text-lg font-semibold text-neutral-900 dark:text-neutral-100">
Are you sure! Are you sure!
</AlertDialog.Title> </AlertDialog.Title>
<AlertDialog.Description className="text-sm leading-tight text-neutral-600 dark:text-neutral-400"> <AlertDialog.Description className="text-sm leading-tight text-neutral-600 dark:text-neutral-400">
@ -40,14 +47,17 @@ export function Logout() {
</div> </div>
<div className="flex justify-end gap-2 px-5 py-3"> <div className="flex justify-end gap-2 px-5 py-3">
<AlertDialog.Cancel asChild> <AlertDialog.Cancel asChild>
<button className="inline-flex h-9 items-center justify-center rounded-md px-4 text-sm font-medium leading-none text-white outline-none hover:bg-white/10 hover:backdrop-blur-xl"> <button
type="button"
className="inline-flex h-9 items-center justify-center rounded-lg px-4 text-sm font-medium text-neutral-900 outline-none hover:bg-neutral-200 dark:text-neutral-100 dark:hover:bg-neutral-800"
>
Cancel Cancel
</button> </button>
</AlertDialog.Cancel> </AlertDialog.Cancel>
<button <button
type="button" type="button"
onClick={() => logout()} onClick={() => logout()}
className="inline-flex h-9 items-center justify-center rounded-md bg-white/10 px-4 text-sm font-medium leading-none text-white outline-none hover:bg-blue-600" className="inline-flex h-9 items-center justify-center rounded-lg bg-red-500 px-4 text-sm font-medium text-white outline-none hover:bg-red-600"
> >
Logout Logout
</button> </button>

View File

@ -2,8 +2,14 @@ import { Link, NavLink } from 'react-router-dom';
import { twMerge } from 'tailwind-merge'; import { twMerge } from 'tailwind-merge';
import { ActiveAccount } from '@shared/accounts/active'; import { ActiveAccount } from '@shared/accounts/active';
import { ComposerModal } from '@shared/composer'; import {
import { ChatsIcon, ExploreIcon, HomeIcon, NwcIcon, RelayIcon } from '@shared/icons'; ChatsIcon,
ComposeIcon,
ExploreIcon,
HomeIcon,
NwcIcon,
RelayIcon,
} from '@shared/icons';
export function Navigation() { export function Navigation() {
return ( return (
@ -21,7 +27,7 @@ export function Navigation() {
'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg', 'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg',
isActive isActive
? 'bg-black/10 text-black dark:bg-white/10 dark:text-white' ? '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'
)} )}
> >
<HomeIcon className="h-6 w-6" /> <HomeIcon className="h-6 w-6" />
@ -42,7 +48,7 @@ export function Navigation() {
'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg', 'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg',
isActive isActive
? 'bg-black/10 text-black dark:bg-white/10 dark:text-white' ? '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'
)} )}
> >
<ChatsIcon className="h-6 w-6" /> <ChatsIcon className="h-6 w-6" />
@ -63,7 +69,7 @@ export function Navigation() {
'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg', 'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg',
isActive isActive
? 'bg-black/10 text-black dark:bg-white/10 dark:text-white' ? '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'
)} )}
> >
<RelayIcon className="h-6 w-6" /> <RelayIcon className="h-6 w-6" />
@ -84,7 +90,7 @@ export function Navigation() {
'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg', 'inline-flex aspect-square h-auto w-full items-center justify-center rounded-lg',
isActive isActive
? 'bg-black/10 text-black dark:bg-white/10 dark:text-white' ? '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'
)} )}
> >
<ExploreIcon className="h-6 w-6" /> <ExploreIcon className="h-6 w-6" />
@ -97,7 +103,12 @@ export function Navigation() {
</NavLink> </NavLink>
</div> </div>
<div className="flex shrink-0 flex-col gap-3 p-1"> <div className="flex shrink-0 flex-col gap-3 p-1">
<ComposerModal /> <Link
to="/notes/new"
className="flex aspect-square h-auto w-full items-center justify-center rounded-lg bg-neutral-100 text-black hover:bg-blue-500 hover:text-white dark:bg-neutral-900 dark:text-white dark:hover:bg-blue-500"
>
<ComposeIcon className="h-5 w-5" />
</Link>
<Link <Link
to="/nwc" to="/nwc"
className="flex aspect-square h-auto w-full items-center justify-center rounded-lg bg-neutral-100 hover:bg-blue-500 hover:text-white dark:bg-neutral-900 dark:hover:bg-blue-500" className="flex aspect-square h-auto w-full items-center justify-center rounded-lg bg-neutral-100 hover:bg-blue-500 hover:text-white dark:bg-neutral-900 dark:hover:bg-blue-500"

View File

@ -317,7 +317,7 @@ export const User = memo(function User({
if (variant === 'thread') { if (variant === 'thread') {
return ( return (
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Avatar.Root className="shrink-0"> <Avatar.Root className="h-10 w-10 shrink-0">
<Avatar.Image <Avatar.Image
src={user?.picture || user?.image} src={user?.picture || user?.image}
alt={pubkey} alt={pubkey}

View File

@ -1,10 +1,10 @@
import react from '@vitejs/plugin-react-swc'; import react from '@vitejs/plugin-react-swc';
import million from 'million/compiler'; //import million from 'million/compiler';
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import viteTsconfigPaths from 'vite-tsconfig-paths'; import viteTsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({ export default defineConfig({
plugins: [million.vite({ auto: true, mute: true }), react(), viteTsconfigPaths()], plugins: [/*million.vite({ auto: true, mute: true }),*/ react(), viteTsconfigPaths()],
envPrefix: ['VITE_', 'TAURI_'], envPrefix: ['VITE_', 'TAURI_'],
build: { build: {
target: process.env.TAURI_PLATFORM === 'windows' ? 'chrome105' : 'safari13', target: process.env.TAURI_PLATFORM === 'windows' ? 'chrome105' : 'safari13',