2023-11-09 21:19:08 +02:00
|
|
|
import * as React from 'react';
|
|
|
|
|
|
|
|
import { UpdateSettingValueCallback } from './types';
|
|
|
|
import { View, Text, TextInput } from 'react-native';
|
2024-06-25 15:01:39 +02:00
|
|
|
import Setting, { AppType } from '@joplin/lib/models/Setting';
|
2023-11-09 21:19:08 +02:00
|
|
|
import Dropdown from '../../Dropdown';
|
|
|
|
import { ConfigScreenStyles } from './configScreenStyles';
|
|
|
|
import Slider from '@react-native-community/slider';
|
|
|
|
import SettingsToggle from './SettingsToggle';
|
|
|
|
import FileSystemPathSelector from './FileSystemPathSelector';
|
|
|
|
import shim from '@joplin/lib/shim';
|
2024-03-09 13:15:13 +02:00
|
|
|
import { themeStyle } from '../../global-style';
|
2024-11-09 14:46:16 +02:00
|
|
|
import { useId } from 'react';
|
2023-11-09 21:19:08 +02:00
|
|
|
|
|
|
|
interface Props {
|
|
|
|
settingId: string;
|
|
|
|
|
|
|
|
// The value associated with the given settings key
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2023-11-09 21:19:08 +02:00
|
|
|
value: any;
|
|
|
|
|
|
|
|
styles: ConfigScreenStyles;
|
|
|
|
themeId: number;
|
|
|
|
|
|
|
|
updateSettingValue: UpdateSettingValueCallback;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const SettingComponent: React.FunctionComponent<Props> = props => {
|
|
|
|
const themeId = props.themeId;
|
|
|
|
const theme = themeStyle(themeId);
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2023-11-09 21:19:08 +02:00
|
|
|
const output: any = null;
|
|
|
|
|
|
|
|
const md = Setting.settingMetadata(props.settingId);
|
2024-06-25 15:01:39 +02:00
|
|
|
const settingDescription = md.description ? md.description(AppType.Mobile) : '';
|
2023-11-09 21:19:08 +02:00
|
|
|
|
|
|
|
const styleSheet = props.styles.styleSheet;
|
|
|
|
|
|
|
|
const descriptionComp = !settingDescription ? null : <Text style={styleSheet.settingDescriptionText}>{settingDescription}</Text>;
|
|
|
|
const containerStyle = props.styles.getContainerStyle(!!settingDescription);
|
|
|
|
|
2024-11-09 14:46:16 +02:00
|
|
|
const labelId = useId();
|
|
|
|
|
2023-11-09 21:19:08 +02:00
|
|
|
if (md.isEnum) {
|
2024-04-04 13:10:13 +02:00
|
|
|
const value = props.value?.toString();
|
2023-11-09 21:19:08 +02:00
|
|
|
|
|
|
|
const items = Setting.enumOptionsToValueLabels(md.options(), md.optionsOrder ? md.optionsOrder() : []);
|
2024-11-09 14:46:16 +02:00
|
|
|
const label = md.label();
|
2023-11-09 21:19:08 +02:00
|
|
|
|
|
|
|
return (
|
|
|
|
<View key={props.settingId} style={{ flexDirection: 'column', borderBottomWidth: 1, borderBottomColor: theme.dividerColor }}>
|
|
|
|
<View style={containerStyle}>
|
|
|
|
<Text key="label" style={styleSheet.settingText}>
|
2024-11-09 14:46:16 +02:00
|
|
|
{label}
|
2023-11-09 21:19:08 +02:00
|
|
|
</Text>
|
|
|
|
<Dropdown
|
|
|
|
key="control"
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2023-11-09 21:19:08 +02:00
|
|
|
items={items as any}
|
|
|
|
selectedValue={value}
|
|
|
|
itemListStyle={{
|
|
|
|
backgroundColor: theme.backgroundColor,
|
|
|
|
}}
|
|
|
|
headerStyle={{
|
|
|
|
color: theme.color,
|
|
|
|
fontSize: theme.fontSize,
|
|
|
|
}}
|
|
|
|
itemStyle={{
|
|
|
|
color: theme.color,
|
|
|
|
fontSize: theme.fontSize,
|
|
|
|
}}
|
|
|
|
onValueChange={(itemValue: string) => {
|
|
|
|
void props.updateSettingValue(props.settingId, itemValue);
|
|
|
|
}}
|
2024-11-09 14:46:16 +02:00
|
|
|
accessibilityHint={label}
|
2023-11-09 21:19:08 +02:00
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
{descriptionComp}
|
|
|
|
</View>
|
|
|
|
);
|
|
|
|
} else if (md.type === Setting.TYPE_BOOL) {
|
|
|
|
return (
|
|
|
|
<SettingsToggle
|
|
|
|
settingId={props.settingId}
|
|
|
|
value={props.value}
|
|
|
|
themeId={props.themeId}
|
|
|
|
styles={props.styles}
|
|
|
|
label={md.label()}
|
|
|
|
updateSettingValue={props.updateSettingValue}
|
|
|
|
description={descriptionComp}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
} else if (md.type === Setting.TYPE_INT) {
|
|
|
|
const unitLabel = md.unitLabel ? md.unitLabel(props.value) : props.value;
|
|
|
|
const minimum = 'minimum' in md ? md.minimum : 0;
|
|
|
|
const maximum = 'maximum' in md ? md.maximum : 10;
|
2024-11-09 14:46:16 +02:00
|
|
|
const label = md.label();
|
2023-11-09 21:19:08 +02:00
|
|
|
|
|
|
|
// Note: Do NOT add the minimumTrackTintColor and maximumTrackTintColor props
|
|
|
|
// on the Slider as they are buggy and can crash the app on certain devices.
|
|
|
|
// https://github.com/laurent22/joplin/issues/2733
|
|
|
|
// https://github.com/react-native-community/react-native-slider/issues/161
|
|
|
|
return (
|
|
|
|
<View key={props.settingId} style={styleSheet.settingContainer}>
|
2024-11-09 14:46:16 +02:00
|
|
|
<Text key="label" style={styleSheet.settingText} nativeID={labelId}>
|
|
|
|
{label}
|
2023-11-09 21:19:08 +02:00
|
|
|
</Text>
|
|
|
|
<View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', flex: 1 }}>
|
|
|
|
<Text style={styleSheet.sliderUnits}>{unitLabel}</Text>
|
|
|
|
<Slider
|
|
|
|
key="control"
|
|
|
|
style={{ flex: 1 }}
|
|
|
|
step={md.step}
|
|
|
|
minimumValue={minimum}
|
|
|
|
maximumValue={maximum}
|
|
|
|
value={props.value}
|
|
|
|
onValueChange={newValue => void props.updateSettingValue(props.settingId, newValue)}
|
2024-11-09 14:46:16 +02:00
|
|
|
accessibilityHint={label}
|
2023-11-09 21:19:08 +02:00
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
);
|
|
|
|
} else if (md.type === Setting.TYPE_STRING) {
|
2024-08-02 15:51:49 +02:00
|
|
|
if (['sync.2.path', 'plugins.devPluginPaths'].includes(md.key) && (shim.fsDriver().isUsingAndroidSAF() || shim.mobilePlatform() === 'web')) {
|
2023-11-09 21:19:08 +02:00
|
|
|
return (
|
|
|
|
<FileSystemPathSelector
|
2024-08-02 15:51:49 +02:00
|
|
|
mode={md.key === 'sync.2.path' ? 'readwrite' : 'read'}
|
2023-11-09 21:19:08 +02:00
|
|
|
styles={props.styles}
|
|
|
|
settingMetadata={md}
|
|
|
|
updateSettingValue={props.updateSettingValue}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<View key={props.settingId} style={{ flexDirection: 'column', borderBottomWidth: 1, borderBottomColor: theme.dividerColor }}>
|
|
|
|
<View key={props.settingId} style={containerStyle}>
|
2024-11-09 14:46:16 +02:00
|
|
|
<Text key="label" style={styleSheet.settingText} nativeID={labelId}>
|
2023-11-09 21:19:08 +02:00
|
|
|
{md.label()}
|
|
|
|
</Text>
|
|
|
|
<TextInput
|
|
|
|
autoCorrect={false}
|
|
|
|
autoComplete="off"
|
|
|
|
selectionColor={theme.textSelectionColor}
|
2024-04-11 09:35:20 +02:00
|
|
|
keyboardAppearance={theme.keyboardAppearance}
|
2023-11-09 21:19:08 +02:00
|
|
|
autoCapitalize="none"
|
|
|
|
key="control"
|
|
|
|
style={styleSheet.settingControl}
|
|
|
|
value={props.value}
|
|
|
|
onChangeText={(newValue: string) => void props.updateSettingValue(props.settingId, newValue)}
|
|
|
|
secureTextEntry={!!md.secure}
|
2024-11-09 14:46:16 +02:00
|
|
|
aria-labelledby={labelId}
|
2023-11-09 21:19:08 +02:00
|
|
|
/>
|
|
|
|
</View>
|
|
|
|
{descriptionComp}
|
|
|
|
</View>
|
|
|
|
);
|
|
|
|
} else if (md.type === Setting.TYPE_BUTTON) {
|
|
|
|
// TODO: Not yet supported
|
|
|
|
} else if (Setting.value('env') === 'dev') {
|
|
|
|
throw new Error(`Unsupported setting type: ${md.type}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return output;
|
|
|
|
};
|
|
|
|
|
|
|
|
export default SettingComponent;
|