Preserve typing events in iOS FFI

This commit is contained in:
Mikhail Kilin
2026-05-21 00:41:18 +03:00
parent b3b02835b6
commit 928a5aeda2
6 changed files with 80 additions and 2 deletions

View File

@@ -173,6 +173,7 @@ public enum SessionEvent: Equatable, Sendable {
case reactionChanged(Int64, Int64, [Reaction])
case incomingNotificationCandidate(ChatSummary, Message, String)
case networkChanged(NetworkState)
case typingChanged(TypingState)
case draftChanged(Draft)
case profileLoaded(Profile)
case mediaDownloadProgress(fileId: Int32, downloadedSize: Int64, totalSize: Int64)
@@ -185,3 +186,8 @@ public enum NetworkState: Equatable, Sendable {
case updating
case ready
}
public enum TypingState: Equatable, Sendable {
case idle
case typing(chatId: Int64, userId: Int64, text: String)
}

View File

@@ -155,6 +155,15 @@ public actor UniFfiSessionBridge: SessionBridge {
}
}
private static func mapTypingState(_ state: IosTypingState) -> TypingState {
switch state {
case .idle:
.idle
case let .typing(chatId, userId, text):
.typing(chatId: chatId, userId: userId, text: text)
}
}
private static func mapFolder(_ folder: IosFolder) -> Folder {
Folder(id: folder.id, name: folder.name)
}
@@ -236,6 +245,8 @@ public actor UniFfiSessionBridge: SessionBridge {
)
case let .networkChanged(state):
.networkChanged(mapNetworkState(state))
case let .typingChanged(state):
.typingChanged(mapTypingState(state))
case let .draftChanged(draft):
.draftChanged(mapDraft(draft))
case let .profileLoaded(profile):

View File

@@ -6,6 +6,7 @@ public final class SessionStore: ObservableObject {
@Published public private(set) var account: Account
@Published public private(set) var authState: AuthState = .waitTdlibParameters
@Published public private(set) var networkState: NetworkState = .ready
@Published public private(set) var typingState: TypingState = .idle
@Published public private(set) var errorMessage: String?
public let bridge: SessionBridge
@@ -40,6 +41,8 @@ public final class SessionStore: ObservableObject {
authState = state
case let .networkChanged(state):
networkState = state
case let .typingChanged(state):
typingState = state
default:
break
}

View File

@@ -25,6 +25,8 @@ struct TeleTuiIOSSmokeTests {
precondition(store.authState == .waitPhoneNumber)
await store.refreshNetworkState()
precondition(store.networkState == .ready)
store.apply(events: [.typingChanged(.typing(chatId: 1, userId: 10, text: "typing"))])
precondition(store.typingState == .typing(chatId: 1, userId: 10, text: "typing"))
viewModel.phone = "+10000000000"
await viewModel.submitCurrentStep()

View File

@@ -813,6 +813,18 @@ mod tests {
CoreNetworkState::from(&NetworkState::WaitingForNetwork),
CoreNetworkState::WaitingForNetwork
);
assert_eq!(
CoreTypingState::Typing {
chat_id: ChatId::new(42),
user_id: UserId::new(7),
text: "typing".to_string(),
},
CoreTypingState::Typing {
chat_id: ChatId::new(42),
user_id: UserId::new(7),
text: "typing".to_string(),
}
);
}
#[tokio::test]

View File

@@ -7,7 +7,7 @@ use std::collections::HashMap;
use tele_core::session::{
CoreAuthState, CoreChatSummary, CoreDownloadedFile, CoreDraft, CoreEvent, CoreFolder,
CoreMedia, CoreMessage, CoreNetworkState, CoreProfile, CoreReaction, CoreSearchResult,
CoreSession,
CoreSession, CoreTypingState,
};
#[cfg(feature = "core-session")]
use tele_core::tdlib::{ChatInfo, MessageBuilder, NetworkState, ProfileInfo};
@@ -86,6 +86,30 @@ impl From<CoreNetworkState> for IosNetworkState {
}
}
#[derive(Debug, Clone, PartialEq, Eq, uniffi::Enum)]
pub enum IosTypingState {
Idle,
Typing {
chat_id: i64,
user_id: i64,
text: String,
},
}
#[cfg(feature = "core-session")]
impl From<CoreTypingState> for IosTypingState {
fn from(value: CoreTypingState) -> Self {
match value {
CoreTypingState::Idle => Self::Idle,
CoreTypingState::Typing { chat_id, user_id, text } => Self::Typing {
chat_id: chat_id.as_i64(),
user_id: user_id.as_i64(),
text,
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, uniffi::Enum)]
pub enum IosDownloadState {
NotDownloaded,
@@ -412,6 +436,9 @@ pub enum IosEvent {
downloaded_size: i64,
total_size: i64,
},
TypingChanged {
state: IosTypingState,
},
}
#[cfg(feature = "core-session")]
@@ -455,7 +482,7 @@ impl From<CoreEvent> for IosEvent {
CoreEvent::MediaDownloadProgress { file_id, downloaded_size, total_size } => {
Self::MediaDownloadProgress { file_id, downloaded_size, total_size }
}
CoreEvent::TypingChanged(_) => Self::NetworkChanged { state: IosNetworkState::Ready },
CoreEvent::TypingChanged(state) => Self::TypingChanged { state: state.into() },
}
}
}
@@ -1363,4 +1390,21 @@ mod tests {
assert!(format!("{error}").contains("real TDLib sessions"));
}
#[cfg(feature = "core-session")]
#[test]
fn typing_events_are_preserved_for_ios() {
let event = IosEvent::from(CoreEvent::TypingChanged(CoreTypingState::Typing {
chat_id: ChatId::new(1),
user_id: tele_core::types::UserId::new(2),
text: "typing".to_string(),
}));
assert!(matches!(
event,
IosEvent::TypingChanged {
state: IosTypingState::Typing { chat_id: 1, user_id: 2, text }
} if text == "typing"
));
}
}