From a9591043335883fe94cd378d104013225cc040c9 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 21 Feb 2022 11:14:20 -0500 Subject: [PATCH 01/16] Create service worker in app --- src/App.js | 2 ++ src/services/sw/sw.js | 2 ++ src/sw.js | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index 1c1b3a8c2..deb413540 100644 --- a/src/App.js +++ b/src/App.js @@ -14,6 +14,7 @@ import EditStatusModal from './components/edit_status_modal/edit_status_modal.vu import PostStatusModal from './components/post_status_modal/post_status_modal.vue' import StatusHistoryModal from './components/status_history_modal/status_history_modal.vue' import GlobalNoticeList from './components/global_notice_list/global_notice_list.vue' +import { getOrCreateServiceWorker } from './services/sw/sw' import { windowWidth, windowHeight } from './services/window_utils/window_utils' import { mapGetters } from 'vuex' import { defineAsyncComponent } from 'vue' @@ -65,6 +66,7 @@ export default { if (useInterfaceStore().themeApplied) { this.removeSplash() } + getOrCreateServiceWorker() }, unmounted () { window.removeEventListener('resize', this.updateMobileState) diff --git a/src/services/sw/sw.js b/src/services/sw/sw.js index 2133db222..0eea849b1 100644 --- a/src/services/sw/sw.js +++ b/src/services/sw/sw.js @@ -146,3 +146,5 @@ export function unregisterPushNotifications (token) { ]).catch((e) => console.warn(`Failed to disable Web Push Notifications: ${e.message}`)) } } + +export { getOrCreateServiceWorker } diff --git a/src/sw.js b/src/sw.js index 3c53d5339..af21a8746 100644 --- a/src/sw.js +++ b/src/sw.js @@ -143,4 +143,4 @@ self.addEventListener('notificationclick', (event) => { })) }) -console.log('sw here') +self.addEventListener('fetch', _ => _) From 2b16dd55c408e8b057a08924cb8bbf8dafba1b81 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Thu, 12 Aug 2021 22:42:08 -0400 Subject: [PATCH 02/16] Cache compiled assets in service worker --- src/sw.js | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/sw.js b/src/sw.js index af21a8746..2b2d2f3e9 100644 --- a/src/sw.js +++ b/src/sw.js @@ -85,6 +85,35 @@ const showPushNotification = async (event) => { return Promise.resolve() } +const shouldCache = process.env.NODE_ENV === 'production' +const cacheKey = 'pleroma-fe' +const cacheFiles = self.serviceWorkerOption.assets + +self.addEventListener('install', async (event) => { + if (shouldCache) { + event.waitUntil((async () => { + const cache = await caches.open(cacheKey) + await cache.addAll(cacheFiles) + })()) + } +}) + +self.addEventListener('activate', async (event) => { + if (shouldCache) { + event.waitUntil((async () => { + const cache = await caches.open(cacheKey) + const keys = await cache.keys() + await Promise.all( + keys.filter(request => { + const url = new URL(request.url) + const shouldKeep = cacheFiles.includes(url.pathname) + return !shouldKeep + }).map(k => cache.delete(k)) + ) + })()) + } +}) + self.addEventListener('push', async (event) => { if (event.data) { // Supposedly, we HAVE to return a promise inside waitUntil otherwise it will @@ -143,4 +172,16 @@ self.addEventListener('notificationclick', (event) => { })) }) -self.addEventListener('fetch', _ => _) +self.addEventListener('fetch', async (event) => { + if (shouldCache) { + event.respondWith((async () => { + const r = await caches.match(event.request) + console.log(`[Service Worker] Fetching resource: ${event.request.url}`) + if (r) { + return r + } + const response = await fetch(event.request) + return response + })()) + } +}) From 63490ad2daf6a49ecfa374e38f678d6606ac95bc Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sun, 15 Aug 2021 10:51:49 -0400 Subject: [PATCH 03/16] Cache emojis in service worker --- src/sw.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sw.js b/src/sw.js index 2b2d2f3e9..94c0a93e8 100644 --- a/src/sw.js +++ b/src/sw.js @@ -88,6 +88,16 @@ const showPushNotification = async (event) => { const shouldCache = process.env.NODE_ENV === 'production' const cacheKey = 'pleroma-fe' const cacheFiles = self.serviceWorkerOption.assets +const emojiCacheKey = 'pleroma-fe-emoji' +const isEmoji = req => { + console.log('req.method=', req.method) + if (req.method !== 'GET') { + return false + } + const url = new URL(req.url) + console.log('pathname=', url.pathname) + return url.pathname.startsWith('/emoji/') +} self.addEventListener('install', async (event) => { if (shouldCache) { @@ -180,7 +190,13 @@ self.addEventListener('fetch', async (event) => { if (r) { return r } + const response = await fetch(event.request) + if (response.ok && isEmoji(event.request)) { + console.log(`[Service Worker] Caching emoji ${event.request.url}`) + const cache = await caches.open(emojiCacheKey) + await cache.put(event.request.clone(), response.clone()) + } return response })()) } From f1f7f5de5126d3aad3ca58d416677225ce080950 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Tue, 11 Jan 2022 23:03:46 -0500 Subject: [PATCH 04/16] Fix the bug where remote media fail to load without media proxy --- src/sw.js | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/sw.js b/src/sw.js index 94c0a93e8..65ab49c3c 100644 --- a/src/sw.js +++ b/src/sw.js @@ -182,8 +182,12 @@ self.addEventListener('notificationclick', (event) => { })) }) -self.addEventListener('fetch', async (event) => { - if (shouldCache) { +self.addEventListener('fetch', (event) => { + console.log(`[Service Worker] Got: ${event.request.url}`) + console.debug(event.request) + // Do not mess up with remote things + const isSameOrigin = (new URL(event.request.url)).origin === self.location.origin + if (shouldCache && event.request.method === 'GET' && isSameOrigin) { event.respondWith((async () => { const r = await caches.match(event.request) console.log(`[Service Worker] Fetching resource: ${event.request.url}`) @@ -191,13 +195,18 @@ self.addEventListener('fetch', async (event) => { return r } - const response = await fetch(event.request) - if (response.ok && isEmoji(event.request)) { - console.log(`[Service Worker] Caching emoji ${event.request.url}`) - const cache = await caches.open(emojiCacheKey) - await cache.put(event.request.clone(), response.clone()) + try { + const response = await fetch(event.request) + if (response.ok && isEmoji(event.request)) { + console.log(`[Service Worker] Caching emoji ${event.request.url}`) + const cache = await caches.open(emojiCacheKey) + await cache.put(event.request.clone(), response.clone()) + } + return response + } catch (e) { + console.log('error:', e) + throw e } - return response })()) } }) From 6ab98db7ad7a611c35fe5c7077079f6741cfe3f3 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Mon, 21 Feb 2022 11:19:10 -0500 Subject: [PATCH 05/16] Clean up debug code --- src/sw.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/sw.js b/src/sw.js index 65ab49c3c..87eb484a5 100644 --- a/src/sw.js +++ b/src/sw.js @@ -90,12 +90,11 @@ const cacheKey = 'pleroma-fe' const cacheFiles = self.serviceWorkerOption.assets const emojiCacheKey = 'pleroma-fe-emoji' const isEmoji = req => { - console.log('req.method=', req.method) if (req.method !== 'GET') { return false } const url = new URL(req.url) - console.log('pathname=', url.pathname) + return url.pathname.startsWith('/emoji/') } @@ -183,14 +182,12 @@ self.addEventListener('notificationclick', (event) => { }) self.addEventListener('fetch', (event) => { - console.log(`[Service Worker] Got: ${event.request.url}`) - console.debug(event.request) // Do not mess up with remote things const isSameOrigin = (new URL(event.request.url)).origin === self.location.origin if (shouldCache && event.request.method === 'GET' && isSameOrigin) { event.respondWith((async () => { const r = await caches.match(event.request) - console.log(`[Service Worker] Fetching resource: ${event.request.url}`) + if (r) { return r } @@ -198,13 +195,11 @@ self.addEventListener('fetch', (event) => { try { const response = await fetch(event.request) if (response.ok && isEmoji(event.request)) { - console.log(`[Service Worker] Caching emoji ${event.request.url}`) const cache = await caches.open(emojiCacheKey) await cache.put(event.request.clone(), response.clone()) } return response } catch (e) { - console.log('error:', e) throw e } })()) From ed04c2ac710cb51f01d34efcfdf8d007fa764c16 Mon Sep 17 00:00:00 2001 From: tusooa Date: Fri, 26 May 2023 16:37:06 -0400 Subject: [PATCH 06/16] Exclude local media requests --- src/sw.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sw.js b/src/sw.js index 87eb484a5..c53927a32 100644 --- a/src/sw.js +++ b/src/sw.js @@ -97,6 +97,15 @@ const isEmoji = req => { return url.pathname.startsWith('/emoji/') } +const isNotMedia = req => { + console.log('req.method=', req.method) + if (req.method !== 'GET') { + return false + } + const url = new URL(req.url) + console.log('pathname=', url.pathname) + return !url.pathname.startsWith('/media/') +} self.addEventListener('install', async (event) => { if (shouldCache) { @@ -184,7 +193,7 @@ self.addEventListener('notificationclick', (event) => { self.addEventListener('fetch', (event) => { // Do not mess up with remote things const isSameOrigin = (new URL(event.request.url)).origin === self.location.origin - if (shouldCache && event.request.method === 'GET' && isSameOrigin) { + if (shouldCache && event.request.method === 'GET' && isSameOrigin && isNotMedia(event.request)) { event.respondWith((async () => { const r = await caches.match(event.request) From cc4d4ccbeb933738e52c9cfd85549215447ea5d4 Mon Sep 17 00:00:00 2001 From: tusooa Date: Fri, 16 Jun 2023 23:12:20 -0400 Subject: [PATCH 07/16] Treat html file as failure in emoji cache --- src/sw.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sw.js b/src/sw.js index c53927a32..3e7af8fcb 100644 --- a/src/sw.js +++ b/src/sw.js @@ -193,17 +193,22 @@ self.addEventListener('notificationclick', (event) => { self.addEventListener('fetch', (event) => { // Do not mess up with remote things const isSameOrigin = (new URL(event.request.url)).origin === self.location.origin + const isEmojiSuccessful = (resp) => { + const type = resp.headers.get('Content-Type') + // Backend will revert to index.html if the file does not exist, so text/html for emojis is a failure + return type && !type.includes('text/html') + } if (shouldCache && event.request.method === 'GET' && isSameOrigin && isNotMedia(event.request)) { event.respondWith((async () => { const r = await caches.match(event.request) - if (r) { + if (r && (isEmojiSuccessful(r) || !isEmoji(event.request))) { return r } try { const response = await fetch(event.request) - if (response.ok && isEmoji(event.request)) { + if (response.ok && isEmojiSuccessful(response) && isEmoji(event.request)) { const cache = await caches.open(emojiCacheKey) await cache.put(event.request.clone(), response.clone()) } From 3315901b077512d3dfaa74e20ad64858a569d6b5 Mon Sep 17 00:00:00 2001 From: tusooa Date: Thu, 22 Aug 2024 21:41:14 -0400 Subject: [PATCH 08/16] Add changelog for caching assets --- changelog.d/sw-cache-assets.add | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/sw-cache-assets.add diff --git a/changelog.d/sw-cache-assets.add b/changelog.d/sw-cache-assets.add new file mode 100644 index 000000000..5f7414eee --- /dev/null +++ b/changelog.d/sw-cache-assets.add @@ -0,0 +1 @@ +Cache assets and emojis with service worker From 67724ad2a437ac380146ea33550e036f1b20cb14 Mon Sep 17 00:00:00 2001 From: tusooa Date: Thu, 22 Aug 2024 21:44:55 -0400 Subject: [PATCH 09/16] Fix useless try-catch --- src/sw.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sw.js b/src/sw.js index 3e7af8fcb..daf4cdd19 100644 --- a/src/sw.js +++ b/src/sw.js @@ -214,6 +214,7 @@ self.addEventListener('fetch', (event) => { } return response } catch (e) { + console.error('[Service worker] error when caching emoji:', e) throw e } })()) From e21bac3d704d42b650b56976b46c42ad9216feaf Mon Sep 17 00:00:00 2001 From: tusooa Date: Fri, 23 Aug 2024 00:31:03 -0400 Subject: [PATCH 10/16] Implement clearing caches in settings --- .../settings_modal/tabs/general_tab.js | 16 +++++++++++++ .../settings_modal/tabs/general_tab.vue | 23 +++++++++++++++++++ src/i18n/en.json | 5 +++- src/services/sw/sw.js | 6 +++++ src/sw.js | 4 +--- 5 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index 517f54eb1..9275d9e6f 100644 --- a/src/components/settings_modal/tabs/general_tab.js +++ b/src/components/settings_modal/tabs/general_tab.js @@ -8,6 +8,7 @@ import InterfaceLanguageSwitcher from 'src/components/interface_language_switche import SharedComputedObject from '../helpers/shared_computed_object.js' import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue' +import { clearCache, cacheKey, emojiCacheKey } from 'src/services/sw/sw.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faGlobe @@ -98,6 +99,21 @@ const GeneralTab = { methods: { changeDefaultScope (value) { this.$store.dispatch('setProfileOption', { name: 'defaultScope', value }) + }, + clearCache (key) { + clearCache(key) + .then((v) => { + this.$store.dispatch('settingsSaved', { success: true }) + }) + .catch(error => { + this.$store.dispatch('settingsSaved', { error }) + }) + }, + clearAssetCache () { + this.clearCache(cacheKey) + }, + clearEmojiCache () { + this.clearCache(emojiCacheKey) } } } diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index 02396114f..b8da782fb 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -509,6 +509,29 @@ +
+

