204 lines
5.7 KiB
Swift
204 lines
5.7 KiB
Swift
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
|
|
}
|
|
}
|