Skip to content

Commit

Permalink
Admins hear success/error sounds when adding items to a pickup for a …
Browse files Browse the repository at this point in the history
…reservation (#1827)

# What it does

Adds sounds when adding/removing items to a pickup for a reservation

# Why it is important

#1793

# Audio Interface Changes

You can test the sounds using the seed data by visiting the [approved
reservation's page](http://localhost:3000/admin/reservations/2/items)
and clicking on "Start Building".

- for the "neutral" sound, add an item that is not part of the
reservation
- for the "success" sound, add an item that is part of the reservation
- for the "failure" sound, add an item that does not exist
- for the "removed" sound, remove something that you've already added

# Implementation notes

I added a custom turbo stream action called `playSound`. Right now it
should only play sounds if the `audio` tags are loaded on the page.

I didn't really add any tests since the general functionality of this
part of the app is already tested and the sounds feel like a bonus.
System testing this behavior seemed weird (I'm not really sure you can
test this sort of thing with them) and so did controller testing
controller instance variables or the turbo stream markup.
  • Loading branch information
crismali authored Feb 8, 2025
1 parent 951fb93 commit 4c8e464
Show file tree
Hide file tree
Showing 14 changed files with 73 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
module Admin
module Reservations
class PendingReservationItemsController < BaseController
include Sounds
before_action :load_pending_reservation_item

# Merge into the reservation
def update
result = ReservationLending.add_pending_item_to_reservation(@pending_reservation_item)
if result.success?
render_turbo_response(
turbo_stream: turbo_stream.action(:redirect,
admin_reservation_loans_path(@pending_reservation_item.reservation))
)
@sound_type = success_sound_path
render_turbo_response(:update)
else
render_turbo_response :error
end
Expand All @@ -19,6 +18,7 @@ def update
# Remove from reservation
def destroy
if @pending_reservation_item.destroy
@sound_type = removed_sound_path
render_turbo_response :destroy
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Admin
module Reservations
class ReservationLoansController < BaseController
include Sounds
before_action :set_reservation_loan, only: :destroy

def index
Expand All @@ -11,6 +12,7 @@ def index
# that we're creating a ReservationLoan for an ItemPool without uniquely numbered items.
# Otherwise, we're creating a ReservationLoan for an individual ReservableItem.
def create
@sound_type = success_sound_path
if (reservation_hold_id = reservation_loan_params[:reservation_hold_id])
@reservation_hold = @reservation.reservation_holds.find(reservation_hold_id)

Expand All @@ -36,6 +38,7 @@ def create
created_by: current_user
)
if pending_item.save
@sound_type = neutral_sound_path
respond_to do |format|
format.turbo_stream
end
Expand All @@ -56,6 +59,7 @@ def create
format.turbo_stream
end
else
@sound_type = failure_sound_path
render_form
end
end
Expand All @@ -65,6 +69,8 @@ def destroy

@reservation_hold = @reservation_loan.reservation_hold

@sound_type = removed_sound_path

respond_to do |format|
format.turbo_stream do
render :create
Expand All @@ -75,13 +81,14 @@ def destroy
private

def render_form_with_error(message)
@sound_type = failure_sound_path
@reservation_loan = ReservationLoan.new
@reservation_loan.errors.add(:reservable_item_number, message)
render_form
end

def render_form
render partial: "admin/reservations/reservation_loans/form", locals: {reservation: @reservation, reservation_loan: @reservation_loan}, status: :unprocessable_entity
render_turbo_response :create_error, status: :unprocessable_entity
end

def set_reservation_loan
Expand Down
13 changes: 12 additions & 1 deletion app/javascript/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { highlightElement } from './lib/highlight'

ActiveStorage.start()

// When we send a custom turob action of "redirect", simply go to that location.
// When we send a custom turbo action of "redirect", simply go to that location.
// Based on https://www.ducktypelabs.com/turbo-break-out-and-redirect/
Turbo.StreamActions.redirect = function () {
Turbo.visit(this.target)
Expand All @@ -20,6 +20,17 @@ Turbo.StreamActions.arrangeAppointment = function () {
arrangeAppointment(this.target)
}

Turbo.StreamActions.playSound = function () {
const soundType = this.getAttribute('sound_type')
const audioTag = document.body.querySelector(`[src="${soundType}"]`)
if (audioTag) {
// Check to make sure fastSeek is implemented
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/fastSeek#browser_compatibility
audioTag.fastSeek && audioTag.fastSeek(0)
audioTag.play()
}
}

document.documentElement.addEventListener('turbo:load', setupFeatherIcons)
document.documentElement.addEventListener(
'turbo:frame-render',
Expand Down
20 changes: 20 additions & 0 deletions app/lib/sounds.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Sounds
extend self

ALL = [
SUCCESS = "success",
NEUTRAL = "neutral",
FAILURE = "failure",
REMOVED = "removed"
]

ALL.each do |sound|
define_method(:"#{sound}_sound_path") do
"/sounds/#{sound}.wav"
end
end

def all_sound_paths
ALL.map { |sound| "/sounds/#{sound}.wav" }
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@
<%= render partial: "admin/reservations/pending_reservation_items", formats: [:html], locals: {reservation: @reservation} %>
</template>
</turbo-stream>

<% if @sound_type %>
<turbo-stream action="playSound" sound_type="<%= @sound_type %>"></turbo-stream>
<% end %>
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
<%= render partial: "admin/reservations/pending_reservation_items", formats: [:html], locals: {reservation: @reservation} %>
</template>
</turbo-stream>

<% if @sound_type %>
<turbo-stream action="playSound" sound_type="<%= @sound_type %>"></turbo-stream>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<%= turbo_stream.action(:redirect, admin_reservation_loans_path(@pending_reservation_item.reservation)) %>

<% if @sound_type %>
<turbo-stream action="playSound" sound_type="<%= @sound_type %>"></turbo-stream>
<% end %>
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
</turbo-stream>
<% end %>

<% if @sound_type %>
<turbo-stream action="playSound" sound_type="<%= @sound_type %>"></turbo-stream>
<% end %>

<turbo-stream action="replace" target="pickup-status">
<template>
<%= render partial: "admin/reservations/status", formats: [:html], locals: {reservation: @reservation} %>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<turbo-stream action="replace" target="reservation-loan-form">
<template>
<%= render partial: "admin/reservations/reservation_loans/form", formats: [:html], locals: {reservation: @reservation, reservation_loan: @reservation_loan} %>
</template>
</turbo-stream>

<% if @sound_type %>
<turbo-stream action="playSound" sound_type="<%= @sound_type %>"></turbo-stream>
<% end %>
3 changes: 3 additions & 0 deletions app/views/admin/reservations/reservation_loans/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,8 @@
<%= render partial: "reservation_hold", locals: {reservation: @reservation, reservation_hold: reservation_hold} %>
<% end %>
</table>
<% end %>

<% Sounds.all_sound_paths.each do |sound_path| %>
<audio src="<%= sound_path %>" preload="auto"></audio>
<% end %>
Binary file added public/sounds/failure.wav
Binary file not shown.
Binary file added public/sounds/neutral.wav
Binary file not shown.
Binary file added public/sounds/removed.wav
Binary file not shown.
Binary file added public/sounds/success.wav
Binary file not shown.

0 comments on commit 4c8e464

Please sign in to comment.