summaryrefslogtreecommitdiff
path: root/web/source/settings/admin/domain-permissions/detail.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'web/source/settings/admin/domain-permissions/detail.tsx')
-rw-r--r--web/source/settings/admin/domain-permissions/detail.tsx254
1 files changed, 254 insertions, 0 deletions
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>
+ );
+}