Share sheet for the file export

Summary:
This diff shows an export sheet when one exports the Flipper data in a file. It also shows if any error happens.
Look at the video in test case.

Reviewed By: danielbuechele

Differential Revision: D14624194

fbshipit-source-id: dea0179a83e626e49593e5dde1d5ff128048db02
This commit is contained in:
Pritesh Nandgaonkar
2019-04-01 08:50:48 -07:00
committed by Facebook Github Bot
parent 2dacea8541
commit 45999a5292
5 changed files with 188 additions and 16 deletions

View File

@@ -15,6 +15,7 @@ import BugReporterDialog from './chrome/BugReporterDialog.js';
import ErrorBar from './chrome/ErrorBar.js';
import ShareSheet from './chrome/ShareSheet.js';
import SignInSheet from './chrome/SignInSheet.js';
import ShareSheetExportFile from './chrome/ShareSheetExportFile.js';
import PluginContainer from './PluginContainer.js';
import Sheet from './chrome/Sheet.js';
import {ipcRenderer, remote} from 'electron';
@@ -24,6 +25,7 @@ import {
ACTIVE_SHEET_PLUGIN_DEBUGGER,
ACTIVE_SHEET_SHARE_DATA,
ACTIVE_SHEET_SIGN_IN,
ACTIVE_SHEET_SHARE_DATA_IN_FILE,
} from './reducers/application.js';
import type {Logger} from './fb-interfaces/Logger.js';
@@ -44,6 +46,7 @@ type Props = {|
selectedDevice: ?BaseDevice,
error: ?string,
activeSheet: ActiveSheet,
exportFile: ?string,
|};
export class App extends React.Component<Props> {
@@ -76,6 +79,12 @@ export class App extends React.Component<Props> {
return <ShareSheet onHide={onHide} />;
} else if (this.props.activeSheet === ACTIVE_SHEET_SIGN_IN) {
return <SignInSheet onHide={onHide} />;
} else if (this.props.activeSheet === ACTIVE_SHEET_SHARE_DATA_IN_FILE) {
const {exportFile} = this.props;
if (!exportFile) {
throw new Error('Tried to export data without passing the file path');
}
return <ShareSheetExportFile onHide={onHide} file={exportFile} />;
} else {
// contents are added via React.Portal
return null;
@@ -103,12 +112,13 @@ export class App extends React.Component<Props> {
export default connect<Props, OwnProps, _, _, _, _>(
({
application: {leftSidebarVisible, activeSheet},
application: {leftSidebarVisible, activeSheet, exportFile},
connections: {selectedDevice, error},
}) => ({
leftSidebarVisible,
selectedDevice,
activeSheet,
exportFile,
error,
}),
)(App);

View File

@@ -6,22 +6,20 @@
*/
import type {FlipperPlugin, FlipperDevicePlugin} from './plugin.js';
import {showOpenDialog} from './utils/exportData.js';
import {
exportStoreToFile,
showOpenDialog,
EXPORT_FLIPPER_TRACE_EVENT,
} from './utils/exportData.js';
import {setActiveSheet, ACTIVE_SHEET_SHARE_DATA} from './reducers/application';
setExportDataToFileActiveSheet,
setActiveSheet,
ACTIVE_SHEET_SHARE_DATA,
} from './reducers/application';
import type {Store} from './reducers/';
import electron from 'electron';
import {ENABLE_SHAREABLE_LINK} from 'flipper';
import {remote} from 'electron';
const {dialog} = remote;
import os from 'os';
import path from 'path';
import {reportPlatformFailures} from './utils/metrics';
export type DefaultKeyboardAction = 'clear' | 'goToBottom' | 'createPaste';
export type TopLevelMenu = 'Edit' | 'View' | 'Window' | 'Help';
const {dialog} = electron.remote;
import os from 'os';
import path from 'path';
type MenuItem = {|
label?: string,
@@ -199,11 +197,11 @@ function getTemplate(
title: 'FlipperExport',
defaultPath: path.join(os.homedir(), 'FlipperExport.flipper'),
},
file => {
reportPlatformFailures(
exportStoreToFile(file, store),
`${EXPORT_FLIPPER_TRACE_EVENT}:UI`,
);
async file => {
if (!file) {
return;
}
store.dispatch(setExportDataToFileActiveSheet(file));
},
);
},

View File

@@ -29,6 +29,7 @@ test('Empty app state matches snapshot', () => {
selectedDevice={null}
error={null}
activeSheet={null}
exportFile={null}
/>
</Provider>,
);

View File

@@ -0,0 +1,143 @@
/**
* 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,
colors,
Text,
LoadingIndicator,
Component,
FlexRow,
Spacer,
} from 'flipper';
import {reportPlatformFailures} from '../utils/metrics';
import {
exportStoreToFile,
EXPORT_FLIPPER_TRACE_EVENT,
} from '../utils/exportData.js';
import PropTypes from 'prop-types';
const Container = styled(FlexColumn)({
padding: 20,
width: 500,
});
const Center = styled(FlexColumn)({
alignItems: 'center',
paddingTop: 50,
paddingBottom: 50,
});
const Uploading = styled(Text)({
marginTop: 15,
});
const ErrorMessage = styled(Text)({
display: 'block',
marginTop: 6,
wordBreak: 'break-all',
whiteSpace: 'pre-line',
lineHeight: 1.35,
});
const Title = styled(Text)({
marginBottom: 6,
});
const InfoText = styled(Text)({
lineHeight: 1.35,
marginBottom: 15,
});
type Props = {
onHide: () => mixed,
file: string,
};
type State = {
result: ?{
success: boolean,
error: ?Error,
},
};
export default class ShareSheetExportFile extends Component<Props, State> {
static contextTypes = {
store: PropTypes.object.isRequired,
};
state = {
result: null,
};
async componentDidMount() {
try {
await reportPlatformFailures(
exportStoreToFile(this.props.file, this.context.store),
`${EXPORT_FLIPPER_TRACE_EVENT}:UI`,
);
this.setState({result: {success: true, error: null}});
} catch (err) {
this.setState({result: {success: false, error: err}});
}
}
render() {
const {result} = this.state;
if (result) {
const {success, error} = result;
if (success) {
return (
<Container>
<FlexColumn>
<Title bold>Data Exported Successfully</Title>
<InfoText>
When sharing your Flipper data, consider that the captured data
might contain sensitive information like access tokens used in
network requests.
</InfoText>
</FlexColumn>
<FlexRow>
<Spacer />
<Button compact padded onClick={this.props.onHide}>
Close
</Button>
</FlexRow>
</Container>
);
}
if (error) {
return (
<Container>
<Title bold>Error</Title>
<ErrorMessage code>
{error?.message || 'File could not be saved.'}
</ErrorMessage>
<FlexRow>
<Spacer />
<Button compact padded onClick={this.props.onHide}>
Close
</Button>
</FlexRow>
</Container>
);
}
return null;
} else {
return (
<Container>
<Center>
<LoadingIndicator size={30} />
<Uploading bold color={colors.macOSTitleBarIcon}>
Exporting Flipper trace...
</Uploading>
</Center>
</Container>
);
}
}
}

View File

@@ -14,6 +14,8 @@ export const ACTIVE_SHEET_PLUGIN_DEBUGGER: 'PLUGIN_DEBUGGER' =
'PLUGIN_DEBUGGER';
export const ACTIVE_SHEET_SHARE_DATA: 'SHARE_DATA' = 'SHARE_DATA';
export const ACTIVE_SHEET_SIGN_IN: 'SIGN_IN' = 'SIGN_IN';
export const ACTIVE_SHEET_SHARE_DATA_IN_FILE: 'SHARE_DATA_IN_FILE' =
'SHARE_DATA_IN_FILE';
export type ActiveSheet =
| typeof ACTIVE_SHEET_PLUGIN_SHEET
@@ -21,6 +23,7 @@ export type ActiveSheet =
| typeof ACTIVE_SHEET_PLUGIN_DEBUGGER
| typeof ACTIVE_SHEET_SHARE_DATA
| typeof ACTIVE_SHEET_SIGN_IN
| typeof ACTIVE_SHEET_SHARE_DATA_IN_FILE
| null;
export type State = {
@@ -29,6 +32,7 @@ export type State = {
rightSidebarAvailable: boolean,
windowIsFocused: boolean,
activeSheet: ActiveSheet,
exportFile: ?string,
sessionId: ?string,
serverPorts: {
insecure: number,
@@ -53,6 +57,10 @@ export type Action =
type: 'SET_ACTIVE_SHEET',
payload: ActiveSheet,
}
| {
type: typeof ACTIVE_SHEET_SHARE_DATA_IN_FILE,
payload: {file: string},
}
| {
type: 'SET_SERVER_PORTS',
payload: {
@@ -67,6 +75,7 @@ const initialState: () => State = () => ({
rightSidebarAvailable: false,
windowIsFocused: remote.getCurrentWindow().isFocused(),
activeSheet: null,
exportFile: null,
sessionId: uuidv1(),
serverPorts: {
insecure: 8089,
@@ -103,6 +112,12 @@ export default function reducer(state: State, action: Action): State {
...state,
activeSheet: action.payload,
};
} else if (action.type === ACTIVE_SHEET_SHARE_DATA_IN_FILE) {
return {
...state,
activeSheet: ACTIVE_SHEET_SHARE_DATA_IN_FILE,
exportFile: action.payload.file,
};
}
if (action.type === 'SET_SERVER_PORTS') {
return {
@@ -122,6 +137,11 @@ export const toggleAction = (
payload,
});
export const setExportDataToFileActiveSheet = (file: string): Action => ({
type: ACTIVE_SHEET_SHARE_DATA_IN_FILE,
payload: {file},
});
export const setActiveSheet = (payload: ActiveSheet): Action => ({
type: 'SET_ACTIVE_SHEET',
payload,