;
diff --git a/src/broadcastChannel/channelMessages.ts b/src/broadcastChannel/channelMessages.ts
index f867671c..caec4871 100644
--- a/src/broadcastChannel/channelMessages.ts
+++ b/src/broadcastChannel/channelMessages.ts
@@ -8,6 +8,7 @@ import {
import { CombinedRankInfo } from "../background/onLabel/InEventGetCombinedRankInfo";
import { OverlayUpdateMatchState } from "../background/store/types";
import { OverlaySettings } from "../common/defaultConfig";
+import { ActionLogV2 } from "../components/action-log-v2/types";
import { DbDraftVote, DbInventoryInfo } from "../types/dbTypes";
import { ClientSceneChange } from "../types/logDecoder";
@@ -66,7 +67,7 @@ export interface StopLogReadingMessage extends ChannelMessageBase {
export interface ActionLogMessage extends ChannelMessageBase {
type: "ACTION_LOG";
- value: string;
+ value: ActionLogV2;
}
export interface LogMessageRecvMessage extends ChannelMessageBase {
diff --git a/src/components/action-log-v2/LineAbility.tsx b/src/components/action-log-v2/LineAbility.tsx
new file mode 100644
index 00000000..5eb37037
--- /dev/null
+++ b/src/components/action-log-v2/LineAbility.tsx
@@ -0,0 +1,19 @@
+import LogAbility from "./LogAbility";
+import LogCard from "./LogCard";
+import { ActionLogLineProps } from "./types";
+
+export default function LineAbility(props: ActionLogLineProps) {
+ const { line } = props;
+
+ if (line.type !== "ABILITY") return <>>;
+
+ return (
+ <>
+
+
+ 's
+ {line.abilityId ? : "ability"}
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineAttack.tsx b/src/components/action-log-v2/LineAttack.tsx
new file mode 100644
index 00000000..af58d326
--- /dev/null
+++ b/src/components/action-log-v2/LineAttack.tsx
@@ -0,0 +1,32 @@
+import getPlayerBySeat from "./getPlayerBySeat";
+import LogCard from "./LogCard";
+import { ActionLogLineProps } from "./types";
+
+export default function LineAttack(props: ActionLogLineProps) {
+ const { line, players } = props;
+
+ if (line.type !== "ATTACK") return <>>;
+
+ return (
+
+ {getPlayerBySeat(line.seat, players)}
+ attacked with
+ {line.grpIds.map((grpId, index) => {
+ const len = line.grpIds.length;
+ return (
+ <>
+
+ {index < len - 2 && len > 2 && <>, >}
+ {index === len - 2 && <> and >}
+ >
+ );
+ })}
+ to
+ {line.targetType === "PLAYER" ? (
+ getPlayerBySeat(line.targetId, players)
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/src/components/action-log-v2/LineCast.tsx b/src/components/action-log-v2/LineCast.tsx
new file mode 100644
index 00000000..7be0bb60
--- /dev/null
+++ b/src/components/action-log-v2/LineCast.tsx
@@ -0,0 +1,20 @@
+import getPlayerBySeat from "./getPlayerBySeat";
+import LogCard from "./LogCard";
+import { ActionLogLineProps } from "./types";
+
+export default function LineCast(props: ActionLogLineProps) {
+ const { line, players } = props;
+
+ if (line.type !== "CAST") return <>>;
+
+ const playerName = getPlayerBySeat(line.seat || 0, players);
+
+ return (
+ <>
+
+ {playerName} cast
+ {line.grpId ? : "a spell"}
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineConceded.tsx b/src/components/action-log-v2/LineConceded.tsx
new file mode 100644
index 00000000..708054dc
--- /dev/null
+++ b/src/components/action-log-v2/LineConceded.tsx
@@ -0,0 +1,18 @@
+import getPlayerBySeat from "./getPlayerBySeat";
+import { ActionLogLineProps } from "./types";
+
+export default function LineConceded(props: ActionLogLineProps) {
+ const { line, players } = props;
+
+ if (line.type !== "CONCEDED") return <>>;
+
+ const playerName = getPlayerBySeat(line.seat || 0, players);
+
+ return (
+ <>
+
+ {playerName} conceded.
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineCountered.tsx b/src/components/action-log-v2/LineCountered.tsx
new file mode 100644
index 00000000..e8528b81
--- /dev/null
+++ b/src/components/action-log-v2/LineCountered.tsx
@@ -0,0 +1,27 @@
+import LogAbility from "./LogAbility";
+import LogCard from "./LogCard";
+import { ActionLogLineProps } from "./types";
+
+export default function LineCountered(props: ActionLogLineProps) {
+ const { line } = props;
+
+ if (line.type !== "COUNTERED") return <>>;
+
+ return (
+ <>
+
+ {line.abilityId ? (
+ <>
+
+ 's
+
+ >
+ ) : (
+
+ )}
+ countered
+
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineDamageDealt.tsx b/src/components/action-log-v2/LineDamageDealt.tsx
new file mode 100644
index 00000000..8bf2884f
--- /dev/null
+++ b/src/components/action-log-v2/LineDamageDealt.tsx
@@ -0,0 +1,23 @@
+import getPlayerBySeat from "./getPlayerBySeat";
+import LogCard from "./LogCard";
+import { ActionLogLineProps } from "./types";
+
+export default function LineDamageDealt(props: ActionLogLineProps) {
+ const { line, players } = props;
+
+ if (line.type !== "DAMAGE_DEALT") return <>>;
+
+ return (
+ <>
+
+
+ dealt {line.amount} damage to
+ {line.targetType === "PLAYER" ? (
+ getPlayerBySeat(line.targetId, players)
+ ) : (
+
+ )}
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineDestroyed.tsx b/src/components/action-log-v2/LineDestroyed.tsx
new file mode 100644
index 00000000..566ea0a2
--- /dev/null
+++ b/src/components/action-log-v2/LineDestroyed.tsx
@@ -0,0 +1,27 @@
+import LogAbility from "./LogAbility";
+import LogCard from "./LogCard";
+import { ActionLogLineProps } from "./types";
+
+export default function LineDestroyed(props: ActionLogLineProps) {
+ const { line } = props;
+
+ if (line.type !== "DESTROYED") return <>>;
+
+ return (
+ <>
+
+ {line.abilityId ? (
+ <>
+
+ 's
+
+ >
+ ) : (
+
+ )}
+ destroyed
+
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineDiscard.tsx b/src/components/action-log-v2/LineDiscard.tsx
new file mode 100644
index 00000000..1a2b8cce
--- /dev/null
+++ b/src/components/action-log-v2/LineDiscard.tsx
@@ -0,0 +1,20 @@
+import getPlayerBySeat from "./getPlayerBySeat";
+import LogCard from "./LogCard";
+import { ActionLogLineProps } from "./types";
+
+export default function LineDiscard(props: ActionLogLineProps) {
+ const { line, players } = props;
+
+ if (line.type !== "DISCARD") return <>>;
+
+ const playerName = getPlayerBySeat(line.seat || 0, players);
+
+ return (
+ <>
+
+ {playerName} discarded
+ {line.grpId ? : "a card"}
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineDraw.tsx b/src/components/action-log-v2/LineDraw.tsx
new file mode 100644
index 00000000..b587a496
--- /dev/null
+++ b/src/components/action-log-v2/LineDraw.tsx
@@ -0,0 +1,20 @@
+import getPlayerBySeat from "./getPlayerBySeat";
+import LogCard from "./LogCard";
+import { ActionLogLineProps } from "./types";
+
+export default function LineDraw(props: ActionLogLineProps) {
+ const { line, players } = props;
+
+ if (line.type !== "DRAW") return <>>;
+
+ const playerName = getPlayerBySeat(line.seat || 0, players);
+
+ return (
+ <>
+
+ {playerName} drew
+ {line.grpId ? : "a card"}
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineExile.tsx b/src/components/action-log-v2/LineExile.tsx
new file mode 100644
index 00000000..a799c4c5
--- /dev/null
+++ b/src/components/action-log-v2/LineExile.tsx
@@ -0,0 +1,27 @@
+import LogAbility from "./LogAbility";
+import LogCard from "./LogCard";
+import { ActionLogLineProps } from "./types";
+
+export default function LineExile(props: ActionLogLineProps) {
+ const { line } = props;
+
+ if (line.type !== "EXILE") return <>>;
+
+ return (
+ <>
+
+ {line.abilityId ? (
+ <>
+
+ 's
+
+ >
+ ) : (
+
+ )}
+ exiled
+
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineModifiedLife.tsx b/src/components/action-log-v2/LineModifiedLife.tsx
new file mode 100644
index 00000000..a293e9eb
--- /dev/null
+++ b/src/components/action-log-v2/LineModifiedLife.tsx
@@ -0,0 +1,19 @@
+import getPlayerBySeat from "./getPlayerBySeat";
+import { ActionLogLineProps } from "./types";
+
+export default function LineModifiedLife(props: ActionLogLineProps) {
+ const { line, players } = props;
+
+ if (line.type !== "MODIFIED_LIFE") return <>>;
+
+ const playerName = getPlayerBySeat(line.seat || 0, players);
+
+ return (
+ <>
+
+ {playerName} {line.delta < 0 ? "lost" : "gained"}
+ {Math.abs(line.delta)} life ({line.total}).
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LinePlay.tsx b/src/components/action-log-v2/LinePlay.tsx
new file mode 100644
index 00000000..5f7855dc
--- /dev/null
+++ b/src/components/action-log-v2/LinePlay.tsx
@@ -0,0 +1,20 @@
+import getPlayerBySeat from "./getPlayerBySeat";
+import LogCard from "./LogCard";
+import { ActionLogLineProps } from "./types";
+
+export default function LinePlay(props: ActionLogLineProps) {
+ const { line, players } = props;
+
+ if (line.type !== "PLAY") return <>>;
+
+ const playerName = getPlayerBySeat(line.seat || 0, players);
+
+ return (
+ <>
+
+ {playerName} played
+ {line.grpId ? : "a land"}
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineScry.tsx b/src/components/action-log-v2/LineScry.tsx
new file mode 100644
index 00000000..188ee771
--- /dev/null
+++ b/src/components/action-log-v2/LineScry.tsx
@@ -0,0 +1,19 @@
+import getPlayerBySeat from "./getPlayerBySeat";
+import { ActionLogLineProps } from "./types";
+
+export default function LineScry(props: ActionLogLineProps) {
+ const { line, players } = props;
+
+ if (line.type !== "SCRY") return <>>;
+
+ const playerName = getPlayerBySeat(line.seat || 0, players);
+
+ return (
+ <>
+
+ {playerName} scry {line.amount}, put {line.top.length}
+ on top and {line.bottom.length} on bottom.
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineTarget.tsx b/src/components/action-log-v2/LineTarget.tsx
new file mode 100644
index 00000000..7b283798
--- /dev/null
+++ b/src/components/action-log-v2/LineTarget.tsx
@@ -0,0 +1,32 @@
+import getPlayerBySeat from "./getPlayerBySeat";
+import LogAbility from "./LogAbility";
+import LogCard from "./LogCard";
+import { ActionLogLineProps } from "./types";
+
+export default function LineTarget(props: ActionLogLineProps) {
+ const { line, players } = props;
+
+ if (line.type !== "TARGET") return <>>;
+
+ return (
+ <>
+
+ {line.abilityId ? (
+ <>
+
+ 's
+
+ >
+ ) : (
+
+ )}
+ targeted
+ {line.targetType === "PLAYER" ? (
+ getPlayerBySeat(line.targetId, players)
+ ) : (
+
+ )}
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineTimedOut.tsx b/src/components/action-log-v2/LineTimedOut.tsx
new file mode 100644
index 00000000..1e71a00d
--- /dev/null
+++ b/src/components/action-log-v2/LineTimedOut.tsx
@@ -0,0 +1,18 @@
+import getPlayerBySeat from "./getPlayerBySeat";
+import { ActionLogLineProps } from "./types";
+
+export default function LineTimedOut(props: ActionLogLineProps) {
+ const { line, players } = props;
+
+ if (line.type !== "TIMED_OUT") return <>>;
+
+ const playerName = getPlayerBySeat(line.seat || 0, players);
+
+ return (
+ <>
+
+ {playerName} timed out.
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineTurnInfo.tsx b/src/components/action-log-v2/LineTurnInfo.tsx
new file mode 100644
index 00000000..dc6ac982
--- /dev/null
+++ b/src/components/action-log-v2/LineTurnInfo.tsx
@@ -0,0 +1,40 @@
+import { toMMSS } from "../../utils/dateTo";
+import getPlayerBySeat from "./getPlayerBySeat";
+import { ActionLogLineProps } from "./types";
+
+const phasesMap: Record = {
+ Phase_None: "None",
+ Phase_Beginning: "Beginning Phase",
+ Phase_Main1: "First Main Phase",
+ Phase_Combat: "Combat",
+ Phase_Main2: "Second Main Phase",
+ Phase_Ending: "End Phase",
+};
+
+export default function LineTurnInfo(props: ActionLogLineProps) {
+ const { line, players, timeStart } = props;
+
+ if (line.type !== "TURN_INFO") return <>>;
+
+ const timeDiff = toMMSS(Math.round((line.timestamp - timeStart) / 1000));
+
+ return (
+ <>
+ {line.subType === "BEGIN" && (
+
+
+ Turn {line.turnNumber}
+ +{timeDiff}
+
+
+ {getPlayerBySeat(line.activePlayer || 0, players)}
+
+
+ )}
+
+ {line.subType === "PHASE" && line.phase && (
+ {phasesMap[line.phase]}
+ )}
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineWon.tsx b/src/components/action-log-v2/LineWon.tsx
new file mode 100644
index 00000000..2c2880ab
--- /dev/null
+++ b/src/components/action-log-v2/LineWon.tsx
@@ -0,0 +1,18 @@
+import getPlayerBySeat from "./getPlayerBySeat";
+import { ActionLogLineProps } from "./types";
+
+export default function LineWin(props: ActionLogLineProps) {
+ const { line, players } = props;
+
+ if (line.type !== "WIN") return <>>;
+
+ const playerName = getPlayerBySeat(line.seat || 0, players);
+
+ return (
+ <>
+
+ {playerName} won!
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineZonePut.tsx b/src/components/action-log-v2/LineZonePut.tsx
new file mode 100644
index 00000000..99527c66
--- /dev/null
+++ b/src/components/action-log-v2/LineZonePut.tsx
@@ -0,0 +1,28 @@
+import LogAbility from "./LogAbility";
+import LogCard from "./LogCard";
+import { ActionLogLineProps } from "./types";
+
+export default function LineZonePut(props: ActionLogLineProps) {
+ const { line } = props;
+
+ if (line.type !== "ZONE_PUT") return <>>;
+
+ return (
+ <>
+
+ {line.abilityId ? (
+ <>
+
+ 's
+
+ >
+ ) : (
+
+ )}
+ put
+
+ in {line.zone}
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LineZoneReturn.tsx b/src/components/action-log-v2/LineZoneReturn.tsx
new file mode 100644
index 00000000..b094165b
--- /dev/null
+++ b/src/components/action-log-v2/LineZoneReturn.tsx
@@ -0,0 +1,28 @@
+import LogAbility from "./LogAbility";
+import LogCard from "./LogCard";
+import { ActionLogLineProps } from "./types";
+
+export default function LineZoneReturn(props: ActionLogLineProps) {
+ const { line } = props;
+
+ if (line.type !== "ZONE_RETURN") return <>>;
+
+ return (
+ <>
+
+ {line.abilityId ? (
+ <>
+
+ 's
+
+ >
+ ) : (
+
+ )}
+ returned
+
+ to {line.zone}
+
+ >
+ );
+}
diff --git a/src/components/action-log-v2/LogAbility.tsx b/src/components/action-log-v2/LogAbility.tsx
new file mode 100644
index 00000000..fda21f90
--- /dev/null
+++ b/src/components/action-log-v2/LogAbility.tsx
@@ -0,0 +1,16 @@
+import { database } from "mtgatool-shared";
+
+interface LogAbilityProps {
+ abId: number;
+}
+
+export default function LogAbility(props: LogAbilityProps): JSX.Element {
+ const { abId } = props;
+ const desc = database.ability(abId);
+
+ return (
+
+ ability
+
+ );
+}
diff --git a/src/components/action-log-v2/LogCard.tsx b/src/components/action-log-v2/LogCard.tsx
new file mode 100644
index 00000000..ad11a261
--- /dev/null
+++ b/src/components/action-log-v2/LogCard.tsx
@@ -0,0 +1,21 @@
+import { database } from "mtgatool-shared";
+
+import useHoverCard from "../../hooks/useHoverCard";
+
+interface LogCardProps {
+ grpId: number;
+}
+
+export default function LogCard(props: LogCardProps): JSX.Element {
+ const { grpId } = props;
+ const cardObj = database.card(grpId);
+ const cardName = cardObj?.Name;
+
+ const [hoverIn, hoverOut] = useHoverCard(grpId);
+
+ return (
+
+ {cardName}
+
+ );
+}
diff --git a/src/components/action-log-v2/getPlayerBySeat.ts b/src/components/action-log-v2/getPlayerBySeat.ts
new file mode 100644
index 00000000..1cfc7e32
--- /dev/null
+++ b/src/components/action-log-v2/getPlayerBySeat.ts
@@ -0,0 +1,11 @@
+import getPlayerNameWithoutSuffix from "../../utils/getPlayerNameWithoutSuffix";
+import { ActionLogPlayer } from "./types";
+
+export default function getPlayerBySeat(
+ seat: number,
+ players: ActionLogPlayer[]
+): string {
+ const playerName =
+ players.filter((player) => player.seat === seat)[0]?.name || "";
+ return getPlayerNameWithoutSuffix(playerName);
+}
diff --git a/src/components/action-log-v2/index.tsx b/src/components/action-log-v2/index.tsx
new file mode 100644
index 00000000..63bc9274
--- /dev/null
+++ b/src/components/action-log-v2/index.tsx
@@ -0,0 +1,132 @@
+import LineAbility from "./LineAbility";
+import LineAttack from "./LineAttack";
+import LineCast from "./LineCast";
+import LineConceded from "./LineConceded";
+import LineCountered from "./LineCountered";
+import LineDamageDealt from "./LineDamageDealt";
+import LineDestroyed from "./LineDestroyed";
+import LineDiscard from "./LineDiscard";
+import LineDraw from "./LineDraw";
+import LineExile from "./LineExile";
+import LineModifiedLife from "./LineModifiedLife";
+import LinePlay from "./LinePlay";
+import LineScry from "./LineScry";
+import LineTarget from "./LineTarget";
+import LineTimedOut from "./LineTimedOut";
+import LineTurnInfo from "./LineTurnInfo";
+import LineWin from "./LineWon";
+import LineZonePut from "./LineZonePut";
+import LineZoneReturn from "./LineZoneReturn";
+import { ActionLogLineProps, ActionLogLineType, ActionLogV2 } from "./types";
+
+const DefaultLineComponent = (_props: ActionLogLineProps): JSX.Element => {
+ return <>>;
+};
+
+function getLineComponent(type: ActionLogLineType) {
+ let lineComponent = DefaultLineComponent;
+
+ switch (type) {
+ case "WIN":
+ lineComponent = LineWin;
+ break;
+ case "CONCEDED":
+ lineComponent = LineConceded;
+ break;
+ case "TIMED_OUT":
+ lineComponent = LineTimedOut;
+ break;
+ case "DRAW":
+ lineComponent = LineDraw;
+ break;
+ case "SCRY":
+ lineComponent = LineScry;
+ break;
+ case "CAST":
+ lineComponent = LineCast;
+ break;
+ case "PLAY":
+ lineComponent = LinePlay;
+ break;
+ case "DISCARD":
+ lineComponent = LineDiscard;
+ break;
+ case "ZONE_PUT":
+ lineComponent = LineZonePut;
+ break;
+ case "ZONE_RETURN":
+ lineComponent = LineZoneReturn;
+ break;
+ case "COUNTERED":
+ lineComponent = LineCountered;
+ break;
+ case "EXILE":
+ lineComponent = LineExile;
+ break;
+ case "DESTROYED":
+ lineComponent = LineDestroyed;
+ break;
+ case "TARGET":
+ lineComponent = LineTarget;
+ break;
+ case "TURN_INFO":
+ lineComponent = LineTurnInfo;
+ break;
+ case "ABILITY":
+ lineComponent = LineAbility;
+ break;
+ case "MODIFIED_LIFE":
+ lineComponent = LineModifiedLife;
+ break;
+ case "DAMAGE_DEALT":
+ lineComponent = LineDamageDealt;
+ break;
+ case "ATTACK":
+ lineComponent = LineAttack;
+ break;
+ default:
+ break;
+ }
+
+ return lineComponent;
+}
+
+interface ActionLogProps {
+ actionLog: ActionLogV2;
+}
+
+export default function ActionLog(props: ActionLogProps): JSX.Element {
+ const { actionLog } = props;
+
+ // const logLength = actionLog.lines.length;
+
+ return (
+
+ {actionLog.lines.map((line, index) => {
+ const LineComponent = getLineComponent(line.type);
+
+ // const nextLine = actionLog.lines[i + 1];
+
+ // if (
+ // line.type === "TURN_INFO" &&
+ // line.subType !== "BEGIN" &&
+ // logLength - 1 !== i &&
+ // nextLine.type === "TURN_INFO" &&
+ // nextLine.subType === line.subType
+ // ) {
+ // return <>>;
+ // }
+
+ return (
+
+ );
+ })}
+
+ );
+}
diff --git a/src/components/action-log-v2/types.ts b/src/components/action-log-v2/types.ts
new file mode 100644
index 00000000..33ae3c5e
--- /dev/null
+++ b/src/components/action-log-v2/types.ts
@@ -0,0 +1,203 @@
+import { TurnInfo } from "mtgatool-shared/dist/types/greTypes";
+
+export type ActionLogLineType =
+ | "START"
+ | "END"
+ | "CONCEDED"
+ | "TIMED_OUT"
+ | "WIN"
+ | "TURN_INFO"
+ | "DRAW"
+ | "CAST"
+ | "DISCARD"
+ | "PLAY"
+ | "ZONE_PUT"
+ | "ZONE_RETURN"
+ | "EXILE"
+ | "COUNTERED"
+ | "DESTROYED"
+ | "ABILITY"
+ | "ATTACK"
+ | "DAMAGE_DEALT"
+ | "MODIFIED_LIFE"
+ | "TARGET"
+ | "SCRY"
+ | "REVEAL";
+
+export interface ActionLogLineBase {
+ timestamp: number;
+ seat: number;
+ type: ActionLogLineType;
+}
+
+export interface ActionLogLineStart extends ActionLogLineBase {
+ type: "START";
+}
+
+export interface ActionLogLineEnd extends ActionLogLineBase {
+ type: "END";
+}
+
+export interface ActionLogLineConceded extends ActionLogLineBase {
+ type: "CONCEDED";
+}
+
+export interface ActionLogLineTimedOut extends ActionLogLineBase {
+ type: "TIMED_OUT";
+}
+
+export interface ActionLogLineWin extends ActionLogLineBase {
+ type: "WIN";
+}
+
+export interface ActionLogLineTurnInfo extends ActionLogLineBase, TurnInfo {
+ type: "TURN_INFO";
+ subType: "BEGIN" | "STEP" | "PHASE" | "PRIORITY";
+}
+
+export interface ActionLogLineDraw extends ActionLogLineBase {
+ type: "DRAW";
+ grpId: number | null;
+}
+
+export interface ActionLogLineCast extends ActionLogLineBase {
+ type: "CAST";
+ grpId: number;
+}
+
+export interface ActionLogLineDiscard extends ActionLogLineBase {
+ type: "DISCARD";
+ grpId: number;
+}
+
+export interface ActionLogLinePlay extends ActionLogLineBase {
+ type: "PLAY";
+ grpId: number;
+}
+
+export interface ActionLogLineZonePut extends ActionLogLineBase {
+ type: "ZONE_PUT";
+ sourceGrpId: number;
+ abilityId: number | undefined;
+ grpId: number;
+ zone: string;
+}
+
+export interface ActionLogLineZoneReturn extends ActionLogLineBase {
+ type: "ZONE_RETURN";
+ sourceGrpId: number;
+ abilityId: number | undefined;
+ grpId: number;
+ zone: string;
+}
+
+export interface ActionLogLineExile extends ActionLogLineBase {
+ type: "EXILE";
+ sourceGrpId: number;
+ abilityId: number | undefined;
+ grpId: number;
+}
+
+export interface ActionLogLineCountered extends ActionLogLineBase {
+ type: "COUNTERED";
+ sourceGrpId: number;
+ abilityId: number | undefined;
+ grpId: number;
+}
+
+export interface ActionLogLineDestroyed extends ActionLogLineBase {
+ type: "DESTROYED";
+ sourceGrpId: number;
+ abilityId: number | undefined;
+ grpId: number;
+}
+
+export interface ActionLogLineAbility extends ActionLogLineBase {
+ type: "ABILITY";
+ sourceGrpId: number;
+ abilityId: number;
+}
+
+export interface ActionLogLineAttack extends ActionLogLineBase {
+ type: "ATTACK";
+ grpIds: number[];
+ targetType: string;
+ targetId: number;
+}
+
+export interface ActionLogLineDamageDealt extends ActionLogLineBase {
+ type: "DAMAGE_DEALT";
+ sourceGrpId: number;
+ amount: number;
+ targetType: string;
+ targetId: number;
+}
+
+export interface ActionLogLineModifiedLife extends ActionLogLineBase {
+ type: "MODIFIED_LIFE";
+ delta: number;
+ total: number;
+}
+
+export interface ActionLogLineTarget extends ActionLogLineBase {
+ type: "TARGET";
+ sourceGrpId: number;
+ abilityId: number | undefined;
+ targetType: string;
+ targetId: number;
+}
+
+export interface ActionLogLineScry extends ActionLogLineBase {
+ type: "SCRY";
+ amount: number;
+ top: number[];
+ bottom: number[];
+}
+
+export interface ActionLogLineReveal extends ActionLogLineBase {
+ type: "REVEAL";
+ grpId: number;
+ zone: string;
+}
+
+export type ActionLogLine =
+ | ActionLogLineStart
+ | ActionLogLineEnd
+ | ActionLogLineConceded
+ | ActionLogLineTimedOut
+ | ActionLogLineWin
+ | ActionLogLineTurnInfo
+ | ActionLogLineDraw
+ | ActionLogLineCast
+ | ActionLogLineDiscard
+ | ActionLogLinePlay
+ | ActionLogLineZonePut
+ | ActionLogLineZoneReturn
+ | ActionLogLineExile
+ | ActionLogLineCountered
+ | ActionLogLineDestroyed
+ | ActionLogLineAbility
+ | ActionLogLineAttack
+ | ActionLogLineDamageDealt
+ | ActionLogLineModifiedLife
+ | ActionLogLineTarget
+ | ActionLogLineScry
+ | ActionLogLineReveal;
+
+export interface ActionLogPlayer {
+ name: string;
+ seat: number;
+ userId: string;
+}
+
+export type ActionLogV2 = {
+ version: number;
+ players: ActionLogPlayer[];
+ lines: ActionLogLine[];
+};
+
+export interface ActionLogLineProps {
+ line: ActionLogLine;
+ players: ActionLogPlayer[];
+ timeStart: number;
+}
diff --git a/src/components/views/history/MatchView.tsx b/src/components/views/history/MatchView.tsx
index 1ed5931d..b797ba5e 100644
--- a/src/components/views/history/MatchView.tsx
+++ b/src/components/views/history/MatchView.tsx
@@ -27,6 +27,7 @@ import { getCardArtCrop, getCardImage } from "../../../utils/getCardArtCrop";
import getEventPrettyName from "../../../utils/getEventPrettyName";
import getPlayerNameWithoutSuffix from "../../../utils/getPlayerNameWithoutSuffix";
import isLimitedEventId from "../../../utils/isLimitedEventId";
+import ActionLogV2 from "../../action-log-v2";
import ActionLog from "../../ActionLog";
import CardList from "../../CardList";
import DeckColorsBar from "../../DeckColorsBar";
@@ -166,9 +167,9 @@ export default function MatchView(): JSX.Element {
: undefined;
const logExists = matchData ? !!matchData.internalMatch.actionLog : undefined;
- const actionLogDataString = matchData
+ const actionLogData = matchData
? matchData.internalMatch.actionLog ?? ""
- : undefined;
+ : "";
const goBack = (): void => {
history.push("/history");
@@ -435,7 +436,7 @@ export default function MatchView(): JSX.Element {
{view == VIEW_LOG ? (
-
+ typeof actionLogData === "string" ? (
+
+ ) : (
+
+ )
) : arrayGameStats[gameSeen] ? (
) : (
diff --git a/src/info.json b/src/info.json
index 7e62c393..451cf8c3 100644
--- a/src/info.json
+++ b/src/info.json
@@ -1 +1 @@
-{"version":"6.5.3","branch":"dev","timestamp":1704328553701}
\ No newline at end of file
+{"version":"6.5.3","branch":"action-logs-v2","timestamp":1704578558609}
\ No newline at end of file
diff --git a/src/overlay/index.tsx b/src/overlay/index.tsx
index cd6b701b..c46c1f09 100644
--- a/src/overlay/index.tsx
+++ b/src/overlay/index.tsx
@@ -13,7 +13,8 @@ import { ChannelMessage } from "../broadcastChannel/channelMessages";
import postChannelMessage from "../broadcastChannel/postChannelMessage";
import { OverlaySettings, Settings } from "../common/defaultConfig";
import { overlayTitleToId } from "../common/maps";
-import ActionLog from "../components/ActionLog";
+import ActionLog from "../components/action-log-v2";
+import { ActionLogV2 } from "../components/action-log-v2/types";
import OverlayDeckList from "../components/OverlayDeckList";
import TopBar from "../components/TopBar";
import useDebounce from "../hooks/useDebounce";
@@ -36,7 +37,7 @@ export default function Overlay() {
const [matchState, setMatchState] = useState();
const [draftState, setDraftState] = useState();
const [draftVotes, setDraftVotes] = useState>({});
- const [actionLog, setActionLog] = useState("");
+ const [actionLog, setActionLog] = useState(null);
const [odds, setOdds] = useState();
const heightDivAdjustRef = useRef(null);
@@ -201,8 +202,8 @@ export default function Overlay() {
}}
/>
)}
- {settings && settings.mode === OVERLAY_LOG && (
-
+ {settings && settings.mode === OVERLAY_LOG && actionLog && (
+
)}
{settings &&
!!settings.clock &&
diff --git a/src/scss/actionLog.scss b/src/scss/actionLog.scss
index b943554d..c0a555cc 100644
--- a/src/scss/actionLog.scss
+++ b/src/scss/actionLog.scss
@@ -73,3 +73,81 @@
cursor: pointer;
margin-right: 3px;
}
+
+.action-log-v2 {
+ font-size: 14px;
+ color: var(--color-text);
+ font-style: italic;
+
+ .log-line {
+ margin-top: 8px;
+ display: flex;
+ font-family: var(--main-font-name-it);
+ flex-wrap: wrap;
+
+ &.seat-1 {
+ &::before {
+ margin-right: 8px;
+ content: "";
+ width: 5px;
+ background-color: var(--color-g);
+ border-radius: 0 3px 3px 0;
+ }
+ }
+
+ &.seat-2 {
+ &::before {
+ margin-right: 8px;
+ content: "";
+ width: 5px;
+ background-color: var(--color-r);
+ border-radius: 0 3px 3px 0;
+ }
+ }
+ }
+
+ .result {
+ font-weight: 600;
+ margin-left: 0px;
+ color: var(--color-text-dark);
+ }
+
+ .winner {
+ font-weight: 600;
+ margin-left: 0px;
+ margin-top: 16px;
+ color: var(--color-text-dark);
+ }
+
+ .card, .ability {
+ color: var(--color-link);
+ text-decoration: underline;
+ }
+
+ .turn-info {
+ margin-top: 12px;
+ display: flex;
+ justify-content: space-between;
+ font-family: var(--sub-font-name-it);
+
+ .time {
+ color: var(--color-text-dark);
+ margin-left: 8px;
+ }
+
+ .name {
+ color: var(--color-text-dark);
+ }
+
+ &.begin {
+ margin-top: 16px;
+ padding-bottom: 4px;
+ border-bottom: var(--color-link) 1px solid;
+ }
+
+ &.phase {
+ font-weight: 600;
+ color: var(--color-text-dark);
+ }
+ }
+}