Address review on the exp_reports watch list

Save both datastores on the save hook so autosave covers them, the
controller also runs it at shutdown. Watch list names are case
insensitive, matching Factorio and Clusterio users. The new
exp_reports.announce_to_all_instances config, on by default, can limit
the join message to the instance the player joined. The Discord embed
lists the three newest unread reports and counts the rest in the footer,
since Discord rejects embeds with more than 25 fields. Both tables now
sort newest first by default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bbassieandClaude Opus 5.5 committed 2026-10-20 19:01:44 +00:00
1 parent 44ae3a5f0e
commit 38fe2ed62a
7 files changed
+92 -22

No files matched your search

+6 -4
View File
@@ -96,6 +96,7 @@ function ReportsTable(props: { onWatch: (values: WatchListValues) => void }) {
const tableState = useTableQueryState<ReportRecord>({
namespace: "report",
defaultSortKey: "time",
defaultSortOrder: "descend",
pagination: { defaultPageSize: 50 },
});
const playerSearch = useColumnSearch<ReportRecord>(tableState, "player", report => report.playerName, "Search players");
@@ -157,7 +158,6 @@ function ReportsTable(props: { onWatch: (values: WatchListValues) => void }) {
render: (_, report) => new Date(report.createdAtMs).toLocaleString(),
sorter: (a, b) => a.createdAtMs - b.createdAtMs,
sortOrder: tableState.sortOrder("time"),
defaultSortOrder: "descend",
},
{
title: "Status",
@@ -175,7 +175,7 @@ function ReportsTable(props: { onWatch: (values: WatchListValues) => void }) {
</Button>}
{canWatch && <Button
size="small"
disabled={watchList.has(report.playerName)}
disabled={watchList.has(report.playerName.toLowerCase())}
onClick={() => props.onWatch({ playerName: report.playerName, reason: report.reason })}
>
Add to watch list
@@ -209,9 +209,12 @@ function WatchListTable() {
const tableState = useTableQueryState<WatchListRecord>({
namespace: "watch",
defaultSortKey: "time",
defaultSortOrder: "descend",
pagination: { defaultPageSize: 50 },
});
const playerSearch = useColumnSearch<WatchListRecord>(tableState, "player", entry => entry.playerName, "Search players");
const playerSearch = useColumnSearch<WatchListRecord>(
tableState, "player", entry => entry.playerName, "Search players",
);
const reasonSearch = useColumnSearch<WatchListRecord>(tableState, "reason", entry => entry.reason, "Search reasons");
const canRemove = account.hasPermission("exp_reports.watch_list.remove");
@@ -247,7 +250,6 @@ function WatchListTable() {
render: (_, entry) => new Date(entry.createdAtMs).toLocaleString(),
sorter: (a, b) => a.createdAtMs - b.createdAtMs,
sortOrder: tableState.sortOrder("time"),
defaultSortOrder: "descend",
},
...canRemove ? [{
key: "actions",