Files
flipper/src/chrome/MainSidebar.tsx
Pritesh Nandgaonkar e5ddb5d709 Show support form in all the cases
Summary: Earlier we used to show the support form only when selected device was android. But this won't work because, I am planning to add a deeplink support for the form, so that the user can directly open flipper from workplace. Because of this we will have to show the litho support form always. In case  when the selected device is not android, we will show an error screen with possible solutions to open the litho support form.

Reviewed By: passy

Differential Revision: D17787686

fbshipit-source-id: 13e15041238d8746f947563bb3c2ea3cec205a5e
2019-10-08 12:01:09 -07:00

473 lines
14 KiB
TypeScript

/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
import config from '../fb-stubs/config';
import BaseDevice from '../devices/BaseDevice';
import Client from '../Client';
import {UninitializedClient} from '../UninitializedClient';
import {FlipperBasePlugin} from '../plugin';
import {PluginNotification} from '../reducers/notifications';
import {ActiveSheet, ACTIVE_SHEET_PLUGINS} from '../reducers/application';
import {State as Store} from '../reducers';
import {
Sidebar,
FlexBox,
colors,
brandColors,
Text,
Glyph,
styled,
FlexColumn,
GK,
FlipperPlugin,
FlipperDevicePlugin,
LoadingIndicator,
} from 'flipper';
import React, {Component, PureComponent} from 'react';
import NotificationsHub from '../NotificationsHub';
import {
selectPlugin,
showMoreOrLessPlugins,
StaticView,
setStaticView,
} from '../reducers/connections';
import {setActiveSheet} from '../reducers/application';
import UserAccount from './UserAccount';
import {connect} from 'react-redux';
import {BackgroundColorProperty} from 'csstype';
import {
MAX_MINIMUM_PLUGINS,
SHOW_REMAINING_PLUGIN_IF_LESS_THAN,
} from '../Client';
import {StyledOtherComponent} from 'create-emotion-styled';
import SupportRequestForm from '../fb-stubs/SupportRequestForm';
const ListItem = styled('div')(({active}: {active?: boolean}) => ({
paddingLeft: 10,
display: 'flex',
alignItems: 'center',
marginBottom: 2,
flexShrink: 0,
backgroundColor: active ? colors.macOSTitleBarIconSelected : 'none',
color: active ? colors.white : colors.macOSSidebarSectionItem,
lineHeight: '25px',
padding: '0 10px',
'&[disabled]': {
color: 'rgba(0, 0, 0, 0.5)',
},
}));
const SidebarHeader = styled(FlexBox)({
display: 'block',
alignItems: 'center',
padding: 3,
color: colors.macOSSidebarSectionTitle,
fontSize: 11,
fontWeight: 500,
marginLeft: 7,
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
flexShrink: 0,
});
const PluginShape = styled(FlexBox)(
({backgroundColor}: {backgroundColor?: BackgroundColorProperty}) => ({
marginRight: 5,
backgroundColor,
borderRadius: 3,
flexShrink: 0,
width: 18,
height: 18,
justifyContent: 'center',
alignItems: 'center',
}),
);
const PluginName = styled(Text)(
(props: {isActive?: boolean; count?: number}) => ({
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,
},
}),
);
const Plugins = styled(FlexColumn)({
flexGrow: 1,
overflow: 'auto',
});
const PluginDebugger = styled(FlexBox)({
color: colors.blackAlpha50,
alignItems: 'center',
padding: 10,
flexShrink: 0,
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
});
const PluginShowMoreOrLess = styled(ListItem)({
color: colors.blue,
fontSize: 10,
lineHeight: '10px',
paddingBottom: 5,
});
function PluginIcon({
isActive,
backgroundColor,
name,
color,
}: {
isActive: boolean;
backgroundColor?: string;
name: string;
color: string;
}) {
return (
<PluginShape backgroundColor={backgroundColor}>
<Glyph size={12} name={name} color={isActive ? colors.white : color} />
</PluginShape>
);
}
class PluginSidebarListItem extends Component<{
onClick: () => void;
isActive: boolean;
plugin: typeof FlipperBasePlugin;
app?: string | null | undefined;
}> {
render() {
const {isActive, plugin} = this.props;
const app = this.props.app || 'Facebook';
let iconColor: string | undefined = (brandColors as any)[app];
if (!iconColor) {
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 (
<ListItem active={isActive} onClick={this.props.onClick}>
<PluginIcon
isActive={isActive}
name={plugin.icon || 'apps'}
backgroundColor={iconColor}
color={colors.white}
/>
<PluginName>{plugin.title || plugin.id}</PluginName>
</ListItem>
);
}
}
const Spinner = centerInSidebar(LoadingIndicator);
const ErrorIndicator = centerInSidebar(Glyph);
function centerInSidebar(
component: StyledOtherComponent<any, {}, any> | React.ComponentType<any>,
) {
return styled(component)({
marginTop: '10px',
marginBottom: '10px',
marginLeft: 'auto',
marginRight: 'auto',
});
}
type OwnProps = {};
type StateFromProps = {
numNotifications: number;
windowIsFocused: boolean;
selectedDevice: BaseDevice | null | undefined;
staticView: StaticView;
selectedPlugin: string | null | undefined;
selectedApp: string | null | undefined;
clients: Array<Client>;
uninitializedClients: Array<{
client: UninitializedClient;
deviceId?: string;
errorMessage?: string;
}>;
devicePlugins: Map<string, typeof FlipperDevicePlugin>;
clientPlugins: Map<string, typeof FlipperPlugin>;
};
type DispatchFromProps = {
selectPlugin: (payload: {
selectedPlugin: string | null;
selectedApp: string | null;
deepLinkPayload: string | null;
}) => void;
setActiveSheet: (activeSheet: ActiveSheet) => void;
setStaticView: (payload: StaticView) => void;
showMoreOrLessPlugins: (payload: string) => void;
};
type Props = OwnProps & StateFromProps & DispatchFromProps;
class MainSidebar extends PureComponent<Props> {
render() {
const {
selectedDevice,
selectedPlugin,
staticView,
selectedApp,
selectPlugin,
setStaticView,
windowIsFocused,
numNotifications,
} = this.props;
let {clients, uninitializedClients} = this.props;
clients = clients
.filter(
(client: Client) =>
selectedDevice && selectedDevice.supportsOS(client.query.os),
)
.sort((a, b) => (a.query.app || '').localeCompare(b.query.app));
const byPluginNameOrId = (
a: typeof FlipperBasePlugin,
b: typeof FlipperBasePlugin,
) => ((a.title || a.id) > (b.title || b.id) ? 1 : -1);
return (
<Sidebar
position="left"
width={200}
backgroundColor={
process.platform === 'darwin' && windowIsFocused ? 'transparent' : ''
}>
<Plugins>
{!GK.get('flipper_disable_notifications') && (
<ListItem
active={selectedPlugin === 'notifications'}
onClick={() =>
selectPlugin({
selectedPlugin: 'notifications',
selectedApp: null,
deepLinkPayload: null,
})
}>
<PluginIcon
color={colors.light50}
name={
numNotifications > 0
? NotificationsHub.icon || 'bell'
: 'bell-null'
}
isActive={selectedPlugin === NotificationsHub.id}
/>
<PluginName
count={numNotifications}
isActive={selectedPlugin === NotificationsHub.id}>
{NotificationsHub.title}
</PluginName>
</ListItem>
)}
{GK.get('flipper_support_requests') && (
<ListItem
active={staticView != null && staticView === SupportRequestForm}
onClick={() => setStaticView(SupportRequestForm)}>
<PluginIcon
color={colors.light50}
name={'app-dailies'}
isActive={
staticView != null && staticView === SupportRequestForm
}
/>
<PluginName
isActive={
staticView != null && staticView === SupportRequestForm
}>
Litho Support Request
</PluginName>
</ListItem>
)}
{selectedDevice && (
<SidebarHeader>{selectedDevice.title}</SidebarHeader>
)}
{selectedDevice &&
Array.from(this.props.devicePlugins.values())
.filter(plugin => plugin.supportsDevice(selectedDevice))
.sort(byPluginNameOrId)
.map((plugin: typeof FlipperDevicePlugin) => (
<PluginSidebarListItem
key={plugin.id}
isActive={plugin.id === selectedPlugin}
onClick={() =>
selectPlugin({
selectedPlugin: plugin.id,
selectedApp: null,
deepLinkPayload: null,
})
}
plugin={plugin}
/>
))}
{clients
.filter(
(client: Client) =>
(selectedDevice &&
client.query.device_id === selectedDevice.serial) ||
// Old android sdk versions don't know their device_id
// Display their plugins under all selected devices until they die out
client.query.device_id === 'unknown',
)
.map((client: Client) => {
const plugins = Array.from(
this.props.clientPlugins.values(),
).filter(
(p: typeof FlipperPlugin) => client.plugins.indexOf(p.id) > -1,
);
const minShowPluginsCount =
plugins.length <
MAX_MINIMUM_PLUGINS + SHOW_REMAINING_PLUGIN_IF_LESS_THAN
? plugins.length
: MAX_MINIMUM_PLUGINS;
return (
<React.Fragment key={client.id}>
<SidebarHeader>{client.query.app}</SidebarHeader>
{plugins
.sort((a: typeof FlipperPlugin, b: typeof FlipperPlugin) =>
client.byClientLRU(plugins.length, a, b),
)
.slice(
0,
client.showAllPlugins
? client.plugins.length
: minShowPluginsCount,
)
.map((plugin: typeof FlipperPlugin) => (
<PluginSidebarListItem
key={plugin.id}
isActive={
plugin.id === selectedPlugin &&
selectedApp === client.id
}
onClick={() =>
selectPlugin({
selectedPlugin: plugin.id,
selectedApp: client.id,
deepLinkPayload: null,
})
}
plugin={plugin}
app={client.query.app}
/>
))}
{plugins.length > minShowPluginsCount && (
<PluginShowMoreOrLess
onClick={() =>
this.props.showMoreOrLessPlugins(client.id)
}>
{client.showAllPlugins ? 'Show less' : 'Show more'}
</PluginShowMoreOrLess>
)}
</React.Fragment>
);
})}
{uninitializedClients.map(entry => (
<React.Fragment key={JSON.stringify(entry.client)}>
<SidebarHeader>{entry.client.appName}</SidebarHeader>
{entry.errorMessage ? (
<ErrorIndicator name={'mobile-cross'} size={16} />
) : (
<Spinner size={16} />
)}
</React.Fragment>
))}
</Plugins>
<PluginDebugger
onClick={() => this.props.setActiveSheet(ACTIVE_SHEET_PLUGINS)}>
<Glyph
name="question-circle"
size={16}
variant="outline"
color={colors.blackAlpha30}
/>
&nbsp;Manage Plugins...
</PluginDebugger>
{config.showLogin && <UserAccount />}
</Sidebar>
);
}
}
export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
({
application: {windowIsFocused},
connections: {
selectedDevice,
selectedPlugin,
selectedApp,
clients,
uninitializedClients,
staticView,
},
notifications: {activeNotifications, blacklistedPlugins},
plugins: {devicePlugins, clientPlugins},
}) => ({
numNotifications: (() => {
const blacklist = new Set(blacklistedPlugins);
return activeNotifications.filter(
(n: PluginNotification) => !blacklist.has(n.pluginId),
).length;
})(),
windowIsFocused,
selectedDevice,
staticView,
selectedPlugin,
selectedApp,
clients,
uninitializedClients,
devicePlugins,
clientPlugins,
}),
{
selectPlugin,
setStaticView,
setActiveSheet,
showMoreOrLessPlugins,
},
)(MainSidebar);