It's time for MVP PWA/ServiceWorker
- Not 100% offline yet, very minimal caching - Fix logo a little
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 47 KiB |
|
@ -1 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;"><rect id="Logo-simple" serif:id="Logo simple" x="0" y="0" width="63.994" height="63.994" style="fill:none;"/><g id="Logo-simple1" serif:id="Logo simple"><path d="M56.352,22.413c-1.293,-5.447 -5.633,-10.525 -10.622,-12.696c-5.656,-2.462 -17.315,-3.499 -23.317,-2.075c-5.293,1.256 -10.462,5.488 -12.696,10.621c-2.462,5.657 -3.499,17.316 -2.075,23.318c1.293,5.447 5.633,10.525 10.621,12.696c5.657,2.462 17.316,3.499 23.318,2.075c5.293,-1.256 10.462,-5.488 12.696,-10.622c2.462,-5.656 3.499,-17.315 2.075,-23.317Z" style="fill:#d8e7fe;stroke:#a4bff7;stroke-width:6px;"/><path d="M38.644,24.754c0.838,4.163 1.381,10.15 1.004,15.758" style="fill:none;stroke:#6892e2;stroke-width:6px;"/><path d="M27.013,23.719c-1.56,3.95 -3.152,9.747 -3.77,15.333" style="fill:none;stroke:#6892e2;stroke-width:6px;"/></g></svg>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 128 128" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||
<rect id="Logo-simple" serif:id="Logo simple" x="0" y="0" width="127.988" height="127.988" style="fill:none;"/>
|
||||
<g id="Logo-simple1" serif:id="Logo simple">
|
||||
<path d="M107.564,46.848c-2.312,-9.745 -10.077,-18.829 -19.001,-22.713c-10.12,-4.404 -30.977,-6.26 -41.715,-3.712c-9.469,2.248 -18.716,9.818 -22.713,19.002c-4.404,10.119 -6.26,30.977 -3.712,41.714c2.313,9.746 10.078,18.83 19.002,22.714c10.119,4.404 30.977,6.26 41.714,3.711c9.47,-2.247 18.717,-9.817 22.714,-19.001c4.404,-10.12 6.26,-30.977 3.711,-41.715Z" style="fill:#d8e7fe;stroke:#a4bff7;stroke-width:12px;"/>
|
||||
<path d="M75.885,51.037c1.5,7.447 2.472,18.158 1.796,28.19" style="fill:none;stroke:#6892e2;stroke-width:12px;"/>
|
||||
<path d="M55.078,49.186c-2.791,7.065 -5.639,17.436 -6.745,27.429" style="fill:none;stroke:#6892e2;stroke-width:12px;"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.3 KiB |
14
index.html
|
@ -4,6 +4,10 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Phanpy</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Minimalistic opinionated Mastodon web client"
|
||||
/>
|
||||
<meta name="color-scheme" content="dark light" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
|
@ -11,6 +15,16 @@
|
|||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<link rel="canonical" href="https://phanpy.social" />
|
||||
<meta
|
||||
name="theme-color"
|
||||
content="#fff"
|
||||
media="(prefers-color-scheme: light)"
|
||||
/>
|
||||
<meta
|
||||
name="theme-color"
|
||||
content="#242526"
|
||||
media="(prefers-color-scheme: dark)"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
5940
package-lock.json
generated
|
@ -15,7 +15,7 @@
|
|||
"fast-blurhash": "~1.1.2",
|
||||
"history": "~5.3.0",
|
||||
"iconify-icon": "~1.0.2",
|
||||
"masto": "~4.10.0",
|
||||
"masto": "~4.10.1",
|
||||
"mem": "~9.0.2",
|
||||
"preact": "~10.11.3",
|
||||
"preact-router": "~4.1.0",
|
||||
|
@ -30,7 +30,12 @@
|
|||
"autoprefixer": "~10.4.13",
|
||||
"postcss": "~8.4.20",
|
||||
"postcss-dark-theme-class": "~0.7.3",
|
||||
"vite": "~4.0.1"
|
||||
"vite": "~4.0.2",
|
||||
"vite-plugin-pwa": "~0.14.0",
|
||||
"workbox-cacheable-response": "~6.5.4",
|
||||
"workbox-expiration": "~6.5.4",
|
||||
"workbox-routing": "~6.5.4",
|
||||
"workbox-strategies": "~6.5.4"
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
|
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 2.2 KiB |
BIN
public/logo-192.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
public/logo-512.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
2
public/robots.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
User-agent: *
|
||||
Allow: /
|
58
public/sw.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { CacheableResponsePlugin } from 'workbox-cacheable-response';
|
||||
import { ExpirationPlugin } from 'workbox-expiration';
|
||||
import { RegExpRoute, registerRoute, Route } from 'workbox-routing';
|
||||
import { CacheFirst, StaleWhileRevalidate } from 'workbox-strategies';
|
||||
|
||||
const imageRoute = new Route(
|
||||
({ request, sameOrigin }) => {
|
||||
return !sameOrigin && request.destination === 'image';
|
||||
},
|
||||
new CacheFirst({
|
||||
cacheName: 'remote-images',
|
||||
plugins: [
|
||||
new ExpirationPlugin({
|
||||
maxAgeSeconds: 7 * 24 * 60 * 60, // 7 days
|
||||
purgeOnQuotaError: true,
|
||||
}),
|
||||
new CacheableResponsePlugin({
|
||||
statuses: [0, 200],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
registerRoute(imageRoute);
|
||||
|
||||
// Cache /instance because masto.js has to keep calling it while initializing
|
||||
const apiExtendedRoute = new RegExpRoute(
|
||||
/^https?:\/\/[^\/]+\/api\/v\d+\/instance/,
|
||||
new StaleWhileRevalidate({
|
||||
cacheName: 'api-extended',
|
||||
plugins: [
|
||||
new ExpirationPlugin({
|
||||
maxAgeSeconds: 24 * 60 * 60, // 1 day
|
||||
}),
|
||||
new CacheableResponsePlugin({
|
||||
statuses: [0, 200],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
);
|
||||
registerRoute(apiExtendedRoute);
|
||||
|
||||
// Not caching API requests, doesn't seem to be necessary fo now
|
||||
//
|
||||
// const apiRoute = new RegExpRoute(
|
||||
// /^https?:\/\/[^\/]+\/api\//,
|
||||
// new StaleWhileRevalidate({
|
||||
// cacheName: 'api',
|
||||
// plugins: [
|
||||
// new ExpirationPlugin({
|
||||
// maxAgeSeconds: 60, // 1 minute
|
||||
// }),
|
||||
// new CacheableResponsePlugin({
|
||||
// statuses: [0, 200],
|
||||
// }),
|
||||
// ],
|
||||
// }),
|
||||
// );
|
||||
// registerRoute(apiRoute);
|
|
@ -1,7 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="1.5" clip-rule="evenodd" viewBox="0 0 64 64">
|
||||
<path fill="none" d="M0 0h63.99v63.99H0z"/>
|
||||
<g stroke-width="6">
|
||||
<path fill="#d8e7fe" stroke="#a4bff7" d="M56.35 22.41a19.43 19.43 0 0 0-10.62-12.7c-5.66-2.46-17.32-3.5-23.32-2.07a19.43 19.43 0 0 0-12.7 10.62c-2.46 5.66-3.5 17.32-2.07 23.32a19.43 19.43 0 0 0 10.62 12.7c5.66 2.46 17.32 3.5 23.32 2.07a19.43 19.43 0 0 0 12.7-10.62c2.46-5.66 3.5-17.31 2.07-23.32Z"/>
|
||||
<path fill="none" stroke="#6892e2" d="M38.64 24.75a63.7 63.7 0 0 1 1 15.76M27.01 23.72a63.64 63.64 0 0 0-3.77 15.33"/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="1.5" clip-rule="evenodd" viewBox="0 0 128 128">
|
||||
<g stroke-width="12">
|
||||
<path fill="#d8e7fe" stroke="#a4bff7" d="M107.56 46.85c-2.3-9.75-10.07-18.83-19-22.72-10.12-4.4-30.97-6.26-41.71-3.7-9.47 2.24-18.72 9.81-22.72 19-4.4 10.11-6.26 30.97-3.7 41.7 2.3 9.76 10.07 18.84 19 22.72 10.11 4.4 30.97 6.26 41.7 3.71 9.48-2.24 18.73-9.81 22.72-19 4.4-10.12 6.26-30.97 3.71-41.71Z"/>
|
||||
<path fill="none" stroke="#6892e2" d="M75.89 51.04c1.5 7.44 2.47 18.16 1.8 28.19M55.08 49.19a113.84 113.84 0 0 0-6.75 27.43"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 703 B After Width: | Height: | Size: 672 B |
|
@ -2,16 +2,53 @@ import preact from '@preact/preset-vite';
|
|||
import { execSync } from 'child_process';
|
||||
import { resolve } from 'path';
|
||||
import { defineConfig, splitVendorChunkPlugin } from 'vite';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
|
||||
const { VITE_CLIENT_NAME: CLIENT_NAME, NODE_ENV } = process.env;
|
||||
|
||||
const commitHash = execSync('git rev-parse --short HEAD').toString().trim();
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
mode: NODE_ENV,
|
||||
define: {
|
||||
__BUILD_TIME__: JSON.stringify(Date.now()),
|
||||
__COMMIT_HASH__: JSON.stringify(commitHash),
|
||||
},
|
||||
plugins: [preact(), splitVendorChunkPlugin()],
|
||||
plugins: [
|
||||
preact(),
|
||||
splitVendorChunkPlugin(),
|
||||
VitePWA({
|
||||
manifest: {
|
||||
name: CLIENT_NAME,
|
||||
short_name: CLIENT_NAME,
|
||||
description: 'Minimalistic opinionated Mastodon web client',
|
||||
theme_color: '#ffffff',
|
||||
icons: [
|
||||
{
|
||||
src: 'logo-192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: 'logo-512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
},
|
||||
],
|
||||
},
|
||||
strategies: 'injectManifest',
|
||||
injectRegister: 'inline',
|
||||
injectManifest: {
|
||||
// Prevent "Unable to find a place to inject the manifest" error
|
||||
injectionPoint: undefined,
|
||||
},
|
||||
devOptions: {
|
||||
enabled: NODE_ENV === 'development',
|
||||
type: 'module',
|
||||
},
|
||||
}),
|
||||
],
|
||||
build: {
|
||||
sourcemap: true,
|
||||
rollupOptions: {
|
||||
|
|