diff options
Diffstat (limited to 'web/source/panels/admin/blocks.js')
-rw-r--r-- | web/source/panels/admin/blocks.js | 318 |
1 files changed, 0 insertions, 318 deletions
diff --git a/web/source/panels/admin/blocks.js b/web/source/panels/admin/blocks.js deleted file mode 100644 index b12eb50a9..000000000 --- a/web/source/panels/admin/blocks.js +++ /dev/null @@ -1,318 +0,0 @@ -/* - GoToSocial - Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org - - 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/>. -*/ - -"use strict"; - -const Promise = require("bluebird"); -const React = require("react"); -const fileDownload = require("js-file-download"); - -function sortBlocks(blocks) { - return blocks.sort((a, b) => { // alphabetical sort - return a.domain.localeCompare(b.domain); - }); -} - -function deduplicateBlocks(blocks) { - let a = new Map(); - blocks.forEach((block) => { - a.set(block.id, block); - }); - return Array.from(a.values()); -} - -module.exports = function Blocks({oauth}) { - const [blocks, setBlocks] = React.useState([]); - const [info, setInfo] = React.useState("Fetching blocks"); - const [errorMsg, setError] = React.useState(""); - const [checked, setChecked] = React.useState(new Set()); - - React.useEffect(() => { - Promise.try(() => { - return oauth.apiRequest("/api/v1/admin/domain_blocks", undefined, undefined, "GET"); - }).then((json) => { - setInfo(""); - setError(""); - setBlocks(sortBlocks(json)); - }).catch((e) => { - setError(e.message); - setInfo(""); - }); - }, []); - - let blockList = blocks.map((block) => { - function update(e) { - let newChecked = new Set(checked.values()); - if (e.target.checked) { - newChecked.add(block.id); - } else { - newChecked.delete(block.id); - } - setChecked(newChecked); - } - - return ( - <React.Fragment key={block.id}> - <div><input type="checkbox" onChange={update} checked={checked.has(block.id)}></input></div> - <div>{block.domain}</div> - <div>{(new Date(block.created_at)).toLocaleString()}</div> - </React.Fragment> - ); - }); - - function clearChecked() { - setChecked(new Set()); - } - - function undoChecked() { - let amount = checked.size; - if(confirm(`Are you sure you want to remove ${amount} block(s)?`)) { - setInfo(""); - Promise.map(Array.from(checked.values()), (block) => { - console.log("deleting", block); - return oauth.apiRequest(`/api/v1/admin/domain_blocks/${block}`, "DELETE"); - }).then((res) => { - console.log(res); - setInfo(`Deleted ${amount} blocks: ${res.map((a) => a.domain).join(", ")}`); - }).catch((e) => { - setError(e); - }); - - let newBlocks = blocks.filter((block) => { - if (checked.size > 0 && checked.has(block.id)) { - checked.delete(block.id); - return false; - } else { - return true; - } - }); - setBlocks(newBlocks); - clearChecked(); - } - } - - return ( - <section className="blocks"> - <h1>Blocks</h1> - <div className="error accent">{errorMsg}</div> - <div>{info}</div> - <AddBlock oauth={oauth} blocks={blocks} setBlocks={setBlocks} /> - <h3>Blocks:</h3> - <div style={{display: "grid", gridTemplateColumns: "1fr auto"}}> - <span onClick={clearChecked} className="accent" style={{alignSelf: "end"}}>uncheck all</span> - <button onClick={undoChecked}>Unblock selected</button> - </div> - <div className="blocklist overflow"> - {blockList} - </div> - <BulkBlocking oauth={oauth} blocks={blocks} setBlocks={setBlocks}/> - </section> - ); -}; - -function BulkBlocking({oauth, blocks, setBlocks}) { - const [bulk, setBulk] = React.useState(""); - const [blockMap, setBlockMap] = React.useState(new Map()); - const [output, setOutput] = React.useState(); - - React.useEffect(() => { - let newBlockMap = new Map(); - blocks.forEach((block) => { - newBlockMap.set(block.domain, block); - }); - setBlockMap(newBlockMap); - }, [blocks]); - - const fileRef = React.useRef(); - - function error(e) { - setOutput(<div className="error accent">{e}</div>); - throw e; - } - - function fileUpload() { - let reader = new FileReader(); - reader.addEventListener("load", (e) => { - try { - // TODO: use validatem? - let json = JSON.parse(e.target.result); - json.forEach((block) => { - console.log("block:", block); - }); - } catch(e) { - error(e.message); - } - }); - reader.readAsText(fileRef.current.files[0]); - } - - React.useEffect(() => { - if (fileRef && fileRef.current) { - fileRef.current.addEventListener("change", fileUpload); - } - return function cleanup() { - fileRef.current.removeEventListener("change", fileUpload); - }; - }); - - function textImport() { - Promise.try(() => { - if (bulk[0] == "[") { - // assume it's json - return JSON.parse(bulk); - } else { - return bulk.split("\n").map((val) => { - return { - domain: val.trim() - }; - }); - } - }).then((domains) => { - console.log(domains); - let before = domains.length; - setOutput(`Importing ${before} domain(s)`); - domains = domains.filter(({domain}) => { - return (domain != "" && !blockMap.has(domain)); - }); - setOutput(<span>{output}<br/>{`Deduplicated ${before - domains.length}/${before} with existing blocks, adding ${domains.length} block(s)`}</span>); - if (domains.length > 0) { - let data = new FormData(); - data.append("domains", new Blob([JSON.stringify(domains)], {type: "application/json"}), "import.json"); - return oauth.apiRequest("/api/v1/admin/domain_blocks?import=true", "POST", data, "form"); - } - }).then((json) => { - console.log("bulk import result:", json); - setBlocks(sortBlocks(deduplicateBlocks([...json, ...blocks]))); - }).catch((e) => { - error(e.message); - }); - } - - function textExport() { - setBulk(blocks.reduce((str, val) => { - if (typeof str == "object") { - return str.domain; - } else { - return str + "\n" + val.domain; - } - })); - } - - function jsonExport() { - Promise.try(() => { - return oauth.apiRequest("/api/v1/admin/domain_blocks?export=true", "GET"); - }).then((json) => { - fileDownload(JSON.stringify(json), "block-export.json"); - }).catch((e) => { - error(e); - }); - } - - function textAreaUpdate(e) { - setBulk(e.target.value); - } - - return ( - <React.Fragment> - <h3>Bulk import/export</h3> - <label htmlFor="bulk">Domains, one per line:</label> - <textarea value={bulk} rows={20} onChange={textAreaUpdate}></textarea> - <div className="controls"> - <button onClick={textImport}>Import All From Field</button> - <button onClick={textExport}>Export To Field</button> - <label className="button" htmlFor="upload">Upload .json</label> - <button onClick={jsonExport}>Download .json</button> - </div> - {output} - <input type="file" id="upload" className="hidden" ref={fileRef}></input> - </React.Fragment> - ); -} - -function AddBlock({oauth, blocks, setBlocks}) { - const [domain, setDomain] = React.useState(""); - const [type, setType] = React.useState("suspend"); - const [obfuscated, setObfuscated] = React.useState(false); - const [privateDescription, setPrivateDescription] = React.useState(""); - const [publicDescription, setPublicDescription] = React.useState(""); - - function addBlock() { - console.log(`${type}ing`, domain); - Promise.try(() => { - return oauth.apiRequest("/api/v1/admin/domain_blocks", "POST", { - domain: domain, - obfuscate: obfuscated, - private_comment: privateDescription, - public_comment: publicDescription - }, "json"); - }).then((json) => { - setDomain(""); - setPrivateDescription(""); - setPublicDescription(""); - setBlocks([json, ...blocks]); - }); - } - - function onDomainChange(e) { - setDomain(e.target.value); - } - - function onTypeChange(e) { - setType(e.target.value); - } - - function onKeyDown(e) { - if (e.key == "Enter") { - addBlock(); - } - } - - return ( - <React.Fragment> - <h3>Add Block:</h3> - <div className="addblock"> - <input id="domain" placeholder="instance" onChange={onDomainChange} value={domain} onKeyDown={onKeyDown} /> - <select value={type} onChange={onTypeChange}> - <option id="suspend">Suspend</option> - <option id="silence">Silence</option> - </select> - <button onClick={addBlock}>Add</button> - <div> - <label htmlFor="private">Private description:</label><br/> - <textarea id="private" value={privateDescription} onChange={(e) => setPrivateDescription(e.target.value)}></textarea> - </div> - <div> - <label htmlFor="public">Public description:</label><br/> - <textarea id="public" value={publicDescription} onChange={(e) => setPublicDescription(e.target.value)}></textarea> - </div> - <div className="single"> - <label htmlFor="obfuscate">Obfuscate:</label> - <input id="obfuscate" type="checkbox" value={obfuscated} onChange={(e) => setObfuscated(e.target.checked)}/> - </div> - </div> - </React.Fragment> - ); -} - -// function Blocklist() { -// return ( -// <section className="blocklists"> -// <h1>Blocklists</h1> -// </section> -// ); -// }
\ No newline at end of file |