feat: Support Additional Plugin Setting Types (Dropdown and Checkboxes)

This commit is contained in:
Rajarshee Chatterjee
2026-01-28 22:23:00 +05:30
parent c40edd5b2c
commit b53ec3352f
2 changed files with 167 additions and 16 deletions
+47 -1
View File
@@ -1,5 +1,51 @@
import { FilterToValues, Filters } from './filterTypes';
export interface TextSetting {
value: string;
label: string;
type?: 'Text';
}
export interface SwitchSetting {
value: boolean;
label: string;
type: 'Switch';
}
export interface SelectOption {
label: string;
value: string;
}
export interface SelectSetting {
value: string;
label: string;
type: 'Select';
options: SelectOption[];
}
export interface CheckboxOption {
label: string;
value: string;
}
export interface CheckboxGroupSetting {
value: string[];
label: string;
type: 'CheckboxGroup';
options: CheckboxOption[];
}
export type PluginSetting =
| TextSetting
| SwitchSetting
| SelectSetting
| CheckboxGroupSetting;
export interface PluginSettings {
[key: string]: PluginSetting;
}
export interface NovelItem {
id: undefined;
name: string;
@@ -69,7 +115,7 @@ export interface ImageRequestInit {
export interface Plugin extends PluginItem {
imageRequestInit: ImageRequestInit;
filters?: Filters;
pluginSettings: any;
pluginSettings?: PluginSettings;
popularNovels: (
pageNo: number,
options?: PopularNovelsOptions<Filters>,
@@ -1,20 +1,12 @@
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { TextInput } from 'react-native-paper';
import { Button, Modal, SwitchItem } from '@components/index';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import { TextInput, Menu, overlay } from 'react-native-paper';
import { Button, Modal, SwitchItem, Checkbox } from '@components/index';
import { useTheme } from '@hooks/persisted';
import { getString } from '@strings/translations';
import { Storage } from '@plugins/helpers/storage';
interface PluginSetting {
value: string;
label: string;
type?: 'Switch';
}
interface PluginSettings {
[key: string]: PluginSetting;
}
import MaterialCommunityIcons from '@react-native-vector-icons/material-design-icons';
import { PluginSettings } from '@plugins/types';
interface SourceSettingsModal {
visible: boolean;
@@ -36,8 +28,9 @@ const SourceSettingsModal: React.FC<SourceSettingsModal> = ({
const theme = useTheme();
const [formValues, setFormValues] = useState<
Record<string, string | boolean>
Record<string, string | boolean | string[]>
>({});
const [openMenus, setOpenMenus] = useState<Record<string, boolean>>({});
useEffect(() => {
if (pluginSettings) {
@@ -66,13 +59,18 @@ const SourceSettingsModal: React.FC<SourceSettingsModal> = ({
}
}, [pluginSettings, pluginId]);
const handleChange = (key: string, value: string | boolean) => {
const handleChange = (key: string, value: string | boolean | string[]) => {
setFormValues(prevValues => ({
...prevValues,
[key]: value,
}));
};
const insertOrRemoveIntoArray = (array: string[], val: string): string[] =>
array.indexOf(val) > -1
? array.filter(ele => ele !== val)
: [...array, val];
const handleSave = () => {
const storage = new Storage(pluginId);
Object.entries(formValues).forEach(([key, value]) => {
@@ -112,6 +110,103 @@ const SourceSettingsModal: React.FC<SourceSettingsModal> = ({
/>
);
}
if (setting?.type === 'Select') {
const selectedOption = setting.options.find(
opt => opt.value === formValues[key],
);
const isMenuOpen = !!openMenus[key];
return (
<View key={key} style={styles.selectContainer}>
<Menu
visible={isMenuOpen}
contentStyle={{ backgroundColor: theme.surfaceVariant }}
anchor={
<Pressable
onPress={() =>
setOpenMenus(prev => ({ ...prev, [key]: !prev[key] }))
}
>
<TextInput
mode="outlined"
label={
<Text
style={{
color: isMenuOpen ? theme.primary : theme.onSurface,
backgroundColor: overlay(2, theme.surface),
}}
>
{` ${setting.label} `}
</Text>
}
value={selectedOption?.label || ''}
editable={false}
theme={{ colors: { background: 'transparent' } }}
outlineColor={
isMenuOpen ? theme.primary : theme.onSurface
}
textColor={isMenuOpen ? theme.primary : theme.onSurface}
pointerEvents="none"
/>
</Pressable>
}
onDismiss={() =>
setOpenMenus(prev => ({ ...prev, [key]: false }))
}
>
{setting.options.map(option => (
<Menu.Item
key={option.value}
title={option.label}
titleStyle={{ color: theme.onSurfaceVariant }}
onPress={() => {
handleChange(key, option.value);
setOpenMenus(prev => ({ ...prev, [key]: false }));
}}
/>
))}
</Menu>
</View>
);
}
if (setting?.type === 'CheckboxGroup') {
const value = (formValues[key] || []) as string[];
const isExpanded = !!openMenus[key];
return (
<View key={key}>
<Pressable
style={styles.checkboxHeader}
onPress={() =>
setOpenMenus(prev => ({ ...prev, [key]: !prev[key] }))
}
android_ripple={{ color: theme.rippleColor }}
>
<Text style={{ color: theme.onSurfaceVariant }}>
{setting.label}
</Text>
<MaterialCommunityIcons
name={isExpanded ? 'chevron-up' : 'chevron-down'}
color={theme.onSurface}
size={24}
/>
</Pressable>
{isExpanded &&
setting.options.map(option => (
<Checkbox
key={option.value}
label={option.label}
theme={theme}
status={value.includes(option.value)}
onPress={() =>
handleChange(
key,
insertOrRemoveIntoArray(value, option.value),
)
}
/>
))}
</View>
);
}
return (
<TextInput
key={key}
@@ -161,4 +256,14 @@ const styles = StyleSheet.create({
marginBottom: 8,
marginTop: 16,
},
selectContainer: {
marginVertical: 8,
},
checkboxHeader: {
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'space-between',
paddingHorizontal: 16,
paddingVertical: 16,
},
});