this repo has no description
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add service worker registration and image caching functionality

Turtlepaw 6dccf930 fdd53eee

+98 -2
+44
public/sw.js
··· 1 + const CACHE_NAME = "scribble-image-cache-v1"; 2 + 3 + self.addEventListener("install", (event) => { 4 + self.skipWaiting(); 5 + }); 6 + 7 + self.addEventListener("activate", (event) => { 8 + event.waitUntil( 9 + caches.keys().then((cacheNames) => { 10 + return Promise.all( 11 + cacheNames.map((cacheName) => { 12 + if (cacheName !== CACHE_NAME) { 13 + return caches.delete(cacheName); 14 + } 15 + }) 16 + ); 17 + }) 18 + ); 19 + }); 20 + 21 + self.addEventListener("fetch", (event) => { 22 + // Only cache image requests 23 + if (event.request.destination === "image") { 24 + event.respondWith( 25 + caches.open(CACHE_NAME).then((cache) => { 26 + return cache.match(event.request).then((cachedResponse) => { 27 + // Return cached response if found 28 + if (cachedResponse) { 29 + return cachedResponse; 30 + } 31 + 32 + // Otherwise fetch from network and cache 33 + return fetch(event.request).then((networkResponse) => { 34 + // Clone the response before using it 35 + const responseToCache = networkResponse.clone(); 36 + 37 + cache.put(event.request, responseToCache); 38 + return networkResponse; 39 + }); 40 + }); 41 + }) 42 + ); 43 + } 44 + });
+3 -1
src/app/layout.tsx
··· 7 7 import { ProfileProvider } from "@/lib/useProfile"; 8 8 import { Toaster } from "sonner"; 9 9 import { BoardsProvider } from "@/lib/hooks/useBoards"; 10 + import { ServiceWorkerRegistration } from "@/components/ServiceWorkerRegistration"; 10 11 11 12 const geistSans = Geist({ 12 13 variable: "--font-geist-sans", ··· 22 23 23 24 export const metadata: Metadata = { 24 25 title: "pin.to.it", 25 - description: "Simple scrapboard client", 26 + description: "Simple scrapboard.org client", 26 27 }; 27 28 28 29 export default function RootLayout({ ··· 38 39 <AuthProvider> 39 40 <ProfileProvider> 40 41 <BoardsProvider> 42 + <ServiceWorkerRegistration /> 41 43 <div className="min-h-screen flex flex-col"> 42 44 <Navbar /> 43 45 <main className="flex-1 py-6">{children}</main>
+26 -1
src/components/Feed.tsx
··· 10 10 import { SaveButton } from "./SaveButton"; 11 11 import { UnsaveButton } from "./UnsaveButton"; 12 12 import { LikeButton } from "./LikeButton"; 13 - import { useState } from "react"; 13 + import { useState, useEffect } from "react"; 14 14 15 15 export type FeedItem = { 16 16 id: string; ··· 51 51 ) { 52 52 return it.embed.images[index]; 53 53 } else return null; 54 + } 55 + 56 + // Add this function to prefetch and cache images 57 + function prefetchAndCacheImages(feed: [number, PostView][] | undefined) { 58 + if (!feed || typeof window === "undefined") return; 59 + 60 + feed.forEach(([index, item]) => { 61 + const image = getImageFromItem(item, index); 62 + if (image && image.fullsize) { 63 + const img = new window.Image(); 64 + img.src = image.fullsize; 65 + 66 + // If service worker is active, explicitly add to cache 67 + if ("serviceWorker" in navigator && navigator.serviceWorker.controller) { 68 + fetch(image.fullsize, { mode: "no-cors" }).catch((err) => 69 + console.warn("Error prefetching image:", err) 70 + ); 71 + } 72 + } 73 + }); 54 74 } 55 75 56 76 function ImageCard({ ··· 184 204 isLoading = false, 185 205 showUnsaveButton = false, 186 206 }: FeedProps) { 207 + // Use effect to prefetch and cache images when feed changes 208 + useEffect(() => { 209 + prefetchAndCacheImages(feed); 210 + }, [feed]); 211 + 187 212 return ( 188 213 <> 189 214 <Masonry
+25
src/components/ServiceWorkerRegistration.tsx
··· 1 + "use client"; 2 + 3 + import { useEffect } from "react"; 4 + 5 + export function ServiceWorkerRegistration() { 6 + useEffect(() => { 7 + if ("serviceWorker" in navigator && process.env.NODE_ENV === "production") { 8 + window.addEventListener("load", () => { 9 + navigator.serviceWorker.register("/sw.js").then( 10 + (registration) => { 11 + console.log( 12 + "Service Worker registered with scope:", 13 + registration.scope 14 + ); 15 + }, 16 + (error) => { 17 + console.error("Service Worker registration failed:", error); 18 + } 19 + ); 20 + }); 21 + } 22 + }, []); 23 + 24 + return null; // This component doesn't render anything 25 + }