This commit is contained in:
Woit 2024-11-21 14:32:38 +01:00
parent ee2f68e33b
commit 22b6f415d2
7 changed files with 507 additions and 13 deletions

View file

@ -133,6 +133,9 @@
54F0B81C282316F5003664BD /* RegisterAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54F0B81B282316F5003664BD /* RegisterAccount.swift */; }; 54F0B81C282316F5003664BD /* RegisterAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54F0B81B282316F5003664BD /* RegisterAccount.swift */; };
6E9488F6997650B805476F25 /* Pods_another_im.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F29121F912380F72CCE51747 /* Pods_another_im.framework */; }; 6E9488F6997650B805476F25 /* Pods_another_im.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F29121F912380F72CCE51747 /* Pods_another_im.framework */; };
7D40218FEAB3BA882811A682 /* Pods_Monal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C40963CED187B2F1B4B88F7 /* Pods_Monal.framework */; }; 7D40218FEAB3BA882811A682 /* Pods_Monal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C40963CED187B2F1B4B88F7 /* Pods_Monal.framework */; };
7E1C0AC72CEF68C000B8FEC0 /* MainTabScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E1C0AC62CEF68C000B8FEC0 /* MainTabScreen.swift */; };
7E1C0ACB2CEF6C7800B8FEC0 /* ContactsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E1C0AC92CEF6C7800B8FEC0 /* ContactsScreen.swift */; };
7E1C0ACC2CEF6C7800B8FEC0 /* AddContactOrChannelScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E1C0AC82CEF6C7800B8FEC0 /* AddContactOrChannelScreen.swift */; };
7E6AF38F2CEB9110004328B5 /* MonalXmppWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E6AF38E2CEB9110004328B5 /* MonalXmppWrapper.swift */; }; 7E6AF38F2CEB9110004328B5 /* MonalXmppWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E6AF38E2CEB9110004328B5 /* MonalXmppWrapper.swift */; };
7E6AF3902CEB982F004328B5 /* sworim.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = 2601D9CA0FBF25EF004DB939 /* sworim.sqlite */; }; 7E6AF3902CEB982F004328B5 /* sworim.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = 2601D9CA0FBF25EF004DB939 /* sworim.sqlite */; };
7E71758D2CECC5C70059F30B /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7E71758B2CECC5C70059F30B /* Localizable.strings */; }; 7E71758D2CECC5C70059F30B /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7E71758B2CECC5C70059F30B /* Localizable.strings */; };
@ -635,6 +638,9 @@
79A6AA4819B69B5FFFA28236 /* Pods-NotificationService.appstore.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.appstore.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.appstore.xcconfig"; sourceTree = "<group>"; }; 79A6AA4819B69B5FFFA28236 /* Pods-NotificationService.appstore.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.appstore.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.appstore.xcconfig"; sourceTree = "<group>"; };
7D281334DB441077E42E3E89 /* Pods-another.im.appstore-quicksy.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-another.im.appstore-quicksy.xcconfig"; path = "Target Support Files/Pods-another.im/Pods-another.im.appstore-quicksy.xcconfig"; sourceTree = "<group>"; }; 7D281334DB441077E42E3E89 /* Pods-another.im.appstore-quicksy.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-another.im.appstore-quicksy.xcconfig"; path = "Target Support Files/Pods-another.im/Pods-another.im.appstore-quicksy.xcconfig"; sourceTree = "<group>"; };
7D6715099247A9CCC180EE30 /* Pods-MonalUITests.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MonalUITests.beta.xcconfig"; path = "Target Support Files/Pods-MonalUITests/Pods-MonalUITests.beta.xcconfig"; sourceTree = "<group>"; }; 7D6715099247A9CCC180EE30 /* Pods-MonalUITests.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MonalUITests.beta.xcconfig"; path = "Target Support Files/Pods-MonalUITests/Pods-MonalUITests.beta.xcconfig"; sourceTree = "<group>"; };
7E1C0AC62CEF68C000B8FEC0 /* MainTabScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabScreen.swift; sourceTree = "<group>"; };
7E1C0AC82CEF6C7800B8FEC0 /* AddContactOrChannelScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactOrChannelScreen.swift; sourceTree = "<group>"; };
7E1C0AC92CEF6C7800B8FEC0 /* ContactsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsScreen.swift; sourceTree = "<group>"; };
7E6AF38E2CEB9110004328B5 /* MonalXmppWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonalXmppWrapper.swift; sourceTree = "<group>"; }; 7E6AF38E2CEB9110004328B5 /* MonalXmppWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonalXmppWrapper.swift; sourceTree = "<group>"; };
7E6E446D2CECB76500505D5C /* another.im.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = another.im.entitlements; sourceTree = "<group>"; }; 7E6E446D2CECB76500505D5C /* another.im.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = another.im.entitlements; sourceTree = "<group>"; };
7E7175892CECC5C70059F30B /* launchscreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = launchscreen.storyboard; sourceTree = "<group>"; }; 7E7175892CECC5C70059F30B /* launchscreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = launchscreen.storyboard; sourceTree = "<group>"; };
@ -1460,6 +1466,15 @@
path = Enter; path = Enter;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
7E1C0ACA2CEF6C7800B8FEC0 /* Contacts */ = {
isa = PBXGroup;
children = (
7E1C0AC82CEF6C7800B8FEC0 /* AddContactOrChannelScreen.swift */,
7E1C0AC92CEF6C7800B8FEC0 /* ContactsScreen.swift */,
);
path = Contacts;
sourceTree = "<group>";
};
7E71758C2CECC5C70059F30B /* Strings */ = { 7E71758C2CECC5C70059F30B /* Strings */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1474,6 +1489,7 @@
7E995F222CEAC5D2005B30EE /* RootView.swift */, 7E995F222CEAC5D2005B30EE /* RootView.swift */,
7E8D7AF92CECEDB3009AD3DF /* SharedComponents */, 7E8D7AF92CECEDB3009AD3DF /* SharedComponents */,
54D8CBD8978DA29C88226FBB /* Enter */, 54D8CBD8978DA29C88226FBB /* Enter */,
D7FD95FF8F72ECE4DBEE1095 /* Main */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1707,6 +1723,15 @@
path = localization; path = localization;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D7FD95FF8F72ECE4DBEE1095 /* Main */ = {
isa = PBXGroup;
children = (
7E1C0AC62CEF68C000B8FEC0 /* MainTabScreen.swift */,
7E1C0ACA2CEF6C7800B8FEC0 /* Contacts */,
);
path = Main;
sourceTree = "<group>";
};
D91581D0612C23AB5B3F867C /* Frameworks */ = { D91581D0612C23AB5B3F867C /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -2607,6 +2632,7 @@
7E8D7B1D2CECEE79009AD3DF /* EdgeInsets+Extensions.swift in Sources */, 7E8D7B1D2CECEE79009AD3DF /* EdgeInsets+Extensions.swift in Sources */,
7E8D7B1E2CECEE79009AD3DF /* Binding+Extensions.swift in Sources */, 7E8D7B1E2CECEE79009AD3DF /* Binding+Extensions.swift in Sources */,
7E8D7B1F2CECEE79009AD3DF /* UIApplication+Extensions.swift in Sources */, 7E8D7B1F2CECEE79009AD3DF /* UIApplication+Extensions.swift in Sources */,
7E1C0AC72CEF68C000B8FEC0 /* MainTabScreen.swift in Sources */,
7E8D7B202CECEE79009AD3DF /* View+Debug.swift in Sources */, 7E8D7B202CECEE79009AD3DF /* View+Debug.swift in Sources */,
7E8D7B212CECEE79009AD3DF /* Map+Extensions.swift in Sources */, 7E8D7B212CECEE79009AD3DF /* Map+Extensions.swift in Sources */,
7E8D7B222CECEE79009AD3DF /* View+Flip.swift in Sources */, 7E8D7B222CECEE79009AD3DF /* View+Flip.swift in Sources */,
@ -2621,6 +2647,8 @@
7E8D7AFB2CECEDB3009AD3DF /* SharedSectionTitle.swift in Sources */, 7E8D7AFB2CECEDB3009AD3DF /* SharedSectionTitle.swift in Sources */,
7E8D7AFC2CECEDB3009AD3DF /* SharedNavigationBar.swift in Sources */, 7E8D7AFC2CECEDB3009AD3DF /* SharedNavigationBar.swift in Sources */,
7E8D7AFD2CECEDB3009AD3DF /* UniversalInputCollection.swift in Sources */, 7E8D7AFD2CECEDB3009AD3DF /* UniversalInputCollection.swift in Sources */,
7E1C0ACB2CEF6C7800B8FEC0 /* ContactsScreen.swift in Sources */,
7E1C0ACC2CEF6C7800B8FEC0 /* AddContactOrChannelScreen.swift in Sources */,
7E8D7AFE2CECEDB3009AD3DF /* LoadingScreen.swift in Sources */, 7E8D7AFE2CECEDB3009AD3DF /* LoadingScreen.swift in Sources */,
7E995F252CEAC5D2005B30EE /* RootView.swift in Sources */, 7E995F252CEAC5D2005B30EE /* RootView.swift in Sources */,
7E6AF38F2CEB9110004328B5 /* MonalXmppWrapper.swift in Sources */, 7E6AF38F2CEB9110004328B5 /* MonalXmppWrapper.swift in Sources */,

View file

@ -13,9 +13,4 @@ struct AnotherIMApp: App {
} }
} }
class AppDelegate: NSObject, UIApplicationDelegate { class AppDelegate: NSObject, UIApplicationDelegate {}
// func application(_: UIApplication, willFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
// // [IPC initializeForProcess:@"MainApp"];
// // [MLProcessLock initializeForProcess:@"MainApp"];
// }
}

