import * as React from "react"; import { Text, View, StyleSheet } from "react-native"; import { AntDesign } from "@expo/vector-icons"; import { useTheme } from "@draftbit/theme"; import Slider from "@react-native-community/slider"; import HeadlessAudioPlayer from "./HeadlessAudioPlayer"; import { AudioPlayerInterfaceProps, HeadlessAudioPlayerProps, } from "./AudioPlayerCommon"; import Pressable from "../../Pressable"; import type { MediaPlayerRef, MediaPlayerStatus } from "../MediaPlayerCommon"; /** * Built on top of HeadlessAudioPlayer to provide a simple interface for playing audio */ const AudioPlayerWithInterface = React.forwardRef< MediaPlayerRef, AudioPlayerInterfaceProps & HeadlessAudioPlayerProps >( ( { style, className, thumbColor, completedTrackColor, remainingTrackColor, togglePlaybackIconSize = 24, togglePlaybackIconColor, onPlaybackStatusUpdate: onPlaybackStatusUpdateProp, onPlaybackFinish: onPlaybackFinishProp, hidePlaybackIcon = false, hideDuration = false, hideSlider = false, ...rest }, ref ) => { const [isPlaying, setIsPlaying] = React.useState(false); const [isLoading, setIsLoading] = React.useState(false); const [durationMillis, setDurationMillis] = React.useState< number | undefined >(1); const [isDraggingSlider, setIsDraggingSlider] = React.useState(false); const [sliderPositionMillis, setSliderPositionMillis] = React.useState(0); const newHeadlessAudioPlayerRef = React.useRef(null); const theme = useTheme(); // Use the provided ref or default to new ref when not provided const headlessAudioPlayerRef = ref ? (ref as React.RefObject) : newHeadlessAudioPlayerRef; const { color, fontFamily, fontWeight, fontSize, lineHeight, letterSpacing, textTransform, textAlign, textDecorationLine, textDecorationColor, textDecorationStyle, ...viewStyles } = StyleSheet.flatten(style || {}); const textStyles = { color: color ?? (className ? undefined : theme.colors.text.strong), fontFamily, fontWeight, fontSize, lineHeight, letterSpacing, textTransform, textAlign, textDecorationLine, textDecorationColor, textDecorationStyle, }; const onPlaybackStatusUpdate = (status: MediaPlayerStatus) => { setIsLoading(status.isLoading); setDurationMillis(status.durationMillis); setSliderPositionMillis(status.currentPositionMillis); setIsPlaying(status.isPlaying); onPlaybackStatusUpdateProp?.(status); }; const onPlaybackFinish = () => { setIsPlaying(false); setSliderPositionMillis(0); headlessAudioPlayerRef.current?.togglePlayback(); headlessAudioPlayerRef.current?.seekToPosition(0); onPlaybackFinishProp?.(); }; const onSlidingComplete = (sliderValue: number) => { if (isDraggingSlider) { setIsDraggingSlider(false); } headlessAudioPlayerRef.current?.seekToPosition(sliderValue); }; const onSliderChange = () => { if (!isDraggingSlider) { setIsDraggingSlider(true); } }; let iconName; if (isLoading) { iconName = "loading"; } else if (isPlaying) { iconName = "pause"; } else { iconName = "caret-right"; } return ( <> {!hidePlaybackIcon && ( headlessAudioPlayerRef.current?.togglePlayback()} style={styles.spacingEnd} > )} {!hideDuration && ( {formatDuration(sliderPositionMillis ?? 0)} /{" "} {formatDuration(durationMillis || 0)} )} {!hideSlider && ( )} ); } ); const styles = StyleSheet.create({ container: { padding: 8, flexDirection: "row", alignItems: "center", borderRadius: 8, borderWidth: 1, }, spacingEnd: { marginEnd: 8, }, slider: { flex: 1, }, }); function formatDuration(duration: number) { if (duration === 0) return "00:00"; const seconds = Math.floor((duration / 1000) % 60); const minutes = Math.floor((duration / (1000 * 60)) % 60); const hours = Math.floor((duration / (1000 * 60 * 60)) % 24); const renderedHours = hours < 10 ? "0" + hours : hours; const renderedMinutes = minutes < 10 ? "0" + minutes : minutes; const renderedSeconds = seconds < 10 ? "0" + seconds : seconds; if (hours > 0) { return renderedHours + ":" + renderedMinutes + ":" + renderedSeconds; } return renderedMinutes + ":" + renderedSeconds; } export default AudioPlayerWithInterface;