refactor channel
This commit is contained in:
@@ -4,95 +4,73 @@ import { ChannelMessageForm } from "@app/channel/components/messages/form";
|
||||
import { ChannelMetadata } from "@app/channel/components/metadata";
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { useChannelMessages } from "@stores/channels";
|
||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
||||
import { dateToUnix, getHourAgo } from "@utils/date";
|
||||
import { dateToUnix } from "@utils/date";
|
||||
import { usePageContext } from "@utils/hooks/usePageContext";
|
||||
import { LumeEvent } from "@utils/types";
|
||||
import { useCallback, useContext, useEffect, useRef } from "react";
|
||||
import { Virtuoso } from "react-virtuoso";
|
||||
import useSWRSubscription from "swr/subscription";
|
||||
|
||||
const now = new Date();
|
||||
const since = dateToUnix(getHourAgo(24, now));
|
||||
|
||||
export function Page() {
|
||||
const ndk = useContext(RelayContext);
|
||||
const pageContext = usePageContext();
|
||||
const virtuosoRef = useRef(null);
|
||||
|
||||
const searchParams: any = pageContext.urlParsed.search;
|
||||
const channelID = searchParams.id;
|
||||
|
||||
const [messages, addMessage, fetchMessages, clearMessages]: any =
|
||||
const [messages, fetchMessages, addMessage, clearMessages] =
|
||||
useChannelMessages((state: any) => [
|
||||
state.messages,
|
||||
state.addMessage,
|
||||
state.fetch,
|
||||
state.add,
|
||||
state.clear,
|
||||
]);
|
||||
|
||||
useSWRSubscription(["channelMessagesSubscribe", channelID], () => {
|
||||
// subscribe to channel
|
||||
const sub = ndk.subscribe({
|
||||
"#e": [channelID],
|
||||
kinds: [42],
|
||||
since: dateToUnix(),
|
||||
});
|
||||
useSWRSubscription(
|
||||
channelID ? ["channelMessagesSubscribe", channelID] : null,
|
||||
() => {
|
||||
// subscribe to channel
|
||||
const sub = ndk.subscribe(
|
||||
{
|
||||
"#e": [channelID],
|
||||
kinds: [42],
|
||||
since: dateToUnix(),
|
||||
},
|
||||
{ closeOnEose: false },
|
||||
);
|
||||
|
||||
sub.addListener("event", (event) => {
|
||||
addMessage(event);
|
||||
});
|
||||
sub.addListener("event", (event: LumeEvent) => {
|
||||
addMessage(channelID, event);
|
||||
});
|
||||
|
||||
return () => {
|
||||
sub.stop();
|
||||
};
|
||||
});
|
||||
return () => {
|
||||
sub.stop();
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchMessages(ndk, channelID, since);
|
||||
fetchMessages(channelID);
|
||||
|
||||
return () => {
|
||||
clearMessages();
|
||||
};
|
||||
}, [fetchMessages]);
|
||||
|
||||
const count = messages.length;
|
||||
const reverseIndex = useCallback((index) => count - 1 - index, [count]);
|
||||
const parentRef = useRef();
|
||||
const virtualizerRef = useRef(null);
|
||||
const itemContent: any = useCallback(
|
||||
(index: string | number) => {
|
||||
return <ChannelMessageItem data={messages[index]} />;
|
||||
},
|
||||
[messages],
|
||||
);
|
||||
|
||||
if (
|
||||
virtualizerRef.current &&
|
||||
count !== virtualizerRef.current.options.count
|
||||
) {
|
||||
const delta = count - virtualizerRef.current.options.count;
|
||||
const nextOffset = virtualizerRef.current.scrollOffset + delta * 200;
|
||||
|
||||
virtualizerRef.current.scrollOffset = nextOffset;
|
||||
virtualizerRef.current.scrollToOffset(nextOffset, { align: "start" });
|
||||
}
|
||||
|
||||
const virtualizer = useVirtualizer({
|
||||
count,
|
||||
getScrollElement: () => parentRef.current,
|
||||
estimateSize: () => 200,
|
||||
getItemKey: useCallback(
|
||||
(index) => messages[reverseIndex(index)].id,
|
||||
[messages, reverseIndex],
|
||||
),
|
||||
overscan: 5,
|
||||
scrollMargin: 50,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
virtualizerRef.current = virtualizer;
|
||||
}, []);
|
||||
|
||||
const items = virtualizer.getVirtualItems();
|
||||
|
||||
const [paddingTop, paddingBottom] =
|
||||
items.length > 0
|
||||
? [
|
||||
Math.max(0, items[0].start - virtualizer.options.scrollMargin),
|
||||
Math.max(0, virtualizer.getTotalSize() - items[items.length - 1].end),
|
||||
]
|
||||
: [0, 0];
|
||||
const computeItemKey = useCallback(
|
||||
(index: string | number) => {
|
||||
return messages[index].event_id;
|
||||
},
|
||||
[messages],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full grid grid-cols-3">
|
||||
@@ -105,40 +83,23 @@ export function Page() {
|
||||
</div>
|
||||
<div className="w-full flex-1 p-3">
|
||||
<div className="flex h-full flex-col justify-between rounded-md bg-zinc-900">
|
||||
<div
|
||||
ref={parentRef}
|
||||
className="scrollbar-hide overflow-y-auto h-full w-full"
|
||||
style={{ contain: "strict" }}
|
||||
>
|
||||
{!messages ? (
|
||||
<p>Loading...</p>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
overflowAnchor: "none",
|
||||
paddingTop,
|
||||
paddingBottom,
|
||||
}}
|
||||
>
|
||||
{items.map((item) => {
|
||||
const index = reverseIndex(item.index);
|
||||
const message = messages[index];
|
||||
|
||||
return (
|
||||
<div
|
||||
key={item.key}
|
||||
data-index={item.index}
|
||||
data-reverse-index={index}
|
||||
ref={virtualizer.measureElement}
|
||||
>
|
||||
<ChannelMessageItem data={message} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="w-full inline-flex shrink-0 border-t border-zinc-800">
|
||||
{!messages ? (
|
||||
<p>Loading...</p>
|
||||
) : (
|
||||
<Virtuoso
|
||||
ref={virtuosoRef}
|
||||
data={messages}
|
||||
itemContent={itemContent}
|
||||
computeItemKey={computeItemKey}
|
||||
initialTopMostItemIndex={messages.length - 1}
|
||||
alignToBottom={true}
|
||||
followOutput={true}
|
||||
overscan={50}
|
||||
increaseViewportBy={{ top: 200, bottom: 200 }}
|
||||
className="scrollbar-hide overflow-y-auto h-full w-full"
|
||||
/>
|
||||
)}
|
||||
<div className="w-full inline-flex shrink-0 px-5 py-3 border-t border-zinc-800">
|
||||
<ChannelMessageForm channelID={channelID} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user