Hide "no application selected" warning when there is no device

Summary:
In the left App Inspect sidebar on Flipper there were two warning messages
- "no devices found"
- "no application selected"

As there can be no application selected, while no device is active, this change removes the "no application selected" warning. Resulting in only showing one warning at a time.

As I am not familiar with all the states, nor what kind of devices or applications can exist in detail, I have moved up the logic to show the "no device found" message. Then reused it for the "no application selected" visibility.

In case the upper device list can show "connecting" without an actual device in the list, the "no application selected" message will be shown.

Reviewed By: passy

Differential Revision: D32139077

fbshipit-source-id: 06456e81fbe1f86d65f9aba598ee84ed8cd6b043
This commit is contained in:
Gerard Delmas
2021-11-04 05:12:35 -07:00
committed by Facebook GitHub Bot
parent 3ff955c42b
commit 6bcb5a435b
3 changed files with 59 additions and 48 deletions

View File

@@ -26,6 +26,7 @@ import {
getActiveDevice, getActiveDevice,
getMetroDevice, getMetroDevice,
} from '../../selectors/connections'; } from '../../selectors/connections';
import * as connections from '../../selectors/connections';
const {Text} = Typography; const {Text} = Typography;
@@ -46,6 +47,7 @@ export function AppInspect() {
const activeDevice = useSelector(getActiveDevice); const activeDevice = useSelector(getActiveDevice);
const isDeviceConnected = useValue(activeDevice?.connected, false); const isDeviceConnected = useValue(activeDevice?.connected, false);
const isAppConnected = useValue(client?.connected, false); const isAppConnected = useValue(client?.connected, false);
const hasSelectableDevices = useSelector(connections.hasSelectableDevices);
return ( return (
<LeftSidebar> <LeftSidebar>
@@ -61,6 +63,7 @@ export function AppInspect() {
activeDevice, activeDevice,
client, client,
isAppConnected, isAppConnected,
hasSelectableDevices,
)} )}
{isDeviceConnected && isAppConnected && <BookmarkSection />} {isDeviceConnected && isAppConnected && <BookmarkSection />}
{isDeviceConnected && activeDevice && ( {isDeviceConnected && activeDevice && (
@@ -96,6 +99,7 @@ function renderStatusMessage(
activeDevice: BaseDevice | null, activeDevice: BaseDevice | null,
client: Client | null, client: Client | null,
isAppConnected: boolean, isAppConnected: boolean,
hasSelectableDevices: boolean,
): React.ReactNode { ): React.ReactNode {
if (!activeDevice) { if (!activeDevice) {
return; return;
@@ -142,7 +146,7 @@ function renderStatusMessage(
</Text> </Text>
</Layout.Horizontal> </Layout.Horizontal>
) )
) : ( ) : hasSelectableDevices ? (
<Layout.Horizontal gap center> <Layout.Horizontal gap center>
<ExclamationCircleOutlined style={{color: theme.warningColor}} /> <ExclamationCircleOutlined style={{color: theme.warningColor}} />
<Text <Text
@@ -155,5 +159,5 @@ function renderStatusMessage(
No application selected No application selected
</Text> </Text>
</Layout.Horizontal> </Layout.Horizontal>
); ) : null /* no selectable devices */;
} }

View File

@@ -18,10 +18,9 @@ import {
} from '@ant-design/icons'; } from '@ant-design/icons';
import {Glyph, Layout, styled} from '../../ui'; import {Glyph, Layout, styled} from '../../ui';
import {DeviceOS, theme, useTrackedCallback, useValue} from 'flipper-plugin'; import {DeviceOS, theme, useTrackedCallback, useValue} from 'flipper-plugin';
import {batch} from 'react-redux'; import {batch, useSelector} from 'react-redux';
import {useDispatch, useStore} from '../../utils/useStore'; import {useDispatch, useStore} from '../../utils/useStore';
import { import {
canBeDefaultDevice,
getClientsByDevice, getClientsByDevice,
selectClient, selectClient,
selectDevice, selectDevice,
@@ -32,6 +31,7 @@ import {State} from '../../reducers';
import {brandColors, brandIcons, colors} from '../../ui/components/colors'; import {brandColors, brandIcons, colors} from '../../ui/components/colors';
import {TroubleshootingGuide} from './fb-stubs/TroubleshootingGuide'; import {TroubleshootingGuide} from './fb-stubs/TroubleshootingGuide';
import GK from '../../fb-stubs/GK'; import GK from '../../fb-stubs/GK';
import {getSelectableDevices} from '../../selectors/connections';
const {Text} = Typography; const {Text} = Typography;
@@ -50,13 +50,9 @@ function getOsIcon(os?: DeviceOS) {
export function AppSelector() { export function AppSelector() {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { const selectableDevices = useSelector(getSelectableDevices);
devices, const {selectedDevice, clients, uninitializedClients, selectedAppId} =
selectedDevice, useStore((state) => state.connections);
clients,
uninitializedClients,
selectedAppId,
} = useStore((state) => state.connections);
useValue(selectedDevice?.connected, false); // subscribe to future archived state changes useValue(selectedDevice?.connected, false); // subscribe to future archived state changes
const onSelectDevice = useTrackedCallback( const onSelectDevice = useTrackedCallback(
@@ -79,7 +75,7 @@ export function AppSelector() {
); );
const entries = computeEntries( const entries = computeEntries(
devices, selectableDevices,
clients, clients,
uninitializedClients, uninitializedClients,
onSelectDevice, onSelectDevice,
@@ -193,21 +189,13 @@ const AppIconContainer = styled.div({
}); });
function computeEntries( function computeEntries(
devices: BaseDevice[], selectableDevices: BaseDevice[],
clients: Map<string, Client>, clients: Map<string, Client>,
uninitializedClients: State['connections']['uninitializedClients'], uninitializedClients: State['connections']['uninitializedClients'],
onSelectDevice: (device: BaseDevice) => void, onSelectDevice: (device: BaseDevice) => void,
onSelectApp: (device: BaseDevice, client: Client) => void, onSelectApp: (device: BaseDevice, client: Client) => void,
) { ) {
const entries = devices const entries = selectableDevices.map((device) => {
.filter(
(device) =>
// hide non default devices, unless they have a connected client or plugins
canBeDefaultDevice(device) ||
device.hasDevicePlugins ||
getClientsByDevice(device, clients).length > 0,
)
.map((device) => {
const deviceEntry = ( const deviceEntry = (
<Menu.Item <Menu.Item
icon={getOsIcon(device.os)} icon={getOsIcon(device.os)}
@@ -219,8 +207,7 @@ function computeEntries(
<DeviceTitle device={device} /> <DeviceTitle device={device} />
</Menu.Item> </Menu.Item>
); );
const clientEntries = getClientsByDevice(device, clients).map( const clientEntries = getClientsByDevice(device, clients).map((client) => (
(client) => (
<Menu.Item <Menu.Item
key={client.id} key={client.id}
onClick={() => { onClick={() => {
@@ -230,8 +217,7 @@ function computeEntries(
<ClientTitle client={client} /> <ClientTitle client={client} />
</Radio> </Radio>
</Menu.Item> </Menu.Item>
), ));
);
return [deviceEntry, ...clientEntries]; return [deviceEntry, ...clientEntries];
}); });
if (uninitializedClients.length) { if (uninitializedClients.length) {

View File

@@ -14,10 +14,12 @@ import {
computeActivePluginList, computeActivePluginList,
} from '../utils/pluginUtils'; } from '../utils/pluginUtils';
import createSelector from './createSelector'; import createSelector from './createSelector';
import {canBeDefaultDevice, getClientsByDevice} from '../reducers/connections';
const getSelectedPluginId = (state: State) => state.connections.selectedPlugin; const getSelectedPluginId = (state: State) => state.connections.selectedPlugin;
const getSelectedDevice = (state: State) => state.connections.selectedDevice; const getSelectedDevice = (state: State) => state.connections.selectedDevice;
const getDevices = (state: State) => state.connections.devices; const getDevices = (state: State) => state.connections.devices;
const getClients = (state: State) => state.connections.clients;
const getPluginDownloads = (state: State) => state.pluginDownloads; const getPluginDownloads = (state: State) => state.pluginDownloads;
// N.B. no useSelector, It can't memoise on maps :-/ // N.B. no useSelector, It can't memoise on maps :-/
@@ -31,6 +33,25 @@ export const getMetroDevice = createSelector(getDevices, (devices) => {
); );
}); });
export const getSelectableDevices = createSelector(
getDevices,
getClients,
(devices, clients) => {
return devices.filter(
(device) =>
// hide non default devices, unless they have a connected client or plugins
canBeDefaultDevice(device) ||
device.hasDevicePlugins ||
getClientsByDevice(device, clients).length > 0,
);
},
);
export const hasSelectableDevices = createSelector(
getSelectableDevices,
(selectableDevices) => selectableDevices.length > 0,
);
export const getActiveDevice = createSelector( export const getActiveDevice = createSelector(
getSelectedDevice, getSelectedDevice,
getActiveClient, getActiveClient,