import {useCallback, useContext, useEffect, useState} from "react"; import {LocaleContext} from "shared/locale"; import { Box, Button, Checkbox, CircularProgress, FormControl, FormControlLabel, FormGroup, FormLabel, Grid, IconButton, Paper, Select, styled, Tab, Table, TableBody, TableCell, TableHead, TableContainer, TableRow, Tabs, TextField, Autocomplete, Chip } from "@mui/material"; import {Link} from "react-router-dom"; import { Add, Delete, Google, LibraryBooks, Mail, RestartAlt, Save, Send, SettingsApplications } from "@mui/icons-material"; import TIME_ZONES from "shared/time-zones"; import ButtonBar from "../elements/button-bar"; const SettingsFormGroup = styled(FormGroup)((props) => ({ marginBottom: props.theme.spacing(1), })); export default function SettingsView(props) { // meta const api = props.api; const showDialog = props.showDialog; const {translate: L, requestModules, currentLocale} = useContext(LocaleContext); const KNOWN_SETTING_KEYS = { "general": [ "base_url", "site_name", "user_registration_enabled", "time_zone", "allowed_extensions", "trusted_domains", ], "mail": [ "mail_enabled", "mail_footer", "mail_from", "mail_host", "mail_port", "mail_username", "mail_password", "mail_async", ], "recaptcha": [ "recaptcha_enabled", "recaptcha_private_key", "recaptcha_public_key", ], "hidden": ["installation_completed", "mail_last_sync"] }; // data const [fetchSettings, setFetchSettings] = useState(true); const [settings, setSettings] = useState(null); const [extra, setExtra] = useState({}); const [uncategorizedKeys, setUncategorizedKeys] = useState([]); // ui const [selectedTab, setSelectedTab] = useState("general"); const [hasChanged, setChanged] = useState(false); const [isSaving, setSaving] = useState(false); const [newKey, setNewKey] = useState(""); const [testMailAddress, setTestMailAddress] = useState(""); const [isSending, setSending] = useState(false); const isUncategorized = (key) => { return !(Object.values(KNOWN_SETTING_KEYS).reduce((acc, arr) => { return [ ...acc, ...arr ]; }, [])).includes(key); } useEffect(() => { requestModules(props.api, ["general", "settings"], currentLocale).then(data => { if (!data.success) { showDialog("Error fetching translations: " + data.msg); } }); }, [currentLocale]); const onFetchSettings = useCallback((force = false) => { if (fetchSettings || force) { setFetchSettings(false); api.getSettings().then(data => { if (!data.success) { showDialog(data.msg, L("settings.fetch_settings_error")); } else { setSettings(Object.keys(data.settings) .filter(key => !KNOWN_SETTING_KEYS.hidden.includes(key)) .reduce((obj, key) => { obj[key] = data.settings[key]; return obj; }, {}) ); setUncategorizedKeys(Object.keys(data.settings).filter(key => isUncategorized(key))); } }); } }, [api, showDialog, fetchSettings]); useEffect(() => { onFetchSettings(); }, [fetchSettings]); const onChangeValue = useCallback((key, value) => { setChanged(true); setSettings({...settings, [key]: value}); }, [settings]); const onSaveSettings = useCallback(() => { setSaving(true); api.saveSettings(settings).then(data => { setSaving(false); if (data.success) { showDialog(L("settings.save_settings_success"), L("general.success")); setChanged(false); } else { showDialog(data.msg, L("settings.save_settings_error")); } }); }, [api, showDialog, settings]); const onDeleteKey = useCallback(key => { if (key && settings.hasOwnProperty(key)) { let index = uncategorizedKeys.indexOf(key); if (index !== -1) { let newUncategorizedKeys = [...uncategorizedKeys]; newUncategorizedKeys.splice(index, 1); setUncategorizedKeys(newUncategorizedKeys); } setChanged(true); setSettings({...settings, [key]: null}); } }, [settings, uncategorizedKeys]); const onAddKey = useCallback(key => { if (key) { if (!isUncategorized(key) || !settings.hasOwnProperty(key) || settings[key] === null) { setChanged(true); setSettings({...settings, [key]: ""}); setUncategorizedKeys([...uncategorizedKeys, key]); setNewKey(""); } else { showDialog("This key is already defined", L("general.error")); } } }, [settings, uncategorizedKeys, showDialog]); const onChangeKey = useCallback((oldKey, newKey) => { if (settings.hasOwnProperty(oldKey) && !settings.hasOwnProperty(newKey)) { let newSettings = {...settings, [newKey]: settings[oldKey]}; delete newSettings[oldKey]; setChanged(true); setSettings(newSettings); } }, [settings]); const onSendTestMail = useCallback(() => { if (!isSending) { setSending(true); api.sendTestMail(testMailAddress).then(data => { setSending(false); if (!data.success) { showDialog(<> {data.msg}
{data.output} , L("settings.send_test_email_error")); } else { showDialog(L("settings.send_test_email_success"), L("general.success")); setTestMailAddress(""); } }); } }, [api, showDialog, testMailAddress, isSending]); const onReset = useCallback(() => { setFetchSettings(true); setNewKey(""); setChanged(false); setExtra({}); }, []); const parseBool = (v) => v !== undefined && (v === true || v === 1 || ["true", "1", "yes"].includes(v.toString().toLowerCase())); const renderTextInput = (key_name, disabled=false, props={}) => { return {L("settings." + key_name)} onChangeValue(key_name, e.target.value)} /> } const renderPasswordInput = (key_name, disabled=false, props={}) => { return {L("settings." + key_name)} onChangeValue(key_name, e.target.value)} /> } const renderNumberInput = (key_name, minValue, maxValue, disabled=false, props={}) => { return {L("settings." + key_name)} onChangeValue(key_name, e.target.value)} /> } const renderCheckBox = (key_name, disabled=false, props={}) => { return onChangeValue(key_name, v)} />} label={L("settings." + key_name)} /> } const renderSelection = (key_name, options, disabled=false, props={}) => { return {L("settings." + key_name)} } const renderTextValuesInput = (key_name, disabled=false, props={}) => { const finishTyping = () => { console.log("finishTyping", key_name); setExtra({...extra, [key_name]: ""}); if (extra[key_name]) { setSettings({...settings, [key_name]: [...settings[key_name], extra[key_name]]}); } } return {L("settings." + key_name)} setSettings({...settings, [key_name]: v})} renderTags={(values, props) => values.map((option, index) => ( )) } renderInput={(params) => setExtra({...extra, [key_name]: e.target.value.trim()})} onKeyDown={e => { if (["Enter", "Tab", " "].includes(e.key)) { e.preventDefault(); e.stopPropagation(); finishTyping(); } }} onBlur={finishTyping} />} /> } const renderTab = () => { if (selectedTab === "general") { return [ renderTextInput("site_name"), renderTextInput("base_url"), renderTextValuesInput("trusted_domains"), renderCheckBox("user_registration_enabled"), renderTextValuesInput("allowed_extensions"), renderSelection("time_zone", TIME_ZONES), ]; } else if (selectedTab === "mail") { return [ renderCheckBox("mail_enabled"), renderTextInput("mail_from", !parseBool(settings.mail_enabled)), renderTextInput("mail_host", !parseBool(settings.mail_enabled)), renderNumberInput("mail_port", 1, 65535, !parseBool(settings.mail_enabled)), renderTextInput("mail_username", !parseBool(settings.mail_enabled)), renderPasswordInput("mail_password", !parseBool(settings.mail_enabled)), renderTextInput("mail_footer", !parseBool(settings.mail_enabled)), renderCheckBox("mail_async", !parseBool(settings.mail_enabled)), {L("settings.send_test_email")} setTestMailAddress(e.target.value)} size={"small"} type={"email"} placeholder={L("settings.mail_address")} /> ]; } else if (selectedTab === "recaptcha") { return [ renderCheckBox("recaptcha_enabled"), renderTextInput("recaptcha_public_key", !parseBool(settings.recaptcha_enabled)), renderPasswordInput("recaptcha_private_key", !parseBool(settings.recaptcha_enabled)), ]; } else if (selectedTab === "uncategorized") { return {L("settings.key")} {L("settings.value")} {L("general.controls")} {uncategorizedKeys.map(key => onChangeKey(key, e.target.value)} /> onChangeValue(key, e.target.value)} /> onDeleteKey(key)} color={"secondary"}> )} setNewKey(e.target.value)} onBlur={() => onAddKey(newKey)} value={newKey} />
} else { return Invalid tab: {selectedTab} } } if (settings === null) { return } return <>

{L("settings.title")}

  1. Home
  2. {L("settings.title")}
setSelectedTab(v)} component={Paper}> } iconPosition={"start"} /> } iconPosition={"start"} /> } iconPosition={"start"} /> } iconPosition={"start"} /> { renderTab() }
}