fix(panel): surface the RCON reply for player-access mutations (#56)
The whitelist/ban/kick mutations reported a canned success message and threw away the server's reply, so a refused command still read as done: live, the vanilla server answers "That player does not exist" for a name it has never seen (any player who has not joined yet), while the panel said the player had been whitelisted/banned. api.ts documents these replies as "surfaced verbatim as confirmation"; now they are. The localized string stays as the fallback for a silent server.
This commit is contained in:
4 files changed
+33
-11
No files matched your search
@@ -12,6 +12,7 @@ import {
|
|||||||
MC_NAME,
|
MC_NAME,
|
||||||
PagerFooter,
|
PagerFooter,
|
||||||
PlayerField,
|
PlayerField,
|
||||||
|
rconReply,
|
||||||
SearchBox,
|
SearchBox,
|
||||||
usePagedNames,
|
usePagedNames,
|
||||||
type Feedback,
|
type Feedback,
|
||||||
@@ -68,8 +69,8 @@ export function BansSection({ name }: { name: string }) {
|
|||||||
setFb(null);
|
setFb(null);
|
||||||
setBanning(true);
|
setBanning(true);
|
||||||
try {
|
try {
|
||||||
await api.accessBan(name, "ban", player);
|
const res = await api.accessBan(name, "ban", player);
|
||||||
setFb({ kind: "ok", msg: t("access_banned", { player }) });
|
setFb({ kind: "ok", msg: rconReply(res.output, t("access_banned", { player })) });
|
||||||
setValue("");
|
setValue("");
|
||||||
setTouched(false);
|
setTouched(false);
|
||||||
setArmed(false);
|
setArmed(false);
|
||||||
@@ -86,8 +87,8 @@ export function BansSection({ name }: { name: string }) {
|
|||||||
setFb(null);
|
setFb(null);
|
||||||
setPardoning(p);
|
setPardoning(p);
|
||||||
try {
|
try {
|
||||||
await api.accessBan(name, "pardon", p);
|
const res = await api.accessBan(name, "pardon", p);
|
||||||
setFb({ kind: "ok", msg: t("access_pardoned", { player: p }) });
|
setFb({ kind: "ok", msg: rconReply(res.output, t("access_pardoned", { player: p })) });
|
||||||
reload();
|
reload();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setFb({ kind: "err", msg: humanizeError(e) });
|
setFb({ kind: "err", msg: humanizeError(e) });
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
CollapsibleSection,
|
CollapsibleSection,
|
||||||
FeedbackLine,
|
FeedbackLine,
|
||||||
PagerFooter,
|
PagerFooter,
|
||||||
|
rconReply,
|
||||||
SearchBox,
|
SearchBox,
|
||||||
usePagedNames,
|
usePagedNames,
|
||||||
type Feedback,
|
type Feedback,
|
||||||
@@ -52,11 +53,16 @@ export function OnlineSection({ name, defaultOpen = true }: { name: string; defa
|
|||||||
setFb(null);
|
setFb(null);
|
||||||
setPending({ player: playerName, action });
|
setPending({ player: playerName, action });
|
||||||
try {
|
try {
|
||||||
if (action === "kick") await api.accessKick(name, playerName);
|
const res =
|
||||||
else await api.accessBan(name, "ban", playerName);
|
action === "kick"
|
||||||
|
? await api.accessKick(name, playerName)
|
||||||
|
: await api.accessBan(name, "ban", playerName);
|
||||||
setFb({
|
setFb({
|
||||||
kind: "ok",
|
kind: "ok",
|
||||||
msg: t(action === "kick" ? "access_kicked" : "access_banned", { player: playerName }),
|
msg: rconReply(
|
||||||
|
res.output,
|
||||||
|
t(action === "kick" ? "access_kicked" : "access_banned", { player: playerName }),
|
||||||
|
),
|
||||||
});
|
});
|
||||||
reload(); // the player just left — refresh so the roster reflects it
|
reload(); // the player just left — refresh so the roster reflects it
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
MC_NAME,
|
MC_NAME,
|
||||||
PagerFooter,
|
PagerFooter,
|
||||||
PlayerField,
|
PlayerField,
|
||||||
|
rconReply,
|
||||||
SearchBox,
|
SearchBox,
|
||||||
usePagedNames,
|
usePagedNames,
|
||||||
type Feedback,
|
type Feedback,
|
||||||
@@ -56,8 +57,11 @@ export function WhitelistSection({ name, defaultOpen = false }: { name: string;
|
|||||||
setFb(null);
|
setFb(null);
|
||||||
setAdding(true);
|
setAdding(true);
|
||||||
try {
|
try {
|
||||||
await api.accessWhitelist(name, "add", player);
|
const res = await api.accessWhitelist(name, "add", player);
|
||||||
setFb({ kind: "ok", msg: t("access_whitelist_added", { player }) });
|
setFb({
|
||||||
|
kind: "ok",
|
||||||
|
msg: rconReply(res.output, t("access_whitelist_added", { player })),
|
||||||
|
});
|
||||||
setValue("");
|
setValue("");
|
||||||
setTouched(false);
|
setTouched(false);
|
||||||
reload();
|
reload();
|
||||||
@@ -73,8 +77,11 @@ export function WhitelistSection({ name, defaultOpen = false }: { name: string;
|
|||||||
setFb(null);
|
setFb(null);
|
||||||
setRemoving(p);
|
setRemoving(p);
|
||||||
try {
|
try {
|
||||||
await api.accessWhitelist(name, "remove", p);
|
const res = await api.accessWhitelist(name, "remove", p);
|
||||||
setFb({ kind: "ok", msg: t("access_whitelist_removed", { player: p }) });
|
setFb({
|
||||||
|
kind: "ok",
|
||||||
|
msg: rconReply(res.output, t("access_whitelist_removed", { player: p })),
|
||||||
|
});
|
||||||
reload();
|
reload();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setFb({ kind: "err", msg: humanizeError(e) });
|
setFb({ kind: "err", msg: humanizeError(e) });
|
||||||
|
|||||||
@@ -22,6 +22,14 @@ export const MC_NAME = /^[A-Za-z0-9_]{1,16}$/;
|
|||||||
|
|
||||||
export type Feedback = { kind: "ok" | "err"; msg: string } | null;
|
export type Feedback = { kind: "ok" | "err"; msg: string } | null;
|
||||||
|
|
||||||
|
/** rconReply decides what a mutation reports: the server's own reply, verbatim,
|
||||||
|
* whenever it says anything — the reply is ground truth and names refusals too
|
||||||
|
* ("That player does not exist"), so a canned confirmation must never speak over
|
||||||
|
* it. The localized fallback only covers a silent server. */
|
||||||
|
export function rconReply(output: string | undefined, fallback: string): string {
|
||||||
|
return (output ?? "").trim() || fallback;
|
||||||
|
}
|
||||||
|
|
||||||
/** FeedbackLine is the shared inline result line for a player action: emerald on
|
/** FeedbackLine is the shared inline result line for a player action: emerald on
|
||||||
* success, destructive on failure. There is no toast library — every section
|
* success, destructive on failure. There is no toast library — every section
|
||||||
* reports here, in place, right under the control that fired. */
|
* reports here, in place, right under the control that fired. */
|
||||||
|
|||||||
Reference in new issue
Block a user