Make getFlipperLib generally available, and use it to decouple opening links from Electron

Summary:
This stack reduces our direct dependency on Electron, for example by exposing our own API to open links.

Also exposing `getFlipperLib` as API from `flipper-plugin`, so that these utility methods are available outside plugin contexts as well.

Reviewed By: timur-valiev

Differential Revision: D29661689

fbshipit-source-id: 0c0523326eeb0d9d8fbe3e03c4609327bb53596b
This commit is contained in:
Michel Weststrate
2021-07-15 01:51:58 -07:00
committed by Facebook GitHub Bot
parent 2b236c6114
commit 5dbd3bd414
20 changed files with 60 additions and 54 deletions

View File

@@ -26,11 +26,7 @@ import {processMessagesLater} from './utils/messageQueue';
import {emitBytesReceived} from './dispatcher/tracking';
import {debounce} from 'lodash';
import {batch} from 'react-redux';
import {
createState,
_SandyPluginInstance,
_getFlipperLibImplementation,
} from 'flipper-plugin';
import {createState, _SandyPluginInstance, getFlipperLib} from 'flipper-plugin';
import {freeze} from 'immer';
import GK from './fb-stubs/GK';
import {message} from 'antd';
@@ -224,7 +220,7 @@ export default class Client extends EventEmitter {
this.sandyPluginStates.set(
plugin.id,
new _SandyPluginInstance(
_getFlipperLibImplementation(),
getFlipperLib(),
plugin,
this,
getPluginKey(this.id, {serial: this.query.device_id}, plugin.id),
@@ -260,7 +256,7 @@ export default class Client extends EventEmitter {
this.sandyPluginStates.set(
plugin.id,
new _SandyPluginInstance(
_getFlipperLibImplementation(),
getFlipperLib(),
plugin,
this,
getPluginKey(this.id, {serial: this.query.device_id}, plugin.id),

View File

@@ -29,6 +29,7 @@ import {
NormalizedMenuEntry,
_buildInMenuEntries,
_wrapInteractionHandler,
getFlipperLib,
} from 'flipper-plugin';
import {StyleGuide} from './sandy-chrome/StyleGuide';
import {showEmulatorLauncher} from './sandy-chrome/appinspect/LaunchEmulator';
@@ -373,19 +374,21 @@ function getTemplate(
{
label: 'Getting started',
click: function () {
shell.openExternal('https://fbflipper.com/docs/getting-started/index');
getFlipperLib().openLink(
'https://fbflipper.com/docs/getting-started/index',
);
},
},
{
label: 'Create plugins',
click: function () {
shell.openExternal('https://fbflipper.com/docs/tutorial/intro');
getFlipperLib().openLink('https://fbflipper.com/docs/tutorial/intro');
},
},
{
label: 'Report problems',
click: function () {
shell.openExternal(constants.FEEDBACK_GROUP_LINK);
getFlipperLib().openLink(constants.FEEDBACK_GROUP_LINK);
},
},
{

View File

@@ -24,7 +24,6 @@ import {
import {PluginDefinition, DevicePluginMap, ClientPluginMap} from './plugin';
import {connect} from 'react-redux';
import React, {Component, Fragment} from 'react';
import {clipboard} from 'electron';
import {
PluginNotification,
updatePluginBlocklist,
@@ -35,6 +34,7 @@ import {State as StoreState} from './reducers/index';
import textContent from './utils/textContent';
import createPaste from './fb-stubs/createPaste';
import {getPluginTitle} from './utils/pluginUtils';
import {getFlipperLib} from 'flipper-plugin';
type OwnProps = {
onClear: () => void;
@@ -459,7 +459,7 @@ class NotificationItem extends Component<
createPaste(this.getContent());
};
copy = () => clipboard.writeText(this.getContent());
copy = () => getFlipperLib().writeTextToClipboard(this.getContent());
getContent = (): string =>
[

View File

@@ -37,7 +37,7 @@ import runHealthchecks, {
HealthcheckSettings,
HealthcheckEventsHandler,
} from '../utils/runHealthchecks';
import {shell} from 'electron';
import {getFlipperLib} from 'flipper-plugin';
import {reportUsage} from '../utils/metrics';
type StateFromProps = {
@@ -295,7 +295,7 @@ class DoctorSheet extends Component<Props, State> {
}
openHelpUrl(helpUrl?: string): void {
helpUrl && shell.openExternal(helpUrl);
helpUrl && getFlipperLib().openLink(helpUrl);
}
async runHealthchecks(): Promise<void> {

View File

@@ -27,7 +27,6 @@ import {
EXPORT_FLIPPER_TRACE_EVENT,
displayFetchMetadataErrors,
} from '../utils/exportData';
import {clipboard} from 'electron';
import ShareSheetErrorList from './ShareSheetErrorList';
import {reportPlatformFailures} from '../utils/metrics';
import CancellableExportStatus from './CancellableExportStatus';
@@ -36,6 +35,7 @@ import ShareSheetPendingDialog from './ShareSheetPendingDialog';
import {getInstance as getLogger} from '../fb-stubs/Logger';
import {resetSupportFormV2State} from '../reducers/supportForm';
import {MiddlewareAPI} from '../reducers/index';
import {getFlipperLib} from 'flipper-plugin';
export const SHARE_FLIPPER_TRACE_EVENT = 'share-flipper-link';
@@ -148,7 +148,7 @@ export default class ShareSheetExportUrl extends Component<Props, State> {
if (flipperUrl) {
this.store.dispatch(setExportURL(flipperUrl));
if (this.state.runInBackground) {
clipboard.writeText(String(flipperUrl));
getFlipperLib().writeTextToClipboard(String(flipperUrl));
new Notification('Shareable Flipper Export created', {
body: 'URL copied to clipboard',
requireInteraction: true,

View File

@@ -16,7 +16,7 @@ import {
DeviceLogListener,
Idler,
createState,
_getFlipperLibImplementation,
getFlipperLib,
} from 'flipper-plugin';
import {PluginDefinition, DevicePluginMap} from '../plugin';
import {DeviceSpec, OS as PluginOS, PluginDetails} from 'flipper-plugin-lib';
@@ -245,7 +245,7 @@ export default class BaseDevice {
this.sandyPluginStates.set(
plugin.id,
new _SandyDevicePluginInstance(
_getFlipperLibImplementation(),
getFlipperLib(),
plugin,
this,
// break circular dep, one of those days again...

View File

@@ -40,13 +40,13 @@ import {
_LoggerContext,
Layout,
theme,
getFlipperLib,
} from 'flipper-plugin';
import isProduction from './utils/isProduction';
import {Button, Input, Result, Typography} from 'antd';
import constants from './fb-stubs/constants';
import styled from '@emotion/styled';
import {CopyOutlined} from '@ant-design/icons';
import {clipboard} from 'electron/common';
import {getVersionString} from './utils/versionString';
import {PersistGate} from 'redux-persist/integration/react';
import {ipcRenderer} from 'electron';
@@ -107,7 +107,7 @@ class AppFrame extends React.Component<
key="copy_error"
icon={<CopyOutlined />}
onClick={() => {
clipboard.writeText(this.getError());
getFlipperLib().writeTextToClipboard(this.getError());
}}>
Copy error
</Button>,

View File

@@ -23,7 +23,7 @@ const {Text, Title} = Typography;
import constants from '../fb-stubs/constants';
import isProduction from '../utils/isProduction';
import {getAppVersion} from '../utils/info';
import {shell} from 'electron';
import {getFlipperLib} from 'flipper-plugin';
const RowContainer = styled(FlexRow)({
alignItems: 'flex-start',
@@ -89,7 +89,7 @@ function WelcomeFooter({
);
}
const openExternal = (url: string) => () => shell && shell.openExternal(url);
const openExternal = (url: string) => () => getFlipperLib().openLink(url);
export default function WelcomeScreen({
visible,

View File

@@ -8,14 +8,14 @@
*/
import React, {useState, useCallback, useMemo} from 'react';
import electron, {MenuItemConstructorOptions} from 'electron';
import {MenuItemConstructorOptions} from 'electron';
import styled from '@emotion/styled';
import {Button as AntdButton, Dropdown, Menu} from 'antd';
import Glyph, {IconSize} from './Glyph';
import type {ButtonProps} from 'antd/lib/button';
import {DownOutlined, CheckOutlined} from '@ant-design/icons';
import {theme} from 'flipper-plugin';
import {theme, getFlipperLib} from 'flipper-plugin';
type ButtonType = 'primary' | 'success' | 'warning' | 'danger';
@@ -124,7 +124,7 @@ function SandyButton({
}
onClick?.(e);
if (href != null) {
electron.shell.openExternal(href); // TODO: decouple from Electron
getFlipperLib().openLink(href);
}
},
[disabled, onClick, href],

View File

@@ -7,7 +7,7 @@
* @format
*/
import {shell} from 'electron';
import {getFlipperLib} from 'flipper-plugin';
import {Typography} from 'antd';
const AntOriginalLink = Typography.Link;
@@ -15,7 +15,7 @@ const AntOriginalLink = Typography.Link;
// used by patch for Typography.Link in AntD
// @ts-ignore
global.flipperOpenLink = function openLinkExternal(url: string) {
shell.openExternal(url);
getFlipperLib().openLink(url);
};
export default AntOriginalLink;

View File

@@ -11,7 +11,7 @@ import React, {CSSProperties, ReactNode} from 'react';
import styled from '@emotion/styled';
import ReactMarkdown from 'react-markdown';
import {colors} from './colors';
import {shell} from 'electron';
import {getFlipperLib} from 'flipper-plugin';
const Container = styled.div({
padding: 10,
@@ -73,7 +73,9 @@ const Link = styled.span({
});
function LinkReference(props: {href: string; children: Array<ReactNode>}) {
return (
<Link onClick={() => shell.openExternal(props.href)}>{props.children}</Link>
<Link onClick={() => getFlipperLib().openLink(props.href)}>
{props.children}
</Link>
);
}

View File

@@ -13,7 +13,7 @@ import type {Store} from '../reducers';
import createPaste from '../fb-stubs/createPaste';
import GK from '../fb-stubs/GK';
import type BaseDevice from '../devices/BaseDevice';
import {clipboard} from 'electron';
import {clipboard, shell} from 'electron';
import constants from '../fb-stubs/constants';
import {addNotification} from '../reducers/notifications';
import {deconstructPluginKey} from './clientUtils';
@@ -50,6 +50,9 @@ export function initializeFlipperLibImplementation(
writeTextToClipboard(text: string) {
clipboard.writeText(text);
},
openLink(url: string) {
shell.openExternal(url);
},
showNotification(pluginId, notification) {
const parts = deconstructPluginKey(pluginId);
store.dispatch(

View File

@@ -7,13 +7,15 @@
* @format
*/
import {getFlipperLib} from 'flipper-plugin';
import {getPreferredEditorUriScheme} from '../fb-stubs/user';
import {shell} from 'electron';
let preferredEditorUriScheme: string | undefined = undefined;
export function callVSCode(plugin: string, command: string, params?: string) {
getVSCodeUrl(plugin, command, params).then((url) => shell.openExternal(url));
getVSCodeUrl(plugin, command, params).then((url) =>
getFlipperLib().openLink(url),
);
}
export async function getVSCodeUrl(

View File

@@ -52,6 +52,7 @@ test('Correct top level API exposed', () => {
"createDataSource",
"createState",
"createTablePlugin",
"getFlipperLib",
"produce",
"renderReactRoot",
"sleep",

View File

@@ -37,7 +37,7 @@ export {createState, useValue, Atom} from './state/atom';
export {batch} from './state/batch';
export {
FlipperLib,
getFlipperLibImplementation as _getFlipperLibImplementation,
getFlipperLib,
setFlipperLibImplementation as _setFlipperLibImplementation,
} from './plugin/FlipperLib';
export {

View File

@@ -30,19 +30,20 @@ export interface FlipperLib {
deeplink: unknown,
): void;
writeTextToClipboard(text: string): void;
openLink(url: string): void;
showNotification(pluginKey: string, notification: Notification): void;
DetailsSidebarImplementation?(
props: DetailSidebarProps,
): React.ReactElement | null;
}
let flipperLibInstance: FlipperLib | undefined;
export let flipperLibInstance: FlipperLib | undefined;
export function tryGetFlipperLibImplementation(): FlipperLib | undefined {
return flipperLibInstance;
}
export function getFlipperLibImplementation(): FlipperLib {
export function getFlipperLib(): FlipperLib {
if (!flipperLibInstance) {
throw new Error('Flipper lib not instantiated');
}

View File

@@ -374,6 +374,7 @@ export function createMockFlipperLib(options?: StartPluginOptions): FlipperLib {
},
selectPlugin: jest.fn(),
writeTextToClipboard: jest.fn(),
openLink: jest.fn(),
showNotification: jest.fn(),
};
}

View File

@@ -14,7 +14,7 @@ import styled from '@emotion/styled';
import React, {MouseEvent, KeyboardEvent} from 'react';
import {theme} from '../theme';
import {Layout} from '../Layout';
import {_getFlipperLibImplementation} from 'flipper-plugin';
import {getFlipperLib} from 'flipper-plugin';
import {DownOutlined, RightOutlined} from '@ant-design/icons';
const {Text} = Typography;
@@ -221,7 +221,7 @@ class ElementsRow extends PureComponent<ElementsRowProps, ElementsRowState> {
{
label: 'Copy',
click: () => {
_getFlipperLibImplementation()?.writeTextToClipboard(
getFlipperLib()?.writeTextToClipboard(
props.onCopyExpandedTree(props.element, 0),
);
},
@@ -229,7 +229,7 @@ class ElementsRow extends PureComponent<ElementsRowProps, ElementsRowState> {
{
label: 'Copy expanded child elements',
click: () =>
_getFlipperLibImplementation()?.writeTextToClipboard(
getFlipperLib()?.writeTextToClipboard(
props.onCopyExpandedTree(props.element, 255),
),
},
@@ -253,7 +253,7 @@ class ElementsRow extends PureComponent<ElementsRowProps, ElementsRowState> {
return {
label: `Copy ${o.name}`,
click: () => {
_getFlipperLibImplementation()?.writeTextToClipboard(o.value);
getFlipperLib()?.writeTextToClipboard(o.value);
},
};
}),
@@ -555,9 +555,7 @@ export class Elements extends PureComponent<ElementsProps, ElementsState> {
) {
e.stopPropagation();
e.preventDefault();
_getFlipperLibImplementation()?.writeTextToClipboard(
selectedElement.name,
);
getFlipperLib()?.writeTextToClipboard(selectedElement.name);
return;
}

View File

@@ -8,8 +8,8 @@
*/
import React from 'react';
import {shell} from 'electron';
import {styled, colors, FlexRow, Text, GK} from 'flipper';
import {Typography} from 'antd';
const BannerContainer = styled(FlexRow)({
height: '30px',
@@ -34,14 +34,6 @@ const BannerLink = styled(CustomLink)({
},
});
const StyledLink = styled.span({
'&:hover': {
cursor: 'pointer',
},
});
StyledLink.displayName = 'CustomLink:StyledLink';
function CustomLink(props: {
href: string;
className?: string;
@@ -49,12 +41,12 @@ function CustomLink(props: {
style?: React.CSSProperties;
}) {
return (
<StyledLink
<Typography.Link
className={props.className}
onClick={() => shell.openExternal(props.href)}
href={props.href}
style={props.style}>
{props.children || props.href}
</StyledLink>
</Typography.Link>
);
}

View File

@@ -935,6 +935,13 @@ See `View > Flipper Style Guide` inside the Flipper application for more details
## Utilities
### getFlipperLib
A set of globally available utilities like opening links, interacting with the clipboard, etc.
Example: `getFlipperLib().writeTextToClipboard("hello from Flipper");
The full set of utilities can be found [here](https://github.com/facebook/flipper/blob/master/desktop/flipper-plugin/src/plugin/FlipperLib.tsx#L20)
### createTablePlugin
Utility to create a plugin that consists of a master table and details JSON view with minimal effort. See the [Showing a table](../tutorial/js-table.mdx) tutorial for an example.