@wavv/ui 2.4.15-alpha.1 → 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;
@@ -35,9 +40,21 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
35
40
  const { current: audio } = audioRef;
36
41
  return audio ? !audio.paused : false;
37
42
  };
43
+ const isNearEnd = (sec)=>totalDuration > 0 && totalDuration - sec <= END_EPSILON_SECONDS;
38
44
  const togglePlayPause = ()=>{
39
45
  if (isPlaying) pause();
40
- else play();
46
+ else {
47
+ if (isNearEnd(progress)) {
48
+ lastRequestedSeekSecondsRef.current = null;
49
+ setProgress(0);
50
+ setDisplayCurrent('0:00');
51
+ const { current: audio } = audioRef;
52
+ if (audio) try {
53
+ audio.currentTime = 0;
54
+ } catch (_e) {}
55
+ }
56
+ play();
57
+ }
41
58
  };
42
59
  useImperativeHandle(ref, ()=>({
43
60
  play,
@@ -57,8 +74,19 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
57
74
  if (onPause) onPause(event);
58
75
  };
59
76
  const handleAudioStop = (event)=>{
77
+ lastRequestedSeekSecondsRef.current = null;
60
78
  setIsPlaying(false);
61
79
  setIsCleared(true);
80
+ setProgress(0);
81
+ setDisplayCurrent('0:00');
82
+ const { current: audio } = audioRef;
83
+ if (audio) try {
84
+ audio.currentTime = 0;
85
+ window.setTimeout(()=>{
86
+ const ct = audio.currentTime ?? 0;
87
+ if (ct > 0.5) audio.currentTime = 0;
88
+ }, 120);
89
+ } catch (_e) {}
62
90
  if (onStop) onStop(event);
63
91
  };
64
92
  const handleAudioError = ()=>{
@@ -93,6 +121,7 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
93
121
  ]);
94
122
  useEffect(()=>()=>{
95
123
  if (seekedTimeoutRef.current) clearTimeout(seekedTimeoutRef.current);
124
+ if (seekVerifyTimeoutRef.current) clearTimeout(seekVerifyTimeoutRef.current);
96
125
  }, []);
97
126
  const formatTime = (time)=>{
98
127
  if (!Number.isFinite(time) || time < 0) return '0:00';
@@ -100,17 +129,41 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
100
129
  const secs = Math.floor(time % 60);
101
130
  return `${mins}:${secs.toString().padStart(2, '0')}`;
102
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
+ };
103
142
  const updateProgress = ()=>{
104
143
  if (isSeekingRef.current) return;
105
144
  const { current: audio } = audioRef;
106
- if (audio) {
107
- const { currentTime } = audio;
108
- if (Number.isFinite(currentTime) && currentTime >= 0) {
109
- const current = formatTime(currentTime);
110
- setProgress(currentTime);
111
- 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;
112
160
  }
161
+ lastRequestedSeekSecondsRef.current = null;
162
+ seekVerifyRetryCountRef.current = 0;
113
163
  }
164
+ const current = formatTime(currentTime);
165
+ setProgress(currentTime);
166
+ setDisplayCurrent(current);
114
167
  };
115
168
  const handleLoad = ()=>{
116
169
  const { current: audio } = audioRef;
@@ -148,6 +201,8 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
148
201
  const maxSeekTarget = Math.max(0, totalDuration - END_EPSILON_SECONDS);
149
202
  const clamped = Math.min(Math.max(sliderPosition, 0), maxSeekTarget);
150
203
  isSeekingRef.current = true;
204
+ lastRequestedSeekSecondsRef.current = clamped;
205
+ seekVerifyRetryCountRef.current = 0;
151
206
  if (seekedTimeoutRef.current) clearTimeout(seekedTimeoutRef.current);
152
207
  seekedTimeoutRef.current = setTimeout(()=>{
153
208
  isSeekingRef.current = false;
@@ -155,7 +210,15 @@ const Audio = ({ src, title, width, infoPosition, centerAlignContent, small, tin
155
210
  }, 300);
156
211
  setProgress(clamped);
157
212
  setDisplayCurrent(formatTime(clamped));
158
- 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);
159
222
  };
160
223
  const hideSlider = isCleared && hideSliderOnStop;
161
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.1",
3
+ "version": "2.4.15-alpha.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {