feat: add PWA
@ -78,7 +78,8 @@
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"react/no-unknown-property": "off",
|
||||
"react/jsx-key": "off"
|
||||
"react/jsx-key": "off",
|
||||
"react/jsx-no-undef": "off"
|
||||
},
|
||||
"globals": {
|
||||
"Astro": "readonly"
|
||||
|
@ -1,7 +1,36 @@
|
||||
import { defineConfig } from 'astro/config';
|
||||
|
||||
import react from '@astrojs/react';
|
||||
import AstroPWA from '@vite-pwa/astro';
|
||||
|
||||
export default defineConfig({
|
||||
integrations: [react()],
|
||||
integrations: [
|
||||
react(),
|
||||
AstroPWA({
|
||||
manifest: {
|
||||
background_color: '#09090b',
|
||||
description: 'Ambient sounds for focus and calm.',
|
||||
display: 'standalone',
|
||||
icons: [
|
||||
...[72, 128, 144, 152, 192, 256, 512].map(size => ({
|
||||
sizes: `${size}x${size}`,
|
||||
src: `/assets/pwa/${size}.png`,
|
||||
type: 'image/png',
|
||||
})),
|
||||
],
|
||||
name: 'Moodist',
|
||||
orientation: 'any',
|
||||
scope: '/',
|
||||
short_name: 'Moodist',
|
||||
start_url: '/',
|
||||
theme_color: '#09090b',
|
||||
},
|
||||
registerType: 'prompt',
|
||||
workbox: {
|
||||
globPatterns: ['**/*'],
|
||||
maximumFileSizeToCacheInBytes: Number.MAX_SAFE_INTEGER,
|
||||
navigateFallback: '/',
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
1050
package-lock.json
generated
@ -34,6 +34,7 @@
|
||||
"@types/howler": "2.2.10",
|
||||
"@types/react": "^18.2.25",
|
||||
"@types/react-dom": "^18.2.10",
|
||||
"@vite-pwa/astro": "0.5.0",
|
||||
"astro": "4.10.3",
|
||||
"deepmerge": "4.3.1",
|
||||
"focus-trap-react": "10.2.3",
|
||||
|
BIN
public/assets/pwa/128.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
public/assets/pwa/144.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
public/assets/pwa/152.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
public/assets/pwa/192.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
public/assets/pwa/256.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
public/assets/pwa/512.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
public/assets/pwa/72.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
1
src/components/reload/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { Reload } from './reload';
|
36
src/components/reload/reload-modal.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { useRegisterSW } from 'virtual:pwa-register/react'; // eslint-disable-line
|
||||
|
||||
import { Modal } from '@/components/modal';
|
||||
|
||||
import styles from './reload.module.css';
|
||||
|
||||
export function ReloadModal() {
|
||||
const {
|
||||
needRefresh: [needRefresh, setNeedRefresh],
|
||||
updateServiceWorker,
|
||||
} = useRegisterSW();
|
||||
|
||||
const close = () => {
|
||||
setNeedRefresh(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal show={needRefresh} onClose={close}>
|
||||
<h2 className={styles.title}>New Content</h2>
|
||||
<p className={styles.desc}>
|
||||
New content available, click on reload button to update.
|
||||
</p>
|
||||
|
||||
<div className={styles.buttons}>
|
||||
<button onClick={close}>Close</button>
|
||||
|
||||
<button
|
||||
className={styles.primary}
|
||||
onClick={() => updateServiceWorker(true)}
|
||||
>
|
||||
Reload
|
||||
</button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
38
src/components/reload/reload.module.css
Normal file
@ -0,0 +1,38 @@
|
||||
.title {
|
||||
font-family: var(--font-heading);
|
||||
font-size: var(--font-md);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin-top: 8px;
|
||||
color: var(--color-foreground-subtle);
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
column-gap: 8px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
|
||||
& button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 40px;
|
||||
padding: 0 12px;
|
||||
font-size: var(--font-sm);
|
||||
font-weight: 500;
|
||||
color: var(--color-foreground-subtle);
|
||||
cursor: pointer;
|
||||
background-color: var(--color-neutral-200);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
|
||||
&.primary {
|
||||
color: var(--color-neutral-50);
|
||||
background-color: var(--color-neutral-950);
|
||||
}
|
||||
}
|
||||
}
|
11
src/components/reload/reload.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { ReloadModal } from './reload-modal';
|
||||
|
||||
export function Reload() {
|
||||
const [isBrowser, setIsBrowser] = useState(false);
|
||||
|
||||
useEffect(() => setIsBrowser(true), []);
|
||||
|
||||
return isBrowser ? <ReloadModal /> : null;
|
||||
}
|
@ -1,4 +1,8 @@
|
||||
---
|
||||
import { pwaInfo } from 'virtual:pwa-info'; // eslint-disable-line
|
||||
|
||||
import { Reload } from '@/components/reload';
|
||||
|
||||
import { count } from '@/lib/sounds';
|
||||
|
||||
import '@/styles/global.css';
|
||||
@ -35,8 +39,12 @@ const description =
|
||||
<meta content="https://moodist.app/og.png" property="og:image" />
|
||||
|
||||
<meta content="summary_large_image" name="twitter:card" />
|
||||
|
||||
{pwaInfo && <Fragment set:html={pwaInfo.webManifest.linkTag} />}
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
|
||||
<Reload client:load />
|
||||
</body>
|
||||
</html>
|
||||
|
@ -6,6 +6,7 @@
|
||||
"baseUrl": "./src",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"types": ["vite-plugin-pwa/react", "vite-plugin-pwa/info"]
|
||||
}
|
||||
}
|
||||
|