132 lines
4.1 KiB
TypeScript
132 lines
4.1 KiB
TypeScript
import { NDKSubscription } from '@nostr-dev-kit/ndk';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { useCallback, useEffect, useRef } from 'react';
|
|
import { useParams } from 'react-router-dom';
|
|
import { Virtuoso } from 'react-virtuoso';
|
|
|
|
import { ChatMessageForm } from '@app/chats/components/messages/form';
|
|
import { ChatMessageItem } from '@app/chats/components/messages/item';
|
|
import { ChatSidebar } from '@app/chats/components/sidebar';
|
|
|
|
import { useNDK } from '@libs/ndk/provider';
|
|
import { useStorage } from '@libs/storage/provider';
|
|
|
|
import { LoaderIcon } from '@shared/icons';
|
|
|
|
import { useStronghold } from '@stores/stronghold';
|
|
|
|
import { useNostr } from '@utils/hooks/useNostr';
|
|
|
|
export function ChatScreen() {
|
|
const virtuosoRef = useRef(null);
|
|
const userPrivkey = useStronghold((state) => state.privkey);
|
|
|
|
const { db } = useStorage();
|
|
const { ndk } = useNDK();
|
|
const { pubkey } = useParams();
|
|
const { fetchNIP04Messages } = useNostr();
|
|
const { status, data } = useQuery(['nip04-dm', pubkey], async () => {
|
|
return await fetchNIP04Messages(pubkey);
|
|
});
|
|
|
|
const itemContent = useCallback(
|
|
(index: string | number) => {
|
|
const message = data[index];
|
|
if (!message) return;
|
|
return (
|
|
<ChatMessageItem
|
|
message={message}
|
|
userPubkey={db.account.pubkey}
|
|
userPrivkey={userPrivkey}
|
|
/>
|
|
);
|
|
},
|
|
[data]
|
|
);
|
|
|
|
const computeItemKey = useCallback(
|
|
(index: string | number) => {
|
|
return data[index].id;
|
|
},
|
|
[data]
|
|
);
|
|
|
|
useEffect(() => {
|
|
const sub: NDKSubscription = ndk.subscribe(
|
|
{
|
|
kinds: [4],
|
|
authors: [db.account.pubkey],
|
|
'#p': [pubkey],
|
|
since: Math.floor(Date.now() / 1000),
|
|
},
|
|
{
|
|
closeOnEose: false,
|
|
}
|
|
);
|
|
|
|
sub.addListener('event', (event) => {
|
|
console.log(event);
|
|
});
|
|
|
|
return () => {
|
|
sub.stop();
|
|
};
|
|
}, [pubkey]);
|
|
|
|
return (
|
|
<div className="grid h-full w-full grid-cols-3 bg-white/10 backdrop-blur-xl">
|
|
<div className="col-span-2 border-r border-white/5">
|
|
<div className="h-full w-full flex-1 p-3">
|
|
<div className="flex h-full flex-col justify-between overflow-hidden rounded-xl bg-white/10 backdrop-blur-xl">
|
|
<div className="h-full w-full flex-1">
|
|
{status === 'loading' ? (
|
|
<div className="flex h-full w-full items-center justify-center">
|
|
<div className="flex flex-col items-center gap-1.5">
|
|
<LoaderIcon className="h-5 w-5 animate-spin text-white" />
|
|
<p className="text-sm font-medium text-white/50">Loading messages</p>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<Virtuoso
|
|
ref={virtuosoRef}
|
|
data={data}
|
|
itemContent={itemContent}
|
|
computeItemKey={computeItemKey}
|
|
initialTopMostItemIndex={data.length - 1}
|
|
alignToBottom={true}
|
|
followOutput={true}
|
|
overscan={50}
|
|
increaseViewportBy={{ top: 200, bottom: 200 }}
|
|
className="scrollbar-hide relative overflow-y-auto"
|
|
components={{
|
|
EmptyPlaceholder: () => Empty,
|
|
}}
|
|
/>
|
|
)}
|
|
</div>
|
|
<div className="z-50 shrink-0 rounded-b-xl border-t border-white/5 bg-white/10 p-3 px-5 backdrop-blur-xl">
|
|
<ChatMessageForm
|
|
receiverPubkey={pubkey}
|
|
userPubkey={db.account.pubkey}
|
|
userPrivkey={userPrivkey}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="col-span-1 pt-3">
|
|
<ChatSidebar pubkey={pubkey} />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const Empty = (
|
|
<div className="absolute left-1/2 top-1/2 flex w-full -translate-x-1/2 -translate-y-1/2 transform flex-col gap-1 text-center">
|
|
<h3 className="mb-2 text-4xl">🙌</h3>
|
|
<p className="leading-none text-white/50">
|
|
You two didn't talk yet, let's send first message
|
|
</p>
|
|
</div>
|
|
);
|