diff options
Diffstat (limited to 'web/source/settings/admin/domain-permissions/detail.tsx')
-rw-r--r-- | web/source/settings/admin/domain-permissions/detail.tsx | 254 |
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> + ); +} |