Make abuse report window more user friendly

This commit is contained in:
2021-05-17 11:16:20 +02:00
parent 5be98caadc
commit 5875d187b6
8 changed files with 92 additions and 50 deletions

View File

@@ -4,7 +4,7 @@ function AbuseReportWindow(viewer) {
this.modal = new Modal( this.modal = new Modal(
document.getElementById("file_viewer"), document.getElementById("file_viewer"),
() => { this.toggle() }, () => { this.toggle() },
"Report abuse", "600px", "auto", "Report abuse", "650px", "auto",
) )
this.btnReportAbuse = document.getElementById("btn_report_abuse") this.btnReportAbuse = document.getElementById("btn_report_abuse")

View File

@@ -1,7 +1,7 @@
function AbuseViewer(viewer, file, next) { function AbuseViewer(viewer, file, next) {
this.viewer = viewer this.viewer = viewer
this.file = file this.file = file
this.next = next this.next = next
this.container = document.createElement("div") this.container = document.createElement("div")
this.container.classList = "image-container" this.container.classList = "image-container"
@@ -12,17 +12,17 @@ function AbuseViewer(viewer, file, next) {
this.container.appendChild(this.title) this.container.appendChild(this.title)
this.description = document.createElement("p") this.description = document.createElement("p")
this.description.innerText = "This file has received an abuse report and "+ this.description.innerText = "This file has received an abuse report and " +
"was taken down." "was taken down."
this.container.appendChild(this.description) this.container.appendChild(this.description)
this.description2 = document.createElement("p") this.description2 = document.createElement("p")
this.description2.innerText = "Type of abuse: '"+file.abuse_type+"'. "+ this.description2.innerText = "Type of abuse: " + file.abuse_type + ". " +
"Reporter: '"+file.abuse_reporter_name+"'." "Reporter: " + file.abuse_reporter_name + "."
this.container.appendChild(this.description2) this.container.appendChild(this.description2)
} }
AbuseViewer.prototype.render = function(parent) { AbuseViewer.prototype.render = function (parent) {
parent.appendChild(this.container) parent.appendChild(this.container)
// Disable the download button // Disable the download button
@@ -30,7 +30,7 @@ AbuseViewer.prototype.render = function(parent) {
this.viewer.toolbar.btnDownload.style.display = "none" this.viewer.toolbar.btnDownload.style.display = "none"
} }
AbuseViewer.prototype.destroy = function(parent) { AbuseViewer.prototype.destroy = function (parent) {
// Restore the download button // Restore the download button
this.viewer.toolbar.btnDownload.style.display = this.btnDownloadDisplay this.viewer.toolbar.btnDownload.style.display = this.btnDownloadDisplay
} }

View File

@@ -265,6 +265,16 @@
table {width: auto !important;} table {width: auto !important;}
/* Abuse report label*/
.abuse_type_form > label {
display: block;
border-bottom: 1px var(--layer_2_color_border) solid;
padding: 0.5em;
}
.abuse_type_form > label:last-of-type {
border-bottom: none;
}
/* ==================== /* ====================
|| LIST NAVIGATOR || || LIST NAVIGATOR ||

View File

@@ -325,18 +325,30 @@
</p> </p>
<form class="abuse_type_form" style="width: 100%"> <form class="abuse_type_form" style="width: 100%">
<h3>Abuse type</h3> <h3>Abuse type</h3>
<input type="radio" id="abuse_type_terrorism" name="abuse_type" value="terrorism"> <p>
<label for="abuse_type_terrorism">Terrorism</label> Which type of abuse is shown in this file? Pick the most
<br/> appropriate one.
<input type="radio" id="abuse_type_gore" name="abuse_type" value="gore"> </p>
<label for="abuse_type_gore">Gore</label> <label for="abuse_type_terrorism">
<br/> <input type="radio" id="abuse_type_terrorism" name="abuse_type" value="terrorism">
<input type="radio" id="abuse_type_child_abuse" name="abuse_type" value="child_abuse"> <b>Terrorism</b>: Videos, images or audio fragments showing
<label for="abuse_type_child_abuse">Child abuse</label> the use of intentional violence to achieve political aims.
<br/> </label>
<input type="radio" id="abuse_type_malware" name="abuse_type" value="malware"> <label for="abuse_type_gore">
<label for="abuse_type_malware">Malware</label> <input type="radio" id="abuse_type_gore" name="abuse_type" value="gore">
<br/> <b>Gore</b>: Graphic and shocking videos or images depicting
severe harm to humans (or animals).
</label>
<label for="abuse_type_child_abuse">
<input type="radio" id="abuse_type_child_abuse" name="abuse_type" value="child_abuse">
<b>Child abuse</b>: Videos or images depicting inappropriate
touching or nudity of minors.
</label>
<label for="abuse_type_malware">
<input type="radio" id="abuse_type_malware" name="abuse_type" value="malware">
<b>Malware</b>: Software programs designed to cause harm to
computer systems.
</label>
<!-- <!--
<h3>E-mail address</h3> <h3>E-mail address</h3>
<p> <p>
@@ -352,7 +364,7 @@
<div class="abuse_report_notification" style="display: none;"></div> <div class="abuse_report_notification" style="display: none;"></div>
<p> <p>
Abuse reports are manually reviewed. Normally this shouldn't Abuse reports are manually reviewed. Normally this shouldn't
take more than 24 hours. But during busy periods it can take take more than 24 hours. During busy periods it can take
longer. longer.
</p> </p>
<div style="text-align: right;"> <div style="text-align: right;">

View File

@@ -286,7 +286,11 @@
</div> </div>
<div> <div>
<div class="feat_label"><a href="javascript:void(0);" onclick="return explainDirectLinking();">Direct linking</a></div> <div class="feat_label"><a href="javascript:void(0);" onclick="return explainDirectLinking();">Direct linking</a></div>
<div class="feat_pro">Rate limiting mode will be enabled when a file has 3 times more downloads than views</div> <div class="feat_pro">
Rate limiting mode will be enabled when a file has 3
times more downloads than views. Pro users will never be
asked to fill in a captcha
</div>
</div> </div>
<div> <div>
<div></div> <div></div>

View File

@@ -28,19 +28,18 @@ let set_status = async (action, report_type) => {
alert(err); alert(err);
} }
} }
</script> </script>
<Expandable bind:this={expandable} expanded={report.status === "pending"}> <Expandable bind:this={expandable} expanded={report.status === "pending" && report.reports.length > 2}>
<div slot="header" class="header" on:click={expandable.toggle}> <div slot="header" class="header" on:click={expandable.toggle}>
<div class="icon_cell"> <div class="icon_cell">
<img class="file_icon" src={"/api/file/"+report.file.id+"/thumbnail"} alt="File thumbnail"/> <img class="file_icon" src={"/api/file/"+report.file.id+"/thumbnail"} alt="File thumbnail"/>
</div> </div>
<div class="title">{report.file.name}</div> <div class="title">{report.file.name}</div>
<div class="stats">Type<br/> <div class="stats">Type<br/>
{report.file.abuse_type === "" ? report.type : report.file.abuse_type} {report.file.abuse_type === "" ? report.type : report.file.abuse_type}
</div> </div>
{#if report.status !== "pending"} {#if report.status !== "pending"}
<div class="stats">Status<br/>{report.status}</div> <div class="stats">Status<br/>{report.status}</div>
{/if} {/if}
@@ -77,7 +76,7 @@ let set_status = async (action, report_type) => {
</tr> </tr>
{#each report.reports as user_report} {#each report.reports as user_report}
<tr> <tr>
<td>{formatDate(user_report.time, true, true, true, true)}</td> <td>{formatDate(user_report.time, true, true, false)}</td>
<td>{user_report.ip_address}</td> <td>{user_report.ip_address}</td>
<td>{user_report.type}</td> <td>{user_report.type}</td>
<td>{user_report.status}</td> <td>{user_report.status}</td>

View File

@@ -4,12 +4,12 @@ import Spinner from "../util/Spinner.svelte";
import AbuseReport from "./AbuseReport.svelte"; import AbuseReport from "./AbuseReport.svelte";
let loading = true let loading = true
let reports = [] let reports_pending = []
let reports_processed = []
let startPicker let startPicker
let endPicker let endPicker
const get_reports = async () => { const get_reports = async () => {
loading = true; loading = true;
@@ -23,11 +23,21 @@ const get_reports = async () => {
if(resp.status >= 400) { if(resp.status >= 400) {
throw new Error(resp.text()); throw new Error(resp.text());
} }
reports = await resp.json(); let reports = await resp.json();
// Sort files from new to old // Sort files by number of reports. If the number of reports is equal we
// sort by number of views. If the number of views is equal we sort by
// date of the first report received
reports.sort((a, b) => { reports.sort((a, b) => {
if (a.first_report_time > b.first_report_time) { if (a.reports.length > b.reports.length) {
return -1
} else if (a.reports.length < b.reports.length) {
return 1
} else if (a.file.views > b.file.views) {
return -1
} else if (a.file.views < b.file.views) {
return 1
} else if (a.first_report_time > b.first_report_time) {
return -1 return -1
} else if (a.first_report_time < b.first_report_time) { } else if (a.first_report_time < b.first_report_time) {
return 1 return 1
@@ -36,7 +46,11 @@ const get_reports = async () => {
} }
}) })
// Sort individual reports from old to new reports_pending = []
reports_processed = []
// Sort individual reports of each file from old to new, then separate
// pending reports and processed reports
reports.forEach(v => { reports.forEach(v => {
v.reports.sort((a, b) => { v.reports.sort((a, b) => {
if (a.time > b.time) { if (a.time > b.time) {
@@ -47,7 +61,17 @@ const get_reports = async () => {
return 0 return 0
} }
}) })
if (v.status === "pending") {
reports_pending.push(v)
} else {
reports_processed.push(v)
}
}) })
// Update svelte views
reports_processed = reports_processed
reports_pending = reports_pending
} catch (err) { } catch (err) {
alert(err); alert(err);
} finally { } finally {
@@ -57,7 +81,7 @@ const get_reports = async () => {
onMount(() => { onMount(() => {
let start = new Date() let start = new Date()
start.setMonth(start.getMonth() - 1) start.setDate(start.getDate() - 14)
let end = new Date() let end = new Date()
startPicker.valueAsNumber = start.getTime() startPicker.valueAsNumber = start.getTime()
@@ -88,14 +112,14 @@ onMount(() => {
</div> </div>
<h2>Pending</h2> <h2>Pending</h2>
{#each reports as report} {#each reports_pending as report}
{#if report.status === "pending"} {#if report.status === "pending"}
<AbuseReport report={report} on:refresh={get_reports}/> <AbuseReport report={report} on:refresh={get_reports}/>
{/if} {/if}
{/each} {/each}
<h2>Resolved</h2> <h2>Resolved</h2>
{#each reports as report} {#each reports_processed as report}
{#if report.status !== "pending"} {#if report.status !== "pending"}
<AbuseReport report={report} on:refresh={get_reports}/> <AbuseReport report={report} on:refresh={get_reports}/>
{/if} {/if}
@@ -107,9 +131,10 @@ onMount(() => {
.spinner_container { .spinner_container {
position: absolute; position: absolute;
top: 10px; top: 10px;
left: 10px; right: 10px;
height: 100px; height: 100px;
width: 100px; width: 100px;
z-index: 1000;
} }
.toolbar { .toolbar {
display: flex; display: flex;

View File

@@ -46,20 +46,12 @@ func adType() int {
// Intn returns a number up to n, but never n itself. So to get a random 0 // Intn returns a number up to n, but never n itself. So to get a random 0
// or 1 we need to give it n=2. We can use this function to make other // or 1 we need to give it n=2. We can use this function to make other
// splits like 1/3 1/4, etc // splits like 1/3 1/4, etc
switch i := rand.Intn(20); i { switch i := rand.Intn(10); i {
case 0, 1: // 10% case 0: // 10%
return amarulaSolutions return amarulaSolutions
case 2: // 5% case 1, 2, 3, 4, 5: // 50%
return pdpro1
case 3: // 5%
return pdpro2
case 4: // 5%
return pdpro3
case 5: // 5%
return pdpro4
case 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17: // 60%
return brave return brave
case 18, 19: // 10% case 6, 7, 8, 9: // 40%
return aAds return aAds
default: default:
panic(fmt.Errorf("random number generator returned unrecognised number: %d", i)) panic(fmt.Errorf("random number generator returned unrecognised number: %d", i))