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
256 lines
6.4 KiB
TypeScript
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>
|
|
);
|
|
}
|
|
}
|