diff --git a/desktop/app/src/Client.tsx b/desktop/app/src/Client.tsx index 24fb45f90..27a95a816 100644 --- a/desktop/app/src/Client.tsx +++ b/desktop/app/src/Client.tsx @@ -14,7 +14,6 @@ import { FlipperDevicePlugin, } from './plugin'; import BaseDevice, {OS} from './devices/BaseDevice'; -import {LegacyApp} from './chrome/LegacyApp'; import {Logger} from './fb-interfaces/Logger'; import {Store} from './reducers/index'; import {setPluginState} from './reducers/pluginStates'; @@ -124,7 +123,6 @@ export interface FlipperClientConnection { } export default class Client extends EventEmitter { - app: LegacyApp | undefined; connected: boolean; id: string; query: ClientQuery; diff --git a/desktop/app/src/chrome/AppWrapper.tsx b/desktop/app/src/chrome/AppWrapper.tsx deleted file mode 100644 index 38979c07f..000000000 --- a/desktop/app/src/chrome/AppWrapper.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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 from 'react'; -import {useEffect} from 'react'; -import LegacyApp from './LegacyApp'; -import fbConfig from '../fb-stubs/config'; -import {isFBEmployee} from '../utils/fbEmployee'; -import {Logger} from '../fb-interfaces/Logger'; -import isSandyEnabled from '../utils/isSandyEnabled'; -import {SandyApp} from '../sandy-chrome/SandyApp'; -import {notification} from 'antd'; -import isProduction from '../utils/isProduction'; - -type Props = {logger: Logger}; - -export default function App(props: Props) { - useEffect(() => { - if (fbConfig.warnFBEmployees && isProduction()) { - isFBEmployee().then((isEmployee) => { - if (isEmployee) { - notification.warning({ - placement: 'bottomLeft', - message: 'Please use Flipper@FB', - description: ( - <> - You are using the open-source version of Flipper. Install the - internal build from Managed Software Center to get access to - more plugins. - - ), - duration: null, - }); - } - }); - } - }, []); - return isSandyEnabled() ? : ; -} diff --git a/desktop/app/src/chrome/ConsoleLogs.tsx b/desktop/app/src/chrome/ConsoleLogs.tsx index cc0beae84..84e9c76fa 100644 --- a/desktop/app/src/chrome/ConsoleLogs.tsx +++ b/desktop/app/src/chrome/ConsoleLogs.tsx @@ -16,7 +16,6 @@ import type {Styles} from 'console-feed/lib/definitions/Styles'; import {createState, useValue} from 'flipper-plugin'; import {useLocalStorage} from '../utils/useLocalStorage'; import {theme} from 'flipper-plugin'; -import {useIsSandy} from '../sandy-chrome/SandyContext'; import {useIsDarkMode} from '../utils/useIsDarkMode'; const MAX_LOG_ITEMS = 1000; @@ -65,7 +64,6 @@ const allLogLevels: Methods[] = [ const defaultLogLevels: Methods[] = ['warn', 'error', 'table', 'assert']; export function ConsoleLogs() { - const isSandy = useIsSandy(); const isDarkMode = useIsDarkMode(); const logs = useValue(logsAtom); const [logLevels, setLogLevels] = useLocalStorage( @@ -90,7 +88,7 @@ export function ConsoleLogs() { ); }, [logLevels, setLogLevels]); - const styles = useMemo(() => buildTheme(isSandy), [isSandy]); + const styles = useMemo(buildTheme, []); return ( @@ -106,7 +104,7 @@ export function ConsoleLogs() { @@ -114,15 +112,7 @@ export function ConsoleLogs() { ); } -function buildTheme(isSandy: boolean): Styles { - if (!isSandy) { - const bg = '#333'; - return { - BASE_BACKGROUND_COLOR: bg, - BASE_COLOR: 'white', - LOG_BACKGROUND: bg, - }; - } +function buildTheme(): Styles { return { // See: https://github.com/samdenty/console-feed/blob/master/src/definitions/Styles.d.ts BASE_BACKGROUND_COLOR: 'transparent', diff --git a/desktop/app/src/chrome/DetailSidebar.tsx b/desktop/app/src/chrome/DetailSidebar.tsx index 9960db5f4..962e98da8 100644 --- a/desktop/app/src/chrome/DetailSidebar.tsx +++ b/desktop/app/src/chrome/DetailSidebar.tsx @@ -12,7 +12,6 @@ import ReactDOM from 'react-dom'; import Sidebar from '../ui/components/Sidebar'; import {toggleRightSidebarAvailable} from '../reducers/application'; import {useDispatch, useStore} from '../utils/useStore'; -import {useIsSandy} from '../sandy-chrome/SandyContext'; import {ContentContainer} from '../sandy-chrome/ContentContainer'; import {Layout} from '../ui'; @@ -33,7 +32,6 @@ export default function DetailSidebar({children, width, minWidth}: OwnProps) { return
{children}
; } - const isSandy = useIsSandy(); const dispatch = useDispatch(); const {rightSidebarAvailable, rightSidebarVisible} = useStore((state) => { const {rightSidebarAvailable, rightSidebarVisible} = state.application; @@ -72,16 +70,10 @@ export default function DetailSidebar({children, width, minWidth}: OwnProps) { minWidth={minWidth} width={width || 300} position="right" - gutter={isSandy}> - {isSandy ? ( - - - {children} - - - ) : ( - children - )} + gutter> + + {children} + , domNode, )) || diff --git a/desktop/app/src/chrome/DevicesButton.tsx b/desktop/app/src/chrome/DevicesButton.tsx deleted file mode 100644 index 5c6a3bf63..000000000 --- a/desktop/app/src/chrome/DevicesButton.tsx +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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 {Button, styled} from '../ui'; -import {connect, ReactReduxContext} from 'react-redux'; - -import {selectDevice, preferDevice} from '../reducers/connections'; -import { - setActiveSheet, - ActiveSheet, - ACTIVE_SHEET_JS_EMULATOR_LAUNCHER, -} from '../reducers/application'; -import {showOpenDialog} from '../utils/exportData'; -import BaseDevice from '../devices/BaseDevice'; -import React, {Component} from 'react'; -import {State} from '../reducers'; -import GK from '../fb-stubs/GK'; -import {launchEmulator} from '../devices/AndroidDevice'; - -type StateFromProps = { - selectedDevice: BaseDevice | null | undefined; - androidEmulators: Array; - devices: Array; -}; - -type DispatchFromProps = { - selectDevice: (device: BaseDevice) => void; - preferDevice: (device: string) => void; - setActiveSheet: (sheet: ActiveSheet) => void; -}; - -type OwnProps = {}; - -type Props = OwnProps & StateFromProps & DispatchFromProps; -const DropdownButton = styled(Button)({ - fontSize: 11, -}); - -class DevicesButton extends Component { - launchEmulator = async (name: string) => { - await launchEmulator(name); - this.props.preferDevice(name); - }; - - render() { - const { - devices, - androidEmulators, - selectedDevice, - selectDevice, - } = this.props; - let buttonLabel = 'No device selected'; - let icon = 'minus-circle'; - - if (selectedDevice && selectedDevice.isArchived) { - buttonLabel = `${selectedDevice.displayTitle() || 'Unknown device'}`; - icon = 'box'; - } else if (selectedDevice && selectedDevice.deviceType === 'physical') { - buttonLabel = selectedDevice.displayTitle() || 'Unknown device'; - icon = 'mobile'; - } else if (selectedDevice && selectedDevice.deviceType === 'emulator') { - buttonLabel = selectedDevice.displayTitle() || 'Unknown emulator'; - icon = 'desktop'; - } - - const dropdown: any[] = []; - - // Physical devices - const connectedDevices = [ - { - label: 'Connected Devices', - enabled: false, - }, - ...devices - .filter((device) => device.deviceType === 'physical') - .map((device: BaseDevice) => ({ - click: () => selectDevice(device), - checked: device === selectedDevice, - label: `📱 ${device.displayTitle()}`, - type: 'checkbox', - })), - ]; - if (connectedDevices.length > 1) { - dropdown.push(...connectedDevices); - } - // Emulators - const runningEmulators = [ - { - label: 'Running Emulators', - enabled: false, - }, - ...devices - .filter((device) => device.deviceType === 'emulator') - .map((device: BaseDevice) => ({ - click: () => selectDevice(device), - checked: device === selectedDevice, - label: device.displayTitle(), - type: 'checkbox', - })), - ]; - if (runningEmulators.length > 1) { - dropdown.push(...runningEmulators); - } - // Archived - const importedFiles = [ - { - label: 'Disconnected Devices', - enabled: false, - }, - ...devices - .filter((device) => device.isArchived) - .map((device: BaseDevice) => ({ - click: () => selectDevice(device), - checked: device === selectedDevice, - label: `📦 ${device.displayTitle()}`, - type: 'checkbox', - })), - ]; - if (importedFiles.length > 1) { - dropdown.push(...importedFiles); - } - // Launch JS emulator - if (GK.get('flipper_js_client_emulator')) { - dropdown.push( - {type: 'separator' as 'separator'}, - { - label: 'Launch JS Web App', - click: () => - this.props.setActiveSheet(ACTIVE_SHEET_JS_EMULATOR_LAUNCHER), - }, - ); - } - // Launch Android emulators - if (androidEmulators.length > 0) { - const emulators = Array.from(androidEmulators) - .filter( - (name: string) => - devices.findIndex( - (device: BaseDevice) => - device.title === name && !device.isArchived, - ) === -1, - ) - .map((name: string) => ({ - label: name, - click: () => this.launchEmulator(name), - })); - - if (emulators.length > 0) { - dropdown.push( - {type: 'separator' as 'separator'}, - { - label: 'Launch Android emulators', - enabled: false, - }, - ...emulators, - ); - } - } - - if (dropdown.length > 0) { - dropdown.push({type: 'separator' as 'separator'}); - } - return ( - - {({store}) => { - dropdown.push({ - label: 'Open File...', - click: () => { - showOpenDialog(store); - }, - }); - return ( - - {buttonLabel} - - ); - }} - - ); - } -} -export default connect( - ({connections: {devices, androidEmulators, selectedDevice}}) => ({ - devices, - androidEmulators, - selectedDevice, - }), - { - selectDevice, - preferDevice, - setActiveSheet, - }, -)(DevicesButton); diff --git a/desktop/app/src/chrome/DoctorBar.tsx b/desktop/app/src/chrome/DoctorBar.tsx deleted file mode 100644 index 6cf241e83..000000000 --- a/desktop/app/src/chrome/DoctorBar.tsx +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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 {styled, colors} from '../ui'; -import React, {Component} from 'react'; -import {connect} from 'react-redux'; -import { - setActiveSheet, - ActiveSheet, - ACTIVE_SHEET_DOCTOR, - ACTIVE_SHEET_SETTINGS, -} from '../reducers/application'; -import {State as Store} from '../reducers/index'; -import {ButtonGroup, Button} from '../ui'; -import {FlexColumn, FlexRow} from '../ui'; -import runHealthchecks, { - HealthcheckSettings, - HealthcheckEventsHandler, -} from '../utils/runHealthchecks'; -import { - updateHealthcheckResult, - startHealthchecks, - finishHealthchecks, - HealthcheckReport, - HealthcheckResult, -} from '../reducers/healthchecks'; - -import {reportUsage} from '../utils/metrics'; - -type StateFromProps = { - healthcheckReport: HealthcheckReport; -} & HealthcheckSettings; - -type DispatchFromProps = { - setActiveSheet: (payload: ActiveSheet) => void; -} & HealthcheckEventsHandler; - -type State = {visible: boolean; message: string; showSettingsButton: boolean}; - -type Props = DispatchFromProps & StateFromProps; -class DoctorBar extends Component { - constructor(props: Props) { - super(props); - this.state = { - visible: false, - message: '', - showSettingsButton: false, - }; - } - componentDidMount() { - this.showMessageIfChecksFailed(); - } - static getDerivedStateFromProps(props: Props, state: State): State | null { - const failedCategories = Object.values( - props.healthcheckReport.categories, - ).filter((cat) => hasProblems(cat.result)); - if (failedCategories.length == 1) { - const failedCat = failedCategories[0]; - if (failedCat.key === 'ios' || failedCat.key === 'android') { - return { - ...state, - message: `Doctor has discovered problems with your ${failedCat.label} setup. If you are not interested in ${failedCat.label} development you can disable it in Settings.`, - showSettingsButton: true, - }; - } - } - if (failedCategories.length) { - return { - ...state, - message: 'Doctor has discovered problems with your installation.', - showSettingsButton: false, - }; - } - return null; - } - async showMessageIfChecksFailed() { - await runHealthchecks(this.props); - const result = this.props.healthcheckReport.result; - if (hasProblems(result)) { - if (result.isAcknowledged) { - reportUsage('doctor:warning:suppressed'); - } else { - this.setVisible(true); - reportUsage('doctor:warning:shown'); - } - } - } - render() { - return ( - this.state.visible && ( - - - - - - - {this.state.showSettingsButton && ( - - )} - - - - - {this.state.message} - - - - - ) - ); - } - setVisible(visible: boolean) { - this.setState((prevState) => { - return { - ...prevState, - visible, - }; - }); - } -} - -export default connect( - ({settingsState, healthchecks: {healthcheckReport}}) => ({ - healthcheckReport, - settings: settingsState, - }), - { - setActiveSheet, - updateHealthcheckResult, - startHealthchecks, - finishHealthchecks, - }, -)(DoctorBar); - -const Container = styled.div({ - boxShadow: '2px 2px 2px #ccc', - userSelect: 'text', -}); - -const WarningContainer = styled.div({ - backgroundColor: colors.orange, - color: '#fff', - maxHeight: '600px', - overflowY: 'auto', - overflowX: 'hidden', - transition: 'max-height 0.3s ease', - '&.collapsed': { - maxHeight: '0px', - }, - padding: '4px 12px', - borderBottom: '1px solid ' + colors.orangeDark3, - verticalAlign: 'middle', - lineHeight: '28px', -}); - -const ButtonSection = styled(FlexColumn)({ - marginLeft: '8px', - flexShrink: 0, - flexGrow: 0, -}); - -function hasProblems(result: HealthcheckResult) { - return result.status === 'WARNING' || result.status === 'FAILED'; -} diff --git a/desktop/app/src/chrome/DoctorSheet.tsx b/desktop/app/src/chrome/DoctorSheet.tsx index 40700a07a..98a6e7632 100644 --- a/desktop/app/src/chrome/DoctorSheet.tsx +++ b/desktop/app/src/chrome/DoctorSheet.tsx @@ -222,7 +222,7 @@ function hasNewProblems(result: HealthcheckResult) { return hasProblems(result) && !result.isAcknowledged; } -export type State = { +type State = { acknowledgeCheckboxVisible: boolean; acknowledgeOnClose?: boolean; selectedCheckKey?: string; diff --git a/desktop/app/src/chrome/JSEmulatorLauncherSheet.tsx b/desktop/app/src/chrome/JSEmulatorLauncherSheet.tsx index a24b562d8..27d49e9e2 100644 --- a/desktop/app/src/chrome/JSEmulatorLauncherSheet.tsx +++ b/desktop/app/src/chrome/JSEmulatorLauncherSheet.tsx @@ -7,16 +7,7 @@ * @format */ -import { - FlexColumn, - Button, - styled, - Text, - FlexRow, - Spacer, - Input, - Label, -} from '../ui'; +import {Button} from '../ui'; import React, {Component} from 'react'; import {connect} from 'react-redux'; import {State as Store} from '../reducers'; @@ -26,31 +17,8 @@ import {Settings} from '../reducers/settings'; import {Collapse, Form, Input as AntInput} from 'antd'; import {Html5Outlined} from '@ant-design/icons'; -const Container = styled(FlexColumn)({ - padding: 20, - width: 800, -}); - -const Title = styled(Text)({ - marginBottom: 18, - marginRight: 10, - fontWeight: 100, - fontSize: '40px', -}); - -const textareaStyle = { - margin: 0, - marginBottom: 10, -}; - -const TitleInput = styled(Input)({ - ...textareaStyle, - height: 30, -}); - type OwnProps = { onHide: () => void; - useSandy?: boolean; }; type StateFromProps = { @@ -96,7 +64,7 @@ class JSEmulatorLauncherSheet extends Component { render() { const {url, height, width} = this.state; - return this.props.useSandy ? ( + return (
@@ -113,27 +81,6 @@ class JSEmulatorLauncherSheet extends Component {
- ) : ( - - Launch Web App - - - - - - - -
- - - - - -
); } } @@ -154,7 +101,7 @@ export function JSEmulatorLauncherSheetSandy({onClose}: {onClose(): void}) { extra={} header="Launch JS Web App" key="launch-js-web-app"> - + ); diff --git a/desktop/app/src/chrome/LegacyApp.tsx b/desktop/app/src/chrome/LegacyApp.tsx deleted file mode 100644 index f7552aa31..000000000 --- a/desktop/app/src/chrome/LegacyApp.tsx +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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 from 'react'; -import {FlexRow, styled, Layout} from '../ui'; -import {connect} from 'react-redux'; -import TitleBar from './TitleBar'; -import MainSidebar2 from './mainsidebar/MainSidebar2'; -import DoctorBar from './DoctorBar'; -import PluginContainer from '../PluginContainer'; -import {ipcRenderer, remote} from 'electron'; -import { - ACTIVE_SHEET_CHANGELOG_RECENT_ONLY, - setActiveSheet, -} from '../reducers/application'; -import {Logger} from '../fb-interfaces/Logger'; -import {State as Store} from '../reducers/index'; -import {StaticView} from '../reducers/connections'; -import StatusBar from './StatusBar'; -import {hasNewChangesToShow} from './ChangelogSheet'; -import QPL, {QuickLogActionType, FLIPPER_QPL_EVENTS} from '../fb-stubs/QPL'; -import {SheetRenderer} from './SheetRenderer'; - -const version = remote.app.getVersion(); - -type OwnProps = { - logger: Logger; -}; - -type StateFromProps = { - leftSidebarVisible: boolean; - staticView: StaticView; -}; - -type DispatchProps = { - setActiveSheet: typeof setActiveSheet; -}; - -/** - * This wrapper is only needed for hacky plugins that place contents out of - * contents, like hermes debugger - */ -const PluginContent = styled(FlexRow)({ - width: '100%', - height: '100%', - position: 'relative', -}); -PluginContent.displayName = 'App:PluginContent'; -type Props = StateFromProps & OwnProps & DispatchProps; - -export function registerStartupTime(logger: Logger) { - // track time since launch - const [s, ns] = process.hrtime(); - const launchEndTime = s * 1e3 + ns / 1e6; - ipcRenderer.on('getLaunchTime', (_: any, launchStartTime: number) => { - logger.track('performance', 'launchTime', launchEndTime - launchStartTime); - - QPL.markerStart(FLIPPER_QPL_EVENTS.STARTUP, 0, launchStartTime); - QPL.markerEnd( - FLIPPER_QPL_EVENTS.STARTUP, - QuickLogActionType.SUCCESS, - 0, - launchEndTime, - ); - }); - - ipcRenderer.send('getLaunchTime'); - ipcRenderer.send('componentDidMount'); -} - -export class LegacyApp extends React.Component { - componentDidMount() { - registerStartupTime(this.props.logger); - if (hasNewChangesToShow(window.localStorage)) { - this.props.setActiveSheet(ACTIVE_SHEET_CHANGELOG_RECENT_ONLY); - } - } - - render() { - return ( - - - <> - - - - <> - - - - - - {this.props.leftSidebarVisible && } - - {this.props.staticView != null ? ( - React.createElement(this.props.staticView, { - logger: this.props.logger, - }) - ) : ( - - )} -
-
-
- - - - - - ); - } -} - -export default connect( - ({application: {leftSidebarVisible}, connections: {staticView}}) => ({ - leftSidebarVisible, - staticView, - }), - { - setActiveSheet, - }, -)(LegacyApp); diff --git a/desktop/app/src/chrome/LocationsButton.tsx b/desktop/app/src/chrome/LocationsButton.tsx deleted file mode 100644 index 07880d53d..000000000 --- a/desktop/app/src/chrome/LocationsButton.tsx +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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} from 'react'; -import {platform} from 'os'; -import {useValue} from 'flipper-plugin'; -import {Button, styled} from '../ui'; -import {useStore} from '../utils/useStore'; -import {useMemoize} from '../utils/useMemoize'; -import {State} from '../reducers'; - -// eslint-disable-next-line flipper/no-relative-imports-across-packages -import type {NavigationPlugin} from '../../../plugins/navigation/index'; -// eslint-disable-next-line flipper/no-relative-imports-across-packages -import type {Bookmark} from '../../../plugins/navigation/types'; - -const DropdownButton = styled(Button)({ - fontSize: 11, -}); - -const shortenText = (text: string, MAX_CHARACTERS = 30): string => { - if (text.length <= MAX_CHARACTERS) { - return text; - } else { - return text.split('').slice(0, MAX_CHARACTERS).join('') + '...'; - } -}; - -const NAVIGATION_PLUGIN_ID = 'Navigation'; - -export function LocationsButton() { - const navPlugin = useStore(navPluginStateSelector); - return navPlugin ? ( - - ) : ( - (none) - ); -} - -function ActiveLocationsButton({navPlugin}: {navPlugin: NavigationPlugin}) { - const currentURI = useValue(navPlugin.currentURI); - const bookmarks = useValue(navPlugin.bookmarks); - - const keyDown = useCallback( - (e: KeyboardEvent) => { - if ( - ((platform() === 'darwin' && e.metaKey) || - (platform() !== 'darwin' && e.ctrlKey)) && - /^\d$/.test(e.key) && - bookmarks.size >= parseInt(e.key, 10) - ) { - navPlugin.navigateTo( - Array.from(bookmarks.values())[parseInt(e.key, 10) - 1].uri, - ); - } - }, - [bookmarks, navPlugin], - ); - - useEffect(() => { - document.addEventListener('keydown', keyDown); - return () => { - document.removeEventListener('keydown', keyDown); - }; - }, [keyDown]); - - const dropdown = useMemoize(computeBookmarkDropdown, [ - navPlugin, - bookmarks, - currentURI, - ]); - - return ( - - {(currentURI && shortenText(currentURI)) || '(none)'} - - ); -} - -export function navPluginStateSelector(state: State) { - const {selectedApp, clients} = state.connections; - if (!selectedApp) return undefined; - const client = clients.find((client) => client.id === selectedApp); - if (!client) return undefined; - return client.sandyPluginStates.get(NAVIGATION_PLUGIN_ID)?.instanceApi as - | undefined - | NavigationPlugin; -} - -function computeBookmarkDropdown( - navPlugin: NavigationPlugin, - bookmarks: Map, - currentURI: string, -) { - const dropdown: Electron.MenuItemConstructorOptions[] = [ - { - label: 'Bookmarks', - enabled: false, - }, - ...Array.from(bookmarks.values()).map((bookmark, i) => { - return { - click: () => { - navPlugin.navigateTo(bookmark.uri); - }, - accelerator: i < 9 ? `CmdOrCtrl+${i + 1}` : undefined, - label: shortenText( - (bookmark.commonName ? bookmark.commonName + ' - ' : '') + - bookmark.uri, - 100, - ), - }; - }), - ]; - - if (currentURI) { - dropdown.push( - {type: 'separator'}, - { - label: 'Bookmark Current Location', - click: () => { - navPlugin.addBookmark({ - uri: currentURI, - commonName: null, - }); - }, - }, - ); - } - return dropdown; -} diff --git a/desktop/app/src/chrome/MetroButton.tsx b/desktop/app/src/chrome/MetroButton.tsx index 5a9a6632e..d54f44c01 100644 --- a/desktop/app/src/chrome/MetroButton.tsx +++ b/desktop/app/src/chrome/MetroButton.tsx @@ -8,53 +8,14 @@ */ import React, {useCallback, useEffect, useState} from 'react'; -import {Button, ButtonGroup, colors} from '../ui'; import MetroDevice, {MetroReportableEvent} from '../devices/MetroDevice'; -import styled from '@emotion/styled'; import {useStore} from '../utils/useStore'; import {Button as AntButton} from 'antd'; import {MenuOutlined, ReloadOutlined} from '@ant-design/icons'; type LogEntry = {}; -export type PersistedState = { - logs: LogEntry[]; -}; - -function ProgressBar({ - progress, - width, - color, -}: { - progress: number; - width: number; - color: string; -}) { - return ( - - - - ); -} - -const ProgressBarContainer = styled.div<{width: number; color: string}>( - ({width, color}) => ({ - border: `1px solid ${color}`, - borderRadius: 4, - height: 6, - width: width, - }), -); - -const ProgressBarBar = styled.div<{progress: number; color: string}>( - ({progress, color}) => ({ - background: color, - width: `${Math.min(100, Math.round(progress * 100))}%`, - height: 4, - }), -); - -export default function MetroButton({useSandy}: {useSandy?: boolean}) { +export default function MetroButton() { const device = useStore((state) => state.connections.devices.find( (device) => device.os === 'Metro' && !device.isArchived, @@ -68,7 +29,7 @@ export default function MetroButton({useSandy}: {useSandy?: boolean}) { [device], ); const [progress, setProgress] = useState(1); - const [hasBuildError, setHasBuildError] = useState(false); + const [_hasBuildError, setHasBuildError] = useState(false); useEffect(() => { if (!device) { @@ -98,7 +59,7 @@ export default function MetroButton({useSandy}: {useSandy?: boolean}) { return null; } - return useSandy ? ( + return ( <> } @@ -118,31 +79,5 @@ export default function MetroButton({useSandy}: {useSandy?: boolean}) { }} /> - ) : ( - - -
-
- Submit -
+ + Submit + +
@@ -259,30 +260,31 @@ exports[`SettingsSheet snapshot with one plugin enabled 1`] = `
-
- Close -
+ + Close + +
-
- Submit -
+ + Submit + +
diff --git a/desktop/app/src/chrome/__tests__/__snapshots__/ShareSheetPendingDialog.node.tsx.snap b/desktop/app/src/chrome/__tests__/__snapshots__/ShareSheetPendingDialog.node.tsx.snap index 3ce16bb60..02559628a 100644 --- a/desktop/app/src/chrome/__tests__/__snapshots__/ShareSheetPendingDialog.node.tsx.snap +++ b/desktop/app/src/chrome/__tests__/__snapshots__/ShareSheetPendingDialog.node.tsx.snap @@ -29,23 +29,24 @@ exports[`ShareSheetPendingDialog is rendered with status update 1`] = `
-
- Cancel -
-
+ Cancel + + +
+ + Run In Background + +
`; @@ -79,23 +80,24 @@ exports[`ShareSheetPendingDialog is rendered without status update 1`] = `
-
- Cancel -
-
+ Cancel + + +
+ + Run In Background + +
`; diff --git a/desktop/app/src/chrome/mainsidebar/MainSidebar2.tsx b/desktop/app/src/chrome/mainsidebar/MainSidebar2.tsx deleted file mode 100644 index ed81a39ca..000000000 --- a/desktop/app/src/chrome/mainsidebar/MainSidebar2.tsx +++ /dev/null @@ -1,625 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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 BaseDevice from '../../devices/BaseDevice'; -import Client from '../../Client'; -import {UninitializedClient} from '../../UninitializedClient'; -import {sortPluginsByName} from '../../utils/pluginUtils'; -import {PluginNotification} from '../../reducers/notifications'; -import {ActiveSheet} from '../../reducers/application'; -import {State as Store} from '../../reducers'; -import { - Sidebar, - colors, - Glyph, - styled, - SmallText, - Info, - HBox, - LoadingIndicator, -} from '../../ui'; -import React, { - PureComponent, - Fragment, - memo, - useCallback, - useState, - useEffect, - useRef, -} from 'react'; -import NotificationScreen from '../NotificationScreen'; -import { - selectPlugin, - starPlugin as starPluginAction, - StaticView, - setStaticView, - getAvailableClients, - canBeDefaultDevice, -} from '../../reducers/connections'; -import {setActiveSheet} from '../../reducers/application'; -import {connect} from 'react-redux'; -import SupportRequestDetails from '../../fb-stubs/SupportRequestDetails'; -import MainSidebarUtilsSection from './MainSidebarUtilsSection'; -import { - ListItem, - PluginName, - Plugins, - CategoryName, - PluginIcon, - PluginSidebarListItem, - NoDevices, - getColorByApp, - getFavoritePlugins, - isStaticViewActive, -} from './sidebarUtils'; -import {useLocalStorage} from '../../utils/useLocalStorage'; -import {PluginDefinition, ClientPluginMap, DevicePluginMap} from '../../plugin'; -import GK from '../../fb-stubs/GK'; -import ArchivedDevice from '../../devices/ArchivedDevice'; - -type FlipperPlugins = PluginDefinition[]; -type PluginsByCategoryType = [string, FlipperPlugins][]; - -type SectionLevel = 1 | 2 | 3; - -const ShowMoreButton = styled('div')<{collapsed: boolean}>(({collapsed}) => ({ - border: `1px solid ${colors.macOSTitleBarIconBlur}`, - width: 16, - height: 16, - borderRadius: 16, - marginLeft: 'auto', - marginRight: 'auto', - lineHeight: '12px', - transform: collapsed ? 'rotate(0deg)' : 'rotate(-180deg)', - transition: `transform 0.3s ease`, -})); - -const SidebarSectionButton = styled('button')<{ - level: SectionLevel; - color: string; - collapsed: boolean; -}>(({level, color}) => ({ - fontWeight: level === 3 ? 'normal' : 'bold', - borderRadius: 0, - border: 'none', - background: level === 1 ? colors.sectionHeaderBorder : 'transparent', - textAlign: level === 3 ? 'center' : 'left', - width: '100%', - fontSize: level === 3 ? 11 : 14, - color, - padding: `${level === 3 ? 0 : 8}px 10px 8px 9px`, - textTransform: 'capitalize', - fontVariantCaps: level === 2 ? 'all-small-caps' : 'normal', -})); - -const SidebarSectionBody = styled('div')<{ - level: SectionLevel; - collapsed: boolean; -}>(({collapsed, level}) => ({ - userSelect: 'none', - flexShrink: 0, - overflow: 'hidden', - maxHeight: collapsed ? 0 : 2000, // might need increase if too many plugins... - transition: collapsed - ? 'max-height 0.3s ease-out' - : 'max-height 0.5s ease-in', - borderBottom: - level === 2 ? `1px solid ${colors.sectionHeaderBorder}` : undefined, -})); - -const SidebarSectionButtonGlyph = styled(Glyph)<{collapsed: boolean}>( - ({collapsed}) => ({ - transform: collapsed ? 'rotate(90deg)' : 'rotate(180deg)', - transition: `transform 0.3s ease`, - }), -); - -const SidebarSection: React.FC<{ - defaultCollapsed?: boolean; - title: string | React.ReactNode | ((collapsed: boolean) => React.ReactNode); - level: SectionLevel; - color?: string; - storageKey: string; -}> = ({children, title, level, color, defaultCollapsed, storageKey}) => { - const hasMounted = useRef(false); - const [collapsed, setCollapsed] = useLocalStorage( - storageKey, - !!defaultCollapsed, - ); - color = color || colors.macOSTitleBarIconActive; - - useEffect(() => { - // if default collapsed changed to false after mounting, propagate that - if (hasMounted.current && !defaultCollapsed && collapsed) { - setCollapsed(!collapsed); - } - hasMounted.current = true; - }, [defaultCollapsed]); - - return ( - <> - setCollapsed((s) => !s)} - level={level} - color={color} - collapsed={collapsed}> - - {typeof title === 'function' ? title(collapsed) : title} - {level < 3 && children && ( - - )} - - - - {children} - - - ); -}; - -type OwnProps = {}; - -type StateFromProps = { - numNotifications: number; - windowIsFocused: boolean; - devices: BaseDevice[]; - selectedDevice: BaseDevice | null | undefined; - staticView: StaticView; - selectedPlugin: string | null | undefined; - selectedApp: string | null | undefined; - userStarredPlugins: Store['connections']['userStarredPlugins']; - clients: Array; - uninitializedClients: Array<{ - client: UninitializedClient; - deviceId?: string; - errorMessage?: string; - }>; - devicePlugins: DevicePluginMap; - clientPlugins: ClientPluginMap; -}; - -type SelectPlugin = (payload: { - selectedPlugin: string | null; - selectedApp?: string | null; - deepLinkPayload: unknown; - selectedDevice: BaseDevice; -}) => void; - -type DispatchFromProps = { - selectPlugin: SelectPlugin; - setActiveSheet: (activeSheet: ActiveSheet) => void; - setStaticView: (payload: StaticView) => void; - starPlugin: typeof starPluginAction; -}; - -type Props = OwnProps & StateFromProps & DispatchFromProps; -type State = { - showWatchDebugRoot: boolean; - showAllPlugins: boolean; -}; - -class MainSidebar2 extends PureComponent { - state: State = { - showWatchDebugRoot: GK.get('watch_team_flipper_clientless_access'), - showAllPlugins: false, - }; - - render() { - const {devices} = this.props; - return ( - - - {devices.length ? ( - devices.map((device) => this.renderDevice(device)) - ) : ( - - )} - {this.renderUnitializedClients()} - - - - ); - } - - renderDevice(device: BaseDevice) { - const { - selectedPlugin, - selectPlugin, - clientPlugins, - starPlugin, - userStarredPlugins, - selectedApp, - selectedDevice, - } = this.props; - const clients = getAvailableClients(device, this.props.clients); - const devicePluginsItems = device.devicePlugins - .map((pluginName) => this.props.devicePlugins.get(pluginName)!) - .sort(sortPluginsByName) - .map((plugin) => ( - - selectPlugin({ - selectedPlugin: plugin.id, - selectedApp: null, - deepLinkPayload: null, - selectedDevice: device, - }) - } - plugin={plugin} - /> - )); - const wrapDevicePlugins = - clients.length > 0 && device.devicePlugins.length > 1 && !device.source; - - return ( - - {this.showArchivedDeviceDetails(device)} - {wrapDevicePlugins ? ( - - {devicePluginsItems} - - ) : ( -
{devicePluginsItems}
- )} - {clients.map((client) => ( - - ))} -
- ); - } - - renderUnitializedClients() { - const {uninitializedClients} = this.props; - return uninitializedClients.length > 0 ? ( - - {uninitializedClients.map((entry) => ( - - {entry.client.appName} - {entry.errorMessage ? ( - - ) : ( - - )} - - } - level={2}> - ))} - - ) : null; - } - - showArchivedDeviceDetails(device: BaseDevice) { - if (!device.isArchived || !device.source) { - return null; - } - const {staticView, setStaticView} = this.props; - const supportRequestDetailsactive = isStaticViewActive( - staticView, - SupportRequestDetails, - ); - return ( - <> - - - {device.source ? 'Imported device' : 'Archived device'} - - - {(device as ArchivedDevice).supportRequestDetails && ( - setStaticView(SupportRequestDetails)}> - - - Support Request Details - - - )} - - ); - } - - renderNotificationsEntry() { - if (GK.get('flipper_disable_notifications')) { - return null; - } - - const active = isStaticViewActive( - this.props.staticView, - NotificationScreen, - ); - return ( - this.props.setStaticView(NotificationScreen)} - style={{ - borderTop: `1px solid ${colors.blackAlpha10}`, - }}> - 0 ? 'bell' : 'bell-null'} - isActive={active} - /> - - Notifications - - - ); - } -} - -function groupPluginsByCategory( - plugins: FlipperPlugins, -): PluginsByCategoryType { - const sortedPlugins = plugins.slice().sort(sortPluginsByName); - const byCategory: {[cat: string]: FlipperPlugins} = {}; - const res: PluginsByCategoryType = []; - sortedPlugins.forEach((plugin) => { - const category = plugin.category || ''; - (byCategory[category] || (byCategory[category] = [])).push(plugin); - }); - // Sort categories - Object.keys(byCategory) - .sort() - .forEach((category) => { - res.push([category, byCategory[category]]); - }); - return res; -} - -export default connect( - ({ - application: {windowIsFocused}, - connections: { - devices, - selectedDevice, - selectedPlugin, - selectedApp, - userStarredPlugins, - clients, - uninitializedClients, - staticView, - }, - notifications: {activeNotifications, blocklistedPlugins}, - plugins: {devicePlugins, clientPlugins}, - }) => ({ - numNotifications: (() => { - const blocklist = new Set(blocklistedPlugins); - return activeNotifications.filter( - (n: PluginNotification) => !blocklist.has(n.pluginId), - ).length; - })(), - windowIsFocused, - devices, - selectedDevice, - staticView, - selectedPlugin, - selectedApp, - userStarredPlugins, - clients, - uninitializedClients, - devicePlugins, - clientPlugins, - }), - { - selectPlugin, - setStaticView, - setActiveSheet, - starPlugin: starPluginAction, - }, -)(MainSidebar2); - -const PluginList = memo(function PluginList({ - client, - device, - clientPlugins, - starPlugin, - userStarredPlugins, - selectedPlugin, - selectedApp, - selectPlugin, -}: { - client: Client; - device: BaseDevice; - clientPlugins: ClientPluginMap; - starPlugin: typeof starPluginAction; - userStarredPlugins: Store['connections']['userStarredPlugins']; - selectedPlugin?: null | string; - selectPlugin: SelectPlugin; - selectedApp?: null | string; -}) { - // client is a mutable structure, so we need the event emitter to detect the addition of plugins.... - const [, setPluginsChanged] = useState(0); - useEffect(() => { - const listener = () => setPluginsChanged((v) => v + 1); - client.on('plugins-change', listener); - return () => { - client.off('plugins-change', listener); - }; - }, [client]); - - const onFavorite = useCallback( - (plugin: PluginDefinition) => { - starPlugin({ - selectedApp: client.query.app, - plugin, - }); - }, - [client, starPlugin], - ); - - const allPlugins = Array.from(clientPlugins.values()).filter( - (p) => client.plugins.indexOf(p.id) > -1, - ); - const favoritePlugins: FlipperPlugins = getFavoritePlugins( - device, - client, - allPlugins, - userStarredPlugins[client.query.app], - true, - ); - const selectedNonFavoritePlugin = - selectedApp === client.id && - client.plugins.includes(selectedPlugin!) && - !favoritePlugins.find((plugin) => plugin.id === selectedPlugin); - const allPluginsStarred = favoritePlugins.length === allPlugins.length; - - return ( - - {favoritePlugins.length === 0 ? ( - - No plugins enabled - - ) : ( - - )} - {!allPluginsStarred && ( - 0 && !selectedNonFavoritePlugin - } - title={(collapsed) => ( - - - - )}> - - - )} - - ); -}); - -const PluginsByCategory = memo(function PluginsByCategory({ - client, - plugins, - starred, - onFavorite, - selectedPlugin, - selectedApp, - selectPlugin, - device, -}: { - client: Client; - device: BaseDevice; - plugins: FlipperPlugins; - starred: boolean; - selectedPlugin?: null | string; - selectedApp?: null | string; - onFavorite: (plugin: PluginDefinition) => void; - selectPlugin: SelectPlugin; -}) { - return ( - <> - {groupPluginsByCategory(plugins).map(([category, plugins]) => ( - - {category && ( - - {category} - - )} - {plugins.map((plugin) => ( - - selectPlugin({ - selectedPlugin: plugin.id, - selectedApp: client.id, - deepLinkPayload: null, - selectedDevice: device, - }) - } - plugin={plugin} - app={client.query.app} - onFavorite={() => onFavorite(plugin)} - starred={device.isArchived ? undefined : starred} - /> - ))} - - ))} - - ); -}); diff --git a/desktop/app/src/chrome/mainsidebar/MainSidebarUtilsSection.tsx b/desktop/app/src/chrome/mainsidebar/MainSidebarUtilsSection.tsx deleted file mode 100644 index 069425927..000000000 --- a/desktop/app/src/chrome/mainsidebar/MainSidebarUtilsSection.tsx +++ /dev/null @@ -1,200 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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 from 'react'; -import {connect} from 'react-redux'; - -import config from '../../fb-stubs/config'; -import {PluginNotification} from '../../reducers/notifications'; -import {ActiveSheet, ACTIVE_SHEET_PLUGINS} from '../../reducers/application'; -import {State as Store} from '../../reducers'; -import NotificationScreen from '../NotificationScreen'; -import {StaticView, setStaticView} from '../../reducers/connections'; -import {setActiveSheet} from '../../reducers/application'; -import UserAccount from '../UserAccount'; -import SupportRequestFormV2 from '../../fb-stubs/SupportRequestFormV2'; -import { - isStaticViewActive, - PluginIcon, - PluginName, - ListItem, -} from './sidebarUtils'; -import {Group} from '../../reducers/supportForm'; -import {getInstance} from '../../fb-stubs/Logger'; -import {ConsoleLogs, errorCounterAtom} from '../ConsoleLogs'; -import {useValue} from 'flipper-plugin'; -import {colors} from '../../ui'; -import GK from '../../fb-stubs/GK'; -import WatchTools from '../../fb-stubs/WatchTools'; - -type OwnProps = {}; - -type StateFromProps = { - staticView: StaticView; - selectedGroup: Group; -}; - -type DispatchFromProps = { - setActiveSheet: (activeSheet: ActiveSheet) => void; - setStaticView: (payload: StaticView) => void; -}; - -type Props = OwnProps & StateFromProps & DispatchFromProps; - -function MainSidebarUtilsSection({ - staticView, - selectedGroup, - setActiveSheet, - setStaticView, -}: Props) { - const showWatchDebugRoot = GK.get('watch_team_flipper_clientless_access'); - - return ( -
- {showWatchDebugRoot && - (function () { - const active = isStaticViewActive(staticView, WatchTools); - return ( - setStaticView(WatchTools)}> - - Watch - - ); - })()} - - {(function () { - const active = isStaticViewActive(staticView, SupportRequestFormV2); - return ( - { - getInstance().track('usage', 'support-form-source', { - source: 'sidebar', - group: selectedGroup.name, - }); - setStaticView(SupportRequestFormV2); - }}> - - Support Requests - - ); - })()} - setActiveSheet(ACTIVE_SHEET_PLUGINS)}> - - Manage Plugins - - - {config.showLogin && } -
- ); -} - -export default connect( - ({connections: {staticView}, supportForm: {supportFormV2}}) => ({ - staticView, - selectedGroup: supportFormV2.selectedGroup, - }), - { - setStaticView, - setActiveSheet, - }, -)(MainSidebarUtilsSection); - -type RenderNotificationsEntryProps = { - numNotifications: number; - staticView: StaticView; -}; - -type RenderNotificationsEntryDispatchFromProps = { - setStaticView: (payload: StaticView) => void; -}; - -type RenderEntryProps = RenderNotificationsEntryProps & - RenderNotificationsEntryDispatchFromProps; - -const RenderNotificationsEntry = connect< - RenderNotificationsEntryProps, - RenderNotificationsEntryDispatchFromProps, - {}, - Store ->( - ({ - connections: {staticView}, - notifications: {activeNotifications, blocklistedPlugins}, - }) => ({ - numNotifications: (() => { - const blocklist = new Set(blocklistedPlugins); - return activeNotifications.filter( - (n: PluginNotification) => !blocklist.has(n.pluginId), - ).length; - })(), - staticView, - }), - { - setStaticView, - }, -)(({staticView, setStaticView, numNotifications}: RenderEntryProps) => { - if (GK.get('flipper_disable_notifications')) { - return null; - } - - const active = isStaticViewActive(staticView, NotificationScreen); - return ( - setStaticView(NotificationScreen)}> - 0 ? 'bell' : 'bell-null'} - isActive={active} - /> - - Notifications - - - ); -}); - -function DebugLogsEntry({ - staticView, - setStaticView, -}: { - staticView: StaticView; - setStaticView: (payload: StaticView) => void; -}) { - const active = isStaticViewActive(staticView, ConsoleLogs); - const errorCount = useValue(errorCounterAtom); - return ( - setStaticView(ConsoleLogs)} active={active}> - - - Debug Logs - - - ); -} diff --git a/desktop/app/src/chrome/mainsidebar/sidebarUtils.tsx b/desktop/app/src/chrome/mainsidebar/sidebarUtils.tsx deleted file mode 100644 index a5dddf974..000000000 --- a/desktop/app/src/chrome/mainsidebar/sidebarUtils.tsx +++ /dev/null @@ -1,260 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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, {useRef, useEffect} from 'react'; -import { - FlexBox, - colors, - Text, - Glyph, - styled, - FlexColumn, - ToggleButton, - brandColors, - Spacer, - Heading, -} from '../../ui'; -import {Property} from 'csstype'; -import {getPluginTitle} from '../../utils/pluginUtils'; -import {PluginDefinition} from '../../plugin'; -import {StaticView} from '../../reducers/connections'; -import BaseDevice from '../../devices/BaseDevice'; -import Client from '../../Client'; - -export type FlipperPlugins = PluginDefinition[]; -export type PluginsByCategory = [string, FlipperPlugins][]; - -export const ListItem = styled.div<{active?: boolean; disabled?: boolean}>( - ({active, disabled}) => ({ - paddingLeft: 10, - display: 'flex', - alignItems: 'center', - marginBottom: 6, - flexShrink: 0, - backgroundColor: active ? colors.macOSTitleBarIconSelected : 'none', - color: disabled - ? 'rgba(0, 0, 0, 0.5)' - : active - ? colors.white - : colors.macOSSidebarSectionItem, - lineHeight: '25px', - padding: '0 10px', - '&[disabled]': { - color: 'rgba(0, 0, 0, 0.5)', - }, - }), -); - -export function PluginIcon({ - isActive, - backgroundColor, - name, - color, -}: { - isActive: boolean; - backgroundColor?: string; - name: string; - color: string; -}) { - return ( - - - - ); -} - -const PluginShape = styled(FlexBox)<{ - backgroundColor?: Property.BackgroundColor; -}>(({backgroundColor}) => ({ - marginRight: 8, - backgroundColor, - borderRadius: 3, - flexShrink: 0, - width: 18, - height: 18, - justifyContent: 'center', - alignItems: 'center', - top: '-1px', -})); - -export const PluginName = styled(Text)<{isActive?: boolean; count?: number}>( - (props) => ({ - cursor: 'default', - minWidth: 0, - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - overflow: 'hidden', - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - flexGrow: 1, - '::after': { - fontSize: 12, - display: props.count ? 'inline-block' : 'none', - padding: '0 8px', - lineHeight: '17px', - height: 17, - alignSelf: 'center', - content: `"${props.count}"`, - borderRadius: '999em', - color: props.isActive ? colors.macOSTitleBarIconSelected : colors.white, - backgroundColor: props.isActive - ? colors.white - : colors.macOSTitleBarIconSelected, - fontWeight: 500, - }, - }), -); - -export function isStaticViewActive( - current: StaticView, - selected: StaticView, -): boolean { - return Boolean(current && selected && current === selected); -} - -export const CategoryName = styled(PluginName)({ - color: colors.macOSSidebarSectionTitle, - textTransform: 'uppercase', - fontSize: '0.9em', -}); - -export const Plugins = styled(FlexColumn)({ - flexGrow: 1, - overflow: 'auto', -}); - -export const PluginSidebarListItem: React.FC<{ - onClick: () => void; - isActive: boolean; - plugin: PluginDefinition; - app?: string | null | undefined; - helpRef?: any; - provided?: any; - onFavorite?: () => void; - starred?: boolean; // undefined means: not starrable -}> = function (props) { - const {isActive, plugin, onFavorite, starred} = props; - const iconColor = getColorByApp(props.app); - const domRef = useRef(null); - - useEffect(() => { - const node = domRef.current; - if (isActive && node) { - const rect = node.getBoundingClientRect(); - if (rect.top < 0 || rect.bottom > document.documentElement.clientHeight) { - node.scrollIntoView(); - } - } - }, [isActive]); - - return ( - - - - {getPluginTitle(plugin)} - - {starred !== undefined && (!starred || isActive) && ( - - )} - - ); -}; - -export function getColorByApp(app?: string | null): string { - let iconColor: string | undefined = (brandColors as any)[app!]; - - if (!iconColor) { - if (!app) { - // Device plugin - iconColor = colors.macOSTitleBarIconBlur; - } else { - const pluginColors = [ - colors.seaFoam, - colors.teal, - colors.lime, - colors.lemon, - colors.orange, - colors.tomato, - colors.cherry, - colors.pink, - colors.grape, - ]; - - iconColor = pluginColors[parseInt(app, 36) % pluginColors.length]; - } - } - return iconColor; -} - -export const NoDevices = () => ( - - - - Select a device to get started - -); - -export const NoClients = () => ( - - - No clients connected - -); - -export function getFavoritePlugins( - device: BaseDevice, - client: Client, - allPlugins: FlipperPlugins, - starredPlugins: undefined | string[], - returnFavoredPlugins: boolean, // if false, unfavoried plugins are returned -): FlipperPlugins { - if (device.isArchived) { - if (!returnFavoredPlugins) { - return []; - } - // for archived plugins, all stored plugins are enabled - return allPlugins.filter( - (plugin) => client.plugins.indexOf(plugin.id) !== -1, - ); - } - if (!starredPlugins || !starredPlugins.length) { - return returnFavoredPlugins ? [] : allPlugins; - } - return allPlugins.filter((plugin) => { - const idx = starredPlugins.indexOf(plugin.id); - return idx === -1 ? !returnFavoredPlugins : returnFavoredPlugins; - }); -} diff --git a/desktop/app/src/chrome/plugin-manager/__tests__/__snapshots__/PluginInstaller.node.tsx.snap b/desktop/app/src/chrome/plugin-manager/__tests__/__snapshots__/PluginInstaller.node.tsx.snap index ac4254728..7f67264e2 100644 --- a/desktop/app/src/chrome/plugin-manager/__tests__/__snapshots__/PluginInstaller.node.tsx.snap +++ b/desktop/app/src/chrome/plugin-manager/__tests__/__snapshots__/PluginInstaller.node.tsx.snap @@ -6,7 +6,7 @@ exports[`load PluginInstaller list 1`] = ` class="css-9dawc5-View-FlexBox-FlexColumn" >
-
- Install -
+ + Install + +
-
- +
-
- Install -
+ + Install + +
-
- Install -
+ + Install + +
@@ -301,7 +308,7 @@ exports[`load PluginInstaller list with one plugin installed 1`] = ` class="css-9dawc5-View-FlexBox-FlexColumn" >
-
- Remove -
+ + Remove + +
-
- +
-
- Install -
+ + Install + +
-
- Install -
+ + Install + +
diff --git a/desktop/app/src/dispatcher/pluginDownloads.tsx b/desktop/app/src/dispatcher/pluginDownloads.tsx index ca9a623ca..d0891c771 100644 --- a/desktop/app/src/dispatcher/pluginDownloads.tsx +++ b/desktop/app/src/dispatcher/pluginDownloads.tsx @@ -14,7 +14,7 @@ import { InstalledPluginDetails, installPluginFromFile, } from 'flipper-plugin-lib'; -import {Actions, State, Store} from '../reducers/index'; +import {State, Store} from '../reducers/index'; import { PluginDownloadStatus, pluginDownloadStarted, @@ -26,17 +26,9 @@ import fs from 'fs-extra'; import path from 'path'; import tmp from 'tmp'; import {promisify} from 'util'; -import {requirePlugin} from './plugins'; -import {registerPluginUpdate, selectPlugin} from '../reducers/connections'; -import {Button} from 'antd'; -import React from 'react'; import {reportPlatformFailures, reportUsage} from '../utils/metrics'; -import {addNotification, removeNotification} from '../reducers/notifications'; -import reloadFlipper from '../utils/reloadFlipper'; import {activatePlugin, pluginInstalled} from '../reducers/pluginManager'; -import {Dispatch} from 'redux'; import {showErrorNotification} from '../utils/notifications'; -import isSandyEnabled from '../utils/isSandyEnabled'; // Adapter which forces node.js implementation for axios instead of browser implementation // used by default in Electron. Node.js implementation is better, because it @@ -144,8 +136,6 @@ async function handlePluginDownload( notifyIfFailed: startedByUser, }), ); - } else if (!isSandyEnabled()) { - notifyAboutUpdatedPluginNonSandy(installedPlugin, store.dispatch); } console.log( `Successfully downloaded and installed plugin "${title}" v${version} from "${downloadUrl}" to "${installationDir}".`, @@ -178,59 +168,3 @@ function pluginIsDisabledForAllConnectedClients( ) ); } - -function notifyAboutUpdatedPluginNonSandy( - plugin: InstalledPluginDetails, - dispatch: Dispatch, -) { - const {name, version, title, id} = plugin; - const reloadPluginAndRemoveNotification = () => { - reportUsage('plugin-auto-update:notification:reloadClicked', undefined, id); - dispatch( - registerPluginUpdate({ - plugin: requirePlugin(plugin), - enablePlugin: false, - }), - ); - dispatch( - removeNotification({ - pluginId: 'plugin-auto-update', - client: null, - notificationId: `auto-update.${name}.${version}`, - }), - ); - dispatch( - selectPlugin({ - selectedPlugin: id, - deepLinkPayload: null, - }), - ); - }; - const reloadAll = () => { - reportUsage('plugin-auto-update:notification:reloadAllClicked'); - reloadFlipper(); - }; - dispatch( - addNotification({ - pluginId: 'plugin-auto-update', - client: null, - notification: { - id: `auto-update.${name}.${version}`, - title: `${title} ${version} is ready to install`, - message: ( -
- {title} {version} has been downloaded. Reload is required to apply - the update.{' '} - - -
- ), - severity: 'warning', - timestamp: Date.now(), - category: `Plugin Auto Update`, - }, - }), - ); -} diff --git a/desktop/app/src/init.tsx b/desktop/app/src/init.tsx index 6e26c5e47..a9132fa7f 100644 --- a/desktop/app/src/init.tsx +++ b/desktop/app/src/init.tsx @@ -13,7 +13,7 @@ import ReactDOM from 'react-dom'; import ContextMenuProvider from './ui/components/ContextMenuProvider'; import GK from './fb-stubs/GK'; import {init as initLogger} from './fb-stubs/Logger'; -import App from './chrome/AppWrapper'; +import {SandyApp} from './sandy-chrome/SandyApp'; import setupPrefetcher from './fb-stubs/Prefetcher'; import {persistStore} from 'redux-persist'; import {Store} from './reducers/index'; @@ -42,7 +42,6 @@ import { _LoggerContext, } from 'flipper-plugin'; import isProduction from './utils/isProduction'; -import isSandyEnabled from './utils/isSandyEnabled'; if (process.env.NODE_ENV === 'development' && os.platform() === 'darwin') { // By default Node.JS has its internal certificate storage and doesn't use @@ -67,7 +66,7 @@ const AppFrame = ({logger}: {logger: Logger}) => ( <_NuxManagerContext.Provider value={_createNuxManager()}> - + @@ -124,18 +123,14 @@ function init() { store, {name: 'loadTheme', fireImmediately: true, throttleMs: 500}, (state) => ({ - sandy: isSandyEnabled(), dark: state.settingsState.darkMode, }), (theme) => { (document.getElementById( 'flipper-theme-import', ) as HTMLLinkElement).href = `themes/${ - theme.sandy && theme.dark ? 'dark' : 'light' + theme.dark ? 'dark' : 'light' }.css`; - document - .getElementById('root') - ?.classList.toggle('flipperlegacy_design', !theme.sandy); }, ); } diff --git a/desktop/app/src/reducers/settings.tsx b/desktop/app/src/reducers/settings.tsx index 42e511bdf..d727a339b 100644 --- a/desktop/app/src/reducers/settings.tsx +++ b/desktop/app/src/reducers/settings.tsx @@ -43,7 +43,6 @@ export type Settings = { openDevMenu: string; }; }; - disableSandy: boolean; darkMode: boolean; showWelcomeAtStartup: boolean; }; @@ -78,7 +77,6 @@ const initialState: Settings = { openDevMenu: 'Alt+Shift+D', }, }, - disableSandy: false, darkMode: false, showWelcomeAtStartup: true, }; diff --git a/desktop/app/src/sandy-chrome/LeftRail.tsx b/desktop/app/src/sandy-chrome/LeftRail.tsx index 96f7222c7..d73731df9 100644 --- a/desktop/app/src/sandy-chrome/LeftRail.tsx +++ b/desktop/app/src/sandy-chrome/LeftRail.tsx @@ -48,9 +48,8 @@ import config from '../fb-stubs/config'; import styled from '@emotion/styled'; import {showEmulatorLauncher} from './appinspect/LaunchEmulator'; import SupportRequestFormV2 from '../fb-stubs/SupportRequestFormV2'; -import {setStaticView} from '../reducers/connections'; +import {setStaticView, StaticView} from '../reducers/connections'; import {getInstance} from '../fb-stubs/Logger'; -import {isStaticViewActive} from '../chrome/mainsidebar/sidebarUtils'; import {getUser} from '../fb-stubs/user'; import {SandyRatingButton} from '../chrome/RatingButton'; import {filterNotifications} from './notification/notificationUtils'; @@ -310,7 +309,7 @@ function ShowSettingsButton() { selected={showSettings} /> {showSettings && ( - + )} ); @@ -417,7 +416,14 @@ function LoginButton() { title="Log In" onClick={() => setShowLogin(true)} /> - {showLogin && } + {showLogin && } ); } + +function isStaticViewActive( + current: StaticView, + selected: StaticView, +): boolean { + return Boolean(current && selected && current === selected); +} diff --git a/desktop/app/src/sandy-chrome/SandyApp.tsx b/desktop/app/src/sandy-chrome/SandyApp.tsx index e0e15e4be..e903bed61 100644 --- a/desktop/app/src/sandy-chrome/SandyApp.tsx +++ b/desktop/app/src/sandy-chrome/SandyApp.tsx @@ -12,11 +12,11 @@ import {TrackingScope, useLogger} from 'flipper-plugin'; import {styled} from '../ui'; import {Layout, Sidebar} from '../ui'; import {theme} from 'flipper-plugin'; +import {ipcRenderer} from 'electron'; +import {Logger} from '../fb-interfaces/Logger'; import {LeftRail} from './LeftRail'; -import {registerStartupTime} from '../chrome/LegacyApp'; import {useStore, useDispatch} from '../utils/useStore'; -import {SandyContext} from './SandyContext'; import {ConsoleLogs} from '../chrome/ConsoleLogs'; import {setStaticView} from '../reducers/connections'; import { @@ -30,10 +30,15 @@ import {ContentContainer} from './ContentContainer'; import {Notification} from './notification/Notification'; import {SheetRenderer} from '../chrome/SheetRenderer'; import {hasNewChangesToShow} from '../chrome/ChangelogSheet'; -import {SandyWelcomScreen} from './SandyWelcomeScreen'; +import {SandyWelcomeScreen} from './SandyWelcomeScreen'; import {getVersionString} from '../utils/versionString'; import config from '../fb-stubs/config'; import {WelcomeScreenStaticView} from './WelcomeScreen'; +import QPL, {QuickLogActionType, FLIPPER_QPL_EVENTS} from '../fb-stubs/QPL'; +import fbConfig from '../fb-stubs/config'; +import {isFBEmployee} from '../utils/fbEmployee'; +import {notification} from 'antd'; +import isProduction from '../utils/isProduction'; export type ToplevelNavItem = | 'appinspect' @@ -99,6 +104,27 @@ export function SandyApp() { // eslint-disable-next-line }, []); + useEffect(() => { + if (fbConfig.warnFBEmployees && isProduction()) { + isFBEmployee().then((isEmployee) => { + if (isEmployee) { + notification.warning({ + placement: 'bottomLeft', + message: 'Please use Flipper@FB', + description: ( + <> + You are using the open-source version of Flipper. Install the + internal build from Managed Software Center to get access to + more plugins. + + ), + duration: null, + }); + } + }); + } + }, []); + const leftMenuContent = !leftSidebarVisible ? null : toplevelSelection === 'appinspect' ? ( @@ -107,53 +133,51 @@ export function SandyApp() { ) : null; return ( - - - <> - - - - - - - - {leftMenuContent && ( - - {leftMenuContent} - - )} - - - - {outOfContentsContainer} - {staticView ? ( - - {staticView === WelcomeScreenStaticView ? ( - React.createElement(staticView) /* avoid shadow */ - ) : ( - - {React.createElement(staticView, { - logger: logger, - })} - - )} + + <> + + + + + + + + {leftMenuContent && ( + + {leftMenuContent} - ) : ( - )} - - - - + + + + {outOfContentsContainer} + {staticView ? ( + + {staticView === WelcomeScreenStaticView ? ( + React.createElement(staticView) /* avoid shadow */ + ) : ( + + {React.createElement(staticView, { + logger: logger, + })} + + )} + + ) : ( + + )} + + + ); } @@ -183,3 +207,23 @@ const MainContainer = styled(Layout.Container)({ background: theme.backgroundWash, padding: `${theme.space.large}px ${theme.space.large}px ${theme.space.large}px 0`, }); + +function registerStartupTime(logger: Logger) { + // track time since launch + const [s, ns] = process.hrtime(); + const launchEndTime = s * 1e3 + ns / 1e6; + ipcRenderer.on('getLaunchTime', (_: any, launchStartTime: number) => { + logger.track('performance', 'launchTime', launchEndTime - launchStartTime); + + QPL.markerStart(FLIPPER_QPL_EVENTS.STARTUP, 0, launchStartTime); + QPL.markerEnd( + FLIPPER_QPL_EVENTS.STARTUP, + QuickLogActionType.SUCCESS, + 0, + launchEndTime, + ); + }); + + ipcRenderer.send('getLaunchTime'); + ipcRenderer.send('componentDidMount'); +} diff --git a/desktop/app/src/sandy-chrome/SandyContext.tsx b/desktop/app/src/sandy-chrome/SandyContext.tsx deleted file mode 100644 index 3a263ce21..000000000 --- a/desktop/app/src/sandy-chrome/SandyContext.tsx +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its 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 {createContext, useContext} from 'react'; - -export const SandyContext = createContext(false); - -export function useIsSandy(): boolean { - return useContext(SandyContext); -} diff --git a/desktop/app/src/sandy-chrome/SandyWelcomeScreen.tsx b/desktop/app/src/sandy-chrome/SandyWelcomeScreen.tsx index bd800fad6..911e8955c 100644 --- a/desktop/app/src/sandy-chrome/SandyWelcomeScreen.tsx +++ b/desktop/app/src/sandy-chrome/SandyWelcomeScreen.tsx @@ -16,7 +16,7 @@ import {useLocalStorage} from '../utils/useLocalStorage'; const {Title, Text, Link} = Typography; -export function SandyWelcomScreen() { +export function SandyWelcomeScreen() { const [dismissed, setDismissed] = useState(false); const [showWelcomeScreen, setShowWelcomeScreen] = useLocalStorage( 'flipper-sandy-show-welcome-screen', @@ -63,10 +63,6 @@ export function SandyWelcomScreen() { to find new or improved features. - It is possible to enable the experimental dark mode, or switch back to - the old design, by using the{' '} -