291 lines
12 KiB
TypeScript
291 lines
12 KiB
TypeScript
import * as electronRemote from '@electron/remote';
|
|
const electronConsole = electronRemote.getGlobal('console') as Console;
|
|
import Logger from '../../logger/logger';
|
|
const LOG = Logger.create(__filename, electronConsole);
|
|
|
|
LOG.silly('script begins');
|
|
|
|
import Controller from './controller';
|
|
|
|
import DBCache from './db-cache';
|
|
import Globals from './globals';
|
|
|
|
import UI from './ui';
|
|
import Actions from './actions';
|
|
import { CacheServerData, Channel, ConnectionInfo, Member, Message, ServerMetaData } from './data-types';
|
|
import ClientController from './client-controller';
|
|
import Q from './q-module';
|
|
import bindWindowButtonEvents from './elements/events-window-buttons';
|
|
import bindTextInputEvents from './elements/events-text-input';
|
|
import bindInfiniteScrollEvents from './elements/events-infinite-scroll';
|
|
import bindConnectionEvents from './elements/events-connection';
|
|
import bindAddServerTitleEvents from './elements/events-server-title';
|
|
import bindAddServerEvents from './elements/events-add-server';
|
|
|
|
LOG.silly('modules loaded');
|
|
|
|
if (Globals.MESSAGES_PER_REQUEST >= Globals.MAX_CURRENT_MESSAGES) throw new Error('messages per request must be less than max current messages');
|
|
|
|
window.addEventListener('unhandledrejection', (e) => {
|
|
LOG.error('Unhandled Promise Rejection', e.reason);
|
|
});
|
|
|
|
window.addEventListener('error', (e) => {
|
|
LOG.error('Uncaught Error', e.error);
|
|
});
|
|
|
|
window.addEventListener('DOMContentLoaded', () => {
|
|
document.body.classList.remove('preload');
|
|
|
|
(async () => {
|
|
await DBCache.connect();
|
|
await DBCache.init();
|
|
|
|
LOG.silly('cache initialized');
|
|
|
|
const controller = new Controller();
|
|
await controller.init();
|
|
|
|
LOG.silly('controller initialized');
|
|
|
|
const q = new Q(document);
|
|
const ui = new UI(document, q);
|
|
const actions = new Actions(ui);
|
|
|
|
LOG.silly('action classes initialized');
|
|
|
|
bindWindowButtonEvents(q);
|
|
bindTextInputEvents(document, q, ui);
|
|
bindInfiniteScrollEvents(q, ui, actions);
|
|
bindConnectionEvents(document, q, ui);
|
|
bindAddServerTitleEvents(document, q, ui);
|
|
bindAddServerEvents(document, q, ui, actions, controller);
|
|
|
|
LOG.silly('events bound');
|
|
|
|
// Add server icons
|
|
await ui.setServers(actions, controller, controller.servers);
|
|
|
|
if (controller.servers.length > 0) {
|
|
// Click on the first server in the list
|
|
q.$('#server-list .server').click();
|
|
}
|
|
|
|
// Receive Current Channel Messages
|
|
controller.on('new-message', async (server: ClientController, message: Message) => {
|
|
if (ui.activeServer === null || ui.activeServer.id !== server.id) return;
|
|
if (ui.activeChannel === null || ui.activeChannel.id !== message.channel.id) return;
|
|
if (ui.messagesAtBottom) {
|
|
// add the message to the bottom of the message feed
|
|
await ui.addMessagesAfter(server, message.channel, [ message ], null);
|
|
ui.jumpMessagesToBottom();
|
|
} else if (message.member.id == server.memberId) {
|
|
// this set of messages will include the new messageserverId
|
|
LOG.debug('not at bottom, jumping down since message was sent by the current user');
|
|
await actions.fetchAndUpdateMessagesRecent(q, server, message.channel);
|
|
}
|
|
});
|
|
|
|
controller.on('verified', async (server: ClientController) => {
|
|
(async () => { // update connection info
|
|
await actions.fetchAndUpdateConnection(server);
|
|
})();
|
|
(async () => { // refresh members cache
|
|
if (server.members) {
|
|
await server.fetchMembers();
|
|
} else {
|
|
await actions.fetchAndUpdateMembers(q, server);
|
|
}
|
|
})();
|
|
(async () => { // refresh channels cache
|
|
if (server.channels) {
|
|
await server.fetchChannels();
|
|
} else {
|
|
await actions.fetchAndUpdateChannels(q, server);
|
|
}
|
|
})();
|
|
(async () => { // refresh current channel messages
|
|
if (ui.activeChannel === null) return;
|
|
if (ui.messagePairs.size == 0) {
|
|
// fetch messages again since there are no messages yet
|
|
await actions.fetchAndUpdateMessagesRecent(q, server, ui.activeChannel);
|
|
} else {
|
|
// Just update the infinite scroll. NOTE: this will not remove deleted messages
|
|
ui.messagesAtTop = false;
|
|
ui.messagesAtBottom = false;
|
|
(q.$('#channel-feed-content-wrapper') as any).updateInfiniteScroll();
|
|
}
|
|
})();
|
|
});
|
|
|
|
controller.on('disconnected', (server: ClientController) => {
|
|
(async () => {
|
|
await actions.fetchAndUpdateConnection(server);
|
|
})();
|
|
(async () => {
|
|
await actions.fetchAndUpdateMembers(q, server);
|
|
})();
|
|
});
|
|
|
|
controller.on('update-server', async (server: ClientController, serverData: ServerMetaData | CacheServerData) => {
|
|
LOG.debug(`s#${server.id} metadata updated`)
|
|
await ui.updateServerName(server, serverData.name);
|
|
|
|
// Not using withPotentialError since keeping the old icon is a fine fallback
|
|
if (serverData.iconResourceId) {
|
|
try {
|
|
let iconBuff = await server.fetchResource(serverData.iconResourceId);
|
|
await ui.updateServerIcon(server, iconBuff);
|
|
} catch (e) {
|
|
LOG.error('Error fetching new server icon', e);
|
|
// Keep the old server icon, just log an error.
|
|
// Should go through another try after a restart
|
|
}
|
|
}
|
|
});
|
|
|
|
controller.on('deleted-members', async (server: ClientController, members: Member[]) => {
|
|
LOG.debug(members.length + ' deleted members');
|
|
await ui.deleteMembers(server, members);
|
|
});
|
|
|
|
controller.on('updated-members', async (server: ClientController, data: { oldDataPoint: Member, newDataPoint: Member }[]) => {
|
|
LOG.debug(data.length + ' updated members s#' + server.id);
|
|
await ui.updateMembers(server, data);
|
|
if (
|
|
ui.activeConnection !== null &&
|
|
data.find((c: any) => c.newDataPoint.id === (ui.activeConnection as ConnectionInfo).id)
|
|
) {
|
|
await actions.fetchAndUpdateConnection(server);
|
|
}
|
|
});
|
|
|
|
controller.on('added-members', async (server: ClientController, members: Member[]) => {
|
|
LOG.debug(members.length + ' added members');
|
|
await ui.addMembers(server, members);
|
|
});
|
|
|
|
controller.on('deleted-channels', async (server: ClientController, channels: Channel[]) => {
|
|
LOG.debug(channels.length + ' deleted channels');
|
|
await ui.deleteChannels(server, channels);
|
|
});
|
|
|
|
controller.on('updated-channels', async (server: ClientController, data: { oldDataPoint: Channel, newDataPoint: Channel }[]) => {
|
|
LOG.debug(data.length + ' updated channels');
|
|
await ui.updateChannels(actions, server, data);
|
|
});
|
|
|
|
controller.on('added-channels', async (server: ClientController, channels: Channel[]) => {
|
|
LOG.debug(channels.length + ' added channels');
|
|
await ui.addChannels(actions, server, channels);
|
|
});
|
|
|
|
controller.on('deleted-messages', async (server: ClientController, channel: Channel, messages: Message[]) => {
|
|
LOG.debug(messages.length + ' deleted messages');
|
|
//LOG.debug('deleted messages:', { messages: deletedMessages.map(message => message.text) });
|
|
// messages were deleted but the cache still had them
|
|
await ui.deleteMessages(server, channel, messages);
|
|
});
|
|
|
|
controller.on('updated-messages', async (server: ClientController, channel: Channel, data: { oldDataPoint: Message, newDataPoint: Message }[]) => {
|
|
LOG.debug(data.length + ' updated messages');
|
|
// messages were updated on the server-side
|
|
await ui.updateMessages(server, channel, data);
|
|
});
|
|
|
|
controller.on('added-messages', async (server: ClientController, channel: Channel, addedAfter: Map<string, Message>, addedBefore: Map<string, Message>) => {
|
|
LOG.debug(addedAfter.size + ' added messages'); // addedBefore.size should equal addedAfter.size
|
|
//LOG.debug('added messages', { messages: Array.from(addedAfter.values()).map(message => message.text) });
|
|
// messages were added in a place that the cache did not have them
|
|
|
|
if (!ui.isMessagePairsServer(server)) return; // these messages are not from the ones in the feed
|
|
if (!ui.isMessagePairsChannel(channel)) return; // these messages are not from the ones in the feed
|
|
|
|
let currentMessagesSorted = Array.from(ui.messagePairs.values()).sort((a, b) => {
|
|
return a.message.sent.getTime() - b.message.sent.getTime();
|
|
});
|
|
|
|
// length could be 0 if all previous messages were 'deleted'
|
|
if (currentMessagesSorted.length == 0) {
|
|
// Simply set the text channel messages rather than calculating where to add them
|
|
ui.setMessages(server, channel, Array.from(addedAfter.values()), { atTop: false, atBottom: true });
|
|
return;
|
|
}
|
|
|
|
let firstMessage = currentMessagesSorted[0].message;
|
|
let lastMessage = currentMessagesSorted[currentMessagesSorted.length - 1].message;
|
|
|
|
// messages that were updated (currently extraneous)
|
|
// Note: this should never happen since these should be handled by updated-messages
|
|
// Note: this may be used to replace dummy pre-sent messages
|
|
let toUpdate: { newDataPoint: Message, oldDataPoint: Message }[] = [];
|
|
for (let addedMessage of addedBefore.values()) {
|
|
if (ui.messagePairs.has(addedMessage.id)) {
|
|
toUpdate.push({
|
|
newDataPoint: addedMessage,
|
|
oldDataPoint: (ui.messagePairs.get(addedMessage.id) as { message: Message, element: HTMLElement }).message
|
|
});
|
|
}
|
|
}
|
|
if (toUpdate.length > 0) {
|
|
LOG.warn('updating messages in added-messages... this was intended to be extraneous...', { toUpdate: toUpdate, toUpdateLength: toUpdate.length });
|
|
await ui.updateMessages(server, channel, toUpdate);
|
|
}
|
|
|
|
// messages before the first message
|
|
let toAddBefore: Message[] = [];
|
|
let nextFirstMessage = firstMessage;
|
|
while (addedBefore.has(nextFirstMessage.id)) {
|
|
nextFirstMessage = addedBefore.get(nextFirstMessage.id) as Message;
|
|
toAddBefore.unshift(nextFirstMessage);
|
|
}
|
|
if (toAddBefore.length > 0) {
|
|
LOG.debug('adding ' + toAddBefore.length + ' before');
|
|
await ui.addMessagesBefore(server, channel, toAddBefore, firstMessage);
|
|
}
|
|
|
|
// messages after the last message
|
|
let toAddAfter: Message[] = [];
|
|
let nextLastMessage = lastMessage;
|
|
while (addedAfter.has(nextLastMessage.id)) {
|
|
nextLastMessage = addedAfter.get(nextLastMessage.id) as Message;
|
|
toAddAfter.push(nextLastMessage);
|
|
}
|
|
if (toAddAfter.length > 0) {
|
|
LOG.debug('adding ' + toAddAfter.length + ' after');
|
|
await ui.addMessagesAfter(server, channel, toAddAfter, lastMessage);
|
|
}
|
|
|
|
// messages added between messages already in the feed
|
|
let toAddBetween: { messageTop: HTMLElement, messageBottom: HTMLElement, betweenMessages: Message[] }[] = [];
|
|
for (let i = 1; i < currentMessagesSorted.length; ++i) {
|
|
let messageTop = currentMessagesSorted[i - 1].message;
|
|
let messageBottom = currentMessagesSorted[i].message;
|
|
let betweenMessages: Message[] = [];
|
|
let followMessage = messageTop;
|
|
// this should never be null as long as there is an addedAfter (will throw error if this is not the case)
|
|
let lastFollowMessage = addedBefore.get(messageBottom.id) as Message;
|
|
while (addedAfter.has(followMessage.id) && followMessage.id != lastFollowMessage.id) {
|
|
followMessage = addedAfter.get(followMessage.id) as Message;
|
|
betweenMessages.push(followMessage);
|
|
}
|
|
if (betweenMessages.length > 0) {
|
|
toAddBetween.push({
|
|
messageTop: (ui.messagePairs.get(messageTop.id) as { message: Message, element: HTMLElement }).element,
|
|
messageBottom: (ui.messagePairs.get(messageBottom.id) as { message: Message, element: HTMLElement }).element,
|
|
betweenMessages: betweenMessages
|
|
});
|
|
}
|
|
}
|
|
// add messages in between
|
|
if (toAddBetween.length > 0) {
|
|
LOG.debug('adding ' + toAddBetween.length + ' between sets');
|
|
}
|
|
for (let messageSet of toAddBetween) {
|
|
await ui.addMessagesBetween(server, channel, messageSet.betweenMessages, messageSet.messageTop, messageSet.messageBottom);
|
|
}
|
|
});
|
|
})();
|
|
});
|
|
|