feat: pwa manifest, icon and minimal service worker
This commit is contained in:
@@ -4,6 +4,9 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||||
<meta name="theme-color" content="#0a0a0a" />
|
<meta name="theme-color" content="#0a0a0a" />
|
||||||
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
|
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
|
||||||
|
<link rel="apple-touch-icon" href="/icon.svg" />
|
||||||
<title>record-shop</title>
|
<title>record-shop</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
7
web/public/icon.svg
Normal file
7
web/public/icon.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||||
|
<rect width="512" height="512" rx="96" fill="#0a0a0a"/>
|
||||||
|
<circle cx="256" cy="256" r="160" fill="#171717" stroke="#34d399" stroke-width="16"/>
|
||||||
|
<circle cx="256" cy="256" r="96" fill="none" stroke="#262626" stroke-width="8"/>
|
||||||
|
<circle cx="256" cy="256" r="40" fill="#34d399"/>
|
||||||
|
<circle cx="256" cy="256" r="12" fill="#0a0a0a"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 403 B |
13
web/public/manifest.webmanifest
Normal file
13
web/public/manifest.webmanifest
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "record-shop",
|
||||||
|
"short_name": "record-shop",
|
||||||
|
"description": "Track your physical music collection",
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#0a0a0a",
|
||||||
|
"theme_color": "#0a0a0a",
|
||||||
|
"icons": [
|
||||||
|
{ "src": "/icon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any" },
|
||||||
|
{ "src": "/icon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "maskable" }
|
||||||
|
]
|
||||||
|
}
|
||||||
11
web/public/sw.js
Normal file
11
web/public/sw.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
self.addEventListener('install', () => {
|
||||||
|
self.skipWaiting()
|
||||||
|
})
|
||||||
|
|
||||||
|
self.addEventListener('activate', (event) => {
|
||||||
|
event.waitUntil(self.clients.claim())
|
||||||
|
})
|
||||||
|
|
||||||
|
self.addEventListener('fetch', () => {
|
||||||
|
// no-op: presence of a fetch handler enables PWA install
|
||||||
|
})
|
||||||
@@ -8,3 +8,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
|
|||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if ('serviceWorker' in navigator && import.meta.env.PROD) {
|
||||||
|
void navigator.serviceWorker.register('/sw.js')
|
||||||
|
}
|
||||||
|
|||||||
26
web/test/pwa.test.ts
Normal file
26
web/test/pwa.test.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
import { readFileSync } from 'node:fs'
|
||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
|
const pub = path.resolve(__dirname, '../public')
|
||||||
|
|
||||||
|
describe('PWA assets', () => {
|
||||||
|
it('manifest has required fields and the icon exists', () => {
|
||||||
|
const manifest = JSON.parse(readFileSync(path.join(pub, 'manifest.webmanifest'), 'utf8')) as {
|
||||||
|
name: string
|
||||||
|
display: string
|
||||||
|
start_url: string
|
||||||
|
icons: { src: string; sizes: string; type: string }[]
|
||||||
|
}
|
||||||
|
expect(manifest.name).toContain('record-shop')
|
||||||
|
expect(manifest.display).toBe('standalone')
|
||||||
|
expect(manifest.start_url).toBe('/')
|
||||||
|
expect(manifest.icons.some((i) => i.src === '/icon.svg')).toBe(true)
|
||||||
|
expect(() => readFileSync(path.join(pub, 'icon.svg'))).not.toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('service worker registers a fetch handler', () => {
|
||||||
|
const sw = readFileSync(path.join(pub, 'sw.js'), 'utf8')
|
||||||
|
expect(sw).toContain("addEventListener('fetch'")
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user