@@ -2,7 +2,7 @@ import {useEffect, useRef, useState} from 'react';
22import QrScanner from 'qr-scanner' ;
33import { useDebouncedValue } from '@mantine/hooks' ;
44import classes from './QrScanner.module.scss' ;
5- import { IconBulb , IconBulbOff , IconCameraRotate , IconX } from "@tabler/icons-react" ;
5+ import { IconBulb , IconBulbOff , IconCameraRotate , IconVolume , IconVolumeOff , IconX } from "@tabler/icons-react" ;
66import { Anchor , Button , Menu } from "@mantine/core" ;
77import { showError } from "../../../utilites/notifications.tsx" ;
88import { t , Trans } from "@lingui/macro" ;
@@ -29,6 +29,20 @@ export const QRScannerComponent = (props: QRScannerComponentProps) => {
2929 const [ isScanFailed , setIsScanFailed ] = useState ( false ) ;
3030 const [ isScanSucceeded , setIsScanSucceeded ] = useState ( false ) ;
3131
32+ const scanSuccessAudioRef = useRef < HTMLAudioElement | null > ( null ) ;
33+ const scanErrorAudioRef = useRef < HTMLAudioElement | null > ( null ) ;
34+ const scanInProgressAudioRef = useRef < HTMLAudioElement | null > ( null ) ;
35+
36+ const [ isSoundOn , setIsSoundOn ] = useState ( ( ) => {
37+ const storedIsSoundOn = localStorage . getItem ( "qrScannerSoundOn" ) ;
38+ return storedIsSoundOn === null ? true : JSON . parse ( storedIsSoundOn ) ;
39+ } ) ;
40+
41+ useEffect ( ( ) => {
42+ localStorage . setItem ( "qrScannerSoundOn" , JSON . stringify ( isSoundOn ) ) ;
43+ } , [ isSoundOn ] ) ;
44+
45+
3246 useEffect ( ( ) => {
3347 latestProcessedAttendeeIdsRef . current = processedAttendeeIds ;
3448 } , [ processedAttendeeIds ] ) ;
@@ -64,30 +78,44 @@ export const QRScannerComponent = (props: QRScannerComponentProps) => {
6478 showError ( t `You already scanned this ticket` ) ;
6579
6680 setIsScanFailed ( true ) ;
67- setInterval ( function ( ) {
81+ setInterval ( function ( ) {
6882 setIsScanFailed ( false ) ;
6983 } , 500 ) ;
84+ if ( isSoundOn && scanErrorAudioRef . current ) {
85+ scanErrorAudioRef . current . play ( ) ;
86+ }
7087
7188 return ;
7289 }
7390
7491 if ( ! isCheckingIn && ! alreadyScanned ) {
7592 setIsCheckingIn ( true ) ;
93+
94+ if ( isSoundOn && scanInProgressAudioRef . current ) {
95+ scanInProgressAudioRef . current . play ( ) ;
96+ }
97+
7698 props . onCheckIn ( debouncedAttendeeId , ( didSucceed ) => {
7799 setIsCheckingIn ( false ) ;
78100 setProcessedAttendeeIds ( prevIds => [ ...prevIds , debouncedAttendeeId ] ) ;
79101 setCurrentAttendeeId ( null ) ;
80102
81103 if ( didSucceed ) {
82104 setIsScanSucceeded ( true ) ;
83- setInterval ( function ( ) {
105+ setInterval ( function ( ) {
84106 setIsScanSucceeded ( false ) ;
85107 } , 500 ) ;
108+ if ( isSoundOn && scanSuccessAudioRef . current ) {
109+ scanSuccessAudioRef . current . play ( ) ;
110+ }
86111 } else {
87112 setIsScanFailed ( true ) ;
88- setInterval ( function ( ) {
113+ setInterval ( function ( ) {
89114 setIsScanFailed ( false ) ;
90115 } , 500 ) ;
116+ if ( isSoundOn && scanErrorAudioRef . current ) {
117+ scanErrorAudioRef . current . play ( ) ;
118+ }
91119 }
92120 } , ( ) => {
93121 setIsCheckingIn ( false ) ;
@@ -126,6 +154,10 @@ export const QRScannerComponent = (props: QRScannerComponentProps) => {
126154 }
127155 } ;
128156
157+ const handleSoundToggle = ( ) => {
158+ setIsSoundOn ( ! isSoundOn ) ;
159+ } ;
160+
129161 const requestPermission = async ( ) => {
130162 setPermissionDenied ( false ) ;
131163 await startScanner ( ) ;
@@ -184,6 +216,13 @@ export const QRScannerComponent = (props: QRScannerComponentProps) => {
184216 { ! isFlashAvailable && < IconBulbOff color = { '#ffffff95' } size = { 30 } /> }
185217 { isFlashAvailable && < IconBulb color = { isFlashOn ? 'yellow' : '#ffffff95' } size = { 30 } /> }
186218 </ Button >
219+ < Button onClick = { handleSoundToggle } variant = { 'transparent' } className = { classes . soundToggle } >
220+ { isSoundOn && < IconVolume color = { '#ffffff95' } size = { 30 } /> }
221+ { ! isSoundOn && < IconVolumeOff color = { '#ffffff95' } size = { 30 } /> }
222+ </ Button >
223+ < audio ref = { scanSuccessAudioRef } src = "/sounds/scan-success.wav" />
224+ < audio ref = { scanErrorAudioRef } src = "/sounds/scan-error.wav" />
225+ < audio ref = { scanInProgressAudioRef } src = "/sounds/scan-in-progress.wav" />
187226 < Button onClick = { handleClose } variant = { 'transparent' } className = { classes . closeButton } >
188227 < IconX color = { '#ffffff95' } size = { 30 } />
189228 </ Button >
0 commit comments