diff options
| author | Adam Soltys <[email protected]> | 2026-10-13 04:04:32 +0000 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-13 04:04:32 +0000 |
| commit | b01ec800019bc0ac74926a4366f045b71bd7c29b (patch) | |
| tree | 7dee4b603ab0258a40ed3685344469bde18878aa /tools/video-tap-test.js | |
| parent | 3ff588d439dbe86da3fd5b2f433f302078bb0222 (diff) | |
| download | coinosv3-b01ec800019bc0ac74926a4366f045b71bd7c29b.tar.gz coinosv3-b01ec800019bc0ac74926a4366f045b71bd7c29b.zip | |
Feed: a clip opens in the media pager, full screen, with the feed's other clips a swipe away and a seek bar to drag. Adam full-screened a video in the feed and swiped up for the next one, and nothing moved: a tap on a feed clip handed it to the player's own full screen (video.requestFullscreen), where every touch is the player's and there is no next. Now, as pictures do since 6eda018, the tap opens the media pager on that clip over the feed's clips in the feed's order (every shape, not only the tall ones the Videos view flips through), carrying on from where it was. What the player's full screen gave is kept: the pager puts the page itself full screen (the tap's own gesture), and a wide clip locks the screen to landscape — for that clip; past it the screen follows the phone. Each clip in the pager (the media grid's too) gets its own seek bar, a thin line along the bottom that thickens with the details up; dragging it scrubs with the time shown, and the drag is never the pager's swipe or tap. A tap pauses the clip and shows the post's details, the × and the bar; another plays on. Leaving full screen another way (Back on Android, Escape) leaves the pager in the same step; × or Escape after moving on lands the feed on the post last watched. Threads, profiles and chats keep the player's full screen. tools/feed-video-pager-test.js (open, resume, landscape lock and release, seek drag, tap pause/play, swipe and arrows, Escape onto the post, exit full screen); video-tap-test now checks the feed opens the pager and runs its native full-screen checks in a thread. tools/puppeteer.js takes CHROME=/path for a machine without /usr/bin/google-chrome. feed-media-views-test's range-less probe check fails on HEAD before this too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KPw21NEpWeTGu6swVASmBt
Diffstat (limited to 'tools/video-tap-test.js')
| -rw-r--r-- | tools/video-tap-test.js | 35 |
1 files changed, 26 insertions, 9 deletions
diff --git a/tools/video-tap-test.js b/tools/video-tap-test.js index afa014e..f82238b 100644 --- a/tools/video-tap-test.js +++ b/tools/video-tap-test.js @@ -1,7 +1,8 @@ -// Tapping a feed video: anywhere on the picture (not the player's own play -// button or control bar) goes full screen — the corner button is a reach on -// a phone. Full screen, a tap pauses a playing clip; a tap on a paused one -// comes back out. Taps on the controls stay the player's. +// Tapping a video: in the feed it opens the media pager (feed-video-pager- +// test.js has the rest). In a thread, anywhere on the picture (not the +// player's own play button or control bar) goes full screen — the corner +// button is a reach on a phone. Full screen, a tap pauses a playing clip; a +// tap on a paused one comes back out. Taps on the controls stay the player's. // Run: bun tools/video-tap-test.js import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; @@ -34,12 +35,12 @@ const errs = []; page.on('pageerror', (e) => errs.push(String(e))); const click = (t) => page.evaluate((x) => { const e = [...document.querySelectorAll('button')].find((n) => n.textContent.trim().toLowerCase().includes(x)); if (e) { e.click(); return true; } return false; }, t); const waitText = async (x, ms = 25000) => { for (let i = 0; i < ms / 250; i++) { if ((await page.evaluate(() => document.body.innerText)).toLowerCase().includes(x)) return true; await sleep(250); } return false; }; -const st = () => page.evaluate(() => { const v = document.querySelector('video.note-video'); return { fs: document.fullscreenElement === v, paused: v.paused, controls: v.controls, thread: !document.querySelector('video.note-video')?.isConnected }; }); +const st = () => page.evaluate(() => { const v = document.querySelector('.thread-card video.note-video') || document.querySelector('video.note-video'); return { fs: document.fullscreenElement === v, paused: v.paused, controls: v.controls, thread: !document.querySelector('video.note-video')?.isConnected }; }); // tap the clip at a fraction of its box (or of the screen, full screen) const tap = async (fx, fy) => { const fs = await page.evaluate(() => !!document.fullscreenElement); const r = fs ? { x: 0, y: 0, width: await page.evaluate(() => innerWidth), height: await page.evaluate(() => innerHeight) } - : await page.evaluate(() => { const b = document.querySelector('video.note-video').getBoundingClientRect(); return { x: b.x, y: b.y, width: b.width, height: b.height }; }); + : await page.evaluate(() => { const b = (document.querySelector('.thread-card video.note-video') || document.querySelector('video.note-video')).getBoundingClientRect(); return { x: b.x, y: b.y, width: b.width, height: b.height }; }); await page.mouse.click(r.x + r.width * fx, r.y + r.height * fy); await sleep(700); }; @@ -69,16 +70,32 @@ try { check('the clip autoplays in the feed', !(await st()).paused); await tap(0.2, 0.3); + check('in the feed, a tap opens the media pager', await page.evaluate(() => !!document.querySelector('.media-pager.videos'))); + await page.keyboard.press('Escape'); await sleep(700); + check('...which Escape closes', await page.evaluate(() => !document.querySelector('.media-pager') && !document.fullscreenElement)); + // the thread: the post's words open it + const words = await page.evaluate(() => { + const w = document.createTreeWalker(document.querySelector('.notes-feed'), NodeFilter.SHOW_TEXT); + for (let n; (n = w.nextNode());) if (n.textContent.includes('watch this')) { const r = document.createRange(); r.selectNodeContents(n); const b = r.getBoundingClientRect(); return { x: b.x + 10, y: b.y + b.height / 2 }; } + return null; + }); + await page.mouse.click(words.x, words.y); + for (let i = 0; i < 40 && !(await page.evaluate(() => !!document.querySelector('.thread-card video.note-video'))); i++) await sleep(250); + check('the thread is open', await page.evaluate(() => !!document.querySelector('.thread-card video.note-video'))); + await page.evaluate(() => document.querySelector('.thread-card video.note-video').scrollIntoView({ block: 'center' })); await sleep(300); + await page.evaluate(() => document.querySelector('.thread-card video.note-video').play().catch(() => {})); + for (let i = 0; i < 40 && (await st()).paused; i++) await sleep(250); + await tap(0.2, 0.3); let s = await st(); - check('a tap on the picture goes full screen', s.fs, JSON.stringify(s)); - check('...without opening the thread', !s.thread); + check('in a thread, a tap on the picture goes full screen', s.fs, JSON.stringify(s)); + check('...and not the pager', await page.evaluate(() => !document.querySelector('.media-pager'))); await tap(0.25, 0.3); s = await st(); check('full screen, a tap pauses the playing clip', s.fs && s.paused, JSON.stringify(s)); await tap(0.5, 0.5); s = await st(); check('a tap on the paused clip\'s play button is left to the player', s.fs, JSON.stringify(s)); - await page.evaluate(() => document.querySelector('video.note-video').pause()); await sleep(300); + await page.evaluate(() => document.querySelector('.thread-card video.note-video').pause()); await sleep(300); await tap(0.2, 0.3); s = await st(); check('a tap elsewhere on the paused clip comes out of full screen', !s.fs, JSON.stringify(s)); |
