mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-18 11:13:30 +00:00
added useDecryptMessage hook and updated chat messages
This commit is contained in:
parent
b778cf6198
commit
a7e95fd18a
@ -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 (
|
||||
<div className="flex h-full w-full flex-col justify-between">
|
||||
<MessageList data={messages.sort((a, b) => a.created_at - b.created_at)} />
|
||||
<Suspense fallback={<>Loading...</>}>
|
||||
<MessageList />
|
||||
</Suspense>
|
||||
<div className="shrink-0 p-3">
|
||||
<FormChat receiverPubkey={params.pubkey} />
|
||||
</div>
|
||||
|
@ -23,9 +23,9 @@ export default function ChannelList() {
|
||||
<div className="flex flex-col gap-px">
|
||||
<Link
|
||||
href="/channels"
|
||||
className="group inline-flex items-center gap-2 rounded-md px-2.5 py-1.5 hover:bg-zinc-950"
|
||||
className="group inline-flex items-center gap-2 rounded-md px-2.5 py-1.5 hover:bg-zinc-900"
|
||||
>
|
||||
<div className="inline-flex h-5 w-5 shrink items-center justify-center rounded bg-zinc-900">
|
||||
<div className="inline-flex h-5 w-5 shrink items-center justify-center rounded bg-zinc-900 group-hover:bg-zinc-800">
|
||||
<Globe width={12} height={12} className="text-zinc-500" />
|
||||
</div>
|
||||
<div>
|
||||
|
@ -51,8 +51,8 @@ export const CreateChannelModal = () => {
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={setOpen}>
|
||||
<Dialog.Trigger asChild>
|
||||
<div className="group inline-flex items-center gap-2 rounded-md px-2.5 py-1.5 hover:bg-zinc-950">
|
||||
<div className="inline-flex h-5 w-5 shrink items-center justify-center rounded bg-zinc-900">
|
||||
<div className="group inline-flex items-center gap-2 rounded-md px-2.5 py-1.5 hover:bg-zinc-900">
|
||||
<div className="inline-flex h-5 w-5 shrink items-center justify-center rounded bg-zinc-900 group-hover:bg-zinc-800">
|
||||
<Plus width={12} height={12} className="text-zinc-500" />
|
||||
</div>
|
||||
<div>
|
||||
|
@ -23,8 +23,8 @@ export const ChatModal = () => {
|
||||
return (
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger asChild>
|
||||
<div className="group inline-flex items-center gap-2 rounded-md px-2.5 py-1.5 hover:bg-zinc-950">
|
||||
<div className="inline-flex h-5 w-5 shrink items-center justify-center rounded bg-zinc-900">
|
||||
<div className="group inline-flex items-center gap-2 rounded-md px-2.5 py-1.5 hover:bg-zinc-900">
|
||||
<div className="group-hover:800 inline-flex h-5 w-5 shrink items-center justify-center rounded bg-zinc-900">
|
||||
<Plus width={12} height={12} className="text-zinc-500" />
|
||||
</div>
|
||||
<div>
|
||||
|
@ -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 (
|
||||
<MessageListItem
|
||||
data={data[index]}
|
||||
activeAccountPubkey={activeAccount.pubkey}
|
||||
activeAccountPrivkey={activeAccount.privkey}
|
||||
/>
|
||||
<MessageListItem data={data[index]} userPubkey={activeAccount.pubkey} userPrivkey={activeAccount.privkey} />
|
||||
);
|
||||
},
|
||||
[activeAccount.privkey, activeAccount.pubkey, data]
|
||||
@ -33,6 +35,7 @@ export const MessageList = ({ data }: { data: any }) => {
|
||||
<Virtuoso
|
||||
ref={virtuosoRef}
|
||||
data={data}
|
||||
components={COMPONENTS}
|
||||
itemContent={itemContent}
|
||||
computeItemKey={computeItemKey}
|
||||
initialTopMostItemIndex={data.length - 1}
|
||||
@ -45,3 +48,8 @@ export const MessageList = ({ data }: { data: any }) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const COMPONENTS = {
|
||||
EmptyPlaceholder: () => <Placeholder />,
|
||||
ScrollSeekPlaceholder: () => <Placeholder />,
|
||||
};
|
||||
|
@ -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 (
|
||||
<div className="flex h-min min-h-min w-full select-text flex-col px-5 py-2 hover:bg-black/20">
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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"
|
||||
>
|
||||
<PeopleTag width={16} height={16} className="text-zinc-500" />
|
||||
<span>Following</span>
|
||||
</ActiveLink>
|
||||
<ActiveLink
|
||||
@ -35,6 +36,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"
|
||||
>
|
||||
<Bonfire width={16} height={16} className="text-zinc-500" />
|
||||
<span>Circle</span>
|
||||
</ActiveLink>
|
||||
</Collapsible.Content>
|
||||
|
8
src/stores/chat.tsx
Normal file
8
src/stores/chat.tsx
Normal file
@ -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);
|
||||
});
|
34
src/utils/hooks/useDecryptMessage.tsx
Normal file
34
src/utils/hooks/useDecryptMessage.tsx
Normal file
@ -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;
|
||||
};
|
@ -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) => {
|
||||
|
Loading…
Reference in New Issue
Block a user