clear last tickets

This commit is contained in:
Thomas Demirdjian
2025-11-26 15:33:22 +01:00
parent ee9cc5dccd
commit d7b14bd873
25 changed files with 1692 additions and 1967 deletions
+134 -59
View File
@@ -61,8 +61,8 @@ const HIDDEN_ROUTE_NAMES = new Set([
Routes.Register,
]);
const noopAsync = async () => {};
const noop = () => {};
const noopAsync = async () => { };
const noop = () => { };
const DEFAULT_CONTEXT = {
currentTrack: null,
@@ -272,36 +272,36 @@ const PlayerProvider = ({ children }) => {
(items = [], options = {}) => {
const normalized = Array.isArray(items)
? items
.map((item) => {
if (!item) return null;
if (typeof item === "object") {
const { metadata, context, ...rest } = item;
const safeMetadata =
metadata && typeof metadata === "object"
? { ...metadata }
: undefined;
if (safeMetadata) {
delete safeMetadata.queue;
}
const safeContext =
context && typeof context === "object"
? { ...context }
: undefined;
if (safeContext) {
delete safeContext.queue;
}
return normalizeTrack(
{
...rest,
...(safeMetadata ? { metadata: safeMetadata } : {}),
...(safeContext ? { context: safeContext } : {}),
},
{}
);
.map((item) => {
if (!item) return null;
if (typeof item === "object") {
const { metadata, context, ...rest } = item;
const safeMetadata =
metadata && typeof metadata === "object"
? { ...metadata }
: undefined;
if (safeMetadata) {
delete safeMetadata.queue;
}
return normalizeTrack(item, {});
})
.filter((track) => !!track?.source)
const safeContext =
context && typeof context === "object"
? { ...context }
: undefined;
if (safeContext) {
delete safeContext.queue;
}
return normalizeTrack(
{
...rest,
...(safeMetadata ? { metadata: safeMetadata } : {}),
...(safeContext ? { context: safeContext } : {}),
},
{}
);
}
return normalizeTrack(item, {});
})
.filter((track) => !!track?.source)
: [];
queueRef.current = normalized;
@@ -448,9 +448,105 @@ const PlayerProvider = ({ children }) => {
if (!player) return;
try {
player.loop = !!isLooping;
} catch (_err) {}
} catch (_err) { }
}, [player, isLooping]);
const seekDebounceRef = useRef(null);
const pendingSeekPosRef = useRef(null);
const isSeekingRef = useRef(false);
const waitForActiveSeek = useCallback(async () => {
if (!isSeekingRef.current) return;
// Poll every 50ms until seek is done
while (isSeekingRef.current) {
await new Promise((resolve) => setTimeout(resolve, 50));
}
}, []);
const seekTo = useCallback(
async (positionMs) => {
if (!player) return;
const bounded = Math.max(0, Number(positionMs) || 0);
const seekValue = toPlayerSeekValue(bounded);
// Cancel any pending debounce
if (seekDebounceRef.current) {
clearTimeout(seekDebounceRef.current);
seekDebounceRef.current = null;
}
// Store pending seek position
pendingSeekPosRef.current = seekValue;
// Update local state immediately for UI responsiveness
setPlayback((prev) => ({
...prev,
positionMs: bounded,
}));
return new Promise((resolve) => {
seekDebounceRef.current = setTimeout(async () => {
try {
isSeekingRef.current = true;
if (status?.isLoaded) {
await player.seekTo?.(seekValue);
} else {
pendingSeekValueRef.current = seekValue;
}
} catch (err) {
setError(err);
} finally {
isSeekingRef.current = false;
pendingSeekPosRef.current = null;
resolve();
}
}, 100); // 100ms debounce
});
},
[player, status?.isLoaded, toPlayerSeekValue]
);
const seekBy = useCallback(
async (deltaMs) => {
const currentPos = pendingSeekPosRef.current !== null
? (Platform.OS === "web" ? pendingSeekPosRef.current : pendingSeekPosRef.current * 1000)
: playback.positionMs;
const next = Math.max(0, currentPos + Number(deltaMs || 0));
await seekTo(next);
},
[playback.positionMs, seekTo]
);
// Helper to flush pending seek before playing
const flushPendingSeek = useCallback(async () => {
// 1. Cancel pending debounce and execute immediately
if (seekDebounceRef.current) {
clearTimeout(seekDebounceRef.current);
seekDebounceRef.current = null;
}
if (pendingSeekPosRef.current !== null) {
const seekValue = pendingSeekPosRef.current;
pendingSeekPosRef.current = null;
try {
isSeekingRef.current = true;
if (status?.isLoaded) {
await player.seekTo?.(seekValue);
} else {
pendingSeekValueRef.current = seekValue;
}
} catch (err) {
setError(err);
} finally {
isSeekingRef.current = false;
}
}
// 2. Wait for any active seek to complete
await waitForActiveSeek();
}, [player, status?.isLoaded, waitForActiveSeek]);
const play = useCallback(
async (trackInput, options = {}) => {
const normalized = normalizeTrack(trackInput, options);
@@ -489,6 +585,9 @@ const PlayerProvider = ({ children }) => {
if (sameTrack) {
setCurrentTrack((prev) => ({ ...prev, ...normalized }));
try {
// Flush any pending seek first
await flushPendingSeek();
if (targetPositionMs > 0) {
await player?.seekTo?.(targetSeekValue);
}
@@ -529,17 +628,18 @@ const PlayerProvider = ({ children }) => {
}));
setCurrentTrack(normalized);
},
[currentTrack?.id, player, toPlayerSeekValue, updateQueue]
[currentTrack?.id, player, toPlayerSeekValue, updateQueue, flushPendingSeek]
);
const resume = useCallback(async () => {
if (!currentTrack) return;
try {
await flushPendingSeek();
await player?.play?.();
} catch (err) {
setError(err);
}
}, [player, currentTrack]);
}, [player, currentTrack, flushPendingSeek]);
const pause = useCallback(async () => {
try {
@@ -578,32 +678,7 @@ const PlayerProvider = ({ children }) => {
[currentTrack, playback.isPlaying, pause, play, resume]
);
const seekTo = useCallback(
async (positionMs) => {
if (!player) return;
const bounded = Math.max(0, Number(positionMs) || 0);
const seekValue = toPlayerSeekValue(bounded);
try {
if (status?.isLoaded) {
await player.seekTo?.(seekValue);
} else {
pendingSeekValueRef.current = seekValue;
}
} catch (err) {
setError(err);
}
},
[player, status?.isLoaded, toPlayerSeekValue]
);
const seekBy = useCallback(
async (deltaMs) => {
const next = Math.max(0, playback.positionMs + Number(deltaMs || 0));
await seekTo(next);
},
[playback.positionMs, seekTo]
);
const stop = useCallback(async () => {
try {