Re-anchor a moving reel on where it is, not what it was playing
A viewport change re-anchors the reel on the video that is playing, resolved by id rather than by a scroll position the change has already invalidated. That is what keeps a rotation on the same video. Mid-swipe it names the wrong one. setActive runs from onScroll's next frame, so a resize landing before that frame still reads the video being swiped *away from* -- and realignToActive writes its position straight to scrollTop, hauling the reel back to it. A phone slides its chrome on every scroll, so the resize lands mid-swipe on nearly every swipe: the reel went where the thumb put it, far enough to show the top of the next video at the bottom of the screen, and was then written back a beat later. The input guard in onScroll could not catch this. There is no scroll event to guard -- the write happens inside the resize. So while the reader is demonstrably driving, take the reel's own position as the authority and re-anchor on the step it is nearest at the new height. It still lands on a snap point, so nothing is left straddling two screens, but the swipe stands. Only a step's worth of disagreement counts as theirs: a scroller just re-parented into or out of the picture-in-picture window reads 0, and following that would land them on the first video instead of their own. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MPZFnNdHbPGDTqQUNiE4ZN
This commit is contained in:
@@ -90,6 +90,22 @@ RESNAP = """(top) => {
|
||||
document.getElementById('feed-scroll').scrollTop = top;
|
||||
}"""
|
||||
|
||||
# A swipe, and the chrome reporting itself before the feed has caught up.
|
||||
# setActive runs from onScroll's next frame, so doing both in one block --
|
||||
# no frame boundary in between -- leaves feedActiveVideoId still naming the
|
||||
# video being left, which is the state a phone resize routinely lands in.
|
||||
# onResize then re-anchors synchronously, with no scroll event for the
|
||||
# input guard in onScroll to see.
|
||||
SWIPE_THEN_RESIZE = """() => {
|
||||
const s = document.getElementById('feed-scroll');
|
||||
const before = App.state.feedActiveIndex;
|
||||
s.dispatchEvent(new WheelEvent('wheel', { deltaY: 120, bubbles: true }));
|
||||
s.scrollTop = s.scrollTop + s.clientHeight;
|
||||
const moved = s.scrollTop;
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
return { before: before, moved: moved, after: s.scrollTop, h: s.clientHeight };
|
||||
}"""
|
||||
|
||||
|
||||
class Checks:
|
||||
def __init__(self):
|
||||
@@ -187,6 +203,22 @@ def main():
|
||||
c.ok("re-anchored onto its own snap point",
|
||||
abs(resnapped["scrollTop"] - resnapped["expected"]) <= 1, str(resnapped))
|
||||
|
||||
print("\nthe chrome reporting itself before the feed has caught up")
|
||||
page.wait_for_timeout(900)
|
||||
pre = page.evaluate(WHERE)
|
||||
got = page.evaluate(SWIPE_THEN_RESIZE)
|
||||
page.wait_for_timeout(2200)
|
||||
kept = page.evaluate(WHERE)
|
||||
print(f" (swiped to {got['moved']}px; the resize left it at {got['after']}px)")
|
||||
c.ok("the resize did not haul the reel back to the video being left",
|
||||
got["after"] >= got["moved"] - 1,
|
||||
f"{got['moved']} -> {got['after']}")
|
||||
c.ok("the swipe still landed on the next step",
|
||||
kept["index"] == pre["index"] + 1,
|
||||
f"step {pre['index']} -> {kept['index']}")
|
||||
c.ok("and on that step's snap point",
|
||||
abs(kept["scrollTop"] - kept["expected"]) <= 1, str(kept))
|
||||
|
||||
print("\na little height on a near-square viewport is not a rotation")
|
||||
page.set_viewport_size(SQUARE)
|
||||
page.wait_for_timeout(1800)
|
||||
@@ -202,8 +234,8 @@ def main():
|
||||
c.ok("and the reel rests on its snap point",
|
||||
abs(settled["scrollTop"] - settled["expected"]) <= 1, str(settled))
|
||||
c.ok("still on the same video",
|
||||
settled["videoId"] == resnapped["videoId"],
|
||||
f"{resnapped['videoId']} -> {settled['videoId']}")
|
||||
settled["videoId"] == kept["videoId"],
|
||||
f"{kept['videoId']} -> {settled['videoId']}")
|
||||
|
||||
c.ok("nothing threw along the way", not crashes, str(crashes[:2]))
|
||||
browser.close()
|
||||
|
||||
Reference in New Issue
Block a user