View file

@ -0,0 +1,145 @@
import SwiftUI
struct AddContactOrChannelScreen: View {
@EnvironmentObject var wrapper: MonalXmppWrapper
@Environment(\.router) var router
enum Field {
case account
case contact
}
@FocusState private var focus: Field?
// @State private var ownerCredentials: Credentials?
@State private var contactJID: String = ""
var body: some View {
ZStack {
// Background color
Color.Material.Background.light
.ignoresSafeArea()
// Content
VStack(spacing: 0) {
// Header
SharedNavigationBar(
leftButton: .init(
image: Image(systemName: "xmark"),
action: {
router.dismissScreen()
}
),
centerText: .init(text: L10n.Contacts.Add.title),
rightButton: .init(
image: Image(systemName: "plus.viewfinder"),
action: {
print("Scan QR-code")
}
)
)
// Content
VStack(spacing: 16) {
// Explanation text
Text(L10n.Contacts.Add.explanation)
.font(.body3)
.foregroundColor(.Material.Shape.separator)
.multilineTextAlignment(.center)
.padding(.top, 16)
// Account selector
HStack(spacing: 0) {
Text("Use account:")
.font(.body2)
.foregroundColor(.Material.Text.main)
.frame(alignment: .leading)
Spacer()
}
// UniversalInputCollection.DropDownMenu(
// prompt: "Use account",
// elements: activeClientsCredentials,
// selected: $ownerCredentials,
// focus: $focus,
// fieldType: .account
// )
// Contact text input
HStack(spacing: 0) {
Text("Contact JID:")
.font(.body2)
.foregroundColor(.Material.Text.main)
.frame(alignment: .leading)
Spacer()
}
// UniversalInputCollection.TextField(
// prompt: "Contact or channel JID",
// text: $contactJID,
// focus: $focus,
// fieldType: .contact,
// contentType: .emailAddress,
// keyboardType: .emailAddress,
// submitLabel: .done,
// action: {
// focus = .account
// }
// )
// Save button
Button {
Task {
await save()
}
} label: {
Text(L10n.Global.save)
}
.buttonStyle(PrimaryButtonStyle())
// .disabled(!inputValid)
.padding(.top)
Spacer()
}
.padding(.horizontal, 32)
}
}
.onAppear {
// if let exists = activeClientsCredentials.first {
// ownerCredentials = exists
// }
}
}
// private var activeClientsCredentials: [Credentials] {
// clientsStore.clients
// .filter { $0.state != .disabled }
// .map { $0.credentials }
// }
// private var inputValid: Bool {
// ownerCredentials != nil && !contactJID.isEmpty && UniversalInputCollection.Validators.isEmail(contactJID)
// }
//
private func save() async {
// guard let ownerCredentials = ownerCredentials else { return }
router.showModal {
LoadingScreen()
}
defer {
router.dismissModal()
}
do {
// try await clientsStore.addRoster(ownerCredentials, contactJID: contactJID, name: nil, groups: [])
router.dismissScreen()
} catch {
router.showAlert(
.alert,
title: L10n.Global.Error.title,
subtitle: L10n.Contacts.Add.serverError
) {
Button(L10n.Global.ok, role: .cancel) {}
}
}
}
}

