From 62ca74994df47bdc387ea8937c69d5b2c8b993ca Mon Sep 17 00:00:00 2001 From: Ren Amamiya <123083837+reyamir@users.noreply.github.com> Date: Thu, 6 Apr 2023 16:42:28 +0700 Subject: [PATCH] added chats --- src/components/chats/chatList.tsx | 44 ++++++++++++++ src/components/chats/message.tsx | 49 +++++++++++++++ src/components/chats/messageList.tsx | 44 ++++++++++++++ src/components/chats/user.tsx | 37 ++++++++++++ src/components/form/chat.tsx | 79 +++++++++++++++++++++++++ src/components/navigation/channels.tsx | 8 +-- src/components/navigation/chats.tsx | 14 +++-- src/components/navigation/newsfeed.tsx | 12 +--- src/components/user/extend.tsx | 4 +- src/components/user/large.tsx | 2 +- src/components/user/mention.tsx | 13 ++-- src/components/user/mini.tsx | 28 --------- src/pages/chats/[pubkey].tsx | 82 ++++++++++++++++++++++++++ 13 files changed, 361 insertions(+), 55 deletions(-) create mode 100644 src/components/chats/chatList.tsx create mode 100644 src/components/chats/message.tsx create mode 100644 src/components/chats/messageList.tsx create mode 100644 src/components/chats/user.tsx create mode 100644 src/components/form/chat.tsx delete mode 100644 src/components/user/mini.tsx create mode 100644 src/pages/chats/[pubkey].tsx diff --git a/src/components/chats/chatList.tsx b/src/components/chats/chatList.tsx new file mode 100644 index 00000000..d3b9c2f2 --- /dev/null +++ b/src/components/chats/chatList.tsx @@ -0,0 +1,44 @@ +import { ImageWithFallback } from '@components/imageWithFallback'; + +import { activeAccountAtom } from '@stores/account'; +import { DEFAULT_AVATAR } from '@stores/constants'; + +import { useAtomValue } from 'jotai'; +import { useRouter } from 'next/router'; + +export default function ChatList() { + const router = useRouter(); + + const activeAccount: any = useAtomValue(activeAccountAtom); + const accountProfile = JSON.parse(activeAccount.metadata); + + const openChats = () => { + router.push({ + pathname: '/chats/[pubkey]', + query: { pubkey: activeAccount.pubkey }, + }); + }; + + return ( +
+
openChats()} + className="inline-flex items-center gap-2 rounded-md px-2.5 py-2 hover:bg-zinc-900" + > +
+ +
+
+
+ {accountProfile.display_name || accountProfile.name} (you) +
+
+
+
+ ); +} diff --git a/src/components/chats/message.tsx b/src/components/chats/message.tsx new file mode 100644 index 00000000..c17b4fb9 --- /dev/null +++ b/src/components/chats/message.tsx @@ -0,0 +1,49 @@ +import { MessageUser } from '@components/chats/user'; + +import { nip04 } from 'nostr-tools'; +import { useCallback, useEffect, useMemo, useState } from 'react'; + +export const Message = ({ + data, + activeAccountPubkey, + activeAccountPrivkey, +}: { + data: any; + activeAccountPubkey: string; + activeAccountPrivkey: string; +}) => { + const [content, setContent] = useState(''); + + 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]); + + return ( +
+
+ +
+
+
+ {content} +
+
+
+
+
+ ); +}; diff --git a/src/components/chats/messageList.tsx b/src/components/chats/messageList.tsx new file mode 100644 index 00000000..6ad400ad --- /dev/null +++ b/src/components/chats/messageList.tsx @@ -0,0 +1,44 @@ +import { Message } from '@components/chats/message'; + +import { useCallback, useRef } from 'react'; +import { Virtuoso } from 'react-virtuoso'; + +export const MessageList = ({ data }: { data: any }) => { + const virtuosoRef = useRef(null); + + const itemContent: any = useCallback( + (index: string | number) => { + const activeAccount = JSON.parse(localStorage.getItem('activeAccount')); + return ( + + ); + }, + [data] + ); + + const computeItemKey = useCallback( + (index: string | number) => { + return data[index].id; + }, + [data] + ); + + return ( +
+ +
+ ); +}; diff --git a/src/components/chats/user.tsx b/src/components/chats/user.tsx new file mode 100644 index 00000000..9ac89062 --- /dev/null +++ b/src/components/chats/user.tsx @@ -0,0 +1,37 @@ +import { ImageWithFallback } from '@components/imageWithFallback'; + +import { DEFAULT_AVATAR } from '@stores/constants'; + +import { useMetadata } from '@utils/metadata'; +import { truncate } from '@utils/truncate'; + +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; + +dayjs.extend(relativeTime); + +export const MessageUser = ({ pubkey, time }: { pubkey: string; time: number }) => { + const profile = useMetadata(pubkey); + + return ( +
+
+ +
+
+
+ + {profile?.display_name || profile?.name || truncate(pubkey, 16, ' .... ')} + + ยท + {dayjs().to(dayjs.unix(time))} +
+
+
+ ); +}; diff --git a/src/components/form/chat.tsx b/src/components/form/chat.tsx new file mode 100644 index 00000000..6681b1d2 --- /dev/null +++ b/src/components/form/chat.tsx @@ -0,0 +1,79 @@ +import ImagePicker from '@components/form/imagePicker'; +import { RelayContext } from '@components/relaysProvider'; + +import { dateToUnix } from '@utils/getDate'; + +import { getEventHash, nip04, signEvent } from 'nostr-tools'; +import { useCallback, useContext, useState } from 'react'; + +export default function FormChat({ receiverPubkey }: { receiverPubkey: string }) { + const [pool, relays]: any = useContext(RelayContext); + const [value, setValue] = useState(''); + + const encryptMessage = useCallback( + async (privkey: string) => { + return await nip04.encrypt(privkey, receiverPubkey, value); + }, + [receiverPubkey, value] + ); + + const submitEvent = useCallback(() => { + const activeAccount = JSON.parse(localStorage.getItem('activeAccount')); + encryptMessage(activeAccount.privkey) + .then((encryptedContent) => { + const event: any = { + content: encryptedContent, + created_at: dateToUnix(), + kind: 4, + pubkey: activeAccount.pubkey, + tags: [['p', receiverPubkey]], + }; + event.id = getEventHash(event); + event.sig = signEvent(event, activeAccount.privkey); + // publish note + pool.publish(event, relays); + // reset state + setValue(''); + }) + .catch(console.error); + }, [encryptMessage, receiverPubkey, pool, relays]); + + const handleEnterPress = (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + submitEvent(); + } + }; + + return ( +
+
+