fix(musik): fix seek slider reset ke 0 - root cause: useEffect dependency
ROOT CAUSE: - filteredMusik di-calculate ulang setiap render (.filter() tanpa memoization) - currentSong = filteredMusik[currentSongIndex] → object reference baru setiap render - useEffect dependency [currentSong, currentSongIndex] trigger setiap render - useEffect reset setCurrentTime(0) → slider kembali ke awal FIX: 1. useMemo untuk filteredMusik - mencegah re-calculate setiap render 2. useEffect dependency [currentSong?.id, currentSongIndex] - hanya trigger saat lagu benar-benar berubah 3. Hapus semua debug console.log yang tidak diperlukan 4. Simplifikasi seekTo function File Changed: - src/app/darmasaba/(pages)/musik/musik-desa/page.tsx - src/app/darmasaba/(pages)/musik/lib/seek.ts Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -3,23 +3,13 @@ export function seekTo(
|
|||||||
time: number,
|
time: number,
|
||||||
setCurrentTime?: (v: number) => void
|
setCurrentTime?: (v: number) => void
|
||||||
) {
|
) {
|
||||||
if (!audioRef.current) {
|
if (!audioRef.current) return;
|
||||||
console.error('[seekTo] Audio element tidak ada!');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('[seekTo] Before seek - currentTime:', audioRef.current.currentTime, 'Target:', time);
|
|
||||||
|
|
||||||
// Set waktu audio
|
// Set waktu audio
|
||||||
audioRef.current.currentTime = time;
|
audioRef.current.currentTime = time;
|
||||||
|
|
||||||
console.log('[seekTo] After seek - currentTime:', audioRef.current.currentTime);
|
|
||||||
|
|
||||||
// Update state jika provided
|
// Update state jika provided
|
||||||
if (setCurrentTime) {
|
if (setCurrentTime) {
|
||||||
setCurrentTime(Math.round(time));
|
setCurrentTime(Math.round(time));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug log
|
|
||||||
console.log('[seekTo] Seek to:', time, 'seconds');
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { ActionIcon, Avatar, Badge, Box, Card, Flex, Grid, Group, Paper, ScrollArea, Slider, Stack, Text, TextInput } from '@mantine/core';
|
import { ActionIcon, Avatar, Badge, Box, Card, Flex, Grid, Group, Paper, ScrollArea, Slider, Stack, Text, TextInput } from '@mantine/core';
|
||||||
import { IconArrowsShuffle, IconPlayerPauseFilled, IconPlayerPlayFilled, IconPlayerSkipBackFilled, IconPlayerSkipForwardFilled, IconRepeat, IconRepeatOff, IconSearch, IconVolume, IconVolumeOff, IconX } from '@tabler/icons-react';
|
import { IconArrowsShuffle, IconPlayerPauseFilled, IconPlayerPlayFilled, IconPlayerSkipBackFilled, IconPlayerSkipForwardFilled, IconRepeat, IconRepeatOff, IconSearch, IconVolume, IconVolumeOff, IconX } from '@tabler/icons-react';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||||
import { togglePlayPause } from '../lib/playPause';
|
import { togglePlayPause } from '../lib/playPause';
|
||||||
import { getNextIndex, getPrevIndex } from '../lib/nextPrev';
|
import { getNextIndex, getPrevIndex } from '../lib/nextPrev';
|
||||||
@@ -68,12 +68,14 @@ const MusicPlayer = () => {
|
|||||||
fetchMusik();
|
fetchMusik();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Filter musik based on search
|
// Filter musik based on search - gunakan useMemo untuk mencegah re-calculate setiap render
|
||||||
const filteredMusik = musikData.filter(musik =>
|
const filteredMusik = useMemo(() => {
|
||||||
musik.judul.toLowerCase().includes(search.toLowerCase()) ||
|
return musikData.filter(musik =>
|
||||||
musik.artis.toLowerCase().includes(search.toLowerCase()) ||
|
musik.judul.toLowerCase().includes(search.toLowerCase()) ||
|
||||||
(musik.genre && musik.genre.toLowerCase().includes(search.toLowerCase()))
|
musik.artis.toLowerCase().includes(search.toLowerCase()) ||
|
||||||
);
|
(musik.genre && musik.genre.toLowerCase().includes(search.toLowerCase()))
|
||||||
|
);
|
||||||
|
}, [musikData, search]);
|
||||||
|
|
||||||
const currentSong = currentSongIndex >= 0 && currentSongIndex < filteredMusik.length
|
const currentSong = currentSongIndex >= 0 && currentSongIndex < filteredMusik.length
|
||||||
? filteredMusik[currentSongIndex]
|
? filteredMusik[currentSongIndex]
|
||||||
@@ -103,12 +105,15 @@ const MusicPlayer = () => {
|
|||||||
// Update duration when song changes
|
// Update duration when song changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentSong && audioRef.current) {
|
if (currentSong && audioRef.current) {
|
||||||
console.log('[useEffect Song Change] currentSong:', currentSong.judul, 'Index:', currentSongIndex);
|
|
||||||
// Gunakan durasi dari database sebagai acuan utama
|
// Gunakan durasi dari database sebagai acuan utama
|
||||||
const durationParts = currentSong.durasi.split(':');
|
const durationParts = currentSong.durasi.split(':');
|
||||||
const durationInSeconds = parseInt(durationParts[0]) * 60 + parseInt(durationParts[1]);
|
const durationInSeconds = parseInt(durationParts[0]) * 60 + parseInt(durationParts[1]);
|
||||||
setDuration(durationInSeconds);
|
setDuration(durationInSeconds);
|
||||||
|
|
||||||
|
// Reset audio currentTime ke 0 hanya untuk lagu baru
|
||||||
|
audioRef.current.currentTime = 0;
|
||||||
setCurrentTime(0);
|
setCurrentTime(0);
|
||||||
|
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
audioRef.current.play().catch(err => {
|
audioRef.current.play().catch(err => {
|
||||||
console.error('Error playing audio:', err);
|
console.error('Error playing audio:', err);
|
||||||
@@ -116,7 +121,7 @@ const MusicPlayer = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [currentSongIndex, currentSong, isPlaying]);
|
}, [currentSong?.id, currentSongIndex]);
|
||||||
|
|
||||||
const formatTime = (seconds: number) => {
|
const formatTime = (seconds: number) => {
|
||||||
const mins = Math.floor(seconds / 60);
|
const mins = Math.floor(seconds / 60);
|
||||||
@@ -162,7 +167,6 @@ const MusicPlayer = () => {
|
|||||||
const skipBack = () => {
|
const skipBack = () => {
|
||||||
const prevIndex = getPrevIndex(currentSongIndex, filteredMusik.length, isShuffle);
|
const prevIndex = getPrevIndex(currentSongIndex, filteredMusik.length, isShuffle);
|
||||||
if (prevIndex >= 0) {
|
if (prevIndex >= 0) {
|
||||||
console.log('[skipBack] Changing song to index:', prevIndex);
|
|
||||||
playSong(prevIndex);
|
playSong(prevIndex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -170,7 +174,6 @@ const MusicPlayer = () => {
|
|||||||
const skipForward = () => {
|
const skipForward = () => {
|
||||||
const nextIndex = getNextIndex(currentSongIndex, filteredMusik.length, isShuffle);
|
const nextIndex = getNextIndex(currentSongIndex, filteredMusik.length, isShuffle);
|
||||||
if (nextIndex >= 0) {
|
if (nextIndex >= 0) {
|
||||||
console.log('[skipForward] Changing song to index:', nextIndex);
|
|
||||||
playSong(nextIndex);
|
playSong(nextIndex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -204,7 +207,6 @@ const MusicPlayer = () => {
|
|||||||
src={currentSong.audioFile.link}
|
src={currentSong.audioFile.link}
|
||||||
muted={isMuted}
|
muted={isMuted}
|
||||||
onLoadedMetadata={(e) => {
|
onLoadedMetadata={(e) => {
|
||||||
console.log('[onLoadedMetadata] Audio loaded, duration:', e.currentTarget.duration);
|
|
||||||
// Jangan override duration dari database
|
// Jangan override duration dari database
|
||||||
// Audio element duration bisa berbeda beberapa ms
|
// Audio element duration bisa berbeda beberapa ms
|
||||||
if (audioRef.current) {
|
if (audioRef.current) {
|
||||||
@@ -216,7 +218,6 @@ const MusicPlayer = () => {
|
|||||||
// Gunakan pembulatan yang lebih smooth
|
// Gunakan pembulatan yang lebih smooth
|
||||||
const time = audioRef.current.currentTime;
|
const time = audioRef.current.currentTime;
|
||||||
const roundedTime = Math.round(time);
|
const roundedTime = Math.round(time);
|
||||||
console.log('[onTimeUpdate] currentTime:', time, 'rounded:', roundedTime, 'isSeeking:', isSeeking);
|
|
||||||
setCurrentTime(roundedTime);
|
setCurrentTime(roundedTime);
|
||||||
}}
|
}}
|
||||||
onEnded={handleSongEnd}
|
onEnded={handleSongEnd}
|
||||||
@@ -281,12 +282,10 @@ const MusicPlayer = () => {
|
|||||||
setCurrentTime(v);
|
setCurrentTime(v);
|
||||||
}}
|
}}
|
||||||
onChangeEnd={(v) => {
|
onChangeEnd={(v) => {
|
||||||
console.log('[Slider Atas onChangeEnd] START - value:', v);
|
|
||||||
// Validasi: jangan seek melebihi durasi
|
// Validasi: jangan seek melebihi durasi
|
||||||
const seekTime = Math.min(Math.max(0, v), duration);
|
const seekTime = Math.min(Math.max(0, v), duration);
|
||||||
// Set seeking false DULUAN sebelum seekTo
|
// Set seeking false DULUAN sebelum seekTo
|
||||||
setIsSeeking(false);
|
setIsSeeking(false);
|
||||||
console.log('[Slider Atas onChangeEnd] Calling seekTo with:', seekTime);
|
|
||||||
seekTo(audioRef, seekTime, setCurrentTime);
|
seekTo(audioRef, seekTime, setCurrentTime);
|
||||||
}}
|
}}
|
||||||
color="#0B4F78"
|
color="#0B4F78"
|
||||||
@@ -428,11 +427,11 @@ const MusicPlayer = () => {
|
|||||||
setCurrentTime(v); // preview - update UI saja
|
setCurrentTime(v); // preview - update UI saja
|
||||||
}}
|
}}
|
||||||
onChangeEnd={(v) => {
|
onChangeEnd={(v) => {
|
||||||
setIsSeeking(false);
|
|
||||||
// Validasi: jangan seek melebihi durasi
|
// Validasi: jangan seek melebihi durasi
|
||||||
const seekTime = Math.min(Math.max(0, v), duration);
|
const seekTime = Math.min(Math.max(0, v), duration);
|
||||||
|
// Set seeking false DULUAN sebelum seekTo
|
||||||
|
setIsSeeking(false);
|
||||||
seekTo(audioRef, seekTime, setCurrentTime);
|
seekTo(audioRef, seekTime, setCurrentTime);
|
||||||
console.log('[Slider Bawah] Seek end:', seekTime, 'Duration:', duration);
|
|
||||||
}}
|
}}
|
||||||
color="#0B4F78"
|
color="#0B4F78"
|
||||||
size="xs"
|
size="xs"
|
||||||
|
|||||||
Reference in New Issue
Block a user