Skip to content

Commit

Permalink
Use 'vite-plugin-pwa' to generate the service worker and PWA manifest
Browse files Browse the repository at this point in the history
The service worker caches all site assets, including web fonts. It also caches user profile pictures (up to 100 entries). The AppUpdated component periodically checks the service worker for update.
  • Loading branch information
davidmz committed Aug 26, 2024
1 parent 55227bf commit 9d25d0e
Show file tree
Hide file tree
Showing 6 changed files with 1,293 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ module.exports = {
'import/no-extraneous-dependencies': 2,
'import/no-named-as-default': 2,
'import/no-named-as-default-member': 2,
'import/no-unresolved': 2,
'import/no-unresolved': ['error', { ignore: ['^virtual:'] }],
'import/order': [
2,
{ groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'] },
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@
"vite": "~5.3.5",
"vite-plugin-compression": "~0.5.1",
"vite-plugin-generate-file": "~0.2.0",
"vitest": "~2.0.4"
"vite-plugin-pwa": "^0.20.1",
"vitest": "~2.0.4",
"workbox-window": "^7.1.0"
},
"scripts": {
"start": "vite",
Expand Down
28 changes: 24 additions & 4 deletions src/components/app-updated.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
/* global CONFIG */
import { useSelector } from 'react-redux';
import { useRegisterSW } from 'virtual:pwa-register/react';

import { useEvent } from 'react-use-event-hook';
import styles from './app-updated.module.scss';
import { ButtonLink } from './button-link';

function reloadPage() {
window.location.reload(true);
}
const { intervalSec } = CONFIG.appVersionCheck;

export function AppUpdated() {
const updated = useSelector((state) => state.appUpdated.updated);

if (!updated) {
const {
needRefresh: [needRefresh],
updateServiceWorker,
} = useRegisterSW({
onRegistered(r) {
r && setInterval(() => r.update(), intervalSec * 1000);
},
});

const reloadPage = useEvent(() => {
if (needRefresh) {
updateServiceWorker();
// Sometimes the updateServiceWorker doesn't refresh the page, so reload
// it manually after some time
setTimeout(() => window.location.reload(true), 2000);
} else {
window.location.reload(true);
}
});

if (!updated && !needRefresh) {
return null;
}

Expand Down
1 change: 1 addition & 0 deletions src/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite-plugin-pwa/react" />
55 changes: 55 additions & 0 deletions vite.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import legacy from '@vitejs/plugin-legacy';
import react from '@vitejs/plugin-react-swc';
import { defineConfig } from 'vite';
import generateFile from 'vite-plugin-generate-file';
import { VitePWA } from 'vite-plugin-pwa';
import { globSync } from 'glob';
import matter from 'gray-matter';
import pkg from './package.json';
Expand All @@ -32,6 +33,60 @@ const vendorChunks = [

export default defineConfig(({ mode }) => ({
plugins: [
VitePWA({
registerType: 'prompt',
devOptions: { enabled: true },
manifest: {
name: 'FreeFeed',
short_name: 'FreeFeed',
description: 'A small and free social network',
id: '/?pwa=1',
start_url: '/',
display: 'minimal-ui',
icons: [
{
src: '/assets/images/pwa-64x64.png',
sizes: '64x64',
type: 'image/png',
},
{
src: '/assets/images/pwa-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: '/assets/images/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
},
{
src: '/assets/images/maskable-icon-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'maskable',
},
],
},
workbox: {
// Don't use 'index.html' fallback for these routes
navigateFallbackDenylist: [/^\/(v\d+|socket\.io|api)\//, /\/(config\.json|version\.txt)$/],
// Add 'woff2' to the default 'globPatterns'
globPatterns: ['**/*.{js,wasm,css,html,woff2}'],
runtimeCaching: [
// Cache profile pictures (up to 100 entries)
{
urlPattern: /^https:\/\/(stable-)?media\.freefeed\.net\/profilepics\//,
handler: 'CacheFirst',
options: {
cacheName: 'profile-pictures',
expiration: {
maxEntries: 100,
},
},
},
],
},
}),
!process.env.MODERN && legacy(),
react(),
injectPreload({
Expand Down
Loading

0 comments on commit 9d25d0e

Please sign in to comment.