Unverified Commit 0790f8df authored by Lemon-miaow's avatar Lemon-miaow
Browse files

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.
parent 9c3a1c5b
Loading
Loading
Loading
Loading
+5 −4
Changes for panel/src/components/players/BansSection.tsx: 5 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ import {
  MC_NAME,
  PagerFooter,
  PlayerField,
  rconReply,
  SearchBox,
  usePagedNames,
  type Feedback,
@@ -68,8 +69,8 @@ export function BansSection({ name }: { name: string }) {
    setFb(null);
    setBanning(true);
    try {
      await api.accessBan(name, "ban", player);
      setFb({ kind: "ok", msg: t("access_banned", { player }) });
      const res = await api.accessBan(name, "ban", player);
      setFb({ kind: "ok", msg: rconReply(res.output, t("access_banned", { player })) });
      setValue("");
      setTouched(false);
      setArmed(false);
@@ -86,8 +87,8 @@ export function BansSection({ name }: { name: string }) {
      setFb(null);
      setPardoning(p);
      try {
        await api.accessBan(name, "pardon", p);
        setFb({ kind: "ok", msg: t("access_pardoned", { player: p }) });
        const res = await api.accessBan(name, "pardon", p);
        setFb({ kind: "ok", msg: rconReply(res.output, t("access_pardoned", { player: p })) });
        reload();
      } catch (e) {
        setFb({ kind: "err", msg: humanizeError(e) });
+9 −3
Changes for panel/src/components/players/OnlineSection.tsx: 9 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ import {
  CollapsibleSection,
  FeedbackLine,
  PagerFooter,
  rconReply,
  SearchBox,
  usePagedNames,
  type Feedback,
@@ -52,11 +53,16 @@ export function OnlineSection({ name, defaultOpen = true }: { name: string; defa
      setFb(null);
      setPending({ player: playerName, action });
      try {
        if (action === "kick") await api.accessKick(name, playerName);
        else await api.accessBan(name, "ban", playerName);
        const res =
          action === "kick"
            ? await api.accessKick(name, playerName)
            : await api.accessBan(name, "ban", playerName);
        setFb({
          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
      } catch (e) {
+11 −4
Changes for panel/src/components/players/WhitelistSection.tsx: 11 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ import {
  MC_NAME,
  PagerFooter,
  PlayerField,
  rconReply,
  SearchBox,
  usePagedNames,
  type Feedback,
@@ -56,8 +57,11 @@ export function WhitelistSection({ name, defaultOpen = false }: { name: string;
    setFb(null);
    setAdding(true);
    try {
      await api.accessWhitelist(name, "add", player);
      setFb({ kind: "ok", msg: t("access_whitelist_added", { player }) });
      const res = await api.accessWhitelist(name, "add", player);
      setFb({
        kind: "ok",
        msg: rconReply(res.output, t("access_whitelist_added", { player })),
      });
      setValue("");
      setTouched(false);
      reload();
@@ -73,8 +77,11 @@ export function WhitelistSection({ name, defaultOpen = false }: { name: string;
      setFb(null);
      setRemoving(p);
      try {
        await api.accessWhitelist(name, "remove", p);
        setFb({ kind: "ok", msg: t("access_whitelist_removed", { player: p }) });
        const res = await api.accessWhitelist(name, "remove", p);
        setFb({
          kind: "ok",
          msg: rconReply(res.output, t("access_whitelist_removed", { player: p })),
        });
        reload();
      } catch (e) {
        setFb({ kind: "err", msg: humanizeError(e) });
+8 −0
Changes for panel/src/components/players/shared.tsx: 8 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -22,6 +22,14 @@ export const MC_NAME = /^[A-Za-z0-9_]{1,16}$/;

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
 *  success, destructive on failure. There is no toast library — every section
 *  reports here, in place, right under the control that fired. */