showing multiple sheets

Summary:
The sheet was only used for the bug-reporter before and we had an explicit boolean flag in the redux store to keep track if the bug reporter is shown.

Changing this it an `activeSheet` property, which allows us to show arbitrary sheets, while making sure to only show one at a time.

Reviewed By: jknoxville

Differential Revision: D13516985

fbshipit-source-id: 3e83f719e2b61d0b2229268ebfdc910123b403d2
This commit is contained in:
Daniel Büchele
2018-12-20 06:07:55 -08:00
committed by Facebook Github Bot
parent 6827515329
commit fa9b85b065
5 changed files with 98 additions and 74 deletions

View File

@@ -20,14 +20,15 @@ import {ipcRenderer} from 'electron';
import type Logger from './fb-stubs/Logger.js'; import type Logger from './fb-stubs/Logger.js';
import type BugReporter from './fb-stubs/BugReporter.js'; import type BugReporter from './fb-stubs/BugReporter.js';
import type BaseDevice from './devices/BaseDevice.js'; import type BaseDevice from './devices/BaseDevice.js';
import type {ActiveSheet} from './reducers/application.js';
type Props = { type Props = {
logger: Logger, logger: Logger,
bugReporter: BugReporter, bugReporter: BugReporter,
leftSidebarVisible: boolean, leftSidebarVisible: boolean,
pluginManagerVisible: boolean,
selectedDevice: ?BaseDevice, selectedDevice: ?BaseDevice,
error: ?string, error: ?string,
activeSheet: ActiveSheet,
}; };
export class App extends React.Component<Props> { export class App extends React.Component<Props> {
@@ -45,18 +46,25 @@ export class App extends React.Component<Props> {
ipcRenderer.send('getLaunchTime'); ipcRenderer.send('getLaunchTime');
ipcRenderer.send('componentDidMount'); ipcRenderer.send('componentDidMount');
} }
getSheet = (onHide: () => mixed) => {
if (this.props.activeSheet === 'BUG_REPORTER') {
return (
<BugReporterDialog
bugReporter={this.props.bugReporter}
onHide={onHide}
/>
);
} else {
return null;
}
};
render() { render() {
return ( return (
<FlexColumn grow={true}> <FlexColumn grow={true}>
<TitleBar /> <TitleBar />
<Sheet> <Sheet>{this.getSheet}</Sheet>
{onHide => (
<BugReporterDialog
bugReporter={this.props.bugReporter}
onHide={onHide}
/>
)}
</Sheet>
<FlexRow grow={true}> <FlexRow grow={true}>
{this.props.leftSidebarVisible && <MainSidebar />} {this.props.leftSidebarVisible && <MainSidebar />}
{this.props.selectedDevice ? ( {this.props.selectedDevice ? (
@@ -76,12 +84,12 @@ export class App extends React.Component<Props> {
* run Flow. */ * run Flow. */
export default connect( export default connect(
({ ({
application: {pluginManagerVisible, leftSidebarVisible}, application: {leftSidebarVisible, activeSheet},
connections: {selectedDevice, error}, connections: {selectedDevice, error},
}) => ({ }) => ({
pluginManagerVisible,
leftSidebarVisible, leftSidebarVisible,
selectedDevice, selectedDevice,
activeSheet,
error, error,
}), }),
)(App); )(App);

View File

@@ -26,11 +26,9 @@ test('Empty app state matches snapshot', () => {
logger={logger} logger={logger}
bugReporter={bugReporter} bugReporter={bugReporter}
leftSidebarVisible={false} leftSidebarVisible={false}
bugDialogVisible={false}
pluginManagerVisible={false}
selectedDevice={null} selectedDevice={null}
toggleBugDialogVisible={() => {}}
error={null} error={null}
activeSheet={null}
/> />
</Provider>, </Provider>,
); );

View File

@@ -7,18 +7,19 @@
import {Component} from 'react'; import {Component} from 'react';
import {Transition} from 'react-transition-group'; import {Transition} from 'react-transition-group';
import {toggleBugDialogVisible} from '../reducers/application.js'; import {setActiveSheet} from '../reducers/application.js';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import {styled} from 'flipper'; import {styled} from 'flipper';
const DialogContainer = styled('div')(({state}) => ({ const DialogContainer = styled('div')(({state}) => ({
transform: `translateY(${ transform: `translate(-50%, ${
state === 'entering' || state === 'exiting' ? '-110' : '' state === 'entering' || state === 'exiting' || state === 'exited'
? '-110'
: '0'
}%)`, }%)`,
transition: '.3s transform', transition: '.3s transform',
position: 'absolute', position: 'absolute',
left: '50%', left: '50%',
marginLeft: -200,
top: 38, top: 38,
zIndex: 2, zIndex: 2,
backgroundColor: '#EFEEEF', backgroundColor: '#EFEEEF',
@@ -31,14 +32,33 @@ const DialogContainer = styled('div')(({state}) => ({
type Props = {| type Props = {|
sheetVisible: boolean, sheetVisible: boolean,
onHideSheet: () => mixed, onHideSheet: () => void,
children: (onHide: () => mixed) => any, children: (onHide: () => mixed) => any,
|}; |};
class Sheet extends Component<Props> { type State = {|
isVisible: boolean,
|};
class Sheet extends Component<Props, State> {
state = {
isVisible: this.props.sheetVisible,
};
static getDerivedStateFromProps(props: Props, state: State) {
if (!props.sheetVisible) {
return {
isVisible: true,
};
} else {
return null;
}
}
componentDidMount() { componentDidMount() {
document.addEventListener('keydown', this.onKeyDown); document.addEventListener('keydown', this.onKeyDown);
} }
componentWillUnmount() { componentWillUnmount() {
document.removeEventListener('keydown', this.onKeyDown); document.removeEventListener('keydown', this.onKeyDown);
} }
@@ -50,12 +70,16 @@ class Sheet extends Component<Props> {
}; };
onHide = () => { onHide = () => {
this.props.onHideSheet(); this.setState({isVisible: false});
}; };
render() { render() {
return ( return (
<Transition in={this.props.sheetVisible} timeout={300} unmountOnExit> <Transition
in={this.props.sheetVisible && this.state.isVisible}
timeout={300}
onExited={() => this.props.onHideSheet()}
unmountOnExit>
{state => ( {state => (
<DialogContainer state={state}> <DialogContainer state={state}>
{this.props.children(this.onHide)} {this.props.children(this.onHide)}
@@ -70,10 +94,10 @@ class Sheet extends Component<Props> {
* deployed. To see the error, delete this comment and * deployed. To see the error, delete this comment and
* run Flow. */ * run Flow. */
export default connect( export default connect(
({application: {bugDialogVisible}}) => ({ ({application: {activeSheet}}) => ({
sheetVisible: bugDialogVisible, sheetVisible: Boolean(activeSheet),
}), }),
{ {
onHideSheet: () => toggleBugDialogVisible(false), onHideSheet: () => setActiveSheet(null),
}, },
)(Sheet); )(Sheet);

View File

@@ -5,6 +5,8 @@
* @format * @format
*/ */
import type {ActiveSheet} from '../reducers/application';
import { import {
colors, colors,
Button, Button,
@@ -12,15 +14,13 @@ import {
FlexRow, FlexRow,
Component, Component,
Spacer, Spacer,
GK,
styled, styled,
} from 'flipper'; } from 'flipper';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import { import {
toggleBugDialogVisible, setActiveSheet,
toggleLeftSidebarVisible, toggleLeftSidebarVisible,
toggleRightSidebarVisible, toggleRightSidebarVisible,
togglePluginManagerVisible,
} from '../reducers/application.js'; } from '../reducers/application.js';
import DevicesButton from './DevicesButton.js'; import DevicesButton from './DevicesButton.js';
import ScreenCaptureButtons from './ScreenCaptureButtons.js'; import ScreenCaptureButtons from './ScreenCaptureButtons.js';
@@ -52,11 +52,9 @@ type Props = {|
leftSidebarVisible: boolean, leftSidebarVisible: boolean,
rightSidebarVisible: boolean, rightSidebarVisible: boolean,
rightSidebarAvailable: boolean, rightSidebarAvailable: boolean,
pluginManagerVisible: boolean,
toggleBugDialogVisible: (visible?: boolean) => void,
toggleLeftSidebarVisible: (visible?: boolean) => void, toggleLeftSidebarVisible: (visible?: boolean) => void,
toggleRightSidebarVisible: (visible?: boolean) => void, toggleRightSidebarVisible: (visible?: boolean) => void,
togglePluginManagerVisible: (visible?: boolean) => void, setActiveSheet: (sheet: ActiveSheet) => void,
|}; |};
class TitleBar extends Component<Props> { class TitleBar extends Component<Props> {
@@ -70,20 +68,11 @@ class TitleBar extends Component<Props> {
{config.bugReportButtonVisible && ( {config.bugReportButtonVisible && (
<Button <Button
compact={true} compact={true}
onClick={() => this.props.toggleBugDialogVisible()} onClick={() => this.props.setActiveSheet('BUG_REPORTER')}
title="Report Bug" title="Report Bug"
icon="bug" icon="bug"
/> />
)} )}
{GK.get('sonar_dynamic_plugins') && (
<Button
compact={true}
onClick={() => this.props.toggleBugDialogVisible()}
selected={this.props.pluginManagerVisible}
title="Plugin Manager"
icon="apps"
/>
)}
<ButtonGroup> <ButtonGroup>
<Button <Button
compact={true} compact={true}
@@ -127,9 +116,8 @@ export default connect(
pluginManagerVisible, pluginManagerVisible,
}), }),
{ {
toggleBugDialogVisible, setActiveSheet,
toggleLeftSidebarVisible, toggleLeftSidebarVisible,
toggleRightSidebarVisible, toggleRightSidebarVisible,
togglePluginManagerVisible,
}, },
)(TitleBar); )(TitleBar);

View File

@@ -7,71 +7,82 @@
import {remote} from 'electron'; import {remote} from 'electron';
export type ActiveSheet = 'BUG_REPORTER' | 'PLUGIN_DEBUGGER' | null;
export type State = { export type State = {
leftSidebarVisible: boolean, leftSidebarVisible: boolean,
rightSidebarVisible: boolean, rightSidebarVisible: boolean,
rightSidebarAvailable: boolean, rightSidebarAvailable: boolean,
bugDialogVisible: boolean,
windowIsFocused: boolean, windowIsFocused: boolean,
pluginManagerVisible: boolean, activeSheet: ActiveSheet,
}; };
type ActionType = type BooleanActionType =
| 'leftSidebarVisible' | 'leftSidebarVisible'
| 'rightSidebarVisible' | 'rightSidebarVisible'
| 'rightSidebarAvailable' | 'rightSidebarAvailable'
| 'bugDialogVisible' | 'windowIsFocused';
| 'windowIsFocused'
| 'pluginManagerVisible';
export type Action = { export type Action =
type: ActionType, | {
payload?: boolean, type: BooleanActionType,
}; payload?: boolean,
}
| {
type: 'SET_ACTIVE_SHEET',
payload: ActiveSheet,
};
const initialState: () => State = () => ({ const initialState: () => State = () => ({
leftSidebarVisible: true, leftSidebarVisible: true,
rightSidebarVisible: true, rightSidebarVisible: true,
rightSidebarAvailable: false, rightSidebarAvailable: false,
bugDialogVisible: false,
windowIsFocused: remote.getCurrentWindow().isFocused(), windowIsFocused: remote.getCurrentWindow().isFocused(),
pluginManagerVisible: false, activeSheet: null,
}); });
export default function reducer(state: State, action: Action): State { export default function reducer(state: State, action: Action): State {
state = state || initialState(); state = state || initialState();
const {payload, type} = action;
const newValue = typeof payload === 'undefined' ? !state[type] : payload;
if ( if (
type === 'leftSidebarVisible' || action.type === 'leftSidebarVisible' ||
type === 'rightSidebarVisible' || action.type === 'rightSidebarVisible' ||
type === 'rightSidebarAvailable' || action.type === 'rightSidebarAvailable' ||
type === 'bugDialogVisible' || action.type === 'windowIsFocused'
type === 'windowIsFocused' ||
type === 'pluginManagerVisible'
) { ) {
if (state[type] === newValue) { const newValue =
typeof action.payload === 'undefined'
? !state[action.type]
: action.payload;
if (state[action.type] === newValue) {
// value hasn't changed // value hasn't changed
return state; return state;
} else { } else {
return { return {
...state, ...state,
[type]: newValue, [action.type]: newValue,
}; };
} }
} else if (action.type === 'SET_ACTIVE_SHEET') {
return {
...state,
activeSheet: action.payload,
};
} else { } else {
return state; return state;
} }
} }
export const toggleAction = (type: ActionType, payload?: boolean): Action => ({ export const toggleAction = (
type: BooleanActionType,
payload?: boolean,
): Action => ({
type, type,
payload, payload,
}); });
export const toggleBugDialogVisible = (payload?: boolean): Action => ({ export const setActiveSheet = (payload: ActiveSheet): Action => ({
type: 'bugDialogVisible', type: 'SET_ACTIVE_SHEET',
payload, payload,
}); });
@@ -89,8 +100,3 @@ export const toggleRightSidebarAvailable = (payload?: boolean): Action => ({
type: 'rightSidebarAvailable', type: 'rightSidebarAvailable',
payload, payload,
}); });
export const togglePluginManagerVisible = (payload?: boolean): Action => ({
type: 'pluginManagerVisible',
payload,
});