diff options
Diffstat (limited to 'web/source/settings/admin/federation')
7 files changed, 0 insertions, 903 deletions
diff --git a/web/source/settings/admin/federation/detail.js b/web/source/settings/admin/federation/detail.js deleted file mode 100644 index 7bdee66cf..000000000 --- a/web/source/settings/admin/federation/detail.js +++ /dev/null @@ -1,168 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -const React = require("react"); -const { useRoute, Redirect, useLocation } = require("wouter"); - -const query = require("../../lib/query"); - -const { useTextInput, useBoolInput } = require("../../lib/form"); - -const useFormSubmit = require("../../lib/form/submit"); - -const { TextInput, Checkbox, TextArea } = require("../../components/form/inputs"); - -const Loading = require("../../components/loading"); -const BackButton = require("../../components/back-button"); -const MutationButton = require("../../components/form/mutation-button"); - -module.exports = function InstanceDetail({ baseUrl }) { - const { data: blockedInstances = {}, isLoading } = query.useInstanceBlocksQuery(); - - let [_match, { domain }] = useRoute(`${baseUrl}/:domain`); - if (domain == "view") { - // Retrieve domain from form field submission. - domain = (new URL(document.location)).searchParams.get("domain"); - } - - // Normalize / decode domain (it may be URL-encoded). - domain = decodeURIComponent(domain); - - const existingBlock = React.useMemo(() => { - return blockedInstances[domain]; - }, [blockedInstances, domain]); - - if (domain == undefined) { - return <Redirect to={baseUrl} />; - } - - let infoContent = null; - - if (isLoading) { - infoContent = <Loading />; - } else if (existingBlock == undefined) { - infoContent = <span>No stored block yet, you can add one below:</span>; - } else { - infoContent = ( - <div className="info"> - <i className="fa fa-fw fa-exclamation-triangle" aria-hidden="true"></i> - <b>Editing domain blocks isn't implemented yet, <a href="https://github.com/superseriousbusiness/gotosocial/issues/1198" target="_blank" rel="noopener noreferrer">check here for progress</a></b> - </div> - ); - } - - return ( - <div> - <h1 className="text-cutoff"><BackButton to={baseUrl} /> Federation settings for: <span title={domain}>{domain}</span></h1> - {infoContent} - <DomainBlockForm defaultDomain={domain} block={existingBlock} baseUrl={baseUrl} /> - </div> - ); -}; - -function DomainBlockForm({ defaultDomain, block = {}, baseUrl }) { - const isExistingBlock = block.domain != undefined; - - const disabledForm = isExistingBlock - ? { - disabled: true, - title: "Domain suspensions currently cannot be edited." - } - : {}; - - const form = { - domain: useTextInput("domain", { source: block, defaultValue: defaultDomain }), - obfuscate: useBoolInput("obfuscate", { source: block }), - commentPrivate: useTextInput("private_comment", { source: block }), - commentPublic: useTextInput("public_comment", { source: block }) - }; - - const [submitForm, addResult] = useFormSubmit(form, query.useAddInstanceBlockMutation(), { changedOnly: false }); - - const [removeBlock, removeResult] = query.useRemoveInstanceBlockMutation({ fixedCacheKey: block.id }); - - const [location, setLocation] = useLocation(); - - function verifyUrlThenSubmit(e) { - // Adding a new block happens on /settings/admin/federation/domain.com - // but if domain input changes, that doesn't match anymore and causes issues later on - // so, before submitting the form, silently change url, then submit - let correctUrl = `${baseUrl}/${form.domain.value}`; - if (location != correctUrl) { - setLocation(correctUrl); - } - return submitForm(e); - } - - return ( - <form onSubmit={verifyUrlThenSubmit}> - <TextInput - field={form.domain} - label="Domain" - placeholder="example.com" - {...disabledForm} - /> - - <Checkbox - field={form.obfuscate} - label="Obfuscate domain in public lists" - {...disabledForm} - /> - - <TextArea - field={form.commentPrivate} - label="Private comment" - rows={3} - {...disabledForm} - /> - - <TextArea - field={form.commentPublic} - label="Public comment" - rows={3} - {...disabledForm} - /> - - <div className="action-buttons row"> - <MutationButton - label="Suspend" - result={addResult} - showError={false} - {...disabledForm} - /> - - { - isExistingBlock && - <MutationButton - type="button" - onClick={() => removeBlock(block.id)} - label="Remove" - result={removeResult} - className="button danger" - showError={false} - /> - } - </div> - - {addResult.error && <Error error={addResult.error} />} - {removeResult.error && <Error error={removeResult.error} />} - - </form> - ); -}
\ No newline at end of file diff --git a/web/source/settings/admin/federation/import-export/export-format-table.jsx b/web/source/settings/admin/federation/import-export/export-format-table.jsx deleted file mode 100644 index 7fcffa348..000000000 --- a/web/source/settings/admin/federation/import-export/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/federation/import-export/form.jsx b/web/source/settings/admin/federation/import-export/form.jsx deleted file mode 100644 index 2086739e3..000000000 --- a/web/source/settings/admin/federation/import-export/form.jsx +++ /dev/null @@ -1,124 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -const React = require("react"); - -const query = require("../../../lib/query"); -const useFormSubmit = require("../../../lib/form/submit"); - -const { - TextArea, - Select, -} = require("../../../components/form/inputs"); - -const MutationButton = require("../../../components/form/mutation-button"); - -const { Error } = require("../../../components/error"); -const ExportFormatTable = require("./export-format-table"); - -module.exports = function ImportExportForm({ form, submitParse, parseResult }) { - const [submitExport, exportResult] = useFormSubmit(form, query.useExportDomainListMutation()); - - function fileChanged(e) { - const reader = new FileReader(); - reader.onload = function (read) { - form.domains.value = read.target.result; - submitParse(); - }; - reader.readAsText(e.target.files[0]); - } - - React.useEffect(() => { - if (exportResult.isSuccess) { - form.domains.setter(exportResult.data); - } - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - }, [exportResult]); - - return ( - <> - <h1>Import / Export suspended domains</h1> - <p> - This page can be used to import and export lists of domains to suspend. - Exports can be done in various formats, with varying functionality and support in other software. - Imports will automatically detect what format is being processed. - </p> - <ExportFormatTable /> - <div className="import-export"> - <TextArea - field={form.domains} - label="Domains" - placeholder={`google.com\nfacebook.com`} - rows={8} - /> - - <div className="button-grid"> - <MutationButton - label="Import" - type="button" - onClick={() => submitParse()} - result={parseResult} - showError={false} - /> - <label className="button with-icon"> - <i className="fa fa-fw " aria-hidden="true" /> - Import file - <input - type="file" - className="hidden" - onChange={fileChanged} - accept="application/json,text/plain,text/csv" - /> - </label> - <b /> {/* grid filler */} - <MutationButton - label="Export" - type="button" - onClick={() => submitExport("export")} - result={exportResult} showError={false} - /> - <MutationButton - label="Export to file" - wrapperClassName="export-file-button" - type="button" - onClick={() => submitExport("export-file")} - result={exportResult} - showError={false} - /> - <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> - </> - ); -};
\ No newline at end of file diff --git a/web/source/settings/admin/federation/import-export/index.jsx b/web/source/settings/admin/federation/import-export/index.jsx deleted file mode 100644 index bff14b939..000000000 --- a/web/source/settings/admin/federation/import-export/index.jsx +++ /dev/null @@ -1,75 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -const React = require("react"); -const { Switch, Route, Redirect, useLocation } = require("wouter"); - -const query = require("../../../lib/query"); - -const { - useTextInput, -} = require("../../../lib/form"); - -const useFormSubmit = require("../../../lib/form/submit"); - -const ProcessImport = require("./process"); -const ImportExportForm = require("./form"); - -module.exports = function ImportExport({ baseUrl }) { - const form = { - domains: useTextInput("domains"), - exportType: useTextInput("exportType", { defaultValue: "plain", dontReset: true }) - }; - - const [submitParse, parseResult] = useFormSubmit(form, query.useProcessDomainListMutation(), { changedOnly: false }); - - const [_location, setLocation] = useLocation(); - - return ( - <Switch> - <Route path={`${baseUrl}/process`}> - {parseResult.isSuccess ? ( - <> - <h1> - <span className="button" onClick={() => { - parseResult.reset(); - setLocation(baseUrl); - }}> - < back - </span> Confirm import: - </h1> - <ProcessImport - list={parseResult.data} - /> - </> - ) : <Redirect to={baseUrl} />} - </Route> - - <Route> - {!parseResult.isSuccess ? ( - <ImportExportForm - form={form} - submitParse={submitParse} - parseResult={parseResult} - /> - ) : <Redirect to={`${baseUrl}/process`} />} - </Route> - </Switch> - ); -};
\ No newline at end of file diff --git a/web/source/settings/admin/federation/import-export/process.jsx b/web/source/settings/admin/federation/import-export/process.jsx deleted file mode 100644 index b39410605..000000000 --- a/web/source/settings/admin/federation/import-export/process.jsx +++ /dev/null @@ -1,329 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -const React = require("react"); - -const query = require("../../../lib/query"); -const { isValidDomainBlock, hasBetterScope } = require("../../../lib/domain-block"); - -const { - useTextInput, - useBoolInput, - useRadioInput, - useCheckListInput -} = require("../../../lib/form"); - -const useFormSubmit = require("../../../lib/form/submit"); - -const { - TextInput, - TextArea, - Checkbox, - Select, - RadioGroup -} = require("../../../components/form/inputs"); - -const CheckList = require("../../../components/check-list"); -const MutationButton = require("../../../components/form/mutation-button"); -const FormWithData = require("../../../lib/form/form-with-data"); - -module.exports = React.memo( - function ProcessImport({ list }) { - return ( - <div className="without-border"> - <FormWithData - dataQuery={query.useInstanceBlocksQuery} - DataForm={ImportList} - list={list} - /> - </div> - ); - } -); - -function ImportList({ list, data: blockedInstances }) { - const hasComment = React.useMemo(() => { - let hasPublic = false; - let hasPrivate = false; - - list.some((entry) => { - if (entry.public_comment?.length > 0) { - hasPublic = true; - } - - if (entry.private_comment?.length > 0) { - 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 }), - 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" - } - }), - }; - - const [importDomains, importResult] = useFormSubmit(form, query.useImportDomainListMutation(), { changedOnly: false }); - - return ( - <> - <form onSubmit={importDomains} className="suspend-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} - blockedInstances={blockedInstances} - commentType={showComment.value} - /> - </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" result={importResult} /> - </form> - </> - ); -} - -function DomainCheckList({ field, blockedInstances, commentType }) { - const getExtraProps = React.useCallback((entry) => { - return { - comment: entry[commentType], - alreadyExists: blockedInstances[entry.domain] != undefined - }; - }, [blockedInstances, commentType]); - - const entriesWithSuggestions = React.useMemo(() => ( - Object.values(field.value).filter((entry) => entry.suggest) - ), [field.value]); - - return ( - <> - <CheckList - field={field} - 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} - /> - </> - ); -} - -const UpdateHint = React.memo( - function UpdateHint({ entries, updateEntry, updateMultiple }) { - 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> - ); - } -); - -const UpdateableEntry = React.memo( - function UpdateableEntry({ entry, updateEntry }) { - 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"; -} - -function DomainEntry({ entry, onChange, extraProps: { alreadyExists, comment } }) { - const domainField = useTextInput("domain", { - defaultValue: entry.domain, - showValidation: entry.checked, - initValidation: domainValidationError(entry.valid), - validator: (value) => domainValidationError(isValidDomainBlock(value)) - }); - - React.useEffect(() => { - if (entry.valid != domainField.valid) { - onChange({ valid: domainField.valid }); - } - }, [onChange, entry.valid, domainField.valid]); - - React.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]); - - React.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} onChange={onChange} /> - </span> - </div> - <p>{comment}</p> - </> - ); -} - -function DomainEntryIcon({ alreadyExists, suggestion }) { - 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 already-blocked"; - text = "Domain block 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 diff --git a/web/source/settings/admin/federation/index.js b/web/source/settings/admin/federation/index.js deleted file mode 100644 index ec536c0be..000000000 --- a/web/source/settings/admin/federation/index.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -const React = require("react"); -const { Switch, Route } = require("wouter"); - -const InstanceOverview = require("./overview"); -const InstanceDetail = require("./detail"); -const InstanceImportExport = require("./import-export"); - -module.exports = function Federation({ baseUrl }) { - return ( - <Switch> - <Route path={`${baseUrl}/import-export/:list?`}> - <InstanceImportExport /> - </Route> - - <Route path={`${baseUrl}/:domain`}> - <InstanceDetail baseUrl={baseUrl} /> - </Route> - - <InstanceOverview baseUrl={baseUrl} /> - </Switch> - ); -};
\ No newline at end of file diff --git a/web/source/settings/admin/federation/overview.js b/web/source/settings/admin/federation/overview.js deleted file mode 100644 index c09289284..000000000 --- a/web/source/settings/admin/federation/overview.js +++ /dev/null @@ -1,101 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -const React = require("react"); -const { Link, useLocation } = require("wouter"); -const { matchSorter } = require("match-sorter"); - -const { useTextInput } = require("../../lib/form"); - -const { TextInput } = require("../../components/form/inputs"); - -const query = require("../../lib/query"); - -const Loading = require("../../components/loading"); - -module.exports = function InstanceOverview({ baseUrl }) { - const { data: blockedInstances = [], isLoading } = query.useInstanceBlocksQuery(); - - const [_location, setLocation] = useLocation(); - - const filterField = useTextInput("filter"); - const filter = filterField.value; - - const blockedInstancesList = React.useMemo(() => { - return Object.values(blockedInstances); - }, [blockedInstances]); - - const filteredInstances = React.useMemo(() => { - return matchSorter(blockedInstancesList, filter, { keys: ["domain"] }); - }, [blockedInstancesList, filter]); - - let filtered = blockedInstancesList.length - filteredInstances.length; - - function filterFormSubmit(e) { - e.preventDefault(); - setLocation(`${baseUrl}/${filter}`); - } - - if (isLoading) { - return <Loading />; - } - - return ( - <> - <h1>Federation</h1> - - <div className="instance-list"> - <h2>Suspended instances</h2> - <p> - Suspending a domain blocks all current and future accounts on that instance. Stored content will be removed, - and no more data is sent to the remote server.<br /> - This extends to all subdomains as well, so blocking 'example.com' also includes 'social.example.com'. - </p> - <form className="filter" role="search" onSubmit={filterFormSubmit}> - <TextInput field={filterField} placeholder="example.com" label="Search or add domain suspension" /> - <Link to={`${baseUrl}/${filter}`}><a className="button">Suspend</a></Link> - </form> - <div> - <span> - {blockedInstancesList.length} blocked instance{blockedInstancesList.length != 1 ? "s" : ""} {filtered > 0 && `(${filtered} filtered by search)`} - </span> - <div className="list"> - <div className="entries scrolling"> - {filteredInstances.map((entry) => { - return ( - <Link key={entry.domain} to={`${baseUrl}/${entry.domain}`}> - <a className="entry nounderline"> - <span id="domain"> - {entry.domain} - </span> - <span id="date"> - {new Date(entry.created_at).toLocaleString()} - </span> - </a> - </Link> - ); - })} - </div> - </div> - </div> - </div> - <Link to={`${baseUrl}/import-export`}><a>Or use the bulk import/export interface</a></Link> - </> - ); -};
\ No newline at end of file |