263 lines
10 KiB
TypeScript
263 lines
10 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('preload begins');
|
|
|
|
import * as path from 'path';
|
|
import * as fs from 'fs/promises';
|
|
|
|
import GuildsManager from './guilds-manager';
|
|
|
|
import Globals from './globals';
|
|
|
|
import UI from './ui';
|
|
import Actions from './actions';
|
|
import { Changes, Channel, ConnectionInfo, GuildMetadata, Member, Message, Resource, Token } from './data-types';
|
|
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 bindAddGuildTitleEvents from './elements/events-guild-title';
|
|
import bindAddGuildEvents from './elements/events-add-guild';
|
|
import PersonalDB from './personal-db';
|
|
import MessageRAMCache from './message-ram-cache';
|
|
import ResourceRAMCache from './resource-ram-cache';
|
|
import CombinedGuild from './guild-combined';
|
|
import { AutoVerifierChangesType } from './auto-verifier';
|
|
|
|
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 () => {
|
|
// Wait for the log to load the typescript source maps so that
|
|
// logs will include typescript files+line numbers instead of
|
|
// compiled javascript ones.
|
|
await LOG.ensureSourceMaps();
|
|
LOG.silly('web client log source maps loaded');
|
|
|
|
// make sure the personaldb directory exists
|
|
await fs.mkdir(path.dirname(Globals.PERSONALDB_FILE), { recursive: true });
|
|
const personalDB = await PersonalDB.create(Globals.PERSONALDB_FILE);
|
|
await personalDB.init();
|
|
|
|
LOG.silly('personal db initialized');
|
|
|
|
let messageRAMCache = new MessageRAMCache();
|
|
let resourceRAMCache = new ResourceRAMCache();
|
|
|
|
LOG.silly('ram caches initialized');
|
|
|
|
const guildsManager = new GuildsManager(messageRAMCache, resourceRAMCache, personalDB);
|
|
await guildsManager.init();
|
|
|
|
LOG.silly('controller initialized');
|
|
|
|
const q = new Q(document);
|
|
const ui = new UI(document, q);
|
|
|
|
LOG.silly('action classes initialized');
|
|
|
|
bindWindowButtonEvents(q);
|
|
bindTextInputEvents(document, q, ui);
|
|
bindInfiniteScrollEvents(q, ui);
|
|
bindConnectionEvents(document, q, ui);
|
|
bindAddGuildTitleEvents(document, q, ui);
|
|
bindAddGuildEvents(document, q, ui, guildsManager);
|
|
|
|
LOG.silly('events bound');
|
|
|
|
// Add guild icons
|
|
await ui.setGuilds(guildsManager, guildsManager.guilds);
|
|
|
|
if (guildsManager.guilds.length > 0) {
|
|
// Click on the first guild in the list
|
|
q.$('#guild-list .guild').click();
|
|
}
|
|
|
|
// Connection Events
|
|
|
|
guildsManager.on('verified', async (guild: CombinedGuild) => {
|
|
(async () => { // update connection info
|
|
await Actions.fetchAndUpdateConnection(ui, guild);
|
|
})();
|
|
(async () => { // refresh members list
|
|
await Actions.fetchAndUpdateMembers(q, ui, guild);
|
|
})();
|
|
(async () => { // refresh channels list
|
|
await Actions.fetchAndUpdateChannels(q, ui, guild);
|
|
})();
|
|
(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, ui, guild, ui.activeChannel);
|
|
} else {
|
|
// If we already have messages, just update the infinite scroll.
|
|
// NOTE: this will not add/remove new/deleted messages
|
|
ui.messagesAtTop = false;
|
|
ui.messagesAtBottom = false;
|
|
(q.$('#channel-feed-content-wrapper') as any).updateInfiniteScroll();
|
|
}
|
|
})();
|
|
});
|
|
|
|
guildsManager.on('disconnect', (guild: CombinedGuild) => {
|
|
// Update everyone with the 'unknown' status
|
|
(async () => {
|
|
await Actions.fetchAndUpdateConnection(ui, guild);
|
|
})();
|
|
(async () => {
|
|
await Actions.fetchAndUpdateMembers(q, ui, guild);
|
|
})();
|
|
});
|
|
|
|
// Change Events
|
|
|
|
guildsManager.on('update-metadata', async (guild: CombinedGuild, guildMeta: GuildMetadata) => {
|
|
LOG.debug(`g#${guild.id} metadata updated`)
|
|
await ui.updateGuildName(guild, guildMeta.name);
|
|
|
|
// Not using withPotentialError since keeping the old icon is a fine fallback
|
|
if (guildMeta.iconResourceId) {
|
|
try {
|
|
let icon = await guild.fetchResource(guildMeta.iconResourceId);
|
|
await ui.updateGuildIcon(guild, icon.data);
|
|
} catch (e) {
|
|
LOG.error('Error fetching new guild icon', e);
|
|
// Keep the old guild icon, just log an error.
|
|
// Should go through another try after a restart
|
|
}
|
|
}
|
|
});
|
|
|
|
guildsManager.on('remove-members', async (guild: CombinedGuild, members: Member[]) => {
|
|
LOG.debug(members.length + ' removed members');
|
|
await ui.deleteMembers(guild, members);
|
|
});
|
|
|
|
guildsManager.on('update-members', async (guild: CombinedGuild, updatedMembers: Member[]) => {
|
|
LOG.debug(updatedMembers.length + ' updated members g#' + guild.id);
|
|
await ui.updateMembers(guild, updatedMembers);
|
|
if (
|
|
ui.activeConnection !== null &&
|
|
updatedMembers.find(member => member.id === (ui.activeConnection as ConnectionInfo).id)
|
|
) {
|
|
await Actions.fetchAndUpdateConnection(ui, guild);
|
|
}
|
|
});
|
|
|
|
guildsManager.on('new-members', async (guild: CombinedGuild, members: Member[]) => {
|
|
LOG.debug(members.length + ' new members');
|
|
await ui.addMembers(guild, members);
|
|
});
|
|
|
|
guildsManager.on('remove-channels', async (guild: CombinedGuild, channels: Channel[]) => {
|
|
LOG.debug(channels.length + ' removed channels');
|
|
await ui.deleteChannels(guild, channels);
|
|
});
|
|
|
|
guildsManager.on('update-channels', async (guild: CombinedGuild, updatedChannels: Channel[]) => {
|
|
LOG.debug(updatedChannels.length + ' updated channels');
|
|
await ui.updateChannels(guild, updatedChannels);
|
|
});
|
|
|
|
guildsManager.on('new-channels', async (guild: CombinedGuild, channels: Channel[]) => {
|
|
LOG.debug(channels.length + ' added channels');
|
|
await ui.addChannels(guild, channels);
|
|
});
|
|
|
|
guildsManager.on('remove-messages', async (guild: CombinedGuild, messages: Message[]) => {
|
|
LOG.debug(messages.length + ' deleted messages');
|
|
await ui.deleteMessages(guild, messages);
|
|
});
|
|
|
|
guildsManager.on('update-messages', async (guild: CombinedGuild, updatedMessages: Message[]) => {
|
|
LOG.debug(updatedMessages.length + ' updated messages');
|
|
await ui.updateMessages(guild, updatedMessages);
|
|
});
|
|
|
|
guildsManager.on('new-messages', async (guild: CombinedGuild, messages: Message[]) => {
|
|
if (ui.activeGuild === null || ui.activeGuild.id !== guild.id) return;
|
|
for (let message of messages) {
|
|
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.addMessages(guild, [ message ]);
|
|
ui.jumpMessagesToBottom();
|
|
} else if (message.member.id == guild.memberId) {
|
|
// this set of messages will include the new messageguildId
|
|
LOG.debug('not at bottom, jumping down since message was sent by the current user');
|
|
await Actions.fetchAndUpdateMessagesRecent(q, ui, guild, message.channel);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Conflict Events
|
|
|
|
guildsManager.on('conflict-metadata', async (guild: CombinedGuild, changesType: AutoVerifierChangesType, oldGuildMeta: GuildMetadata, newGuildMeta: GuildMetadata) => {
|
|
LOG.debug('metadata conflict', { newGuildMeta: newGuildMeta });
|
|
(async () => { await ui.updateGuildName(guild, newGuildMeta.name); })();
|
|
(async () => {
|
|
let icon = await guild.fetchResource(newGuildMeta.iconResourceId);
|
|
await ui.updateGuildIcon(guild, icon.data);
|
|
})();
|
|
});
|
|
|
|
guildsManager.on('conflict-channels', async (guild: CombinedGuild, changesType: AutoVerifierChangesType, changes: Changes<Channel>) => {
|
|
LOG.debug('channels conflict', { changes });
|
|
if (changes.deleted.length > 0) await ui.deleteChannels(guild, changes.deleted);
|
|
if (changes.added.length > 0) await ui.addChannels(guild, changes.added);
|
|
if (changes.updated.length > 0) await ui.updateChannels(guild, changes.updated.map(pair => pair.newDataPoint));
|
|
});
|
|
|
|
guildsManager.on('conflict-members', async (guild: CombinedGuild, changesType: AutoVerifierChangesType, changes: Changes<Member>) => {
|
|
//LOG.debug('members conflict', { changes });
|
|
if (changes.deleted.length > 0) await ui.deleteMembers(guild, changes.deleted);
|
|
if (changes.added.length > 0) await ui.addMembers(guild, changes.added);
|
|
if (changes.updated.length > 0) {
|
|
(async () => {
|
|
await ui.updateMembers(guild, changes.updated.map(pair => pair.newDataPoint));
|
|
})();
|
|
(async () => {
|
|
LOG.debug('updating conflict members connection...');
|
|
// Likely getting called before the ram is updated
|
|
await Actions.fetchAndUpdateConnection(ui, guild);
|
|
})();
|
|
}
|
|
});
|
|
|
|
guildsManager.on('conflict-messages', async (guild: CombinedGuild, changesType: AutoVerifierChangesType, changes: Changes<Message>) => {
|
|
//LOG.debug('messages conflict', { changes });
|
|
if (changes.deleted.length > 0) await ui.deleteMessages(guild, changes.deleted);
|
|
if (changes.added.length > 0) await ui.addMessages(guild, changes.added);
|
|
if (changes.updated.length > 0) await ui.updateMessages(guild, changes.updated.map(pair => pair.newDataPoint));
|
|
});
|
|
|
|
guildsManager.on('conflict-tokens', async (guild: CombinedGuild, changesType: AutoVerifierChangesType, changes: Changes<Token>) => {
|
|
LOG.debug('tokens conflict', { changes });
|
|
// TODO
|
|
});
|
|
|
|
guildsManager.on('conflict-resource', async (guild: CombinedGuild, changesType: AutoVerifierChangesType, oldResource: Resource, newResource: Resource) => {
|
|
LOG.debug('resource conflict', { oldResource, newResource });
|
|
// TODO (these changes should not happen often if at all)
|
|
});
|
|
})();
|
|
});
|
|
|