mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 17:40:46 -04:00
more (modular local offline not selling data) analytics!
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import useBarcodeGameState from '../../barcodeGames/useBarcodeGameState.js';
|
||||
import CardFrame from '../CardFrame/index.jsx';
|
||||
import { trackAnalyticsEvent } from '../../analytics/index.js';
|
||||
|
||||
function clampRgbChannel(value) {
|
||||
if (!Number.isFinite(value)) return null;
|
||||
@@ -189,7 +190,7 @@ function Leaderboard({ players, ownPlayer }) {
|
||||
}
|
||||
|
||||
export default function BarcodeGamesPanel() {
|
||||
const { state, voteForGame } = useBarcodeGameState();
|
||||
const { state, connectionState, voteForGame } = useBarcodeGameState();
|
||||
const [pendingGameId, setPendingGameId] = useState(null);
|
||||
const activeGame = state.activeGame;
|
||||
const display = activeGame?.display || {};
|
||||
@@ -204,10 +205,41 @@ export default function BarcodeGamesPanel() {
|
||||
const participants = Array.isArray(state.participants) ? state.participants : [];
|
||||
const activeTheme = getGameTheme(activeGame?.themeColor);
|
||||
|
||||
useEffect(() => {
|
||||
if (!connectionState.stale || !connectionState.lastReceivedAt) return;
|
||||
/*
|
||||
Stale barcode-game state is worth tracking because it points to a real
|
||||
interaction problem: users can be looking at old game choices or scores
|
||||
even though the rest of the page appears loaded.
|
||||
*/
|
||||
trackAnalyticsEvent('barcode_game_state_stale', {
|
||||
connected: connectionState.connected,
|
||||
phase: state.phase || 'unknown',
|
||||
});
|
||||
}, [connectionState.connected, connectionState.lastReceivedAt, connectionState.stale, state.phase]);
|
||||
|
||||
const handleVote = async (gameId) => {
|
||||
setPendingGameId(gameId);
|
||||
trackAnalyticsEvent('barcode_game_vote', {
|
||||
gameId,
|
||||
phase: state.phase || 'unknown',
|
||||
status: 'started',
|
||||
});
|
||||
try {
|
||||
await voteForGame(gameId);
|
||||
trackAnalyticsEvent('barcode_game_vote', {
|
||||
gameId,
|
||||
phase: state.phase || 'unknown',
|
||||
status: 'accepted',
|
||||
});
|
||||
} catch (error) {
|
||||
trackAnalyticsEvent('barcode_game_vote', {
|
||||
gameId,
|
||||
phase: state.phase || 'unknown',
|
||||
status: 'failed',
|
||||
reason: error?.message || 'unknown',
|
||||
});
|
||||
throw error;
|
||||
} finally {
|
||||
setPendingGameId(null);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSessionActions } from '../../context/SessionContext.jsx';
|
||||
import { useSettingsNamespace } from '../../settings/index.js';
|
||||
import { trackAnalyticsEvent } from '../../analytics/index.js';
|
||||
|
||||
export default function NicknameForm({ compact = false }) {
|
||||
const { setNickname } = useSessionActions();
|
||||
@@ -26,6 +27,14 @@ export default function NicknameForm({ compact = false }) {
|
||||
// the driver UI so chat/user-list labels stay consistent across routes.
|
||||
await setNickname(trimmed);
|
||||
save({ nickname: trimmed });
|
||||
/*
|
||||
The event records that a nickname was saved, while the shared analytics
|
||||
session reporter owns the actual nickname field. Keeping that split
|
||||
prevents every form call site from needing to know privacy/config rules.
|
||||
*/
|
||||
trackAnalyticsEvent('nickname_set', {
|
||||
length: trimmed.length,
|
||||
});
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
} finally {
|
||||
|
||||
@@ -29,9 +29,10 @@ import ButtonBoxPanel from '../ButtonBoxPanel/index.jsx';
|
||||
import BarcodeGamesPanel from '../BarcodeGamesPanel/index.jsx';
|
||||
import OverseerPreferencePanel from '../OverseerPreferencePanel/index.jsx';
|
||||
import CardFrame from '../CardFrame/index.jsx';
|
||||
import { useLayoutEffect, useRef, useState } from 'react';
|
||||
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { useManualDockAssist } from '../../features/manualDockAssist/useManualDockAssist.js';
|
||||
import { themeGapClass, themeStackClass } from '../../themeFlags.js';
|
||||
import { trackAnalyticsEvent } from '../../analytics/index.js';
|
||||
|
||||
const CHAT_DOCK_INITIAL_HEIGHT = 224;
|
||||
const CHAT_DOCK_MIN_HEIGHT = 144;
|
||||
@@ -173,6 +174,18 @@ export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
|
||||
// unavailable, including disabled service and direct-address mode.
|
||||
return Boolean(vote?.votingEnabled);
|
||||
});
|
||||
const handleTabChange = useCallback(
|
||||
(tab) => {
|
||||
/*
|
||||
Desktop users spend most of their time on this one route, so panel
|
||||
changes are the cleanest way to understand feature usage without adding
|
||||
analytics calls to every nested control in the rover dashboard.
|
||||
*/
|
||||
setActiveTab(tab);
|
||||
trackAnalyticsEvent('tab_change', { tab, layout, surface: 'desktop_right_pane' });
|
||||
},
|
||||
[layout],
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const chatDock = chatDockRef.current;
|
||||
@@ -259,7 +272,7 @@ export default function RightPaneTabs({ layout, onOpenHelpOverlay }) {
|
||||
|
||||
return (
|
||||
<section className="text-base">
|
||||
<Tabs defaultTab="telemetry" currentTab={activeTab} onTabChange={setActiveTab}>
|
||||
<Tabs defaultTab="telemetry" currentTab={activeTab} onTabChange={handleTabChange}>
|
||||
<TabList>
|
||||
<Tab id="telemetry">Controls</Tab>
|
||||
<Tab id="activities">Activities</Tab>
|
||||
|
||||
Reference in New Issue
Block a user