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

Wait for Chelonia, more error logs #2471

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
29 changes: 29 additions & 0 deletions frontend/controller/service-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,34 @@ sbp('sbp/selectors/register', {
await swRegistration.update()
setInterval(() => sbp('service-worker/update'), HOURS_MILLIS)

// Send a 'ready' message to the SW and wait back for a response
taoeffect marked this conversation as resolved.
Show resolved Hide resolved
// This way we ensure that Chelonia has been set up
await new Promise((resolve, reject) => {
const messageChannel = new MessageChannel()
messageChannel.port1.onmessage = (event) => {
if (event.data.type === 'ready') {
resolve()
} else {
reject(event.data.error)
}
messageChannel.port1.close()
}
messageChannel.port1.onmessageerror = () => {
reject(new Error('Message error'))
messageChannel.port1.close()
}

navigator.serviceWorker.ready.then((worker) => {
worker.active.postMessage({
type: 'ready',
port: messageChannel.port2
}, [messageChannel.port2])
}).catch((e) => {
reject(e)
messageChannel.port1.close()
})
})

Comment on lines +54 to +81
Copy link
Member

@taoeffect taoeffect Dec 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like boilerplate we could abstract away in a function somewhere.

Can you create a function so that chunk is shortened like so?

await postWorkerMessage(worker, { type: 'ready' })

And if there are any other parts of the app that also do something like this, they can reuse this function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could make it into another function, but this particular message is unique in how it's sent, that it expects a response and that it expects a response (sbp also expects a response, but is handled by a more general-purpose function)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's also not entirely general purpose because the handler for onmessage is custom

// Keep the service worker alive while the window is open
// The default idle timeout on Chrome and Firefox is 30 seconds. We send
// a ping message every 5 seconds to ensure that the worker remains
Expand Down Expand Up @@ -92,6 +120,7 @@ sbp('sbp/selectors/register', {
})
} catch (e) {
console.error('error setting up service worker:', e)
throw e
}
},
// We call this when the notification permission changes, to create a push
Expand Down
4 changes: 2 additions & 2 deletions frontend/controller/serviceworkers/push.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default (sbp('sbp/selectors/register', {
const pubsub = sbp('okTurtles.data/get', PUBSUB_INSTANCE)
if (!pubsub) reject(new Error('Missing pubsub instance'))

const readyState = pubsub.socket.readyState
const readyState = pubsub.socket?.readyState
if (readyState !== WebSocket.OPEN) {
reject(new Error('WebSocket connection is not open'))
}
Expand Down Expand Up @@ -71,7 +71,7 @@ export default (sbp('sbp/selectors/register', {
const pubsub = sbp('okTurtles.data/get', PUBSUB_INSTANCE)
if (!pubsub) throw new Error('Missing pubsub instance')

const readyState = pubsub.socket.readyState
const readyState = pubsub.socket?.readyState
if (readyState !== WebSocket.OPEN) {
throw new Error('WebSocket connection is not open')
}
Expand Down
20 changes: 20 additions & 0 deletions frontend/controller/serviceworkers/sw-primary.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,26 @@ self.addEventListener('message', function (event) {
case 'event':
sbp('okTurtles.events/emit', event.data.subtype, ...deserializer(event.data.data))
break
case 'ready': {
// The 'ready' message is sent by a client (i.e., a tab or window) to
// ensure that Chelonia has been setup
const port = event.data.port
Promise.race([
setupChelonia(),
new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Timed out setting up Chelonia'))
}, 30e3)
})
]).then(() => {
port.postMessage({ type: 'ready' })
}, (e) => {
port.postMessage({ type: 'error', error: e })
}).finally(() => {
port.close()
})
break
}
default:
console.error('[sw] unknown message type:', event.data)
break
Expand Down
2 changes: 1 addition & 1 deletion frontend/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ async function startApp () {
new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Timed out setting up service worker'))
}, 16e3)
}, 8e3)
})]
).catch(e => {
console.error('[main] Error setting up service worker', e)
Expand Down
10 changes: 7 additions & 3 deletions frontend/setupChelonia.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,12 @@ const setupChelonia = async (): Promise<*> => {
if (cheloniaState.loggedIn?.identityContractID !== identityContractID) return
// it is important we first login before syncing any contracts here since that will load the
// state and the contract sideEffects will sometimes need that state, e.g. loggedIn.identityContractID
await sbp('chelonia/contract/sync', identityContractID)
const contractIDs = groupContractsByType(cheloniaState.contracts)
await syncContractsInOrder(contractIDs)
await sbp('chelonia/contract/sync', identityContractID).then(async () => {
const contractIDs = groupContractsByType(cheloniaState.contracts)
await syncContractsInOrder(contractIDs)
}).catch(e => {
console.error('[setupChelonia] Error syncing identity contract and groups', e)
})
})
}

Expand All @@ -310,6 +313,7 @@ export default ((() => {
return () => {
if (!promise) {
promise = setupChelonia().catch((e) => {
console.error('[setupChelonia] Error during chelonia setup', e)
taoeffect marked this conversation as resolved.
Show resolved Hide resolved
promise = undefined // Reset on error
throw e // Re-throw the error
})
Expand Down