{{ $t('settings.cache') }}

+
    +
  • + +
  • +
  • + +
  • +
+
diff --git a/src/i18n/en.json b/src/i18n/en.json index 9d3400d10..304a64ea3 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -1049,7 +1049,10 @@ "reset_value": "Reset", "reset_value_tooltip": "Reset draft", "hard_reset_value": "Hard reset", - "hard_reset_value_tooltip": "Remove setting from storage, forcing use of default value" + "hard_reset_value_tooltip": "Remove setting from storage, forcing use of default value", + "cache": "Cache", + "clear_asset_cache": "Clear asset cache", + "clear_emoji_cache": "Clear emoji cache" }, "admin_dash": { "window_title": "Administration", diff --git a/src/services/sw/sw.js b/src/services/sw/sw.js index 0eea849b1..a986e98dd 100644 --- a/src/services/sw/sw.js +++ b/src/services/sw/sw.js @@ -147,4 +147,10 @@ export function unregisterPushNotifications (token) { } } +export const shouldCache = process.env.NODE_ENV === 'production' +export const cacheKey = 'pleroma-fe' +export const emojiCacheKey = 'pleroma-fe-emoji' + +export const clearCache = (key) => caches.delete(key) + export { getOrCreateServiceWorker } diff --git a/src/sw.js b/src/sw.js index daf4cdd19..2ce1a0648 100644 --- a/src/sw.js +++ b/src/sw.js @@ -3,6 +3,7 @@ import { storage } from 'src/lib/storage.js' import { parseNotification } from './services/entity_normalizer/entity_normalizer.service.js' import { prepareNotificationObject } from './services/notification_utils/notification_utils.js' +import { shouldCache, cacheKey, emojiCacheKey } from './services/sw/sw.js' import { createI18n } from 'vue-i18n' // Collects all messages for service workers // Needed because service workers cannot use dynamic imports @@ -85,10 +86,7 @@ const showPushNotification = async (event) => { return Promise.resolve() } -const shouldCache = process.env.NODE_ENV === 'production' -const cacheKey = 'pleroma-fe' const cacheFiles = self.serviceWorkerOption.assets -const emojiCacheKey = 'pleroma-fe-emoji' const isEmoji = req => { if (req.method !== 'GET') { return false From bbeafab1efca446c984eae037d78732c8e6fcc69 Mon Sep 17 00:00:00 2001 From: tusooa Date: Fri, 23 Aug 2024 01:45:36 -0400 Subject: [PATCH 11/16] Verify response is not html when pre-caching in install event --- src/sw.js | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/sw.js b/src/sw.js index 2ce1a0648..f9ae8dc17 100644 --- a/src/sw.js +++ b/src/sw.js @@ -105,11 +105,35 @@ const isNotMedia = req => { return !url.pathname.startsWith('/media/') } +const isSuccessful = (resp) => { + if (!resp.ok) { + return false + } + if ((new URL(resp.url)).pathname === '/index.html') { + // For index.html itself, there is no fallback possible. + return true + } + const type = resp.headers.get('Content-Type') + // Backend will revert to index.html if the file does not exist, so text/html for emojis and assets is a failure + return type && !type.includes('text/html') +} + self.addEventListener('install', async (event) => { if (shouldCache) { event.waitUntil((async () => { const cache = await caches.open(cacheKey) - await cache.addAll(cacheFiles) + await Promise.allSettled(cacheFiles.map(async (route) => { + // https://developer.mozilla.org/en-US/docs/Web/API/Cache/add + // originally we used addAll() but it will raise a problem in one edge case: + // when the file for the route is not found, backend will return index.html with code 200 + // but it's wrong, and it's cached, so we end up with a bad cache. + // this can happen when you refresh when you are in the process of upgrading + // the frontend. + const resp = await fetch(route) + if (isSuccessful(resp)) { + await cache.put(route, resp) + } + })) })()) } }) @@ -191,22 +215,17 @@ self.addEventListener('notificationclick', (event) => { self.addEventListener('fetch', (event) => { // Do not mess up with remote things const isSameOrigin = (new URL(event.request.url)).origin === self.location.origin - const isEmojiSuccessful = (resp) => { - const type = resp.headers.get('Content-Type') - // Backend will revert to index.html if the file does not exist, so text/html for emojis is a failure - return type && !type.includes('text/html') - } if (shouldCache && event.request.method === 'GET' && isSameOrigin && isNotMedia(event.request)) { event.respondWith((async () => { const r = await caches.match(event.request) - if (r && (isEmojiSuccessful(r) || !isEmoji(event.request))) { + if (r && (isSuccessful(r) || !isEmoji(event.request))) { return r } try { const response = await fetch(event.request) - if (response.ok && isEmojiSuccessful(response) && isEmoji(event.request)) { + if (response.ok && isSuccessful(response) && isEmoji(event.request)) { const cache = await caches.open(emojiCacheKey) await cache.put(event.request.clone(), response.clone()) } From 8b8f6cbca243f356b6a16c01d6f6900cfc9eccb9 Mon Sep 17 00:00:00 2001 From: tusooa Date: Fri, 23 Aug 2024 02:49:07 -0400 Subject: [PATCH 12/16] Cache assets when cache is missing --- src/sw.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/sw.js b/src/sw.js index f9ae8dc17..aa061e14f 100644 --- a/src/sw.js +++ b/src/sw.js @@ -96,14 +96,16 @@ const isEmoji = req => { return url.pathname.startsWith('/emoji/') } const isNotMedia = req => { - console.log('req.method=', req.method) if (req.method !== 'GET') { return false } const url = new URL(req.url) - console.log('pathname=', url.pathname) return !url.pathname.startsWith('/media/') } +const isAsset = req => { + const url = new URL(req.url) + return cacheFiles.includes(url.pathname) +} const isSuccessful = (resp) => { if (!resp.ok) { @@ -216,17 +218,23 @@ self.addEventListener('fetch', (event) => { // Do not mess up with remote things const isSameOrigin = (new URL(event.request.url)).origin === self.location.origin if (shouldCache && event.request.method === 'GET' && isSameOrigin && isNotMedia(event.request)) { + console.debug('[Service worker] fetch:', event.request.url) event.respondWith((async () => { const r = await caches.match(event.request) + const isEmojiReq = isEmoji(event.request) - if (r && (isSuccessful(r) || !isEmoji(event.request))) { + if (r && isSuccessful(r)) { + console.debug('[Service worker] already cached:', event.request.url) return r } try { const response = await fetch(event.request) - if (response.ok && isSuccessful(response) && isEmoji(event.request)) { - const cache = await caches.open(emojiCacheKey) + if (response.ok && + isSuccessful(response) && + (isEmojiReq || isAsset(event.request))) { + console.debug(`[Service worker] caching ${isEmojiReq ? 'emoji' : 'asset'}:`, event.request.url) + const cache = await caches.open(isEmojiReq ? emojiCacheKey : cacheKey) await cache.put(event.request.clone(), response.clone()) } return response From 652781fcc8ff1384555383bf1d81bc893cc28fc5 Mon Sep 17 00:00:00 2001 From: tusooa Date: Fri, 23 Aug 2024 02:57:44 -0400 Subject: [PATCH 13/16] Do not preload emoji annotations and i18n files --- src/sw.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sw.js b/src/sw.js index aa061e14f..8c83762ee 100644 --- a/src/sw.js +++ b/src/sw.js @@ -123,8 +123,12 @@ const isSuccessful = (resp) => { self.addEventListener('install', async (event) => { if (shouldCache) { event.waitUntil((async () => { + // Do not preload i18n and emoji annotations to speed up loading + const shouldPreload = (route) => { + return !route.startsWith('/static/js/i18n/') && !route.startsWith('/static/js/emoji-annotations/') + } const cache = await caches.open(cacheKey) - await Promise.allSettled(cacheFiles.map(async (route) => { + await Promise.allSettled(cacheFiles.filter(shouldPreload).map(async (route) => { // https://developer.mozilla.org/en-US/docs/Web/API/Cache/add // originally we used addAll() but it will raise a problem in one edge case: // when the file for the route is not found, backend will return index.html with code 200 From 0cb47652b88a0c2a3a7572f784246b7ed51dc2a1 Mon Sep 17 00:00:00 2001 From: tusooa Date: Tue, 4 Mar 2025 18:39:20 -0500 Subject: [PATCH 14/16] Make asset caching work with vite --- build/sw_plugin.js | 48 ++++++++++++++++++++++++++++++++++++++++++++++ src/sw.js | 1 + vite.config.js | 9 ++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/build/sw_plugin.js b/build/sw_plugin.js index 90ab856ad..a2c792b7d 100644 --- a/build/sw_plugin.js +++ b/build/sw_plugin.js @@ -11,6 +11,11 @@ const getSWMessagesAsText = async () => { } const projectRoot = dirname(dirname(fileURLToPath(import.meta.url))) +const swEnvName = 'virtual:pleroma-fe/service_worker_env' +const swEnvNameResolved = '\0' + swEnvName +const getDevSwEnv = () => `self.serviceWorkerOption = { assets: [] };` +const getProdSwEnv = ({ assets }) => `self.serviceWorkerOption = { assets: ${JSON.stringify(assets)} };` + export const devSwPlugin = ({ swSrc, swDest, @@ -32,12 +37,16 @@ export const devSwPlugin = ({ const name = id.startsWith('/') ? id.slice(1) : id if (name === swDest) { return swFullSrc + } else if (name === swEnvName) { + return swEnvNameResolved } return null }, async load (id) { if (id === swFullSrc) { return readFile(swFullSrc, 'utf-8') + } else if (id === swEnvNameResolved) { + return getDevSwEnv() } return null }, @@ -79,6 +88,21 @@ export const devSwPlugin = ({ contents: await getSWMessagesAsText() })) } + }, { + name: 'sw-env', + setup (b) { + b.onResolve( + { filter: new RegExp('^' + swEnvName + '$') }, + args => ({ + path: args.path, + namespace: 'sw-env' + })) + b.onLoad( + { filter: /.*/, namespace: 'sw-env' }, + () => ({ + contents: getDevSwEnv() + })) + } }] }) const text = res.outputFiles[0].text @@ -126,6 +150,30 @@ export const buildSwPlugin = ({ configFile: false } }, + generateBundle: { + order: 'post', + sequential: true, + async handler (_, bundle) { + const assets = Object.keys(bundle) + .filter(name => !/\.map$/.test(name)) + .map(name => '/' + name) + config.plugins.push({ + name: 'build-sw-env-plugin', + resolveId (id) { + if (id === swEnvName) { + return swEnvNameResolved + } + return null + }, + load (id) { + if (id === swEnvNameResolved) { + return getProdSwEnv({ assets }) + } + return null + } + }) + } + }, closeBundle: { order: 'post', sequential: true, diff --git a/src/sw.js b/src/sw.js index 8c83762ee..7ba910589 100644 --- a/src/sw.js +++ b/src/sw.js @@ -1,5 +1,6 @@ /* eslint-env serviceworker */ +import 'virtual:pleroma-fe/service_worker_env' import { storage } from 'src/lib/storage.js' import { parseNotification } from './services/entity_normalizer/entity_normalizer.service.js' import { prepareNotificationObject } from './services/notification_utils/notification_utils.js' diff --git a/vite.config.js b/vite.config.js index 2abf18e50..4f78fc43e 100644 --- a/vite.config.js +++ b/vite.config.js @@ -165,7 +165,14 @@ export default defineConfig(async ({ mode, command }) => { return 'static/js/[name].[hash].js' } }, - chunkFileNames () { + chunkFileNames (chunkInfo) { + if (chunkInfo.facadeModuleId) { + if (chunkInfo.facadeModuleId.includes('node_modules/@kazvmoe-infra/unicode-emoji-json/annotations/')) { + return 'static/js/emoji-annotations/[name].[hash].js' + } else if (chunkInfo.facadeModuleId.includes('src/i18n/')) { + return 'static/js/i18n/[name].[hash].js' + } + } return 'static/js/[name].[hash].js' }, assetFileNames (assetInfo) { From 7f880dea0e33b6a52ad645b212e79867dc5907b3 Mon Sep 17 00:00:00 2001 From: tusooa Date: Tue, 4 Mar 2025 19:37:24 -0500 Subject: [PATCH 15/16] Fix clear cache lint --- src/components/settings_modal/tabs/general_tab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index 9275d9e6f..df28a23e3 100644 --- a/src/components/settings_modal/tabs/general_tab.js +++ b/src/components/settings_modal/tabs/general_tab.js @@ -102,7 +102,7 @@ const GeneralTab = { }, clearCache (key) { clearCache(key) - .then((v) => { + .then(() => { this.$store.dispatch('settingsSaved', { success: true }) }) .catch(error => { From 917fc1def0daf203f9b1e5bcdb1c4408ad9bf483 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Wed, 25 Jun 2025 15:58:19 +0300 Subject: [PATCH 16/16] reduce spam --- src/sw.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sw.js b/src/sw.js index 7ba910589..834c68e7f 100644 --- a/src/sw.js +++ b/src/sw.js @@ -223,7 +223,8 @@ self.addEventListener('fetch', (event) => { // Do not mess up with remote things const isSameOrigin = (new URL(event.request.url)).origin === self.location.origin if (shouldCache && event.request.method === 'GET' && isSameOrigin && isNotMedia(event.request)) { - console.debug('[Service worker] fetch:', event.request.url) + // this is a bit spammy + // console.debug('[Service worker] fetch:', event.request.url) event.respondWith((async () => { const r = await caches.match(event.request) const isEmojiReq = isEmoji(event.request)