Add gray-scaling when guessing
All checks were successful
Deploy Feature / deploy-feature (push) Successful in 25s
All checks were successful
Deploy Feature / deploy-feature (push) Successful in 25s
This commit is contained in:
63
src/App.vue
63
src/App.vue
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="app">
|
||||
<div class="app" :class="{ suspended: isGuessSuspended }">
|
||||
<header class="app-header">
|
||||
<div>
|
||||
<p class="eyebrow">Music Jeopardy</p>
|
||||
@@ -263,12 +263,14 @@
|
||||
@pause="handlePlayerPause"
|
||||
@ended="handlePlayerPause"
|
||||
></audio>
|
||||
<div v-if="viewerGuessVisible || showEnableAudio" class="viewer-actions">
|
||||
<button v-if="showEnableAudio" class="primary enable-audio" @click="enableViewerAudio">
|
||||
Tap To Enable Audio
|
||||
</button>
|
||||
<button v-if="canViewerGuess" class="primary viewer-guess" @click="requestGuessStop">
|
||||
Guess Now
|
||||
<button class="primary viewer-guess" :disabled="!canViewerGuess" @click="requestGuessStop">
|
||||
Stop Song And Guess
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="!currentClipUrl" class="player-empty"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -300,6 +302,9 @@
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<div v-if="isGuessSuspended" class="guess-overlay">
|
||||
<p>{{ guessingTeamLabel }} is guessing</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -334,6 +339,7 @@ type RealtimeState = {
|
||||
currentSelectorId: string | null
|
||||
lastAwardedTeamId: string | null
|
||||
isAnswerClip: boolean
|
||||
guessingTeamId: string | null
|
||||
}
|
||||
|
||||
type RealtimeMessage = {
|
||||
@@ -399,7 +405,8 @@ export default {
|
||||
syncTimer: 0,
|
||||
audioUnlocked: true,
|
||||
latestRemoteState: null as RealtimeState | null,
|
||||
viewerTeamId: ''
|
||||
viewerTeamId: '',
|
||||
guessingTeamId: null as string | null
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
@@ -437,14 +444,26 @@ export default {
|
||||
canViewerGuess() {
|
||||
return (
|
||||
!this.canControlGame &&
|
||||
this.audioUnlocked &&
|
||||
!!this.viewerTeamId &&
|
||||
this.getCurrentTileStatus() === 'playing'
|
||||
)
|
||||
},
|
||||
viewerGuessVisible() {
|
||||
return !this.canControlGame && this.getCurrentTileStatus() === 'playing'
|
||||
},
|
||||
canControlGame() {
|
||||
return !this.gameId || this.isHost
|
||||
},
|
||||
isGuessSuspended() {
|
||||
return this.step === 'game' && this.getCurrentTileStatus() === 'paused'
|
||||
},
|
||||
guessingTeamLabel() {
|
||||
if (this.guessingTeamId) {
|
||||
const team = this.teams.find((candidate) => candidate.id === this.guessingTeamId)
|
||||
if (team?.name?.trim()) return team.name.trim()
|
||||
}
|
||||
return 'A team'
|
||||
},
|
||||
currentSelector() {
|
||||
return this.teams.find((team) => team.id === this.currentSelectorId) || null
|
||||
},
|
||||
@@ -571,6 +590,7 @@ export default {
|
||||
this.audioUnlocked = true
|
||||
this.latestRemoteState = null
|
||||
this.viewerTeamId = ''
|
||||
this.guessingTeamId = null
|
||||
this.setGameInUrl('')
|
||||
},
|
||||
async connectSession() {
|
||||
@@ -632,7 +652,8 @@ export default {
|
||||
currentClipUrl: this.currentClipUrl,
|
||||
currentSelectorId: this.currentSelectorId,
|
||||
lastAwardedTeamId: this.lastAwardedTeamId,
|
||||
isAnswerClip: this.isAnswerClip
|
||||
isAnswerClip: this.isAnswerClip,
|
||||
guessingTeamId: this.guessingTeamId
|
||||
}
|
||||
},
|
||||
publishState() {
|
||||
@@ -675,6 +696,7 @@ export default {
|
||||
this.currentSelectorId = state.currentSelectorId
|
||||
this.lastAwardedTeamId = state.lastAwardedTeamId
|
||||
this.isAnswerClip = state.isAnswerClip
|
||||
this.guessingTeamId = state.guessingTeamId || null
|
||||
if (this.viewerTeamId && !this.teams.some((team) => team.id === this.viewerTeamId)) {
|
||||
this.viewerTeamId = ''
|
||||
}
|
||||
@@ -710,27 +732,12 @@ export default {
|
||||
const status = this.getCurrentTileStatus()
|
||||
if (status !== 'playing') return
|
||||
const key = this.currentTileKey
|
||||
const [cIndex, qIndex] = key.split('-').map(Number)
|
||||
const clue = this.selectedGame.categories[cIndex].clues[qIndex]
|
||||
this.tiles[key].status = 'guessed'
|
||||
this.tiles[key].status = 'paused'
|
||||
this.lastAwardedTeamId = null
|
||||
if (clue.answer) {
|
||||
this.currentClipUrl = encodeURI(clue.answer)
|
||||
this.isAnswerClip = true
|
||||
await nextTick()
|
||||
const player = this.getPlayer()
|
||||
if (player) {
|
||||
this.ensureAudioContext()
|
||||
player.currentTime = 0
|
||||
player.load()
|
||||
player.play().catch(() => {})
|
||||
}
|
||||
} else {
|
||||
this.guessingTeamId = teamId
|
||||
this.isAnswerClip = false
|
||||
const player = this.getPlayer()
|
||||
player?.pause()
|
||||
this.currentClipUrl = ''
|
||||
this.isAnswerClip = false
|
||||
}
|
||||
this.queueStateSync()
|
||||
},
|
||||
getCurrentTileStatus() {
|
||||
@@ -956,6 +963,7 @@ export default {
|
||||
this.currentTileKey = null
|
||||
this.currentClipUrl = ''
|
||||
this.lastAwardedTeamId = null
|
||||
this.guessingTeamId = null
|
||||
this.teams = this.teams.map((team) => ({ ...team, score: 0 }))
|
||||
const randomTeam = this.teams[Math.floor(Math.random() * this.teams.length)]
|
||||
this.currentSelectorId = randomTeam?.id || null
|
||||
@@ -971,6 +979,7 @@ export default {
|
||||
this.currentClipUrl = ''
|
||||
this.lastAwardedTeamId = null
|
||||
this.isAnswerClip = false
|
||||
this.guessingTeamId = null
|
||||
this.teardownAudio()
|
||||
this.queueStateSync()
|
||||
},
|
||||
@@ -1018,6 +1027,7 @@ export default {
|
||||
this.currentTileKey = key
|
||||
this.currentClipUrl = encodeURI(clue.song)
|
||||
this.isAnswerClip = false
|
||||
this.guessingTeamId = null
|
||||
await nextTick()
|
||||
const player = this.getPlayer()
|
||||
if (player) {
|
||||
@@ -1032,6 +1042,7 @@ export default {
|
||||
|
||||
if (status === 'playing') {
|
||||
this.tiles[key].status = 'paused'
|
||||
this.guessingTeamId = this.currentSelectorId
|
||||
const player = this.getPlayer()
|
||||
player?.pause()
|
||||
this.queueStateSync()
|
||||
@@ -1065,6 +1076,7 @@ export default {
|
||||
this.currentTileKey = null
|
||||
this.currentClipUrl = ''
|
||||
this.lastAwardedTeamId = null
|
||||
this.guessingTeamId = null
|
||||
this.checkEnd()
|
||||
this.queueStateSync()
|
||||
}
|
||||
@@ -1077,6 +1089,7 @@ export default {
|
||||
|
||||
if (status === 'paused') {
|
||||
this.tiles[key].status = 'playing'
|
||||
this.guessingTeamId = null
|
||||
const player = this.getPlayer()
|
||||
player?.play().catch(() => {})
|
||||
this.queueStateSync()
|
||||
@@ -1088,6 +1101,7 @@ export default {
|
||||
this.currentTileKey = null
|
||||
this.currentClipUrl = ''
|
||||
this.isAnswerClip = false
|
||||
this.guessingTeamId = null
|
||||
const player = this.getPlayer()
|
||||
player?.pause()
|
||||
this.teardownAudio()
|
||||
@@ -1122,6 +1136,7 @@ export default {
|
||||
const finished = allTiles.every((status) => status === 'won' || status === 'void')
|
||||
if (finished) {
|
||||
this.step = 'end'
|
||||
this.guessingTeamId = null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -29,6 +29,42 @@ code {
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.app-header,
|
||||
.app-main {
|
||||
transition: filter 0.25s ease, opacity 0.25s ease;
|
||||
}
|
||||
|
||||
.app.suspended .app-header,
|
||||
.app.suspended .app-main {
|
||||
filter: grayscale(1) brightness(0.55);
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.guess-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: rgba(8, 12, 26, 0.35);
|
||||
backdrop-filter: blur(2px) grayscale(0.15);
|
||||
pointer-events: none;
|
||||
z-index: 12;
|
||||
}
|
||||
|
||||
.guess-overlay p {
|
||||
margin: 0;
|
||||
padding: 16px 24px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
background: rgba(13, 17, 35, 0.88);
|
||||
color: #ffffff;
|
||||
font-family: 'Bebas Neue', sans-serif;
|
||||
font-size: clamp(2.1rem, 4vw, 3.3rem);
|
||||
letter-spacing: 0.07em;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.app-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -554,16 +590,33 @@ audio.hidden-audio {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.enable-audio {
|
||||
.viewer-actions {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 20px;
|
||||
z-index: 3;
|
||||
transform: translateX(-50%);
|
||||
z-index: 4;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.enable-audio {
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.viewer-guess {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
z-index: 3;
|
||||
font-size: 1rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.03em;
|
||||
padding: 14px 28px;
|
||||
background: linear-gradient(135deg, #ffce3a, #ff6a3a);
|
||||
box-shadow: 0 12px 30px rgba(255, 106, 58, 0.35);
|
||||
}
|
||||
|
||||
.viewer-guess:disabled {
|
||||
background: linear-gradient(135deg, #8f8f8f, #666666);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.end-panel {
|
||||
@@ -606,4 +659,13 @@ audio.hidden-audio {
|
||||
.session-row {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.viewer-actions {
|
||||
flex-direction: column;
|
||||
width: calc(100% - 24px);
|
||||
}
|
||||
|
||||
.viewer-actions .primary {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user