feat: Refactor service worker registration and enhance offline download manager with client-side checks

This commit is contained in:
2025-08-11 12:31:08 +00:00
committed by GitHub
parent 452af2f6f0
commit 8b5dbbe854
4 changed files with 116 additions and 13 deletions

View File

@@ -15,8 +15,9 @@ import { LoginForm } from "./start-screen";
import Image from "next/image";
import PageTransition from "./PageTransition";
// Service Worker registration
if (typeof window !== 'undefined' && 'serviceWorker' in navigator) {
// Service Worker registration - moved to useEffect to ensure it only runs client-side
React.useEffect(() => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then((registration) => {
console.log('Service Worker registered successfully:', registration);
@@ -25,6 +26,7 @@ if (typeof window !== 'undefined' && 'serviceWorker' in navigator) {
console.error('Service Worker registration failed:', error);
});
}
}, []);
function NavidromeErrorBoundary({ children }: { children: React.ReactNode }) {
// For now, since we're switching to offline-first, we'll handle errors differently

View File

@@ -394,7 +394,95 @@ class DownloadManager {
}
}
const downloadManager = new DownloadManager();
// Create a singleton instance that will be initialized on the client side
let downloadManagerInstance: DownloadManager | null = null;
// Only create the download manager instance on the client side
if (typeof window !== 'undefined') {
downloadManagerInstance = new DownloadManager();
}
// Create a safe wrapper around the download manager
const downloadManager = {
initialize: async () => {
if (!downloadManagerInstance) return false;
return downloadManagerInstance.initialize();
},
getOfflineStats: async () => {
if (!downloadManagerInstance) return {
totalSize: 0,
audioSize: 0,
imageSize: 0,
metaSize: 0,
downloadedAlbums: 0,
downloadedSongs: 0,
lastDownload: null,
downloadErrors: 0,
remainingStorage: null,
autoDownloadEnabled: false,
downloadQuality: 'original' as const,
downloadOnWifiOnly: true,
priorityContent: []
};
return downloadManagerInstance.getOfflineStats();
},
downloadAlbum: async (album: Album, songs: Song[], progressCallback: (progress: DownloadProgress) => void) => {
if (!downloadManagerInstance) return;
return downloadManagerInstance.downloadAlbum(album, songs, progressCallback);
},
downloadAlbumFallback: async (album: Album, songs: Song[]) => {
if (!downloadManagerInstance) return;
return downloadManagerInstance.downloadAlbumFallback(album, songs);
},
downloadSong: async (song: Song) => {
if (!downloadManagerInstance) return;
return downloadManagerInstance.downloadSong(song);
},
getOfflineData: () => {
if (!downloadManagerInstance) return { albums: {}, songs: {} };
return downloadManagerInstance.getOfflineData();
},
saveOfflineData: (data: any) => {
if (!downloadManagerInstance) return;
return downloadManagerInstance.saveOfflineData(data);
},
checkOfflineStatus: async (id: string, type: 'album' | 'song') => {
if (!downloadManagerInstance) return false;
return downloadManagerInstance.checkOfflineStatus(id, type);
},
checkOfflineStatusFallback: (id: string, type: 'album' | 'song') => {
if (!downloadManagerInstance) return false;
return downloadManagerInstance.checkOfflineStatusFallback(id, type);
},
deleteOfflineContent: async (id: string, type: 'album' | 'song') => {
if (!downloadManagerInstance) return;
return downloadManagerInstance.deleteOfflineContent(id, type);
},
deleteOfflineContentFallback: async (id: string, type: 'album' | 'song') => {
if (!downloadManagerInstance) return;
return downloadManagerInstance.deleteOfflineContentFallback(id, type);
},
getOfflineItems: async () => {
if (!downloadManagerInstance) return { albums: [], songs: [] };
return downloadManagerInstance.getOfflineItems();
},
getOfflineAlbums: () => {
if (!downloadManagerInstance) return [];
return downloadManagerInstance.getOfflineAlbums();
},
getOfflineSongs: () => {
if (!downloadManagerInstance) return [];
return downloadManagerInstance.getOfflineSongs();
},
downloadQueue: async (songs: Song[]) => {
if (!downloadManagerInstance) return;
return downloadManagerInstance.downloadQueue(songs);
},
enableOfflineMode: async (settings: any) => {
if (!downloadManagerInstance) return;
return downloadManagerInstance.enableOfflineMode(settings);
}
};
export function useOfflineDownloads() {
const [isSupported, setIsSupported] = useState(false);
@@ -424,6 +512,13 @@ export function useOfflineDownloads() {
useEffect(() => {
const initializeDownloadManager = async () => {
// Skip initialization on server-side
if (!downloadManager) {
setIsSupported(false);
setIsInitialized(true);
return;
}
const supported = await downloadManager.initialize();
setIsSupported(supported);
setIsInitialized(true);

View File

@@ -86,7 +86,10 @@ export function useOfflineLibrarySync() {
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
// Check if navigator is available (client-side only)
if (typeof navigator !== 'undefined') {
setIsOnline(navigator.onLine);
}
return () => {
window.removeEventListener('online', handleOnline);

View File

@@ -19,9 +19,12 @@ export interface OfflineLibraryState {
}
export function useOfflineLibrary() {
// Check if we're on the client side
const isClient = typeof window !== 'undefined';
const [state, setState] = useState<OfflineLibraryState>({
isInitialized: false,
isOnline: navigator.onLine,
isOnline: isClient ? navigator.onLine : true, // Default to true during SSR
isSyncing: false,
lastSync: null,
stats: {