import * as _ from 'lodash'; import {Message as MessageImpl} from '../chat/common'; import core from '../chat/core'; import {Conversation, Logs as Logging, Settings} from '../chat/interfaces'; import l from '../chat/localize'; declare global { const NativeFile: { read(name: string): Promise write(name: string, data: string): Promise listDirectories(name: string): Promise listFiles(name: string): Promise getSize(name: string): Promise ensureDirectory(name: string): Promise }; type NativeMessage = {time: number, type: number, sender: string, text: string}; const NativeLogs: { init(character: string): Promise getCharacters(): Promise> loadIndex(character: string): Promise logMessage(key: string, conversation: string, time: number, type: Conversation.Message.Type, sender: string, message: string): Promise; getBacklog(key: string): Promise>; getLogs(character: string, key: string, date: number): Promise> repair(character: string): Promise }; } const dayMs = 86400000; export const appVersion = (<{version: string}>require('./package.json')).version; //tslint:disable-line:no-require-imports export class GeneralSettings { account = ''; password = ''; host = 'wss://chat.f-list.net/chat2'; theme = 'default'; version = appVersion; } type Index = {[key: string]: {name: string, dates: number[]} | undefined}; export class Logs implements Logging { canZip = false; private index: Index = {}; private loadedIndex?: Index; private loadedCharacter?: string; attemptedFix = false; constructor() { core.connection.onEvent('connecting', async() => { this.attemptedFix = false; try { this.index = await NativeLogs.init(core.connection.character); } catch { await this.fixLogs(core.connection.character); } }); } async logMessage(conversation: Conversation, message: Conversation.Message): Promise { const time = message.time.getTime(); const date = Math.floor(time / dayMs); let index = this.index[conversation.key]; if(index === undefined) index = this.index[conversation.key] = {name: conversation.name, dates: []}; if(index.dates[index.dates.length - 1] !== date) index.dates.push(date); return NativeLogs.logMessage(conversation.key, conversation.name, time / 1000, message.type, message.type === Conversation.Message.Type.Event ? '' : message.sender.name, message.text); } async getBacklog(conversation: Conversation): Promise> { try { return (await NativeLogs.getBacklog(conversation.key)) .map((x) => new MessageImpl(x.type, core.characters.get(x.sender), x.text, new Date(x.time * 1000))); } catch { await this.fixLogs(this.loadedCharacter!); return []; } } private async getIndex(name: string): Promise { if(this.loadedCharacter === name) return this.loadedIndex!; this.loadedCharacter = name; try { return this.loadedIndex = name === core.connection.character ? this.index : await NativeLogs.loadIndex(name); } catch { await this.fixLogs(name); return {}; } } async getLogs(character: string, key: string, date: Date): Promise> { try { await NativeLogs.loadIndex(character); return (await NativeLogs.getLogs(character, key, Math.floor(date.getTime() / dayMs - date.getTimezoneOffset() / 1440))) .map((x) => new MessageImpl(x.type, core.characters.get(x.sender), x.text, new Date(x.time * 1000))); } catch { await this.fixLogs(character); return []; } } async getLogDates(character: string, key: string): Promise> { const entry = (await this.getIndex(character))[key]; if(entry === undefined) return []; return entry.dates.map((x) => { const date = new Date(x * dayMs); return new Date(date.getTime() + date.getTimezoneOffset() * 60000); }); } async getConversations(character: string): Promise> { const index = await this.getIndex(character); const conversations: {key: string, name: string}[] = []; for(const key in index) conversations.push({key, name: index[key]!.name}); return conversations; } async getAvailableCharacters(): Promise> { return NativeLogs.getCharacters(); } async fixLogs(character: string): Promise { if(this.attemptedFix) return alert(l('logs.corruption.mobile.error')); this.attemptedFix = true; alert(l('logs.corruption.mobile')); try { await NativeLogs.repair(character); this.index = await NativeLogs.init(core.connection.character); alert(l('logs.corruption.mobile.success')); } catch { alert(l('logs.corruption.mobile.error')); } } } export async function getGeneralSettings(): Promise { const file = await NativeFile.read('!settings'); if(file === undefined) return undefined; const settings = _.merge(new GeneralSettings(), JSON.parse(file)); if(settings.host === 'wss://chat.f-list.net:9799') settings.host = 'wss://chat.f-list.net/chat2'; return settings; } export async function setGeneralSettings(value: GeneralSettings): Promise { return NativeFile.write('!settings', JSON.stringify(value)); } export class SettingsStore implements Settings.Store { async get(key: K, character: string = core.connection.character): Promise { const file = await NativeFile.read(`${character}/${key}`); if(file === undefined) return undefined; return JSON.parse(file); } async set(key: K, value: Settings.Keys[K]): Promise { return NativeFile.write(`${core.connection.character}/${key}`, JSON.stringify(value)); } async getAvailableCharacters(): Promise { return NativeFile.listDirectories('/'); } }