diff --git a/desktop/flipper-ui-core/src/chrome/MetroButton.tsx b/desktop/flipper-ui-core/src/chrome/MetroButton.tsx new file mode 100644 index 000000000..54652960a --- /dev/null +++ b/desktop/flipper-ui-core/src/chrome/MetroButton.tsx @@ -0,0 +1,94 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React, {useCallback, useEffect, useState} from 'react'; +import {MetroReportableEvent} from 'flipper-common'; +import {useStore} from '../utils/useStore'; +import {Button as AntButton} from 'antd'; +import {MenuOutlined, ReloadOutlined} from '@ant-design/icons'; +import {theme} from 'flipper-plugin'; +import {BaseDevice} from 'flipper-frontend-core'; + +export default function MetroButton() { + const device = useStore((state) => + state.connections.devices.find( + (device) => device.os === 'Metro' && device.connected.get(), + ), + ) as BaseDevice | undefined; + + const sendCommand = useCallback( + (command: string) => { + device?.sendMetroCommand(command); + }, + [device], + ); + const [progress, setProgress] = useState(1); + const [_hasBuildError, setHasBuildError] = useState(false); + + useEffect(() => { + if (!device) { + return; + } + function metroEventListener(event: MetroReportableEvent) { + if (event.type === 'bundle_build_started') { + setHasBuildError(false); + setProgress(0); + } else if (event.type === 'bundle_build_failed') { + setHasBuildError(true); + setProgress(1); + } else if (event.type === 'bundle_build_done') { + setHasBuildError(false); + setProgress(1); + } else if (event.type === 'bundle_transform_progressed') { + setProgress(event.transformedFileCount / event.totalFileCount); + } + } + + const handle = device.addLogListener((l) => { + if (l.tag !== 'client_log') { + try { + metroEventListener(JSON.parse(l.message)); + } catch (e) { + console.warn('Failed to parse metro message: ', l, e); + } + } + }); + + return () => { + device.removeLogListener(handle); + }; + }, [device]); + + if (!device) { + return null; + } + + return ( + <> + } + title="Reload React Native App" + type="ghost" + onClick={() => { + sendCommand('reload'); + }} + loading={progress < 1} + style={{color: _hasBuildError ? theme.errorColor : undefined}} + /> + } + title="Open the React Native Dev Menu on the device" + type="ghost" + onClick={() => { + sendCommand('devMenu'); + }} + /> + + ); +} diff --git a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx index b54caee10..ce9aa6586 100644 --- a/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx +++ b/desktop/flipper-ui-core/src/sandy-chrome/appinspect/AppInspect.tsx @@ -13,6 +13,7 @@ import {LeftSidebar, SidebarTitle} from '../LeftSidebar'; import {Layout, styled} from '../../ui'; import {theme, useValue} from 'flipper-plugin'; import {PluginList} from './PluginList'; +import MetroButton from '../../chrome/MetroButton'; import {BookmarkSection} from './BookmarkSection'; import Client from '../../Client'; import {BaseDevice} from 'flipper-frontend-core'; @@ -41,6 +42,11 @@ export function AppInspect() { {isDeviceConnected && isAppConnected && } + {isDeviceConnected && activeDevice && ( + + + + )}