Compare commits
4 Commits
0d33462349
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
034aa69f8d | ||
|
|
060dfb3099 | ||
|
|
ad11bce308 | ||
|
|
1ffe99285d |
17
public/sw.js
17
public/sw.js
@@ -1,7 +1,7 @@
|
||||
// StripStream Service Worker - Version 2
|
||||
// Architecture: SWR (Stale-While-Revalidate) for all resources
|
||||
|
||||
const VERSION = "v2.4";
|
||||
const VERSION = "v2.5";
|
||||
const STATIC_CACHE = `stripstream-static-${VERSION}`;
|
||||
const PAGES_CACHE = `stripstream-pages-${VERSION}`; // Navigation + RSC (client-side navigation)
|
||||
const API_CACHE = `stripstream-api-${VERSION}`;
|
||||
@@ -129,10 +129,23 @@ async function cacheFirstStrategy(request, cacheName, options = {}) {
|
||||
/**
|
||||
* Stale-While-Revalidate: Serve from cache immediately, update in background
|
||||
* Used for: API calls, images
|
||||
* Respects Cache-Control: no-cache to force network-first (for refresh buttons)
|
||||
*/
|
||||
async function staleWhileRevalidateStrategy(request, cacheName, options = {}) {
|
||||
const cache = await caches.open(cacheName);
|
||||
const cached = await cache.match(request);
|
||||
|
||||
// Check if client requested no-cache (refresh button, router.refresh(), etc.)
|
||||
// 1. Check Cache-Control header
|
||||
const cacheControl = request.headers.get("Cache-Control");
|
||||
const noCacheHeader =
|
||||
cacheControl && (cacheControl.includes("no-cache") || cacheControl.includes("no-store"));
|
||||
// 2. Check request.cache mode (used by Next.js router.refresh())
|
||||
const noCacheMode =
|
||||
request.cache === "no-cache" || request.cache === "no-store" || request.cache === "reload";
|
||||
const noCache = noCacheHeader || noCacheMode;
|
||||
|
||||
// If no-cache, skip cached response and go network-first
|
||||
const cached = noCache ? null : await cache.match(request);
|
||||
|
||||
// Start network request (don't await)
|
||||
const fetchPromise = fetch(request)
|
||||
|
||||
@@ -17,21 +17,45 @@ interface LibraryClientWrapperProps {
|
||||
preferences: UserPreferences;
|
||||
}
|
||||
|
||||
export function LibraryClientWrapper({ children }: LibraryClientWrapperProps) {
|
||||
export function LibraryClientWrapper({
|
||||
children,
|
||||
libraryId,
|
||||
currentPage,
|
||||
unreadOnly,
|
||||
search,
|
||||
pageSize,
|
||||
}: LibraryClientWrapperProps) {
|
||||
const router = useRouter();
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
|
||||
const handleRefresh = async () => {
|
||||
try {
|
||||
setIsRefreshing(true);
|
||||
// Revalider la page côté serveur
|
||||
|
||||
// Fetch fresh data from network with cache bypass
|
||||
const params = new URLSearchParams({
|
||||
page: String(currentPage),
|
||||
size: String(pageSize),
|
||||
...(unreadOnly && { unreadOnly: "true" }),
|
||||
...(search && { search }),
|
||||
});
|
||||
|
||||
const response = await fetch(`/api/komga/libraries/${libraryId}/series?${params}`, {
|
||||
cache: "no-store",
|
||||
headers: { "Cache-Control": "no-cache" },
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to refresh library");
|
||||
}
|
||||
|
||||
// Trigger Next.js revalidation to update the UI
|
||||
router.refresh();
|
||||
return { success: true };
|
||||
} catch {
|
||||
return { success: false, error: "Error refreshing library" };
|
||||
} finally {
|
||||
// Petit délai pour laisser le temps au serveur de revalider
|
||||
setTimeout(() => setIsRefreshing(false), 500);
|
||||
setIsRefreshing(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -18,6 +18,10 @@ interface SeriesClientWrapperProps {
|
||||
|
||||
export function SeriesClientWrapper({
|
||||
children,
|
||||
seriesId,
|
||||
currentPage,
|
||||
unreadOnly,
|
||||
pageSize,
|
||||
}: SeriesClientWrapperProps) {
|
||||
const router = useRouter();
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
@@ -25,14 +29,30 @@ export function SeriesClientWrapper({
|
||||
const handleRefresh = async () => {
|
||||
try {
|
||||
setIsRefreshing(true);
|
||||
// Revalider la page côté serveur
|
||||
|
||||
// Fetch fresh data from network with cache bypass
|
||||
const params = new URLSearchParams({
|
||||
page: String(currentPage),
|
||||
size: String(pageSize),
|
||||
...(unreadOnly && { unreadOnly: "true" }),
|
||||
});
|
||||
|
||||
const response = await fetch(`/api/komga/series/${seriesId}/books?${params}`, {
|
||||
cache: "no-store",
|
||||
headers: { "Cache-Control": "no-cache" },
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to refresh series");
|
||||
}
|
||||
|
||||
// Trigger Next.js revalidation to update the UI
|
||||
router.refresh();
|
||||
return { success: true };
|
||||
} catch {
|
||||
return { success: false, error: "Error refreshing series" };
|
||||
} finally {
|
||||
// Petit délai pour laisser le temps au serveur de revalider
|
||||
setTimeout(() => setIsRefreshing(false), 500);
|
||||
setIsRefreshing(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -52,10 +72,7 @@ export function SeriesClientWrapper({
|
||||
canRefresh={pullToRefresh.canRefresh}
|
||||
isHiding={pullToRefresh.isHiding}
|
||||
/>
|
||||
<RefreshProvider refreshSeries={handleRefresh}>
|
||||
{children}
|
||||
</RefreshProvider>
|
||||
<RefreshProvider refreshSeries={handleRefresh}>{children}</RefreshProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -311,14 +311,15 @@ function BookDownloadCard({ book, status, onDelete, onRetry }: BookDownloadCardP
|
||||
return (
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="relative w-12 aspect-[2/3] bg-muted/80 backdrop-blur-md rounded overflow-hidden">
|
||||
<div className="relative w-16 aspect-[2/3] bg-muted rounded overflow-hidden flex-shrink-0">
|
||||
<Image
|
||||
src={`/api/komga/images/books/${book.id}/thumbnail`}
|
||||
alt={t("books.coverAlt", { title: book.metadata?.title })}
|
||||
className="object-cover"
|
||||
fill
|
||||
sizes="48px"
|
||||
sizes="64px"
|
||||
priority={false}
|
||||
unoptimized
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
|
||||
@@ -20,15 +20,25 @@ export function HomeClientWrapper({ children }: HomeClientWrapperProps) {
|
||||
const handleRefresh = async () => {
|
||||
try {
|
||||
setIsRefreshing(true);
|
||||
// Revalider la page côté serveur
|
||||
|
||||
// Fetch fresh data from network with cache bypass
|
||||
const response = await fetch("/api/komga/home", {
|
||||
cache: "no-store",
|
||||
headers: { "Cache-Control": "no-cache" },
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to refresh home");
|
||||
}
|
||||
|
||||
// Trigger Next.js revalidation to update the UI
|
||||
router.refresh();
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
logger.error({ err: error }, "Erreur lors du rafraîchissement:");
|
||||
return { success: false, error: "Erreur lors du rafraîchissement de la page d'accueil" };
|
||||
} finally {
|
||||
// Petit délai pour laisser le temps au serveur de revalider
|
||||
setTimeout(() => setIsRefreshing(false), 500);
|
||||
setIsRefreshing(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user