Add update indicators to PluginInstaller

Summary:
Display is functional, the update itself isn't just yet.
Want to keep this easier to review. Instead of GK, I just
have a top-level toggle for now, because that will go
away with one of the next diffs anyway.

Reviewed By: jknoxville

Differential Revision: D18479290

fbshipit-source-id: b49394d4ab681c9d1dc5db0e4bee54f9255494b9
This commit is contained in:
Pascal Hartig
2019-11-14 08:51:33 -08:00
committed by Facebook Github Bot
parent 7a148ef7a6
commit 144338e74a
2 changed files with 135 additions and 12 deletions

View File

@@ -42,12 +42,15 @@ import {
readInstalledPlugins,
providePluginManager,
provideSearchIndex,
findPluginUpdates,
UpdateResult,
} from '../utils/pluginManager';
import {State as AppState} from '../reducers';
import {connect} from 'react-redux';
import {Dispatch, Action} from 'redux';
const TAG = 'PluginInstaller';
const ENABLE_PLUGIN_UPDATES = false;
const EllipsisText = styled(Text)({
overflow: 'hidden',
@@ -114,6 +117,26 @@ const defaultProps: OwnProps = {
autoHeight: false,
};
type UpdatablePlugin = {
updateStatus: UpdateResult;
};
type UpdatablePluginDefinition = PluginDefinition & UpdatablePlugin;
// exported for testing
export function annotatePluginsWithUpdates(
installedPlugins: Map<string, PluginDefinition>,
updates: Map<string, UpdateResult>,
): Map<string, UpdatablePluginDefinition> {
const annotated: Array<[string, UpdatablePluginDefinition]> = Array.from(
installedPlugins.entries(),
).map(([key, value]) => {
const updateStatus = updates.get(key) || {kind: 'up-to-date'};
return [key, {...value, updateStatus: updateStatus}];
});
return new Map(annotated);
}
const PluginInstaller = function props(props: Props) {
const [restartRequired, setRestartRequired] = useState(false);
const [query, setQuery] = useState('');
@@ -174,16 +197,25 @@ const AlignedGlyph = styled(Glyph)({
marginTop: 6,
});
function liftUpdatable(val: PluginDefinition): UpdatablePluginDefinition {
return {
...val,
updateStatus: {kind: 'up-to-date'},
};
}
function InstallButton(props: {
name: string;
version: string;
onInstall: () => void;
installed: boolean;
updateStatus: UpdateResult;
}) {
type InstallAction =
| {kind: 'Install'; error?: string}
| {kind: 'Waiting'}
| {kind: 'Remove'; error?: string};
| {kind: 'Remove'; error?: string}
| {kind: 'Update'; error?: string};
const catchError = (
actionKind: 'Install' | 'Remove',
@@ -245,7 +277,11 @@ function InstallButton(props: {
);
const [action, setAction] = useState<InstallAction>(
props.installed ? {kind: 'Remove'} : {kind: 'Install'},
props.updateStatus.kind === 'update-available'
? {kind: 'Update'}
: props.installed
? {kind: 'Remove'}
: {kind: 'Install'},
);
if (action.kind === 'Waiting') {
@@ -256,12 +292,20 @@ function InstallButton(props: {
const button = (
<TableButton
compact
type={action.kind === 'Install' ? 'primary' : undefined}
onClick={
action.kind === 'Install'
? () => reportPlatformFailures(performInstall(), `${TAG}:install`)
: () => reportPlatformFailures(performRemove(), `${TAG}:remove`)
}>
type={action.kind !== 'Remove' ? 'primary' : undefined}
onClick={() => {
switch (action.kind) {
case 'Install':
reportPlatformFailures(performInstall(), `${TAG}:install`);
break;
case 'Remove':
reportPlatformFailures(performRemove(), `${TAG}:remove`);
break;
case 'Update':
alert('Not implemented yet.');
break;
}
}}>
{action.kind}
</TableButton>
);
@@ -305,7 +349,7 @@ function useNPMSearch(
}, []);
const createRow = useCallback(
(h: PluginDefinition) => ({
(h: UpdatablePluginDefinition) => ({
key: h.name,
columns: {
name: {value: <EllipsisText>{h.name}</EllipsisText>},
@@ -331,6 +375,7 @@ function useNPMSearch(
version={h.version}
onInstall={onInstall}
installed={installedPlugins.has(h.name)}
updateStatus={h.updateStatus}
/>
),
align: 'center' as 'center',
@@ -340,7 +385,13 @@ function useNPMSearch(
[installedPlugins],
);
const [searchResults, setSearchResults] = useState<PluginDefinition[]>([]);
const [searchResults, setSearchResults] = useState<
UpdatablePluginDefinition[]
>([]);
const [
updateAnnotatedInstalledPlugins,
setUpdateAnnotatedInstalledPlugins,
] = useState<Map<string, UpdatablePluginDefinition>>(new Map());
useEffect(() => {
(async () => {
@@ -353,12 +404,27 @@ function useNPMSearch(
`${TAG}:queryIndex`,
);
setSearchResults(hits.filter(hit => !installedPlugins.has(hit.name)));
setSearchResults(
hits.filter(hit => !installedPlugins.has(hit.name)).map(liftUpdatable),
);
setQuery(query);
})();
}, [query, installedPlugins]);
const results = Array.from(installedPlugins.values()).concat(searchResults);
useEffect(() => {
(async () => {
const updates = ENABLE_PLUGIN_UPDATES
? new Map(await findPluginUpdates(installedPlugins))
: new Map();
setUpdateAnnotatedInstalledPlugins(
annotatePluginsWithUpdates(installedPlugins, updates),
);
})();
}, [installedPlugins]);
const results = Array.from(updateAnnotatedInstalledPlugins.values()).concat(
searchResults,
);
return List(results.map(createRow));
}

View File

@@ -0,0 +1,57 @@
/**
* 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 {annotatePluginsWithUpdates} from '../PluginInstaller';
import {UpdateResult} from '../../utils/pluginManager';
test('annotatePluginsWithUpdates', async () => {
const installedPlugins = new Map([
[
'example',
{
name: 'example',
version: '0.1.0',
description: 'Gaze into the death crystal',
},
],
[
'ricksybusiness',
{
name: 'ricksybusiness',
version: '1.0.0',
description: 'Rick Die Rickpeat',
},
],
]);
const updates = new Map<string, UpdateResult>([
['example', {kind: 'update-available', version: '1.1.0'}],
]);
const res = annotatePluginsWithUpdates(installedPlugins, updates);
expect(res).toMatchInlineSnapshot(`
Map {
"example" => Object {
"description": "Gaze into the death crystal",
"name": "example",
"updateStatus": Object {
"kind": "update-available",
"version": "1.1.0",
},
"version": "0.1.0",
},
"ricksybusiness" => Object {
"description": "Rick Die Rickpeat",
"name": "ricksybusiness",
"updateStatus": Object {
"kind": "up-to-date",
},
"version": "1.0.0",
},
}
`);
});