View file

@ -0,0 +1,202 @@
import SwiftUI
struct ContactsScreen: View {
@EnvironmentObject var wrapper: MonalXmppWrapper
@Environment(\.router) var router
var body: some View {
ZStack {
// Background color
Color.Material.Background.light
.ignoresSafeArea()
// Content
VStack(spacing: 0) {
// Header
SharedNavigationBar(
centerText: .init(text: L10n.Contacts.title),
rightButton: .init(
image: Image(systemName: "plus"),
action: {
router.showScreen(.fullScreenCover) { _ in
AddContactOrChannelScreen()
}
}
)
)
// Contacts list
// if !clientsStore.actualRosters.isEmpty {
// List {
// ForEach(elements.indices, id: \.self) { index in
// let element = elements[index]
// if let roster = element as? Roster {
// ContactsScreenRow(
// roster: roster
// )
// } else if let bareJid = element as? String {
// SharedSectionTitle(text: bareJid)
// }
// }
// }
// .listStyle(.plain)
// .background(Color.Material.Background.light)
// } else {
// Spacer()
// }
}
}
}
private var elements: [Any] {
[]
// if clientsStore.clients.filter({ $0.credentials.isActive }).count == 1 {
// return clientsStore.actualRosters
// } else {
// var result: [Any] = []
// for roster in clientsStore.actualRosters {
// if result.isEmpty {
// result.append(roster.bareJid)
// } else if let last = result.last as? Roster, last.bareJid != roster.bareJid {
// result.append(roster.bareJid)
// }
// result.append(roster)
// }
// return result
// }
}
}
private struct ContactsScreenRow: View {
@EnvironmentObject var wrapper: MonalXmppWrapper
@Environment(\.router) var router
// var roster: Roster
var body: some View {
Text("nothing for now")
// SharedListRow(
// iconType: .charCircle(roster.name?.firstLetter ?? roster.contactBareJid.firstLetter),
// text: roster.contactBareJid,
// controlType: .none
// )
// .onTapGesture {
// startChat()
// }
// .swipeActions(edge: .trailing, allowsFullSwipe: false) {
// Button {
// router.showAlert(.confirmationDialog, title: L10n.Contacts.deleteContact, subtitle: L10n.Contacts.Delete.message) {
// deleteConfirmation
// }
// } label: {
// Label("", systemImage: "trash")
// }
// .tint(Color.red)
// }
// .contextMenu {
// Button(L10n.Contacts.sendMessage, systemImage: "message") {
// startChat()
// }
// Divider()
//
// Button(L10n.Contacts.editContact) {
// print("Edit contact")
// }
//
// Button(L10n.Contacts.selectContact) {
// print("Select contact")
// }
//
// Divider()
// Button(L10n.Contacts.deleteContact, systemImage: "trash", role: .destructive) {
// router.showAlert(.confirmationDialog, title: L10n.Contacts.deleteContact, subtitle: L10n.Contacts.Delete.message) {
// deleteConfirmation
// }
// }
// }
}
@ViewBuilder private var deleteConfirmation: some View {
Button(role: .destructive) {
Task {
await deleteFromDevice()
}
} label: {
Text(L10n.Contacts.Delete.deleteFromDevice)
}
Button(role: .destructive) {
Task {
await deleteCompletely()
}
} label: {
Text(L10n.Contacts.Delete.deleteCompletely)
}
Button(role: .cancel) {} label: {
Text(L10n.Global.cancel)
}
}
private func deleteFromDevice() async {
router.showModal {
LoadingScreen()
}
defer {
router.dismissModal()
}
//var roster = roster
//try? await roster.setLocallyDeleted(true)
}
private func deleteCompletely() async {
router.showModal {
LoadingScreen()
}
defer {
router.dismissModal()
}
do {
// try await clientsStore.deleteRoster(roster)
} catch {
router.showAlert(
.alert,
title: L10n.Global.Error.title,
subtitle: L10n.Contacts.Delete.error
) {
Button(L10n.Global.ok, role: .cancel) {}
}
}
}
private func startChat() {
Task {
router.showModal {
LoadingScreen()
}
defer {
router.dismissModal()
}
do {
// let (messages, attachments, settings) = try await clientsStore.conversationStores(for: roster)
// router.showScreen(.push) { _ in
// ConversationScreen(messagesStore: messages, attachments: attachments, settings: settings)
// .navigationBarHidden(true)
// }
} catch {
router.showAlert(
.alert,
title: L10n.Global.Error.title,
subtitle: L10n.Conversation.startError
) {
Button(L10n.Global.ok, role: .cancel) {}
}
}
}
}
}

