diff options
Diffstat (limited to 'web/source/settings/admin/domain-permissions')
7 files changed, 0 insertions, 1211 deletions
diff --git a/web/source/settings/admin/domain-permissions/detail.tsx b/web/source/settings/admin/domain-permissions/detail.tsx deleted file mode 100644 index f74802666..000000000 --- a/web/source/settings/admin/domain-permissions/detail.tsx +++ /dev/null @@ -1,254 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -import 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/domain-permissions/export-format-table.jsx b/web/source/settings/admin/domain-permissions/export-format-table.jsx deleted file mode 100644 index 7fcffa348..000000000 --- a/web/source/settings/admin/domain-permissions/export-format-table.jsx +++ /dev/null @@ -1,65 +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"); - -module.exports = function ExportFormatTable() { - return ( - <div className="export-format-table-wrapper without-border"> - <table className="export-format-table"> - <thead> - <tr> - <th rowSpan={2} /> - <th colSpan={2}>Includes</th> - <th colSpan={2}>Importable by</th> - </tr> - <tr> - <th>Domain</th> - <th>Public comment</th> - <th>GoToSocial</th> - <th>Mastodon</th> - </tr> - </thead> - <tbody> - <Format name="Text" info={[true, false, true, false]} /> - <Format name="JSON" info={[true, true, true, false]} /> - <Format name="CSV" info={[true, true, true, true]} /> - </tbody> - </table> - </div> - ); -}; - -function Format({ name, info }) { - return ( - <tr> - <td><b>{name}</b></td> - {info.map((b, key) => <td key={key} className="bool">{bool(b)}</td>)} - </tr> - ); -} - -function bool(val) { - return ( - <> - <i className={`fa fa-${val ? "check" : "times"}`} aria-hidden="true"></i> - <span className="sr-only">{val ? "Yes" : "No"}</span> - </> - ); -}
\ No newline at end of file diff --git a/web/source/settings/admin/domain-permissions/form.tsx b/web/source/settings/admin/domain-permissions/form.tsx deleted file mode 100644 index 57502d6d9..000000000 --- a/web/source/settings/admin/domain-permissions/form.tsx +++ /dev/null @@ -1,153 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -import React from "react"; - -import { useEffect } from "react"; - -import { useExportDomainListMutation } from "../../lib/query/admin/domain-permissions/export"; -import useFormSubmit from "../../lib/form/submit"; - -import { - RadioGroup, - TextArea, - Select, -} from "../../components/form/inputs"; - -import MutationButton from "../../components/form/mutation-button"; - -import { Error } from "../../components/error"; -import ExportFormatTable from "./export-format-table"; - -import type { - FormSubmitFunction, - FormSubmitResult, - RadioFormInputHook, - TextFormInputHook, -} from "../../lib/form/types"; - -export interface ImportExportFormProps { - form: { - domains: TextFormInputHook; - exportType: TextFormInputHook; - permType: RadioFormInputHook; - }; - submitParse: FormSubmitFunction; - parseResult: FormSubmitResult; -} - -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) { - const res = read.target?.result; - if (typeof res === "string") { - form.domains.value = res; - submitParse(); - } - }; - reader.readAsText(e.target.files[0]); - } - - useEffect(() => { - if (exportResult.isSuccess) { - form.domains.setter(exportResult.data); - } - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - }, [exportResult]); - - return ( - <> - <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 - field={form.domains} - label="Domains" - placeholder={`google.com\nfacebook.com`} - rows={8} - /> - - <RadioGroup - field={form.permType} - /> - - <div className="button-grid"> - <MutationButton - label="Import" - type="button" - onClick={() => submitParse()} - result={parseResult} - showError={false} - disabled={form.permType.value === undefined || form.permType.value.length === 0} - /> - <label className={`button with-icon${form.permType.value === undefined || form.permType.value.length === 0 ? " disabled" : ""}`}> - <i className="fa fa-fw " aria-hidden="true" /> - Import file - <input - type="file" - className="hidden" - onChange={fileChanged} - accept="application/json,text/plain,text/csv" - disabled={form.permType.value === undefined || form.permType.value.length === 0} - /> - </label> - <b /> {/* grid filler */} - <MutationButton - label="Export" - type="button" - onClick={() => submitExport("export")} - result={exportResult} showError={false} - disabled={form.permType.value === undefined || form.permType.value.length === 0} - /> - <MutationButton - label="Export to file" - wrapperClassName="export-file-button" - type="button" - onClick={() => submitExport("export-file")} - result={exportResult} - showError={false} - disabled={form.permType.value === undefined || form.permType.value.length === 0} - /> - <div className="export-file"> - <span> - as - </span> - <Select - field={form.exportType} - options={<> - <option value="plain">Text</option> - <option value="json">JSON</option> - <option value="csv">CSV</option> - </>} - /> - </div> - </div> - - {parseResult.error && <Error error={parseResult.error} />} - {exportResult.error && <Error error={exportResult.error} />} - </div> - </> - ); -} diff --git a/web/source/settings/admin/domain-permissions/import-export.tsx b/web/source/settings/admin/domain-permissions/import-export.tsx deleted file mode 100644 index 871bca131..000000000 --- a/web/source/settings/admin/domain-permissions/import-export.tsx +++ /dev/null @@ -1,90 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -import 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); - }} - > - < back - </span> - 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/domain-permissions/index.tsx b/web/source/settings/admin/domain-permissions/index.tsx deleted file mode 100644 index 7d790cfc8..000000000 --- a/web/source/settings/admin/domain-permissions/index.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -import 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 ( - <Switch> - <Route path="/settings/admin/domain-permissions/:permType/:domain"> - {params => ( - <DomainPermDetail - permType={params.permType as PermType} - baseUrl={baseUrl} - domain={params.domain} - /> - )} - </Route> - <Route path="/settings/admin/domain-permissions/:permType"> - {params => ( - <DomainPermissionsOverview - permType={params.permType as PermType} - baseUrl={baseUrl} - /> - )} - </Route> - </Switch> - ); -} diff --git a/web/source/settings/admin/domain-permissions/overview.tsx b/web/source/settings/admin/domain-permissions/overview.tsx deleted file mode 100644 index bdfd214bc..000000000 --- a/web/source/settings/admin/domain-permissions/overview.tsx +++ /dev/null @@ -1,198 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -import 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="/settings/admin/domain-permissions/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} {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/domain-permissions/process.tsx b/web/source/settings/admin/domain-permissions/process.tsx deleted file mode 100644 index bb9411b9d..000000000 --- a/web/source/settings/admin/domain-permissions/process.tsx +++ /dev/null @@ -1,402 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -import React from "react"; - -import { memo, useMemo, useCallback, useEffect } from "react"; - -import { isValidDomainPermission, hasBetterScope } from "../../lib/util/domain-permission"; - -import { - useTextInput, - useBoolInput, - useRadioInput, - useCheckListInput, -} from "../../lib/form"; - -import { - Select, - TextArea, - RadioGroup, - Checkbox, - TextInput, -} from "../../components/form/inputs"; - -import useFormSubmit from "../../lib/form/submit"; - -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 ( - <div className="without-border"> - <FormWithData - dataQuery={permType.value == "allow" - ? useDomainAllowsQuery - : useDomainBlocksQuery - } - DataForm={ImportList} - {...{ list, permType }} - /> - </div> - ); - } -); - -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) { - hasPublic = true; - } - - if (entry.private_comment) { - hasPrivate = true; - } - - return hasPublic && hasPrivate; - }); - - if (hasPublic && hasPrivate) { - return { both: true }; - } else if (hasPublic) { - return { type: "public_comment" }; - } else if (hasPrivate) { - return { type: "private_comment" }; - } else { - return {}; - } - }, [list]); - - const showComment = useTextInput("showComment", { defaultValue: hasComment.type ?? "public_comment" }); - - const form = { - domains: useCheckListInput("domains", { entries: list }), // DomainPerm is actually also a Checkable. - obfuscate: useBoolInput("obfuscate"), - privateComment: useTextInput("private_comment", { - defaultValue: `Imported on ${new Date().toLocaleString()}` - }), - privateCommentBehavior: useRadioInput("private_comment_behavior", { - defaultValue: "append", - options: { - append: "Append to", - replace: "Replace" - } - }), - publicComment: useTextInput("public_comment"), - publicCommentBehavior: useRadioInput("public_comment_behavior", { - defaultValue: "append", - options: { - append: "Append to", - replace: "Replace" - } - }), - permType: permType, - }; - - const [importDomains, importResult] = useFormSubmit(form, useImportDomainPermsMutation(), { changedOnly: false }); - - return ( - <> - <form - onSubmit={importDomains} - className="domain-perm-import-list" - > - <span>{list.length} domain{list.length != 1 ? "s" : ""} in this list</span> - - {hasComment.both && - <Select field={showComment} options={ - <> - <option value="public_comment">Show public comments</option> - <option value="private_comment">Show private comments</option> - </> - } /> - } - - <div className="checkbox-list-wrapper"> - <DomainCheckList - field={form.domains} - domainPerms={domainPerms} - commentType={showComment.value as "public_comment" | "private_comment"} - permType={form.permType} - /> - </div> - - <TextArea - field={form.privateComment} - label="Private comment" - rows={3} - /> - <RadioGroup - field={form.privateCommentBehavior} - label="imported private comment" - /> - - <TextArea - field={form.publicComment} - label="Public comment" - rows={3} - /> - <RadioGroup - field={form.publicCommentBehavior} - label="imported public comment" - /> - - <Checkbox - field={form.obfuscate} - label="Obfuscate domains in public lists" - /> - - <MutationButton - label="Import" - disabled={false} - result={importResult} - /> - </form> - </> - ); -} - -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: entry.domain in domainPerms, - permType: permType, - }; - }, [domainPerms, commentType, permType]); - - 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 as ChecklistInputHook} - header={<> - <b>Domain</b> - <b> - {commentType == "public_comment" && "Public comment"} - {commentType == "private_comment" && "Private comment"} - </b> - </>} - EntryComponent={DomainEntry} - getExtraProps={getExtraProps} - /> - <UpdateHint - entries={entriesWithSuggestions} - updateEntry={field.onChange} - updateMultiple={field.updateMultiple} - /> - </> - ); -} - -interface UpdateHintProps { - entries, - updateEntry, - updateMultiple, -} - -const UpdateHint = memo( - function UpdateHint({ entries, updateEntry, updateMultiple }: UpdateHintProps) { - if (entries.length == 0) { - return null; - } - - function changeAll() { - updateMultiple( - entries.map((entry) => [entry.key, { domain: entry.suggest, suggest: null }]) - ); - } - - return ( - <div className="update-hints"> - <p> - {entries.length} {entries.length == 1 ? "entry uses" : "entries use"} a specific subdomain, - which you might want to change to the main domain, as that includes all it's (future) subdomains. - </p> - <div className="hints"> - {entries.map((entry) => ( - <UpdateableEntry key={entry.key} entry={entry} updateEntry={updateEntry} /> - ))} - </div> - {entries.length > 0 && <a onClick={changeAll}>change all</a>} - </div> - ); - } -); - -interface UpdateableEntryProps { - entry, - updateEntry, -} - -const UpdateableEntry = memo( - function UpdateableEntry({ entry, updateEntry }: UpdateableEntryProps) { - return ( - <> - <span className="text-cutoff">{entry.domain}</span> - <i className="fa fa-long-arrow-right" aria-hidden="true"></i> - <span>{entry.suggest}</span> - <a role="button" onClick={() => - updateEntry(entry.key, { domain: entry.suggest, suggest: null }) - }>change</a> - </> - ); - } -); - -function domainValidationError(isValid) { - return isValid ? "" : "Invalid domain"; -} - -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(isValidDomainPermission(value)) - }); - - useEffect(() => { - if (entry.valid != domainField.valid) { - onChange({ valid: domainField.valid }); - } - }, [onChange, entry.valid, domainField.valid]); - - useEffect(() => { - if (entry.domain != domainField.value) { - domainField.setter(entry.domain); - } - // domainField.setter is enough, eslint wants domainField - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [entry.domain, domainField.setter]); - - 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]); - - function clickIcon(e) { - if (entry.suggest) { - e.stopPropagation(); - e.preventDefault(); - domainField.setter(entry.suggest); - onChange({ domain: entry.suggest, checked: true }); - } - } - - return ( - <> - <div className="domain-input"> - <TextInput - field={domainField} - onChange={(e) => { - domainField.onChange(e); - onChange({ domain: e.target.value, checked: true }); - }} - /> - <span id="icon" onClick={clickIcon}> - <DomainEntryIcon - alreadyExists={alreadyExists} - suggestion={entry.suggest} - permTypeString={permType.value?? ""} - /> - </span> - </div> - <p>{comment}</p> - </> - ); -} - -interface DomainEntryIconProps { - alreadyExists: boolean; - suggestion: string; - permTypeString: string; -} - -function DomainEntryIcon({ alreadyExists, suggestion, permTypeString }: DomainEntryIconProps) { - let icon; - let text; - - if (suggestion) { - icon = "fa-info-circle suggest-changes"; - text = `Entry targets a specific subdomain, consider changing it to '${suggestion}'.`; - } else if (alreadyExists) { - icon = "fa-history permission-already-exists"; - text = `Domain ${permTypeString} already exists.`; - } - - if (!icon) { - return null; - } - - return ( - <> - <i className={`fa fa-fw ${icon}`} aria-hidden="true" title={text}></i> - <span className="sr-only">{text}</span> - </> - ); -}
\ No newline at end of file |