Code cleanup, update eslint from webapp, add ts-prune (#1884)
This commit is contained in:
@@ -7,11 +7,7 @@ export const CLOSE_TAB = 'close-tab';
|
||||
export const OPEN_TAB = 'open-tab';
|
||||
export const SET_ACTIVE_VIEW = 'set-active-view';
|
||||
export const UPDATE_LAST_ACTIVE = 'update-last-active';
|
||||
export const MARK_READ = 'mark-read';
|
||||
export const FOCUS_BROWSERVIEW = 'focus-browserview';
|
||||
export const ZOOM = 'zoom';
|
||||
export const UNDO = 'undo';
|
||||
export const REDO = 'redo';
|
||||
export const HISTORY = 'history';
|
||||
|
||||
export const QUIT = 'quit';
|
||||
@@ -34,8 +30,6 @@ export const LOAD_FAILED = 'load_fail';
|
||||
|
||||
export const MAXIMIZE_CHANGE = 'maximized_change';
|
||||
|
||||
export const OPEN_EXTERNAL = 'open_external';
|
||||
|
||||
export const DOUBLE_CLICK_ON_WINDOW = 'double_click';
|
||||
|
||||
export const SHOW_NEW_SERVER_MODAL = 'show_new_server_modal';
|
||||
@@ -83,7 +77,6 @@ export const TOGGLE_LOADING_SCREEN_VISIBILITY = 'toggle-loading-screen-visibilit
|
||||
|
||||
export const SELECT_NEXT_TAB = 'select-next-tab';
|
||||
export const SELECT_PREVIOUS_TAB = 'select-previous-tab';
|
||||
export const ADD_SERVER = 'add-server';
|
||||
export const FOCUS_THREE_DOT_MENU = 'focus-three-dot-menu';
|
||||
|
||||
export const LOADSCREEN_END = 'loadscreen-end';
|
||||
|
@@ -1,11 +1,6 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// channel types for managing permissions
|
||||
export const REQUEST_PERMISSION_CHANNEL = 'request-permission';
|
||||
export const GRANT_PERMISSION_CHANNEL = 'grant-permission';
|
||||
export const DENY_PERMISSION_CHANNEL = 'deny-permission';
|
||||
|
||||
// Permission types that can be requested
|
||||
export const BASIC_AUTH_PERMISSION = 'canBasicAuth';
|
||||
|
||||
|
@@ -44,7 +44,6 @@ export function getDefaultTeamWithTabsFromTeam(team: Team) {
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Might need to move this out
|
||||
export function getServerView(srv: MattermostServer, tab: Tab) {
|
||||
switch (tab.name) {
|
||||
case TAB_MESSAGING:
|
||||
@@ -76,6 +75,5 @@ export function getTabDisplayName(tabType: TabType) {
|
||||
}
|
||||
|
||||
export function canCloseTab(tabType: TabType) {
|
||||
// TODO: maybe rework to make the property belong to the class somehow
|
||||
return tabType !== TAB_MESSAGING;
|
||||
}
|
||||
|
@@ -25,7 +25,7 @@ function createErrorReport(err: Error) {
|
||||
}
|
||||
|
||||
function openDetachedExternal(url: string) {
|
||||
const spawnOption = {detached: true, stdio: 'ignore' as any};
|
||||
const spawnOption = {detached: true, stdio: 'ignore' as const};
|
||||
switch (process.platform) {
|
||||
case 'win32':
|
||||
return spawn('cmd', ['/C', 'start', url], spawnOption);
|
||||
|
@@ -67,7 +67,7 @@ export default class UserActivityMonitor extends EventEmitter {
|
||||
|
||||
this.config = Object.assign({}, this.config, config);
|
||||
|
||||
// TODO: Node typings don't map Timeout to number, but then clearInterval requires a number?
|
||||
// Node typings don't map Timeout to number, but then clearInterval requires a number?
|
||||
this.systemIdleTimeIntervalID = setInterval(() => {
|
||||
try {
|
||||
this.updateIdleTime(electron.powerMonitor.getSystemIdleTime());
|
||||
|
@@ -36,7 +36,7 @@ const emitDropdown = (expired?: Map<string, boolean>, mentions?: Map<string, num
|
||||
status.emitter.emit(UPDATE_DROPDOWN_MENTIONS, expired, mentions, unreads);
|
||||
};
|
||||
|
||||
export const emitStatus = () => {
|
||||
const emitStatus = () => {
|
||||
const expired = anyExpired();
|
||||
const mentions = totalMentions();
|
||||
const unreads = anyUnreads();
|
||||
@@ -65,28 +65,19 @@ export const updateBadge = () => {
|
||||
emitBadge(expired, mentions, unreads);
|
||||
};
|
||||
|
||||
export const getUnreads = (serverName: string) => {
|
||||
const getUnreads = (serverName: string) => {
|
||||
return status.unreads.get(serverName) || false;
|
||||
};
|
||||
|
||||
export const getMentions = (serverName: string) => {
|
||||
const getMentions = (serverName: string) => {
|
||||
return status.mentions.get(serverName) || 0; // this might be undefined as a way to tell that we don't know as it might need to login still.
|
||||
};
|
||||
|
||||
export const getIsExpired = (serverName: string) => {
|
||||
const getIsExpired = (serverName: string) => {
|
||||
return status.expired.get(serverName) || false;
|
||||
};
|
||||
|
||||
export const anyMentions = () => {
|
||||
for (const v of status.mentions.values()) {
|
||||
if (v > 0) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const totalMentions = () => {
|
||||
const totalMentions = () => {
|
||||
let total = 0;
|
||||
for (const v of status.mentions.values()) {
|
||||
total += v;
|
||||
@@ -94,7 +85,7 @@ export const totalMentions = () => {
|
||||
return total;
|
||||
};
|
||||
|
||||
export const anyUnreads = () => {
|
||||
const anyUnreads = () => {
|
||||
for (const v of status.unreads.values()) {
|
||||
if (v) {
|
||||
return v;
|
||||
@@ -103,7 +94,7 @@ export const anyUnreads = () => {
|
||||
return false;
|
||||
};
|
||||
|
||||
export const anyExpired = () => {
|
||||
const anyExpired = () => {
|
||||
for (const v of status.expired.values()) {
|
||||
if (v) {
|
||||
return v;
|
||||
@@ -113,11 +104,9 @@ export const anyExpired = () => {
|
||||
};
|
||||
|
||||
// add any other event emitter methods if needed
|
||||
export const on = (event: string, listener: (...args: any[]) => void) => {
|
||||
status.emitter.on(event, listener);
|
||||
};
|
||||
export const on = status.emitter.on;
|
||||
|
||||
export const setSessionExpired = (serverName: string, expired: boolean) => {
|
||||
const setSessionExpired = (serverName: string, expired: boolean) => {
|
||||
const isExpired = Boolean(expired);
|
||||
const old = status.expired.get(serverName);
|
||||
status.expired.set(serverName, isExpired);
|
||||
|
@@ -1,201 +0,0 @@
|
||||
// Copyright (c) 2015-2016 Yuya Ochiai
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// TODO: This needs to be rebuilt anyways, skipping TS migration for now
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-nocheck
|
||||
|
||||
import path from 'path';
|
||||
|
||||
import {app, BrowserWindow, BrowserWindowConstructorOptions, dialog, ipcMain, IpcMainEvent, shell} from 'electron';
|
||||
|
||||
import log from 'electron-log';
|
||||
import {autoUpdater, CancellationToken} from 'electron-updater';
|
||||
import semver from 'semver';
|
||||
|
||||
// eslint-disable-next-line no-magic-numbers
|
||||
const UPDATER_INTERVAL_IN_MS = 48 * 60 * 60 * 1000; // 48 hours
|
||||
|
||||
autoUpdater.log = log;
|
||||
autoUpdater.log.transports.file.level = 'info';
|
||||
|
||||
let updaterModal = null;
|
||||
|
||||
function createEventListener(win: BrowserWindow, eventName: string) {
|
||||
return (event: IpcMainEvent) => {
|
||||
if (event.sender === win.webContents) {
|
||||
win.emit(eventName);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createUpdaterModal(parentWindow: BrowserWindow, options: {linuxAppIcon: string; notifyOnly: boolean}) {
|
||||
const windowWidth = 480;
|
||||
const windowHeight = 280;
|
||||
const windowOptions: BrowserWindowConstructorOptions = {
|
||||
title: `${app.name} Updater`,
|
||||
parent: parentWindow,
|
||||
modal: true,
|
||||
maximizable: false,
|
||||
show: false,
|
||||
width: windowWidth,
|
||||
height: windowHeight,
|
||||
resizable: false,
|
||||
autoHideMenuBar: true,
|
||||
backgroundColor: '#fff', // prevents blurry text: https://electronjs.org/docs/faq#the-font-looks-blurry-what-is-this-and-what-can-i-do
|
||||
};
|
||||
if (process.platform === 'linux') {
|
||||
windowOptions.icon = options.linuxAppIcon;
|
||||
}
|
||||
|
||||
const modal = new BrowserWindow(windowOptions);
|
||||
modal.once('ready-to-show', () => {
|
||||
modal.show();
|
||||
});
|
||||
let updaterURL = (global.isDev ? 'http://localhost:8080' : `file://${app.getAppPath()}`) + '/browser/updater.html';
|
||||
|
||||
if (options.notifyOnly) {
|
||||
updaterURL += '?notifyOnly=true';
|
||||
}
|
||||
modal.loadURL(updaterURL);
|
||||
|
||||
for (const eventName of ['click-release-notes', 'click-skip', 'click-remind', 'click-install', 'click-download', 'click-cancel']) {
|
||||
const listener = createEventListener(modal, eventName);
|
||||
ipcMain.on(eventName, listener);
|
||||
modal.on('closed', () => {
|
||||
ipcMain.removeListener(eventName, listener);
|
||||
});
|
||||
}
|
||||
|
||||
return modal;
|
||||
}
|
||||
|
||||
function isUpdateApplicable(now: Date, skippedVersion, updateInfo) {
|
||||
const releaseTime = new Date(updateInfo.releaseDate).getTime();
|
||||
|
||||
// 48 hours after a new version is added to releases.mattermost.com, user receives a “New update is available” dialog
|
||||
if (now.getTime() - releaseTime < UPDATER_INTERVAL_IN_MS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If a version was skipped, compare version.
|
||||
if (skippedVersion) {
|
||||
return semver.gt(updateInfo.version, skippedVersion);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function downloadAndInstall(cancellationToken?: CancellationToken) {
|
||||
autoUpdater.on('update-downloaded', () => {
|
||||
global.willAppQuit = true;
|
||||
autoUpdater.quitAndInstall();
|
||||
});
|
||||
autoUpdater.downloadUpdate(cancellationToken);
|
||||
}
|
||||
|
||||
function initialize(appState, mainWindow, notifyOnly = false) {
|
||||
autoUpdater.autoDownload = false; // To prevent upgrading on quit
|
||||
const assetsDir = path.resolve(app.getAppPath(), 'assets');
|
||||
autoUpdater.on('error', (err) => {
|
||||
log.error('Error in autoUpdater:', err.message);
|
||||
}).on('update-available', (info) => {
|
||||
let cancellationToken = null;
|
||||
if (isUpdateApplicable(new Date(), appState.skippedVersion, info)) {
|
||||
updaterModal = createUpdaterModal(mainWindow, {
|
||||
linuxAppIcon: path.join(assetsDir, 'appicon.png'),
|
||||
notifyOnly,
|
||||
});
|
||||
updaterModal.on('closed', () => {
|
||||
updaterModal = null;
|
||||
});
|
||||
updaterModal.on('click-skip', () => {
|
||||
appState.skippedVersion = info.version;
|
||||
updaterModal.close();
|
||||
}).on('click-remind', () => {
|
||||
appState.updateCheckedDate = new Date();
|
||||
setTimeout(() => { // eslint-disable-line max-nested-callbacks
|
||||
autoUpdater.checkForUpdates();
|
||||
}, UPDATER_INTERVAL_IN_MS);
|
||||
updaterModal.close();
|
||||
}).on('click-install', () => {
|
||||
updaterModal.webContents.send('start-download');
|
||||
autoUpdater.signals.progress((data) => { // eslint-disable-line max-nested-callbacks
|
||||
updaterModal.send('progress', Math.floor(data.percent));
|
||||
log.info('progress:', data);
|
||||
});
|
||||
cancellationToken = new CancellationToken();
|
||||
downloadAndInstall(cancellationToken);
|
||||
}).on('click-download', () => {
|
||||
shell.openExternal('https://about.mattermost.com/download/#mattermostApps');
|
||||
}).on('click-release-notes', () => {
|
||||
shell.openExternal(`https://github.com/mattermost/desktop/releases/v${info.version}`);
|
||||
}).on('click-cancel', () => {
|
||||
cancellationToken.cancel();
|
||||
updaterModal.close();
|
||||
});
|
||||
updaterModal.focus();
|
||||
} else if (autoUpdater.isManual) {
|
||||
autoUpdater.emit('update-not-available');
|
||||
}
|
||||
}).on('update-not-available', () => {
|
||||
if (autoUpdater.isManual) {
|
||||
dialog.showMessageBox(mainWindow, {
|
||||
type: 'info',
|
||||
buttons: ['Close'],
|
||||
title: 'Your Desktop App is up to date',
|
||||
message: 'You have the latest version of the Mattermost Desktop App.',
|
||||
});
|
||||
}
|
||||
setTimeout(() => {
|
||||
autoUpdater.checkForUpdates();
|
||||
}, UPDATER_INTERVAL_IN_MS);
|
||||
});
|
||||
}
|
||||
|
||||
function shouldCheckForUpdatesOnStart(updateCheckedDate: Date) {
|
||||
if (updateCheckedDate) {
|
||||
if (Date.now() - updateCheckedDate.getTime() < UPDATER_INTERVAL_IN_MS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function checkForUpdates(isManual = false) {
|
||||
autoUpdater.isManual = isManual;
|
||||
if (!updaterModal) {
|
||||
autoUpdater.checkForUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
class AutoUpdaterConfig {
|
||||
data: {notifyOnly?: boolean};
|
||||
|
||||
constructor() {
|
||||
this.data = {};
|
||||
}
|
||||
|
||||
isNotifyOnly() {
|
||||
if (process.platform === 'win32') {
|
||||
return true;
|
||||
}
|
||||
if (this.data.notifyOnly === true) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function loadConfig() {
|
||||
return new AutoUpdaterConfig();
|
||||
}
|
||||
|
||||
export default {
|
||||
UPDATER_INTERVAL_IN_MS,
|
||||
checkForUpdates,
|
||||
shouldCheckForUpdatesOnStart,
|
||||
initialize,
|
||||
loadConfig,
|
||||
};
|
@@ -274,6 +274,6 @@ export function createTemplate(config: Config) {
|
||||
}
|
||||
|
||||
export function createMenu(config: Config) {
|
||||
// TODO: Electron is enforcing certain variables that it doesn't need
|
||||
// Electron is enforcing certain variables that it doesn't need
|
||||
return Menu.buildFromTemplate(createTemplate(config) as Array<MenuItemConstructorOptions | MenuItem>);
|
||||
}
|
||||
|
@@ -35,6 +35,6 @@ export function createTemplate(config: CombinedConfig) {
|
||||
}
|
||||
|
||||
export function createMenu(config: CombinedConfig) {
|
||||
// TODO: Electron is enforcing certain variables that it doesn't need
|
||||
// Electron is enforcing certain variables that it doesn't need
|
||||
return Menu.buildFromTemplate(createTemplate(config) as Array<MenuItemConstructorOptions | MenuItem>);
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ const defaultOptions = {
|
||||
icon: appIconURL,
|
||||
urgency: 'normal' as Notification['urgency'],
|
||||
};
|
||||
export const DEFAULT_WIN7 = 'Ding';
|
||||
const DEFAULT_WIN7 = 'Ding';
|
||||
|
||||
export class Mention extends Notification {
|
||||
customSound: string;
|
||||
|
@@ -37,11 +37,6 @@ export function getAdjustedWindowBoundaries(width: number, height: number, hasBa
|
||||
}
|
||||
|
||||
export function getLocalURLString(urlPath: string, query?: Map<string, string>, isMain?: boolean) {
|
||||
const localURL = getLocalURL(urlPath, query, isMain);
|
||||
return localURL.href;
|
||||
}
|
||||
|
||||
export function getLocalURL(urlPath: string, query?: Map<string, string>, isMain?: boolean) {
|
||||
let pathname;
|
||||
const processPath = isMain ? '' : '/renderer';
|
||||
const mode = Utils.runMode();
|
||||
@@ -61,7 +56,7 @@ export function getLocalURL(urlPath: string, query?: Map<string, string>, isMain
|
||||
});
|
||||
}
|
||||
|
||||
return localUrl;
|
||||
return localUrl.href;
|
||||
}
|
||||
|
||||
export function getLocalPreload(file: string) {
|
||||
|
@@ -165,7 +165,7 @@ export class MattermostView extends EventEmitter {
|
||||
};
|
||||
}
|
||||
|
||||
loadRetry = (loadURL: string, err: any) => {
|
||||
loadRetry = (loadURL: string, err: Error) => {
|
||||
this.retryLoad = setTimeout(this.retry(loadURL), RELOAD_INTERVAL);
|
||||
WindowManager.sendToRenderer(LOAD_RETRY, this.tab.name, Date.now() + RELOAD_INTERVAL, err.toString(), loadURL.toString());
|
||||
log.info(`[${Util.shorten(this.tab.name)}] failed loading ${loadURL}: ${err}, retrying in ${RELOAD_INTERVAL / SECOND} seconds`);
|
||||
@@ -211,7 +211,6 @@ export class MattermostView extends EventEmitter {
|
||||
hide = () => this.show(false);
|
||||
|
||||
setBounds = (boundaries: Electron.Rectangle) => {
|
||||
// todo: review this, as it might not work properly with devtools/minimizing/resizing
|
||||
this.view.setBounds(boundaries);
|
||||
}
|
||||
|
||||
|
@@ -446,7 +446,7 @@ export class ViewManager {
|
||||
}
|
||||
};
|
||||
|
||||
sendToAllViews = (channel: string, ...args: any[]) => {
|
||||
sendToAllViews = (channel: string, ...args: unknown[]) => {
|
||||
this.views.forEach((view) => view.view.webContents.send(channel, ...args));
|
||||
}
|
||||
}
|
||||
|
@@ -126,7 +126,7 @@ const generateNewWindowListener = (getServersFunction: () => TeamWithTabs[], spe
|
||||
}
|
||||
|
||||
// Public download links case
|
||||
// TODO: We might be handling different types differently in the future, for now
|
||||
// We might be handling different types differently in the future, for now
|
||||
// we are going to mimic the browser and just pop a new browser window for public links
|
||||
if (parsedURL.pathname.match(/^(\/api\/v[3-4]\/public)*\/files\//)) {
|
||||
shell.openExternal(details.url);
|
||||
@@ -218,7 +218,7 @@ export const addWebContentsEventListeners = (mmview: MattermostView, getServersF
|
||||
}
|
||||
|
||||
const willNavigate = generateWillNavigate(getServersFunction);
|
||||
contents.on('will-navigate', willNavigate as (e: Event, u: string) => void); // TODO: Electron types don't include sender for some reason
|
||||
contents.on('will-navigate', willNavigate as (e: Event, u: string) => void); // Electron types don't include sender for some reason
|
||||
|
||||
// handle custom login requests (oath, saml):
|
||||
// 1. are we navigating to a supported local custom login path from the `/login` page?
|
||||
|
@@ -150,8 +150,6 @@ export function getMainWindow(ensureCreated?: boolean) {
|
||||
return status.mainWindow;
|
||||
}
|
||||
|
||||
export const on = status.mainWindow?.on;
|
||||
|
||||
function handleMaximizeMainWindow() {
|
||||
sendToRenderer(MAXIMIZE_CHANGE, true);
|
||||
}
|
||||
@@ -204,15 +202,6 @@ export function sendToRenderer(channel: string, ...args: any[]) {
|
||||
}
|
||||
}
|
||||
|
||||
export function sendToAll(channel: string, ...args: any[]) {
|
||||
sendToRenderer(channel, ...args);
|
||||
if (status.settingsWindow) {
|
||||
status.settingsWindow.webContents.send(channel, ...args);
|
||||
}
|
||||
|
||||
// TODO: should we include popups?
|
||||
}
|
||||
|
||||
export function sendToMattermostViews(channel: string, ...args: any[]) {
|
||||
if (status.viewManager) {
|
||||
status.viewManager.sendToAllViews(channel, ...args);
|
||||
@@ -321,10 +310,6 @@ export async function setOverlayIcon(badgeText: string | undefined, description:
|
||||
}
|
||||
}
|
||||
|
||||
export function isMainWindow(window: BrowserWindow) {
|
||||
return status.mainWindow && status.mainWindow === window;
|
||||
}
|
||||
|
||||
export function handleDoubleClick(e: IpcMainEvent, windowType?: string) {
|
||||
let action = 'Maximize';
|
||||
if (process.platform === 'darwin') {
|
||||
@@ -446,11 +431,6 @@ export function updateLoadingScreenDarkMode(darkMode: boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getViewNameByWebContentsId(webContentsId: number) {
|
||||
const view = status.viewManager?.findViewByWebContent(webContentsId);
|
||||
return view?.name;
|
||||
}
|
||||
|
||||
export function getServerNameByWebContentsId(webContentsId: number) {
|
||||
const view = status.viewManager?.findViewByWebContent(webContentsId);
|
||||
return view?.tab.server.name;
|
||||
@@ -497,7 +477,7 @@ export function sendToFind() {
|
||||
}
|
||||
}
|
||||
|
||||
export function handleHistory(event: IpcMainEvent, offset: number) {
|
||||
function handleHistory(event: IpcMainEvent, offset: number) {
|
||||
if (status.viewManager) {
|
||||
const activeView = status.viewManager.getCurrentView();
|
||||
if (activeView && activeView.view.webContents.canGoToOffset(offset)) {
|
||||
@@ -606,7 +586,8 @@ function handleAppLoggedOut(event: IpcMainEvent, viewName: string) {
|
||||
}
|
||||
|
||||
function handleGetViewName(event: IpcMainInvokeEvent) {
|
||||
return getViewNameByWebContentsId(event.sender.id);
|
||||
const view = status.viewManager?.findViewByWebContent(event.sender.id);
|
||||
return view?.name;
|
||||
}
|
||||
function handleGetWebContentsId(event: IpcMainInvokeEvent) {
|
||||
return event.sender.id;
|
||||
|
@@ -11,7 +11,7 @@ import hello from 'static/sounds/hello.mp3';
|
||||
import ripple from 'static/sounds/ripple.mp3';
|
||||
import upstairs from 'static/sounds/upstairs.mp3';
|
||||
|
||||
export const DEFAULT_WIN7 = 'Ding';
|
||||
const DEFAULT_WIN7 = 'Ding';
|
||||
const notificationSounds = new Map([
|
||||
[DEFAULT_WIN7, ding],
|
||||
['Bing', bing],
|
||||
|
Reference in New Issue
Block a user