Loading mobile/src/contexts/audio_context.tsx 0 → 100644 +88 −0 Original line number Diff line number Diff line import { PropsWithChildren, createContext, useCallback, useContext, useEffect, useRef, useState } from "react"; import { AVPlaybackSource, AVPlaybackStatus, Audio } from "expo-av"; type AudioContextType = { loadAudio: (source?: AVPlaybackSource) => Promise<void>; togglePlay: () => Promise<void>; isPlaying: boolean; duration: number; position: number; onValueChange: (value: number) => void; onUnmount: () => void; } const AudioContext = createContext( { loadAudio: async (source?: AVPlaybackSource) => {}, togglePlay: async () => {}, isPlaying: false, duration: 0, position: 0, onValueChange: (value: number) => {}, onUnmount: () => {} } as AudioContextType ); type AudioContextProviderProps = PropsWithChildren<{}>; export const AudioContextProvider = ({children}: AudioContextProviderProps) => { const soundRef = useRef<Audio.Sound | null>(null); const [isPlaying, setIsPlaying] = useState(false); const [duration, setDuration] = useState(0); const [position, setPosition] = useState(0); const onPlaybackStatusUpdate = (status: AVPlaybackStatus) => { status.isLoaded && setPosition(status.positionMillis); } const onValueChange = (value: number) => { if (soundRef.current) { soundRef.current.setPositionAsync(value); } } const loadAudio = async (source?: AVPlaybackSource) => { if (!source) return; const { sound, status } = await Audio.Sound.createAsync(source, { shouldPlay: false }); if (status.isLoaded) { sound.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate); setDuration(status.durationMillis || 0); soundRef.current = sound; setIsPlaying(status.isPlaying); console.log('audio loaded'); } } const togglePlay = async () => { if (soundRef.current) { if (isPlaying) { await soundRef.current.pauseAsync(); } else { await soundRef.current.playAsync(); } setIsPlaying(!isPlaying); } } const onUnmount = () => { if (soundRef.current) { soundRef.current.unloadAsync(); } } return ( <AudioContext.Provider value={{ loadAudio, togglePlay, isPlaying, duration, position, onValueChange, onUnmount}}> {children} </AudioContext.Provider> ); } export const useAudio = () => { const context = useContext(AudioContext); if (!context) { throw new Error("useAudio must be used within an AudioContextProvider"); } return context; } No newline at end of file Loading
mobile/src/contexts/audio_context.tsx 0 → 100644 +88 −0 Original line number Diff line number Diff line import { PropsWithChildren, createContext, useCallback, useContext, useEffect, useRef, useState } from "react"; import { AVPlaybackSource, AVPlaybackStatus, Audio } from "expo-av"; type AudioContextType = { loadAudio: (source?: AVPlaybackSource) => Promise<void>; togglePlay: () => Promise<void>; isPlaying: boolean; duration: number; position: number; onValueChange: (value: number) => void; onUnmount: () => void; } const AudioContext = createContext( { loadAudio: async (source?: AVPlaybackSource) => {}, togglePlay: async () => {}, isPlaying: false, duration: 0, position: 0, onValueChange: (value: number) => {}, onUnmount: () => {} } as AudioContextType ); type AudioContextProviderProps = PropsWithChildren<{}>; export const AudioContextProvider = ({children}: AudioContextProviderProps) => { const soundRef = useRef<Audio.Sound | null>(null); const [isPlaying, setIsPlaying] = useState(false); const [duration, setDuration] = useState(0); const [position, setPosition] = useState(0); const onPlaybackStatusUpdate = (status: AVPlaybackStatus) => { status.isLoaded && setPosition(status.positionMillis); } const onValueChange = (value: number) => { if (soundRef.current) { soundRef.current.setPositionAsync(value); } } const loadAudio = async (source?: AVPlaybackSource) => { if (!source) return; const { sound, status } = await Audio.Sound.createAsync(source, { shouldPlay: false }); if (status.isLoaded) { sound.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate); setDuration(status.durationMillis || 0); soundRef.current = sound; setIsPlaying(status.isPlaying); console.log('audio loaded'); } } const togglePlay = async () => { if (soundRef.current) { if (isPlaying) { await soundRef.current.pauseAsync(); } else { await soundRef.current.playAsync(); } setIsPlaying(!isPlaying); } } const onUnmount = () => { if (soundRef.current) { soundRef.current.unloadAsync(); } } return ( <AudioContext.Provider value={{ loadAudio, togglePlay, isPlaying, duration, position, onValueChange, onUnmount}}> {children} </AudioContext.Provider> ); } export const useAudio = () => { const context = useContext(AudioContext); if (!context) { throw new Error("useAudio must be used within an AudioContextProvider"); } return context; } No newline at end of file