@mentra/engine 3.1.0-dev.98 → 3.1.1-beta.230
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 +1 -1
- package/build/generated/releaseMetadata.js +6 -6
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/react/MentraLiveOtaFlow.d.ts.map +1 -1
- package/build/react/MentraLiveOtaFlow.js +65 -15
- package/build/react/MentraLiveOtaFlow.js.map +1 -1
- package/build/react/useMentraLiveOta.d.ts +9 -0
- package/build/react/useMentraLiveOta.d.ts.map +1 -1
- package/build/react/useMentraLiveOta.js +4 -0
- package/build/react/useMentraLiveOta.js.map +1 -1
- package/build/services/OtaArtifactDownloader.d.ts.map +1 -1
- package/build/services/OtaArtifactDownloader.js +10 -0
- package/build/services/OtaArtifactDownloader.js.map +1 -1
- package/build/services/OtaInstallCoordinator.d.ts +3 -0
- package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
- package/build/services/OtaInstallCoordinator.js +12 -4
- package/build/services/OtaInstallCoordinator.js.map +1 -1
- package/build/services/OtaUpdateCheckService.d.ts +1 -1
- package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
- package/build/services/OtaUpdateCheckService.js +5 -1
- package/build/services/OtaUpdateCheckService.js.map +1 -1
- package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
- package/build/services/PhoneStreamCoordinator.js +9 -6
- package/build/services/PhoneStreamCoordinator.js.map +1 -1
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +17 -2
- package/build/stores/settings.js.map +1 -1
- package/build/utils/devLogging.d.ts.map +1 -1
- package/build/utils/devLogging.js +0 -1
- package/build/utils/devLogging.js.map +1 -1
- package/package.json +7 -7
- package/src/generated/releaseMetadata.ts +6 -6
- package/src/react/MentraLiveOtaFlow.tsx +82 -14
- package/src/react/useMentraLiveOta.ts +9 -0
- package/src/services/OtaArtifactDownloader.ts +10 -0
- package/src/services/OtaInstallCoordinator.ts +13 -3
- package/src/services/OtaUpdateCheckService.ts +5 -1
- package/src/services/PhoneStreamCoordinator.ts +10 -6
- package/src/stores/settings.ts +17 -2
- package/src/utils/devLogging.ts +0 -1
|
@@ -70,6 +70,20 @@ const DEFAULT_THEME: MentraLiveOtaFlowTheme = {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
const ENGLISH_COPY: Record<string, string> = {
|
|
73
|
+
"ota:downloadingToPhone": "Downloading update to phone...",
|
|
74
|
+
"ota:startingGlassesHotspot": "Starting glasses hotspot...",
|
|
75
|
+
"ota:connectingPhoneToGlasses": "Connecting phone to glasses...",
|
|
76
|
+
"ota:startingHotspotUpdate": "Starting update...",
|
|
77
|
+
"ota:transferringToGlasses": "Transferring update to glasses...",
|
|
78
|
+
"ota:installingOnGlasses": "Installing update on glasses...",
|
|
79
|
+
"ota:componentApk": "Glasses software",
|
|
80
|
+
"ota:componentMtk": "System firmware",
|
|
81
|
+
"ota:componentBes": "Bluetooth firmware",
|
|
82
|
+
"ota:updateFile": "File {{current}} of {{total}} · {{component}}",
|
|
83
|
+
"ota:updatePart": "Update {{current}} of {{total}} · {{component}}",
|
|
84
|
+
"ota:phoneFileProgress": "Each file downloads separately. Progress is for the current file.",
|
|
85
|
+
"ota:transferFileProgress": "Progress is for this file’s transfer from your phone.",
|
|
86
|
+
|
|
73
87
|
"common:continue": "Continue",
|
|
74
88
|
"common:done": "Done",
|
|
75
89
|
"ota:checkingForUpdates": "Checking for updates",
|
|
@@ -125,6 +139,12 @@ const ENGLISH_COPY: Record<string, string> = {
|
|
|
125
139
|
"Your glasses restarted with new firmware. One more step: they'll now continue to the required version.",
|
|
126
140
|
}
|
|
127
141
|
|
|
142
|
+
const componentCopyKey = {
|
|
143
|
+
apk: "ota:componentApk",
|
|
144
|
+
mtk: "ota:componentMtk",
|
|
145
|
+
bes: "ota:componentBes",
|
|
146
|
+
} as const
|
|
147
|
+
|
|
128
148
|
function defaultTranslate(key: string, options?: Record<string, string>): string {
|
|
129
149
|
let value = ENGLISH_COPY[key] ?? key
|
|
130
150
|
for (const [name, replacement] of Object.entries(options ?? {})) {
|
|
@@ -374,28 +394,65 @@ function OtaFlowContent({
|
|
|
374
394
|
}
|
|
375
395
|
|
|
376
396
|
if (state.screen === "starting" || state.screen === "preparing_hotspot") {
|
|
377
|
-
const title =
|
|
397
|
+
const title = translate(
|
|
378
398
|
state.hotspotPhase === "downloading"
|
|
379
|
-
? "
|
|
399
|
+
? "ota:downloadingToPhone"
|
|
380
400
|
: state.hotspotPhase === "starting_hotspot"
|
|
381
|
-
? "
|
|
401
|
+
? "ota:startingGlassesHotspot"
|
|
382
402
|
: state.hotspotPhase === "joining_hotspot"
|
|
383
|
-
? "
|
|
384
|
-
: "
|
|
403
|
+
? "ota:connectingPhoneToGlasses"
|
|
404
|
+
: "ota:startingHotspotUpdate",
|
|
405
|
+
)
|
|
406
|
+
const artifact = state.hotspotPhase === "downloading" ? state.hotspotArtifact : null
|
|
385
407
|
return (
|
|
386
408
|
<FlowPage colors={colors} icon="download" title={title}>
|
|
409
|
+
{artifact ? (
|
|
410
|
+
<BodyText colors={colors}>
|
|
411
|
+
{translate("ota:updateFile", {
|
|
412
|
+
current: String(artifact.index + 1),
|
|
413
|
+
total: String(artifact.totalCount),
|
|
414
|
+
component: translate(componentCopyKey[artifact.kind]),
|
|
415
|
+
})}
|
|
416
|
+
</BodyText>
|
|
417
|
+
) : null}
|
|
387
418
|
{state.hotspotPhase === "downloading" && state.hotspotArtifactPercent !== null ? (
|
|
388
419
|
<PercentText colors={colors} percent={state.hotspotArtifactPercent} />
|
|
389
420
|
) : null}
|
|
390
421
|
<ActivityIndicator size="large" color={colors.foreground} />
|
|
422
|
+
{state.hotspotPhase === "downloading" ? (
|
|
423
|
+
<BodyText colors={colors}>{translate("ota:phoneFileProgress")}</BodyText>
|
|
424
|
+
) : null}
|
|
391
425
|
<BodyText colors={colors}>Do not disconnect your glasses</BodyText>
|
|
392
426
|
</FlowPage>
|
|
393
427
|
)
|
|
394
428
|
}
|
|
395
429
|
|
|
396
430
|
if (state.screen === "updating") {
|
|
431
|
+
const hotspot = state.transport === "hotspot"
|
|
432
|
+
const title = hotspot
|
|
433
|
+
? translate(state.phase === "download" ? "ota:transferringToGlasses" : "ota:installingOnGlasses")
|
|
434
|
+
: state.phase === "download"
|
|
435
|
+
? "Downloading..."
|
|
436
|
+
: "Installing..."
|
|
437
|
+
const component = state.step ? translate(componentCopyKey[state.step]) : null
|
|
438
|
+
const hasStepCount =
|
|
439
|
+
state.currentStep !== null &&
|
|
440
|
+
state.totalSteps !== null &&
|
|
441
|
+
state.currentStep > 0 &&
|
|
442
|
+
state.currentStep <= state.totalSteps
|
|
397
443
|
return (
|
|
398
|
-
<FlowPage colors={colors} icon="download" title={
|
|
444
|
+
<FlowPage colors={colors} icon="download" title={title}>
|
|
445
|
+
{hotspot && component ? (
|
|
446
|
+
<BodyText colors={colors}>
|
|
447
|
+
{hasStepCount
|
|
448
|
+
? translate("ota:updatePart", {
|
|
449
|
+
current: String(state.currentStep),
|
|
450
|
+
total: String(state.totalSteps),
|
|
451
|
+
component,
|
|
452
|
+
})
|
|
453
|
+
: component}
|
|
454
|
+
</BodyText>
|
|
455
|
+
) : null}
|
|
399
456
|
{state.installingApkOnly ? (
|
|
400
457
|
<ActivityIndicator size="large" color={colors.foreground} />
|
|
401
458
|
) : (
|
|
@@ -409,6 +466,9 @@ function OtaFlowContent({
|
|
|
409
466
|
</>
|
|
410
467
|
)}
|
|
411
468
|
<BodyText colors={colors}>Do not disconnect your glasses</BodyText>
|
|
469
|
+
{hotspot && state.phase === "download" ? (
|
|
470
|
+
<BodyText colors={colors}>{translate("ota:transferFileProgress")}</BodyText>
|
|
471
|
+
) : null}
|
|
412
472
|
{state.versionChange && state.phase === "install" ? (
|
|
413
473
|
<BodyText colors={colors}>{translate("ota:downgradeDuration")}</BodyText>
|
|
414
474
|
) : null}
|
|
@@ -512,11 +572,15 @@ type FlowPageProps = {
|
|
|
512
572
|
function FlowPage({actions, children, colors, contentAlignment = "center", icon, title}: FlowPageProps) {
|
|
513
573
|
return (
|
|
514
574
|
<View style={styles.page} testID="mentra-live-ota-flow">
|
|
515
|
-
<
|
|
575
|
+
<ScrollView
|
|
576
|
+
contentContainerStyle={[styles.centerContent, contentAlignment === "top" && styles.topContent]}
|
|
577
|
+
nestedScrollEnabled
|
|
578
|
+
style={styles.contentScroll}
|
|
579
|
+
testID="ota-page-scroll">
|
|
516
580
|
<FlowIcon colors={colors} name={icon} />
|
|
517
581
|
<Text style={[styles.title, {color: colors.foreground}]}>{title}</Text>
|
|
518
582
|
{children}
|
|
519
|
-
</
|
|
583
|
+
</ScrollView>
|
|
520
584
|
{actions ? <View style={styles.actions}>{actions}</View> : <View style={styles.actionSpacer} />}
|
|
521
585
|
</View>
|
|
522
586
|
)
|
|
@@ -628,12 +692,14 @@ export function ChangelogList({
|
|
|
628
692
|
title: string
|
|
629
693
|
}) {
|
|
630
694
|
if (changelogs.length === 0) return null
|
|
695
|
+
|
|
631
696
|
return (
|
|
632
697
|
<View style={[styles.changelogCard, {borderColor: colors.border}]} testID="ota-changelog-card">
|
|
633
698
|
<Text style={[styles.changelogTitle, {color: colors.foreground}]}>{title}</Text>
|
|
634
699
|
<ScrollView
|
|
635
700
|
contentContainerStyle={styles.changelogContent}
|
|
636
701
|
nestedScrollEnabled
|
|
702
|
+
persistentScrollbar
|
|
637
703
|
showsVerticalScrollIndicator
|
|
638
704
|
style={styles.changelogList}
|
|
639
705
|
testID="ota-changelog-scroll">
|
|
@@ -715,8 +781,9 @@ const styles = StyleSheet.create({
|
|
|
715
781
|
paddingHorizontal: 20,
|
|
716
782
|
},
|
|
717
783
|
page: {flex: 1, paddingBottom: 24, paddingHorizontal: 24},
|
|
718
|
-
|
|
719
|
-
|
|
784
|
+
contentScroll: {flex: 1},
|
|
785
|
+
centerContent: {alignItems: "center", flexGrow: 1, gap: 16, justifyContent: "center"},
|
|
786
|
+
topContent: {justifyContent: "flex-start", paddingBottom: 16, paddingTop: 12},
|
|
720
787
|
actionSpacer: {height: 48},
|
|
721
788
|
actions: {gap: 12},
|
|
722
789
|
icon: {fontSize: 64, fontWeight: "500", lineHeight: 72, textAlign: "center"},
|
|
@@ -728,16 +795,17 @@ const styles = StyleSheet.create({
|
|
|
728
795
|
changelogCard: {
|
|
729
796
|
borderRadius: 16,
|
|
730
797
|
borderWidth: 1,
|
|
731
|
-
|
|
798
|
+
flexGrow: 1,
|
|
732
799
|
gap: 12,
|
|
733
|
-
maxHeight: 300,
|
|
734
800
|
maxWidth: 420,
|
|
801
|
+
minHeight: 200,
|
|
735
802
|
padding: 16,
|
|
736
803
|
width: "100%",
|
|
737
804
|
},
|
|
738
805
|
changelogTitle: {fontSize: 16, fontWeight: "700"},
|
|
739
|
-
|
|
740
|
-
|
|
806
|
+
// Bound the notes themselves so the card can grow for its title, but not for all of the Markdown.
|
|
807
|
+
changelogList: {flexGrow: 1, height: 120, width: "100%"},
|
|
808
|
+
changelogContent: {gap: 20, paddingBottom: 4},
|
|
741
809
|
changelogEntry: {gap: 8},
|
|
742
810
|
changelogEntryDivider: {borderTopWidth: StyleSheet.hairlineWidth, paddingTop: 20},
|
|
743
811
|
changelogVersion: {fontSize: 14, fontWeight: "600"},
|
|
@@ -82,6 +82,11 @@ export type MentraLiveOtaState = {
|
|
|
82
82
|
hotspotSupported: boolean
|
|
83
83
|
hotspotPhase: MentraLiveOtaHotspotPhase
|
|
84
84
|
hotspotArtifactPercent: number | null
|
|
85
|
+
/**
|
|
86
|
+
* Current phone download context. Index is zero-based; percent applies to this file only.
|
|
87
|
+
* Optional for source compatibility; this hook returns null when no file is active.
|
|
88
|
+
*/
|
|
89
|
+
hotspotArtifact?: {kind: MentraLiveOtaStep; index: number; totalCount: number} | null
|
|
85
90
|
phase: MentraLiveOtaInstallPhase | null
|
|
86
91
|
step: MentraLiveOtaStep | null
|
|
87
92
|
currentStep: number | null
|
|
@@ -624,6 +629,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
624
629
|
hotspotSupported,
|
|
625
630
|
hotspotPhase: "idle",
|
|
626
631
|
hotspotArtifactPercent: null,
|
|
632
|
+
hotspotArtifact: null,
|
|
627
633
|
phase: null,
|
|
628
634
|
step: null,
|
|
629
635
|
currentStep: null,
|
|
@@ -678,6 +684,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
678
684
|
hotspotSupported,
|
|
679
685
|
hotspotPhase: "idle",
|
|
680
686
|
hotspotArtifactPercent: null,
|
|
687
|
+
hotspotArtifact: null,
|
|
681
688
|
phase: null,
|
|
682
689
|
step: null,
|
|
683
690
|
currentStep: null,
|
|
@@ -732,6 +739,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
732
739
|
}
|
|
733
740
|
: null
|
|
734
741
|
const totalSteps = installSnapshot.otaStatus?.totalSteps ?? null
|
|
742
|
+
const artifact = installSnapshot.hotspotArtifact
|
|
735
743
|
const changelogs = screen === "complete" ? releaseChangelogsForActiveChain() : []
|
|
736
744
|
return {
|
|
737
745
|
screen,
|
|
@@ -747,6 +755,7 @@ export function useMentraLiveOta(options: UseMentraLiveOtaOptions = {}): MentraL
|
|
|
747
755
|
hotspotSupported,
|
|
748
756
|
hotspotPhase: installSnapshot.hotspotPhase,
|
|
749
757
|
hotspotArtifactPercent: installSnapshot.hotspotArtifactPercent,
|
|
758
|
+
hotspotArtifact: artifact ? {kind: artifact.kind, index: artifact.index, totalCount: artifact.totalCount} : null,
|
|
750
759
|
phase: installSnapshot.otaStatus?.phase ?? installSnapshot.otaProgress?.stage ?? null,
|
|
751
760
|
step: installSnapshot.otaStatus?.stepType ?? null,
|
|
752
761
|
currentStep: installSnapshot.otaStatus?.currentStep ?? null,
|
|
@@ -139,6 +139,16 @@ export async function prepareArtifacts(
|
|
|
139
139
|
const prepared: PreparedOtaArtifact[] = []
|
|
140
140
|
for (let index = 0; index < plan.length; index++) {
|
|
141
141
|
const entry = plan[index]
|
|
142
|
+
// Announce each file before cache verification or native download callbacks,
|
|
143
|
+
// so the previous file's 100% cannot linger under the next file's label.
|
|
144
|
+
onProgress?.({
|
|
145
|
+
kind: entry.kind,
|
|
146
|
+
index,
|
|
147
|
+
totalCount: plan.length,
|
|
148
|
+
artifactPercent: 0,
|
|
149
|
+
bytesWritten: 0,
|
|
150
|
+
contentLength: 0,
|
|
151
|
+
})
|
|
142
152
|
|
|
143
153
|
const cachedPath = `${directory}/${entry.sha256}`
|
|
144
154
|
if (await RNFS.exists(cachedPath)) {
|
|
@@ -22,6 +22,7 @@ import GlobalEventEmitter from "../utils/GlobalEventEmitter"
|
|
|
22
22
|
import {isGlassesConnected, useGlassesStore} from "../stores/glasses"
|
|
23
23
|
import {resolveOtaManifestUrl} from "./otaManifestUrl"
|
|
24
24
|
import {hotspotOtaTransport, type HotspotOtaPhase} from "./HotspotOtaTransport"
|
|
25
|
+
import type {OtaArtifactDownloadProgress} from "./OtaArtifactDownloader"
|
|
25
26
|
import type {OtaCheckCurrentGlassesResult} from "./OtaUpdateCheckService"
|
|
26
27
|
import {deriveDisplayState, type DisplayState} from "./otaDisplayState"
|
|
27
28
|
import {
|
|
@@ -139,8 +140,10 @@ function latestPercentForStuck(otaStatus: OtaStatus | null, otaProgress: OtaProg
|
|
|
139
140
|
* reply that cancels the fallback.
|
|
140
141
|
*/
|
|
141
142
|
function hasRecoveringOtaReply(otaStatus: OtaStatus | null, otaProgress: OtaProgress | null): boolean {
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
// OtaService projects status into legacy progress too: idle becomes STARTED.
|
|
144
|
+
// Neither that projection nor progress retained across reboot proves activity.
|
|
145
|
+
if (otaStatus) return otaStatus.status !== "idle"
|
|
146
|
+
return !!otaProgress
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
/** Read model the host progress screen renders from. */
|
|
@@ -171,6 +174,7 @@ export interface OtaInstallSnapshot {
|
|
|
171
174
|
/** Pre-ota_start phone staging/join state for a hotspot attempt. */
|
|
172
175
|
hotspotPhase: HotspotOtaPhase
|
|
173
176
|
hotspotArtifactPercent: number | null
|
|
177
|
+
hotspotArtifact: OtaArtifactDownloadProgress | null
|
|
174
178
|
/** Transport selected from the checked glasses capabilities and Wi-Fi state. */
|
|
175
179
|
transport: "wifi" | "hotspot"
|
|
176
180
|
}
|
|
@@ -189,6 +193,7 @@ class OtaInstallCoordinator {
|
|
|
189
193
|
private hotspotManifestUrl: string | null = null
|
|
190
194
|
private hotspotPhase: HotspotOtaPhase = "idle"
|
|
191
195
|
private hotspotArtifactPercent: number | null = null
|
|
196
|
+
private hotspotArtifact: OtaArtifactDownloadProgress | null = null
|
|
192
197
|
|
|
193
198
|
// Genuinely session-local state (was component state/refs).
|
|
194
199
|
private errorMsg = ""
|
|
@@ -303,6 +308,7 @@ class OtaInstallCoordinator {
|
|
|
303
308
|
this.hotspotManifestUrl = null
|
|
304
309
|
this.hotspotPhase = "idle"
|
|
305
310
|
this.hotspotArtifactPercent = null
|
|
311
|
+
this.hotspotArtifact = null
|
|
306
312
|
return this.selectedTransport
|
|
307
313
|
}
|
|
308
314
|
|
|
@@ -510,6 +516,7 @@ class OtaInstallCoordinator {
|
|
|
510
516
|
versionChangePhase: this.deriveVersionChangePhase(connected),
|
|
511
517
|
hotspotPhase: this.hotspotPhase,
|
|
512
518
|
hotspotArtifactPercent: this.hotspotArtifactPercent,
|
|
519
|
+
hotspotArtifact: this.hotspotArtifact ? {...this.hotspotArtifact} : null,
|
|
513
520
|
transport: this.selectedTransport,
|
|
514
521
|
}
|
|
515
522
|
}
|
|
@@ -1501,7 +1508,9 @@ class OtaInstallCoordinator {
|
|
|
1501
1508
|
}
|
|
1502
1509
|
this.hotspotManifestUrl = await hotspotOtaTransport.prepare(this.preparedCheckResult, (progress) => {
|
|
1503
1510
|
this.hotspotPhase = progress.phase
|
|
1504
|
-
this.
|
|
1511
|
+
this.hotspotArtifact = progress.artifact ? {...progress.artifact} : null
|
|
1512
|
+
this.hotspotArtifactPercent =
|
|
1513
|
+
progress.artifact && progress.artifact.contentLength > 0 ? progress.artifact.artifactPercent : null
|
|
1505
1514
|
this.emitInternalChange()
|
|
1506
1515
|
})
|
|
1507
1516
|
}
|
|
@@ -1581,6 +1590,7 @@ class OtaInstallCoordinator {
|
|
|
1581
1590
|
this.hotspotManifestUrl = null
|
|
1582
1591
|
this.hotspotPhase = "idle"
|
|
1583
1592
|
this.hotspotArtifactPercent = null
|
|
1593
|
+
this.hotspotArtifact = null
|
|
1584
1594
|
this.emitInternalChange()
|
|
1585
1595
|
}
|
|
1586
1596
|
|
|
@@ -13,7 +13,11 @@ import {resolveOtaReleaseVersion} from "./otaReleaseVersion"
|
|
|
13
13
|
* glasses reporting a bare "complete", which would otherwise present as false success. Keep this
|
|
14
14
|
* in lockstep with the ASG/recovery constant at release time.
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
// Mentra 3.0: matches OtaConstants and RecoveryConstants on the glasses.
|
|
17
|
+
// Coordinated releases guarantee higher supported ASG builds can downgrade; no source gate is needed.
|
|
18
|
+
// The shipped floor may increase, never decrease, and must stay aligned across all checkers.
|
|
19
|
+
// See asg_client/docs/mentra-live-spec.md#ota-and-updates.
|
|
20
|
+
export const DOWNGRADE_FLOOR_VERSION_CODE = 51518114
|
|
17
21
|
|
|
18
22
|
export interface VersionInfo {
|
|
19
23
|
versionCode: number
|
|
@@ -813,12 +813,6 @@ export class PhoneStreamCoordinator {
|
|
|
813
813
|
for (const reject of entry.hlsReadyRejecters) reject(pendingErr)
|
|
814
814
|
entry.hlsReadyResolvers = []
|
|
815
815
|
entry.hlsReadyRejecters = []
|
|
816
|
-
// Cloudflare DELETE is fire-and-forget — failing it doesn't undo the
|
|
817
|
-
// BLE stop we're about to do, and Cloudflare's idle-input scavenger
|
|
818
|
-
// will eventually clean up leaked inputs.
|
|
819
|
-
teardownManagedStream(entry.liveInputId).catch((err) => {
|
|
820
|
-
console.warn("[STREAM] teardownManagedStream failed:", err)
|
|
821
|
-
})
|
|
822
816
|
}
|
|
823
817
|
|
|
824
818
|
try {
|
|
@@ -833,6 +827,16 @@ export class PhoneStreamCoordinator {
|
|
|
833
827
|
// Only clear if we're still the active entry (defensive — runExclusive
|
|
834
828
|
// serializes us, so this should always be true).
|
|
835
829
|
if (this.current === entry) this.current = null
|
|
830
|
+
|
|
831
|
+
if (entry.kind === "managed") {
|
|
832
|
+
// Start remote cleanup only after the publisher has stopped, but do not
|
|
833
|
+
// hold the local transition lock on an unbounded network request. The
|
|
834
|
+
// runtime's durable cleanup queue owns retries if this request fails or
|
|
835
|
+
// the app exits before Cloudflare finishes finalizing the recording.
|
|
836
|
+
void teardownManagedStream(entry.liveInputId).catch((err) => {
|
|
837
|
+
console.warn("[STREAM] teardownManagedStream failed:", err)
|
|
838
|
+
})
|
|
839
|
+
}
|
|
836
840
|
}
|
|
837
841
|
}
|
|
838
842
|
}
|
package/src/stores/settings.ts
CHANGED
|
@@ -410,10 +410,10 @@ export const SETTINGS: Record<string, Setting> = {
|
|
|
410
410
|
saveOnServer: true,
|
|
411
411
|
persist: true,
|
|
412
412
|
},
|
|
413
|
-
// Mentra Live center-mic loudness / "Barrier" gate (cs_swit type 10).
|
|
413
|
+
// Mentra Live center-mic loudness / "Barrier" gate (cs_swit type 10). Opt-in.
|
|
414
414
|
loudness_gate_enabled: {
|
|
415
415
|
key: "loudness_gate_enabled",
|
|
416
|
-
defaultValue: () =>
|
|
416
|
+
defaultValue: () => false,
|
|
417
417
|
writable: true,
|
|
418
418
|
saveOnServer: true,
|
|
419
419
|
persist: true,
|
|
@@ -1048,6 +1048,21 @@ export const useSettingsStore = create<SettingsState>()(
|
|
|
1048
1048
|
}
|
|
1049
1049
|
}
|
|
1050
1050
|
|
|
1051
|
+
// Reset existing installs once; later user/app opt-ins remain available.
|
|
1052
|
+
const LOUDNESS_GATE_MIGRATION_KEY = "migration:loudness_gate_default_off_v1"
|
|
1053
|
+
const loudnessGateMigrationDone = storage.load<boolean>(LOUDNESS_GATE_MIGRATION_KEY)
|
|
1054
|
+
if (loudnessGateMigrationDone.is_error() || !loudnessGateMigrationDone.value) {
|
|
1055
|
+
// updateServer: true, matching the android_blur / camera_fov migrations. The flag is
|
|
1056
|
+
// inert until the Cloud V2 settings sync lands, but this setting is saveOnServer, so
|
|
1057
|
+
// the intent recorded here is the one that should carry over.
|
|
1058
|
+
const result = await get().setSetting(SETTINGS.loudness_gate_enabled.key, false, true)
|
|
1059
|
+
if (result.is_error()) {
|
|
1060
|
+
console.log("SETTINGS: loudness gate migration failed:", result.error)
|
|
1061
|
+
} else {
|
|
1062
|
+
storage.save(LOUDNESS_GATE_MIGRATION_KEY, true)
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1051
1066
|
const NOTIFICATION_LISTENER_MIGRATION_KEY = "migration:android_notification_listener_default_on_v1"
|
|
1052
1067
|
const notificationListenerMigrationDone = storage.load<boolean>(NOTIFICATION_LISTENER_MIGRATION_KEY)
|
|
1053
1068
|
if (notificationListenerMigrationDone.is_error() || !notificationListenerMigrationDone.value) {
|
package/src/utils/devLogging.ts
CHANGED
|
@@ -59,7 +59,6 @@ class LogRingBuffer {
|
|
|
59
59
|
/The action 'POP_TO_TOP' was not handled/,
|
|
60
60
|
/socket-0 binding/,
|
|
61
61
|
/socket-0 bound to/,
|
|
62
|
-
/Error while flushing PostHog/,
|
|
63
62
|
/backTitleFontFamily prop is not available on Android/,
|
|
64
63
|
/disableBackButtonMenu prop is not available on Android/,
|
|
65
64
|
/backTitleVisible prop is not available on Android/,
|