Files
desa-darmasaba/src/app/darmasaba/(pages)/musik/lib/useAudioProgress.ts
nico a5bd91b580 feat(berita): add multiple images gallery and YouTube video support
- Update schema: add images relation list and linkVideo field
- API: support multiple image upload and YouTube link in create/update
- Admin create page: add gallery upload (max 10) and YouTube embed preview
- Admin edit page: manage existing/new gallery images and YouTube link
- Admin detail page: display gallery grid and YouTube video embed
- Public detail page: show gallery images and YouTube video with responsive layout
- State: add imageIds[] and linkVideo fields with proper type handling
- Music player: fix seek functionality and ESLint warnings

Breaking changes:
- Prisma schema updated - requires migration
- API create/update endpoints now expect imageIds array and linkVideo string

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-03-02 16:06:53 +08:00

146 lines
3.8 KiB
TypeScript

import { useRef, useEffect, useCallback } from 'react';
/**
* Custom hook untuk smooth audio progress update menggunakan requestAnimationFrame
* Lebih smooth dan reliable dibanding onTimeUpdate event
*/
export function useAudioProgress(
audioRef: React.RefObject<HTMLAudioElement>,
isPlaying: boolean,
setCurrentTime: (time: number) => void,
isSeekingRef: React.RefObject<boolean>
) {
const rafRef = useRef<number | null>(null);
const lastTimeRef = useRef<number>(0);
const updateProgress = useCallback(() => {
if (!audioRef.current || audioRef.current.paused || isSeekingRef.current) {
rafRef.current = requestAnimationFrame(updateProgress);
return;
}
const audio = audioRef.current;
const currentTime = Math.floor(audio.currentTime);
// Hanya update state jika waktu berubah
if (currentTime !== lastTimeRef.current) {
lastTimeRef.current = currentTime;
setCurrentTime(currentTime);
}
rafRef.current = requestAnimationFrame(updateProgress);
}, [audioRef, setCurrentTime, isSeekingRef]);
useEffect(() => {
if (isPlaying) {
rafRef.current = requestAnimationFrame(updateProgress);
} else if (rafRef.current) {
cancelAnimationFrame(rafRef.current);
}
return () => {
if (rafRef.current) {
cancelAnimationFrame(rafRef.current);
}
};
}, [isPlaying, updateProgress]);
return rafRef;
}
// 'use client'
// import { useEffect, useRef, useState, useCallback } from 'react';
// export function useAudioEngine() {
// const audioRef = useRef<HTMLAudioElement | null>(null);
// const rafRef = useRef<number | null>(null);
// const isSeekingRef = useRef(false);
// const [isPlaying, setIsPlaying] = useState(false);
// const [currentTime, setCurrentTime] = useState(0);
// const [duration, setDuration] = useState(0);
// const load = useCallback((src: string) => {
// if (!audioRef.current) return;
// audioRef.current.src = src;
// audioRef.current.load();
// setCurrentTime(0);
// }, []);
// const play = async () => {
// if (!audioRef.current) return;
// await audioRef.current.play();
// setIsPlaying(true);
// };
// const pause = () => {
// if (!audioRef.current) return;
// audioRef.current.pause();
// setIsPlaying(false);
// };
// const toggle = () => {
// if (!audioRef.current) return;
// audioRef.current.paused ? play() : pause();
// };
// const seek = (time: number) => {
// if (!audioRef.current) return;
// isSeekingRef.current = true;
// audioRef.current.currentTime = time;
// setCurrentTime(time);
// requestAnimationFrame(() => {
// isSeekingRef.current = false;
// });
// };
// useEffect(() => {
// if (!audioRef.current) return;
// const audio = audioRef.current;
// const onLoaded = () => {
// setDuration(Math.floor(audio.duration));
// };
// const onEnded = () => {
// setIsPlaying(false);
// setCurrentTime(0);
// };
// audio.addEventListener('loadedmetadata', onLoaded);
// audio.addEventListener('ended', onEnded);
// return () => {
// audio.removeEventListener('loadedmetadata', onLoaded);
// audio.removeEventListener('ended', onEnded);
// };
// }, []);
// useEffect(() => {
// const loop = () => {
// if (
// audioRef.current &&
// !audioRef.current.paused &&
// !isSeekingRef.current
// ) {
// setCurrentTime(Math.floor(audioRef.current.currentTime));
// }
// rafRef.current = requestAnimationFrame(loop);
// };
// rafRef.current = requestAnimationFrame(loop);
// return () => {
// if (rafRef.current) cancelAnimationFrame(rafRef.current);
// };
// }, []);
// return {
// audioRef,
// isPlaying,
// currentTime,
// duration,
// load,
// toggle,
// seek,
// };
// }