Update UserProfile avatar size; add development-only debug tools in SettingsPage
This commit is contained in:
@@ -1 +1 @@
|
||||
NEXT_PUBLIC_COMMIT_SHA=74b9648
|
||||
NEXT_PUBLIC_COMMIT_SHA=25e9bd6
|
||||
|
||||
@@ -101,104 +101,89 @@ export const FullScreenPlayer: React.FC<FullScreenPlayerProps> = ({ isOpen, onCl
|
||||
}
|
||||
}, [lyrics, currentTime, currentLyricIndex]);
|
||||
|
||||
// Auto-scroll lyrics using lyricsRef
|
||||
// Auto-scroll lyrics using lyricsRef - Simplified for iOS compatibility
|
||||
useEffect(() => {
|
||||
// Only auto-scroll if lyrics are visible
|
||||
const shouldScroll = isMobile ? (activeTab === 'lyrics' && lyrics.length > 0) : (showLyrics && lyrics.length > 0);
|
||||
// Only auto-scroll if lyrics are visible and we're not on mobile to avoid iOS audio issues
|
||||
const shouldScroll = !isMobile && showLyrics && lyrics.length > 0;
|
||||
|
||||
if (currentLyricIndex >= 0 && shouldScroll && lyricsRef.current) {
|
||||
const scrollTimeout = setTimeout(() => {
|
||||
try {
|
||||
// Simplified scroll container detection for better iOS compatibility
|
||||
let scrollContainer = lyricsRef.current?.querySelector('[data-radix-scroll-area-viewport]') as HTMLElement;
|
||||
|
||||
// Fallback to the lyrics container itself on mobile (iOS)
|
||||
if (!scrollContainer && isMobile && lyricsRef.current) {
|
||||
scrollContainer = lyricsRef.current;
|
||||
}
|
||||
|
||||
const scrollContainer = lyricsRef.current?.querySelector('[data-radix-scroll-area-viewport]') as HTMLElement;
|
||||
const currentLyricElement = lyricsRef.current?.querySelector(`[data-lyric-index="${currentLyricIndex}"]`) as HTMLElement;
|
||||
|
||||
if (scrollContainer && currentLyricElement) {
|
||||
const containerHeight = scrollContainer.clientHeight;
|
||||
const elementTop = currentLyricElement.offsetTop;
|
||||
const elementHeight = currentLyricElement.offsetHeight;
|
||||
|
||||
// Calculate scroll position to center the current lyric
|
||||
const targetScrollTop = elementTop - (containerHeight / 2) + (elementHeight / 2);
|
||||
|
||||
// Use requestAnimationFrame for smoother iOS performance
|
||||
requestAnimationFrame(() => {
|
||||
try {
|
||||
scrollContainer.scrollTo({
|
||||
top: Math.max(0, targetScrollTop),
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} catch (error) {
|
||||
// Simple fallback for iOS
|
||||
scrollContainer.scrollTop = Math.max(0, targetScrollTop);
|
||||
}
|
||||
scrollContainer.scrollTo({
|
||||
top: Math.max(0, targetScrollTop),
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// Silently fail to prevent breaking audio playback
|
||||
console.warn('Lyrics scroll failed:', error);
|
||||
}
|
||||
}, 100);
|
||||
}, 200);
|
||||
|
||||
return () => clearTimeout(scrollTimeout);
|
||||
}
|
||||
}, [currentLyricIndex, showLyrics, lyrics.length, isMobile, activeTab]);
|
||||
}, [currentLyricIndex, showLyrics, lyrics.length, isMobile]);
|
||||
|
||||
// Reset lyrics to top when song changes
|
||||
// Reset lyrics to top when song changes - Disabled on mobile to prevent iOS audio issues
|
||||
useEffect(() => {
|
||||
const shouldReset = isMobile ? (activeTab === 'lyrics' && lyrics.length > 0) : (showLyrics && lyrics.length > 0);
|
||||
// Only reset scroll on desktop to avoid iOS audio interference
|
||||
const shouldReset = !isMobile && showLyrics && lyrics.length > 0;
|
||||
|
||||
if (currentTrack && shouldReset && lyricsRef.current) {
|
||||
// Simplified reset scroll logic for better iOS compatibility
|
||||
const resetScroll = () => {
|
||||
const resetTimeout = setTimeout(() => {
|
||||
try {
|
||||
let scrollContainer = lyricsRef.current?.querySelector('[data-radix-scroll-area-viewport]') as HTMLElement;
|
||||
|
||||
// Fallback to the lyrics container itself on mobile (iOS)
|
||||
if (!scrollContainer && isMobile && lyricsRef.current) {
|
||||
scrollContainer = lyricsRef.current;
|
||||
}
|
||||
const scrollContainer = lyricsRef.current?.querySelector('[data-radix-scroll-area-viewport]') as HTMLElement;
|
||||
|
||||
if (scrollContainer) {
|
||||
// Use requestAnimationFrame for smoother iOS performance
|
||||
requestAnimationFrame(() => {
|
||||
try {
|
||||
scrollContainer.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'instant'
|
||||
});
|
||||
} catch (error) {
|
||||
scrollContainer.scrollTop = 0;
|
||||
}
|
||||
scrollContainer.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'instant'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// Silently fail to prevent breaking audio playback
|
||||
console.warn('Lyrics reset scroll failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// Small delay to ensure DOM is ready
|
||||
const resetTimeout = setTimeout(() => {
|
||||
resetScroll();
|
||||
setCurrentLyricIndex(-1);
|
||||
}, 50);
|
||||
|
||||
return () => clearTimeout(resetTimeout);
|
||||
}
|
||||
}, [currentTrack?.id, showLyrics, currentTrack, isMobile, activeTab, lyrics.length]); // Only reset when track ID changes
|
||||
}, [currentTrack?.id, showLyrics, currentTrack, isMobile, lyrics.length]);
|
||||
|
||||
// Sync with main audio player (improved responsiveness)
|
||||
useEffect(() => {
|
||||
const syncWithMainPlayer = () => {
|
||||
const mainAudio = document.querySelector('audio') as HTMLAudioElement;
|
||||
if (mainAudio && currentTrack) {
|
||||
// Console log audio information for debugging
|
||||
console.log('=== FULLSCREEN PLAYER AUDIO DEBUG ===');
|
||||
console.log('Audio element src:', mainAudio.src);
|
||||
console.log('Audio element currentSrc:', mainAudio.currentSrc);
|
||||
console.log('Current track:', {
|
||||
id: currentTrack.id,
|
||||
name: currentTrack.name,
|
||||
url: currentTrack.url,
|
||||
artist: currentTrack.artist,
|
||||
album: currentTrack.album
|
||||
});
|
||||
console.log('Audio state:', {
|
||||
currentTime: mainAudio.currentTime,
|
||||
duration: mainAudio.duration,
|
||||
paused: mainAudio.paused,
|
||||
ended: mainAudio.ended,
|
||||
readyState: mainAudio.readyState,
|
||||
networkState: mainAudio.networkState
|
||||
});
|
||||
console.log('==========================================');
|
||||
|
||||
const newCurrentTime = mainAudio.currentTime;
|
||||
const newDuration = mainAudio.duration || 0;
|
||||
const newIsPlaying = !mainAudio.paused;
|
||||
@@ -268,12 +253,28 @@ export const FullScreenPlayer: React.FC<FullScreenPlayerProps> = ({ isOpen, onCl
|
||||
|
||||
const togglePlayPause = () => {
|
||||
const mainAudio = document.querySelector('audio') as HTMLAudioElement;
|
||||
if (!mainAudio) return;
|
||||
if (!mainAudio) {
|
||||
console.log('❌ No audio element found');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('🎵 Toggle Play/Pause clicked');
|
||||
console.log('Audio src before toggle:', mainAudio.src);
|
||||
console.log('Audio currentSrc before toggle:', mainAudio.currentSrc);
|
||||
console.log('Audio paused state before toggle:', mainAudio.paused);
|
||||
console.log('Audio currentTime before toggle:', mainAudio.currentTime);
|
||||
console.log('Audio duration before toggle:', mainAudio.duration);
|
||||
|
||||
if (isPlaying) {
|
||||
console.log('⏸️ Pausing audio');
|
||||
mainAudio.pause();
|
||||
} else {
|
||||
mainAudio.play();
|
||||
console.log('▶️ Playing audio');
|
||||
mainAudio.play().then(() => {
|
||||
console.log('✅ Audio play() succeeded');
|
||||
}).catch((error) => {
|
||||
console.error('❌ Audio play() failed:', error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -537,12 +538,8 @@ export const FullScreenPlayer: React.FC<FullScreenPlayerProps> = ({ isOpen, onCl
|
||||
{activeTab === 'lyrics' && lyrics.length > 0 && (
|
||||
<div className="h-full flex flex-col px-4">
|
||||
<div
|
||||
className="flex-1 overflow-y-auto scrollable-area"
|
||||
className="flex-1 overflow-y-auto"
|
||||
ref={lyricsRef}
|
||||
style={{
|
||||
WebkitOverflowScrolling: 'touch', // Enable momentum scrolling on iOS
|
||||
scrollBehavior: 'smooth'
|
||||
}}
|
||||
>
|
||||
<div className="space-y-3 py-4">
|
||||
{lyrics.map((line, index) => (
|
||||
|
||||
@@ -150,8 +150,8 @@ export function UserProfile({ variant = 'desktop' }: UserProfileProps) {
|
||||
<Image
|
||||
src={gravatarUrl}
|
||||
alt={`${userInfo.username}'s avatar`}
|
||||
width={16}
|
||||
height={16}
|
||||
width={32}
|
||||
height={32}
|
||||
className="rounded-full"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
@@ -171,8 +171,8 @@ export function UserProfile({ variant = 'desktop' }: UserProfileProps) {
|
||||
<Image
|
||||
src={gravatarUrl}
|
||||
alt={`${userInfo.username}'s avatar`}
|
||||
width={16}
|
||||
height={16}
|
||||
width={32}
|
||||
height={32}
|
||||
className="rounded-full"
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -789,6 +789,47 @@ const SettingsPage = () => {
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Debug Section - Development Only */}
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<Card className="mb-6 break-inside-avoid py-5 border-orange-200 bg-orange-50/50">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-orange-700">
|
||||
<Settings className="w-5 h-5" />
|
||||
Debug Tools
|
||||
</CardTitle>
|
||||
<CardDescription className="text-orange-600">
|
||||
Development-only debugging utilities
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Button
|
||||
onClick={() => {
|
||||
// Save Navidrome config before clearing
|
||||
const navidromeConfig = localStorage.getItem('navidrome-config');
|
||||
|
||||
// Clear all localStorage
|
||||
localStorage.clear();
|
||||
|
||||
// Restore Navidrome config
|
||||
if (navidromeConfig) {
|
||||
localStorage.setItem('navidrome-config', navidromeConfig);
|
||||
}
|
||||
|
||||
// Reload page to reset state
|
||||
window.location.reload();
|
||||
}}
|
||||
variant="outline"
|
||||
className="w-full bg-orange-100 border-orange-300 text-orange-700 hover:bg-orange-200"
|
||||
>
|
||||
Clear All Data (Keep Navidrome Config)
|
||||
</Button>
|
||||
<p className="text-xs text-orange-600 mt-2">
|
||||
This will clear all localStorage data except your Navidrome server configuration, then reload the page.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user