From 791c141aeed236153df46ab2b07576acea0b5752 Mon Sep 17 00:00:00 2001 From: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> Date: Wed, 16 Nov 2022 10:40:18 -0400 Subject: [PATCH] [MM-47706] Remove exposure of the ipcRenderer methods in the desktop app and only expose specific endpoints (#2387) --- src/main/preload/mainWindow.js | 118 +++++++++++++++- src/renderer/components/MainPage.tsx | 131 +++++++----------- src/renderer/components/SettingsPage.tsx | 40 ++---- src/renderer/components/TabBar.tsx | 2 +- .../components/TeamDropdownButton.tsx | 8 +- src/renderer/index.tsx | 18 ++- src/renderer/settings.tsx | 6 +- src/types/settings.ts | 10 ++ src/types/window.ts | 61 ++++++++ 9 files changed, 261 insertions(+), 133 deletions(-) create mode 100644 src/types/settings.ts diff --git a/src/main/preload/mainWindow.js b/src/main/preload/mainWindow.js index fc4e30ac..6e09dd41 100644 --- a/src/main/preload/mainWindow.js +++ b/src/main/preload/mainWindow.js @@ -9,13 +9,63 @@ import {ipcRenderer, contextBridge} from 'electron'; import { GET_LANGUAGE_INFORMATION, RETRIEVED_LANGUAGE_INFORMATION, + QUIT, + GET_VIEW_NAME, + GET_VIEW_WEBCONTENTS_ID, + OPEN_APP_MENU, + CLOSE_TEAMS_DROPDOWN, + OPEN_TEAMS_DROPDOWN, + SWITCH_TAB, + CLOSE_TAB, + WINDOW_CLOSE, + WINDOW_MINIMIZE, + WINDOW_MAXIMIZE, + WINDOW_RESTORE, + DOUBLE_CLICK_ON_WINDOW, + FOCUS_BROWSERVIEW, + RELOAD_CURRENT_VIEW, + CLOSE_DOWNLOADS_DROPDOWN, + CLOSE_DOWNLOADS_DROPDOWN_MENU, + OPEN_DOWNLOADS_DROPDOWN, + HISTORY, + CHECK_FOR_UPDATES, + UPDATE_CONFIGURATION, + UPDATE_TEAMS, + GET_CONFIGURATION, + GET_DARK_MODE, + REQUEST_HAS_DOWNLOADS, + GET_FULL_SCREEN_STATUS, + GET_AVAILABLE_SPELL_CHECKER_LANGUAGES, + GET_AVAILABLE_LANGUAGES, + GET_LOCAL_CONFIGURATION, + GET_DOWNLOAD_LOCATION, + RELOAD_CONFIGURATION, + DARK_MODE_CHANGE, + LOAD_RETRY, + LOAD_SUCCESS, + LOAD_FAILED, + SET_ACTIVE_VIEW, + MAXIMIZE_CHANGE, + PLAY_SOUND, + MODAL_OPEN, + MODAL_CLOSE, + TOGGLE_BACK_BUTTON, + UPDATE_MENTIONS, + SHOW_DOWNLOADS_DROPDOWN_BUTTON_BADGE, + HIDE_DOWNLOADS_DROPDOWN_BUTTON_BADGE, + UPDATE_DOWNLOADS_DROPDOWN, + APP_MENU_WILL_CLOSE, + FOCUS_THREE_DOT_MENU, } from 'common/communication'; -contextBridge.exposeInMainWorld('ipcRenderer', { - send: ipcRenderer.send, - on: (channel, listener) => ipcRenderer.on(channel, (_, ...args) => listener(null, ...args)), - invoke: ipcRenderer.invoke, -}); +console.log('Preload initialized'); + +if (process.env.NODE_ENV === 'test') { + contextBridge.exposeInMainWorld('testHelper', { + getViewName: () => ipcRenderer.invoke(GET_VIEW_NAME), + getWebContentsId: () => ipcRenderer.invoke(GET_VIEW_WEBCONTENTS_ID), + }); +} contextBridge.exposeInMainWorld('process', { platform: process.platform, @@ -29,6 +79,64 @@ contextBridge.exposeInMainWorld('timers', { setImmediate, }); +contextBridge.exposeInMainWorld('desktop', { + quit: (reason, stack) => ipcRenderer.send(QUIT, reason, stack), + openAppMenu: () => ipcRenderer.send(OPEN_APP_MENU), + closeTeamsDropdown: () => ipcRenderer.send(CLOSE_TEAMS_DROPDOWN), + openTeamsDropdown: () => ipcRenderer.send(OPEN_TEAMS_DROPDOWN), + switchTab: (serverName, tabName) => ipcRenderer.send(SWITCH_TAB, serverName, tabName), + closeTab: (serverName, tabName) => ipcRenderer.send(CLOSE_TAB, serverName, tabName), + closeWindow: () => ipcRenderer.send(WINDOW_CLOSE), + minimizeWindow: () => ipcRenderer.send(WINDOW_MINIMIZE), + maximizeWindow: () => ipcRenderer.send(WINDOW_MAXIMIZE), + restoreWindow: () => ipcRenderer.send(WINDOW_RESTORE), + doubleClickOnWindow: (windowName) => ipcRenderer.send(DOUBLE_CLICK_ON_WINDOW, windowName), + focusBrowserView: () => ipcRenderer.send(FOCUS_BROWSERVIEW), + reloadCurrentView: () => ipcRenderer.send(RELOAD_CURRENT_VIEW), + closeDownloadsDropdown: () => ipcRenderer.send(CLOSE_DOWNLOADS_DROPDOWN), + closeDownloadsDropdownMenu: () => ipcRenderer.send(CLOSE_DOWNLOADS_DROPDOWN_MENU), + openDownloadsDropdown: () => ipcRenderer.send(OPEN_DOWNLOADS_DROPDOWN), + goBack: () => ipcRenderer.send(HISTORY, -1), + checkForUpdates: () => ipcRenderer.send(CHECK_FOR_UPDATES), + updateConfiguration: (saveQueueItems) => ipcRenderer.send(UPDATE_CONFIGURATION, saveQueueItems), + + updateTeams: (updatedTeams) => ipcRenderer.invoke(UPDATE_TEAMS, updatedTeams), + getConfiguration: (option) => ipcRenderer.invoke(GET_CONFIGURATION, option), + getVersion: () => ipcRenderer.invoke('get-app-version'), + getDarkMode: () => ipcRenderer.invoke(GET_DARK_MODE), + requestHasDownloads: () => ipcRenderer.invoke(REQUEST_HAS_DOWNLOADS), + getFullScreenStatus: () => ipcRenderer.invoke(GET_FULL_SCREEN_STATUS), + getAvailableSpellCheckerLanguages: () => ipcRenderer.invoke(GET_AVAILABLE_SPELL_CHECKER_LANGUAGES), + getAvailableLanguages: () => ipcRenderer.invoke(GET_AVAILABLE_LANGUAGES), + getLocalConfiguration: (option) => ipcRenderer.invoke(GET_LOCAL_CONFIGURATION, option), + getDownloadLocation: (downloadLocation) => ipcRenderer.invoke(GET_DOWNLOAD_LOCATION, downloadLocation), + + onSynchronizeConfig: (listener) => ipcRenderer.on('synchronize-config', () => listener()), + onReloadConfiguration: (listener) => ipcRenderer.on(RELOAD_CONFIGURATION, () => listener()), + onDarkModeChange: (listener) => ipcRenderer.on(DARK_MODE_CHANGE, (_, darkMode) => listener(darkMode)), + onLoadRetry: (listener) => ipcRenderer.on(LOAD_RETRY, (_, viewName, retry, err, loadUrl) => listener(viewName, retry, err, loadUrl)), + onLoadSuccess: (listener) => ipcRenderer.on(LOAD_SUCCESS, (_, viewName) => listener(viewName)), + onLoadFailed: (listener) => ipcRenderer.on(LOAD_FAILED, (_, viewName, err, loadUrl) => listener(viewName, err, loadUrl)), + onSetActiveView: (listener) => ipcRenderer.on(SET_ACTIVE_VIEW, (_, serverName, tabName) => listener(serverName, tabName)), + onMaximizeChange: (listener) => ipcRenderer.on(MAXIMIZE_CHANGE, (_, maximize) => listener(maximize)), + onEnterFullScreen: (listener) => ipcRenderer.on('enter-full-screen', () => listener()), + onLeaveFullScreen: (listener) => ipcRenderer.on('leave-full-screen', () => listener()), + onPlaySound: (listener) => ipcRenderer.on(PLAY_SOUND, (_, soundName) => listener(soundName)), + onModalOpen: (listener) => ipcRenderer.on(MODAL_OPEN, () => listener()), + onModalClose: (listener) => ipcRenderer.on(MODAL_CLOSE, () => listener()), + onToggleBackButton: (listener) => ipcRenderer.on(TOGGLE_BACK_BUTTON, (_, showExtraBar) => listener(showExtraBar)), + onUpdateMentions: (listener) => ipcRenderer.on(UPDATE_MENTIONS, (_event, view, mentions, unreads, isExpired) => listener(view, mentions, unreads, isExpired)), + onCloseTeamsDropdown: (listener) => ipcRenderer.on(CLOSE_TEAMS_DROPDOWN, () => listener()), + onOpenTeamsDropdown: (listener) => ipcRenderer.on(OPEN_TEAMS_DROPDOWN, () => listener()), + onCloseDownloadsDropdown: (listener) => ipcRenderer.on(CLOSE_DOWNLOADS_DROPDOWN, () => listener()), + onOpenDownloadsDropdown: (listener) => ipcRenderer.on(OPEN_DOWNLOADS_DROPDOWN, () => listener()), + onShowDownloadsDropdownButtonBadge: (listener) => ipcRenderer.on(SHOW_DOWNLOADS_DROPDOWN_BUTTON_BADGE, () => listener()), + onHideDownloadsDropdownButtonBadge: (listener) => ipcRenderer.on(HIDE_DOWNLOADS_DROPDOWN_BUTTON_BADGE, () => listener()), + onUpdateDownloadsDropdown: (listener) => ipcRenderer.on(UPDATE_DOWNLOADS_DROPDOWN, (_, downloads) => listener(downloads)), + onAppMenuWillClose: (listener) => ipcRenderer.on(APP_MENU_WILL_CLOSE, () => listener()), + onFocusThreeDotMenu: (listener) => ipcRenderer.on(FOCUS_THREE_DOT_MENU, () => listener()), +}); + window.addEventListener('message', async (event) => { switch (event.data.type) { case GET_LANGUAGE_INFORMATION: diff --git a/src/renderer/components/MainPage.tsx b/src/renderer/components/MainPage.tsx index 5d37ee1f..bbef16a1 100644 --- a/src/renderer/components/MainPage.tsx +++ b/src/renderer/components/MainPage.tsx @@ -9,49 +9,12 @@ import React, {Fragment} from 'react'; import {Container, Row} from 'react-bootstrap'; import {DropResult} from 'react-beautiful-dnd'; import {injectIntl, IntlShape} from 'react-intl'; -import {IpcRendererEvent} from 'electron/renderer'; import {TeamWithTabs} from 'types/config'; import {DownloadedItems} from 'types/downloads'; import {getTabViewName} from 'common/tabs/TabView'; -import { - FOCUS_BROWSERVIEW, - MAXIMIZE_CHANGE, - DARK_MODE_CHANGE, - HISTORY, - LOAD_RETRY, - LOAD_SUCCESS, - LOAD_FAILED, - WINDOW_CLOSE, - WINDOW_MINIMIZE, - WINDOW_RESTORE, - WINDOW_MAXIMIZE, - DOUBLE_CLICK_ON_WINDOW, - PLAY_SOUND, - MODAL_OPEN, - MODAL_CLOSE, - SET_ACTIVE_VIEW, - UPDATE_MENTIONS, - TOGGLE_BACK_BUTTON, - FOCUS_THREE_DOT_MENU, - GET_FULL_SCREEN_STATUS, - CLOSE_TEAMS_DROPDOWN, - OPEN_TEAMS_DROPDOWN, - SWITCH_TAB, - CLOSE_TAB, - RELOAD_CURRENT_VIEW, - CLOSE_DOWNLOADS_DROPDOWN, - OPEN_DOWNLOADS_DROPDOWN, - SHOW_DOWNLOADS_DROPDOWN_BUTTON_BADGE, - HIDE_DOWNLOADS_DROPDOWN_BUTTON_BADGE, - UPDATE_DOWNLOADS_DROPDOWN, - REQUEST_HAS_DOWNLOADS, - CLOSE_DOWNLOADS_DROPDOWN_MENU, - APP_MENU_WILL_CLOSE, -} from 'common/communication'; - import restoreButton from '../../assets/titlebar/chrome-restore.svg'; import maximizeButton from '../../assets/titlebar/chrome-maximize.svg'; import minimizeButton from '../../assets/titlebar/chrome-minimize.svg'; @@ -90,7 +53,7 @@ type State = { activeServerName?: string; activeTabName?: string; sessionsExpired: Record; - unreadCounts: Record; + unreadCounts: Record; mentionCounts: Record; maximized: boolean; tabViewStatus: Map; @@ -160,7 +123,7 @@ class MainPage extends React.PureComponent { async requestDownloadsLength() { try { - const hasDownloads = await window.ipcRenderer.invoke(REQUEST_HAS_DOWNLOADS); + const hasDownloads = await window.desktop.requestHasDownloads(); this.setState({ hasDownloads, }); @@ -174,7 +137,7 @@ class MainPage extends React.PureComponent { this.requestDownloadsLength(); // set page on retry - window.ipcRenderer.on(LOAD_RETRY, (_, viewName, retry, err, loadUrl) => { + window.desktop.onLoadRetry((viewName, retry, err, loadUrl) => { console.log(`${viewName}: failed to load ${err}, but retrying`); const statusValue = { status: Status.RETRY, @@ -187,11 +150,11 @@ class MainPage extends React.PureComponent { this.updateTabStatus(viewName, statusValue); }); - window.ipcRenderer.on(LOAD_SUCCESS, (_, viewName) => { + window.desktop.onLoadSuccess((viewName) => { this.updateTabStatus(viewName, {status: Status.DONE}); }); - window.ipcRenderer.on(LOAD_FAILED, (_, viewName, err, loadUrl) => { + window.desktop.onLoadFailed((viewName, err, loadUrl) => { console.log(`${viewName}: failed to load ${err}`); const statusValue = { status: Status.FAILED, @@ -203,39 +166,39 @@ class MainPage extends React.PureComponent { this.updateTabStatus(viewName, statusValue); }); - window.ipcRenderer.on(DARK_MODE_CHANGE, (_, darkMode) => { + window.desktop.onDarkModeChange((darkMode) => { this.setState({darkMode}); }); // can't switch tabs sequentially for some reason... - window.ipcRenderer.on(SET_ACTIVE_VIEW, (event, serverName, tabName) => { + window.desktop.onSetActiveView((serverName, tabName) => { this.setState({activeServerName: serverName, activeTabName: tabName}); }); - window.ipcRenderer.on(MAXIMIZE_CHANGE, this.handleMaximizeState); + window.desktop.onMaximizeChange(this.handleMaximizeState); - window.ipcRenderer.on('enter-full-screen', () => this.handleFullScreenState(true)); - window.ipcRenderer.on('leave-full-screen', () => this.handleFullScreenState(false)); + window.desktop.onEnterFullScreen(() => this.handleFullScreenState(true)); + window.desktop.onLeaveFullScreen(() => this.handleFullScreenState(false)); - window.ipcRenderer.invoke(GET_FULL_SCREEN_STATUS).then((fullScreenStatus) => this.handleFullScreenState(fullScreenStatus)); + window.desktop.getFullScreenStatus().then((fullScreenStatus) => this.handleFullScreenState(fullScreenStatus)); - window.ipcRenderer.on(PLAY_SOUND, (_event, soundName) => { + window.desktop.onPlaySound((soundName) => { playSound(soundName); }); - window.ipcRenderer.on(MODAL_OPEN, () => { + window.desktop.onModalOpen(() => { this.setState({modalOpen: true}); }); - window.ipcRenderer.on(MODAL_CLOSE, () => { + window.desktop.onModalClose(() => { this.setState({modalOpen: false}); }); - window.ipcRenderer.on(TOGGLE_BACK_BUTTON, (event, showExtraBar) => { + window.desktop.onToggleBackButton((showExtraBar) => { this.setState({showExtraBar}); }); - window.ipcRenderer.on(UPDATE_MENTIONS, (_event, view, mentions, unreads, isExpired) => { + window.desktop.onUpdateMentions((view, mentions, unreads, isExpired) => { const {unreadCounts, mentionCounts, sessionsExpired} = this.state; const newMentionCounts = {...mentionCounts}; @@ -250,40 +213,40 @@ class MainPage extends React.PureComponent { this.setState({unreadCounts: newUnreads, mentionCounts: newMentionCounts, sessionsExpired: expired}); }); - window.ipcRenderer.on(CLOSE_TEAMS_DROPDOWN, () => { + window.desktop.onCloseTeamsDropdown(() => { this.setState({isMenuOpen: false}); }); - window.ipcRenderer.on(OPEN_TEAMS_DROPDOWN, () => { + window.desktop.onOpenTeamsDropdown(() => { this.setState({isMenuOpen: true}); }); - window.ipcRenderer.on(CLOSE_DOWNLOADS_DROPDOWN, () => { + window.desktop.onCloseDownloadsDropdown(() => { this.setState({isDownloadsDropdownOpen: false}); }); - window.ipcRenderer.on(OPEN_DOWNLOADS_DROPDOWN, () => { + window.desktop.onOpenDownloadsDropdown(() => { this.setState({isDownloadsDropdownOpen: true}); }); - window.ipcRenderer.on(SHOW_DOWNLOADS_DROPDOWN_BUTTON_BADGE, () => { + window.desktop.onShowDownloadsDropdownButtonBadge(() => { this.setState({showDownloadsBadge: true}); }); - window.ipcRenderer.on(HIDE_DOWNLOADS_DROPDOWN_BUTTON_BADGE, () => { + window.desktop.onHideDownloadsDropdownButtonBadge(() => { this.setState({showDownloadsBadge: false}); }); - window.ipcRenderer.on(UPDATE_DOWNLOADS_DROPDOWN, (event, downloads: DownloadedItems) => { + window.desktop.onUpdateDownloadsDropdown((downloads: DownloadedItems) => { this.setState({ hasDownloads: (Object.values(downloads)?.length || 0) > 0, }); }); - window.ipcRenderer.on(APP_MENU_WILL_CLOSE, this.unFocusThreeDotsButton); + window.desktop.onAppMenuWillClose(this.unFocusThreeDotsButton); if (window.process.platform !== 'darwin') { - window.ipcRenderer.on(FOCUS_THREE_DOT_MENU, this.focusThreeDotsButton); + window.desktop.onFocusThreeDotMenu(this.focusThreeDotsButton); } window.addEventListener('click', this.handleCloseDropdowns); @@ -294,11 +257,11 @@ class MainPage extends React.PureComponent { } handleCloseDropdowns = () => { - window.ipcRenderer.send(CLOSE_TEAMS_DROPDOWN); + window.desktop.closeTeamsDropdown(); this.closeDownloadsDropdown(); } - handleMaximizeState = (_: IpcRendererEvent, maximized: boolean) => { + handleMaximizeState = (maximized: boolean) => { this.setState({maximized}); } @@ -307,11 +270,17 @@ class MainPage extends React.PureComponent { } handleSelectTab = (name: string) => { - window.ipcRenderer.send(SWITCH_TAB, this.state.activeServerName, name); + if (!this.state.activeServerName) { + return; + } + window.desktop.switchTab(this.state.activeServerName, name); } handleCloseTab = (name: string) => { - window.ipcRenderer.send(CLOSE_TAB, this.state.activeServerName, name); + if (!this.state.activeServerName) { + return; + } + window.desktop.closeTab(this.state.activeServerName, name); } handleDragAndDrop = async (dropResult: DropResult) => { @@ -338,21 +307,21 @@ class MainPage extends React.PureComponent { handleClose = (e: React.MouseEvent) => { e.stopPropagation(); // since it is our button, the event goes into MainPage's onclick event, getting focus back. - window.ipcRenderer.send(WINDOW_CLOSE); + window.desktop.closeWindow(); } handleMinimize = (e: React.MouseEvent) => { e.stopPropagation(); - window.ipcRenderer.send(WINDOW_MINIMIZE); + window.desktop.minimizeWindow(); } handleMaximize = (e: React.MouseEvent) => { e.stopPropagation(); - window.ipcRenderer.send(WINDOW_MAXIMIZE); + window.desktop.maximizeWindow(); } handleRestore = () => { - window.ipcRenderer.send(WINDOW_RESTORE); + window.desktop.restoreWindow(); } openMenu = () => { @@ -360,16 +329,16 @@ class MainPage extends React.PureComponent { } handleDoubleClick = () => { - window.ipcRenderer.send(DOUBLE_CLICK_ON_WINDOW); + window.desktop.doubleClickOnWindow(); } focusOnWebView = () => { - window.ipcRenderer.send(FOCUS_BROWSERVIEW); + window.desktop.focusBrowserView(); this.handleCloseDropdowns(); } reloadCurrentView = () => { - window.ipcRenderer.send(RELOAD_CURRENT_VIEW); + window.desktop.reloadCurrentView(); } showHideDownloadsBadge(value = false) { @@ -377,12 +346,12 @@ class MainPage extends React.PureComponent { } closeDownloadsDropdown() { - window.ipcRenderer.send(CLOSE_DOWNLOADS_DROPDOWN); - window.ipcRenderer.send(CLOSE_DOWNLOADS_DROPDOWN_MENU); + window.desktop.closeDownloadsDropdown(); + window.desktop.closeDownloadsDropdownMenu(); } openDownloadsDropdown() { - window.ipcRenderer.send(OPEN_DOWNLOADS_DROPDOWN); + window.desktop.openDownloadsDropdown(); } focusThreeDotsButton = () => { @@ -497,12 +466,12 @@ class MainPage extends React.PureComponent { } return sum + this.state.mentionCounts[key]; }, 0); - const totalUnreadCount = Object.keys(this.state.unreadCounts).reduce((sum, key) => { + const hasAnyUnreads = Object.keys(this.state.unreadCounts).reduce((sum, key) => { if (this.state.activeServerName && key.match(serverMatch)) { return sum; } - return sum + this.state.unreadCounts[key]; - }, 0); + return sum || this.state.unreadCounts[key]; + }, false); const topRow = ( { isDisabled={this.state.modalOpen} activeServerName={this.state.activeServerName} totalMentionCount={totalMentionCount} - hasUnreads={totalUnreadCount > 0} + hasUnreads={hasAnyUnreads} isMenuOpen={this.state.isMenuOpen} darkMode={this.state.darkMode} /> @@ -586,7 +555,7 @@ class MainPage extends React.PureComponent { darkMode={this.state.darkMode} show={this.state.showExtraBar} goBack={() => { - window.ipcRenderer.send(HISTORY, -1); + window.desktop.goBack(); }} /> diff --git a/src/renderer/components/SettingsPage.tsx b/src/renderer/components/SettingsPage.tsx index 50f1bef7..e999c028 100644 --- a/src/renderer/components/SettingsPage.tsx +++ b/src/renderer/components/SettingsPage.tsx @@ -12,28 +12,16 @@ import {FormattedMessage, injectIntl, IntlShape} from 'react-intl'; import ReactSelect, {ActionMeta, MultiValue} from 'react-select'; import {CombinedConfig, LocalConfiguration} from 'types/config'; +import {SaveQueueItem} from 'types/settings'; import {DeepPartial} from 'types/utils'; import {localeTranslations} from 'common/utils/constants'; -import { - GET_LOCAL_CONFIGURATION, - UPDATE_CONFIGURATION, - DOUBLE_CLICK_ON_WINDOW, - GET_DOWNLOAD_LOCATION, - RELOAD_CONFIGURATION, - GET_AVAILABLE_SPELL_CHECKER_LANGUAGES, - CHECK_FOR_UPDATES, - GET_AVAILABLE_LANGUAGES, -} from 'common/communication'; - import AutoSaveIndicator, {SavingState} from './AutoSaveIndicator'; const CONFIG_TYPE_UPDATES = 'updates'; const CONFIG_TYPE_APP_OPTIONS = 'appOptions'; -type ConfigType = typeof CONFIG_TYPE_UPDATES | typeof CONFIG_TYPE_APP_OPTIONS; - type Props = { intl: IntlShape; } @@ -54,12 +42,6 @@ type SavingStateItems = { updates: SavingState; } -type SaveQueueItem = { - configType: ConfigType; - key: keyof CombinedConfig; - data: CombinedConfig[keyof CombinedConfig]; -} - class SettingsPage extends React.PureComponent { trayIconThemeRef: React.RefObject; downloadLocationRef: React.RefObject; @@ -125,18 +107,18 @@ class SettingsPage extends React.PureComponent { } componentDidMount() { - window.ipcRenderer.on(RELOAD_CONFIGURATION, () => { + window.desktop.onReloadConfiguration(() => { this.updateSaveState(); this.getConfig(); }); - window.ipcRenderer.invoke(GET_AVAILABLE_SPELL_CHECKER_LANGUAGES).then((languages: string[]) => { + window.desktop.getAvailableSpellCheckerLanguages().then((languages: string[]) => { const availableSpellcheckerLanguages = languages.filter((language) => localeTranslations[language]).map((language) => ({label: localeTranslations[language], value: language})); availableSpellcheckerLanguages.sort((a, b) => a.label.localeCompare(b.label)); this.setState({availableSpellcheckerLanguages}); }); - window.ipcRenderer.invoke(GET_AVAILABLE_LANGUAGES).then((languages: string[]) => { + window.desktop.getAvailableLanguages().then((languages: string[]) => { const availableLanguages = languages.filter((language) => localeTranslations[language]).map((language) => ({label: localeTranslations[language], value: language})); availableLanguages.sort((a, b) => a.label.localeCompare(b.label)); this.setState({availableLanguages}); @@ -144,8 +126,8 @@ class SettingsPage extends React.PureComponent { } getConfig = () => { - window.ipcRenderer.invoke(GET_LOCAL_CONFIGURATION).then((config) => { - this.setState({ready: true, maximized: false, ...this.convertConfigDataToState(config, this.state) as Omit}); + window.desktop.getLocalConfiguration().then((config) => { + this.setState({ready: true, maximized: false, ...this.convertConfigDataToState(config as Partial, this.state) as Omit}); }); } @@ -159,7 +141,7 @@ class SettingsPage extends React.PureComponent { return newState; } - saveSetting = (configType: ConfigType, {key, data}: {key: keyof CombinedConfig; data: CombinedConfig[keyof CombinedConfig]}) => { + saveSetting = (configType: 'updates' | 'appOptions', {key, data}: {key: keyof CombinedConfig; data: CombinedConfig[keyof CombinedConfig]}) => { this.saveQueue.push({ configType, key, @@ -177,7 +159,7 @@ class SettingsPage extends React.PureComponent { this.savingIsDebounced = true; setTimeout(() => { this.savingIsDebounced = false; - window.ipcRenderer.send(UPDATE_CONFIGURATION, this.saveQueue.splice(0, this.saveQueue.length)); + window.desktop.updateConfiguration(this.saveQueue.splice(0, this.saveQueue.length)); }, 500); } @@ -353,7 +335,7 @@ class SettingsPage extends React.PureComponent { } checkForUpdates = () => { - window.ipcRenderer.send(CHECK_FOR_UPDATES); + window.desktop.checkForUpdates(); } handleChangeSpellCheckerLocales = (value: MultiValue<{label: string; value: string}>, actionMeta: ActionMeta<{label: string; value: string}>) => { @@ -402,7 +384,7 @@ class SettingsPage extends React.PureComponent { selectDownloadLocation = () => { if (!this.state.userOpenedDownloadDialog) { - window.ipcRenderer.invoke(GET_DOWNLOAD_LOCATION, this.state.downloadLocation).then((result) => this.saveDownloadLocation(result)); + window.desktop.getDownloadLocation(this.state.downloadLocation).then((result) => this.saveDownloadLocation(result)); this.setState({userOpenedDownloadDialog: true}); } this.setState({userOpenedDownloadDialog: false}); @@ -434,7 +416,7 @@ class SettingsPage extends React.PureComponent { } handleDoubleClick = () => { - window.ipcRenderer.send(DOUBLE_CLICK_ON_WINDOW, 'settings'); + window.desktop.doubleClickOnWindow('settings'); } render() { diff --git a/src/renderer/components/TabBar.tsx b/src/renderer/components/TabBar.tsx index 2b1c7e66..5234e713 100644 --- a/src/renderer/components/TabBar.tsx +++ b/src/renderer/components/TabBar.tsx @@ -21,7 +21,7 @@ type Props = { onCloseTab: (name: string) => void; tabs: Tab[]; sessionsExpired: Record; - unreadCounts: Record; + unreadCounts: Record; mentionCounts: Record; onDrop: (result: DropResult) => void; tabsDisabled?: boolean; diff --git a/src/renderer/components/TeamDropdownButton.tsx b/src/renderer/components/TeamDropdownButton.tsx index 8e15ec46..44794d9e 100644 --- a/src/renderer/components/TeamDropdownButton.tsx +++ b/src/renderer/components/TeamDropdownButton.tsx @@ -5,8 +5,6 @@ import classNames from 'classnames'; import React, {useEffect} from 'react'; import {FormattedMessage} from 'react-intl'; -import {CLOSE_TEAMS_DROPDOWN, OPEN_TEAMS_DROPDOWN} from 'common/communication'; - import '../css/components/TeamDropdownButton.scss'; type Props = { @@ -31,7 +29,11 @@ const TeamDropdownButton: React.FC = (props: Props) => { const handleToggleButton = (event: React.MouseEvent) => { event.preventDefault(); event.stopPropagation(); - window.ipcRenderer.send(isMenuOpen ? CLOSE_TEAMS_DROPDOWN : OPEN_TEAMS_DROPDOWN); + if (isMenuOpen) { + window.desktop.closeTeamsDropdown(); + } else { + window.desktop.openTeamsDropdown(); + } }; let badgeDiv: React.ReactNode; diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx index 678e23bf..9167db6b 100644 --- a/src/renderer/index.tsx +++ b/src/renderer/index.tsx @@ -10,8 +10,6 @@ import ReactDOM from 'react-dom'; import {CombinedConfig, Team} from 'types/config'; -import {GET_CONFIGURATION, UPDATE_TEAMS, QUIT, RELOAD_CONFIGURATION, OPEN_APP_MENU} from 'common/communication'; - import MainPage from './components/MainPage'; import IntlProvider from './intl_provider'; @@ -28,11 +26,11 @@ class Root extends React.PureComponent, State> { async componentDidMount() { await this.setInitialConfig(); - window.ipcRenderer.on('synchronize-config', () => { + window.desktop.onSynchronizeConfig(() => { this.reloadConfig(); }); - window.ipcRenderer.on(RELOAD_CONFIGURATION, () => { + window.desktop.onReloadConfiguration(() => { this.reloadConfig(); }); @@ -84,7 +82,7 @@ class Root extends React.PureComponent, State> { }; teamConfigChange = async (updatedTeams: Team[]) => { - window.ipcRenderer.invoke(UPDATE_TEAMS, updatedTeams).then(() => { + window.desktop.updateTeams(updatedTeams).then(() => { this.reloadConfig(); }); }; @@ -97,20 +95,20 @@ class Root extends React.PureComponent, State> { requestConfig = async (exitOnError?: boolean) => { // todo: should we block? try { - const configRequest = await window.ipcRenderer.invoke(GET_CONFIGURATION); + const configRequest = await window.desktop.getConfiguration() as CombinedConfig; return configRequest; } catch (err: any) { console.log(`there was an error with the config: ${err}`); if (exitOnError) { - window.ipcRenderer.send(QUIT, `unable to load configuration: ${err}`, err.stack); + window.desktop.quit(`unable to load configuration: ${err}`, err.stack); } } - return null; + return undefined; }; openMenu = () => { if (window.process.platform !== 'darwin') { - window.ipcRenderer.send(OPEN_APP_MENU); + window.desktop.openAppMenu(); } } @@ -134,7 +132,7 @@ class Root extends React.PureComponent, State> { ); } } -window.ipcRenderer.invoke('get-app-version').then(({name, version}) => { +window.desktop.getVersion().then(({name, version}) => { // eslint-disable-next-line no-undef // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore diff --git a/src/renderer/settings.tsx b/src/renderer/settings.tsx index 495215fa..7da49b01 100644 --- a/src/renderer/settings.tsx +++ b/src/renderer/settings.tsx @@ -9,8 +9,6 @@ import 'renderer/css/settings.css'; import React from 'react'; import ReactDOM from 'react-dom'; -import {DARK_MODE_CHANGE, GET_DARK_MODE} from 'common/communication'; - import darkStyles from 'renderer/css/lazy/settings-dark.lazy.css'; import SettingsPage from './components/SettingsPage'; @@ -24,8 +22,8 @@ const setDarkMode = (darkMode: boolean) => { } }; -window.ipcRenderer.on(DARK_MODE_CHANGE, (_, darkMode) => setDarkMode(darkMode)); -window.ipcRenderer.invoke(GET_DARK_MODE).then(setDarkMode); +window.desktop.onDarkModeChange((darkMode) => setDarkMode(darkMode)); +window.desktop.getDarkMode().then(setDarkMode); const start = async () => { ReactDOM.render( diff --git a/src/types/settings.ts b/src/types/settings.ts new file mode 100644 index 00000000..637ca5cb --- /dev/null +++ b/src/types/settings.ts @@ -0,0 +1,10 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {CombinedConfig} from './config'; + +export type SaveQueueItem = { + configType: 'updates' | 'appOptions'; + key: keyof CombinedConfig; + data: CombinedConfig[keyof CombinedConfig]; +}; diff --git a/src/types/window.ts b/src/types/window.ts index cb188ebf..de648319 100644 --- a/src/types/window.ts +++ b/src/types/window.ts @@ -3,6 +3,10 @@ import {ipcRenderer} from 'electron/renderer'; +import {CombinedConfig, LocalConfiguration, Team} from './config'; +import {DownloadedItems} from './downloads'; +import {SaveQueueItem} from './settings'; + declare global { interface Window { ipcRenderer: { @@ -23,5 +27,62 @@ declare global { mas: { getThumbnailLocation: (location: string) => Promise; }; + desktop: { + quit: (reason: string, stack: string) => void; + openAppMenu: () => void; + closeTeamsDropdown: () => void; + openTeamsDropdown: () => void; + switchTab: (serverName: string, tabName: string) => void; + closeTab: (serverName: string, tabName: string) => void; + closeWindow: () => void; + minimizeWindow: () => void; + maximizeWindow: () => void; + restoreWindow: () => void; + doubleClickOnWindow: (windowName?: string) => void; + focusBrowserView: () => void; + reloadCurrentView: () => void; + closeDownloadsDropdown: () => void; + closeDownloadsDropdownMenu: () => void; + openDownloadsDropdown: () => void; + goBack: () => void; + checkForUpdates: () => void; + updateConfiguration: (saveQueueItems: SaveQueueItem[]) => void; + + updateTeams: (updatedTeams: Team[]) => Promise; + getConfiguration: (option?: keyof CombinedConfig) => Promise; + getVersion: () => Promise<{name: string; version: string}>; + getDarkMode: () => Promise; + requestHasDownloads: () => Promise; + getFullScreenStatus: () => Promise; + getAvailableSpellCheckerLanguages: () => Promise; + getAvailableLanguages: () => Promise; + getLocalConfiguration: (option?: keyof LocalConfiguration) => Promise>; + getDownloadLocation: (downloadLocation?: string) => Promise; + + onSynchronizeConfig: (listener: () => void) => void; + onReloadConfiguration: (listener: () => void) => void; + onDarkModeChange: (listener: (darkMode: boolean) => void) => void; + onLoadRetry: (listener: (viewName: string, retry: Date, err: string, loadUrl: string) => void) => void; + onLoadSuccess: (listener: (viewName: string) => void) => void; + onLoadFailed: (listener: (viewName: string, err: string, loadUrl: string) => void) => void; + onSetActiveView: (listener: (serverName: string, tabName: string) => void) => void; + onMaximizeChange: (listener: (maximize: boolean) => void) => void; + onEnterFullScreen: (listener: () => void) => void; + onLeaveFullScreen: (listener: () => void) => void; + onPlaySound: (listener: (soundName: string) => void) => void; + onModalOpen: (listener: () => void) => void; + onModalClose: (listener: () => void) => void; + onToggleBackButton: (listener: (showExtraBar: boolean) => void) => void; + onUpdateMentions: (listener: (view: string, mentions: number, unreads: boolean, isExpired: boolean) => void) => void; + onCloseTeamsDropdown: (listener: () => void) => void; + onOpenTeamsDropdown: (listener: () => void) => void; + onCloseDownloadsDropdown: (listener: () => void) => void; + onOpenDownloadsDropdown: (listener: () => void) => void; + onShowDownloadsDropdownButtonBadge: (listener: () => void) => void; + onHideDownloadsDropdownButtonBadge: (listener: () => void) => void; + onUpdateDownloadsDropdown: (listener: (downloads: DownloadedItems) => void) => void; + onAppMenuWillClose: (listener: () => void) => void; + onFocusThreeDotMenu: (listener: () => void) => void; + }; } }