wip: clean up & refactor
This commit is contained in:
@@ -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 <div className="h-9 w-9 animate-pulse rounded-md bg-white/50" />;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 (
|
||||
<Dialog.Root open={open} onOpenChange={toggle}>
|
||||
<Dialog.Trigger asChild>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeModal}
|
||||
className="inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium text-white/50 hover:bg-white/10"
|
||||
>
|
||||
Cancel
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import * as Tooltip from '@radix-ui/react-tooltip';
|
||||
|
||||
import { useStorage } from '@libs/storage/provider';
|
||||
|
||||
import { ThreadIcon } from '@shared/icons';
|
||||
import { MoreActions } from '@shared/notes/actions/more';
|
||||
import { NoteReaction } from '@shared/notes/actions/reaction';
|
||||
@@ -7,7 +9,7 @@ import { NoteReply } from '@shared/notes/actions/reply';
|
||||
import { NoteRepost } from '@shared/notes/actions/repost';
|
||||
import { NoteZap } from '@shared/notes/actions/zap';
|
||||
|
||||
import { BLOCK_KINDS } from '@stores/constants';
|
||||
import { widgetKinds } from '@stores/constants';
|
||||
import { useWidgets } from '@stores/widgets';
|
||||
|
||||
export function NoteActions({
|
||||
@@ -21,6 +23,7 @@ export function NoteActions({
|
||||
noOpenThread?: boolean;
|
||||
root?: string;
|
||||
}) {
|
||||
const { db } = useStorage();
|
||||
const setWidget = useWidgets((state) => state.setWidget);
|
||||
|
||||
return (
|
||||
@@ -40,8 +43,8 @@ export function NoteActions({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setWidget({
|
||||
kind: BLOCK_KINDS.thread,
|
||||
setWidget(db, {
|
||||
kind: widgetKinds.thread,
|
||||
title: 'Thread',
|
||||
content: id,
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BLOCK_KINDS } from '@stores/constants';
|
||||
import { widgetKinds } from '@stores/constants';
|
||||
import { useWidgets } from '@stores/widgets';
|
||||
|
||||
export function Hashtag({ tag }: { tag: string }) {
|
||||
@@ -9,7 +9,7 @@ export function Hashtag({ tag }: { tag: string }) {
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setWidget({
|
||||
kind: BLOCK_KINDS.hashtag,
|
||||
kind: widgetKinds.hashtag,
|
||||
title: tag,
|
||||
content: tag.replace('#', ''),
|
||||
})
|
||||
|
||||
@@ -24,11 +24,11 @@ export function NoteKind_1({
|
||||
<div className="w-11 shrink-0" />
|
||||
<div className="relative z-20 flex-1">
|
||||
<NoteContent content={content} />
|
||||
<NoteActions id={event.event_id || event.id} pubkey={event.pubkey} />
|
||||
<NoteActions id={event.id || event.id} pubkey={event.pubkey} />
|
||||
</div>
|
||||
</div>
|
||||
{!skipMetadata ? (
|
||||
<NoteMetadata id={event.event_id || event.id} />
|
||||
<NoteMetadata id={event.id || event.id} />
|
||||
) : (
|
||||
<div className="pb-3" />
|
||||
)}
|
||||
|
||||
@@ -27,10 +27,10 @@ export function NoteKind_1063({ event }: { event: LumeEvent }) {
|
||||
className="h-auto w-full rounded-lg object-cover"
|
||||
/>
|
||||
)}
|
||||
<NoteActions id={event.event_id} pubkey={event.pubkey} />
|
||||
<NoteActions id={event.id} pubkey={event.pubkey} />
|
||||
</div>
|
||||
</div>
|
||||
<NoteMetadata id={event.event_id} />
|
||||
<NoteMetadata id={event.id} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,7 @@ export function SubNote({ id, root }: { id: string; root?: string }) {
|
||||
<div className="w-11 shrink-0" />
|
||||
<div className="relative z-20 flex-1">
|
||||
<NoteContent content={data.content} long={data.kind === 30023} />
|
||||
<NoteActions id={data.event_id} pubkey={data.pubkey} root={root} />
|
||||
<NoteActions id={data.id} pubkey={data.pubkey} root={root} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -28,10 +28,10 @@ export function NoteThread({
|
||||
<div className="w-11 shrink-0" />
|
||||
<div className="relative z-20 flex-1">
|
||||
<NoteContent content={content} />
|
||||
<NoteActions id={event.event_id} pubkey={event.pubkey} />
|
||||
<NoteActions id={event.id} pubkey={event.pubkey} />
|
||||
</div>
|
||||
</div>
|
||||
<NoteMetadata id={event.event_id} />
|
||||
<NoteMetadata id={event.id} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -25,10 +25,10 @@ export function NoteKindUnsupport({ event }: { event: LumeEvent }) {
|
||||
<p>{event.content.toString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
<NoteActions id={event.event_id} pubkey={event.pubkey} />
|
||||
<NoteActions id={event.id} pubkey={event.pubkey} />
|
||||
</div>
|
||||
</div>
|
||||
<NoteMetadata id={event.event_id} />
|
||||
<NoteMetadata id={event.id} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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 }) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setWidget({ kind: BLOCK_KINDS.thread, title: 'Thread', content: id })
|
||||
setWidget(db, {
|
||||
kind: widgetKinds.thread,
|
||||
title: 'Thread',
|
||||
content: id,
|
||||
})
|
||||
}
|
||||
className="text-white/50"
|
||||
>
|
||||
|
||||
@@ -18,11 +18,7 @@ export function Reply({ event, root }: { event: LumeEvent; root?: string }) {
|
||||
<div className="w-11 shrink-0" />
|
||||
<div className="flex-1">
|
||||
<NoteContent content={content} />
|
||||
<NoteActions
|
||||
id={event.event_id || event.id}
|
||||
pubkey={event.pubkey}
|
||||
root={root}
|
||||
/>
|
||||
<NoteActions id={event.id || event.id} pubkey={event.pubkey} root={root} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -16,7 +16,7 @@ export function SubReply({ event }: { event: LumeEvent }) {
|
||||
<div className="w-11 shrink-0" />
|
||||
<div className="flex-1">
|
||||
<NoteContent content={content} />
|
||||
<NoteActions id={event.event_id || event.id} pubkey={event.pubkey} />
|
||||
<NoteActions id={event.id || event.id} pubkey={event.pubkey} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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 (
|
||||
<div className="flex h-full w-full items-center justify-center bg-black/90">
|
||||
<LoaderIcon className="h-6 w-6 animate-spin text-white" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!account) {
|
||||
return <Navigate to="/auth/welcome" replace />;
|
||||
}
|
||||
|
||||
if (account && account.privkey.length > 35) {
|
||||
return <Navigate to="/auth/migrate" replace />;
|
||||
}
|
||||
|
||||
if (account && !privkey) {
|
||||
return <Navigate to="/auth/unlock" replace />;
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
@@ -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 ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => 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"
|
||||
>
|
||||
<CancelIcon className="h-3 w-3 text-white" />
|
||||
|
||||
@@ -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)}
|
||||
</h5>
|
||||
<span className="leading-none text-white/50">·</span>
|
||||
<span className="leading-none text-white/50">{createdAt}</span>
|
||||
|
||||
Reference in New Issue
Block a user