Try to avoid the black screen again & improve error messages (#7327)
* Fix the black screen of death * fmt * make check * Clean up * Fix up zoom to fit * Change how emulateNetworkConditions work * Do NOT use browser's offline/online mechanisms * Fix test * Improve network error messages * Signal offline when failed event comes in * Don't use logic on components that only want a loader * Remove unnecessary pause state transition --------- Co-authored-by: jacebrowning <jacebrowning@gmail.com>
This commit is contained in:
@ -1,4 +1,3 @@
|
||||
import { TEST } from '@src/env'
|
||||
import type { Models } from '@kittycad/lib'
|
||||
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from '@src/env'
|
||||
import { jsAppSettings } from '@src/lib/settings/settingsUtils'
|
||||
@ -83,6 +82,9 @@ export enum ConnectionError {
|
||||
TooManyConnections,
|
||||
Outage,
|
||||
|
||||
// Observed to happen on a local network outage.
|
||||
PeerConnectionRemoteDisconnected,
|
||||
|
||||
// An unknown error is the most severe because it has not been classified
|
||||
// or encountered before.
|
||||
Unknown,
|
||||
@ -93,22 +95,18 @@ export const CONNECTION_ERROR_TEXT: Record<ConnectionError, string> = {
|
||||
[ConnectionError.LongLoadingTime]:
|
||||
'Loading is taking longer than expected...',
|
||||
[ConnectionError.VeryLongLoadingTime]:
|
||||
'Loading seems stuck. Do you have a firewall turned on?',
|
||||
"It's possible there's a connection issue.",
|
||||
[ConnectionError.ICENegotiate]: 'ICE negotiation failed.',
|
||||
[ConnectionError.DataChannelError]: 'The data channel signaled an error.',
|
||||
[ConnectionError.WebSocketError]: 'The websocket signaled an error.',
|
||||
[ConnectionError.LocalDescriptionInvalid]:
|
||||
'The local description is invalid.',
|
||||
[ConnectionError.MissingAuthToken]:
|
||||
'Your authorization token is missing; please login again.',
|
||||
[ConnectionError.BadAuthToken]:
|
||||
'Your authorization token is invalid; please login again.',
|
||||
[ConnectionError.TooManyConnections]:
|
||||
'There are too many open engine connections associated with your account.',
|
||||
[ConnectionError.Outage]:
|
||||
'We seem to be experiencing an outage. Please visit [status.zoo.dev](https://status.zoo.dev) for updates.',
|
||||
[ConnectionError.Unknown]:
|
||||
'An unexpected error occurred. Please report this to us.',
|
||||
[ConnectionError.DataChannelError]: 'Data channel error.',
|
||||
[ConnectionError.WebSocketError]: 'Websocket error.',
|
||||
[ConnectionError.LocalDescriptionInvalid]: 'Local description invalid',
|
||||
[ConnectionError.MissingAuthToken]: 'Missing authorization token',
|
||||
[ConnectionError.BadAuthToken]: 'Bad authorization token',
|
||||
[ConnectionError.TooManyConnections]: 'Too many connections',
|
||||
[ConnectionError.Outage]: 'Outage',
|
||||
[ConnectionError.PeerConnectionRemoteDisconnected]:
|
||||
'Peer connection disconnected',
|
||||
[ConnectionError.Unknown]: 'Unknown',
|
||||
}
|
||||
|
||||
export const WEBSOCKET_READYSTATE_TEXT: Record<number, string> = {
|
||||
@ -226,6 +224,9 @@ export enum EngineConnectionEvents {
|
||||
Opened = 'opened', // (engineConnection: EngineConnection) => void
|
||||
Closed = 'closed', // (engineConnection: EngineConnection) => void
|
||||
NewTrack = 'new-track', // (track: NewTrackArgs) => void
|
||||
|
||||
// A general offline state.
|
||||
Offline = 'offline',
|
||||
}
|
||||
|
||||
function toRTCSessionDescriptionInit(
|
||||
@ -669,14 +670,28 @@ class EngineConnection extends EventTarget {
|
||||
},
|
||||
},
|
||||
}
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EngineConnectionEvents.Offline, {})
|
||||
)
|
||||
this.disconnectAll()
|
||||
break
|
||||
|
||||
// The remote end broke up with us! :(
|
||||
case 'disconnected':
|
||||
this.state = {
|
||||
type: EngineConnectionStateType.Disconnecting,
|
||||
value: {
|
||||
type: DisconnectingType.Error,
|
||||
value: {
|
||||
error: ConnectionError.PeerConnectionRemoteDisconnected,
|
||||
context: event,
|
||||
},
|
||||
},
|
||||
}
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EngineConnectionEvents.RestartRequest, {})
|
||||
new CustomEvent(EngineConnectionEvents.Offline, {})
|
||||
)
|
||||
this.disconnectAll()
|
||||
break
|
||||
case 'closed':
|
||||
this.pc?.removeEventListener('icecandidate', this.onIceCandidate)
|
||||
@ -847,7 +862,6 @@ class EngineConnection extends EventTarget {
|
||||
'message',
|
||||
this.onDataChannelMessage
|
||||
)
|
||||
this.disconnectAll()
|
||||
}
|
||||
|
||||
this.unreliableDataChannel?.addEventListener(
|
||||
@ -866,7 +880,6 @@ class EngineConnection extends EventTarget {
|
||||
},
|
||||
},
|
||||
}
|
||||
this.disconnectAll()
|
||||
}
|
||||
this.unreliableDataChannel?.addEventListener(
|
||||
'error',
|
||||
@ -956,6 +969,9 @@ class EngineConnection extends EventTarget {
|
||||
this.onNetworkStatusReady
|
||||
)
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EngineConnectionEvents.Offline, {})
|
||||
)
|
||||
this.disconnectAll()
|
||||
}
|
||||
this.websocket.addEventListener('close', this.onWebSocketClose)
|
||||
@ -974,8 +990,6 @@ class EngineConnection extends EventTarget {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
this.disconnectAll()
|
||||
}
|
||||
this.websocket.addEventListener('error', this.onWebSocketError)
|
||||
|
||||
@ -1331,6 +1345,9 @@ export enum EngineCommandManagerEvents {
|
||||
|
||||
// the whole scene is ready (settings loaded)
|
||||
SceneReady = 'scene-ready',
|
||||
|
||||
// we're offline
|
||||
Offline = 'offline',
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1380,6 +1397,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
* This is compared to the {@link outSequence} number to determine if we should ignore
|
||||
* any out-of-order late responses in the unreliable channel.
|
||||
*/
|
||||
keepForcefulOffline = false
|
||||
inSequence = 1
|
||||
engineConnection?: EngineConnection
|
||||
commandLogs: CommandLog[] = []
|
||||
@ -1453,13 +1471,8 @@ export class EngineCommandManager extends EventTarget {
|
||||
)
|
||||
}
|
||||
|
||||
private onOffline = () => {
|
||||
console.log('Browser reported network is offline')
|
||||
if (TEST) {
|
||||
console.warn('DURING TESTS ENGINECONNECTION.ONOFFLINE WILL DO NOTHING.')
|
||||
return
|
||||
}
|
||||
this.onEngineConnectionRestartRequest()
|
||||
private onEngineOffline = () => {
|
||||
this.dispatchEvent(new CustomEvent(EngineCommandManagerEvents.Offline, {}))
|
||||
}
|
||||
|
||||
idleMode: boolean = false
|
||||
@ -1494,6 +1507,11 @@ export class EngineCommandManager extends EventTarget {
|
||||
if (settings) {
|
||||
this.settings = settings
|
||||
}
|
||||
|
||||
if (this.keepForcefulOffline) {
|
||||
return
|
||||
}
|
||||
|
||||
if (width === 0 || height === 0) {
|
||||
return
|
||||
}
|
||||
@ -1509,8 +1527,6 @@ export class EngineCommandManager extends EventTarget {
|
||||
return
|
||||
}
|
||||
|
||||
window.addEventListener('offline', this.onOffline)
|
||||
|
||||
let additionalSettings = this.settings.enableSSAO ? '&post_effect=ssao' : ''
|
||||
additionalSettings +=
|
||||
'&show_grid=' + (this.settings.showScaleGrid ? 'true' : 'false')
|
||||
@ -1537,6 +1553,11 @@ export class EngineCommandManager extends EventTarget {
|
||||
this.onEngineConnectionRestartRequest as EventListener
|
||||
)
|
||||
|
||||
this.engineConnection.addEventListener(
|
||||
EngineConnectionEvents.Offline,
|
||||
this.onEngineOffline as EventListener
|
||||
)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
this.onEngineConnectionOpened = async () => {
|
||||
console.log('onEngineConnectionOpened')
|
||||
@ -1548,13 +1569,8 @@ export class EngineCommandManager extends EventTarget {
|
||||
this.codeManager?.currentFilePath || undefined
|
||||
)
|
||||
} catch (e) {
|
||||
// If this happens shit's actually gone south aka the websocket closed.
|
||||
// Let's restart.
|
||||
console.warn("shit's gone south")
|
||||
console.warn(e)
|
||||
this.engineConnection?.dispatchEvent(
|
||||
new CustomEvent(EngineConnectionEvents.RestartRequest, {})
|
||||
)
|
||||
// If this happens, the websocket may have closed and we need to restart
|
||||
console.warn('unknown error:', e)
|
||||
return
|
||||
}
|
||||
|
||||
@ -1597,23 +1613,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
console.log('camControlsCameraChange')
|
||||
this._camControlsCameraChange()
|
||||
|
||||
// We should eventually only have 1 restoral call.
|
||||
if (this.idleMode) {
|
||||
await this.sceneInfra?.camControls.restoreRemoteCameraStateAndTriggerSync()
|
||||
} else {
|
||||
// NOTE: This code is old. It uses the old hack to restore camera.
|
||||
console.log('call default_camera_get_settings')
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
await this.sendSceneCommand({
|
||||
// CameraControls subscribes to default_camera_get_settings response events
|
||||
// firing this at connection ensure the camera's are synced initially
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_get_settings',
|
||||
},
|
||||
})
|
||||
}
|
||||
await this.sceneInfra?.camControls.restoreRemoteCameraStateAndTriggerSync()
|
||||
|
||||
setIsStreamReady(true)
|
||||
|
||||
@ -1877,8 +1877,6 @@ export class EngineCommandManager extends EventTarget {
|
||||
tearDown(opts?: { idleMode: boolean }) {
|
||||
this.idleMode = opts?.idleMode ?? false
|
||||
|
||||
window.removeEventListener('offline', this.onOffline)
|
||||
|
||||
if (this.engineConnection) {
|
||||
for (const [cmdId, pending] of Object.entries(this.pendingCommands)) {
|
||||
pending.reject([
|
||||
@ -1928,7 +1926,26 @@ export class EngineCommandManager extends EventTarget {
|
||||
this.engineCommandManager.engineConnection = null
|
||||
}
|
||||
this.engineConnection = undefined
|
||||
|
||||
// It is possible all connections never even started, but we still want
|
||||
// to signal to the whole application we are "offline".
|
||||
this.dispatchEvent(new CustomEvent(EngineCommandManagerEvents.Offline, {}))
|
||||
}
|
||||
|
||||
offline() {
|
||||
this.keepForcefulOffline = true
|
||||
this.tearDown()
|
||||
console.log('offline')
|
||||
}
|
||||
|
||||
online() {
|
||||
this.keepForcefulOffline = false
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EngineCommandManagerEvents.EngineRestartRequest, {})
|
||||
)
|
||||
console.log('online')
|
||||
}
|
||||
|
||||
async startNewSession() {
|
||||
this.responseMap = {}
|
||||
}
|
||||
|
Reference in New Issue
Block a user