Make it possible to export support form V2 with meta data

Summary:
This diff enables a full roundtrip of exporting a bug report to file / link, and importing it again.

Styling is not part of this story.

Reviewed By: jknoxville

Differential Revision: D18636418

fbshipit-source-id: ef9a8e3622bdac9361f612d51415a593f4268b80
This commit is contained in:
Michel Weststrate
2019-11-21 08:00:25 -08:00
committed by Facebook Github Bot
parent f33666a4b9
commit dd65ec6ed0
7 changed files with 77 additions and 55 deletions

View File

@@ -8,9 +8,12 @@
*/ */
import {FlipperPlugin, FlipperDevicePlugin} from './plugin'; import {FlipperPlugin, FlipperDevicePlugin} from './plugin';
import {showOpenDialog} from './utils/exportData';
import { import {
setSelectPluginsToExportActiveSheet, showOpenDialog,
startFileExport,
startLinkExport,
} from './utils/exportData';
import {
setActiveSheet, setActiveSheet,
ACTIVE_SHEET_PLUGINS, ACTIVE_SHEET_PLUGINS,
ACTIVE_SHEET_SETTINGS, ACTIVE_SHEET_SETTINGS,
@@ -21,8 +24,6 @@ import {Store} from './reducers/';
import electron, {MenuItemConstructorOptions} from 'electron'; import electron, {MenuItemConstructorOptions} from 'electron';
import {notNull} from './utils/typeUtils'; import {notNull} from './utils/typeUtils';
import constants from './fb-stubs/constants'; import constants from './fb-stubs/constants';
import os from 'os';
import path from 'path';
import GK from './fb-stubs/GK'; import GK from './fb-stubs/GK';
export type DefaultKeyboardAction = 'clear' | 'goToBottom' | 'createPaste'; export type DefaultKeyboardAction = 'clear' | 'goToBottom' | 'createPaste';
@@ -186,44 +187,14 @@ function getTemplate(
{ {
label: 'File...', label: 'File...',
accelerator: 'CommandOrControl+E', accelerator: 'CommandOrControl+E',
click: function() { click: () => startFileExport(store.dispatch),
electron.remote.dialog
.showSaveDialog(
// @ts-ignore This appears to work but isn't allowed by the types
null,
{
title: 'FlipperExport',
defaultPath: path.join(os.homedir(), 'FlipperExport.flipper'),
},
)
.then(async (result: electron.SaveDialogReturnValue) => {
const file = result.filePath;
if (!file) {
return;
}
store.dispatch(
setSelectPluginsToExportActiveSheet({
type: 'file',
file: file,
closeOnFinish: false,
}),
);
});
},
}, },
]; ];
if (constants.ENABLE_SHAREABLE_LINK) { if (constants.ENABLE_SHAREABLE_LINK) {
exportSubmenu.push({ exportSubmenu.push({
label: 'Shareable Link', label: 'Shareable Link',
accelerator: 'CommandOrControl+Shift+E', accelerator: 'CommandOrControl+Shift+E',
click: function() { click: () => startLinkExport(store.dispatch),
store.dispatch(
setSelectPluginsToExportActiveSheet({
type: 'link',
closeOnFinish: false,
}),
);
},
}); });
} }
const fileSubmenu: MenuItemConstructorOptions[] = [ const fileSubmenu: MenuItemConstructorOptions[] = [

View File

@@ -29,12 +29,6 @@ const Container = styled(FlexColumn)({
width: 500, width: 500,
}); });
const Center = styled(FlexColumn)({
alignItems: 'center',
paddingTop: 50,
paddingBottom: 50,
});
const ErrorMessage = styled(Text)({ const ErrorMessage = styled(Text)({
display: 'block', display: 'block',
marginTop: 6, marginTop: 6,

View File

@@ -39,6 +39,7 @@ import CancellableExportStatus from './CancellableExportStatus';
import {performance} from 'perf_hooks'; import {performance} from 'perf_hooks';
import ShareSheetPendingDialog from './ShareSheetPendingDialog'; import ShareSheetPendingDialog from './ShareSheetPendingDialog';
import {getInstance as getLogger} from '../fb-stubs/Logger'; import {getInstance as getLogger} from '../fb-stubs/Logger';
import {resetSupportFormV2State} from '../reducers/supportForm';
export const SHARE_FLIPPER_TRACE_EVENT = 'share-flipper-link'; export const SHARE_FLIPPER_TRACE_EVENT = 'share-flipper-link';
const Container = styled(FlexColumn)({ const Container = styled(FlexColumn)({
@@ -142,6 +143,7 @@ export default class ShareSheetExportUrl extends Component<Props, State> {
requireInteraction: true, requireInteraction: true,
}); });
} }
store.dispatch(resetSupportFormV2State());
this.props.logger.trackTimeSince(mark, 'export:url-success'); this.props.logger.trackTimeSince(mark, 'export:url-success');
} catch (e) { } catch (e) {
if (!this.state.runInBackground) { if (!this.state.runInBackground) {

View File

@@ -9,7 +9,7 @@
import BaseDevice from './BaseDevice'; import BaseDevice from './BaseDevice';
import {DeviceType, OS, DeviceShell, DeviceLogEntry} from './BaseDevice'; import {DeviceType, OS, DeviceShell, DeviceLogEntry} from './BaseDevice';
import {SupportRequestDetailsMetaData} from '../utils/exportData'; import {SupportFormV2State} from '../reducers/supportForm';
function normalizeArchivedDeviceType(deviceType: DeviceType): DeviceType { function normalizeArchivedDeviceType(deviceType: DeviceType): DeviceType {
let archivedDeviceType = deviceType; let archivedDeviceType = deviceType;
@@ -29,7 +29,7 @@ export default class ArchivedDevice extends BaseDevice {
os: OS, os: OS,
logEntries: Array<DeviceLogEntry>, logEntries: Array<DeviceLogEntry>,
source: string = '', source: string = '',
supportRequestDetails?: SupportRequestDetailsMetaData, supportRequestDetails?: SupportFormV2State,
) { ) {
super(serial, normalizeArchivedDeviceType(deviceType), title, os); super(serial, normalizeArchivedDeviceType(deviceType), title, os);
this.logs = logEntries; this.logs = logEntries;
@@ -41,7 +41,7 @@ export default class ArchivedDevice extends BaseDevice {
isArchived = true; isArchived = true;
supportRequestDetails?: SupportRequestDetailsMetaData; supportRequestDetails?: SupportFormV2State;
getLogs() { getLogs() {
return this.logs; return this.logs;

View File

@@ -169,6 +169,14 @@ const reducer = (state: State = INITAL_STATE, action: Actions): State => {
selectedPlugin: payload != null ? null : selectedPlugin, selectedPlugin: payload != null ? null : selectedPlugin,
}; };
} }
case 'RESET_SUPPORT_FORM_V2_STATE': {
return updateSelection({
...state,
staticView: null,
});
}
case 'SELECT_DEVICE': { case 'SELECT_DEVICE': {
const {payload} = action; const {payload} = action;
return updateSelection({ return updateSelection({

View File

@@ -13,6 +13,8 @@ export type SupportFormV2State = {
description: string; description: string;
commitHash: string; commitHash: string;
appName: string; appName: string;
screenshots?: {image: string; description: string}[];
videos?: {url: string; description: string}[];
}; };
export type State = { export type State = {
@@ -27,6 +29,9 @@ export type Action =
| { | {
type: 'SET_SUPPORT_FORM_V2_STATE'; type: 'SET_SUPPORT_FORM_V2_STATE';
payload: SupportFormV2State; payload: SupportFormV2State;
}
| {
type: 'RESET_SUPPORT_FORM_V2_STATE';
}; };
export type NTUsersFormData = { export type NTUsersFormData = {
@@ -66,6 +71,8 @@ export default function reducer(
...state, ...state,
supportFormV2: action.payload, supportFormV2: action.payload,
}; };
} else if (action.type === 'RESET_SUPPORT_FORM_V2_STATE') {
return initialState();
} else { } else {
return state; return state;
} }
@@ -82,3 +89,7 @@ export const setSupportFormV2State = (payload: SupportFormV2State): Action => ({
type: 'SET_SUPPORT_FORM_V2_STATE', type: 'SET_SUPPORT_FORM_V2_STATE',
payload, payload,
}); });
export const resetSupportFormV2State = (): Action => ({
type: 'RESET_SUPPORT_FORM_V2_STATE',
});

View File

@@ -7,6 +7,9 @@
* @format * @format
*/ */
import os from 'os';
import path from 'path';
import electron from 'electron';
import {getInstance as getLogger} from '../fb-stubs/Logger'; import {getInstance as getLogger} from '../fb-stubs/Logger';
import {Store, MiddlewareAPI} from '../reducers'; import {Store, MiddlewareAPI} from '../reducers';
import {DeviceExport} from '../devices/BaseDevice'; import {DeviceExport} from '../devices/BaseDevice';
@@ -33,6 +36,12 @@ import {promisify} from 'util';
import promiseTimeout from './promiseTimeout'; import promiseTimeout from './promiseTimeout';
import {Idler} from './Idler'; import {Idler} from './Idler';
import {setStaticView} from '../reducers/connections'; import {setStaticView} from '../reducers/connections';
import {
SupportFormV2State,
resetSupportFormV2State,
} from '../reducers/supportForm';
import {setSelectPluginsToExportActiveSheet} from '../reducers/application';
export const IMPORT_FLIPPER_TRACE_EVENT = 'import-flipper-trace'; export const IMPORT_FLIPPER_TRACE_EVENT = 'import-flipper-trace';
export const EXPORT_FLIPPER_TRACE_EVENT = 'export-flipper-trace'; export const EXPORT_FLIPPER_TRACE_EVENT = 'export-flipper-trace';
export const EXPORT_FLIPPER_TRACE_TIME_SERIALIZATION_EVENT = `${EXPORT_FLIPPER_TRACE_EVENT}:serialization`; export const EXPORT_FLIPPER_TRACE_TIME_SERIALIZATION_EVENT = `${EXPORT_FLIPPER_TRACE_EVENT}:serialization`;
@@ -49,7 +58,7 @@ export type ExportType = {
pluginStates: PluginStatesExportState; pluginStates: PluginStatesExportState;
activeNotifications: Array<PluginNotification>; activeNotifications: Array<PluginNotification>;
}; };
supportRequestDetails?: SupportRequestDetailsMetaData; supportRequestDetails?: SupportFormV2State;
}; };
type ProcessPluginStatesOptions = { type ProcessPluginStatesOptions = {
@@ -83,15 +92,6 @@ type AddSaltToDeviceSerialOptions = {
statusUpdate?: (msg: string) => void; statusUpdate?: (msg: string) => void;
}; };
export type SupportRequestDetailsMetaData = {
title: string;
app: string;
description: string;
commitHash: string;
screenshots: {image: string; description: string}[];
videos: {url: string; description: string}[];
};
export function processClients( export function processClients(
clients: Array<ClientExport>, clients: Array<ClientExport>,
serial: string, serial: string,
@@ -545,6 +545,7 @@ export function exportStore(
idler, idler,
); );
if (exportData != null) { if (exportData != null) {
exportData.supportRequestDetails = store.getState().supportForm?.supportFormV2;
statusUpdate && statusUpdate('Serializing Flipper data...'); statusUpdate && statusUpdate('Serializing Flipper data...');
const serializedString = JSON.stringify(exportData); const serializedString = JSON.stringify(exportData);
if (serializedString.length <= 0) { if (serializedString.length <= 0) {
@@ -578,6 +579,7 @@ export const exportStoreToFile = (
({serializedString, errorArray}) => { ({serializedString, errorArray}) => {
return promisify(fs.writeFile)(exportFilePath, serializedString).then( return promisify(fs.writeFile)(exportFilePath, serializedString).then(
() => { () => {
store.dispatch(resetSupportFormV2State());
return {errorArray}; return {errorArray};
}, },
); );
@@ -697,3 +699,37 @@ export function showOpenDialog(store: Store) {
} }
}); });
} }
export function startFileExport(dispatch: Store['dispatch']) {
electron.remote.dialog
.showSaveDialog(
// @ts-ignore This appears to work but isn't allowed by the types
null,
{
title: 'FlipperExport',
defaultPath: path.join(os.homedir(), 'FlipperExport.flipper'),
},
)
.then(async (result: electron.SaveDialogReturnValue) => {
const file = result.filePath;
if (!file) {
return;
}
dispatch(
setSelectPluginsToExportActiveSheet({
type: 'file',
file: file,
closeOnFinish: false,
}),
);
});
}
export function startLinkExport(dispatch: Store['dispatch']) {
dispatch(
setSelectPluginsToExportActiveSheet({
type: 'link',
closeOnFinish: false,
}),
);
}