Various QoL fixes for Desktop App (#2999)

* Some ESLint fixes

* Add login/logout signal to API, clear mentions on logout and flush cookies on login/logout

* Fix issue where local and HTTP-only servers would not validate correctly

* Reduce noise of renderer logging, adjust a few local renderer logs to be louder when needed

* Fallback to beginning of hostname for servers that don't change the site name

* Fix Save Image crash

* Update the name for insecure servers too

* Fix test

* Fix lint

* Reduce repetition
This commit is contained in:
Devin Binnie
2024-04-08 09:12:35 -04:00
committed by GitHub
parent 2456e68ae9
commit e1c957e774
21 changed files with 116 additions and 74 deletions

View File

@@ -87,9 +87,9 @@ import {
shouldShowTrayIcon,
updateSpellCheckerLocales,
wasUpdated,
initCookieManager,
migrateMacAppStore,
updateServerInfos,
flushCookiesStore,
} from './utils';
import {
handleClose,
@@ -200,6 +200,12 @@ function initializeAppEventListeners() {
app.on('child-process-gone', handleChildProcessGone);
app.on('login', AuthManager.handleAppLogin);
app.on('will-finish-launching', handleAppWillFinishLaunching);
// Somehow cookies are not immediately saved to disk.
// So manually flush cookie store to disk on closing the app.
// https://github.com/electron/electron/issues/8416
// TODO: We can remove this once every server supported will flush on login/logout
app.on('before-quit', flushCookiesStore);
}
function initializeBeforeAppReady() {
@@ -347,7 +353,6 @@ async function initializeAfterAppReady() {
catch((err) => log.error('An error occurred: ', err));
}
initCookieManager(defaultSession);
MainWindow.show();
let deeplinkingURL;

View File

@@ -4,7 +4,7 @@
import fs from 'fs';
import path from 'path';
import type {BrowserWindow, Rectangle, Session} from 'electron';
import type {BrowserWindow, Rectangle} from 'electron';
import {app, Menu, session, dialog, nativeImage, screen} from 'electron';
import isDev from 'electron-is-dev';
@@ -166,26 +166,13 @@ export function resizeScreen(browserWindow: BrowserWindow) {
handle();
}
export function flushCookiesStore(session: Session) {
export function flushCookiesStore() {
log.debug('flushCookiesStore');
session.cookies.flushStore().catch((err) => {
session.defaultSession.cookies.flushStore().catch((err) => {
log.error(`There was a problem flushing cookies:\n${err}`);
});
}
export function initCookieManager(session: Session) {
// Somehow cookies are not immediately saved to disk.
// So manually flush cookie store to disk on closing the app.
// https://github.com/electron/electron/issues/8416
app.on('before-quit', () => {
flushCookiesStore(session);
});
app.on('browser-window-blur', () => {
flushCookiesStore(session);
});
}
export function migrateMacAppStore() {
const migrationPrefs = new JsonFileManager<MigrationInfo>(migrationInfoPath);
const oldPath = path.join(app.getPath('userData'), '../../../../../../../Library/Application Support/Mattermost');

View File

@@ -70,6 +70,9 @@ const desktopAPI: DesktopAPI = {
setSessionExpired: (isExpired) => ipcRenderer.send(SESSION_EXPIRED, isExpired),
onUserActivityUpdate: (listener) => createListener(USER_ACTIVITY_UPDATE, listener),
onLogin: () => ipcRenderer.send(APP_LOGGED_IN),
onLogout: () => ipcRenderer.send(APP_LOGGED_OUT),
// Unreads/mentions/notifications
sendNotification: (title, body, channelId, teamId, url, silent, soundName) =>
ipcRenderer.invoke(NOTIFY_MENTION, title, body, channelId, teamId, url, silent, soundName),
@@ -178,11 +181,11 @@ setInterval(() => {
const onLoad = () => {
if (document.getElementById('root') === null) {
console.log('The guest is not assumed as mattermost-webapp');
console.warn('The guest is not assumed as mattermost-webapp');
return;
}
watchReactAppUntilInitialized(() => {
console.log('Legacy preload initialized');
console.warn('Legacy preload initialized');
ipcRenderer.send(REACT_APP_INITIALIZED);
ipcRenderer.invoke(REQUEST_BROWSER_HISTORY_STATUS).then(sendHistoryButtonReturn);
});

View File

@@ -56,6 +56,10 @@ jest.mock('common/utils/url', () => ({
equalUrlsIgnoringSubpath: jest.fn(),
}));
jest.mock('main/app/utils', () => ({
flushCookiesStore: jest.fn(),
}));
jest.mock('main/i18nManager', () => ({
localizeMessage: jest.fn(),
}));

View File

@@ -43,6 +43,7 @@ import {getFormattedPathName, parseURL} from 'common/utils/url';
import Utils from 'common/utils/util';
import type {MattermostView} from 'common/views/View';
import {TAB_MESSAGING} from 'common/views/View';
import {flushCookiesStore} from 'main/app/utils';
import {localizeMessage} from 'main/i18nManager';
import MainWindow from 'main/windows/mainWindow';
@@ -471,11 +472,24 @@ export class ViewManager {
};
private handleAppLoggedIn = (event: IpcMainEvent) => {
this.getViewByWebContentsId(event.sender.id)?.onLogin(true);
log.debug('handleAppLoggedIn', event.sender.id);
const view = this.getViewByWebContentsId(event.sender.id);
if (!view) {
return;
}
view.onLogin(true);
flushCookiesStore();
};
private handleAppLoggedOut = (event: IpcMainEvent) => {
this.getViewByWebContentsId(event.sender.id)?.onLogin(false);
log.debug('handleAppLoggedOut', event.sender.id);
const view = this.getViewByWebContentsId(event.sender.id);
if (!view) {
return;
}
view.onLogin(false);
AppState.clear(view.id);
flushCookiesStore();
};
private handleBrowserHistoryPush = (e: IpcMainEvent, pathName: string) => {

View File

@@ -245,8 +245,7 @@ describe('main/views/webContentsEvents', () => {
const logObject = {
error: jest.fn(),
warn: jest.fn(),
info: jest.fn(),
verbose: jest.fn(),
debug: jest.fn(),
withPrefix: jest.fn().mockReturnThis(),
};
webContentsEventManager.log = jest.fn().mockReturnValue(logObject);
@@ -258,10 +257,10 @@ describe('main/views/webContentsEvents', () => {
it('should respect logging levels', () => {
consoleMessage({}, 0, 'test0', 0, '');
expect(logObject.verbose).toHaveBeenCalledWith('test0');
expect(logObject.debug).toHaveBeenCalledWith('test0');
consoleMessage({}, 1, 'test1', 0, '');
expect(logObject.info).toHaveBeenCalledWith('test1');
expect(logObject.debug).toHaveBeenCalledWith('test1');
consoleMessage({}, 2, 'test2', 0, '');
expect(logObject.warn).toHaveBeenCalledWith('test2');
@@ -273,11 +272,11 @@ describe('main/views/webContentsEvents', () => {
it('should only add line numbers for debug and silly', () => {
getLevel.mockReturnValue('debug');
consoleMessage({}, 0, 'test1', 42, 'meaning_of_life.js');
expect(logObject.verbose).toHaveBeenCalledWith('test1', '(meaning_of_life.js:42)');
expect(logObject.debug).toHaveBeenCalledWith('test1', '(meaning_of_life.js:42)');
getLevel.mockReturnValue('info');
getLevel.mockReturnValue('warn');
consoleMessage({}, 0, 'test2', 42, 'meaning_of_life.js');
expect(logObject.verbose).not.toHaveBeenCalledWith('test2', '(meaning_of_life.js:42)');
expect(logObject.warn).not.toHaveBeenCalledWith('test2', '(meaning_of_life.js:42)');
});
});
});

View File

@@ -4,7 +4,7 @@
import path from 'path';
import type {WebContents, Event} from 'electron';
import {BrowserWindow, session, shell} from 'electron';
import {BrowserWindow, shell} from 'electron';
import Config from 'common/config';
import {Logger, getLevel} from 'common/log';
@@ -116,7 +116,7 @@ export class WebContentsEventManager {
return;
}
if (this.customLogins[webContentsId]?.inProgress) {
flushCookiesStore(session.defaultSession);
flushCookiesStore();
return;
}
@@ -298,7 +298,7 @@ export class WebContentsEventManager {
private generateHandleConsoleMessage = (webContentsId: number) => (_: Event, level: number, message: string, line: number, sourceId: string) => {
const wcLog = this.log(webContentsId).withPrefix('renderer');
let logFn = wcLog.verbose;
let logFn = wcLog.debug;
switch (level) {
case ConsoleMessageLevel.Error:
logFn = wcLog.error;
@@ -306,9 +306,6 @@ export class WebContentsEventManager {
case ConsoleMessageLevel.Warning:
logFn = wcLog.warn;
break;
case ConsoleMessageLevel.Info:
logFn = wcLog.info;
break;
}
// Only include line entries if we're debugging