Convert to Shared Preference Plugin to Sandy
Summary: per title In addition, this diff adds `startUnactivated` option to allow setting up `onSend` mock implementation. Reviewed By: mweststrate Differential Revision: D24477989 fbshipit-source-id: f913574ebacdd436e8511baa43744249a014e90b
This commit is contained in:
committed by
Facebook GitHub Bot
parent
467a6b16fb
commit
402ea2fc14
@@ -43,6 +43,7 @@ interface StartPluginOptions {
|
|||||||
initialState?: Record<string, any>;
|
initialState?: Record<string, any>;
|
||||||
isArchived?: boolean;
|
isArchived?: boolean;
|
||||||
isBackgroundPlugin?: boolean;
|
isBackgroundPlugin?: boolean;
|
||||||
|
startUnactivated?: boolean;
|
||||||
/** Provide a set of unsupported methods to simulate older clients that don't support certain methods yet */
|
/** Provide a set of unsupported methods to simulate older clients that don't support certain methods yet */
|
||||||
unsupportedMethods?: string[];
|
unsupportedMethods?: string[];
|
||||||
}
|
}
|
||||||
@@ -235,7 +236,9 @@ export function startPlugin<Module extends FlipperPluginModule<any>>(
|
|||||||
if (options?.isBackgroundPlugin) {
|
if (options?.isBackgroundPlugin) {
|
||||||
pluginInstance.connect(); // otherwise part of activate
|
pluginInstance.connect(); // otherwise part of activate
|
||||||
}
|
}
|
||||||
pluginInstance.activate();
|
if (!options?.startUnactivated) {
|
||||||
|
pluginInstance.activate();
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,291 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*
|
|
||||||
* @format
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
ManagedTable,
|
|
||||||
Text,
|
|
||||||
Heading,
|
|
||||||
FlexColumn,
|
|
||||||
colors,
|
|
||||||
FlexRow,
|
|
||||||
ManagedDataInspector,
|
|
||||||
styled,
|
|
||||||
Select,
|
|
||||||
} from 'flipper';
|
|
||||||
import {FlipperPlugin} from 'flipper';
|
|
||||||
|
|
||||||
import {clone} from 'lodash';
|
|
||||||
|
|
||||||
type SharedPreferencesChangeEvent = {|
|
|
||||||
preferences: string,
|
|
||||||
name: string,
|
|
||||||
time: number,
|
|
||||||
deleted: boolean,
|
|
||||||
value: string,
|
|
||||||
|};
|
|
||||||
|
|
||||||
export type SharedPreferences = {|
|
|
||||||
[name: string]: any,
|
|
||||||
|};
|
|
||||||
|
|
||||||
type SharedPreferencesEntry = {
|
|
||||||
preferences: SharedPreferences,
|
|
||||||
changesList: Array<SharedPreferencesChangeEvent>,
|
|
||||||
};
|
|
||||||
|
|
||||||
type SharedPreferencesMap = {
|
|
||||||
[name: string]: SharedPreferencesEntry,
|
|
||||||
};
|
|
||||||
|
|
||||||
type SharedPreferencesState = {|
|
|
||||||
selectedPreferences: ?string,
|
|
||||||
sharedPreferences: SharedPreferencesMap,
|
|
||||||
|};
|
|
||||||
|
|
||||||
const CHANGELOG_COLUMNS = {
|
|
||||||
event: {
|
|
||||||
value: 'Event',
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
value: 'Name',
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
value: 'Value',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const CHANGELOG_COLUMN_SIZES = {
|
|
||||||
event: '30%',
|
|
||||||
name: '30%',
|
|
||||||
value: '30%',
|
|
||||||
};
|
|
||||||
|
|
||||||
const UPDATED_LABEL = <Text color={colors.lime}>Updated</Text>;
|
|
||||||
const DELETED_LABEL = <Text color={colors.cherry}>Deleted</Text>;
|
|
||||||
|
|
||||||
const InspectorColumn = styled(FlexColumn)({
|
|
||||||
flexGrow: 0.2,
|
|
||||||
});
|
|
||||||
|
|
||||||
const ChangelogColumn = styled(FlexColumn)({
|
|
||||||
flexGrow: 0.8,
|
|
||||||
paddingLeft: '16px',
|
|
||||||
});
|
|
||||||
|
|
||||||
const RootColumn = styled(FlexColumn)({
|
|
||||||
paddingLeft: '16px',
|
|
||||||
paddingRight: '16px',
|
|
||||||
paddingTop: '16px',
|
|
||||||
});
|
|
||||||
|
|
||||||
export default class extends FlipperPlugin<SharedPreferencesState> {
|
|
||||||
state = {
|
|
||||||
selectedPreferences: null,
|
|
||||||
sharedPreferences: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
reducers = {
|
|
||||||
UpdateSharedPreferences(state: SharedPreferencesState, results: Object) {
|
|
||||||
const update = results.update;
|
|
||||||
const entry = state.sharedPreferences[update.name] || {changesList: []};
|
|
||||||
entry.preferences = update.preferences;
|
|
||||||
state.sharedPreferences[update.name] = entry;
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
selectedPreferences: state.selectedPreferences || update.name,
|
|
||||||
sharedPreferences: {
|
|
||||||
...state.sharedPreferences,
|
|
||||||
[update.name]: entry,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
ChangeSharedPreferences(state: SharedPreferencesState, event: Object) {
|
|
||||||
const change = event.change;
|
|
||||||
const entry = state.sharedPreferences[change.preferences];
|
|
||||||
if (entry == null) {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
let newEntry;
|
|
||||||
if (change.deleted) {
|
|
||||||
const newPreferences = {
|
|
||||||
...entry.preferences,
|
|
||||||
};
|
|
||||||
delete newPreferences[change.name];
|
|
||||||
newEntry = {
|
|
||||||
...entry,
|
|
||||||
preferences: newPreferences,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
newEntry = {
|
|
||||||
...entry,
|
|
||||||
preferences: {
|
|
||||||
...entry.preferences,
|
|
||||||
[change.name]: change.value,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
newEntry.changesList = [change, ...entry.changesList];
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
sharedPreferences: {
|
|
||||||
...state.sharedPreferences,
|
|
||||||
[change.preferences]: newEntry,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
UpdateSelectedSharedPreferences(
|
|
||||||
state: SharedPreferencesState,
|
|
||||||
event: Object,
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
selectedPreferences: event.selected,
|
|
||||||
sharedPreferences: state.sharedPreferences,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
init() {
|
|
||||||
this.client
|
|
||||||
.call('getAllSharedPreferences')
|
|
||||||
.then((results: {[name: string]: SharedPreferences}) => {
|
|
||||||
Object.entries(results).forEach(([name, prefs]) => {
|
|
||||||
const update = {name: name, preferences: prefs};
|
|
||||||
this.dispatchAction({update, type: 'UpdateSharedPreferences'});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.client.subscribe(
|
|
||||||
'sharedPreferencesChange',
|
|
||||||
(change: SharedPreferencesChangeEvent) => {
|
|
||||||
this.dispatchAction({change, type: 'ChangeSharedPreferences'});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
onSharedPreferencesChanged = (path: Array<string>, value: any) => {
|
|
||||||
const selectedPreferences = this.state.selectedPreferences;
|
|
||||||
if (selectedPreferences == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const entry = this.state.sharedPreferences[selectedPreferences];
|
|
||||||
if (entry == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const values = entry.preferences;
|
|
||||||
let newValue = value;
|
|
||||||
if (path.length === 2 && values) {
|
|
||||||
newValue = clone(values[path[0]]);
|
|
||||||
newValue[path[1]] = value;
|
|
||||||
}
|
|
||||||
this.client
|
|
||||||
.call('setSharedPreference', {
|
|
||||||
sharedPreferencesName: this.state.selectedPreferences,
|
|
||||||
preferenceName: path[0],
|
|
||||||
preferenceValue: newValue,
|
|
||||||
})
|
|
||||||
.then((results: SharedPreferences) => {
|
|
||||||
const update = {
|
|
||||||
name: this.state.selectedPreferences,
|
|
||||||
preferences: results,
|
|
||||||
};
|
|
||||||
this.dispatchAction({update, type: 'UpdateSharedPreferences'});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onSharedPreferencesSelected = (selected: string) => {
|
|
||||||
this.dispatchAction({
|
|
||||||
selected: selected,
|
|
||||||
type: 'UpdateSelectedSharedPreferences',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onSharedPreferencesDeleted = (path: Array<string>) => {
|
|
||||||
this.client
|
|
||||||
.call('deleteSharedPreference', {
|
|
||||||
sharedPreferencesName: this.state.selectedPreferences,
|
|
||||||
preferenceName: path[0],
|
|
||||||
})
|
|
||||||
.then((results: SharedPreferences) => {
|
|
||||||
const update = {
|
|
||||||
name: this.state.selectedPreferences,
|
|
||||||
preferences: results,
|
|
||||||
};
|
|
||||||
this.dispatchAction({update, type: 'UpdateSharedPreferences'});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const selectedPreferences = this.state.selectedPreferences;
|
|
||||||
if (selectedPreferences == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const entry = this.state.sharedPreferences[selectedPreferences];
|
|
||||||
if (entry == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<RootColumn grow={true}>
|
|
||||||
<Heading>
|
|
||||||
<span style={{marginRight: '16px'}}>Preference File</span>
|
|
||||||
<Select
|
|
||||||
options={Object.keys(this.state.sharedPreferences)
|
|
||||||
.sort((a, b) => (a.toLowerCase() > b.toLowerCase() ? 1 : -1))
|
|
||||||
.reduce((obj, item) => {
|
|
||||||
obj[item] = item;
|
|
||||||
return obj;
|
|
||||||
}, {})}
|
|
||||||
selected={this.state.selectedPreferences}
|
|
||||||
onChange={this.onSharedPreferencesSelected}
|
|
||||||
/>
|
|
||||||
</Heading>
|
|
||||||
<FlexRow grow={true} scrollable={true}>
|
|
||||||
<InspectorColumn>
|
|
||||||
<Heading>Inspector</Heading>
|
|
||||||
<ManagedDataInspector
|
|
||||||
data={entry.preferences}
|
|
||||||
setValue={this.onSharedPreferencesChanged}
|
|
||||||
onDelete={this.onSharedPreferencesDeleted}
|
|
||||||
/>
|
|
||||||
</InspectorColumn>
|
|
||||||
<ChangelogColumn>
|
|
||||||
<Heading>Changelog</Heading>
|
|
||||||
<ManagedTable
|
|
||||||
columnSizes={CHANGELOG_COLUMN_SIZES}
|
|
||||||
columns={CHANGELOG_COLUMNS}
|
|
||||||
rowLineHeight={26}
|
|
||||||
rows={entry.changesList.map((element, index) => {
|
|
||||||
return {
|
|
||||||
columns: {
|
|
||||||
event: {
|
|
||||||
value: element.deleted ? DELETED_LABEL : UPDATED_LABEL,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
value: element.name,
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
value: String(element.value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
key: String(index),
|
|
||||||
};
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</ChangelogColumn>
|
|
||||||
</FlexRow>
|
|
||||||
</RootColumn>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"id": "Preferences",
|
"id": "Preferences",
|
||||||
"version": "0.63.0",
|
"version": "0.63.0",
|
||||||
"main": "dist/bundle.js",
|
"main": "dist/bundle.js",
|
||||||
"flipperBundlerEntry": "index.js",
|
"flipperBundlerEntry": "src/index.tsx",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"flipper-plugin"
|
"flipper-plugin"
|
||||||
@@ -12,6 +12,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash": "^4.17.19"
|
"lodash": "^4.17.19"
|
||||||
},
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"flipper-plugin": "0.63.0"
|
||||||
|
},
|
||||||
"title": "Shared Preferences Viewer",
|
"title": "Shared Preferences Viewer",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"email": "halsibai@fb.com"
|
"email": "halsibai@fb.com"
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {TestUtils} from 'flipper-plugin';
|
||||||
|
import * as plugin from '..';
|
||||||
|
|
||||||
|
async function sleep(ms: number) {
|
||||||
|
return new Promise((resolve) => setTimeout(() => resolve(), ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
// this testing is inspired by Flipper sample app
|
||||||
|
test('general plugin logic testing', async () => {
|
||||||
|
const {instance, onSend, connect, sendEvent} = TestUtils.startPlugin(plugin, {
|
||||||
|
startUnactivated: true,
|
||||||
|
});
|
||||||
|
onSend.mockImplementation(async (method, params) => {
|
||||||
|
switch (method) {
|
||||||
|
case 'getAllSharedPreferences':
|
||||||
|
return {
|
||||||
|
sample: {Hello: 'world'},
|
||||||
|
other_sample: {SomeKey: 1337},
|
||||||
|
};
|
||||||
|
case 'setSharedPreference': {
|
||||||
|
const p = params as plugin.SetSharedPreferenceParams;
|
||||||
|
return {[p.preferenceName]: p.preferenceValue};
|
||||||
|
}
|
||||||
|
case 'deleteSharedPreference': {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Retrieve some data when connect
|
||||||
|
connect();
|
||||||
|
await sleep(1000);
|
||||||
|
expect(onSend).toBeCalledWith('getAllSharedPreferences', {});
|
||||||
|
expect(instance.sharedPreferences.get()).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"other_sample": Object {
|
||||||
|
"changesList": Array [],
|
||||||
|
"preferences": Object {
|
||||||
|
"SomeKey": 1337,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sample": Object {
|
||||||
|
"changesList": Array [],
|
||||||
|
"preferences": Object {
|
||||||
|
"Hello": "world",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(instance.selectedPreferences.get()).toEqual('sample');
|
||||||
|
|
||||||
|
instance.setSelectedPreferences('other_sample');
|
||||||
|
expect(instance.selectedPreferences.get()).toEqual('other_sample');
|
||||||
|
|
||||||
|
// test changing preference
|
||||||
|
const changedPref = {
|
||||||
|
sharedPreferencesName: 'other_sample',
|
||||||
|
preferenceName: 'SomeKey',
|
||||||
|
preferenceValue: 5555,
|
||||||
|
};
|
||||||
|
await instance.setSharedPreference(changedPref);
|
||||||
|
// this is sent from client after successful update
|
||||||
|
sendEvent('sharedPreferencesChange', {
|
||||||
|
deleted: false,
|
||||||
|
name: 'SomeKey',
|
||||||
|
preferences: 'sample',
|
||||||
|
time: 1,
|
||||||
|
value: 5555,
|
||||||
|
});
|
||||||
|
expect(onSend).toBeCalledWith('setSharedPreference', changedPref);
|
||||||
|
expect(instance.sharedPreferences.get().sample.preferences.SomeKey).toEqual(
|
||||||
|
5555,
|
||||||
|
);
|
||||||
|
expect(instance.sharedPreferences.get()).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"other_sample": Object {
|
||||||
|
"changesList": Array [],
|
||||||
|
"preferences": Object {
|
||||||
|
"SomeKey": 5555,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sample": Object {
|
||||||
|
"changesList": Array [
|
||||||
|
Object {
|
||||||
|
"deleted": false,
|
||||||
|
"name": "SomeKey",
|
||||||
|
"preferences": "sample",
|
||||||
|
"time": 1,
|
||||||
|
"value": 5555,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"preferences": Object {
|
||||||
|
"Hello": "world",
|
||||||
|
"SomeKey": 5555,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
// test deleting preference
|
||||||
|
const deletedPref = {
|
||||||
|
sharedPreferencesName: 'other_sample',
|
||||||
|
preferenceName: 'SomeKey',
|
||||||
|
};
|
||||||
|
await instance.deleteSharedPreference(deletedPref);
|
||||||
|
// this is sent from client after successful update
|
||||||
|
sendEvent('sharedPreferencesChange', {
|
||||||
|
deleted: true,
|
||||||
|
name: 'SomeKey',
|
||||||
|
preferences: 'sample',
|
||||||
|
time: 2,
|
||||||
|
});
|
||||||
|
expect(onSend).toBeCalledWith('deleteSharedPreference', deletedPref);
|
||||||
|
expect(
|
||||||
|
instance.sharedPreferences.get().sample.preferences.SomeKey,
|
||||||
|
).toBeUndefined();
|
||||||
|
expect(instance.sharedPreferences.get()).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"other_sample": Object {
|
||||||
|
"changesList": Array [],
|
||||||
|
"preferences": Object {},
|
||||||
|
},
|
||||||
|
"sample": Object {
|
||||||
|
"changesList": Array [
|
||||||
|
Object {
|
||||||
|
"deleted": true,
|
||||||
|
"name": "SomeKey",
|
||||||
|
"preferences": "sample",
|
||||||
|
"time": 2,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"deleted": false,
|
||||||
|
"name": "SomeKey",
|
||||||
|
"preferences": "sample",
|
||||||
|
"time": 1,
|
||||||
|
"value": 5555,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"preferences": Object {
|
||||||
|
"Hello": "world",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Add unit test for UI
|
||||||
235
desktop/plugins/shared_preferences/src/index.tsx
Normal file
235
desktop/plugins/shared_preferences/src/index.tsx
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
ManagedTable,
|
||||||
|
Text,
|
||||||
|
Heading,
|
||||||
|
FlexColumn,
|
||||||
|
colors,
|
||||||
|
FlexRow,
|
||||||
|
ManagedDataInspector,
|
||||||
|
styled,
|
||||||
|
Select,
|
||||||
|
} from 'flipper';
|
||||||
|
import {PluginClient, createState, usePlugin, useValue} from 'flipper-plugin';
|
||||||
|
import {clone} from 'lodash';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
type SharedPreferencesChangeEvent = {
|
||||||
|
preferences: string;
|
||||||
|
name: string;
|
||||||
|
time: number;
|
||||||
|
deleted: boolean;
|
||||||
|
value?: any;
|
||||||
|
};
|
||||||
|
type SharedPreferences = Record<string, any>;
|
||||||
|
type SharedPreferencesEntry = {
|
||||||
|
preferences: SharedPreferences;
|
||||||
|
changesList: Array<SharedPreferencesChangeEvent>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SetSharedPreferenceParams = {
|
||||||
|
sharedPreferencesName: string;
|
||||||
|
preferenceName: string;
|
||||||
|
preferenceValue: any;
|
||||||
|
};
|
||||||
|
type DeleteSharedPreferenceParams = {
|
||||||
|
sharedPreferencesName: string;
|
||||||
|
preferenceName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Events = {sharedPreferencesChange: SharedPreferencesChangeEvent};
|
||||||
|
type Methods = {
|
||||||
|
getAllSharedPreferences: (params: {}) => Promise<
|
||||||
|
Record<string, SharedPreferences>
|
||||||
|
>;
|
||||||
|
setSharedPreference: (
|
||||||
|
params: SetSharedPreferenceParams,
|
||||||
|
) => Promise<SharedPreferences>;
|
||||||
|
deleteSharedPreference: (
|
||||||
|
params: DeleteSharedPreferenceParams,
|
||||||
|
) => Promise<SharedPreferences>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function plugin(client: PluginClient<Events, Methods>) {
|
||||||
|
const selectedPreferences = createState<string | null>(null, {
|
||||||
|
persist: 'selectedPreferences',
|
||||||
|
});
|
||||||
|
const setSelectedPreferences = (value: string) =>
|
||||||
|
selectedPreferences.set(value);
|
||||||
|
const sharedPreferences = createState<Record<string, SharedPreferencesEntry>>(
|
||||||
|
{},
|
||||||
|
{persist: 'sharedPreferences'},
|
||||||
|
);
|
||||||
|
|
||||||
|
function updateSharedPreferences(update: {name: string; preferences: any}) {
|
||||||
|
if (selectedPreferences.get() == null) {
|
||||||
|
selectedPreferences.set(update.name);
|
||||||
|
}
|
||||||
|
sharedPreferences.update((draft) => {
|
||||||
|
const entry = draft[update.name] || {changesList: []};
|
||||||
|
entry.preferences = update.preferences;
|
||||||
|
draft[update.name] = entry;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setSharedPreference(params: SetSharedPreferenceParams) {
|
||||||
|
const results = await client.send('setSharedPreference', params);
|
||||||
|
updateSharedPreferences({
|
||||||
|
name: params.sharedPreferencesName,
|
||||||
|
preferences: results,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function deleteSharedPreference(params: DeleteSharedPreferenceParams) {
|
||||||
|
const results = await client.send('deleteSharedPreference', params);
|
||||||
|
updateSharedPreferences({
|
||||||
|
name: params.sharedPreferencesName,
|
||||||
|
preferences: results,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
client.onMessage('sharedPreferencesChange', (change) =>
|
||||||
|
sharedPreferences.update((draft) => {
|
||||||
|
const entry = draft[change.preferences];
|
||||||
|
if (entry == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (change.deleted) {
|
||||||
|
delete entry.preferences[change.name];
|
||||||
|
} else {
|
||||||
|
entry.preferences[change.name] = change.value;
|
||||||
|
}
|
||||||
|
entry.changesList.unshift(change);
|
||||||
|
draft[change.preferences] = entry;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
client.onConnect(async () => {
|
||||||
|
const results = await client.send('getAllSharedPreferences', {});
|
||||||
|
Object.entries(results).forEach(([name, prefs]) =>
|
||||||
|
updateSharedPreferences({name: name, preferences: prefs}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectedPreferences,
|
||||||
|
sharedPreferences,
|
||||||
|
setSelectedPreferences,
|
||||||
|
setSharedPreference,
|
||||||
|
deleteSharedPreference,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const CHANGELOG_COLUMNS = {
|
||||||
|
event: {value: 'Event'},
|
||||||
|
name: {value: 'Name'},
|
||||||
|
value: {value: 'Value'},
|
||||||
|
};
|
||||||
|
const CHANGELOG_COLUMN_SIZES = {
|
||||||
|
event: '30%',
|
||||||
|
name: '30%',
|
||||||
|
value: '30%',
|
||||||
|
};
|
||||||
|
|
||||||
|
const UPDATED_LABEL = <Text color={colors.lime}>Updated</Text>;
|
||||||
|
const DELETED_LABEL = <Text color={colors.cherry}>Deleted</Text>;
|
||||||
|
|
||||||
|
const InspectorColumn = styled(FlexColumn)({flexGrow: 0.2});
|
||||||
|
const ChangelogColumn = styled(FlexColumn)({
|
||||||
|
flexGrow: 0.8,
|
||||||
|
paddingLeft: '16px',
|
||||||
|
});
|
||||||
|
const RootColumn = styled(FlexColumn)({
|
||||||
|
paddingLeft: '16px',
|
||||||
|
paddingRight: '16px',
|
||||||
|
paddingTop: '16px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export function Component() {
|
||||||
|
const instance = usePlugin(plugin);
|
||||||
|
const selectedPreferences = useValue(instance.selectedPreferences);
|
||||||
|
const sharedPreferences = useValue(instance.sharedPreferences);
|
||||||
|
|
||||||
|
if (selectedPreferences == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const entry = sharedPreferences[selectedPreferences];
|
||||||
|
if (entry == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RootColumn grow={true}>
|
||||||
|
<Heading>
|
||||||
|
<span style={{marginRight: '16px'}}>Preference File</span>
|
||||||
|
<Select
|
||||||
|
options={Object.keys(sharedPreferences)
|
||||||
|
.sort((a, b) => (a.toLowerCase() > b.toLowerCase() ? 1 : -1))
|
||||||
|
.reduce((obj, item) => {
|
||||||
|
obj[item] = item;
|
||||||
|
return obj;
|
||||||
|
}, {} as Record<string, string>)}
|
||||||
|
selected={selectedPreferences}
|
||||||
|
onChange={instance.setSelectedPreferences}
|
||||||
|
/>
|
||||||
|
</Heading>
|
||||||
|
<FlexRow grow={true} scrollable={true}>
|
||||||
|
<InspectorColumn>
|
||||||
|
<Heading>Inspector</Heading>
|
||||||
|
<ManagedDataInspector
|
||||||
|
data={entry.preferences}
|
||||||
|
setValue={async (path: Array<string>, value: any) => {
|
||||||
|
if (entry == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const values = entry.preferences;
|
||||||
|
let newValue = value;
|
||||||
|
if (path.length === 2 && values) {
|
||||||
|
newValue = clone(values[path[0]]);
|
||||||
|
newValue[path[1]] = value;
|
||||||
|
}
|
||||||
|
await instance.setSharedPreference({
|
||||||
|
sharedPreferencesName: selectedPreferences,
|
||||||
|
preferenceName: path[0],
|
||||||
|
preferenceValue: newValue,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onDelete={async (path: Array<string>) =>
|
||||||
|
await instance.deleteSharedPreference({
|
||||||
|
sharedPreferencesName: selectedPreferences,
|
||||||
|
preferenceName: path[0],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</InspectorColumn>
|
||||||
|
<ChangelogColumn>
|
||||||
|
<Heading>Changelog</Heading>
|
||||||
|
<ManagedTable
|
||||||
|
columnSizes={CHANGELOG_COLUMN_SIZES}
|
||||||
|
columns={CHANGELOG_COLUMNS}
|
||||||
|
rowLineHeight={26}
|
||||||
|
rows={entry.changesList.map((element, index) => {
|
||||||
|
return {
|
||||||
|
columns: {
|
||||||
|
event: {
|
||||||
|
value: element.deleted ? DELETED_LABEL : UPDATED_LABEL,
|
||||||
|
},
|
||||||
|
name: {value: element.name},
|
||||||
|
value: {value: String(element.value)},
|
||||||
|
},
|
||||||
|
key: String(index),
|
||||||
|
};
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</ChangelogColumn>
|
||||||
|
</FlexRow>
|
||||||
|
</RootColumn>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user