feat(web/player): per-button transport gating honoring guest flags

This commit is contained in:
saopig1 committed 2026-06-25 12:15:09 +08:00
1 parent 15fcb11f4f
commit b17057cc41
1 file changed
+14 -11
+14 -11
View File
@@ -6,7 +6,7 @@
<!-- Progress bar (read-only display; seek interaction gated on player.control) --> <!-- Progress bar (read-only display; seek interaction gated on player.control) -->
<div <div
class="progress-bar-container" class="progress-bar-container"
:class="{ 'no-seek': !canControl }" :class="{ 'no-seek': !canTransport }"
ref="progressBarRef" ref="progressBarRef"
@click="onProgressClick" @click="onProgressClick"
@mousemove="onProgressHover" @mousemove="onProgressHover"
@@ -38,18 +38,18 @@
<div class="player-center"> <div class="player-center">
<span class="time-display time-current">{{ formatTime(currentElapsed) }}</span> <span class="time-display time-current">{{ formatTime(currentElapsed) }}</span>
<!-- Transport controls require player.control --> <!-- Transport controls: per-button gating honoring guest flags -->
<template v-if="canControl"> <template v-if="canControl || canTransport || canSkip || canModeCtl">
<button class="control-btn" @click="store.prev()"> <button v-if="canControl" class="control-btn" @click="store.prev()">
<Icon icon="mdi:skip-previous" /> <Icon icon="mdi:skip-previous" />
</button> </button>
<button class="play-btn" @click="togglePlay"> <button v-if="canTransport" class="play-btn" @click="togglePlay">
<Icon :icon="store.isPlaying ? 'mdi:pause' : 'mdi:play'" /> <Icon :icon="store.isPlaying ? 'mdi:pause' : 'mdi:play'" />
</button> </button>
<button class="control-btn" @click="store.next()"> <button v-if="canSkip" class="control-btn" @click="store.next()">
<Icon icon="mdi:skip-next" /> <Icon icon="mdi:skip-next" />
</button> </button>
<button class="control-btn mode-btn" @click="cycleMode" :title="modeLabel"> <button v-if="canModeCtl" class="control-btn mode-btn" @click="cycleMode" :title="modeLabel">
<Icon :icon="modeIcon" /> <Icon :icon="modeIcon" />
<span class="mode-label">{{ modeLabel }}</span> <span class="mode-label">{{ modeLabel }}</span>
</button> </button>
@@ -58,8 +58,8 @@
</div> </div>
<div class="player-right"> <div class="player-right">
<!-- Volume requires player.control --> <!-- Volume gated on transport -->
<template v-if="canControl"> <template v-if="canTransport">
<Icon icon="mdi:volume-high" class="volume-icon" /> <Icon icon="mdi:volume-high" class="volume-icon" />
<input <input
type="range" type="range"
@@ -94,8 +94,11 @@ const route = useRoute();
const router = useRouter(); const router = useRouter();
const showQueue = ref(false); const showQueue = ref(false);
const { can } = useSession(); const { can, guestCan } = useSession();
const canControl = computed(() => can('player.control')); const canControl = computed(() => can('player.control'));
const canTransport = computed(() => can('player.control') || guestCan('transport'));
const canSkip = computed(() => can('player.control') || guestCan('skip'));
const canModeCtl = computed(() => can('player.control') || guestCan('playMode'));
const store = usePlayerStore(); const store = usePlayerStore();
const activeBot = computed(() => store.activeBot); const activeBot = computed(() => store.activeBot);
@@ -144,7 +147,7 @@ function updateProgress() {
} }
async function onProgressClick(e: MouseEvent) { async function onProgressClick(e: MouseEvent) {
if (!canControl.value) return; // seek requires player.control if (!canTransport.value) return; // seek requires player.control
const bar = progressBarRef.value; const bar = progressBarRef.value;
if (!bar) return; if (!bar) return;
const rect = bar.getBoundingClientRect(); const rect = bar.getBoundingClientRect();