fix: improve service worker offline flow and dev toggle UX
This commit is contained in:
@@ -8,6 +8,8 @@ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/com
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import {
|
||||
@@ -197,12 +199,15 @@ export function CacheSettings() {
|
||||
const {
|
||||
isSupported,
|
||||
isReady,
|
||||
isDevModeEnabled,
|
||||
version,
|
||||
getCacheStats,
|
||||
getCacheEntries,
|
||||
clearCache,
|
||||
reinstallServiceWorker,
|
||||
setDevModeEnabled,
|
||||
} = useServiceWorker();
|
||||
const isDevelopment = process.env.NODE_ENV === "development";
|
||||
|
||||
const [stats, setStats] = useState<CacheStats | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -276,6 +281,25 @@ export function CacheSettings() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleServiceWorkerDevToggle = async (checked: boolean) => {
|
||||
try {
|
||||
const success = await setDevModeEnabled(checked);
|
||||
if (!success) {
|
||||
throw new Error("Failed to toggle service worker in development");
|
||||
}
|
||||
toast({
|
||||
title: t("settings.title"),
|
||||
description: t("settings.cache.devServiceWorker.saved"),
|
||||
});
|
||||
} catch {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t("settings.error.title"),
|
||||
description: t("settings.cache.devServiceWorker.error"),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Calculer le pourcentage du cache utilisé (basé sur 100MB limite images)
|
||||
const maxCacheSize = 100 * 1024 * 1024; // 100MB
|
||||
const usagePercent = stats ? Math.min((stats.images.size / maxCacheSize) * 100, 100) : 0;
|
||||
@@ -328,6 +352,22 @@ export function CacheSettings() {
|
||||
<CardDescription>{t("settings.cache.description")}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
{isDevelopment && (
|
||||
<div className="flex items-center justify-between rounded-lg border p-3">
|
||||
<div className="space-y-0.5">
|
||||
<Label htmlFor="dev-sw-toggle">{t("settings.cache.devServiceWorker.label")}</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("settings.cache.devServiceWorker.description")}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="dev-sw-toggle"
|
||||
checked={isDevModeEnabled}
|
||||
onCheckedChange={handleServiceWorkerDevToggle}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Barre de progression globale */}
|
||||
{stats && (
|
||||
<div className="space-y-2">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import type { KomgaConfig } from "@/types/komga";
|
||||
import type { KomgaLibrary } from "@/types/komga";
|
||||
import { useTranslate } from "@/hooks/useTranslate";
|
||||
@@ -16,15 +17,35 @@ interface ClientSettingsProps {
|
||||
initialLibraries: KomgaLibrary[];
|
||||
}
|
||||
|
||||
const SETTINGS_TAB_STORAGE_KEY = "stripstream:settings-active-tab";
|
||||
|
||||
export function ClientSettings({ initialConfig, initialLibraries }: ClientSettingsProps) {
|
||||
const { t } = useTranslate();
|
||||
const [activeTab, setActiveTab] = useState<"display" | "connection">("display");
|
||||
|
||||
useEffect(() => {
|
||||
const savedTab = window.sessionStorage.getItem(SETTINGS_TAB_STORAGE_KEY);
|
||||
if (savedTab === "display" || savedTab === "connection") {
|
||||
const rafId = window.requestAnimationFrame(() => {
|
||||
setActiveTab(savedTab);
|
||||
});
|
||||
return () => window.cancelAnimationFrame(rafId);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleTabChange = (tab: string) => {
|
||||
if (tab === "display" || tab === "connection") {
|
||||
setActiveTab(tab);
|
||||
window.sessionStorage.setItem(SETTINGS_TAB_STORAGE_KEY, tab);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="max-w-4xl mx-auto space-y-8">
|
||||
<h1 className="text-3xl font-bold">{t("settings.title")}</h1>
|
||||
|
||||
<Tabs defaultValue="display" className="w-full">
|
||||
<Tabs value={activeTab} onValueChange={handleTabChange} className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="display" className="flex items-center gap-2">
|
||||
<Monitor className="h-4 w-4" />
|
||||
|
||||
@@ -9,7 +9,7 @@ export function NetworkStatus() {
|
||||
if (isOnline) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-4 left-4 z-[100] flex items-center gap-2 rounded-lg bg-destructive/90 backdrop-blur-md px-4 py-2 text-sm text-destructive-foreground shadow-lg">
|
||||
<div className="fixed right-4 top-[calc(4.5rem+env(safe-area-inset-top,0px))] z-[110] flex items-center gap-2 rounded-full border border-destructive/40 bg-destructive/90 px-3 py-1.5 text-xs font-semibold uppercase tracking-wide text-destructive-foreground shadow-lg backdrop-blur-md animate-in fade-in slide-in-from-top-1">
|
||||
<WifiOff className="h-4 w-4" />
|
||||
<span>Hors ligne</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user