View file

@ -0,0 +1,118 @@
import Foundation
import SwiftfulRouting
import SwiftUI
private enum Tab {
case chats
case contacts
case settings
}
struct MainTabScreen: View {
@EnvironmentObject var wrapper: MonalXmppWrapper
@State private var selectedTab: Tab = .chats
var body: some View {
ZStack {
// Background color
Color.Material.Background.light
.ignoresSafeArea()
// Content
VStack(spacing: 0) {
switch selectedTab {
case .chats:
Text("chats")
// ChatsListScreen()
case .contacts:
Text("contacts")
// ContactsScreen()
case .settings:
Text("settings")
// SettingsScreen()
// .environment(\.settingsParent, .main)
}
// Tab bar
TabBar(selectedTab: $selectedTab)
}
}
.onLoad {
wrapper.reconnectAll()
}
}
}
private struct TabBar: View {
@Binding var selectedTab: Tab
var body: some View {
VStack(spacing: 0) {
Rectangle()
.frame(maxWidth: .infinity)
.frame(height: 0.2)
.foregroundColor(.Material.Shape.separator)
HStack(spacing: 0) {
TabBarButton(buttonType: .contacts, selectedTab: $selectedTab)
TabBarButton(buttonType: .chats, selectedTab: $selectedTab)
TabBarButton(buttonType: .settings, selectedTab: $selectedTab)
}
.background(Color.Material.Background.dark)
}
.frame(height: 50)
}
}
private struct TabBarButton: View {
let buttonType: Tab
@Binding var selectedTab: Tab
var body: some View {
ZStack {
VStack(spacing: 2) {
buttonImg
.foregroundColor(buttonType == selectedTab ? .Material.Elements.active : .Material.Elements.inactive)
.font(.system(size: 24, weight: .light))
.symbolRenderingMode(.hierarchical)
Text(buttonTitle)
.font(.sub1)
.foregroundColor(buttonType == selectedTab ? .Material.Text.main : .Material.Elements.inactive)
}
Rectangle()
.foregroundColor(.white.opacity(0.01))
.onTapGesture {
selectedTab = buttonType
}
}
}
var buttonImg: Image {
switch buttonType {
case .contacts:
return Image(systemName: "person.2.fill")
case .chats:
return Image(systemName: "bubble.left.fill")
case .settings:
return Image(systemName: "gearshape.fill")
}
}
var buttonTitle: String {
switch buttonType {
case .contacts:
return L10n.Tabs.Name.contacts
case .chats:
return L10n.Tabs.Name.conversations
case .settings:
return L10n.Tabs.Name.settings
}
}
}

