diff --git a/src/__tests__/sidebar-room-actions.test.js b/src/__tests__/sidebar-room-actions.test.js index ce481e0bce..43cc6d6e86 100644 --- a/src/__tests__/sidebar-room-actions.test.js +++ b/src/__tests__/sidebar-room-actions.test.js @@ -16,7 +16,7 @@ vi.mock('@/components/ThePwaInstall.vue', () => ({ default: { template: '
vi.mock('@/components/ReportProblem.vue', () => ({ default: { template: '
' } })); vi.mock('@/components/TheSettingsDialog.vue', () => ({ default: { template: '
' } })); -const setup = (component, inRoom = true) => { +const setup = (component, inRoom = true, host = null) => { const disconnect = vi.fn(); const store = createStore({ state: { isLeftSidebarOpen: true, isRightSidebarOpen: true }, @@ -31,7 +31,7 @@ const setup = (component, inRoom = true) => { getters: { IS_IN_ROOM: () => inRoom, GET_USERS: () => ({ me: { username: 'Me' } }), - GET_HOST_USER: () => null, + GET_HOST_USER: () => host, AM_I_HOST: () => false, IS_PARTY_PAUSING_ENABLED: () => false, IS_AUTO_HOST_ENABLED: () => false, @@ -79,6 +79,14 @@ describe('sidebar close and room exit', () => { } finally { wrapper.unmount(); } }); + it('omits host-only control rows for a viewer during playback', () => { + const { wrapper } = setup(TheSidebarRight, true, { username: 'Host', state: 'playing' }); + try { + expect(wrapper.findAll('.switch-item')).toHaveLength(0); + expect(wrapper.get('[aria-label="Pause party"]').exists()).toBe(true); + } finally { wrapper.unmount(); } + }); + it('disconnects only through the labeled navigation action', async () => { const { wrapper, store, disconnect } = setup(TheSidebarLeft); try { diff --git a/src/components/TheSidebarRight.vue b/src/components/TheSidebarRight.vue index c3cc893515..5c26d2c8f3 100644 --- a/src/components/TheSidebarRight.vue +++ b/src/components/TheSidebarRight.vue @@ -41,6 +41,7 @@ @update:model-value="SET_ADVANCED_PARTY_MODE" /> @@ -59,7 +60,7 @@ v-if="AM_I_HOST" class="text-caption text-medium-emphasis pb-2" > - Viewers can pause or resume everyone’s playback when this is enabled. + Allow viewers to pause or resume everyone.

(you)
- {{ getTimeFromMs(getAdjustedTime(user)) }} +
+ {{ getTimeFromMs(getAdjustedTime(user)) }} + {{ readinessLabel(user, id) }} +
{{ getTitle(user.media) }} @@ -59,7 +62,7 @@
-
- {{ readinessLabel(user, id) }} - {{ driftLabel(user) }} +
+ {{ driftLabel(user) }}
@@ -368,7 +375,8 @@ export default { .moderation-actions { flex-wrap: wrap; } .user-list { - max-height: calc(50vh - 100px); + padding-block: 0; + min-height: 0; } .avatar-img { @@ -404,7 +412,12 @@ export default { border-color: #0de47499; } -.user-heading { display: flex; align-items: center; gap: 10px; min-width: 0; } +.user-row { min-height: 52px; padding: 6px 12px; } +.user-meta { display: flex; align-items: baseline; gap: 8px; line-height: 18px; color: rgb(255 255 255 / 65%); } +.user-readiness { font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.participant-action { width: 32px; height: 32px; } +@media (pointer: coarse) { .participant-action { width: 40px; height: 40px; } } +.user-heading { display: flex; align-items: center; gap: 8px; min-width: 0; } .user-avatar, .user-actions { flex-shrink: 0; } .user-identity { flex: 1 1 auto; min-width: 0; } .user-actions { display: flex; align-items: center; gap: 2px; } @@ -414,6 +427,8 @@ export default { .user-row + .user-row { border-top: 1px solid rgba(255, 255, 255, 0.06); } .user-name { + font-size: 14px; + line-height: 20px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -421,8 +436,7 @@ export default { } .user-time { - font-size: 75%; - opacity: 0.7; + font-size: 12px; flex-shrink: 0; } diff --git a/test/fixtures/ui-preview.js b/test/fixtures/ui-preview.js index e98902cf14..7242fca078 100644 --- a/test/fixtures/ui-preview.js +++ b/test/fixtures/ui-preview.js @@ -14,11 +14,12 @@ const image = (text, background = '#29231d') => `data:image/svg+xml,${encodeURIC // eslint-disable-next-line max-len `${text}`, )}`; -const users = Object.fromEntries(['Alex', 'Morgan', 'Sam', 'Taylor with a long name'].map((username, i) => [ +const participantNames = ['Alex', 'Morgan', 'Sam', 'Taylor with a long name', 'Jordan', 'Riley']; +const users = Object.fromEntries(participantNames.map((username, i) => [ `user-${i}`, { username, - thumb: image(username.slice(0, 1), ['#303e52', '#49304b', '#28504b', '#503b27'][i]), - state: ['playing', 'paused', 'buffering', 'playing'][i], + thumb: image(username.slice(0, 1), ['#303e52', '#49304b', '#28504b', '#503b27'][i % 4]), + state: ['playing', 'paused', 'buffering', 'playing', 'playing', 'playing'][i], time: 360000, duration: 7200000, updatedAt: Date.now(), @@ -155,6 +156,8 @@ const store = createStore({ IS_AUTO_HOST_ENABLED: (state) => state.autoHost, }, mutations: { + SET_PREVIEW_HOST(state, id) { state.hostId = id; }, + SET_PREVIEW_MESSAGES(state, messages) { state.messages = messages; }, ADD_MESSAGE(state, text) { state.messages.push({ senderId: 'user-0', time: Date.now(), text }); }, SET_SYNC_PRESET(state, value) { state.syncPreset = value; }, SET_PAUSING(state, value) { state.pausing = value; }, diff --git a/test/fixtures/ui-preview.vue b/test/fixtures/ui-preview.vue index 88b61a135c..1819a0403f 100644 --- a/test/fixtures/ui-preview.vue +++ b/test/fixtures/ui-preview.vue @@ -114,7 +114,7 @@ Watch party preview

- Four sample participants. Messages and party controls stay in this fixture’s memory. + Six sample participants. Messages and party controls stay in this fixture’s memory.

Open navigation + Preview as viewer + Empty chat