Skip to content

Conversation

@Arukuen
Copy link
Contributor

@Arukuen Arukuen commented Dec 22, 2025

fixes #5

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Enhanced timeline playback to properly prevent duplicate instances when "onceOnly" mode is enabled by tracking individual trigger states.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 22, 2025

Walkthrough

This change refactors the "play once" interaction state tracking in the Timeline class from a single global flag to per-trigger tracking using a WeakSet, enabling "play once" to function correctly when the same interaction targets multiple elements.

Changes

Cohort / File(s) Summary
Per-trigger playback tracking
src/frontend/scripts/class-timeline.js
Replaces single _played flag with _playedTriggers WeakSet in Timeline constructor. Modified createInstance to retrieve current trigger, check if already played, and track each trigger independently instead of globally.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

  • WeakSet appropriateness: Verify WeakSet is suitable for trigger object lifecycle and verify no memory concerns
  • currentTrigger availability: Confirm this.interaction.getCurrentTrigger() is reliably available in all code paths where createInstance executes

Poem

🐰 A WeakSet holds the triggers true,
No more one flag for the whole crew!
Each element now gets its own brief mark,
"Play once" shines bright, even in the dark! ✨

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: converting 'play once' behavior from per-timeline to per-trigger.
Linked Issues check ✅ Passed The code changes implement per-trigger playback tracking via WeakSet, preventing play-once from blocking subsequent triggers on different matched elements.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the per-trigger playback behavior described in issue #5; no unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/5-play-once-multiple-trigger

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f199ce and 0696ad0.

📒 Files selected for processing (1)
  • src/frontend/scripts/class-timeline.js
🔇 Additional comments (1)
src/frontend/scripts/class-timeline.js (1)

27-29: Good use of WeakSet for per-trigger tracking.

Using a WeakSet to track played triggers is an excellent choice because it allows garbage collection of DOM elements that are removed from the page, preventing memory leaks. The comment clearly explains the purpose.

Comment on lines +46 to +53
const currentTrigger = this.interaction.getCurrentTrigger()
if ( this.getRunner().isFrontend ) {
if ( this.timelineData.onceOnly && this.timelineData._played ) {
if ( this.timelineData.onceOnly && this.timelineData._playedTriggers.has( currentTrigger ) ) {
return null
}
}

this.timelineData._playedTriggers.add( currentTrigger )
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== Finding getCurrentTrigger implementation ==="
find . -name "*.js" -type f | head -20
echo ""
echo "=== Searching for getCurrentTrigger usage and definition ==="
rg -n "getCurrentTrigger" --type=js -B2 -A5

Repository: gambitph/Interactions

Length of output: 10296


🏁 Script executed:

#!/bin/bash
echo "=== Reading class-interaction.js around getCurrentTrigger ==="
sed -n '100,130p' src/frontend/scripts/class-interaction.js

echo ""
echo "=== Reading class-timeline.js around the WeakSet usage ==="
sed -n '40,60p' src/frontend/scripts/class-timeline.js

echo ""
echo "=== Checking how triggerElements is set ==="
rg -n "triggerElements" src/frontend/scripts/class-interaction.js -B2 -A2

echo ""
echo "=== Checking initialization of timelineData._playedTriggers ==="
rg -n "_playedTriggers" src/frontend/scripts/class-timeline.js -B2 -A2

Repository: gambitph/Interactions

Length of output: 4174


🏁 Script executed:

#!/bin/bash
echo "=== Searching for where _currentTrigger is set ==="
rg -n "_currentTrigger" src/frontend/scripts/class-interaction.js -B2 -A2

echo ""
echo "=== Checking cloneWithTrigger implementation ==="
rg -n "cloneWithTrigger" src/frontend/scripts/class-interaction.js -B2 -A8

echo ""
echo "=== Checking if triggerElements can be undefined or empty ==="
sed -n '70,90p' src/frontend/scripts/class-interaction.js

Repository: gambitph/Interactions

Length of output: 2272


🏁 Script executed:

#!/bin/bash
echo "=== Check if getTargets can return empty array ==="
rg -n "getTargets" src/frontend/scripts/class-interaction.js -A10 | head -40

echo ""
echo "=== Verify what happens in non-cloned instance scenario ==="
rg -n "createInstance\|new Timeline" src/frontend/scripts/class-interaction.js -B2 -A2

echo ""
echo "=== Check initialization context for timelines ==="
sed -n '15,35p' src/frontend/scripts/class-timeline.js

Repository: gambitph/Interactions

Length of output: 2834


WeakSet operations will throw TypeError if getCurrentTrigger() returns undefined.

WeakSet only accepts objects as values; passing primitives or non-objects like undefined will result in a TypeError. The getCurrentTrigger() method (src/frontend/scripts/class-interaction.js:114) can return undefined when _currentTrigger is undefined and triggerElements is empty or falsy.

In class-timeline.js:48, the WeakSet .has() check only executes in frontend mode with onceOnly enabled, but line 53's .add() call executes unconditionally. If currentTrigger is undefined, line 53 will throw a TypeError regardless of the frontend mode or timeline configuration, crashing the interaction.

Additionally, confirm that non-page interactions without defined triggers should always have at least a fallback trigger element to avoid this edge case entirely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

If interaction trigger matched multiple elements, play once will not work on the next match

2 participants