diff --git a/mobile/ios/NativeControlTests/MeAgentAccessibilityTests.swift b/mobile/ios/NativeControlTests/MeAgentAccessibilityTests.swift index a8fdd8440a..a21e4260a5 100644 --- a/mobile/ios/NativeControlTests/MeAgentAccessibilityTests.swift +++ b/mobile/ios/NativeControlTests/MeAgentAccessibilityTests.swift @@ -6,6 +6,7 @@ struct MeAgentAccessibilityTests { @MainActor static func main() { for locked in [false, true] { for empty in [false, true] { + var submitted: [String: String]? let controller = NativeProfileTextEditorViewController( title: "New chat", initialValue: "Chat", placeholder: "Chat name", multiline: false, pageBackgroundColor: .systemGroupedBackground, @@ -14,8 +15,8 @@ struct MeAgentAccessibilityTests { channelDescription: nil, originalName: nil, originalDescription: nil, canEditDetails: true, canEditCanvas: false, canvasContent: "", canvasLoaded: false, - chatOptions: ["presentationId": "test", "assistantsLoading": empty, "fieldsLocked": locked, "assistants": empty ? [] : [["id": "agent", "name": "Agent"]]], - onCancel: {}, onSet: { _ in } + chatOptions: ["selectedAssistant": "agent", "presentationId": "test", "assistantsLoading": empty, "fieldsLocked": locked, "assistants": empty ? [] : [["id": "agent", "name": "Agent"]]], + onCancel: {}, onSet: { submitted = $0 as? [String: String] } ) controller.loadViewIfNeeded() precondition(controller.numberOfSections(in: controller.tableView) == 3) @@ -31,6 +32,11 @@ struct MeAgentAccessibilityTests { precondition(cell.accessibilityTraits.contains(.notEnabled) == disabled) precondition(cell.accessibilityTraits.contains(.button) == !disabled) precondition((cell.selectionStyle == .none) == disabled) + let canSubmit = locked || !empty + precondition(controller.navigationItem.rightBarButtonItem?.isEnabled == canSubmit) + // Invoke the action even when disabled to check its independent gate. + _ = controller.perform(NSSelectorFromString("setTapped")) + precondition(submitted?["assistant"] == (canSubmit ? "agent" : nil)) if empty && !locked { controller.tableView.frame = CGRect(x: 0, y: 0, width: 390, height: 844) controller.tableView.reloadData() @@ -47,6 +53,14 @@ struct MeAgentAccessibilityTests { precondition(!updated.accessibilityTraits.contains(.notEnabled)) precondition(controller.tableView(controller.tableView, titleForFooterInSection: 2) == nil) } + submitted = nil + controller.updateChatAssistants([ + "presentationId": "test", + "assistants": empty ? [] : [["id": "agent", "name": "Agent"]], + ]) + precondition(controller.navigationItem.rightBarButtonItem?.isEnabled == canSubmit) + _ = controller.perform(NSSelectorFromString("setTapped")) + precondition(submitted?["assistant"] == (canSubmit ? "agent" : nil)) } } avatarTests() diff --git a/mobile/ios/Runner/NativeProfileTextEditor.swift b/mobile/ios/Runner/NativeProfileTextEditor.swift index 0b62fec004..317f64563b 100644 --- a/mobile/ios/Runner/NativeProfileTextEditor.swift +++ b/mobile/ios/Runner/NativeProfileTextEditor.swift @@ -449,13 +449,20 @@ private final class NativeProfileTextEditorViewController: updateNavigation() } + // A durable retry keeps its saved recipient even if discovery hides it. + // Flutter revalidates ownership before any creation writes. + private var canSubmitAssistant: Bool { + guard let selectedAssistant else { return false } + return fieldsLocked || assistants.contains { $0["id"] == selectedAssistant } + } + private func updateNavigation() { navigationItem.rightBarButtonItem?.isEnabled = canEditDetails && hasUnsavedChanges && (!isChannelEditor || !canonicalChannelName.isEmpty) && (!isChatEditor || ( !currentValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && currentValue.count <= 120 - && (isProjectEditor || assistants.contains { $0["id"] == selectedAssistant }) + && (isProjectEditor || canSubmitAssistant) )) navigationController?.isModalInPresentation = hasUnsavedChanges placeholderLabel.isHidden = !textView.text.isEmpty @@ -497,7 +504,7 @@ private final class NativeProfileTextEditorViewController: onSet(name) } else if isChatEditor { guard let selectedAssistant, - assistants.contains(where: { $0["id"] == selectedAssistant }), + canSubmitAssistant, !currentValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, currentValue.count <= 120 else { return } onSet(["name": currentValue, "assistant": selectedAssistant, "project": selectedProject ?? ""])