refactor: render messages in sections

This commit is contained in:
Shibo Lyu 2024-09-04 12:59:31 +08:00
parent 4be1380d69
commit c80b2cbf10
8 changed files with 129 additions and 36 deletions

View file

@ -1,7 +1,16 @@
import { readable, type Readable } from 'svelte/store';
import { derived, readable, type Readable } from 'svelte/store';
import type { BlahChatServerConnection } from './blah/connection/chatServer';
import type { BlahRichText } from './richText';
import { messageFromBlah, type Chat, type Message } from './types';
import { messageFromBlah, type Chat, type Message, type User } from './types';
const MAX_MESSAGES_PER_SECTION = 10;
const SHOW_TIME_AFTER_SILENCE = 30 * 60 * 1000;
export type MessageSection = {
sender?: User;
messages: Message[];
date?: Date;
};
export function useChat(
server: BlahChatServerConnection,
@ -9,6 +18,7 @@ export function useChat(
): {
info: Readable<Chat>;
messages: Readable<Message[]>;
sectionedMessages: Readable<MessageSection[]>;
sendMessage: (brt: BlahRichText) => Promise<void>;
} {
const info = readable<Chat>(
@ -37,9 +47,41 @@ export function useChat(
return unsubscribe;
});
const sectionedMessages = derived([messages], ([messages]) => {
const sections: MessageSection[] = [];
let lastMessage: Message | undefined = messages[0];
let currentSection: MessageSection = {
messages: [],
sender: lastMessage?.sender,
date: lastMessage?.date
};
for (const message of messages) {
const reachesMaxMessages = currentSection.messages.length >= MAX_MESSAGES_PER_SECTION;
const senderChanged = message.sender.id !== lastMessage.sender.id;
const silentForTooLong =
message.date.getTime() - lastMessage.date.getTime() > SHOW_TIME_AFTER_SILENCE;
if (reachesMaxMessages || senderChanged || silentForTooLong) {
if (currentSection.messages.length > 0) {
sections.push(currentSection);
}
currentSection = { messages: [], sender: message.sender };
if (silentForTooLong) currentSection.date = message.date;
}
currentSection.messages.push(message);
lastMessage = message;
}
sections.push(currentSection);
console.log(sections);
return sections;
});
const sendMessage = async (brt: BlahRichText) => {
await server.sendMessage(chatId, brt);
};
return { info, messages, sendMessage };
return { info, messages, sectionedMessages, sendMessage };
}

View file

@ -7,7 +7,7 @@
<div
class={tw(
'backdrop mx-4 cursor-default rounded-full px-2 py-0.5 text-center backdrop-blur-sm',
'backdrop mx-4 inline-block cursor-default rounded-full px-2 py-0.5 text-center text-sm text-sf-secondary backdrop-blur-sm',
className
)}
>

View file

@ -7,19 +7,29 @@ export function formatUnreadCount(count: number) {
return unreadCountFormatter.format(count);
}
const sameDayFormatter = new Intl.DateTimeFormat('default', {
hour: '2-digit',
minute: '2-digit'
});
const timeOptions: Intl.DateTimeFormatOptions = { hour: '2-digit', minute: '2-digit' };
const sameDayFormatter = new Intl.DateTimeFormat('default', timeOptions);
const sameYearFormatter = new Intl.DateTimeFormat('default', {
month: 'short',
day: 'numeric'
});
const sameYearWithTimeFormatter = new Intl.DateTimeFormat('default', {
month: 'short',
day: 'numeric',
...timeOptions
});
const otherYearFormatter = new Intl.DateTimeFormat('default', {
year: 'numeric',
month: 'short',
day: 'numeric'
});
const otherYearWithTimeFormatter = new Intl.DateTimeFormat('default', {
year: 'numeric',
month: 'short',
day: 'numeric',
...timeOptions
});
const fullDateTimeFormatter = new Intl.DateTimeFormat('default', {
year: 'numeric',
month: 'short',
@ -29,9 +39,7 @@ const fullDateTimeFormatter = new Intl.DateTimeFormat('default', {
second: '2-digit'
});
export const formatMessageDate = (date: Date, full: boolean = false) => {
if (full) return fullDateTimeFormatter.format(date);
export const formatMessageDate = (date: Date) => {
const now = new Date();
if (date.getFullYear() === now.getFullYear()) {
if (date.getMonth() === now.getMonth() && date.getDate() === now.getDate()) {
@ -43,3 +51,19 @@ export const formatMessageDate = (date: Date, full: boolean = false) => {
return otherYearFormatter.format(date);
}
};
export const formatFullMessageDate = (date: Date) => {
return fullDateTimeFormatter.format(date);
};
export const formatMessageSectionDate = (date: Date) => {
const now = new Date();
if (date.getFullYear() === now.getFullYear()) {
if (date.getMonth() === now.getMonth() && date.getDate() === now.getDate()) {
return sameDayFormatter.format(date);
} else {
return sameYearWithTimeFormatter.format(date);
}
} else {
return otherYearWithTimeFormatter.format(date);
}
};