diff options
| author | Adam Soltys <[email protected]> | 2026-10-20 12:15:32 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-20 12:15:32 -0700 |
| commit | 1e0d551daae8d09f86dbf54ab67bf52610c0c9ac (patch) | |
| tree | 7e118cc5a49a8308c77b17cbd291012f45cf0875 /tools | |
| parent | 84b6a68000c942e17bfe75c1493eddad7ceb5b78 (diff) | |
| download | coinosv3-1e0d551daae8d09f86dbf54ab67bf52610c0c9ac.tar.gz coinosv3-1e0d551daae8d09f86dbf54ab67bf52610c0c9ac.zip | |
Timed reactions sit on the seek bar at their moment, and the usual buttons carry the time
The "at this time" reply/like/zap buttons are gone, and with them the panel they made over the pager. A reply, like or zap made from a post's own buttons (the feed's, or the pager's ♥ ⚡ and post) while its clip has played is about that moment; the reply box says so and its × makes it about the whole post. What was said, reacted or zapped at a moment sits over the bar at that moment (a custom :emoji: as its picture, from the reaction's own tag; it was showing as :cb-rofl: text), a tap goes there and says who and what, and in the pager it floats up off the bar as the clip plays past. Your reaction no longer turns the count red, only the heart.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013ZDf1cyCQLkfjftYhymZAg
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/media-moments-feed-test.js | 22 | ||||
| -rw-r--r-- | tools/media-timeline-test.js | 10 |
2 files changed, 21 insertions, 11 deletions
diff --git a/tools/media-moments-feed-test.js b/tools/media-moments-feed-test.js index f2d2774..a188c5d 100644 --- a/tools/media-moments-feed-test.js +++ b/tools/media-moments-feed-test.js @@ -135,15 +135,19 @@ try { check('published audio waveform renders in the actual feed', true); await page.evaluate(() => { const a = document.querySelector('.note-audio audio'); a.pause(); a.currentTime = 5; }); await sleep(100); - await page.evaluate(() => document.querySelectorAll('.note-audio .media-moment-actions button')[1].click()); + // the post's own Like carries where the clip is: no buttons of its own for that + const act = (sel, label) => page.evaluate(([sel, label]) => document.querySelector(sel).closest('[data-zap-post]').querySelector('.note-acts [aria-label="' + label + '"]').click(), [sel, label]); + check('no "at this time" buttons', await page.evaluate(() => !document.body.innerText.includes('at this time'))); + await act('.note-audio', 'Like'); await page.waitForSelector('.msg-sheet'); await page.evaluate(() => document.querySelector('.msg-sheet button').click()); await sleep(400); const reaction = await page.evaluate(() => window.__relayGuard.events.find(e => e.kind === 7 && e.tags.some(t => t[0] === 'media'))); check('reaction is signed with the selected file and position', reaction?.tags.some(t => t[0] === 'media' && t[2] === '5')); check('reaction marker appears while paused', await page.evaluate(() => !!document.querySelector('.note-audio .media-mark'))); + check('...on the bar at its moment', await page.evaluate(() => { const m = document.querySelector('.note-audio .media-mark'); return Math.abs(parseFloat(m.style.left) - 5 / document.querySelector('.note-audio audio').duration * 100) < 0.5; })); await page.screenshot({ path: '/tmp/media-timeline-playing.png' }); - await page.evaluate(() => document.querySelector('.note-audio .media-moment-actions button').click()); + await act('.note-audio', 'Reply'); await page.waitForSelector('.thread-reply-input'); check('timestamp is visible in the reply composer', (await page.evaluate(() => document.body.innerText)).includes('Comment at 0:05')); await page.evaluate(() => { const input = document.querySelector('.thread-reply-input'); input.value = 'Great moment'; input.dispatchEvent(new Event('input', { bubbles: true })); }); @@ -156,15 +160,21 @@ try { await page.waitForFunction(() => document.querySelector('.note-audio audio')?.currentTime >= 5); check('the comment link starts playback at its timestamp', true); await click('back'); await sleep(250); - await page.evaluate(() => document.querySelector('video.note-video').play()); - await page.waitForSelector('.note-video-box .media-moment-actions'); - await page.evaluate(() => { const v = document.querySelector('video.note-video'); v.pause(); v.currentTime = 1; }); + // a clip in the pager: its ♥ carries the moment, which then sits on its seek bar + await page.evaluate(() => { const v = document.querySelector('video.note-video'); const r = v.getBoundingClientRect(); + v.dispatchEvent(new MouseEvent('click', { bubbles: true, clientX: r.left + r.width / 2, clientY: r.top + r.height / 3 })); }); + await page.waitForSelector('.media-pager .mp-cur video', { timeout: 8000 }); + await page.waitForFunction(() => document.querySelector('.media-pager .mp-cur video').duration > 0, { timeout: 8000 }); + await page.evaluate(() => { const v = document.querySelector('.media-pager .mp-cur video'); v.pause(); v.currentTime = 1; }); await sleep(150); - await page.evaluate(() => document.querySelectorAll('.note-video-box .media-moment-actions button')[1].click()); + await page.evaluate(() => document.querySelector('.media-pager .mp-cur .mp-like').click()); await page.waitForSelector('.msg-sheet'); await page.evaluate(() => document.querySelector('.msg-sheet button').click()); await sleep(300); check('video reaction carries the video URL and selected second', await page.evaluate(() => window.__relayGuard.events.some(e => e.kind === 7 && e.tags.some(t => t[0] === 'media' && t[1].endsWith('.mp4') && t[2] === '1')))); + check('...and shows over the pager\'s seek bar', await page.waitForFunction(() => !!document.querySelector('.media-pager .mp-cur .mp-seek .mp-mark'), { timeout: 3000 }).then(() => true, () => false)); + check('no timeline panel over the pager', await page.evaluate(() => !document.querySelector('.media-pager .media-timeline'))); + await page.screenshot({ path: '/tmp/media-timeline-pager.png' }); check('no browser exceptions', errs.length === 0, errs.join(' | ')); await page.screenshot({ path: '/tmp/media-timeline-feed.png' }); } finally { await browser.close(); server.stop(); } diff --git a/tools/media-timeline-test.js b/tools/media-timeline-test.js index c8f1fd9..3b812ff 100644 --- a/tools/media-timeline-test.js +++ b/tools/media-timeline-test.js @@ -54,27 +54,27 @@ try { const url = 'https://example.com/song.mp3'; const ev = { id: 'post', tags: [['imeta', 'url ' + url, 'waveform 1 10 4 8']] }; const calls = [], items = [{ id: 'a', moment: { seconds: 30 }, icon: '♡', text: 'Nice!' }]; - const box = mediaTimeline(player, url, { h, event: () => ev, moments: () => items, action: (...a) => calls.push(a.slice(0, 3)), label: x => ({ mediaAtTime: 'at this time' }[x] || x) }); + const box = mediaTimeline(player, url, { h, event: () => ev, moments: () => items, glyph: x => x.icon, label: x => x }); document.body.append(player, box); player.dispatchEvent(new Event('timeupdate')); const wave = box.querySelectorAll('.media-wave i').length; const range = box.querySelector('input'); range.value = 500; range.dispatchEvent(new Event('input')); const seek = at; - for (const b of box.querySelectorAll('.media-moment-actions button')) b.click(); box.querySelector('.media-mark').click(); const marker = at; items.push({ id: 'b', moment: { seconds: 70 }, icon: '⚡', text: '' }); player.dispatchEvent(new Event('mediaactivity')); window.fixture = box; - return { wave, seek, marker, calls, markers: box.querySelectorAll('.media-mark').length, aria: range.getAttribute('aria-valuetext') }; + return { wave, seek, marker, calls, markers: box.querySelectorAll('.media-mark').length, left: [...box.querySelectorAll('.media-mark')].map(m => m.style.left), buttons: box.querySelectorAll('button:not(.media-mark)').length, aria: range.getAttribute('aria-valuetext') }; }); assert.equal(result.wave, 4); assert.equal(result.seek, 60); assert.equal(result.marker, 30); assert.equal(result.markers, 2); - assert.deepEqual(result.calls.map(c => [c[0], c[1].id, c[2].seconds]), [['comment', 'post', 60], ['reaction', 'post', 60], ['zap', 'post', 60]]); + assert.deepEqual(result.left, ['25%', '58.3333%']); // each on the bar at its moment + assert.equal(result.buttons, 0); // no "at this time" buttons await page.setViewport({ width: 360, height: 740 }); assert.equal(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), true); await page.screenshot({ path: '/tmp/media-timeline-mobile.png' }); - console.log('✓ Timestamp validation, Lightning receipt round-trip, untimed zap isolation, waveform peaks, browser seeking, all three actions, marker updates and mobile layout'); + console.log('✓ Timestamp validation, Lightning receipt round-trip, untimed zap isolation, waveform peaks, browser seeking, markers placed at their moments and mobile layout'); } finally { await browser.close(); server.stop(); } |
