From 9763af4c962208c3d5a2a118ee0fb8c56014fed0 Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Tue, 26 Oct 2021 12:06:18 -0700 Subject: [PATCH] Decouple open/save dialogs, reload, shouldUseDarkColors from Electron Summary: Per title. Less imports from Electron. Reviewed By: timur-valiev, aigoncharov Differential Revision: D31923504 fbshipit-source-id: dc7557cf7c88c0c8168ba22f7dca7b3e2d339a09 --- desktop/app/src/MenuBar.tsx | 9 ++--- desktop/app/src/RenderHost.tsx | 9 ++++- .../app/src/chrome/settings/configFields.tsx | 4 +- desktop/app/src/init.tsx | 36 ++++++++++------- desktop/app/src/test-utils/MockFlipper.tsx | 7 +++- .../app/src/ui/components/FileSelector.tsx | 28 +++---------- desktop/app/src/utils/exportData.tsx | 39 ++++++++----------- .../src/utils/flipperLibImplementation.tsx | 5 +++ desktop/app/src/utils/reloadFlipper.tsx | 3 +- desktop/app/src/utils/useIsDarkMode.tsx | 20 +++++----- .../flipper-plugin/src/plugin/FlipperLib.tsx | 13 +++++++ .../request-mocking/NetworkRouteManager.tsx | 36 +++++++---------- 12 files changed, 106 insertions(+), 103 deletions(-) diff --git a/desktop/app/src/MenuBar.tsx b/desktop/app/src/MenuBar.tsx index af6ab51ee..62c3ef0c8 100644 --- a/desktop/app/src/MenuBar.tsx +++ b/desktop/app/src/MenuBar.tsx @@ -37,6 +37,7 @@ import React from 'react'; import ChangelogSheet from './chrome/ChangelogSheet'; import PluginManager from './chrome/plugin-manager/PluginManager'; import SettingsSheet from './chrome/SettingsSheet'; +import reloadFlipper from './utils/reloadFlipper'; export type DefaultKeyboardAction = keyof typeof _buildInMenuEntries; export type TopLevelMenu = 'Edit' | 'View' | 'Window' | 'Help'; @@ -294,11 +295,9 @@ function getTemplate( { label: 'Reload', accelerator: 'CmdOrCtrl+R', - click: function (_, focusedWindow: electron.BrowserWindow | undefined) { - if (focusedWindow) { - logger.track('usage', 'reload'); - focusedWindow.reload(); - } + click: function (_, _focusedWindow: electron.BrowserWindow | undefined) { + logger.track('usage', 'reload'); + reloadFlipper(); }, }, { diff --git a/desktop/app/src/RenderHost.tsx b/desktop/app/src/RenderHost.tsx index b5b564356..b985ad531 100644 --- a/desktop/app/src/RenderHost.tsx +++ b/desktop/app/src/RenderHost.tsx @@ -10,6 +10,7 @@ import {NotificationEvents} from './dispatcher/notifications'; import {PluginNotification} from './reducers/notifications'; import type {NotificationConstructorOptions} from 'electron'; +import {FlipperLib} from 'flipper-plugin'; // Events that are emitted from the main.ts ovr the IPC process bridge in Electron type MainProcessEvents = { @@ -44,7 +45,9 @@ type ChildProcessEvents = { export interface RenderHost { readonly processId: number; readTextFromClipboard(): string | undefined; - selectDirectory?(defaultPath?: string): Promise; + showSaveDialog?: FlipperLib['showSaveDialog']; + showOpenDialog?: FlipperLib['showOpenDialog']; + showSelectDirectoryDialog?(defaultPath?: string): Promise; registerShortcut(shortCut: string, callback: () => void): void; hasFocus(): boolean; onIpcEvent( @@ -55,6 +58,7 @@ export interface RenderHost { event: Event, ...args: ChildProcessEvents[Event] ): void; + shouldUseDarkColors(): boolean; } let renderHostInstance: RenderHost | undefined; @@ -82,5 +86,8 @@ if (process.env.NODE_ENV === 'test') { }, onIpcEvent() {}, sendIpcEvent() {}, + shouldUseDarkColors() { + return false; + }, }); } diff --git a/desktop/app/src/chrome/settings/configFields.tsx b/desktop/app/src/chrome/settings/configFields.tsx index 97f404669..7108689b1 100644 --- a/desktop/app/src/chrome/settings/configFields.tsx +++ b/desktop/app/src/chrome/settings/configFields.tsx @@ -99,11 +99,11 @@ export function FilePathConfigField(props: { .catch((_) => setIsValid(false)); }} /> - {renderHost.selectDirectory && ( + {renderHost.showSelectDirectoryDialog && ( { renderHost - .selectDirectory?.() + .showSelectDirectoryDialog?.() .then((path) => { if (path) { setValue(path); diff --git a/desktop/app/src/init.tsx b/desktop/app/src/init.tsx index 1100dffb9..c1d017d50 100644 --- a/desktop/app/src/init.tsx +++ b/desktop/app/src/init.tsx @@ -205,7 +205,7 @@ function init() { setPersistor(persistor); - initializeFlipperLibImplementation(store, logger); + initializeFlipperLibImplementation(getRenderHostInstance(), store, logger); _setGlobalInteractionReporter((r) => { logger.track('usage', 'interaction', r); if (!isProduction()) { @@ -230,26 +230,20 @@ function init() { sideEffect( store, {name: 'loadTheme', fireImmediately: false, throttleMs: 500}, - (state) => { - const theme = state.settingsState.darkMode; + (state) => state.settingsState.darkMode, + (theme) => { let shouldUseDarkMode = false; if (theme === 'dark') { shouldUseDarkMode = true; } else if (theme === 'light') { shouldUseDarkMode = false; } else if (theme === 'system') { - shouldUseDarkMode = remote.nativeTheme.shouldUseDarkColors; + shouldUseDarkMode = getRenderHostInstance().shouldUseDarkColors(); } - return { - shouldUseDarkMode: shouldUseDarkMode, - theme: theme, - }; - }, - (result) => { ( document.getElementById('flipper-theme-import') as HTMLLinkElement - ).href = `themes/${result.shouldUseDarkMode ? 'dark' : 'light'}.css`; - getRenderHostInstance().sendIpcEvent('setTheme', result.theme); + ).href = `themes/${shouldUseDarkMode ? 'dark' : 'light'}.css`; + getRenderHostInstance().sendIpcEvent('setTheme', theme); }, ); } @@ -272,10 +266,21 @@ function initializeFlipperForElectron() { readTextFromClipboard() { return clipboard.readText(); }, - selectDirectory(defaultPath = path.resolve('/')) { + async showSaveDialog(options) { + return (await remote.dialog.showSaveDialog(options))?.filePath; + }, + async showOpenDialog({filter, defaultPath}) { + const result = await remote.dialog.showOpenDialog({ + defaultPath, + properties: ['openFile'], + filters: filter ? [filter] : undefined, + }); + return result.filePaths?.[0]; + }, + showSelectDirectoryDialog(defaultPath = path.resolve('/')) { return remote.dialog .showOpenDialog({ - properties: ['openDirectory', 'showHiddenFiles'], + properties: ['openDirectory'], defaultPath, }) .then((result: SaveDialogReturnValue & {filePaths: string[]}) => { @@ -305,5 +310,8 @@ function initializeFlipperForElectron() { sendIpcEvent(event, ...args: any[]) { ipcRenderer.send(event, ...args); }, + shouldUseDarkColors() { + return remote.nativeTheme.shouldUseDarkColors; + }, }); } diff --git a/desktop/app/src/test-utils/MockFlipper.tsx b/desktop/app/src/test-utils/MockFlipper.tsx index f5fa9b961..bc4d53a31 100644 --- a/desktop/app/src/test-utils/MockFlipper.tsx +++ b/desktop/app/src/test-utils/MockFlipper.tsx @@ -28,6 +28,7 @@ import ArchivedDevice from '../devices/ArchivedDevice'; import {ClientQuery, DeviceOS} from 'flipper-common'; import {TestDevice} from './TestDevice'; import {createFlipperServerMock} from './createFlipperServerMock'; +import {getRenderHostInstance} from '../RenderHost'; export interface AppOptions { plugins?: PluginDefinition[]; @@ -89,7 +90,11 @@ export default class MockFlipper { this.unsubscribePluginManager = pluginManager(this._store, this._logger, { runSideEffectsSynchronously: true, }); - initializeFlipperLibImplementation(this._store, this._logger); + initializeFlipperLibImplementation( + getRenderHostInstance(), + this._store, + this._logger, + ); this._store.dispatch(registerPlugins(plugins ?? [])); this._store.dispatch({ type: 'SET_FLIPPER_SERVER', diff --git a/desktop/app/src/ui/components/FileSelector.tsx b/desktop/app/src/ui/components/FileSelector.tsx index 94bbaf66f..3f593b646 100644 --- a/desktop/app/src/ui/components/FileSelector.tsx +++ b/desktop/app/src/ui/components/FileSelector.tsx @@ -11,12 +11,11 @@ import React, {useState} from 'react'; import FlexRow from './FlexRow'; import Glyph from './Glyph'; import Input from './Input'; -import electron from 'electron'; import styled from '@emotion/styled'; import {colors} from './colors'; -import Electron from 'electron'; import fs from 'fs'; import {Tooltip} from '..'; +import {getFlipperLib} from 'flipper-plugin'; const CenteredGlyph = styled(Glyph)({ margin: 'auto', @@ -41,40 +40,25 @@ const FileInputBox = styled(Input)<{isValid: boolean}>(({isValid}) => ({ }, })); -function strToArr(item: T): T[] { - return [item]; -} - export interface Props { onPathChanged: (evtArgs: {path: string; isValid: boolean}) => void; placeholderText: string; defaultPath: string; - showHiddenFiles: boolean; } const defaultProps: Props = { onPathChanged: (_) => {}, placeholderText: '', defaultPath: '/', - showHiddenFiles: false, }; export default function FileSelector({ onPathChanged, placeholderText, - defaultPath, - showHiddenFiles, }: Props) { const [value, setValue] = useState(''); const [isValid, setIsValid] = useState(false); - const options: Electron.OpenDialogOptions = { - properties: [ - 'openFile', - ...(showHiddenFiles ? strToArr('showHiddenFiles') : []), - ], - defaultPath, - }; const onChange = (path: string) => { setValue(path); let isNewPathValid = false; @@ -103,11 +87,11 @@ export default function FileSelector({ /> - electron.remote.dialog - .showOpenDialog(options) - .then((result: electron.OpenDialogReturnValue) => { - if (result && !result.canceled && result.filePaths.length) { - onChange(result.filePaths[0]); + getFlipperLib() + .showOpenDialog?.({defaultPath}) + .then((path) => { + if (path) { + onChange(path); } }) }> diff --git a/desktop/app/src/utils/exportData.tsx b/desktop/app/src/utils/exportData.tsx index 056f81ca3..4fc68cf64 100644 --- a/desktop/app/src/utils/exportData.tsx +++ b/desktop/app/src/utils/exportData.tsx @@ -10,7 +10,6 @@ import * as React from 'react'; import os from 'os'; import path from 'path'; -import electron from 'electron'; import {getLogger} from 'flipper-common'; import {Store, MiddlewareAPI} from '../reducers'; import {DeviceExport} from '../devices/BaseDevice'; @@ -24,7 +23,6 @@ import {default as BaseDevice} from '../devices/BaseDevice'; import {default as ArchivedDevice} from '../devices/ArchivedDevice'; import fs from 'fs'; import {v4 as uuidv4} from 'uuid'; -import {remote, OpenDialogOptions} from 'electron'; import {readCurrentRevision} from './packageMetadata'; import {tryCatchReportPlatformFailures} from 'flipper-common'; import {promisify} from 'util'; @@ -45,6 +43,7 @@ import {ClientQuery} from 'flipper-common'; import ShareSheetExportUrl from '../chrome/ShareSheetExportUrl'; import ShareSheetExportFile from '../chrome/ShareSheetExportFile'; import ExportDataPluginSheet from '../chrome/ExportDataPluginSheet'; +import {getRenderHostInstance} from '../RenderHost'; export const IMPORT_FLIPPER_TRACE_EVENT = 'import-flipper-trace'; export const EXPORT_FLIPPER_TRACE_EVENT = 'export-flipper-trace'; @@ -602,30 +601,24 @@ export const importFileToStore = (file: string, store: Store) => { }; export function showOpenDialog(store: Store) { - const options: OpenDialogOptions = { - properties: ['openFile'], - filters: [{extensions: ['flipper', 'json', 'txt'], name: 'Flipper files'}], - }; - remote.dialog.showOpenDialog(options).then((result) => { - const filePaths = result.filePaths; - if (filePaths.length > 0) { - tryCatchReportPlatformFailures(() => { - importFileToStore(filePaths[0], store); - }, `${IMPORT_FLIPPER_TRACE_EVENT}:UI`); - } - }); + return getRenderHostInstance() + .showOpenDialog?.({ + filter: {extensions: ['flipper', 'json', 'txt'], name: 'Flipper files'}, + }) + .then((filePath) => { + if (filePath) { + tryCatchReportPlatformFailures(() => { + importFileToStore(filePath, store); + }, `${IMPORT_FLIPPER_TRACE_EVENT}:UI`); + } + }); } export async function startFileExport(dispatch: Store['dispatch']) { - const result = await electron.remote.dialog.showSaveDialog( - // @ts-ignore This appears to work but isn't allowed by the types - null, - { - title: 'FlipperExport', - defaultPath: path.join(os.homedir(), 'FlipperExport.flipper'), - }, - ); - const file = result.filePath; + const file = await getRenderHostInstance().showSaveDialog?.({ + title: 'FlipperExport', + defaultPath: path.join(os.homedir(), 'FlipperExport.flipper'), + }); if (!file) { return; } diff --git a/desktop/app/src/utils/flipperLibImplementation.tsx b/desktop/app/src/utils/flipperLibImplementation.tsx index 926eac01b..69280aa6d 100644 --- a/desktop/app/src/utils/flipperLibImplementation.tsx +++ b/desktop/app/src/utils/flipperLibImplementation.tsx @@ -18,8 +18,10 @@ import constants from '../fb-stubs/constants'; import {addNotification} from '../reducers/notifications'; import {deconstructPluginKey} from 'flipper-common'; import {DetailSidebarImpl} from '../sandy-chrome/DetailSidebarImpl'; +import {RenderHost} from '../RenderHost'; export function initializeFlipperLibImplementation( + renderHost: RenderHost, store: Store, logger: Logger, ) { @@ -64,5 +66,8 @@ export function initializeFlipperLibImplementation( ); }, DetailsSidebarImplementation: DetailSidebarImpl, + showSaveDialog: renderHost.showSaveDialog, + showOpenDialog: renderHost.showOpenDialog, + showSelectDirectoryDialog: renderHost.showSelectDirectoryDialog, }); } diff --git a/desktop/app/src/utils/reloadFlipper.tsx b/desktop/app/src/utils/reloadFlipper.tsx index 244e48155..bb8556065 100644 --- a/desktop/app/src/utils/reloadFlipper.tsx +++ b/desktop/app/src/utils/reloadFlipper.tsx @@ -7,5 +7,4 @@ * @format */ -import {remote} from 'electron'; -export default () => remote.getCurrentWindow().reload(); +export default () => window.location.reload(); diff --git a/desktop/app/src/utils/useIsDarkMode.tsx b/desktop/app/src/utils/useIsDarkMode.tsx index 220d2b93b..c631fa9b2 100644 --- a/desktop/app/src/utils/useIsDarkMode.tsx +++ b/desktop/app/src/utils/useIsDarkMode.tsx @@ -8,7 +8,7 @@ */ import {useStore} from './useStore'; -import {remote} from 'electron'; +import {getRenderHostInstance} from '../RenderHost'; /** * This hook returns whether dark mode is currently being used. @@ -16,15 +16,13 @@ import {remote} from 'electron'; * which will provide colors that reflect the theme */ export function useIsDarkMode(): boolean { - return useStore((state) => { - const darkMode = state.settingsState.darkMode; - if (darkMode === 'dark') { - return true; - } else if (darkMode === 'light') { - return false; - } else if (darkMode === 'system') { - return remote.nativeTheme.shouldUseDarkColors; - } + const darkMode = useStore((state) => state.settingsState.darkMode); + if (darkMode === 'dark') { + return true; + } else if (darkMode === 'light') { return false; - }); + } else if (darkMode === 'system') { + return getRenderHostInstance().shouldUseDarkColors(); + } + return false; } diff --git a/desktop/flipper-plugin/src/plugin/FlipperLib.tsx b/desktop/flipper-plugin/src/plugin/FlipperLib.tsx index 0c3efd6c9..a76df8e8b 100644 --- a/desktop/flipper-plugin/src/plugin/FlipperLib.tsx +++ b/desktop/flipper-plugin/src/plugin/FlipperLib.tsx @@ -35,6 +35,19 @@ export interface FlipperLib { DetailsSidebarImplementation?( props: DetailSidebarProps, ): React.ReactElement | null; + showSaveDialog?(options: { + defaultPath?: string; + message?: string; + title?: string; + }): Promise; + showOpenDialog?(options: { + defaultPath?: string; + filter?: { + extensions: string[]; + name: string; + }; + }): Promise; + showSelectDirectoryDialog?(defaultPath?: string): Promise; } export let flipperLibInstance: FlipperLib | undefined; diff --git a/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx b/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx index 57b76d662..3ed289303 100644 --- a/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx +++ b/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx @@ -10,7 +10,7 @@ import fs from 'fs'; // eslint-disable-next-line import electron, {OpenDialogOptions, remote} from 'electron'; -import {Atom, DataTableManager} from 'flipper-plugin'; +import {Atom, DataTableManager, getFlipperLib} from 'flipper-plugin'; import {createContext} from 'react'; import {Header, Request} from '../types'; import {message} from 'antd'; @@ -137,16 +137,13 @@ export function createNetworkManager( informClientMockChange(routes.get()); }, importRoutes() { - const options: OpenDialogOptions = { - properties: ['openFile'], - filters: [{extensions: ['json'], name: 'Flipper Route Files'}], - }; - remote.dialog - .showOpenDialog(options) - .then((result) => { - const filePaths = result.filePaths; - if (filePaths.length > 0) { - fs.readFile(filePaths[0], 'utf8', (err, data) => { + getFlipperLib() + .showOpenDialog?.({ + filter: {extensions: ['json'], name: 'Flipper Route Files'}, + }) + .then((filePath) => { + if (filePath) { + fs.readFile(filePath, 'utf8', (err, data) => { if (err) { message.error('Unable to import file'); return; @@ -177,17 +174,12 @@ export function createNetworkManager( ); }, exportRoutes() { - remote.dialog - .showSaveDialog( - // @ts-ignore This appears to work but isn't allowed by the types - null, - { - title: 'Export Routes', - defaultPath: 'NetworkPluginRoutesExport.json', - }, - ) - .then((result: electron.SaveDialogReturnValue) => { - const file = result.filePath; + getFlipperLib() + .showSaveDialog?.({ + title: 'Export Routes', + defaultPath: 'NetworkPluginRoutesExport.json', + }) + .then((file) => { if (!file) { return; }