From bb3e6c8228e78447d8bf05b7f3ac3b853e269826 Mon Sep 17 00:00:00 2001 From: Alek Evans Date: Tue, 5 Nov 2024 12:35:34 -0900 Subject: [PATCH] Improved connectivity handling --- package.json | 2 +- src/App.vue | 2 +- src/components/DomainModal.vue | 4 +- src/components/Online.vue | 2 +- src/main.ts | 2 + src/sw.ts | 58 ++++++++++++++++++- src/utils/ConnectivityCheck.ts | 46 +++++++++++++++ src/utils/loadWhenOnline.ts | 2 +- src/views/AccountSwitcher.vue | 2 +- src/views/Auth.vue | 4 +- src/views/staff-space/Domains.vue | 2 +- src/views/staff-space/Files.vue | 2 +- src/views/staff-space/KillSwitches.vue | 2 +- src/views/staff-space/SetupGuide.vue | 2 +- src/views/staff-space/SetupGuides.vue | 2 +- src/views/staff-space/User.vue | 2 +- src/views/staff-space/UserSecondFactors.vue | 2 +- src/views/staff-space/UserSessions.vue | 2 +- src/views/staff-space/Users.vue | 2 +- src/views/user-space/Account.vue | 2 +- src/views/user-space/AccountSecondFactors.vue | 2 +- src/views/user-space/AccountSessions.vue | 2 +- src/views/user-space/Files.vue | 2 +- src/views/user-space/SetupGuides.vue | 2 +- src/views/user-space/Verify.vue | 2 +- vite.config.ts | 2 + 26 files changed, 130 insertions(+), 26 deletions(-) create mode 100644 src/utils/ConnectivityCheck.ts diff --git a/package.json b/package.json index 6ae6720..b2d5228 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cumulonimbus-frontend", "private": true, - "version": "4.6.0", + "version": "4.6.1", "type": "module", "license": "GNU-GPL-3.0", "scripts": { diff --git a/src/App.vue b/src/App.vue index 3717784..eef912a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -112,7 +112,7 @@ // External Modules import { ref, onMounted, watch, computed } from 'vue'; import { useRouter, useRoute } from 'vue-router'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; const user = userStore(), toast = toastStore(), diff --git a/src/components/DomainModal.vue b/src/components/DomainModal.vue index 257bdd6..f4eb6ee 100644 --- a/src/components/DomainModal.vue +++ b/src/components/DomainModal.vue @@ -73,7 +73,7 @@ // External Modules import { ref, onMounted, watch } from 'vue'; - import { useNetwork } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; const emit = defineEmits<{ (event: 'submit', data: { domain: string; subdomain?: string }): void; @@ -101,7 +101,7 @@ domain = ref(), subdomain = ref(), allowsSubdomains = ref(false), - { isOnline: online } = useNetwork(); + online = useOnline(); function onSubdomainInput(event: Event) { const input = event.target as HTMLInputElement; diff --git a/src/components/Online.vue b/src/components/Online.vue index 9b10ff5..c07ac1a 100644 --- a/src/components/Online.vue +++ b/src/components/Online.vue @@ -20,7 +20,7 @@ // No Store Modules to import here. // External Modules - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; const props = defineProps({ noMsg: { diff --git a/src/main.ts b/src/main.ts index 368559a..c3d9c27 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,6 @@ import { createApp } from 'vue'; import App from './App.vue'; +import { ConnectivityCheckPlugin } from './utils/ConnectivityCheck'; import { createPinia } from 'pinia'; import { router } from './router'; import packageJson from '../package.json'; @@ -13,5 +14,6 @@ app.config.globalProperties.$dependencies = packageJson.dependencies; app.config.globalProperties.$devDependencies = packageJson.devDependencies; app.use(router); +app.use(ConnectivityCheckPlugin); app.use(createPinia()); app.mount('html body'); diff --git a/src/sw.ts b/src/sw.ts index f2400b6..fac071f 100644 --- a/src/sw.ts +++ b/src/sw.ts @@ -93,6 +93,60 @@ async function threadedPrecache(urls: string[], threads: number = 4) { debugLog('ServiceWorkerThreadedPrecache', 'Precaching complete.'); } +// ---- Refined Connectivity Check ---- + +const checkInterval = 60e3, + checkTarget = 'https://connectivitycheck.alekeagle.com', + checkTargetResponseStatus = 204; + +// The variable we'll use to track if we're online or not. We'll initialize with the navigator's online status. +let isOnline = navigator.onLine, + isOnlineInterval: ReturnType | null = null; + +async function makeConnectionCheck() { + try { + // Make a call to a known online resource to check if we're online. + const res = await fetch(checkTarget, { method: 'HEAD' }); + // If the response status matches the expected status, we're online. + if (isOnline !== (res.status === checkTargetResponseStatus)) { + isOnline = res.status === checkTargetResponseStatus; + debugLog( + 'ServiceWorkerConnectivityCheck', + 'Connection status changed:', + isOnline, + ); + } + } catch (err) { + // If the fetch fails, we're probably offline. + if (isOnline) { + isOnline = false; + debugLog( + 'ServiceWorkerConnectivityCheck', + 'Connection status changed:', + isOnline, + ); + } + } +} + +async function runConnectionCheck() { + // Clear the interval if it exists. + if (isOnlineInterval) { + clearInterval(isOnlineInterval); + isOnlineInterval = null; + } + // Make a connection check immediately. + await makeConnectionCheck(); + // Set the interval back up to check every x seconds. + isOnlineInterval = setInterval(makeConnectionCheck, checkInterval); +} + +self.addEventListener('online', runConnectionCheck); +self.addEventListener('offline', runConnectionCheck); + +// Start the connection check interval. +runConnectionCheck(); + // ---- Service worker lifecycle ---- self.addEventListener('install', async (event) => { @@ -149,7 +203,7 @@ router.addRoute( 'Cache hit', `URL: ${options.url}`, ); - if (navigator.onLine) { + if (isOnline) { debugLog( 'ServiceWorkerOfflineCacheManager', 'Revalidating cache', @@ -167,7 +221,7 @@ router.addRoute( 'Cache miss', `URL: ${options.url}`, ); - if (!navigator.onLine) { + if (!isOnline) { debugLog( 'ServiceWorkerOfflineCacheManager', 'Offline, serving default route', diff --git a/src/utils/ConnectivityCheck.ts b/src/utils/ConnectivityCheck.ts new file mode 100644 index 0000000..7360426 --- /dev/null +++ b/src/utils/ConnectivityCheck.ts @@ -0,0 +1,46 @@ +import { inject, Plugin, ref } from 'vue'; + +const checkInterval = 60e3, + checkTarget = 'https://connectivitycheck.alekeagle.com', + checkTargetResponseStatus = 204; + +export const ConnectivityCheckPlugin: Plugin = { + install(app) { + let isOnlineInterval: ReturnType | null = null; + // Initialize the isOnline ref with the current navigator status + const isOnline = ref(navigator.onLine); + + app.provide('isOnline', isOnline); + + async function makeConnectionCheck() { + try { + const response = await fetch(checkTarget, { method: 'HEAD' }); + isOnline.value = response.status === checkTargetResponseStatus; + } catch (error) { + isOnline.value = false; + } + } + + async function runConnectionCheck() { + // Clear the interval if it exists. + if (isOnlineInterval) { + clearInterval(isOnlineInterval); + isOnlineInterval = null; + } + // Make a connection check immediately. + await makeConnectionCheck(); + // Set the interval back up to check every x seconds. + isOnlineInterval = setInterval(makeConnectionCheck, checkInterval); + } + + window.addEventListener('online', runConnectionCheck); + window.addEventListener('offline', runConnectionCheck); + + // Start the connection check interval. + runConnectionCheck(); + }, +}; + +export const useOnline = () => { + return inject('isOnline') as ReturnType; +}; diff --git a/src/utils/loadWhenOnline.ts b/src/utils/loadWhenOnline.ts index 4b0eee8..197105b 100644 --- a/src/utils/loadWhenOnline.ts +++ b/src/utils/loadWhenOnline.ts @@ -1,4 +1,4 @@ -import { useOnline } from '@vueuse/core'; +import { useOnline } from '@/utils/ConnectivityCheck'; import { watch } from 'vue'; // This function takes a callback and conditions for when to call the callback. diff --git a/src/views/AccountSwitcher.vue b/src/views/AccountSwitcher.vue index 144d5d9..c81858c 100644 --- a/src/views/AccountSwitcher.vue +++ b/src/views/AccountSwitcher.vue @@ -110,7 +110,7 @@ // External Modules import { ref, onBeforeMount, computed } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import { useRouter } from 'vue-router'; const user = userStore(), diff --git a/src/views/Auth.vue b/src/views/Auth.vue index aa2f516..56744b4 100644 --- a/src/views/Auth.vue +++ b/src/views/Auth.vue @@ -117,7 +117,7 @@ // External Modules import { ref, onMounted } from 'vue'; - import { useNetwork } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import { useRouter, useRoute } from 'vue-router'; const user = userStore(), @@ -128,7 +128,7 @@ toast = toastStore(), loginForm = ref>(), registerForm = ref>(), - { isOnline: online } = useNetwork(); + online = useOnline(); async function toggleState() { switch (action.value) { diff --git a/src/views/staff-space/Domains.vue b/src/views/staff-space/Domains.vue index 75e2bf4..330c937 100644 --- a/src/views/staff-space/Domains.vue +++ b/src/views/staff-space/Domains.vue @@ -168,7 +168,7 @@ // External Modules import { ref, onMounted } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import { useRouter } from 'vue-router'; const online = useOnline(), diff --git a/src/views/staff-space/Files.vue b/src/views/staff-space/Files.vue index 9215a7e..d62d508 100644 --- a/src/views/staff-space/Files.vue +++ b/src/views/staff-space/Files.vue @@ -95,7 +95,7 @@ // External Modules import { ref, onMounted } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import { useRouter } from 'vue-router'; import loadWhenOnline from '@/utils/loadWhenOnline'; diff --git a/src/views/staff-space/KillSwitches.vue b/src/views/staff-space/KillSwitches.vue index 8c97539..4a637ba 100644 --- a/src/views/staff-space/KillSwitches.vue +++ b/src/views/staff-space/KillSwitches.vue @@ -93,7 +93,7 @@ // External Modules import { ref, onMounted } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import LoadingMessage from '@/components/LoadingMessage.vue'; const killSwitches = killSwitchesStore(), diff --git a/src/views/staff-space/SetupGuide.vue b/src/views/staff-space/SetupGuide.vue index b6cecd1..71b66f9 100644 --- a/src/views/staff-space/SetupGuide.vue +++ b/src/views/staff-space/SetupGuide.vue @@ -227,7 +227,7 @@ // External Modules import { ref, watch, onMounted } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import { useRouter } from 'vue-router'; const online = useOnline(), diff --git a/src/views/staff-space/SetupGuides.vue b/src/views/staff-space/SetupGuides.vue index 9996be2..94a94be 100644 --- a/src/views/staff-space/SetupGuides.vue +++ b/src/views/staff-space/SetupGuides.vue @@ -132,7 +132,7 @@ // External Modules import { ref, watch, onMounted } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import { useRouter } from 'vue-router'; import loadWhenOnline from '@/utils/loadWhenOnline'; diff --git a/src/views/staff-space/User.vue b/src/views/staff-space/User.vue index d6aa5aa..ecc8761 100644 --- a/src/views/staff-space/User.vue +++ b/src/views/staff-space/User.vue @@ -562,7 +562,7 @@ // External Modules import { ref, computed, onMounted } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import { useRouter } from 'vue-router'; const user = userStore(), diff --git a/src/views/staff-space/UserSecondFactors.vue b/src/views/staff-space/UserSecondFactors.vue index 4b618c9..0e71ef6 100644 --- a/src/views/staff-space/UserSecondFactors.vue +++ b/src/views/staff-space/UserSecondFactors.vue @@ -184,7 +184,7 @@ // External Modules import { ref, onMounted } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import { useRouter } from 'vue-router'; const secondFactors = secondFactorsStore(), diff --git a/src/views/staff-space/UserSessions.vue b/src/views/staff-space/UserSessions.vue index af01741..68e5c36 100644 --- a/src/views/staff-space/UserSessions.vue +++ b/src/views/staff-space/UserSessions.vue @@ -128,7 +128,7 @@ // External Modules import { ref, onMounted, computed } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import { useRouter } from 'vue-router'; const router = useRouter(), diff --git a/src/views/staff-space/Users.vue b/src/views/staff-space/Users.vue index 334e32b..bf5983d 100644 --- a/src/views/staff-space/Users.vue +++ b/src/views/staff-space/Users.vue @@ -97,7 +97,7 @@ // External Modules import { ref, onMounted } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import loadWhenOnline from '@/utils/loadWhenOnline'; const users = usersStore(), diff --git a/src/views/user-space/Account.vue b/src/views/user-space/Account.vue index 7c6a999..440b7d6 100644 --- a/src/views/user-space/Account.vue +++ b/src/views/user-space/Account.vue @@ -396,7 +396,7 @@ // External Modules import { ref } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import { useRouter } from 'vue-router'; const deleteAccountModal = ref>(), diff --git a/src/views/user-space/AccountSecondFactors.vue b/src/views/user-space/AccountSecondFactors.vue index 31e2699..25dc9af 100644 --- a/src/views/user-space/AccountSecondFactors.vue +++ b/src/views/user-space/AccountSecondFactors.vue @@ -337,7 +337,7 @@ // External Modules import { ref, onMounted, computed } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import QRCode from 'qrcode'; import { startRegistration } from '@simplewebauthn/browser'; diff --git a/src/views/user-space/AccountSessions.vue b/src/views/user-space/AccountSessions.vue index 2948c73..15ca0bf 100644 --- a/src/views/user-space/AccountSessions.vue +++ b/src/views/user-space/AccountSessions.vue @@ -479,7 +479,7 @@ // External Modules import { ref, onMounted, computed } from 'vue'; import { useRouter } from 'vue-router'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import { useClipboard } from '@vueuse/core'; const confirmDeleteModal = ref>(), diff --git a/src/views/user-space/Files.vue b/src/views/user-space/Files.vue index d5a38dd..0b2760e 100644 --- a/src/views/user-space/Files.vue +++ b/src/views/user-space/Files.vue @@ -86,7 +86,7 @@ // External Modules import { ref, onMounted } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; const files = filesStore(), page = ref(0), diff --git a/src/views/user-space/SetupGuides.vue b/src/views/user-space/SetupGuides.vue index f9660e5..285256c 100644 --- a/src/views/user-space/SetupGuides.vue +++ b/src/views/user-space/SetupGuides.vue @@ -63,7 +63,7 @@ // External Modules import { ref, onMounted } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; const instructions = instructionsStore(), toast = toastStore(), diff --git a/src/views/user-space/Verify.vue b/src/views/user-space/Verify.vue index 8ae5ffd..642874e 100644 --- a/src/views/user-space/Verify.vue +++ b/src/views/user-space/Verify.vue @@ -31,7 +31,7 @@ // External Modules import { onMounted, ref } from 'vue'; - import { useOnline } from '@vueuse/core'; + import { useOnline } from '@/utils/ConnectivityCheck'; import { useRouter } from 'vue-router'; const user = userStore(), diff --git a/vite.config.ts b/vite.config.ts index 4cceba1..70f69a6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,3 +1,5 @@ +//TODO: Add a way to extend the configuration without having to copy the whole file. + import { defineConfig } from 'vite'; import { VitePWA } from 'vite-plugin-pwa'; import vue from '@vitejs/plugin-vue';