Compare commits

...

7 commits

Author SHA1 Message Date
Daenney 98f0f7a945
Merge d94eceb3ee into aecf74951c 2024-04-25 13:57:28 -04:00
tobi aecf74951c
[chore] Settings refactor 2: the re-refactoring-ing (#2866)
* [chore] Bit more refactoring of settings panel

* fix up some remaining things

* groovy baby yeah!

* remove unused Suspense
2024-04-25 18:24:24 +02:00
Daniele Sluijters d94eceb3ee [chore] Update Dockerfile to newer Alpine version 2024-04-22 16:09:46 +02:00
Daniele Sluijters 487374c410 [chore] Update Docker container to Go 1.22 2024-04-22 16:09:35 +02:00
Daniele Sluijters 7ab37a6940 [chore] Update golangci-lint to 1.25.7
Newer version should know about Go 1.22 and run fine.
2024-04-22 16:07:54 +02:00
Daniele Sluijters ddccaef3d0 [chore] Update CI to Go 1.22 2024-04-22 16:07:40 +02:00
Daniele Sluijters 9a2ce5c19e [chore] Upgrade our Go version to 1.22
With Go 1.22 having been released at the start of February, it's now
been a few months. No major issues have shown up, and the two point
release since then have primarily been security fixes plus some general
bug fixing.

This sets the required Go version to 1.22, as there's nothing in 1.22.1
or 1.22.2 that we would explicitly require. It sets the toolchain to the
latest point release, to ensure we pick up any fixes from there when
building releases etc.
2024-04-22 15:51:49 +02:00
44 changed files with 1369 additions and 967 deletions

View file

@ -12,7 +12,7 @@ steps:
# We use golangci-lint for linting.
# See: https://golangci-lint.run/
- name: lint
image: golangci/golangci-lint:v1.55.0
image: golangci/golangci-lint:v1.57.2
volumes:
- name: go-build-cache
path: /root/.cache/go-build
@ -28,7 +28,7 @@ steps:
- pull_request
- name: test
image: golang:1.21-alpine
image: golang:1.22-alpine
volumes:
- name: go-build-cache
path: /root/.cache/go-build
@ -80,7 +80,7 @@ steps:
- yarn --cwd ./web/source build
- name: snapshot
image: superseriousbusiness/gotosocial-drone-build:0.4.0 # https://github.com/superseriousbusiness/gotosocial-drone-build
image: superseriousbusiness/gotosocial-drone-build:0.5.0 # https://github.com/superseriousbusiness/gotosocial-drone-build
volumes:
- name: go-build-cache
path: /root/.cache/go-build
@ -121,7 +121,7 @@ steps:
- main
- name: release
image: superseriousbusiness/gotosocial-drone-build:0.4.0 # https://github.com/superseriousbusiness/gotosocial-drone-build
image: superseriousbusiness/gotosocial-drone-build:0.5.0 # https://github.com/superseriousbusiness/gotosocial-drone-build
volumes:
- name: go-build-cache
path: /root/.cache/go-build
@ -180,7 +180,7 @@ clone:
steps:
- name: mirror
image: superseriousbusiness/gotosocial-drone-build:0.4.0
image: superseriousbusiness/gotosocial-drone-build:0.5.0
environment:
ORIGIN_REPO: https://github.com/superseriousbusiness/gotosocial
TARGET_REPO: https://codeberg.org/superseriousbusiness/gotosocial

View file

@ -2,7 +2,7 @@
# Dockerfile reference: https://docs.docker.com/engine/reference/builder/
# stage 1: generate up-to-date swagger.yaml to put in the final container
FROM --platform=${BUILDPLATFORM} golang:1.21-alpine AS swagger
FROM --platform=${BUILDPLATFORM} golang:1.22-alpine AS swagger
RUN \
### Installs goswagger for building swagger definitions inside this container
@ -28,7 +28,7 @@ RUN yarn --cwd ./web/source install && \
rm -rf ./web/source
# stage 3: build the executor container
FROM --platform=${TARGETPLATFORM} alpine:3.17.2 as executor
FROM --platform=${TARGETPLATFORM} alpine:3.19.1 as executor
# switch to non-root user:group for GtS
USER 1000:1000

4
go.mod
View file

@ -1,10 +1,10 @@
module github.com/superseriousbusiness/gotosocial
go 1.21
go 1.22
replace modernc.org/sqlite => gitlab.com/NyaaaWhatsUpDoc/sqlite v1.29.8-concurrency-workaround
toolchain go1.21.3
toolchain go1.22.2
require (
codeberg.org/gruf/go-bytes v1.0.2

View file

@ -96,4 +96,4 @@ skulk({
}]]
}
}
});
});

