feat: add PWA
@ -78,7 +78,8 @@
|
|||||||
"rules": {
|
"rules": {
|
||||||
"prettier/prettier": "error",
|
"prettier/prettier": "error",
|
||||||
"react/no-unknown-property": "off",
|
"react/no-unknown-property": "off",
|
||||||
"react/jsx-key": "off"
|
"react/jsx-key": "off",
|
||||||
|
"react/jsx-no-undef": "off"
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"Astro": "readonly"
|
"Astro": "readonly"
|
||||||
|
@ -1,7 +1,36 @@
|
|||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
import react from '@astrojs/react';
|
import react from '@astrojs/react';
|
||||||
|
import AstroPWA from '@vite-pwa/astro';
|
||||||
|
|
||||||
export default defineConfig({
|
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/howler": "2.2.10",
|
||||||
"@types/react": "^18.2.25",
|
"@types/react": "^18.2.25",
|
||||||
"@types/react-dom": "^18.2.10",
|
"@types/react-dom": "^18.2.10",
|
||||||
|
"@vite-pwa/astro": "0.5.0",
|
||||||
"astro": "4.10.3",
|
"astro": "4.10.3",
|
||||||
"deepmerge": "4.3.1",
|
"deepmerge": "4.3.1",
|
||||||
"focus-trap-react": "10.2.3",
|
"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 { count } from '@/lib/sounds';
|
||||||
|
|
||||||
import '@/styles/global.css';
|
import '@/styles/global.css';
|
||||||
@ -35,8 +39,12 @@ const description =
|
|||||||
<meta content="https://moodist.app/og.png" property="og:image" />
|
<meta content="https://moodist.app/og.png" property="og:image" />
|
||||||
|
|
||||||
<meta content="summary_large_image" name="twitter:card" />
|
<meta content="summary_large_image" name="twitter:card" />
|
||||||
|
|
||||||
|
{pwaInfo && <Fragment set:html={pwaInfo.webManifest.linkTag} />}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
|
<Reload client:load />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
"baseUrl": "./src",
|
"baseUrl": "./src",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"]
|
"@/*": ["./*"]
|
||||||
}
|
},
|
||||||
|
"types": ["vite-plugin-pwa/react", "vite-plugin-pwa/info"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|