lisudoku / lisudoku_frontend

React front-end for lisudoku
https://lisudoku.xyz
GNU Affero General Public License v3.0
0 stars 1 forks source link

Make lisudoku a PWA #54

Closed PlayLikeNeverB4 closed 11 months ago

PlayLikeNeverB4 commented 12 months ago

Fixes https://github.com/lisudoku/lisudoku_frontend/issues/51

Users can now play offline too!

Changes

Tested on

Acceptance test

Old trash code that might be useful someday (but probably not) ``` // "workbox-background-sync": "^6.6.0", // "workbox-broadcast-update": "^6.6.0", // "workbox-cacheable-response": "^6.5.4", // "workbox-navigation-preload": "^6.6.0", // "workbox-range-requests": "^6.6.0", // "workbox-streams": "^6.6.0" // precache(routes); // if (!Array.isArray(routes)) { // routes = [routes] // } // for (let route of routes) { // if (typeof route !== 'string') { // route = route.url // } // // createHandlerBoundToURL(route) // console.log(route) // registerRoute(route, new NetworkFirst()) // } // cleanupOutdatedCaches(); // console.log(routes); // precacheAndRoute(routes); // - precache(entries); // - addRoute(options); // Set up App Shell-style routing, so that all navigation requests // are fulfilled with your index.html shell. Learn more at // https://developers.google.com/web/fundamentals/architecture/app-shell // const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$'); // registerRoute( // // Return false to exempt requests from being fulfilled by index.html. // ({ request, url }: { request: Request; url: URL }) => { // console.log('index handler!', url); // // If this isn't a navigation, skip. // if (request.mode !== 'navigate') { // return false; // } // // If this is a URL that starts with /_, skip. // if (url.pathname.startsWith('/_')) { // return false; // } // // If this looks like a URL for a resource, because it contains // // a file extension, skip. // if (url.pathname.match(fileExtensionRegexp)) { // return false; // } // // Return true to signal that we want to use the handler. // return true; // }, // // new NetworkFirst() // createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html') // ); // // An example runtime caching route for requests that aren't handled by the // // precache, in this case same-origin .png requests like those from in public/ // registerRoute( // // Add in any other file extensions or routing criteria as needed. // ({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'), // // Customize this strategy as needed, e.g., by changing to CacheFirst. // new StaleWhileRevalidate({ // cacheName: 'images', // plugins: [ // // Ensure that once this runtime cache reaches a maximum size the // // least-recently used images are removed. // new ExpirationPlugin({ maxEntries: 50 }), // ], // }) // ); // This allows the web app to trigger skipWaiting via // registration.waiting.postMessage({type: 'SKIP_WAITING'}) // self.addEventListener('message', (event) => { // if (event.data && event.data.type === 'SKIP_WAITING') { // self.skipWaiting(); // } // }); // const routeUrls = routes.map(route => { // if (typeof route !== 'string') { // route = route.url // } // return route; // }); // Installing Service Worker // self.addEventListener('install', (e) => { // console.log('[Service Worker] Install'); // e.waitUntil((async () => { // const cache = await caches.open(cacheName); // console.log('[Service Worker] Caching all: app shell and content'); // console.log(routeUrls); // await cache.addAll(routeUrls); // })()); // }); // This works! // Fetching content using Service Worker // self.addEventListener('fetch', (e) => { // // Cache http and https only, skip unsupported chrome-extension:// and file://... // if (!( // e.request.url.startsWith('http:') || e.request.url.startsWith('https:') // )) { // return; // } // e.respondWith((async () => { // try { // console.log(`[Service Worker] Fetching resource: ${e.request.url}`); // const response = await fetch(e.request); // const cache = await caches.open(cacheName); // console.log(`[Service Worker] Caching new resource: ${e.request.url}`); // cache.put(e.request, response.clone()); // return response; // } catch (err) { // console.log('Look like offline, trying cache'); // const r = await caches.match(e.request); // // if (r) return r; // return r!; // } // })()); // }); // /api/puzzles/random // self.addEventListener('fetch', (e) => { // // Cache http and https only, skip unsupported chrome-extension:// and file://... // console.log('POST3', e.request.url); // if (!( // e.request.url.startsWith('http:') || e.request.url.startsWith('https:') // )) { // return // } // const url = new URL(e.request.url) // if (e.request.method !== 'POST' || !url.pathname.startsWith('/api/puzzles/random')) { // console.warn('POST3 skipping', e.request.url) // return // } // const requestClone = e.request.clone() // e.respondWith((async () => { // try { // // console.log(`[Service Worker] Fetching resource: ${e.request.url}`); // const response = await fetch(e.request) // return response; // } catch (err) { // console.log('offline, get from cache'); // const cache = await caches.open(CACHE_NAME) // const cachedData = await cache.match(DOWNLOAD_PATH) // if (!cachedData) { // console.warn('No cached puzzle found :(') // return Response.error() // } // // console.log(`[Service Worker] Caching new resource: ${e.request.url}`); // const body = await cachedData.json() // const puzzles = body.puzzles // console.log('Got this puzzles content', JSON.stringify(puzzles).substring(0, 100)) // const requestBody = await requestClone.json() // console.log(requestBody) // // requestBody, searchParams, puzzles // const solvedPuzzleIds = requestBody.id_blacklist // console.log('Solved puzzles', solvedPuzzleIds) // const validPuzzles = puzzles.filter((puzzle: any) => ( // puzzle.variant === url.searchParams.get('variant') && // puzzle.difficulty === url.searchParams.get('difficulty') // )) // if (validPuzzles.length === 0) { // console.warn('No valid puzzle in the cache') // return Response.error() // } // let candidatePuzzles = validPuzzles.filter((puzzle: any) => ( // !solvedPuzzleIds.includes(puzzle.public_id)) // ) // if (candidatePuzzles.length === 0) { // console.warn('No unsolved puzzles') // candidatePuzzles = validPuzzles // } // console.log(`Valid puzzle count = ${candidatePuzzles.length}`) // console.log(`Looking for variant = ${url.searchParams.get('variant')}`) // console.log(`Looking for difficulty = ${url.searchParams.get('difficulty')}`) // const puzzle = _.sample(candidatePuzzles) // console.log('Got this puzzle content', JSON.stringify(puzzle).substring(0, 100)) // let dataHeaders = { "status": 200, headers: { "Content-Type": "application/json" } }; // return new Response(JSON.stringify(puzzle), dataHeaders) // // cache.put(url, response.clone()); // // console.log('Cached downloaded puzzles') // // const r = await caches.match(e.request); // // // if (r) return r; // // return r!; // // return Response.error() // } // })()); // }); // /api/puzzles/download // self.addEventListener('fetch', (e) => { // // Cache http and https only, skip unsupported chrome-extension:// and file://... // console.log('POST2', e.request.url); // if (!( // e.request.url.startsWith('http:') || e.request.url.startsWith('https:') // )) { // return // } // const url = new URL(e.request.url) // if (e.request.method !== 'POST' || url.pathname !== DOWNLOAD_PATH) { // console.warn('POST2 skipping', e.request.url) // return // } // e.respondWith((async () => { // try { // console.log('[POST2] Checkng cache') // const cache = await caches.open(CACHE_NAME) // const cachedData = await cache.match(DOWNLOAD_PATH) // if (cachedData) { // console.log('Cached data found, stop!') // return cachedData // } // console.log('[POST2] Requesting shit') // const response = await fetch(e.request) // console.log('[POST2] Received shit') // // console.log(`[Service Worker] Caching new resource: ${e.request.url}`); // // e.request.method = 'GET' // const cachedResponse = response.clone() // const cachedResponse2 = response.clone() // await cache.put(DOWNLOAD_PATH, cachedResponse); // console.log('Cached downloaded puzzles') // const content = await cachedResponse2.json() // console.log('Downloaded content size', JSON.stringify(content).length) // console.log('Downloaded content', JSON.stringify(content).substring(0, 100)) // return response // } catch (err) { // console.error('offline, ignore'); // console.warn(err); // // const r = await caches.match(e.request); // // // if (r) return r; // // return r!; // return Response.error() // } // })()); // }); // /api/puzzles/:id/check // self.addEventListener('fetch', (e) => { // // Cache http and https only, skip unsupported chrome-extension:// and file://... // console.log('CHECK', e.request.url); // if (!( // e.request.url.startsWith('http:') || e.request.url.startsWith('https:') // )) { // return // } // const url = new URL(e.request.url) // if (e.request.method !== 'POST' || !url.pathname.match(/\/api\/puzzles\/[^\/]+\/check/)) { // console.warn('CHECK skipping', e.request.url) // return // } // e.respondWith((async () => { // const data = { // correct: true, // } // let dataHeaders = { "status": 200, headers: { "Content-Type": "application/json" } }; // return new Response(JSON.stringify(data), dataHeaders) // })()); // }); ```