Add SwiftUI iOS app shell
This commit is contained in:
203
apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/ViewModels.swift
Normal file
203
apps/ios/TeleTuiIOS/Sources/TeleTuiIOSCore/ViewModels.swift
Normal file
@@ -0,0 +1,203 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
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 errorMessage: String?
|
||||
|
||||
public let bridge: SessionBridge
|
||||
|
||||
public init(account: Account, bridge: SessionBridge) {
|
||||
self.account = account
|
||||
self.bridge = bridge
|
||||
}
|
||||
|
||||
public func refreshAuthState() async {
|
||||
do {
|
||||
authState = try await bridge.authState()
|
||||
errorMessage = nil
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
public func apply(events: [SessionEvent]) {
|
||||
for event in events {
|
||||
if case let .authChanged(state) = event {
|
||||
authState = state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public final class AuthViewModel: ObservableObject {
|
||||
@Published public var phone = ""
|
||||
@Published public var code = ""
|
||||
@Published public var password = ""
|
||||
@Published public private(set) var isLoading = false
|
||||
@Published public private(set) var errorMessage: String?
|
||||
|
||||
private let store: SessionStore
|
||||
|
||||
public init(store: SessionStore) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
public func submitCurrentStep() async {
|
||||
isLoading = true
|
||||
defer { isLoading = false }
|
||||
|
||||
do {
|
||||
switch store.authState {
|
||||
case .waitPhoneNumber:
|
||||
try await store.bridge.sendPhoneNumber(phone)
|
||||
case .waitCode:
|
||||
try await store.bridge.sendCode(code)
|
||||
case .waitPassword:
|
||||
try await store.bridge.sendPassword(password)
|
||||
default:
|
||||
break
|
||||
}
|
||||
let events = try await store.bridge.pollEvents()
|
||||
store.apply(events: events)
|
||||
errorMessage = nil
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public final class ChatListViewModel: ObservableObject {
|
||||
@Published public private(set) var folders: [Folder] = []
|
||||
@Published public private(set) var chats: [ChatSummary] = []
|
||||
@Published public var selectedFolderId: Int32?
|
||||
@Published public var searchText = ""
|
||||
@Published public private(set) var isLoading = false
|
||||
@Published public private(set) var errorMessage: String?
|
||||
|
||||
private let bridge: SessionBridge
|
||||
|
||||
public init(bridge: SessionBridge) {
|
||||
self.bridge = bridge
|
||||
}
|
||||
|
||||
public var filteredChats: [ChatSummary] {
|
||||
guard !searchText.isEmpty else {
|
||||
return chats
|
||||
}
|
||||
return chats.filter { chat in
|
||||
chat.title.localizedCaseInsensitiveContains(searchText)
|
||||
|| (chat.username?.localizedCaseInsensitiveContains(searchText) ?? false)
|
||||
}
|
||||
}
|
||||
|
||||
public func load() async {
|
||||
isLoading = true
|
||||
defer { isLoading = false }
|
||||
|
||||
do {
|
||||
folders = try await bridge.loadFolders()
|
||||
chats = try await bridge.loadChats(folderId: selectedFolderId)
|
||||
errorMessage = nil
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public final class ChatViewModel: ObservableObject {
|
||||
@Published public private(set) var chat: ChatSummary
|
||||
@Published public private(set) var messages: [Message] = []
|
||||
@Published public var composeText: String
|
||||
@Published public var replyTo: Message?
|
||||
@Published public private(set) var isLoading = false
|
||||
@Published public private(set) var errorMessage: String?
|
||||
|
||||
private let bridge: SessionBridge
|
||||
|
||||
public init(chat: ChatSummary, bridge: SessionBridge) {
|
||||
self.chat = chat
|
||||
self.bridge = bridge
|
||||
self.composeText = chat.draft?.text ?? ""
|
||||
}
|
||||
|
||||
public func load() async {
|
||||
isLoading = true
|
||||
defer { isLoading = false }
|
||||
|
||||
do {
|
||||
messages = try await bridge.loadHistory(chatId: chat.id)
|
||||
errorMessage = nil
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
|
||||
public func send() async {
|
||||
let text = composeText.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !text.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let sent = try await bridge.sendMessage(
|
||||
chatId: chat.id,
|
||||
text: text,
|
||||
replyToMessageId: replyTo?.id
|
||||
)
|
||||
messages.append(sent)
|
||||
composeText = ""
|
||||
replyTo = nil
|
||||
errorMessage = nil
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public final class ProfileViewModel: ObservableObject {
|
||||
@Published public private(set) var profile: Profile?
|
||||
@Published public private(set) var isLoading = false
|
||||
@Published public private(set) var errorMessage: String?
|
||||
|
||||
private let bridge: SessionBridge
|
||||
|
||||
public init(bridge: SessionBridge) {
|
||||
self.bridge = bridge
|
||||
}
|
||||
|
||||
public func load(chatId: Int64) async {
|
||||
isLoading = true
|
||||
defer { isLoading = false }
|
||||
|
||||
do {
|
||||
profile = try await bridge.openProfile(chatId: chatId)
|
||||
errorMessage = nil
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public final class MediaViewModel: ObservableObject {
|
||||
@Published public private(set) var activePhotoPath: String?
|
||||
@Published public private(set) var activeVoicePath: String?
|
||||
|
||||
public init() {}
|
||||
|
||||
public func showPhoto(path: String) {
|
||||
activePhotoPath = path
|
||||
}
|
||||
|
||||
public func showVoice(path: String) {
|
||||
activeVoicePath = path
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user