Skip to content

Commit f20ba47

Browse files
author
“Cengiz
committed
feat: add elevator music
1 parent b368c97 commit f20ba47

File tree

14 files changed

+291
-19
lines changed

14 files changed

+291
-19
lines changed
Binary file not shown.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<button type="button"
2+
id="music-toggle-button"
3+
data-elevator-music-target="button"
4+
data-action="click->elevator-music#toggle"
5+
<%= "data-playing" if playing? %>
6+
class="h-10 sm:h-12 w-10 sm:w-12 font-bold text-lg border-[2px] rounded-[1px] transition-all duration-200 shadow-[3px_3px_0px_0px_rgba(0,0,0,1)] sm:shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] hover:shadow-none hover:-translate-y-0.5 active:translate-y-0 border-zinc-900 flex items-center justify-center
7+
data-[playing]:bg-emerald-400 data-[playing]:text-zinc-900
8+
bg-white text-zinc-900 hover:bg-zinc-100"
9+
title="<%= playing? ? 'Stop elevator music' : 'Play elevator music' %>">
10+
<span class="text-base"><%= playing? ? "🔊" : "🎵" %></span>
11+
</button>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class MusicToggleComponent < ApplicationComponent
2+
def initialize(retro:)
3+
@retro = retro
4+
end
5+
6+
private
7+
8+
def playing?
9+
@retro.music_playing?
10+
end
11+
end
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
class Retros::MusicsController < ApplicationController
2+
before_action :set_retro
3+
before_action :ensure_participant
4+
before_action :ensure_admin
5+
6+
def update
7+
@retro.update!(music_playing: !@retro.music_playing)
8+
broadcast_music_state
9+
10+
head :ok
11+
end
12+
13+
private
14+
15+
def set_retro
16+
@retro = Current.account.retros.find(params[:retro_id])
17+
end
18+
19+
def ensure_participant
20+
redirect_to retros_path unless @retro.participant?(Current.user)
21+
end
22+
23+
def ensure_admin
24+
head :forbidden unless @retro.admin?(Current.user)
25+
end
26+
27+
def broadcast_music_state
28+
Turbo::StreamsChannel.broadcast_action_to(
29+
@retro,
30+
action: :music,
31+
attributes: { state: @retro.music_playing ? "playing" : "stopped" }
32+
)
33+
end
34+
end
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { Controller } from "@hotwired/stimulus"
2+
3+
export default class extends Controller {
4+
static values = {
5+
url: String,
6+
playing: Boolean
7+
}
8+
9+
static targets = ["audio", "button", "prompt"]
10+
11+
connect() {
12+
// If music was playing when page loaded, try to start it
13+
if (this.playingValue && this.hasAudioTarget) {
14+
this.audioTarget.play().then(() => {
15+
if (this.hasPromptTarget) this.promptTarget.classList.add("hidden")
16+
}).catch(e => {
17+
console.log("[Music] Autoplay blocked on connect:", e)
18+
// Show prompt for user to enable audio
19+
if (this.hasPromptTarget) this.promptTarget.classList.remove("hidden")
20+
})
21+
}
22+
}
23+
24+
toggle() {
25+
const isPlaying = !this.audioTarget.paused
26+
27+
// Update UI immediately for responsive feel
28+
if (isPlaying) {
29+
this.audioTarget.pause()
30+
if (this.hasButtonTarget) delete this.buttonTarget.dataset.playing
31+
if (this.hasPromptTarget) this.promptTarget.classList.add("hidden")
32+
} else {
33+
this.audioTarget.play().catch(e => console.log("[Music] Autoplay blocked:", e))
34+
if (this.hasButtonTarget) this.buttonTarget.dataset.playing = "true"
35+
}
36+
37+
// Persist to server (will broadcast to other clients)
38+
fetch(this.urlValue, {
39+
method: "PATCH",
40+
headers: {
41+
"Content-Type": "application/json",
42+
"X-CSRF-Token": document.querySelector('meta[name="csrf-token"]').content
43+
}
44+
})
45+
}
46+
47+
// Called when participant clicks the "enable audio" prompt
48+
enableAudio() {
49+
if (this.hasAudioTarget) {
50+
this.audioTarget.play().then(() => {
51+
if (this.hasPromptTarget) this.promptTarget.classList.add("hidden")
52+
if (this.hasButtonTarget) this.buttonTarget.dataset.playing = "true"
53+
}).catch(e => console.log("[Music] Still blocked:", e))
54+
}
55+
}
56+
}

