From a7e95fd18aebfe16a6f268bab9df8a4cf3b9044e Mon Sep 17 00:00:00 2001 From: Ren Amamiya <123083837+reyamir@users.noreply.github.com> Date: Sun, 16 Apr 2023 12:45:17 +0700 Subject: [PATCH] added useDecryptMessage hook and updated chat messages --- src/app/chats/[pubkey]/page.tsx | 36 ++++++++++++++----- src/components/channels/channelList.tsx | 4 +-- .../channels/createChannelModal.tsx | 4 +-- src/components/chats/chatModal.tsx | 4 +-- src/components/chats/messageList.tsx | 20 +++++++---- src/components/chats/messageListItem.tsx | 33 +++-------------- src/components/eventCollector.tsx | 6 +--- src/components/navigation/newsfeed.tsx | 4 ++- src/stores/chat.tsx | 8 +++++ src/utils/hooks/useDecryptMessage.tsx | 34 ++++++++++++++++++ src/utils/metadata.tsx | 16 ++++++--- 11 files changed, 108 insertions(+), 61 deletions(-) create mode 100644 src/stores/chat.tsx create mode 100644 src/utils/hooks/useDecryptMessage.tsx diff --git a/src/app/chats/[pubkey]/page.tsx b/src/app/chats/[pubkey]/page.tsx index b1bc4600..22d8d5a2 100644 --- a/src/app/chats/[pubkey]/page.tsx +++ b/src/app/chats/[pubkey]/page.tsx @@ -4,17 +4,24 @@ import { MessageList } from '@components/chats/messageList'; import FormChat from '@components/form/chat'; import { RelayContext } from '@components/relaysProvider'; +import { chatMessagesAtom } from '@stores/chat'; + import useLocalStorage from '@rehooks/local-storage'; -import { useContext, useEffect, useState } from 'react'; +import { useSetAtom } from 'jotai'; +import { useResetAtom } from 'jotai/utils'; +import { Suspense, useCallback, useContext, useEffect, useRef } from 'react'; export default function Page({ params }: { params: { pubkey: string } }) { const [pool, relays]: any = useContext(RelayContext); - const [activeAccount]: any = useLocalStorage('activeAccount', {}); - const [messages, setMessages] = useState([]); - useEffect(() => { - const unsubscribe = pool.subscribe( + const setChatMessages = useSetAtom(chatMessagesAtom); + const resetChatMessages = useResetAtom(chatMessagesAtom); + + const unsubscribe = useRef(null); + + const fetchMessages = useCallback(() => { + unsubscribe.current = pool.subscribe( [ { kinds: [4], @@ -29,18 +36,29 @@ export default function Page({ params }: { params: { pubkey: string } }) { ], relays, (event: any) => { - setMessages((messages) => [event, ...messages]); + setChatMessages((data) => [...data, event]); } ); + }, [activeAccount.pubkey, params.pubkey, pool, relays, setChatMessages]); + + useEffect(() => { + // reset stored messages + resetChatMessages(); + // fetch messages from relays + fetchMessages(); return () => { - unsubscribe(); + if (unsubscribe.current) { + unsubscribe.current(); + } }; - }, [pool, relays, params.pubkey, activeAccount.pubkey]); + }, [fetchMessages, resetChatMessages]); return (
- a.created_at - b.created_at)} /> + Loading...}> + +
diff --git a/src/components/channels/channelList.tsx b/src/components/channels/channelList.tsx index f9a0a78e..94809b05 100644 --- a/src/components/channels/channelList.tsx +++ b/src/components/channels/channelList.tsx @@ -23,9 +23,9 @@ export default function ChannelList() {
-
+
diff --git a/src/components/channels/createChannelModal.tsx b/src/components/channels/createChannelModal.tsx index c225a787..3c83fc6f 100644 --- a/src/components/channels/createChannelModal.tsx +++ b/src/components/channels/createChannelModal.tsx @@ -51,8 +51,8 @@ export const CreateChannelModal = () => { return ( -
-
+
+
diff --git a/src/components/chats/chatModal.tsx b/src/components/chats/chatModal.tsx index fa89345a..f9bc0a28 100644 --- a/src/components/chats/chatModal.tsx +++ b/src/components/chats/chatModal.tsx @@ -23,8 +23,8 @@ export const ChatModal = () => { return ( -
-
+
+
diff --git a/src/components/chats/messageList.tsx b/src/components/chats/messageList.tsx index 8ffaf9ef..af91b114 100644 --- a/src/components/chats/messageList.tsx +++ b/src/components/chats/messageList.tsx @@ -1,21 +1,23 @@ import MessageListItem from '@components/chats/messageListItem'; +import { Placeholder } from '@components/note/placeholder'; + +import { sortedChatMessagesAtom } from '@stores/chat'; import useLocalStorage from '@rehooks/local-storage'; +import { useAtomValue } from 'jotai'; import { useCallback, useRef } from 'react'; import { Virtuoso } from 'react-virtuoso'; -export const MessageList = ({ data }: { data: any }) => { +export const MessageList = () => { const [activeAccount]: any = useLocalStorage('activeAccount', {}); const virtuosoRef = useRef(null); + const data = useAtomValue(sortedChatMessagesAtom); + const itemContent: any = useCallback( (index: string | number) => { return ( - + ); }, [activeAccount.privkey, activeAccount.pubkey, data] @@ -33,6 +35,7 @@ export const MessageList = ({ data }: { data: any }) => { {
); }; + +const COMPONENTS = { + EmptyPlaceholder: () => , + ScrollSeekPlaceholder: () => , +}; diff --git a/src/components/chats/messageListItem.tsx b/src/components/chats/messageListItem.tsx index 4947fb1f..f91918cd 100644 --- a/src/components/chats/messageListItem.tsx +++ b/src/components/chats/messageListItem.tsx @@ -1,36 +1,11 @@ import { MessageUser } from '@components/chats/messageUser'; -import { nip04 } from 'nostr-tools'; -import { memo, useCallback, useEffect, useMemo, useState } from 'react'; +import { useDecryptMessage } from '@utils/hooks/useDecryptMessage'; -const MessageListItem = ({ - data, - activeAccountPubkey, - activeAccountPrivkey, -}: { - data: any; - activeAccountPubkey: string; - activeAccountPrivkey: string; -}) => { - const [content, setContent] = useState(''); +import { memo } from 'react'; - const sender = useMemo(() => { - const pTag = data.tags.find(([k, v]) => k === 'p' && v && v !== '')[1]; - if (pTag === activeAccountPubkey) { - return data.pubkey; - } else { - return pTag; - } - }, [data.pubkey, data.tags, activeAccountPubkey]); - - const decryptContent = useCallback(async () => { - const result = await nip04.decrypt(activeAccountPrivkey, sender, data.content); - setContent(result); - }, [data.content, activeAccountPrivkey, sender]); - - useEffect(() => { - decryptContent().catch(console.error); - }, [decryptContent]); +const MessageListItem = ({ data, userPubkey, userPrivkey }: { data: any; userPubkey: string; userPrivkey: string }) => { + const content = useDecryptMessage(userPubkey, userPrivkey, data.pubkey, data.tags, data.content); return (
diff --git a/src/components/eventCollector.tsx b/src/components/eventCollector.tsx index 0e0bb569..918ff1f0 100644 --- a/src/components/eventCollector.tsx +++ b/src/components/eventCollector.tsx @@ -25,7 +25,6 @@ export default function EventCollector() { const now = useRef(new Date()); const unsubscribe = useRef(null); - const unlisten = useRef(null); const createFollowingPlebs = useCallback( async (tags) => { @@ -115,7 +114,7 @@ export default function EventCollector() { }, [pool, relays, activeAccount.id, activeAccount.pubkey, follows, setHasNewerNote, createFollowingPlebs]); const listenWindowClose = useCallback(async () => { - unlisten.current = window.getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, () => { + window.getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, () => { writeStorage('lastLogin', now.current); window.getCurrent().close(); }); @@ -129,9 +128,6 @@ export default function EventCollector() { if (unsubscribe.current) { unsubscribe.current(); } - if (unlisten.current) { - unlisten.current; - } }; }, [setHasNewerNote, subscribe, listenWindowClose]); diff --git a/src/components/navigation/newsfeed.tsx b/src/components/navigation/newsfeed.tsx index 4ecbf6b5..a949d8a0 100644 --- a/src/components/navigation/newsfeed.tsx +++ b/src/components/navigation/newsfeed.tsx @@ -3,7 +3,7 @@ import { ActiveLink } from '@components/activeLink'; import * as Collapsible from '@radix-ui/react-collapsible'; -import { NavArrowUp } from 'iconoir-react'; +import { Bonfire, NavArrowUp, PeopleTag } from 'iconoir-react'; import { useState } from 'react'; export default function Newsfeed() { @@ -28,6 +28,7 @@ export default function Newsfeed() { activeClassName="dark:bg-zinc-900 dark:text-zinc-100 hover:dark:bg-zinc-800" className="flex h-8 items-center gap-2.5 rounded-md px-2.5 text-sm font-medium hover:text-zinc-200" > + Following + Circle diff --git a/src/stores/chat.tsx b/src/stores/chat.tsx new file mode 100644 index 00000000..a16559f2 --- /dev/null +++ b/src/stores/chat.tsx @@ -0,0 +1,8 @@ +import { atom } from 'jotai'; +import { atomWithReset } from 'jotai/utils'; + +export const chatMessagesAtom = atomWithReset([]); +export const sortedChatMessagesAtom = atom((get) => { + const messages = get(chatMessagesAtom); + return messages.sort((x: { created_at: number }, y: { created_at: number }) => x.created_at - y.created_at); +}); diff --git a/src/utils/hooks/useDecryptMessage.tsx b/src/utils/hooks/useDecryptMessage.tsx new file mode 100644 index 00000000..e6fa43d2 --- /dev/null +++ b/src/utils/hooks/useDecryptMessage.tsx @@ -0,0 +1,34 @@ +import { nip04 } from 'nostr-tools'; +import { useCallback, useEffect, useState } from 'react'; + +export const useDecryptMessage = ( + userKey: string, + userPriv: string, + eventKey: string, + eventTags: string[], + encryptedContent: string +) => { + const [content, setContent] = useState(''); + + const extractSenderKey = useCallback(() => { + const keyInTags = eventTags.find(([k, v]) => k === 'p' && v && v !== '')[1]; + if (keyInTags === userKey) { + return eventKey; + } else { + return keyInTags; + } + }, [eventKey, eventTags, userKey]); + + const decrypt = useCallback(async () => { + const senderKey = extractSenderKey(); + const result = await nip04.decrypt(userPriv, senderKey, encryptedContent); + // update state with decrypt content + setContent(result); + }, [userPriv, encryptedContent, extractSenderKey]); + + useEffect(() => { + decrypt().catch(console.error); + }, [decrypt]); + + return content; +}; diff --git a/src/utils/metadata.tsx b/src/utils/metadata.tsx index faf7ee78..8f86436e 100644 --- a/src/utils/metadata.tsx +++ b/src/utils/metadata.tsx @@ -16,14 +16,20 @@ export const useMetadata = (pubkey) => { const [profile, setProfile] = useState(null); const cacheProfile = useMemo(() => { - const findInStorage = plebs.find((item) => item.pubkey === pubkey); + let metadata = false; - if (findInStorage !== undefined) { - return JSON.parse(findInStorage.metadata); + if (pubkey === activeAccount.pubkey) { + metadata = JSON.parse(activeAccount.metadata); } else { - return false; + const findInStorage = plebs.find((item) => item.pubkey === pubkey); + + if (findInStorage !== undefined) { + metadata = JSON.parse(findInStorage.metadata); + } } - }, [plebs, pubkey]); + + return metadata; + }, [plebs, pubkey, activeAccount.pubkey, activeAccount.metadata]); const insertPlebToDB = useCallback( async (pubkey: string, metadata: string) => {