View file

@ -6,8 +6,9 @@ struct RootView: View {
var body: some View { var body: some View {
if wrapper.accountsAvailability == .someEnabled { if wrapper.accountsAvailability == .someEnabled {
// main flow here RouterView { _ in
Text("Test ME!") MainTabScreen()
}
} else { } else {
RouterView { _ in RouterView { _ in
WelcomeScreen() WelcomeScreen()

View file

@ -14,12 +14,13 @@ final class MonalXmppWrapper: ObservableObject {
private let db: DataLayer private let db: DataLayer
init() { init() {
xmpp = MLXMPPManager.sharedInstance()
db = DataLayer.sharedInstance()
// here is some inits (just for now) // here is some inits (just for now)
MLProcessLock.initialize(forProcess: "MainApp") MLProcessLock.initialize(forProcess: "MainApp")
// init monalxmpp components
xmpp = MLXMPPManager.sharedInstance()
db = DataLayer.sharedInstance()
checkAccountsOnLoad() checkAccountsOnLoad()
} }
@ -31,6 +32,10 @@ final class MonalXmppWrapper: ObservableObject {
throw AimErrors.loginError throw AimErrors.loginError
} }
} }
func reconnectAll() {
xmpp.reconnectAll()
}
} }
// MARK: - Accounts // MARK: - Accounts
@ -50,7 +55,7 @@ private extension MonalXmppWrapper {
} }
} }
// MARK: - Login from Login screen // MARK: - Try login from Login screen
private final class LoginTry { private final class LoginTry {
weak var xmpp: MLXMPPManager? weak var xmpp: MLXMPPManager?
@ -61,7 +66,7 @@ private final class LoginTry {
self.xmpp = xmpp self.xmpp = xmpp
} }
// TODO: Добавить автовключение отключенных аккаунтов при попытке ввести тот же JID\ // TODO: Добавить автовключение отключенных аккаунтов при попытке ввести тот же JID
// Обработать кейс когда бесячий monalxmpp возвращает nil при попытке добавить тот же JID // Обработать кейс когда бесячий monalxmpp возвращает nil при попытке добавить тот же JID
func tryLogin(_ login: String, _ password: String) async -> Bool { func tryLogin(_ login: String, _ password: String) async -> Bool {
async let notify = await withCheckedContinuation { [weak self] continuation in async let notify = await withCheckedContinuation { [weak self] continuation in