diff --git a/src/app/channel/components/messages/user.tsx b/src/app/channel/components/messages/user.tsx index a502c1c0..fccc2eae 100644 --- a/src/app/channel/components/messages/user.tsx +++ b/src/app/channel/components/messages/user.tsx @@ -1,4 +1,4 @@ -import { DEFAULT_AVATAR } from '@lume/stores/constants'; +import { DEFAULT_AVATAR, IMGPROXY_URL } from '@lume/stores/constants'; import { useProfile } from '@lume/utils/hooks/useProfile'; import { shortenKey } from '@lume/utils/shortenKey'; @@ -25,7 +25,7 @@ export default function ChannelMessageUser({ pubkey, time }: { pubkey: string; t <>
{pubkey}
{pubkey} { - return ( - - ); + return ; }, - [activeAccount.privkey, activeAccount.pubkey, data] + [account.privkey, account.pubkey, data] ); const computeItemKey = useCallback( @@ -34,7 +31,6 @@ export default function MessageList() { ); } - -const COMPONENTS = { - EmptyPlaceholder: () => , -}; diff --git a/src/shared/form/chat.tsx b/src/app/chat/components/messages/form.tsx similarity index 70% rename from src/shared/form/chat.tsx rename to src/app/chat/components/messages/form.tsx index e0448945..9a5f9b62 100644 --- a/src/shared/form/chat.tsx +++ b/src/app/chat/components/messages/form.tsx @@ -1,18 +1,17 @@ -import { AccountContext } from '@lume/shared/accountProvider'; import { ImagePicker } from '@lume/shared/form/imagePicker'; -import { RelayContext } from '@lume/shared/relaysProvider'; import { chatContentAtom } from '@lume/stores/chat'; -import { FULL_RELAYS } from '@lume/stores/constants'; +import { FULL_RELAYS, WRITEONLY_RELAYS } from '@lume/stores/constants'; import { dateToUnix } from '@lume/utils/getDate'; +import { useActiveAccount } from '@lume/utils/hooks/useActiveAccount'; import { useAtom } from 'jotai'; import { useResetAtom } from 'jotai/utils'; +import { RelayPool } from 'nostr-relaypool'; import { getEventHash, nip04, signEvent } from 'nostr-tools'; -import { useCallback, useContext } from 'react'; +import { useCallback } from 'react'; -export default function FormChat({ receiverPubkey }: { receiverPubkey: string }) { - const pool: any = useContext(RelayContext); - const activeAccount: any = useContext(AccountContext); +export default function ChatMessageForm({ receiverPubkey }: { receiverPubkey: string }) { + const { account, isLoading, isError } = useActiveAccount(); const [value, setValue] = useAtom(chatContentAtom); const resetValue = useResetAtom(chatContentAtom); @@ -24,25 +23,28 @@ export default function FormChat({ receiverPubkey }: { receiverPubkey: string }) [receiverPubkey, value] ); - const submitEvent = useCallback(() => { - 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, FULL_RELAYS); - // reset state - resetValue(); - }) - .catch(console.error); - }, [activeAccount.privkey, activeAccount.pubkey, receiverPubkey, pool, resetValue, encryptMessage]); + const submitEvent = () => { + if (!isError && !isLoading && account) { + encryptMessage(account.privkey) + .then((encryptedContent) => { + const pool = new RelayPool(WRITEONLY_RELAYS); + const event: any = { + content: encryptedContent, + created_at: dateToUnix(), + kind: 4, + pubkey: account.pubkey, + tags: [['p', receiverPubkey]], + }; + event.id = getEventHash(event); + event.sig = signEvent(event, account.privkey); + // publish note + pool.publish(event, FULL_RELAYS); + // reset state + resetValue(); + }) + .catch(console.error); + } + }; const handleEnterPress = (e) => { if (e.key === 'Enter' && !e.shiftKey) { diff --git a/src/app/chat/components/messages/item.tsx b/src/app/chat/components/messages/item.tsx new file mode 100644 index 00000000..eb38a1cc --- /dev/null +++ b/src/app/chat/components/messages/item.tsx @@ -0,0 +1,27 @@ +import ChatMessageUser from '@lume/app/chat/components/messages/user'; +import { useDecryptMessage } from '@lume/utils/hooks/useDecryptMessage'; + +import { memo } from 'react'; + +export const ChatMessageItem = memo(function MessageListItem({ + data, + userPubkey, + userPrivkey, +}: { + data: any; + userPubkey: string; + userPrivkey: string; +}) { + const content = useDecryptMessage(userPubkey, userPrivkey, data.pubkey, data.tags, data.content); + + return ( +
+
+ +
+
{content}
+
+
+
+ ); +}); diff --git a/src/app/chat/components/messages/user.tsx b/src/app/chat/components/messages/user.tsx new file mode 100644 index 00000000..8cb7f8c8 --- /dev/null +++ b/src/app/chat/components/messages/user.tsx @@ -0,0 +1,48 @@ +import { DEFAULT_AVATAR, IMGPROXY_URL } from '@lume/stores/constants'; +import { useProfile } from '@lume/utils/hooks/useProfile'; +import { shortenKey } from '@lume/utils/shortenKey'; + +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; + +dayjs.extend(relativeTime); + +export default function ChatMessageUser({ pubkey, time }: { pubkey: string; time: number }) { + const { user, isError, isLoading } = useProfile(pubkey); + + return ( +
+ {isError || isLoading ? ( + <> +
+
+
+
+
+
+ + ) : ( + <> +
+ {pubkey} +
+
+
+ + {user?.display_name || user?.name || shortenKey(pubkey)} + + · + {dayjs().to(dayjs.unix(time))} +
+
+ + )} +
+ ); +} diff --git a/src/app/chat/pages/index.page.tsx b/src/app/chat/pages/index.page.tsx index 0521263a..8f0fdb9e 100644 --- a/src/app/chat/pages/index.page.tsx +++ b/src/app/chat/pages/index.page.tsx @@ -1,82 +1,66 @@ -import { AccountContext } from '@lume/shared/accountProvider'; -import { MessageListItem } from '@lume/shared/chats/messageListItem'; -import FormChat from '@lume/shared/form/chat'; -import { RelayContext } from '@lume/shared/relaysProvider'; +import ChatMessageForm from '@lume/app/chat/components/messages/form'; +import { chatMessagesAtom } from '@lume/stores/chat'; import { FULL_RELAYS } from '@lume/stores/constants'; +import { useActiveAccount } from '@lume/utils/hooks/useActiveAccount'; import { usePageContext } from '@lume/utils/hooks/usePageContext'; -import { sortMessages } from '@lume/utils/transform'; -import { useContext } from 'react'; +import { useSetAtom } from 'jotai'; +import { useResetAtom } from 'jotai/utils'; +import { RelayPool } from 'nostr-relaypool'; +import { Suspense, lazy, useEffect } from 'react'; import useSWRSubscription from 'swr/subscription'; +const ChatMessageList = lazy(() => import('@lume/app/chat/components/messageList')); + export function Page() { const pageContext = usePageContext(); const searchParams: any = pageContext.urlParsed.search; const pubkey = searchParams.pubkey; - const pool: any = useContext(RelayContext); - const activeAccount: any = useContext(AccountContext); + const { account } = useActiveAccount(); - const { data, error } = useSWRSubscription( - pubkey - ? [ - { - kinds: [4], - authors: [pubkey], - '#p': [activeAccount.pubkey], - }, - { - kinds: [4], - authors: [activeAccount.pubkey], - '#p': [pubkey], - }, - ] - : null, - (key, { next }) => { - const unsubscribe = pool.subscribe(key, FULL_RELAYS, (event: any) => { - next(null, (prev) => (prev ? [event, ...prev] : [event])); - }); + const setChatMessages = useSetAtom(chatMessagesAtom); + const resetChatMessages = useResetAtom(chatMessagesAtom); - return () => { - unsubscribe(); - }; - } - ); + useSWRSubscription(pubkey ? pubkey : null, (key: string, {}: any) => { + const pool = new RelayPool(FULL_RELAYS); + const unsubscribe = pool.subscribe( + [ + { + kinds: [4], + authors: [key], + '#p': [account.pubkey], + }, + { + kinds: [4], + authors: [account.pubkey], + '#p': [key], + }, + ], + FULL_RELAYS, + (event: any) => { + setChatMessages((prev) => [...prev, event]); + } + ); + + return () => { + unsubscribe(); + }; + }); + + useEffect(() => { + // reset channel messages + resetChatMessages(); + }); return (
-
- {error &&
failed to load
} - {!data ? ( -
-
-
-
-
-
- -
-
-
-
-
-
-
-
- ) : ( - sortMessages(data).map((message) => ( - - )) - )} -
+ Loading...

}> + +
- +
); diff --git a/src/app/newsfeed/components/note/parent.tsx b/src/app/newsfeed/components/note/parent.tsx index ce196437..3947c673 100644 --- a/src/app/newsfeed/components/note/parent.tsx +++ b/src/app/newsfeed/components/note/parent.tsx @@ -41,7 +41,7 @@ export const NoteParent = memo(function NoteParent({ id }: { id: string }) {
{error &&
failed to load
} {!data ? ( -
+
diff --git a/src/app/newsfeed/components/user/default.tsx b/src/app/newsfeed/components/user/default.tsx index 3cb91cf9..ff3acf65 100644 --- a/src/app/newsfeed/components/user/default.tsx +++ b/src/app/newsfeed/components/user/default.tsx @@ -1,4 +1,4 @@ -import { DEFAULT_AVATAR } from '@lume/stores/constants'; +import { DEFAULT_AVATAR, IMGPROXY_URL } from '@lume/stores/constants'; import { useProfile } from '@lume/utils/hooks/useProfile'; import { shortenKey } from '@lume/utils/shortenKey'; @@ -28,7 +28,7 @@ export const NoteDefaultUser = ({ pubkey, time }: { pubkey: string; time: number <>
{pubkey} - ); -} diff --git a/src/shared/chats/chatListItem.tsx b/src/shared/chats/chatListItem.tsx deleted file mode 100644 index 8fc1f4ca..00000000 --- a/src/shared/chats/chatListItem.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { DEFAULT_AVATAR } from '@lume/stores/constants'; -import { usePageContext } from '@lume/utils/hooks/usePageContext'; -import { useProfile } from '@lume/utils/hooks/useProfile'; -import { shortenKey } from '@lume/utils/shortenKey'; - -import { twMerge } from 'tailwind-merge'; - -export const ChatListItem = ({ pubkey }: { pubkey: string }) => { - const profile = useProfile(pubkey); - const pageContext = usePageContext(); - - const searchParams: any = pageContext.urlParsed.search; - const pagePubkey = searchParams.pubkey; - - return ( - -
- {pubkey} -
-
-
- {profile?.display_name || profile?.name || shortenKey(pubkey)} -
-
-
- ); -}; diff --git a/src/shared/chats/chatModal.tsx b/src/shared/chats/chatModal.tsx deleted file mode 100644 index f6c7346e..00000000 --- a/src/shared/chats/chatModal.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Plus } from 'iconoir-react'; - -export const ChatModal = () => { - return ( -
-
- -
-
-
Add a new chat
-
-
- ); -}; diff --git a/src/shared/chats/chatModalUser.tsx b/src/shared/chats/chatModalUser.tsx deleted file mode 100644 index 687b176f..00000000 --- a/src/shared/chats/chatModalUser.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { DEFAULT_AVATAR } from '@lume/stores/constants'; -import { shortenKey } from '@lume/utils/shortenKey'; - -import { navigate } from 'vite-plugin-ssr/client/router'; - -export const ChatModalUser = ({ data }: { data: any }) => { - const profile = JSON.parse(data.metadata); - - const openNewChat = () => { - navigate(`/chat?pubkey=${data.pubkey}`); - }; - - return ( -
-
-
- {data.pubkey} -
-
- - {profile?.display_name || profile?.name} - - {shortenKey(data.pubkey)} -
-
-
- -
-
- ); -}; diff --git a/src/shared/chats/messageListItem.tsx b/src/shared/chats/messageListItem.tsx deleted file mode 100644 index 33a91e3a..00000000 --- a/src/shared/chats/messageListItem.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { MessageUser } from '@lume/shared/chats/messageUser'; -import { useDecryptMessage } from '@lume/utils/hooks/useDecryptMessage'; - -import { memo } from 'react'; - -export const MessageListItem = memo(function MessageListItem({ - data, - userPubkey, - userPrivkey, -}: { - data: any; - userPubkey: string; - userPrivkey: string; -}) { - const content = useDecryptMessage(userPubkey, userPrivkey, data.pubkey, data.tags, data.content); - - return ( -
-
- -
-
-
- {content} -
-
-
-
-
- ); -}); diff --git a/src/shared/chats/messageUser.tsx b/src/shared/chats/messageUser.tsx deleted file mode 100644 index 75c7cf31..00000000 --- a/src/shared/chats/messageUser.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { DEFAULT_AVATAR } from '@lume/stores/constants'; -import { useProfile } from '@lume/utils/hooks/useProfile'; -import { shortenKey } from '@lume/utils/shortenKey'; - -import dayjs from 'dayjs'; -import relativeTime from 'dayjs/plugin/relativeTime'; - -dayjs.extend(relativeTime); - -export const MessageUser = ({ pubkey, time }: { pubkey: string; time: number }) => { - const profile = useProfile(pubkey); - - return ( -
-
- {pubkey} -
-
-
- - {profile?.display_name || profile?.name || shortenKey(pubkey)} - - · - {dayjs().to(dayjs.unix(time))} -
-
-
- ); -}; diff --git a/src/shared/form/channel.tsx b/src/shared/form/channel.tsx deleted file mode 100644 index f239c546..00000000 --- a/src/shared/form/channel.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { AccountContext } from '@lume/shared/accountProvider'; -import { ImagePicker } from '@lume/shared/form/imagePicker'; -import { RelayContext } from '@lume/shared/relaysProvider'; -import { UserMini } from '@lume/shared/user/mini'; -import { channelContentAtom, channelReplyAtom } from '@lume/stores/channel'; -import { FULL_RELAYS } from '@lume/stores/constants'; -import { dateToUnix } from '@lume/utils/getDate'; - -import { Cancel } from 'iconoir-react'; -import { useAtom, useAtomValue } from 'jotai'; -import { useResetAtom } from 'jotai/utils'; -import { getEventHash, signEvent } from 'nostr-tools'; -import { useCallback, useContext } from 'react'; - -export const FormChannel = ({ eventId }: { eventId: string | string[] }) => { - const pool: any = useContext(RelayContext); - const activeAccount: any = useContext(AccountContext); - - const [value, setValue] = useAtom(channelContentAtom); - const resetValue = useResetAtom(channelContentAtom); - - const channelReply = useAtomValue(channelReplyAtom); - const resetChannelReply = useResetAtom(channelReplyAtom); - - const submitEvent = useCallback(() => { - let tags: any[][]; - - if (channelReply.id !== null) { - tags = [ - ['e', eventId, '', 'root'], - ['e', channelReply.id, '', 'reply'], - ['p', channelReply.pubkey, ''], - ]; - } else { - tags = [['e', eventId, '', 'root']]; - } - - const event: any = { - content: value, - created_at: dateToUnix(), - kind: 42, - pubkey: activeAccount.pubkey, - tags: tags, - }; - event.id = getEventHash(event); - event.sig = signEvent(event, activeAccount.privkey); - - // publish note - pool.publish(event, FULL_RELAYS); - // reset state - resetValue(); - // reset channel reply - resetChannelReply(); - }, [ - value, - channelReply.id, - channelReply.pubkey, - activeAccount.pubkey, - activeAccount.privkey, - eventId, - resetChannelReply, - resetValue, - pool, - ]); - - const handleEnterPress = (e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - submitEvent(); - } - }; - - const stopReply = () => { - resetChannelReply(); - }; - - return ( -
- {channelReply.id && ( -
-
-
- -
-
{channelReply.content}
-
-
- -
-
- )} -