Mobile: Plugin settings: Fix plugins without settings can't be disabled without reinstall (#10579)

Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
pull/10598/head
Henry Heino 2024-06-14 11:36:26 -07:00 committed by GitHub
parent 9e2b9e5b8d
commit ce22d8238c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 51 additions and 12 deletions

View File

@ -52,6 +52,9 @@ const showInstalledTab = async () => {
await userEvent.press(installedTab); await userEvent.press(installedTab);
}; };
const abcPluginId = 'org.joplinapp.plugins.AbcSheetMusic';
const backlinksPluginId = 'joplin.plugin.ambrt.backlinksToNote';
describe('PluginStates.installed', () => { describe('PluginStates.installed', () => {
beforeEach(async () => { beforeEach(async () => {
await setupDatabaseAndSynchronizer(0); await setupDatabaseAndSynchronizer(0);
@ -77,9 +80,6 @@ describe('PluginStates.installed', () => {
expect(shim.mobilePlatform()).toBe(platform); expect(shim.mobilePlatform()).toBe(platform);
await mockRepositoryApiConstructor(); await mockRepositoryApiConstructor();
const abcPluginId = 'org.joplinapp.plugins.AbcSheetMusic';
const backlinksPluginId = 'joplin.plugin.ambrt.backlinksToNote';
const defaultPluginSettings: PluginSettings = { const defaultPluginSettings: PluginSettings = {
[abcPluginId]: defaultPluginSetting(), [abcPluginId]: defaultPluginSetting(),
[backlinksPluginId]: defaultPluginSetting(), [backlinksPluginId]: defaultPluginSetting(),
@ -117,7 +117,6 @@ describe('PluginStates.installed', () => {
}); });
it('should show the current plugin version on updatable plugins', async () => { it('should show the current plugin version on updatable plugins', async () => {
const abcPluginId = 'org.joplinapp.plugins.AbcSheetMusic';
const defaultPluginSettings: PluginSettings = { [abcPluginId]: defaultPluginSetting() }; const defaultPluginSettings: PluginSettings = { [abcPluginId]: defaultPluginSetting() };
const outdatedVersion = '0.0.1'; const outdatedVersion = '0.0.1';
@ -173,7 +172,6 @@ describe('PluginStates.installed', () => {
}); });
it('should support disabling plugins from the info modal', async () => { it('should support disabling plugins from the info modal', async () => {
const abcPluginId = 'org.joplinapp.plugins.AbcSheetMusic';
const defaultPluginSettings: PluginSettings = { [abcPluginId]: defaultPluginSetting() }; const defaultPluginSettings: PluginSettings = { [abcPluginId]: defaultPluginSetting() };
await loadMockPlugin(abcPluginId, 'ABC Sheet Music', '1.2.3', defaultPluginSettings); await loadMockPlugin(abcPluginId, 'ABC Sheet Music', '1.2.3', defaultPluginSettings);
@ -213,8 +211,6 @@ describe('PluginStates.installed', () => {
it('should support updating plugins from the info modal', async () => { it('should support updating plugins from the info modal', async () => {
await mockRepositoryApiConstructor(); await mockRepositoryApiConstructor();
const abcPluginId = 'org.joplinapp.plugins.AbcSheetMusic';
const defaultPluginSettings: PluginSettings = { const defaultPluginSettings: PluginSettings = {
[abcPluginId]: defaultPluginSetting(), [abcPluginId]: defaultPluginSetting(),
}; };
@ -268,4 +264,41 @@ describe('PluginStates.installed', () => {
wrapper.unmount(); wrapper.unmount();
}); });
it('should be possible to disable plugins, even if missing from plugins.states', async () => {
await mockRepositoryApiConstructor();
const defaultPluginSettings: PluginSettings = {};
await loadMockPlugin(abcPluginId, 'ABC Sheet Music', '3.4.5', defaultPluginSettings);
expect(PluginService.instance().plugins[abcPluginId]).toBeTruthy();
const wrapper = render(
<WrappedPluginStates
initialPluginSettings={defaultPluginSettings}
store={reduxStore}
/>,
);
await showInstalledTab();
// Should be shown as installed.
const card = await screen.findByText('ABC Sheet Music');
expect(card).toBeVisible();
const user = userEvent.setup();
await user.press(card);
// Should be considered installed -- should be possible to disable:
const enabledSwitch = await screen.findByLabelText('Enabled');
expect(enabledSwitch).toBeVisible();
fireEvent(enabledSwitch, 'valueChange', false);
// Disabling should add the plugin to plugins.states, if not present before.
await waitFor(() => {
expect(Setting.value('plugins.states')).toMatchObject({
[abcPluginId]: { enabled: false },
});
});
wrapper.unmount();
});
}); });

View File

@ -2,7 +2,7 @@ import { ItemEvent, OnPluginSettingChangeEvent } from '@joplin/lib/components/sh
import useOnDeleteHandler from '@joplin/lib/components/shared/config/plugins/useOnDeleteHandler'; import useOnDeleteHandler from '@joplin/lib/components/shared/config/plugins/useOnDeleteHandler';
import useOnInstallHandler from '@joplin/lib/components/shared/config/plugins/useOnInstallHandler'; import useOnInstallHandler from '@joplin/lib/components/shared/config/plugins/useOnInstallHandler';
import NavService from '@joplin/lib/services/NavService'; import NavService from '@joplin/lib/services/NavService';
import { PluginSettings } from '@joplin/lib/services/plugins/PluginService'; import { PluginSettings, defaultPluginSetting } from '@joplin/lib/services/plugins/PluginService';
import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi'; import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
@ -29,14 +29,18 @@ const usePluginCallbacks = (props: Props) => {
const updatePluginEnabled = useCallback((pluginId: string, enabled: boolean) => { const updatePluginEnabled = useCallback((pluginId: string, enabled: boolean) => {
const newSettings = { ...props.pluginSettings }; const newSettings = { ...props.pluginSettings };
newSettings[pluginId].enabled = enabled; newSettings[pluginId] = {
...defaultPluginSetting(),
...newSettings[pluginId],
enabled,
};
props.updatePluginStates(newSettings); props.updatePluginStates(newSettings);
}, [props.pluginSettings, props.updatePluginStates]); }, [props.pluginSettings, props.updatePluginStates]);
const onToggle = useCallback((event: ItemEvent) => { const onToggle = useCallback((event: ItemEvent) => {
const pluginId = event.item.manifest.id; const pluginId = event.item.manifest.id;
const settings = props.pluginSettings[pluginId]; const settings = props.pluginSettings[pluginId] ?? defaultPluginSetting();
updatePluginEnabled(pluginId, !settings.enabled); updatePluginEnabled(pluginId, !settings.enabled);
}, [props.pluginSettings, updatePluginEnabled]); }, [props.pluginSettings, updatePluginEnabled]);

View File

@ -21,12 +21,14 @@ const usePluginItem = (id: string, pluginSettings: PluginSettings, initialItem:
if (!manifest) return null; if (!manifest) return null;
const settings = pluginSettings[id]; const settings = pluginSettings[id];
const installed = !!settings || !!plugin;
return { return {
id, id,
manifest, manifest,
installed: !!settings, installed,
enabled: settings?.enabled ?? false, enabled: settings?.enabled ?? installed,
deleted: settings?.deleted ?? false, deleted: settings?.deleted ?? false,
hasBeenUpdated: settings?.hasBeenUpdated ?? false, hasBeenUpdated: settings?.hasBeenUpdated ?? false,
devMode: plugin?.devMode ?? false, devMode: plugin?.devMode ?? false,