mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-25 23:18:51 -07:00
[www] show moderated image
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { KickReportCategory } from '../../../notify/src/notification-payloads'
|
import { KickReportCategory } from '../../../notify/src/notification-payloads'
|
||||||
import { accountIdForUsername, call, isAdmin, useAction, usernamesFor } from './api'
|
import { accountIdForUsername, call, isAdmin, useAction, usernamesFor, where } from './api'
|
||||||
|
|
||||||
import type { PublicAccount } from './api'
|
import type { PublicAccount } from './api'
|
||||||
|
|
||||||
@@ -95,6 +95,11 @@ interface ReportRow {
|
|||||||
banned_at: string | null
|
banned_at: string | null
|
||||||
chat_message_id: number | null
|
chat_message_id: number | null
|
||||||
image_id: number | null
|
image_id: number | null
|
||||||
|
/**
|
||||||
|
* The reported photo's key on `img`, joined on by `www` (the `report` table stores only
|
||||||
|
* the id). Null for a report that isn't against a photo, or one whose photo is gone.
|
||||||
|
*/
|
||||||
|
image_name?: string | null
|
||||||
/**
|
/**
|
||||||
* Who lifted the ban that once stood on this row, and when — null until one is lifted,
|
* Who lifted the ban that once stood on this row, and when — null until one is lifted,
|
||||||
* and cleared again by a re-ban. A lift wipes the four ban columns, so this pair is the
|
* and cleared again by a re-ban. A lift wipes the four ban columns, so this pair is the
|
||||||
@@ -925,7 +930,20 @@ function ReportTable({
|
|||||||
<td>
|
<td>
|
||||||
<PlayerName id={report.reporter_player_id} names={names} />
|
<PlayerName id={report.reporter_player_id} names={names} />
|
||||||
</td>
|
</td>
|
||||||
<td>{reportKind(report)}</td>
|
<td>
|
||||||
|
{report.image_name ? (
|
||||||
|
<a
|
||||||
|
href={`${where().img}/${report.image_name}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
title={`Photo #${report.image_id}`}
|
||||||
|
>
|
||||||
|
{reportKind(report)}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
reportKind(report)
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
<td>{categoryLabel(report.report_category)}</td>
|
<td>{categoryLabel(report.report_category)}</td>
|
||||||
{/* The reporter's own words, untruncated in the title so a long
|
{/* The reporter's own words, untruncated in the title so a long
|
||||||
description is readable without leaving the table. */}
|
description is readable without leaving the table. */}
|
||||||
|
|||||||
+26
-3
@@ -5,6 +5,7 @@ import {
|
|||||||
clearPasswordHash,
|
clearPasswordHash,
|
||||||
createGift,
|
createGift,
|
||||||
getAccount,
|
getAccount,
|
||||||
|
getImageById,
|
||||||
getOnlinePlayerIds,
|
getOnlinePlayerIds,
|
||||||
getPlayerIdsInRoom,
|
getPlayerIdsInRoom,
|
||||||
getPresences,
|
getPresences,
|
||||||
@@ -340,6 +341,22 @@ async function recordBanAudit(
|
|||||||
/** The evasion arms in force, read from the same knob `api` and `match` read. */
|
/** The evasion arms in force, read from the same knob `api` and `match` read. */
|
||||||
const armsFor = (env: Env) => banEvasionMatch(env.BAN_EVASION_MATCH)
|
const armsFor = (env: Env) => banEvasionMatch(env.BAN_EVASION_MATCH)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports with the reported photo's `image_name` alongside its `image_id`, so the panel can
|
||||||
|
* link to the picture — `img` serves by name, and the row only stores the id. Read whatever
|
||||||
|
* the photo's accessibility: one made private after it was reported is still evidence. A
|
||||||
|
* photo since deleted gets a null name and the panel shows the bare id.
|
||||||
|
*/
|
||||||
|
async function withImageNames(db: D1Database, reports: ReportRow[]) {
|
||||||
|
const ids = [...new Set(reports.flatMap((r) => (r.image_id == null ? [] : [r.image_id])))]
|
||||||
|
const images = await Promise.all(ids.map((id) => getImageById(db, id)))
|
||||||
|
const names = new Map(ids.map((id, i) => [id, images[i]?.ImageName ?? null]))
|
||||||
|
return reports.map((r) => ({
|
||||||
|
...r,
|
||||||
|
image_name: r.image_id == null ? null : (names.get(r.image_id) ?? null),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Handlers ---------------------------------------------------------------
|
// ---- Handlers ---------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -361,7 +378,7 @@ export async function searchReportsHandler(c: Context<App>) {
|
|||||||
skip: intQuery(c, 'skip') ?? 0,
|
skip: intQuery(c, 'skip') ?? 0,
|
||||||
take: Math.min(intQuery(c, 'take') ?? 50, MAX_TAKE),
|
take: Math.min(intQuery(c, 'take') ?? 50, MAX_TAKE),
|
||||||
})
|
})
|
||||||
return c.json(page)
|
return c.json({ ...page, reports: await withImageNames(c.env.DB, page.reports) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/** One report by id — the detail behind a search row. */
|
/** One report by id — the detail behind a search row. */
|
||||||
@@ -370,7 +387,8 @@ export async function getReportHandler(c: Context<App>) {
|
|||||||
if (Number.isNaN(id)) return c.json({ error: 'A numeric report id is required' }, 400)
|
if (Number.isNaN(id)) return c.json({ error: 'A numeric report id is required' }, 400)
|
||||||
const report = await getReportById(c.env.DB, id)
|
const report = await getReportById(c.env.DB, id)
|
||||||
if (report === null) return c.json({ error: 'No such report' }, 404)
|
if (report === null) return c.json({ error: 'No such report' }, 404)
|
||||||
return c.json(report)
|
const [withName] = await withImageNames(c.env.DB, [report])
|
||||||
|
return c.json(withName)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -525,7 +543,12 @@ export async function playerHistoryHandler(c: Context<App>) {
|
|||||||
getWarningsAgainst(c.env.DB, playerId),
|
getWarningsAgainst(c.env.DB, playerId),
|
||||||
getActiveBan(c.env.DB, playerId),
|
getActiveBan(c.env.DB, playerId),
|
||||||
])
|
])
|
||||||
return c.json({ playerId, reports, warnings, activeBan })
|
return c.json({
|
||||||
|
playerId,
|
||||||
|
reports: await withImageNames(c.env.DB, reports),
|
||||||
|
warnings,
|
||||||
|
activeBan,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user