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
This commit is contained in:
Michel Weststrate
2021-10-26 12:06:18 -07:00
committed by Facebook GitHub Bot
parent d5e4b0c360
commit 9763af4c96
12 changed files with 106 additions and 103 deletions

View File

@@ -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) {
click: function (_, _focusedWindow: electron.BrowserWindow | undefined) {
logger.track('usage', 'reload');
focusedWindow.reload();
}
reloadFlipper();
},
},
{

View File

@@ -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<string | undefined>;
showSaveDialog?: FlipperLib['showSaveDialog'];
showOpenDialog?: FlipperLib['showOpenDialog'];
showSelectDirectoryDialog?(defaultPath?: string): Promise<string | undefined>;
registerShortcut(shortCut: string, callback: () => void): void;
hasFocus(): boolean;
onIpcEvent<Event extends keyof MainProcessEvents>(
@@ -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;
},
});
}

View File

@@ -99,11 +99,11 @@ export function FilePathConfigField(props: {
.catch((_) => setIsValid(false));
}}
/>
{renderHost.selectDirectory && (
{renderHost.showSelectDirectoryDialog && (
<FlexColumn
onClick={() => {
renderHost
.selectDirectory?.()
.showSelectDirectoryDialog?.()
.then((path) => {
if (path) {
setValue(path);

View File

@@ -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;
},
});
}

View File

@@ -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',

View File

@@ -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<T extends string>(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({
/>
<GlyphContainer
onClick={() =>
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);
}
})
}>

View File

@@ -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) {
return getRenderHostInstance()
.showOpenDialog?.({
filter: {extensions: ['flipper', 'json', 'txt'], name: 'Flipper files'},
})
.then((filePath) => {
if (filePath) {
tryCatchReportPlatformFailures(() => {
importFileToStore(filePaths[0], store);
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,
{
const file = await getRenderHostInstance().showSaveDialog?.({
title: 'FlipperExport',
defaultPath: path.join(os.homedir(), 'FlipperExport.flipper'),
},
);
const file = result.filePath;
});
if (!file) {
return;
}

View File

@@ -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,
});
}

View File

@@ -7,5 +7,4 @@
* @format
*/
import {remote} from 'electron';
export default () => remote.getCurrentWindow().reload();
export default () => window.location.reload();

View File

@@ -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;
const darkMode = useStore((state) => state.settingsState.darkMode);
if (darkMode === 'dark') {
return true;
} else if (darkMode === 'light') {
return false;
} else if (darkMode === 'system') {
return remote.nativeTheme.shouldUseDarkColors;
return getRenderHostInstance().shouldUseDarkColors();
}
return false;
});
}

View File

@@ -35,6 +35,19 @@ export interface FlipperLib {
DetailsSidebarImplementation?(
props: DetailSidebarProps,
): React.ReactElement | null;
showSaveDialog?(options: {
defaultPath?: string;
message?: string;
title?: string;
}): Promise<string | undefined>;
showOpenDialog?(options: {
defaultPath?: string;
filter?: {
extensions: string[];
name: string;
};
}): Promise<string | undefined>;
showSelectDirectoryDialog?(defaultPath?: string): Promise<string | undefined>;
}
export let flipperLibInstance: FlipperLib | undefined;

View File

@@ -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,
{
getFlipperLib()
.showSaveDialog?.({
title: 'Export Routes',
defaultPath: 'NetworkPluginRoutesExport.json',
},
)
.then((result: electron.SaveDialogReturnValue) => {
const file = result.filePath;
})
.then((file) => {
if (!file) {
return;
}