Hide app selector if no devices are available

Summary:
As reported in https://fb.workplace.com/groups/748354712423318/permalink/773382183253904/, special case no devices being present and suggest to launch an emulator.

Also extends `useStore()` hook to accept no arguments, in which case it works as Redux's own `useStore` hook, except that it is strongly typed.

Reviewed By: passy

Differential Revision: D25803497

fbshipit-source-id: d448ac41e0ba7b713ee883caeb27736a2901975c
This commit is contained in:
Michel Weststrate
2021-01-06 08:23:06 -08:00
committed by Facebook GitHub Bot
parent dbf194b952
commit 30ea098a63
3 changed files with 51 additions and 13 deletions

View File

@@ -47,7 +47,6 @@ import {logout, USER_NOT_SIGNEDIN, USER_UNAUTHORIZED} from '../reducers/user';
import config from '../fb-stubs/config'; import config from '../fb-stubs/config';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import {showEmulatorLauncher} from './appinspect/LaunchEmulator'; import {showEmulatorLauncher} from './appinspect/LaunchEmulator';
import {useStore as useReduxStore} from 'react-redux';
import SupportRequestFormV2 from '../fb-stubs/SupportRequestFormV2'; import SupportRequestFormV2 from '../fb-stubs/SupportRequestFormV2';
import {setStaticView} from '../reducers/connections'; import {setStaticView} from '../reducers/connections';
import {getInstance} from '../fb-stubs/Logger'; import {getInstance} from '../fb-stubs/Logger';
@@ -252,7 +251,7 @@ function DebugLogsButton({
} }
function LaunchEmulatorButton() { function LaunchEmulatorButton() {
const store = useReduxStore(); const store = useStore();
return ( return (
<LeftRailButton <LeftRailButton

View File

@@ -8,12 +8,13 @@
*/ */
import React from 'react'; import React from 'react';
import {Button, Dropdown, Menu, Radio, Typography} from 'antd'; import {Alert, Button, Dropdown, Menu, Radio, Typography} from 'antd';
import { import {
AppleOutlined, AppleOutlined,
AndroidOutlined, AndroidOutlined,
WindowsOutlined, WindowsOutlined,
CaretDownOutlined, CaretDownOutlined,
RocketOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import {Glyph, Layout, styled} from '../../ui'; import {Glyph, Layout, styled} from '../../ui';
import {theme, useTrackedCallback} from 'flipper-plugin'; import {theme, useTrackedCallback} from 'flipper-plugin';
@@ -30,8 +31,9 @@ import {getColorByApp} from '../../chrome/mainsidebar/sidebarUtils';
import Client from '../../Client'; import Client from '../../Client';
import {State} from '../../reducers'; import {State} from '../../reducers';
import {brandIcons} from '../../ui/components/colors'; import {brandIcons} from '../../ui/components/colors';
import {showEmulatorLauncher} from './LaunchEmulator';
const {Text} = Typography; const {Text, Link, Title} = Typography;
function getOsIcon(os?: OS) { function getOsIcon(os?: OS) {
switch (os) { switch (os) {
@@ -86,7 +88,7 @@ export function AppSelector() {
); );
const client = clients.find((client) => client.id === selectedApp); const client = clients.find((client) => client.id === selectedApp);
return ( return entries.length ? (
<Radio.Group <Radio.Group
value={selectedApp} value={selectedApp}
size="small" size="small"
@@ -96,9 +98,7 @@ export function AppSelector() {
}}> }}>
<Dropdown <Dropdown
overlay={ overlay={
<Menu selectedKeys={selectedApp ? [selectedApp] : []}> <Menu selectedKeys={selectedApp ? [selectedApp] : []}>{entries}</Menu>
{entries.flat()}
</Menu>
}> }>
<AppInspectButton title="Select the device / app to inspect"> <AppInspectButton title="Select the device / app to inspect">
<Layout.Horizontal gap center> <Layout.Horizontal gap center>
@@ -114,6 +114,8 @@ export function AppSelector() {
</AppInspectButton> </AppInspectButton>
</Dropdown> </Dropdown>
</Radio.Group> </Radio.Group>
) : (
<NoDevices />
); );
} }
@@ -216,5 +218,36 @@ function computeEntries(
)), )),
]); ]);
} }
return entries; return entries.flat();
}
function NoDevices() {
const store = useStore();
const onLaunchEmulator = useTrackedCallback(
'select-emulator',
() => {
showEmulatorLauncher(store);
},
[],
);
return (
<Alert
type="info"
message={
<>
<Title level={4}>No devices found</Title>
<Text>
Start a fresh emulator <RocketOutlined onClick={onLaunchEmulator} />{' '}
or check the{' '}
<Link href="https://fbflipper.com/docs/troubleshooting">
troubleshooting guide
</Link>
.
</Text>
</>
}
/>
);
} }

View File

@@ -8,12 +8,13 @@
*/ */
import { import {
useStore as useReduxStore,
useSelector, useSelector,
shallowEqual, shallowEqual,
useDispatch as useDispatchBase, useDispatch as useDispatchBase,
} from 'react-redux'; } from 'react-redux';
import {Dispatch as ReduxDispatch} from 'redux'; import {Dispatch as ReduxDispatch} from 'redux';
import {State, Actions} from '../reducers/index'; import {State, Actions, Store} from '../reducers/index';
/** /**
* Strongly typed wrapper or Redux's useSelector. * Strongly typed wrapper or Redux's useSelector.
@@ -22,9 +23,14 @@ import {State, Actions} from '../reducers/index';
*/ */
export function useStore<Selected>( export function useStore<Selected>(
selector: (state: State) => Selected, selector: (state: State) => Selected,
equalityFn: (left: Selected, right: Selected) => boolean = shallowEqual, equalityFn?: (left: Selected, right: Selected) => boolean,
): Selected { ): Selected;
return useSelector(selector, equalityFn); export function useStore(): Store;
export function useStore(selector?: any, equalityFn?: any) {
// eslint-disable-next-line react-hooks/rules-of-hooks
if (arguments.length === 0) return useReduxStore();
// eslint-disable-next-line react-hooks/rules-of-hooks
return useSelector(selector, equalityFn ?? shallowEqual);
} }
export type Dispatch = ReduxDispatch<Actions>; export type Dispatch = ReduxDispatch<Actions>;