diff --git a/src/common/communication.ts b/src/common/communication.ts index bd4fa1b9..2e99b459 100644 --- a/src/common/communication.ts +++ b/src/common/communication.ts @@ -100,6 +100,7 @@ export const GET_MODAL_UNCLOSEABLE = 'get-modal-uncloseable'; 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 OPEN_SERVER_EXTERNALLY = 'open-server-externally'; diff --git a/src/main/preload/internalAPI.js b/src/main/preload/internalAPI.js index 8e248486..440babd4 100644 --- a/src/main/preload/internalAPI.js +++ b/src/main/preload/internalAPI.js @@ -100,6 +100,7 @@ import { LOAD_INCOMPATIBLE_SERVER, OPEN_SERVER_UPGRADE_LINK, OPEN_CHANGELOG_LINK, + SET_URL_FOR_URL_VIEW, } from 'common/communication'; 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)), onAppMenuWillClose: (listener) => ipcRenderer.on(APP_MENU_WILL_CLOSE, () => 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), openNotificationPreferences: () => ipcRenderer.send(OPEN_NOTIFICATION_PREFERENCES), openWindowsCameraPreferences: () => ipcRenderer.send(OPEN_WINDOWS_CAMERA_PREFERENCES), diff --git a/src/main/views/viewManager.ts b/src/main/views/viewManager.ts index 59564eff..6ccd86d9 100644 --- a/src/main/views/viewManager.ts +++ b/src/main/views/viewManager.ts @@ -1,7 +1,7 @@ // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // 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 isDev from 'electron-is-dev'; @@ -33,6 +33,7 @@ import { UNREADS_AND_MENTIONS, TAB_LOGIN_CHANGED, DEVELOPER_MODE_UPDATED, + SET_URL_FOR_URL_VIEW, } from 'common/communication'; import Config from 'common/config'; import {DEFAULT_CHANGELOG_LINK} from 'common/constants'; @@ -68,6 +69,7 @@ export class ViewManager { private views: Map; private currentView?: string; + private urlView?: WebContentsView; private urlViewCancel?: () => void; constructor() { @@ -110,6 +112,8 @@ export class ViewManager { await this.initServer(currentServer); this.showInitial(); } + + this.initURLView(); }; private initServer = async (server: MattermostServer) => { @@ -117,6 +121,24 @@ export class ViewManager { 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) => { log.debug('handleDeveloperModeUpdated', json); @@ -147,6 +169,17 @@ export class ViewManager { 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) => { this.getViewLogger(viewId).debug('showById', viewId); @@ -372,35 +405,24 @@ export class ViewManager { if (this.urlViewCancel) { this.urlViewCancel(); } + if (url && url !== '') { 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 - // See: https://github.com/electron/electron/issues/42339 - // @ts-expect-error Using an undocumented event that fires last when the URL view pops up - urlView.webContents.once('ready-to-show', () => { - log.debug('URL view focus prevented'); - this.getCurrentView()?.focus(); - }); + if (this.urlView && !this.isViewInFront(this.urlView)) { + log.silly('moving URL view to front'); + MainWindow.get()?.contentView.addChildView(this.urlView); + } + + 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 hideView = () => { delete this.urlViewCancel; - try { - mainWindow.contentView.removeChildView(urlView); - } catch (e) { - log.error('Failed to remove URL view', e); - } - performanceMonitor.unregisterView(urlView.webContents.id); - urlView.webContents.close(); + this.urlView?.setVisible(false); }; const adjustWidth = (event: IpcMainEvent, width: number) => { @@ -418,7 +440,7 @@ export class ViewManager { }; log.silly('showURLView.setBounds', boundaries, bounds); - urlView.setBounds(bounds); + this.urlView?.setBounds(bounds); }; ipcMain.on(UPDATE_URL_VIEW_WIDTH, adjustWidth); diff --git a/src/renderer/components/urlDescription.tsx b/src/renderer/components/urlDescription.tsx index 72340aef..bfbbeb79 100644 --- a/src/renderer/components/urlDescription.tsx +++ b/src/renderer/components/urlDescription.tsx @@ -1,22 +1,32 @@ // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // 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}) { - const urlRef = React.createRef(); +export default function UrlDescription() { + const urlRef = useRef(null); + + const [url, setUrl] = useState(); 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 (
-

{props.url}

+

{url}

); } diff --git a/src/renderer/modals/urlView/urlView.tsx b/src/renderer/modals/urlView/urlView.tsx index 484ff231..46ebc436 100644 --- a/src/renderer/modals/urlView/urlView.tsx +++ b/src/renderer/modals/urlView/urlView.tsx @@ -3,9 +3,6 @@ import 'renderer/css/components/HoveringURL.css'; -const queryString = window.location.search; -const urlParams = new URLSearchParams(queryString); - import React from 'react'; import ReactDOM from 'react-dom'; @@ -13,9 +10,7 @@ import UrlDescription from '../../components/urlDescription'; const start = async () => { ReactDOM.render( - , + , document.getElementById('app'), ); }; diff --git a/src/types/window.ts b/src/types/window.ts index 9a2a1fd6..4784574b 100644 --- a/src/types/window.ts +++ b/src/types/window.ts @@ -98,6 +98,7 @@ declare global { onAppMenuWillClose: (listener: () => void) => void; onFocusThreeDotMenu: (listener: () => void) => void; + onSetURLForURLView: (listener: (link?: string) => void) => void; updateURLViewWidth: (width?: number) => void; openNotificationPreferences: () => void; openWindowsCameraPreferences: () => void;