mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-08 11:02:26 +00:00
fix(ios): gate demo mode live gateway actions
This commit is contained in:
@@ -332,11 +332,14 @@ struct ConfigPatchParams: Encodable {
|
||||
}
|
||||
|
||||
enum SkillMutationError: LocalizedError {
|
||||
case liveGatewayUnavailable
|
||||
case missingConfigHash
|
||||
case invalidPatchPayload
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .liveGatewayUnavailable:
|
||||
"Connect a live gateway to edit agent skills."
|
||||
case .missingConfigHash:
|
||||
"Config hash missing; refresh and retry."
|
||||
case .invalidPatchPayload:
|
||||
|
||||
@@ -99,14 +99,14 @@ extension AgentProTab {
|
||||
} label: {
|
||||
Label("Run", systemImage: "play.fill")
|
||||
}
|
||||
.disabled(busy || !self.gatewayConnected)
|
||||
.disabled(busy || !self.liveGatewayConnected)
|
||||
|
||||
Button {
|
||||
Task { await self.setCronJob(job, enabled: !job.enabled) }
|
||||
} label: {
|
||||
Label(job.enabled ? "Pause" : "Enable", systemImage: job.enabled ? "pause.fill" : "checkmark")
|
||||
}
|
||||
.disabled(busy || !self.gatewayConnected)
|
||||
.disabled(busy || !self.liveGatewayConnected)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.controlSize(.mini)
|
||||
@@ -149,7 +149,7 @@ extension AgentProTab {
|
||||
success: String,
|
||||
action: () async throws -> Void) async
|
||||
{
|
||||
guard self.gatewayConnected else { return }
|
||||
guard self.liveGatewayConnected else { return }
|
||||
self.cronActionBusyIDs.insert(job.id)
|
||||
self.cronActionStatusText = nil
|
||||
defer { self.cronActionBusyIDs.remove(job.id) }
|
||||
|
||||
@@ -117,7 +117,7 @@ extension AgentProTab {
|
||||
@MainActor
|
||||
func refreshOverview(force: Bool) async {
|
||||
guard self.scenePhase == .active else { return }
|
||||
guard self.appModel.isOperatorGatewayConnected else {
|
||||
guard self.liveGatewayConnected else {
|
||||
self.overview = nil
|
||||
self.overviewErrorText = nil
|
||||
self.overviewLoading = false
|
||||
|
||||
@@ -542,6 +542,12 @@ extension AgentProTab {
|
||||
GatewayStatusBuilder.build(appModel: self.appModel) == .connected
|
||||
}
|
||||
|
||||
var liveGatewayConnected: Bool {
|
||||
!self.appModel.isAppleReviewDemoModeEnabled &&
|
||||
self.gatewayConnected &&
|
||||
self.appModel.isOperatorGatewayConnected
|
||||
}
|
||||
|
||||
private var searchFieldFill: Color {
|
||||
self.colorScheme == .dark ? Color.white.opacity(0.045) : Color.white.opacity(0.78)
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ extension AgentProTab {
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.controlSize(.small)
|
||||
.disabled(self.clawHubLoading || !self.gatewayConnected)
|
||||
.disabled(self.clawHubLoading || !self.liveGatewayConnected)
|
||||
.accessibilityLabel("Search ClawHub")
|
||||
}
|
||||
|
||||
@@ -212,6 +212,7 @@ extension AgentProTab {
|
||||
}
|
||||
|
||||
var skillPolicySummary: String {
|
||||
if self.appModel.isAppleReviewDemoModeEnabled { return "Demo mode keeps live skill changes disabled." }
|
||||
guard self.gatewayConnected else { return "Connect a gateway to edit skills." }
|
||||
guard let filter = self.agentSkillFilter else {
|
||||
return "All available skills are allowed for this agent."
|
||||
@@ -601,7 +602,7 @@ extension AgentProTab {
|
||||
|
||||
@MainActor
|
||||
func patchAgentSkills(_ skills: [String]?, busyKey: String) async {
|
||||
guard self.gatewayConnected else { return }
|
||||
guard self.liveGatewayConnected else { return }
|
||||
self.skillMutationBusyKeys.insert(busyKey)
|
||||
self.skillMutationErrorText = nil
|
||||
self.skillMutationStatusText = nil
|
||||
@@ -676,7 +677,7 @@ extension AgentProTab {
|
||||
|
||||
@MainActor
|
||||
func installClawHubSkill(_ result: ClawHubSearchResultLite) async {
|
||||
guard self.gatewayConnected else { return }
|
||||
guard self.liveGatewayConnected else { return }
|
||||
self.clawHubInstallSlug = result.slug
|
||||
self.clawHubErrorText = nil
|
||||
defer { self.clawHubInstallSlug = nil }
|
||||
@@ -692,7 +693,7 @@ extension AgentProTab {
|
||||
|
||||
@MainActor
|
||||
func searchClawHubSkills() async {
|
||||
guard self.gatewayConnected else { return }
|
||||
guard self.liveGatewayConnected else { return }
|
||||
self.clawHubLoading = true
|
||||
self.clawHubErrorText = nil
|
||||
defer { self.clawHubLoading = false }
|
||||
@@ -711,6 +712,7 @@ extension AgentProTab {
|
||||
_ skill: SkillStatusEntryLite,
|
||||
action: () async throws -> String) async
|
||||
{
|
||||
guard self.liveGatewayConnected else { return }
|
||||
let key = skill.effectiveSkillKey
|
||||
self.skillConfigBusyKeys.insert(key)
|
||||
self.skillConfigMessages[key] = nil
|
||||
@@ -733,6 +735,9 @@ extension AgentProTab {
|
||||
params: some Encodable,
|
||||
timeoutSeconds: Int) async throws -> Data
|
||||
{
|
||||
guard self.liveGatewayConnected else {
|
||||
throw SkillMutationError.liveGatewayUnavailable
|
||||
}
|
||||
let data = try JSONEncoder().encode(params)
|
||||
guard let json = String(data: data, encoding: .utf8) else {
|
||||
throw SkillMutationError.invalidPatchPayload
|
||||
@@ -744,6 +749,9 @@ extension AgentProTab {
|
||||
}
|
||||
|
||||
func requestConfigSnapshot() async throws -> ConfigSnapshotLite {
|
||||
guard self.liveGatewayConnected else {
|
||||
throw SkillMutationError.liveGatewayUnavailable
|
||||
}
|
||||
let data = try await self.appModel.operatorSession.request(
|
||||
method: "config.get",
|
||||
paramsJSON: "{}",
|
||||
|
||||
@@ -42,9 +42,9 @@ extension SettingsProTab {
|
||||
self.diagnosticCheckRow(
|
||||
icon: "antenna.radiowaves.left.and.right",
|
||||
title: "Gateway Link",
|
||||
detail: self.appModel.gatewayDisplayStatusText,
|
||||
value: self.gatewayConnected ? "online" : "offline",
|
||||
color: self.gatewayConnected ? OpenClawBrand.ok : .secondary)
|
||||
detail: self.gatewayStatusDetail,
|
||||
value: self.gatewayStatusValue,
|
||||
color: self.gatewayStatusColor)
|
||||
Divider().padding(.leading, 60)
|
||||
self.diagnosticCheckRow(
|
||||
icon: "dot.radiowaves.left.and.right",
|
||||
@@ -56,9 +56,9 @@ extension SettingsProTab {
|
||||
self.diagnosticCheckRow(
|
||||
icon: "waveform",
|
||||
title: "Talk Config",
|
||||
detail: self.appModel.talkMode.gatewayTalkTransportLabel,
|
||||
value: self.appModel.talkMode.gatewayTalkConfigLoaded ? "loaded" : "missing",
|
||||
color: self.appModel.talkMode.gatewayTalkConfigLoaded ? OpenClawBrand.ok : .secondary)
|
||||
detail: self.gatewayTalkConfigDetail,
|
||||
value: self.gatewayTalkConfigValue,
|
||||
color: self.gatewayTalkConfigColor)
|
||||
Divider().padding(.leading, 60)
|
||||
self.diagnosticCheckRow(
|
||||
icon: "bell",
|
||||
@@ -132,6 +132,7 @@ extension SettingsProTab {
|
||||
}
|
||||
|
||||
func reconnectGateway() async {
|
||||
guard !self.appModel.isAppleReviewDemoModeEnabled else { return }
|
||||
guard !self.isReconnectingGateway else { return }
|
||||
self.isReconnectingGateway = true
|
||||
defer { self.isReconnectingGateway = false }
|
||||
@@ -153,16 +154,18 @@ extension SettingsProTab {
|
||||
self.isRefreshingGateway = true
|
||||
defer { self.isRefreshingGateway = false }
|
||||
|
||||
self.gatewayController.refreshActiveGatewayRegistrationFromSettings()
|
||||
self.gatewayController.restartDiscovery()
|
||||
await self.appModel.refreshGatewayOverviewIfConnected()
|
||||
if !self.appModel.isAppleReviewDemoModeEnabled {
|
||||
self.gatewayController.refreshActiveGatewayRegistrationFromSettings()
|
||||
self.gatewayController.restartDiscovery()
|
||||
await self.appModel.refreshGatewayOverviewIfConnected()
|
||||
}
|
||||
let notificationSettings = await UNUserNotificationCenter.current().notificationSettings()
|
||||
self.applyNotificationStatus(notificationSettings.authorizationStatus)
|
||||
|
||||
let issueCount = SettingsDiagnostics.issueCount(
|
||||
gatewayConnected: self.gatewayConnected,
|
||||
gatewayConnected: self.gatewayDiagnosticConnected,
|
||||
discoveredGatewayCount: self.gatewayController.gateways.count,
|
||||
talkConfigLoaded: self.appModel.talkMode.gatewayTalkConfigLoaded,
|
||||
talkConfigLoaded: self.gatewayDiagnosticTalkConfigLoaded,
|
||||
notificationStatusText: self.notificationStatusText)
|
||||
self.diagnosticsIssueCount = issueCount
|
||||
self.diagnosticsLastRunText = SettingsDiagnostics.timestamp(Date())
|
||||
@@ -620,7 +623,53 @@ extension SettingsProTab {
|
||||
}
|
||||
|
||||
var gatewayConnected: Bool {
|
||||
GatewayStatusBuilder.build(appModel: self.appModel) == .connected
|
||||
!self.appModel.isAppleReviewDemoModeEnabled &&
|
||||
GatewayStatusBuilder.build(appModel: self.appModel) == .connected
|
||||
}
|
||||
|
||||
var gatewayStatusDetail: String {
|
||||
if self.appModel.isAppleReviewDemoModeEnabled { return "Apple Review demo mode" }
|
||||
return self.gatewayConnected ? "Connected" : self.appModel.gatewayDisplayStatusText
|
||||
}
|
||||
|
||||
var gatewayStatusValue: String {
|
||||
if self.appModel.isAppleReviewDemoModeEnabled { return "demo" }
|
||||
return self.gatewayConnected ? "online" : "offline"
|
||||
}
|
||||
|
||||
var gatewayStatusColor: Color {
|
||||
if self.appModel.isAppleReviewDemoModeEnabled { return OpenClawBrand.accent }
|
||||
return self.gatewayConnected ? OpenClawBrand.ok : .secondary
|
||||
}
|
||||
|
||||
var gatewayDiagnosticConnected: Bool {
|
||||
self.appModel.isAppleReviewDemoModeEnabled || self.gatewayConnected
|
||||
}
|
||||
|
||||
var gatewayDiagnosticTalkConfigLoaded: Bool {
|
||||
self.appModel.isAppleReviewDemoModeEnabled || self.appModel.talkMode.gatewayTalkConfigLoaded
|
||||
}
|
||||
|
||||
var approvalEmptyDetail: String {
|
||||
if self.appModel.isAppleReviewDemoModeEnabled {
|
||||
return "Live gateway requests are disabled in demo mode."
|
||||
}
|
||||
return self.gatewayConnected ? "Gateway requests will appear here." : "Connect to the gateway."
|
||||
}
|
||||
|
||||
var gatewayTalkConfigDetail: String {
|
||||
if self.appModel.isAppleReviewDemoModeEnabled { return "Demo mode only" }
|
||||
return self.appModel.talkMode.gatewayTalkTransportLabel
|
||||
}
|
||||
|
||||
var gatewayTalkConfigValue: String {
|
||||
if self.appModel.isAppleReviewDemoModeEnabled { return "demo" }
|
||||
return self.appModel.talkMode.gatewayTalkConfigLoaded ? "loaded" : "missing"
|
||||
}
|
||||
|
||||
var gatewayTalkConfigColor: Color {
|
||||
if self.appModel.isAppleReviewDemoModeEnabled { return .secondary }
|
||||
return self.appModel.talkMode.gatewayTalkConfigLoaded ? OpenClawBrand.ok : .secondary
|
||||
}
|
||||
|
||||
var gatewayAddress: String {
|
||||
@@ -679,6 +728,7 @@ extension SettingsProTab {
|
||||
}
|
||||
|
||||
var diagnosticsHealthValue: String {
|
||||
if self.appModel.isAppleReviewDemoModeEnabled { return "demo" }
|
||||
if self.gatewayConnected { return "ready" }
|
||||
if self.gatewayController.gateways.isEmpty { return "check" }
|
||||
return "partial"
|
||||
|
||||
@@ -60,14 +60,14 @@ extension SettingsProTab {
|
||||
HStack(spacing: 12) {
|
||||
ProIconBadge(
|
||||
systemName: "antenna.radiowaves.left.and.right",
|
||||
color: self.gatewayConnected ? OpenClawBrand.ok : .secondary)
|
||||
color: self.gatewayStatusColor)
|
||||
|
||||
VStack(alignment: .leading, spacing: 3) {
|
||||
Text("Connection")
|
||||
.font(.subheadline.weight(.semibold))
|
||||
Text(self.gatewayConnected ? "Connected" : self.appModel.gatewayDisplayStatusText)
|
||||
Text(self.gatewayStatusDetail)
|
||||
.font(.caption)
|
||||
.foregroundStyle(self.gatewayConnected ? OpenClawBrand.ok : .secondary)
|
||||
.foregroundStyle(self.gatewayStatusColor)
|
||||
}
|
||||
|
||||
Spacer(minLength: 8)
|
||||
@@ -100,7 +100,8 @@ extension SettingsProTab {
|
||||
title: "Reconnect",
|
||||
icon: "arrow.triangle.2.circlepath",
|
||||
color: OpenClawBrand.warn,
|
||||
isBusy: self.isReconnectingGateway)
|
||||
isBusy: self.isReconnectingGateway,
|
||||
isDisabled: self.appModel.isAppleReviewDemoModeEnabled)
|
||||
{
|
||||
Task { await self.reconnectGateway() }
|
||||
}
|
||||
@@ -234,9 +235,9 @@ extension SettingsProTab {
|
||||
self.detailStatusCard(
|
||||
icon: "antenna.radiowaves.left.and.right",
|
||||
title: "Gateway",
|
||||
detail: self.gatewayConnected ? "Connected" : self.appModel.gatewayDisplayStatusText,
|
||||
value: self.gatewayConnected ? "online" : "offline",
|
||||
color: self.gatewayConnected ? OpenClawBrand.ok : .secondary)
|
||||
detail: self.gatewayStatusDetail,
|
||||
value: self.gatewayStatusValue,
|
||||
color: self.gatewayStatusColor)
|
||||
|
||||
self.detailListCard {
|
||||
self.detailRow("Address", value: self.gatewayAddress)
|
||||
@@ -335,8 +336,7 @@ extension SettingsProTab {
|
||||
VStack(alignment: .leading, spacing: 3) {
|
||||
Text("No approvals waiting")
|
||||
.font(.subheadline.weight(.semibold))
|
||||
Text(self
|
||||
.gatewayConnected ? "Gateway requests will appear here." : "Connect to the gateway.")
|
||||
Text(self.approvalEmptyDetail)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.lineLimit(2)
|
||||
@@ -390,7 +390,7 @@ extension SettingsProTab {
|
||||
title: "Health Check",
|
||||
detail: "Run app, permission, and gateway-adjacent checks without editing setup.",
|
||||
value: self.diagnosticsHealthValue,
|
||||
color: self.gatewayConnected ? OpenClawBrand.ok : OpenClawBrand.warn)
|
||||
color: self.gatewayDiagnosticConnected ? OpenClawBrand.ok : OpenClawBrand.warn)
|
||||
|
||||
ProCard(radius: SettingsLayout.cardRadius) {
|
||||
self.gatewayActionButton(
|
||||
@@ -504,6 +504,7 @@ extension SettingsProTab {
|
||||
icon: String,
|
||||
color: Color,
|
||||
isBusy: Bool,
|
||||
isDisabled: Bool = false,
|
||||
action: @escaping () -> Void) -> some View
|
||||
{
|
||||
Button(action: action) {
|
||||
@@ -525,7 +526,7 @@ extension SettingsProTab {
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(isBusy)
|
||||
.disabled(isBusy || isDisabled)
|
||||
}
|
||||
|
||||
func toggleCard(
|
||||
@@ -764,8 +765,13 @@ extension SettingsProTab {
|
||||
self.appModel.setVoiceWakeEnabled(enabled)
|
||||
}
|
||||
self.settingsToggle("Talk Mode", isOn: self.$talkEnabled) { enabled in
|
||||
guard !self.appModel.isAppleReviewDemoModeEnabled else {
|
||||
self.talkEnabled = false
|
||||
return
|
||||
}
|
||||
self.appModel.setTalkEnabled(enabled)
|
||||
}
|
||||
.disabled(self.appModel.isAppleReviewDemoModeEnabled)
|
||||
Picker("Speech Language", selection: self.$talkSpeechLocale) {
|
||||
ForEach(TalkSpeechLocale.supportedOptions()) { option in
|
||||
Text(option.label).tag(option.id)
|
||||
|
||||
@@ -13,6 +13,7 @@ struct TalkProTab: View {
|
||||
private var state: TalkProState {
|
||||
TalkProState(
|
||||
gatewayConnected: self.gatewayConnected,
|
||||
isDemoMode: self.appModel.isAppleReviewDemoModeEnabled,
|
||||
isEnabled: self.appModel.talkMode.isEnabled || self.talkEnabled,
|
||||
statusText: self.appModel.talkMode.statusText,
|
||||
isConfigLoaded: self.appModel.talkMode.gatewayTalkConfigLoaded,
|
||||
@@ -282,7 +283,8 @@ struct TalkProTab: View {
|
||||
}
|
||||
|
||||
private var gatewayConnected: Bool {
|
||||
GatewayStatusBuilder.build(appModel: self.appModel) == .connected
|
||||
!self.appModel.isAppleReviewDemoModeEnabled &&
|
||||
GatewayStatusBuilder.build(appModel: self.appModel) == .connected
|
||||
}
|
||||
|
||||
private var headerSubtitle: String {
|
||||
@@ -296,6 +298,7 @@ struct TalkProTab: View {
|
||||
private var heroSubtitle: String {
|
||||
if self.state
|
||||
.prefersPermissionCopy { return "Gateway approval is required before this phone can capture voice." }
|
||||
if self.appModel.isAppleReviewDemoModeEnabled { return "Voice is disabled in Apple Review demo mode." }
|
||||
if !self.gatewayConnected { return "Connect to your gateway to start a voice conversation." }
|
||||
if !self.appModel.talkMode.gatewayTalkConfigLoaded {
|
||||
return "Open Voice settings after the gateway loads Talk configuration."
|
||||
@@ -327,10 +330,14 @@ struct TalkProTab: View {
|
||||
}
|
||||
|
||||
private func alignPersistedTalkState() {
|
||||
if self.appModel.talkMode.gatewayTalkPermissionState.requiresTalkPermissionAction,
|
||||
if self.appModel.isAppleReviewDemoModeEnabled,
|
||||
self.talkEnabled || self.appModel.talkMode.isEnabled
|
||||
{
|
||||
self.stopTalk()
|
||||
} else if self.appModel.talkMode.gatewayTalkPermissionState.requiresTalkPermissionAction,
|
||||
self.talkEnabled || self.appModel.talkMode.isEnabled
|
||||
{
|
||||
self.stopTalk()
|
||||
} else if self.talkEnabled != self.appModel.talkMode.isEnabled {
|
||||
self.appModel.setTalkEnabled(self.talkEnabled)
|
||||
}
|
||||
@@ -362,6 +369,7 @@ struct TalkProTab: View {
|
||||
}
|
||||
|
||||
private func startTalk() {
|
||||
guard !self.appModel.isAppleReviewDemoModeEnabled else { return }
|
||||
self.talkEnabled = true
|
||||
self.appModel.talkMode.updateMainSessionKey(self.appModel.chatSessionKey)
|
||||
self.appModel.setTalkEnabled(true)
|
||||
@@ -391,6 +399,7 @@ enum TalkProWaveformMode: Equatable {
|
||||
|
||||
struct TalkProState: Equatable {
|
||||
let gatewayConnected: Bool
|
||||
let isDemoMode: Bool
|
||||
let isEnabled: Bool
|
||||
let statusText: String
|
||||
let isConfigLoaded: Bool
|
||||
@@ -404,6 +413,7 @@ struct TalkProState: Equatable {
|
||||
}
|
||||
|
||||
var title: String {
|
||||
if self.isDemoMode { return "Demo mode only" }
|
||||
if !self.gatewayConnected { return "Gateway offline" }
|
||||
switch self.permissionState {
|
||||
case .missingScope, .requestFailed:
|
||||
@@ -429,6 +439,7 @@ struct TalkProState: Equatable {
|
||||
}
|
||||
|
||||
var chipText: String {
|
||||
if self.isDemoMode { return "Demo" }
|
||||
if !self.gatewayConnected { return "Offline" }
|
||||
switch self.permissionState {
|
||||
case .missingScope, .requestFailed:
|
||||
@@ -450,6 +461,7 @@ struct TalkProState: Equatable {
|
||||
}
|
||||
|
||||
var icon: String {
|
||||
if self.isDemoMode { return "waveform.slash" }
|
||||
if !self.gatewayConnected { return "wifi.slash" }
|
||||
switch self.permissionState {
|
||||
case .missingScope, .requestFailed:
|
||||
@@ -472,6 +484,7 @@ struct TalkProState: Equatable {
|
||||
}
|
||||
|
||||
var color: Color {
|
||||
if self.isDemoMode { return .secondary }
|
||||
if !self.gatewayConnected { return .secondary }
|
||||
switch self.permissionState {
|
||||
case .requestFailed, .loadFailed:
|
||||
@@ -485,6 +498,7 @@ struct TalkProState: Equatable {
|
||||
}
|
||||
|
||||
var primaryAction: TalkProPrimaryAction {
|
||||
if self.isDemoMode { return .waiting }
|
||||
if !self.gatewayConnected { return .openSettings }
|
||||
switch self.permissionState {
|
||||
case .missingScope, .requestFailed:
|
||||
@@ -504,7 +518,7 @@ struct TalkProState: Equatable {
|
||||
case .stop: "Stop Talk"
|
||||
case .enablePermission: "Enable Talk"
|
||||
case .openSettings: self.gatewayConnected ? "Open Voice Settings" : "Open Gateway Settings"
|
||||
case .waiting: "Waiting for Approval"
|
||||
case .waiting: self.isDemoMode ? "Demo Mode Only" : "Waiting for Approval"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,7 +528,7 @@ struct TalkProState: Equatable {
|
||||
case .stop: "stop.fill"
|
||||
case .enablePermission: "key.fill"
|
||||
case .openSettings: "gearshape.fill"
|
||||
case .waiting: "hourglass"
|
||||
case .waiting: self.isDemoMode ? "lock.fill" : "hourglass"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,6 +556,7 @@ struct TalkProState: Equatable {
|
||||
}
|
||||
|
||||
func waveformMode(micLevel: Double) -> TalkProWaveformMode {
|
||||
if self.isDemoMode { return .still }
|
||||
if !self.gatewayConnected { return .still }
|
||||
switch self.permissionState {
|
||||
case .requestingUpgrade, .upgradeRequested:
|
||||
|
||||
@@ -2790,6 +2790,8 @@ extension NodeAppModel {
|
||||
self.activeGatewayConnectConfig = nil
|
||||
self.gatewayConnected = true
|
||||
self.setOperatorConnected(false)
|
||||
UserDefaults.standard.set(false, forKey: "talk.enabled")
|
||||
UserDefaults.standard.set(false, forKey: "talk.background.enabled")
|
||||
self.talkMode.updateGatewayConnected(false)
|
||||
self.talkMode.setEnabled(false)
|
||||
self.talkMode.statusText = "Demo mode only"
|
||||
|
||||
@@ -5,6 +5,7 @@ import Testing
|
||||
@Test func disabledTalkWithoutLoadedConfigCanStartAndRetryLoad() {
|
||||
let state = TalkProState(
|
||||
gatewayConnected: true,
|
||||
isDemoMode: false,
|
||||
isEnabled: false,
|
||||
statusText: "Offline",
|
||||
isConfigLoaded: false,
|
||||
@@ -23,6 +24,7 @@ import Testing
|
||||
@Test func enabledTalkWithoutLoadedConfigCanBeStopped() {
|
||||
let state = TalkProState(
|
||||
gatewayConnected: true,
|
||||
isDemoMode: false,
|
||||
isEnabled: true,
|
||||
statusText: "Offline",
|
||||
isConfigLoaded: false,
|
||||
@@ -41,6 +43,7 @@ import Testing
|
||||
@Test func enabledTalkWithLoadedConfigCanBeStopped() {
|
||||
let state = TalkProState(
|
||||
gatewayConnected: true,
|
||||
isDemoMode: false,
|
||||
isEnabled: true,
|
||||
statusText: "Ready",
|
||||
isConfigLoaded: true,
|
||||
@@ -57,6 +60,7 @@ import Testing
|
||||
@Test func missingScopeTakesPriorityOverUnloadedConfig() {
|
||||
let state = TalkProState(
|
||||
gatewayConnected: true,
|
||||
isDemoMode: false,
|
||||
isEnabled: false,
|
||||
statusText: "Offline",
|
||||
isConfigLoaded: false,
|
||||
@@ -70,4 +74,25 @@ import Testing
|
||||
#expect(state.primaryAction == .enablePermission)
|
||||
#expect(state.primaryButtonTitle == "Enable Talk")
|
||||
}
|
||||
|
||||
@Test func demoModeKeepsTalkDisabled() {
|
||||
let state = TalkProState(
|
||||
gatewayConnected: true,
|
||||
isDemoMode: true,
|
||||
isEnabled: true,
|
||||
statusText: "Ready",
|
||||
isConfigLoaded: true,
|
||||
isListening: true,
|
||||
isSpeaking: true,
|
||||
isUserSpeechDetected: true,
|
||||
permissionState: .ready)
|
||||
|
||||
#expect(state.title == "Demo mode only")
|
||||
#expect(state.chipText == "Demo")
|
||||
#expect(state.icon == "waveform.slash")
|
||||
#expect(state.primaryAction == .waiting)
|
||||
#expect(state.primaryButtonTitle == "Demo Mode Only")
|
||||
#expect(state.primaryButtonIcon == "lock.fill")
|
||||
#expect(state.waveformMode(micLevel: 0.8) == .still)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user