From 6ec613c2c240dc13bb189d6eeecb9a6a53790418 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 20 May 2020 19:56:54 +0100 Subject: [PATCH] Convert various things to Typescript Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/@types/global.d.ts | 7 ++ ...ectronPlatform.js => ElectronPlatform.tsx} | 118 ++++++++++-------- ...rBasePlatform.js => VectorBasePlatform.ts} | 58 +++------ .../{WebPlatform.js => WebPlatform.ts} | 33 ++--- 4 files changed, 104 insertions(+), 112 deletions(-) rename src/vector/platform/{ElectronPlatform.js => ElectronPlatform.tsx} (83%) rename src/vector/platform/{VectorBasePlatform.js => VectorBasePlatform.ts} (67%) rename src/vector/platform/{WebPlatform.js => WebPlatform.ts} (90%) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 4cb74963..e2bfafe7 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -32,3 +32,10 @@ declare global { InstallTrigger: any; } } + +// add method which is missing from the node typing +declare module "url" { + interface Url { + format(): string; + } +} diff --git a/src/vector/platform/ElectronPlatform.js b/src/vector/platform/ElectronPlatform.tsx similarity index 83% rename from src/vector/platform/ElectronPlatform.js rename to src/vector/platform/ElectronPlatform.tsx index 8bc731c1..af27e2ee 100644 --- a/src/vector/platform/ElectronPlatform.js +++ b/src/vector/platform/ElectronPlatform.tsx @@ -1,5 +1,3 @@ -// @flow - /* Copyright 2016 Aviral Dasgupta Copyright 2016 OpenMarket Ltd @@ -21,11 +19,20 @@ limitations under the License. */ import VectorBasePlatform, {updateCheckStatusEnum} from './VectorBasePlatform'; -import BaseEventIndexManager from 'matrix-react-sdk/src/indexing/BaseEventIndexManager'; +import BaseEventIndexManager, { + MatrixEvent, + MatrixProfile, + SearchConfig, + SearchResult, + HistoricEvent, + CrawlerCheckpoint, + EventAndProfile, +} from 'matrix-react-sdk/src/indexing/BaseEventIndexManager'; import dis from 'matrix-react-sdk/src/dispatcher/dispatcher'; import { _t, _td } from 'matrix-react-sdk/src/languageHandler'; import * as rageshake from 'matrix-react-sdk/src/rageshake/rageshake'; -import {MatrixClient} from "matrix-js-sdk"; +import {MatrixClient} from "matrix-js-sdk/src/client"; +import {Room} from "matrix-js-sdk/src/models/room"; import Modal from "matrix-react-sdk/src/Modal"; import InfoDialog from "matrix-react-sdk/src/components/views/dialogs/InfoDialog"; import Spinner from "matrix-react-sdk/src/components/views/elements/Spinner"; @@ -34,6 +41,7 @@ import {Key} from "matrix-react-sdk/src/Keyboard"; import React from "react"; import {randomString} from "matrix-js-sdk/src/randomstring"; import {Action} from "matrix-react-sdk/src/dispatcher/actions"; +import { ActionPayload } from "matrix-react-sdk/src/dispatcher/payloads"; const ipcRenderer = window.ipcRenderer; const isMac = navigator.platform.toUpperCase().includes('MAC'); @@ -57,7 +65,7 @@ function platformFriendlyName(): string { } } -function _onAction(payload: Object) { +function _onAction(payload: ActionPayload) { // Whitelist payload actions, no point sending most across if (['call_state'].includes(payload.action)) { ipcRenderer.send('app_onAction', payload); @@ -77,53 +85,60 @@ function getUpdateCheckStatus(status) { } } +interface IPCPayload { + id?: number; + error?: string; + reply?: any; +} + class SeshatIndexManager extends BaseEventIndexManager { + private pendingIpcCalls: Record = {}; + private nextIpcCallId: number = 0; + constructor() { super(); - this._pendingIpcCalls = {}; - this._nextIpcCallId = 0; - ipcRenderer.on('seshatReply', this._onIpcReply.bind(this)); + ipcRenderer.on('seshatReply', this._onIpcReply); } - async _ipcCall(name: string, ...args: []): Promise<{}> { + async _ipcCall(name: string, ...args: any[]): Promise { // TODO this should be moved into the preload.js file. - const ipcCallId = ++this._nextIpcCallId; + const ipcCallId = ++this.nextIpcCallId; return new Promise((resolve, reject) => { - this._pendingIpcCalls[ipcCallId] = {resolve, reject}; + this.pendingIpcCalls[ipcCallId] = {resolve, reject}; window.ipcRenderer.send('seshat', {id: ipcCallId, name, args}); }); } - _onIpcReply(ev: {}, payload: {}) { + _onIpcReply = (ev: {}, payload: IPCPayload) => { if (payload.id === undefined) { console.warn("Ignoring IPC reply with no ID"); return; } - if (this._pendingIpcCalls[payload.id] === undefined) { + if (this.pendingIpcCalls[payload.id] === undefined) { console.warn("Unknown IPC payload ID: " + payload.id); return; } - const callbacks = this._pendingIpcCalls[payload.id]; - delete this._pendingIpcCalls[payload.id]; + const callbacks = this.pendingIpcCalls[payload.id]; + delete this.pendingIpcCalls[payload.id]; if (payload.error) { callbacks.reject(payload.error); } else { callbacks.resolve(payload.reply); } - } + }; async supportsEventIndexing(): Promise { return this._ipcCall('supportsEventIndexing'); } - async initEventIndex(): Promise<> { + async initEventIndex(): Promise { return this._ipcCall('initEventIndex'); } - async addEventToIndex(ev: MatrixEvent, profile: MatrixProfile): Promise<> { + async addEventToIndex(ev: MatrixEvent, profile: MatrixProfile): Promise { return this._ipcCall('addEventToIndex', ev, profile); } @@ -135,7 +150,7 @@ class SeshatIndexManager extends BaseEventIndexManager { return this._ipcCall('isEventIndexEmpty'); } - async commitLiveEvents(): Promise<> { + async commitLiveEvents(): Promise { return this._ipcCall('commitLiveEvents'); } @@ -147,15 +162,15 @@ class SeshatIndexManager extends BaseEventIndexManager { events: [HistoricEvent], checkpoint: CrawlerCheckpoint | null, oldCheckpoint: CrawlerCheckpoint | null, - ): Promise<> { + ): Promise { return this._ipcCall('addHistoricEvents', events, checkpoint, oldCheckpoint); } - async addCrawlerCheckpoint(checkpoint: CrawlerCheckpoint): Promise<> { + async addCrawlerCheckpoint(checkpoint: CrawlerCheckpoint): Promise { return this._ipcCall('addCrawlerCheckpoint', checkpoint); } - async removeCrawlerCheckpoint(checkpoint: CrawlerCheckpoint): Promise<> { + async removeCrawlerCheckpoint(checkpoint: CrawlerCheckpoint): Promise { return this._ipcCall('removeCrawlerCheckpoint', checkpoint); } @@ -167,27 +182,29 @@ class SeshatIndexManager extends BaseEventIndexManager { return this._ipcCall('loadCheckpoints'); } - async closeEventIndex(): Promise<> { + async closeEventIndex(): Promise { return this._ipcCall('closeEventIndex'); } - async getStats(): Promise<> { + async getStats(): Promise { return this._ipcCall('getStats'); } - async deleteEventIndex(): Promise<> { + async deleteEventIndex(): Promise { return this._ipcCall('deleteEventIndex'); } } export default class ElectronPlatform extends VectorBasePlatform { + private eventIndexManager: BaseEventIndexManager = new SeshatIndexManager(); + private pendingIpcCalls: Record = {}; + private nextIpcCallId: number = 0; + // this is the opaque token we pass to the HS which when we get it in our callback we can resolve to a profile + private ssoID: string = randomString(32); + constructor() { super(); - this._pendingIpcCalls = {}; - this._nextIpcCallId = 0; - this.eventIndexManager = new SeshatIndexManager(); - dis.register(_onAction); /* IPC Call `check_updates` returns: @@ -217,9 +234,6 @@ export default class ElectronPlatform extends VectorBasePlatform { dis.fire(Action.ViewUserSettings); }); - this.startUpdateCheck = this.startUpdateCheck.bind(this); - this.stopUpdateCheck = this.stopUpdateCheck.bind(this); - // register OS-specific shortcuts if (isMac) { registerShortcut(Categories.NAVIGATION, { @@ -253,8 +267,6 @@ export default class ElectronPlatform extends VectorBasePlatform { }); } - // this is the opaque token we pass to the HS which when we get it in our callback we can resolve to a profile - this.ssoID = randomString(32); this._ipcCall("startSSOFlow", this.ssoID); } @@ -290,7 +302,7 @@ export default class ElectronPlatform extends VectorBasePlatform { return true; } - displayNotification(title: string, msg: string, avatarUrl: string, room: Object): Notification { + displayNotification(title: string, msg: string, avatarUrl: string, room: Room): Notification { // GNOME notification spec parses HTML tags for styling... // Electron Docs state all supported linux notification systems follow this markup spec // https://github.com/electron/electron/blob/master/docs/tutorial/desktop-environment-integration.md#linux @@ -307,14 +319,14 @@ export default class ElectronPlatform extends VectorBasePlatform { silent: true, // we play our own sounds }; if (avatarUrl) notifBody['icon'] = avatarUrl; - const notification = new global.Notification(title, notifBody); + const notification = new window.Notification(title, notifBody); notification.onclick = () => { dis.dispatch({ action: 'view_room', room_id: room.roomId, }); - global.focus(); + window.focus(); this._ipcCall('focusWindow'); }; @@ -337,11 +349,11 @@ export default class ElectronPlatform extends VectorBasePlatform { return true; } - async getAutoLaunchEnabled(): boolean { + async getAutoLaunchEnabled(): Promise { return this._ipcCall('getAutoLaunchEnabled'); } - async setAutoLaunchEnabled(enabled: boolean): void { + async setAutoLaunchEnabled(enabled: boolean): Promise { return this._ipcCall('setAutoLaunchEnabled', enabled); } @@ -350,11 +362,11 @@ export default class ElectronPlatform extends VectorBasePlatform { return !isMac; } - async getAutoHideMenuBarEnabled(): boolean { + async getAutoHideMenuBarEnabled(): Promise { return this._ipcCall('getAutoHideMenuBarEnabled'); } - async setAutoHideMenuBarEnabled(enabled: boolean): void { + async setAutoHideMenuBarEnabled(enabled: boolean): Promise { return this._ipcCall('setAutoHideMenuBarEnabled', enabled); } @@ -363,25 +375,25 @@ export default class ElectronPlatform extends VectorBasePlatform { return !isMac; } - async getMinimizeToTrayEnabled(): boolean { + async getMinimizeToTrayEnabled(): Promise { return this._ipcCall('getMinimizeToTrayEnabled'); } - async setMinimizeToTrayEnabled(enabled: boolean): void { + async setMinimizeToTrayEnabled(enabled: boolean): Promise { return this._ipcCall('setMinimizeToTrayEnabled', enabled); } - async canSelfUpdate(): boolean { + async canSelfUpdate(): Promise { const feedUrl = await this._ipcCall('getUpdateFeedUrl'); return Boolean(feedUrl); } - startUpdateCheck() { + startUpdateCheck = () => { if (this.showUpdateCheck) return; super.startUpdateCheck(); ipcRenderer.send('check_updates'); - } + }; installUpdate() { // IPC to the main process to install the update, since quitAndInstall @@ -394,7 +406,7 @@ export default class ElectronPlatform extends VectorBasePlatform { return _t('Riot Desktop (%(platformName)s)', { platformName: platformFriendlyName() }); } - screenCaptureErrorString(): ?string { + screenCaptureErrorString(): string | null { return null; } @@ -409,10 +421,10 @@ export default class ElectronPlatform extends VectorBasePlatform { window.location.reload(false); } - async _ipcCall(name, ...args) { - const ipcCallId = ++this._nextIpcCallId; + async _ipcCall(name: string, ...args: any[]): Promise { + const ipcCallId = ++this.nextIpcCallId; return new Promise((resolve, reject) => { - this._pendingIpcCalls[ipcCallId] = {resolve, reject}; + this.pendingIpcCalls[ipcCallId] = {resolve, reject}; window.ipcRenderer.send('ipcCall', {id: ipcCallId, name, args}); // Maybe add a timeout to these? Probably not necessary. }); @@ -424,13 +436,13 @@ export default class ElectronPlatform extends VectorBasePlatform { return; } - if (this._pendingIpcCalls[payload.id] === undefined) { + if (this.pendingIpcCalls[payload.id] === undefined) { console.warn("Unknown IPC payload ID: " + payload.id); return; } - const callbacks = this._pendingIpcCalls[payload.id]; - delete this._pendingIpcCalls[payload.id]; + const callbacks = this.pendingIpcCalls[payload.id]; + delete this.pendingIpcCalls[payload.id]; if (payload.error) { callbacks.reject(payload.error); } else { diff --git a/src/vector/platform/VectorBasePlatform.js b/src/vector/platform/VectorBasePlatform.ts similarity index 67% rename from src/vector/platform/VectorBasePlatform.js rename to src/vector/platform/VectorBasePlatform.ts index fad3045c..4e223c04 100644 --- a/src/vector/platform/VectorBasePlatform.js +++ b/src/vector/platform/VectorBasePlatform.ts @@ -1,9 +1,7 @@ -// @flow - /* Copyright 2016 Aviral Dasgupta Copyright 2016 OpenMarket Ltd -Copyright 2018 New Vector Ltd +Copyright 2018, 2020 New Vector Ltd Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,7 +22,7 @@ import { _t } from 'matrix-react-sdk/src/languageHandler'; import dis from 'matrix-react-sdk/src/dispatcher/dispatcher'; import {getVectorConfig} from "../getconfig"; -import Favico from '../../favicon'; +import Favicon from "../../favicon"; export const updateCheckStatusEnum = { CHECKING: 'CHECKING', @@ -38,13 +36,8 @@ export const updateCheckStatusEnum = { * Vector-specific extensions to the BasePlatform template */ export default class VectorBasePlatform extends BasePlatform { - constructor() { - super(); - - this.showUpdateCheck = false; - this.startUpdateCheck = this.startUpdateCheck.bind(this); - this.stopUpdateCheck = this.stopUpdateCheck.bind(this); - } + protected showUpdateCheck: boolean = false; + protected _favicon: Favicon; async getConfig(): Promise<{}> { return getVectorConfig(); @@ -55,40 +48,27 @@ export default class VectorBasePlatform extends BasePlatform { } /** - * Delay creating the `Favico` instance until first use (on the first notification) as - * it uses canvas, which can trigger a permission prompt in Firefox's resist - * fingerprinting mode. + * Delay creating the `Favicon` instance until first use (on the first notification) as + * it uses canvas, which can trigger a permission prompt in Firefox's resist fingerprinting mode. * See https://github.com/vector-im/riot-web/issues/9605. */ get favicon() { if (this._favicon) { return this._favicon; } - // The 'animations' are really low framerate and look terrible. - // Also it re-starts the animation every time you set the badge, - // and we set the state each time, even if the value hasn't changed, - // so we'd need to fix that if enabling the animation. - this._favicon = new Favico({ animation: 'none' }); - return this._favicon; + return this._favicon = new Favicon(); } _updateFavicon() { - try { - // This needs to be in in a try block as it will throw - // if there are more than 100 badge count changes in - // its internal queue - let bgColor = "#d00"; - let notif = this.notificationCount; + let bgColor = "#d00"; + let notif: string | number = this.notificationCount; - if (this.errorDidOccur) { - notif = notif || "×"; - bgColor = "#f00"; - } - - this.favicon.badge(notif, { bgColor }); - } catch (e) { - console.warn(`Failed to set badge count: ${e.message}`); + if (this.errorDidOccur) { + notif = notif || "×"; + bgColor = "#f00"; } + + this.favicon.badge(notif, { bgColor }); } setNotificationCount(count: number) { @@ -112,25 +92,25 @@ export default class VectorBasePlatform extends BasePlatform { /** * Whether we can call checkForUpdate on this platform build */ - async canSelfUpdate(): boolean { + async canSelfUpdate(): Promise { return false; } - startUpdateCheck() { + startUpdateCheck = () => { this.showUpdateCheck = true; dis.dispatch({ action: 'check_updates', value: { status: updateCheckStatusEnum.CHECKING }, }); - } + }; - stopUpdateCheck() { + stopUpdateCheck = () => { this.showUpdateCheck = false; dis.dispatch({ action: 'check_updates', value: false, }); - } + }; getUpdateCheckStatusEnum() { return updateCheckStatusEnum; diff --git a/src/vector/platform/WebPlatform.js b/src/vector/platform/WebPlatform.ts similarity index 90% rename from src/vector/platform/WebPlatform.js rename to src/vector/platform/WebPlatform.ts index 831ca8cc..8c924e49 100644 --- a/src/vector/platform/WebPlatform.js +++ b/src/vector/platform/WebPlatform.ts @@ -1,5 +1,3 @@ -// @flow - /* Copyright 2016 Aviral Dasgupta Copyright 2016 OpenMarket Ltd @@ -22,6 +20,7 @@ import VectorBasePlatform, {updateCheckStatusEnum} from './VectorBasePlatform'; import request from 'browser-request'; import dis from 'matrix-react-sdk/src/dispatcher/dispatcher'; import { _t } from 'matrix-react-sdk/src/languageHandler'; +import {Room} from "matrix-js-sdk/src/models/room"; import url from 'url'; import UAParser from 'ua-parser-js'; @@ -29,13 +28,7 @@ import UAParser from 'ua-parser-js'; const POKE_RATE_MS = 10 * 60 * 1000; // 10 min export default class WebPlatform extends VectorBasePlatform { - constructor() { - super(); - this.runningVersion = null; - - this.startUpdateCheck = this.startUpdateCheck.bind(this); - this.stopUpdateCheck = this.stopUpdateCheck.bind(this); - } + private runningVersion: string = null; getHumanReadableName(): string { return 'Web Platform'; // no translation required: only used for analytics @@ -46,7 +39,7 @@ export default class WebPlatform extends VectorBasePlatform { * notifications, otherwise false. */ supportsNotifications(): boolean { - return Boolean(global.Notification); + return Boolean(window.Notification); } /** @@ -54,7 +47,7 @@ export default class WebPlatform extends VectorBasePlatform { * to display notifications. Otherwise false. */ maySendNotifications(): boolean { - return global.Notification.permission === 'granted'; + return window.Notification.permission === 'granted'; } /** @@ -69,27 +62,27 @@ export default class WebPlatform extends VectorBasePlatform { // promise, but this is only supported in Chrome 46 // and Firefox 47, so adapt the callback API. return new Promise(function(resolve, reject) { - global.Notification.requestPermission((result) => { + window.Notification.requestPermission((result) => { resolve(result); }); }); } - displayNotification(title: string, msg: string, avatarUrl: string, room: Object) { + displayNotification(title: string, msg: string, avatarUrl: string, room: Room) { const notifBody = { body: msg, tag: "vector", silent: true, // we play our own sounds }; if (avatarUrl) notifBody['icon'] = avatarUrl; - const notification = new global.Notification(title, notifBody); + const notification = new window.Notification(title, notifBody); notification.onclick = function() { dis.dispatch({ action: 'view_room', room_id: room.roomId, }); - global.focus(); + window.focus(); notification.close(); }; } @@ -134,7 +127,7 @@ export default class WebPlatform extends VectorBasePlatform { setInterval(this.pollForUpdate.bind(this), POKE_RATE_MS); } - async canSelfUpdate(): boolean { + async canSelfUpdate(): Promise { return true; } @@ -161,7 +154,7 @@ export default class WebPlatform extends VectorBasePlatform { }); } - startUpdateCheck() { + startUpdateCheck = () => { if (this.showUpdateCheck) return; super.startUpdateCheck(); this.pollForUpdate().then((updateState) => { @@ -172,7 +165,7 @@ export default class WebPlatform extends VectorBasePlatform { value: updateState, }); }); - } + }; installUpdate() { window.location.reload(true); @@ -204,9 +197,9 @@ export default class WebPlatform extends VectorBasePlatform { }); } - screenCaptureErrorString(): ?string { + screenCaptureErrorString(): string | null { // it won't work at all if you're not on HTTPS so whine whine whine - if (!global.window || global.window.location.protocol !== "https:") { + if (window.location.protocol !== "https:") { return _t("You need to be using HTTPS to place a screen-sharing call."); } return null;