Support handling deeplinks in plugins
Summary:
This adds support for handling incoming deeplinks in a Sandy plugin, which can be done by using a `client.onDeepLink(deepLink => { } )` listener
Also generalized deeplinks to not just support strings, but also richer objects, which is beneficial to plugin to plugin linking.
Reviewed By: jknoxville
Differential Revision: D22524749
fbshipit-source-id: 2cbe8d52f6eac91a1c1c8c8494706952920b9181
This commit is contained in:
committed by
Facebook GitHub Bot
parent
485b4c9827
commit
f0c54667e0
@@ -55,7 +55,7 @@ type DispatchFromProps = {
|
|||||||
selectPlugin: (payload: {
|
selectPlugin: (payload: {
|
||||||
selectedPlugin: string | null;
|
selectedPlugin: string | null;
|
||||||
selectedApp: string | null;
|
selectedApp: string | null;
|
||||||
deepLinkPayload: string | null;
|
deepLinkPayload: unknown;
|
||||||
}) => any;
|
}) => any;
|
||||||
updatePluginBlacklist: (blacklist: Array<string>) => any;
|
updatePluginBlacklist: (blacklist: Array<string>) => any;
|
||||||
updateCategoryBlacklist: (blacklist: Array<string>) => any;
|
updateCategoryBlacklist: (blacklist: Array<string>) => any;
|
||||||
@@ -414,7 +414,7 @@ type ItemProps = {
|
|||||||
selectPlugin?: (payload: {
|
selectPlugin?: (payload: {
|
||||||
selectedPlugin: string | null;
|
selectedPlugin: string | null;
|
||||||
selectedApp: string | null;
|
selectedApp: string | null;
|
||||||
deepLinkPayload: string | null;
|
deepLinkPayload: unknown;
|
||||||
}) => any;
|
}) => any;
|
||||||
logger?: Logger;
|
logger?: Logger;
|
||||||
plugin: PluginDefinition | null | undefined;
|
plugin: PluginDefinition | null | undefined;
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ type StateFromProps = {
|
|||||||
activePlugin: PluginDefinition | undefined;
|
activePlugin: PluginDefinition | undefined;
|
||||||
target: Client | BaseDevice | null;
|
target: Client | BaseDevice | null;
|
||||||
pluginKey: string | null;
|
pluginKey: string | null;
|
||||||
deepLinkPayload: string | null;
|
deepLinkPayload: unknown;
|
||||||
selectedApp: string | null;
|
selectedApp: string | null;
|
||||||
isArchivedDevice: boolean;
|
isArchivedDevice: boolean;
|
||||||
pendingMessages: Message[] | undefined;
|
pendingMessages: Message[] | undefined;
|
||||||
@@ -113,7 +113,7 @@ type DispatchFromProps = {
|
|||||||
selectPlugin: (payload: {
|
selectPlugin: (payload: {
|
||||||
selectedPlugin: string | null;
|
selectedPlugin: string | null;
|
||||||
selectedApp?: string | null;
|
selectedApp?: string | null;
|
||||||
deepLinkPayload: string | null;
|
deepLinkPayload: unknown;
|
||||||
}) => any;
|
}) => any;
|
||||||
setPluginState: (payload: {pluginKey: string; state: any}) => void;
|
setPluginState: (payload: {pluginKey: string; state: any}) => void;
|
||||||
setStaticView: (payload: StaticView) => void;
|
setStaticView: (payload: StaticView) => void;
|
||||||
@@ -178,6 +178,13 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
this.processMessageQueue();
|
this.processMessageQueue();
|
||||||
|
// make sure deeplinks are propagated
|
||||||
|
const {deepLinkPayload, target, activePlugin} = this.props;
|
||||||
|
if (deepLinkPayload && target instanceof Client && activePlugin) {
|
||||||
|
target.sandyPluginStates
|
||||||
|
.get(activePlugin.id)
|
||||||
|
?.triggerDeepLink(deepLinkPayload);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processMessageQueue() {
|
processMessageQueue() {
|
||||||
@@ -373,7 +380,7 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
setPersistedState: (state) => setPluginState({pluginKey, state}),
|
setPersistedState: (state) => setPluginState({pluginKey, state}),
|
||||||
target,
|
target,
|
||||||
deepLinkPayload: this.props.deepLinkPayload,
|
deepLinkPayload: this.props.deepLinkPayload,
|
||||||
selectPlugin: (pluginID: string, deepLinkPayload: string | null) => {
|
selectPlugin: (pluginID: string, deepLinkPayload: unknown) => {
|
||||||
const {target} = this.props;
|
const {target} = this.props;
|
||||||
// check if plugin will be available
|
// check if plugin will be available
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -16,8 +16,11 @@ import {
|
|||||||
FlipperClient,
|
FlipperClient,
|
||||||
TestUtils,
|
TestUtils,
|
||||||
usePlugin,
|
usePlugin,
|
||||||
|
createState,
|
||||||
|
useValue,
|
||||||
} from 'flipper-plugin';
|
} from 'flipper-plugin';
|
||||||
import {selectPlugin, starPlugin} from '../reducers/connections';
|
import {selectPlugin, starPlugin} from '../reducers/connections';
|
||||||
|
import {updateSettings} from '../reducers/settings';
|
||||||
|
|
||||||
interface PersistedState {
|
interface PersistedState {
|
||||||
count: 1;
|
count: 1;
|
||||||
@@ -121,9 +124,6 @@ test('PluginContainer can render Sandy plugins', async () => {
|
|||||||
Component: MySandyPlugin,
|
Component: MySandyPlugin,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// any cast because this plugin is not enriched with the meta data that the plugin loader
|
|
||||||
// normally adds. Our further sandy plugin test infra won't need this, but
|
|
||||||
// for this test we do need to act a s a loaded plugin, to make sure PluginContainer itself can handle it
|
|
||||||
const {
|
const {
|
||||||
renderer,
|
renderer,
|
||||||
act,
|
act,
|
||||||
@@ -230,3 +230,140 @@ test('PluginContainer can render Sandy plugins', async () => {
|
|||||||
).toBeCalledTimes(1);
|
).toBeCalledTimes(1);
|
||||||
expect(client.rawSend).toBeCalledWith('init', {plugin: 'TestPlugin'});
|
expect(client.rawSend).toBeCalledWith('init', {plugin: 'TestPlugin'});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('PluginContainer + Sandy plugin supports deeplink', async () => {
|
||||||
|
const linksSeen: any[] = [];
|
||||||
|
|
||||||
|
const plugin = (client: FlipperClient) => {
|
||||||
|
const linkState = createState('');
|
||||||
|
client.onDeepLink((link) => {
|
||||||
|
linksSeen.push(link);
|
||||||
|
linkState.set(String(link));
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
linkState,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const definition = new SandyPluginDefinition(
|
||||||
|
TestUtils.createMockPluginDetails(),
|
||||||
|
{
|
||||||
|
plugin,
|
||||||
|
Component() {
|
||||||
|
const instance = usePlugin(plugin);
|
||||||
|
const linkState = useValue(instance.linkState);
|
||||||
|
return <h1>hello {linkState || 'world'}</h1>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const {renderer, act, client, store} = await renderMockFlipperWithPlugin(
|
||||||
|
definition,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(client.rawSend).toBeCalledWith('init', {plugin: 'TestPlugin'});
|
||||||
|
|
||||||
|
expect(linksSeen).toEqual([]);
|
||||||
|
expect(renderer.baseElement).toMatchInlineSnapshot(`
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="css-1orvm1g-View-FlexBox-FlexColumn"
|
||||||
|
>
|
||||||
|
<h1>
|
||||||
|
hello
|
||||||
|
world
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="css-bxcvv9-View-FlexBox-FlexRow"
|
||||||
|
id="detailsSidebar"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`);
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
store.dispatch(
|
||||||
|
selectPlugin({
|
||||||
|
selectedPlugin: definition.id,
|
||||||
|
deepLinkPayload: 'universe!',
|
||||||
|
selectedApp: client.query.app,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(linksSeen).toEqual(['universe!']);
|
||||||
|
expect(renderer.baseElement).toMatchInlineSnapshot(`
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="css-1orvm1g-View-FlexBox-FlexColumn"
|
||||||
|
>
|
||||||
|
<h1>
|
||||||
|
hello
|
||||||
|
universe!
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="css-bxcvv9-View-FlexBox-FlexRow"
|
||||||
|
id="detailsSidebar"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Sending same link doesn't trigger again
|
||||||
|
act(() => {
|
||||||
|
store.dispatch(
|
||||||
|
selectPlugin({
|
||||||
|
selectedPlugin: definition.id,
|
||||||
|
deepLinkPayload: 'universe!',
|
||||||
|
selectedApp: client.query.app,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(linksSeen).toEqual(['universe!']);
|
||||||
|
|
||||||
|
// ...nor does a random other store update that does trigger a plugin container render
|
||||||
|
act(() => {
|
||||||
|
store.dispatch(
|
||||||
|
updateSettings({
|
||||||
|
...store.getState().settingsState,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(linksSeen).toEqual(['universe!']);
|
||||||
|
|
||||||
|
// Different link does trigger again
|
||||||
|
act(() => {
|
||||||
|
store.dispatch(
|
||||||
|
selectPlugin({
|
||||||
|
selectedPlugin: definition.id,
|
||||||
|
deepLinkPayload: 'london!',
|
||||||
|
selectedApp: client.query.app,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(linksSeen).toEqual(['universe!', 'london!']);
|
||||||
|
|
||||||
|
// and same link does trigger if something else was selected in the mean time
|
||||||
|
act(() => {
|
||||||
|
store.dispatch(
|
||||||
|
selectPlugin({
|
||||||
|
selectedPlugin: 'Logs',
|
||||||
|
deepLinkPayload: 'london!',
|
||||||
|
selectedApp: client.query.app,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
act(() => {
|
||||||
|
store.dispatch(
|
||||||
|
selectPlugin({
|
||||||
|
selectedPlugin: definition.id,
|
||||||
|
deepLinkPayload: 'london!',
|
||||||
|
selectedApp: client.query.app,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(linksSeen).toEqual(['universe!', 'london!', 'london!']);
|
||||||
|
});
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {selectPlugin} from '../reducers/connections';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
type StateFromProps = {
|
type StateFromProps = {
|
||||||
deepLinkPayload: string | null;
|
deepLinkPayload: unknown;
|
||||||
blacklistedPlugins: Array<string>;
|
blacklistedPlugins: Array<string>;
|
||||||
blacklistedCategories: Array<string>;
|
blacklistedCategories: Array<string>;
|
||||||
};
|
};
|
||||||
@@ -28,7 +28,7 @@ type DispatchFromProps = {
|
|||||||
selectPlugin: (payload: {
|
selectPlugin: (payload: {
|
||||||
selectedPlugin: string | null;
|
selectedPlugin: string | null;
|
||||||
selectedApp: string | null | undefined;
|
selectedApp: string | null | undefined;
|
||||||
deepLinkPayload: string | null;
|
deepLinkPayload: unknown;
|
||||||
}) => any;
|
}) => any;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,7 +62,9 @@ class Notifications extends PureComponent<Props, State> {
|
|||||||
<Container>
|
<Container>
|
||||||
<ConnectedNotificationsTable
|
<ConnectedNotificationsTable
|
||||||
onClear={clearAllNotifications}
|
onClear={clearAllNotifications}
|
||||||
selectedID={deepLinkPayload}
|
selectedID={
|
||||||
|
typeof deepLinkPayload === 'string' ? deepLinkPayload : null
|
||||||
|
}
|
||||||
onSelectPlugin={selectPlugin}
|
onSelectPlugin={selectPlugin}
|
||||||
logger={logger}
|
logger={logger}
|
||||||
defaultFilters={[
|
defaultFilters={[
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ type StateFromProps = {
|
|||||||
type SelectPlugin = (payload: {
|
type SelectPlugin = (payload: {
|
||||||
selectedPlugin: string | null;
|
selectedPlugin: string | null;
|
||||||
selectedApp?: string | null;
|
selectedApp?: string | null;
|
||||||
deepLinkPayload: string | null;
|
deepLinkPayload: unknown;
|
||||||
selectedDevice: BaseDevice;
|
selectedDevice: BaseDevice;
|
||||||
}) => void;
|
}) => void;
|
||||||
|
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ export type Props<T> = {
|
|||||||
persistedState: T;
|
persistedState: T;
|
||||||
setPersistedState: (state: Partial<T>) => void;
|
setPersistedState: (state: Partial<T>) => void;
|
||||||
target: PluginTarget;
|
target: PluginTarget;
|
||||||
deepLinkPayload: string | null;
|
deepLinkPayload: unknown;
|
||||||
selectPlugin: (pluginID: string, deepLinkPayload: string | null) => boolean;
|
selectPlugin: (pluginID: string, deepLinkPayload: unknown) => boolean;
|
||||||
isArchivedDevice: boolean;
|
isArchivedDevice: boolean;
|
||||||
selectedApp: string | null;
|
selectedApp: string | null;
|
||||||
setStaticView: (payload: StaticView) => void;
|
setStaticView: (payload: StaticView) => void;
|
||||||
|
|||||||
@@ -23,10 +23,7 @@ const WelcomeScreen = isHeadless()
|
|||||||
import NotificationScreen from '../chrome/NotificationScreen';
|
import NotificationScreen from '../chrome/NotificationScreen';
|
||||||
import SupportRequestFormV2 from '../fb-stubs/SupportRequestFormV2';
|
import SupportRequestFormV2 from '../fb-stubs/SupportRequestFormV2';
|
||||||
import SupportRequestDetails from '../fb-stubs/SupportRequestDetails';
|
import SupportRequestDetails from '../fb-stubs/SupportRequestDetails';
|
||||||
import {
|
import {getPluginKey} from '../utils/pluginUtils';
|
||||||
getPluginKey,
|
|
||||||
defaultEnabledBackgroundPlugins,
|
|
||||||
} from '../utils/pluginUtils';
|
|
||||||
import {deconstructClientId} from '../utils/clientUtils';
|
import {deconstructClientId} from '../utils/clientUtils';
|
||||||
import {FlipperDevicePlugin, PluginDefinition, isSandyPlugin} from '../plugin';
|
import {FlipperDevicePlugin, PluginDefinition, isSandyPlugin} from '../plugin';
|
||||||
import {RegisterPluginAction} from './plugins';
|
import {RegisterPluginAction} from './plugins';
|
||||||
@@ -63,7 +60,7 @@ export type State = {
|
|||||||
deviceId?: string;
|
deviceId?: string;
|
||||||
errorMessage?: string;
|
errorMessage?: string;
|
||||||
}>;
|
}>;
|
||||||
deepLinkPayload: string | null;
|
deepLinkPayload: unknown;
|
||||||
staticView: StaticView;
|
staticView: StaticView;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -89,7 +86,7 @@ export type Action =
|
|||||||
payload: {
|
payload: {
|
||||||
selectedPlugin: null | string;
|
selectedPlugin: null | string;
|
||||||
selectedApp?: null | string;
|
selectedApp?: null | string;
|
||||||
deepLinkPayload: null | string;
|
deepLinkPayload: unknown;
|
||||||
selectedDevice?: null | BaseDevice;
|
selectedDevice?: null | BaseDevice;
|
||||||
time: number;
|
time: number;
|
||||||
};
|
};
|
||||||
@@ -245,8 +242,8 @@ export default (state: State = INITAL_STATE, action: Actions): State => {
|
|||||||
const {payload} = action;
|
const {payload} = action;
|
||||||
const {selectedPlugin, selectedApp, deepLinkPayload} = payload;
|
const {selectedPlugin, selectedApp, deepLinkPayload} = payload;
|
||||||
let selectedDevice = payload.selectedDevice;
|
let selectedDevice = payload.selectedDevice;
|
||||||
if (deepLinkPayload) {
|
if (typeof deepLinkPayload === 'string') {
|
||||||
const deepLinkParams = new URLSearchParams(deepLinkPayload || '');
|
const deepLinkParams = new URLSearchParams(deepLinkPayload);
|
||||||
const deviceParam = deepLinkParams.get('device');
|
const deviceParam = deepLinkParams.get('device');
|
||||||
const deviceMatch = state.devices.find((v) => v.title === deviceParam);
|
const deviceMatch = state.devices.find((v) => v.title === deviceParam);
|
||||||
if (deviceMatch) {
|
if (deviceMatch) {
|
||||||
@@ -460,7 +457,7 @@ export const selectPlugin = (payload: {
|
|||||||
selectedPlugin: null | string;
|
selectedPlugin: null | string;
|
||||||
selectedApp?: null | string;
|
selectedApp?: null | string;
|
||||||
selectedDevice?: BaseDevice | null;
|
selectedDevice?: BaseDevice | null;
|
||||||
deepLinkPayload: null | string;
|
deepLinkPayload: unknown;
|
||||||
time?: number;
|
time?: number;
|
||||||
}): Action => ({
|
}): Action => ({
|
||||||
type: 'SELECT_PLUGIN',
|
type: 'SELECT_PLUGIN',
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
import * as TestUtils from '../test-utils/test-utils';
|
import * as TestUtils from '../test-utils/test-utils';
|
||||||
import * as testPlugin from './TestPlugin';
|
import * as testPlugin from './TestPlugin';
|
||||||
import {createState} from '../state/atom';
|
import {createState} from '../state/atom';
|
||||||
|
import {FlipperClient} from '../plugin/Plugin';
|
||||||
|
|
||||||
test('it can start a plugin and lifecycle events', () => {
|
test('it can start a plugin and lifecycle events', () => {
|
||||||
const {instance, ...p} = TestUtils.startPlugin(testPlugin);
|
const {instance, ...p} = TestUtils.startPlugin(testPlugin);
|
||||||
@@ -193,3 +194,24 @@ test('plugins cannot use a persist key twice', async () => {
|
|||||||
`"Some other state is already persisting with key \\"test\\""`,
|
`"Some other state is already persisting with key \\"test\\""`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('plugins can receive deeplinks', async () => {
|
||||||
|
const plugin = TestUtils.startPlugin({
|
||||||
|
plugin(client: FlipperClient) {
|
||||||
|
client.onDeepLink((deepLink) => {
|
||||||
|
if (typeof deepLink === 'string') {
|
||||||
|
field1.set(deepLink);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const field1 = createState('', {persist: 'test'});
|
||||||
|
return {field1};
|
||||||
|
},
|
||||||
|
Component() {
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(plugin.instance.field1.get()).toBe('');
|
||||||
|
plugin.triggerDeepLink('test');
|
||||||
|
expect(plugin.instance.field1.get()).toBe('test');
|
||||||
|
});
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ export interface FlipperClient<
|
|||||||
*/
|
*/
|
||||||
onDisconnect(cb: () => void): void;
|
onDisconnect(cb: () => void): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered when this plugin is opened through a deeplink
|
||||||
|
*/
|
||||||
|
onDeepLink(cb: (deepLink: unknown) => void): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a message to the connected client
|
* Send a message to the connected client
|
||||||
*/
|
*/
|
||||||
@@ -113,6 +118,8 @@ export class SandyPluginInstance {
|
|||||||
initialStates?: Record<string, any>;
|
initialStates?: Record<string, any>;
|
||||||
// all the atoms that should be serialized when making an export / import
|
// all the atoms that should be serialized when making an export / import
|
||||||
rootStates: Record<string, Atom<any>> = {};
|
rootStates: Record<string, Atom<any>> = {};
|
||||||
|
// last seen deeplink
|
||||||
|
lastDeeplink?: any;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
realClient: RealFlipperClient,
|
realClient: RealFlipperClient,
|
||||||
@@ -143,6 +150,9 @@ export class SandyPluginInstance {
|
|||||||
onMessage: (event, callback) => {
|
onMessage: (event, callback) => {
|
||||||
this.events.on('event-' + event, callback);
|
this.events.on('event-' + event, callback);
|
||||||
},
|
},
|
||||||
|
onDeepLink: (callback) => {
|
||||||
|
this.events.on('deeplink', callback);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
currentPluginInstance = this;
|
currentPluginInstance = this;
|
||||||
this.initialStates = initialStates;
|
this.initialStates = initialStates;
|
||||||
@@ -165,6 +175,7 @@ export class SandyPluginInstance {
|
|||||||
|
|
||||||
// the plugin is deselected in the UI
|
// the plugin is deselected in the UI
|
||||||
deactivate() {
|
deactivate() {
|
||||||
|
this.lastDeeplink = undefined;
|
||||||
if (this.destroyed) {
|
if (this.destroyed) {
|
||||||
// this can happen if the plugin is disabled while active in the UI.
|
// this can happen if the plugin is disabled while active in the UI.
|
||||||
// In that case deinit & destroy is already triggered from the STAR_PLUGIN action
|
// In that case deinit & destroy is already triggered from the STAR_PLUGIN action
|
||||||
@@ -211,6 +222,14 @@ export class SandyPluginInstance {
|
|||||||
return '[SandyPluginInstance]';
|
return '[SandyPluginInstance]';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
triggerDeepLink(deepLink: unknown) {
|
||||||
|
this.assertNotDestroyed();
|
||||||
|
if (deepLink !== this.lastDeeplink) {
|
||||||
|
this.lastDeeplink = deepLink;
|
||||||
|
this.events.emit('deeplink', deepLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exportState() {
|
exportState() {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(this.rootStates).map(([key, atom]) => [key, atom.get()]),
|
Object.entries(this.rootStates).map(([key, atom]) => [key, atom.get()]),
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ interface StartPluginResult<Module extends FlipperPluginModule<any>> {
|
|||||||
}[],
|
}[],
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
|
triggerDeepLink(deeplink: unknown): void;
|
||||||
|
|
||||||
exportState(): any;
|
exportState(): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,6 +166,9 @@ export function startPlugin<Module extends FlipperPluginModule<any>>(
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
exportState: () => pluginInstance.exportState(),
|
exportState: () => pluginInstance.exportState(),
|
||||||
|
triggerDeepLink: (deepLink: unknown) => {
|
||||||
|
pluginInstance.triggerDeepLink(deepLink);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
res._backingInstance = pluginInstance;
|
res._backingInstance = pluginInstance;
|
||||||
|
|||||||
@@ -242,7 +242,8 @@ export default class LayoutPlugin extends FlipperPlugin<
|
|||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
init: true,
|
init: true,
|
||||||
selectedElement: this.props.deepLinkPayload
|
selectedElement:
|
||||||
|
typeof this.props.deepLinkPayload === 'string'
|
||||||
? this.props.deepLinkPayload
|
? this.props.deepLinkPayload
|
||||||
: null,
|
: null,
|
||||||
});
|
});
|
||||||
@@ -458,7 +459,11 @@ export default class LayoutPlugin extends FlipperPlugin<
|
|||||||
this.setState({searchResults})
|
this.setState({searchResults})
|
||||||
}
|
}
|
||||||
inAXMode={this.state.inAXMode}
|
inAXMode={this.state.inAXMode}
|
||||||
initialQuery={this.props.deepLinkPayload}
|
initialQuery={
|
||||||
|
typeof this.props.deepLinkPayload === 'string'
|
||||||
|
? this.props.deepLinkPayload
|
||||||
|
: null
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
<Layout.Right>
|
<Layout.Right>
|
||||||
|
|||||||
@@ -438,11 +438,11 @@ export default class LogTable extends FlipperDevicePlugin<
|
|||||||
};
|
};
|
||||||
|
|
||||||
calculateHighlightedRows = (
|
calculateHighlightedRows = (
|
||||||
deepLinkPayload: string | null,
|
deepLinkPayload: unknown,
|
||||||
rows: ReadonlyArray<TableBodyRow>,
|
rows: ReadonlyArray<TableBodyRow>,
|
||||||
): Set<string> => {
|
): Set<string> => {
|
||||||
const highlightedRows = new Set<string>();
|
const highlightedRows = new Set<string>();
|
||||||
if (!deepLinkPayload) {
|
if (typeof deepLinkPayload !== 'string') {
|
||||||
return highlightedRows;
|
return highlightedRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -273,10 +273,10 @@ export default class extends FlipperPlugin<State, any, PersistedState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
parseDeepLinkPayload = (
|
parseDeepLinkPayload = (
|
||||||
deepLinkPayload: string | null,
|
deepLinkPayload: unknown,
|
||||||
): Pick<State, 'selectedIds' | 'searchTerm'> => {
|
): Pick<State, 'selectedIds' | 'searchTerm'> => {
|
||||||
const searchTermDelim = 'searchTerm=';
|
const searchTermDelim = 'searchTerm=';
|
||||||
if (deepLinkPayload === null) {
|
if (typeof deepLinkPayload !== 'string') {
|
||||||
return {
|
return {
|
||||||
selectedIds: [],
|
selectedIds: [],
|
||||||
searchTerm: '',
|
searchTerm: '',
|
||||||
|
|||||||
Reference in New Issue
Block a user