Capture team chat and expose moderation match archives
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
import { pollVisible } from '$ui/poll.js';
|
||||
import { timeFormat } from '$ui/stores/time-zone.js';
|
||||
import Button from './primitives/Button.svelte';
|
||||
import Badge from './primitives/Badge.svelte';
|
||||
import Freshness from './Freshness.svelte';
|
||||
import MatchEvidenceDialog from './moderation/MatchEvidenceDialog.svelte';
|
||||
|
||||
@@ -337,7 +338,10 @@
|
||||
<span class="sr-only">Select evidence</span>
|
||||
</label>
|
||||
{/if}
|
||||
<strong>{item.entry.message.player}</strong
|
||||
<strong
|
||||
>{item.entry.message
|
||||
.player}{#if item.entry.message.channel === 'team'}
|
||||
<Badge>Team chat</Badge>{/if}</strong
|
||||
>{@render chatContext(item.entry.context)}
|
||||
{#if canReadModeration($page.data?.user?.role) && (moderationGame === 't5' || moderationGame === 't6')}<a
|
||||
class="moderation-link"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import RelativeTime from '$ui/components/RelativeTime.svelte';
|
||||
import { untrack } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { canReadModeration } from '$lib/auth.js';
|
||||
import { browser } from '$app/environment';
|
||||
import { contextHref } from '$lib/navigation-context.js';
|
||||
import { timestampISO } from '$lib/time-zone.js';
|
||||
@@ -42,6 +43,7 @@
|
||||
let navigationId = 0;
|
||||
const now = $derived(browser ? $uiNow : Date.now());
|
||||
const filters = $derived(readMatchQuery(matchApiParams($page.url)));
|
||||
const canViewArchive = $derived(canReadModeration($page.data?.user?.role));
|
||||
const apiUrl = $derived.by(() => {
|
||||
const params = matchApiParams($page.url);
|
||||
params.set('limit', String(matchPageSize));
|
||||
@@ -131,6 +133,19 @@
|
||||
</script>
|
||||
|
||||
<section class="history" aria-label="Match history">
|
||||
{#if canViewArchive}<div class="archive-control">
|
||||
<label
|
||||
><input
|
||||
type="checkbox"
|
||||
checked={filters.archive}
|
||||
onchange={(event) =>
|
||||
update({ archive: event.currentTarget.checked ? '1' : '' })}
|
||||
/> Moderation archive</label
|
||||
>
|
||||
{#if filters.archive}<p>
|
||||
Includes round-one, interrupted, and unfinished matches for evidence review.
|
||||
</p>{/if}
|
||||
</div>{/if}
|
||||
{#snippet filterFields(suffix: string)}
|
||||
<div class="filters">
|
||||
{#if server === undefined}<Field forId={`match-game${suffix}`} label="Game">
|
||||
@@ -357,9 +372,12 @@
|
||||
<tr>
|
||||
<td><RelativeTime value={match.startedAt} /></td>
|
||||
<td class="match">
|
||||
{#if match.status === 'completed'}<a
|
||||
{#if match.status === 'completed' || (canViewArchive && filters.archive)}<a
|
||||
class="match-link"
|
||||
href={contextHref(`/matches/${match.id}`, $page.url)}
|
||||
href={contextHref(
|
||||
`/matches/${match.id}${canViewArchive && filters.archive ? '?matchArchive=1' : ''}`,
|
||||
$page.url
|
||||
)}
|
||||
aria-label={`View ${match.map} match ${match.id} from ${$timeFormat.dateTime(match.startedAt)}`}
|
||||
>{match.map} · Match #{match.id}</a
|
||||
>
|
||||
@@ -379,7 +397,8 @@
|
||||
? ` · ${duration}`
|
||||
: ''}</small
|
||||
>
|
||||
{#if notReporting}<small class="mobile-match-context"
|
||||
{#if notReporting || (filters.archive && match.status !== 'completed')}<small
|
||||
class="mobile-match-context"
|
||||
>Last report <RelativeTime
|
||||
value={match.lastReportedAt}
|
||||
missing="Not recorded"
|
||||
@@ -403,7 +422,7 @@
|
||||
class:interrupted={match.status === 'interrupted' ||
|
||||
match.status === 'end-report-missing' ||
|
||||
notReporting}>{status}</span
|
||||
>{#if notReporting}<small
|
||||
>{#if notReporting || (filters.archive && match.status !== 'completed')}<small
|
||||
>Last report <RelativeTime
|
||||
value={match.lastReportedAt}
|
||||
missing="Not recorded"
|
||||
@@ -469,14 +488,16 @@
|
||||
<summary>About match history</summary>
|
||||
<p>
|
||||
Players counts unique players across all reports. A dash means the value is unavailable,
|
||||
not zero. In-progress matches show elapsed time since their recorded start and link to
|
||||
the live server while reports are current. Otherwise, they show Not reporting with the
|
||||
last report time. Completed durations require a recorded start and completed ending. End
|
||||
report missing means a newer game was reported before this game's end report arrived.
|
||||
Missing report counts show gaps in the reporter's sequence and can decrease as delayed
|
||||
reports arrive. Only completed games contribute to the leaderboard. Round shows the
|
||||
final round for completed matches and the highest observed round for unfinished matches.
|
||||
Open a completed match for exact timestamps and starting and ending player counts.
|
||||
not zero. In-progress matches show elapsed time since their recorded start. {#if filters.archive}Archive
|
||||
links open retained match reports and chat.{:else}They link to the live server while
|
||||
reports are current.{/if} Otherwise, they show Not reporting with the last report time.
|
||||
Completed durations require a recorded start and completed ending. End report missing means
|
||||
a newer game was reported before this game's end report arrived. Missing report counts show
|
||||
gaps in the reporter's sequence and can decrease as delayed reports arrive. Only completed
|
||||
games contribute to the leaderboard. Round shows the final round for completed matches and
|
||||
the highest observed round for unfinished matches. Open a {filters.archive
|
||||
? 'recorded'
|
||||
: 'completed'} match for timestamps and participant details.
|
||||
</p>
|
||||
</details>
|
||||
</section>
|
||||
@@ -487,6 +508,26 @@
|
||||
gap: var(--space-4);
|
||||
min-width: 0;
|
||||
}
|
||||
.archive-control {
|
||||
display: grid;
|
||||
gap: var(--space-2);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
.archive-control label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
width: fit-content;
|
||||
cursor: pointer;
|
||||
}
|
||||
.archive-control input {
|
||||
accent-color: var(--accent-primary);
|
||||
}
|
||||
.archive-control p {
|
||||
margin: 0;
|
||||
color: var(--text-secondary);
|
||||
line-height: var(--leading-body);
|
||||
}
|
||||
.filters {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
<li class:highlighted>
|
||||
<div class="author">
|
||||
<strong>{entry.message.player}</strong>
|
||||
{#if entry.message.channel === 'team'}<Badge>Team chat</Badge>{/if}
|
||||
{#if highlighted}<Badge variant="warning">Subject message</Badge>
|
||||
{:else if selected}<Badge>Selected context</Badge>
|
||||
{:else}<span>Surrounding context</span>{/if}
|
||||
|
||||
Reference in New Issue
Block a user