diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..760bab9 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "Rosslyn Inn & Suites", "short_name": "Rosslyn Inn", "description": "Experience timeless comfort and hospitality at Rosslyn Inn and Suites", "start_url": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#000000", "orientation": "portrait-primary", "icons": [ + { + "src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" + }, + { + "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" + } + ] +} diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..aaffe86 --- /dev/null +++ b/public/sw.js @@ -0,0 +1,96 @@ +const CACHE_NAME = 'rosslyn-inn-v1'; +const urlsToCache = [ + '/', + '/index.html', + '/manifest.json' +]; + +self.addEventListener('install', (event) => { + event.waitUntil( + caches.open(CACHE_NAME) + .then((cache) => { + console.log('Opened cache'); + return cache.addAll(urlsToCache); + }) + .catch((error) => { + console.error('Cache opening failed:', error); + }) + ); +}); + +self.addEventListener('fetch', (event) => { + const { request } = event; + const url = new URL(request.url); + + if (url.protocol !== 'http:' && url.protocol !== 'https:') { + return; + } + + if (request.method !== 'GET') { + return; + } + + event.respondWith( + caches.match(request) + .then((response) => { + if (response) { + return response; + } + + return fetch(request) + .then((response) => { + if (!response || response.status !== 200 || response.type === 'error') { + return response; + } + + const responseToCache = response.clone(); + const isImage = request.headers.get('accept')?.includes('image'); + const isFont = request.headers.get('accept')?.includes('font') || request.url.includes('.woff') || request.url.includes('.woff2'); + const isScript = request.headers.get('accept')?.includes('javascript'); + const isStylesheet = request.headers.get('accept')?.includes('text/css'); + + if (isImage || isFont || isScript || isStylesheet) { + caches.open(CACHE_NAME) + .then((cache) => { + cache.put(request, responseToCache); + }) + .catch((error) => { + console.error('Cache update failed:', error); + }); + } + + return response; + }) + .catch(() => { + if (request.destination === 'image') { + return new Response( + '', + { headers: { 'Content-Type': 'image/svg+xml', 'Cache-Control': 'no-store' } } + ); + } + return new Response('Network request failed and no cache available', { + status: 503, + statusText: 'Service Unavailable', + headers: new Headers({ + 'Content-Type': 'text/plain' + }) + }); + }); + }) + ); +}); + +self.addEventListener('activate', (event) => { + event.waitUntil( + caches.keys().then((cacheNames) => { + return Promise.all( + cacheNames.map((cacheName) => { + if (cacheName !== CACHE_NAME) { + console.log('Deleting old cache:', cacheName); + return caches.delete(cacheName); + } + }) + ); + }) + ); +}); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 58c7d12..9ebd29a 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -47,6 +47,64 @@ export default function RootLayout({ }>) { return ( + + + + + ); -} +} \ No newline at end of file diff --git a/src/app/page.tsx b/src/app/page.tsx index c1011c0..f58ec5f 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -206,4 +206,4 @@ export default function LandingPage() { ); -} +} \ No newline at end of file