Skip to content

Commit d29c418

Browse files
committed
fix: guard delayed return ball drag cleanup
1 parent 733b362 commit d29c418

2 files changed

Lines changed: 13 additions & 8 deletions

File tree

static/avatar-ui-buttons.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3929,7 +3929,8 @@ const AvatarButtonMixin = {
39293929
dragSafetyTimer = 0;
39303930
};
39313931

3932-
const finishDragState = (moved) => {
3932+
const finishDragState = (moved, safetyToken) => {
3933+
if (safetyToken !== dragSafetyToken) return;
39333934
container.setAttribute('data-dragging', 'false');
39343935
if (moved) {
39353936
_dispatchNekoIdleReturnBallManualMove(container, 'return-ball-drag-end', {
@@ -3947,7 +3948,7 @@ const AvatarButtonMixin = {
39473948
isDragging = false;
39483949
dragActiveDispatched = false;
39493950
container.style.cursor = 'grab';
3950-
finishDragState(moved);
3951+
finishDragState(moved, safetyToken);
39513952
};
39523953

39533954
const handleStart = (clientX, clientY) => {
@@ -3995,12 +3996,13 @@ const AvatarButtonMixin = {
39953996
const handleEnd = () => {
39963997
clearDragSafetyTimer();
39973998
if (isDragging) {
3999+
const safetyToken = dragSafetyToken;
39984000
const moved = container.getAttribute('data-dragging') === 'true';
39994001
isDragging = false;
40004002
dragActiveDispatched = false;
40014003
container.style.cursor = 'grab';
40024004
setTimeout(() => {
4003-
finishDragState(moved);
4005+
finishDragState(moved, safetyToken);
40044006
}, 10);
40054007
}
40064008
};

tests/unit/test_avatar_return_button_idle_tiers_static.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -547,17 +547,18 @@ def test_cat1_walk_is_blocked_while_return_ball_drag_is_active_or_pending():
547547
"const clearDragSafetyTimer = () => {",
548548
"const resetDragStateAfterMissingEnd = (safetyToken) => {",
549549
"if (dragSafetyToken !== safetyToken || !isDragging) return;",
550-
"const finishDragState = (moved) => {",
550+
"const finishDragState = (moved, safetyToken) => {",
551+
"if (safetyToken !== dragSafetyToken) return;",
551552
"container.setAttribute('data-dragging', 'false');",
552553
"_dispatchNekoIdleReturnBallManualMove(container, 'return-ball-drag-end'",
553554
):
554555
_assert_source_contains(drag_setup, expected, "return button drag setup")
555556
_assert_source_order(
556557
drag_setup,
557558
"return button drag setup helpers",
558-
"const finishDragState = (moved) => {",
559+
"const finishDragState = (moved, safetyToken) => {",
559560
"const resetDragStateAfterMissingEnd = (safetyToken) => {",
560-
"finishDragState(moved);",
561+
"finishDragState(moved, safetyToken);",
561562
)
562563
_assert_source_contains(
563564
handle_start,
@@ -580,17 +581,19 @@ def test_cat1_walk_is_blocked_while_return_ball_drag_is_active_or_pending():
580581
"dragSafetyTimer = setTimeout(() => {",
581582
)
582583
_assert_source_contains(handle_end, "clearDragSafetyTimer();", "return button drag end handler")
584+
_assert_source_contains(handle_end, "const safetyToken = dragSafetyToken;", "return button drag end handler")
583585
_assert_source_contains(
584586
handle_end,
585-
"finishDragState(moved);",
587+
"finishDragState(moved, safetyToken);",
586588
"return button drag end handler",
587589
)
588590
_assert_source_order(
589591
handle_end,
590592
"return button drag end handler",
591593
"clearDragSafetyTimer();",
592594
"if (isDragging) {",
593-
"finishDragState(moved);",
595+
"const safetyToken = dragSafetyToken;",
596+
"finishDragState(moved, safetyToken);",
594597
)
595598

596599
sync_block = _source_slice_between(

0 commit comments

Comments
 (0)