Add a modal with status updates for the universal export
Summary: Design doc: https://docs.google.com/document/d/1HLCFl46RfqG0o1mSt8SWrwf_HMfOCRg_oENioc1rkvQ/edit# Exporting all files form a device and export Flipper's own state could take a long time. We need to keep our users updated on the status. Reviewed By: passy Differential Revision: D40551661 fbshipit-source-id: d5c94fb99d4bc8b4495ce463915b77c475548f01
This commit is contained in:
committed by
Facebook GitHub Bot
parent
96aa0ac02b
commit
80f947212b
@@ -8,7 +8,16 @@
|
||||
*/
|
||||
|
||||
import React, {cloneElement, useState, useCallback, useMemo} from 'react';
|
||||
import {Button, Divider, Badge, Tooltip, Avatar, Popover, Menu} from 'antd';
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Badge,
|
||||
Tooltip,
|
||||
Avatar,
|
||||
Popover,
|
||||
Menu,
|
||||
Modal,
|
||||
} from 'antd';
|
||||
import {
|
||||
MobileFilled,
|
||||
AppstoreOutlined,
|
||||
@@ -64,12 +73,14 @@ import {
|
||||
startFileExport,
|
||||
startLinkExport,
|
||||
startFlipperLogsExport,
|
||||
ExportEverythingEverywhereAllAtOnceStatus,
|
||||
} from '../utils/exportData';
|
||||
import {openDeeplinkDialog} from '../deeplink';
|
||||
import {css} from '@emotion/css';
|
||||
import {getRenderHostInstance} from 'flipper-frontend-core';
|
||||
import openSupportRequestForm from '../fb-stubs/openSupportRequestForm';
|
||||
import {StyleGuide} from './StyleGuide';
|
||||
import {useEffect} from 'react';
|
||||
|
||||
const LeftRailButtonElem = styled(Button)<{kind?: 'small'}>(({kind}) => ({
|
||||
width: kind === 'small' ? 32 : 36,
|
||||
@@ -435,14 +446,88 @@ function DebugLogsButton({
|
||||
|
||||
function ExportEverythingEverywhereAllAtOnceButton() {
|
||||
const store = useStore();
|
||||
const [status, setStatus] = useState<
|
||||
ExportEverythingEverywhereAllAtOnceStatus | undefined
|
||||
>();
|
||||
const [statusMessage, setStatusMessage] = useState<JSX.Element | undefined>();
|
||||
|
||||
const exportEverythingEverywhereAllAtOnceTracked = useTrackedCallback(
|
||||
'Debug data export',
|
||||
() => exportEverythingEverywhereAllAtOnce(store),
|
||||
[store],
|
||||
() => exportEverythingEverywhereAllAtOnce(store, setStatus),
|
||||
[store, setStatus],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
switch (status) {
|
||||
case 'logs': {
|
||||
setStatusMessage(<p>Exporting Flipper logs...</p>);
|
||||
return;
|
||||
}
|
||||
case 'files': {
|
||||
let sheepCount = 0;
|
||||
const setFileExportMessage = () => {
|
||||
setStatusMessage(
|
||||
<>
|
||||
<p>Exporting Flipper debug files from all devices...</p>
|
||||
<p>It could take a long time!</p>
|
||||
<p>Let's count sheep while we wait: {sheepCount++}.</p>
|
||||
<p>
|
||||
Scream for help if the sheep count reaches 42, but not earlier.
|
||||
</p>
|
||||
</>,
|
||||
);
|
||||
};
|
||||
|
||||
setFileExportMessage();
|
||||
|
||||
const interval = setInterval(setFileExportMessage, 3000);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
case 'state': {
|
||||
let dinosaursCount = 0;
|
||||
const setStateExportMessage = () => {
|
||||
setStatusMessage(
|
||||
<>
|
||||
<p>Exporting Flipper state...</p>
|
||||
<p>It also could take a long time!</p>
|
||||
<p>This time we could count dinosaurs: {dinosaursCount++}.</p>
|
||||
<p>You already know what to do when the counter reaches 42.</p>
|
||||
</>,
|
||||
);
|
||||
};
|
||||
|
||||
setStateExportMessage();
|
||||
|
||||
const interval = setInterval(setStateExportMessage, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
case 'archive': {
|
||||
setStatusMessage(<p>Creating an archive...</p>);
|
||||
return;
|
||||
}
|
||||
case 'done': {
|
||||
setStatusMessage(<p>Done!</p>);
|
||||
return;
|
||||
}
|
||||
case 'cancelled': {
|
||||
setStatusMessage(<p>Cancelled! Why? 😱🤯👏</p>);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, [status]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
visible={!!status}
|
||||
centered
|
||||
onCancel={() => {
|
||||
setStatus(undefined);
|
||||
}}
|
||||
title="Exporting everything everywhere all at once"
|
||||
footer={null}>
|
||||
{statusMessage}
|
||||
</Modal>
|
||||
<LeftRailButton
|
||||
icon={<BugOutlined />}
|
||||
title="Export Flipper debug data"
|
||||
@@ -451,6 +536,7 @@ function ExportEverythingEverywhereAllAtOnceButton() {
|
||||
}}
|
||||
small
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -616,16 +616,27 @@ export async function startFlipperLogsExport() {
|
||||
}
|
||||
|
||||
async function startDeviceFlipperFolderExport() {
|
||||
return await getRenderHostInstance().flipperServer.exec('fetch-debug-data');
|
||||
return await getRenderHostInstance().flipperServer.exec(
|
||||
{timeout: 3 * 60 * 1000},
|
||||
'fetch-debug-data',
|
||||
);
|
||||
}
|
||||
|
||||
export type ExportEverythingEverywhereAllAtOnceStatus =
|
||||
| 'logs'
|
||||
| 'files'
|
||||
| 'state'
|
||||
| 'archive'
|
||||
| 'done'
|
||||
| 'cancelled';
|
||||
export async function exportEverythingEverywhereAllAtOnce(
|
||||
store: MiddlewareAPI,
|
||||
onStatusUpdate?: (status: ExportEverythingEverywhereAllAtOnceStatus) => void,
|
||||
) {
|
||||
// TODO: Show a progress dialog
|
||||
const zip = new JSZip();
|
||||
|
||||
// Step 1: Export Flipper logs
|
||||
onStatusUpdate?.('logs');
|
||||
const serializedLogs = exportLogs
|
||||
.map((item) => JSON.stringify(item))
|
||||
.join('\n');
|
||||
@@ -633,6 +644,7 @@ export async function exportEverythingEverywhereAllAtOnce(
|
||||
zip.file('flipper_logs.txt', serializedLogs);
|
||||
|
||||
// Step 2: Export device logs
|
||||
onStatusUpdate?.('files');
|
||||
const flipperFolderContent = await startDeviceFlipperFolderExport();
|
||||
|
||||
const deviceFlipperFolder = zip.folder('device_flipper_folder')!;
|
||||
@@ -661,6 +673,7 @@ export async function exportEverythingEverywhereAllAtOnce(
|
||||
});
|
||||
|
||||
// Step 3: Export Flipper State
|
||||
onStatusUpdate?.('state');
|
||||
const exportablePlugins = getExportablePlugins(store.getState());
|
||||
// TODO: no need to put this in the store,
|
||||
// need to be cleaned up later in combination with SupportForm
|
||||
@@ -669,9 +682,21 @@ export async function exportEverythingEverywhereAllAtOnce(
|
||||
|
||||
zip.file('flipper_export', serializedString);
|
||||
|
||||
onStatusUpdate?.('archive');
|
||||
const archiveData = await zip.generateAsync({type: 'uint8array'});
|
||||
|
||||
await getRenderHostInstance().exportFileBinary?.(archiveData);
|
||||
const exportedFilePath = await getRenderHostInstance().exportFileBinary?.(
|
||||
archiveData,
|
||||
{
|
||||
defaultPath: 'flipper_EEAaO_export',
|
||||
},
|
||||
);
|
||||
|
||||
if (exportedFilePath) {
|
||||
onStatusUpdate?.('done');
|
||||
} else {
|
||||
onStatusUpdate?.('cancelled');
|
||||
}
|
||||
}
|
||||
|
||||
export async function startFileExport(dispatch: Store['dispatch']) {
|
||||
|
||||
Reference in New Issue
Block a user