summaryrefslogtreecommitdiff
path: root/web/source/settings-panel
diff options
context:
space:
mode:
authorLibravatar f0x52 <f0x@cthu.lu>2022-10-03 16:46:38 +0200
committerLibravatar GitHub <noreply@github.com>2022-10-03 16:46:38 +0200
commit5249294a166c901469eeac1d3297e913b4a125e7 (patch)
treed68ff7bebbc0135d23c18520417c668155aa56f2 /web/source/settings-panel
parent[performance] add user cache and database (#879) (diff)
downloadgotosocial-5249294a166c901469eeac1d3297e913b4a125e7.tar.xz
[chore] Bundler restructure (#880)
* re-structure bundler, settings panel files * add more info logging * tidy up CSS syntax errors * split into lib/ files * livereloading server * fix factor function for production builds * remove testing console.log * default to production env, saves 300kb bundle size
Diffstat (limited to 'web/source/settings-panel')
-rw-r--r--web/source/settings-panel/admin/actions.js61
-rw-r--r--web/source/settings-panel/admin/emoji.js212
-rw-r--r--web/source/settings-panel/admin/federation.js382
-rw-r--r--web/source/settings-panel/admin/settings.js110
-rw-r--r--web/source/settings-panel/components/error.jsx45
-rw-r--r--web/source/settings-panel/components/fake-toot.jsx43
-rw-r--r--web/source/settings-panel/components/form-fields.jsx167
-rw-r--r--web/source/settings-panel/components/languages.jsx98
-rw-r--r--web/source/settings-panel/components/login.jsx102
-rw-r--r--web/source/settings-panel/components/nav-button.jsx33
-rw-r--r--web/source/settings-panel/components/submit.jsx35
-rw-r--r--web/source/settings-panel/index.js178
-rw-r--r--web/source/settings-panel/lib/api/admin.js192
-rw-r--r--web/source/settings-panel/lib/api/index.js185
-rw-r--r--web/source/settings-panel/lib/api/oauth.js124
-rw-r--r--web/source/settings-panel/lib/api/user.js67
-rw-r--r--web/source/settings-panel/lib/errors.js27
-rw-r--r--web/source/settings-panel/lib/get-views.js102
-rw-r--r--web/source/settings-panel/lib/panel.js134
-rw-r--r--web/source/settings-panel/lib/submit.js48
-rw-r--r--web/source/settings-panel/redux/index.js48
-rw-r--r--web/source/settings-panel/redux/reducers/admin.js131
-rw-r--r--web/source/settings-panel/redux/reducers/instances.js42
-rw-r--r--web/source/settings-panel/redux/reducers/oauth.js52
-rw-r--r--web/source/settings-panel/redux/reducers/temporary.js32
-rw-r--r--web/source/settings-panel/redux/reducers/user.js51
-rw-r--r--web/source/settings-panel/style.css498
-rw-r--r--web/source/settings-panel/user/profile.js113
-rw-r--r--web/source/settings-panel/user/settings.js140
29 files changed, 0 insertions, 3452 deletions
diff --git a/web/source/settings-panel/admin/actions.js b/web/source/settings-panel/admin/actions.js
deleted file mode 100644
index d4980d021..000000000
--- a/web/source/settings-panel/admin/actions.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Redux = require("react-redux");
-
-const Submit = require("../components/submit");
-
-const api = require("../lib/api");
-const submit = require("../lib/submit");
-
-module.exports = function AdminActionPanel() {
- const dispatch = Redux.useDispatch();
-
- const [days, setDays] = React.useState(30);
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- const removeMedia = submit(
- () => dispatch(api.admin.mediaCleanup(days)),
- {setStatus, setError}
- );
-
- return (
- <>
- <h1>Admin Actions</h1>
- <div>
- <h2>Media cleanup</h2>
- <p>
- Clean up remote media older than the specified number of days.
- If the remote instance is still online they will be refetched when needed.
- Also cleans up unused headers and avatars from the media cache.
- </p>
- <div>
- <label htmlFor="days">Days: </label>
- <input id="days" type="number" value={days} onChange={(e) => setDays(e.target.value)}/>
- </div>
- <Submit onClick={removeMedia} label="Remove media" errorMsg={errorMsg} statusMsg={statusMsg} />
- </div>
- </>
- );
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/admin/emoji.js b/web/source/settings-panel/admin/emoji.js
deleted file mode 100644
index 1ef4a54a3..000000000
--- a/web/source/settings-panel/admin/emoji.js
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Redux = require("react-redux");
-const {Switch, Route, Link, Redirect, useRoute, useLocation} = require("wouter");
-
-const Submit = require("../components/submit");
-const FakeToot = require("../components/fake-toot");
-const { formFields } = require("../components/form-fields");
-
-const api = require("../lib/api");
-const adminActions = require("../redux/reducers/admin").actions;
-const submit = require("../lib/submit");
-
-const base = "/settings/admin/custom-emoji";
-
-module.exports = function CustomEmoji() {
- return (
- <Switch>
- <Route path={`${base}/:emojiId`}>
- <EmojiDetailWrapped />
- </Route>
- <EmojiOverview />
- </Switch>
- );
-};
-
-function EmojiOverview() {
- const dispatch = Redux.useDispatch();
- const [loaded, setLoaded] = React.useState(false);
-
- const [errorMsg, setError] = React.useState("");
-
- React.useEffect(() => {
- if (!loaded) {
- Promise.try(() => {
- return dispatch(api.admin.fetchCustomEmoji());
- }).then(() => {
- setLoaded(true);
- }).catch((e) => {
- setLoaded(true);
- setError(e.message);
- });
- }
- }, []);
-
- if (!loaded) {
- return (
- <>
- <h1>Custom Emoji</h1>
- Loading...
- </>
- );
- }
-
- return (
- <>
- <h1>Custom Emoji</h1>
- <EmojiList/>
- <NewEmoji/>
- {errorMsg.length > 0 &&
- <div className="error accent">{errorMsg}</div>
- }
- </>
- );
-}
-
-const NewEmojiForm = formFields(adminActions.updateNewEmojiVal, (state) => state.admin.newEmoji);
-function NewEmoji() {
- const dispatch = Redux.useDispatch();
- const newEmojiForm = Redux.useSelector((state) => state.admin.newEmoji);
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- const uploadEmoji = submit(
- () => dispatch(api.admin.newEmoji()),
- {
- setStatus, setError,
- onSuccess: function() {
- URL.revokeObjectURL(newEmojiForm.image);
- return Promise.all([
- dispatch(adminActions.updateNewEmojiVal(["image", undefined])),
- dispatch(adminActions.updateNewEmojiVal(["imageFile", undefined])),
- dispatch(adminActions.updateNewEmojiVal(["shortcode", ""])),
- ]);
- }
- }
- );
-
- React.useEffect(() => {
- if (newEmojiForm.shortcode.length == 0) {
- if (newEmojiForm.imageFile != undefined) {
- let [name, ext] = newEmojiForm.imageFile.name.split(".");
- dispatch(adminActions.updateNewEmojiVal(["shortcode", name]));
- }
- }
- });
-
- let emojiOrShortcode = `:${newEmojiForm.shortcode}:`;
-
- if (newEmojiForm.image != undefined) {
- emojiOrShortcode = <img
- className="emoji"
- src={newEmojiForm.image}
- title={`:${newEmojiForm.shortcode}:`}
- alt={newEmojiForm.shortcode}
- />;
- }
-
- return (
- <div>
- <h2>Add new custom emoji</h2>
-
- <FakeToot>
- Look at this new custom emoji {emojiOrShortcode} isn&apos;t it cool?
- </FakeToot>
-
- <NewEmojiForm.File
- id="image"
- name="Image"
- fileType="image/png,image/gif"
- showSize={true}
- maxSize={50 * 1000}
- />
-
- <NewEmojiForm.TextInput
- id="shortcode"
- name="Shortcode (without : :), must be unique on the instance"
- placeHolder="blobcat"
- />
-
- <Submit onClick={uploadEmoji} label="Upload" errorMsg={errorMsg} statusMsg={statusMsg} />
- </div>
- );
-}
-
-function EmojiList() {
- const emoji = Redux.useSelector((state) => state.admin.emoji);
-
- return (
- <div>
- <h2>Overview</h2>
- <div className="list emoji-list">
- {Object.entries(emoji).map(([category, entries]) => {
- return <EmojiCategory key={category} category={category} entries={entries}/>;
- })}
- </div>
- </div>
- );
-}
-
-function EmojiCategory({category, entries}) {
- return (
- <div className="entry">
- <b>{category}</b>
- <div className="emoji-group">
- {entries.map((e) => {
- return (
- // <Link key={e.static_url} to={`${base}/${e.shortcode}`}>
- <Link key={e.static_url} to={`${base}`}>
- <a>
- <img src={e.static_url} alt={e.shortcode} title={`:${e.shortcode}:`}/>
- </a>
- </Link>
- );
- })}
- </div>
- </div>
- );
-}
-
-function EmojiDetailWrapped() {
- /* We wrap the component to generate formFields with a setter depending on the domain
- if formFields() is used inside the same component that is re-rendered with their state,
- inputs get re-created on every change, causing them to lose focus, and bad performance
- */
- let [_match, {emojiId}] = useRoute(`${base}/:emojiId`);
-
- function alterEmoji([key, val]) {
- return adminActions.updateDomainBlockVal([emojiId, key, val]);
- }
-
- const fields = formFields(alterEmoji, (state) => state.admin.blockedInstances[emojiId]);
-
- return <EmojiDetail id={emojiId} Form={fields} />;
-}
-
-function EmojiDetail({id, Form}) {
- return (
- "Not implemented yet"
- );
-} \ No newline at end of file
diff --git a/web/source/settings-panel/admin/federation.js b/web/source/settings-panel/admin/federation.js
deleted file mode 100644
index 7afc3c699..000000000
--- a/web/source/settings-panel/admin/federation.js
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Redux = require("react-redux");
-const {Switch, Route, Link, Redirect, useRoute, useLocation} = require("wouter");
-const fileDownload = require("js-file-download");
-
-const { formFields } = require("../components/form-fields");
-
-const api = require("../lib/api");
-const adminActions = require("../redux/reducers/admin").actions;
-const submit = require("../lib/submit");
-
-const base = "/settings/admin/federation";
-
-// const {
-// TextInput,
-// TextArea,
-// File
-// } = require("../components/form-fields").formFields(adminActions.setAdminSettingsVal, (state) => state.instances.adminSettings);
-
-module.exports = function AdminSettings() {
- const dispatch = Redux.useDispatch();
- // const instance = Redux.useSelector(state => state.instances.adminSettings);
- const loadedBlockedInstances = Redux.useSelector(state => state.admin.loadedBlockedInstances);
-
- React.useEffect(() => {
- if (!loadedBlockedInstances ) {
- Promise.try(() => {
- return dispatch(api.admin.fetchDomainBlocks());
- });
- }
- }, []);
-
- if (!loadedBlockedInstances) {
- return (
- <div>
- <h1>Federation</h1>
- Loading...
- </div>
- );
- }
-
- return (
- <Switch>
- <Route path={`${base}/:domain`}>
- <InstancePageWrapped />
- </Route>
- <InstanceOverview />
- </Switch>
- );
-};
-
-function InstanceOverview() {
- const [filter, setFilter] = React.useState("");
- const blockedInstances = Redux.useSelector(state => state.admin.blockedInstances);
- const [_location, setLocation] = useLocation();
-
- function filterFormSubmit(e) {
- e.preventDefault();
- setLocation(`${base}/${filter}`);
- }
-
- return (
- <>
- <h1>Federation</h1>
- Here you can see an overview of blocked instances.
-
- <div className="instance-list">
- <h2>Blocked instances</h2>
- <form action={`${base}/view`} className="filter" role="search" onSubmit={filterFormSubmit}>
- <input name="domain" value={filter} onChange={(e) => setFilter(e.target.value)}/>
- <Link to={`${base}/${filter}`}><a className="button">Add block</a></Link>
- </form>
- <div className="list">
- {Object.values(blockedInstances).filter((a) => a.domain.startsWith(filter)).map((entry) => {
- return (
- <Link key={entry.domain} to={`${base}/${entry.domain}`}>
- <a className="entry nounderline">
- <span id="domain">
- {entry.domain}
- </span>
- <span id="date">
- {new Date(entry.created_at).toLocaleString()}
- </span>
- </a>
- </Link>
- );
- })}
- </div>
- </div>
-
- <BulkBlocking/>
- </>
- );
-}
-
-const Bulk = formFields(adminActions.updateBulkBlockVal, (state) => state.admin.bulkBlock);
-function BulkBlocking() {
- const dispatch = Redux.useDispatch();
- const {bulkBlock, blockedInstances} = Redux.useSelector(state => state.admin);
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- function importBlocks() {
- setStatus("Processing");
- setError("");
- return Promise.try(() => {
- return dispatch(api.admin.bulkDomainBlock());
- }).then(({success, invalidDomains}) => {
- return Promise.try(() => {
- return resetBulk();
- }).then(() => {
- dispatch(adminActions.updateBulkBlockVal(["list", invalidDomains.join("\n")]));
-
- let stat = "";
- if (success == 0) {
- return setError("No valid domains in import");
- } else if (success == 1) {
- stat = "Imported 1 domain";
- } else {
- stat = `Imported ${success} domains`;
- }
-
- if (invalidDomains.length > 0) {
- if (invalidDomains.length == 1) {
- stat += ", input contained 1 invalid domain.";
- } else {
- stat += `, input contained ${invalidDomains.length} invalid domains.`;
- }
- } else {
- stat += "!";
- }
-
- setStatus(stat);
- });
- }).catch((e) => {
- console.error(e);
- setError(e.message);
- setStatus("");
- });
- }
-
- function exportBlocks() {
- return Promise.try(() => {
- setStatus("Exporting");
- setError("");
- let asJSON = bulkBlock.exportType.startsWith("json");
- let _asCSV = bulkBlock.exportType.startsWith("csv");
-
- let exportList = Object.values(blockedInstances).map((entry) => {
- if (asJSON) {
- return {
- domain: entry.domain,
- public_comment: entry.public_comment
- };
- } else {
- return entry.domain;
- }
- });
-
- if (bulkBlock.exportType == "json") {
- return dispatch(adminActions.updateBulkBlockVal(["list", JSON.stringify(exportList)]));
- } else if (bulkBlock.exportType == "json-download") {
- return fileDownload(JSON.stringify(exportList), "block-export.json");
- } else if (bulkBlock.exportType == "plain") {
- return dispatch(adminActions.updateBulkBlockVal(["list", exportList.join("\n")]));
- }
- }).then(() => {
- setStatus("Exported!");
- }).catch((e) => {
- setError(e.message);
- setStatus("");
- });
- }
-
- function resetBulk(e) {
- if (e != undefined) {
- e.preventDefault();
- }
- return dispatch(adminActions.resetBulkBlockVal());
- }
-
- function disableInfoFields(props={}) {
- if (bulkBlock.list[0] == "[") {
- return {
- ...props,
- disabled: true,
- placeHolder: "Domain list is a JSON import, input disabled"
- };
- } else {
- return props;
- }
- }
-
- return (
- <div className="bulk">
- <h2>Import / Export <a onClick={resetBulk}>reset</a></h2>
- <Bulk.TextArea
- id="list"
- name="Domains, one per line"
- placeHolder={`google.com\nfacebook.com`}
- />
-
- <Bulk.TextArea
- id="public_comment"
- name="Public comment"
- inputProps={disableInfoFields({rows: 3})}
- />
-
- <Bulk.TextArea
- id="private_comment"
- name="Private comment"
- inputProps={disableInfoFields({rows: 3})}
- />
-
- <Bulk.Checkbox
- id="obfuscate"
- name="Obfuscate domains? "
- inputProps={disableInfoFields()}
- />
-
- <div className="hidden">
- <Bulk.File
- id="json"
- fileType="application/json"
- withPreview={false}
- />
- </div>
-
- <div className="messagebutton">
- <div>
- <button type="submit" onClick={importBlocks}>Import</button>
- </div>
-
- <div>
- <button type="submit" onClick={exportBlocks}>Export</button>
-
- <Bulk.Select id="exportType" name="Export type" options={
- <>
- <option value="plain">One per line in text field</option>
- <option value="json">JSON in text field</option>
- <option value="json-download">JSON file download</option>
- <option disabled value="csv">CSV in text field (glitch-soc)</option>
- <option disabled value="csv-download">CSV file download (glitch-soc)</option>
- </>
- }/>
- </div>
- <br/>
- <div>
- {errorMsg.length > 0 &&
- <div className="error accent">{errorMsg}</div>
- }
- {statusMsg.length > 0 &&
- <div className="accent">{statusMsg}</div>
- }
- </div>
- </div>
- </div>
- );
-}
-
-function BackButton() {
- return (
- <Link to={base}>
- <a className="button">&lt; back</a>
- </Link>
- );
-}
-
-function InstancePageWrapped() {
- /* We wrap the component to generate formFields with a setter depending on the domain
- if formFields() is used inside the same component that is re-rendered with their state,
- inputs get re-created on every change, causing them to lose focus, and bad performance
- */
- let [_match, {domain}] = useRoute(`${base}/:domain`);
-
- if (domain == "view") { // from form field submission
- let realDomain = (new URL(document.location)).searchParams.get("domain");
- if (realDomain == undefined) {
- return <Redirect to={base}/>;
- } else {
- domain = realDomain;
- }
- }
-
- function alterDomain([key, val]) {
- return adminActions.updateDomainBlockVal([domain, key, val]);
- }
-
- const fields = formFields(alterDomain, (state) => state.admin.newInstanceBlocks[domain]);
-
- return <InstancePage domain={domain} Form={fields} />;
-}
-
-function InstancePage({domain, Form}) {
- const dispatch = Redux.useDispatch();
- const entry = Redux.useSelector(state => state.admin.newInstanceBlocks[domain]);
- const [_location, setLocation] = useLocation();
-
- React.useEffect(() => {
- if (entry == undefined) {
- dispatch(api.admin.getEditableDomainBlock(domain));
- }
- }, []);
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- if (entry == undefined) {
- return "Loading...";
- }
-
- const updateBlock = submit(
- () => dispatch(api.admin.updateDomainBlock(domain)),
- {setStatus, setError}
- );
-
- const removeBlock = submit(
- () => dispatch(api.admin.removeDomainBlock(domain)),
- {setStatus, setError, startStatus: "Removing", successStatus: "Removed!", onSuccess: () => {
- setLocation(base);
- }}
- );
-
- return (
- <div>
- <h1><BackButton/> Federation settings for: {domain}</h1>
- {entry.new && "No stored block yet, you can add one below:"}
-
- <Form.TextArea
- id="public_comment"
- name="Public comment"
- />
-
- <Form.TextArea
- id="private_comment"
- name="Private comment"
- />
-
- <Form.Checkbox
- id="obfuscate"
- name="Obfuscate domain? "
- />
-
- <div className="messagebutton">
- <button type="submit" onClick={updateBlock}>{entry.new ? "Add block" : "Save block"}</button>
-
- {!entry.new &&
- <button className="danger" onClick={removeBlock}>Remove block</button>
- }
-
- {errorMsg.length > 0 &&
- <div className="error accent">{errorMsg}</div>
- }
- {statusMsg.length > 0 &&
- <div className="accent">{statusMsg}</div>
- }
- </div>
- </div>
- );
-} \ No newline at end of file
diff --git a/web/source/settings-panel/admin/settings.js b/web/source/settings-panel/admin/settings.js
deleted file mode 100644
index 845a1f924..000000000
--- a/web/source/settings-panel/admin/settings.js
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Redux = require("react-redux");
-
-const Submit = require("../components/submit");
-
-const api = require("../lib/api");
-const submit = require("../lib/submit");
-
-const adminActions = require("../redux/reducers/instances").actions;
-
-const {
- TextInput,
- TextArea,
- File
-} = require("../components/form-fields").formFields(adminActions.setAdminSettingsVal, (state) => state.instances.adminSettings);
-
-module.exports = function AdminSettings() {
- const dispatch = Redux.useDispatch();
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- const updateSettings = submit(
- () => dispatch(api.admin.updateInstance()),
- {setStatus, setError}
- );
-
- return (
- <div>
- <h1>Instance Settings</h1>
- <TextInput
- id="title"
- name="Title"
- placeHolder="My GoToSocial instance"
- />
-
- <TextArea
- id="short_description"
- name="Short description"
- placeHolder="A small testing instance for the GoToSocial alpha."
- />
- <TextArea
- id="description"
- name="Full description"
- placeHolder="A small testing instance for the GoToSocial alpha."
- />
-
- <TextInput
- id="contact_account.username"
- name="Contact user (local account username)"
- placeHolder="admin"
- />
- <TextInput
- id="email"
- name="Contact email"
- placeHolder="admin@example.com"
- />
-
- <TextArea
- id="terms"
- name="Terms & Conditions"
- placeHolder=""
- />
-
- {/* <div className="file-upload">
- <h3>Instance avatar</h3>
- <div>
- <img className="preview avatar" src={instance.avatar} alt={instance.avatar ? `Avatar image for the instance` : "No instance avatar image set"} />
- <File
- id="avatar"
- fileType="image/*"
- />
- </div>
- </div>
-
- <div className="file-upload">
- <h3>Instance header</h3>
- <div>
- <img className="preview header" src={instance.header} alt={instance.header ? `Header image for the instance` : "No instance header image set"} />
- <File
- id="header"
- fileType="image/*"
- />
- </div>
- </div> */}
- <Submit onClick={updateSettings} label="Save" errorMsg={errorMsg} statusMsg={statusMsg} />
- </div>
- );
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/components/error.jsx b/web/source/settings-panel/components/error.jsx
deleted file mode 100644
index 13dc686b7..000000000
--- a/web/source/settings-panel/components/error.jsx
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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");
-
-module.exports = function ErrorFallback({error, resetErrorBoundary}) {
- return (
- <div className="error">
- <p>
- {"An error occured, please report this on the "}
- <a href="https://github.com/superseriousbusiness/gotosocial/issues">GoToSocial issue tracker</a>
- {" or "}
- <a href="https://matrix.to/#/#gotosocial-help:superseriousbusiness.org">Matrix support room</a>.
- <br/>Include the details below:
- </p>
- <pre>
- {error.name}: {error.message}
- </pre>
- <pre>
- {error.stack}
- </pre>
- <p>
- <button onClick={resetErrorBoundary}>Try again</button> or <a href="">refresh the page</a>
- </p>
- </div>
- );
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/components/fake-toot.jsx b/web/source/settings-panel/components/fake-toot.jsx
deleted file mode 100644
index f79e24eb9..000000000
--- a/web/source/settings-panel/components/fake-toot.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Redux = require("react-redux");
-
-module.exports = function FakeToot({children}) {
- const account = Redux.useSelector((state) => state.user.profile);
-
- return (
- <div className="toot expanded">
- <div className="contentgrid">
- <span className="avatar">
- <img src={account.avatar} alt=""/>
- </span>
- <span className="displayname">{account.display_name.trim().length > 0 ? account.display_name : account.username}</span>
- <span className="username">@{account.username}</span>
- <div className="text">
- <div className="content">
- {children}
- </div>
- </div>
- </div>
- </div>
- );
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/components/form-fields.jsx b/web/source/settings-panel/components/form-fields.jsx
deleted file mode 100644
index cb402c3b2..000000000
--- a/web/source/settings-panel/components/form-fields.jsx
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Redux = require("react-redux");
-const d = require("dotty");
-const prettierBytes = require("prettier-bytes");
-
-function eventListeners(dispatch, setter, obj) {
- return {
- onTextChange: function (key) {
- return function (e) {
- dispatch(setter([key, e.target.value]));
- };
- },
-
- onCheckChange: function (key) {
- return function (e) {
- dispatch(setter([key, e.target.checked]));
- };
- },
-
- onFileChange: function (key, withPreview) {
- return function (e) {
- let file = e.target.files[0];
- if (withPreview) {
- let old = d.get(obj, key);
- if (old != undefined) {
- URL.revokeObjectURL(old); // no error revoking a non-Object URL as provided by instance
- }
- let objectURL = URL.createObjectURL(file);
- dispatch(setter([key, objectURL]));
- }
- dispatch(setter([`${key}File`, file]));
- };
- }
- };
-}
-
-function get(state, id, defaultVal) {
- let value;
- if (id.includes(".")) {
- value = d.get(state, id);
- } else {
- value = state[id];
- }
- if (value == undefined) {
- value = defaultVal;
- }
- return value;
-}
-
-// function removeFile(name) {
-// return function(e) {
-// e.preventDefault();
-// dispatch(user.setProfileVal([name, ""]));
-// dispatch(user.setProfileVal([`${name}File`, ""]));
-// };
-// }
-
-module.exports = {
- formFields: function formFields(setter, selector) {
- function FormField({
- type, id, name, className="", placeHolder="", fileType="", children=null,
- options=null, inputProps={}, withPreview=true, showSize=false, maxSize=Infinity
- }) {
- const dispatch = Redux.useDispatch();
- let state = Redux.useSelector(selector);
- let {
- onTextChange,
- onCheckChange,
- onFileChange
- } = eventListeners(dispatch, setter, state);
-
- let field;
- let defaultLabel = true;
- if (type == "text") {
- field = <input type="text" id={id} value={get(state, id, "")} placeholder={placeHolder} className={className} onChange={onTextChange(id)} {...inputProps}/>;
- } else if (type == "textarea") {
- field = <textarea type="text" id={id} value={get(state, id, "")} placeholder={placeHolder} className={className} onChange={onTextChange(id)} rows={8} {...inputProps}/>;
- } else if (type == "checkbox") {
- field = <input type="checkbox" id={id} checked={get(state, id, false)} className={className} onChange={onCheckChange(id)} {...inputProps}/>;
- } else if (type == "select") {
- field = (
- <select id={id} value={get(state, id, "")} className={className} onChange={onTextChange(id)} {...inputProps}>
- {options}
- </select>
- );
- } else if (type == "file") {
- defaultLabel = false;
- let file = get(state, `${id}File`);
-
- let size = null;
- if (showSize && file) {
- size = `(${prettierBytes(file.size)})`;
-
- if (file.size > maxSize) {
- size = <span className="error-text">{size}</span>;
- }
- }
-
- field = (
- <>
- <label htmlFor={id} className="file-input button">Browse</label>
- <span>
- {file ? file.name : "no file selected"} {size}
- </span>
- {/* <a onClick={removeFile("header")}>remove</a> */}
- <input className="hidden" id={id} type="file" accept={fileType} onChange={onFileChange(id, withPreview)} {...inputProps}/>
- </>
- );
- } else {
- defaultLabel = false;
- field = `unsupported FormField ${type}, this is a developer error`;
- }
-
- let label = <label htmlFor={id}>{name}</label>;
- return (
- <div className={`form-field ${type}`}>
- {defaultLabel ? label : null} {field}
- {children}
- </div>
- );
- }
-
- return {
- TextInput: function(props) {
- return <FormField type="text" {...props} />;
- },
-
- TextArea: function(props) {
- return <FormField type="textarea" {...props} />;
- },
-
- Checkbox: function(props) {
- return <FormField type="checkbox" {...props} />;
- },
-
- Select: function(props) {
- return <FormField type="select" {...props} />;
- },
-
- File: function(props) {
- return <FormField type="file" {...props} />;
- },
- };
- },
-
- eventListeners
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/components/languages.jsx b/web/source/settings-panel/components/languages.jsx
deleted file mode 100644
index 1522495da..000000000
--- a/web/source/settings-panel/components/languages.jsx
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Languages() {
- return <React.Fragment>
- <option value="AF">Afrikaans</option>
- <option value="SQ">Albanian</option>
- <option value="AR">Arabic</option>
- <option value="HY">Armenian</option>
- <option value="EU">Basque</option>
- <option value="BN">Bengali</option>
- <option value="BG">Bulgarian</option>
- <option value="CA">Catalan</option>
- <option value="KM">Cambodian</option>
- <option value="ZH">Chinese (Mandarin)</option>
- <option value="HR">Croatian</option>
- <option value="CS">Czech</option>
- <option value="DA">Danish</option>
- <option value="NL">Dutch</option>
- <option value="EN">English</option>
- <option value="ET">Estonian</option>
- <option value="FJ">Fiji</option>
- <option value="FI">Finnish</option>
- <option value="FR">French</option>
- <option value="KA">Georgian</option>
- <option value="DE">German</option>
- <option value="EL">Greek</option>
- <option value="GU">Gujarati</option>
- <option value="HE">Hebrew</option>
- <option value="HI">Hindi</option>
- <option value="HU">Hungarian</option>
- <option value="IS">Icelandic</option>
- <option value="ID">Indonesian</option>
- <option value="GA">Irish</option>
- <option value="IT">Italian</option>
- <option value="JA">Japanese</option>
- <option value="JW">Javanese</option>
- <option value="KO">Korean</option>
- <option value="LA">Latin</option>
- <option value="LV">Latvian</option>
- <option value="LT">Lithuanian</option>
- <option value="MK">Macedonian</option>
- <option value="MS">Malay</option>
- <option value="ML">Malayalam</option>
- <option value="MT">Maltese</option>
- <option value="MI">Maori</option>
- <option value="MR">Marathi</option>
- <option value="MN">Mongolian</option>
- <option value="NE">Nepali</option>
- <option value="NO">Norwegian</option>
- <option value="FA">Persian</option>
- <option value="PL">Polish</option>
- <option value="PT">Portuguese</option>
- <option value="PA">Punjabi</option>
- <option value="QU">Quechua</option>
- <option value="RO">Romanian</option>
- <option value="RU">Russian</option>
- <option value="SM">Samoan</option>
- <option value="SR">Serbian</option>
- <option value="SK">Slovak</option>
- <option value="SL">Slovenian</option>
- <option value="ES">Spanish</option>
- <option value="SW">Swahili</option>
- <option value="SV">Swedish </option>
- <option value="TA">Tamil</option>
- <option value="TT">Tatar</option>
- <option value="TE">Telugu</option>
- <option value="TH">Thai</option>
- <option value="BO">Tibetan</option>
- <option value="TO">Tonga</option>
- <option value="TR">Turkish</option>
- <option value="UK">Ukrainian</option>
- <option value="UR">Urdu</option>
- <option value="UZ">Uzbek</option>
- <option value="VI">Vietnamese</option>
- <option value="CY">Welsh</option>
- <option value="XH">Xhosa</option>
- </React.Fragment>;
-};
diff --git a/web/source/settings-panel/components/login.jsx b/web/source/settings-panel/components/login.jsx
deleted file mode 100644
index c67e99acd..000000000
--- a/web/source/settings-panel/components/login.jsx
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Redux = require("react-redux");
-
-const { setInstance } = require("../redux/reducers/oauth").actions;
-const api = require("../lib/api");
-
-module.exports = function Login({error}) {
- const dispatch = Redux.useDispatch();
- const [ instanceField, setInstanceField ] = React.useState("");
- const [ errorMsg, setErrorMsg ] = React.useState();
- const instanceFieldRef = React.useRef("");
-
- React.useEffect(() => {
- // check if current domain runs an instance
- let currentDomain = window.location.origin;
- Promise.try(() => {
- return dispatch(api.instance.fetchWithoutStore(currentDomain));
- }).then(() => {
- if (instanceFieldRef.current.length == 0) { // user hasn't started typing yet
- dispatch(setInstance(currentDomain));
- instanceFieldRef.current = currentDomain;
- setInstanceField(currentDomain);
- }
- }).catch((e) => {
- console.log("Current domain does not host a valid instance: ", e);
- });
- }, []);
-
- function tryInstance() {
- let domain = instanceFieldRef.current;
- Promise.try(() => {
- return dispatch(api.instance.fetchWithoutStore(domain)).catch((e) => {
- // TODO: clearer error messages for common errors
- console.log(e);
- throw e;
- });
- }).then(() => {
- dispatch(setInstance(domain));
-
- return dispatch(api.oauth.register()).catch((e) => {
- console.log(e);
- throw e;
- });
- }).then(() => {
- return dispatch(api.oauth.authorize()); // will send user off-page
- }).catch((e) => {
- setErrorMsg(
- <>
- <b>{e.type}</b>
- <span>{e.message}</span>
- </>
- );
- });
- }
-
- function updateInstanceField(e) {
- if (e.key == "Enter") {
- tryInstance(instanceField);
- } else {
- setInstanceField(e.target.value);
- instanceFieldRef.current = e.target.value;
- }
- }
-
- return (
- <section className="login">
- <h1>OAUTH Login:</h1>
- {error}
- <form onSubmit={(e) => e.preventDefault()}>
- <label htmlFor="instance">Instance: </label>
- <input value={instanceField} onChange={updateInstanceField} id="instance"/>
- {errorMsg &&
- <div className="error">
- {errorMsg}
- </div>
- }
- <button onClick={tryInstance}>Authenticate</button>
- </form>
- </section>
- );
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/components/nav-button.jsx b/web/source/settings-panel/components/nav-button.jsx
deleted file mode 100644
index 3c76711fb..000000000
--- a/web/source/settings-panel/components/nav-button.jsx
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 { Link, useRoute } = require("wouter");
-
-module.exports = function NavButton({href, name}) {
- const [isActive] = useRoute(`${href}/:anything?`);
- return (
- <Link href={href}>
- <a className={isActive ? "active" : ""} data-content={name}>
- {name}
- </a>
- </Link>
- );
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/components/submit.jsx b/web/source/settings-panel/components/submit.jsx
deleted file mode 100644
index 0187fc81f..000000000
--- a/web/source/settings-panel/components/submit.jsx
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Submit({onClick, label, errorMsg, statusMsg}) {
- return (
- <div className="messagebutton">
- <button type="submit" onClick={onClick}>{ label }</button>
- {errorMsg.length > 0 &&
- <div className="error accent">{errorMsg}</div>
- }
- {statusMsg.length > 0 &&
- <div className="accent">{statusMsg}</div>
- }
- </div>
- );
-};
diff --git a/web/source/settings-panel/index.js b/web/source/settings-panel/index.js
deleted file mode 100644
index 34720e818..000000000
--- a/web/source/settings-panel/index.js
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 ReactDom = require("react-dom/client");
-const Redux = require("react-redux");
-const { Switch, Route, Redirect } = require("wouter");
-const { Provider } = require("react-redux");
-const { PersistGate } = require("redux-persist/integration/react");
-
-const { store, persistor } = require("./redux");
-const api = require("./lib/api");
-const oauth = require("./redux/reducers/oauth").actions;
-const { AuthenticationError } = require("./lib/errors");
-
-const Login = require("./components/login");
-
-require("./style.css");
-
-// TODO: nested categories?
-const nav = {
- "User": {
- "Profile": require("./user/profile.js"),
- "Settings": require("./user/settings.js"),
- },
- "Admin": {
- adminOnly: true,
- "Instance Settings": require("./admin/settings.js"),
- "Actions": require("./admin/actions"),
- "Federation": require("./admin/federation.js"),
- "Custom Emoji": require("./admin/emoji.js"),
- }
-};
-
-const { sidebar, panelRouter } = require("./lib/get-views")(nav);
-
-function App() {
- const dispatch = Redux.useDispatch();
-
- const { loginState, isAdmin } = Redux.useSelector((state) => state.oauth);
- const reduxTempStatus = Redux.useSelector((state) => state.temporary.status);
-
- const [errorMsg, setErrorMsg] = React.useState();
- const [tokenChecked, setTokenChecked] = React.useState(false);
-
- React.useEffect(() => {
- if (loginState == "login" || loginState == "callback") {
- Promise.try(() => {
- // Process OAUTH authorization token from URL if available
- if (loginState == "callback") {
- let urlParams = new URLSearchParams(window.location.search);
- let code = urlParams.get("code");
-
- if (code == undefined) {
- setErrorMsg(new Error("Waiting for OAUTH callback but no ?code= provided. You can try logging in again:"));
- } else {
- return dispatch(api.oauth.tokenize(code));
- }
- }
- }).then(() => {
- // Fetch current instance info
- return dispatch(api.instance.fetch());
- }).then(() => {
- // Check currently stored auth token for validity if available
- return dispatch(api.user.fetchAccount());
- }).then(() => {
- setTokenChecked(true);
-
- return dispatch(api.oauth.checkIfAdmin());
- }).catch((e) => {
- if (e instanceof AuthenticationError) {
- dispatch(oauth.remove());
- e.message = "Stored OAUTH token no longer valid, please log in again.";
- }
- setErrorMsg(e);
- console.error(e);
- });
- }
- }, []);
-
- let ErrorElement = null;
- if (errorMsg != undefined) {
- ErrorElement = (
- <div className="error">
- <b>{errorMsg.type}</b>
- <span>{errorMsg.message}</span>
- </div>
- );
- }
-
- const LogoutElement = (
- <button className="logout" onClick={() => { dispatch(api.oauth.logout()); }}>
- Log out
- </button>
- );
-
- if (reduxTempStatus != undefined) {
- return (
- <section>
- {reduxTempStatus}
- </section>
- );
- } else if (tokenChecked && loginState == "login") {
- return (
- <>
- <div className="sidebar">
- {sidebar.all}
- {isAdmin && sidebar.admin}
- {LogoutElement}
- </div>
- <section className="with-sidebar">
- {ErrorElement}
- <Switch>
- {panelRouter.all}
- {isAdmin && panelRouter.admin}
- <Route> {/* default route */}
- <Redirect to="/settings/user" />
- </Route>
- </Switch>
- </section>
- </>
- );
- } else if (loginState == "none") {
- return (
- <Login error={ErrorElement} />
- );
- } else {
- let status;
-
- if (loginState == "login") {
- status = "Verifying stored login...";
- } else if (loginState == "callback") {
- status = "Processing OAUTH callback...";
- }
-
- return (
- <section>
- <div>
- {status}
- </div>
- {ErrorElement}
- {LogoutElement}
- </section>
- );
- }
-
-}
-
-function Main() {
- return (
- <Provider store={store}>
- <PersistGate loading={"loading..."} persistor={persistor}>
- <App />
- </PersistGate>
- </Provider>
- );
-}
-
-const root = ReactDom.createRoot(document.getElementById("root"));
-root.render(<React.StrictMode><Main /></React.StrictMode>); \ No newline at end of file
diff --git a/web/source/settings-panel/lib/api/admin.js b/web/source/settings-panel/lib/api/admin.js
deleted file mode 100644
index 1df47b693..000000000
--- a/web/source/settings-panel/lib/api/admin.js
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 isValidDomain = require("is-valid-domain");
-
-const instance = require("../../redux/reducers/instances").actions;
-const admin = require("../../redux/reducers/admin").actions;
-
-module.exports = function ({ apiCall, getChanges }) {
- const adminAPI = {
- updateInstance: function updateInstance() {
- return function (dispatch, getState) {
- return Promise.try(() => {
- const state = getState().instances.adminSettings;
-
- const update = getChanges(state, {
- formKeys: ["title", "short_description", "description", "contact_account.username", "email", "terms"],
- renamedKeys: {"contact_account.username": "contact_username"},
- // fileKeys: ["avatar", "header"]
- });
-
- return dispatch(apiCall("PATCH", "/api/v1/instance", update, "form"));
- }).then((data) => {
- return dispatch(instance.setInstanceInfo(data));
- });
- };
- },
-
- fetchDomainBlocks: function fetchDomainBlocks() {
- return function (dispatch, _getState) {
- return Promise.try(() => {
- return dispatch(apiCall("GET", "/api/v1/admin/domain_blocks"));
- }).then((data) => {
- return dispatch(admin.setBlockedInstances(data));
- });
- };
- },
-
- updateDomainBlock: function updateDomainBlock(domain) {
- return function (dispatch, getState) {
- return Promise.try(() => {
- const state = getState().admin.newInstanceBlocks[domain];
- const update = getChanges(state, {
- formKeys: ["domain", "obfuscate", "public_comment", "private_comment"],
- });
-
- return dispatch(apiCall("POST", "/api/v1/admin/domain_blocks", update, "form"));
- }).then((block) => {
- return Promise.all([
- dispatch(admin.newDomainBlock([domain, block])),
- dispatch(admin.setDomainBlock([domain, block]))
- ]);
- });
- };
- },
-
- getEditableDomainBlock: function getEditableDomainBlock(domain) {
- return function (dispatch, getState) {
- let data = getState().admin.blockedInstances[domain];
- return dispatch(admin.newDomainBlock([domain, data]));
- };
- },
-
- bulkDomainBlock: function bulkDomainBlock() {
- return function (dispatch, getState) {
- let invalidDomains = [];
- let success = 0;
-
- return Promise.try(() => {
- const state = getState().admin.bulkBlock;
- let list = state.list;
- let domains;
-
- let fields = getChanges(state, {
- formKeys: ["obfuscate", "public_comment", "private_comment"]
- });
-
- let defaultDate = new Date().toUTCString();
-
- if (list[0] == "[") {
- domains = JSON.parse(state.list);
- } else {
- domains = list.split("\n").map((line_) => {
- let line = line_.trim();
- if (line.length == 0) {
- return null;
- }
-
- if (!isValidDomain(line, {wildcard: true, allowUnicode: true})) {
- invalidDomains.push(line);
- return null;
- }
-
- return {
- domain: line,
- created_at: defaultDate,
- ...fields
- };
- }).filter((a) => a != null);
- }
-
- if (domains.length == 0) {
- return;
- }
-
- const update = {
- domains: new Blob([JSON.stringify(domains)], {type: "application/json"})
- };
-
- return dispatch(apiCall("POST", "/api/v1/admin/domain_blocks?import=true", update, "form"));
- }).then((blocks) => {
- if (blocks != undefined) {
- return Promise.each(blocks, (block) => {
- success += 1;
- return dispatch(admin.setDomainBlock([block.domain, block]));
- });
- }
- }).then(() => {
- return {
- success,
- invalidDomains
- };
- });
- };
- },
-
- removeDomainBlock: function removeDomainBlock(domain) {
- return function (dispatch, getState) {
- return Promise.try(() => {
- const id = getState().admin.blockedInstances[domain].id;
- return dispatch(apiCall("DELETE", `/api/v1/admin/domain_blocks/${id}`));
- }).then((removed) => {
- return dispatch(admin.removeDomainBlock(removed.domain));
- });
- };
- },
-
- mediaCleanup: function mediaCleanup(days) {
- return function (dispatch, _getState) {
- return Promise.try(() => {
- return dispatch(apiCall("POST", `/api/v1/admin/media_cleanup?remote_cache_days=${days}`));
- });
- };
- },
-
- fetchCustomEmoji: function fetchCustomEmoji() {
- return function (dispatch, _getState) {
- return Promise.try(() => {
- return dispatch(apiCall("GET", "/api/v1/custom_emojis"));
- }).then((emoji) => {
- return dispatch(admin.setEmoji(emoji));
- });
- };
- },
-
- newEmoji: function newEmoji() {
- return function (dispatch, getState) {
- return Promise.try(() => {
- const state = getState().admin.newEmoji;
-
- const update = getChanges(state, {
- formKeys: ["shortcode"],
- fileKeys: ["image"]
- });
-
- return dispatch(apiCall("POST", "/api/v1/admin/custom_emojis", update, "form"));
- }).then((emoji) => {
- return dispatch(admin.addEmoji(emoji));
- });
- };
- }
- };
- return adminAPI;
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/lib/api/index.js b/web/source/settings-panel/lib/api/index.js
deleted file mode 100644
index e699011bd..000000000
--- a/web/source/settings-panel/lib/api/index.js
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 { isPlainObject } = require("is-plain-object");
-const d = require("dotty");
-
-const { APIError, AuthenticationError } = require("../errors");
-const { setInstanceInfo, setNamedInstanceInfo } = require("../../redux/reducers/instances").actions;
-const oauth = require("../../redux/reducers/oauth").actions;
-
-function apiCall(method, route, payload, type = "json") {
- return function (dispatch, getState) {
- const state = getState();
- let base = state.oauth.instance;
- let auth = state.oauth.token;
- console.log(method, base, route, "auth:", auth != undefined);
-
- return Promise.try(() => {
- let url = new URL(base);
- let [path, query] = route.split("?");
- url.pathname = path;
- if (query != undefined) {
- url.search = query;
- }
- let body;
-
- let headers = {
- "Accept": "application/json",
- };
-
- if (payload != undefined) {
- if (type == "json") {
- headers["Content-Type"] = "application/json";
- body = JSON.stringify(payload);
- } else if (type == "form") {
- const formData = new FormData();
- Object.entries(payload).forEach(([key, val]) => {
- if (isPlainObject(val)) {
- Object.entries(val).forEach(([key2, val2]) => {
- if (val2 != undefined) {
- formData.set(`${key}[${key2}]`, val2);
- }
- });
- } else {
- if (val != undefined) {
- formData.set(key, val);
- }
- }
- });
- body = formData;
- }
- }
-
- if (auth != undefined) {
- headers["Authorization"] = auth;
- }
-
- return fetch(url.toString(), {
- method,
- headers,
- body
- });
- }).then((res) => {
- // try parse json even with error
- let json = res.json().catch((e) => {
- throw new APIError(`JSON parsing error: ${e.message}`);
- });
-
- return Promise.all([res, json]);
- }).then(([res, json]) => {
- if (!res.ok) {
- if (auth != undefined && (res.status == 401 || res.status == 403)) {
- // stored access token is invalid
- throw new AuthenticationError("401: Authentication error", {json, status: res.status});
- } else {
- throw new APIError(json.error, { json });
- }
- } else {
- return json;
- }
- });
- };
-}
-
-function getChanges(state, keys) {
- const { formKeys = [], fileKeys = [], renamedKeys = {} } = keys;
- const update = {};
-
- formKeys.forEach((key) => {
- let value = d.get(state, key);
- if (value == undefined) {
- return;
- }
- if (renamedKeys[key]) {
- key = renamedKeys[key];
- }
- d.put(update, key, value);
- });
-
- fileKeys.forEach((key) => {
- let file = d.get(state, `${key}File`);
- if (file != undefined) {
- if (renamedKeys[key]) {
- key = renamedKeys[key];
- }
- d.put(update, key, file);
- }
- });
-
- return update;
-}
-
-function getCurrentUrl() {
- return `${window.location.origin}${window.location.pathname}`;
-}
-
-function fetchInstanceWithoutStore(domain) {
- return function (dispatch, getState) {
- return Promise.try(() => {
- let lookup = getState().instances.info[domain];
- if (lookup != undefined) {
- return lookup;
- }
-
- // apiCall expects to pull the domain from state,
- // but we don't want to store it there yet
- // so we mock the API here with our function argument
- let fakeState = {
- oauth: { instance: domain }
- };
-
- return apiCall("GET", "/api/v1/instance")(dispatch, () => fakeState);
- }).then((json) => {
- if (json && json.uri) { // TODO: validate instance json more?
- dispatch(setNamedInstanceInfo([domain, json]));
- return json;
- }
- });
- };
-}
-
-function fetchInstance() {
- return function (dispatch, _getState) {
- return Promise.try(() => {
- return dispatch(apiCall("GET", "/api/v1/instance"));
- }).then((json) => {
- if (json && json.uri) {
- dispatch(setInstanceInfo(json));
- return json;
- }
- });
- };
-}
-
-let submoduleArgs = { apiCall, getCurrentUrl, getChanges };
-
-module.exports = {
- instance: {
- fetchWithoutStore: fetchInstanceWithoutStore,
- fetch: fetchInstance
- },
- oauth: require("./oauth")(submoduleArgs),
- user: require("./user")(submoduleArgs),
- admin: require("./admin")(submoduleArgs),
- apiCall,
- getChanges
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/lib/api/oauth.js b/web/source/settings-panel/lib/api/oauth.js
deleted file mode 100644
index 76d0e9d2f..000000000
--- a/web/source/settings-panel/lib/api/oauth.js
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 { OAUTHError, AuthenticationError } = require("../errors");
-
-const oauth = require("../../redux/reducers/oauth").actions;
-const temporary = require("../../redux/reducers/temporary").actions;
-const admin = require("../../redux/reducers/admin").actions;
-
-module.exports = function oauthAPI({ apiCall, getCurrentUrl }) {
- return {
-
- register: function register(scopes = []) {
- return function (dispatch, _getState) {
- return Promise.try(() => {
- return dispatch(apiCall("POST", "/api/v1/apps", {
- client_name: "GoToSocial Settings",
- scopes: scopes.join(" "),
- redirect_uris: getCurrentUrl(),
- website: getCurrentUrl()
- }));
- }).then((json) => {
- json.scopes = scopes;
- dispatch(oauth.setRegistration(json));
- });
- };
- },
-
- authorize: function authorize() {
- return function (dispatch, getState) {
- let state = getState();
- let reg = state.oauth.registration;
- let base = new URL(state.oauth.instance);
-
- base.pathname = "/oauth/authorize";
- base.searchParams.set("client_id", reg.client_id);
- base.searchParams.set("redirect_uri", getCurrentUrl());
- base.searchParams.set("response_type", "code");
- base.searchParams.set("scope", reg.scopes.join(" "));
-
- dispatch(oauth.setLoginState("callback"));
- dispatch(temporary.setStatus("Redirecting to instance login..."));
-
- // send user to instance's login flow
- window.location.assign(base.href);
- };
- },
-
- tokenize: function tokenize(code) {
- return function (dispatch, getState) {
- let reg = getState().oauth.registration;
-
- return Promise.try(() => {
- if (reg == undefined || reg.client_id == undefined) {
- throw new OAUTHError("Callback code present, but no client registration is available from localStorage. \nNote: localStorage is unavailable in Private Browsing.");
- }
-
- return dispatch(apiCall("POST", "/oauth/token", {
- client_id: reg.client_id,
- client_secret: reg.client_secret,
- redirect_uri: getCurrentUrl(),
- grant_type: "authorization_code",
- code: code
- }));
- }).then((json) => {
- window.history.replaceState({}, document.title, window.location.pathname);
- return dispatch(oauth.login(json));
- });
- };
- },
-
- checkIfAdmin: function checkIfAdmin() {
- return function (dispatch, getState) {
- const state = getState();
- let stored = state.oauth.isAdmin;
- if (stored != undefined) {
- return stored;
- }
-
- // newer GoToSocial version will include a `role` in the Account data, check that first
- // TODO: check account data for admin status
-
- // no role info, try fetching an admin-only route and see if we get an error
- return Promise.try(() => {
- return dispatch(apiCall("GET", "/api/v1/admin/domain_blocks"));
- }).then((data) => {
- return Promise.all([
- dispatch(oauth.setAdmin(true)),
- dispatch(admin.setBlockedInstances(data))
- ]);
- }).catch(AuthenticationError, () => {
- return dispatch(oauth.setAdmin(false));
- });
- };
- },
-
- logout: function logout() {
- return function (dispatch, _getState) {
- // TODO: GoToSocial does not have a logout API route yet
-
- return dispatch(oauth.remove());
- };
- }
- };
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/lib/api/user.js b/web/source/settings-panel/lib/api/user.js
deleted file mode 100644
index 18b54bd73..000000000
--- a/web/source/settings-panel/lib/api/user.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 user = require("../../redux/reducers/user").actions;
-
-module.exports = function ({ apiCall, getChanges }) {
- function updateCredentials(selector, keys) {
- return function (dispatch, getState) {
- return Promise.try(() => {
- const state = selector(getState());
-
- const update = getChanges(state, keys);
-
- return dispatch(apiCall("PATCH", "/api/v1/accounts/update_credentials", update, "form"));
- }).then((account) => {
- return dispatch(user.setAccount(account));
- });
- };
- }
-
- return {
- fetchAccount: function fetchAccount() {
- return function (dispatch, _getState) {
- return Promise.try(() => {
- return dispatch(apiCall("GET", "/api/v1/accounts/verify_credentials"));
- }).then((account) => {
- return dispatch(user.setAccount(account));
- });
- };
- },
-
- updateProfile: function updateProfile() {
- const formKeys = ["display_name", "locked", "source", "custom_css", "source.note"];
- const renamedKeys = {
- "source.note": "note"
- };
- const fileKeys = ["header", "avatar"];
-
- return updateCredentials((state) => state.user.profile, {formKeys, renamedKeys, fileKeys});
- },
-
- updateSettings: function updateProfile() {
- const formKeys = ["source"];
-
- return updateCredentials((state) => state.user.settings, {formKeys});
- }
- };
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/lib/errors.js b/web/source/settings-panel/lib/errors.js
deleted file mode 100644
index c2f781cb2..000000000
--- a/web/source/settings-panel/lib/errors.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 createError = require("create-error");
-
-module.exports = {
- APIError: createError("APIError"),
- OAUTHError: createError("OAUTHError"),
- AuthenticationError: createError("AuthenticationError"),
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/lib/get-views.js b/web/source/settings-panel/lib/get-views.js
deleted file mode 100644
index 39f627435..000000000
--- a/web/source/settings-panel/lib/get-views.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Redux = require("react-redux");
-const { Link, Route, Switch, Redirect } = require("wouter");
-const { ErrorBoundary } = require("react-error-boundary");
-
-const ErrorFallback = require("../components/error");
-const NavButton = require("../components/nav-button");
-
-function urlSafe(str) {
- return str.toLowerCase().replace(/\s+/g, "-");
-}
-
-module.exports = function getViews(struct) {
- const sidebar = {
- all: [],
- admin: [],
- };
-
- const panelRouter = {
- all: [],
- admin: [],
- };
-
- Object.entries(struct).forEach(([name, entries]) => {
- let sidebarEl = sidebar.all;
- let panelRouterEl = panelRouter.all;
-
- if (entries.adminOnly) {
- sidebarEl = sidebar.admin;
- panelRouterEl = panelRouter.admin;
- delete entries.adminOnly;
- }
-
- let base = `/settings/${urlSafe(name)}`;
-
- let links = [];
-
- let firstRoute;
-
- Object.entries(entries).forEach(([name, ViewComponent]) => {
- let url = `${base}/${urlSafe(name)}`;
-
- if (firstRoute == undefined) {
- firstRoute = url;
- }
-
- panelRouterEl.push((
- <Route path={`${url}/:page?`} key={url}>
- <ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => { }}>
- {/* FIXME: implement onReset */}
- <ViewComponent />
- </ErrorBoundary>
- </Route>
- ));
-
- links.push(
- <NavButton key={url} href={url} name={name} />
- );
- });
-
- panelRouterEl.push(
- <Route key={base} path={base}>
- <Redirect to={firstRoute} />
- </Route>
- );
-
- sidebarEl.push(
- <React.Fragment key={name}>
- <Link href={firstRoute}>
- <a>
- <h2>{name}</h2>
- </a>
- </Link>
- <nav>
- {links}
- </nav>
- </React.Fragment>
- );
- });
-
- return { sidebar, panelRouter };
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/lib/panel.js b/web/source/settings-panel/lib/panel.js
deleted file mode 100644
index df723bc74..000000000
--- a/web/source/settings-panel/lib/panel.js
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 ReactDom = require("react-dom");
-
-const oauthLib = require("./oauth");
-
-module.exports = function createPanel(clientName, scope, Component) {
- ReactDom.render(<Panel/>, document.getElementById("root"));
-
- function Panel() {
- const [oauth, setOauth] = React.useState();
- const [hasAuth, setAuth] = React.useState(false);
- const [oauthState, setOauthState] = React.useState(localStorage.getItem("oauth"));
-
- React.useEffect(() => {
- let state = localStorage.getItem("oauth");
- if (state != undefined) {
- state = JSON.parse(state);
- let restoredOauth = oauthLib(state.config, state);
- Promise.try(() => {
- return restoredOauth.callback();
- }).then(() => {
- setAuth(true);
- });
- setOauth(restoredOauth);
- }
- }, [setAuth, setOauth]);
-
- if (!hasAuth && oauth && oauth.isAuthorized()) {
- setAuth(true);
- }
-
- if (oauth && oauth.isAuthorized()) {
- return <Component oauth={oauth} />;
- } else if (oauthState != undefined) {
- return "processing oauth...";
- } else {
- return <Auth setOauth={setOauth} />;
- }
- }
-
- function Auth({setOauth}) {
- const [ instance, setInstance ] = React.useState("");
-
- React.useEffect(() => {
- let isStillMounted = true;
- // check if current domain runs an instance
- let thisUrl = new URL(window.location.origin);
- thisUrl.pathname = "/api/v1/instance";
- Promise.try(() => {
- return fetch(thisUrl.href);
- }).then((res) => {
- if (res.status == 200) {
- return res.json();
- }
- }).then((json) => {
- if (json && json.uri && isStillMounted) {
- setInstance(json.uri);
- }
- }).catch((e) => {
- console.log("error checking instance response:", e);
- });
-
- return () => {
- // cleanup function
- isStillMounted = false;
- };
- }, []);
-
- function doAuth() {
- return Promise.try(() => {
- return new URL(instance);
- }).catch(TypeError, () => {
- return new URL(`https://${instance}`);
- }).then((parsedURL) => {
- let url = parsedURL.toString();
- let oauth = oauthLib({
- instance: url,
- client_name: clientName,
- scope: scope,
- website: window.location.href
- });
- setOauth(oauth);
- setInstance(url);
- return oauth.register().then(() => {
- return oauth;
- });
- }).then((oauth) => {
- return oauth.authorize();
- }).catch((e) => {
- console.log("error authenticating:", e);
- });
- }
-
- function updateInstance(e) {
- if (e.key == "Enter") {
- doAuth();
- } else {
- setInstance(e.target.value);
- }
- }
-
- return (
- <section className="login">
- <h1>OAUTH Login:</h1>
- <form onSubmit={(e) => e.preventDefault()}>
- <label htmlFor="instance">Instance: </label>
- <input value={instance} onChange={updateInstance} id="instance"/>
- <button onClick={doAuth}>Authenticate</button>
- </form>
- </section>
- );
- }
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/lib/submit.js b/web/source/settings-panel/lib/submit.js
deleted file mode 100644
index f268b5cf9..000000000
--- a/web/source/settings-panel/lib/submit.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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");
-
-module.exports = function submit(func, {
- setStatus, setError,
- startStatus="PATCHing", successStatus="Saved!",
- onSuccess,
- onError
-}) {
- return function() {
- setStatus(startStatus);
- setError("");
- return Promise.try(() => {
- return func();
- }).then(() => {
- setStatus(successStatus);
- if (onSuccess != undefined) {
- return onSuccess();
- }
- }).catch((e) => {
- setError(e.message);
- setStatus("");
- console.error(e);
- if (onError != undefined) {
- onError(e);
- }
- });
- };
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/redux/index.js b/web/source/settings-panel/redux/index.js
deleted file mode 100644
index e0dbe9b23..000000000
--- a/web/source/settings-panel/redux/index.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 { createStore, combineReducers, applyMiddleware } = require("redux");
-const { persistStore, persistReducer } = require("redux-persist");
-const thunk = require("redux-thunk").default;
-const { composeWithDevTools } = require("redux-devtools-extension");
-
-const persistConfig = {
- key: "gotosocial-settings",
- storage: require("redux-persist/lib/storage").default,
- stateReconciler: require("redux-persist/lib/stateReconciler/autoMergeLevel2").default,
- whitelist: ["oauth"],
- blacklist: ["temporary"]
-};
-
-const combinedReducers = combineReducers({
- oauth: require("./reducers/oauth").reducer,
- instances: require("./reducers/instances").reducer,
- temporary: require("./reducers/temporary").reducer,
- user: require("./reducers/user").reducer,
- admin: require("./reducers/admin").reducer,
-});
-
-const persistedReducer = persistReducer(persistConfig, combinedReducers);
-const composedEnhancer = composeWithDevTools(applyMiddleware(thunk));
-
-const store = createStore(persistedReducer, composedEnhancer);
-const persistor = persistStore(store);
-
-module.exports = { store, persistor }; \ No newline at end of file
diff --git a/web/source/settings-panel/redux/reducers/admin.js b/web/source/settings-panel/redux/reducers/admin.js
deleted file mode 100644
index 20d3d748d..000000000
--- a/web/source/settings-panel/redux/reducers/admin.js
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 { createSlice } = require("@reduxjs/toolkit");
-const defaultValue = require("default-value");
-
-function sortBlocks(blocks) {
- return blocks.sort((a, b) => { // alphabetical sort
- return a.domain.localeCompare(b.domain);
- });
-}
-
-function emptyBlock() {
- return {
- public_comment: "",
- private_comment: "",
- obfuscate: false
- };
-}
-
-function emptyEmojiForm() {
- return {
- shortcode: ""
- };
-}
-
-module.exports = createSlice({
- name: "admin",
- initialState: {
- loadedBlockedInstances: false,
- blockedInstances: undefined,
- bulkBlock: {
- list: "",
- exportType: "plain",
- ...emptyBlock()
- },
- newInstanceBlocks: {},
- emoji: {},
- newEmoji: emptyEmojiForm()
- },
- reducers: {
- setBlockedInstances: (state, { payload }) => {
- state.blockedInstances = {};
- sortBlocks(payload).forEach((entry) => {
- state.blockedInstances[entry.domain] = entry;
- });
- state.loadedBlockedInstances = true;
- },
-
- newDomainBlock: (state, { payload: [domain, data] }) => {
- if (data == undefined) {
- data = {
- new: true,
- domain,
- ...emptyBlock()
- };
- }
- state.newInstanceBlocks[domain] = data;
- },
-
- setDomainBlock: (state, { payload: [domain, data = {}] }) => {
- state.blockedInstances[domain] = data;
- },
-
- removeDomainBlock: (state, {payload: domain}) => {
- delete state.blockedInstances[domain];
- },
-
- updateDomainBlockVal: (state, { payload: [domain, key, val] }) => {
- state.newInstanceBlocks[domain][key] = val;
- },
-
- updateBulkBlockVal: (state, { payload: [key, val] }) => {
- state.bulkBlock[key] = val;
- },
-
- resetBulkBlockVal: (state, { _payload }) => {
- state.bulkBlock = {
- list: "",
- exportType: "plain",
- ...emptyBlock()
- };
- },
-
- exportToField: (state, { _payload }) => {
- state.bulkBlock.list = Object.values(state.blockedInstances).map((entry) => {
- return entry.domain;
- }).join("\n");
- },
-
- setEmoji: (state, {payload}) => {
- state.emoji = {};
- payload.forEach((emoji) => {
- if (emoji.category == undefined) {
- emoji.category = "Unsorted";
- }
- state.emoji[emoji.category] = defaultValue(state.emoji[emoji.category], []);
- state.emoji[emoji.category].push(emoji);
- });
- },
-
- updateNewEmojiVal: (state, { payload: [key, val] }) => {
- state.newEmoji[key] = val;
- },
-
- addEmoji: (state, {payload: emoji}) => {
- if (emoji.category == undefined) {
- emoji.category = "Unsorted";
- }
- state.emoji[emoji.category] = defaultValue(state.emoji[emoji.category], []);
- state.emoji[emoji.category].push(emoji);
- },
- }
-}); \ No newline at end of file
diff --git a/web/source/settings-panel/redux/reducers/instances.js b/web/source/settings-panel/redux/reducers/instances.js
deleted file mode 100644
index 3ad5bb7cb..000000000
--- a/web/source/settings-panel/redux/reducers/instances.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 {createSlice} = require("@reduxjs/toolkit");
-const d = require("dotty");
-
-module.exports = createSlice({
- name: "instances",
- initialState: {
- info: {},
- },
- reducers: {
- setNamedInstanceInfo: (state, {payload}) => {
- let [key, info] = payload;
- state.info[key] = info;
- },
- setInstanceInfo: (state, {payload}) => {
- state.current = payload;
- state.adminSettings = payload;
- },
- setAdminSettingsVal: (state, {payload: [key, val]}) => {
- d.put(state.adminSettings, key, val);
- }
- }
-}); \ No newline at end of file
diff --git a/web/source/settings-panel/redux/reducers/oauth.js b/web/source/settings-panel/redux/reducers/oauth.js
deleted file mode 100644
index c332a7d06..000000000
--- a/web/source/settings-panel/redux/reducers/oauth.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 {createSlice} = require("@reduxjs/toolkit");
-
-module.exports = createSlice({
- name: "oauth",
- initialState: {
- loginState: 'none',
- },
- reducers: {
- setInstance: (state, {payload}) => {
- state.instance = payload;
- },
- setRegistration: (state, {payload}) => {
- state.registration = payload;
- },
- setLoginState: (state, {payload}) => {
- state.loginState = payload;
- },
- login: (state, {payload}) => {
- state.token = `${payload.token_type} ${payload.access_token}`;
- state.loginState = "login";
- },
- remove: (state, {_payload}) => {
- delete state.token;
- delete state.registration;
- delete state.isAdmin;
- state.loginState = "none";
- },
- setAdmin: (state, {payload}) => {
- state.isAdmin = payload;
- }
- }
-}); \ No newline at end of file
diff --git a/web/source/settings-panel/redux/reducers/temporary.js b/web/source/settings-panel/redux/reducers/temporary.js
deleted file mode 100644
index c887d2eee..000000000
--- a/web/source/settings-panel/redux/reducers/temporary.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 {createSlice} = require("@reduxjs/toolkit");
-
-module.exports = createSlice({
- name: "temporary",
- initialState: {
- },
- reducers: {
- setStatus: function(state, {payload}) {
- state.status = payload;
- }
- }
-}); \ No newline at end of file
diff --git a/web/source/settings-panel/redux/reducers/user.js b/web/source/settings-panel/redux/reducers/user.js
deleted file mode 100644
index b4463c9f9..000000000
--- a/web/source/settings-panel/redux/reducers/user.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 { createSlice } = require("@reduxjs/toolkit");
-const d = require("dotty");
-const defaultValue = require("default-value");
-
-module.exports = createSlice({
- name: "user",
- initialState: {
- profile: {},
- settings: {}
- },
- reducers: {
- setAccount: (state, { payload }) => {
- payload.source = defaultValue(payload.source, {});
- payload.source.language = defaultValue(payload.source.language.toUpperCase(), "EN");
- payload.source.status_format = defaultValue(payload.source.status_format, "plain");
- payload.source.sensitive = defaultValue(payload.source.sensitive, false);
-
- state.profile = payload;
- // /user/settings only needs a copy of the 'source' obj
- state.settings = {
- source: payload.source
- };
- },
- setProfileVal: (state, { payload: [key, val] }) => {
- d.put(state.profile, key, val);
- },
- setSettingsVal: (state, { payload: [key, val] }) => {
- d.put(state.settings, key, val);
- }
- }
-}); \ No newline at end of file
diff --git a/web/source/settings-panel/style.css b/web/source/settings-panel/style.css
deleted file mode 100644
index 35d11fa08..000000000
--- a/web/source/settings-panel/style.css
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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/>.
-*/
-
-body {
- grid-template-rows: auto 1fr;
-}
-
-.content {
- grid-column: 1 / span 3; /* stretch entire width, to fit panel + sidebar nav */
-}
-
-section {
- grid-column: 2;
-}
-
-#root {
- display: grid;
- grid-template-columns: 1fr 90ch 1fr;
- width: 100vw;
- max-width: 100vw;
- box-sizing: border-box;
-
- section.with-sidebar {
- border-left: none;
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
-
- & > div {
- border-left: 0.2rem solid $border-accent;
- padding-left: 0.4rem;
- display: flex;
- flex-direction: column;
- gap: 0.5rem;
- margin: 2rem 0;
-
- h2 {
- margin: 0;
- margin-bottom: 0.5rem;
- }
-
- &:only-child {
- border-left: none;
- }
-
- &:first-child {
- margin-top: 0;
- }
-
- &:last-child {
- margin-bottom: 0;
- }
- }
- }
-
- .sidebar {
- align-self: start;
- justify-self: end;
- background: $settings-nav-bg;
- border: $boxshadow-border;
- box-shadow: $boxshadow;
- border-radius: $br;
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
- display: flex;
- flex-direction: column;
- min-width: 12rem;
-
- a {
- text-decoration: none;
- }
-
- a:first-child h2 {
- border-top-left-radius: $br;
- }
-
- h2 {
- margin: 0;
- padding: 0.5rem;
- font-size: 0.9rem;
- font-weight: bold;
- text-transform: uppercase;
- color: $settings-nav-header-fg;
- background: $settings-nav-header-bg;
- }
-
- nav {
- display: flex;
- flex-direction: column;
-
- a {
- padding: 1rem;
- text-decoration: none;
- transition: 0.1s;
- color: $fg;
-
- &:hover {
- color: $settings-nav-fg-hover;
- background: $settings-nav-bg-hover;
- }
-
- &.active {
- color: $settings-nav-fg-active;
- background: $settings-nav-bg-active;
- font-weight: bold;
- text-decoration: underline;
- }
-
- /* reserve space for bold version of the element, so .active doesn't
- change container size */
- &::after {
- font-weight: bold;
- text-decoration: underline;
- display: block;
- content: attr(data-content);
- height: 1px;
- color: transparent;
- overflow: hidden;
- visibility: hidden;
- }
- }
- }
-
-
- nav:last-child a:last-child {
- border-bottom-left-radius: $br;
- border-bottom: none;
- }
- }
-}
-
-.capitalize {
- text-transform: capitalize;
-}
-
-section {
- margin-bottom: 1rem;
-}
-
-input, select, textarea {
- box-sizing: border-box;
-}
-
-.error {
- color: $error-fg;
- background: $error-bg;
- border: 0.02rem solid $error-fg;
- border-radius: $br;
- font-weight: bold;
- padding: 0.5rem;
- white-space: pre-wrap;
-
- a {
- color: $error-link;
- }
-
- pre {
- background: $bg;
- color: $fg;
- padding: 1rem;
- overflow: auto;
- margin: 0;
- }
-}
-
-.hidden {
- display: none;
-}
-
-.messagebutton, .messagebutton > div {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
-
- div.padded {
- margin-left: 1rem;
- }
-
- button, .button {
- white-space: nowrap;
- margin-right: 1rem;
- }
-}
-
-.messagebutton > div {
- button, .button {
- margin-top: 1rem;
- }
-}
-
-.notImplemented {
- border: 2px solid rgb(70, 79, 88);
- background: repeating-linear-gradient(
- -45deg,
- #525c66,
- #525c66 10px,
- rgb(70, 79, 88) 10px,
- rgb(70, 79, 88) 20px
- ) !important;
-}
-
-section.with-sidebar > div {
- display: flex;
- flex-direction: column;
- gap: 1rem;
-
- input, textarea {
- width: 100%;
- line-height: 1.5rem;
- }
-
- input[type=checkbox] {
- justify-self: start;
- width: initial;
- }
-
- input:read-only {
- border: none;
- }
-
- input:invalid {
- border-color: red;
- }
-
- textarea {
- width: 100%;
- }
-
- h1 {
- margin-bottom: 0.5rem;
- }
-
- .moreinfolink {
- font-size: 0.9em;
- }
-
- .labelinput .border {
- border-radius: 0.2rem;
- border: 0.15rem solid $border_accent;
- padding: 0.3rem;
- display: flex;
- flex-direction: column;
- }
-
- .file-input.button {
- display: inline-block;
- font-size: 1rem;
- font-weight: normal;
- padding: 0.3rem 0.3rem;
- align-self: flex-start;
- margin-right: 0.2rem;
- }
-
- .labelinput, .labelselect {
- display: flex;
- flex-direction: column;
- gap: 0.4rem;
- }
-
- .labelcheckbox {
- display: flex;
- gap: 0.4rem;
- }
-
- .titlesave {
- display: flex;
- flex-wrap: wrap;
- gap: 0.4rem;
- }
-}
-
-.file-upload > div {
- display: flex;
- gap: 1rem;
-
- img {
- height: 8rem;
- border: 0.2rem solid $border-accent;
- }
-
- img.avatar {
- width: 8rem;
- }
-
- img.header {
- width: 24rem;
- }
-}
-
-.user-profile {
- .overview {
- display: grid;
- grid-template-columns: 70% 30%;
-
- .basic {
- margin-top: -4.5rem;
-
- .avatar {
- height: 5rem;
- width: 5rem;
- }
-
- .displayname {
- font-size: 1.3rem;
- padding-top: 0;
- padding-bottom: 0;
- margin-top: 0.7rem;
- }
- }
-
- .files {
- width: 100%;
- margin: 1rem;
- margin-right: 0;
- display: flex;
- flex-direction: column;
- justify-content: center;
-
- div.form-field {
- width: 100%;
- display: flex;
-
- span {
- flex: 1 1 auto;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- padding: 0.3rem 0;
- }
- }
-
- h3 {
- margin-top: 0;
- margin-bottom: 0.5rem;
- }
-
- div:first-child {
- margin-bottom: 1rem;
- }
-
- span {
- font-style: italic;
- }
- }
- }
-}
-
-.form-field label {
- font-weight: bold;
-}
-
-.list {
- display: flex;
- flex-direction: column;
- margin-top: 0.5rem;
- max-height: 40rem;
- overflow: auto;
-
- .entry {
- display: flex;
- flex-wrap: wrap;
- background: $settings-entry-bg;
-
- &:hover {
- background: $settings-entry-hover-bg;
- }
- }
-}
-
-.instance-list {
- .filter {
- display: flex;
- gap: 0.5rem;
-
- input {
- width: auto;
- flex: 1 1 auto;
- }
- }
-
- .entry {
- padding: 0.3rem;
- margin: 0.2rem 0;
-
- #domain {
- flex: 1 1 auto;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- }
- }
-}
-
-.bulk h2 {
- display: flex;
- justify-content: space-between;
-}
-
-.emoji-list {
- background: $settings-entry-bg;
-
- .entry {
- padding: 0.5rem;
- flex-direction: column;
-
- .emoji-group {
- display: flex;
-
- a {
- border-radius: $br;
- padding: 0.4rem;
- line-height: 0;
-
- img {
- height: 2rem;
- width: 2rem;
- object-fit: contain;
- vertical-align: middle;
- }
-
- &:hover {
- background: $settings-entry-hover-bg;
- }
- }
- }
-
- &:hover {
- background: inherit;
- }
- }
-}
-
-.toot {
- padding-top: 0.5rem;
- .contentgrid {
- padding: 0 0.5rem;
- }
-}
-
-@media screen and (max-width: 100ch) {
- #root {
- padding: 1rem;
- grid-template-columns: 100%;
- grid-template-rows: auto auto;
-
- .sidebar {
- justify-self: auto;
- margin-bottom: 2rem;
- }
-
- .sidebar, section.with-sidebar {
- border-top-left-radius: $br;
- border-top-right-radius: $br;
- border-bottom-left-radius: $br;
- border-bottom-right-radius: $br;
- }
-
- .sidebar a:first-child h2 {
- border-top-right-radius: $br;
- }
- }
-
- section {
- grid-column: 1;
- }
-
- .user-profile .overview {
- grid-template-columns: 100%;
- grid-template-rows: auto auto;
-
- .files {
- margin: 0;
- margin-top: 1rem;
- }
- }
-
- main section {
- padding: 0.75rem;
- }
-
- .instance-list .filter {
- flex-direction: column;
- }
-} \ No newline at end of file
diff --git a/web/source/settings-panel/user/profile.js b/web/source/settings-panel/user/profile.js
deleted file mode 100644
index 7cf3a7b52..000000000
--- a/web/source/settings-panel/user/profile.js
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Redux = require("react-redux");
-
-const Submit = require("../components/submit");
-
-const api = require("../lib/api");
-const user = require("../redux/reducers/user").actions;
-const submit = require("../lib/submit");
-
-const { formFields } = require("../components/form-fields");
-
-const {
- TextInput,
- TextArea,
- Checkbox,
- File
-} = formFields(user.setProfileVal, (state) => state.user.profile);
-
-module.exports = function UserProfile() {
- const dispatch = Redux.useDispatch();
- const account = Redux.useSelector(state => state.user.profile);
- const instance = Redux.useSelector(state => state.instances.current);
-
- const allowCustomCSS = instance.configuration.accounts.allow_custom_css;
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- const saveProfile = submit(
- () => dispatch(api.user.updateProfile()),
- {setStatus, setError}
- );
-
- return (
- <div className="user-profile">
- <h1>Profile</h1>
- <div className="overview">
- <div className="profile">
- <div className="headerimage">
- <img className="headerpreview" src={account.header} alt={account.header ? `header image for ${account.username}` : "None set"} />
- </div>
- <div className="basic">
- <div id="profile-basic-filler2"></div>
- <span className="avatar"><img className="avatarpreview" src={account.avatar} alt={account.avatar ? `avatar image for ${account.username}` : "None set"} /></span>
- <div className="displayname">{account.display_name.trim().length > 0 ? account.display_name : account.username}</div>
- <div className="username"><span>@{account.username}</span></div>
- </div>
- </div>
- <div className="files">
- <div>
- <h3>Header</h3>
- <File
- id="header"
- fileType="image/*"
- />
- </div>
- <div>
- <h3>Avatar</h3>
- <File
- id="avatar"
- fileType="image/*"
- />
- </div>
- </div>
- </div>
- <TextInput
- id="display_name"
- name="Name"
- placeHolder="A GoToSocial user"
- />
- <TextArea
- id="source.note"
- name="Bio"
- placeHolder="Just trying out GoToSocial, my pronouns are they/them and I like sloths."
- />
- <Checkbox
- id="locked"
- name="Manually approve follow requests? "
- />
- { !allowCustomCSS ? null :
- <TextArea
- id="custom_css"
- name="Custom CSS"
- className="monospace"
- >
- <a href="https://docs.gotosocial.org/en/latest/user_guide/custom_css" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about custom profile CSS (opens in a new tab)</a>
- </TextArea>
- }
- <Submit onClick={saveProfile} label="Save profile info" errorMsg={errorMsg} statusMsg={statusMsg} />
- </div>
- );
-}; \ No newline at end of file
diff --git a/web/source/settings-panel/user/settings.js b/web/source/settings-panel/user/settings.js
deleted file mode 100644
index ccb3e911d..000000000
--- a/web/source/settings-panel/user/settings.js
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021-2022 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 Redux = require("react-redux");
-
-const api = require("../lib/api");
-const user = require("../redux/reducers/user").actions;
-const submit = require("../lib/submit");
-
-const Languages = require("../components/languages");
-const Submit = require("../components/submit");
-
-const {
- Checkbox,
- Select,
-} = require("../components/form-fields").formFields(user.setSettingsVal, (state) => state.user.settings);
-
-module.exports = function UserSettings() {
- const dispatch = Redux.useDispatch();
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- const updateSettings = submit(
- () => dispatch(api.user.updateSettings()),
- {setStatus, setError}
- );
-
- return (
- <>
- <div className="user-settings">
- <h1>Post settings</h1>
- <Select id="source.language" name="Default post language" options={
- <Languages/>
- }>
- </Select>
- <Select id="source.privacy" name="Default post privacy" options={
- <>
- <option value="private">Private / followers-only</option>
- <option value="unlisted">Unlisted</option>
- <option value="public">Public</option>
- </>
- }>
- <a href="https://docs.gotosocial.org/en/latest/user_guide/posts/#privacy-settings" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about post privacy settings (opens in a new tab)</a>
- </Select>
- <Select id="source.status_format" name="Default post format" options={
- <>
- <option value="plain">Plain (default)</option>
- <option value="markdown">Markdown</option>
- </>
- }>
- <a href="https://docs.gotosocial.org/en/latest/user_guide/posts/#input-types" target="_blank" className="moreinfolink" rel="noreferrer">Learn more about post format settings (opens in a new tab)</a>
- </Select>
- <Checkbox
- id="source.sensitive"
- name="Mark my posts as sensitive by default"
- />
-
- <Submit onClick={updateSettings} label="Save post settings" errorMsg={errorMsg} statusMsg={statusMsg}/>
- </div>
- <div>
- <PasswordChange/>
- </div>
- </>
- );
-};
-
-function PasswordChange() {
- const dispatch = Redux.useDispatch();
-
- const [errorMsg, setError] = React.useState("");
- const [statusMsg, setStatus] = React.useState("");
-
- const [oldPassword, setOldPassword] = React.useState("");
- const [newPassword, setNewPassword] = React.useState("");
- const [newPasswordConfirm, setNewPasswordConfirm] = React.useState("");
-
- function changePassword() {
- if (newPassword !== newPasswordConfirm) {
- setError("New password and confirm new password did not match!");
- return;
- }
-
- setStatus("PATCHing");
- setError("");
- return Promise.try(() => {
- let data = {
- old_password: oldPassword,
- new_password: newPassword
- };
- return dispatch(api.apiCall("POST", "/api/v1/user/password_change", data, "form"));
- }).then(() => {
- setStatus("Saved!");
- setOldPassword("");
- setNewPassword("");
- setNewPasswordConfirm("");
- }).catch((e) => {
- setError(e.message);
- setStatus("");
- });
- }
-
- return (
- <>
- <h1>Change password</h1>
- <div className="labelinput">
- <label htmlFor="password">Current password</label>
- <input name="password" id="password" type="password" autoComplete="current-password" value={oldPassword} onChange={(e) => setOldPassword(e.target.value)} />
- </div>
- <div className="labelinput">
- <label htmlFor="new-password">New password</label>
- <input name="new-password" id="new-password" type="password" autoComplete="new-password" value={newPassword} onChange={(e) => setNewPassword(e.target.value)} />
- </div>
- <div className="labelinput">
- <label htmlFor="confirm-new-password">Confirm new password</label>
- <input name="confirm-new-password" id="confirm-new-password" type="password" autoComplete="new-password" value={newPasswordConfirm} onChange={(e) => setNewPasswordConfirm(e.target.value)} />
- </div>
- <Submit onClick={changePassword} label="Save new password" errorMsg={errorMsg} statusMsg={statusMsg}/>
- </>
- );
-} \ No newline at end of file