diff --git a/web/source/package.json b/web/source/package.json
index a12d6fe77..8a561af42 100644
--- a/web/source/package.json
+++ b/web/source/package.json
@@ -34,6 +34,7 @@
"pretty-bytes": "^5.6.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
+ "react-error-boundary": "^3.1.4",
"reactify": "^1.1.1",
"uglifyify": "^5.0.2",
"wouter": "^2.8.0-alpha.2"
diff --git a/web/source/settings-panel/admin/index.js b/web/source/settings-panel/admin/index.js
new file mode 100644
index 000000000..551f71af2
--- /dev/null
+++ b/web/source/settings-panel/admin/index.js
@@ -0,0 +1,33 @@
+/*
+ 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 .
+*/
+
+"use strict";
+
+const Promise = require("bluebird");
+const React = require("react");
+const { Route, Switch } = require("wouter");
+
+module.exports = function AdminPanel({oauth, routes}) {
+ return (
+
+ {routes.map(([path, component]) => {
+ return ;
+ })}
+
+ );
+};
\ No newline at end of file
diff --git a/web/source/settings-panel/components/error.js b/web/source/settings-panel/components/error.js
new file mode 100644
index 000000000..e5e0ff139
--- /dev/null
+++ b/web/source/settings-panel/components/error.js
@@ -0,0 +1,45 @@
+/*
+ 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 .
+*/
+
+"use strict";
+
+const Promise = require("bluebird");
+const React = require("react");
+
+module.exports = function ErrorFallback({error, resetErrorBoundary}) {
+ return (
+
+ );
+};
\ No newline at end of file
diff --git a/web/source/lib/submit.js b/web/source/settings-panel/components/submit.js
similarity index 100%
rename from web/source/lib/submit.js
rename to web/source/settings-panel/components/submit.js
diff --git a/web/source/settings-panel/index.js b/web/source/settings-panel/index.js
index c2586d12e..a69819f9c 100644
--- a/web/source/settings-panel/index.js
+++ b/web/source/settings-panel/index.js
@@ -22,23 +22,35 @@ const Promise = require("bluebird");
const React = require("react");
const ReactDom = require("react-dom");
const { Link, Route, Switch, useRoute, Redirect } = require("wouter");
+const { ErrorBoundary } = require("react-error-boundary");
const Auth = require("./components/auth");
+const ErrorFallback = require("./components/error");
+
const oauthLib = require("./lib/oauth");
require("./style.css");
+const UserPanel = require("./user");
+const AdminPanel = require("./admin");
+
const nav = {
- "User": [
- ["Profile", require("./user/profile.js")],
- ["Settings", require("./user/settings.js")],
- ["Customization", require("./user/customization.js")]
- ],
- "Admin": [
- ["Instance Settings", require("./admin/settings.js")],
- ["Federation", require("./admin/federation.js")],
- ["Customization", require("./admin/customization.js")]
- ]
+ "User": {
+ Component: require("./user"),
+ entries: {
+ "Profile": require("./user/profile.js"),
+ "Settings": require("./user/settings.js"),
+ "Customization": require("./user/customization.js")
+ }
+ },
+ "Admin": {
+ Component: require("./admin"),
+ entries: {
+ "Instance Settings": require("./admin/settings.js"),
+ "Federation": require("./admin/federation.js"),
+ "Customization": require("./admin/customization.js")
+ }
+ }
};
function urlSafe(str) {
@@ -49,31 +61,50 @@ function urlSafe(str) {
const sidebar = [];
const panelRouter = [];
-Object.entries(nav).forEach(([category, entries]) => {
- let base = `/settings/${urlSafe(category)}`;
+// Generate component tree from `nav` object once, as it won't change
+Object.entries(nav).forEach(([name, {Component, entries}]) => {
+ let base = `/settings/${urlSafe(name)}`;
+
+ let links = [];
+ let routes = [];
+
+ let firstRoute;
+
+ Object.entries(entries).forEach(([name, component]) => {
+ let url = `${base}/${urlSafe(name)}`;
+
+ if (firstRoute == undefined) {
+ firstRoute = `${base}/${urlSafe(name)}`;
+ }
+
+ routes.push([url, component]);
+
+ links.push(
+
+ );
+ });
- // Category header goes to first page in category
panelRouter.push(
-
+
);
- let links = entries.map(([name, component]) => {
- let url = `${base}/${urlSafe(name)}`;
-
- panelRouter.push(
-
- );
-
- return ;
- });
+ let childrenPath = `${base}/:section`;
+ panelRouter.push(
+
+ {}}>
+ {/* FIXME: implement onReset */}
+
+
+
+ );
sidebar.push(
-
-
+
+
- {category}
+ {name}
diff --git a/web/source/settings-panel/style.css b/web/source/settings-panel/style.css
index 36c7708f6..051038c58 100644
--- a/web/source/settings-panel/style.css
+++ b/web/source/settings-panel/style.css
@@ -129,6 +129,13 @@ input, select, textarea {
.error {
font-weight: bold;
+
+ pre {
+ background: $bg;
+ padding: 1rem;
+ overflow: auto;
+ margin: 0;
+ }
}
.hidden {
diff --git a/web/source/settings-panel/user/index.js b/web/source/settings-panel/user/index.js
new file mode 100644
index 000000000..dddd3118c
--- /dev/null
+++ b/web/source/settings-panel/user/index.js
@@ -0,0 +1,51 @@
+/*
+ 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 .
+*/
+
+"use strict";
+
+const Promise = require("bluebird");
+const React = require("react");
+const { Route, Switch } = require("wouter");
+
+module.exports = function UserPanel({oauth, routes}) {
+ // const [account, setAccount] = React.useState({});
+ // const [errorMsg, setError] = React.useState("");
+ // const [statusMsg, setStatus] = React.useState("Fetching user info");
+
+ // React.useEffect(() => {
+ // Promise.try(() => {
+ // return oauth.apiRequest("/api/v1/accounts/verify_credentials", "GET");
+ // }).then((json) => {
+ // setAccount(json);
+ // }).catch((e) => {
+ // setError(e.message);
+ // setStatus("");
+ // });
+ // }, [oauth, setAccount, setError, setStatus]);
+
+ // throw new Error("test");
+
+ return (
+
+ {routes.map(([path, component]) => {
+ console.log(component);
+ return ;
+ })}
+
+ );
+};
\ No newline at end of file
diff --git a/web/source/settings-panel/user/profile.js b/web/source/settings-panel/user/profile.js
index 517f60cdc..0127ca08b 100644
--- a/web/source/settings-panel/user/profile.js
+++ b/web/source/settings-panel/user/profile.js
@@ -18,6 +18,121 @@
"use strict";
-module.exports = function UserProfile() {
- return "user profile";
+const Promise = require("bluebird");
+const React = require("react");
+const { useErrorHandler } = require("react-error-boundary");
+
+const Submit = require("../components/submit");
+
+module.exports = function UserProfile({account, oauth}) {
+ const [errorMsg, setError] = React.useState("");
+ const [statusMsg, setStatus] = React.useState("");
+
+ const [headerFile, setHeaderFile] = React.useState(undefined);
+ const [headerSrc, setHeaderSrc] = React.useState("");
+
+ const [avatarFile, setAvatarFile] = React.useState(undefined);
+ const [avatarSrc, setAvatarSrc] = React.useState("");
+
+ const [displayName, setDisplayName] = React.useState("");
+ const [bio, setBio] = React.useState("");
+ const [locked, setLocked] = React.useState(false);
+
+ React.useEffect(() => {
+ setHeaderSrc(account.header);
+ setAvatarSrc(account.avatar);
+
+ setDisplayName(account.display_name);
+ setBio(account.source ? account.source.note : "");
+ setLocked(account.locked);
+ }, [account, setHeaderSrc, setAvatarSrc, setDisplayName, setBio, setLocked]);
+
+ const headerOnChange = (e) => {
+ setHeaderFile(e.target.files[0]);
+ setHeaderSrc(URL.createObjectURL(e.target.files[0]));
+ };
+
+ const avatarOnChange = (e) => {
+ setAvatarFile(e.target.files[0]);
+ setAvatarSrc(URL.createObjectURL(e.target.files[0]));
+ };
+
+ const submit = (e) => {
+ e.preventDefault();
+
+ setStatus("PATCHing");
+ setError("");
+ return Promise.try(() => {
+ let formDataInfo = new FormData();
+
+ if (headerFile) {
+ formDataInfo.set("header", headerFile);
+ }
+
+ if (avatarFile) {
+ formDataInfo.set("avatar", avatarFile);
+ }
+
+ formDataInfo.set("display_name", displayName);
+ formDataInfo.set("note", bio);
+ formDataInfo.set("locked", locked);
+
+ return oauth.apiRequest("/api/v1/accounts/update_credentials", "PATCH", formDataInfo, "form");
+ }).then((json) => {
+ setStatus("Saved!");
+
+ setHeaderSrc(json.header);
+ setAvatarSrc(json.avatar);
+
+ setDisplayName(json.display_name);
+ setBio(json.source.note);
+ setLocked(json.locked);
+ }).catch((e) => {
+ setError(e.message);
+ setStatus("");
+ });
+ };
+
+ return (
+
+ @{account.username}'s Profile Info
+
+
+ );
};
\ No newline at end of file
diff --git a/web/source/yarn.lock b/web/source/yarn.lock
index 41a2e6398..d213230b8 100644
--- a/web/source/yarn.lock
+++ b/web/source/yarn.lock
@@ -920,6 +920,13 @@
"@babel/plugin-transform-react-jsx-development" "^7.18.6"
"@babel/plugin-transform-react-pure-annotations" "^7.18.6"
+"@babel/runtime@^7.12.5":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
+ integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/runtime@^7.8.4":
version "7.18.9"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
@@ -5078,6 +5085,13 @@ react-dom@^17.0.1:
object-assign "^4.1.1"
scheduler "^0.20.2"
+react-error-boundary@^3.1.4:
+ version "3.1.4"
+ resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0"
+ integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+
react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"