mirror of
https://github.com/Blah-IM/Weblah.git
synced 2025-05-31 06:01:10 +00:00
refactor: Update @blah-im/core to 0.10.0 and refactor message manager
- Move MessageManager to src/lib/blah/chat with sectioning logic - Remove old messageManager from connection - Add message sectioning utility - Update apiCall to support query params - Update chat exports and types
This commit is contained in:
parent
e410dc915f
commit
75239c865d
9 changed files with 119 additions and 56 deletions
|
@ -37,7 +37,7 @@
|
|||
"vitest": "^3.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blah-im/core": "^0.9.0",
|
||||
"@blah-im/core": "^0.10.0",
|
||||
"@zeabur/svelte-adapter": "^1.0.0",
|
||||
"bits-ui": "^1.5.3",
|
||||
"canonicalize": "^2.1.0",
|
||||
|
|
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
|
@ -9,8 +9,8 @@ importers:
|
|||
.:
|
||||
dependencies:
|
||||
'@blah-im/core':
|
||||
specifier: ^0.9.0
|
||||
version: 0.9.0
|
||||
specifier: ^0.10.0
|
||||
version: 0.10.0
|
||||
'@zeabur/svelte-adapter':
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0(@sveltejs/kit@2.21.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.33.0)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.33.0)(vite@6.3.5(jiti@2.4.2)(lightningcss@1.30.1)))
|
||||
|
@ -133,8 +133,8 @@ packages:
|
|||
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@blah-im/core@0.9.0':
|
||||
resolution: {integrity: sha512-DJ3TjBNuPTsnyk9FSM2v3+mLHOX9W0JEKYPvMYyhHFfjAJTuXEZV2TmPWP9legGnm3bUCJFHrFec+ew9hMwuTw==}
|
||||
'@blah-im/core@0.10.0':
|
||||
resolution: {integrity: sha512-AQTOi7LOdP3KW6tBpIuz9s/z59mpXY5O9dmeH8gQqMxX2S9gfcKCqq+38Kf847WJtrX08+8ehykA2EI42qtxmw==}
|
||||
|
||||
'@esbuild/aix-ppc64@0.19.12':
|
||||
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
|
||||
|
@ -2104,7 +2104,7 @@ snapshots:
|
|||
'@jridgewell/gen-mapping': 0.3.8
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
|
||||
'@blah-im/core@0.9.0':
|
||||
'@blah-im/core@0.10.0':
|
||||
dependencies:
|
||||
zod: 3.25.20
|
||||
|
||||
|
|
1
src/lib/blah/chat/index.ts
Normal file
1
src/lib/blah/chat/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './manager.svelte';
|
58
src/lib/blah/chat/manager.svelte.ts
Normal file
58
src/lib/blah/chat/manager.svelte.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import type { BlahRichText } from '@blah-im/core/richText';
|
||||
import type { BlahSignedPayload } from '@blah-im/core/crypto';
|
||||
|
||||
import type { BlahChatServerConnection } from '../connection';
|
||||
import type { BlahMessage } from '../structures';
|
||||
import { sectionMessages, type MessageSection } from './sectioning';
|
||||
import { messageFromBlah } from '$lib/types';
|
||||
|
||||
export class MessageManager {
|
||||
connection: BlahChatServerConnection;
|
||||
roomID: string;
|
||||
rawMessages: BlahSignedPayload<BlahMessage>[] = $state([]);
|
||||
sectionedMessages: MessageSection[] = $derived(
|
||||
sectionMessages(this.rawMessages.map(messageFromBlah))
|
||||
);
|
||||
#currentMessageID: string | null = $state(null);
|
||||
|
||||
get currentMessageID(): string | null {
|
||||
return this.#currentMessageID;
|
||||
}
|
||||
|
||||
set currentMessageID(id: string | null) {
|
||||
this.currentMessageID = id;
|
||||
this.fetchRoomHistory(id);
|
||||
}
|
||||
|
||||
constructor(
|
||||
connection: BlahChatServerConnection,
|
||||
roomID: string,
|
||||
currentMessageID: string | null
|
||||
) {
|
||||
this.connection = connection;
|
||||
this.roomID = roomID;
|
||||
this.currentMessageID = currentMessageID;
|
||||
|
||||
$effect(() => this.listen());
|
||||
this.fetchRoomHistory();
|
||||
}
|
||||
|
||||
async sendMessage(message: BlahRichText): Promise<void> {
|
||||
if (!this.connection.identity) throw new Error('Must send message with a keypair');
|
||||
const payload: BlahMessage = { room: this.roomID, rich_text: message, typ: 'chat' };
|
||||
await this.connection.apiCall('POST', `/room/${payload.room}/msg`, payload);
|
||||
}
|
||||
|
||||
async fetchRoomHistory(skipToken: string | null = null) {
|
||||
const { items }: { items: BlahSignedPayload<BlahMessage>[] } = await this.connection.apiCall(
|
||||
'GET',
|
||||
[`/room/${this.roomID}/msg`, { skip_token: skipToken ?? this.currentMessageID }]
|
||||
);
|
||||
this.rawMessages = items;
|
||||
}
|
||||
|
||||
listen() {
|
||||
return this.connection.subscribe((m) => this.rawMessages.push(m), { roomID: this.roomID })
|
||||
.unsubscribe;
|
||||
}
|
||||
}
|
41
src/lib/blah/chat/sectioning.ts
Normal file
41
src/lib/blah/chat/sectioning.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import type { Message, User } from '$lib/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 sectionMessages(messages: Message[]): MessageSection[] {
|
||||
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);
|
||||
|
||||
return sections;
|
||||
}
|
|
@ -1,12 +1,9 @@
|
|||
import { version } from '$app/environment';
|
||||
import type { BlahRichText } from '@blah-im/core/richText';
|
||||
import type { BlahKeyPair, BlahSignedPayload, SignOptions } from '@blah-im/core/crypto';
|
||||
import type { BlahSignedPayload, SignOptions } from '@blah-im/core/crypto';
|
||||
import {
|
||||
blahUserUnregisteredResponseSchema,
|
||||
type BlahAuth,
|
||||
type BlahMessage,
|
||||
type BlahRoomInfo,
|
||||
type BlahUserJoinMessage,
|
||||
type BlahUserRegisterRequest
|
||||
} from '../structures';
|
||||
import { BlahError } from './error';
|
||||
|
@ -54,7 +51,7 @@ export class BlahChatServerConnection {
|
|||
return { Authorization: JSON.stringify(signedAuthPayload) };
|
||||
}
|
||||
|
||||
private async fetchWithAuthHeader(url: string, init?: RequestInit): Promise<Response> {
|
||||
private async fetchWithAuthHeader(url: string | URL, init?: RequestInit): Promise<Response> {
|
||||
const authHeader = await this.generateAuthHeader();
|
||||
return fetch(url, {
|
||||
...init,
|
||||
|
@ -84,19 +81,27 @@ export class BlahChatServerConnection {
|
|||
|
||||
public async apiCall<P, R>(
|
||||
method: 'POST' | 'GET',
|
||||
path: `/${string}`,
|
||||
path: `/${string}` | [`/${string}`, { [query: string]: string | null | undefined }],
|
||||
payload?: P,
|
||||
signOptions?: Omit<SignOptions, 'identityKeyID'>
|
||||
): Promise<R> {
|
||||
if (payload && !this.identity)
|
||||
throw new Error('Must make authorized API call with an identity');
|
||||
|
||||
const url = new URL(typeof path === 'string' ? path : path[0], this.endpoint_);
|
||||
const query = typeof path === 'string' ? undefined : path[1];
|
||||
if (query) {
|
||||
for (const [key, value] of Object.entries(query)) {
|
||||
if (value) url.searchParams.append(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
let response: Response;
|
||||
if (method === 'GET') {
|
||||
if (this.identity) {
|
||||
response = await this.fetchWithAuthHeader(`${this.endpoint_}${path}`);
|
||||
response = await this.fetchWithAuthHeader(url);
|
||||
} else {
|
||||
response = await fetch(`${this.endpoint_}${path}`, {
|
||||
response = await fetch(url, {
|
||||
headers: BlahChatServerConnection.commonHeaders
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
export * from './connection';
|
||||
export * from './error';
|
||||
export * from './messageManager.svelte';
|
||||
export * from './roomManager.svelte';
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
import type { BlahRichText } from '@blah-im/core/richText';
|
||||
import type { BlahSignedPayload } from '@blah-im/core/crypto';
|
||||
|
||||
import type { BlahChatServerConnection } from './connection';
|
||||
import type { BlahMessage } from '../structures';
|
||||
|
||||
export class MessageManager {
|
||||
connection: BlahChatServerConnection;
|
||||
roomID: string;
|
||||
messages: BlahSignedPayload<BlahMessage>[] = $state([]);
|
||||
|
||||
constructor(connection: BlahChatServerConnection, roomID: string) {
|
||||
this.connection = connection;
|
||||
this.roomID = roomID;
|
||||
}
|
||||
|
||||
async sendMessage(message: BlahRichText): Promise<void> {
|
||||
if (!this.connection.identity) throw new Error('Must send message with a keypair');
|
||||
const payload: BlahMessage = { room: this.roomID, rich_text: message, typ: 'chat' };
|
||||
await this.connection.apiCall('POST', `/room/${payload.room}/item`, payload);
|
||||
}
|
||||
|
||||
async fetchRoomHistory() {
|
||||
const { items }: { items: BlahSignedPayload<BlahMessage>[] } = await this.connection.apiCall(
|
||||
'GET',
|
||||
`/room/${this.roomID}/item`
|
||||
);
|
||||
this.messages = items;
|
||||
}
|
||||
|
||||
listen() {
|
||||
return this.connection.subscribe((m) => this.messages.push(m), { roomID: this.roomID })
|
||||
.unsubscribe;
|
||||
}
|
||||
}
|
|
@ -7,12 +7,6 @@ import { BlahError } from './blah/connection/error';
|
|||
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,
|
||||
chatId: string
|
||||
|
|
Loading…
Add table
Reference in a new issue