app/javascript/turbo_stream_actions.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,29 @@ Turbo.StreamActions.highlight = function() {
4141
}
4242
})
4343
}
44+
45+
// Custom Turbo Stream action to control elevator music across all connected clients
46+
Turbo.StreamActions.music = function() {
47+
const state = this.getAttribute("state")
48+
const audio = document.getElementById("elevator-music-audio")
49+
const toggleButton = document.getElementById("music-toggle-button")
50+
const enablePrompt = document.getElementById("music-enable-prompt")
51+
52+
if (!audio) return
53+
54+
if (state === "playing") {
55+
audio.play().then(() => {
56+
// Autoplay worked, hide prompt if visible
57+
if (enablePrompt) enablePrompt.classList.add("hidden")
58+
if (toggleButton) toggleButton.dataset.playing = "true"
59+
}).catch(e => {
60+
console.log("[Music] Autoplay blocked:", e)
61+
// Show prompt for users to click to enable audio
62+
if (enablePrompt) enablePrompt.classList.remove("hidden")
63+
})
64+
} else {
65+
audio.pause()
66+
if (toggleButton) delete toggleButton.dataset.playing
67+
if (enablePrompt) enablePrompt.classList.add("hidden")
68+
}
69+
}

app/models/retro.rb

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,9 @@ def back_phase!
4949
current_index = PHASE_ORDER.index(phase.to_sym)
5050
return if current_index.nil? || current_index <= PHASE_ORDER.index(:brainstorming)
5151

52-
from_phase = phase
5352
prev_phase = PHASE_ORDER[current_index - 1]
5453
update!(phase: prev_phase, highlighted_user_id: nil)
5554
participants.update_all(finished: false)
56-
57-
record_event("retro.phase_changed", particulars: { from: from_phase, to: phase, direction: "back" })
5855
end
5956

6057
def previous_phase

app/views/layouts/retro.html.erb

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,40 @@
9090
</p>
9191
</div>
9292

93-
<div class="flex flex-wrap items-center gap-2 justify-center sm:justify-end w-full sm:w-auto">
93+
<div class="flex flex-wrap items-center gap-2 justify-center sm:justify-end w-full sm:w-auto"
94+
data-controller="elevator-music"
95+
data-elevator-music-url-value="<%= retro_music_path(@retro) %>"
96+
data-elevator-music-playing-value="<%= @retro.music_playing? %>">
97+
98+
<%# Hidden audio element for elevator music %>
99+
<audio id="elevator-music-audio"
100+
data-elevator-music-target="audio"
101+
src="<%= asset_path('elevator-music-bossa-nova-background-music-version-60s-10900.mp3') %>"
102+
loop
103+
preload="auto"
104+
style="display: none;">
105+
</audio>
106+
107+
<%# Enable audio prompt for participants (shown when browser blocks autoplay) %>
108+
<button type="button"
109+
id="music-enable-prompt"
110+
data-elevator-music-target="prompt"
111+
data-action="click->elevator-music#enableAudio"
112+
class="hidden h-10 sm:h-12 px-4 sm:px-6 font-bold uppercase tracking-widest text-xs sm:text-sm bg-amber-400 text-zinc-900 border-[2px] border-zinc-900 rounded-[1px] transition-all duration-200 shadow-[3px_3px_0px_0px_rgba(0,0,0,1)] hover:shadow-none hover:-translate-y-0.5 active:translate-y-0 animate-pulse">
113+
<span class="flex items-center gap-2">
114+
<span>🎵</span>
115+
<span>Click to enable music</span>
116+
</span>
117+
</button>
118+
94119
<%# Phase-specific actions (e.g., votes remaining) %>
95120
<%= yield :header_actions %>
96121

122+
<%# Music toggle button - admin only %>
123+
<% if @retro.admin?(Current.user) %>
124+
<%= render MusicToggleComponent.new(retro: @retro) %>
125+
<% end %>
126+
97127
<%# I am finished button - available to all participants, hidden when retro is complete %>
98128
<% if @current_participant.present? && !@retro.complete? %>
99129
<%= button_to retro_finished_path(@retro),

config/initializers/assets.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55

66
# Add additional assets to the asset load path.
77
# Rails.application.config.assets.paths << Emoji.images_path
8+
Rails.application.config.assets.paths << Rails.root.join("app/assets/music")

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
end
3131
resources :votes, only: %i[create destroy]
3232
resource :highlight, only: %i[update destroy]
33+
resource :music, only: :update
3334
end
3435
resources :feedbacks, only: %i[ new create show edit update destroy ] do
3536
resource :publish, only: :create, module: :feedbacks

0 commit comments

Comments
 (0)