Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve GI_VERSION update logic #2568

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 31 additions & 4 deletions frontend/controller/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import type { JSONObject } from '~/shared/types.js'

import sbp from '@sbp/sbp'
import { HOURS_MILLIS } from '~/frontend/model/contracts/shared/time.js'
import { NOTIFICATION_TYPE } from '~/shared/pubsub.js'
import { handleFetchResult } from './utils/misc.js'
import { PUBSUB_INSTANCE } from './instance-keys.js'
Expand All @@ -14,14 +15,40 @@ const languageFileMap = new Map([
])

sbp('okTurtles.events/on', NOTIFICATION_TYPE.VERSION_INFO, (versionInfo) => {
if (versionInfo.GI_VERSION === process.env.GI_VERSION) {
// No refresh necessary, we're already at the latest version
sessionStorage.removeItem(NOTIFICATION_TYPE.VERSION_INFO)
return
}

console.info('New Group Income version available:', versionInfo)
// Prevent the client from trying to reconnect when the page starts unloading.
sbp('okTurtles.data/get', PUBSUB_INSTANCE).destroy()
// TODO: allow the user to manually reload the page later.
try {
// Store the current VERSION_INFO in session storage to prevent infinite
// reload loops
const existingSerialized = sessionStorage.getItem(NOTIFICATION_TYPE.VERSION_INFO)
if (existingSerialized) {
const existingVersionInfo = JSON.parse(existingSerialized)
if (
Array.isArray(existingVersionInfo) &&
!(Date.now() - existingVersionInfo[0] >= 2.5 * HOURS_MILLIS) &&
versionInfo.GI_VERSION === existingVersionInfo[1].GI_VERSION
) {
console.warn('[NOTIFICATION_TYPE.VERSION_INFO] A different Group Income version is available, but reloading has failed to address it', { existingVersionInfo, versionInfo })
taoeffect marked this conversation as resolved.
Show resolved Hide resolved
return
}
}

sessionStorage.setItem(NOTIFICATION_TYPE.VERSION_INFO, JSON.stringify([Date.now(), versionInfo]))
} catch (e) {
console.error('[NOTIFICATION_TYPE.VERSION_INFO] Error in handler', e)
}
// Prevent the client from trying to reconnect when the page starts unloading.
sbp('okTurtles.data/get', PUBSUB_INSTANCE)?.destroy()
window.location.reload()
})

