From 637f188ebec71fe4b0b80bbab4592d4c269d7d93 Mon Sep 17 00:00:00 2001
From: tobi <31960611+tsmethurst@users.noreply.github.com>
Date: Tue, 17 Oct 2023 12:46:06 +0200
Subject: [PATCH] [feature] Allow import/export/creation of domain allows via
admin panel (#2264)
* it's happening!
* aaa
* fix silly whoopsie
* it's working pa! it's working ma!
* model report parameters
* shuffle some more stuff around
* getting there
* oo hoo
* finish tidying up for now
* aaa
* fix use form submit errors
* peepee poo poo
* aaaaa
* ffff
* they see me typin', they hatin'
* boop
* aaa
* oooo
* typing typing tappa tappa
* almost done typing
* weee
* alright
* push it push it real good doo doo doo doo doo doo
* thingy no worky
* almost done
* mutation modifers not quite right
* hmm
* it works
* view blocks + allows nicely
* it works!
* typia install
* the old linterino
* linter plz
---
.drone.yml | 3 +-
.goreleaser.yml | 1 +
CONTRIBUTING.md | 6 +-
Dockerfile | 1 +
internal/api/client/admin/domainpermission.go | 2 +-
web/source/package.json | 9 +-
web/source/settings/admin/accounts/detail.jsx | 6 +-
.../admin/domain-permissions/detail.tsx | 254 ++++++++++
.../export-format-table.jsx | 0
.../form.jsx => domain-permissions/form.tsx} | 68 ++-
.../domain-permissions/import-export.tsx | 90 ++++
.../admin/domain-permissions/index.tsx | 49 ++
.../admin/domain-permissions/overview.tsx | 198 ++++++++
.../process.tsx} | 179 ++++---
.../settings/admin/emoji/category-select.jsx | 5 +-
.../settings/admin/emoji/local/detail.js | 18 +-
.../settings/admin/emoji/local/new-emoji.js | 10 +-
.../settings/admin/emoji/local/overview.js | 4 +-
.../admin/emoji/local/use-shortcode.js | 8 +-
.../settings/admin/emoji/remote/index.js | 4 +-
.../admin/emoji/remote/parse-from-toot.js | 14 +-
.../settings/admin/federation/detail.js | 168 -------
.../admin/federation/import-export/index.jsx | 75 ---
.../settings/admin/federation/overview.js | 101 ----
web/source/settings/admin/reports/detail.jsx | 14 +-
web/source/settings/admin/reports/index.jsx | 7 +-
web/source/settings/admin/settings/index.jsx | 13 +-
web/source/settings/admin/settings/rules.jsx | 4 +-
.../components/authorization/index.tsx | 7 +-
.../components/authorization/login.tsx | 15 +-
.../{check-list.jsx => check-list.tsx} | 46 +-
.../form/{inputs.jsx => inputs.tsx} | 106 +++--
.../settings/components/user-logout-card.jsx | 14 +-
web/source/settings/index.js | 12 +-
.../settings/lib/form/{bool.jsx => bool.tsx} | 17 +-
.../form/{check-list.jsx => check-list.tsx} | 158 ++++---
.../lib/form/{combo-box.jsx => combo-box.tsx} | 23 +-
.../form/{field-array.jsx => field-array.tsx} | 50 +-
.../settings/lib/form/{file.jsx => file.tsx} | 92 ++--
...{form-with-data.jsx => form-with-data.tsx} | 33 +-
...orm-mutations.js => get-form-mutations.ts} | 46 +-
web/source/settings/lib/form/index.js | 83 ----
web/source/settings/lib/form/index.ts | 114 +++++
.../lib/form/{radio.jsx => radio.tsx} | 18 +-
web/source/settings/lib/form/submit.js | 67 ---
web/source/settings/lib/form/submit.ts | 140 ++++++
.../settings/lib/form/{text.jsx => text.tsx} | 51 +-
web/source/settings/lib/form/types.ts | 264 +++++++++++
.../settings/lib/query/admin/custom-emoji.js | 194 --------
.../lib/query/admin/custom-emoji/index.ts | 307 ++++++++++++
.../query/admin/domain-permissions/export.ts | 155 ++++++
.../lib/query/admin/domain-permissions/get.ts | 56 +++
.../query/admin/domain-permissions/import.ts | 140 ++++++
.../query/admin/domain-permissions/process.ts | 163 +++++++
.../query/admin/domain-permissions/update.ts | 109 +++++
.../settings/lib/query/admin/import-export.js | 264 -----------
web/source/settings/lib/query/admin/index.js | 165 -------
web/source/settings/lib/query/admin/index.ts | 148 ++++++
.../settings/lib/query/admin/reports.js | 51 --
.../settings/lib/query/admin/reports/index.ts | 83 ++++
web/source/settings/lib/query/gts-api.ts | 16 +-
web/source/settings/lib/query/lib.js | 81 ----
web/source/settings/lib/query/oauth/index.ts | 10 +-
.../settings/lib/query/query-modifiers.ts | 150 ++++++
web/source/settings/lib/query/transforms.ts | 78 +++
web/source/settings/lib/query/user/index.ts | 8 +-
.../index.js => lib/types/custom-emoji.ts} | 46 +-
.../settings/lib/types/domain-permission.ts | 97 ++++
web/source/settings/lib/types/instance.ts | 91 ++++
web/source/settings/lib/types/query.ts | 95 ++++
web/source/settings/lib/types/report.ts | 144 ++++++
.../domain-permission.ts} | 37 +-
web/source/settings/style.css | 14 +-
web/source/settings/user/profile.js | 16 +-
web/source/settings/user/settings.js | 4 +-
web/source/tsconfig.json | 8 +-
web/source/yarn.lock | 447 +++++++++++++++++-
77 files changed, 4154 insertions(+), 1690 deletions(-)
create mode 100644 web/source/settings/admin/domain-permissions/detail.tsx
rename web/source/settings/admin/{federation/import-export => domain-permissions}/export-format-table.jsx (100%)
rename web/source/settings/admin/{federation/import-export/form.jsx => domain-permissions/form.tsx} (62%)
create mode 100644 web/source/settings/admin/domain-permissions/import-export.tsx
create mode 100644 web/source/settings/admin/domain-permissions/index.tsx
create mode 100644 web/source/settings/admin/domain-permissions/overview.tsx
rename web/source/settings/admin/{federation/import-export/process.jsx => domain-permissions/process.tsx} (61%)
delete mode 100644 web/source/settings/admin/federation/detail.js
delete mode 100644 web/source/settings/admin/federation/import-export/index.jsx
delete mode 100644 web/source/settings/admin/federation/overview.js
rename web/source/settings/components/{check-list.jsx => check-list.tsx} (68%)
rename web/source/settings/components/form/{inputs.jsx => inputs.tsx} (52%)
rename web/source/settings/lib/form/{bool.jsx => bool.tsx} (78%)
rename web/source/settings/lib/form/{check-list.jsx => check-list.tsx} (53%)
rename web/source/settings/lib/form/{combo-box.jsx => combo-box.tsx} (72%)
rename web/source/settings/lib/form/{field-array.jsx => field-array.tsx} (57%)
rename web/source/settings/lib/form/{file.jsx => file.tsx} (51%)
rename web/source/settings/lib/form/{form-with-data.jsx => form-with-data.tsx} (55%)
rename web/source/settings/lib/form/{get-form-mutations.js => get-form-mutations.ts} (52%)
delete mode 100644 web/source/settings/lib/form/index.js
create mode 100644 web/source/settings/lib/form/index.ts
rename web/source/settings/lib/form/{radio.jsx => radio.tsx} (77%)
delete mode 100644 web/source/settings/lib/form/submit.js
create mode 100644 web/source/settings/lib/form/submit.ts
rename web/source/settings/lib/form/{text.jsx => text.tsx} (64%)
create mode 100644 web/source/settings/lib/form/types.ts
delete mode 100644 web/source/settings/lib/query/admin/custom-emoji.js
create mode 100644 web/source/settings/lib/query/admin/custom-emoji/index.ts
create mode 100644 web/source/settings/lib/query/admin/domain-permissions/export.ts
create mode 100644 web/source/settings/lib/query/admin/domain-permissions/get.ts
create mode 100644 web/source/settings/lib/query/admin/domain-permissions/import.ts
create mode 100644 web/source/settings/lib/query/admin/domain-permissions/process.ts
create mode 100644 web/source/settings/lib/query/admin/domain-permissions/update.ts
delete mode 100644 web/source/settings/lib/query/admin/import-export.js
delete mode 100644 web/source/settings/lib/query/admin/index.js
create mode 100644 web/source/settings/lib/query/admin/index.ts
delete mode 100644 web/source/settings/lib/query/admin/reports.js
create mode 100644 web/source/settings/lib/query/admin/reports/index.ts
delete mode 100644 web/source/settings/lib/query/lib.js
create mode 100644 web/source/settings/lib/query/query-modifiers.ts
create mode 100644 web/source/settings/lib/query/transforms.ts
rename web/source/settings/{admin/federation/index.js => lib/types/custom-emoji.ts} (58%)
create mode 100644 web/source/settings/lib/types/domain-permission.ts
create mode 100644 web/source/settings/lib/types/instance.ts
create mode 100644 web/source/settings/lib/types/query.ts
create mode 100644 web/source/settings/lib/types/report.ts
rename web/source/settings/lib/{domain-block.js => util/domain-permission.ts} (54%)
diff --git a/.drone.yml b/.drone.yml
index c398db390..8e2aebb86 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -54,6 +54,7 @@ steps:
path: /tmp/cache
commands:
- yarn --cwd ./web/source install --frozen-lockfile --cache-folder /tmp/cache
+ - yarn --cwd ./web/source ts-patch install # https://typia.io/docs/setup/#manual-setup
- name: web-lint
image: node:18-alpine
@@ -191,6 +192,6 @@ steps:
---
kind: signature
-hmac: c3efbd528a76016562f88ae435141cfb5fd6d4d07b6ad2a24ecc23cb529cc1c6
+hmac: d7b93470276a0df7e4d862941489f00da107df3d085200009b776d33599e6043
...
diff --git a/.goreleaser.yml b/.goreleaser.yml
index 1b49136c7..a49bb32e8 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -8,6 +8,7 @@ before:
- sed -i "s/REPLACE_ME/{{ incpatch .Version }}/" web/assets/swagger.yaml
# Install web deps + bundle web assets
- yarn --cwd ./web/source install
+ - yarn --cwd ./web/source ts-patch install # https://typia.io/docs/setup/#manual-setup
- yarn --cwd ./web/source build
builds:
# https://goreleaser.com/customization/build/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c8218564d..628832e1c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -229,13 +229,15 @@ Using [NVM](https://github.com/nvm-sh/nvm) is one convenient way to install them
To install frontend dependencies:
```bash
-yarn --cwd web/source
+yarn --cwd ./web/source install && yarn --cwd ./web/source ts-patch install
```
+The `ts-patch` step is necessary because of Typia, which we use for some type validation: see [Typia install docs](https://typia.io/docs/setup/#manual-setup).
+
To recompile frontend bundles into `web/assets/dist`:
```bash
-yarn --cwd web/source build
+yarn --cwd ./web/source build
```
#### Live Reloading
diff --git a/Dockerfile b/Dockerfile
index d772f7497..7c1cce4d2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -16,6 +16,7 @@ FROM --platform=${BUILDPLATFORM} node:18-alpine AS bundler
COPY web web
RUN yarn --cwd ./web/source install && \
+ yarn --cwd ./web/source ts-patch install && \
yarn --cwd ./web/source build && \
rm -rf ./web/source
diff --git a/internal/api/client/admin/domainpermission.go b/internal/api/client/admin/domainpermission.go
index bd6b83425..203eddc8b 100644
--- a/internal/api/client/admin/domainpermission.go
+++ b/internal/api/client/admin/domainpermission.go
@@ -95,7 +95,7 @@ func (m *Module) createDomainPermissions(
if importing && form.Domains.Size == 0 {
err = errors.New("import was specified but list of domains is empty")
- } else if form.Domain == "" {
+ } else if !importing && form.Domain == "" {
err = errors.New("empty domain provided")
}
diff --git a/web/source/package.json b/web/source/package.json
index d3c1cbe2b..20f525228 100644
--- a/web/source/package.json
+++ b/web/source/package.json
@@ -45,6 +45,10 @@
"@browserify/envify": "^6.0.0",
"@browserify/uglifyify": "^6.0.0",
"@joepie91/eslint-config": "^1.1.1",
+ "@types/bluebird": "^3.5.39",
+ "@types/is-valid-domain": "^0.0.2",
+ "@types/papaparse": "^5.3.9",
+ "@types/psl": "^1.1.1",
"@types/react-dom": "^18.2.8",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
@@ -63,7 +67,10 @@
"postcss-nested": "^6.0.0",
"source-map-loader": "^4.0.1",
"ts-loader": "^9.4.4",
+ "ts-node": "^10.9.1",
+ "ts-patch": "^3.0.2",
"tsify": "^5.0.4",
- "typescript": "^5.2.2"
+ "typescript": "^5.2.2",
+ "typia": "^5.1.6"
}
}
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 .
+*/
+
+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 = ;
+ } else if (existingPerm == undefined) {
+ infoContent = No stored {permType} yet, you can add one below: ;
+ } else {
+ infoContent = (
+
- 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.
-
+ This page can be used to import and export lists of domain permissions.
+ Exports can be done in various formats, with varying functionality and support in other software.
+ Imports will automatically detect what format is being processed.
+
+
submitParse()}
result={parseResult}
showError={false}
+ disabled={false}
/>
@@ -92,6 +118,7 @@ module.exports = function ImportExportForm({ form, submitParse, parseResult }) {
type="button"
onClick={() => submitExport("export")}
result={exportResult} showError={false}
+ disabled={false}
/>
submitExport("export-file")}
result={exportResult}
showError={false}
+ disabled={false}
/>
@@ -121,4 +149,4 @@ module.exports = function ImportExportForm({ form, submitParse, parseResult }) {
>
);
-};
\ 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 .
+*/
+
+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 (
+
+
+ {
+ parseResult.isSuccess
+ ? (
+ <>
+
+ {
+ parseResult.reset();
+ setLocation(baseUrl);
+ }}
+ >
+ < back
+
+ Confirm import of domain {form.permType.value}s:
+
+
+ >
+ )
+ :
+ }
+
+
+ {
+ parseResult.isSuccess
+ ?
+ :
+ }
+
+
+ );
+}
diff --git a/web/source/settings/admin/domain-permissions/index.tsx b/web/source/settings/admin/domain-permissions/index.tsx
new file mode 100644
index 000000000..7d790cfc8
--- /dev/null
+++ b/web/source/settings/admin/domain-permissions/index.tsx
@@ -0,0 +1,49 @@
+/*
+ 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 .
+*/
+
+import React from "react";
+import { Switch, Route } from "wouter";
+
+import DomainPermissionsOverview from "./overview";
+import { PermType } from "../../lib/types/domain-permission";
+import DomainPermDetail from "./detail";
+
+export default function DomainPermissions({ baseUrl }: { baseUrl: string }) {
+ return (
+
+
+ {params => (
+
+ )}
+
+
+ {params => (
+
+ )}
+
+
+ );
+}
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 .
+*/
+
+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 ;
+ }
+
+ return (
+
+ );
+}
+
+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 = (
+
+ {perms.length} {permType}ed domain{perms.length != 1 ? "s" : ""} {filtered > 0 && `(${filtered} filtered by search)`}
+
+ );
+
+ const entries = filteredPerms.map((entry) => {
+ return (
+
+
+ {entry.domain}
+ {new Date(entry.created_at ?? "").toLocaleString()}
+
+
+ );
+ });
+
+ return (
+
+ );
+}
+
+function BlockHelperText() {
+ return (
+
+ 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'.
+
+
+ Learn more about domain blocks (opens in a new tab)
+
+
+
+ );
+}
+
+function AllowHelperText() {
+ return (
+
+ 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.
+
+
+ Learn more about federation modes (opens in a new tab)
+
+
+ );
+}
diff --git a/web/source/settings/admin/federation/import-export/process.jsx b/web/source/settings/admin/domain-permissions/process.tsx
similarity index 61%
rename from web/source/settings/admin/federation/import-export/process.jsx
rename to 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 .
*/
-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");
+ useCheckListInput,
+} from "../../lib/form";
-const useFormSubmit = require("../../../lib/form/submit");
-
-const {
- TextInput,
- TextArea,
- Checkbox,
+import {
Select,
- RadioGroup
-} = require("../../../components/form/inputs");
+ TextArea,
+ RadioGroup,
+ Checkbox,
+ TextInput,
+} from "../../components/form/inputs";
-const CheckList = require("../../../components/check-list");
-const MutationButton = require("../../../components/form/mutation-button");
-const FormWithData = require("../../../lib/form/form-with-data");
+import useFormSubmit from "../../lib/form/submit";
-module.exports = React.memo(
- function ProcessImport({ list }) {
+import CheckList from "../../components/check-list";
+import MutationButton from "../../components/form/mutation-button";
+import FormWithData from "../../lib/form/form-with-data";
+
+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 (
);
}
);
-function ImportList({ list, data: blockedInstances }) {
- const hasComment = React.useMemo(() => {
+export interface ImportListProps {
+ list: Array,
+ 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 (
<>
-
>
);
}
-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 (
<>
Domain
@@ -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 (
<>
{entry.domain}
@@ -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 } }
}}
/>
-
+
{comment}
@@ -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 (
);
}
@@ -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
;
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
.
-*/
-
-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
;
- }
-
- let infoContent = null;
-
- if (isLoading) {
- infoContent =
;
- } else if (existingBlock == undefined) {
- infoContent =
No stored block yet, you can add one below: ;
- } else {
- infoContent = (
-
- );
- }
-
- return (
-
-
Federation settings for: {domain}
- {infoContent}
-
-
- );
-};
-
-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 (
-
- );
-}
\ 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
.
-*/
-
-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 (
-
-
- {parseResult.isSuccess ? (
- <>
-
- {
- parseResult.reset();
- setLocation(baseUrl);
- }}>
- < back
- Confirm import:
-
-
- >
- ) : }
-
-
-
- {!parseResult.isSuccess ? (
-
- ) : }
-
-
- );
-};
\ 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
.
-*/
-
-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
;
- }
-
- return (
- <>
-
Federation
-
-
-
Suspended instances
-
- 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.
- This extends to all subdomains as well, so blocking 'example.com' also includes 'social.example.com'.
-
-
-
- Suspend
-
-
-
- {blockedInstancesList.length} blocked instance{blockedInstancesList.length != 1 ? "s" : ""} {filtered > 0 && `(${filtered} filtered by search)`}
-
-
-
-
-
Or use the bulk import/export interface
- >
- );
-};
\ 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({ }) {
Report Details
@@ -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 (
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({ }) {