From 4fd2a18ed9020df27ea1fb593050de21e78fda9b Mon Sep 17 00:00:00 2001 From: Mikhail Kilin Date: Thu, 21 May 2026 00:23:33 +0300 Subject: [PATCH] Expose pinned messages through iOS FFI --- .../TeleTuiIOSCore/UniFfiSessionBridge.swift | 3 +- crates/tele-core/src/session.rs | 25 ++++++++++++++++ .../src/test_support/fake_tdclient_impl.rs | 8 ++++- crates/tele-ios-ffi/src/lib.rs | 30 +++++++++++++++++-- scripts/smoke-ios-ffi-swift.sh | 3 ++ 5 files changed, 64 insertions(+), 5 deletions(-) diff --git a/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/UniFfiSessionBridge.swift b/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/UniFfiSessionBridge.swift index 3fe7793..3508c29 100644 --- a/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/UniFfiSessionBridge.swift +++ b/apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/UniFfiSessionBridge.swift @@ -97,7 +97,8 @@ public actor UniFfiSessionBridge: SessionBridge { } public func pinnedMessages(chatId: Int64) async throws -> [Message] { - [] + try handle.pinnedMessages(chatId: chatId) + .map { Self.mapMessage($0, chatId: chatId) } } public func copyPayload(chatId: Int64, messageId: Int64) async throws -> String { diff --git a/crates/tele-core/src/session.rs b/crates/tele-core/src/session.rs index 03fe8bd..03dae3d 100644 --- a/crates/tele-core/src/session.rs +++ b/crates/tele-core/src/session.rs @@ -218,6 +218,13 @@ impl CoreSession { .collect()) } + pub async fn pinned_messages(&mut self, chat_id: ChatId) -> Result, String> { + self.client + .get_pinned_messages(chat_id) + .await + .map(|messages| messages.iter().map(CoreMessage::from).collect()) + } + pub async fn copy_payload( &mut self, chat_id: ChatId, @@ -907,6 +914,24 @@ mod tests { ); } + #[tokio::test] + async fn pinned_messages_are_mapped_for_native_clients() { + let chat_id = ChatId::new(42); + let pinned = MessageBuilder::new(MessageId::new(10)) + .sender_name("Alice") + .text("Pinned") + .build(); + let mut client = FakeTdClient::new(); + client.set_current_pinned_message(Some(pinned)); + let mut session = CoreSession::new(client); + + let pinned = session.pinned_messages(chat_id).await.unwrap(); + + assert_eq!(pinned.len(), 1); + assert_eq!(pinned[0].id, MessageId::new(10)); + assert_eq!(pinned[0].text, "Pinned"); + } + #[tokio::test] async fn facade_delegates_auth_forward_reactions_and_downloads() { let chat_id = ChatId::new(42); diff --git a/crates/tele-core/src/test_support/fake_tdclient_impl.rs b/crates/tele-core/src/test_support/fake_tdclient_impl.rs index 93d7afa..a0a4d6e 100644 --- a/crates/tele-core/src/test_support/fake_tdclient_impl.rs +++ b/crates/tele-core/src/test_support/fake_tdclient_impl.rs @@ -114,7 +114,13 @@ impl MessageClient for FakeTdClient { } async fn get_pinned_messages(&mut self, _chat_id: ChatId) -> Result, String> { - Ok(vec![]) + Ok(self + .current_pinned_message + .lock() + .unwrap() + .clone() + .into_iter() + .collect()) } async fn load_current_pinned_message(&mut self, _chat_id: ChatId) {} diff --git a/crates/tele-ios-ffi/src/lib.rs b/crates/tele-ios-ffi/src/lib.rs index cc25006..c67d2ec 100644 --- a/crates/tele-ios-ffi/src/lib.rs +++ b/crates/tele-ios-ffi/src/lib.rs @@ -591,6 +591,14 @@ impl SessionHandle { .map_err(IosFfiError::from) } + pub fn pinned_messages(&self, chat_id: i64) -> Result, IosFfiError> { + let mut session = self.session.lock().expect("session mutex poisoned"); + self.runtime + .block_on(session.pinned_messages(ChatId::new(chat_id))) + .map(|messages| messages.into_iter().map(IosMessage::from).collect()) + .map_err(IosFfiError::from) + } + pub fn open_profile(&self, chat_id: i64) -> Result { let mut session = self.session.lock().expect("session mutex poisoned"); self.runtime @@ -766,13 +774,15 @@ fn seeded_fake_client() -> FakeTdClient { online_status: Some("online".to_string()), }; - FakeTdClient::new() + let mut client = FakeTdClient::new() .with_chat(chat.clone()) - .with_message(chat.id.as_i64(), message) + .with_message(chat.id.as_i64(), message.clone()) .with_profile(chat.id.as_i64(), profile) .with_network_state(NetworkState::Ready) .with_downloaded_file(100, "/tmp/fake-photo.jpg") - .with_downloaded_file(200, "/tmp/fake-voice.ogg") + .with_downloaded_file(200, "/tmp/fake-voice.ogg"); + client.set_current_pinned_message(Some(message)); + client } #[derive(uniffi::Object)] @@ -1002,6 +1012,18 @@ impl SessionHandle { .collect()) } + pub fn pinned_messages(&self, chat_id: i64) -> Result, IosFfiError> { + let state = self.state.lock().expect("session mutex poisoned"); + Ok(state + .messages + .get(&chat_id) + .cloned() + .unwrap_or_default() + .into_iter() + .take(1) + .collect()) + } + pub fn open_profile(&self, chat_id: i64) -> Result { let mut state = self.state.lock().expect("session mutex poisoned"); let profile = state @@ -1277,6 +1299,8 @@ mod tests { let history = session.load_history(chats[0].id, 20).unwrap(); assert_eq!(history[0].text, "Hello from fake TDLib"); + let pinned = session.pinned_messages(chats[0].id).unwrap(); + assert_eq!(pinned[0].text, "Hello from fake TDLib"); 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 7f92866..caa0bfe 100755 --- a/scripts/smoke-ios-ffi-swift.sh +++ b/scripts/smoke-ios-ffi-swift.sh @@ -60,6 +60,9 @@ struct Smoke { let history = try session.loadHistory(chatId: chat.id, limit: 20) require(history.first?.text == "Hello from fake TDLib", "expected seeded history") + let pinned = try session.pinnedMessages(chatId: chat.id) + require(pinned.first?.text == "Hello from fake TDLib", "expected pinned message") + 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")