Handle message updates via websockets for current channel
parent
d9a7282929
commit
7dbb36ddb0
|
@ -3,27 +3,27 @@ import { WebSocket } from "ws";
|
|||
|
||||
export const attachEvents = (ws: WebSocket) => {
|
||||
events.on('file-uploaded', (id, channelId, messageId, filePath, fileType, fileSize, originalName) => {
|
||||
ws.send(JSON.stringify({ type: 'file-uploaded', id, channelId, messageId, filePath, fileType, fileSize, originalName }));
|
||||
ws.send(JSON.stringify({ type: 'file-uploaded', data: {id, channelId, messageId, filePath, fileType, fileSize, originalName }}));
|
||||
});
|
||||
events.on('message-created', (id, channelId, content) => {
|
||||
ws.send(JSON.stringify({ type: 'message-created', id, channelId, content }));
|
||||
ws.send(JSON.stringify({ type: 'message-created', data: {id, channelId, content }}));
|
||||
});
|
||||
events.on('message-updated', (id, content) => {
|
||||
ws.send(JSON.stringify({ type: 'message-updated', id, content }));
|
||||
ws.send(JSON.stringify({ type: 'message-updated', data: {id, content }}));
|
||||
});
|
||||
events.on('message-deleted', (id) => {
|
||||
ws.send(JSON.stringify({ type: 'message-deleted', id }));
|
||||
ws.send(JSON.stringify({ type: 'message-deleted', data: {id }}));
|
||||
});
|
||||
events.on('channel-created', (channel) => {
|
||||
ws.send(JSON.stringify({ type: 'channel-created', channel }));
|
||||
ws.send(JSON.stringify({ type: 'channel-created', data: {channel }}));
|
||||
});
|
||||
events.on('channel-deleted', (id) => {
|
||||
ws.send(JSON.stringify({ type: 'channel-deleted', id }));
|
||||
ws.send(JSON.stringify({ type: 'channel-deleted', data: {id} }));
|
||||
});
|
||||
events.on('channel-merged', (channelId, targetChannelId) => {
|
||||
ws.send(JSON.stringify({ type: 'channel-merged', channelId, targetChannelId }));
|
||||
ws.send(JSON.stringify({ type: 'channel-merged', data: {channelId, targetChannelId }}));
|
||||
});
|
||||
events.on('channel-updated', (id, name) => {
|
||||
ws.send(JSON.stringify({ type: 'channel-updated', id, name }));
|
||||
ws.send(JSON.stringify({ type: 'channel-updated', data: {id, name }}));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
export type MessageCreated = {
|
||||
channelId: string,
|
||||
id: string,
|
||||
content: string,
|
||||
};
|
||||
|
||||
export type MessageDeleted = {
|
||||
channelId: string,
|
||||
messageId: string,
|
||||
};
|
||||
|
||||
export type MessageUpdated = {
|
||||
id: string,
|
||||
content: string,
|
||||
};
|
||||
|
||||
export type ChannelCreated = {
|
||||
name: string,
|
||||
};
|
||||
|
||||
export type ChannelDeleted = {
|
||||
channelId: string,
|
||||
};
|
||||
|
||||
export type ChannelUpdated = {
|
||||
channelId: string,
|
||||
name: string,
|
||||
};
|
|
@ -0,0 +1,60 @@
|
|||
export type Message<T> = {
|
||||
type: string,
|
||||
data?: T,
|
||||
};
|
||||
|
||||
export type MessageHandler<T> = (message: Message<T>) => void;
|
||||
|
||||
export class MessagingSystem {
|
||||
private handlers: Record<string, MessageHandler<any>[]> = {};
|
||||
|
||||
public registerHandler<T>(type: string, handler: MessageHandler<T>): void {
|
||||
if (!this.handlers[type]) {
|
||||
this.handlers[type] = [];
|
||||
}
|
||||
if (!this.handlers[type].includes(handler)) {
|
||||
this.handlers[type].push(handler);
|
||||
}
|
||||
}
|
||||
|
||||
public unregisterHandler<T>(type: string, handler: MessageHandler<T>): void {
|
||||
if (this.handlers[type]) {
|
||||
this.handlers[type] = this.handlers[type].filter(h => h !== handler);
|
||||
}
|
||||
}
|
||||
|
||||
public registerHandlerOnce<T>(type: string, handler: MessageHandler<T>): void {
|
||||
const wrappedHandler = (message: Message<T>) => {
|
||||
handler(message);
|
||||
this.unregisterHandler(type, wrappedHandler);
|
||||
};
|
||||
this.registerHandler(type, wrappedHandler);
|
||||
}
|
||||
|
||||
public waitForMessage<T>(type: string, timeout?: number): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const handler = (message: Message<T>) => {
|
||||
if (timer) clearTimeout(timer);
|
||||
resolve(message.data!);
|
||||
this.unregisterHandler(type, handler);
|
||||
};
|
||||
|
||||
this.registerHandler(type, handler);
|
||||
|
||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||
if (timeout) {
|
||||
timer = setTimeout(() => {
|
||||
this.unregisterHandler(type, handler);
|
||||
reject(new Error(`Timeout waiting for message of type '${type}'`));
|
||||
}, timeout);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public sendMessage<T>(message: Message<T>): void {
|
||||
const handlers = this.handlers[message.type];
|
||||
if (handlers) {
|
||||
handlers.forEach(handler => handler(message));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
import { MessagingSystem } from "./events/messaging-system";
|
||||
import { IChannel, Channel } from "./model/channel";
|
||||
import { IChannelList, ChannelList } from "./model/channel-list";
|
||||
import { IState } from "./model/state";
|
||||
|
@ -12,11 +13,13 @@ export class State implements IState {
|
|||
unsentMessages!: IUnsentMessage[];
|
||||
currentChannel!: Channel | null;
|
||||
defaultChannelId!: number;
|
||||
public events: MessagingSystem;
|
||||
|
||||
constructor() {
|
||||
this.token = "";
|
||||
this.channelList = new ChannelList();
|
||||
this.unsentMessages = [];
|
||||
this.events = new MessagingSystem();
|
||||
}
|
||||
|
||||
public getToken(): string {
|
||||
|
@ -45,7 +48,7 @@ export class State implements IState {
|
|||
|
||||
public async save(): Promise<void> {
|
||||
// stringify everything here except the currentChannel object.
|
||||
const { currentChannel, ...state } = this;
|
||||
const { currentChannel, events, ...state } = this;
|
||||
await set("notebrook", state);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import { RecordAudioDialog } from "../dialogs/record-audio";
|
|||
import { SearchDialog } from "../dialogs/search";
|
||||
import { SettingsDialog } from "../dialogs/settings";
|
||||
import { TakePhotoDialog } from "../dialogs/take-photo";
|
||||
import { MessageUpdated } from "../events/message-events";
|
||||
import { Channel } from "../model/channel";
|
||||
import { IMessage, Message } from "../model/message";
|
||||
import { UnsentMessage } from "../model/unsent-message";
|
||||
|
@ -52,6 +53,24 @@ export class MainView extends View {
|
|||
this.updateVisibleMessageShownTimestamps();
|
||||
}, 1000);
|
||||
setTimeout(() => this.attemptToSendUnsentMessages(), 2000);
|
||||
|
||||
state.events.registerHandler<MessageUpdated>("message-updated", (message) => {
|
||||
const { data } = message;
|
||||
if (!data) return;
|
||||
const channel = state.currentChannel;
|
||||
if (!channel) return;
|
||||
const existing = channel.getMessage(parseInt(data!.id));
|
||||
if (!existing) {
|
||||
return;
|
||||
} else {
|
||||
existing.content = data.content;
|
||||
state.save();
|
||||
const renderedMessage = this.messageElementMap.get(existing.id);
|
||||
if (renderedMessage) {
|
||||
(renderedMessage as ListItem).setText(`${existing.content}; ${this.convertIsoTimeStringToRelative(existing.createdAt)}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -257,7 +276,7 @@ export class MainView extends View {
|
|||
|
||||
private renderMessage(message: IMessage): UINode {
|
||||
const itm = new ListItem(`${message.content}; ${this.convertIsoTimeStringToRelative(message.createdAt)}`);
|
||||
itm.setUserData(message);
|
||||
itm.setUserData(message.id);
|
||||
itm.onClick(() => {
|
||||
new MessageDialog(message).open();
|
||||
})
|
||||
|
@ -430,7 +449,8 @@ export class MainView extends View {
|
|||
for (let i = lowerIndex; i < upperIndex; i++) {
|
||||
const child = this.messageList.children[i];
|
||||
if (!child) break;
|
||||
const message = child.getUserData() as IMessage;
|
||||
const messageId = child.getUserData() as number;
|
||||
const message = state.currentChannel?.getMessage(messageId);
|
||||
if (message) {
|
||||
(child as ListItem).setText(`${message.content}; ${this.convertIsoTimeStringToRelative(message.createdAt)}`);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { API } from "./api";
|
||||
import { state } from "./state";
|
||||
|
||||
export const connectToWebsocket = () => {
|
||||
const ws = new WebSocket(`ws://localhost:3000`);
|
||||
|
@ -7,6 +8,7 @@ export const connectToWebsocket = () => {
|
|||
}
|
||||
ws.onmessage = (data) => {
|
||||
const message = JSON.parse(data.data.toString());
|
||||
state.events.sendMessage(message);
|
||||
console.log(message);
|
||||
}
|
||||
ws.onclose= () => {
|
||||
|
|
Loading…
Reference in New Issue