@glassly/engine 0.1.0-dev.1 → 0.1.0-dev.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -19
- package/build/bluetooth-sdk/debug.d.ts +2 -0
- package/build/bluetooth-sdk/debug.d.ts.map +1 -0
- package/build/bluetooth-sdk/debug.js +2 -0
- package/build/bluetooth-sdk/debug.js.map +1 -0
- package/build/bluetooth-sdk/index.d.ts +3 -0
- package/build/bluetooth-sdk/index.d.ts.map +1 -0
- package/build/bluetooth-sdk/index.js +3 -0
- package/build/bluetooth-sdk/index.js.map +1 -0
- package/build/bluetooth-sdk/ota-transport.d.ts +2 -0
- package/build/bluetooth-sdk/ota-transport.d.ts.map +1 -0
- package/build/bluetooth-sdk/ota-transport.js +2 -0
- package/build/bluetooth-sdk/ota-transport.js.map +1 -0
- package/build/bluetooth-sdk/photo-receiver.d.ts +3 -0
- package/build/bluetooth-sdk/photo-receiver.d.ts.map +1 -0
- package/build/bluetooth-sdk/photo-receiver.js +3 -0
- package/build/bluetooth-sdk/photo-receiver.js.map +1 -0
- package/build/bluetooth-sdk/react.d.ts +2 -0
- package/build/bluetooth-sdk/react.d.ts.map +1 -0
- package/build/bluetooth-sdk/react.js +2 -0
- package/build/bluetooth-sdk/react.js.map +1 -0
- package/build/bluetooth-sdk/types.d.ts +2 -0
- package/build/bluetooth-sdk/types.d.ts.map +1 -0
- package/build/bluetooth-sdk/types.js +2 -0
- package/build/bluetooth-sdk/types.js.map +1 -0
- package/build/engine.d.ts +23 -16
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +9 -3
- package/build/engine.js.map +1 -1
- package/build/facades/dev.d.ts +1 -1
- package/build/facades/display.d.ts +1 -1
- package/build/facades/display.d.ts.map +1 -1
- package/build/facades/display.js +1 -1
- package/build/facades/display.js.map +1 -1
- package/build/facades/glasses.d.ts +5 -13
- package/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +5 -3
- package/build/facades/glasses.js.map +1 -1
- package/build/facades/ota.d.ts +19 -21
- package/build/facades/ota.d.ts.map +1 -1
- package/build/facades/ota.js +23 -9
- package/build/facades/ota.js.map +1 -1
- package/build/facades/pairing.d.ts +11 -5
- package/build/facades/pairing.d.ts.map +1 -1
- package/build/facades/pairing.js +93 -17
- package/build/facades/pairing.js.map +1 -1
- package/build/facades/speakers.d.ts +1 -1
- package/build/facades/speakers.d.ts.map +1 -1
- package/build/facades/speakers.js.map +1 -1
- package/build/facades/speech.d.ts +7 -6
- package/build/facades/speech.d.ts.map +1 -1
- package/build/facades/speech.js +1 -1
- package/build/facades/speech.js.map +1 -1
- package/build/generated/releaseMetadata.d.ts +12 -0
- package/build/generated/releaseMetadata.d.ts.map +1 -0
- package/build/generated/releaseMetadata.js +10 -0
- package/build/generated/releaseMetadata.js.map +1 -0
- package/build/index.d.ts +7 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +5 -3
- package/build/index.js.map +1 -1
- package/build/react/MentraLiveOtaFlow.d.ts +39 -0
- package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
- package/build/react/MentraLiveOtaFlow.js +200 -0
- package/build/react/MentraLiveOtaFlow.js.map +1 -0
- package/build/react/index.d.ts +3 -0
- package/build/react/index.d.ts.map +1 -0
- package/build/react/index.js +3 -0
- package/build/react/index.js.map +1 -0
- package/build/react/useEngineSnapshot.d.ts +2 -0
- package/build/react/useEngineSnapshot.d.ts.map +1 -0
- package/build/react/useEngineSnapshot.js +14 -0
- package/build/react/useEngineSnapshot.js.map +1 -0
- package/build/react/useMentraLiveOta.d.ts +80 -0
- package/build/react/useMentraLiveOta.d.ts.map +1 -0
- package/build/react/useMentraLiveOta.js +550 -0
- package/build/react/useMentraLiveOta.js.map +1 -0
- package/build/services/AppRegistry.d.ts +8 -0
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js +15 -0
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/BlobStore.d.ts +2 -0
- package/build/services/BlobStore.d.ts.map +1 -1
- package/build/services/BlobStore.js +63 -10
- package/build/services/BlobStore.js.map +1 -1
- package/build/services/CustomDashboardModel.d.ts +48 -0
- package/build/services/CustomDashboardModel.d.ts.map +1 -0
- package/build/services/CustomDashboardModel.js +73 -0
- package/build/services/CustomDashboardModel.js.map +1 -0
- package/build/services/CustomDashboardService.d.ts +61 -0
- package/build/services/CustomDashboardService.d.ts.map +1 -0
- package/build/services/CustomDashboardService.js +552 -0
- package/build/services/CustomDashboardService.js.map +1 -0
- package/build/services/DeviceEventRouter.d.ts.map +1 -1
- package/build/services/DeviceEventRouter.js +44 -3
- package/build/services/DeviceEventRouter.js.map +1 -1
- package/build/services/GestureDotsService.d.ts +35 -0
- package/build/services/GestureDotsService.d.ts.map +1 -0
- package/build/services/GestureDotsService.js +149 -0
- package/build/services/GestureDotsService.js.map +1 -0
- package/build/services/GlassesStatusProjection.d.ts +15 -1
- package/build/services/GlassesStatusProjection.d.ts.map +1 -1
- package/build/services/GlassesStatusProjection.js +18 -11
- package/build/services/GlassesStatusProjection.js.map +1 -1
- package/build/services/HotspotOtaTransport.d.ts.map +1 -1
- package/build/services/HotspotOtaTransport.js +8 -7
- package/build/services/HotspotOtaTransport.js.map +1 -1
- package/build/services/LocalDisplayManager.d.ts +43 -1
- package/build/services/LocalDisplayManager.d.ts.map +1 -1
- package/build/services/LocalDisplayManager.js +128 -2
- package/build/services/LocalDisplayManager.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +13 -0
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +32 -0
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/MiniappExitGesture.d.ts +33 -0
- package/build/services/MiniappExitGesture.d.ts.map +1 -0
- package/build/services/MiniappExitGesture.js +54 -0
- package/build/services/MiniappExitGesture.js.map +1 -0
- package/build/services/OtaAutoChain.d.ts +41 -0
- package/build/services/OtaAutoChain.d.ts.map +1 -0
- package/build/services/OtaAutoChain.js +91 -0
- package/build/services/OtaAutoChain.js.map +1 -0
- package/build/services/OtaErrorMapping.d.ts +18 -0
- package/build/services/OtaErrorMapping.d.ts.map +1 -0
- package/build/services/OtaErrorMapping.js +63 -0
- package/build/services/OtaErrorMapping.js.map +1 -0
- package/build/services/OtaInstallCoordinator.d.ts +24 -3
- package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
- package/build/services/OtaInstallCoordinator.js +17 -12
- package/build/services/OtaInstallCoordinator.js.map +1 -1
- package/build/services/OtaService.d.ts.map +1 -1
- package/build/services/OtaService.js +1 -1
- package/build/services/OtaService.js.map +1 -1
- package/build/services/OtaUpdateCheckService.d.ts +4 -1
- package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
- package/build/services/OtaUpdateCheckService.js +28 -16
- package/build/services/OtaUpdateCheckService.js.map +1 -1
- package/build/services/asg/localNetworkTransport.d.ts.map +1 -1
- package/build/services/asg/localNetworkTransport.js +13 -12
- package/build/services/asg/localNetworkTransport.js.map +1 -1
- package/build/services/gestureClaims.d.ts +18 -0
- package/build/services/gestureClaims.d.ts.map +1 -0
- package/build/services/gestureClaims.js +24 -0
- package/build/services/gestureClaims.js.map +1 -0
- package/build/services/gestureDotGlyphs.d.ts +8 -0
- package/build/services/gestureDotGlyphs.d.ts.map +1 -0
- package/build/services/gestureDotGlyphs.js +12 -0
- package/build/services/gestureDotGlyphs.js.map +1 -0
- package/build/services/glassesClockSync.d.ts.map +1 -1
- package/build/services/glassesClockSync.js +5 -1
- package/build/services/glassesClockSync.js.map +1 -1
- package/build/services/otaDisplayState.d.ts +1 -1
- package/build/services/otaDisplayState.d.ts.map +1 -1
- package/build/services/otaDisplayState.js.map +1 -1
- package/build/services/otaInstallPolicy.d.ts +1 -1
- package/build/services/otaInstallPolicy.d.ts.map +1 -1
- package/build/services/otaInstallPolicy.js.map +1 -1
- package/build/services/otaLegacyMapping.d.ts +1 -1
- package/build/services/otaLegacyMapping.d.ts.map +1 -1
- package/build/services/otaLegacyMapping.js.map +1 -1
- package/build/services/otaManifestPolicy.d.ts +14 -0
- package/build/services/otaManifestPolicy.d.ts.map +1 -0
- package/build/services/otaManifestPolicy.js +20 -0
- package/build/services/otaManifestPolicy.js.map +1 -0
- package/build/services/otaManifestUrl.d.ts +2 -1
- package/build/services/otaManifestUrl.d.ts.map +1 -1
- package/build/services/otaManifestUrl.js +24 -32
- package/build/services/otaManifestUrl.js.map +1 -1
- package/build/services/otaReleaseVersion.d.ts +10 -0
- package/build/services/otaReleaseVersion.d.ts.map +1 -0
- package/build/services/otaReleaseVersion.js +14 -0
- package/build/services/otaReleaseVersion.js.map +1 -0
- package/build/stores/appVisibility.d.ts +3 -0
- package/build/stores/appVisibility.d.ts.map +1 -0
- package/build/stores/appVisibility.js +3 -0
- package/build/stores/appVisibility.js.map +1 -0
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +7 -1
- package/build/stores/apps.js.map +1 -1
- package/build/stores/core.d.ts +3 -3
- package/build/stores/core.d.ts.map +1 -1
- package/build/stores/core.js.map +1 -1
- package/build/stores/glasses.d.ts +10 -4
- package/build/stores/glasses.d.ts.map +1 -1
- package/build/stores/glasses.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +87 -3
- package/build/stores/settings.js.map +1 -1
- package/build/types/applet.d.ts +2 -0
- package/build/types/applet.d.ts.map +1 -1
- package/build/types/applet.js +0 -3
- package/build/types/applet.js.map +1 -1
- package/build/types/enums.d.ts +1 -5
- package/build/types/enums.d.ts.map +1 -1
- package/build/types/enums.js +1 -8
- package/build/types/enums.js.map +1 -1
- package/build/types/hardware.d.ts +1 -1
- package/build/types/hardware.d.ts.map +1 -1
- package/build/types/hardware.js.map +1 -1
- package/build/types/index.d.ts +2 -6
- package/build/types/index.d.ts.map +1 -1
- package/build/types/index.js +2 -6
- package/build/types/index.js.map +1 -1
- package/build/utils/display/scene/process.d.ts.map +1 -1
- package/build/utils/display/scene/process.js +4 -3
- package/build/utils/display/scene/process.js.map +1 -1
- package/build/utils/display/scene/types.d.ts +6 -2
- package/build/utils/display/scene/types.d.ts.map +1 -1
- package/build/utils/display/scene/types.js.map +1 -1
- package/package.json +65 -33
- package/src/bluetooth-sdk/debug.ts +1 -0
- package/src/bluetooth-sdk/index.ts +2 -0
- package/src/bluetooth-sdk/ota-transport.ts +1 -0
- package/src/bluetooth-sdk/photo-receiver.ts +2 -0
- package/src/bluetooth-sdk/react.ts +1 -0
- package/src/bluetooth-sdk/types.ts +1 -0
- package/src/engine.ts +9 -3
- package/src/facades/display.ts +1 -1
- package/src/facades/glasses.ts +6 -4
- package/src/facades/ota.ts +33 -10
- package/src/facades/pairing.ts +103 -23
- package/src/facades/speakers.ts +1 -1
- package/src/facades/speech.ts +21 -7
- package/src/generated/releaseMetadata.ts +20 -0
- package/src/index.ts +29 -4
- package/src/react/MentraLiveOtaFlow.tsx +569 -0
- package/src/react/index.ts +21 -0
- package/src/react/useEngineSnapshot.ts +16 -0
- package/src/react/useMentraLiveOta.ts +707 -0
- package/src/services/AppRegistry.ts +16 -0
- package/src/services/BlobStore.ts +57 -10
- package/src/services/CustomDashboardModel.ts +83 -0
- package/src/services/CustomDashboardService.ts +575 -0
- package/src/services/DeviceEventRouter.ts +47 -5
- package/src/services/GestureDotsService.ts +147 -0
- package/src/services/GlassesStatusProjection.ts +20 -11
- package/src/services/HotspotOtaTransport.ts +8 -7
- package/src/services/LocalDisplayManager.ts +130 -2
- package/src/services/LocalMiniappRuntime.ts +37 -0
- package/src/services/MiniappExitGesture.ts +76 -0
- package/src/services/OtaAutoChain.ts +130 -0
- package/src/services/OtaErrorMapping.ts +80 -0
- package/src/services/OtaInstallCoordinator.ts +19 -13
- package/src/services/OtaService.ts +1 -2
- package/src/services/OtaUpdateCheckService.ts +33 -19
- package/src/services/asg/localNetworkTransport.ts +20 -19
- package/src/services/gestureClaims.ts +28 -0
- package/src/services/gestureDotGlyphs.ts +14 -0
- package/src/services/glassesClockSync.ts +5 -1
- package/src/services/otaDisplayState.ts +1 -1
- package/src/services/otaInstallPolicy.ts +1 -1
- package/src/services/otaLegacyMapping.ts +1 -1
- package/src/services/otaManifestPolicy.ts +43 -0
- package/src/services/otaManifestUrl.ts +27 -36
- package/src/services/otaReleaseVersion.ts +22 -0
- package/src/stores/appVisibility.ts +3 -0
- package/src/stores/apps.ts +7 -1
- package/src/stores/core.ts +4 -4
- package/src/stores/glasses.ts +13 -6
- package/src/stores/settings.ts +87 -3
- package/src/types/applet.ts +2 -0
- package/src/types/enums.ts +1 -9
- package/src/types/hardware.ts +1 -1
- package/src/types/index.ts +2 -6
- package/src/utils/display/scene/process.ts +4 -3
- package/src/utils/display/scene/types.ts +0 -0
- package/build/devtools.d.ts +0 -11
- package/build/devtools.d.ts.map +0 -1
- package/build/devtools.js +0 -13
- package/build/devtools.js.map +0 -1
- package/build/internal.d.ts +0 -67
- package/build/internal.d.ts.map +0 -1
- package/build/internal.js +0 -90
- package/build/internal.js.map +0 -1
- package/src/devtools.ts +0 -13
- package/src/internal.ts +0 -155
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
/* eslint-disable react-native/no-raw-text -- BodyText and PercentText are local Text wrappers. */
|
|
2
|
+
import React, {useMemo} from "react"
|
|
3
|
+
import {
|
|
4
|
+
ActivityIndicator,
|
|
5
|
+
Pressable,
|
|
6
|
+
ScrollView,
|
|
7
|
+
StyleSheet,
|
|
8
|
+
Text,
|
|
9
|
+
View,
|
|
10
|
+
type StyleProp,
|
|
11
|
+
type ViewStyle,
|
|
12
|
+
} from "react-native"
|
|
13
|
+
import {SafeAreaView} from "react-native-safe-area-context"
|
|
14
|
+
import Svg, {Path, Rect} from "react-native-svg"
|
|
15
|
+
|
|
16
|
+
import {useMentraLiveOta, type MentraLiveOtaController, type MentraLiveOtaFlowPage} from "./useMentraLiveOta"
|
|
17
|
+
|
|
18
|
+
export type {MentraLiveOtaFlowPage} from "./useMentraLiveOta"
|
|
19
|
+
|
|
20
|
+
export type MentraLiveOtaFlowTheme = {
|
|
21
|
+
background: string
|
|
22
|
+
border: string
|
|
23
|
+
error: string
|
|
24
|
+
foreground: string
|
|
25
|
+
primary: string
|
|
26
|
+
primaryText: string
|
|
27
|
+
textDim: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type MentraLiveOtaFlowTranslate = (key: string, options?: Record<string, string>) => string
|
|
31
|
+
|
|
32
|
+
export type MentraLiveOtaFlowProps = {
|
|
33
|
+
/** Display name used in update copy. */
|
|
34
|
+
deviceName?: string
|
|
35
|
+
/** Entry page. `progress` exists for recovery/deep-link compatibility. */
|
|
36
|
+
initialPage?: MentraLiveOtaFlowPage
|
|
37
|
+
/** Start the OTA-only projections. Full Engine hosts should pass false. */
|
|
38
|
+
initializeRuntime?: boolean
|
|
39
|
+
/** Called after the final check or when the user leaves an optional update. */
|
|
40
|
+
onFinished: () => void
|
|
41
|
+
/** Host-owned Wi-Fi setup for glasses that do not support hotspot OTA. */
|
|
42
|
+
onOpenWifiSetup: () => void
|
|
43
|
+
/** Lets a host coordinate its global connection overlay with OTA progress and firmware restarts. */
|
|
44
|
+
onFirmwareRestartingChange?: (restarting: boolean, progressActive: boolean) => void
|
|
45
|
+
/** Enables the existing developer-only escape hatches. */
|
|
46
|
+
allowDevSkip?: boolean
|
|
47
|
+
/** Enables the existing super-mode interrupted-session escape hatch. */
|
|
48
|
+
superMode?: boolean
|
|
49
|
+
/** Optional host localization. Defaults to the Glassly App English OTA copy. */
|
|
50
|
+
translate?: MentraLiveOtaFlowTranslate
|
|
51
|
+
/** Optional host theme. Both hosts use the same layout and state machine. */
|
|
52
|
+
theme?: Partial<MentraLiveOtaFlowTheme>
|
|
53
|
+
style?: StyleProp<ViewStyle>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const DEFAULT_THEME: MentraLiveOtaFlowTheme = {
|
|
57
|
+
background: "#FFFFFF",
|
|
58
|
+
border: "#D7DFDA",
|
|
59
|
+
error: "#C43131",
|
|
60
|
+
foreground: "#0E2C1A",
|
|
61
|
+
primary: "#00B869",
|
|
62
|
+
primaryText: "#FFFFFF",
|
|
63
|
+
textDim: "#66736B",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const ENGLISH_COPY: Record<string, string> = {
|
|
67
|
+
"common:continue": "Continue",
|
|
68
|
+
"ota:checkingForUpdates": "Checking for updates",
|
|
69
|
+
"ota:checkingForUpdatesMessage":
|
|
70
|
+
"Connected devices will perform automatic updates. Automatic updates can be disabled in Device Settings",
|
|
71
|
+
"ota:updateAvailable": "{{deviceName}} Update Available",
|
|
72
|
+
"ota:updateConnectWifi": "Connect your {{deviceName}} to WiFi to install the update.",
|
|
73
|
+
"ota:updateDescription":
|
|
74
|
+
"A new update is available for your glasses. We recommend updating now for the best experience.",
|
|
75
|
+
"ota:releaseTransition": "{{fromVersion}} → {{toVersion}}",
|
|
76
|
+
"ota:releaseTransitionUnknown": "Current version unknown → {{toVersion}}",
|
|
77
|
+
"ota:updatedToVersion": "Updated to {{version}}",
|
|
78
|
+
"ota:downgradeAvailable": "{{deviceName}} Version Change Required",
|
|
79
|
+
"ota:downgradeDescription":
|
|
80
|
+
"This app requires an earlier glasses software version. Your photos and videos will be preserved, but glasses settings will be reset and restored automatically after the change.",
|
|
81
|
+
"ota:updateNow": "Update Now",
|
|
82
|
+
"ota:setupWifi": "Setup WiFi",
|
|
83
|
+
"ota:updateLater": "Later",
|
|
84
|
+
"ota:upToDate": "Up To Date",
|
|
85
|
+
"ota:devBuild": "Development Build",
|
|
86
|
+
"ota:devBuildNoOta":
|
|
87
|
+
"This mobile app is a development build, so automatic glasses updates are disabled. Use the developer settings manifest override to update them manually.",
|
|
88
|
+
"ota:noUpdatesAvailable": "Your glasses are running the latest version.",
|
|
89
|
+
"ota:checkFailed": "Check Failed",
|
|
90
|
+
"ota:checkFailedMessage": "Couldn't check for updates. Please check your connection and try again.",
|
|
91
|
+
"ota:updateInfoUnavailable": "Update Info Unavailable",
|
|
92
|
+
"ota:updateInfoUnavailableMessage":
|
|
93
|
+
"Update information for this version of the app is unavailable. Please check the app store for a newer version of the Glassly App.",
|
|
94
|
+
"ota:downgradeDuration": "Your glasses will restart twice — this may take up to 2 minutes.",
|
|
95
|
+
"ota:versionChangeRestarting": "Installing a different version…",
|
|
96
|
+
"ota:versionChangeVerifying": "Verifying your glasses…",
|
|
97
|
+
"ota:versionChangeKeepNearby": "Keep your glasses nearby and connected. They will restart on their own.",
|
|
98
|
+
"ota:versionChangeComplete": "Version Change Complete",
|
|
99
|
+
"ota:versionChangeCompleteMessage":
|
|
100
|
+
"Your glasses are now on the required version. Their settings were reset and are being restored automatically.",
|
|
101
|
+
"ota:versionChangeFirmwarePassComplete": "Firmware updated",
|
|
102
|
+
"ota:versionChangeFirmwarePassCompleteMessage":
|
|
103
|
+
"Your glasses restarted with new firmware. One more step: they'll now continue to the required version.",
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function defaultTranslate(key: string, options?: Record<string, string>): string {
|
|
107
|
+
let value = ENGLISH_COPY[key] ?? key
|
|
108
|
+
for (const [name, replacement] of Object.entries(options ?? {})) {
|
|
109
|
+
value = value.replaceAll(`{{${name}}}`, replacement)
|
|
110
|
+
}
|
|
111
|
+
return value
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function MentraLiveOtaFlow({
|
|
115
|
+
allowDevSkip = typeof __DEV__ !== "undefined" && __DEV__,
|
|
116
|
+
deviceName = "Mentra Live",
|
|
117
|
+
initialPage = "check",
|
|
118
|
+
initializeRuntime = true,
|
|
119
|
+
onFinished,
|
|
120
|
+
onFirmwareRestartingChange,
|
|
121
|
+
onOpenWifiSetup,
|
|
122
|
+
style,
|
|
123
|
+
superMode = false,
|
|
124
|
+
theme,
|
|
125
|
+
translate = defaultTranslate,
|
|
126
|
+
}: MentraLiveOtaFlowProps) {
|
|
127
|
+
const colors = useMemo(() => ({...DEFAULT_THEME, ...theme}), [theme])
|
|
128
|
+
const controller = useMentraLiveOta({
|
|
129
|
+
initialPage,
|
|
130
|
+
initializeRuntime,
|
|
131
|
+
onFinished,
|
|
132
|
+
onFirmwareRestartingChange,
|
|
133
|
+
onOpenWifiSetup,
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<SafeAreaView style={[styles.safeArea, {backgroundColor: colors.background}, style]}>
|
|
138
|
+
<View style={styles.header}>
|
|
139
|
+
<View />
|
|
140
|
+
<GlasslyMark color={colors.primary} />
|
|
141
|
+
</View>
|
|
142
|
+
<OtaFlowContent
|
|
143
|
+
allowDevSkip={allowDevSkip}
|
|
144
|
+
colors={colors}
|
|
145
|
+
controller={controller}
|
|
146
|
+
deviceName={deviceName}
|
|
147
|
+
superMode={superMode}
|
|
148
|
+
translate={translate}
|
|
149
|
+
/>
|
|
150
|
+
</SafeAreaView>
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function OtaFlowContent({
|
|
155
|
+
allowDevSkip,
|
|
156
|
+
colors,
|
|
157
|
+
controller,
|
|
158
|
+
deviceName,
|
|
159
|
+
superMode,
|
|
160
|
+
translate,
|
|
161
|
+
}: {
|
|
162
|
+
allowDevSkip: boolean
|
|
163
|
+
colors: MentraLiveOtaFlowTheme
|
|
164
|
+
controller: MentraLiveOtaController
|
|
165
|
+
deviceName: string
|
|
166
|
+
superMode: boolean
|
|
167
|
+
translate: MentraLiveOtaFlowTranslate
|
|
168
|
+
}) {
|
|
169
|
+
const {state} = controller
|
|
170
|
+
|
|
171
|
+
if (state.screen === "initializing") {
|
|
172
|
+
return (
|
|
173
|
+
<FlowPage colors={colors} icon="download" title={translate("ota:checkingForUpdates")}>
|
|
174
|
+
<ActivityIndicator size="large" color={colors.foreground} />
|
|
175
|
+
</FlowPage>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (state.screen === "checking") {
|
|
180
|
+
return (
|
|
181
|
+
<FlowPage colors={colors} icon="download" title={translate("ota:checkingForUpdates")}>
|
|
182
|
+
<BodyText colors={colors}>{translate("ota:checkingForUpdatesMessage")}</BodyText>
|
|
183
|
+
<ActivityIndicator size="large" color={colors.foreground} />
|
|
184
|
+
</FlowPage>
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (state.screen === "update_available" || state.screen === "wifi_required") {
|
|
189
|
+
return (
|
|
190
|
+
<FlowPage
|
|
191
|
+
colors={colors}
|
|
192
|
+
icon="download"
|
|
193
|
+
title={translate(state.versionChange ? "ota:downgradeAvailable" : "ota:updateAvailable", {deviceName})}
|
|
194
|
+
actions={
|
|
195
|
+
<>
|
|
196
|
+
<FlowButton
|
|
197
|
+
colors={colors}
|
|
198
|
+
disabled={!state.wifiStatusKnown}
|
|
199
|
+
label={translate(state.screen === "wifi_required" ? "ota:setupWifi" : "ota:updateNow")}
|
|
200
|
+
onPress={state.screen === "wifi_required" ? controller.openWifiSetup : controller.install}
|
|
201
|
+
/>
|
|
202
|
+
{state.canDismiss ? (
|
|
203
|
+
<FlowButton colors={colors} label={translate("ota:updateLater")} onPress={controller.finish} secondary />
|
|
204
|
+
) : null}
|
|
205
|
+
{allowDevSkip && state.updateRequired ? (
|
|
206
|
+
<FlowButton colors={colors} label="Skip (dev only)" onPress={controller.finish} secondary />
|
|
207
|
+
) : null}
|
|
208
|
+
</>
|
|
209
|
+
}>
|
|
210
|
+
{state.releaseTransition ? (
|
|
211
|
+
<BodyText colors={colors}>
|
|
212
|
+
{state.releaseTransition.fromVersion
|
|
213
|
+
? translate("ota:releaseTransition", {
|
|
214
|
+
fromVersion: state.releaseTransition.fromVersion,
|
|
215
|
+
toVersion: state.releaseTransition.toVersion,
|
|
216
|
+
})
|
|
217
|
+
: translate("ota:releaseTransitionUnknown", {toVersion: state.releaseTransition.toVersion})}
|
|
218
|
+
</BodyText>
|
|
219
|
+
) : null}
|
|
220
|
+
<BodyText colors={colors}>
|
|
221
|
+
{state.screen === "wifi_required"
|
|
222
|
+
? translate("ota:updateConnectWifi", {deviceName})
|
|
223
|
+
: translate(state.versionChange ? "ota:downgradeDescription" : "ota:updateDescription")}
|
|
224
|
+
</BodyText>
|
|
225
|
+
</FlowPage>
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (state.screen === "dev_build") {
|
|
230
|
+
return (
|
|
231
|
+
<FlowPage
|
|
232
|
+
actions={<FlowButton colors={colors} label={translate("common:continue")} onPress={controller.finish} />}
|
|
233
|
+
colors={colors}
|
|
234
|
+
icon="settings"
|
|
235
|
+
title={translate("ota:devBuild")}>
|
|
236
|
+
<BodyText colors={colors}>{translate("ota:devBuildNoOta")}</BodyText>
|
|
237
|
+
</FlowPage>
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (state.screen === "up_to_date") {
|
|
242
|
+
return (
|
|
243
|
+
<FlowPage
|
|
244
|
+
actions={<FlowButton colors={colors} label={translate("common:continue")} onPress={controller.finish} />}
|
|
245
|
+
colors={colors}
|
|
246
|
+
icon="check"
|
|
247
|
+
title={translate("ota:upToDate")}>
|
|
248
|
+
<BodyText colors={colors}>{translate("ota:noUpdatesAvailable")}</BodyText>
|
|
249
|
+
{state.releaseTransition ? (
|
|
250
|
+
<BodyText colors={colors}>
|
|
251
|
+
{translate("ota:updatedToVersion", {version: state.releaseTransition.toVersion})}
|
|
252
|
+
</BodyText>
|
|
253
|
+
) : null}
|
|
254
|
+
<ChangelogList changelogs={state.changelogs} colors={colors} />
|
|
255
|
+
</FlowPage>
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (state.screen === "update_info_unavailable") {
|
|
260
|
+
return (
|
|
261
|
+
<FlowPage
|
|
262
|
+
actions={<FlowButton colors={colors} label={translate("common:continue")} onPress={controller.finish} />}
|
|
263
|
+
colors={colors}
|
|
264
|
+
icon="alert"
|
|
265
|
+
title={translate("ota:updateInfoUnavailable")}>
|
|
266
|
+
<BodyText colors={colors}>{translate("ota:updateInfoUnavailableMessage")}</BodyText>
|
|
267
|
+
</FlowPage>
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (state.screen === "check_failed") {
|
|
272
|
+
return (
|
|
273
|
+
<FlowPage
|
|
274
|
+
actions={
|
|
275
|
+
<>
|
|
276
|
+
<FlowButton colors={colors} label="Retry" onPress={controller.retryCheck} />
|
|
277
|
+
{allowDevSkip ? (
|
|
278
|
+
<FlowButton colors={colors} label="Skip (dev only)" onPress={controller.finish} secondary />
|
|
279
|
+
) : null}
|
|
280
|
+
</>
|
|
281
|
+
}
|
|
282
|
+
colors={colors}
|
|
283
|
+
icon="alert"
|
|
284
|
+
title={translate("ota:checkFailed")}>
|
|
285
|
+
<BodyText colors={colors}>{translate("ota:checkFailedMessage")}</BodyText>
|
|
286
|
+
</FlowPage>
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (state.versionChangePhase === "restarting" || state.versionChangePhase === "verifying") {
|
|
291
|
+
return (
|
|
292
|
+
<FlowPage
|
|
293
|
+
colors={colors}
|
|
294
|
+
icon="download"
|
|
295
|
+
title={translate(
|
|
296
|
+
state.versionChangePhase === "verifying" ? "ota:versionChangeVerifying" : "ota:versionChangeRestarting",
|
|
297
|
+
)}>
|
|
298
|
+
<ActivityIndicator size="large" color={colors.foreground} />
|
|
299
|
+
<BodyText colors={colors}>{translate("ota:versionChangeKeepNearby")}</BodyText>
|
|
300
|
+
<BodyText colors={colors}>{translate("ota:downgradeDuration")}</BodyText>
|
|
301
|
+
</FlowPage>
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (state.screen === "starting" || state.screen === "preparing_hotspot") {
|
|
306
|
+
const title =
|
|
307
|
+
state.hotspotPhase === "downloading"
|
|
308
|
+
? "Downloading update to phone..."
|
|
309
|
+
: state.hotspotPhase === "starting_hotspot"
|
|
310
|
+
? "Starting glasses hotspot..."
|
|
311
|
+
: state.hotspotPhase === "joining_hotspot"
|
|
312
|
+
? "Connecting phone to glasses..."
|
|
313
|
+
: "Starting update..."
|
|
314
|
+
return (
|
|
315
|
+
<FlowPage colors={colors} icon="download" title={title}>
|
|
316
|
+
{state.hotspotPhase === "downloading" && state.hotspotArtifactPercent !== null ? (
|
|
317
|
+
<PercentText colors={colors} percent={state.hotspotArtifactPercent} />
|
|
318
|
+
) : null}
|
|
319
|
+
<ActivityIndicator size="large" color={colors.foreground} />
|
|
320
|
+
<BodyText colors={colors}>Do not disconnect your glasses</BodyText>
|
|
321
|
+
</FlowPage>
|
|
322
|
+
)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (state.screen === "updating") {
|
|
326
|
+
return (
|
|
327
|
+
<FlowPage colors={colors} icon="download" title={state.phase === "download" ? "Downloading..." : "Installing..."}>
|
|
328
|
+
{state.installingApkOnly ? (
|
|
329
|
+
<ActivityIndicator size="large" color={colors.foreground} />
|
|
330
|
+
) : (
|
|
331
|
+
<>
|
|
332
|
+
<PercentText colors={colors} percent={state.progress ?? 0} />
|
|
333
|
+
<View style={[styles.progressTrack, {backgroundColor: colors.border}]}>
|
|
334
|
+
<View
|
|
335
|
+
style={[styles.progressFill, {backgroundColor: colors.primary, width: `${state.progress ?? 0}%`}]}
|
|
336
|
+
/>
|
|
337
|
+
</View>
|
|
338
|
+
</>
|
|
339
|
+
)}
|
|
340
|
+
<BodyText colors={colors}>Do not disconnect your glasses</BodyText>
|
|
341
|
+
{state.versionChange && state.phase === "install" ? (
|
|
342
|
+
<BodyText colors={colors}>{translate("ota:downgradeDuration")}</BodyText>
|
|
343
|
+
) : null}
|
|
344
|
+
</FlowPage>
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (state.screen === "restarting") {
|
|
349
|
+
return (
|
|
350
|
+
<FlowPage
|
|
351
|
+
actions={
|
|
352
|
+
<FlowButton colors={colors} disabled={state.continueDisabled} label="Continue" onPress={controller.finish} />
|
|
353
|
+
}
|
|
354
|
+
colors={colors}
|
|
355
|
+
icon="check"
|
|
356
|
+
title="Update Installed"
|
|
357
|
+
/>
|
|
358
|
+
)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (state.screen === "complete") {
|
|
362
|
+
const title = state.versionChangeConverged
|
|
363
|
+
? translate("ota:versionChangeComplete")
|
|
364
|
+
: state.versionChange
|
|
365
|
+
? translate("ota:versionChangeFirmwarePassComplete")
|
|
366
|
+
: "Update complete!"
|
|
367
|
+
const message = state.versionChangeConverged
|
|
368
|
+
? translate("ota:versionChangeCompleteMessage")
|
|
369
|
+
: state.versionChange
|
|
370
|
+
? translate("ota:versionChangeFirmwarePassCompleteMessage")
|
|
371
|
+
: "Your glasses are up to date."
|
|
372
|
+
return (
|
|
373
|
+
<FlowPage
|
|
374
|
+
actions={
|
|
375
|
+
<FlowButton
|
|
376
|
+
colors={colors}
|
|
377
|
+
label={state.versionChange && !state.versionChangeConverged ? "Continue" : "Done"}
|
|
378
|
+
onPress={controller.finish}
|
|
379
|
+
/>
|
|
380
|
+
}
|
|
381
|
+
colors={colors}
|
|
382
|
+
icon="check"
|
|
383
|
+
title={title}>
|
|
384
|
+
<BodyText colors={colors}>{message}</BodyText>
|
|
385
|
+
{state.releaseTransition ? (
|
|
386
|
+
<BodyText colors={colors}>
|
|
387
|
+
{translate("ota:updatedToVersion", {version: state.releaseTransition.toVersion})}
|
|
388
|
+
</BodyText>
|
|
389
|
+
) : null}
|
|
390
|
+
<ChangelogList changelogs={state.changelogs} colors={colors} />
|
|
391
|
+
</FlowPage>
|
|
392
|
+
)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (state.screen === "failed") {
|
|
396
|
+
return (
|
|
397
|
+
<FlowPage
|
|
398
|
+
actions={
|
|
399
|
+
<>
|
|
400
|
+
<FlowButton
|
|
401
|
+
colors={colors}
|
|
402
|
+
label={state.canRetry ? "Retry" : "Done"}
|
|
403
|
+
onPress={state.canRetry ? controller.retryInstall : controller.finish}
|
|
404
|
+
/>
|
|
405
|
+
{state.canOpenWifiSetup ? (
|
|
406
|
+
<FlowButton colors={colors} label="Change WiFi" onPress={controller.openWifiSetup} secondary />
|
|
407
|
+
) : null}
|
|
408
|
+
</>
|
|
409
|
+
}
|
|
410
|
+
colors={colors}
|
|
411
|
+
icon="alert"
|
|
412
|
+
title="Update Failed">
|
|
413
|
+
<BodyText colors={colors}>{state.error?.message}</BodyText>
|
|
414
|
+
</FlowPage>
|
|
415
|
+
)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return (
|
|
419
|
+
<FlowPage
|
|
420
|
+
actions={
|
|
421
|
+
superMode ? (
|
|
422
|
+
<FlowButton colors={colors} label="Skip (super)" onPress={controller.discard} secondary />
|
|
423
|
+
) : undefined
|
|
424
|
+
}
|
|
425
|
+
colors={colors}
|
|
426
|
+
icon="bluetooth"
|
|
427
|
+
title="Glasses disconnected">
|
|
428
|
+
<BodyText colors={colors}>Reconnecting...</BodyText>
|
|
429
|
+
<ActivityIndicator size="large" color={colors.foreground} />
|
|
430
|
+
</FlowPage>
|
|
431
|
+
)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
type FlowPageProps = {
|
|
435
|
+
actions?: React.ReactNode
|
|
436
|
+
children?: React.ReactNode
|
|
437
|
+
colors: MentraLiveOtaFlowTheme
|
|
438
|
+
icon: "alert" | "bluetooth" | "check" | "download" | "settings"
|
|
439
|
+
title: string
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function FlowPage({actions, children, colors, icon, title}: FlowPageProps) {
|
|
443
|
+
return (
|
|
444
|
+
<View style={styles.page} testID="mentra-live-ota-flow">
|
|
445
|
+
<View style={styles.centerContent}>
|
|
446
|
+
<FlowIcon colors={colors} name={icon} />
|
|
447
|
+
<Text style={[styles.title, {color: colors.foreground}]}>{title}</Text>
|
|
448
|
+
{children}
|
|
449
|
+
</View>
|
|
450
|
+
{actions ? <View style={styles.actions}>{actions}</View> : <View style={styles.actionSpacer} />}
|
|
451
|
+
</View>
|
|
452
|
+
)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function BodyText({children, colors}: {children: React.ReactNode; colors: MentraLiveOtaFlowTheme}) {
|
|
456
|
+
return <Text style={[styles.body, {color: colors.textDim}]}>{children}</Text>
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function PercentText({colors, percent}: {colors: MentraLiveOtaFlowTheme; percent: number}) {
|
|
460
|
+
return <Text style={[styles.percent, {color: colors.primary}]}>{Math.round(percent)}%</Text>
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function ChangelogList({
|
|
464
|
+
changelogs,
|
|
465
|
+
colors,
|
|
466
|
+
}: {
|
|
467
|
+
changelogs: MentraLiveOtaController["state"]["changelogs"]
|
|
468
|
+
colors: MentraLiveOtaFlowTheme
|
|
469
|
+
}) {
|
|
470
|
+
if (changelogs.length === 0) return null
|
|
471
|
+
return (
|
|
472
|
+
<ScrollView contentContainerStyle={styles.changelogContent} style={styles.changelogList}>
|
|
473
|
+
{changelogs.map((entry) => (
|
|
474
|
+
<View key={entry.version} style={styles.changelogEntry}>
|
|
475
|
+
<Text selectable style={[styles.changelogVersion, {color: colors.foreground}]}>
|
|
476
|
+
{entry.version}
|
|
477
|
+
</Text>
|
|
478
|
+
<Text selectable style={[styles.changelogMarkdown, {color: colors.textDim}]}>
|
|
479
|
+
{entry.markdown}
|
|
480
|
+
</Text>
|
|
481
|
+
</View>
|
|
482
|
+
))}
|
|
483
|
+
</ScrollView>
|
|
484
|
+
)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function FlowButton({
|
|
488
|
+
colors,
|
|
489
|
+
disabled = false,
|
|
490
|
+
label,
|
|
491
|
+
onPress,
|
|
492
|
+
secondary = false,
|
|
493
|
+
}: {
|
|
494
|
+
colors: MentraLiveOtaFlowTheme
|
|
495
|
+
disabled?: boolean
|
|
496
|
+
label: string
|
|
497
|
+
onPress: () => void
|
|
498
|
+
secondary?: boolean
|
|
499
|
+
}) {
|
|
500
|
+
return (
|
|
501
|
+
<Pressable
|
|
502
|
+
accessibilityRole="button"
|
|
503
|
+
disabled={disabled}
|
|
504
|
+
onPress={onPress}
|
|
505
|
+
style={({pressed}) => [
|
|
506
|
+
styles.button,
|
|
507
|
+
{
|
|
508
|
+
backgroundColor: secondary ? colors.background : colors.foreground,
|
|
509
|
+
borderColor: secondary ? colors.border : colors.foreground,
|
|
510
|
+
opacity: disabled ? 0.45 : pressed ? 0.75 : 1,
|
|
511
|
+
},
|
|
512
|
+
]}
|
|
513
|
+
testID={`button-${label}`}>
|
|
514
|
+
<Text style={[styles.buttonText, {color: secondary ? colors.foreground : colors.background}]}>{label}</Text>
|
|
515
|
+
</Pressable>
|
|
516
|
+
)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function GlasslyMark({color}: {color: string}) {
|
|
520
|
+
return (
|
|
521
|
+
<Svg width={33} height={18} viewBox="0 0 50 27" fill="none">
|
|
522
|
+
<Rect y={14.8072} width={11.8457} height={11.8457} fill={color} />
|
|
523
|
+
<Path d="M9.36639 0L30.7163 14.8072V26.6529L9.36639 11.8457V0Z" fill={color} />
|
|
524
|
+
<Path d="M28.6501 0L50 14.8072V26.6529L28.6501 11.8457V0Z" fill={color} />
|
|
525
|
+
</Svg>
|
|
526
|
+
)
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function FlowIcon({colors, name}: {colors: MentraLiveOtaFlowTheme; name: FlowPageProps["icon"]}) {
|
|
530
|
+
const color = name === "alert" || name === "bluetooth" ? colors.error : colors.primary
|
|
531
|
+
const glyph =
|
|
532
|
+
name === "check" ? "✓" : name === "alert" ? "!" : name === "settings" ? "⚙" : name === "bluetooth" ? "⌁" : "↓"
|
|
533
|
+
return <Text style={[styles.icon, {color}]}>{glyph}</Text>
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const styles = StyleSheet.create({
|
|
537
|
+
safeArea: {flex: 1},
|
|
538
|
+
header: {
|
|
539
|
+
alignItems: "center",
|
|
540
|
+
flexDirection: "row",
|
|
541
|
+
height: 48,
|
|
542
|
+
justifyContent: "space-between",
|
|
543
|
+
paddingHorizontal: 20,
|
|
544
|
+
},
|
|
545
|
+
page: {flex: 1, paddingBottom: 24, paddingHorizontal: 24},
|
|
546
|
+
centerContent: {alignItems: "center", flex: 1, gap: 16, justifyContent: "center"},
|
|
547
|
+
actionSpacer: {height: 48},
|
|
548
|
+
actions: {gap: 12},
|
|
549
|
+
icon: {fontSize: 64, fontWeight: "500", lineHeight: 72, textAlign: "center"},
|
|
550
|
+
title: {fontSize: 20, fontWeight: "600", textAlign: "center"},
|
|
551
|
+
body: {fontSize: 14, lineHeight: 20, maxWidth: 420, textAlign: "center"},
|
|
552
|
+
percent: {fontSize: 30, fontVariant: ["tabular-nums"], fontWeight: "700"},
|
|
553
|
+
progressTrack: {borderRadius: 4, height: 8, maxWidth: 420, overflow: "hidden", width: "100%"},
|
|
554
|
+
progressFill: {borderRadius: 4, height: 8},
|
|
555
|
+
changelogList: {flexShrink: 1, maxHeight: 260, maxWidth: 420, width: "100%"},
|
|
556
|
+
changelogContent: {gap: 18, paddingVertical: 4},
|
|
557
|
+
changelogEntry: {gap: 8},
|
|
558
|
+
changelogVersion: {fontSize: 16, fontWeight: "600"},
|
|
559
|
+
changelogMarkdown: {fontSize: 14, lineHeight: 20},
|
|
560
|
+
button: {
|
|
561
|
+
alignItems: "center",
|
|
562
|
+
borderRadius: 50,
|
|
563
|
+
borderWidth: 1,
|
|
564
|
+
justifyContent: "center",
|
|
565
|
+
minHeight: 44,
|
|
566
|
+
paddingHorizontal: 20,
|
|
567
|
+
},
|
|
568
|
+
buttonText: {fontSize: 14, fontWeight: "500"},
|
|
569
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export {
|
|
2
|
+
MentraLiveOtaFlow,
|
|
3
|
+
type MentraLiveOtaFlowPage,
|
|
4
|
+
type MentraLiveOtaFlowProps,
|
|
5
|
+
type MentraLiveOtaFlowTheme,
|
|
6
|
+
type MentraLiveOtaFlowTranslate,
|
|
7
|
+
} from "./MentraLiveOtaFlow"
|
|
8
|
+
export {
|
|
9
|
+
useMentraLiveOta,
|
|
10
|
+
type MentraLiveOtaController,
|
|
11
|
+
type MentraLiveOtaError,
|
|
12
|
+
type MentraLiveOtaErrorCode,
|
|
13
|
+
type MentraLiveOtaHotspotPhase,
|
|
14
|
+
type MentraLiveOtaInstallPhase,
|
|
15
|
+
type MentraLiveOtaReleaseTransition,
|
|
16
|
+
type MentraLiveOtaScreen,
|
|
17
|
+
type MentraLiveOtaState,
|
|
18
|
+
type MentraLiveOtaStep,
|
|
19
|
+
type MentraLiveOtaTransport,
|
|
20
|
+
type UseMentraLiveOtaOptions,
|
|
21
|
+
} from "./useMentraLiveOta"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {useEffect, useRef, useState} from "react"
|
|
2
|
+
|
|
3
|
+
export function useEngineSnapshot<T>(getSnapshot: () => T, subscribe: (onChange: () => void) => () => void): T {
|
|
4
|
+
const getSnapshotRef = useRef(getSnapshot)
|
|
5
|
+
const subscribeRef = useRef(subscribe)
|
|
6
|
+
getSnapshotRef.current = getSnapshot
|
|
7
|
+
subscribeRef.current = subscribe
|
|
8
|
+
const [snapshot, setSnapshot] = useState(() => getSnapshot())
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
setSnapshot(getSnapshotRef.current())
|
|
12
|
+
return subscribeRef.current(() => setSnapshot(getSnapshotRef.current()))
|
|
13
|
+
}, [])
|
|
14
|
+
|
|
15
|
+
return snapshot
|
|
16
|
+
}
|