diff options
Diffstat (limited to 'web/source/settings')
37 files changed, 706 insertions, 513 deletions
diff --git a/web/source/settings/components/back-button.jsx b/web/source/settings/components/back-button.tsx index bf9038b2b..bf9038b2b 100644 --- a/web/source/settings/components/back-button.jsx +++ b/web/source/settings/components/back-button.tsx diff --git a/web/source/settings/components/combo-box.jsx b/web/source/settings/components/combo-box.tsx index fc0133583..113e6179d 100644 --- a/web/source/settings/components/combo-box.jsx +++ b/web/source/settings/components/combo-box.tsx @@ -17,15 +17,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -const React = require("react"); +import React from "react"; -const { - Combobox, - ComboboxItem, - ComboboxPopover, -} = require("ariakit/combobox"); +import { Combobox, ComboboxItem, ComboboxPopover } from "ariakit/combobox"; -module.exports = function ComboBox({ field, items, label, children, ...inputProps }) { +export default function ComboBox({ field, items, label, children, ...inputProps }) { return ( <div className="form-field combobox-wrapper"> <label> @@ -48,4 +44,4 @@ module.exports = function ComboBox({ field, items, label, children, ...inputProp </ComboboxPopover> </div> ); -};
\ No newline at end of file +}
\ No newline at end of file diff --git a/web/source/settings/components/error.jsx b/web/source/settings/components/error.tsx index 3205cd5e3..15c3bccd4 100644 --- a/web/source/settings/components/error.jsx +++ b/web/source/settings/components/error.tsx @@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -const React = require("react"); +import React from "react"; function ErrorFallback({ error, resetErrorBoundary }) { return ( @@ -81,4 +81,4 @@ function Error({ error }) { ); } -module.exports = { ErrorFallback, Error };
\ No newline at end of file +export { ErrorFallback, Error }; diff --git a/web/source/settings/components/fake-profile.jsx b/web/source/settings/components/fake-profile.tsx index 6cb8d0187..4a5157378 100644 --- a/web/source/settings/components/fake-profile.jsx +++ b/web/source/settings/components/fake-profile.tsx @@ -17,9 +17,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -const React = require("react"); +import React from "react"; -module.exports = function FakeProfile({ avatar, header, display_name, username, role }) { +export default function FakeProfile({ avatar, header, display_name, username, role }) { return ( // Keep in sync with web/template/profile.tmpl <div className="profile"> <div className="profile-header"> @@ -49,4 +49,4 @@ module.exports = function FakeProfile({ avatar, header, display_name, username, </div> </div> ); -};
\ No newline at end of file +} diff --git a/web/source/settings/components/fake-toot.jsx b/web/source/settings/components/fake-toot.tsx index 08f806008..ad0c387a4 100644 --- a/web/source/settings/components/fake-toot.jsx +++ b/web/source/settings/components/fake-toot.tsx @@ -17,16 +17,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -const React = require("react"); +import React from "react"; +import { useVerifyCredentialsQuery } from "../lib/query/oauth"; -const query = require("../lib/query"); - -module.exports = function FakeToot({ children }) { +export default function FakeToot({ children }) { const { data: account = { avatar: "/assets/default_avatars/GoToSocial_icon1.png", display_name: "", username: "" - } } = query.useVerifyCredentialsQuery(); + } } = useVerifyCredentialsQuery(); return ( <article className="status expanded"> @@ -54,4 +53,4 @@ module.exports = function FakeToot({ children }) { </section> </article> ); -};
\ No newline at end of file +} diff --git a/web/source/settings/components/languages.jsx b/web/source/settings/components/languages.tsx index 61c76e03a..7c2986438 100644 --- a/web/source/settings/components/languages.jsx +++ b/web/source/settings/components/languages.tsx @@ -17,10 +17,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -const React = require("react"); -const langs = require("langs"); +import React from "react"; +import { all } from "langs"; -const asElements = langs.all().map((l) => { +const asElements = all().map((l) => { let code = l["1"].toUpperCase(); let name = l.name; if (l.name != l.local) { @@ -29,6 +29,6 @@ const asElements = langs.all().map((l) => { return <option key={code} value={code}>{name}</option>; }); -module.exports = function Languages() { +export default function Languages() { return asElements; -};
\ No newline at end of file +}
\ No newline at end of file diff --git a/web/source/settings/components/loading.jsx b/web/source/settings/components/loading.tsx index c62c72a0a..f7635534f 100644 --- a/web/source/settings/components/loading.jsx +++ b/web/source/settings/components/loading.tsx @@ -17,10 +17,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -const React = require("react"); +import React from "react"; -module.exports = function Loading() { +export default function Loading() { return ( <i className="fa fa-spin fa-refresh loading-icon" aria-label="Loading" title="Loading" /> ); -};
\ No newline at end of file +} diff --git a/web/source/settings/components/user-logout-card.jsx b/web/source/settings/components/user-logout-card.tsx index 9d88642a5..f8eeaf63c 100644 --- a/web/source/settings/components/user-logout-card.jsx +++ b/web/source/settings/components/user-logout-card.tsx @@ -17,15 +17,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -const React = require("react"); -const Loading = require("./loading"); -const { - useVerifyCredentialsQuery, - useLogoutMutation, -} = require("../lib/query/oauth"); -const { useInstanceV1Query } = require("../lib/query"); +import React from "react"; +import Loading from "./loading"; +import { useVerifyCredentialsQuery, useLogoutMutation } from "../lib/query/oauth"; +import { useInstanceV1Query } from "../lib/query/gts-api"; -module.exports = function UserLogoutCard() { +export default function UserLogoutCard() { const { data: profile, isLoading } = useVerifyCredentialsQuery(); const { data: instance } = useInstanceV1Query(); const [logoutQuery] = useLogoutMutation(); @@ -44,4 +41,4 @@ module.exports = function UserLogoutCard() { </div> ); } -};
\ No newline at end of file +}
\ No newline at end of file diff --git a/web/source/settings/index.tsx b/web/source/settings/index.tsx index d0af8524d..977a94150 100644 --- a/web/source/settings/index.tsx +++ b/web/source/settings/index.tsx @@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import React, { StrictMode } from "react"; +import React, { StrictMode, useMemo } from "react"; import "./style.css"; import { createRoot } from "react-dom/client"; @@ -29,18 +29,21 @@ import Loading from "./components/loading"; import { Account } from "./lib/types/account"; import { BaseUrlContext, RoleContext } from "./lib/navigation/util"; import { SidebarMenu } from "./lib/navigation/menu"; -import { UserMenu, UserRouter } from "./views/user/routes"; -import { ModerationMenu, ModerationRouter } from "./views/moderation/routes"; -import { AdminMenu, AdminRouter } from "./views/admin/routes"; import { Redirect, Route, Router } from "wouter"; +import AdminMenu from "./views/admin/menu"; +import ModerationMenu from "./views/moderation/menu"; +import UserMenu from "./views/user/menu"; +import UserRouter from "./views/user/router"; +import { ErrorBoundary } from "./lib/navigation/error"; +import ModerationRouter from "./views/moderation/router"; +import AdminRouter from "./views/admin/router"; interface AppProps { account: Account; } export function App({ account }: AppProps) { - const roles: string[] = [ account.role.name ]; - + const roles: string[] = useMemo(() => [ account.role.name ], [account]); return ( <RoleContext.Provider value={roles}> <BaseUrlContext.Provider value={"/settings"}> @@ -51,15 +54,17 @@ export function App({ account }: AppProps) { </SidebarMenu> <section className="with-sidebar"> <Router base="/settings"> - <UserRouter /> - <ModerationRouter /> - <AdminRouter /> - {/* - Redirect to first part of UserRouter if - just the bare settings page is open, so - user isn't greeted with a blank page. - */} - <Route><Redirect to="/user/profile" /></Route> + <ErrorBoundary> + <UserRouter /> + <ModerationRouter /> + <AdminRouter /> + {/* + Redirect to first part of UserRouter if + just the bare settings page is open, so + user isn't greeted with a blank page. + */} + <Route><Redirect to="/user/profile" /></Route> + </ErrorBoundary> </Router> </section> </BaseUrlContext.Provider> diff --git a/web/source/settings/style.css b/web/source/settings/style.css index 57f8bf4cf..5af9dbc67 100644 --- a/web/source/settings/style.css +++ b/web/source/settings/style.css @@ -601,29 +601,31 @@ span.form-info { @media screen and (max-width: 60rem) { /* vertical layout */ #root { - padding: 1rem; + padding: 0.5rem; + margin: 0; grid-template-columns: 100%; grid-template-rows: auto auto; - .sidebar { + div.sidebar { justify-self: auto; - margin-bottom: 2rem; + margin-bottom: 0; } - .sidebar, section.with-sidebar { + div.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.with-sidebar { + grid-column: 1; + padding: 1rem; } - } - section { - grid-column: 1; + div.sidebar a:first-child h2 { + border-top-right-radius: $br; + } } .user-profile .overview { diff --git a/web/source/settings/views/admin/actions/keys/expireremote.tsx b/web/source/settings/views/admin/actions/keys/expireremote.tsx index c7a410267..82045942c 100644 --- a/web/source/settings/views/admin/actions/keys/expireremote.tsx +++ b/web/source/settings/views/admin/actions/keys/expireremote.tsx @@ -18,10 +18,10 @@ */ import React from "react"; -import { useInstanceKeysExpireMutation } from "../../../../lib/query"; import { TextInput } from "../../../../components/form/inputs"; import MutationButton from "../../../../components/form/mutation-button"; import { useTextInput } from "../../../../lib/form"; +import { useInstanceKeysExpireMutation } from "../../../../lib/query/admin"; export default function ExpireRemote({}) { const domainField = useTextInput("domain"); diff --git a/web/source/settings/views/admin/actions/media/cleanup.tsx b/web/source/settings/views/admin/actions/media/cleanup.tsx index d4bae24a6..c1df511e1 100644 --- a/web/source/settings/views/admin/actions/media/cleanup.tsx +++ b/web/source/settings/views/admin/actions/media/cleanup.tsx @@ -19,10 +19,10 @@ import React from "react"; -import { useMediaCleanupMutation } from "../../../../lib/query"; import { useTextInput } from "../../../../lib/form"; import { TextInput } from "../../../../components/form/inputs"; import MutationButton from "../../../../components/form/mutation-button"; +import { useMediaCleanupMutation } from "../../../../lib/query/admin"; export default function Cleanup({}) { const daysField = useTextInput("days", { defaultValue: "30" }); diff --git a/web/source/settings/views/admin/emoji/local/new-emoji.tsx b/web/source/settings/views/admin/emoji/local/new-emoji.tsx index 73e846f16..8ff8236a7 100644 --- a/web/source/settings/views/admin/emoji/local/new-emoji.tsx +++ b/web/source/settings/views/admin/emoji/local/new-emoji.tsx @@ -26,7 +26,7 @@ import { CategorySelect } from '../category-select'; import FakeToot from "../../../../components/fake-toot"; import MutationButton from "../../../../components/form/mutation-button"; import { useAddEmojiMutation } from "../../../../lib/query/admin/custom-emoji"; -import { useInstanceV1Query } from "../../../../lib/query"; +import { useInstanceV1Query } from "../../../../lib/query/gts-api"; export default function NewEmojiForm() { const shortcode = useShortcode(); diff --git a/web/source/settings/views/admin/emoji/local/overview.tsx b/web/source/settings/views/admin/emoji/local/overview.tsx index b28af59f3..b4fa48d74 100644 --- a/web/source/settings/views/admin/emoji/local/overview.tsx +++ b/web/source/settings/views/admin/emoji/local/overview.tsx @@ -29,7 +29,7 @@ import { TextInput } from "../../../../components/form/inputs"; import { useListEmojiQuery } from "../../../../lib/query/admin/custom-emoji"; import { CustomEmoji } from "../../../../lib/types/custom-emoji"; -export function EmojiOverview() { +export default function EmojiOverview() { const { data: emoji = [], isLoading, isError, error } = useListEmojiQuery({ filter: "domain:local" }); let content: React.JSX.Element; diff --git a/web/source/settings/views/admin/settings/rules.tsx b/web/source/settings/views/admin/instance/ruledetail.tsx index 2b8a51c22..31447c74b 100644 --- a/web/source/settings/views/admin/settings/rules.tsx +++ b/web/source/settings/views/admin/instance/ruledetail.tsx @@ -18,66 +18,18 @@ */ import React from "react"; -import { Link, Redirect, useParams } from "wouter"; -import { useInstanceRulesQuery, useAddInstanceRuleMutation, useUpdateInstanceRuleMutation, useDeleteInstanceRuleMutation } from "../../../lib/query"; +import { Redirect, useParams } from "wouter"; import { useBaseUrl } from "../../../lib/navigation/util"; import { useValue, useTextInput } from "../../../lib/form"; import useFormSubmit from "../../../lib/form/submit"; import { TextArea } from "../../../components/form/inputs"; import MutationButton from "../../../components/form/mutation-button"; -import { Error } from "../../../components/error"; import BackButton from "../../../components/back-button"; -import { InstanceRule, MappedRules } from "../../../lib/types/rules"; import Loading from "../../../components/loading"; -import FormWithData from "../../../lib/form/form-with-data"; - -export function InstanceRules() { - return ( - <> - <h1>Instance Rules</h1> - <FormWithData - dataQuery={useInstanceRulesQuery} - DataForm={InstanceRulesForm} - /> - </> - ); -} - -function InstanceRulesForm({ data: rules }: { data: MappedRules }) { - const baseUrl = useBaseUrl(); - const newRule = useTextInput("text"); - - const [submitForm, result] = useFormSubmit({ newRule }, useAddInstanceRuleMutation(), { - changedOnly: true, - onFinish: () => newRule.reset() - }); - - return ( - <form onSubmit={submitForm} className="new-rule"> - <ol className="instance-rules"> - {Object.values(rules).map((rule: InstanceRule) => ( - <Link className="rule" to={`~${baseUrl}/instance-rules/${rule.id}`}> - <li> - <h2>{rule.text} <i className="fa fa-pencil edit-icon" /></h2> - </li> - <span>{new Date(rule.created_at).toLocaleString()}</span> - </Link> - ))} - </ol> - <TextArea - field={newRule} - label="New instance rule" - /> - <MutationButton - disabled={newRule.value === undefined || newRule.value.length === 0} - label="Add rule" - result={result} - /> - </form> - ); -} +import { useDeleteInstanceRuleMutation, useInstanceRulesQuery, useUpdateInstanceRuleMutation } from "../../../lib/query/admin"; +import { Error } from "../../../components/error"; -export function InstanceRuleDetail() { +export default function InstanceRuleDetail() { const baseUrl = useBaseUrl(); const params: { ruleId: string } = useParams(); @@ -94,7 +46,7 @@ export function InstanceRuleDetail() { return ( <> - <BackButton to={`~${baseUrl}/instance-rules`} /> + <BackButton to={`~${baseUrl}/rules`} /> <EditInstanceRuleForm rule={rules[params.ruleId]} /> </> ); @@ -113,7 +65,7 @@ function EditInstanceRuleForm({ rule }) { if (result.isSuccess || deleteResult.isSuccess) { return ( - <Redirect to={`~${baseUrl}/instance-rules`} /> + <Redirect to={`~${baseUrl}/rules`} /> ); } diff --git a/web/source/settings/views/admin/instance/rules.tsx b/web/source/settings/views/admin/instance/rules.tsx new file mode 100644 index 000000000..45ad90103 --- /dev/null +++ b/web/source/settings/views/admin/instance/rules.tsx @@ -0,0 +1,75 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + 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/>. +*/ + +import React from "react"; +import { Link } from "wouter"; +import { useInstanceRulesQuery, useAddInstanceRuleMutation } from "../../../lib/query/admin"; +import { useBaseUrl } from "../../../lib/navigation/util"; +import { useTextInput } from "../../../lib/form"; +import useFormSubmit from "../../../lib/form/submit"; +import { TextArea } from "../../../components/form/inputs"; +import MutationButton from "../../../components/form/mutation-button"; +import { InstanceRule, MappedRules } from "../../../lib/types/rules"; +import FormWithData from "../../../lib/form/form-with-data"; + +export default function InstanceRules() { + return ( + <> + <h1>Instance Rules</h1> + <FormWithData + dataQuery={useInstanceRulesQuery} + DataForm={InstanceRulesForm} + /> + </> + ); +} + +function InstanceRulesForm({ data: rules }: { data: MappedRules }) { + const baseUrl = useBaseUrl(); + const newRule = useTextInput("text"); + + const [submitForm, result] = useFormSubmit({ newRule }, useAddInstanceRuleMutation(), { + changedOnly: true, + onFinish: () => newRule.reset() + }); + + return ( + <form onSubmit={submitForm} className="new-rule"> + <ol className="instance-rules"> + {Object.values(rules).map((rule: InstanceRule) => ( + <Link key={"link-"+rule.id} className="rule" to={`~${baseUrl}/rules/${rule.id}`}> + <li key={rule.id}> + <h2>{rule.text} <i className="fa fa-pencil edit-icon" /></h2> + </li> + <span>{new Date(rule.created_at).toLocaleString()}</span> + </Link> + ))} + </ol> + <TextArea + field={newRule} + label="New instance rule" + /> + <MutationButton + disabled={newRule.value === undefined || newRule.value.length === 0} + label="Add rule" + result={result} + /> + </form> + ); +} diff --git a/web/source/settings/views/admin/settings/index.tsx b/web/source/settings/views/admin/instance/settings.tsx index abb34cf66..03a961589 100644 --- a/web/source/settings/views/admin/settings/index.tsx +++ b/web/source/settings/views/admin/instance/settings.tsx @@ -20,17 +20,13 @@ import React from "react"; import { useTextInput, useFileInput } from "../../../lib/form"; - -const useFormSubmit = require("../../../lib/form/submit").default; - import { TextInput, TextArea, FileInput } from "../../../components/form/inputs"; - -const FormWithData = require("../../../lib/form/form-with-data").default; import MutationButton from "../../../components/form/mutation-button"; - -import { useInstanceV1Query } from "../../../lib/query"; +import { useInstanceV1Query } from "../../../lib/query/gts-api"; import { useUpdateInstanceMutation } from "../../../lib/query/admin"; import { InstanceV1 } from "../../../lib/types/instance"; +import FormWithData from "../../../lib/form/form-with-data"; +import useFormSubmit from "../../../lib/form/submit"; export default function InstanceSettings() { return ( diff --git a/web/source/settings/views/admin/menu.tsx b/web/source/settings/views/admin/menu.tsx new file mode 100644 index 000000000..2cf5a35c2 --- /dev/null +++ b/web/source/settings/views/admin/menu.tsx @@ -0,0 +1,129 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + 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/>. +*/ + +import { MenuItem } from "../../lib/navigation/menu"; +import React from "react"; +import { useHasPermission } from "../../lib/navigation/util"; + +/* + EXPORTED COMPONENTS +*/ + +/** + * - /settings/admin/instance/settings + * - /settings/admin/instance/rules + * - /settings/admin/instance/rules/:ruleId + * - /settings/admin/emojis + * - /settings/admin/emojis/local + * - /settings/admin/emojis/local/:emojiId + * - /settings/admin/emojis/remote + * - /settings/admin/actions + * - /settings/admin/actions/media + * - /settings/admin/actions/keys + */ +export default function AdminMenu() { + const permissions = ["admin"]; + const admin = useHasPermission(permissions); + if (!admin) { + return null; + } + + return ( + <MenuItem + name="Administration" + itemUrl="admin" + defaultChild="actions" + permissions={permissions} + > + <AdminInstanceMenu /> + <AdminEmojisMenu /> + <AdminActionsMenu /> + </MenuItem> + ); +} + +/* + INTERNAL COMPONENTS +*/ + +function AdminInstanceMenu() { + return ( + <MenuItem + name="Instance" + itemUrl="instance" + defaultChild="settings" + icon="fa-sitemap" + > + <MenuItem + name="Settings" + itemUrl="settings" + icon="fa-sliders" + /> + <MenuItem + name="Rules" + itemUrl="rules" + icon="fa-dot-circle-o" + /> + </MenuItem> + ); +} + +function AdminActionsMenu() { + return ( + <MenuItem + name="Actions" + itemUrl="actions" + defaultChild="media" + icon="fa-bolt" + > + <MenuItem + name="Media" + itemUrl="media" + icon="fa-photo" + /> + <MenuItem + name="Keys" + itemUrl="keys" + icon="fa-key-modern" + /> + </MenuItem> + ); +} + +function AdminEmojisMenu() { + return ( + <MenuItem + name="Custom Emoji" + itemUrl="emojis" + defaultChild="local" + icon="fa-smile-o" + > + <MenuItem + name="Local" + itemUrl="local" + icon="fa-home" + /> + <MenuItem + name="Remote" + itemUrl="remote" + icon="fa-cloud" + /> + </MenuItem> + ); +} diff --git a/web/source/settings/views/admin/router.tsx b/web/source/settings/views/admin/router.tsx new file mode 100644 index 000000000..95d146510 --- /dev/null +++ b/web/source/settings/views/admin/router.tsx @@ -0,0 +1,151 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + 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/>. +*/ + +import React from "react"; +import { BaseUrlContext, useBaseUrl, useHasPermission } from "../../lib/navigation/util"; +import { Redirect, Route, Router, Switch } from "wouter"; +import { ErrorBoundary } from "../../lib/navigation/error"; +import InstanceSettings from "./instance/settings"; +import InstanceRules from "./instance/rules"; +import InstanceRuleDetail from "./instance/ruledetail"; +import Media from "./actions/media"; +import Keys from "./actions/keys"; +import EmojiOverview from "./emoji/local/overview"; +import EmojiDetail from "./emoji/local/detail"; +import RemoteEmoji from "./emoji/remote"; + +/* + EXPORTED COMPONENTS +*/ + +/** + * - /settings/instance/settings + * - /settings/instance/rules + * - /settings/instance/rules/:ruleId + * - /settings/admin/emojis + * - /settings/admin/emojis/local + * - /settings/admin/emojis/local/:emojiId + * - /settings/admin/emojis/remote + * - /settings/admin/actions + * - /settings/admin/actions/media + * - /settings/admin/actions/keys + */ +export default function AdminRouter() { + const parentUrl = useBaseUrl(); + const thisBase = "/admin"; + const absBase = parentUrl + thisBase; + + return ( + <BaseUrlContext.Provider value={absBase}> + <Router base={thisBase}> + <AdminInstanceRouter /> + <AdminEmojisRouter /> + <AdminActionsRouter /> + </Router> + </BaseUrlContext.Provider> + ); +} + +/* + INTERNAL COMPONENTS +*/ + +/** + * - /settings/admin/emojis + * - /settings/admin/emojis/local + * - /settings/admin/emojis/local/:emojiId + * - /settings/admin/emojis/remote + */ +function AdminEmojisRouter() { + const parentUrl = useBaseUrl(); + const thisBase = "/emojis"; + const absBase = parentUrl + thisBase; + + const permissions = ["admin"]; + const admin = useHasPermission(permissions); + if (!admin) { + return null; + } + + return ( + <BaseUrlContext.Provider value={absBase}> + <Router base={thisBase}> + <ErrorBoundary> + <Switch> + <Route path="/local" component={EmojiOverview} /> + <Route path="/local/:emojiId" component={EmojiDetail} /> + <Route path="/remote" component={RemoteEmoji} /> + <Route><Redirect to="/local" /></Route> + </Switch> + </ErrorBoundary> + </Router> + </BaseUrlContext.Provider> + ); +} + +/** + * - /settings/admin/actions + * - /settings/admin/actions/media + * - /settings/admin/actions/keys + */ +function AdminActionsRouter() { + const parentUrl = useBaseUrl(); + const thisBase = "/actions"; + const absBase = parentUrl + thisBase; + + return ( + <BaseUrlContext.Provider value={absBase}> + <Router base={thisBase}> + <ErrorBoundary> + <Switch> + <Route path="/media" component={Media} /> + <Route path="/keys" component={Keys} /> + <Route><Redirect to="/media" /></Route> + </Switch> + </ErrorBoundary> + </Router> + </BaseUrlContext.Provider> + ); +} + +/** + * - /settings/instance/settings + * - /settings/instance/rules + * - /settings/instance/rules/:ruleId + */ +function AdminInstanceRouter() { + const parentUrl = useBaseUrl(); + const thisBase = "/instance"; + const absBase = parentUrl + thisBase; + + return ( + <BaseUrlContext.Provider value={absBase}> + <Router base={thisBase}> + <ErrorBoundary> + <Switch> + <Route path="/settings" component={InstanceSettings}/> + <Route path="/rules" component={InstanceRules} /> + <Route path="/rules/:ruleId" component={InstanceRuleDetail} /> + <Route><Redirect to="/settings" /></Route> + </Switch> + </ErrorBoundary> + </Router> + </BaseUrlContext.Provider> + ); +} diff --git a/web/source/settings/views/admin/routes.tsx b/web/source/settings/views/admin/routes.tsx deleted file mode 100644 index 29889046c..000000000 --- a/web/source/settings/views/admin/routes.tsx +++ /dev/null @@ -1,177 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - 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/>. -*/ - -import { MenuItem } from "../../lib/navigation/menu"; -import React from "react"; -import { BaseUrlContext, useBaseUrl } from "../../lib/navigation/util"; -import { Route, Router, Switch } from "wouter"; -import EmojiDetail from "./emoji/local/detail"; -import { EmojiOverview } from "./emoji/local/overview"; -import RemoteEmoji from "./emoji/remote"; -import InstanceSettings from "./settings"; -import { InstanceRuleDetail, InstanceRules } from "./settings/rules"; -import Media from "./actions/media"; -import Keys from "./actions/keys"; - -/* - EXPORTED COMPONENTS -*/ - -/** - * Admininistration menu. Admin actions, - * emoji import, instance settings. - */ -export function AdminMenu() { - return ( - <MenuItem - name="Administration" - itemUrl="admin" - defaultChild="actions" - permissions={["admin"]} - > - <MenuItem - name="Instance Settings" - itemUrl="instance-settings" - icon="fa-sliders" - /> - <MenuItem - name="Instance Rules" - itemUrl="instance-rules" - icon="fa-dot-circle-o" - /> - <AdminEmojisMenu /> - <AdminActionsMenu /> - </MenuItem> - ); -} - -/** - * Admininistration router. Admin actions, - * emoji import, instance settings. - */ -export function AdminRouter() { - const parentUrl = useBaseUrl(); - const thisBase = "/admin"; - const absBase = parentUrl + thisBase; - - return ( - <BaseUrlContext.Provider value={absBase}> - <Router base={thisBase}> - <Route path="/instance-settings" component={InstanceSettings}/> - <Route path="/instance-rules" component={InstanceRules} /> - <Route path="/instance-rules/:ruleId" component={InstanceRuleDetail} /> - <AdminEmojisRouter /> - <AdminActionsRouter /> - </Router> - </BaseUrlContext.Provider> - ); -} - -/* - INTERNAL COMPONENTS -*/ - -/* - MENUS -*/ - -function AdminActionsMenu() { - return ( - <MenuItem - name="Actions" - itemUrl="actions" - defaultChild="media" - icon="fa-bolt" - > - <MenuItem - name="Media" - itemUrl="media" - icon="fa-photo" - /> - <MenuItem - name="Keys" - itemUrl="keys" - icon="fa-key-modern" - /> - </MenuItem> - ); -} - -function AdminEmojisMenu() { - return ( - <MenuItem - name="Custom Emoji" - itemUrl="emojis" - defaultChild="local" - icon="fa-smile-o" - > - <MenuItem - name="Local" - itemUrl="local" - icon="fa-home" - /> - <MenuItem - name="Remote" - itemUrl="remote" - icon="fa-cloud" - /> - </MenuItem> - ); -} - -/* - ROUTERS -*/ - -function AdminEmojisRouter() { - const parentUrl = useBaseUrl(); - const thisBase = "/emojis"; - const absBase = parentUrl + thisBase; - - return ( - <BaseUrlContext.Provider value={absBase}> - <Router base={thisBase}> - <Switch> - <Route path="/local/:emojiId" component={EmojiDetail} /> - <Route path="/local" component={EmojiOverview} /> - <Route path="/remote" component={RemoteEmoji} /> - <Route component={EmojiOverview}/> - </Switch> - </Router> - </BaseUrlContext.Provider> - ); -} - -function AdminActionsRouter() { - const parentUrl = useBaseUrl(); - const thisBase = "/actions"; - const absBase = parentUrl + thisBase; - - return ( - <BaseUrlContext.Provider value={absBase}> - <Router base={thisBase}> - <Switch> - <Route path="/media" component={Media} /> - <Route path="/keys" component={Keys} /> - <Route component={Media}/> - </Switch> - </Router> - </BaseUrlContext.Provider> - ); -} diff --git a/web/source/settings/views/moderation/accounts/detail/actions.tsx b/web/source/settings/views/moderation/accounts/detail/actions.tsx index 74c5371f1..212bb4089 100644 --- a/web/source/settings/views/moderation/accounts/detail/actions.tsx +++ b/web/source/settings/views/moderation/accounts/detail/actions.tsx @@ -19,17 +19,14 @@ import React from "react"; -import { useActionAccountMutation } from "../../../../lib/query"; - +import { useActionAccountMutation } from "../../../../lib/query/admin"; import MutationButton from "../../../../components/form/mutation-button"; - import useFormSubmit from "../../../../lib/form/submit"; import { useValue, useTextInput, useBoolInput, } from "../../../../lib/form"; - import { Checkbox, TextInput } from "../../../../components/form/inputs"; import { AdminAccount } from "../../../../lib/types/account"; diff --git a/web/source/settings/views/moderation/accounts/detail/handlesignup.tsx b/web/source/settings/views/moderation/accounts/detail/handlesignup.tsx index 5655421ea..59fa8bc65 100644 --- a/web/source/settings/views/moderation/accounts/detail/handlesignup.tsx +++ b/web/source/settings/views/moderation/accounts/detail/handlesignup.tsx @@ -19,18 +19,14 @@ import React from "react"; import { useLocation } from "wouter"; - -import { useHandleSignupMutation } from "../../../../lib/query"; - +import { useHandleSignupMutation } from "../../../../lib/query/admin"; import MutationButton from "../../../../components/form/mutation-button"; - import useFormSubmit from "../../../../lib/form/submit"; import { useValue, useTextInput, useBoolInput, } from "../../../../lib/form"; - import { Checkbox, Select, TextInput } from "../../../../components/form/inputs"; import { AdminAccount } from "../../../../lib/types/account"; diff --git a/web/source/settings/views/moderation/accounts/detail/index.tsx b/web/source/settings/views/moderation/accounts/detail/index.tsx index f507391d3..f34bc7481 100644 --- a/web/source/settings/views/moderation/accounts/detail/index.tsx +++ b/web/source/settings/views/moderation/accounts/detail/index.tsx @@ -19,12 +19,9 @@ import React from "react"; -import { useGetAccountQuery } from "../../../../lib/query"; - +import { useGetAccountQuery } from "../../../../lib/query/admin"; import FormWithData from "../../../../lib/form/form-with-data"; - import FakeProfile from "../../../../components/fake-profile"; - import { AdminAccount } from "../../../../lib/types/account"; import { HandleSignup } from "./handlesignup"; import { AccountActions } from "./actions"; diff --git a/web/source/settings/views/moderation/accounts/pending/index.tsx b/web/source/settings/views/moderation/accounts/pending/index.tsx index 96b7796e5..d5a32f09b 100644 --- a/web/source/settings/views/moderation/accounts/pending/index.tsx +++ b/web/source/settings/views/moderation/accounts/pending/index.tsx @@ -18,7 +18,7 @@ */ import React from "react"; -import { useSearchAccountsQuery } from "../../../../lib/query"; +import { useSearchAccountsQuery } from "../../../../lib/query/admin"; import { AccountList } from "../../../../components/account-list"; export default function AccountsPending() { diff --git a/web/source/settings/views/moderation/accounts/search/index.tsx b/web/source/settings/views/moderation/accounts/search/index.tsx index 7d5515a43..8ee579e16 100644 --- a/web/source/settings/views/moderation/accounts/search/index.tsx +++ b/web/source/settings/views/moderation/accounts/search/index.tsx @@ -19,9 +19,8 @@ import React from "react"; -import { useLazySearchAccountsQuery } from "../../../../lib/query"; +import { useLazySearchAccountsQuery } from "../../../../lib/query/admin"; import { useTextInput } from "../../../../lib/form"; - import { AccountList } from "../../../../components/account-list"; import { SearchAccountParams } from "../../../../lib/types/account"; import { Select, TextInput } from "../../../../components/form/inputs"; diff --git a/web/source/settings/views/moderation/domain-permissions/form.tsx b/web/source/settings/views/moderation/domain-permissions/form.tsx index ba0808873..ea7fdbc23 100644 --- a/web/source/settings/views/moderation/domain-permissions/form.tsx +++ b/web/source/settings/views/moderation/domain-permissions/form.tsx @@ -20,18 +20,14 @@ import React from "react"; import { useEffect } from "react"; - import { useExportDomainListMutation } from "../../../lib/query/admin/domain-permissions/export"; import useFormSubmit from "../../../lib/form/submit"; - import { RadioGroup, TextArea, Select, } from "../../../components/form/inputs"; - import MutationButton from "../../../components/form/mutation-button"; - import { Error } from "../../../components/error"; import ExportFormatTable from "./export-format-table"; diff --git a/web/source/settings/views/moderation/domain-permissions/overview.tsx b/web/source/settings/views/moderation/domain-permissions/overview.tsx index d2bb77087..fca8e348e 100644 --- a/web/source/settings/views/moderation/domain-permissions/overview.tsx +++ b/web/source/settings/views/moderation/domain-permissions/overview.tsx @@ -22,11 +22,8 @@ import React from "react"; import { useMemo } from "react"; import { Link, useLocation, useParams } from "wouter"; import { matchSorter } from "match-sorter"; - import { useTextInput } from "../../../lib/form"; - import { TextInput } from "../../../components/form/inputs"; - import Loading from "../../../components/loading"; import { useDomainAllowsQuery, useDomainBlocksQuery } from "../../../lib/query/admin/domain-permissions/get"; import type { MappedDomainPerms, PermType } from "../../../lib/types/domain-permission"; diff --git a/web/source/settings/views/moderation/domain-permissions/process.tsx b/web/source/settings/views/moderation/domain-permissions/process.tsx index 6c7cb218e..d54d7399c 100644 --- a/web/source/settings/views/moderation/domain-permissions/process.tsx +++ b/web/source/settings/views/moderation/domain-permissions/process.tsx @@ -18,9 +18,7 @@ */ import React from "react"; - import { memo, useMemo, useCallback, useEffect } from "react"; - import { isValidDomainPermission, hasBetterScope } from "../../../lib/util/domain-permission"; import { diff --git a/web/source/settings/views/moderation/menu.tsx b/web/source/settings/views/moderation/menu.tsx new file mode 100644 index 000000000..4f01e0798 --- /dev/null +++ b/web/source/settings/views/moderation/menu.tsx @@ -0,0 +1,121 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + 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/>. +*/ + +import { MenuItem } from "../../lib/navigation/menu"; +import React from "react"; +import { useHasPermission } from "../../lib/navigation/util"; + +/* + EXPORTED COMPONENTS +*/ + +/** + * - /settings/moderation/reports/overview + * - /settings/moderation/reports/:reportId + * - /settings/moderation/accounts/overview + * - /settings/moderation/accounts/pending + * - /settings/moderation/accounts/:accountID + * - /settings/moderation/domain-permissions/:permType + * - /settings/moderation/domain-permissions/:permType/:domain + * - /settings/moderation/domain-permissions/import-export + * - /settings/moderation/domain-permissions/process + */ +export default function ModerationMenu() { + const permissions = ["moderator"]; + const moderator = useHasPermission(permissions); + if (!moderator) { + return null; + } + + return ( + <MenuItem + name="Moderation" + itemUrl="moderation" + defaultChild="reports" + permissions={permissions} + > + <ModerationReportsMenu /> + <ModerationAccountsMenu /> + <ModerationDomainPermsMenu /> + </MenuItem> + ); +} + +/* + INTERNAL COMPONENTS +*/ + +function ModerationReportsMenu() { + return ( + <MenuItem + name="Reports" + itemUrl="reports" + icon="fa-flag" + /> + ); +} + +function ModerationAccountsMenu() { + return ( + <MenuItem + name="Accounts" + itemUrl="accounts" + defaultChild="overview" + icon="fa-users" + > + <MenuItem + name="Overview" + itemUrl="overview" + icon="fa-list" + /> + <MenuItem + name="Pending" + itemUrl="pending" + icon="fa-question" + /> + </MenuItem> + ); +} + +function ModerationDomainPermsMenu() { + return ( + <MenuItem + name="Domain Permissions" + itemUrl="domain-permissions" + defaultChild="blocks" + icon="fa-hubzilla" + > + <MenuItem + name="Blocks" + itemUrl="blocks" + icon="fa-close" + /> + <MenuItem + name="Allows" + itemUrl="allows" + icon="fa-check" + /> + <MenuItem + name="Import/Export" + itemUrl="import-export" + icon="fa-floppy-o" + /> + </MenuItem> + ); +} diff --git a/web/source/settings/views/moderation/reports/detail.tsx b/web/source/settings/views/moderation/reports/detail.tsx index 9bb2de6b2..bc356edce 100644 --- a/web/source/settings/views/moderation/reports/detail.tsx +++ b/web/source/settings/views/moderation/reports/detail.tsx @@ -52,7 +52,15 @@ function ReportDetailForm({ data: report }) { return ( <div className="report detail"> <div className="usernames"> - <Username user={from} /> reported <Username user={target} /> + <Username + user={from} + link={`~/settings/moderation/accounts/${from.id}`} + /> + <> reported </> + <Username + user={target} + link={`~/settings/moderation/accounts/${target.id}`} + /> </div> {report.action_taken && diff --git a/web/source/settings/views/moderation/reports/overview.tsx b/web/source/settings/views/moderation/reports/overview.tsx index ca8fc185c..03ce1a382 100644 --- a/web/source/settings/views/moderation/reports/overview.tsx +++ b/web/source/settings/views/moderation/reports/overview.tsx @@ -19,9 +19,7 @@ import React from "react"; import { Link } from "wouter"; - import FormWithData from "../../../lib/form/form-with-data"; - import Username from "./username"; import { useListReportsQuery } from "../../../lib/query/admin/reports"; @@ -77,7 +75,7 @@ function ReportEntry({ report }) { <div className={`report entry${report.action_taken ? " resolved" : ""}`}> <div className="byline"> <div className="usernames"> - <Username user={from} link={false} /> reported <Username user={target} link={false} /> + <Username user={from} /> reported <Username user={target} /> </div> <h3 className="report-status"> {report.action_taken ? "Resolved" : "Open"} diff --git a/web/source/settings/views/moderation/reports/username.tsx b/web/source/settings/views/moderation/reports/username.tsx index 6fba0b804..294d97e8b 100644 --- a/web/source/settings/views/moderation/reports/username.tsx +++ b/web/source/settings/views/moderation/reports/username.tsx @@ -19,8 +19,14 @@ import React from "react"; import { Link } from "wouter"; +import { AdminAccount } from "../../../lib/types/account"; -export default function Username({ user, link = true }) { +interface UsernameProps { + user: AdminAccount; + link?: string; +} + +export default function Username({ user, link }: UsernameProps) { let className = "user"; let isLocal = user.domain == null; @@ -36,19 +42,25 @@ export default function Username({ user, link = true }) { ? { fa: "fa-home", info: "Local user" } : { fa: "fa-external-link-square", info: "Remote user" }; - let Element: any = "div"; - let href: any = null; - - if (link) { - Element = Link; - href = `/settings/admin/accounts/${user.id}`; - } - - return ( - <Element className={className} to={href}> + const content = ( + <> <span className="acct">@{user.account.acct}</span> <i className={`fa fa-fw ${icon.fa}`} aria-hidden="true" title={icon.info} /> <span className="sr-only">{icon.info}</span> - </Element> + </> ); + + if (link) { + return ( + <Link className={className} to={link}> + {content} + </Link> + ); + } else { + return ( + <div className={className}> + {content} + </div> + ); + } } diff --git a/web/source/settings/views/moderation/routes.tsx b/web/source/settings/views/moderation/router.tsx index 238abaff6..37344462b 100644 --- a/web/source/settings/views/moderation/routes.tsx +++ b/web/source/settings/views/moderation/router.tsx @@ -17,51 +17,45 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import { MenuItem } from "../../lib/navigation/menu"; import React from "react"; -import { BaseUrlContext, useBaseUrl } from "../../lib/navigation/util"; +import { BaseUrlContext, useBaseUrl, useHasPermission } from "../../lib/navigation/util"; import { Redirect, Route, Router, Switch } from "wouter"; -import AccountsOverview from "./accounts"; -import AccountsPending from "./accounts/pending"; -import AccountDetail from "./accounts/detail"; import { ReportOverview } from "./reports/overview"; +import ReportDetail from "./reports/detail"; +import { ErrorBoundary } from "../../lib/navigation/error"; +import ImportExport from "./domain-permissions/import-export"; import DomainPermissionsOverview from "./domain-permissions/overview"; import DomainPermDetail from "./domain-permissions/detail"; -import ImportExport from "./domain-permissions/import-export"; -import ReportDetail from "./reports/detail"; +import AccountsOverview from "./accounts"; +import AccountsPending from "./accounts/pending"; +import AccountDetail from "./accounts/detail"; /* EXPORTED COMPONENTS */ /** - * Moderation menu. Reports, accounts, - * domain permissions import + export. - */ -export function ModerationMenu() { - return ( - <MenuItem - name="Moderation" - itemUrl="moderation" - defaultChild="reports" - permissions={["moderator"]} - > - <ModerationReportsMenu /> - <ModerationAccountsMenu /> - <ModerationDomainPermsMenu /> - </MenuItem> - ); -} - -/** - * Moderation router. Reports, accounts, - * domain permissions import + export. + * - /settings/moderation/reports/overview + * - /settings/moderation/reports/:reportId + * - /settings/moderation/accounts/overview + * - /settings/moderation/accounts/pending + * - /settings/moderation/accounts/:accountID + * - /settings/moderation/domain-permissions/:permType + * - /settings/moderation/domain-permissions/:permType/:domain + * - /settings/moderation/domain-permissions/import-export + * - /settings/moderation/domain-permissions/process */ -export function ModerationRouter() { +export default function ModerationRouter() { const parentUrl = useBaseUrl(); const thisBase = "/moderation"; const absBase = parentUrl + thisBase; - + + const permissions = ["moderator"]; + const moderator = useHasPermission(permissions); + if (!moderator) { + return null; + } + return ( <BaseUrlContext.Provider value={absBase}> <Router base={thisBase}> @@ -77,73 +71,10 @@ export function ModerationRouter() { INTERNAL COMPONENTS */ -/* - MENUS -*/ - -function ModerationReportsMenu() { - return ( - <MenuItem - name="Reports" - itemUrl="reports" - icon="fa-flag" - /> - ); -} - -function ModerationAccountsMenu() { - return ( - <MenuItem - name="Accounts" - itemUrl="accounts" - defaultChild="overview" - icon="fa-users" - > - <MenuItem - name="Overview" - itemUrl="overview" - icon="fa-list" - /> - <MenuItem - name="Pending" - itemUrl="pending" - icon="fa-question" - /> - </MenuItem> - ); -} - -function ModerationDomainPermsMenu() { - return ( - <MenuItem - name="Domain Permissions" - itemUrl="domain-permissions" - defaultChild="blocks" - icon="fa-hubzilla" - > - <MenuItem - name="Blocks" - itemUrl="blocks" - icon="fa-close" - /> - <MenuItem - name="Allows" - itemUrl="allows" - icon="fa-check" - /> - <MenuItem - name="Import/Export" - itemUrl="import-export" - icon="fa-floppy-o" - /> - </MenuItem> - ); -} - -/* - ROUTERS -*/ - +/** + * - /settings/moderation/reports/overview + * - /settings/moderation/reports/:reportId + */ function ModerationReportsRouter() { const parentUrl = useBaseUrl(); const thisBase = "/reports"; @@ -152,49 +83,66 @@ function ModerationReportsRouter() { return ( <BaseUrlContext.Provider value={absBase}> <Router base={thisBase}> - <Switch> - <Route path={"/:reportId"} component={ReportDetail} /> - <Route component={ReportOverview}/> - </Switch> + <ErrorBoundary> + <Switch> + <Route path={"/:reportId"} component={ReportDetail} /> + <Route component={ReportOverview}/> + </Switch> + </ErrorBoundary> </Router> </BaseUrlContext.Provider> ); } +/** + * - /settings/moderation/accounts/overview + * - /settings/moderation/accounts/pending + * - /settings/moderation/accounts/:accountID + */ function ModerationAccountsRouter() { const parentUrl = useBaseUrl(); const thisBase = "/accounts"; const absBase = parentUrl + thisBase; - + return ( <BaseUrlContext.Provider value={absBase}> <Router base={thisBase}> - <Switch> - <Route path="/overview" component={AccountsOverview}/> - <Route path="/pending" component={AccountsPending}/> - <Route path="/:accountID" component={AccountDetail}/> - <Route><Redirect to="/overview"/></Route> - </Switch> + <ErrorBoundary> + <Switch> + <Route path="/overview" component={AccountsOverview}/> + <Route path="/pending" component={AccountsPending}/> + <Route path="/:accountID" component={AccountDetail}/> + <Route><Redirect to="/overview"/></Route> + </Switch> + </ErrorBoundary> </Router> </BaseUrlContext.Provider> ); } +/** + * - /settings/moderation/domain-permissions/:permType + * - /settings/moderation/domain-permissions/:permType/:domain + * - /settings/moderation/domain-permissions/import-export + * - /settings/moderation/domain-permissions/process + */ function ModerationDomainPermsRouter() { const parentUrl = useBaseUrl(); const thisBase = "/domain-permissions"; const absBase = parentUrl + thisBase; - + return ( <BaseUrlContext.Provider value={absBase}> <Router base={thisBase}> - <Switch> - <Route path="/import-export" component={ImportExport} /> - <Route path="/process" component={ImportExport} /> - <Route path="/:permType/:domain" component={DomainPermDetail} /> - <Route path="/:permType" component={DomainPermissionsOverview} /> - <Route><Redirect to="/blocks"/></Route> - </Switch> + <ErrorBoundary> + <Switch> + <Route path="/import-export" component={ImportExport} /> + <Route path="/process" component={ImportExport} /> + <Route path="/:permType" component={DomainPermissionsOverview} /> + <Route path="/:permType/:domain" component={DomainPermDetail} /> + <Route><Redirect to="/blocks"/></Route> + </Switch> + </ErrorBoundary> </Router> </BaseUrlContext.Provider> ); diff --git a/web/source/settings/lib/query/index.js b/web/source/settings/views/user/menu.tsx index aeaa4a1d7..578bd8ae0 100644 --- a/web/source/settings/lib/query/index.js +++ b/web/source/settings/views/user/menu.tsx @@ -17,9 +17,36 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -module.exports = { - ...require("./gts-api"), - ...require("./oauth"), - ...require("./user"), - ...require("./admin") -}; +import { MenuItem } from "../../lib/navigation/menu"; +import React from "react"; + +/** + * - /settings/user/profile + * - /settings/user/settings + * - /settings/user/migration + */ +export default function UserMenu() { + return ( + <MenuItem + name="User" + itemUrl="user" + defaultChild="profile" + > + <MenuItem + name="Profile" + itemUrl="profile" + icon="fa-user" + /> + <MenuItem + name="Settings" + itemUrl="settings" + icon="fa-cogs" + /> + <MenuItem + name="Migration" + itemUrl="migration" + icon="fa-exchange" + /> + </MenuItem> + ); +} diff --git a/web/source/settings/views/user/profile.tsx b/web/source/settings/views/user/profile.tsx index 08cd74bda..c1735259e 100644 --- a/web/source/settings/views/user/profile.tsx +++ b/web/source/settings/views/user/profile.tsx @@ -42,9 +42,10 @@ import FormWithData from "../../lib/form/form-with-data"; import FakeProfile from "../../components/fake-profile"; import MutationButton from "../../components/form/mutation-button"; -import { useAccountThemesQuery, useInstanceV1Query } from "../../lib/query"; +import { useAccountThemesQuery } from "../../lib/query/user"; import { useUpdateCredentialsMutation } from "../../lib/query/user"; import { useVerifyCredentialsQuery } from "../../lib/query/oauth"; +import { useInstanceV1Query } from "../../lib/query/gts-api"; export default function UserProfile() { return ( diff --git a/web/source/settings/views/user/routes.tsx b/web/source/settings/views/user/router.tsx index 76ac50bc2..e763c0c2b 100644 --- a/web/source/settings/views/user/routes.tsx +++ b/web/source/settings/views/user/router.tsx @@ -17,49 +17,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -import { MenuItem } from "../../lib/navigation/menu"; import React from "react"; import { BaseUrlContext, useBaseUrl } from "../../lib/navigation/util"; +import { Redirect, Route, Router, Switch } from "wouter"; +import { ErrorBoundary } from "../../lib/navigation/error"; import UserProfile from "./profile"; -import UserSettings from "./settings"; import UserMigration from "./migration"; -import { Redirect, Route, Router, Switch } from "wouter"; +import UserSettings from "./settings"; /** - * - * Basic user menu. Profile + accounts - * settings, post settings, migration. + * - /settings/user/profile + * - /settings/user/settings + * - /settings/user/migration */ -export function UserMenu() { - return ( - <MenuItem - name="User" - itemUrl="user" - defaultChild="profile" - > - {/* Profile */} - <MenuItem - name="Profile" - itemUrl="profile" - icon="fa-user" - /> - {/* Settings */} - <MenuItem - name="Settings" - itemUrl="settings" - icon="fa-cogs" - /> - {/* Migration */} - <MenuItem - name="Migration" - itemUrl="migration" - icon="fa-exchange" - /> - </MenuItem> - ); -} - -export function UserRouter() { +export default function UserRouter() { const baseUrl = useBaseUrl(); const thisBase = "/user"; const absBase = baseUrl + thisBase; @@ -67,13 +38,14 @@ export function UserRouter() { return ( <BaseUrlContext.Provider value={absBase}> <Router base={thisBase}> - <Switch> - <Route path="/profile" component={UserProfile} /> - <Route path="/settings" component={UserSettings} /> - <Route path="/migration" component={UserMigration} /> - {/* Fallback component */} - <Route><Redirect to="/profile" /></Route> - </Switch> + <ErrorBoundary> + <Switch> + <Route path="/profile" component={UserProfile} /> + <Route path="/settings" component={UserSettings} /> + <Route path="/migration" component={UserMigration} /> + <Route><Redirect to="/profile" /></Route> + </Switch> + </ErrorBoundary> </Router> </BaseUrlContext.Provider> ); diff --git a/web/source/settings/views/user/settings.tsx b/web/source/settings/views/user/settings.tsx index 2827cc53f..cbd973706 100644 --- a/web/source/settings/views/user/settings.tsx +++ b/web/source/settings/views/user/settings.tsx @@ -18,18 +18,19 @@ */ import React from "react"; -import query from "../../lib/query"; import { useTextInput, useBoolInput } from "../../lib/form"; import useFormSubmit from "../../lib/form/submit"; import { Select, TextInput, Checkbox } from "../../components/form/inputs"; import FormWithData from "../../lib/form/form-with-data"; import Languages from "../../components/languages"; import MutationButton from "../../components/form/mutation-button"; +import { useVerifyCredentialsQuery } from "../../lib/query/oauth"; +import { usePasswordChangeMutation, useUpdateCredentialsMutation } from "../../lib/query/user"; export default function UserSettings() { return ( <FormWithData - dataQuery={query.useVerifyCredentialsQuery} + dataQuery={useVerifyCredentialsQuery} DataForm={UserSettingsForm} /> ); @@ -50,7 +51,7 @@ function UserSettingsForm({ data }) { statusContentType: useTextInput("source[status_content_type]", { source: data, defaultValue: "text/plain" }), }; - const [submitForm, result] = useFormSubmit(form, query.useUpdateCredentialsMutation()); + const [submitForm, result] = useFormSubmit(form, useUpdateCredentialsMutation()); return ( <> @@ -123,7 +124,7 @@ function PasswordChange() { } }); - const [submitForm, result] = useFormSubmit(form, query.usePasswordChangeMutation()); + const [submitForm, result] = useFormSubmit(form, usePasswordChangeMutation()); return ( <form className="change-password" onSubmit={submitForm}> |