@cuemath/leap 3.5.29-mb-2 → 3.5.29-mb-3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,127 +1,145 @@
|
|
|
1
|
-
import { useRef as v, useCallback as
|
|
1
|
+
import { useRef as v, useCallback as d, useEffect as L } from "react";
|
|
2
2
|
import { useAutoPlayPermission as P } from "../../../hooks/use-auto-play-permission/use-auto-play-permission.js";
|
|
3
|
-
import { SWIPE_SOUND_ORDER as I, CircleSoundKeyMapper as
|
|
4
|
-
import { CircleSoundKey as
|
|
3
|
+
import { SWIPE_SOUND_ORDER as I, CircleSoundKeyMapper as N } from "./constants.js";
|
|
4
|
+
import { CircleSoundKey as t } from "./use-circle-sounds-enums.js";
|
|
5
5
|
let C = 0;
|
|
6
|
-
const
|
|
7
|
-
[
|
|
8
|
-
[
|
|
9
|
-
[
|
|
10
|
-
[
|
|
11
|
-
[
|
|
12
|
-
[
|
|
13
|
-
[
|
|
14
|
-
[
|
|
15
|
-
[
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
19
|
-
[
|
|
20
|
-
[
|
|
21
|
-
[
|
|
22
|
-
[
|
|
23
|
-
[
|
|
24
|
-
[
|
|
25
|
-
[
|
|
26
|
-
[
|
|
27
|
-
[
|
|
28
|
-
[
|
|
29
|
-
[
|
|
30
|
-
[
|
|
31
|
-
[
|
|
32
|
-
[
|
|
33
|
-
[
|
|
34
|
-
[
|
|
35
|
-
[
|
|
36
|
-
[
|
|
37
|
-
},
|
|
38
|
-
let
|
|
39
|
-
const
|
|
40
|
-
if (
|
|
41
|
-
|
|
6
|
+
const r = new (window.AudioContext || window.webkitAudioContext)(), _ = {
|
|
7
|
+
[t.BACKGROUND]: null,
|
|
8
|
+
[t.BACKGROUND_RUSHHOUR]: null,
|
|
9
|
+
[t.TUTORIAL]: null,
|
|
10
|
+
[t.SWIPE_01]: null,
|
|
11
|
+
[t.SWIPE_02]: null,
|
|
12
|
+
[t.SWIPE_03]: null,
|
|
13
|
+
[t.SWIPE_04]: null,
|
|
14
|
+
[t.SWIPE_DOWN]: null,
|
|
15
|
+
[t.TOGGLE]: null,
|
|
16
|
+
[t.POINTS_AWARDED]: null,
|
|
17
|
+
[t.POINTS_ADDED]: null,
|
|
18
|
+
[t.GAME_CARD_CLICK]: null,
|
|
19
|
+
[t.CLOCK_IN]: null,
|
|
20
|
+
[t.CLOCK_OUT]: null,
|
|
21
|
+
[t.ACCURACY_IN]: null,
|
|
22
|
+
[t.ACCURACY_OUT]: null,
|
|
23
|
+
[t.STREAK_IN]: null,
|
|
24
|
+
[t.STREAK_OUT]: null,
|
|
25
|
+
[t.ACCURACY_INTRO]: null,
|
|
26
|
+
[t.ACCURACY_TARGET]: null,
|
|
27
|
+
[t.TIME_INTRO]: null,
|
|
28
|
+
[t.TIME_TARGET]: null,
|
|
29
|
+
[t.METER_FILL]: null,
|
|
30
|
+
[t.YOUR_SCORE]: null,
|
|
31
|
+
[t.HIGH_SCORE]: null,
|
|
32
|
+
[t.KEEP_IT_UP]: null,
|
|
33
|
+
[t.DOING_GREAT]: null,
|
|
34
|
+
[t.ALL_DONE]: null,
|
|
35
|
+
[t.ACTIVITY_COMPLETE]: null,
|
|
36
|
+
[t.ALL_ACTIVITIES_COMPLETE]: null
|
|
37
|
+
}, i = {}, a = {};
|
|
38
|
+
let O = !1;
|
|
39
|
+
const R = async () => {
|
|
40
|
+
if (r.state === "suspended" && !O) {
|
|
41
|
+
O = !0;
|
|
42
42
|
try {
|
|
43
|
-
await
|
|
44
|
-
} catch (
|
|
45
|
-
console.warn("Failed to resume AudioContext:",
|
|
43
|
+
await r.resume();
|
|
44
|
+
} catch (c) {
|
|
45
|
+
console.warn("Failed to resume AudioContext:", c);
|
|
46
46
|
} finally {
|
|
47
|
-
|
|
47
|
+
O = !1;
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
}, K = () => {
|
|
51
|
-
const { canAutoPlayAudio:
|
|
52
|
-
if (!
|
|
53
|
-
const
|
|
54
|
-
|
|
51
|
+
const { canAutoPlayAudio: c } = P(), s = v({}), E = d(async (e) => {
|
|
52
|
+
if (!_[e]) {
|
|
53
|
+
const n = await U(N[e]);
|
|
54
|
+
n && (_[e] = n);
|
|
55
55
|
}
|
|
56
|
-
}, []),
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
}, []), m = (e, n = 0.5) => {
|
|
57
|
+
const o = r.currentTime;
|
|
58
|
+
e.gain.cancelScheduledValues(o), e.gain.setValueAtTime(e.gain.value, o), e.gain.linearRampToValueAtTime(1, o + n);
|
|
59
|
+
}, S = (e, n, o = 0.5) => {
|
|
60
|
+
a[e] && (clearTimeout(a[e]), delete a[e]);
|
|
61
|
+
const l = r.currentTime;
|
|
62
|
+
n.gainNode.gain.cancelScheduledValues(l), n.gainNode.gain.setValueAtTime(n.gainNode.gain.value, l), n.gainNode.gain.linearRampToValueAtTime(0, l + o), a[e] = window.setTimeout(() => {
|
|
63
|
+
var u;
|
|
64
|
+
try {
|
|
65
|
+
n.source.stop();
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
((u = i[e]) == null ? void 0 : u.source) === n.source && delete i[e], delete a[e];
|
|
69
|
+
}, o * 1e3);
|
|
70
|
+
}, f = d(
|
|
71
|
+
async (e, n = !0, o = !1) => {
|
|
72
|
+
await R(), await E(e);
|
|
73
|
+
const l = _[e];
|
|
74
|
+
if (!l || !c) {
|
|
75
|
+
console.warn("Cannot play sound", e, { buffer: l, canPlayAudio: c });
|
|
66
76
|
return;
|
|
67
77
|
}
|
|
68
|
-
if (
|
|
69
|
-
|
|
78
|
+
if (i[e] && o) {
|
|
79
|
+
a[e] && (clearTimeout(a[e]), delete a[e]), m(i[e].gainNode);
|
|
70
80
|
return;
|
|
71
81
|
}
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
var
|
|
75
|
-
((
|
|
82
|
+
const u = r.createBufferSource(), T = r.createGain();
|
|
83
|
+
u.buffer = l, u.loop = o, u.connect(T).connect(r.destination), n ? T.gain.setValueAtTime(1, r.currentTime) : (T.gain.setValueAtTime(0, r.currentTime), m(T)), u.start(), i[e] = { source: u, gainNode: T, loop: o }, o || (u.onended = () => {
|
|
84
|
+
var w;
|
|
85
|
+
((w = i[e]) == null ? void 0 : w.source) === u && delete i[e];
|
|
76
86
|
});
|
|
77
87
|
},
|
|
78
|
-
[
|
|
79
|
-
),
|
|
80
|
-
const o =
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
[c, E]
|
|
89
|
+
), h = d(async (e, n = !0) => {
|
|
90
|
+
const o = i[e];
|
|
91
|
+
if (o)
|
|
92
|
+
if (n) {
|
|
93
|
+
try {
|
|
94
|
+
o.source.stop();
|
|
95
|
+
} catch {
|
|
96
|
+
}
|
|
97
|
+
delete i[e], a[e] && (clearTimeout(a[e]), delete a[e]);
|
|
98
|
+
} else
|
|
99
|
+
S(e, o);
|
|
100
|
+
}, []), p = d(() => {
|
|
101
|
+
const e = I[C] || t.SWIPE_01;
|
|
102
|
+
C = (C + 1) % I.length, f(e);
|
|
103
|
+
}, [f]), g = d(() => {
|
|
104
|
+
f(t.TOGGLE);
|
|
105
|
+
}, [f]), A = d(async () => {
|
|
106
|
+
document.visibilityState === "visible" ? (await R(), Object.values(i).forEach(({ gainNode: e }) => {
|
|
107
|
+
m(e);
|
|
108
|
+
})) : Object.entries(i).forEach(([e, n]) => {
|
|
109
|
+
S(e, n);
|
|
92
110
|
});
|
|
93
111
|
}, []);
|
|
94
112
|
return L(() => {
|
|
95
|
-
const e = ["pointerdown", "touchstart"],
|
|
96
|
-
|
|
113
|
+
const e = ["pointerdown", "touchstart"], n = () => {
|
|
114
|
+
R();
|
|
97
115
|
};
|
|
98
116
|
document.addEventListener("visibilitychange", A), e.forEach((l) => {
|
|
99
|
-
window.addEventListener(l,
|
|
117
|
+
window.addEventListener(l, n, { once: !0 });
|
|
100
118
|
});
|
|
101
|
-
const o =
|
|
119
|
+
const o = s.current;
|
|
102
120
|
return () => {
|
|
103
121
|
document.removeEventListener("visibilitychange", A), e.forEach((l) => {
|
|
104
|
-
window.removeEventListener(l,
|
|
105
|
-
}), Object.values(o).forEach((l) => clearTimeout(l));
|
|
122
|
+
window.removeEventListener(l, n);
|
|
123
|
+
}), Object.values(o).forEach((l) => clearTimeout(l)), Object.values(a).forEach((l) => clearTimeout(l));
|
|
106
124
|
};
|
|
107
125
|
}, [A]), {
|
|
108
126
|
playSwipeSound: p,
|
|
109
|
-
play:
|
|
110
|
-
stop:
|
|
111
|
-
playButtonSound:
|
|
127
|
+
play: f,
|
|
128
|
+
stop: h,
|
|
129
|
+
playButtonSound: g
|
|
112
130
|
};
|
|
113
|
-
}, U = async (
|
|
131
|
+
}, U = async (c) => {
|
|
114
132
|
try {
|
|
115
|
-
const
|
|
133
|
+
const s = await fetch(c, {
|
|
116
134
|
mode: "cors",
|
|
117
135
|
credentials: "omit"
|
|
118
136
|
});
|
|
119
|
-
if (!
|
|
120
|
-
throw new Error(`HTTP error! status: ${
|
|
121
|
-
const
|
|
122
|
-
return await
|
|
123
|
-
} catch (
|
|
124
|
-
console.error("CORS error loading audio:",
|
|
137
|
+
if (!s.ok)
|
|
138
|
+
throw new Error(`HTTP error! status: ${s.status}`);
|
|
139
|
+
const E = await s.arrayBuffer();
|
|
140
|
+
return await r.decodeAudioData(E);
|
|
141
|
+
} catch (s) {
|
|
142
|
+
console.error("CORS error loading audio:", s);
|
|
125
143
|
return;
|
|
126
144
|
}
|
|
127
145
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-circle-sounds.js","sources":["../../../../../src/features/circle-games/hooks/use-circle-sounds/use-circle-sounds.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\n\nimport { useAutoPlayPermission } from '../../../hooks/use-auto-play-permission/use-auto-play-permission';\nimport { CircleSoundKeyMapper, SWIPE_SOUND_ORDER } from './constants';\nimport type { TimeoutMap } from './use-circle-sound-types';\nimport { CircleSoundKey } from './use-circle-sounds-enums';\n\nlet swipeSoundIndex = 0;\nconst audioContext = new (window.AudioContext || window.webkitAudioContext)();\n\nconst bufferMapper: Record<CircleSoundKey, AudioBuffer | null> = {\n [CircleSoundKey.BACKGROUND]: null,\n [CircleSoundKey.BACKGROUND_RUSHHOUR]: null,\n [CircleSoundKey.TUTORIAL]: null,\n [CircleSoundKey.SWIPE_01]: null,\n [CircleSoundKey.SWIPE_02]: null,\n [CircleSoundKey.SWIPE_03]: null,\n [CircleSoundKey.SWIPE_04]: null,\n [CircleSoundKey.SWIPE_DOWN]: null,\n [CircleSoundKey.TOGGLE]: null,\n [CircleSoundKey.POINTS_AWARDED]: null,\n [CircleSoundKey.POINTS_ADDED]: null,\n [CircleSoundKey.GAME_CARD_CLICK]: null,\n [CircleSoundKey.CLOCK_IN]: null,\n [CircleSoundKey.CLOCK_OUT]: null,\n [CircleSoundKey.ACCURACY_IN]: null,\n [CircleSoundKey.ACCURACY_OUT]: null,\n [CircleSoundKey.STREAK_IN]: null,\n [CircleSoundKey.STREAK_OUT]: null,\n [CircleSoundKey.ACCURACY_INTRO]: null,\n [CircleSoundKey.ACCURACY_TARGET]: null,\n [CircleSoundKey.TIME_INTRO]: null,\n [CircleSoundKey.TIME_TARGET]: null,\n [CircleSoundKey.METER_FILL]: null,\n [CircleSoundKey.YOUR_SCORE]: null,\n [CircleSoundKey.HIGH_SCORE]: null,\n [CircleSoundKey.KEEP_IT_UP]: null,\n [CircleSoundKey.DOING_GREAT]: null,\n [CircleSoundKey.ALL_DONE]: null,\n [CircleSoundKey.ACTIVITY_COMPLETE]: null,\n [CircleSoundKey.ALL_ACTIVITIES_COMPLETE]: null,\n};\n\ntype ActiveSound = {\n source: AudioBufferSourceNode;\n gainNode: GainNode;\n loop: boolean;\n};\n\nconst activeSounds: Partial<Record<CircleSoundKey, ActiveSound>> = {};\n\nlet resuming = false;\nconst resumeAudioContext = async () => {\n if (audioContext.state === 'suspended' && !resuming) {\n resuming = true;\n try {\n await audioContext.resume();\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn('Failed to resume AudioContext:', err);\n } finally {\n resuming = false;\n }\n }\n};\n\nexport const useCircleSounds = () => {\n const { canAutoPlayAudio: canPlayAudio } = useAutoPlayPermission();\n\n const timeoutRefs = useRef<TimeoutMap>({});\n\n const loadSound = useCallback(async (key: CircleSoundKey) => {\n if (!bufferMapper[key]) {\n const audioBuffer = await fetchAudio(CircleSoundKeyMapper[key]);\n\n if (audioBuffer) {\n bufferMapper[key] = audioBuffer;\n }\n }\n }, []);\n\n const fadeIn = (gainNode: GainNode) => {\n gainNode.gain.cancelScheduledValues(audioContext.currentTime);\n gainNode.gain.setValueAtTime(gainNode.gain.value, audioContext.currentTime);\n gainNode.gain.linearRampToValueAtTime(1, audioContext.currentTime + 0.5);\n };\n\n const fadeOut = (gainNode: GainNode) => {\n gainNode.gain.cancelScheduledValues(audioContext.currentTime);\n gainNode.gain.setValueAtTime(gainNode.gain.value, audioContext.currentTime);\n gainNode.gain.linearRampToValueAtTime(0, audioContext.currentTime + 0.5);\n };\n\n const play = useCallback(\n async (key: CircleSoundKey, immediately = true, loop = false) => {\n await resumeAudioContext();\n await loadSound(key);\n const buffer = bufferMapper[key];\n\n if (!buffer || !canPlayAudio) {\n // eslint-disable-next-line no-console\n console.warn('Cannot play sound', key, { buffer, canPlayAudio });\n\n return;\n }\n\n // prevent duplicate loop sounds\n if (activeSounds[key] && loop) {\n // if it's just faded out, fade it back in\n fadeIn(activeSounds[key]!.gainNode);\n\n return;\n }\n\n const source = audioContext.createBufferSource();\n const gainNode = audioContext.createGain();\n\n source.buffer = buffer;\n source.loop = loop;\n source.connect(gainNode).connect(audioContext.destination);\n\n if (immediately) {\n gainNode.gain.setValueAtTime(1, audioContext.currentTime);\n } else {\n fadeIn(gainNode);\n }\n\n source.start();\n activeSounds[key] = { source, gainNode, loop };\n\n if (!loop) {\n source.onended = () => {\n if (activeSounds[key]?.source === source) {\n delete activeSounds[key];\n }\n };\n }\n },\n [canPlayAudio, loadSound],\n );\n\n const stop = useCallback(async (key: CircleSoundKey, immediately = true) => {\n const sound = activeSounds[key];\n\n if (!sound) return;\n\n if (immediately) {\n sound.source.stop();\n delete activeSounds[key];\n } else {\n fadeOut(sound.gainNode);\n }\n }, []);\n\n const playSwipeSound = useCallback(() => {\n const key = SWIPE_SOUND_ORDER[swipeSoundIndex] || CircleSoundKey.SWIPE_01;\n\n swipeSoundIndex = (swipeSoundIndex + 1) % SWIPE_SOUND_ORDER.length;\n play(key);\n }, [play]);\n\n const playButtonSound = useCallback(() => {\n play(CircleSoundKey.TOGGLE);\n }, [play]);\n\n const handleVisibilityChange = useCallback(async () => {\n if (document.visibilityState === 'visible') {\n await resumeAudioContext();\n // fade back in active loop sounds\n Object.values(activeSounds).forEach(({ gainNode }) => {\n fadeIn(gainNode);\n });\n } else {\n // fade out but don’t stop\n Object.values(activeSounds).forEach(({ gainNode }) => {\n fadeOut(gainNode);\n });\n }\n }, []);\n\n useEffect(() => {\n const interactionEvents = ['pointerdown', 'touchstart'];\n const tryResume = () => {\n resumeAudioContext();\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n interactionEvents.forEach(event => {\n window.addEventListener(event, tryResume, { once: true });\n });\n\n const timeouts = timeoutRefs.current;\n\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n interactionEvents.forEach(event => {\n window.removeEventListener(event, tryResume);\n });\n Object.values(timeouts).forEach(id => clearTimeout(id));\n };\n }, [handleVisibilityChange]);\n\n return {\n playSwipeSound,\n play,\n stop,\n playButtonSound,\n };\n};\n\nconst fetchAudio = async (url: string): Promise<AudioBuffer | undefined> => {\n try {\n const response: Response = await fetch(url, {\n mode: 'cors',\n credentials: 'omit',\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const arrayBuffer: ArrayBuffer = await response.arrayBuffer();\n const decoded: AudioBuffer = await audioContext.decodeAudioData(arrayBuffer);\n\n return decoded;\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('CORS error loading audio:', error);\n\n return undefined;\n }\n};\n"],"names":["swipeSoundIndex","audioContext","bufferMapper","CircleSoundKey","activeSounds","resuming","resumeAudioContext","err","useCircleSounds","canPlayAudio","useAutoPlayPermission","timeoutRefs","useRef","loadSound","useCallback","key","audioBuffer","fetchAudio","CircleSoundKeyMapper","fadeIn","gainNode","fadeOut","play","immediately","loop","buffer","source","_a","stop","sound","playSwipeSound","SWIPE_SOUND_ORDER","playButtonSound","handleVisibilityChange","useEffect","interactionEvents","tryResume","event","timeouts","id","url","response","arrayBuffer","error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-circle-sounds.js","sources":["../../../../../src/features/circle-games/hooks/use-circle-sounds/use-circle-sounds.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\n\nimport { useAutoPlayPermission } from '../../../hooks/use-auto-play-permission/use-auto-play-permission';\nimport { CircleSoundKeyMapper, SWIPE_SOUND_ORDER } from './constants';\nimport type { TimeoutMap } from './use-circle-sound-types';\nimport { CircleSoundKey } from './use-circle-sounds-enums';\n\nlet swipeSoundIndex = 0;\nconst audioContext = new (window.AudioContext || window.webkitAudioContext)();\n\nconst bufferMapper: Record<CircleSoundKey, AudioBuffer | null> = {\n [CircleSoundKey.BACKGROUND]: null,\n [CircleSoundKey.BACKGROUND_RUSHHOUR]: null,\n [CircleSoundKey.TUTORIAL]: null,\n [CircleSoundKey.SWIPE_01]: null,\n [CircleSoundKey.SWIPE_02]: null,\n [CircleSoundKey.SWIPE_03]: null,\n [CircleSoundKey.SWIPE_04]: null,\n [CircleSoundKey.SWIPE_DOWN]: null,\n [CircleSoundKey.TOGGLE]: null,\n [CircleSoundKey.POINTS_AWARDED]: null,\n [CircleSoundKey.POINTS_ADDED]: null,\n [CircleSoundKey.GAME_CARD_CLICK]: null,\n [CircleSoundKey.CLOCK_IN]: null,\n [CircleSoundKey.CLOCK_OUT]: null,\n [CircleSoundKey.ACCURACY_IN]: null,\n [CircleSoundKey.ACCURACY_OUT]: null,\n [CircleSoundKey.STREAK_IN]: null,\n [CircleSoundKey.STREAK_OUT]: null,\n [CircleSoundKey.ACCURACY_INTRO]: null,\n [CircleSoundKey.ACCURACY_TARGET]: null,\n [CircleSoundKey.TIME_INTRO]: null,\n [CircleSoundKey.TIME_TARGET]: null,\n [CircleSoundKey.METER_FILL]: null,\n [CircleSoundKey.YOUR_SCORE]: null,\n [CircleSoundKey.HIGH_SCORE]: null,\n [CircleSoundKey.KEEP_IT_UP]: null,\n [CircleSoundKey.DOING_GREAT]: null,\n [CircleSoundKey.ALL_DONE]: null,\n [CircleSoundKey.ACTIVITY_COMPLETE]: null,\n [CircleSoundKey.ALL_ACTIVITIES_COMPLETE]: null,\n};\n\ntype ActiveSound = {\n source: AudioBufferSourceNode;\n gainNode: GainNode;\n loop: boolean;\n};\n\nconst activeSounds: Partial<Record<CircleSoundKey, ActiveSound>> = {};\nconst fadeTimeouts: Partial<Record<CircleSoundKey, number>> = {};\n\nlet resuming = false;\nconst resumeAudioContext = async () => {\n if (audioContext.state === 'suspended' && !resuming) {\n resuming = true;\n try {\n await audioContext.resume();\n } catch (err) {\n // eslint-disable-next-line no-console\n console.warn('Failed to resume AudioContext:', err);\n } finally {\n resuming = false;\n }\n }\n};\n\nexport const useCircleSounds = () => {\n const { canAutoPlayAudio: canPlayAudio } = useAutoPlayPermission();\n\n const timeoutRefs = useRef<TimeoutMap>({});\n\n const loadSound = useCallback(async (key: CircleSoundKey) => {\n if (!bufferMapper[key]) {\n const audioBuffer = await fetchAudio(CircleSoundKeyMapper[key]);\n\n if (audioBuffer) {\n bufferMapper[key] = audioBuffer;\n }\n }\n }, []);\n\n const fadeIn = (gainNode: GainNode, duration = 0.5) => {\n const now = audioContext.currentTime;\n\n gainNode.gain.cancelScheduledValues(now);\n gainNode.gain.setValueAtTime(gainNode.gain.value, now);\n gainNode.gain.linearRampToValueAtTime(1, now + duration);\n };\n\n const fadeOut = (key: CircleSoundKey, sound: ActiveSound, duration = 0.5) => {\n // cancel any previous fade-out stop timer\n if (fadeTimeouts[key]) {\n clearTimeout(fadeTimeouts[key]);\n delete fadeTimeouts[key];\n }\n\n const now = audioContext.currentTime;\n\n sound.gainNode.gain.cancelScheduledValues(now);\n sound.gainNode.gain.setValueAtTime(sound.gainNode.gain.value, now);\n sound.gainNode.gain.linearRampToValueAtTime(0, now + duration);\n\n // Stop and cleanup after fade completes\n fadeTimeouts[key] = window.setTimeout(() => {\n try {\n sound.source.stop();\n } catch {\n // already stopped\n }\n if (activeSounds[key]?.source === sound.source) {\n delete activeSounds[key];\n }\n delete fadeTimeouts[key];\n }, duration * 1000);\n };\n\n const play = useCallback(\n async (key: CircleSoundKey, immediately = true, loop = false) => {\n await resumeAudioContext();\n await loadSound(key);\n const buffer = bufferMapper[key];\n\n if (!buffer || !canPlayAudio) {\n // eslint-disable-next-line no-console\n console.warn('Cannot play sound', key, { buffer, canPlayAudio });\n\n return;\n }\n\n // already active loop sound → cancel fade-out and fade back in\n if (activeSounds[key] && loop) {\n if (fadeTimeouts[key]) {\n clearTimeout(fadeTimeouts[key]);\n delete fadeTimeouts[key];\n }\n fadeIn(activeSounds[key]!.gainNode);\n\n return;\n }\n\n const source = audioContext.createBufferSource();\n const gainNode = audioContext.createGain();\n\n source.buffer = buffer;\n source.loop = loop;\n source.connect(gainNode).connect(audioContext.destination);\n\n if (immediately) {\n gainNode.gain.setValueAtTime(1, audioContext.currentTime);\n } else {\n gainNode.gain.setValueAtTime(0, audioContext.currentTime);\n fadeIn(gainNode);\n }\n\n source.start();\n activeSounds[key] = { source, gainNode, loop };\n\n if (!loop) {\n source.onended = () => {\n if (activeSounds[key]?.source === source) {\n delete activeSounds[key];\n }\n };\n }\n },\n [canPlayAudio, loadSound],\n );\n\n const stop = useCallback(async (key: CircleSoundKey, immediately = true) => {\n const sound = activeSounds[key];\n\n if (!sound) return;\n\n if (immediately) {\n try {\n sound.source.stop();\n } catch {\n // already stopped\n }\n delete activeSounds[key];\n if (fadeTimeouts[key]) {\n clearTimeout(fadeTimeouts[key]);\n delete fadeTimeouts[key];\n }\n } else {\n fadeOut(key, sound);\n }\n }, []);\n\n const playSwipeSound = useCallback(() => {\n const key = SWIPE_SOUND_ORDER[swipeSoundIndex] || CircleSoundKey.SWIPE_01;\n\n swipeSoundIndex = (swipeSoundIndex + 1) % SWIPE_SOUND_ORDER.length;\n play(key);\n }, [play]);\n\n const playButtonSound = useCallback(() => {\n play(CircleSoundKey.TOGGLE);\n }, [play]);\n\n const handleVisibilityChange = useCallback(async () => {\n if (document.visibilityState === 'visible') {\n await resumeAudioContext();\n // fade back in loop sounds\n Object.values(activeSounds).forEach(({ gainNode }) => {\n fadeIn(gainNode);\n });\n } else {\n // fade out but don’t stop immediately\n Object.entries(activeSounds).forEach(([key, sound]) => {\n fadeOut(key as CircleSoundKey, sound);\n });\n }\n }, []);\n\n useEffect(() => {\n const interactionEvents = ['pointerdown', 'touchstart'];\n const tryResume = () => {\n resumeAudioContext();\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n interactionEvents.forEach(event => {\n window.addEventListener(event, tryResume, { once: true });\n });\n\n const timeouts = timeoutRefs.current;\n\n return () => {\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n interactionEvents.forEach(event => {\n window.removeEventListener(event, tryResume);\n });\n Object.values(timeouts).forEach(id => clearTimeout(id));\n Object.values(fadeTimeouts).forEach(id => clearTimeout(id));\n };\n }, [handleVisibilityChange]);\n\n return {\n playSwipeSound,\n play,\n stop,\n playButtonSound,\n };\n};\n\nconst fetchAudio = async (url: string): Promise<AudioBuffer | undefined> => {\n try {\n const response: Response = await fetch(url, {\n mode: 'cors',\n credentials: 'omit',\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const arrayBuffer: ArrayBuffer = await response.arrayBuffer();\n const decoded: AudioBuffer = await audioContext.decodeAudioData(arrayBuffer);\n\n return decoded;\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('CORS error loading audio:', error);\n\n return undefined;\n }\n};\n"],"names":["swipeSoundIndex","audioContext","bufferMapper","CircleSoundKey","activeSounds","fadeTimeouts","resuming","resumeAudioContext","err","useCircleSounds","canPlayAudio","useAutoPlayPermission","timeoutRefs","useRef","loadSound","useCallback","key","audioBuffer","fetchAudio","CircleSoundKeyMapper","fadeIn","gainNode","duration","now","fadeOut","sound","_a","play","immediately","loop","buffer","source","stop","playSwipeSound","SWIPE_SOUND_ORDER","playButtonSound","handleVisibilityChange","useEffect","interactionEvents","tryResume","event","timeouts","id","url","response","arrayBuffer","error"],"mappings":"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"}
|