diff --git a/src/chrome/ShareSheet.js b/src/chrome/ShareSheet.js index a8833ce2b..1dc84b243 100644 --- a/src/chrome/ShareSheet.js +++ b/src/chrome/ShareSheet.js @@ -80,6 +80,7 @@ type State = { | { flipperUrl: string, }, + statusUpdate: ?string, }; export default class ShareSheet extends Component { @@ -90,6 +91,7 @@ export default class ShareSheet extends Component { state = { errorArray: [], result: null, + statusUpdate: null, }; idler = new Idler(); @@ -99,7 +101,9 @@ export default class ShareSheet extends Component { performance.mark(mark); try { const {serializedString, errorArray} = await reportPlatformFailures( - exportStore(this.context.store, this.idler), + exportStore(this.context.store, this.idler, (msg: string) => { + this.setState({statusUpdate: msg}); + }), `${EXPORT_FLIPPER_TRACE_EVENT}:UI_LINK`, ); @@ -129,66 +133,80 @@ export default class ShareSheet extends Component { } } + renderTheProgessState(onHide: () => void, statusUpdate: ?string) { + return ( + + +
+ + {statusUpdate && statusUpdate.length > 0 ? ( + + {statusUpdate} + + ) : ( + + Uploading Flipper trace... + + )} +
+ + + + +
+
+ ); + } + render() { const onHide = () => { this.props.onHide(); this.idler.cancel(); }; + const {result, statusUpdate, errorArray} = this.state; + if (!result || !result.flipperUrl) { + return this.renderTheProgessState(onHide, statusUpdate); + } return ( - {this.state.result ? ( - <> - - {this.state.result.flipperUrl ? ( - <> - Data Upload Successful - - Flipper's data was successfully uploaded. This URL can be - used to share with other Flipper users. Opening it will - import the data from your trace. - - - - When sharing your Flipper link, consider that the captured - data might contain sensitve information like access tokens - used in network requests. - - - )} - - ) : ( - <> - {this.state.result.error_class || 'Error'} - - {this.state.result.error || - 'The data could not be uploaded'} - - - )} - - - - - - - ) : ( + <> -
- - - Uploading Flipper trace... - -
- - - - + {result.flipperUrl ? ( + <> + Data Upload Successful + + Flipper's data was successfully uploaded. This URL can be used + to share with other Flipper users. Opening it will import the + data from your trace. + + + + When sharing your Flipper link, consider that the captured + data might contain sensitve information like access tokens + used in network requests. + + + )} + + ) : ( + <> + {result.error_class || 'Error'} + + {result.error || 'The data could not be uploaded'} + + + )}
- )} + + + + + + )
); } diff --git a/src/chrome/ShareSheetExportFile.js b/src/chrome/ShareSheetExportFile.js index 8cc92af08..2ab46d9c7 100644 --- a/src/chrome/ShareSheetExportFile.js +++ b/src/chrome/ShareSheetExportFile.js @@ -71,6 +71,7 @@ type State = { success: boolean, error: ?Error, }, + statusUpdate: ?string, }; export default class ShareSheetExportFile extends Component { @@ -81,6 +82,7 @@ export default class ShareSheetExportFile extends Component { state = { errorArray: [], result: null, + statusUpdate: null, }; idler = new Idler(); @@ -95,7 +97,14 @@ export default class ShareSheetExportFile extends Component { return; } const {errorArray} = await reportPlatformFailures( - exportStoreToFile(this.props.file, this.context.store, this.idler), + exportStoreToFile( + this.props.file, + this.context.store, + this.idler, + (msg: string) => { + this.setState({statusUpdate: msg}); + }, + ), `${EXPORT_FLIPPER_TRACE_EVENT}:UI_FILE`, ); this.setState({errorArray, result: {success: true, error: null}}); @@ -115,7 +124,7 @@ export default class ShareSheetExportFile extends Component { if (!this.props.file) { return this.renderNoFileError(onHide); } - const {result} = this.state; + const {result, statusUpdate} = this.state; if (result) { const {success, error} = result; if (success) { @@ -161,9 +170,15 @@ export default class ShareSheetExportFile extends Component {
- - Exporting Flipper trace... - + {statusUpdate && statusUpdate.length > 0 ? ( + + {statusUpdate} + + ) : ( + + Exporting Flipper trace... + + )}
diff --git a/src/utils/exportData.js b/src/utils/exportData.js index 036882b88..7ffeb9561 100644 --- a/src/utils/exportData.js +++ b/src/utils/exportData.js @@ -43,8 +43,14 @@ export type ExportType = {| export function processClients( clients: Array, serial: string, + statusUpdate?: (msg: string) => void, ): Array { - return clients.filter(client => client.query.device_id === serial); + statusUpdate && + statusUpdate(`Filtering Clients for the device id ${serial}...`); + const filteredClients = clients.filter( + client => client.query.device_id === serial, + ); + return filteredClients; } export function pluginsClassMap( @@ -68,8 +74,11 @@ export function processPluginStates( serial: string, allPluginStates: PluginStatesState, devicePlugins: Map>>, + statusUpdate?: (msg: string) => void, ): PluginStatesState { let pluginStates = {}; + statusUpdate && + statusUpdate('Filtering the plugin states for the filtered Clients...'); for (const key in allPluginStates) { const keyArray = key.split('#'); const pluginName = keyArray.pop(); @@ -93,7 +102,10 @@ export function processNotificationStates( serial: string, allActiveNotifications: Array, devicePlugins: Map>>, + statusUpdate?: (msg: string) => void, ): Array { + statusUpdate && + statusUpdate('Filtering the notifications for the filtered Clients...'); const activeNotifications = allActiveNotifications.filter(notif => { const filteredClients = clients.filter(client => notif.client ? client.id.includes(notif.client) : false, @@ -112,6 +124,7 @@ const addSaltToDeviceSerial = async ( clients: Array, pluginStates: PluginStatesState, pluginNotification: Array, + statusUpdate?: (msg: string) => void, ): Promise => { const {serial} = device; const newSerial = salt + '-' + serial; @@ -122,6 +135,8 @@ const addSaltToDeviceSerial = async ( device.os, device.getLogs(), ); + statusUpdate && + statusUpdate('Adding salt to the selected device id in the client data...'); const updatedClients = clients.map((client: ClientExport) => { return { ...client, @@ -130,6 +145,10 @@ const addSaltToDeviceSerial = async ( }; }); + statusUpdate && + statusUpdate( + 'Adding salt to the selected device id in the plugin states...', + ); const updatedPluginStates: PluginStatesState = {}; for (let key in pluginStates) { if (!key.includes(serial)) { @@ -142,6 +161,10 @@ const addSaltToDeviceSerial = async ( updatedPluginStates[key] = pluginData; } + statusUpdate && + statusUpdate( + 'Adding salt to the selected device id in the notification data...', + ); const updatedPluginNotifications = pluginNotification.map(notif => { if (!notif.client || !notif.client.includes(serial)) { throw new Error( @@ -172,21 +195,24 @@ export const processStore = async ( clients: Array, devicePlugins: Map>>, salt: string, + statusUpdate?: (msg: string) => void, ): Promise => { if (device) { const {serial} = device; - const processedClients = processClients(clients, serial); + const processedClients = processClients(clients, serial, statusUpdate); const processedPluginStates = processPluginStates( processedClients, serial, pluginStates, devicePlugins, + statusUpdate, ); const processedActiveNotifications = processNotificationStates( processedClients, serial, activeNotifications, devicePlugins, + statusUpdate, ); // Adding salt to the device id, so that the device_id in the device list is unique. const exportFlipperData = await addSaltToDeviceSerial( @@ -195,6 +221,7 @@ export const processStore = async ( processedClients, processedPluginStates, processedActiveNotifications, + statusUpdate, ); return exportFlipperData; } @@ -205,6 +232,7 @@ export async function fetchMetadata( pluginStates: PluginStatesState, pluginsMap: Map | FlipperPlugin<>>>, store: MiddlewareAPI, + statusUpdate?: (msg: string) => void, ): Promise<{pluginStates: PluginStatesState, errorArray: Array}> { const newPluginState = {...pluginStates}; const errorArray: Array = []; @@ -226,6 +254,8 @@ export async function fetchMetadata( if (exportState) { const key = pluginKey(client.id, plugin); try { + statusUpdate && + statusUpdate(`Fetching metadata for plugin ${plugin}...`); const data = await promiseTimeout( 120000, // Timeout in 2 mins exportState(callClient(client, plugin), newPluginState[key], store), @@ -244,6 +274,7 @@ export async function fetchMetadata( export async function getStoreExport( store: MiddlewareAPI, + statusUpdate?: (msg: string) => void, ): Promise<{exportData: ?ExportType, errorArray: Array}> { const state = store.getState(); const {clients} = state.connections; @@ -266,7 +297,13 @@ export async function getStoreExport( plugins.devicePlugins.forEach((val, key) => { pluginsMap.set(key, val); }); - const metadata = await fetchMetadata(pluginStates, pluginsMap, store); + statusUpdate && statusUpdate('Preparing to fetch metadata from client...'); + const metadata = await fetchMetadata( + pluginStates, + pluginsMap, + store, + statusUpdate, + ); const {errorArray} = metadata; const newPluginState = metadata.pluginStates; @@ -279,6 +316,7 @@ export async function getStoreExport( clients.map(client => client.toJSON()), devicePlugins, uuid.v4(), + statusUpdate, ); return {exportData, errorArray}; } @@ -286,16 +324,22 @@ export async function getStoreExport( export function exportStore( store: MiddlewareAPI, idler?: Idler, + statusUpdate?: (msg: string) => void, ): Promise<{serializedString: string, errorArray: Array}> { getLogger().track('usage', EXPORT_FLIPPER_TRACE_EVENT); return new Promise(async (resolve, reject) => { try { - const {exportData, errorArray} = await getStoreExport(store); + statusUpdate && statusUpdate('Preparing to export Flipper data...'); + const {exportData, errorArray} = await getStoreExport( + store, + statusUpdate, + ); if (!exportData) { console.error('Make sure a device is connected'); reject(new Error('No device is selected')); } try { + statusUpdate && statusUpdate('Serializing Flipper data...'); const serializedString = await serialize(exportData, idler); if (serializedString.length <= 0) { reject(new Error('Serialize function returned empty string')); @@ -314,14 +358,17 @@ export const exportStoreToFile = ( exportFilePath: string, store: Store, idler?: Idler, + statusUpdate?: (msg: string) => void, ): Promise<{errorArray: Array}> => { - return exportStore(store, idler).then(({serializedString, errorArray}) => { - return promisify(fs.writeFile)(exportFilePath, serializedString).then( - () => { - return {errorArray}; - }, - ); - }); + return exportStore(store, idler, statusUpdate).then( + ({serializedString, errorArray}) => { + return promisify(fs.writeFile)(exportFilePath, serializedString).then( + () => { + return {errorArray}; + }, + ); + }, + ); }; export function importDataToStore(data: string, store: Store) {