@inherits LocalizableComponentBase @* The reports moderators handle (/clientapi/moderator/reports): from someone here (never said who) or from another server, about an account and some of its posts, a violation with the server's rules it names (their texts as PrivaPub keeps them, a removed rule's too). "Resolve and…" resolves one with what the staff does about it (nothing more, the account silenced or suspended, the reported posts removed), a note for the staff and a warning for a persona here; the act comes first, so one that fails leaves the report open. A resolved report shows what was done and its note. *@
@if (IsLoading) {

@Localizer["loading"]

} else if (reports.Count == 0) {

@(resolved ? Localizer["No resolved report."] : Localizer["No open report."])

} @foreach (var report in reports) {
@CategoryName(report.Category) @if (report.Forwarded) { @Localizer["Forwarded"] } @report.CreatedAt.GetPassedTime(Localizer) @if (!report.IsResolved) { @Localizer["Resolve and…"] @Localizer["Resolve only"] @if (report.TargetAccountId is { Length: > 0 }) { @Localizer["Silence the account"] @Localizer["Suspend the account"] } @if (report.PostIds.Count > 0) { @Localizer["Remove the reported posts"] } } else if (report.ResolvedAt is { } resolvedAt) { @Localizer["Resolved {0}", resolvedAt.GetPassedTime(Localizer)] }
@if (report.IsResolved && report.Resolution is { Length: > 0 } resolution) {
@ResolutionName(resolution) @if (report.ResolutionNote is { Length: > 0 } resolutionNote) { @resolutionNote }
}

@Localizer["About"] @if (report.TargetAccountId is { Length: > 0 } targetId) { @report.TargetActorURI } else { @report.TargetActorURI }

@Localizer["From"] @(report.Reporter == "local" ? Localizer["someone here"] : report.Reporter)

@if (report.RuleIds.Count > 0) {
@Localizer["Rules it names"]
    @foreach (var (ruleId, text) in RulesOf(report)) {
  1. @if (text is { Length: > 0 }) { @text } else { @Localizer["A rule no longer kept"] }
  2. }
} @if (report.Comment is { Length: > 0 } comment) {

@comment

} @if (report.PostIds.Count > 0 || report.ObjectURIs.Count > 0) {
@foreach (var postId in report.PostIds) { @Localizer["Post {0}", postId] } @* a report from another server names posts by their ids there when we hold none of them *@ @if (report.PostIds.Count == 0) { @foreach (var uri in report.ObjectURIs) { @uri } }
}
}
@if (resolving is not null) { } @code { [Inject] AdminService AdminService { get; set; } [Inject] ModerationService ModerationService { get; set; } List reports = []; bool resolved; // the report whose "Resolve and…" menu is open string menuOpen; // the report being resolved and how, while its dialog asks ViewReport resolving; string resolveAction; protected override async Task OnInitializedAsync() { if (CascadingState.IsOffline) { WhenOnline(LoadData); StartOnlinePolling(); IsLoading = false; return; } await LoadData(); IsLoading = false; } async Task LoadData() => reports = await AdminService.Reports(resolved); async Task ShowResolved(bool value) { resolved = value; IsLoading = true; await LoadData(); IsLoading = false; } // each rule the report names with its text, in the report's order (an empty text: the server no longer has it) static IEnumerable<(string Id, string Text)> RulesOf(ViewReport report) => report.RuleIds.Select((id, i) => (id, i < report.Rules.Count ? report.Rules[i] : null)); string CategoryName(string category) => category switch { "spam" => Localizer["Spam"], "legal" => Localizer["Illegal"], "violation" => Localizer["Breaks the rules"], _ => Localizer["Other"] }; string ResolutionName(string action) => action switch { ModerationActions.Silence => Localizer["The account was silenced."], ModerationActions.Suspend => Localizer["The account was suspended."], ModerationActions.RemovePosts => Localizer["The reported posts were removed."], _ => Localizer["Resolved without an act."] }; string ResolveQuestion => resolveAction switch { ModerationActions.Silence => Localizer["Resolve it and silence the account?"], ModerationActions.Suspend => Localizer["Resolve it and suspend the account?"], ModerationActions.RemovePosts => Localizer["Resolve it and remove the reported posts?"], _ => Localizer["Resolve the report?"] }; string ResolveText => resolveAction switch { ModerationActions.Silence => Localizer["Its posts stay out of public timelines, hashtags, trends and search; its followers still read them."], ModerationActions.Suspend => Localizer["Nothing of it is shown here any more and it can't act; nothing new is told to other servers. It can be lifted later."], ModerationActions.RemovePosts => Localizer["A post written here is deleted as its author would delete it; a copy of one from elsewhere is hidden here."], _ => Localizer["Nothing is done to the account or its posts."] }; string ResolveConfirm => resolveAction switch { ModerationActions.Silence => Localizer["Silence"], ModerationActions.Suspend => Localizer["Suspend"], ModerationActions.RemovePosts => Localizer["Remove the posts"], _ => Localizer["Resolve"] }; void AskResolve(ViewReport report, string action) { menuOpen = null; resolving = report; resolveAction = action; } void CloseResolve() => (resolving, resolveAction) = (null, null); async Task Resolve(ActDialog.Answer answer) { var report = resolving; var action = resolveAction; var result = await ModerationService.ResolveReport(report.ID, new ResolveReportForm { Action = action, Note = answer.Note, Warning = action == ModerationActions.None ? null : answer.Warning }); if (result.IsInvalid) return result; // shown at once as PrivaPub now keeps it: resolved, with what was done and its note report.IsResolved = true; report.ResolvedAt = DateTime.UtcNow; report.Resolution = action; report.ResolutionNote = answer.Note; Toast.AddResponseSuccess(Localizer["Report resolved."]); return result; } }