Skip to content

Commit 5bbcc8f

Browse files
authored
feat: analytics (#1078)
1 parent b87340f commit 5bbcc8f

8 files changed

Lines changed: 82 additions & 11 deletions

File tree

kaggle_environments/envs/open_spiel_env/games/chess/visualizer/v2/src/components/Annotation.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useTransition } from '../hooks/useReducedMotion';
44
import useGameStore from '../stores/useGameStore';
55
import usePreferences from '../stores/usePreferences';
66
import styles from './Annotation.module.css';
7+
import { trackEvent } from '../utils/analytics';
78

89
type Notation = { term: string; title: string; text: string };
910

@@ -103,6 +104,11 @@ export default function Annotation() {
103104
const moveNumber = game.moveNumber();
104105
const notation = showAnnotations && player ? resolveAnnotation(player, moveNumber) : null;
105106

107+
if (notation) {
108+
const trackingTerm = notation.term.replace(/[^a-z\s]/g, '').replace(/\s/g, '-');
109+
trackEvent(`annotation-${trackingTerm}`);
110+
}
111+
106112
return (
107113
<div className={styles.notationSlot} aria-live="polite" ref={inertRef}>
108114
<AnimatePresence mode="wait">

kaggle_environments/envs/open_spiel_env/games/chess/visualizer/v2/src/components/BoardControls.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { useTransition } from '../hooks/useReducedMotion';
77
import popoverStyles from './WithPopover.module.css';
88
import { WithPopover } from './WithPopover.tsx';
99
import { FeatureToggles } from './FeatureToggles';
10+
import { trackEvent } from '../utils/analytics.ts';
1011
import styles from './BoardControls.module.css';
1112

1213
export default function BoardControls() {
@@ -26,7 +27,14 @@ export default function BoardControls() {
2627

2728
return (
2829
<div className={styles.boardControls} ref={inertRef}>
29-
<WithPopover id="info" icon="info" label="Game info">
30+
<WithPopover
31+
id="info"
32+
icon="info"
33+
label="Game info"
34+
onChange={(open) => {
35+
trackEvent(`info-${open ? 'open' : 'closed'}`);
36+
}}
37+
>
3038
<p>
3139
Chess is a strategic board game for two players. The object of the game is simple: trap the opponent's king.
3240
The game has been played for over a thousand years.
@@ -36,7 +44,10 @@ export default function BoardControls() {
3644
<input
3745
type="checkbox"
3846
checked={soundEnabled}
39-
onChange={() => toggle('soundEnabled')}
47+
onChange={() => {
48+
toggle('soundEnabled');
49+
trackEvent(`sound-${soundEnabled ? 'off' : 'on'}`);
50+
}}
4051
className={popoverStyles.trigger}
4152
aria-label="Sound"
4253
/>
@@ -52,7 +63,14 @@ export default function BoardControls() {
5263
<use xlinkHref={`${svgSymbolPath}#sound-off`} />
5364
</svg>
5465
</label>
55-
<WithPopover id="settings" icon="settings" label="Settings">
66+
<WithPopover
67+
id="settings"
68+
icon="settings"
69+
label="Settings"
70+
onChange={(open) => {
71+
trackEvent(`settings-${open ? 'open' : 'closed'}`);
72+
}}
73+
>
5674
<FeatureToggles />
5775
</WithPopover>
5876

kaggle_environments/envs/open_spiel_env/games/chess/visualizer/v2/src/components/FeatureToggles.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import usePreferences from '../stores/usePreferences';
22
import styles from './FeatureToggles.module.css';
3+
import { trackEvent } from '../utils/analytics';
34

45
export function FeatureToggles() {
56
const { toggle, showHeroAnimations, showAnnotations, showHighlights, reducedMotion } = usePreferences();
@@ -14,7 +15,10 @@ export function FeatureToggles() {
1415
type="checkbox"
1516
className={styles.switch}
1617
checked={showHeroAnimations}
17-
onChange={() => toggle('showHeroAnimations')}
18+
onChange={() => {
19+
toggle('showHeroAnimations');
20+
trackEvent(`settings-pop-up-animations-${showHeroAnimations ? 'off' : 'on'}`);
21+
}}
1822
/>
1923
</label>
2024
</li>
@@ -25,7 +29,10 @@ export function FeatureToggles() {
2529
type="checkbox"
2630
className={styles.switch}
2731
checked={reducedMotion}
28-
onChange={() => toggle('reducedMotion')}
32+
onChange={() => {
33+
toggle('reducedMotion');
34+
trackEvent(`settings-reduce-motion-${reducedMotion ? 'off' : 'on'}`);
35+
}}
2936
/>
3037
</label>
3138
</li>
@@ -36,7 +43,10 @@ export function FeatureToggles() {
3643
type="checkbox"
3744
className={styles.switch}
3845
checked={showHighlights}
39-
onChange={() => toggle('showHighlights')}
46+
onChange={() => {
47+
toggle('showHighlights');
48+
trackEvent(`settings-highlights-${showHighlights ? 'off' : 'on'}`);
49+
}}
4050
/>
4151
</label>
4252
</li>
@@ -47,7 +57,10 @@ export function FeatureToggles() {
4757
type="checkbox"
4858
className={styles.switch}
4959
checked={showAnnotations}
50-
onChange={() => toggle('showAnnotations')}
60+
onChange={() => {
61+
toggle('showAnnotations');
62+
trackEvent(`settings-board-annotations-${showAnnotations ? 'off' : 'on'}`);
63+
}}
5164
/>
5265
</label>
5366
</li>

kaggle_environments/envs/open_spiel_env/games/chess/visualizer/v2/src/components/GameOver.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useEffect, useRef } from 'react';
22
import { Ribbon } from './Ribbon';
33
import useGameStore from '../stores/useGameStore';
4+
import { trackEvent } from '../utils/analytics';
45
import styles from './GameOver.module.css';
56

67
function formatDuration(totalSeconds: number): string {
@@ -100,6 +101,8 @@ export default function GameOver() {
100101
},
101102
];
102103

104+
trackEvent('game-over');
105+
103106
return (
104107
<dialog ref={dialogRef} className={styles.modal} aria-label="Game over" tabIndex={-1}>
105108
<div className="ribbon">

kaggle_environments/envs/open_spiel_env/games/chess/visualizer/v2/src/components/HeroAnimation.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { HeroTypes, detectHeroType } from '../utils/heroTypes';
99
import { RivePopover } from './RivePopover';
1010
import useGameStore from '../stores/useGameStore';
1111
import usePreferences from '../stores/usePreferences';
12+
import { trackEvent } from '../utils/analytics';
1213

1314
interface Hero {
1415
src: string;
@@ -40,29 +41,35 @@ export default function HeroAnimation() {
4041
const player = color === 'black' ? 'Black' : 'White';
4142
const opponent = color === 'black' ? 'White' : 'Black';
4243

43-
let src, text;
44+
let src, text, event;
4445
switch (heroType) {
4546
case HeroTypes.CHECKMATE:
4647
src = checkmateRiv;
4748
text = 'Checkmate';
49+
event = 'checkmate';
4850
break;
4951
case HeroTypes.QUEEN_LOSS:
5052
src = queenLossRiv;
5153
text = `${opponent} Loses their queen`;
54+
event = 'queen-loss';
5255
// Invert color when on queen loss.
56+
// TODO: This should be switched in the Rive file instead.
5357
color = color === 'white' ? 'black' : 'white';
5458
break;
5559
case HeroTypes.PROMOTION:
5660
src = promotionRiv;
5761
text = `${player} Queens`;
62+
event = 'promotion';
5863
break;
5964
case HeroTypes.CASTLING:
6065
src = castlingRiv;
6166
text = `${player} Castles`;
67+
event = 'castling';
6268
break;
6369
case HeroTypes.FIRST_CAPTURE:
6470
src = firstCaptureRiv;
6571
text = `${player} Captures First`;
72+
event = 'first-capture';
6673
break;
6774
default:
6875
return;
@@ -71,13 +78,14 @@ export default function HeroAnimation() {
7178
// Let the board play out before showing the Rive animation.
7279
const timeout = setTimeout(() => {
7380
setHero({ src, text, color, step });
81+
if (!options.replay.steps.at(options.step)?.winner) trackEvent(`pop-up-animation-${event}`);
7482
}, 600);
7583

7684
return () => {
7785
clearTimeout(timeout);
7886
setHero(null);
7987
};
80-
}, [game, showHeroAnimations]);
88+
}, [game, showHeroAnimations, options]);
8189

8290
const isVisible = !!hero && !options.replay.steps.at(options.step)?.winner;
8391

kaggle_environments/envs/open_spiel_env/games/chess/visualizer/v2/src/components/VersusBanner.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
import { AnimatePresence, motion } from 'motion/react';
22
import { Ribbon } from './Ribbon';
33
import useGameStore from '../stores/useGameStore';
4+
import usePreloader from '../stores/usePreloader';
45
import { useTransition } from '../hooks/useReducedMotion';
6+
import { trackEvent } from '../utils/analytics';
57
import styles from './VersusBanner.module.css';
68

79
export default function VersusBanner() {
810
const game = useGameStore((state) => state.game);
11+
const loaded = usePreloader((s) => s.pixiReady && s.assetsReady);
912
const enterTransition = useTransition({ duration: 1.3, ease: [0.22, 1.15, 0.36, 1] });
1013
const exitTransition = useTransition({ duration: 0.3 });
1114
const whiteName = game.getHeaders()['w'];
1215
const blackName = game.getHeaders()['b'];
1316

17+
if (loaded && game.moveNumber() === 1 && game.turn() === 'w') trackEvent('versus-banner');
18+
1419
return (
1520
<AnimatePresence>
1621
{game.moveNumber() === 1 && game.turn() === 'w' && (

kaggle_environments/envs/open_spiel_env/games/chess/visualizer/v2/src/components/WithPopover.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,23 @@ interface Props {
1010
icon: string;
1111
id: string;
1212
label: string;
13+
onChange?: (open: boolean) => void;
1314
}
1415

15-
export function WithPopover({ children, icon, id, label }: Props) {
16+
export function WithPopover({ children, icon, id, label, onChange }: Props) {
1617
const [open, setOpen] = useState(false);
18+
const onChangeRef = useRef(open);
1719
const transition = useTransition({ duration: 0.2, ease: 'easeOut' });
1820
const iconPath = `${svgSymbolPath}#${icon}`;
1921
const triggerRef = useRef<HTMLButtonElement>(null);
2022
const panelRef = useRef<HTMLDivElement>(null);
2123

2224
useEffect(() => {
25+
if (onChange && onChangeRef.current !== open) {
26+
onChangeRef.current = open;
27+
onChange(open);
28+
}
29+
2330
if (!open) return;
2431

2532
// Wait a frame, then focus the popover.
@@ -47,7 +54,7 @@ export function WithPopover({ children, icon, id, label }: Props) {
4754
document.removeEventListener('mousedown', handleMouseDown);
4855
document.removeEventListener('keydown', handleKeyDown);
4956
};
50-
}, [open]);
57+
}, [open, onChange]);
5158

5259
return (
5360
<div className={styles.wrapper}>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { postAnalyticsEvent } from '@kaggle-environments/core';
2+
3+
export const GAME_NAME = 'open_spiel_chess';
4+
5+
export function trackEvent(event: string) {
6+
if (import.meta.env.DEV && import.meta.env.VITE_LOG_ANALYTICS) {
7+
console.log(`Track Event: ${event}`);
8+
}
9+
10+
postAnalyticsEvent({ game: GAME_NAME, action: event });
11+
}

0 commit comments

Comments
 (0)