@wavv/ui 2.4.15-alpha.2 → 2.4.15-alpha.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.
@@ -15,7 +15,12 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
15
15
  const audioRef = useRef(null);
16
16
  const isSeekingRef = useRef(false);
17
17
  const seekedTimeoutRef = useRef(null);
18
+ const lastRequestedSeekSecondsRef = useRef(null);
19
+ const seekVerifyRetryCountRef = useRef(0);
20
+ const seekVerifyTimeoutRef = useRef(null);
18
21
  const END_EPSILON_SECONDS = 0.75;
22
+ const SEEK_TOLERANCE_SECONDS = 0.5;
23
+ const MAX_SEEK_VERIFY_RETRIES = 3;
19
24
  const { height = 30, showHoverTime = false, playedColor, unplayedColor } = 'object' == typeof waveform ? waveform : {};
20
25
  const play = ()=>{
21
26
  const { current: audio } = audioRef;
@@ -40,6 +45,7 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
40
45
  if (isPlaying) pause();
41
46
  else {
42
47
  if (isNearEnd(progress)) {
48
+ lastRequestedSeekSecondsRef.current = null;
43
49
  setProgress(0);
44
50
  setDisplayCurrent('0:00');
45
51
  const { current: audio } = audioRef;
@@ -68,6 +74,7 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
68
74
  if (onPause) onPause(event);
69
75
  };
70
76
  const handleAudioStop = (event)=>{
77
+ lastRequestedSeekSecondsRef.current = null;
71
78
  setIsPlaying(false);
72
79
  setIsCleared(true);
73
80
  setProgress(0);
@@ -114,6 +121,7 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
114
121
  ]);
115
122
  useEffect(()=>()=>{
116
123
  if (seekedTimeoutRef.current) clearTimeout(seekedTimeoutRef.current);
124
+ if (seekVerifyTimeoutRef.current) clearTimeout(seekVerifyTimeoutRef.current);
117
125
  }, []);
118
126
  const formatTime = (time)=>{
119
127
  if (!Number.isFinite(time) || time < 0) return '0:00';
@@ -121,17 +129,41 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
121
129
  const secs = Math.floor(time % 60);
122
130
  return `${mins}:${secs.toString().padStart(2, '0')}`;
123
131
  };
132
+ const performSeek = (targetSeconds)=>{
133
+ const { current: audio } = audioRef;
134
+ if (!audio || !Number.isFinite(targetSeconds)) return false;
135
+ try {
136
+ audio.currentTime = targetSeconds;
137
+ return true;
138
+ } catch (_e) {
139
+ return false;
140
+ }
141
+ };
124
142
  const updateProgress = ()=>{
125
143
  if (isSeekingRef.current) return;
126
144
  const { current: audio } = audioRef;
127
- if (audio) {
128
- const { currentTime } = audio;
129
- if (Number.isFinite(currentTime) && currentTime >= 0) {
130
- const current = formatTime(currentTime);
131
- setProgress(currentTime);
132
- setDisplayCurrent(current);
145
+ if (!audio) return;
146
+ const currentTime = audio.currentTime;
147
+ if (!Number.isFinite(currentTime) || currentTime < 0) return;
148
+ const requestedSeek = lastRequestedSeekSecondsRef.current;
149
+ if ('number' == typeof requestedSeek) {
150
+ const drift = Math.abs(currentTime - requestedSeek);
151
+ if (drift > SEEK_TOLERANCE_SECONDS) {
152
+ if (seekVerifyRetryCountRef.current < MAX_SEEK_VERIFY_RETRIES) {
153
+ seekVerifyRetryCountRef.current += 1;
154
+ performSeek(requestedSeek);
155
+ return;
156
+ }
157
+ lastRequestedSeekSecondsRef.current = null;
158
+ seekVerifyRetryCountRef.current = 0;
159
+ return;
133
160
  }
161
+ lastRequestedSeekSecondsRef.current = null;
162
+ seekVerifyRetryCountRef.current = 0;
134
163
  }
164
+ const current = formatTime(currentTime);
165
+ setProgress(currentTime);
166
+ setDisplayCurrent(current);
135
167
  };
136
168
  const handleLoad = ()=>{
137
169
  const { current: audio } = audioRef;
@@ -169,6 +201,8 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
169
201
  const maxSeekTarget = Math.max(0, totalDuration - END_EPSILON_SECONDS);
170
202
  const clamped = Math.min(Math.max(sliderPosition, 0), maxSeekTarget);
171
203
  isSeekingRef.current = true;
204
+ lastRequestedSeekSecondsRef.current = clamped;
205
+ seekVerifyRetryCountRef.current = 0;
172
206
  if (seekedTimeoutRef.current) clearTimeout(seekedTimeoutRef.current);
173
207
  seekedTimeoutRef.current = setTimeout(()=>{
174
208
  isSeekingRef.current = false;
@@ -176,7 +210,15 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
176
210
  }, 300);
177
211
  setProgress(clamped);
178
212
  setDisplayCurrent(formatTime(clamped));
179
- audioRef.current.currentTime = clamped;
213
+ performSeek(clamped);
214
+ if (seekVerifyTimeoutRef.current) clearTimeout(seekVerifyTimeoutRef.current);
215
+ seekVerifyTimeoutRef.current = window.setTimeout(()=>{
216
+ seekVerifyTimeoutRef.current = null;
217
+ const { current: audio } = audioRef;
218
+ if (!audio || null === lastRequestedSeekSecondsRef.current) return;
219
+ const ct = audio.currentTime ?? 0;
220
+ if (Math.abs(ct - clamped) > SEEK_TOLERANCE_SECONDS) performSeek(clamped);
221
+ }, 150);
180
222
  };
181
223
  const hideSlider = isCleared && hideSliderOnStop;
182
224
  const hideTime = isCleared && hideTimeOnStop;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wavv/ui",
3
- "version": "2.4.15-alpha.2",
3
+ "version": "2.4.15-alpha.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {