(feat): add theming for testers #20
2 changed files with 76 additions and 33 deletions
commit
4a0f9e3b58
|
|
@ -266,7 +266,11 @@ export default function Provider(props: { children: ReactNode }) {
|
||||||
return updated ? next : prev;
|
return updated ? next : prev;
|
||||||
});
|
});
|
||||||
|
|
||||||
const queryKey = ["chat-messages", String(userIdValue)] as const;
|
const queryKey = [
|
||||||
|
"chat-messages",
|
||||||
|
String(userIdValue),
|
||||||
|
currentSharedSecret.length > 0,
|
||||||
|
] as const;
|
||||||
queryClient.setQueryData<InfiniteData<RawMessages>>(
|
queryClient.setQueryData<InfiniteData<RawMessages>>(
|
||||||
queryKey,
|
queryKey,
|
||||||
(current) => {
|
(current) => {
|
||||||
|
|
@ -301,7 +305,7 @@ export default function Provider(props: { children: ReactNode }) {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}, [subscribePush, userIdValue]);
|
}, [currentSharedSecret, subscribePush, userIdValue]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,16 @@ import {
|
||||||
PAGE_SIZE,
|
PAGE_SIZE,
|
||||||
FALLBACK_MESSAGE_HEIGHT,
|
FALLBACK_MESSAGE_HEIGHT,
|
||||||
MESSAGES_PER_VIRTUAL_ROW,
|
MESSAGES_PER_VIRTUAL_ROW,
|
||||||
|
type LiveMessage,
|
||||||
|
type RawMessage,
|
||||||
} from "./values";
|
} from "./values";
|
||||||
|
|
||||||
|
type MessageChunk = {
|
||||||
|
key: string;
|
||||||
|
messages: Array<RawMessage | LiveMessage>;
|
||||||
|
startIndex: number;
|
||||||
|
};
|
||||||
|
|
||||||
function getEstimatedMessageHeight(message: { height?: number }) {
|
function getEstimatedMessageHeight(message: { height?: number }) {
|
||||||
return typeof message.height === "number" && Number.isFinite(message.height)
|
return typeof message.height === "number" && Number.isFinite(message.height)
|
||||||
? Math.max(1, Math.ceil(message.height))
|
? Math.max(1, Math.ceil(message.height))
|
||||||
|
|
@ -39,6 +47,34 @@ function shouldFetchPreviousPage({
|
||||||
].every(Boolean);
|
].every(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMessageRenderKey(message: RawMessage | LiveMessage) {
|
||||||
|
return "localId" in message ? message.localId : String(message.send_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMessageChunks(
|
||||||
|
messages: Array<RawMessage | LiveMessage>,
|
||||||
|
keyPrefix: string,
|
||||||
|
startOffset = 0,
|
||||||
|
) {
|
||||||
|
const chunks: MessageChunk[] = [];
|
||||||
|
|
||||||
|
for (let end = messages.length; end > 0; end -= MESSAGES_PER_VIRTUAL_ROW) {
|
||||||
|
const start = Math.max(0, end - MESSAGES_PER_VIRTUAL_ROW);
|
||||||
|
const chunkMessages = messages.slice(start, end);
|
||||||
|
const firstMessage = chunkMessages[0];
|
||||||
|
|
||||||
|
if (firstMessage) {
|
||||||
|
chunks.push({
|
||||||
|
key: `${keyPrefix}-${getMessageRenderKey(firstMessage)}`,
|
||||||
|
messages: chunkMessages,
|
||||||
|
startIndex: startOffset + start,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunks;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the chat screen with virtualized history and live message updates.
|
* Renders the chat screen with virtualized history and live message updates.
|
||||||
* @returns Chat screen JSX.
|
* @returns Chat screen JSX.
|
||||||
|
|
@ -148,49 +184,52 @@ export default function Screen() {
|
||||||
|
|
||||||
const historicalMessages = React.useMemo(() => {
|
const historicalMessages = React.useMemo(() => {
|
||||||
const pages = messagesQuery.data?.pages ?? [];
|
const pages = messagesQuery.data?.pages ?? [];
|
||||||
return [...pages].reverse().flat();
|
const seenSendTimes = new Set<number>();
|
||||||
|
const dedupedMessages: RawMessage[] = [];
|
||||||
|
|
||||||
|
for (const message of [...pages].reverse().flat()) {
|
||||||
|
if (seenSendTimes.has(message.send_time)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
seenSendTimes.add(message.send_time);
|
||||||
|
dedupedMessages.push(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dedupedMessages;
|
||||||
}, [messagesQuery.data]);
|
}, [messagesQuery.data]);
|
||||||
|
|
||||||
const liveMessagesSnapshot = liveMessages();
|
const liveMessagesSnapshot = liveMessages();
|
||||||
|
|
||||||
const messages = React.useMemo(() => {
|
const liveWithoutDuplicates = React.useMemo(() => {
|
||||||
if (historicalMessages.length === 0) {
|
|
||||||
return liveMessagesSnapshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
const historicalSendTimes = new Set(
|
const historicalSendTimes = new Set(
|
||||||
historicalMessages.map((message) => message.send_time),
|
historicalMessages.map((message) => message.send_time),
|
||||||
);
|
);
|
||||||
const liveWithoutDuplicates = liveMessagesSnapshot.filter(
|
|
||||||
|
return liveMessagesSnapshot.filter(
|
||||||
(message) => !historicalSendTimes.has(message.send_time),
|
(message) => !historicalSendTimes.has(message.send_time),
|
||||||
);
|
);
|
||||||
|
|
||||||
return [...historicalMessages, ...liveWithoutDuplicates];
|
|
||||||
}, [historicalMessages, liveMessagesSnapshot]);
|
}, [historicalMessages, liveMessagesSnapshot]);
|
||||||
|
|
||||||
|
const messages = React.useMemo(() => {
|
||||||
|
return [...historicalMessages, ...liveWithoutDuplicates];
|
||||||
|
}, [historicalMessages, liveWithoutDuplicates]);
|
||||||
|
|
||||||
|
const historicalMessageChunks = React.useMemo(() => {
|
||||||
|
return buildMessageChunks(historicalMessages, "history");
|
||||||
|
}, [historicalMessages]);
|
||||||
|
|
||||||
|
const liveMessageChunks = React.useMemo(() => {
|
||||||
|
return buildMessageChunks(
|
||||||
|
liveWithoutDuplicates,
|
||||||
|
"live",
|
||||||
|
historicalMessages.length,
|
||||||
|
);
|
||||||
|
}, [historicalMessages.length, liveWithoutDuplicates]);
|
||||||
|
|
||||||
const messageChunks = React.useMemo(() => {
|
const messageChunks = React.useMemo(() => {
|
||||||
const chunks: Array<{
|
return [...liveMessageChunks, ...historicalMessageChunks];
|
||||||
key: number;
|
}, [historicalMessageChunks, liveMessageChunks]);
|
||||||
messages: typeof messages;
|
|
||||||
startIndex: number;
|
|
||||||
}> = [];
|
|
||||||
|
|
||||||
for (let end = messages.length; end > 0; end -= MESSAGES_PER_VIRTUAL_ROW) {
|
|
||||||
const start = Math.max(0, end - MESSAGES_PER_VIRTUAL_ROW);
|
|
||||||
const chunkMessages = messages.slice(start, end);
|
|
||||||
const firstMessage = chunkMessages[0];
|
|
||||||
|
|
||||||
if (firstMessage) {
|
|
||||||
chunks.push({
|
|
||||||
key: firstMessage.send_time,
|
|
||||||
messages: chunkMessages,
|
|
||||||
startIndex: start,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return chunks;
|
|
||||||
}, [messages]);
|
|
||||||
|
|
||||||
const shouldShowConversationStart =
|
const shouldShowConversationStart =
|
||||||
!!messagesQuery.data && !messagesQuery.hasNextPage;
|
!!messagesQuery.data && !messagesQuery.hasNextPage;
|
||||||
|
|
@ -530,7 +569,7 @@ export default function Screen() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Message
|
<Message
|
||||||
key={message.send_time}
|
key={getMessageRenderKey(message)}
|
||||||
grouped={isGrouped}
|
grouped={isGrouped}
|
||||||
message={message}
|
message={message}
|
||||||
user={
|
user={
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue