diff options
Diffstat (limited to 'web/source/settings/lib/form')
-rw-r--r-- | web/source/settings/lib/form/bool.jsx | 50 | ||||
-rw-r--r-- | web/source/settings/lib/form/check-list.jsx | 147 | ||||
-rw-r--r-- | web/source/settings/lib/form/combo-box.jsx | 56 | ||||
-rw-r--r-- | web/source/settings/lib/form/file.jsx | 91 | ||||
-rw-r--r-- | web/source/settings/lib/form/form-with-data.jsx | 39 | ||||
-rw-r--r-- | web/source/settings/lib/form/index.js | 46 | ||||
-rw-r--r-- | web/source/settings/lib/form/radio.jsx | 51 | ||||
-rw-r--r-- | web/source/settings/lib/form/submit.js | 83 | ||||
-rw-r--r-- | web/source/settings/lib/form/text.jsx | 67 |
9 files changed, 630 insertions, 0 deletions
diff --git a/web/source/settings/lib/form/bool.jsx b/web/source/settings/lib/form/bool.jsx new file mode 100644 index 000000000..b124abd50 --- /dev/null +++ b/web/source/settings/lib/form/bool.jsx @@ -0,0 +1,50 @@ +/* + GoToSocial + Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const React = require("react"); + +module.exports = function useBoolInput({ name, Name }, { defaultValue = false } = {}) { + const [value, setValue] = React.useState(defaultValue); + + function onChange(e) { + setValue(e.target.checked); + } + + function reset() { + setValue(defaultValue); + } + + // Array / Object hybrid, for easier access in different contexts + return Object.assign([ + onChange, + reset, + { + [name]: value, + [`set${Name}`]: setValue + } + ], { + name, + onChange, + reset, + value, + setter: setValue, + hasChanged: () => value != defaultValue + }); +};
\ No newline at end of file diff --git a/web/source/settings/lib/form/check-list.jsx b/web/source/settings/lib/form/check-list.jsx new file mode 100644 index 000000000..c1233273d --- /dev/null +++ b/web/source/settings/lib/form/check-list.jsx @@ -0,0 +1,147 @@ +/* + GoToSocial + Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const React = require("react"); +const syncpipe = require("syncpipe"); + +function createState(entries, uniqueKey, oldState, defaultValue) { + return syncpipe(entries, [ + (_) => _.map((entry) => { + let key = entry[uniqueKey]; + return [ + key, + { + ...entry, + key, + checked: oldState[key]?.checked ?? entry.checked ?? defaultValue + } + ]; + }), + (_) => Object.fromEntries(_) + ]); +} + +function updateAllState(state, newValue) { + return syncpipe(state, [ + (_) => Object.values(_), + (_) => _.map((entry) => [entry.key, { + ...entry, + checked: newValue + }]), + (_) => Object.fromEntries(_) + ]); +} + +function updateState(state, key, newValue) { + return { + ...state, + [key]: { + ...state[key], + ...newValue + } + }; +} + +module.exports = function useCheckListInput({ name }, { entries, uniqueKey = "key", defaultValue = false }) { + const [state, setState] = React.useState({}); + + const [someSelected, setSomeSelected] = React.useState(false); + const [toggleAllState, setToggleAllState] = React.useState(0); + const toggleAllRef = React.useRef(null); + + React.useEffect(() => { + /* + entries changed, update state, + re-using old state if available for key + */ + setState(createState(entries, uniqueKey, state, defaultValue)); + + /* eslint-disable-next-line react-hooks/exhaustive-deps */ + }, [entries]); + + React.useEffect(() => { + /* Updates (un)check all checkbox, based on shortcode checkboxes + Can be 0 (not checked), 1 (checked) or 2 (indeterminate) + */ + if (toggleAllRef.current == null) { + return; + } + + let values = Object.values(state); + /* one or more boxes are checked */ + let some = values.some((v) => v.checked); + + let all = false; + if (some) { + /* there's not at least one unchecked box */ + all = !values.some((v) => v.checked == false); + } + + setSomeSelected(some); + + if (some && !all) { + setToggleAllState(2); + toggleAllRef.current.indeterminate = true; + } else { + setToggleAllState(all ? 1 : 0); + toggleAllRef.current.indeterminate = false; + } + }, [state, toggleAllRef]); + + function toggleAll(e) { + let selectAll = e.target.checked; + + if (toggleAllState == 2) { // indeterminate + selectAll = false; + } + + setState(updateAllState(state, selectAll)); + setToggleAllState(selectAll); + } + + function reset() { + setState(updateAllState(state, defaultValue)); + } + + function selectedValues() { + return syncpipe(state, [ + (_) => Object.values(_), + (_) => _.filter((entry) => entry.checked) + ]); + } + + return Object.assign([ + state, + reset, + { name } + ], { + name, + value: state, + onChange: (key, newValue) => setState(updateState(state, key, newValue)), + selectedValues, + reset, + someSelected, + toggleAll: { + ref: toggleAllRef, + value: toggleAllState, + onChange: toggleAll + } + }); +};
\ No newline at end of file diff --git a/web/source/settings/lib/form/combo-box.jsx b/web/source/settings/lib/form/combo-box.jsx new file mode 100644 index 000000000..3e8cea44a --- /dev/null +++ b/web/source/settings/lib/form/combo-box.jsx @@ -0,0 +1,56 @@ +/* + GoToSocial + Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const React = require("react"); + +const { useComboboxState } = require("ariakit/combobox"); + +module.exports = function useComboBoxInput({ name, Name }, { defaultValue } = {}) { + const [isNew, setIsNew] = React.useState(false); + + const state = useComboboxState({ + defaultValue, + gutter: 0, + sameWidth: true + }); + + function reset() { + state.setValue(""); + } + + return Object.assign([ + state, + reset, + { + [name]: state.value, + name, + [`${name}IsNew`]: isNew, + [`set${Name}IsNew`]: setIsNew + } + ], { + name, + state, + value: state.value, + hasChanged: () => state.value != defaultValue, + isNew, + setIsNew, + reset + }); +};
\ No newline at end of file diff --git a/web/source/settings/lib/form/file.jsx b/web/source/settings/lib/form/file.jsx new file mode 100644 index 000000000..85f23e274 --- /dev/null +++ b/web/source/settings/lib/form/file.jsx @@ -0,0 +1,91 @@ +/* + GoToSocial + Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const React = require("react"); +const prettierBytes = require("prettier-bytes"); + +module.exports = function useFileInput({ name, _Name }, { + withPreview, + maxSize, + initialInfo = "no file selected" +} = {}) { + const [file, setFile] = React.useState(); + const [imageURL, setImageURL] = React.useState(); + const [info, setInfo] = React.useState(); + + function onChange(e) { + let file = e.target.files[0]; + setFile(file); + + URL.revokeObjectURL(imageURL); + + if (file != undefined) { + if (withPreview) { + setImageURL(URL.createObjectURL(file)); + } + + let size = prettierBytes(file.size); + if (maxSize && file.size > maxSize) { + size = <span className="error-text">{size}</span>; + } + + setInfo(<> + {file.name} ({size}) + </>); + } else { + setInfo(); + } + } + + function reset() { + URL.revokeObjectURL(imageURL); + setImageURL(); + setFile(); + setInfo(); + } + + const infoComponent = ( + <span className="form-info"> + {info + ? info + : initialInfo + } + </span> + ); + + // Array / Object hybrid, for easier access in different contexts + return Object.assign([ + onChange, + reset, + { + [name]: file, + [`${name}URL`]: imageURL, + [`${name}Info`]: infoComponent, + } + ], { + onChange, + reset, + name, + value: file, + previewValue: imageURL, + hasChanged: () => file != undefined, + infoComponent + }); +};
\ No newline at end of file diff --git a/web/source/settings/lib/form/form-with-data.jsx b/web/source/settings/lib/form/form-with-data.jsx new file mode 100644 index 000000000..a383af502 --- /dev/null +++ b/web/source/settings/lib/form/form-with-data.jsx @@ -0,0 +1,39 @@ +/* + GoToSocial + Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const React = require("react"); + +const Loading = require("../../components/loading"); + +// Wrap Form component inside component that fires the RTK Query call, +// so Form will only be rendered when data is available to generate form-fields for +module.exports = function FormWithData({ dataQuery, DataForm, queryArg, ...formProps }) { + const { data, isLoading } = dataQuery(queryArg); + + if (isLoading) { + return ( + <div> + <Loading /> + </div> + ); + } else { + return <DataForm data={data} {...formProps} />; + } +};
\ No newline at end of file diff --git a/web/source/settings/lib/form/index.js b/web/source/settings/lib/form/index.js new file mode 100644 index 000000000..aef3bf0d2 --- /dev/null +++ b/web/source/settings/lib/form/index.js @@ -0,0 +1,46 @@ +/* + GoToSocial + Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +function capitalizeFirst(str) { + return str.slice(0, 1).toUpperCase() + str.slice(1); +} + +function makeHook(func) { + return (name, ...args) => func({ + name, + Name: capitalizeFirst(name) + }, ...args); +} + +module.exports = { + useTextInput: makeHook(require("./text")), + useFileInput: makeHook(require("./file")), + useBoolInput: makeHook(require("./bool")), + useRadioInput: makeHook(require("./radio")), + useComboBoxInput: makeHook(require("./combo-box")), + useCheckListInput: makeHook(require("./check-list")), + useValue: function (name, value) { + return { + name, + value, + hasChanged: () => true // always included + }; + } +};
\ No newline at end of file diff --git a/web/source/settings/lib/form/radio.jsx b/web/source/settings/lib/form/radio.jsx new file mode 100644 index 000000000..47ab6c726 --- /dev/null +++ b/web/source/settings/lib/form/radio.jsx @@ -0,0 +1,51 @@ +/* + GoToSocial + Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const React = require("react"); + +module.exports = function useRadioInput({ name, Name }, { defaultValue, options } = {}) { + const [value, setValue] = React.useState(defaultValue); + + function onChange(e) { + setValue(e.target.value); + } + + function reset() { + setValue(defaultValue); + } + + // Array / Object hybrid, for easier access in different contexts + return Object.assign([ + onChange, + reset, + { + [name]: value, + [`set${Name}`]: setValue + } + ], { + name, + onChange, + reset, + value, + setter: setValue, + options, + hasChanged: () => value != defaultValue + }); +};
\ No newline at end of file diff --git a/web/source/settings/lib/form/submit.js b/web/source/settings/lib/form/submit.js new file mode 100644 index 000000000..6f20165a5 --- /dev/null +++ b/web/source/settings/lib/form/submit.js @@ -0,0 +1,83 @@ +/* + GoToSocial + Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const Promise = require("bluebird"); +const React = require("react"); +const syncpipe = require("syncpipe"); + +module.exports = function useFormSubmit(form, mutationQuery, { changedOnly = true } = {}) { + if (!Array.isArray(mutationQuery)) { + throw new ("useFormSubmit: mutationQuery was not an Array. Is a valid useMutation RTK Query provided?"); + } + const [runMutation, result] = mutationQuery; + const [usedAction, setUsedAction] = React.useState(); + return [ + function submitForm(e) { + let action; + if (e?.preventDefault) { + e.preventDefault(); + action = e.nativeEvent.submitter.name; + } else { + action = e; + } + + if (action == "") { + action = undefined; + } + setUsedAction(action); + // transform the field definitions into an object with just their values + let updatedFields = []; + const mutationData = syncpipe(form, [ + (_) => Object.values(_), + (_) => _.map((field) => { + if (field.selectedValues != undefined) { + let selected = field.selectedValues(); + if (!changedOnly || selected.length > 0) { + updatedFields.push(field); + return [field.name, selected]; + } + } else if (!changedOnly || field.hasChanged()) { + updatedFields.push(field); + return [field.name, field.value]; + } + return null; + }), + (_) => _.filter((value) => value != null), + (_) => Object.fromEntries(_) + ]); + + mutationData.action = action; + + return Promise.try(() => { + return runMutation(mutationData); + }).then((res) => { + if (res.error == undefined) { + updatedFields.forEach((field) => { + field.reset(); + }); + } + }); + }, + { + ...result, + action: usedAction + } + ]; +};
\ No newline at end of file diff --git a/web/source/settings/lib/form/text.jsx b/web/source/settings/lib/form/text.jsx new file mode 100644 index 000000000..70e61657c --- /dev/null +++ b/web/source/settings/lib/form/text.jsx @@ -0,0 +1,67 @@ +/* + GoToSocial + Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +"use strict"; + +const React = require("react"); + +module.exports = function useTextInput({ name, Name }, { validator, defaultValue = "", dontReset = false } = {}) { + const [text, setText] = React.useState(defaultValue); + const [valid, setValid] = React.useState(true); + const textRef = React.useRef(null); + + function onChange(e) { + let input = e.target.value; + setText(input); + } + + function reset() { + if (!dontReset) { + setText(defaultValue); + } + } + + React.useEffect(() => { + if (validator && textRef.current) { + let res = validator(text); + setValid(res == ""); + textRef.current.setCustomValidity(res); + } + }, [text, textRef, validator]); + + // Array / Object hybrid, for easier access in different contexts + return Object.assign([ + onChange, + reset, + { + [name]: text, + [`${name}Ref`]: textRef, + [`set${Name}`]: setText, + [`${name}Valid`]: valid, + } + ], { + onChange, + reset, + name, + value: text, + ref: textRef, + setter: setText, + valid, + hasChanged: () => text != defaultValue + }); +};
\ No newline at end of file |