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:
committed by
Facebook Github Bot
parent
7a148ef7a6
commit
144338e74a
@@ -42,12 +42,15 @@ import {
|
|||||||
readInstalledPlugins,
|
readInstalledPlugins,
|
||||||
providePluginManager,
|
providePluginManager,
|
||||||
provideSearchIndex,
|
provideSearchIndex,
|
||||||
|
findPluginUpdates,
|
||||||
|
UpdateResult,
|
||||||
} from '../utils/pluginManager';
|
} from '../utils/pluginManager';
|
||||||
import {State as AppState} from '../reducers';
|
import {State as AppState} from '../reducers';
|
||||||
import {connect} from 'react-redux';
|
import {connect} from 'react-redux';
|
||||||
import {Dispatch, Action} from 'redux';
|
import {Dispatch, Action} from 'redux';
|
||||||
|
|
||||||
const TAG = 'PluginInstaller';
|
const TAG = 'PluginInstaller';
|
||||||
|
const ENABLE_PLUGIN_UPDATES = false;
|
||||||
|
|
||||||
const EllipsisText = styled(Text)({
|
const EllipsisText = styled(Text)({
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
@@ -114,6 +117,26 @@ const defaultProps: OwnProps = {
|
|||||||
autoHeight: false,
|
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 PluginInstaller = function props(props: Props) {
|
||||||
const [restartRequired, setRestartRequired] = useState(false);
|
const [restartRequired, setRestartRequired] = useState(false);
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
@@ -174,16 +197,25 @@ const AlignedGlyph = styled(Glyph)({
|
|||||||
marginTop: 6,
|
marginTop: 6,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function liftUpdatable(val: PluginDefinition): UpdatablePluginDefinition {
|
||||||
|
return {
|
||||||
|
...val,
|
||||||
|
updateStatus: {kind: 'up-to-date'},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function InstallButton(props: {
|
function InstallButton(props: {
|
||||||
name: string;
|
name: string;
|
||||||
version: string;
|
version: string;
|
||||||
onInstall: () => void;
|
onInstall: () => void;
|
||||||
installed: boolean;
|
installed: boolean;
|
||||||
|
updateStatus: UpdateResult;
|
||||||
}) {
|
}) {
|
||||||
type InstallAction =
|
type InstallAction =
|
||||||
| {kind: 'Install'; error?: string}
|
| {kind: 'Install'; error?: string}
|
||||||
| {kind: 'Waiting'}
|
| {kind: 'Waiting'}
|
||||||
| {kind: 'Remove'; error?: string};
|
| {kind: 'Remove'; error?: string}
|
||||||
|
| {kind: 'Update'; error?: string};
|
||||||
|
|
||||||
const catchError = (
|
const catchError = (
|
||||||
actionKind: 'Install' | 'Remove',
|
actionKind: 'Install' | 'Remove',
|
||||||
@@ -245,7 +277,11 @@ function InstallButton(props: {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const [action, setAction] = useState<InstallAction>(
|
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') {
|
if (action.kind === 'Waiting') {
|
||||||
@@ -256,12 +292,20 @@ function InstallButton(props: {
|
|||||||
const button = (
|
const button = (
|
||||||
<TableButton
|
<TableButton
|
||||||
compact
|
compact
|
||||||
type={action.kind === 'Install' ? 'primary' : undefined}
|
type={action.kind !== 'Remove' ? 'primary' : undefined}
|
||||||
onClick={
|
onClick={() => {
|
||||||
action.kind === 'Install'
|
switch (action.kind) {
|
||||||
? () => reportPlatformFailures(performInstall(), `${TAG}:install`)
|
case 'Install':
|
||||||
: () => reportPlatformFailures(performRemove(), `${TAG}:remove`)
|
reportPlatformFailures(performInstall(), `${TAG}:install`);
|
||||||
}>
|
break;
|
||||||
|
case 'Remove':
|
||||||
|
reportPlatformFailures(performRemove(), `${TAG}:remove`);
|
||||||
|
break;
|
||||||
|
case 'Update':
|
||||||
|
alert('Not implemented yet.');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}}>
|
||||||
{action.kind}
|
{action.kind}
|
||||||
</TableButton>
|
</TableButton>
|
||||||
);
|
);
|
||||||
@@ -305,7 +349,7 @@ function useNPMSearch(
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const createRow = useCallback(
|
const createRow = useCallback(
|
||||||
(h: PluginDefinition) => ({
|
(h: UpdatablePluginDefinition) => ({
|
||||||
key: h.name,
|
key: h.name,
|
||||||
columns: {
|
columns: {
|
||||||
name: {value: <EllipsisText>{h.name}</EllipsisText>},
|
name: {value: <EllipsisText>{h.name}</EllipsisText>},
|
||||||
@@ -331,6 +375,7 @@ function useNPMSearch(
|
|||||||
version={h.version}
|
version={h.version}
|
||||||
onInstall={onInstall}
|
onInstall={onInstall}
|
||||||
installed={installedPlugins.has(h.name)}
|
installed={installedPlugins.has(h.name)}
|
||||||
|
updateStatus={h.updateStatus}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
align: 'center' as 'center',
|
align: 'center' as 'center',
|
||||||
@@ -340,7 +385,13 @@ function useNPMSearch(
|
|||||||
[installedPlugins],
|
[installedPlugins],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [searchResults, setSearchResults] = useState<PluginDefinition[]>([]);
|
const [searchResults, setSearchResults] = useState<
|
||||||
|
UpdatablePluginDefinition[]
|
||||||
|
>([]);
|
||||||
|
const [
|
||||||
|
updateAnnotatedInstalledPlugins,
|
||||||
|
setUpdateAnnotatedInstalledPlugins,
|
||||||
|
] = useState<Map<string, UpdatablePluginDefinition>>(new Map());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
@@ -353,12 +404,27 @@ function useNPMSearch(
|
|||||||
`${TAG}:queryIndex`,
|
`${TAG}:queryIndex`,
|
||||||
);
|
);
|
||||||
|
|
||||||
setSearchResults(hits.filter(hit => !installedPlugins.has(hit.name)));
|
setSearchResults(
|
||||||
|
hits.filter(hit => !installedPlugins.has(hit.name)).map(liftUpdatable),
|
||||||
|
);
|
||||||
setQuery(query);
|
setQuery(query);
|
||||||
})();
|
})();
|
||||||
}, [query, installedPlugins]);
|
}, [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));
|
return List(results.map(createRow));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
57
src/chrome/__tests__/PluginInstaller.node.tsx
Normal file
57
src/chrome/__tests__/PluginInstaller.node.tsx
Normal 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",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user