diff options
| author | Adam Soltys <[email protected]> | 2026-10-25 07:52:27 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-25 07:52:27 -0700 |
| commit | 67e184a659c6a4b3251a57e0a862e3435bfda751 (patch) | |
| tree | 3aed0bd0bdb05e5568183bfe6e917f4f2d493dcb /src | |
| parent | 4c3ec3ea6b8d909466044071bee3b852c6bf0845 (diff) | |
| download | coinosv3-67e184a659c6a4b3251a57e0a862e3435bfda751.tar.gz coinosv3-67e184a659c6a4b3251a57e0a862e3435bfda751.zip | |
A feed clip with timed reactions seeks on the moments bar alone: the player's own controls (its seek bar) stay off, the middle play/pause shows whenever it's paused (touch too), and the sound button stays to turn it down again
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
Diffstat (limited to 'src')
| -rw-r--r-- | src/features/messages.js | 39 | ||||
| -rw-r--r-- | src/media-timeline.js | 3 | ||||
| -rw-r--r-- | src/style.css | 3 |
3 files changed, 37 insertions, 8 deletions
diff --git a/src/features/messages.js b/src/features/messages.js index 74b7d4a..da79671 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -4562,7 +4562,8 @@ export function messagesFeature(ctx) { }; const box = player.parentElement; box._skipMorph = true; - box.append(mediaTimeline(player, url, { h, event, label: t, moments: momentsOn, glyph: momentGlyph })); + box.append(mediaTimeline(player, url, { h, event, label: t, moments: momentsOn, glyph: momentGlyph, + onMarks: player.tagName === 'VIDEO' ? (on) => ownBar(player, on) : null })); player.dispatchEvent(new Event('timeupdate')); } function noteEvent(ev) { @@ -7967,7 +7968,7 @@ export function messagesFeature(ctx) { if (v) { if (v.getAttribute('src') !== playSrc(url)) v.setAttribute('src', playSrc(url)); v.preload = 'auto'; - v.play().catch(() => { wokeClips.add(url); box.classList.remove('idle'); v.controls = true; }); + v.play().catch(() => { wokeClips.add(url); box.classList.remove('idle'); nativeControls(v); }); } } }, h('span', { class: 'vid-heavy-play', 'aria-hidden': 'true' }, '\u25b6'), h('span', {}, heavyLabel(url))); box.append(cover); @@ -8108,6 +8109,25 @@ export function messagesFeature(ctx) { document.addEventListener('pointermove', (e) => { if (e.pointerType === 'mouse') mouseIs(true); }, { passive: true }); document.addEventListener('pointerdown', (e) => { if (e.pointerType !== 'mouse') mouseIs(false); }, { passive: true }); } + // A clip whose post has timed reactions seeks on the moments bar under it + // (the reactions sit on it at their moments), so the player's own controls + // — a second seek bar — stay off; the middle button and the sound button + // are its controls then (Adam: "hide the main seek bar … and only show a + // moments seek bar"). Otherwise the player's controls, once it has woken. + const nativeControls = (v) => { v.controls = !v._ownBar; }; + function ownBar(player, on) { + const box = player.closest('.note-video-box'); + if (!box || player._ownBar === on) return; + player._ownBar = on; + box.classList.toggle('own-bar', on); + if (!box.classList.contains('idle')) nativeControls(player); + // with no player controls, the sound button stays to turn it back down + if (on && !box.querySelector('.vid-sound')) { + const b = soundBtn((button) => { player.muted = !player.muted; button.setSound(!player.muted); }); + b.setSound(!player.muted); + box.append(b); + } + } const fsElement = () => (typeof document !== 'undefined' && (document.fullscreenElement || document.webkitFullscreenElement)) || null; let videoTapAt = 0; // a touch's tap on a clip, taken at pointerup: the click after it lands on whatever opened function videoTap(e, url) { @@ -8138,7 +8158,7 @@ export function messagesFeature(ctx) { wokeClips.add(url); const box = v.closest('.note-video-box'); if (box) { box.classList.remove('idle'); box._skipMorph = true; } - v.controls = true; + nativeControls(v); return true; } if (openClip(v, url)) return true; @@ -8146,7 +8166,7 @@ export function messagesFeature(ctx) { wokeClips.add(url); const box = v.closest('.note-video-box'); if (box) { box.classList.remove('idle'); box._skipMorph = true; } - v.controls = true; + nativeControls(v); const go = v.requestFullscreen || v.webkitRequestFullscreen || v.webkitEnterFullscreen; if (!go) return true; // (the screen isn't turned for it: a wide clip plays with bars, and the @@ -8239,7 +8259,7 @@ export function messagesFeature(ctx) { const wake = () => { wokeClips.add(url); box.classList.remove('idle'); - v.controls = true; + nativeControls(v); }; v.addEventListener('playing', wake); const btn = loud ? null : soundBtn((button) => { @@ -8248,14 +8268,19 @@ export function messagesFeature(ctx) { const liveBox = button.closest('.note-video-box'); const player = liveBox?.querySelector('video'); if (!player) return; + if (player._ownBar && !player.muted) { player.muted = true; button.setSound(false); return; } player.muted = false; unmutedClips.add(url); liveBox._skipMorph = true; // the box is the viewer's now: no repaint rebuilds it muted if (player.paused) player.play().catch(() => {}); - button.remove(); // the player's own controls carry mute from here + // the player's own controls carry mute from here — unless they're off + // for the moments bar: then this button turns it down again + if (player._ownBar) button.setSound(true); else button.remove(); }); v.onvolumechange = () => { - if (!v.muted) { unmutedClips.add(url); if (btn && btn.isConnected) btn.remove(); } // unmuted from the native control: same + const b = v.closest('.note-video-box')?.querySelector('.vid-sound'); + if (v._ownBar && b && b.setSound) b.setSound(!v.muted); + if (!v.muted) { unmutedClips.add(url); if (btn && btn.isConnected && !v._ownBar) btn.remove(); } // unmuted from the native control: same else unmutedClips.delete(url); // muted again from the control: the next box starts quiet }; if (btn) box.append(btn); diff --git a/src/media-timeline.js b/src/media-timeline.js index 648c998..d8ae008 100644 --- a/src/media-timeline.js +++ b/src/media-timeline.js @@ -85,7 +85,7 @@ export function markAt(seconds, duration) { // with closures pointing at a detached player. What was said, reacted or // zapped at a moment sits on the bar at that moment; replies, reactions and // zaps made while a clip plays carry its time on their own (see momentOf). -export function mediaTimeline(player, url, { h, event, moments, glyph, label }) { +export function mediaTimeline(player, url, { h, event, moments, glyph, label, onMarks = null }) { const audio = player.tagName === 'AUDIO'; const box = h('div', { class: 'media-timeline', onClick: e => e.stopPropagation(), onPointerdown: e => e.stopPropagation() }); box._skipMorph = true; @@ -126,6 +126,7 @@ export function mediaTimeline(player, url, { h, event, moments, glyph, label }) // a video has its own seek bar: this one shows under it only to carry // what was said at a moment (two bars read as a glitch on a phone) box.classList.toggle('has-marks', items.length > 0); + if (onMarks) onMarks(items.length > 0); if (signature !== next) { signature = next; marks.replaceChildren(...items.map(x => h('button', { type: 'button', class: 'media-mark', title: mediaClock(x.moment.seconds) + ' ' + x.text, diff --git a/src/style.css b/src/style.css index ebfe47a..0fb8e9a 100644 --- a/src/style.css +++ b/src/style.css @@ -866,6 +866,9 @@ html.no-scroll { overflow: hidden; scrollbar-gutter: auto; } .vid-pp-i { width: 0; height: 0; margin-left: 5px; border-style: solid; border-width: 13px 0 13px 22px; border-color: transparent transparent transparent #fff; } .vid-pp.playing .vid-pp-i { width: 20px; height: 24px; margin: 0; border-width: 0 7px; border-color: #fff; } .has-mouse .note-video-box:hover .vid-pp, .vid-pp:focus-visible { display: flex; } +/* the moments bar is the clip's only seek bar: the middle button is its play + control on a touch screen too, up whenever the clip is paused */ +.note-video-box.own-bar .vid-pp:not(.playing) { display: flex; } .note-video-box:has(.vid-heavy) .vid-pp { display: none; } .vid-sound { position: absolute; top: 8px; left: 8px; z-index: 2; margin: 0; padding: 4px 10px; border: 0; |
