diff --git a/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/Bridge.swift b/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/Bridge.swift index 6c12733..e01c503 100644 --- a/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/Bridge.swift +++ b/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/Bridge.swift @@ -18,6 +18,7 @@ public protocol SessionBridge: Sendable { func react(chatId: Int64, messageId: Int64, reaction: String) async throws -> [Reaction] func pinnedMessages(chatId: Int64) async throws -> [Message] func copyPayload(chatId: Int64, messageId: Int64) async throws -> String + func setDraft(chatId: Int64, text: String) async throws func downloadPhoto(fileId: Int32) async throws -> DownloadedFile func downloadVoice(fileId: Int32) async throws -> DownloadedFile } @@ -204,6 +205,14 @@ public actor FakeSessionBridge: SessionBridge { return message.text } + public func setDraft(chatId: Int64, text: String) async throws { + let draft = Draft(chatId: chatId, text: text) + if let index = chats.firstIndex(where: { $0.id == chatId }) { + chats[index].draft = text.isEmpty ? nil : draft + } + events.append(.draftChanged(draft)) + } + public func downloadPhoto(fileId: Int32) async throws -> DownloadedFile { DownloadedFile(fileId: fileId, path: "/tmp/fake-photo-\(fileId).jpg") } diff --git a/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/UniFfiSessionBridge.swift b/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/UniFfiSessionBridge.swift index 437efd9..7a47406 100644 --- a/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/UniFfiSessionBridge.swift +++ b/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/UniFfiSessionBridge.swift @@ -105,6 +105,10 @@ public actor UniFfiSessionBridge: SessionBridge { try handle.copyPayload(chatId: chatId, messageId: messageId) } + public func setDraft(chatId: Int64, text: String) async throws { + try handle.setDraft(chatId: chatId, text: text) + } + public func downloadPhoto(fileId: Int32) async throws -> DownloadedFile { try Self.mapDownloadedFile(handle.downloadPhoto(fileId: fileId)) } diff --git a/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/ViewModels.swift b/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/ViewModels.swift index 48344d8..262915f 100644 --- a/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/ViewModels.swift +++ b/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/ViewModels.swift @@ -228,6 +228,15 @@ public final class ChatViewModel: ObservableObject { } } + public func saveDraft() async { + do { + try await bridge.setDraft(chatId: chat.id, text: composeText) + errorMessage = nil + } catch { + errorMessage = error.localizedDescription + } + } + private func replaceMessage(_ message: Message) { if let index = messages.firstIndex(where: { $0.id == message.id }) { messages[index] = message diff --git a/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSSmokeTests/main.swift b/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSSmokeTests/main.swift index 53fc820..4fc3ee6 100644 --- a/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSSmokeTests/main.swift +++ b/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSSmokeTests/main.swift @@ -100,6 +100,16 @@ struct TeleTuiIOSSmokeTests { await viewModel.copyPayload(for: viewModel.messages[0]) precondition(viewModel.copiedPayload == "Edited text") + viewModel.composeText = "Draft text" + await viewModel.saveDraft() + let draftEvents = try await bridge.pollEvents() + precondition(draftEvents.contains { event in + if case let .draftChanged(draft) = event { + return draft.chatId == chat.id && draft.text == "Draft text" + } + return false + }) + let photo = try await bridge.downloadPhoto(fileId: 100) let voice = try await bridge.downloadVoice(fileId: 200) precondition(photo.path == "/tmp/fake-photo-100.jpg") diff --git a/crates/tele-ios-ffi/src/lib.rs b/crates/tele-ios-ffi/src/lib.rs index a9246f1..7e160fd 100644 --- a/crates/tele-ios-ffi/src/lib.rs +++ b/crates/tele-ios-ffi/src/lib.rs @@ -1303,6 +1303,13 @@ mod tests { assert_eq!(pinned[0].text, "Hello from fake TDLib"); assert_eq!(session.download_photo(100).unwrap().path, "/tmp/fake-photo.jpg"); assert_eq!(session.download_voice(200).unwrap().path, "/tmp/fake-voice.ogg"); + session + .set_draft(chats[0].id, "Draft from Rust test".to_string()) + .unwrap(); + assert!(session.poll_events().iter().any(|event| matches!( + event, + IosEvent::DraftChanged { draft } if draft.text == "Draft from Rust test" + ))); let sent = session .send_message(chats[0].id, "Hi from Swift".to_string(), None) diff --git a/scripts/smoke-ios-ffi-swift.sh b/scripts/smoke-ios-ffi-swift.sh index 422c11b..61d6234 100755 --- a/scripts/smoke-ios-ffi-swift.sh +++ b/scripts/smoke-ios-ffi-swift.sh @@ -69,6 +69,15 @@ struct Smoke { let voice = try session.downloadVoice(fileId: 200) require(voice.path == "/tmp/fake-voice.ogg", "expected downloaded voice path") + try session.setDraft(chatId: chat.id, text: "Draft from Swift FFI") + let draftEvents = session.pollEvents() + require(draftEvents.contains { + if case let .draftChanged(draft) = $0 { + return draft.text == "Draft from Swift FFI" + } + return false + }, "expected draftChanged event") + let sent = try session.sendMessage(chatId: chat.id, text: "Hi from Swift FFI", replyToMessageId: nil) require(sent.text == "Hi from Swift FFI", "expected sent message text")