Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions docs/.vitepress/theme/components/TutorialPlayer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,38 @@ const entry = ref('')
const isSheetOpen = ref(false)
const isExpanded = ref(false)
const isModalOpen = ref(false)
const tutorialCompleted = ref(false)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Retake exits go uncounted

When a player completes the tutorial, navigates to another chapter, and clicks Exit, the route watcher rerenders the chapter without resetting tutorialCompleted, so trackExit() suppresses exit_tutorial and GA4 misses the retake exit.

Reset tutorialCompleted when the route watcher loads another chapter.

Also at docs/.vitepress/theme/components/TutorialPlayer.vue:105.

Prompt for AI agents
In `docs/.vitepress/theme/components/TutorialPlayer.vue` at line 98:

**Retake exits go uncounted**

When a player completes the tutorial, navigates to another chapter, and clicks Exit, the route watcher rerenders the chapter without resetting `tutorialCompleted`, so `trackExit()` suppresses `exit_tutorial` and GA4 misses the retake exit.

Reset `tutorialCompleted` when the route watcher loads another chapter.

Also at `docs/.vitepress/theme/components/TutorialPlayer.vue:105`.

const activeModalImage = ref<{ src: string; alt: string } | null>(null)

function openModal() {
isModalOpen.value = true
// Reaching the end-of-tutorial modal means the player finished, so a later Exit
// click should not be counted as an early exit.
tutorialCompleted.value = true
}

function closeModal() {
isModalOpen.value = false
}

// Fire a GA4 event when a player leaves the tutorial early (exit_tutorial) or
// finishes and heads to the game (tutorial_complete_play). GA is already set up in
// the theme; this only sends a named signal with the chapter they were on.
function trackTutorial(name: string) {
try {
;(window as any).gtag?.('event', name, { event_category: 'tutorial', chapter: chapterNum.value })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Navigation drops queued tutorial events

When a player clicks Exit or Play before the asynchronously loaded GA script has processed the queued event, trackTutorial only queues the event in the page's in-memory dataLayer, while the Exit and Play anchors immediately navigate away. If the async GA script has not consumed and sent the queued event, unloading the page discards it and GA4 misses the conversion.

Send the event using a navigation-safe mechanism, or wait for GA's event callback before allowing the anchor navigation.

Also at docs/.vitepress/theme/components/TutorialPlayer.vue:724, docs/.vitepress/theme/components/TutorialPlayer.vue:904.

Prompt for AI agents
In `docs/.vitepress/theme/components/TutorialPlayer.vue` at line 117:

**Navigation drops queued tutorial events**

When a player clicks Exit or Play before the asynchronously loaded GA script has processed the queued event, `trackTutorial` only queues the event in the page's in-memory `dataLayer`, while the Exit and Play anchors immediately navigate away. If the async GA script has not consumed and sent the queued event, unloading the page discards it and GA4 misses the conversion.

Send the event using a navigation-safe mechanism, or wait for GA's event callback before allowing the anchor navigation.

Also at `docs/.vitepress/theme/components/TutorialPlayer.vue:724`, `docs/.vitepress/theme/components/TutorialPlayer.vue:904`.

} catch (err) {
// Analytics is best-effort and must never block navigation; surface it quietly.
console.debug('tutorial analytics event failed', name, err)
}
}

// The Exit control is always visible, including after completion, so only count a
// click as an early exit when the player has not reached the end-of-tutorial screen.
function trackExit() {
if (!tutorialCompleted.value) trackTutorial('exit_tutorial')
}

function openImageModal(src: string, alt = '') {
activeModalImage.value = { src, alt }
}
Expand Down Expand Up @@ -610,6 +632,9 @@ function submit() {
}

watch(() => route.path, () => {
// Navigation here is SPA, so this component is not remounted between chapters.
// Clear the completion flag on each chapter so a retake-then-exit is counted.
tutorialCompleted.value = false
renderStepLog()
}, { immediate: true })

Expand Down Expand Up @@ -699,7 +724,7 @@ onUnmounted(() => {
<span class="tut-btn-text">{{ isExpanded ? 'Shrink' : 'Full' }}</span>
</button>

<a :href="withBase(NEWCOMERS_URL)" class="tut-action-btn tut-exit-btn" title="Exit Tutorial to Newcomers Hub" aria-label="Exit Tutorial">
<a :href="withBase(NEWCOMERS_URL)" class="tut-action-btn tut-exit-btn" title="Exit Tutorial to Newcomers Hub" aria-label="Exit Tutorial" @click="trackExit()">
<i class="fa fa-sign-out" aria-hidden="true"></i>
<span class="tut-btn-text">Exit</span>
</a>
Expand Down Expand Up @@ -879,7 +904,7 @@ onUnmounted(() => {
</p>

<div class="tut-modal-buttons">
<a class="tut-modal-btn tut-btn-primary" :href="withBase(BROWSER_PLAY_URL)">
<a class="tut-modal-btn tut-btn-primary" :href="withBase(BROWSER_PLAY_URL)" @click="trackTutorial('tutorial_complete_play')">
<i class="fa fa-gamepad" aria-hidden="true"></i> Play MUME Now (Web Client) &rarr;
</a>

Expand Down
Loading