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:
committed by
Facebook GitHub Bot
parent
2b236c6114
commit
5dbd3bd414
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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 =>
|
||||
[
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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...
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -52,6 +52,7 @@ test('Correct top level API exposed', () => {
|
||||
"createDataSource",
|
||||
"createState",
|
||||
"createTablePlugin",
|
||||
"getFlipperLib",
|
||||
"produce",
|
||||
"renderReactRoot",
|
||||
"sleep",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -374,6 +374,7 @@ export function createMockFlipperLib(options?: StartPluginOptions): FlipperLib {
|
||||
},
|
||||
selectPlugin: jest.fn(),
|
||||
writeTextToClipboard: jest.fn(),
|
||||
openLink: jest.fn(),
|
||||
showNotification: jest.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user