sbp('sbp/selectors/register', {
export default (sbp('sbp/selectors/register', {
/**
* Fetches a JSON object containing translation strings for a given language.
*
Expand All @@ -39,4 +66,4 @@ sbp('sbp/selectors/register', {
.then(handleFetchResult('json'))
}
}
})
}): string[])
79 changes: 40 additions & 39 deletions frontend/controller/service-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,44 @@ sbp('sbp/selectors/register', {
await swRegistration.update()
setInterval(() => sbp('service-worker/update'), HOURS_MILLIS)

navigator.serviceWorker.addEventListener('message', event => {
const data = event.data
const silentEmit = sbp('sbp/selectors/fn', 'okTurtles.events/emit')

if (typeof data === 'object' && data.type) {
switch (data.type) {
case 'pong':
break
case 'event': {
sbp('okTurtles.events/emit', event.data.subtype, ...deserializer(event.data.data))
break
}
case 'navigate': {
if (data.groupID) {
sbp('state/vuex/commit', 'setCurrentGroupId', { contractID: data.groupID })
}
sbp('controller/router').push({ path: data.path }).catch(console.warn)
break
}
// `sbp` invocations from the SW to the app. Used by the
// `notificationclick` handler for notifications that have an
// `sbpInvocation` instead of a `linkTo` property.
case 'sbp': {
sbp(...deserializer(event.data.data))
break
}
case CAPTURED_LOGS: {
// Emit silently to avoid flooding logs with event emitted entries
silentEmit(CAPTURED_LOGS, ...deserializer(event.data.data))
break
}
default:
console.error('[sw] Received unknown message type from the service worker:', data)
break
}
}
})

// Send a 'ready' message to the SW and wait back for a response
// This way we ensure that Chelonia has been set up
await new Promise((resolve, reject) => {
Expand All @@ -71,7 +109,8 @@ sbp('sbp/selectors/register', {
navigator.serviceWorker.ready.then((worker) => {
worker.active.postMessage({
type: 'ready',
port: messageChannel.port2
port: messageChannel.port2,
GI_VERSION: process.env.GI_VERSION
}, [messageChannel.port2])
}).catch((e) => {
reject(e)
Expand Down Expand Up @@ -110,44 +149,6 @@ sbp('sbp/selectors/register', {
// there are open tabs, which makes it faster and smoother to interact
// with contracts than if the service worker had to be restarted.
setInterval(() => navigator.serviceWorker.controller?.postMessage({ type: 'ping' }), 5000)

navigator.serviceWorker.addEventListener('message', event => {
const data = event.data
const silentEmit = sbp('sbp/selectors/fn', 'okTurtles.events/emit')

if (typeof data === 'object' && data.type) {
switch (data.type) {
case 'pong':
break
case 'event': {
sbp('okTurtles.events/emit', event.data.subtype, ...deserializer(event.data.data))
break
}
case 'navigate': {
if (data.groupID) {
sbp('state/vuex/commit', 'setCurrentGroupId', { contractID: data.groupID })
}
sbp('controller/router').push({ path: data.path }).catch(console.warn)
break
}
// `sbp` invocations from the SW to the app. Used by the
// `notificationclick` handler for notifications that have an
// `sbpInvocation` instead of a `linkTo` property.
case 'sbp': {
sbp(...deserializer(event.data.data))
break
}
case CAPTURED_LOGS: {
// Emit silently to avoid flooding logs with event emitted entries
silentEmit(CAPTURED_LOGS, ...deserializer(event.data.data))
break
}
default:
console.error('[sw] Received unknown message type from the service worker:', data)
break
}
}
})
} catch (e) {
console.error('error setting up service worker:', e)
throw e
Expand Down
36 changes: 30 additions & 6 deletions frontend/controller/serviceworkers/sw-primary.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import { KV_KEYS } from '~/frontend/utils/constants.js'
import { CHELONIA_STATE_MODIFIED, LOGIN, LOGIN_ERROR, LOGOUT } from '~/frontend/utils/events.js'
import { GIMessage } from '~/shared/domains/chelonia/GIMessage.js'
import { Secret } from '~/shared/domains/chelonia/Secret.js'
import { CHELONIA_RESET, CONTRACTS_MODIFIED, CONTRACT_IS_SYNCING, EVENT_HANDLED } from '~/shared/domains/chelonia/events.js'
import { CHELONIA_RESET, CONTRACTS_MODIFIED, CONTRACT_IS_SYNCING, CONTRACT_REGISTERED, EVENT_HANDLED } from '~/shared/domains/chelonia/events.js'
import { NOTIFICATION_TYPE } from '~/shared/pubsub.js'
import { deserializer, serializer } from '~/shared/serdes/index.js'
import {
ACCEPTED_GROUP, CAPTURED_LOGS, CHATROOM_USER_STOP_TYPING,
Expand Down Expand Up @@ -103,11 +104,12 @@ sbp('okTurtles.events/on', CHELONIA_RESET, setupRootState)

// These are all of the events that will be forwarded to all open tabs and windows
;[
CHELONIA_RESET, CONTRACTS_MODIFIED, CONTRACT_IS_SYNCING, EVENT_HANDLED, LOGIN,
LOGIN_ERROR, LOGOUT, ACCEPTED_GROUP, CHATROOM_USER_STOP_TYPING,
CHATROOM_USER_TYPING, DELETED_CHATROOM, LEFT_CHATROOM, LEFT_GROUP,
JOINED_CHATROOM, JOINED_GROUP, KV_EVENT, MESSAGE_RECEIVE, MESSAGE_SEND,
NAMESPACE_REGISTRATION, NEW_LAST_LOGGED_IN,
CHELONIA_RESET, CONTRACTS_MODIFIED, CONTRACT_IS_SYNCING, CONTRACT_REGISTERED,
EVENT_HANDLED, LOGIN, LOGIN_ERROR, LOGOUT, ACCEPTED_GROUP,
CHATROOM_USER_STOP_TYPING, CHATROOM_USER_TYPING, DELETED_CHATROOM,
LEFT_CHATROOM, LEFT_GROUP, JOINED_CHATROOM, JOINED_GROUP, KV_EVENT,
NOTIFICATION_TYPE.VERSION_INFO,
MESSAGE_RECEIVE, MESSAGE_SEND, NAMESPACE_REGISTRATION, NEW_LAST_LOGGED_IN,
NEW_PREFERENCES, NEW_UNREAD_MESSAGES, NOTIFICATION_EMITTED,
NOTIFICATION_REMOVED, NOTIFICATION_STATUS_LOADED, OFFLINE, ONLINE,
PROPOSAL_ARCHIVED, SERIOUS_ERROR, SWITCH_GROUP
Expand Down Expand Up @@ -265,6 +267,11 @@ sbp('okTurtles.events/on', CHELONIA_RESET, () => {
sbp('gi.periodicNotifications/init')
})

let currentVersionInfo
sbp('okTurtles.events/on', NOTIFICATION_TYPE.VERSION_INFO, (versionInfo) => {
currentVersionInfo = versionInfo
})

taoeffect marked this conversation as resolved.
Show resolved Hide resolved
sbp('okTurtles.data/set', 'API_URL', self.location.origin)
setupRootState()
const setupPromise = setupChelonia()
Expand Down Expand Up @@ -359,6 +366,23 @@ self.addEventListener('message', function (event) {
}).finally(() => {
port.close()
})

// If the window is outdated (different GI_VERSION), trigger an event
// of type 'NOTIFICATION_TYPE.VERSION_INFO'.
// This handles new SW clients that have an outdated
// `process.env.GI_VERSION` (for example, by having loaded a cached
// version of `main.js`).
if (
currentVersionInfo &&
event.source &&
event.data.GI_VERSION !== currentVersionInfo.GI_VERSION
) {
event.source.postMessage({
type: 'event',
subtype: NOTIFICATION_TYPE.VERSION_INFO,
data: [currentVersionInfo]
})
}
break
}
default:
Expand Down