Files
flipper/src/chrome/ShareSheetExportFile.tsx
Pritesh Nandgaonkar 8d4d642330 Attach Flipper Trace in the support form
Summary: This diff sets up Flipper to attach flipper trace to the support form. This diff adds a property named `exportResult` in the redux store. This will hold the export url or the path of the export, depending on the type of the flipper export. Once the exportResult is populated, we listen for this change and update the button style and fill the comment box.

Reviewed By: passy

Differential Revision: D17478491

fbshipit-source-id: 10dd5e130a9e3df5f41afde42b92b08959d9ed9e
2019-09-25 06:09:13 -07:00

256 lines
6.4 KiB
TypeScript

/**
* 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,
FlexRow,
Spacer,
} from 'flipper';
import React, {Component} from 'react';
import {setExportStatusComponent, unsetShare} from '../reducers/application';
import {reportPlatformFailures} from '../utils/metrics';
import CancellableExportStatus from './CancellableExportStatus';
import {performance} from 'perf_hooks';
import {Logger} from '../fb-interfaces/Logger';
import {Idler} from '../utils/Idler';
import {
exportStoreToFile,
EXPORT_FLIPPER_TRACE_EVENT,
} from '../utils/exportData';
import PropTypes from 'prop-types';
import ShareSheetErrorList from './ShareSheetErrorList';
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: () => any;
file: string | null | undefined;
logger: Logger;
};
type State = {
errorArray: Array<Error>;
result:
| {
success: boolean;
error: Error | null | undefined;
}
| null
| undefined;
statusUpdate: string | null | undefined;
runInBackground: boolean;
};
export default class ShareSheetExportFile extends Component<Props, State> {
static contextTypes = {
store: PropTypes.object.isRequired,
};
state: State = {
errorArray: [],
result: null,
statusUpdate: null,
runInBackground: false,
};
idler = new Idler();
dispatchAndUpdateToolBarStatus(msg: string) {
this.context.store.dispatch(
setExportStatusComponent(
<CancellableExportStatus
msg={msg}
onCancel={() => {
this.idler.cancel();
this.context.store.dispatch(unsetShare());
}}
/>,
),
);
}
async componentDidMount() {
const mark = 'shareSheetExportFile';
performance.mark(mark);
try {
if (!this.props.file) {
return;
}
const {errorArray} = await reportPlatformFailures(
exportStoreToFile(
this.props.file,
this.context.store,
this.idler,
(msg: string) => {
if (this.state.runInBackground) {
this.dispatchAndUpdateToolBarStatus(msg);
} else {
this.setState({statusUpdate: msg});
}
},
),
`${EXPORT_FLIPPER_TRACE_EVENT}:UI_FILE`,
);
this.context.store.dispatch(unsetShare());
if (this.state.runInBackground) {
new Notification('Sharable Flipper trace created', {
body: `Flipper trace exported to the ${this.props.file}`,
requireInteraction: true,
});
return;
}
this.setState({errorArray, result: {success: true, error: null}});
this.props.logger.trackTimeSince(mark, 'export:file-success');
} catch (err) {
if (!this.state.runInBackground) {
this.setState({errorArray: [], result: {success: false, error: err}});
}
this.props.logger.trackTimeSince(mark, 'export:file-error');
}
}
render() {
const onHide = () => {
this.context.store.dispatch(unsetShare());
this.props.onHide();
this.idler.cancel();
};
if (!this.props.file) {
return this.renderNoFileError(onHide);
}
const {result, statusUpdate} = 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>
<ShareSheetErrorList errors={this.state.errorArray} />
</FlexColumn>
<FlexRow>
<Spacer />
<Button compact padded onClick={onHide}>
Close
</Button>
</FlexRow>
</Container>
);
}
if (error) {
return (
<Container>
<Title bold>Error</Title>
<ErrorMessage code>
{(error && error.message) || 'File could not be saved.'}
</ErrorMessage>
<FlexRow>
<Spacer />
<Button compact padded onClick={onHide}>
Close
</Button>
</FlexRow>
</Container>
);
}
return null;
} else {
return (
<Container>
<Center>
<LoadingIndicator size={30} />
{statusUpdate && statusUpdate.length > 0 ? (
<Uploading bold color={colors.macOSTitleBarIcon}>
{statusUpdate}
</Uploading>
) : (
<Uploading bold color={colors.macOSTitleBarIcon}>
Exporting Flipper trace...
</Uploading>
)}
</Center>
<FlexRow>
<Spacer />
<Button
compact
padded
onClick={() => {
this.setState({runInBackground: true});
const {statusUpdate} = this.state;
if (statusUpdate) {
this.dispatchAndUpdateToolBarStatus(statusUpdate);
}
this.props.onHide();
}}>
Run In Background
</Button>
<Button compact padded onClick={onHide}>
Cancel
</Button>
</FlexRow>
</Container>
);
}
}
renderNoFileError(onHide: () => void) {
return (
<Container>
<Center>
<Title bold>No file selected</Title>
</Center>
<FlexRow>
<Spacer />
<Button compact padded onClick={onHide}>
Close
</Button>
</FlexRow>
</Container>
);
}
}