Run export flipper trace in background
Summary: Adds the capability to run the export in the background along with the display of the status in the title bar. Reviewed By: danielbuechele Differential Revision: D16567026 fbshipit-source-id: 3955243cd7f094a7ee33eef3511804ff6e6476be
This commit is contained in:
committed by
Facebook Github Bot
parent
84b64b75dc
commit
8c9eb30060
37
src/chrome/CancellableExportStatus.js
Normal file
37
src/chrome/CancellableExportStatus.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* 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 {FlexRow, Button} from '../ui/index';
|
||||||
|
import {styled, LoadingIndicator, Text} from 'flipper';
|
||||||
|
import {Component} from 'react';
|
||||||
|
import {colors} from '../ui/components/colors';
|
||||||
|
|
||||||
|
const Wrapper = styled(FlexRow)({
|
||||||
|
color: colors.light50,
|
||||||
|
alignItems: 'center',
|
||||||
|
marginLeft: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
msg: string,
|
||||||
|
onCancel: void => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class CancellableExportStatus extends Component<Props> {
|
||||||
|
render() {
|
||||||
|
const {msg, onCancel} = this.props;
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<LoadingIndicator size={16} />
|
||||||
|
|
||||||
|
<Text>{msg}</Text>
|
||||||
|
|
||||||
|
<Button onClick={onCancel}> Cancel </Button>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
Spacer,
|
Spacer,
|
||||||
Input,
|
Input,
|
||||||
} from 'flipper';
|
} from 'flipper';
|
||||||
|
import {setExportStatusComponent, unsetShare} from '../reducers/application';
|
||||||
import type {Logger} from '../fb-interfaces/Logger.js';
|
import type {Logger} from '../fb-interfaces/Logger.js';
|
||||||
import {Idler} from '../utils/Idler';
|
import {Idler} from '../utils/Idler';
|
||||||
import {shareFlipperData} from '../fb-stubs/user';
|
import {shareFlipperData} from '../fb-stubs/user';
|
||||||
@@ -25,6 +26,7 @@ import PropTypes from 'prop-types';
|
|||||||
import {clipboard} from 'electron';
|
import {clipboard} from 'electron';
|
||||||
import ShareSheetErrorList from './ShareSheetErrorList.js';
|
import ShareSheetErrorList from './ShareSheetErrorList.js';
|
||||||
import {reportPlatformFailures} from '../utils/metrics';
|
import {reportPlatformFailures} from '../utils/metrics';
|
||||||
|
import CancellableExportStatus from './CancellableExportStatus';
|
||||||
// $FlowFixMe: Missing type defs for node built-in.
|
// $FlowFixMe: Missing type defs for node built-in.
|
||||||
import {performance} from 'perf_hooks';
|
import {performance} from 'perf_hooks';
|
||||||
export const SHARE_FLIPPER_TRACE_EVENT = 'share-flipper-link';
|
export const SHARE_FLIPPER_TRACE_EVENT = 'share-flipper-link';
|
||||||
@@ -71,6 +73,7 @@ type Props = {
|
|||||||
logger: Logger,
|
logger: Logger,
|
||||||
};
|
};
|
||||||
type State = {
|
type State = {
|
||||||
|
runInBackground: boolean,
|
||||||
errorArray: Array<Error>,
|
errorArray: Array<Error>,
|
||||||
result:
|
result:
|
||||||
| ?{
|
| ?{
|
||||||
@@ -92,21 +95,42 @@ export default class ShareSheet extends Component<Props, State> {
|
|||||||
errorArray: [],
|
errorArray: [],
|
||||||
result: null,
|
result: null,
|
||||||
statusUpdate: null,
|
statusUpdate: null,
|
||||||
|
runInBackground: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
idler = new Idler();
|
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() {
|
async componentDidMount() {
|
||||||
const mark = 'shareSheetExportUrl';
|
const mark = 'shareSheetExportUrl';
|
||||||
performance.mark(mark);
|
performance.mark(mark);
|
||||||
try {
|
try {
|
||||||
const {serializedString, errorArray} = await reportPlatformFailures(
|
const {serializedString, errorArray} = await reportPlatformFailures(
|
||||||
exportStore(this.context.store, this.idler, (msg: string) => {
|
exportStore(this.context.store, this.idler, (msg: string) => {
|
||||||
|
if (this.state.runInBackground) {
|
||||||
|
this.dispatchAndUpdateToolBarStatus(msg);
|
||||||
|
} else {
|
||||||
this.setState({statusUpdate: msg});
|
this.setState({statusUpdate: msg});
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
`${EXPORT_FLIPPER_TRACE_EVENT}:UI_LINK`,
|
`${EXPORT_FLIPPER_TRACE_EVENT}:UI_LINK`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.context.store.dispatch(unsetShare());
|
||||||
|
|
||||||
const result = await reportPlatformFailures(
|
const result = await reportPlatformFailures(
|
||||||
shareFlipperData(serializedString),
|
shareFlipperData(serializedString),
|
||||||
`${SHARE_FLIPPER_TRACE_EVENT}`,
|
`${SHARE_FLIPPER_TRACE_EVENT}`,
|
||||||
@@ -151,6 +175,19 @@ export default class ShareSheet extends Component<Props, State> {
|
|||||||
</Center>
|
</Center>
|
||||||
<FlexRow>
|
<FlexRow>
|
||||||
<Spacer />
|
<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}>
|
<Button compact padded onClick={onHide}>
|
||||||
Close
|
Close
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ import {
|
|||||||
FlexRow,
|
FlexRow,
|
||||||
Spacer,
|
Spacer,
|
||||||
} from 'flipper';
|
} from 'flipper';
|
||||||
|
import {setExportStatusComponent, unsetShare} from '../reducers/application';
|
||||||
import {reportPlatformFailures} from '../utils/metrics';
|
import {reportPlatformFailures} from '../utils/metrics';
|
||||||
|
import CancellableExportStatus from './CancellableExportStatus';
|
||||||
// $FlowFixMe: Missing type defs for node built-in.
|
// $FlowFixMe: Missing type defs for node built-in.
|
||||||
import {performance} from 'perf_hooks';
|
import {performance} from 'perf_hooks';
|
||||||
import type {Logger} from '../fb-interfaces/Logger.js';
|
import type {Logger} from '../fb-interfaces/Logger.js';
|
||||||
@@ -72,6 +74,7 @@ type State = {
|
|||||||
error: ?Error,
|
error: ?Error,
|
||||||
},
|
},
|
||||||
statusUpdate: ?string,
|
statusUpdate: ?string,
|
||||||
|
runInBackground: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class ShareSheetExportFile extends Component<Props, State> {
|
export default class ShareSheetExportFile extends Component<Props, State> {
|
||||||
@@ -83,10 +86,25 @@ export default class ShareSheetExportFile extends Component<Props, State> {
|
|||||||
errorArray: [],
|
errorArray: [],
|
||||||
result: null,
|
result: null,
|
||||||
statusUpdate: null,
|
statusUpdate: null,
|
||||||
|
runInBackground: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
idler = new Idler();
|
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() {
|
async componentDidMount() {
|
||||||
const mark = 'shareSheetExportFile';
|
const mark = 'shareSheetExportFile';
|
||||||
performance.mark(mark);
|
performance.mark(mark);
|
||||||
@@ -102,11 +120,24 @@ export default class ShareSheetExportFile extends Component<Props, State> {
|
|||||||
this.context.store,
|
this.context.store,
|
||||||
this.idler,
|
this.idler,
|
||||||
(msg: string) => {
|
(msg: string) => {
|
||||||
|
if (this.state.runInBackground) {
|
||||||
|
this.dispatchAndUpdateToolBarStatus(msg);
|
||||||
|
} else {
|
||||||
this.setState({statusUpdate: msg});
|
this.setState({statusUpdate: msg});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
`${EXPORT_FLIPPER_TRACE_EVENT}:UI_FILE`,
|
`${EXPORT_FLIPPER_TRACE_EVENT}:UI_FILE`,
|
||||||
);
|
);
|
||||||
|
this.context.store.dispatch(unsetShare());
|
||||||
|
if (this.state.runInBackground) {
|
||||||
|
new window.Notification('Sharable Flipper trace created', {
|
||||||
|
//$FlowFixMe: Already checked that props.file exists
|
||||||
|
body: `Flipper trace exported to the ${this.props.file}`,
|
||||||
|
requireInteraction: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.setState({errorArray, result: {success: true, error: null}});
|
this.setState({errorArray, result: {success: true, error: null}});
|
||||||
this.props.logger.trackTimeSince(mark, 'export:file-success');
|
this.props.logger.trackTimeSince(mark, 'export:file-success');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -182,6 +213,19 @@ export default class ShareSheetExportFile extends Component<Props, State> {
|
|||||||
</Center>
|
</Center>
|
||||||
<FlexRow>
|
<FlexRow>
|
||||||
<Spacer />
|
<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}>
|
<Button compact padded onClick={onHide}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ActiveSheet, LauncherMsg} from '../reducers/application.js';
|
import {ActiveSheet, LauncherMsg, ShareType} from '../reducers/application.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
colors,
|
colors,
|
||||||
@@ -75,6 +75,7 @@ type StateFromProps = {
|
|||||||
downloadingImportData: boolean,
|
downloadingImportData: boolean,
|
||||||
launcherMsg: LauncherMsg,
|
launcherMsg: LauncherMsg,
|
||||||
flipperRating: number | null,
|
flipperRating: number | null,
|
||||||
|
share: ShareType | null | undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
const VersionText = styled(Text)({
|
const VersionText = styled(Text)({
|
||||||
@@ -115,20 +116,36 @@ const Importing = styled(FlexRow)({
|
|||||||
marginLeft: 10,
|
marginLeft: 10,
|
||||||
});
|
});
|
||||||
|
|
||||||
type Props = OwnProps & DispatchFromProps & StateFromProps;
|
function statusMessageComponent(
|
||||||
class TitleBar extends React.Component<Props> {
|
downloadingImportData: boolean,
|
||||||
render() {
|
statusComponent?: React.ReactElement<any> | undefined,
|
||||||
|
) {
|
||||||
|
if (downloadingImportData) {
|
||||||
return (
|
return (
|
||||||
<AppTitleBar focused={this.props.windowIsFocused} className="toolbar">
|
|
||||||
<DevicesButton />
|
|
||||||
<ScreenCaptureButtons />
|
|
||||||
{this.props.downloadingImportData && (
|
|
||||||
<Importing>
|
<Importing>
|
||||||
<LoadingIndicator size={16} />
|
<LoadingIndicator size={16} />
|
||||||
Importing data...
|
Importing data...
|
||||||
</Importing>
|
</Importing>
|
||||||
)}
|
);
|
||||||
|
}
|
||||||
|
if (statusComponent) {
|
||||||
|
return statusComponent;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = OwnProps & DispatchFromProps & StateFromProps;
|
||||||
|
class TitleBar extends React.Component<Props, StateFromProps> {
|
||||||
|
render() {
|
||||||
|
const {share} = this.props;
|
||||||
|
return (
|
||||||
|
<AppTitleBar focused={this.props.windowIsFocused} className="toolbar">
|
||||||
|
<DevicesButton />
|
||||||
|
<ScreenCaptureButtons />
|
||||||
|
{statusMessageComponent(
|
||||||
|
this.props.downloadingImportData,
|
||||||
|
share != null ? share.statusComponent : undefined,
|
||||||
|
)}
|
||||||
<Spacer />
|
<Spacer />
|
||||||
{config.showFlipperRating ? (
|
{config.showFlipperRating ? (
|
||||||
<RatingButton
|
<RatingButton
|
||||||
@@ -187,6 +204,7 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
|||||||
downloadingImportData,
|
downloadingImportData,
|
||||||
launcherMsg,
|
launcherMsg,
|
||||||
flipperRating,
|
flipperRating,
|
||||||
|
share,
|
||||||
},
|
},
|
||||||
}) => ({
|
}) => ({
|
||||||
windowIsFocused,
|
windowIsFocused,
|
||||||
@@ -196,6 +214,7 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
|
|||||||
downloadingImportData,
|
downloadingImportData,
|
||||||
launcherMsg,
|
launcherMsg,
|
||||||
flipperRating,
|
flipperRating,
|
||||||
|
share,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
setActiveSheet,
|
setActiveSheet,
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
|
|
||||||
import {remote} from 'electron';
|
import {remote} from 'electron';
|
||||||
import uuidv1 from 'uuid/v1';
|
import uuidv1 from 'uuid/v1';
|
||||||
|
import {type Element as ReactElement} from 'react';
|
||||||
|
import CancellableExportStatus from '../chrome/CancellableExportStatus';
|
||||||
export const ACTIVE_SHEET_PLUGIN_SHEET: 'PLUGIN_SHEET' = 'PLUGIN_SHEET';
|
export const ACTIVE_SHEET_PLUGIN_SHEET: 'PLUGIN_SHEET' = 'PLUGIN_SHEET';
|
||||||
export const ACTIVE_SHEET_BUG_REPORTER: 'BUG_REPORTER' = 'BUG_REPORTER';
|
export const ACTIVE_SHEET_BUG_REPORTER: 'BUG_REPORTER' = 'BUG_REPORTER';
|
||||||
export const ACTIVE_SHEET_PLUGIN_DEBUGGER: 'PLUGIN_DEBUGGER' =
|
export const ACTIVE_SHEET_PLUGIN_DEBUGGER: 'PLUGIN_DEBUGGER' =
|
||||||
@@ -18,6 +19,9 @@ export const ACTIVE_SHEET_SHARE_DATA: 'SHARE_DATA' = 'SHARE_DATA';
|
|||||||
export const ACTIVE_SHEET_SIGN_IN: 'SIGN_IN' = 'SIGN_IN';
|
export const ACTIVE_SHEET_SIGN_IN: 'SIGN_IN' = 'SIGN_IN';
|
||||||
export const ACTIVE_SHEET_SHARE_DATA_IN_FILE: 'SHARE_DATA_IN_FILE' =
|
export const ACTIVE_SHEET_SHARE_DATA_IN_FILE: 'SHARE_DATA_IN_FILE' =
|
||||||
'SHARE_DATA_IN_FILE';
|
'SHARE_DATA_IN_FILE';
|
||||||
|
export const SET_EXPORT_STATUS_MESSAGE: 'SET_EXPORT_STATUS_MESSAGE' =
|
||||||
|
'SET_EXPORT_STATUS_MESSAGE';
|
||||||
|
export const UNSET_SHARE: 'UNSET_SHARE' = 'UNSET_SHARE';
|
||||||
|
|
||||||
export type ActiveSheet =
|
export type ActiveSheet =
|
||||||
| typeof ACTIVE_SHEET_PLUGIN_SHEET
|
| typeof ACTIVE_SHEET_PLUGIN_SHEET
|
||||||
@@ -38,12 +42,12 @@ export type ServerPorts = {
|
|||||||
secure: number,
|
secure: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ShareType =
|
type SubShareType = {type: 'file', file: string} | {type: 'link'};
|
||||||
| {
|
|
||||||
type: 'file',
|
export type ShareType = {
|
||||||
file: string,
|
statusComponent?: ReactElement<typeof CancellableExportStatus>,
|
||||||
}
|
...SubShareType,
|
||||||
| {type: 'link'};
|
};
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
leftSidebarVisible: boolean,
|
leftSidebarVisible: boolean,
|
||||||
@@ -102,6 +106,13 @@ export type Action =
|
|||||||
payload: {
|
payload: {
|
||||||
rating: number,
|
rating: number,
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: typeof UNSET_SHARE,
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: typeof SET_EXPORT_STATUS_MESSAGE,
|
||||||
|
payload: ReactElement<typeof CancellableExportStatus>,
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState: () => State = () => ({
|
const initialState: () => State = () => ({
|
||||||
@@ -179,6 +190,18 @@ export default function reducer(state: State, action: Action): State {
|
|||||||
...state,
|
...state,
|
||||||
flipperRating: action.payload.rating,
|
flipperRating: action.payload.rating,
|
||||||
};
|
};
|
||||||
|
} else if (action.type === 'SET_EXPORT_STATUS_MESSAGE') {
|
||||||
|
if (state.share) {
|
||||||
|
const {share} = state;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
//$FlowFixMe: T48110490, its not able to understand for which case it needs to apply the changes
|
||||||
|
share: {...share, statusComponent: action.payload},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
} else if (action.type === 'UNSET_SHARE') {
|
||||||
|
return {...state, share: null};
|
||||||
} else {
|
} else {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -192,6 +215,17 @@ export const toggleAction = (
|
|||||||
payload,
|
payload,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const unsetShare = (): Action => ({
|
||||||
|
type: UNSET_SHARE,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setExportStatusComponent = (
|
||||||
|
payload: ReactElement<typeof CancellableExportStatus>,
|
||||||
|
): Action => ({
|
||||||
|
type: SET_EXPORT_STATUS_MESSAGE,
|
||||||
|
payload,
|
||||||
|
});
|
||||||
|
|
||||||
export const setSelectPluginsToExportActiveSheet = (
|
export const setSelectPluginsToExportActiveSheet = (
|
||||||
payload: ShareType,
|
payload: ShareType,
|
||||||
): Action => ({
|
): Action => ({
|
||||||
|
|||||||
Reference in New Issue
Block a user