feat(pwa): add screenshots and orientation to webmanifest (#2109)
@ -136,9 +136,10 @@ export function useUploadMediaAttachment(draftRef: Ref<Draft>) {
|
||||
let failedAttachments = $ref<MediaAttachmentUploadError[]>([])
|
||||
const dropZoneRef = ref<HTMLDivElement>()
|
||||
|
||||
const maxPixels
|
||||
= currentInstance.value!.configuration?.mediaAttachments?.imageMatrixLimit
|
||||
?? 4096 ** 2
|
||||
const maxPixels = $computed(() => {
|
||||
return currentInstance.value?.configuration?.mediaAttachments?.imageMatrixLimit
|
||||
?? 4096 ** 2
|
||||
})
|
||||
|
||||
const loadImage = (inputFile: Blob) => new Promise<HTMLImageElement>((resolve, reject) => {
|
||||
const url = URL.createObjectURL(inputFile)
|
||||
|
@ -323,6 +323,10 @@
|
||||
"dismiss": "Dismiss",
|
||||
"install": "Install",
|
||||
"install_title": "Install Elk",
|
||||
"screenshots": {
|
||||
"dark": "Screenshot of Elk running in dark mode",
|
||||
"light": "Screenshot of Elk running in light mode"
|
||||
},
|
||||
"title": "New Elk update available!",
|
||||
"update": "Update",
|
||||
"update_available_short": "Update Elk",
|
||||
|
@ -311,6 +311,10 @@
|
||||
"dismiss": "Descartar",
|
||||
"install": "Instalar",
|
||||
"install_title": "Instalar Elk",
|
||||
"screenshots": {
|
||||
"dark": "Captura de pantalla de Elk ejecutándose en modo oscuro",
|
||||
"light": "Captura de pantalla de Elk ejecutándose en modo claro"
|
||||
},
|
||||
"title": "Nueva versión de Elk disponible",
|
||||
"update": "Actualizar",
|
||||
"update_available_short": "Actualiza Elk",
|
||||
|
@ -6,48 +6,124 @@ import { getEnv } from '../../config/env'
|
||||
import { i18n } from '../../config/i18n'
|
||||
import type { LocaleObject } from '#i18n'
|
||||
|
||||
// We have to extend the ManifestOptions interface from 'vite-plugin-pwa'
|
||||
// as that interface doesn't define the share_target field of Web App Manifests.
|
||||
interface ExtendedManifestOptions extends ManifestOptions {
|
||||
share_target: {
|
||||
action: string
|
||||
method: string
|
||||
enctype: string
|
||||
params: {
|
||||
title: string
|
||||
text: string
|
||||
url: string
|
||||
files: [{
|
||||
name: string
|
||||
accept: string[]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type LocalizedWebManifest = Record<string, Partial<ExtendedManifestOptions>>
|
||||
export type LocalizedWebManifest = Record<string, Partial<ManifestOptions>>
|
||||
|
||||
export const pwaLocales = i18n.locales as LocaleObject[]
|
||||
|
||||
type WebManifestEntry = Pick<ExtendedManifestOptions, 'name' | 'short_name' | 'description'>
|
||||
type RequiredWebManifestEntry = Required<WebManifestEntry & Pick<ExtendedManifestOptions, 'dir' | 'lang'>>
|
||||
type WebManifestEntry = Pick<ManifestOptions, 'name' | 'short_name' | 'description' | 'screenshots' | 'shortcuts'>
|
||||
type RequiredWebManifestEntry = Required<WebManifestEntry & Pick<ManifestOptions, 'dir' | 'lang' | 'screenshots' | 'shortcuts'>>
|
||||
|
||||
export async function createI18n(): Promise<LocalizedWebManifest> {
|
||||
const { env } = await getEnv()
|
||||
const envName = `${env === 'release' ? '' : `(${env})`}`
|
||||
const { pwa } = await readI18nFile('en.json')
|
||||
const { action, nav, pwa } = await readI18nFile('en.json')
|
||||
|
||||
const defaultManifest: Required<WebManifestEntry> = pwa.webmanifest[env]
|
||||
|
||||
const defaultShortcuts: ManifestOptions['shortcuts'] = [{
|
||||
name: nav.home,
|
||||
url: '/home',
|
||||
icons: [
|
||||
{ src: 'shortcuts/home-96x96.png', sizes: '96x96', type: 'image/png' },
|
||||
{ src: 'shortcuts/home.png', sizes: '192x192', type: 'image/png' },
|
||||
],
|
||||
}, {
|
||||
name: nav.local,
|
||||
url: '/',
|
||||
icons: [
|
||||
{ src: 'shortcuts/local-96x96.png', sizes: '96x96', type: 'image/png' },
|
||||
{ src: 'shortcuts/local.png', sizes: '192x192', type: 'image/png' },
|
||||
],
|
||||
}, {
|
||||
name: nav.notifications,
|
||||
url: '/notifications',
|
||||
icons: [
|
||||
{ src: 'shortcuts/notifications-96x96.png', sizes: '96x96', type: 'image/png' },
|
||||
{ src: 'shortcuts/notifications.png', sizes: '192x192', type: 'image/png' },
|
||||
],
|
||||
}, {
|
||||
name: action.compose,
|
||||
url: '/compose',
|
||||
icons: [
|
||||
{ src: 'shortcuts/compose-96x96.png', sizes: '96x96', type: 'image/png' },
|
||||
{ src: 'shortcuts/compose.png', sizes: '192x192', type: 'image/png' },
|
||||
],
|
||||
}, {
|
||||
name: nav.settings,
|
||||
url: '/settings',
|
||||
icons: [
|
||||
{ src: 'shortcuts/settings-96x96.png', sizes: '96x96', type: 'image/png' },
|
||||
{ src: 'shortcuts/settings.png', sizes: '192x192', type: 'image/png' },
|
||||
],
|
||||
}]
|
||||
|
||||
const defaultScreenshots: ManifestOptions['screenshots'] = [{
|
||||
src: 'screenshots/dark-1.webp',
|
||||
sizes: '3840x2400',
|
||||
type: 'image/webp',
|
||||
label: pwa.screenshots.dark,
|
||||
}, {
|
||||
src: 'screenshots/light-1.webp',
|
||||
sizes: '3840x2400',
|
||||
type: 'image/webp',
|
||||
label: pwa.screenshots.light,
|
||||
}]
|
||||
|
||||
const manifestEntries: Partial<ManifestOptions> = {
|
||||
scope: '/',
|
||||
id: '/',
|
||||
start_url: '/',
|
||||
orientation: 'natural',
|
||||
display: 'standalone',
|
||||
display_override: ['window-controls-overlay'],
|
||||
categories: ['social', 'social networking'],
|
||||
icons: [
|
||||
{
|
||||
src: 'pwa-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: 'pwa-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'any',
|
||||
},
|
||||
{
|
||||
src: 'maskable-icon.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'maskable',
|
||||
},
|
||||
],
|
||||
share_target: {
|
||||
action: '/web-share-target',
|
||||
method: 'POST',
|
||||
enctype: 'multipart/form-data',
|
||||
params: {
|
||||
title: 'title',
|
||||
text: 'text',
|
||||
url: 'url',
|
||||
files: [
|
||||
{
|
||||
name: 'files',
|
||||
accept: ['image/*', 'video/*'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const locales: RequiredWebManifestEntry[] = await Promise.all(
|
||||
pwaLocales
|
||||
.filter(l => l.code !== 'en-US')
|
||||
.map(async ({ code, dir = 'ltr', file, files }) => {
|
||||
// read locale file or files
|
||||
const { pwa, app_name, app_desc_short } = file
|
||||
const { action, app_desc_short, app_name, nav, pwa } = file
|
||||
? await readI18nFile(file)
|
||||
: await findBestWebManifestData(files, env)
|
||||
const entry: WebManifestEntry = pwa?.webmanifest?.[env] ?? {}
|
||||
const entry = pwa?.webmanifest?.[env] ?? {}
|
||||
|
||||
if (!entry.name && app_name)
|
||||
entry.name = dir === 'rtl' ? `${envName} ${app_name}` : `${app_name} ${envName}`
|
||||
|
||||
@ -57,11 +133,45 @@ export async function createI18n(): Promise<LocalizedWebManifest> {
|
||||
if (!entry.description && app_desc_short)
|
||||
entry.description = app_desc_short
|
||||
|
||||
// clone default screenshots and shortcuts
|
||||
const useScreenshots = [...defaultScreenshots.map(screenshot => ({ ...screenshot }))]
|
||||
const useShortcuts = [...defaultShortcuts.map(shortcut => ({ ...shortcut }))]
|
||||
|
||||
const pwaScreenshots = pwa?.screenshots
|
||||
if (pwaScreenshots) {
|
||||
useScreenshots.forEach((screenshot, idx) => {
|
||||
if (idx === 0 && pwaScreenshots?.dark)
|
||||
screenshot.label = pwaScreenshots.dark
|
||||
|
||||
if (idx === 1 && pwaScreenshots?.light)
|
||||
screenshot.label = pwaScreenshots.light
|
||||
})
|
||||
}
|
||||
|
||||
useShortcuts.forEach((shortcut, idx) => {
|
||||
if (idx === 0 && nav?.home)
|
||||
shortcut.name = nav.home
|
||||
|
||||
if (idx === 1 && nav?.local)
|
||||
shortcut.name = nav.local
|
||||
|
||||
if (idx === 2 && nav?.notifications)
|
||||
shortcut.name = nav.notifications
|
||||
|
||||
if (idx === 3 && action?.compose)
|
||||
shortcut.name = action?.compose
|
||||
|
||||
if (idx === 4 && nav?.settings)
|
||||
shortcut.name = nav.settings
|
||||
})
|
||||
|
||||
return <RequiredWebManifestEntry>{
|
||||
...defaultManifest,
|
||||
...entry,
|
||||
lang: code,
|
||||
dir,
|
||||
screenshots: useScreenshots,
|
||||
shortcuts: useShortcuts,
|
||||
}
|
||||
}),
|
||||
)
|
||||
@ -69,13 +179,19 @@ export async function createI18n(): Promise<LocalizedWebManifest> {
|
||||
...defaultManifest,
|
||||
lang: 'en-US',
|
||||
dir: 'ltr',
|
||||
screenshots: defaultScreenshots,
|
||||
shortcuts: defaultShortcuts,
|
||||
})
|
||||
return locales.reduce((acc, { lang, dir, name, short_name, description }) => {
|
||||
return locales.reduce((acc, {
|
||||
lang,
|
||||
dir,
|
||||
name,
|
||||
short_name,
|
||||
description,
|
||||
shortcuts,
|
||||
screenshots,
|
||||
}) => {
|
||||
acc[lang] = {
|
||||
scope: '/',
|
||||
id: '/',
|
||||
start_url: '/',
|
||||
display: 'standalone',
|
||||
lang,
|
||||
name,
|
||||
short_name,
|
||||
@ -83,47 +199,11 @@ export async function createI18n(): Promise<LocalizedWebManifest> {
|
||||
dir,
|
||||
background_color: '#ffffff',
|
||||
theme_color: '#ffffff',
|
||||
icons: [
|
||||
{
|
||||
src: 'pwa-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: 'pwa-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'any',
|
||||
},
|
||||
{
|
||||
src: 'maskable-icon.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'maskable',
|
||||
},
|
||||
],
|
||||
share_target: {
|
||||
action: '/web-share-target',
|
||||
method: 'POST',
|
||||
enctype: 'multipart/form-data',
|
||||
params: {
|
||||
title: 'title',
|
||||
text: 'text',
|
||||
url: 'url',
|
||||
files: [
|
||||
{
|
||||
name: 'files',
|
||||
accept: ['image/*', 'video/*'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
...manifestEntries,
|
||||
shortcuts,
|
||||
screenshots,
|
||||
}
|
||||
acc[`${lang}-dark`] = {
|
||||
scope: '/',
|
||||
id: '/',
|
||||
start_url: '/',
|
||||
display: 'standalone',
|
||||
lang,
|
||||
name,
|
||||
short_name,
|
||||
@ -131,41 +211,9 @@ export async function createI18n(): Promise<LocalizedWebManifest> {
|
||||
dir,
|
||||
background_color: '#111111',
|
||||
theme_color: '#111111',
|
||||
icons: [
|
||||
{
|
||||
src: 'pwa-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: 'pwa-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'any',
|
||||
},
|
||||
{
|
||||
src: 'maskable-icon.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'maskable',
|
||||
},
|
||||
],
|
||||
share_target: {
|
||||
action: '/web-share-target',
|
||||
method: 'POST',
|
||||
enctype: 'multipart/form-data',
|
||||
params: {
|
||||
title: 'title',
|
||||
text: 'text',
|
||||
url: 'url',
|
||||
files: [
|
||||
{
|
||||
name: 'files',
|
||||
accept: ['image/*', 'video/*'],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
...manifestEntries,
|
||||
shortcuts,
|
||||
screenshots,
|
||||
}
|
||||
|
||||
return acc
|
||||
@ -185,23 +233,30 @@ interface PWAEntry {
|
||||
short_name?: string
|
||||
description?: string
|
||||
}>
|
||||
screenshots?: Record<string, string>
|
||||
shortcuts?: Record<string, string>
|
||||
}
|
||||
|
||||
interface JsonEntry {
|
||||
pwa?: PWAEntry
|
||||
app_name?: string
|
||||
app_desc_short?: string
|
||||
action?: Record<string, any>
|
||||
nav?: Record<string, any>
|
||||
screenshots?: Record<string, string>
|
||||
}
|
||||
|
||||
async function findBestWebManifestData(files: string[], env: string) {
|
||||
const entries: JsonEntry[] = await Promise.all(files.map(async (file) => {
|
||||
const { pwa, app_name, app_desc_short } = await readI18nFile(file)
|
||||
return { pwa, app_name, app_desc_short }
|
||||
const { action, app_name, app_desc_short, nav, pwa } = await readI18nFile(file)
|
||||
return { action, app_name, app_desc_short, nav, pwa }
|
||||
}))
|
||||
|
||||
let pwa: PWAEntry | undefined
|
||||
let app_name: string | undefined
|
||||
let app_desc_short: string | undefined
|
||||
const action: Record<string, any> = {}
|
||||
const nav: Record<string, any> = {}
|
||||
|
||||
for (const entry of entries) {
|
||||
const webmanifest = entry?.pwa?.webmanifest?.[env]
|
||||
@ -226,7 +281,28 @@ async function findBestWebManifestData(files: string[], env: string) {
|
||||
|
||||
if (entry.app_desc_short)
|
||||
app_desc_short = entry.app_desc_short
|
||||
|
||||
if (entry.nav) {
|
||||
['home', 'local', 'notifications', 'settings'].forEach((key) => {
|
||||
const value = entry.nav![key]
|
||||
if (value)
|
||||
nav[key] = value
|
||||
})
|
||||
}
|
||||
|
||||
if (entry.action?.compose)
|
||||
action.compose = entry.action.compose
|
||||
|
||||
if (entry.pwa?.screenshots) {
|
||||
if (!pwa)
|
||||
pwa = {}
|
||||
|
||||
pwa.screenshots = pwa.screenshots ?? {}
|
||||
Object
|
||||
.entries(entry.pwa.screenshots)
|
||||
.forEach(([key, value]) => pwa!.screenshots![key] = value)
|
||||
}
|
||||
}
|
||||
|
||||
return { pwa, app_name, app_desc_short }
|
||||
return { action, app_desc_short, app_name, nav, pwa }
|
||||
}
|
||||
|
@ -94,7 +94,7 @@
|
||||
"ufo": "^1.1.1",
|
||||
"ultrahtml": "^1.2.0",
|
||||
"unimport": "^3.0.6",
|
||||
"vite-plugin-pwa": "^0.14.7",
|
||||
"vite-plugin-pwa": "^0.15.0",
|
||||
"vue-advanced-cropper": "^2.8.8",
|
||||
"vue-virtual-scroller": "2.0.0-beta.8",
|
||||
"workbox-build": "^6.5.4",
|
||||
|
@ -1,5 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
middleware: 'auth',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
useHydratedHead({
|
||||
title: () => t('nav.compose'),
|
||||
})
|
||||
|
@ -185,10 +185,10 @@ importers:
|
||||
version: 5.0.1
|
||||
tauri-plugin-log-api:
|
||||
specifier: github:tauri-apps/tauri-plugin-log
|
||||
version: github.com/tauri-apps/tauri-plugin-log/be811332676ff73e1e891f31ce3de8d447fdf07e
|
||||
version: github.com/tauri-apps/tauri-plugin-log/5e14c2cad7335a4284a6caad81d8cf37dd675a27
|
||||
tauri-plugin-store-api:
|
||||
specifier: github:tauri-apps/tauri-plugin-store
|
||||
version: github.com/tauri-apps/tauri-plugin-store/f4ef29684e4a32eddf51befaae98a5e498df8574
|
||||
version: github.com/tauri-apps/tauri-plugin-store/0558a1f6c869ae0afdb0181dfa1ea31be8cf4893
|
||||
theme-vitesse:
|
||||
specifier: ^0.6.4
|
||||
version: 0.6.4
|
||||
@ -208,8 +208,8 @@ importers:
|
||||
specifier: ^3.0.6
|
||||
version: 3.0.6(rollup@2.79.1)
|
||||
vite-plugin-pwa:
|
||||
specifier: ^0.14.7
|
||||
version: 0.14.7(vite@4.3.4)(workbox-build@6.5.4)(workbox-window@6.5.4)
|
||||
specifier: ^0.15.0
|
||||
version: 0.15.0(vite@4.3.4)(workbox-build@6.5.4)(workbox-window@6.5.4)
|
||||
vue-advanced-cropper:
|
||||
specifier: ^2.8.8
|
||||
version: 2.8.8(vue@3.2.45)
|
||||
@ -13387,18 +13387,16 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/vite-plugin-pwa@0.14.7(vite@4.3.4)(workbox-build@6.5.4)(workbox-window@6.5.4):
|
||||
resolution: {integrity: sha512-dNJaf0fYOWncmjxv9HiSa2xrSjipjff7IkYE5oIUJ2x5HKu3cXgA8LRgzOwTc5MhwyFYRSU0xyN0Phbx3NsQYw==}
|
||||
/vite-plugin-pwa@0.15.0(vite@4.3.4)(workbox-build@6.5.4)(workbox-window@6.5.4):
|
||||
resolution: {integrity: sha512-gpmx3BeubsRIXRBkjPToOTJbo8fknNmZFQs24i0TPZyaNVa0n27YHDo0Y72amnO70WvHKGE3e1fn8SYUP7e8SA==}
|
||||
peerDependencies:
|
||||
vite: ^3.1.0 || ^4.0.0
|
||||
workbox-build: ^6.5.4
|
||||
workbox-window: ^6.5.4
|
||||
dependencies:
|
||||
'@rollup/plugin-replace': 5.0.2(rollup@3.21.3)
|
||||
debug: 4.3.4
|
||||
fast-glob: 3.2.12
|
||||
pretty-bytes: 6.1.0
|
||||
rollup: 3.21.3
|
||||
vite: 4.3.4(@types/node@18.16.3)
|
||||
workbox-build: 6.5.4
|
||||
workbox-window: 6.5.4
|
||||
@ -14276,16 +14274,16 @@ packages:
|
||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||
dev: true
|
||||
|
||||
github.com/tauri-apps/tauri-plugin-log/be811332676ff73e1e891f31ce3de8d447fdf07e:
|
||||
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-log/tar.gz/be811332676ff73e1e891f31ce3de8d447fdf07e}
|
||||
github.com/tauri-apps/tauri-plugin-log/5e14c2cad7335a4284a6caad81d8cf37dd675a27:
|
||||
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-log/tar.gz/5e14c2cad7335a4284a6caad81d8cf37dd675a27}
|
||||
name: tauri-plugin-log-api
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@tauri-apps/api': 1.2.0
|
||||
dev: false
|
||||
|
||||
github.com/tauri-apps/tauri-plugin-store/f4ef29684e4a32eddf51befaae98a5e498df8574:
|
||||
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-store/tar.gz/f4ef29684e4a32eddf51befaae98a5e498df8574}
|
||||
github.com/tauri-apps/tauri-plugin-store/0558a1f6c869ae0afdb0181dfa1ea31be8cf4893:
|
||||
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-store/tar.gz/0558a1f6c869ae0afdb0181dfa1ea31be8cf4893}
|
||||
name: tauri-plugin-store-api
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
|
BIN
public/screenshots/dark-1.webp
Normal file
After Width: | Height: | Size: 160 KiB |
BIN
public/screenshots/light-1.webp
Normal file
After Width: | Height: | Size: 162 KiB |
BIN
public/shortcuts/compose-96x96.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
public/shortcuts/compose.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
public/shortcuts/home-96x96.png
Normal file
After Width: | Height: | Size: 781 B |
BIN
public/shortcuts/home.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
public/shortcuts/local-96x96.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
public/shortcuts/local.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
public/shortcuts/notifications-96x96.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
public/shortcuts/notifications.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
public/shortcuts/settings-96x96.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
public/shortcuts/settings.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
@ -32,8 +32,23 @@ if (import.meta.env.DEV)
|
||||
|
||||
// deny api and server page calls
|
||||
let denylist: undefined | RegExp[]
|
||||
if (import.meta.env.PROD)
|
||||
denylist = [/^\/api\//, /^\/login\//, /^\/oauth\//, /^\/signin\//, /^\/web-share-target\//]
|
||||
if (import.meta.env.PROD) {
|
||||
denylist = [
|
||||
/^\/api\//,
|
||||
/^\/login\//,
|
||||
/^\/oauth\//,
|
||||
/^\/signin\//,
|
||||
/^\/web-share-target\//,
|
||||
// exclude shiki: has its own cache
|
||||
/^\/shiki\//,
|
||||
// exclude shiki: has its own cache
|
||||
/^\/emojis\//,
|
||||
// exclude sw: if the user navigates to it, fallback to index.html
|
||||
/^\/sw.js$/,
|
||||
// exclude webmanifest: has its own cache
|
||||
/^\/manifest-(.*).webmanifest$/,
|
||||
]
|
||||
}
|
||||
|
||||
// only cache pages and external assets on local build + start or in production
|
||||
if (import.meta.env.PROD) {
|
||||
@ -59,7 +74,7 @@ if (import.meta.env.PROD) {
|
||||
plugins: [
|
||||
new CacheableResponsePlugin({ statuses: [200] }),
|
||||
// 365 days max
|
||||
new ExpirationPlugin({ maxAgeSeconds: 60 * 60 * 24 * 365 }),
|
||||
new ExpirationPlugin({ purgeOnQuotaError: true, maxAgeSeconds: 60 * 60 * 24 * 365 }),
|
||||
],
|
||||
}),
|
||||
)
|
||||
@ -74,7 +89,7 @@ if (import.meta.env.PROD) {
|
||||
plugins: [
|
||||
new CacheableResponsePlugin({ statuses: [200] }),
|
||||
// 15 days max
|
||||
new ExpirationPlugin({ maxAgeSeconds: 60 * 60 * 24 * 15 }),
|
||||
new ExpirationPlugin({ purgeOnQuotaError: true, maxAgeSeconds: 60 * 60 * 24 * 15 }),
|
||||
],
|
||||
}),
|
||||
)
|
||||
|