feat: show spent time in participant list, fix timer sounds
This commit is contained in:
@@ -162,7 +162,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
function playSound(name) {
|
||||
async function playSound(name) {
|
||||
// Ensure AudioContext is running (may be suspended after inactivity)
|
||||
const ctx = getAudioContext()
|
||||
if (ctx.state === 'suspended') {
|
||||
await ctx.resume()
|
||||
}
|
||||
|
||||
switch (name) {
|
||||
case 'warning':
|
||||
playBeep(880, 0.15)
|
||||
|
||||
@@ -8,10 +8,17 @@
|
||||
|
||||
$: allSpeakers = timerState?.allSpeakers || []
|
||||
$: currentSpeakerId = timerState?.currentSpeakerId || 0
|
||||
$: currentElapsed = timerState?.speakerElapsed || 0
|
||||
|
||||
function handleSkip(speakerId) {
|
||||
dispatch('skip', { speakerId })
|
||||
}
|
||||
|
||||
function formatTime(seconds) {
|
||||
const mins = Math.floor(seconds / 60)
|
||||
const secs = seconds % 60
|
||||
return `${mins}:${secs.toString().padStart(2, '0')}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="participant-list">
|
||||
@@ -23,7 +30,23 @@
|
||||
<li class="speaker-item {speaker.status}">
|
||||
<span class="order">{speaker.order}</span>
|
||||
<span class="name">{speaker.name}</span>
|
||||
<span class="time-limit">{Math.floor(speaker.timeLimit / 60)}:{(speaker.timeLimit % 60).toString().padStart(2, '0')}</span>
|
||||
<span class="time-display">
|
||||
{#if speaker.status === 'done'}
|
||||
<span class="time-spent" class:overtime={speaker.timeSpent > speaker.timeLimit}>
|
||||
{formatTime(speaker.timeSpent)}
|
||||
</span>
|
||||
<span class="time-sep">/</span>
|
||||
<span class="time-limit">{formatTime(speaker.timeLimit)}</span>
|
||||
{:else if speaker.status === 'speaking'}
|
||||
<span class="time-spent" class:overtime={currentElapsed > speaker.timeLimit}>
|
||||
{formatTime(currentElapsed)}
|
||||
</span>
|
||||
<span class="time-sep">/</span>
|
||||
<span class="time-limit">{formatTime(speaker.timeLimit)}</span>
|
||||
{:else}
|
||||
<span class="time-limit">{formatTime(speaker.timeLimit)}</span>
|
||||
{/if}
|
||||
</span>
|
||||
{#if speaker.status === 'pending' || speaker.status === 'skipped'}
|
||||
<button class="skip-btn" on:click={() => handleSkip(speaker.id)} title="{$t('controls.skip')}">
|
||||
⏭
|
||||
@@ -129,13 +152,6 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.time-limit {
|
||||
font-family: 'SF Mono', 'Menlo', monospace;
|
||||
font-size: 12px;
|
||||
color: #6b7280;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.skip-btn {
|
||||
margin-left: 8px;
|
||||
padding: 4px 8px;
|
||||
@@ -163,4 +179,29 @@
|
||||
text-align: center;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.time-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
font-family: 'SF Mono', 'Menlo', monospace;
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.time-spent {
|
||||
color: #4ade80;
|
||||
}
|
||||
|
||||
.time-spent.overtime {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.time-sep {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.time-limit {
|
||||
color: #6b7280;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -33,6 +33,7 @@ type SpeakerInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
TimeLimit int `json:"timeLimit"`
|
||||
TimeSpent int `json:"timeSpent"`
|
||||
Order int `json:"order"`
|
||||
Status SpeakerStatus `json:"status"`
|
||||
}
|
||||
|
||||
@@ -118,12 +118,14 @@ func (t *Timer) startNextSpeaker(now time.Time) {
|
||||
return
|
||||
}
|
||||
|
||||
// Mark previous speaker as done (only if they were speaking, not skipped)
|
||||
// Mark previous speaker as done and save their time spent
|
||||
if t.currentSpeakerID != 0 {
|
||||
timeSpent := int(now.Sub(t.speakerStartTime).Seconds())
|
||||
for i := range t.allSpeakers {
|
||||
if t.allSpeakers[i].ID == t.currentSpeakerID {
|
||||
if t.allSpeakers[i].Status == models.SpeakerStatusSpeaking {
|
||||
t.allSpeakers[i].Status = models.SpeakerStatusDone
|
||||
t.allSpeakers[i].TimeSpent = timeSpent
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -299,9 +301,17 @@ func (t *Timer) Stop() {
|
||||
t.mu.Unlock()
|
||||
return
|
||||
}
|
||||
// Mark current speaker as done before stopping
|
||||
// Mark current speaker as done and save their time spent
|
||||
if t.currentSpeakerID != 0 {
|
||||
t.updateSpeakerStatus(t.currentSpeakerID, models.SpeakerStatusDone)
|
||||
now := time.Now()
|
||||
timeSpent := int(now.Sub(t.speakerStartTime).Seconds())
|
||||
for i := range t.allSpeakers {
|
||||
if t.allSpeakers[i].ID == t.currentSpeakerID {
|
||||
t.allSpeakers[i].Status = models.SpeakerStatusDone
|
||||
t.allSpeakers[i].TimeSpent = timeSpent
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
t.running = false
|
||||
t.paused = false
|
||||
|
||||
Reference in New Issue
Block a user