1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-02 22:49:09 +02:00

Mobile: Implement plugin screen redesign (#10465)

This commit is contained in:
Henry Heino
2024-06-04 01:57:52 -07:00
committed by GitHub
parent 19f0b667b1
commit 06f42e8246
39 changed files with 1460 additions and 633 deletions

View File

@@ -4,21 +4,32 @@ import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
import { _ } from '@joplin/lib/locale';
import { PluginManifest } from '@joplin/lib/services/plugins/utils/types';
import { useCallback, useMemo, useState } from 'react';
import { FlatList, View } from 'react-native';
import { Searchbar } from 'react-native-paper';
import { FlatList, StyleSheet, View } from 'react-native';
import { TextInput, Text } from 'react-native-paper';
import PluginBox, { InstallState } from './PluginBox';
import PluginService, { PluginSettings, SerializedPluginSettings } from '@joplin/lib/services/plugins/PluginService';
import useInstallHandler from '@joplin/lib/components/shared/config/plugins/useOnInstallHandler';
import { OnPluginSettingChangeEvent, PluginItem } from '@joplin/lib/components/shared/config/plugins/types';
import PluginService, { PluginSettings } from '@joplin/lib/services/plugins/PluginService';
import { PluginItem } from '@joplin/lib/components/shared/config/plugins/types';
import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
import openWebsiteForPlugin from './utils/openWebsiteForPlugin';
import { PluginCallback, PluginCallbacks } from './utils/usePluginCallbacks';
import InstalledPluginBox from './InstalledPluginBox';
interface Props {
themeId: number;
pluginSettings: SerializedPluginSettings;
pluginSettings: PluginSettings;
repoApiInitialized: boolean;
onUpdatePluginStates: (states: PluginSettings)=> void;
repoApi: RepositoryApi;
installingPluginIds: Record<string, boolean>;
updatingPluginIds: Record<string, boolean>;
updatablePluginIds: Record<string, boolean>;
callbacks: PluginCallbacks;
onShowPluginInfo: PluginCallback;
searchQuery: string;
setSearchQuery: (newQuery: string)=> void;
}
interface SearchResultRecord {
@@ -27,8 +38,20 @@ interface SearchResultRecord {
installState: InstallState;
}
const styles = StyleSheet.create({
container: {
flexDirection: 'column',
margin: 12,
},
resultsCounter: {
margin: 12,
marginTop: 17,
marginBottom: 4,
},
});
const PluginSearch: React.FC<Props> = props => {
const [searchQuery, setSearchQuery] = useState('');
const { searchQuery, setSearchQuery } = props;
const [searchResultManifests, setSearchResultManifests] = useState<PluginManifest[]>([]);
useAsyncEffect(async event => {
@@ -42,8 +65,6 @@ const PluginSearch: React.FC<Props> = props => {
}
}, [searchQuery, props.repoApi, setSearchResultManifests, props.repoApiInitialized]);
const [installingPluginsIds, setInstallingPluginIds] = useState<Record<string, boolean>>({});
const pluginSettings = useMemo(() => {
return { ...PluginService.instance().unserializePluginSettings(props.pluginSettings) };
}, [props.pluginSettings]);
@@ -56,12 +77,13 @@ const PluginSearch: React.FC<Props> = props => {
if (settings && !settings.deleted) {
installState = InstallState.Installed;
}
if (installingPluginsIds[manifest.id]) {
if (props.installingPluginIds[manifest.id]) {
installState = InstallState.Installing;
}
const item: PluginItem = {
manifest,
installed: !!settings,
enabled: settings && settings.enabled,
deleted: settings && !settings.deleted,
devMode: false,
@@ -75,41 +97,62 @@ const PluginSearch: React.FC<Props> = props => {
installState,
};
});
}, [searchResultManifests, installingPluginsIds, pluginSettings]);
}, [searchResultManifests, props.installingPluginIds, pluginSettings]);
const onPluginSettingsChange = useCallback((event: OnPluginSettingChangeEvent) => {
props.onUpdatePluginStates(event.value);
}, [props.onUpdatePluginStates]);
const installPlugin = useInstallHandler(
setInstallingPluginIds, pluginSettings, props.repoApi, onPluginSettingsChange, false,
);
const onInstall = props.callbacks.onInstall;
const renderResult = useCallback(({ item }: { item: SearchResultRecord }) => {
const manifest = item.item.manifest;
return (
<PluginBox
themeId={props.themeId}
key={manifest.id}
item={item.item}
installState={item.installState}
isCompatible={PluginService.instance().isCompatible(manifest)}
onInstall={installPlugin}
onAboutPress={openWebsiteForPlugin}
/>
);
}, [installPlugin, props.themeId]);
if (item.installState === InstallState.Installed && PluginService.instance().isPluginLoaded(manifest.id)) {
return (
<InstalledPluginBox
pluginId={manifest.id}
themeId={props.themeId}
pluginSettings={props.pluginSettings}
updatablePluginIds={props.updatablePluginIds}
updatingPluginIds={props.updatingPluginIds}
showInstalledChip={true}
callbacks={props.callbacks}
onShowPluginInfo={props.onShowPluginInfo}
/>
);
} else {
return (
<PluginBox
themeId={props.themeId}
key={manifest.id}
item={item.item}
installState={item.installState}
showInstalledChip={false}
isCompatible={PluginService.instance().isCompatible(manifest)}
onInstall={onInstall}
onAboutPress={openWebsiteForPlugin}
/>
);
}
}, [onInstall, props.themeId, props.pluginSettings, props.updatingPluginIds, props.updatablePluginIds, props.onShowPluginInfo, props.callbacks]);
const renderResultsCount = () => {
if (!searchQuery.length) return null;
return <Text style={styles.resultsCounter} variant='labelLarge'>
{_('Results (%d):', searchResults.length)}
</Text>;
};
return (
<View style={{ flexDirection: 'column' }}>
<Searchbar
<View style={styles.container}>
<TextInput
testID='searchbar'
placeholder={_('Search')}
mode='outlined'
left={<TextInput.Icon icon='magnify' />}
placeholder={_('Search plugins')}
onChangeText={setSearchQuery}
value={searchQuery}
editable={props.repoApiInitialized}
/>
{renderResultsCount()}
<FlatList
data={searchResults}
renderItem={renderResult}