watchparty/client/src/views/player/Player.html
Nicolas c14d0bc08b
All checks were successful
Publier l'image Docker / test (push) Successful in 25s
Publier l'image Docker / publish (push) Successful in 17s
Replace avatar-click with a request toolbar; add "request resume"
Asked for directly: a visible bottom toolbar for non-host members
instead of clicking your own small avatar circle (never a very
discoverable affordance), and a second request alongside pause -
"please resume".

- server/src/types.ts, server/src/realtime/SocketGateway.ts: new
  requestResume/resumeRequested, byte-for-byte the same shape and
  handler pattern as requestPause/pauseRequested, independently
  throttled (its own lastResumeRequestUtc - not a shared budget with
  pause). Kept as a plain broadcast signal, same as pause always was -
  no accept/deny state tracked server-side, the host sees the same
  banner/avatar highlight as everyone else and acts with their own
  existing controls.
- client/src/api/party.ts, client/src/types.ts: requestResume() on
  PartyConnection, onResumeRequested callback, a ResumeRequestedMsg type
  (same shape as PauseRequestedMsg, kept separate so a callback's own
  signature says which request it's for).
- client/src/views/player/Player.{html,css,ts}: the avatar is presence-
  only now (no click handler, no "- demander une pause" title) - a new
  .bottomOverlay wraps the request banner, the follower notice, and a
  new .requestToolbar (Demander une pause / Demander une reprise) as one
  flex column, same "stack in normal flow instead of hand-picking a
  pixel offset per box" fix .topOverlay already needed. Pause and resume
  share one showRequestBanner() - they only ever differed by which word
  appears in the banner text. Renamed the shared CSS classes
  (avatarPauseRequested/avatarPauseBadge -> avatarActionRequested/
  avatarRequestBadge) since they're no longer pause-specific.
- server/test/socketGateway.test.ts: new case confirming requestResume's
  throttle is independent from requestPause's own.

Verified against the real compiled bundle: the toolbar shows for a
follower and stays hidden for the host; each button's own client-side
cooldown collapses a rapid double-click to one emission; a pushed
resumeRequested shows the banner with the right text and highlights the
right avatar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 17:09:01 +02:00

45 lines
2 KiB
HTML

<div class="playerWrap">
<video id="video" playsinline></video>
<p id="loadingNotice" class="loadingNotice">Chargement du flux...</p>
<div id="playerError" class="playerError hide">
<p id="playerErrorText"></p>
<button id="playerErrorRetry" class="linklike">Réessayer</button>
</div>
<div class="topOverlay">
<div class="actionBar">
<span id="itemTitle" class="itemTitle"></span>
<span id="members" class="members"></span>
<button id="nextBtn" class="linklike hide">Épisode suivant &rarr;</button>
<div id="settingsWrap" class="settingsWrap hide">
<button id="settingsBtn" class="iconBtn" title="Paramètres de lecture" aria-label="Paramètres de lecture">&#9881;</button>
<div id="settingsPanel" class="settingsPanel hide">
<label>Langue <select id="audioSelect"></select></label>
<label>Sous-titres <select id="subtitleSelect"><option value="">Aucun</option></select></label>
<label>Qualité <select id="qualitySelect"></select></label>
</div>
</div>
<button id="leaveBtn" class="linklike">Quitter</button>
</div>
<div id="presenceOverlay" class="presenceOverlay"></div>
</div>
<!-- Stacked as one column (same reasoning as .topOverlay's own
actionBar+presenceOverlay grouping - see Player.css) so the
transient banner, the passive notice and the request toolbar
always sit in the right order relative to each other regardless
of which are visible at a given moment, instead of each guessing
a fixed pixel offset from the bottom edge. -->
<div class="bottomOverlay">
<div id="requestBanner" class="requestBanner hide"></div>
<p id="followerNotice" class="followerNotice hide">L'hôte contrôle la lecture.</p>
<div id="requestToolbar" class="requestToolbar hide">
<button id="requestPauseBtn" class="requestBtn">Demander une pause</button>
<button id="requestResumeBtn" class="requestBtn">Demander une reprise</button>
</div>
</div>
</div>