message updates no longer crashing

This commit is contained in:
Michael Peters 2021-11-07 14:13:59 -06:00
parent 84c05330ba
commit 6e38c4202a
7 changed files with 64 additions and 44 deletions

View File

@ -20,9 +20,11 @@ electronMain.initialize();
(async () => { (async () => {
await electron.app.whenReady(); await electron.app.whenReady();
LOG.silly('electron app is ready'); LOG.silly('electron app is ready');
await LOG.ensureSourceMaps();
LOG.silly('base log source maps loaded');
const window = new electron.BrowserWindow({ const window = new electron.BrowserWindow({
width: 1580,//1080, width: 1580,//1080,
height: 720, height: 720,

View File

@ -70,10 +70,8 @@ export default class Actions {
async fetchAndUpdateMessagesRecent(q: Q, server: ClientController, channel: Channel | { id: string }) { async fetchAndUpdateMessagesRecent(q: Q, server: ClientController, channel: Channel | { id: string }) {
await Util.withPotentialErrorWarnOnCancel(q, { await Util.withPotentialErrorWarnOnCancel(q, {
taskFunc: async () => { taskFunc: async () => {
LOG.debug('active server is s#' + this.ui.activeServer?.id);
LOG.debug('active channel is ch#' + this.ui.activeChannel?.id);
if (this.ui.activeServer === null || this.ui.activeServer.id !== server.id) return; if (this.ui.activeServer === null || this.ui.activeServer.id !== server.id) return;
if (this.ui.activeChannel === null || this.ui.activeChannel.id !== server.id) return; if (this.ui.activeChannel === null || this.ui.activeChannel.id !== channel.id) return;
let messages = await server.grabRecentMessages(channel.id, Globals.MESSAGES_PER_REQUEST); let messages = await server.grabRecentMessages(channel.id, Globals.MESSAGES_PER_REQUEST);
await this.ui.setMessages(server, channel, messages, { atTop: messages.length < Globals.MESSAGES_PER_REQUEST, atBottom: true }); await this.ui.setMessages(server, channel, messages, { atTop: messages.length < Globals.MESSAGES_PER_REQUEST, atBottom: true });
}, },
@ -89,7 +87,7 @@ export default class Actions {
await Util.withPotentialErrorWarnOnCancel(q, { await Util.withPotentialErrorWarnOnCancel(q, {
taskFunc: async () => { taskFunc: async () => {
if (this.ui.activeServer === null || this.ui.activeServer.id !== server.id) return; if (this.ui.activeServer === null || this.ui.activeServer.id !== server.id) return;
if (this.ui.activeChannel === null || this.ui.activeChannel.id !== server.id) return; if (this.ui.activeChannel === null || this.ui.activeChannel.id !== channel.id) return;
let topPair = this.ui.getTopMessagePair(); let topPair = this.ui.getTopMessagePair();
if (topPair == null) return; if (topPair == null) return;
let messages = await server.fetchMessagesBefore(channel.id, topPair.message.id, Globals.MESSAGES_PER_REQUEST); let messages = await server.fetchMessagesBefore(channel.id, topPair.message.id, Globals.MESSAGES_PER_REQUEST);
@ -112,7 +110,7 @@ export default class Actions {
await Util.withPotentialErrorWarnOnCancel(q, { await Util.withPotentialErrorWarnOnCancel(q, {
taskFunc: async () => { taskFunc: async () => {
if (this.ui.activeServer === null || this.ui.activeServer.id !== server.id) return; if (this.ui.activeServer === null || this.ui.activeServer.id !== server.id) return;
if (this.ui.activeChannel === null || this.ui.activeChannel.id !== server.id) return; if (this.ui.activeChannel === null || this.ui.activeChannel.id !== channel.id) return;
let bottomPair = this.ui.getBottomMessagePair(); let bottomPair = this.ui.getBottomMessagePair();
if (bottomPair == null) return; if (bottomPair == null) return;
let messages = await server.fetchMessagesAfter(channel.id, bottomPair.message.id, Globals.MESSAGES_PER_REQUEST); let messages = await server.fetchMessagesAfter(channel.id, bottomPair.message.id, Globals.MESSAGES_PER_REQUEST);

View File

@ -14,6 +14,7 @@ import { Message, Member, Channel, Changes, ConnectionInfo, CacheServerData, Ser
import DBCache from './db-cache'; import DBCache from './db-cache';
import ResourceRAMCache from './resource-ram-cache'; import ResourceRAMCache from './resource-ram-cache';
import RecentMessageRAMCache from './message-ram-cache'; import RecentMessageRAMCache from './message-ram-cache';
import { channel } from 'diagnostics_channel';
// Events: // Events:
// 'connected' function() called when connected to the server // 'connected' function() called when connected to the server
@ -23,11 +24,11 @@ import RecentMessageRAMCache from './message-ram-cache';
// 'update-server' function(serverData) called when the server metadata updates // 'update-server' function(serverData) called when the server metadata updates
// 'deleted-members' function(members) called when members were deleted on the server-side // 'deleted-members' function(members) called when members were deleted on the server-side
// 'updated-members' function(data: Array [ { oldDataPoint, newDataPoint } ]) called when a member was updated on the server-side // 'updated-members' function(data: Array [ { oldMember, newMember } ]) called when a member was updated on the server-side
// 'added-members' function(members) called when members were added on the server-side // 'added-members' function(members) called when members were added on the server-side
// 'deleted-channels' function(channels) called when channels were deleted on the server-side // 'deleted-channels' function(channels) called when channels were deleted on the server-side
// 'updated-channels' function(data: Array [ { oldDataPoint, newDataPoint } ]) called when a channel was updated on the server-side // 'updated-channels' function(data: Array [ { oldChannel, newChannel } ]) called when a channel was updated on the server-side
// 'added-channels' function(channels) called when channels are added on the server-side // 'added-channels' function(channels) called when channels are added on the server-side
// 'new-message' function(message) called when a message is received in a channel // 'new-message' function(message) called when a message is received in a channel
@ -54,7 +55,7 @@ import RecentMessageRAMCache from './message-ram-cache';
// async setStatus(status) Set the current logged in user's status // async setStatus(status) Set the current logged in user's status
// async setDisplayName(displayName) Sets the current logged in user's display name // async setDisplayName(displayName) Sets the current logged in user's display name
// async setAvatar(avatarBu{ updated: { oldDataPoint: T, newDataPoint: T }[], added: T[], removed: T[] } // async setAvatar(avatarBuff)
// async updateChannel(channelId, name, flavorText) Updates a channel's name and flavor text // async updateChannel(channelId, name, flavorText) Updates a channel's name and flavor text
// async queryTokens() Queries for the login tokens as [ { token, member_id, created, expires }, ... ] // async queryTokens() Queries for the login tokens as [ { token, member_id, created, expires }, ... ]
@ -162,7 +163,7 @@ export default class ClientController extends EventEmitter {
await this.ensureMembers(); await this.ensureMembers();
let oldMember = this.members.get(member.id); let oldMember = this.members.get(member.id);
if (oldMember) { if (oldMember) {
this.emit('updated-members', [ { oldDataPoint: oldMember, newDataPoint: member } ]); this.emit('updated-members', [ { oldMember: oldMember, newMember: member } ]);
} else { } else {
this.emit('added-members', [ member ]); this.emit('added-members', [ member ]);
} }
@ -171,7 +172,7 @@ export default class ClientController extends EventEmitter {
await this.ensureChannels(); await this.ensureChannels();
let oldChannel = this.channels.get(channel.id); let oldChannel = this.channels.get(channel.id);
if (oldChannel) { if (oldChannel) {
this.emit('updated-channels', [ { oldDataPoint: oldChannel, newDataPoint: channel } ]); this.emit('updated-channels', [ { oldChannel: oldChannel, newChannel: channel } ]);
} else { } else {
this.emit('added-channels', [ channel ]); this.emit('added-channels', [ channel ]);
} }
@ -193,9 +194,9 @@ export default class ClientController extends EventEmitter {
} }
await DBCache.updateServerMembers(this.id, Array.from(this.members.values())); await DBCache.updateServerMembers(this.id, Array.from(this.members.values()));
}); });
this.on('updated-members', async (data: { oldDataPoint: Member, newDataPoint: Member }[]) => { this.on('updated-members', async (data: { oldMember: Member, newMember: Member }[]) => {
for (const { oldDataPoint, newDataPoint } of data) { for (const { oldMember, newMember } of data) {
this.members.set(newDataPoint.id, newDataPoint); this.members.set(newMember.id, newMember);
} }
await DBCache.updateServerMembers(this.id, Array.from(this.members.values())); await DBCache.updateServerMembers(this.id, Array.from(this.members.values()));
}); });
@ -212,9 +213,9 @@ export default class ClientController extends EventEmitter {
} }
await DBCache.updateServerChannels(this.id, Array.from(this.channels.values())); await DBCache.updateServerChannels(this.id, Array.from(this.channels.values()));
}); });
this.on('updated-channels', async (data: { oldDataPoint: Channel, newDataPoint: Channel }[]) => { this.on('updated-channels', async (data: { oldChannel: Channel, newChannel: Channel }[]) => {
for (const { oldDataPoint, newDataPoint } of data) { for (const { oldChannel, newChannel } of data) {
this.channels.set(newDataPoint.id, newDataPoint); this.channels.set(newChannel.id, newChannel);
} }
await DBCache.updateServerChannels(this.id, Array.from(this.channels.values())); await DBCache.updateServerChannels(this.id, Array.from(this.channels.values()));
}); });
@ -236,13 +237,12 @@ export default class ClientController extends EventEmitter {
// Alternatively, just store the date in the message and use order-by // Alternatively, just store the date in the message and use order-by
this._recentMessages.dropChannel(this.id, channel.id); this._recentMessages.dropChannel(this.id, channel.id);
}); });
this.on('updated-messages', async (data: { oldDataPoint: Message, newDataPoint: Message }[]) => { this.on('updated-messages', async (channel: Channel, data: { oldMessage: Message, newMessage: Message }[]) => {
LOG.debug('updated-messages: ', data); for (let { oldMessage, newMessage } of data) {
for (let { oldDataPoint, newDataPoint } of data) { this._recentMessages.updateMessage(this.id, oldMessage, newMessage);
this._recentMessages.updateMessage(this.id, oldDataPoint, newDataPoint);
} }
}); });
this.on('deleted-messages', async (messages: Message[]) => { this.on('deleted-messages', async (_channel: Channel, messages: Message[]) => {
for (let message of messages) { for (let message of messages) {
this._recentMessages.deleteMessage(this.id, message); this._recentMessages.deleteMessage(this.id, message);
} }
@ -515,7 +515,7 @@ export default class ClientController extends EventEmitter {
async grabRecentMessages(channelId: string, number: number): Promise<Message[]> { async grabRecentMessages(channelId: string, number: number): Promise<Message[]> {
let cached = this._recentMessages.getRecentMessages(this.id, channelId, number); let cached = this._recentMessages.getRecentMessages(this.id, channelId, number);
if (cached != null) return cached; if (cached !== null) return cached;
return await this.fetchMessagesRecent(channelId, number); return await this.fetchMessagesRecent(channelId, number);
} }
@ -621,7 +621,7 @@ export default class ClientController extends EventEmitter {
this.emit('added-members', changes.added); this.emit('added-members', changes.added);
} }
if (changes.updated.length > 0) { if (changes.updated.length > 0) {
this.emit('updated-members', changes.updated); this.emit('updated-members', changes.updated.map(change => ({ oldMember: change.oldDataPoint, newMember: change.newDataPoint })));
} }
return true; return true;
@ -662,7 +662,7 @@ export default class ClientController extends EventEmitter {
this.emit('added-channels', changes.added); this.emit('added-channels', changes.added);
} }
if (changes.updated.length > 0) { if (changes.updated.length > 0) {
this.emit('updated-channels', changes.updated); this.emit('updated-channels', changes.updated.map(change => ({ oldChannel: change.oldDataPoint, newChannel: change.newDataPoint })));
} }
return true; return true;

View File

@ -38,6 +38,9 @@ window.addEventListener('DOMContentLoaded', () => {
document.body.classList.remove('preload'); document.body.classList.remove('preload');
(async () => { (async () => {
await LOG.ensureSourceMaps();
LOG.silly('web client log source maps loaded');
await DBCache.connect(); await DBCache.connect();
await DBCache.init(); await DBCache.init();
@ -149,12 +152,12 @@ window.addEventListener('DOMContentLoaded', () => {
await ui.deleteMembers(server, members); await ui.deleteMembers(server, members);
}); });
controller.on('updated-members', async (server: ClientController, data: { oldDataPoint: Member, newDataPoint: Member }[]) => { controller.on('updated-members', async (server: ClientController, data: { oldMember: Member, newMember: Member }[]) => {
LOG.debug(data.length + ' updated members s#' + server.id); LOG.debug(data.length + ' updated members s#' + server.id);
await ui.updateMembers(server, data); await ui.updateMembers(server, data);
if ( if (
ui.activeConnection !== null && ui.activeConnection !== null &&
data.find((c: any) => c.newDataPoint.id === (ui.activeConnection as ConnectionInfo).id) data.find(change => change.newMember.id === (ui.activeConnection as ConnectionInfo).id)
) { ) {
await actions.fetchAndUpdateConnection(server); await actions.fetchAndUpdateConnection(server);
} }
@ -170,7 +173,7 @@ window.addEventListener('DOMContentLoaded', () => {
await ui.deleteChannels(server, channels); await ui.deleteChannels(server, channels);
}); });
controller.on('updated-channels', async (server: ClientController, data: { oldDataPoint: Channel, newDataPoint: Channel }[]) => { controller.on('updated-channels', async (server: ClientController, data: { oldChannel: Channel, newChannel: Channel }[]) => {
LOG.debug(data.length + ' updated channels'); LOG.debug(data.length + ' updated channels');
await ui.updateChannels(actions, server, data); await ui.updateChannels(actions, server, data);
}); });
@ -187,7 +190,7 @@ window.addEventListener('DOMContentLoaded', () => {
await ui.deleteMessages(server, channel, messages); await ui.deleteMessages(server, channel, messages);
}); });
controller.on('updated-messages', async (server: ClientController, channel: Channel, data: { oldDataPoint: Message, newDataPoint: Message }[]) => { controller.on('updated-messages', async (server: ClientController, channel: Channel, data: { oldMessage: Message, newMessage: Message }[]) => {
LOG.debug(data.length + ' updated messages'); LOG.debug(data.length + ' updated messages');
// messages were updated on the server-side // messages were updated on the server-side
await ui.updateMessages(server, channel, data); await ui.updateMessages(server, channel, data);
@ -218,12 +221,12 @@ window.addEventListener('DOMContentLoaded', () => {
// messages that were updated (currently extraneous) // messages that were updated (currently extraneous)
// Note: this should never happen since these should be handled by updated-messages // Note: this should never happen since these should be handled by updated-messages
// Note: this may be used to replace dummy pre-sent messages // Note: this may be used to replace dummy pre-sent messages
let toUpdate: { newDataPoint: Message, oldDataPoint: Message }[] = []; let toUpdate: { newMessage: Message, oldMessage: Message }[] = [];
for (let addedMessage of addedBefore.values()) { for (let addedMessage of addedBefore.values()) {
if (ui.messagePairs.has(addedMessage.id)) { if (ui.messagePairs.has(addedMessage.id)) {
toUpdate.push({ toUpdate.push({
newDataPoint: addedMessage, newMessage: addedMessage,
oldDataPoint: (ui.messagePairs.get(addedMessage.id) as { message: Message, element: HTMLElement }).message oldMessage: (ui.messagePairs.get(addedMessage.id) as { message: Message, element: HTMLElement }).message
}); });
} }
} }

View File

@ -263,10 +263,9 @@ export default class UI {
}); });
} }
public async updateChannels(actions: Actions, server: ClientController, data: { oldDataPoint: Channel, newDataPoint: Channel }[]): Promise<void> { public async updateChannels(actions: Actions, server: ClientController, data: { oldChannel: Channel, newChannel: Channel }[]): Promise<void> {
await this.lockChannels(server, () => { await this.lockChannels(server, () => {
for (const { oldDataPoint, newDataPoint } of data) { for (const { oldChannel, newChannel } of data) {
let newChannel = newDataPoint;
let oldElement = this.q.$('#channel-list .channel[meta-id="' + newChannel.id + '"]'); let oldElement = this.q.$('#channel-list .channel[meta-id="' + newChannel.id + '"]');
let newElement = createChannel(this.document, this.q, this, actions, server, newChannel); let newElement = createChannel(this.document, this.q, this, actions, server, newChannel);
oldElement.parentElement?.replaceChild(newElement, oldElement); oldElement.parentElement?.replaceChild(newElement, oldElement);
@ -359,10 +358,9 @@ export default class UI {
}); });
} }
public async updateMembers(server: ClientController, data: { oldDataPoint: Member, newDataPoint: Member }[]): Promise<void> { public async updateMembers(server: ClientController, data: { oldMember: Member, newMember: Member }[]): Promise<void> {
await this.lockMembers(server, () => { await this.lockMembers(server, () => {
for (const { oldDataPoint, newDataPoint } of data) { for (const { oldMember, newMember } of data) {
let newMember = newDataPoint;
let oldElement = this.q.$_('#server-members .member[meta-id="' + newMember.id + '"]'); let oldElement = this.q.$_('#server-members .member[meta-id="' + newMember.id + '"]');
if (oldElement) { if (oldElement) {
let newElement = createMember(this.q, server, newMember); let newElement = createMember(this.q, server, newMember);
@ -373,8 +371,7 @@ export default class UI {
}); });
if (this.activeChannel === null) return; if (this.activeChannel === null) return;
await this.lockMessages(server, this.activeChannel, () => { await this.lockMessages(server, this.activeChannel, () => {
for (const { oldDataPoint, newDataPoint } of data) { for (const { oldMember, newMember } of data) {
let newMember = newDataPoint;
let newStyle = newMember.roleColor ? 'color: ' + newMember.roleColor : null; let newStyle = newMember.roleColor ? 'color: ' + newMember.roleColor : null;
let newName = newMember.displayName; let newName = newMember.displayName;
// the extra query selectors may be overkill // the extra query selectors may be overkill
@ -635,11 +632,9 @@ export default class UI {
}); });
} }
public async updateMessages(server: ClientController, channel: Channel, data: { oldDataPoint: Message, newDataPoint: Message }[]): Promise<void> { public async updateMessages(server: ClientController, channel: Channel, data: { oldMessage: Message, newMessage: Message }[]): Promise<void> {
await this.lockMessages(server, channel, () => { await this.lockMessages(server, channel, () => {
for (const { oldDataPoint, newDataPoint } of data) { for (const { oldMessage, newMessage } of data) {
let oldMessage = oldDataPoint;
let newMessage = newDataPoint;
if (this.messagePairs.has(oldMessage.id)) { if (this.messagePairs.has(oldMessage.id)) {
let oldElement = (this.messagePairs.get(oldMessage.id) as { message: Message, element: HTMLElement }).element; let oldElement = (this.messagePairs.get(oldMessage.id) as { message: Message, element: HTMLElement }).element;
let prevElement = Q.previousElement(oldElement); let prevElement = Q.previousElement(oldElement);

View File

@ -146,6 +146,8 @@ export default class Logger {
private name: string; private name: string;
private console: Console; private console: Console;
private sourceMaps = new Map<string, SourceMapConsumer>(); private sourceMaps = new Map<string, SourceMapConsumer>();
private hasSourceMaps = false;
private sourceMapsWaiters: (() => void)[] = [];
private constructor(name: string, processConsole?: Console) { private constructor(name: string, processConsole?: Console) {
this.name = name; this.name = name;
@ -158,10 +160,25 @@ export default class Logger {
(async () => { (async () => {
let sourceMaps = await getStaticSourceMaps(); let sourceMaps = await getStaticSourceMaps();
log.sourceMaps = sourceMaps; log.sourceMaps = sourceMaps;
log.onGetSourceMaps();
})(); })();
return log; return log;
} }
private onGetSourceMaps() {
this.hasSourceMaps = true;
for (let sourceMapWaiter of this.sourceMapsWaiters) {
sourceMapWaiter();
}
}
public async ensureSourceMaps(): Promise<void> {
if (this.hasSourceMaps) return;
return new Promise((resolve) => {
this.sourceMapsWaiters.push(resolve);
});
}
/** Logs a message and potentially corresponding data */ /** Logs a message and potentially corresponding data */
private log(level: LoggerLevel, message: string | null, data?: Error | any): void { private log(level: LoggerLevel, message: string | null, data?: Error | any): void {
let frames: StackTrace.Frame[] = StackTrace.parse(new Error()); let frames: StackTrace.Frame[] = StackTrace.parse(new Error());

View File

@ -1,3 +1,6 @@
Moved to https://cowfield.atlassian.net
------ Minimum Viable Product ------ ------ Minimum Viable Product ------
Clean up TypeScript type names Clean up TypeScript type names
@ -6,6 +9,8 @@ Clean up HTML / CSS
Get rid of all static classes that store state Get rid of all static classes that store state
There's a bug with 'undefined' when
Unit tests would be pretty nice for the cache stuff Unit tests would be pretty nice for the cache stuff
Get rid of the duplicate fetch metadata query at startup (fetch-server) Get rid of the duplicate fetch metadata query at startup (fetch-server)