Moderation follow-ups: block on contact profile, z-index, account suspend, reporter notify
Addresses tester feedback on the Report/Block feature: 1. Block/Unblock is now on the CONTACT's profile card (openMiniProfile), not only in a message's ⋮/long-press menu — you can block someone straight from their info popup. 2. Admin Delete/confirm popups were appearing BEHIND the Reports window. The moderation modals used z-index 100000 (above bzConfirm's .modal-ov at 9800); lowered them to 9750 so the confirm dialog sits on top. 3. Clarified admin action. "Block" is a PERSONAL mute (per guideline 1.2) and does not touch login. The report view now offers a real account action instead: "Suspend account" (deactivate -> signed out + cannot log in) with a confirm, toggling to "Reactivate account" — both reversible in-place, driven by a new reportedActive flag on /api/reports. (Uses the existing /api/users/manage deactivate/activate.) 4. Resolving a report now notifies the reporter (live 'report-resolved' event + background push), and admins get a 'report-new' activity entry. repos: reports.byId. Verified: moderation suite 18/18 (adds reportedActive + suspend->login-blocked->reactivate->login-restored). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+9
-1
@@ -1725,8 +1725,10 @@ route('GET', '/api/reports', async (req, res) => {
|
||||
if (!u) return json(res, 401, { error: 'unauthorized' });
|
||||
if (u.role !== 'admin') return json(res, 403, { error: 'admins only' });
|
||||
const names = await namesFor(u.team_id);
|
||||
const roster = await R.users.listByTenant(u.team_id);
|
||||
const activeById = {}; roster.forEach((x) => { activeById[x.id] = x.active !== 0; }); // is the reported user still able to log in?
|
||||
const rows = await R.reports.listForTeam(u.team_id);
|
||||
json(res, 200, rows.map((r) => ({ id: r.id, messageId: r.message_id, reporter: names[r.reporter_id] || 'Unknown', reported: names[r.reported_id] || 'Unknown', reportedId: r.reported_id, reason: r.reason || '', snippet: r.snippet || '', at: r.created_at, status: r.status })));
|
||||
json(res, 200, rows.map((r) => ({ id: r.id, messageId: r.message_id, reporter: names[r.reporter_id] || 'Unknown', reported: names[r.reported_id] || 'Unknown', reportedId: r.reported_id, reportedActive: activeById[r.reported_id] !== false, reason: r.reason || '', snippet: r.snippet || '', at: r.created_at, status: r.status })));
|
||||
});
|
||||
|
||||
route('POST', '/api/reports/resolve', async (req, res) => {
|
||||
@@ -1735,7 +1737,13 @@ route('POST', '/api/reports/resolve', async (req, res) => {
|
||||
if (u.role !== 'admin') return json(res, 403, { error: 'admins only' });
|
||||
const { id, status } = await readBody(req);
|
||||
if (!id) return json(res, 400, { error: 'id required' });
|
||||
const rep = await R.reports.byId(id);
|
||||
await R.reports.setStatus(id, status === 'open' ? 'open' : 'resolved');
|
||||
// Close the loop: tell the reporter their report was reviewed (live + background push).
|
||||
if (rep && status !== 'open' && rep.reporter_id && rep.reporter_id !== u.id) {
|
||||
try { CHAT.pushToUser(rep.reporter_id, { type: 'report-resolved' }); } catch (_) {}
|
||||
try { PUSH.sendToUser(rep.reporter_id, { title: 'Report reviewed', body: 'An admin reviewed the message you reported.' }); } catch (_) {}
|
||||
}
|
||||
json(res, 200, { ok: true });
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user