View file

@ -35,10 +35,10 @@
"wouter": "^3.1.0"
},
"devDependencies": {
"@babel/core": "^7.23.0",
"@babel/preset-env": "^7.22.20",
"@babel/preset-react": "^7.22.15",
"@babel/preset-typescript": "^7.23.0",
"@babel/core": "^7.24.4",
"@babel/preset-env": "^7.24.4",
"@babel/preset-react": "^7.24.1",
"@babel/preset-typescript": "^7.24.1",
"@browserify/envify": "^6.0.0",
"@browserify/uglifyify": "^6.0.0",
"@joepie91/eslint-config": "^1.1.1",

View file

@ -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>
);
};
}

View file

@ -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 };
export { ErrorFallback, Error };

View file

@ -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>
);
};
}

View file

@ -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>
);
};
}

View file

@ -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;
};
}

View file

@ -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" />
);
};
}

View file

@ -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>
);
}
};
}

View file

@ -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>

View file

@ -601,31 +601,33 @@ 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 {
section.with-sidebar {
grid-column: 1;
padding: 1rem;
}
div.sidebar a:first-child h2 {
border-top-right-radius: $br;
}
}
section {
grid-column: 1;
}
.user-profile .overview {
grid-template-columns: auto;
grid-template-rows: auto 1fr;

View file

@ -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");

View file

@ -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" });

View file

@ -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();

View file

@ -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;

View file

@ -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";
import { useDeleteInstanceRuleMutation, useInstanceRulesQuery, useUpdateInstanceRuleMutation } from "../../../lib/query/admin";
import { Error } from "../../../components/error";
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>
);
}
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`} />
);
}

View file

@ -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>
);
}

View file

@ -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 (

View file

@ -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>
);
}

View file

@ -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>
);
}

View file

@ -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>
);
}

View file

@ -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";

View file

@ -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";

View file

@ -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";

View file

@ -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() {

View file

@ -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";

View file

@ -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";

View file

@ -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";

View file

@ -18,9 +18,7 @@
*/
import React from "react";
import { memo, useMemo, useCallback, useEffect } from "react";
import { isValidDomainPermission, hasBetterScope } from "../../../lib/util/domain-permission";
import {

View file

@ -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>
);
}

View file

@ -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 &&

View file

@ -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"}

View file

@ -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>
);
}
}

View file

@ -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 { 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 AccountsOverview from "./accounts";
import AccountsPending from "./accounts/pending";
import AccountDetail from "./accounts/detail";
import { ReportOverview } from "./reports/overview";
import DomainPermissionsOverview from "./domain-permissions/overview";
import DomainPermDetail from "./domain-permissions/detail";
import ImportExport from "./domain-permissions/import-export";
import ReportDetail from "./reports/detail";
/*
EXPORTED COMPONENTS
*/
/**
* Moderation menu. 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 ModerationMenu() {
return (
<MenuItem
name="Moderation"
itemUrl="moderation"
defaultChild="reports"
permissions={["moderator"]}
>
<ModerationReportsMenu />
<ModerationAccountsMenu />
<ModerationDomainPermsMenu />
</MenuItem>
);
}
/**
* Moderation router. Reports, accounts,
* domain permissions import + export.
*/
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>
);

View file

@ -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>
);
}

View file

@ -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 (

View file

@ -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 UserProfile from "./profile";
import UserSettings from "./settings";
import UserMigration from "./migration";
import { Redirect, Route, Router, Switch } from "wouter";
import { ErrorBoundary } from "../../lib/navigation/error";
import UserProfile from "./profile";
import UserMigration from "./migration";
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>
);

View file

@ -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}>

View file

@ -25,9 +25,9 @@
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
"module": "preserve", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
"moduleResolution": "bundler", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
@ -44,7 +44,7 @@
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */

File diff suppressed because it is too large Load diff