feat: fix player
This commit is contained in:
+308
-71
@@ -91,11 +91,13 @@ export const PlayerContext = createContext(DEFAULT_CONTEXT)
|
||||
const convertToMs = (value) => {
|
||||
const numeric = Number(value ?? 0)
|
||||
if (!Number.isFinite(numeric) || numeric <= 0) return 0
|
||||
if (Platform.OS === 'web') return numeric
|
||||
if (numeric > 100000) return numeric
|
||||
return numeric * 1000
|
||||
if (Platform.OS === 'web') return Math.round(numeric)
|
||||
if (numeric > 100000) return Math.round(numeric)
|
||||
return Math.round(numeric * 1000)
|
||||
}
|
||||
|
||||
const waitForNextTick = () => new Promise((resolve) => setTimeout(resolve, 0))
|
||||
|
||||
const ensureSource = (track = {}, options = {}) => {
|
||||
const candidate = track?.source ?? options?.source ?? null
|
||||
if (typeof candidate === 'number') return candidate
|
||||
@@ -141,6 +143,23 @@ const getTrackId = (track = {}, options = {}) => {
|
||||
)
|
||||
}
|
||||
|
||||
const areSourcesEquivalent = (a, b) => {
|
||||
if (a === b) return true
|
||||
if (!a || !b) return false
|
||||
if (typeof a !== typeof b) return false
|
||||
if (typeof a === 'string' || typeof a === 'number') {
|
||||
return a === b
|
||||
}
|
||||
if (typeof a === 'object' && typeof b === 'object') {
|
||||
const aUri = typeof a?.uri === 'string' ? a.uri : null
|
||||
const bUri = typeof b?.uri === 'string' ? b.uri : null
|
||||
const aAssetId = typeof a?.assetId === 'number' ? a.assetId : null
|
||||
const bAssetId = typeof b?.assetId === 'number' ? b.assetId : null
|
||||
return aUri === bUri && aAssetId === bAssetId
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const normalizeTrack = (trackInput = {}, options = {}) => {
|
||||
const track =
|
||||
typeof trackInput === 'string' ? { uri: trackInput } : trackInput ? { ...trackInput } : {}
|
||||
@@ -197,6 +216,7 @@ const normalizeTrack = (trackInput = {}, options = {}) => {
|
||||
const PlayerProvider = ({ children }) => {
|
||||
const [activeRouteName] = useGlobal('activeRouteName')
|
||||
const [currentTrack, setCurrentTrack] = useState(null)
|
||||
const currentTrackId = currentTrack?.id ?? null
|
||||
const queueRef = useRef([])
|
||||
const queueInfoRef = useRef({ id: null, type: null, name: null })
|
||||
const queueIndexRef = useRef(-1)
|
||||
@@ -215,8 +235,23 @@ const PlayerProvider = ({ children }) => {
|
||||
|
||||
const autoPlayRef = useRef(false)
|
||||
const pendingSeekValueRef = useRef(null)
|
||||
const pendingSeekPosRef = useRef(null)
|
||||
const isSeekingRef = useRef(false)
|
||||
const isApplyingPendingSeekRef = useRef(false)
|
||||
const didJustFinishRef = useRef(false)
|
||||
const trackInfoRef = useRef({})
|
||||
const playRequestIdRef = useRef(0)
|
||||
|
||||
const reportError = useCallback((err) => {
|
||||
setError((prev) => {
|
||||
const nextMessage = String(err?.message || err || '')
|
||||
const prevMessage = String(prev?.message || prev || '')
|
||||
if (prevMessage && prevMessage === nextMessage) {
|
||||
return prev
|
||||
}
|
||||
return err
|
||||
})
|
||||
}, [])
|
||||
|
||||
const setQueueIndexValue = useCallback((index = -1) => {
|
||||
const list = queueRef.current
|
||||
@@ -338,6 +373,20 @@ const PlayerProvider = ({ children }) => {
|
||||
const player = useAudioPlayer(source || null, 200)
|
||||
const status = useAudioPlayerStatus(player)
|
||||
const shouldHideOnRoute = activeRouteName ? HIDDEN_ROUTE_NAMES.has(activeRouteName) : false
|
||||
const isStatusLoaded = useMemo(() => {
|
||||
try {
|
||||
return !!status?.isLoaded
|
||||
} catch (_error) {
|
||||
return false
|
||||
}
|
||||
}, [status?.isLoaded])
|
||||
const statusDidJustFinish = useMemo(() => {
|
||||
try {
|
||||
return !!status?.didJustFinish
|
||||
} catch (_error) {
|
||||
return false
|
||||
}
|
||||
}, [status?.didJustFinish])
|
||||
|
||||
const toPlayerSeekValue = useCallback((milliseconds) => {
|
||||
const ms = Math.max(0, Number(milliseconds) || 0)
|
||||
@@ -357,16 +406,57 @@ const PlayerProvider = ({ children }) => {
|
||||
useEffect(() => {
|
||||
if (!status) return
|
||||
|
||||
const durationMs = convertToMs(status.duration)
|
||||
const positionMs = convertToMs(status.currentTime)
|
||||
const isLoaded = !!status.isLoaded
|
||||
const isPlaying = !!status.playing
|
||||
const isBuffering = !!status.isBuffering
|
||||
const didJustFinish = !!status.didJustFinish
|
||||
let durationMs = 0
|
||||
let positionMs = 0
|
||||
let isLoaded = false
|
||||
let isPlaying = false
|
||||
let isBuffering = false
|
||||
let didJustFinish = false
|
||||
|
||||
try {
|
||||
durationMs = convertToMs(status.duration)
|
||||
positionMs = convertToMs(status.currentTime)
|
||||
isLoaded = !!status.isLoaded
|
||||
isPlaying = !!status.playing
|
||||
isBuffering = !!status.isBuffering
|
||||
didJustFinish = !!status.didJustFinish
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
'PlayerProvider: impossible de lire le statut du player',
|
||||
err?.message || err
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const previousTrackInfo = currentTrackId ? trackInfoRef.current[currentTrackId] : null
|
||||
const nextDurationMs = durationMs > 0 ? durationMs : previousTrackInfo?.durationMs || 0
|
||||
|
||||
const activePendingSeekMs =
|
||||
pendingSeekPosRef.current !== null
|
||||
? Platform.OS === 'web'
|
||||
? pendingSeekPosRef.current
|
||||
: pendingSeekPosRef.current * 1000
|
||||
: null
|
||||
const queuedPendingSeekMs =
|
||||
pendingSeekValueRef.current !== null
|
||||
? Platform.OS === 'web'
|
||||
? pendingSeekValueRef.current
|
||||
: pendingSeekValueRef.current * 1000
|
||||
: null
|
||||
const rawEffectivePositionMs = currentTrackId
|
||||
? activePendingSeekMs ?? queuedPendingSeekMs ?? positionMs
|
||||
: 0
|
||||
const positionQuantizationStep =
|
||||
activePendingSeekMs !== null || queuedPendingSeekMs !== null ? 1 : 80
|
||||
const effectivePositionMs = Math.round(rawEffectivePositionMs / positionQuantizationStep) *
|
||||
positionQuantizationStep
|
||||
const effectiveDurationMs = currentTrackId ? nextDurationMs : 0
|
||||
const effectiveIsPlaying = currentTrackId ? isPlaying : false
|
||||
const effectiveIsBuffering = currentTrackId ? isBuffering : false
|
||||
|
||||
setPlayback((prev) => {
|
||||
let nextStatus = prev.status
|
||||
if (!currentTrack) {
|
||||
if (!currentTrackId) {
|
||||
nextStatus = 'idle'
|
||||
} else if (!isLoaded) {
|
||||
nextStatus = 'loading'
|
||||
@@ -380,49 +470,83 @@ const PlayerProvider = ({ children }) => {
|
||||
nextStatus = 'paused'
|
||||
}
|
||||
|
||||
if (
|
||||
prev.status === nextStatus &&
|
||||
prev.isPlaying === effectiveIsPlaying &&
|
||||
prev.isBuffering === effectiveIsBuffering &&
|
||||
prev.positionMs === effectivePositionMs &&
|
||||
prev.durationMs === effectiveDurationMs
|
||||
) {
|
||||
return prev
|
||||
}
|
||||
|
||||
return {
|
||||
status: nextStatus,
|
||||
isPlaying,
|
||||
isBuffering,
|
||||
positionMs,
|
||||
durationMs,
|
||||
isPlaying: effectiveIsPlaying,
|
||||
isBuffering: effectiveIsBuffering,
|
||||
positionMs: effectivePositionMs,
|
||||
durationMs: effectiveDurationMs,
|
||||
}
|
||||
})
|
||||
|
||||
if (currentTrack?.id) {
|
||||
trackInfoRef.current[currentTrack.id] = {
|
||||
durationMs:
|
||||
durationMs > 0 ? durationMs : trackInfoRef.current[currentTrack.id]?.durationMs || 0,
|
||||
positionMs,
|
||||
isLoaded,
|
||||
updatedAt: Date.now(),
|
||||
if (currentTrackId) {
|
||||
if (
|
||||
!previousTrackInfo ||
|
||||
previousTrackInfo.durationMs !== nextDurationMs ||
|
||||
previousTrackInfo.positionMs !== positionMs ||
|
||||
previousTrackInfo.isLoaded !== isLoaded
|
||||
) {
|
||||
trackInfoRef.current[currentTrackId] = {
|
||||
durationMs: nextDurationMs,
|
||||
positionMs,
|
||||
isLoaded,
|
||||
updatedAt: Date.now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [status, currentTrack])
|
||||
}, [
|
||||
currentTrackId,
|
||||
status?.duration,
|
||||
status?.currentTime,
|
||||
status?.isLoaded,
|
||||
status?.playing,
|
||||
status?.isBuffering,
|
||||
status?.didJustFinish,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (!player || !currentTrack) return
|
||||
if (!player || !currentTrackId || !isStatusLoaded) return
|
||||
|
||||
const run = async () => {
|
||||
try {
|
||||
const pendingSeek = pendingSeekValueRef.current
|
||||
if (isApplyingPendingSeekRef.current) return
|
||||
|
||||
const pendingSeek = pendingSeekValueRef.current
|
||||
if (typeof pendingSeek === 'number' && pendingSeek >= 0) {
|
||||
pendingSeekValueRef.current = null
|
||||
|
||||
if (typeof pendingSeek === 'number' && pendingSeek >= 0) {
|
||||
isApplyingPendingSeekRef.current = true
|
||||
try {
|
||||
await player.seekTo?.(pendingSeek)
|
||||
} catch (err) {
|
||||
pendingSeekValueRef.current = pendingSeek
|
||||
reportError(err)
|
||||
} finally {
|
||||
isApplyingPendingSeekRef.current = false
|
||||
}
|
||||
}
|
||||
|
||||
if (autoPlayRef.current) {
|
||||
autoPlayRef.current = false
|
||||
if (autoPlayRef.current) {
|
||||
autoPlayRef.current = false
|
||||
try {
|
||||
await player.play?.()
|
||||
} catch (err) {
|
||||
autoPlayRef.current = true
|
||||
reportError(err)
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err)
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
||||
}, [player, currentTrack?.id])
|
||||
}, [player, currentTrackId, isStatusLoaded])
|
||||
|
||||
useEffect(() => {
|
||||
if (!player) return
|
||||
@@ -432,8 +556,17 @@ const PlayerProvider = ({ children }) => {
|
||||
}, [player, isLooping])
|
||||
|
||||
const seekDebounceRef = useRef(null)
|
||||
const pendingSeekPosRef = useRef(null)
|
||||
const isSeekingRef = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (seekDebounceRef.current) {
|
||||
clearTimeout(seekDebounceRef.current)
|
||||
seekDebounceRef.current = null
|
||||
}
|
||||
pendingSeekPosRef.current = null
|
||||
isSeekingRef.current = false
|
||||
}
|
||||
}, [player])
|
||||
|
||||
const waitForActiveSeek = useCallback(async () => {
|
||||
if (!isSeekingRef.current) return
|
||||
@@ -458,23 +591,17 @@ const PlayerProvider = ({ children }) => {
|
||||
// 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) {
|
||||
if (isStatusLoaded) {
|
||||
await player.seekTo?.(seekValue)
|
||||
} else {
|
||||
pendingSeekValueRef.current = seekValue
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err)
|
||||
reportError(err)
|
||||
} finally {
|
||||
isSeekingRef.current = false
|
||||
pendingSeekPosRef.current = null
|
||||
@@ -483,7 +610,7 @@ const PlayerProvider = ({ children }) => {
|
||||
}, 100) // 100ms debounce
|
||||
})
|
||||
},
|
||||
[player, status?.isLoaded, toPlayerSeekValue]
|
||||
[player, isStatusLoaded, toPlayerSeekValue]
|
||||
)
|
||||
|
||||
const seekBy = useCallback(
|
||||
@@ -512,13 +639,13 @@ const PlayerProvider = ({ children }) => {
|
||||
pendingSeekPosRef.current = null
|
||||
try {
|
||||
isSeekingRef.current = true
|
||||
if (status?.isLoaded) {
|
||||
if (isStatusLoaded) {
|
||||
await player.seekTo?.(seekValue)
|
||||
} else {
|
||||
pendingSeekValueRef.current = seekValue
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err)
|
||||
reportError(err)
|
||||
} finally {
|
||||
isSeekingRef.current = false
|
||||
}
|
||||
@@ -526,17 +653,43 @@ const PlayerProvider = ({ children }) => {
|
||||
|
||||
// 2. Wait for any active seek to complete
|
||||
await waitForActiveSeek()
|
||||
}, [player, status?.isLoaded, waitForActiveSeek])
|
||||
}, [player, isStatusLoaded, waitForActiveSeek])
|
||||
|
||||
const stopCurrentTrackBeforeSwitch = useCallback(async () => {
|
||||
autoPlayRef.current = false
|
||||
pendingSeekValueRef.current = null
|
||||
|
||||
if (seekDebounceRef.current) {
|
||||
clearTimeout(seekDebounceRef.current)
|
||||
seekDebounceRef.current = null
|
||||
}
|
||||
pendingSeekPosRef.current = null
|
||||
isSeekingRef.current = false
|
||||
isApplyingPendingSeekRef.current = false
|
||||
|
||||
try {
|
||||
await flushPendingSeek()
|
||||
} catch (_err) {}
|
||||
|
||||
try {
|
||||
await player?.pause?.()
|
||||
} catch (err) {
|
||||
reportError(err)
|
||||
}
|
||||
}, [flushPendingSeek, player, reportError])
|
||||
|
||||
const play = useCallback(
|
||||
async (trackInput, options = {}) => {
|
||||
const requestId = playRequestIdRef.current + 1
|
||||
playRequestIdRef.current = requestId
|
||||
|
||||
const normalized = normalizeTrack(trackInput, options)
|
||||
if (!normalized.source) {
|
||||
console.warn('PlayerProvider: impossible de lancer la lecture, source audio manquante')
|
||||
return
|
||||
}
|
||||
|
||||
const sameTrack = currentTrack?.id && normalized.id === currentTrack.id
|
||||
const sameTrack = currentTrackId && normalized.id === currentTrackId
|
||||
const targetPositionMs =
|
||||
typeof options?.startPositionMs === 'number'
|
||||
? options.startPositionMs
|
||||
@@ -548,6 +701,13 @@ const PlayerProvider = ({ children }) => {
|
||||
|
||||
setError(null)
|
||||
|
||||
if (seekDebounceRef.current) {
|
||||
clearTimeout(seekDebounceRef.current)
|
||||
seekDebounceRef.current = null
|
||||
}
|
||||
pendingSeekPosRef.current = null
|
||||
isSeekingRef.current = false
|
||||
|
||||
if (Array.isArray(options.queue)) {
|
||||
updateQueue(options.queue, {
|
||||
id: options.queueId,
|
||||
@@ -559,19 +719,43 @@ const PlayerProvider = ({ children }) => {
|
||||
}
|
||||
|
||||
if (sameTrack) {
|
||||
setCurrentTrack((prev) => ({ ...prev, ...normalized }))
|
||||
if (requestId !== playRequestIdRef.current) return
|
||||
|
||||
setCurrentTrack((prev) => {
|
||||
if (!prev || prev.id !== normalized.id) {
|
||||
return normalized
|
||||
}
|
||||
const preservedSource = areSourcesEquivalent(prev.source, normalized.source)
|
||||
? prev.source
|
||||
: normalized.source
|
||||
const merged = {
|
||||
...prev,
|
||||
...normalized,
|
||||
source: preservedSource,
|
||||
}
|
||||
const unchanged =
|
||||
prev.title === merged.title &&
|
||||
prev.artist === merged.artist &&
|
||||
prev.artwork === merged.artwork &&
|
||||
prev.coverUrl === merged.coverUrl &&
|
||||
prev.source === merged.source
|
||||
return unchanged ? prev : merged
|
||||
})
|
||||
|
||||
try {
|
||||
// Flush any pending seek first
|
||||
await flushPendingSeek()
|
||||
if (requestId !== playRequestIdRef.current) return
|
||||
|
||||
if (targetPositionMs > 0) {
|
||||
await player?.seekTo?.(targetSeekValue)
|
||||
if (requestId !== playRequestIdRef.current) return
|
||||
}
|
||||
if (shouldAutoPlay) {
|
||||
await player?.play?.()
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err)
|
||||
reportError(err)
|
||||
}
|
||||
if (!shouldAutoPlay) {
|
||||
setPlayback((prev) => ({
|
||||
@@ -584,12 +768,31 @@ const PlayerProvider = ({ children }) => {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (player?.playing) {
|
||||
await player.pause?.()
|
||||
}
|
||||
} catch (err) {
|
||||
setError(err)
|
||||
if (currentTrackId) {
|
||||
await stopCurrentTrackBeforeSwitch()
|
||||
if (requestId !== playRequestIdRef.current) return
|
||||
|
||||
setPlayback((prev) => {
|
||||
if (
|
||||
prev.status === 'idle' &&
|
||||
!prev.isPlaying &&
|
||||
!prev.isBuffering &&
|
||||
prev.positionMs === 0 &&
|
||||
prev.durationMs === 0
|
||||
) {
|
||||
return prev
|
||||
}
|
||||
return {
|
||||
status: 'idle',
|
||||
isPlaying: false,
|
||||
isBuffering: false,
|
||||
positionMs: 0,
|
||||
durationMs: 0,
|
||||
}
|
||||
})
|
||||
setCurrentTrack(null)
|
||||
await waitForNextTick()
|
||||
if (requestId !== playRequestIdRef.current) return
|
||||
}
|
||||
|
||||
autoPlayRef.current = shouldAutoPlay
|
||||
@@ -601,9 +804,35 @@ const PlayerProvider = ({ children }) => {
|
||||
isPlaying: false,
|
||||
positionMs: targetPositionMs,
|
||||
}))
|
||||
setCurrentTrack(normalized)
|
||||
setCurrentTrack((prev) => {
|
||||
if (!prev) return normalized
|
||||
if (prev.id !== normalized.id) return normalized
|
||||
const preservedSource = areSourcesEquivalent(prev.source, normalized.source)
|
||||
? prev.source
|
||||
: normalized.source
|
||||
const merged = {
|
||||
...prev,
|
||||
...normalized,
|
||||
source: preservedSource,
|
||||
}
|
||||
const unchanged =
|
||||
prev.title === merged.title &&
|
||||
prev.artist === merged.artist &&
|
||||
prev.artwork === merged.artwork &&
|
||||
prev.coverUrl === merged.coverUrl &&
|
||||
prev.source === merged.source
|
||||
return unchanged ? prev : merged
|
||||
})
|
||||
},
|
||||
[currentTrack?.id, player, toPlayerSeekValue, updateQueue, flushPendingSeek]
|
||||
[
|
||||
currentTrackId,
|
||||
player,
|
||||
toPlayerSeekValue,
|
||||
updateQueue,
|
||||
flushPendingSeek,
|
||||
stopCurrentTrackBeforeSwitch,
|
||||
reportError,
|
||||
]
|
||||
)
|
||||
|
||||
const resume = useCallback(async () => {
|
||||
@@ -612,9 +841,9 @@ const PlayerProvider = ({ children }) => {
|
||||
await flushPendingSeek()
|
||||
await player?.play?.()
|
||||
} catch (err) {
|
||||
setError(err)
|
||||
reportError(err)
|
||||
}
|
||||
}, [player, currentTrack, flushPendingSeek])
|
||||
}, [player, currentTrack, flushPendingSeek, reportError])
|
||||
|
||||
const pause = useCallback(async () => {
|
||||
try {
|
||||
@@ -622,9 +851,9 @@ const PlayerProvider = ({ children }) => {
|
||||
pendingSeekValueRef.current = null
|
||||
await player?.pause?.()
|
||||
} catch (err) {
|
||||
setError(err)
|
||||
reportError(err)
|
||||
}
|
||||
}, [player])
|
||||
}, [player, reportError])
|
||||
|
||||
const togglePlay = useCallback(
|
||||
async (trackInput, options = {}) => {
|
||||
@@ -651,13 +880,21 @@ const PlayerProvider = ({ children }) => {
|
||||
)
|
||||
|
||||
const stop = useCallback(async () => {
|
||||
playRequestIdRef.current += 1
|
||||
try {
|
||||
autoPlayRef.current = false
|
||||
pendingSeekValueRef.current = null
|
||||
await player?.pause?.()
|
||||
} catch (err) {
|
||||
setError(err)
|
||||
reportError(err)
|
||||
} finally {
|
||||
if (seekDebounceRef.current) {
|
||||
clearTimeout(seekDebounceRef.current)
|
||||
seekDebounceRef.current = null
|
||||
}
|
||||
pendingSeekPosRef.current = null
|
||||
isSeekingRef.current = false
|
||||
isApplyingPendingSeekRef.current = false
|
||||
setCurrentTrack(null)
|
||||
setPlayback({
|
||||
status: 'idle',
|
||||
@@ -667,7 +904,7 @@ const PlayerProvider = ({ children }) => {
|
||||
durationMs: 0,
|
||||
})
|
||||
}
|
||||
}, [player])
|
||||
}, [player, reportError])
|
||||
|
||||
const setLooping = useCallback((value) => {
|
||||
setIsLooping(!!value)
|
||||
@@ -682,17 +919,17 @@ const PlayerProvider = ({ children }) => {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentTrack?.id) {
|
||||
if (!currentTrackId) {
|
||||
if (queueIndexRef.current !== -1) {
|
||||
setQueueIndexValue(-1)
|
||||
}
|
||||
return
|
||||
}
|
||||
const idx = queueRef.current.findIndex((item) => item.id === currentTrack.id)
|
||||
const idx = queueRef.current.findIndex((item) => item.id === currentTrackId)
|
||||
if (idx !== queueIndexRef.current) {
|
||||
setQueueIndexValue(idx)
|
||||
}
|
||||
}, [currentTrack?.id, setQueueIndexValue])
|
||||
}, [currentTrackId, setQueueIndexValue])
|
||||
|
||||
const handleTrackDidFinish = useCallback(() => {
|
||||
if (isLooping) return
|
||||
@@ -700,7 +937,7 @@ const PlayerProvider = ({ children }) => {
|
||||
if (!Array.isArray(items) || items.length === 0) return
|
||||
|
||||
let idx = queueIndexRef.current
|
||||
const currentId = currentTrack?.id ?? null
|
||||
const currentId = currentTrackId
|
||||
if ((idx == null || idx < 0) && currentId) {
|
||||
idx = items.findIndex((item) => item.id === currentId)
|
||||
}
|
||||
@@ -718,12 +955,12 @@ const PlayerProvider = ({ children }) => {
|
||||
queueType: queueInfoRef.current.type,
|
||||
queueName: queueInfoRef.current.name,
|
||||
}).catch((err) => {
|
||||
setError(err)
|
||||
reportError(err)
|
||||
})
|
||||
}, [currentTrack?.id, isLooping, play])
|
||||
}, [currentTrackId, isLooping, play, reportError])
|
||||
|
||||
useEffect(() => {
|
||||
const finished = !!status?.didJustFinish
|
||||
const finished = statusDidJustFinish
|
||||
if (finished) {
|
||||
if (!didJustFinishRef.current) {
|
||||
didJustFinishRef.current = true
|
||||
@@ -732,7 +969,7 @@ const PlayerProvider = ({ children }) => {
|
||||
} else {
|
||||
didJustFinishRef.current = false
|
||||
}
|
||||
}, [status?.didJustFinish, handleTrackDidFinish])
|
||||
}, [statusDidJustFinish, handleTrackDidFinish])
|
||||
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useDataFromRef } from 'react-native-minuit/src/hooks'
|
||||
import useMinuit from 'react-native-minuit/src/hooks/useMinuit'
|
||||
import { createContext, useContext, useGlobal } from 'reactn'
|
||||
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { checkIfEmailIsValid } from '../actions/signupActions'
|
||||
import { showPremiumRequiredAlert } from '../components/Alert'
|
||||
import firebase, {
|
||||
@@ -126,6 +126,7 @@ export default ({ children }) => {
|
||||
|
||||
// Selected project state and helpers
|
||||
const [selectedProjectId, setSelectedProjectId] = useState(null)
|
||||
const selectedProjectIdRef = useRef(null)
|
||||
const { data: selectedProject = null, setData: setSelectedProject } = useDataFromRef({
|
||||
ref: selectedProjectId ? projectsRef.doc(selectedProjectId) : null,
|
||||
simpleRef: true,
|
||||
@@ -134,6 +135,10 @@ export default ({ children }) => {
|
||||
refreshArray: [selectedProjectId],
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
selectedProjectIdRef.current = selectedProjectId
|
||||
}, [selectedProjectId])
|
||||
|
||||
const persistSelectedProjectId = useCallback(
|
||||
async (projectId) => {
|
||||
if (!currentUID) return
|
||||
@@ -144,7 +149,6 @@ export default ({ children }) => {
|
||||
},
|
||||
{ merge: true }
|
||||
)
|
||||
console.log('update selected project')
|
||||
} catch (error) {
|
||||
console.log(
|
||||
'UserDataProvider: unable to persist selectedProjectId',
|
||||
@@ -158,6 +162,14 @@ export default ({ children }) => {
|
||||
const selectProject = useCallback(
|
||||
(projectId) => {
|
||||
const safeId = projectId || null
|
||||
if (safeId === selectedProjectIdRef.current) {
|
||||
if (!safeId) {
|
||||
setSelectedProject(null)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
selectedProjectIdRef.current = safeId
|
||||
setSelectedProjectId(safeId)
|
||||
if (!safeId) {
|
||||
setSelectedProject(null)
|
||||
|
||||
Reference in New Issue
Block a user