From eb64ff083260cb4f235d2f672e2e4ecf788425f7 Mon Sep 17 00:00:00 2001 From: John Knox Date: Mon, 7 Oct 2019 08:49:05 -0700 Subject: [PATCH] Add settings UI Summary: Adds a simple UI for editing settings, a reducer and persistance config for the data. These values aren't yet used for anything. Reviewed By: passy Differential Revision: D17684490 fbshipit-source-id: e76ac43ffa17d3606e59f4a1ccb940e8d9fbd9e8 --- src/App.tsx | 4 + src/MenuBar.tsx | 6 ++ src/chrome/SettingsSheet.tsx | 168 +++++++++++++++++++++++++++++++++++ src/chrome/TitleBar.tsx | 7 ++ src/reducers/application.tsx | 2 + src/reducers/index.tsx | 10 +++ src/reducers/settings.tsx | 49 ++++++++++ src/utils/icons.js | 3 + 8 files changed, 249 insertions(+) create mode 100644 src/chrome/SettingsSheet.tsx create mode 100644 src/reducers/settings.tsx diff --git a/src/App.tsx b/src/App.tsx index c086e7e28..204e1c829 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -26,6 +26,7 @@ import { ACTIVE_SHEET_PLUGINS, ACTIVE_SHEET_SHARE_DATA, ACTIVE_SHEET_SIGN_IN, + ACTIVE_SHEET_SETTINGS, ACTIVE_SHEET_SHARE_DATA_IN_FILE, ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT, ACTIVE_SHEET_PLUGIN_SHEET, @@ -37,6 +38,7 @@ import {StaticView} from './reducers/connections'; import PluginManager from './chrome/PluginManager'; import BaseDevice from './devices/BaseDevice'; import StatusBar from './chrome/StatusBar'; +import SettingsSheet from './chrome/SettingsSheet'; const version = remote.app.getVersion(); type OwnProps = { @@ -88,6 +90,8 @@ export class App extends React.Component { return ; case ACTIVE_SHEET_SIGN_IN: return ; + case ACTIVE_SHEET_SETTINGS: + return ; case ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT: return ; case ACTIVE_SHEET_SHARE_DATA_IN_FILE: diff --git a/src/MenuBar.tsx b/src/MenuBar.tsx index 0cc2489b2..8117ab745 100644 --- a/src/MenuBar.tsx +++ b/src/MenuBar.tsx @@ -11,6 +11,7 @@ import { setSelectPluginsToExportActiveSheet, setActiveSheet, ACTIVE_SHEET_PLUGINS, + ACTIVE_SHEET_SETTINGS, } from './reducers/application'; import {Store} from './reducers/'; import electron, {MenuItemConstructorOptions} from 'electron'; @@ -214,6 +215,11 @@ function getTemplate( { label: 'File', submenu: [ + { + label: 'Preferences', + accelerator: 'Cmd+,', + click: () => store.dispatch(setActiveSheet(ACTIVE_SHEET_SETTINGS)), + }, { label: 'Import Flipper File...', accelerator: 'CommandOrControl+O', diff --git a/src/chrome/SettingsSheet.tsx b/src/chrome/SettingsSheet.tsx new file mode 100644 index 000000000..2f3402ad8 --- /dev/null +++ b/src/chrome/SettingsSheet.tsx @@ -0,0 +1,168 @@ +/** + * Copyright 2018-present Facebook. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * @format + */ + +import { + FlexColumn, + Button, + styled, + Text, + FlexRow, + Spacer, + Input, + colors, + Glyph, +} from 'flipper'; +import React, {Component, useState} from 'react'; +import {updateSettings, Action} from '../reducers/settings'; +import {connect} from 'react-redux'; +import {State as Store} from '../reducers'; +import {Settings} from '../reducers/settings'; +import {promises as fs} from 'fs'; +import {remote} from 'electron'; +import path from 'path'; + +const Container = styled(FlexColumn)({ + padding: 20, + width: 800, +}); + +const Title = styled(Text)({ + marginBottom: 18, + marginRight: 10, + fontWeight: 100, + fontSize: '40px', +}); + +const InfoText = styled(Text)({ + lineHeight: 1.35, + paddingTop: 5, +}); + +const FileInputBox = styled(Input)(({isValid}: {isValid: boolean}) => ({ + marginRight: 0, + flexGrow: 1, + fontFamily: 'monospace', + color: isValid ? undefined : colors.red, + marginLeft: 10, + marginTop: 'auto', + marginBottom: 'auto', +})); + +const CenteredGlyph = styled(Glyph)({ + margin: 'auto', + marginLeft: 10, +}); + +type OwnProps = { + onHide: () => void; +}; + +type StateFromProps = { + settings: Settings; +}; + +type DispatchFromProps = { + updateSettings: (settings: Settings) => Action; +}; + +type State = { + updatedSettings: Settings; +}; + +function FilePathConfigField(props: { + label: string; + defaultValue: string; + onChange: (path: string) => void; +}) { + const [value, setValue] = useState(props.defaultValue); + const [isValid, setIsValid] = useState(true); + fs.stat(value) + .then(stat => setIsValid(stat.isDirectory())) + .catch(_ => setIsValid(false)); + return ( + + {props.label} + { + setValue(e.target.value); + props.onChange(e.target.value); + fs.stat(e.target.value) + .then(stat => setIsValid(stat.isDirectory())) + .catch(_ => setIsValid(false)); + }} + /> + + remote.dialog.showOpenDialog( + { + properties: ['openDirectory', 'showHiddenFiles'], + defaultPath: path.resolve('/'), + }, + (paths: Array | undefined) => { + paths && setValue(paths[0]); + paths && props.onChange(paths[0]); + }, + ) + }> + + + {isValid ? null : ( + + )} + + ); +} + +type Props = OwnProps & StateFromProps & DispatchFromProps; +class SignInSheet extends Component { + state = { + updatedSettings: {...this.props.settings}, + }; + + applyChanges = async () => { + this.props.updateSettings(this.state.updatedSettings); + this.props.onHide(); + }; + + render() { + return ( + + Settings + { + this.setState({ + updatedSettings: { + ...this.state.updatedSettings, + androidHome: v, + }, + }); + }} + /> +
+ + + + + +
+ ); + } +} + +export default connect( + ({settingsState}) => ({settings: settingsState.settings}), + {updateSettings}, +)(SignInSheet); diff --git a/src/chrome/TitleBar.tsx b/src/chrome/TitleBar.tsx index 180c7bf0f..9e150aa37 100644 --- a/src/chrome/TitleBar.tsx +++ b/src/chrome/TitleBar.tsx @@ -14,6 +14,7 @@ import { toggleRightSidebarVisible, ACTIVE_SHEET_BUG_REPORTER, setFlipperRating, + ACTIVE_SHEET_SETTINGS, } from '../reducers/application'; import { colors, @@ -181,6 +182,12 @@ class TitleBar extends React.Component { icon="bug" /> )} +