MM-62005 Change UrlView to always stay mounted (#3453)
* MM-62005 Change UrlView to always stay mounted * Rename message for URL view and corresponding methods * Change log level
This commit is contained in:
@@ -100,6 +100,7 @@ export const GET_MODAL_UNCLOSEABLE = 'get-modal-uncloseable';
|
|||||||
|
|
||||||
export const UPDATE_PATHS = 'update-paths';
|
export const UPDATE_PATHS = 'update-paths';
|
||||||
|
|
||||||
|
export const SET_URL_FOR_URL_VIEW = 'set-url-for-url-view';
|
||||||
export const UPDATE_URL_VIEW_WIDTH = 'update-url-view-width';
|
export const UPDATE_URL_VIEW_WIDTH = 'update-url-view-width';
|
||||||
|
|
||||||
export const OPEN_SERVER_EXTERNALLY = 'open-server-externally';
|
export const OPEN_SERVER_EXTERNALLY = 'open-server-externally';
|
||||||
|
@@ -100,6 +100,7 @@ import {
|
|||||||
LOAD_INCOMPATIBLE_SERVER,
|
LOAD_INCOMPATIBLE_SERVER,
|
||||||
OPEN_SERVER_UPGRADE_LINK,
|
OPEN_SERVER_UPGRADE_LINK,
|
||||||
OPEN_CHANGELOG_LINK,
|
OPEN_CHANGELOG_LINK,
|
||||||
|
SET_URL_FOR_URL_VIEW,
|
||||||
} from 'common/communication';
|
} from 'common/communication';
|
||||||
|
|
||||||
console.log('Preload initialized');
|
console.log('Preload initialized');
|
||||||
@@ -188,6 +189,7 @@ contextBridge.exposeInMainWorld('desktop', {
|
|||||||
onUpdateDownloadsDropdown: (listener) => ipcRenderer.on(UPDATE_DOWNLOADS_DROPDOWN, (_, downloads, darkMode, windowBounds, item) => listener(downloads, darkMode, windowBounds, item)),
|
onUpdateDownloadsDropdown: (listener) => ipcRenderer.on(UPDATE_DOWNLOADS_DROPDOWN, (_, downloads, darkMode, windowBounds, item) => listener(downloads, darkMode, windowBounds, item)),
|
||||||
onAppMenuWillClose: (listener) => ipcRenderer.on(APP_MENU_WILL_CLOSE, () => listener()),
|
onAppMenuWillClose: (listener) => ipcRenderer.on(APP_MENU_WILL_CLOSE, () => listener()),
|
||||||
onFocusThreeDotMenu: (listener) => ipcRenderer.on(FOCUS_THREE_DOT_MENU, () => listener()),
|
onFocusThreeDotMenu: (listener) => ipcRenderer.on(FOCUS_THREE_DOT_MENU, () => listener()),
|
||||||
|
onSetURLForURLView: (listener) => ipcRenderer.on(SET_URL_FOR_URL_VIEW, (_, url) => listener(url)),
|
||||||
updateURLViewWidth: (width) => ipcRenderer.send(UPDATE_URL_VIEW_WIDTH, width),
|
updateURLViewWidth: (width) => ipcRenderer.send(UPDATE_URL_VIEW_WIDTH, width),
|
||||||
openNotificationPreferences: () => ipcRenderer.send(OPEN_NOTIFICATION_PREFERENCES),
|
openNotificationPreferences: () => ipcRenderer.send(OPEN_NOTIFICATION_PREFERENCES),
|
||||||
openWindowsCameraPreferences: () => ipcRenderer.send(OPEN_WINDOWS_CAMERA_PREFERENCES),
|
openWindowsCameraPreferences: () => ipcRenderer.send(OPEN_WINDOWS_CAMERA_PREFERENCES),
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import type {IpcMainEvent, IpcMainInvokeEvent} from 'electron';
|
import type {IpcMainEvent, IpcMainInvokeEvent, View} from 'electron';
|
||||||
import {WebContentsView, ipcMain, shell} from 'electron';
|
import {WebContentsView, ipcMain, shell} from 'electron';
|
||||||
import isDev from 'electron-is-dev';
|
import isDev from 'electron-is-dev';
|
||||||
|
|
||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
UNREADS_AND_MENTIONS,
|
UNREADS_AND_MENTIONS,
|
||||||
TAB_LOGIN_CHANGED,
|
TAB_LOGIN_CHANGED,
|
||||||
DEVELOPER_MODE_UPDATED,
|
DEVELOPER_MODE_UPDATED,
|
||||||
|
SET_URL_FOR_URL_VIEW,
|
||||||
} from 'common/communication';
|
} from 'common/communication';
|
||||||
import Config from 'common/config';
|
import Config from 'common/config';
|
||||||
import {DEFAULT_CHANGELOG_LINK} from 'common/constants';
|
import {DEFAULT_CHANGELOG_LINK} from 'common/constants';
|
||||||
@@ -68,6 +69,7 @@ export class ViewManager {
|
|||||||
private views: Map<string, MattermostWebContentsView>;
|
private views: Map<string, MattermostWebContentsView>;
|
||||||
private currentView?: string;
|
private currentView?: string;
|
||||||
|
|
||||||
|
private urlView?: WebContentsView;
|
||||||
private urlViewCancel?: () => void;
|
private urlViewCancel?: () => void;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -110,6 +112,8 @@ export class ViewManager {
|
|||||||
await this.initServer(currentServer);
|
await this.initServer(currentServer);
|
||||||
this.showInitial();
|
this.showInitial();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.initURLView();
|
||||||
};
|
};
|
||||||
|
|
||||||
private initServer = async (server: MattermostServer) => {
|
private initServer = async (server: MattermostServer) => {
|
||||||
@@ -117,6 +121,24 @@ export class ViewManager {
|
|||||||
this.loadServer(server);
|
this.loadServer(server);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private initURLView = () => {
|
||||||
|
const mainWindow = MainWindow.get();
|
||||||
|
if (!mainWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlView = new WebContentsView({webPreferences: {preload: getLocalPreload('internalAPI.js')}});
|
||||||
|
urlView.setBackgroundColor('#00000000');
|
||||||
|
|
||||||
|
urlView.webContents.loadURL('mattermost-desktop://renderer/urlView.html');
|
||||||
|
|
||||||
|
MainWindow.get()?.contentView.addChildView(urlView);
|
||||||
|
|
||||||
|
performanceMonitor.registerView('URLView', urlView.webContents);
|
||||||
|
|
||||||
|
this.urlView = urlView;
|
||||||
|
};
|
||||||
|
|
||||||
private handleDeveloperModeUpdated = (json: DeveloperSettings) => {
|
private handleDeveloperModeUpdated = (json: DeveloperSettings) => {
|
||||||
log.debug('handleDeveloperModeUpdated', json);
|
log.debug('handleDeveloperModeUpdated', json);
|
||||||
|
|
||||||
@@ -147,6 +169,17 @@ export class ViewManager {
|
|||||||
return this.closedViews.has(viewId);
|
return this.closedViews.has(viewId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private isViewInFront = (view: View) => {
|
||||||
|
const mainWindow = MainWindow.get();
|
||||||
|
if (!mainWindow) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = mainWindow.contentView.children.indexOf(view);
|
||||||
|
const front = mainWindow.contentView.children.length - 1;
|
||||||
|
return index === front;
|
||||||
|
};
|
||||||
|
|
||||||
showById = (viewId: string) => {
|
showById = (viewId: string) => {
|
||||||
this.getViewLogger(viewId).debug('showById', viewId);
|
this.getViewLogger(viewId).debug('showById', viewId);
|
||||||
|
|
||||||
@@ -372,35 +405,24 @@ export class ViewManager {
|
|||||||
if (this.urlViewCancel) {
|
if (this.urlViewCancel) {
|
||||||
this.urlViewCancel();
|
this.urlViewCancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url && url !== '') {
|
if (url && url !== '') {
|
||||||
const urlString = typeof url === 'string' ? url : url.toString();
|
const urlString = typeof url === 'string' ? url : url.toString();
|
||||||
const urlView = new WebContentsView({webPreferences: {preload: getLocalPreload('internalAPI.js')}});
|
|
||||||
urlView.setBackgroundColor('#00000000');
|
|
||||||
const localURL = `mattermost-desktop://renderer/urlView.html?url=${encodeURIComponent(urlString)}`;
|
|
||||||
performanceMonitor.registerView('URLView', urlView.webContents);
|
|
||||||
urlView.webContents.loadURL(localURL);
|
|
||||||
|
|
||||||
// This is a workaround for an issue where the URL view would steal focus from the main window
|
if (this.urlView && !this.isViewInFront(this.urlView)) {
|
||||||
// See: https://github.com/electron/electron/issues/42339
|
log.silly('moving URL view to front');
|
||||||
// @ts-expect-error Using an undocumented event that fires last when the URL view pops up
|
MainWindow.get()?.contentView.addChildView(this.urlView);
|
||||||
urlView.webContents.once('ready-to-show', () => {
|
}
|
||||||
log.debug('URL view focus prevented');
|
|
||||||
this.getCurrentView()?.focus();
|
this.urlView?.webContents.send(SET_URL_FOR_URL_VIEW, urlString);
|
||||||
});
|
this.urlView?.setVisible(true);
|
||||||
|
|
||||||
MainWindow.get()?.contentView.addChildView(urlView);
|
|
||||||
const boundaries = this.views.get(this.currentView || '')?.getBounds() ?? MainWindow.getBounds();
|
const boundaries = this.views.get(this.currentView || '')?.getBounds() ?? MainWindow.getBounds();
|
||||||
|
|
||||||
const hideView = () => {
|
const hideView = () => {
|
||||||
delete this.urlViewCancel;
|
delete this.urlViewCancel;
|
||||||
try {
|
|
||||||
mainWindow.contentView.removeChildView(urlView);
|
|
||||||
} catch (e) {
|
|
||||||
log.error('Failed to remove URL view', e);
|
|
||||||
}
|
|
||||||
|
|
||||||
performanceMonitor.unregisterView(urlView.webContents.id);
|
this.urlView?.setVisible(false);
|
||||||
urlView.webContents.close();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const adjustWidth = (event: IpcMainEvent, width: number) => {
|
const adjustWidth = (event: IpcMainEvent, width: number) => {
|
||||||
@@ -418,7 +440,7 @@ export class ViewManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
log.silly('showURLView.setBounds', boundaries, bounds);
|
log.silly('showURLView.setBounds', boundaries, bounds);
|
||||||
urlView.setBounds(bounds);
|
this.urlView?.setBounds(bounds);
|
||||||
};
|
};
|
||||||
|
|
||||||
ipcMain.on(UPDATE_URL_VIEW_WIDTH, adjustWidth);
|
ipcMain.on(UPDATE_URL_VIEW_WIDTH, adjustWidth);
|
||||||
|
@@ -1,22 +1,32 @@
|
|||||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React, {useEffect} from 'react';
|
import React, {useEffect, useRef, useState} from 'react';
|
||||||
|
|
||||||
export default function UrlDescription(props: {url: string}) {
|
export default function UrlDescription() {
|
||||||
const urlRef = React.createRef<HTMLDivElement>();
|
const urlRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const [url, setUrl] = useState<string | undefined>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.desktop.updateURLViewWidth(urlRef.current?.scrollWidth);
|
window.desktop.onSetURLForURLView((newUrl) => {
|
||||||
|
setUrl(newUrl);
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (props.url) {
|
useEffect(() => {
|
||||||
|
if (url) {
|
||||||
|
window.desktop.updateURLViewWidth(urlRef.current?.scrollWidth);
|
||||||
|
}
|
||||||
|
}, [url]);
|
||||||
|
|
||||||
|
if (url) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={urlRef}
|
ref={urlRef}
|
||||||
className='HoveringURL HoveringURL-left'
|
className='HoveringURL HoveringURL-left'
|
||||||
>
|
>
|
||||||
<p>{props.url}</p>
|
<p>{url}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -3,9 +3,6 @@
|
|||||||
|
|
||||||
import 'renderer/css/components/HoveringURL.css';
|
import 'renderer/css/components/HoveringURL.css';
|
||||||
|
|
||||||
const queryString = window.location.search;
|
|
||||||
const urlParams = new URLSearchParams(queryString);
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
@@ -13,9 +10,7 @@ import UrlDescription from '../../components/urlDescription';
|
|||||||
|
|
||||||
const start = async () => {
|
const start = async () => {
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<UrlDescription
|
<UrlDescription/>,
|
||||||
url={decodeURIComponent(urlParams.get('url')!)}
|
|
||||||
/>,
|
|
||||||
document.getElementById('app'),
|
document.getElementById('app'),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -98,6 +98,7 @@ declare global {
|
|||||||
onAppMenuWillClose: (listener: () => void) => void;
|
onAppMenuWillClose: (listener: () => void) => void;
|
||||||
onFocusThreeDotMenu: (listener: () => void) => void;
|
onFocusThreeDotMenu: (listener: () => void) => void;
|
||||||
|
|
||||||
|
onSetURLForURLView: (listener: (link?: string) => void) => void;
|
||||||
updateURLViewWidth: (width?: number) => void;
|
updateURLViewWidth: (width?: number) => void;
|
||||||
openNotificationPreferences: () => void;
|
openNotificationPreferences: () => void;
|
||||||
openWindowsCameraPreferences: () => void;
|
openWindowsCameraPreferences: () => void;
|
||||||
|
Reference in New Issue
Block a user