From 671b857077bb78332688d1269ab0b52f84fde51d Mon Sep 17 00:00:00 2001 From: Ren Amamiya <123083837+reyamir@users.noreply.github.com> Date: Fri, 26 May 2023 14:45:12 +0700 Subject: [PATCH] wip: migrate to zustand --- package.json | 3 +- pnpm-lock.yaml | 19 ++ .../auth/pages/create/step-2/index.page.tsx | 12 +- .../auth/pages/create/step-3/index.page.tsx | 13 +- .../auth/pages/import/step-2/index.page.tsx | 18 +- src/app/channel/components/createModal.tsx | 21 +- src/app/channel/components/list.tsx | 19 +- src/app/channel/components/messages/form.tsx | 15 +- .../components/messages/hideButton.tsx | 19 +- .../components/messages/muteButton.tsx | 17 +- src/app/channel/components/updateModal.tsx | 18 +- src/app/channel/pages/index.page.tsx | 15 +- src/app/chat/components/list.tsx | 24 +- src/app/chat/components/messageList.tsx | 7 +- src/app/chat/components/messages/form.tsx | 43 ++-- src/app/chat/components/self.tsx | 10 +- src/app/chat/pages/index.page.tsx | 11 +- src/app/index/pages/index.page.tsx | 14 +- src/app/note/components/metadata/like.tsx | 48 ++-- src/app/note/components/metadata/reply.tsx | 48 ++-- src/app/note/components/metadata/repost.tsx | 49 ++-- src/app/note/components/replies/form.tsx | 39 ++- src/app/prefetch/pages/index.page.tsx | 189 +++++++-------- src/app/space/components/create.tsx | 10 +- src/shared/composer/modal.tsx | 24 +- src/shared/eventCollector.tsx | 222 ++++++++---------- src/stores/accounts.tsx | 13 + src/stores/channels.tsx | 17 ++ src/stores/chats.tsx | 21 ++ src/stores/composer.tsx | 3 - src/stores/note.tsx | 8 - src/stores/onboarding.tsx | 8 - src/utils/hooks/useActiveAccount.tsx | 14 -- src/utils/storage.tsx | 13 +- 34 files changed, 494 insertions(+), 530 deletions(-) create mode 100644 src/stores/accounts.tsx create mode 100644 src/stores/channels.tsx create mode 100644 src/stores/chats.tsx delete mode 100644 src/stores/composer.tsx delete mode 100644 src/stores/note.tsx delete mode 100644 src/stores/onboarding.tsx delete mode 100644 src/utils/hooks/useActiveAccount.tsx diff --git a/package.json b/package.json index 7f13a0af..efa633a9 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,8 @@ "swr": "^2.1.5", "tailwind-merge": "^1.12.0", "tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql", - "vidstack": "^0.4.5" + "vidstack": "^0.4.5", + "zustand": "^4.3.8" }, "devDependencies": { "@tailwindcss/typography": "^0.5.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1169a5e3..8d8c83d7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -76,6 +76,9 @@ dependencies: vidstack: specifier: ^0.4.5 version: 0.4.5 + zustand: + specifier: ^4.3.8 + version: 4.3.8(react@18.2.0) devDependencies: '@tailwindcss/typography': @@ -3082,6 +3085,22 @@ packages: engines: {node: '>= 14', npm: '>= 7'} dev: true + /zustand@4.3.8(react@18.2.0): + resolution: {integrity: sha512-4h28KCkHg5ii/wcFFJ5Fp+k1J3gJoasaIbppdgZFO4BPJnsNxL0mQXBSFgOgAdCdBj35aDTPvdAJReTMntFPGg==} + engines: {node: '>=12.7.0'} + peerDependencies: + immer: '>=9.0' + react: '>=16.8' + peerDependenciesMeta: + immer: + optional: true + react: + optional: true + dependencies: + react: 18.2.0 + use-sync-external-store: 1.2.0(react@18.2.0) + dev: false + /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} dev: false diff --git a/src/app/auth/pages/create/step-2/index.page.tsx b/src/app/auth/pages/create/step-2/index.page.tsx index 0747cf4d..69386640 100644 --- a/src/app/auth/pages/create/step-2/index.page.tsx +++ b/src/app/auth/pages/create/step-2/index.page.tsx @@ -1,8 +1,8 @@ import { AvatarUploader } from "@shared/avatarUploader"; import { Image } from "@shared/image"; import { RelayContext } from "@shared/relayProvider"; +import { useActiveAccount } from "@stores/accounts"; import { DEFAULT_AVATAR, WRITEONLY_RELAYS } from "@stores/constants"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; import { getEventHash, getSignature } from "nostr-tools"; import { useContext, useEffect, useState } from "react"; import { useForm } from "react-hook-form"; @@ -11,8 +11,10 @@ import { navigate } from "vite-plugin-ssr/client/router"; export function Page() { const pool: any = useContext(RelayContext); - const { account } = useActiveAccount(); - + const [account, fetchAccount] = useActiveAccount((state: any) => [ + state.account, + state.fetch, + ]); const [image, setImage] = useState(DEFAULT_AVATAR); const [loading, setLoading] = useState(false); @@ -50,6 +52,10 @@ export function Page() { ); }; + useEffect(() => { + fetchAccount(); + }, [fetchAccount]); + useEffect(() => { setValue("picture", image); }, [setValue, image]); diff --git a/src/app/auth/pages/create/step-3/index.page.tsx b/src/app/auth/pages/create/step-3/index.page.tsx index dde35aaf..f223595d 100644 --- a/src/app/auth/pages/create/step-3/index.page.tsx +++ b/src/app/auth/pages/create/step-3/index.page.tsx @@ -1,8 +1,8 @@ import { User } from "@app/auth/components/user"; import CheckCircleIcon from "@icons/checkCircle"; import { RelayContext } from "@shared/relayProvider"; +import { useActiveAccount } from "@stores/accounts"; import { WRITEONLY_RELAYS } from "@stores/constants"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; import { updateAccount } from "@utils/storage"; import { arrayToNIP02 } from "@utils/transform"; import { getEventHash, getSignature } from "nostr-tools"; @@ -111,8 +111,10 @@ const initialList = [ export function Page() { const pool: any = useContext(RelayContext); - const { account } = useActiveAccount(); - + const [account, updateFollows] = useActiveAccount((state: any) => [ + state.account, + state.updateFollows, + ]); const [loading, setLoading] = useState(false); const [follows, setFollows] = useState([]); @@ -128,9 +130,12 @@ export function Page() { const submit = async () => { setLoading(true); - // update account follows + // update account follows in database updateAccount("follows", follows, account.pubkey); + // update account follows in state + updateFollows(JSON.stringify(follows)); + const tags = arrayToNIP02(follows); const event: any = { diff --git a/src/app/auth/pages/import/step-2/index.page.tsx b/src/app/auth/pages/import/step-2/index.page.tsx index 28a208ad..ac856d76 100644 --- a/src/app/auth/pages/import/step-2/index.page.tsx +++ b/src/app/auth/pages/import/step-2/index.page.tsx @@ -1,18 +1,19 @@ import { User } from "@app/auth/components/user"; import { RelayContext } from "@shared/relayProvider"; +import { useActiveAccount } from "@stores/accounts"; import { READONLY_RELAYS } from "@stores/constants"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; import { updateAccount } from "@utils/storage"; import { nip02ToArray } from "@utils/transform"; -import { useContext, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import useSWRSubscription from "swr/subscription"; import { navigate } from "vite-plugin-ssr/client/router"; export function Page() { const pool: any = useContext(RelayContext); - const { account } = useActiveAccount(); - + const [account, fetchAccount, updateFollows] = useActiveAccount( + (state: any) => [state.account, state.fetch, state.updateFollows], + ); const [loading, setLoading] = useState(false); const [follows, setFollows] = useState([]); @@ -42,9 +43,12 @@ export function Page() { // follows as list const followsList = nip02ToArray(follows); - // update account follows + // update account follows in database updateAccount("follows", followsList, account.pubkey); + // update account follows in store + updateFollows(JSON.stringify(followsList)); + // redirect to home setTimeout( () => navigate("/app/prefetch", { overwriteLastHistoryEntry: true }), @@ -52,6 +56,10 @@ export function Page() { ); }; + useEffect(() => { + fetchAccount(); + }, [fetchAccount]); + return (
diff --git a/src/app/channel/components/createModal.tsx b/src/app/channel/components/createModal.tsx index 1be56def..9140bd2e 100644 --- a/src/app/channel/components/createModal.tsx +++ b/src/app/channel/components/createModal.tsx @@ -1,28 +1,21 @@ +import { Dialog, Transition } from "@headlessui/react"; +import CancelIcon from "@icons/cancel"; +import PlusIcon from "@icons/plus"; import { AvatarUploader } from "@shared/avatarUploader"; import { Image } from "@shared/image"; import { RelayContext } from "@shared/relayProvider"; - -import CancelIcon from "@icons/cancel"; -import PlusIcon from "@icons/plus"; - +import { useActiveAccount } from "@stores/accounts"; import { DEFAULT_AVATAR, WRITEONLY_RELAYS } from "@stores/constants"; - import { dateToUnix } from "@utils/date"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; import { createChannel } from "@utils/storage"; - -import { Dialog, Transition } from "@headlessui/react"; import { getEventHash, getSignature } from "nostr-tools"; import { Fragment, useContext, useEffect, useState } from "react"; import { useForm } from "react-hook-form"; -import { useSWRConfig } from "swr"; import { navigate } from "vite-plugin-ssr/client/router"; export default function ChannelCreateModal() { const pool: any = useContext(RelayContext); - - const { account, isError, isLoading } = useActiveAccount(); - const { mutate } = useSWRConfig(); + const account = useActiveAccount((state: any) => state.account); const [isOpen, setIsOpen] = useState(false); const [image, setImage] = useState(DEFAULT_AVATAR); @@ -47,7 +40,7 @@ export default function ChannelCreateModal() { const onSubmit = (data: any) => { setLoading(true); - if (!isError && !isLoading && account) { + if (account) { const event: any = { content: JSON.stringify(data), created_at: dateToUnix(), @@ -62,8 +55,6 @@ export default function ChannelCreateModal() { pool.publish(event, WRITEONLY_RELAYS); // insert to database createChannel(event.id, event.pubkey, event.content, event.created_at); - // update channe llist - mutate("channels"); // reset form reset(); setTimeout(() => { diff --git a/src/app/channel/components/list.tsx b/src/app/channel/components/list.tsx index 8e556211..6a3c5891 100644 --- a/src/app/channel/components/list.tsx +++ b/src/app/channel/components/list.tsx @@ -1,18 +1,19 @@ import ChannelCreateModal from "@app/channel/components/createModal"; import ChannelsListItem from "@app/channel/components/item"; - -import { getChannels } from "@utils/storage"; - -import useSWR from "swr"; - -const fetcher = () => getChannels(10, 0); +import { useChannels } from "@stores/channels"; +import { useEffect } from "react"; export default function ChannelsList() { - const { data, error }: any = useSWR("channels", fetcher); + const channels = useChannels((state: any) => state.channels); + const fetchChannels = useChannels((state: any) => state.fetch); + + useEffect(() => { + fetchChannels(); + }, [fetchChannels]); return (
- {!data || error ? ( + {!channels ? ( <>
@@ -24,7 +25,7 @@ export default function ChannelsList() {
) : ( - data.map((item: { event_id: string }) => ( + channels.map((item: { event_id: string }) => ( )) )} diff --git a/src/app/channel/components/messages/form.tsx b/src/app/channel/components/messages/form.tsx index 552fdf3c..caf651ed 100644 --- a/src/app/channel/components/messages/form.tsx +++ b/src/app/channel/components/messages/form.tsx @@ -1,16 +1,11 @@ import UserReply from "@app/channel/components/messages/userReply"; - +import CancelIcon from "@icons/cancel"; import { ImagePicker } from "@shared/form/imagePicker"; import { RelayContext } from "@shared/relayProvider"; - -import CancelIcon from "@icons/cancel"; - +import { useActiveAccount } from "@stores/accounts"; import { channelContentAtom, channelReplyAtom } from "@stores/channel"; import { WRITEONLY_RELAYS } from "@stores/constants"; - import { dateToUnix } from "@utils/date"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; - import { useAtom, useAtomValue } from "jotai"; import { useResetAtom } from "jotai/utils"; import { getEventHash, getSignature } from "nostr-tools"; @@ -20,7 +15,7 @@ export default function ChannelMessageForm({ channelID, }: { channelID: string | string[] }) { const pool: any = useContext(RelayContext); - const { account, isLoading, isError } = useActiveAccount(); + const account = useActiveAccount((state: any) => state.account); const [value, setValue] = useAtom(channelContentAtom); const resetValue = useResetAtom(channelContentAtom); @@ -41,7 +36,7 @@ export default function ChannelMessageForm({ tags = [["e", channelID, "", "root"]]; } - if (!isError && !isLoading && account) { + if (account) { const event: any = { content: value, created_at: dateToUnix(), @@ -49,11 +44,13 @@ export default function ChannelMessageForm({ pubkey: account.pubkey, tags: tags, }; + event.id = getEventHash(event); event.sig = getSignature(event, account.privkey); // publish note pool.publish(event, WRITEONLY_RELAYS); + // reset state resetValue(); // reset channel reply diff --git a/src/app/channel/components/messages/hideButton.tsx b/src/app/channel/components/messages/hideButton.tsx index cfbab276..4a0883fe 100644 --- a/src/app/channel/components/messages/hideButton.tsx +++ b/src/app/channel/components/messages/hideButton.tsx @@ -1,23 +1,19 @@ -import { RelayContext } from "@shared/relayProvider"; -import { Tooltip } from "@shared/tooltip"; - +import { Dialog, Transition } from "@headlessui/react"; import CancelIcon from "@icons/cancel"; import HideIcon from "@icons/hide"; - +import { RelayContext } from "@shared/relayProvider"; +import { Tooltip } from "@shared/tooltip"; +import { useActiveAccount } from "@stores/accounts"; import { channelMessagesAtom } from "@stores/channel"; import { WRITEONLY_RELAYS } from "@stores/constants"; - import { dateToUnix } from "@utils/date"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; - -import { Dialog, Transition } from "@headlessui/react"; import { useAtom } from "jotai"; import { getEventHash, getSignature } from "nostr-tools"; import { Fragment, useContext, useState } from "react"; export default function MessageHideButton({ id }: { id: string }) { const pool: any = useContext(RelayContext); - const { account, isError, isLoading } = useActiveAccount(); + const account = useActiveAccount((state: any) => state.account); const [isOpen, setIsOpen] = useState(false); const [messages, setMessages] = useAtom(channelMessagesAtom); @@ -31,7 +27,7 @@ export default function MessageHideButton({ id }: { id: string }) { }; const hideMessage = () => { - if (!isError && !isLoading && account) { + if (account) { const event: any = { content: "", created_at: dateToUnix(), @@ -44,13 +40,16 @@ export default function MessageHideButton({ id }: { id: string }) { // publish note pool.publish(event, WRITEONLY_RELAYS); + // update local state const cloneMessages = [...messages]; const targetMessage = cloneMessages.findIndex( (message) => message.id === id, ); + cloneMessages[targetMessage]["hide"] = true; setMessages(cloneMessages); + // close modal closeModal(); } else { diff --git a/src/app/channel/components/messages/muteButton.tsx b/src/app/channel/components/messages/muteButton.tsx index c42ab800..f4b01b45 100644 --- a/src/app/channel/components/messages/muteButton.tsx +++ b/src/app/channel/components/messages/muteButton.tsx @@ -1,23 +1,19 @@ -import { RelayContext } from "@shared/relayProvider"; -import { Tooltip } from "@shared/tooltip"; - +import { Dialog, Transition } from "@headlessui/react"; import CancelIcon from "@icons/cancel"; import MuteIcon from "@icons/mute"; - +import { RelayContext } from "@shared/relayProvider"; +import { Tooltip } from "@shared/tooltip"; +import { useActiveAccount } from "@stores/accounts"; import { channelMessagesAtom } from "@stores/channel"; import { WRITEONLY_RELAYS } from "@stores/constants"; - import { dateToUnix } from "@utils/date"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; - -import { Dialog, Transition } from "@headlessui/react"; import { useAtom } from "jotai"; import { getEventHash, getSignature } from "nostr-tools"; import { Fragment, useContext, useState } from "react"; export default function MessageMuteButton({ pubkey }: { pubkey: string }) { const pool: any = useContext(RelayContext); - const { account, isError, isLoading } = useActiveAccount(); + const account = useActiveAccount((state: any) => state.account); const [messages, setMessages] = useAtom(channelMessagesAtom); const [isOpen, setIsOpen] = useState(false); @@ -31,7 +27,7 @@ export default function MessageMuteButton({ pubkey }: { pubkey: string }) { }; const muteUser = () => { - if (!isError && !isLoading && account) { + if (account) { const event: any = { content: "", created_at: dateToUnix(), @@ -44,6 +40,7 @@ export default function MessageMuteButton({ pubkey }: { pubkey: string }) { // publish note pool.publish(event, WRITEONLY_RELAYS); + // update local state const cloneMessages = [...messages]; const finalMessages = cloneMessages.filter( diff --git a/src/app/channel/components/updateModal.tsx b/src/app/channel/components/updateModal.tsx index ef76269b..16267372 100644 --- a/src/app/channel/components/updateModal.tsx +++ b/src/app/channel/components/updateModal.tsx @@ -1,24 +1,20 @@ +import { Dialog, Transition } from "@headlessui/react"; +import CancelIcon from "@icons/cancel"; +import EditIcon from "@icons/edit"; import { AvatarUploader } from "@shared/avatarUploader"; import { Image } from "@shared/image"; import { RelayContext } from "@shared/relayProvider"; - -import CancelIcon from "@icons/cancel"; -import EditIcon from "@icons/edit"; - +import { useActiveAccount } from "@stores/accounts"; import { DEFAULT_AVATAR, WRITEONLY_RELAYS } from "@stores/constants"; - import { dateToUnix } from "@utils/date"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; import { getChannel } from "@utils/storage"; - -import { Dialog, Transition } from "@headlessui/react"; import { getEventHash, getSignature } from "nostr-tools"; import { Fragment, useContext, useEffect, useState } from "react"; import { useForm } from "react-hook-form"; export default function ChannelUpdateModal({ id }: { id: string }) { const pool: any = useContext(RelayContext); - const { account, isError, isLoading } = useActiveAccount(); + const account = useActiveAccount((state: any) => state.account); const [isOpen, setIsOpen] = useState(false); const [image, setImage] = useState(DEFAULT_AVATAR); @@ -52,7 +48,7 @@ export default function ChannelUpdateModal({ id }: { id: string }) { const onSubmit = (data: any) => { setLoading(true); - if (!isError && !isLoading && account) { + if (account) { const event: any = { content: JSON.stringify(data), created_at: dateToUnix(), @@ -60,11 +56,13 @@ export default function ChannelUpdateModal({ id }: { id: string }) { pubkey: account.pubkey, tags: [["e", id]], }; + event.id = getEventHash(event); event.sig = getSignature(event, account.privkey); // publish channel pool.publish(event, WRITEONLY_RELAYS); + // reset form reset(); // close modal diff --git a/src/app/channel/pages/index.page.tsx b/src/app/channel/pages/index.page.tsx index 8a8504ab..5e0d55f6 100644 --- a/src/app/channel/pages/index.page.tsx +++ b/src/app/channel/pages/index.page.tsx @@ -3,18 +3,14 @@ import ChannelMembers from "@app/channel/components/members"; import ChannelMessageForm from "@app/channel/components/messages/form"; import ChannelMetadata from "@app/channel/components/metadata"; import ChannelUpdateModal from "@app/channel/components/updateModal"; - import { RelayContext } from "@shared/relayProvider"; - +import { useActiveAccount } from "@stores/accounts"; import { channelMessagesAtom, channelReplyAtom } from "@stores/channel"; import { READONLY_RELAYS } from "@stores/constants"; - import { dateToUnix, getHourAgo } from "@utils/date"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; import { usePageContext } from "@utils/hooks/usePageContext"; import { getActiveBlacklist, getBlacklist } from "@utils/storage"; import { arrayObjToPureArr } from "@utils/transform"; - import { useSetAtom } from "jotai"; import { useResetAtom } from "jotai/utils"; import { Suspense, lazy, useContext, useEffect, useRef } from "react"; @@ -39,19 +35,20 @@ const ChannelMessageList = lazy( export function Page() { const pool: any = useContext(RelayContext); + const account: any = useActiveAccount((state: any) => state.account); + const pageContext = usePageContext(); const searchParams: any = pageContext.urlParsed.search; const channelID = searchParams.id; const channelPubkey = searchParams.channelpub; - const { account, isLoading, isError } = useActiveAccount(); const { data: muted } = useSWR( - !isLoading && !isError && account ? ["muted", account.id] : null, + account ? ["muted", account.id] : null, fetchMuted, ); const { data: hided } = useSWR( - !isLoading && !isError && account ? ["hided", account.id] : null, + account ? ["hided", account.id] : null, fetchHided, ); @@ -118,7 +115,7 @@ export function Page() {
{!muted ? <> : } - {!isLoading && !isError && account ? ( + {account ? ( account.pubkey === channelPubkey && ( ) diff --git a/src/app/chat/components/list.tsx b/src/app/chat/components/list.tsx index 9af60cf6..2903353a 100644 --- a/src/app/chat/components/list.tsx +++ b/src/app/chat/components/list.tsx @@ -1,25 +1,23 @@ import ChatsListItem from "@app/chat/components/item"; import ChatsListSelfItem from "@app/chat/components/self"; - -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; -import { getChatsByPubkey } from "@utils/storage"; - -import useSWR from "swr"; - -const fetcher = ([, pubkey]) => getChatsByPubkey(pubkey); +import { useActiveAccount } from "@stores/accounts"; +import { useChats } from "@stores/chats"; +import { useEffect } from "react"; export default function ChatsList() { - const { account, isLoading, isError } = useActiveAccount(); + const account = useActiveAccount((state: any) => state.account); + const chats = useChats((state: any) => state.chats); + const fetchChats = useChats((state: any) => state.fetch); - const { data: chats, error }: any = useSWR( - !isLoading && !isError && account ? ["chats", account.pubkey] : null, - fetcher, - ); + useEffect(() => { + if (!account) return; + fetchChats(account.pubkey); + }, [fetchChats]); return (
- {!chats || error ? ( + {!chats ? ( <>
diff --git a/src/app/chat/components/messageList.tsx b/src/app/chat/components/messageList.tsx index c38557c5..210eee8d 100644 --- a/src/app/chat/components/messageList.tsx +++ b/src/app/chat/components/messageList.tsx @@ -1,15 +1,12 @@ import { ChatMessageItem } from "@app/chat/components/messages/item"; - +import { useActiveAccount } from "@stores/accounts"; import { sortedChatMessagesAtom } from "@stores/chat"; - -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; - import { useAtomValue } from "jotai"; import { useCallback, useRef } from "react"; import { Virtuoso } from "react-virtuoso"; export default function ChatMessageList() { - const { account } = useActiveAccount(); + const account = useActiveAccount((state: any) => state.account); const virtuosoRef = useRef(null); const data = useAtomValue(sortedChatMessagesAtom); diff --git a/src/app/chat/components/messages/form.tsx b/src/app/chat/components/messages/form.tsx index 49816c26..153d54ea 100644 --- a/src/app/chat/components/messages/form.tsx +++ b/src/app/chat/components/messages/form.tsx @@ -1,12 +1,9 @@ import { ImagePicker } from "@shared/form/imagePicker"; import { RelayContext } from "@shared/relayProvider"; - +import { useActiveAccount } from "@stores/accounts"; import { chatContentAtom } from "@stores/chat"; import { WRITEONLY_RELAYS } from "@stores/constants"; - import { dateToUnix } from "@utils/date"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; - import { useAtom } from "jotai"; import { useResetAtom } from "jotai/utils"; import { getEventHash, getSignature, nip04 } from "nostr-tools"; @@ -16,7 +13,7 @@ export default function ChatMessageForm({ receiverPubkey, }: { receiverPubkey: string }) { const pool: any = useContext(RelayContext); - const { account, isLoading, isError } = useActiveAccount(); + const account = useActiveAccount((state: any) => state.account); const [value, setValue] = useAtom(chatContentAtom); const resetValue = useResetAtom(chatContentAtom); @@ -29,25 +26,23 @@ export default function ChatMessageForm({ ); const submitEvent = () => { - if (!isError && !isLoading && account) { - encryptMessage(account.privkey) - .then((encryptedContent) => { - const event: any = { - content: encryptedContent, - created_at: dateToUnix(), - kind: 4, - pubkey: account.pubkey, - tags: [["p", receiverPubkey]], - }; - event.id = getEventHash(event); - event.sig = getSignature(event, account.privkey); - // publish note - pool.publish(event, WRITEONLY_RELAYS); - // reset state - resetValue(); - }) - .catch(console.error); - } + encryptMessage(account.privkey) + .then((encryptedContent) => { + const event: any = { + content: encryptedContent, + created_at: dateToUnix(), + kind: 4, + pubkey: account.pubkey, + tags: [["p", receiverPubkey]], + }; + event.id = getEventHash(event); + event.sig = getSignature(event, account.privkey); + // publish note + pool.publish(event, WRITEONLY_RELAYS); + // reset state + resetValue(); + }) + .catch(console.error); }; const handleEnterPress = (e) => { diff --git a/src/app/chat/components/self.tsx b/src/app/chat/components/self.tsx index 91379fc0..5d7edc0d 100644 --- a/src/app/chat/components/self.tsx +++ b/src/app/chat/components/self.tsx @@ -1,11 +1,8 @@ import { Image } from "@shared/image"; - +import { useActiveAccount } from "@stores/accounts"; import { DEFAULT_AVATAR } from "@stores/constants"; - -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; import { usePageContext } from "@utils/hooks/usePageContext"; import { shortenKey } from "@utils/shortenKey"; - import { twMerge } from "tailwind-merge"; export default function ChatsListSelfItem() { @@ -14,12 +11,11 @@ export default function ChatsListSelfItem() { const searchParams: any = pageContext.urlParsed.search; const pagePubkey = searchParams.pubkey; - const { account, isLoading, isError } = useActiveAccount(); + const account = useActiveAccount((state: any) => state.account); return ( <> - {isError &&
error
} - {isLoading && !account ? ( + {!account ? (
diff --git a/src/app/chat/pages/index.page.tsx b/src/app/chat/pages/index.page.tsx index d03d998f..196ab742 100644 --- a/src/app/chat/pages/index.page.tsx +++ b/src/app/chat/pages/index.page.tsx @@ -1,13 +1,9 @@ import ChatMessageForm from "@app/chat/components/messages/form"; - import { RelayContext } from "@shared/relayProvider"; - +import { useActiveAccount } from "@stores/accounts"; import { chatMessagesAtom } from "@stores/chat"; import { READONLY_RELAYS } from "@stores/constants"; - -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; import { usePageContext } from "@utils/hooks/usePageContext"; - import { useSetAtom } from "jotai"; import { useResetAtom } from "jotai/utils"; import { Suspense, lazy, useContext, useEffect } from "react"; @@ -17,13 +13,12 @@ const ChatMessageList = lazy(() => import("@app/chat/components/messageList")); export function Page() { const pool: any = useContext(RelayContext); + const account = useActiveAccount((state: any) => state.account); + const pageContext = usePageContext(); const searchParams: any = pageContext.urlParsed.search; - const pubkey = searchParams.pubkey; - const { account } = useActiveAccount(); - const setChatMessages = useSetAtom(chatMessagesAtom); const resetChatMessages = useResetAtom(chatMessagesAtom); diff --git a/src/app/index/pages/index.page.tsx b/src/app/index/pages/index.page.tsx index 31c349fa..5c669d28 100644 --- a/src/app/index/pages/index.page.tsx +++ b/src/app/index/pages/index.page.tsx @@ -1,17 +1,23 @@ -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; +import { useActiveAccount } from "@stores/accounts"; +import { useEffect } from "react"; import { navigate } from "vite-plugin-ssr/client/router"; export function Page() { - const { account, isLoading } = useActiveAccount(); + const fetchAccount = useActiveAccount((state: any) => state.fetch); + const account = useActiveAccount((state: any) => state.account); - if (!isLoading && !account) { + if (!account) { navigate("/app/auth", { overwriteLastHistoryEntry: true }); } - if (!isLoading && account) { + if (account) { navigate("/app/prefetch", { overwriteLastHistoryEntry: true }); } + useEffect(() => { + fetchAccount(); + }, [fetchAccount]); + return (
); diff --git a/src/app/note/components/metadata/like.tsx b/src/app/note/components/metadata/like.tsx index 3f074abb..955ca5b3 100644 --- a/src/app/note/components/metadata/like.tsx +++ b/src/app/note/components/metadata/like.tsx @@ -1,12 +1,8 @@ -import { RelayContext } from "@shared/relayProvider"; - import LikeIcon from "@icons/like"; - +import { RelayContext } from "@shared/relayProvider"; +import { useActiveAccount } from "@stores/accounts"; import { WRITEONLY_RELAYS } from "@stores/constants"; - import { dateToUnix } from "@utils/date"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; - import { getEventHash, getSignature } from "nostr-tools"; import { useContext, useEffect, useState } from "react"; @@ -16,33 +12,31 @@ export default function NoteLike({ likes, }: { id: string; pubkey: string; likes: number }) { const pool: any = useContext(RelayContext); - const { account, isLoading, isError } = useActiveAccount(); + const account = useActiveAccount((state: any) => state.account); const [count, setCount] = useState(0); const submitEvent = (e: any) => { e.stopPropagation(); - if (!isLoading && !isError && account) { - const event: any = { - content: "+", - kind: 7, - tags: [ - ["e", id], - ["p", pubkey], - ], - created_at: dateToUnix(), - pubkey: account.pubkey, - }; - event.id = getEventHash(event); - event.sig = getSignature(event, account.privkey); - // publish event to all relays - pool.publish(event, WRITEONLY_RELAYS); - // update state - setCount(count + 1); - } else { - console.log("error"); - } + const event: any = { + content: "+", + kind: 7, + tags: [ + ["e", id], + ["p", pubkey], + ], + created_at: dateToUnix(), + pubkey: account.pubkey, + }; + + event.id = getEventHash(event); + event.sig = getSignature(event, account.privkey); + + // publish event to all relays + pool.publish(event, WRITEONLY_RELAYS); + // update state + setCount(count + 1); }; useEffect(() => { diff --git a/src/app/note/components/metadata/reply.tsx b/src/app/note/components/metadata/reply.tsx index 30740a7c..7ccd9f5c 100644 --- a/src/app/note/components/metadata/reply.tsx +++ b/src/app/note/components/metadata/reply.tsx @@ -1,14 +1,10 @@ +import { Dialog, Transition } from "@headlessui/react"; +import ReplyIcon from "@icons/reply"; import { Image } from "@shared/image"; import { RelayContext } from "@shared/relayProvider"; - -import ReplyIcon from "@icons/reply"; - +import { useActiveAccount } from "@stores/accounts"; import { WRITEONLY_RELAYS } from "@stores/constants"; - import { dateToUnix } from "@utils/date"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; - -import { Dialog, Transition } from "@headlessui/react"; import { compactNumber } from "@utils/number"; import { getEventHash, getSignature } from "nostr-tools"; import { Fragment, useContext, useEffect, useState } from "react"; @@ -18,13 +14,12 @@ export default function NoteReply({ replies, }: { id: string; replies: number }) { const pool: any = useContext(RelayContext); + const account = useActiveAccount((state: any) => state.account); const [count, setCount] = useState(0); const [isOpen, setIsOpen] = useState(false); const [value, setValue] = useState(""); - const { account, isLoading, isError } = useActiveAccount(); - const closeModal = () => { setIsOpen(false); }; @@ -34,25 +29,24 @@ export default function NoteReply({ }; const submitEvent = () => { - if (!isLoading && !isError && account) { - const event: any = { - content: value, - created_at: dateToUnix(), - kind: 1, - pubkey: account.pubkey, - tags: [["e", id]], - }; - event.id = getEventHash(event); - event.sig = getSignature(event, account.privkey); + const event: any = { + content: value, + created_at: dateToUnix(), + kind: 1, + pubkey: account.pubkey, + tags: [["e", id]], + }; - // publish event - pool.publish(event, WRITEONLY_RELAYS); - // close modal - setIsOpen(false); - setCount(count + 1); - } else { - console.log("error"); - } + event.id = getEventHash(event); + event.sig = getSignature(event, account.privkey); + + // publish event + pool.publish(event, WRITEONLY_RELAYS); + + // close modal + setIsOpen(false); + // increment replies + setCount(count + 1); }; useEffect(() => { diff --git a/src/app/note/components/metadata/repost.tsx b/src/app/note/components/metadata/repost.tsx index f12dd7e9..0bfa3bfb 100644 --- a/src/app/note/components/metadata/repost.tsx +++ b/src/app/note/components/metadata/repost.tsx @@ -1,12 +1,8 @@ -import { RelayContext } from "@shared/relayProvider"; - import RepostIcon from "@icons/repost"; - +import { RelayContext } from "@shared/relayProvider"; +import { useActiveAccount } from "@stores/accounts"; import { WRITEONLY_RELAYS } from "@stores/constants"; - import { dateToUnix } from "@utils/date"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; - import { compactNumber } from "@utils/number"; import { getEventHash, getSignature } from "nostr-tools"; import { useContext, useEffect, useState } from "react"; @@ -17,33 +13,32 @@ export default function NoteRepost({ reposts, }: { id: string; pubkey: string; reposts: number }) { const pool: any = useContext(RelayContext); - const { account, isLoading, isError } = useActiveAccount(); + const account = useActiveAccount((state: any) => state.account); const [count, setCount] = useState(0); const submitEvent = (e: any) => { e.stopPropagation(); - if (!isLoading && !isError && account) { - const event: any = { - content: "", - kind: 6, - tags: [ - ["e", id], - ["p", pubkey], - ], - created_at: dateToUnix(), - pubkey: account.pubkey, - }; - event.id = getEventHash(event); - event.sig = getSignature(event, account.privkey); - // publish event to all relays - pool.publish(event, WRITEONLY_RELAYS); - // update state - setCount(count + 1); - } else { - console.log("error"); - } + const event: any = { + content: "", + kind: 6, + tags: [ + ["e", id], + ["p", pubkey], + ], + created_at: dateToUnix(), + pubkey: account.pubkey, + }; + + event.id = getEventHash(event); + event.sig = getSignature(event, account.privkey); + + // publish event to all relays + pool.publish(event, WRITEONLY_RELAYS); + + // update state + setCount(count + 1); }; useEffect(() => { diff --git a/src/app/note/components/replies/form.tsx b/src/app/note/components/replies/form.tsx index ec8ca266..a922048a 100644 --- a/src/app/note/components/replies/form.tsx +++ b/src/app/note/components/replies/form.tsx @@ -1,39 +1,34 @@ import { Image } from "@shared/image"; import { RelayContext } from "@shared/relayProvider"; - +import { useActiveAccount } from "@stores/accounts"; import { WRITEONLY_RELAYS } from "@stores/constants"; - import { dateToUnix } from "@utils/date"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; - import { getEventHash, getSignature } from "nostr-tools"; import { useContext, useState } from "react"; export default function NoteReplyForm({ id }: { id: string }) { const pool: any = useContext(RelayContext); + const account = useActiveAccount((state: any) => state.account); - const { account, isLoading, isError } = useActiveAccount(); const [value, setValue] = useState(""); const submitEvent = () => { - if (!isLoading && !isError && account) { - const event: any = { - content: value, - created_at: dateToUnix(), - kind: 1, - pubkey: account.pubkey, - tags: [["e", id]], - }; - event.id = getEventHash(event); - event.sig = getSignature(event, account.privkey); + const event: any = { + content: value, + created_at: dateToUnix(), + kind: 1, + pubkey: account.pubkey, + tags: [["e", id]], + }; - // publish note - pool.publish(event, WRITEONLY_RELAYS); - // reset form - setValue(""); - } else { - console.log("error"); - } + event.id = getEventHash(event); + event.sig = getSignature(event, account.privkey); + + // publish note + pool.publish(event, WRITEONLY_RELAYS); + + // reset form + setValue(""); }; return ( diff --git a/src/app/prefetch/pages/index.page.tsx b/src/app/prefetch/pages/index.page.tsx index 109d264a..03513770 100644 --- a/src/app/prefetch/pages/index.page.tsx +++ b/src/app/prefetch/pages/index.page.tsx @@ -1,8 +1,8 @@ import LumeIcon from "@icons/lume"; import { RelayContext } from "@shared/relayProvider"; +import { useActiveAccount } from "@stores/accounts"; import { READONLY_RELAYS } from "@stores/constants"; import { dateToUnix, getHourAgo } from "@utils/date"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; import { addToBlacklist, countTotalNotes, @@ -15,15 +15,6 @@ import { useCallback, useContext, useRef } from "react"; import useSWRSubscription from "swr/subscription"; import { navigate } from "vite-plugin-ssr/client/router"; -function isJSON(str: string) { - try { - JSON.parse(str); - } catch (e) { - return false; - } - return true; -} - let lastLogin: string; let totalNotes: number; @@ -34,12 +25,11 @@ if (typeof window !== "undefined") { export function Page() { const pool: any = useContext(RelayContext); + const account = useActiveAccount((state: any) => state.account); const now = useRef(new Date()); const eose = useRef(0); - const { account, isLoading, isError } = useActiveAccount(); - const getQuery = useCallback(() => { const query = []; const follows = JSON.parse(account.follows); @@ -79,98 +69,95 @@ export function Page() { }); return query; - }, [account.follows]); + }, [account]); - useSWRSubscription( - !isLoading && !isError && account ? "prefetch" : null, - () => { - const query = getQuery(); - const unsubscribe = pool.subscribe( - query, - READONLY_RELAYS, - (event: any) => { - switch (event.kind) { - // short text note - case 1: { - const parentID = getParentID(event.tags, event.id); - // insert event to local database - createNote( - event.id, - account.id, - event.pubkey, - event.kind, - event.tags, - event.content, - event.created_at, - parentID, - ); - break; + useSWRSubscription(account ? "prefetch" : null, () => { + const query = getQuery(); + const unsubscribe = pool.subscribe( + query, + READONLY_RELAYS, + (event: any) => { + switch (event.kind) { + // short text note + case 1: { + const parentID = getParentID(event.tags, event.id); + // insert event to local database + createNote( + event.id, + account.id, + event.pubkey, + event.kind, + event.tags, + event.content, + event.created_at, + parentID, + ); + break; + } + // chat + case 4: + createChat( + event.id, + account.pubkey, + event.pubkey, + event.content, + event.created_at, + ); + break; + // repost + case 6: + createNote( + event.id, + account.id, + event.pubkey, + event.kind, + event.tags, + event.content, + event.created_at, + event.id, + ); + break; + // hide message (channel only) + case 43: + if (event.tags[0][0] === "e") { + addToBlacklist(account.id, event.tags[0][1], 43, 1); } - // chat - case 4: - createChat( - event.id, - account.pubkey, - event.pubkey, - event.content, - event.created_at, - ); - break; - // repost - case 6: - createNote( - event.id, - account.id, - event.pubkey, - event.kind, - event.tags, - event.content, - event.created_at, - event.id, - ); - break; - // hide message (channel only) - case 43: - if (event.tags[0][0] === "e") { - addToBlacklist(account.id, event.tags[0][1], 43, 1); - } - break; - // mute user (channel only) - case 44: - if (event.tags[0][0] === "p") { - addToBlacklist(account.id, event.tags[0][1], 44, 1); - } - break; - case 1063: - createNote( - event.id, - account.id, - event.pubkey, - event.kind, - event.tags, - event.content, - event.created_at, - "", - ); - break; - default: - break; - } - }, - undefined, - () => { - eose.current += 1; - if (eose.current === READONLY_RELAYS.length) { - navigate("/app/space", { overwriteLastHistoryEntry: true }); - } - }, - ); + break; + // mute user (channel only) + case 44: + if (event.tags[0][0] === "p") { + addToBlacklist(account.id, event.tags[0][1], 44, 1); + } + break; + case 1063: + createNote( + event.id, + account.id, + event.pubkey, + event.kind, + event.tags, + event.content, + event.created_at, + "", + ); + break; + default: + break; + } + }, + undefined, + () => { + eose.current += 1; + if (eose.current === READONLY_RELAYS.length) { + navigate("/app/space", { overwriteLastHistoryEntry: true }); + } + }, + ); - return () => { - unsubscribe(); - }; - }, - ); + return () => { + unsubscribe(); + }; + }); return (
diff --git a/src/app/space/components/create.tsx b/src/app/space/components/create.tsx index 1a1026d5..40aa7465 100644 --- a/src/app/space/components/create.tsx +++ b/src/app/space/components/create.tsx @@ -3,19 +3,15 @@ import { Dialog, Transition } from "@headlessui/react"; import CancelIcon from "@icons/cancel"; import PlusIcon from "@icons/plus"; import { Image } from "@shared/image"; +import { useActiveAccount } from "@stores/accounts"; import { DEFAULT_AVATAR } from "@stores/constants"; -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; -import { createBlock, getPlebs } from "@utils/storage"; +import { createBlock } from "@utils/storage"; import { Fragment, useEffect, useState } from "react"; import { useForm } from "react-hook-form"; -import useSWR from "swr"; - -const fetcher = () => getPlebs(); export function CreateBlockModal() { - const { account } = useActiveAccount(); + const account = useActiveAccount((state: any) => state.account); const { register, handleSubmit, reset, watch, setValue } = useForm(); - const { data: plebs } = useSWR("plebs", fetcher); const [image, setImage] = useState(DEFAULT_AVATAR); const [isOpen, setIsOpen] = useState(false); diff --git a/src/shared/composer/modal.tsx b/src/shared/composer/modal.tsx index e5a8c1be..0a260548 100644 --- a/src/shared/composer/modal.tsx +++ b/src/shared/composer/modal.tsx @@ -1,24 +1,18 @@ -import { Post } from "@shared/composer/types/post"; -import { User } from "@shared/composer/user"; - +import { Dialog, Transition } from "@headlessui/react"; import CancelIcon from "@icons/cancel"; import ChevronDownIcon from "@icons/chevronDown"; import ChevronRightIcon from "@icons/chevronRight"; import ComposeIcon from "@icons/compose"; - -import { composerAtom } from "@stores/composer"; - -import { useActiveAccount } from "@utils/hooks/useActiveAccount"; - -import { Dialog, Transition } from "@headlessui/react"; -import { useAtom } from "jotai"; +import { Post } from "@shared/composer/types/post"; +import { User } from "@shared/composer/user"; +import { useActiveAccount } from "@stores/accounts"; import { Fragment, useState } from "react"; export function ComposerModal() { const [isOpen, setIsOpen] = useState(false); - const [composer] = useAtom(composerAtom); + const [composer] = useState({ type: "post" }); - const { account, isLoading, isError } = useActiveAccount(); + const account = useActiveAccount((state: any) => state.account); const closeModal = () => { setIsOpen(false); @@ -64,11 +58,7 @@ export function ComposerModal() {
-
- {!isLoading && !isError && account && ( - - )} -
+
{account && }
state.account); const now = useRef(new Date()); - const { account, isLoading, isError } = useActiveAccount(); - - useSWRSubscription( - !isLoading && !isError && account ? ["eventCollector", account] : null, - ([, key]) => { - const follows = JSON.parse(key.follows); - const followsAsArray = nip02ToArray(follows); - const unsubscribe = pool.subscribe( - [ - { - kinds: [1, 6], - authors: followsAsArray, - since: dateToUnix(now.current), - }, - { - kinds: [3], - authors: [key.pubkey], - }, - { - kinds: [4], - "#p": [key.pubkey], - since: dateToUnix(now.current), - }, - { - kinds: [30023], - since: dateToUnix(now.current), - }, - ], - READONLY_RELAYS, - (event: any) => { - switch (event.kind) { - // short text note - case 1: { - const parentID = getParentID(event.tags, event.id); - createNote( - event.id, - account.id, - event.pubkey, - event.kind, - event.tags, - event.content, - event.created_at, - parentID, - ); - // notify user reload to get newer note - setHasNewerNote(true); - break; - } - // contacts - case 3: { - const follows = nip02ToArray(event.tags); - // update account's folllows with NIP-02 tag list - updateAccount("follows", follows, event.pubkey); - break; - } - // chat - case 4: - createChat( - event.id, - key.pubkey, - event.pubkey, - event.content, - event.created_at, - ); - break; - // repost - case 6: - createNote( - event.id, - key.id, - event.pubkey, - event.kind, - event.tags, - event.content, - event.created_at, - event.id, - ); - break; - // long post - case 30023: { - const verifyMetadata = isJSON(event.tags); - if (verifyMetadata) { - // insert event to local database - createNote( - event.id, - account.id, - event.pubkey, - event.kind, - event.tags, - event.content, - event.created_at, - "", - ); - } - break; - } - default: - break; - } + useSWRSubscription(account ? "eventCollector" : null, () => { + const follows = JSON.parse(account.follows); + const unsubscribe = pool.subscribe( + [ + { + kinds: [1, 6], + authors: follows, + since: dateToUnix(now.current), }, - ); + { + kinds: [3], + authors: [account.pubkey], + }, + { + kinds: [4], + "#p": [account.pubkey], + since: dateToUnix(now.current), + }, + ], + READONLY_RELAYS, + (event: any) => { + switch (event.kind) { + // short text note + case 1: { + const parentID = getParentID(event.tags, event.id); + createNote( + event.id, + account.id, + event.pubkey, + event.kind, + event.tags, + event.content, + event.created_at, + parentID, + ); + // notify user reload to get newer note + setHasNewerNote(true); + break; + } + // contacts + case 3: { + const follows = nip02ToArray(event.tags); + // update account's folllows with NIP-02 tag list + updateAccount("follows", follows, event.pubkey); + break; + } + // chat + case 4: + createChat( + event.id, + account.pubkey, + event.pubkey, + event.content, + event.created_at, + ); + break; + // repost + case 6: + createNote( + event.id, + account.id, + event.pubkey, + event.kind, + event.tags, + event.content, + event.created_at, + event.id, + ); + break; + default: + break; + } + }, + ); - return () => { - unsubscribe(); - }; - }, - ); + return () => { + unsubscribe(); + }; + }); + + useEffect(() => { + // listen window close event + getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, () => { + // update last login time + updateLastLogin(dateToUnix(now.current)); + // close window + appWindow.close(); + }); + }, []); return (
diff --git a/src/stores/accounts.tsx b/src/stores/accounts.tsx new file mode 100644 index 00000000..0f220971 --- /dev/null +++ b/src/stores/accounts.tsx @@ -0,0 +1,13 @@ +import { getActiveAccount } from "@utils/storage"; +import { create } from "zustand"; + +export const useActiveAccount = create((set) => ({ + account: null, + fetch: async () => { + const response = await getActiveAccount(); + set({ account: response }); + }, + updateFollows: (list: any) => { + set((state: any) => ({ account: { ...state.account, follows: list } })); + }, +})); diff --git a/src/stores/channels.tsx b/src/stores/channels.tsx new file mode 100644 index 00000000..4af42377 --- /dev/null +++ b/src/stores/channels.tsx @@ -0,0 +1,17 @@ +import { getChannels } from "@utils/storage"; +import { create } from "zustand"; + +export const useChannels = create((set) => ({ + channels: [], + fetch: async () => { + const response = await getChannels(10, 0); + set({ channels: response }); + }, +})); + +export const useChannelMessage = create((set) => ({ + messages: [], + add: (message: any) => { + set((state: any) => ({ messages: [...state.messages, message] })); + }, +})); diff --git a/src/stores/chats.tsx b/src/stores/chats.tsx new file mode 100644 index 00000000..a6b55f27 --- /dev/null +++ b/src/stores/chats.tsx @@ -0,0 +1,21 @@ +import { getChatMessages, getChatsByPubkey } from "@utils/storage"; +import { create } from "zustand"; + +export const useChats = create((set) => ({ + chats: [], + fetch: async (pubkey: string) => { + const response = await getChatsByPubkey(pubkey); + set({ chats: response }); + }, +})); + +export const useChatMessages = create((set) => ({ + messages: [], + fetch: async (receiver_pubkey: string, sender_pubkey: string) => { + const response = await getChatMessages(receiver_pubkey, sender_pubkey); + set({ messages: response }); + }, + add: (message: any) => { + set((state: any) => ({ messages: [...state.messages, message] })); + }, +})); diff --git a/src/stores/composer.tsx b/src/stores/composer.tsx deleted file mode 100644 index 63849403..00000000 --- a/src/stores/composer.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import { atom } from "jotai"; - -export const composerAtom = atom({ type: "post" }); diff --git a/src/stores/note.tsx b/src/stores/note.tsx deleted file mode 100644 index 237d84a9..00000000 --- a/src/stores/note.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { atom } from "jotai"; -import { atomWithReset } from "jotai/utils"; - -// note content -export const noteContentAtom = atomWithReset(""); - -// notify user that connector has receive newer note -export const hasNewerNoteAtom = atom(false); diff --git a/src/stores/onboarding.tsx b/src/stores/onboarding.tsx deleted file mode 100644 index ba81a9b3..00000000 --- a/src/stores/onboarding.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { atom } from "jotai"; - -export const onboardingAtom = atom({ - pubkey: null, - privkey: null, - metadata: null, - follows: null, -}); diff --git a/src/utils/hooks/useActiveAccount.tsx b/src/utils/hooks/useActiveAccount.tsx deleted file mode 100644 index 1db28ae5..00000000 --- a/src/utils/hooks/useActiveAccount.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { getActiveAccount } from "@utils/storage"; -import useSWR from "swr"; - -const fetcher = () => getActiveAccount(); - -export function useActiveAccount() { - const { data, error, isLoading } = useSWR("activeAcount", fetcher); - - return { - account: data, - isLoading, - isError: error, - }; -} diff --git a/src/utils/storage.tsx b/src/utils/storage.tsx index 3da2aa78..50c8bccc 100644 --- a/src/utils/storage.tsx +++ b/src/utils/storage.tsx @@ -272,7 +272,7 @@ export async function updateChannelMetadata(event_id: string, value: string) { ); } -// get all chats +// get all chats by pubkey export async function getChatsByPubkey(pubkey: string) { const db = await connect(); return await db.select( @@ -280,6 +280,17 @@ export async function getChatsByPubkey(pubkey: string) { ); } +// get chat messages +export async function getChatMessages( + receiver_pubkey: string, + sender_pubkey: string, +) { + const db = await connect(); + return await db.select( + `SELECT * FROM chats WHERE receiver_pubkey = "${receiver_pubkey}" AND sender_pubkey = "${sender_pubkey}" ORDER BY created_at ASC;`, + ); +} + // create chat export async function createChat( event_id: string,