summaryrefslogtreecommitdiff
path: root/web/source/settings/admin
diff options
context:
space:
mode:
Diffstat (limited to 'web/source/settings/admin')
-rw-r--r--web/source/settings/admin/accounts/detail.jsx6
-rw-r--r--web/source/settings/admin/domain-permissions/detail.tsx254
-rw-r--r--web/source/settings/admin/domain-permissions/export-format-table.jsx (renamed from web/source/settings/admin/federation/import-export/export-format-table.jsx)0
-rw-r--r--web/source/settings/admin/domain-permissions/form.tsx (renamed from web/source/settings/admin/federation/import-export/form.jsx)68
-rw-r--r--web/source/settings/admin/domain-permissions/import-export.tsx90
-rw-r--r--web/source/settings/admin/domain-permissions/index.tsx (renamed from web/source/settings/admin/federation/index.js)36
-rw-r--r--web/source/settings/admin/domain-permissions/overview.tsx198
-rw-r--r--web/source/settings/admin/domain-permissions/process.tsx (renamed from web/source/settings/admin/federation/import-export/process.jsx)177
-rw-r--r--web/source/settings/admin/emoji/category-select.jsx5
-rw-r--r--web/source/settings/admin/emoji/local/detail.js18
-rw-r--r--web/source/settings/admin/emoji/local/new-emoji.js10
-rw-r--r--web/source/settings/admin/emoji/local/overview.js4
-rw-r--r--web/source/settings/admin/emoji/local/use-shortcode.js8
-rw-r--r--web/source/settings/admin/emoji/remote/index.js4
-rw-r--r--web/source/settings/admin/emoji/remote/parse-from-toot.js14
-rw-r--r--web/source/settings/admin/federation/detail.js168
-rw-r--r--web/source/settings/admin/federation/import-export/index.jsx75
-rw-r--r--web/source/settings/admin/federation/overview.js101
-rw-r--r--web/source/settings/admin/reports/detail.jsx14
-rw-r--r--web/source/settings/admin/reports/index.jsx7
-rw-r--r--web/source/settings/admin/settings/index.jsx13
-rw-r--r--web/source/settings/admin/settings/rules.jsx4
22 files changed, 794 insertions, 480 deletions
diff --git a/web/source/settings/admin/accounts/detail.jsx b/web/source/settings/admin/accounts/detail.jsx
index 0e906cd1c..63049c149 100644
--- a/web/source/settings/admin/accounts/detail.jsx
+++ b/web/source/settings/admin/accounts/detail.jsx
@@ -22,13 +22,13 @@ const { useRoute, Redirect } = require("wouter");
const query = require("../../lib/query");
-const FormWithData = require("../../lib/form/form-with-data");
+const FormWithData = require("../../lib/form/form-with-data").default;
const { useBaseUrl } = require("../../lib/navigation/util");
const FakeProfile = require("../../components/fake-profile");
const MutationButton = require("../../components/form/mutation-button");
-const useFormSubmit = require("../../lib/form/submit");
+const useFormSubmit = require("../../lib/form/submit").default;
const { useValue, useTextInput } = require("../../lib/form");
const { TextInput } = require("../../components/form/inputs");
@@ -77,7 +77,7 @@ function AccountDetailForm({ data: account }) {
function ModifyAccount({ account }) {
const form = {
id: useValue("id", account.id),
- reason: useTextInput("text", {})
+ reason: useTextInput("text")
};
const [modifyAccount, result] = useFormSubmit(form, query.useActionAccountMutation());
diff --git a/web/source/settings/admin/domain-permissions/detail.tsx b/web/source/settings/admin/domain-permissions/detail.tsx
new file mode 100644
index 000000000..f74802666
--- /dev/null
+++ b/web/source/settings/admin/domain-permissions/detail.tsx
@@ -0,0 +1,254 @@
+/*
+ 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 { useMemo } from "react";
+import { useLocation } from "wouter";
+
+import { useTextInput, useBoolInput } from "../../lib/form";
+
+import useFormSubmit from "../../lib/form/submit";
+
+import { TextInput, Checkbox, TextArea } from "../../components/form/inputs";
+
+import Loading from "../../components/loading";
+import BackButton from "../../components/back-button";
+import MutationButton from "../../components/form/mutation-button";
+
+import { useDomainAllowsQuery, useDomainBlocksQuery } from "../../lib/query/admin/domain-permissions/get";
+import { useAddDomainAllowMutation, useAddDomainBlockMutation, useRemoveDomainAllowMutation, useRemoveDomainBlockMutation } from "../../lib/query/admin/domain-permissions/update";
+import { DomainPerm, PermType } from "../../lib/types/domain-permission";
+import { NoArg } from "../../lib/types/query";
+import { Error } from "../../components/error";
+
+export interface DomainPermDetailProps {
+ baseUrl: string;
+ permType: PermType;
+ domain: string;
+}
+
+export default function DomainPermDetail({ baseUrl, permType, domain }: DomainPermDetailProps) {
+ const { data: domainBlocks = {}, isLoading: isLoadingDomainBlocks } = useDomainBlocksQuery(NoArg, { skip: permType !== "block" });
+ const { data: domainAllows = {}, isLoading: isLoadingDomainAllows } = useDomainAllowsQuery(NoArg, { skip: permType !== "allow" });
+
+ let isLoading;
+ switch (permType) {
+ case "block":
+ isLoading = isLoadingDomainBlocks;
+ break;
+ case "allow":
+ isLoading = isLoadingDomainAllows;
+ break;
+ default:
+ throw "perm type unknown";
+ }
+
+ if (domain == "view") {
+ // Retrieve domain from form field submission.
+ domain = (new URL(document.location.toString())).searchParams.get("domain")?? "unknown";
+ }
+
+ if (domain == "unknown") {
+ throw "unknown domain";
+ }
+
+ // Normalize / decode domain (it may be URL-encoded).
+ domain = decodeURIComponent(domain);
+
+ // Check if we already have a perm of the desired type for this domain.
+ const existingPerm: DomainPerm | undefined = useMemo(() => {
+ if (permType == "block") {
+ return domainBlocks[domain];
+ } else {
+ return domainAllows[domain];
+ }
+ }, [domainBlocks, domainAllows, domain, permType]);
+
+ let infoContent: React.JSX.Element;
+
+ if (isLoading) {
+ infoContent = <Loading />;
+ } else if (existingPerm == undefined) {
+ infoContent = <span>No stored {permType} yet, you can add one below:</span>;
+ } else {
+ infoContent = (
+ <div className="info">
+ <i className="fa fa-fw fa-exclamation-triangle" aria-hidden="true"></i>
+ <b>Editing domain permissions isn't implemented yet, <a href="https://github.com/superseriousbusiness/gotosocial/issues/1198" target="_blank" rel="noopener noreferrer">check here for progress</a></b>
+ </div>
+ );
+ }
+
+ return (
+ <div>
+ <h1 className="text-cutoff"><BackButton to={baseUrl} /> Domain {permType} for: <span title={domain}>{domain}</span></h1>
+ {infoContent}
+ <DomainPermForm
+ defaultDomain={domain}
+ perm={existingPerm}
+ permType={permType}
+ baseUrl={baseUrl}
+ />
+ </div>
+ );
+}
+
+interface DomainPermFormProps {
+ defaultDomain: string;
+ perm?: DomainPerm;
+ permType: PermType;
+ baseUrl: string;
+}
+
+function DomainPermForm({ defaultDomain, perm, permType, baseUrl }: DomainPermFormProps) {
+ const isExistingPerm = perm !== undefined;
+ const disabledForm = isExistingPerm
+ ? {
+ disabled: true,
+ title: "Domain permissions currently cannot be edited."
+ }
+ : {
+ disabled: false,
+ title: "",
+ };
+
+ const form = {
+ domain: useTextInput("domain", { source: perm, defaultValue: defaultDomain }),
+ obfuscate: useBoolInput("obfuscate", { source: perm }),
+ commentPrivate: useTextInput("private_comment", { source: perm }),
+ commentPublic: useTextInput("public_comment", { source: perm })
+ };
+
+ // Check which perm type we're meant to be handling
+ // here, and use appropriate mutations and results.
+ // We can't call these hooks conditionally because
+ // react is like "weh" (mood), but we can decide
+ // which ones to use conditionally.
+ const [ addBlock, addBlockResult ] = useAddDomainBlockMutation();
+ const [ removeBlock, removeBlockResult] = useRemoveDomainBlockMutation({ fixedCacheKey: perm?.id });
+ const [ addAllow, addAllowResult ] = useAddDomainAllowMutation();
+ const [ removeAllow, removeAllowResult ] = useRemoveDomainAllowMutation({ fixedCacheKey: perm?.id });
+
+ const [
+ addTrigger,
+ addResult,
+ removeTrigger,
+ removeResult,
+ ] = useMemo(() => {
+ return permType == "block"
+ ? [
+ addBlock,
+ addBlockResult,
+ removeBlock,
+ removeBlockResult,
+ ]
+ : [
+ addAllow,
+ addAllowResult,
+ removeAllow,
+ removeAllowResult,
+ ];
+ }, [permType,
+ addBlock, addBlockResult, removeBlock, removeBlockResult,
+ addAllow, addAllowResult, removeAllow, removeAllowResult,
+ ]);
+
+ // Use appropriate submission params for this permType.
+ const [submitForm, submitFormResult] = useFormSubmit(form, [addTrigger, addResult], { changedOnly: false });
+
+ // Uppercase first letter of given permType.
+ const permTypeUpper = useMemo(() => {
+ return permType.charAt(0).toUpperCase() + permType.slice(1);
+ }, [permType]);
+
+ const [location, setLocation] = useLocation();
+
+ function verifyUrlThenSubmit(e) {
+ // Adding a new domain permissions happens on a url like
+ // "/settings/admin/domain-permissions/:permType/domain.com",
+ // but if domain input changes, that doesn't match anymore
+ // and causes issues later on so, before submitting the form,
+ // silently change url, and THEN submit.
+ let correctUrl = `${baseUrl}/${form.domain.value}`;
+ if (location != correctUrl) {
+ setLocation(correctUrl);
+ }
+ return submitForm(e);
+ }
+
+ return (
+ <form onSubmit={verifyUrlThenSubmit}>
+ <TextInput
+ field={form.domain}
+ label="Domain"
+ placeholder="example.com"
+ {...disabledForm}
+ />
+
+ <Checkbox
+ field={form.obfuscate}
+ label="Obfuscate domain in public lists"
+ {...disabledForm}
+ />
+
+ <TextArea
+ field={form.commentPrivate}
+ label="Private comment"
+ rows={3}
+ {...disabledForm}
+ />
+
+ <TextArea
+ field={form.commentPublic}
+ label="Public comment"
+ rows={3}
+ {...disabledForm}
+ />
+
+ <div className="action-buttons row">
+ <MutationButton
+ label={permTypeUpper}
+ result={submitFormResult}
+ showError={false}
+ {...disabledForm}
+ />
+
+ {
+ isExistingPerm &&
+ <MutationButton
+ type="button"
+ onClick={() => removeTrigger(perm.id?? "")}
+ label="Remove"
+ result={removeResult}
+ className="button danger"
+ showError={false}
+ disabled={!isExistingPerm}
+ />
+ }
+ </div>
+
+ <>
+ {addResult.error && <Error error={addResult.error} />}
+ {removeResult.error && <Error error={removeResult.error} />}
+ </>
+
+ </form>
+ );
+}
diff --git a/web/source/settings/admin/federation/import-export/export-format-table.jsx b/web/source/settings/admin/domain-permissions/export-format-table.jsx
index 7fcffa348..7fcffa348 100644
--- a/web/source/settings/admin/federation/import-export/export-format-table.jsx
+++ b/web/source/settings/admin/domain-permissions/export-format-table.jsx
diff --git a/web/source/settings/admin/federation/import-export/form.jsx b/web/source/settings/admin/domain-permissions/form.tsx
index 2086739e3..fb639202d 100644
--- a/web/source/settings/admin/federation/import-export/form.jsx
+++ b/web/source/settings/admin/domain-permissions/form.tsx
@@ -17,34 +17,57 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-const React = require("react");
+import React from "react";
-const query = require("../../../lib/query");
-const useFormSubmit = require("../../../lib/form/submit");
+import { useEffect } from "react";
-const {
+import { useExportDomainListMutation } from "../../lib/query/admin/domain-permissions/export";
+import useFormSubmit from "../../lib/form/submit";
+
+import {
+ RadioGroup,
TextArea,
Select,
-} = require("../../../components/form/inputs");
+} from "../../components/form/inputs";
+
+import MutationButton from "../../components/form/mutation-button";
+
+import { Error } from "../../components/error";
+import ExportFormatTable from "./export-format-table";
-const MutationButton = require("../../../components/form/mutation-button");
+import type {
+ FormSubmitFunction,
+ FormSubmitResult,
+ RadioFormInputHook,
+ TextFormInputHook,
+} from "../../lib/form/types";
-const { Error } = require("../../../components/error");
-const ExportFormatTable = require("./export-format-table");
+export interface ImportExportFormProps {
+ form: {
+ domains: TextFormInputHook;
+ exportType: TextFormInputHook;
+ permType: RadioFormInputHook;
+ };
+ submitParse: FormSubmitFunction;
+ parseResult: FormSubmitResult;
+}
-module.exports = function ImportExportForm({ form, submitParse, parseResult }) {
- const [submitExport, exportResult] = useFormSubmit(form, query.useExportDomainListMutation());
+export default function ImportExportForm({ form, submitParse, parseResult }: ImportExportFormProps) {
+ const [submitExport, exportResult] = useFormSubmit(form, useExportDomainListMutation());
function fileChanged(e) {
const reader = new FileReader();
reader.onload = function (read) {
- form.domains.value = read.target.result;
- submitParse();
+ const res = read.target?.result;
+ if (typeof res === "string") {
+ form.domains.value = res;
+ submitParse();
+ }
};
reader.readAsText(e.target.files[0]);
}
- React.useEffect(() => {
+ useEffect(() => {
if (exportResult.isSuccess) {
form.domains.setter(exportResult.data);
}
@@ -53,12 +76,10 @@ module.exports = function ImportExportForm({ form, submitParse, parseResult }) {
return (
<>
- <h1>Import / Export suspended domains</h1>
- <p>
- This page can be used to import and export lists of domains to suspend.
- Exports can be done in various formats, with varying functionality and support in other software.
- Imports will automatically detect what format is being processed.
- </p>
+ <h1>Import / Export domain permissions</h1>
+ <p>This page can be used to import and export lists of domain permissions.</p>
+ <p>Exports can be done in various formats, with varying functionality and support in other software.</p>
+ <p>Imports will automatically detect what format is being processed.</p>
<ExportFormatTable />
<div className="import-export">
<TextArea
@@ -68,6 +89,10 @@ module.exports = function ImportExportForm({ form, submitParse, parseResult }) {
rows={8}
/>
+ <RadioGroup
+ field={form.permType}
+ />
+
<div className="button-grid">
<MutationButton
label="Import"
@@ -75,6 +100,7 @@ module.exports = function ImportExportForm({ form, submitParse, parseResult }) {
onClick={() => submitParse()}
result={parseResult}
showError={false}
+ disabled={false}
/>
<label className="button with-icon">
<i className="fa fa-fw " aria-hidden="true" />
@@ -92,6 +118,7 @@ module.exports = function ImportExportForm({ form, submitParse, parseResult }) {
type="button"
onClick={() => submitExport("export")}
result={exportResult} showError={false}
+ disabled={false}
/>
<MutationButton
label="Export to file"
@@ -100,6 +127,7 @@ module.exports = function ImportExportForm({ form, submitParse, parseResult }) {
onClick={() => submitExport("export-file")}
result={exportResult}
showError={false}
+ disabled={false}
/>
<div className="export-file">
<span>
@@ -121,4 +149,4 @@ module.exports = function ImportExportForm({ form, submitParse, parseResult }) {
</div>
</>
);
-}; \ No newline at end of file
+}
diff --git a/web/source/settings/admin/domain-permissions/import-export.tsx b/web/source/settings/admin/domain-permissions/import-export.tsx
new file mode 100644
index 000000000..871bca131
--- /dev/null
+++ b/web/source/settings/admin/domain-permissions/import-export.tsx
@@ -0,0 +1,90 @@
+/*
+ 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 { Switch, Route, Redirect, useLocation } from "wouter";
+
+import { useProcessDomainPermissionsMutation } from "../../lib/query/admin/domain-permissions/process";
+
+import { useTextInput, useRadioInput } from "../../lib/form";
+
+import useFormSubmit from "../../lib/form/submit";
+
+import { ProcessImport } from "./process";
+import ImportExportForm from "./form";
+
+export default function ImportExport({ baseUrl }) {
+ const form = {
+ domains: useTextInput("domains"),
+ exportType: useTextInput("exportType", { defaultValue: "plain", dontReset: true }),
+ permType: useRadioInput("permType", {
+ options: {
+ block: "Domain blocks",
+ allow: "Domain allows",
+ }
+ })
+ };
+
+ const [submitParse, parseResult] = useFormSubmit(form, useProcessDomainPermissionsMutation(), { changedOnly: false });
+
+ const [_location, setLocation] = useLocation();
+
+ return (
+ <Switch>
+ <Route path={`${baseUrl}/process`}>
+ {
+ parseResult.isSuccess
+ ? (
+ <>
+ <h1>
+ <span
+ className="button"
+ onClick={() => {
+ parseResult.reset();
+ setLocation(baseUrl);
+ }}
+ >
+ &lt; back
+ </span>
+ &nbsp; Confirm import of domain {form.permType.value}s:
+ </h1>
+ <ProcessImport
+ list={parseResult.data}
+ permType={form.permType}
+ />
+ </>
+ )
+ : <Redirect to={baseUrl} />
+ }
+ </Route>
+ <Route>
+ {
+ parseResult.isSuccess
+ ? <Redirect to={`${baseUrl}/process`} />
+ : <ImportExportForm
+ form={form}
+ submitParse={submitParse}
+ parseResult={parseResult}
+ />
+ }
+ </Route>
+ </Switch>
+ );
+}
diff --git a/web/source/settings/admin/federation/index.js b/web/source/settings/admin/domain-permissions/index.tsx
index ec536c0be..7d790cfc8 100644
--- a/web/source/settings/admin/federation/index.js
+++ b/web/source/settings/admin/domain-permissions/index.tsx
@@ -17,25 +17,33 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-const React = require("react");
-const { Switch, Route } = require("wouter");
+import React from "react";
+import { Switch, Route } from "wouter";
-const InstanceOverview = require("./overview");
-const InstanceDetail = require("./detail");
-const InstanceImportExport = require("./import-export");
+import DomainPermissionsOverview from "./overview";
+import { PermType } from "../../lib/types/domain-permission";
+import DomainPermDetail from "./detail";
-module.exports = function Federation({ baseUrl }) {
+export default function DomainPermissions({ baseUrl }: { baseUrl: string }) {
return (
<Switch>
- <Route path={`${baseUrl}/import-export/:list?`}>
- <InstanceImportExport />
+ <Route path="/settings/admin/domain-permissions/:permType/:domain">
+ {params => (
+ <DomainPermDetail
+ permType={params.permType as PermType}
+ baseUrl={baseUrl}
+ domain={params.domain}
+ />
+ )}
</Route>
-
- <Route path={`${baseUrl}/:domain`}>
- <InstanceDetail baseUrl={baseUrl} />
+ <Route path="/settings/admin/domain-permissions/:permType">
+ {params => (
+ <DomainPermissionsOverview
+ permType={params.permType as PermType}
+ baseUrl={baseUrl}
+ />
+ )}
</Route>
-
- <InstanceOverview baseUrl={baseUrl} />
</Switch>
);
-}; \ No newline at end of file
+}
diff --git a/web/source/settings/admin/domain-permissions/overview.tsx b/web/source/settings/admin/domain-permissions/overview.tsx
new file mode 100644
index 000000000..a37ec9184
--- /dev/null
+++ b/web/source/settings/admin/domain-permissions/overview.tsx
@@ -0,0 +1,198 @@
+/*
+ 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 { useMemo } from "react";
+import { Link, useLocation } 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";
+import { NoArg } from "../../lib/types/query";
+
+export interface DomainPermissionsOverviewProps {
+ // Params injected by
+ // the wouter router.
+ permType: PermType;
+ baseUrl: string,
+}
+
+export default function DomainPermissionsOverview({ permType, baseUrl }: DomainPermissionsOverviewProps) {
+ if (permType !== "block" && permType !== "allow") {
+ throw "unrecognized perm type " + permType;
+ }
+
+ // Uppercase first letter of given permType.
+ const permTypeUpper = useMemo(() => {
+ return permType.charAt(0).toUpperCase() + permType.slice(1);
+ }, [permType]);
+
+ // Fetch / wait for desired perms to load.
+ const { data: blocks, isLoading: isLoadingBlocks } = useDomainBlocksQuery(NoArg, { skip: permType !== "block" });
+ const { data: allows, isLoading: isLoadingAllows } = useDomainAllowsQuery(NoArg, { skip: permType !== "allow" });
+
+ let data: MappedDomainPerms | undefined;
+ let isLoading: boolean;
+
+ if (permType == "block") {
+ data = blocks;
+ isLoading = isLoadingBlocks;
+ } else {
+ data = allows;
+ isLoading = isLoadingAllows;
+ }
+
+ if (isLoading || data === undefined) {
+ return <Loading />;
+ }
+
+ return (
+ <div>
+ <h1>Domain {permTypeUpper}s</h1>
+ { permType == "block" ? <BlockHelperText/> : <AllowHelperText/> }
+ <DomainPermsList
+ data={data}
+ baseUrl={baseUrl}
+ permType={permType}
+ permTypeUpper={permTypeUpper}
+ />
+ <Link to={`${baseUrl}/import-export`}>
+ <a>Or use the bulk import/export interface</a>
+ </Link>
+ </div>
+ );
+}
+
+interface DomainPermsListProps {
+ data: MappedDomainPerms;
+ baseUrl: string;
+ permType: PermType;
+ permTypeUpper: string;
+}
+
+function DomainPermsList({ data, baseUrl, permType, permTypeUpper }: DomainPermsListProps) {
+ // Format perms into a list.
+ const perms = useMemo(() => {
+ return Object.values(data);
+ }, [data]);
+
+ const [_location, setLocation] = useLocation();
+ const filterField = useTextInput("filter");
+
+ function filterFormSubmit(e) {
+ e.preventDefault();
+ setLocation(`${baseUrl}/${filter}`);
+ }
+
+ const filter = filterField.value ?? "";
+ const filteredPerms = useMemo(() => {
+ return matchSorter(perms, filter, { keys: ["domain"] });
+ }, [perms, filter]);
+ const filtered = perms.length - filteredPerms.length;
+
+ const filterInfo = (
+ <span>
+ {perms.length} {permType}ed domain{perms.length != 1 ? "s" : ""} {filtered > 0 && `(${filtered} filtered by search)`}
+ </span>
+ );
+
+ const entries = filteredPerms.map((entry) => {
+ return (
+ <Link key={entry.domain} to={`${baseUrl}/${entry.domain}`}>
+ <a className="entry nounderline">
+ <span id="domain">{entry.domain}</span>
+ <span id="date">{new Date(entry.created_at ?? "").toLocaleString()}</span>
+ </a>
+ </Link>
+ );
+ });
+
+ return (
+ <div className="domain-permissions-list">
+ <form className="filter" role="search" onSubmit={filterFormSubmit}>
+ <TextInput
+ field={filterField}
+ placeholder="example.org"
+ label={`Search or add domain ${permType}`}
+ />
+ <Link to={`${baseUrl}/${filter}`}>
+ <a className="button">{permTypeUpper}&nbsp;{filter}</a>
+ </Link>
+ </form>
+ <div>
+ {filterInfo}
+ <div className="list">
+ <div className="entries scrolling">
+ {entries}
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+}
+
+function BlockHelperText() {
+ return (
+ <p>
+ Blocking a domain blocks interaction between your instance, and all current and future accounts on
+ instance(s) running on the blocked domain. Stored content will be removed, and no more data is sent to
+ the remote server. This extends to all subdomains as well, so blocking 'example.com' also blocks 'social.example.com'.
+ <br/>
+ <a
+ href="https://docs.gotosocial.org/en/latest/admin/domain_blocks/"
+ target="_blank"
+ className="docslink"
+ rel="noreferrer"
+ >
+ Learn more about domain blocks (opens in a new tab)
+ </a>
+ <br/>
+ </p>
+ );
+}
+
+function AllowHelperText() {
+ return (
+ <p>
+ Allowing a domain explicitly allows instance(s) running on that domain to interact with your instance.
+ If you're running in allowlist mode, this is how you "allow" instances through.
+ If you're running in blocklist mode (the default federation mode), you can use explicit domain allows
+ to override domain blocks. In blocklist mode, explicitly allowed instances will be able to interact with
+ your instance regardless of any domain blocks in place. This extends to all subdomains as well, so allowing
+ 'example.com' also allows 'social.example.com'. This is useful when you're importing a block list but
+ there are some domains on the list you don't want to block: just create an explicit allow for those domains
+ before importing the list.
+ <br/>
+ <a
+ href="https://docs.gotosocial.org/en/latest/admin/federation_modes/"
+ target="_blank"
+ className="docslink"
+ rel="noreferrer"
+ >
+ Learn more about federation modes (opens in a new tab)
+ </a>
+ </p>
+ );
+}
diff --git a/web/source/settings/admin/federation/import-export/process.jsx b/web/source/settings/admin/domain-permissions/process.tsx
index b39410605..bb9411b9d 100644
--- a/web/source/settings/admin/federation/import-export/process.jsx
+++ b/web/source/settings/admin/domain-permissions/process.tsx
@@ -17,57 +17,81 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-const React = require("react");
+import React from "react";
-const query = require("../../../lib/query");
-const { isValidDomainBlock, hasBetterScope } = require("../../../lib/domain-block");
+import { memo, useMemo, useCallback, useEffect } from "react";
-const {
+import { isValidDomainPermission, hasBetterScope } from "../../lib/util/domain-permission";
+
+import {
useTextInput,
useBoolInput,
useRadioInput,
- useCheckListInput
-} = require("../../../lib/form");
-
-const useFormSubmit = require("../../../lib/form/submit");
+ useCheckListInput,
+} from "../../lib/form";
-const {
- TextInput,
+import {
+ Select,
TextArea,
+ RadioGroup,
Checkbox,
- Select,
- RadioGroup
-} = require("../../../components/form/inputs");
+ TextInput,
+} from "../../components/form/inputs";
+
+import useFormSubmit from "../../lib/form/submit";
-const CheckList = require("../../../components/check-list");
-const MutationButton = require("../../../components/form/mutation-button");
-const FormWithData = require("../../../lib/form/form-with-data");
+import CheckList from "../../components/check-list";
+import MutationButton from "../../components/form/mutation-button";
+import FormWithData from "../../lib/form/form-with-data";
-module.exports = React.memo(
- function ProcessImport({ list }) {
+import { useImportDomainPermsMutation } from "../../lib/query/admin/domain-permissions/import";
+import {
+ useDomainAllowsQuery,
+ useDomainBlocksQuery
+} from "../../lib/query/admin/domain-permissions/get";
+
+import type { DomainPerm, MappedDomainPerms } from "../../lib/types/domain-permission";
+import type { ChecklistInputHook, RadioFormInputHook } from "../../lib/form/types";
+
+export interface ProcessImportProps {
+ list: DomainPerm[],
+ permType: RadioFormInputHook,
+}
+
+export const ProcessImport = memo(
+ function ProcessImport({ list, permType }: ProcessImportProps) {
return (
<div className="without-border">
<FormWithData
- dataQuery={query.useInstanceBlocksQuery}
+ dataQuery={permType.value == "allow"
+ ? useDomainAllowsQuery
+ : useDomainBlocksQuery
+ }
DataForm={ImportList}
- list={list}
+ {...{ list, permType }}
/>
</div>
);
}
);
-function ImportList({ list, data: blockedInstances }) {
- const hasComment = React.useMemo(() => {
+export interface ImportListProps {
+ list: Array<DomainPerm>,
+ data: MappedDomainPerms,
+ permType: RadioFormInputHook,
+}
+
+function ImportList({ list, data: domainPerms, permType }: ImportListProps) {
+ const hasComment = useMemo(() => {
let hasPublic = false;
let hasPrivate = false;
list.some((entry) => {
- if (entry.public_comment?.length > 0) {
+ if (entry.public_comment) {
hasPublic = true;
}
- if (entry.private_comment?.length > 0) {
+ if (entry.private_comment) {
hasPrivate = true;
}
@@ -88,7 +112,7 @@ function ImportList({ list, data: blockedInstances }) {
const showComment = useTextInput("showComment", { defaultValue: hasComment.type ?? "public_comment" });
const form = {
- domains: useCheckListInput("domains", { entries: list }),
+ domains: useCheckListInput("domains", { entries: list }), // DomainPerm is actually also a Checkable.
obfuscate: useBoolInput("obfuscate"),
privateComment: useTextInput("private_comment", {
defaultValue: `Imported on ${new Date().toLocaleString()}`
@@ -108,13 +132,17 @@ function ImportList({ list, data: blockedInstances }) {
replace: "Replace"
}
}),
+ permType: permType,
};
- const [importDomains, importResult] = useFormSubmit(form, query.useImportDomainListMutation(), { changedOnly: false });
+ const [importDomains, importResult] = useFormSubmit(form, useImportDomainPermsMutation(), { changedOnly: false });
return (
<>
- <form onSubmit={importDomains} className="suspend-import-list">
+ <form
+ onSubmit={importDomains}
+ className="domain-perm-import-list"
+ >
<span>{list.length} domain{list.length != 1 ? "s" : ""} in this list</span>
{hasComment.both &&
@@ -129,8 +157,9 @@ function ImportList({ list, data: blockedInstances }) {
<div className="checkbox-list-wrapper">
<DomainCheckList
field={form.domains}
- blockedInstances={blockedInstances}
- commentType={showComment.value}
+ domainPerms={domainPerms}
+ commentType={showComment.value as "public_comment" | "private_comment"}
+ permType={form.permType}
/>
</div>
@@ -159,28 +188,41 @@ function ImportList({ list, data: blockedInstances }) {
label="Obfuscate domains in public lists"
/>
- <MutationButton label="Import" result={importResult} />
+ <MutationButton
+ label="Import"
+ disabled={false}
+ result={importResult}
+ />
</form>
</>
);
}
-function DomainCheckList({ field, blockedInstances, commentType }) {
- const getExtraProps = React.useCallback((entry) => {
+interface DomainCheckListProps {
+ field: ChecklistInputHook,
+ domainPerms: MappedDomainPerms,
+ commentType: "public_comment" | "private_comment",
+ permType: RadioFormInputHook,
+}
+
+function DomainCheckList({ field, domainPerms, commentType, permType }: DomainCheckListProps) {
+ const getExtraProps = useCallback((entry: DomainPerm) => {
return {
comment: entry[commentType],
- alreadyExists: blockedInstances[entry.domain] != undefined
+ alreadyExists: entry.domain in domainPerms,
+ permType: permType,
};
- }, [blockedInstances, commentType]);
+ }, [domainPerms, commentType, permType]);
- const entriesWithSuggestions = React.useMemo(() => (
- Object.values(field.value).filter((entry) => entry.suggest)
- ), [field.value]);
+ const entriesWithSuggestions = useMemo(() => {
+ const fieldValue = (field.value ?? {}) as { [k: string]: DomainPerm; };
+ return Object.values(fieldValue).filter((entry) => entry.suggest);
+ }, [field.value]);
return (
<>
<CheckList
- field={field}
+ field={field as ChecklistInputHook}
header={<>
<b>Domain</b>
<b>
@@ -200,8 +242,14 @@ function DomainCheckList({ field, blockedInstances, commentType }) {
);
}
-const UpdateHint = React.memo(
- function UpdateHint({ entries, updateEntry, updateMultiple }) {
+interface UpdateHintProps {
+ entries,
+ updateEntry,
+ updateMultiple,
+}
+
+const UpdateHint = memo(
+ function UpdateHint({ entries, updateEntry, updateMultiple }: UpdateHintProps) {
if (entries.length == 0) {
return null;
}
@@ -229,8 +277,13 @@ const UpdateHint = React.memo(
}
);
-const UpdateableEntry = React.memo(
- function UpdateableEntry({ entry, updateEntry }) {
+interface UpdateableEntryProps {
+ entry,
+ updateEntry,
+}
+
+const UpdateableEntry = memo(
+ function UpdateableEntry({ entry, updateEntry }: UpdateableEntryProps) {
return (
<>
<span className="text-cutoff">{entry.domain}</span>
@@ -248,21 +301,31 @@ function domainValidationError(isValid) {
return isValid ? "" : "Invalid domain";
}
-function DomainEntry({ entry, onChange, extraProps: { alreadyExists, comment } }) {
+interface DomainEntryProps {
+ entry;
+ onChange;
+ extraProps: {
+ alreadyExists: boolean;
+ comment: string;
+ permType: RadioFormInputHook;
+ };
+}
+
+function DomainEntry({ entry, onChange, extraProps: { alreadyExists, comment, permType } }: DomainEntryProps) {
const domainField = useTextInput("domain", {
defaultValue: entry.domain,
showValidation: entry.checked,
initValidation: domainValidationError(entry.valid),
- validator: (value) => domainValidationError(isValidDomainBlock(value))
+ validator: (value) => domainValidationError(isValidDomainPermission(value))
});
- React.useEffect(() => {
+ useEffect(() => {
if (entry.valid != domainField.valid) {
onChange({ valid: domainField.valid });
}
}, [onChange, entry.valid, domainField.valid]);
- React.useEffect(() => {
+ useEffect(() => {
if (entry.domain != domainField.value) {
domainField.setter(entry.domain);
}
@@ -270,8 +333,8 @@ function DomainEntry({ entry, onChange, extraProps: { alreadyExists, comment } }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [entry.domain, domainField.setter]);
- React.useEffect(() => {
- onChange({ suggest: hasBetterScope(domainField.value) });
+ useEffect(() => {
+ onChange({ suggest: hasBetterScope(domainField.value ?? "") });
// only need this update if it's the entry.checked that updated, not onChange
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [domainField.value]);
@@ -296,7 +359,11 @@ function DomainEntry({ entry, onChange, extraProps: { alreadyExists, comment } }
}}
/>
<span id="icon" onClick={clickIcon}>
- <DomainEntryIcon alreadyExists={alreadyExists} suggestion={entry.suggest} onChange={onChange} />
+ <DomainEntryIcon
+ alreadyExists={alreadyExists}
+ suggestion={entry.suggest}
+ permTypeString={permType.value?? ""}
+ />
</span>
</div>
<p>{comment}</p>
@@ -304,7 +371,13 @@ function DomainEntry({ entry, onChange, extraProps: { alreadyExists, comment } }
);
}
-function DomainEntryIcon({ alreadyExists, suggestion }) {
+interface DomainEntryIconProps {
+ alreadyExists: boolean;
+ suggestion: string;
+ permTypeString: string;
+}
+
+function DomainEntryIcon({ alreadyExists, suggestion, permTypeString }: DomainEntryIconProps) {
let icon;
let text;
@@ -312,8 +385,8 @@ function DomainEntryIcon({ alreadyExists, suggestion }) {
icon = "fa-info-circle suggest-changes";
text = `Entry targets a specific subdomain, consider changing it to '${suggestion}'.`;
} else if (alreadyExists) {
- icon = "fa-history already-blocked";
- text = "Domain block already exists.";
+ icon = "fa-history permission-already-exists";
+ text = `Domain ${permTypeString} already exists.`;
}
if (!icon) {
diff --git a/web/source/settings/admin/emoji/category-select.jsx b/web/source/settings/admin/emoji/category-select.jsx
index da2604602..e5cf29939 100644
--- a/web/source/settings/admin/emoji/category-select.jsx
+++ b/web/source/settings/admin/emoji/category-select.jsx
@@ -22,9 +22,8 @@ const splitFilterN = require("split-filter-n");
const syncpipe = require('syncpipe');
const { matchSorter } = require("match-sorter");
-const query = require("../../lib/query");
-
const ComboBox = require("../../components/combo-box");
+const { useListEmojiQuery } = require("../../lib/query/admin/custom-emoji");
function useEmojiByCategory(emoji) {
// split all emoji over an object keyed by the category names (or Unsorted)
@@ -43,7 +42,7 @@ function CategorySelect({ field, children }) {
isLoading,
isSuccess,
error
- } = query.useListEmojiQuery({ filter: "domain:local" });
+ } = useListEmojiQuery({ filter: "domain:local" });
const emojiByCategory = useEmojiByCategory(emoji);
diff --git a/web/source/settings/admin/emoji/local/detail.js b/web/source/settings/admin/emoji/local/detail.js
index daf7a2dac..18a681b6e 100644
--- a/web/source/settings/admin/emoji/local/detail.js
+++ b/web/source/settings/admin/emoji/local/detail.js
@@ -20,21 +20,25 @@
const React = require("react");
const { useRoute, Link, Redirect } = require("wouter");
-const query = require("../../../lib/query");
-
const { useComboBoxInput, useFileInput, useValue } = require("../../../lib/form");
const { CategorySelect } = require("../category-select");
-const useFormSubmit = require("../../../lib/form/submit");
+const useFormSubmit = require("../../../lib/form/submit").default;
const { useBaseUrl } = require("../../../lib/navigation/util");
const FakeToot = require("../../../components/fake-toot");
-const FormWithData = require("../../../lib/form/form-with-data");
+const FormWithData = require("../../../lib/form/form-with-data").default;
const Loading = require("../../../components/loading");
const { FileInput } = require("../../../components/form/inputs");
const MutationButton = require("../../../components/form/mutation-button");
const { Error } = require("../../../components/error");
+const {
+ useGetEmojiQuery,
+ useEditEmojiMutation,
+ useDeleteEmojiMutation,
+} = require("../../../lib/query/admin/custom-emoji");
+
module.exports = function EmojiDetailRoute({ }) {
const baseUrl = useBaseUrl();
let [_match, params] = useRoute(`${baseUrl}/:emojiId`);
@@ -44,7 +48,7 @@ module.exports = function EmojiDetailRoute({ }) {
return (
<div className="emoji-detail">
<Link to={baseUrl}><a>&lt; go back</a></Link>
- <FormWithData dataQuery={query.useGetEmojiQuery} queryArg={params.emojiId} DataForm={EmojiDetailForm} />
+ <FormWithData dataQuery={useGetEmojiQuery} queryArg={params.emojiId} DataForm={EmojiDetailForm} />
</div>
);
}
@@ -61,7 +65,7 @@ function EmojiDetailForm({ data: emoji }) {
})
};
- const [modifyEmoji, result] = useFormSubmit(form, query.useEditEmojiMutation());
+ const [modifyEmoji, result] = useFormSubmit(form, useEditEmojiMutation());
// Automatic submitting of category change
React.useEffect(() => {
@@ -74,7 +78,7 @@ function EmojiDetailForm({ data: emoji }) {
/* eslint-disable-next-line react-hooks/exhaustive-deps */
}, [form.category.hasChanged(), form.category.isNew, form.category.state.open]);
- const [deleteEmoji, deleteResult] = query.useDeleteEmojiMutation();
+ const [deleteEmoji, deleteResult] = useDeleteEmojiMutation();
if (deleteResult.isSuccess) {
return <Redirect to={baseUrl} />;
diff --git a/web/source/settings/admin/emoji/local/new-emoji.js b/web/source/settings/admin/emoji/local/new-emoji.js
index 439d09e62..ecb0465cb 100644
--- a/web/source/settings/admin/emoji/local/new-emoji.js
+++ b/web/source/settings/admin/emoji/local/new-emoji.js
@@ -19,15 +19,13 @@
const React = require("react");
-const query = require("../../../lib/query");
-
const {
useFileInput,
useComboBoxInput
} = require("../../../lib/form");
const useShortcode = require("./use-shortcode");
-const useFormSubmit = require("../../../lib/form/submit");
+const useFormSubmit = require("../../../lib/form/submit").default;
const {
TextInput, FileInput
@@ -36,11 +34,13 @@ const {
const { CategorySelect } = require('../category-select');
const FakeToot = require("../../../components/fake-toot");
const MutationButton = require("../../../components/form/mutation-button");
+const { useAddEmojiMutation } = require("../../../lib/query/admin/custom-emoji");
+const { useInstanceV1Query } = require("../../../lib/query");
module.exports = function NewEmojiForm() {
const shortcode = useShortcode();
- const { data: instance } = query.useInstanceQuery();
+ const { data: instance } = useInstanceV1Query();
const emojiMaxSize = React.useMemo(() => {
return instance?.configuration?.emojis?.emoji_size_limit ?? 50 * 1024;
}, [instance]);
@@ -54,7 +54,7 @@ module.exports = function NewEmojiForm() {
const [submitForm, result] = useFormSubmit({
shortcode, image, category
- }, query.useAddEmojiMutation());
+ }, useAddEmojiMutation());
React.useEffect(() => {
if (shortcode.value.length == 0) {
diff --git a/web/source/settings/admin/emoji/local/overview.js b/web/source/settings/admin/emoji/local/overview.js
index 38dc1feba..757f07c43 100644
--- a/web/source/settings/admin/emoji/local/overview.js
+++ b/web/source/settings/admin/emoji/local/overview.js
@@ -25,13 +25,13 @@ const { matchSorter } = require("match-sorter");
const NewEmojiForm = require("./new-emoji");
const { useTextInput } = require("../../../lib/form");
-const query = require("../../../lib/query");
const { useEmojiByCategory } = require("../category-select");
const { useBaseUrl } = require("../../../lib/navigation/util");
const Loading = require("../../../components/loading");
const { Error } = require("../../../components/error");
const { TextInput } = require("../../../components/form/inputs");
+const { useListEmojiQuery } = require("../../../lib/query/admin/custom-emoji");
module.exports = function EmojiOverview({ }) {
const {
@@ -39,7 +39,7 @@ module.exports = function EmojiOverview({ }) {
isLoading,
isError,
error
- } = query.useListEmojiQuery({ filter: "domain:local" });
+ } = useListEmojiQuery({ filter: "domain:local" });
let content = null;
diff --git a/web/source/settings/admin/emoji/local/use-shortcode.js b/web/source/settings/admin/emoji/local/use-shortcode.js
index 7e1bae0ad..67255860f 100644
--- a/web/source/settings/admin/emoji/local/use-shortcode.js
+++ b/web/source/settings/admin/emoji/local/use-shortcode.js
@@ -19,15 +19,15 @@
const React = require("react");
-const query = require("../../../lib/query");
const { useTextInput } = require("../../../lib/form");
+const { useListEmojiQuery } = require("../../../lib/query/admin/custom-emoji");
const shortcodeRegex = /^\w{2,30}$/;
module.exports = function useShortcode() {
- const {
- data: emoji = []
- } = query.useListEmojiQuery({ filter: "domain:local" });
+ const { data: emoji = [] } = useListEmojiQuery({
+ filter: "domain:local"
+ });
const emojiCodes = React.useMemo(() => {
return new Set(emoji.map((e) => e.shortcode));
diff --git a/web/source/settings/admin/emoji/remote/index.js b/web/source/settings/admin/emoji/remote/index.js
index e877efb89..1a8c719dd 100644
--- a/web/source/settings/admin/emoji/remote/index.js
+++ b/web/source/settings/admin/emoji/remote/index.js
@@ -21,9 +21,9 @@ const React = require("react");
const ParseFromToot = require("./parse-from-toot");
-const query = require("../../../lib/query");
const Loading = require("../../../components/loading");
const { Error } = require("../../../components/error");
+const { useListEmojiQuery } = require("../../../lib/query/admin/custom-emoji");
module.exports = function RemoteEmoji() {
// local emoji are queried for shortcode collision detection
@@ -31,7 +31,7 @@ module.exports = function RemoteEmoji() {
data: emoji = [],
isLoading,
error
- } = query.useListEmojiQuery({ filter: "domain:local" });
+ } = useListEmojiQuery({ filter: "domain:local" });
const emojiCodes = React.useMemo(() => {
return new Set(emoji.map((e) => e.shortcode));
diff --git a/web/source/settings/admin/emoji/remote/parse-from-toot.js b/web/source/settings/admin/emoji/remote/parse-from-toot.js
index e6438a4d2..503a341c8 100644
--- a/web/source/settings/admin/emoji/remote/parse-from-toot.js
+++ b/web/source/settings/admin/emoji/remote/parse-from-toot.js
@@ -19,25 +19,27 @@
const React = require("react");
-const query = require("../../../lib/query");
-
const {
useTextInput,
useComboBoxInput,
useCheckListInput
} = require("../../../lib/form");
-const useFormSubmit = require("../../../lib/form/submit");
+const useFormSubmit = require("../../../lib/form/submit").default;
-const CheckList = require("../../../components/check-list");
+const CheckList = require("../../../components/check-list").default;
const { CategorySelect } = require('../category-select');
const { TextInput } = require("../../../components/form/inputs");
const MutationButton = require("../../../components/form/mutation-button");
const { Error } = require("../../../components/error");
+const {
+ useSearchItemForEmojiMutation,
+ usePatchRemoteEmojisMutation
+} = require("../../../lib/query/admin/custom-emoji");
module.exports = function ParseFromToot({ emojiCodes }) {
- const [searchStatus, result] = query.useSearchStatusForEmojiMutation();
+ const [searchStatus, result] = useSearchItemForEmojiMutation();
const [onURLChange, _resetURL, { url }] = useTextInput("url");
@@ -121,7 +123,7 @@ function CopyEmojiForm({ localEmojiCodes, type, emojiList }) {
const [formSubmit, result] = useFormSubmit(
form,
- query.usePatchRemoteEmojisMutation(),
+ usePatchRemoteEmojisMutation(),
{
changedOnly: false,
onFinish: ({ data }) => {
diff --git a/web/source/settings/admin/federation/detail.js b/web/source/settings/admin/federation/detail.js
deleted file mode 100644
index 7bdee66cf..000000000
--- a/web/source/settings/admin/federation/detail.js
+++ /dev/null
@@ -1,168 +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/>.
-*/
-
-const React = require("react");
-const { useRoute, Redirect, useLocation } = require("wouter");
-
-const query = require("../../lib/query");
-
-const { useTextInput, useBoolInput } = require("../../lib/form");
-
-const useFormSubmit = require("../../lib/form/submit");
-
-const { TextInput, Checkbox, TextArea } = require("../../components/form/inputs");
-
-const Loading = require("../../components/loading");
-const BackButton = require("../../components/back-button");
-const MutationButton = require("../../components/form/mutation-button");
-
-module.exports = function InstanceDetail({ baseUrl }) {
- const { data: blockedInstances = {}, isLoading } = query.useInstanceBlocksQuery();
-
- let [_match, { domain }] = useRoute(`${baseUrl}/:domain`);
- if (domain == "view") {
- // Retrieve domain from form field submission.
- domain = (new URL(document.location)).searchParams.get("domain");
- }
-
- // Normalize / decode domain (it may be URL-encoded).
- domain = decodeURIComponent(domain);
-
- const existingBlock = React.useMemo(() => {
- return blockedInstances[domain];
- }, [blockedInstances, domain]);
-
- if (domain == undefined) {
- return <Redirect to={baseUrl} />;
- }
-
- let infoContent = null;
-
- if (isLoading) {
- infoContent = <Loading />;
- } else if (existingBlock == undefined) {
- infoContent = <span>No stored block yet, you can add one below:</span>;
- } else {
- infoContent = (
- <div className="info">
- <i className="fa fa-fw fa-exclamation-triangle" aria-hidden="true"></i>
- <b>Editing domain blocks isn't implemented yet, <a href="https://github.com/superseriousbusiness/gotosocial/issues/1198" target="_blank" rel="noopener noreferrer">check here for progress</a></b>
- </div>
- );
- }
-
- return (
- <div>
- <h1 className="text-cutoff"><BackButton to={baseUrl} /> Federation settings for: <span title={domain}>{domain}</span></h1>
- {infoContent}
- <DomainBlockForm defaultDomain={domain} block={existingBlock} baseUrl={baseUrl} />
- </div>
- );
-};
-
-function DomainBlockForm({ defaultDomain, block = {}, baseUrl }) {
- const isExistingBlock = block.domain != undefined;
-
- const disabledForm = isExistingBlock
- ? {
- disabled: true,
- title: "Domain suspensions currently cannot be edited."
- }
- : {};
-
- const form = {
- domain: useTextInput("domain", { source: block, defaultValue: defaultDomain }),
- obfuscate: useBoolInput("obfuscate", { source: block }),
- commentPrivate: useTextInput("private_comment", { source: block }),
- commentPublic: useTextInput("public_comment", { source: block })
- };
-
- const [submitForm, addResult] = useFormSubmit(form, query.useAddInstanceBlockMutation(), { changedOnly: false });
-
- const [removeBlock, removeResult] = query.useRemoveInstanceBlockMutation({ fixedCacheKey: block.id });
-
- const [location, setLocation] = useLocation();
-
- function verifyUrlThenSubmit(e) {
- // Adding a new block happens on /settings/admin/federation/domain.com
- // but if domain input changes, that doesn't match anymore and causes issues later on
- // so, before submitting the form, silently change url, then submit
- let correctUrl = `${baseUrl}/${form.domain.value}`;
- if (location != correctUrl) {
- setLocation(correctUrl);
- }
- return submitForm(e);
- }
-
- return (
- <form onSubmit={verifyUrlThenSubmit}>
- <TextInput
- field={form.domain}
- label="Domain"
- placeholder="example.com"
- {...disabledForm}
- />
-
- <Checkbox
- field={form.obfuscate}
- label="Obfuscate domain in public lists"
- {...disabledForm}
- />
-
- <TextArea
- field={form.commentPrivate}
- label="Private comment"
- rows={3}
- {...disabledForm}
- />
-
- <TextArea
- field={form.commentPublic}
- label="Public comment"
- rows={3}
- {...disabledForm}
- />
-
- <div className="action-buttons row">
- <MutationButton
- label="Suspend"
- result={addResult}
- showError={false}
- {...disabledForm}
- />
-
- {
- isExistingBlock &&
- <MutationButton
- type="button"
- onClick={() => removeBlock(block.id)}
- label="Remove"
- result={removeResult}
- className="button danger"
- showError={false}
- />
- }
- </div>
-
- {addResult.error && <Error error={addResult.error} />}
- {removeResult.error && <Error error={removeResult.error} />}
-
- </form>
- );
-} \ No newline at end of file
diff --git a/web/source/settings/admin/federation/import-export/index.jsx b/web/source/settings/admin/federation/import-export/index.jsx
deleted file mode 100644
index bff14b939..000000000
--- a/web/source/settings/admin/federation/import-export/index.jsx
+++ /dev/null
@@ -1,75 +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/>.
-*/
-
-const React = require("react");
-const { Switch, Route, Redirect, useLocation } = require("wouter");
-
-const query = require("../../../lib/query");
-
-const {
- useTextInput,
-} = require("../../../lib/form");
-
-const useFormSubmit = require("../../../lib/form/submit");
-
-const ProcessImport = require("./process");
-const ImportExportForm = require("./form");
-
-module.exports = function ImportExport({ baseUrl }) {
- const form = {
- domains: useTextInput("domains"),
- exportType: useTextInput("exportType", { defaultValue: "plain", dontReset: true })
- };
-
- const [submitParse, parseResult] = useFormSubmit(form, query.useProcessDomainListMutation(), { changedOnly: false });
-
- const [_location, setLocation] = useLocation();
-
- return (
- <Switch>
- <Route path={`${baseUrl}/process`}>
- {parseResult.isSuccess ? (
- <>
- <h1>
- <span className="button" onClick={() => {
- parseResult.reset();
- setLocation(baseUrl);
- }}>
- &lt; back
- </span> Confirm import:
- </h1>
- <ProcessImport
- list={parseResult.data}
- />
- </>
- ) : <Redirect to={baseUrl} />}
- </Route>
-
- <Route>
- {!parseResult.isSuccess ? (
- <ImportExportForm
- form={form}
- submitParse={submitParse}
- parseResult={parseResult}
- />
- ) : <Redirect to={`${baseUrl}/process`} />}
- </Route>
- </Switch>
- );
-}; \ No newline at end of file
diff --git a/web/source/settings/admin/federation/overview.js b/web/source/settings/admin/federation/overview.js
deleted file mode 100644
index c09289284..000000000
--- a/web/source/settings/admin/federation/overview.js
+++ /dev/null
@@ -1,101 +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/>.
-*/
-
-const React = require("react");
-const { Link, useLocation } = require("wouter");
-const { matchSorter } = require("match-sorter");
-
-const { useTextInput } = require("../../lib/form");
-
-const { TextInput } = require("../../components/form/inputs");
-
-const query = require("../../lib/query");
-
-const Loading = require("../../components/loading");
-
-module.exports = function InstanceOverview({ baseUrl }) {
- const { data: blockedInstances = [], isLoading } = query.useInstanceBlocksQuery();
-
- const [_location, setLocation] = useLocation();
-
- const filterField = useTextInput("filter");
- const filter = filterField.value;
-
- const blockedInstancesList = React.useMemo(() => {
- return Object.values(blockedInstances);
- }, [blockedInstances]);
-
- const filteredInstances = React.useMemo(() => {
- return matchSorter(blockedInstancesList, filter, { keys: ["domain"] });
- }, [blockedInstancesList, filter]);
-
- let filtered = blockedInstancesList.length - filteredInstances.length;
-
- function filterFormSubmit(e) {
- e.preventDefault();
- setLocation(`${baseUrl}/${filter}`);
- }
-
- if (isLoading) {
- return <Loading />;
- }
-
- return (
- <>
- <h1>Federation</h1>
-
- <div className="instance-list">
- <h2>Suspended instances</h2>
- <p>
- Suspending a domain blocks all current and future accounts on that instance. Stored content will be removed,
- and no more data is sent to the remote server.<br />
- This extends to all subdomains as well, so blocking 'example.com' also includes 'social.example.com'.
- </p>
- <form className="filter" role="search" onSubmit={filterFormSubmit}>
- <TextInput field={filterField} placeholder="example.com" label="Search or add domain suspension" />
- <Link to={`${baseUrl}/${filter}`}><a className="button">Suspend</a></Link>
- </form>
- <div>
- <span>
- {blockedInstancesList.length} blocked instance{blockedInstancesList.length != 1 ? "s" : ""} {filtered > 0 && `(${filtered} filtered by search)`}
- </span>
- <div className="list">
- <div className="entries scrolling">
- {filteredInstances.map((entry) => {
- return (
- <Link key={entry.domain} to={`${baseUrl}/${entry.domain}`}>
- <a className="entry nounderline">
- <span id="domain">
- {entry.domain}
- </span>
- <span id="date">
- {new Date(entry.created_at).toLocaleString()}
- </span>
- </a>
- </Link>
- );
- })}
- </div>
- </div>
- </div>
- </div>
- <Link to={`${baseUrl}/import-export`}><a>Or use the bulk import/export interface</a></Link>
- </>
- );
-}; \ No newline at end of file
diff --git a/web/source/settings/admin/reports/detail.jsx b/web/source/settings/admin/reports/detail.jsx
index 6b85872c4..d686b92bd 100644
--- a/web/source/settings/admin/reports/detail.jsx
+++ b/web/source/settings/admin/reports/detail.jsx
@@ -20,19 +20,21 @@
const React = require("react");
const { useRoute, Redirect } = require("wouter");
-const query = require("../../lib/query");
-
-const FormWithData = require("../../lib/form/form-with-data");
+const FormWithData = require("../../lib/form/form-with-data").default;
const BackButton = require("../../components/back-button");
const { useValue, useTextInput } = require("../../lib/form");
-const useFormSubmit = require("../../lib/form/submit");
+const useFormSubmit = require("../../lib/form/submit").default;
const { TextArea } = require("../../components/form/inputs");
const MutationButton = require("../../components/form/mutation-button");
const Username = require("./username");
const { useBaseUrl } = require("../../lib/navigation/util");
+const {
+ useGetReportQuery,
+ useResolveReportMutation,
+} = require("../../lib/query/admin/reports");
module.exports = function ReportDetail({ }) {
const baseUrl = useBaseUrl();
@@ -46,7 +48,7 @@ module.exports = function ReportDetail({ }) {
<BackButton to={baseUrl} /> Report Details
</h1>
<FormWithData
- dataQuery={query.useGetReportQuery}
+ dataQuery={useGetReportQuery}
queryArg={params.reportId}
DataForm={ReportDetailForm}
/>
@@ -115,7 +117,7 @@ function ReportActionForm({ report }) {
comment: useTextInput("action_taken_comment")
};
- const [submit, result] = useFormSubmit(form, query.useResolveReportMutation(), { changedOnly: false });
+ const [submit, result] = useFormSubmit(form, useResolveReportMutation(), { changedOnly: false });
return (
<form onSubmit={submit} className="info-block">
diff --git a/web/source/settings/admin/reports/index.jsx b/web/source/settings/admin/reports/index.jsx
index 2f7a09517..5ffbfd3a0 100644
--- a/web/source/settings/admin/reports/index.jsx
+++ b/web/source/settings/admin/reports/index.jsx
@@ -20,13 +20,12 @@
const React = require("react");
const { Link, Switch, Route } = require("wouter");
-const query = require("../../lib/query");
-
-const FormWithData = require("../../lib/form/form-with-data");
+const FormWithData = require("../../lib/form/form-with-data").default;
const ReportDetail = require("./detail");
const Username = require("./username");
const { useBaseUrl } = require("../../lib/navigation/util");
+const { useListReportsQuery } = require("../../lib/query/admin/reports");
module.exports = function Reports({ baseUrl }) {
return (
@@ -51,7 +50,7 @@ function ReportOverview({ }) {
</p>
</div>
<FormWithData
- dataQuery={query.useListReportsQuery}
+ dataQuery={useListReportsQuery}
DataForm={ReportsList}
/>
</>
diff --git a/web/source/settings/admin/settings/index.jsx b/web/source/settings/admin/settings/index.jsx
index 5ea227fb1..c0da83a2a 100644
--- a/web/source/settings/admin/settings/index.jsx
+++ b/web/source/settings/admin/settings/index.jsx
@@ -19,14 +19,12 @@
const React = require("react");
-const query = require("../../lib/query");
-
const {
useTextInput,
useFileInput
} = require("../../lib/form");
-const useFormSubmit = require("../../lib/form/submit");
+const useFormSubmit = require("../../lib/form/submit").default;
const {
TextInput,
@@ -34,13 +32,16 @@ const {
FileInput
} = require("../../components/form/inputs");
-const FormWithData = require("../../lib/form/form-with-data");
+const FormWithData = require("../../lib/form/form-with-data").default;
const MutationButton = require("../../components/form/mutation-button");
+const { useInstanceV1Query } = require("../../lib/query");
+const { useUpdateInstanceMutation } = require("../../lib/query/admin");
+
module.exports = function AdminSettings() {
return (
<FormWithData
- dataQuery={query.useInstanceQuery}
+ dataQuery={useInstanceV1Query}
DataForm={AdminSettingsForm}
/>
);
@@ -61,7 +62,7 @@ function AdminSettingsForm({ data: instance }) {
terms: useTextInput("terms", { source: instance })
};
- const [submitForm, result] = useFormSubmit(form, query.useUpdateInstanceMutation());
+ const [submitForm, result] = useFormSubmit(form, useUpdateInstanceMutation());
return (
<form onSubmit={submitForm}>
diff --git a/web/source/settings/admin/settings/rules.jsx b/web/source/settings/admin/settings/rules.jsx
index 7c78e6601..4280ccea7 100644
--- a/web/source/settings/admin/settings/rules.jsx
+++ b/web/source/settings/admin/settings/rules.jsx
@@ -21,11 +21,11 @@ const React = require("react");
const { Switch, Route, Link, Redirect, useRoute } = require("wouter");
const query = require("../../lib/query");
-const FormWithData = require("../../lib/form/form-with-data");
+const FormWithData = require("../../lib/form/form-with-data").default;
const { useBaseUrl } = require("../../lib/navigation/util");
const { useValue, useTextInput } = require("../../lib/form");
-const useFormSubmit = require("../../lib/form/submit");
+const useFormSubmit = require("../../lib/form/submit").default;
const { TextArea } = require("../../components/form/inputs");
const MutationButton = require("../../components/form/mutation-button");