浏览代码

XMPPController is divided into mmanagers

Sergey 2 年之前
父节点
当前提交
9320c2e031

+ 90 - 8
Chat.xcodeproj/project.pbxproj

@@ -31,6 +31,18 @@
 		2F6045B4288EA0E6008F005E /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6045B2288EA0E6008F005E /* LoginView.swift */; };
 		2F6045B6288EA10B008F005E /* LoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6045B5288EA10B008F005E /* LoginViewModel.swift */; };
 		2F6045B7288EA10B008F005E /* LoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6045B5288EA10B008F005E /* LoginViewModel.swift */; };
+		2F81974128ACD9A6008D8C45 /* StreamManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F81974028ACD9A6008D8C45 /* StreamManager.swift */; };
+		2F81974328ACDBC4008D8C45 /* RoomManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F81974228ACDBC4008D8C45 /* RoomManager.swift */; };
+		2F81974528ACDBCF008D8C45 /* RosterManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F81974428ACDBCF008D8C45 /* RosterManager.swift */; };
+		2F81974828ACEDC8008D8C45 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F81974728ACEDC8008D8C45 /* User.swift */; };
+		2F81974A28ACF91E008D8C45 /* MUCManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F81974928ACF91E008D8C45 /* MUCManager.swift */; };
+		2F81974C28ACFDEA008D8C45 /* Room.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F81974B28ACFDEA008D8C45 /* Room.swift */; };
+		2FCB75E728A425580097BD1D /* RoomsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FCB75E628A425580097BD1D /* RoomsListView.swift */; };
+		2FCB75E828A425580097BD1D /* RoomsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FCB75E628A425580097BD1D /* RoomsListView.swift */; };
+		2FCB75EA28A425900097BD1D /* RoomsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FCB75E928A425900097BD1D /* RoomsListViewModel.swift */; };
+		2FCB75EB28A425900097BD1D /* RoomsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FCB75E928A425900097BD1D /* RoomsListViewModel.swift */; };
+		2FCB75ED28A42B1E0097BD1D /* ChatViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FCB75EC28A42B1E0097BD1D /* ChatViewModel.swift */; };
+		2FCB75EE28A42B1E0097BD1D /* ChatViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FCB75EC28A42B1E0097BD1D /* ChatViewModel.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
@@ -48,6 +60,15 @@
 		2F6045AF288E9D3E008F005E /* ChatListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListViewModel.swift; sourceTree = "<group>"; };
 		2F6045B2288EA0E6008F005E /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
 		2F6045B5288EA10B008F005E /* LoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewModel.swift; sourceTree = "<group>"; };
+		2F81974028ACD9A6008D8C45 /* StreamManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamManager.swift; sourceTree = "<group>"; };
+		2F81974228ACDBC4008D8C45 /* RoomManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomManager.swift; sourceTree = "<group>"; };
+		2F81974428ACDBCF008D8C45 /* RosterManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RosterManager.swift; sourceTree = "<group>"; };
+		2F81974728ACEDC8008D8C45 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
+		2F81974928ACF91E008D8C45 /* MUCManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MUCManager.swift; sourceTree = "<group>"; };
+		2F81974B28ACFDEA008D8C45 /* Room.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Room.swift; sourceTree = "<group>"; };
+		2FCB75E628A425580097BD1D /* RoomsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomsListView.swift; sourceTree = "<group>"; };
+		2FCB75E928A425900097BD1D /* RoomsListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomsListViewModel.swift; sourceTree = "<group>"; };
+		2FCB75EC28A42B1E0097BD1D /* ChatViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatViewModel.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -83,15 +104,12 @@
 			isa = PBXGroup;
 			children = (
 				2F60457A288D787E008F005E /* ChatApp.swift */,
-				2F6045AC288E9B73008F005E /* XMPPController.swift */,
 				2F60457B288D787E008F005E /* ContentView.swift */,
-				2F6045B2288EA0E6008F005E /* LoginView.swift */,
-				2F6045B5288EA10B008F005E /* LoginViewModel.swift */,
-				2F60459E288D869B008F005E /* ChatListView.swift */,
-				2F6045AF288E9D3E008F005E /* ChatListViewModel.swift */,
-				2F604598288D7E00008F005E /* ChatView.swift */,
-				2F60459B288D8000008F005E /* MessageView.swift */,
-				2F6045A4288E19ED008F005E /* KeyboardReadable.swift */,
+				2FCB762528AA59620097BD1D /* Managers */,
+				2F81974628ACEDB6008D8C45 /* Models */,
+				2FCB762328AA58EA0097BD1D /* Auth */,
+				2FCB762228AA58E00097BD1D /* Core */,
+				2F81973C28AB7FE8008D8C45 /* Other */,
 				2F60457C288D787F008F005E /* Assets.xcassets */,
 			);
 			path = Shared;
@@ -114,6 +132,58 @@
 			path = macOS;
 			sourceTree = "<group>";
 		};
+		2F81973C28AB7FE8008D8C45 /* Other */ = {
+			isa = PBXGroup;
+			children = (
+				2F60459B288D8000008F005E /* MessageView.swift */,
+				2F6045A4288E19ED008F005E /* KeyboardReadable.swift */,
+			);
+			path = Other;
+			sourceTree = "<group>";
+		};
+		2F81974628ACEDB6008D8C45 /* Models */ = {
+			isa = PBXGroup;
+			children = (
+				2F81974728ACEDC8008D8C45 /* User.swift */,
+				2F81974B28ACFDEA008D8C45 /* Room.swift */,
+			);
+			path = Models;
+			sourceTree = "<group>";
+		};
+		2FCB762228AA58E00097BD1D /* Core */ = {
+			isa = PBXGroup;
+			children = (
+				2F60459E288D869B008F005E /* ChatListView.swift */,
+				2F6045AF288E9D3E008F005E /* ChatListViewModel.swift */,
+				2FCB75E628A425580097BD1D /* RoomsListView.swift */,
+				2FCB75E928A425900097BD1D /* RoomsListViewModel.swift */,
+				2F604598288D7E00008F005E /* ChatView.swift */,
+				2FCB75EC28A42B1E0097BD1D /* ChatViewModel.swift */,
+			);
+			path = Core;
+			sourceTree = "<group>";
+		};
+		2FCB762328AA58EA0097BD1D /* Auth */ = {
+			isa = PBXGroup;
+			children = (
+				2F6045B2288EA0E6008F005E /* LoginView.swift */,
+				2F6045B5288EA10B008F005E /* LoginViewModel.swift */,
+			);
+			path = Auth;
+			sourceTree = "<group>";
+		};
+		2FCB762528AA59620097BD1D /* Managers */ = {
+			isa = PBXGroup;
+			children = (
+				2F6045AC288E9B73008F005E /* XMPPController.swift */,
+				2F81974028ACD9A6008D8C45 /* StreamManager.swift */,
+				2F81974428ACDBCF008D8C45 /* RosterManager.swift */,
+				2F81974928ACF91E008D8C45 /* MUCManager.swift */,
+				2F81974228ACDBC4008D8C45 /* RoomManager.swift */,
+			);
+			path = Managers;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
@@ -219,9 +289,18 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				2FCB75ED28A42B1E0097BD1D /* ChatViewModel.swift in Sources */,
+				2F81974328ACDBC4008D8C45 /* RoomManager.swift in Sources */,
+				2F81974C28ACFDEA008D8C45 /* Room.swift in Sources */,
 				2F60459C288D8000008F005E /* MessageView.swift in Sources */,
 				2F6045AD288E9B73008F005E /* XMPPController.swift in Sources */,
+				2F81974128ACD9A6008D8C45 /* StreamManager.swift in Sources */,
 				2F6045B0288E9D3E008F005E /* ChatListViewModel.swift in Sources */,
+				2FCB75EA28A425900097BD1D /* RoomsListViewModel.swift in Sources */,
+				2F81974A28ACF91E008D8C45 /* MUCManager.swift in Sources */,
+				2F81974828ACEDC8008D8C45 /* User.swift in Sources */,
+				2FCB75E728A425580097BD1D /* RoomsListView.swift in Sources */,
+				2F81974528ACDBCF008D8C45 /* RosterManager.swift in Sources */,
 				2F6045B3288EA0E6008F005E /* LoginView.swift in Sources */,
 				2F60459F288D869B008F005E /* ChatListView.swift in Sources */,
 				2F6045A5288E19ED008F005E /* KeyboardReadable.swift in Sources */,
@@ -236,9 +315,12 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				2FCB75EE28A42B1E0097BD1D /* ChatViewModel.swift in Sources */,
 				2F60459D288D8000008F005E /* MessageView.swift in Sources */,
 				2F6045AE288E9B73008F005E /* XMPPController.swift in Sources */,
 				2F6045B1288E9D3E008F005E /* ChatListViewModel.swift in Sources */,
+				2FCB75EB28A425900097BD1D /* RoomsListViewModel.swift in Sources */,
+				2FCB75E828A425580097BD1D /* RoomsListView.swift in Sources */,
 				2F6045B4288EA0E6008F005E /* LoginView.swift in Sources */,
 				2F6045A0288D869C008F005E /* ChatListView.swift in Sources */,
 				2F6045A6288E19ED008F005E /* KeyboardReadable.swift in Sources */,

+ 0 - 0
Shared/LoginView.swift → Shared/Auth/LoginView.swift


+ 3 - 3
Shared/LoginViewModel.swift → Shared/Auth/LoginViewModel.swift

@@ -8,7 +8,7 @@
 import Foundation
 
 class LoginViewModel: ObservableObject {
-    let xmppController = XMPPController.shared
+    let manager = StreamManager.shared
 
     @Published var login: String = "test11@msg.sharix-app.org"
     @Published var password: String = "test11_-"
@@ -16,8 +16,8 @@ class LoginViewModel: ObservableObject {
     var isShowError: String? = nil
 
     func didTouchLogIn(_ completion: @escaping (Bool) -> Void) {
-        self.xmppController.setupStream(with: login, password: password)
-        self.xmppController.connect()
+        self.manager.setupStream(with: login, password: password)
+        self.manager.connect()
         completion(true)
     }
 }

+ 10 - 3
Shared/ChatApp.swift

@@ -10,6 +10,8 @@ import XMPPFramework
 
 @main
 struct ChatApp: App {
+    @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
+
     var body: some Scene {
         WindowGroup {
             ContentView()
@@ -23,14 +25,16 @@ class AppDelegate: NSObject, UIApplicationDelegate {
         _ application: UIApplication,
         didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
     ) -> Bool {
-        DDLog.add(DDTTYLogger.sharedInstance!, with: DDLogLevel.all)
+        DDLog.add(DDOSLogger.sharedInstance, with: DDLogLevel.all)
         return true
     }
 }
 
 extension UIApplication {
     func addTapGestureRecognizer() {
-        guard let window = windows.first else { return }
+        let scenes = UIApplication.shared.connectedScenes
+        let windowScene = scenes.first as? UIWindowScene
+        guard let window = windowScene?.windows.first else { return }
         let tapGesture = UITapGestureRecognizer(target: window, action: #selector(UIView.endEditing))
         tapGesture.requiresExclusiveTouchType = false
         tapGesture.cancelsTouchesInView = false
@@ -40,7 +44,10 @@ extension UIApplication {
 }
 
 extension UIApplication: UIGestureRecognizerDelegate {
-    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
+    public func gestureRecognizer(
+        _ gestureRecognizer: UIGestureRecognizer,
+        shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
+    ) -> Bool {
         return false
     }
 }

+ 38 - 3
Shared/ContentView.swift

@@ -7,16 +7,51 @@
 
 import SwiftUI
 
+enum Tab: Int {
+    case contacts = 0
+    case rooms = 1
+    case settings = 2
+}
+
 struct ContentView: View {
-    @ObservedObject var viewModel: ChatListViewModel = ChatListViewModel()
+    @ObservedObject var contactsViewModel: ChatListViewModel = ChatListViewModel()
+    @ObservedObject var roomsViewModel: RoomsListViewModel = RoomsListViewModel()
     @ObservedObject var loginViewModel: LoginViewModel = LoginViewModel()
 
     @State var authStatus: Bool = false
+    @State var selectedTab: Tab = .rooms
     var body: some View {
         Group {
             if authStatus {
-                NavigationView {
-                    ChatListView(viewModel: viewModel)
+                TabView(selection: $selectedTab) {
+                    NavigationView {
+                        ChatListView(viewModel: contactsViewModel)
+                    }
+                    .navigationViewStyle(StackNavigationViewStyle())
+                    .tabItem {
+                        Label("Контакты", systemImage: "person.crop.circle")
+                    }
+                    .tag(Tab.contacts)
+
+                    NavigationView {
+                        RoomsListView(viewModel: roomsViewModel)
+                    }
+                    .navigationViewStyle(StackNavigationViewStyle())
+                    .tabItem {
+                        Label("Чаты", systemImage: "bubble.left.and.bubble.right")
+                    }
+                    .tag(Tab.rooms)
+
+                    NavigationView {
+                        Text("")
+                            .navigationTitle("Настройки")
+                            .navigationBarTitleDisplayMode(.inline)
+                    }
+                    .navigationViewStyle(StackNavigationViewStyle())
+                    .tabItem {
+                        Label("Настройки", systemImage: "gear")
+                    }
+                    .tag(Tab.settings)
                 }
             } else {
                 LoginView(viewModel: loginViewModel, authStatus: $authStatus)

+ 16 - 6
Shared/ChatListView.swift → Shared/Core/ChatListView.swift

@@ -6,17 +6,19 @@
 //
 
 import SwiftUI
+import XMPPFramework
+import XMPPFrameworkSwift
 
 struct ChatListView: View {
     @Environment(\.colorScheme) var colorScheme
     @ObservedObject var viewModel: ChatListViewModel
     var body: some View {
         Group {
-            if viewModel.jids.isEmpty {
+            if viewModel.users.isEmpty {
                 ProgressView()
             } else {
                 List {
-                    ForEach(viewModel.jids.indices, id: \.self) { index in
+                    ForEach(viewModel.users.indices, id: \.self) { index in
                         ZStack {
                             HStack {
                                 Image(systemName: "person.crop.circle")
@@ -26,7 +28,7 @@ struct ChatListView: View {
                                     .padding(.vertical, 8)
                                 VStack(alignment: .leading, spacing: 0) {
                                     HStack {
-                                        Text(viewModel.jids[index].user ?? "")
+                                        Text(viewModel.nicknames[index])
                                         Spacer()
                                         Text("23:04")
                                             .font(.callout)
@@ -54,8 +56,7 @@ struct ChatListView: View {
                             .foregroundColor(.primary)
                             .frame(height: 60)
                             NavigationLink {
-                                ChatView()
-                                    .navigationTitle(viewModel.jids[index].user ?? "")
+                                ChatView(viewModel: ChatViewModel(with: viewModel.users[index].jidString))
                             } label: {
                                 EmptyView()
                             }
@@ -66,8 +67,17 @@ struct ChatListView: View {
                 .listStyle(.plain)
             }
         }
-        .navigationTitle("Чаты")
+        .navigationTitle("Контакты")
         .navigationBarTitleDisplayMode(.inline)
+        .toolbar {
+            ToolbarItem(placement: .navigationBarTrailing) {
+                Button {
+                    viewModel.fetchChatList()
+                } label: {
+                    Image(systemName: "arrow.counterclockwise")
+                }
+            }
+        }
     }
 }
 

+ 45 - 0
Shared/Core/ChatListViewModel.swift

@@ -0,0 +1,45 @@
+//
+//  ChatListViewModel.swift
+//  Chat
+//
+//  Created by Sergey Tarasov on 25.07.2022.
+//
+
+import Foundation
+import XMPPFramework
+import XMPPFrameworkSwift
+
+class ChatListViewModel: ObservableObject {
+    let manager = RosterManager()
+
+    @Published var users: [User] = []
+
+    var nicknames: [String] {
+        return users.map({ $0.nickname.isEmpty ? $0.jidString : $0.nickname })
+    }
+
+    var timer: Timer?
+
+    init() {
+        self.timer = Timer.scheduledTimer(
+            timeInterval: 2,
+            target: self,
+            selector: #selector(fetchChatList),
+            userInfo: nil,
+            repeats: true
+        )
+    }
+
+    @objc
+    public func fetchChatList() {
+        if users.isEmpty {
+            let xmppUsers = manager.storage.sortedUsersByAvailabilityName() as? [XMPPUser]
+            guard let users = xmppUsers?.compactMap({ User(jidString: $0.jid.bare, nickname: $0.nickname) }) else {
+                return
+            }
+            self.users = users
+        } else {
+            self.timer?.invalidate()
+        }
+    }
+}

+ 29 - 17
Shared/ChatView.swift → Shared/Core/ChatView.swift

@@ -6,6 +6,8 @@
 //
 
 import SwiftUI
+import XMPPFramework
+import XMPPFrameworkSwift
 
 extension CGFloat {
     static let sidePadding: CGFloat = 14
@@ -13,9 +15,9 @@ extension CGFloat {
 
 struct ChatView: View, KeyboardReadable {
 
+    @ObservedObject var viewModel: ChatViewModel
+
     @State private var isKeyboardVisible = false
-    @State var message: String = ""
-    @State var messages: [String] = ["Display a short text message, called an alert, that draws attention to something new in your app.", "Play a notification sound.", "Set a badge number on the app’s icon to let the user know there are new items.", "Provide actions the user can take without opening the app.", "Show a media attachment.", "Be silent, allowing the app to perform a task in the background.", "No", "Group notifications into threads.", "Yes", "Edit or remove delivered notifications.", "Run code to change your notification before displaying it.", "Display a custom, interactive UI for your notification.", "And probably more.", "Yes", "Edit or remove delivered notifications.", "Run code to change your notification before displaying it.", "Display a custom, interactive UI for your notification.", "And probably more."]
 
     var body: some View {
         VStack(alignment: .center, spacing: 0) {
@@ -23,30 +25,30 @@ struct ChatView: View, KeyboardReadable {
                 ScrollViewReader { value in
                     VStack {
                         withAnimation {
-                            ForEach(messages.indices, id: \.self) { i in
+                            ForEach(viewModel.messages.indices, id: \.self) { i in
                                 HStack {
-                                    if messages[i] == messages[3] || messages[i] == messages[6] || messages[i] == messages[8] || i > 17{
+                                    if viewModel.messages[i].is(from: StreamManager.shared.stream.myJID ?? XMPPJID()) {
                                         Spacer()
-                                        MessageView(text: messages[i], time: "23:03", isSelf: true)
+                                        MessageView(text: viewModel.messages[i].body ?? "-", time: "23:03", isSelf: true)
                                     } else {
-                                        MessageView(text: messages[i], time: "23:03", isSelf: false)
+                                        MessageView(text: viewModel.messages[i].body ?? "-", time: "23:03", isSelf: false)
                                         Spacer()
                                     }
                                 }
                             }
-                            .onChange(of: messages) { _ in
+                            .onChange(of: viewModel.messages) { _ in
                                 withAnimation {
-                                    value.scrollTo(messages.count - 1, anchor: .bottomTrailing)
+                                    value.scrollTo(viewModel.messages.count - 1, anchor: .bottomTrailing)
                                 }
                             }
                             .onReceive(keyboardPublisher) { newIsKeyboardVisible in
                                 isKeyboardVisible = newIsKeyboardVisible
                                 withAnimation {
-                                    value.scrollTo(messages.count - 2, anchor: .topTrailing)
+                                    value.scrollTo(viewModel.messages.count - 2, anchor: .topTrailing)
                                 }
                             }
                             .onAppear {
-                                value.scrollTo(messages.count - 1, anchor: .top)
+                                value.scrollTo(viewModel.messages.count - 1, anchor: .top)
                             }
                         }
                     }
@@ -57,37 +59,47 @@ struct ChatView: View, KeyboardReadable {
             }
             Divider()
             HStack {
-                TextField("Сообщение", text: $message)
+                TextField("Сообщение", text: $viewModel.messageText)
                     .textFieldStyle(.automatic)
                     .padding(.vertical, 6)
                     .padding(.horizontal, 12)
                     .background(.background)
                 Button {
-                    messages.append(message)
-                    message = ""
+                    viewModel.messageText = ""
                 } label: {
                     Image(systemName: "arrow.up.circle.fill")
                         .font(.title)
-                        .foregroundColor(message.isEmpty ? .gray : .accentColor)
+                        .foregroundColor(viewModel.messageText.isEmpty ? .gray : .accentColor)
                 }
-                .disabled(message.isEmpty)
+                .disabled(viewModel.messageText.isEmpty)
             }
             .padding(.horizontal, .sidePadding)
             .padding(.vertical, 6)
             .background()
         }
+        .navigationTitle(viewModel.manager.room.roomJID.user ?? "Room")
         .navigationBarTitleDisplayMode(.inline)
+        .toolbar {
+            ToolbarItem(placement: .navigationBarTrailing) {
+                Button {
+                    viewModel.fetchMessages()
+                } label: {
+                    Image(systemName: "arrow.counterclockwise")
+                }
+            }
+        }
     }
 }
 
 struct ChatView_Previews: PreviewProvider {
+    static var viewModel: ChatViewModel = ChatViewModel(with: "")
     static var previews: some View {
         Group {
             NavigationView {
-                ChatView()
+                ChatView(viewModel: viewModel)
             }
             NavigationView {
-                ChatView()
+                ChatView(viewModel: viewModel)
                     .preferredColorScheme(.dark)
             }
         }

+ 47 - 0
Shared/Core/ChatViewModel.swift

@@ -0,0 +1,47 @@
+//
+//  ChatViewModel.swift
+//  Chat
+//
+//  Created by Sergey Tarasov on 10.08.2022.
+//
+
+import Foundation
+import XMPPFramework
+import XMPPFrameworkSwift
+
+class ChatViewModelExample {
+    static let messages: [String] = ["Display a short text message, called an alert, that draws attention to something new in your app.", "Play a notification sound.", "Set a badge number on the app’s icon to let the user know there are new items.", "Provide actions the user can take without opening the app.", "Show a media attachment.", "Be silent, allowing the app to perform a task in the background.", "No", "Group notifications into threads.", "Yes", "Edit or remove delivered notifications.", "Run code to change your notification before displaying it.", "Display a custom, interactive UI for your notification.", "And probably more.", "Yes", "Edit or remove delivered notifications.", "Run code to change your notification before displaying it.", "Display a custom, interactive UI for your notification.", "And probably more."]
+}
+
+class ChatViewModel: NSObject, ObservableObject {
+    let manager: RoomManager
+
+    @Published var messageText: String = ""
+    @Published var messages: [XMPPMessage] = ChatViewModelExample.messages.map { text in
+        let message = XMPPMessage()
+        message.addBody(text)
+        return message
+    }
+
+    init(with jidString: String) {
+        self.manager = RoomManager(with: jidString)
+        super.init()
+    }
+
+    func fetchMessages() {
+        self.manager.fetchMessages()
+        self.messages = manager.messages
+    }
+
+    func sendMessage() {
+        if !self.messageText.isEmpty {
+            self.manager.room.sendMessage(withBody: self.messageText)
+        }
+    }
+}
+
+extension ChatViewModel: XMPPRoomDelegate {
+    func xmppRoom(_ sender: XMPPRoom, didReceive message: XMPPMessage, fromOccupant occupantJID: XMPPJID) {
+        print(message.debugDescription)
+    }
+}

+ 95 - 0
Shared/Core/RoomsListView.swift

@@ -0,0 +1,95 @@
+//
+//  RoomsListView.swift
+//  Chat
+//
+//  Created by Sergey Tarasov on 10.08.2022.
+//
+
+import SwiftUI
+
+struct RoomsListView: View {
+    @Environment(\.colorScheme) var colorScheme
+    @ObservedObject var viewModel: RoomsListViewModel
+    var body: some View {
+        Group {
+            if viewModel.rooms.isEmpty {
+                ProgressView()
+            } else {
+                List {
+                    ForEach(viewModel.rooms.indices, id: \.self) { index in
+                        ZStack {
+                            HStack {
+                                Image(systemName: "person.crop.circle")
+                                    .resizable()
+                                    .scaledToFit()
+                                    .foregroundColor(.secondary.opacity(0.5))
+                                    .padding(.vertical, 8)
+                                VStack(alignment: .leading, spacing: 0) {
+                                    HStack {
+                                        Text(viewModel.rooms[index].name)
+                                        Spacer()
+                                        Text("23:04")
+                                            .font(.callout)
+                                            .foregroundColor(.secondary)
+                                    }
+                                    .padding(.vertical, 4)
+                                    HStack {
+                                        VStack {
+                                            Text("Сообщение \(index)")
+                                                .foregroundColor(.secondary)
+                                            Spacer()
+                                        }
+                                        Spacer()
+                                        Text("\(index)")
+                                            .font(.callout)
+                                            .foregroundColor(colorScheme == .light ? .white : .black)
+                                            .padding(.horizontal, 6)
+                                            .background(
+                                                Capsule().foregroundColor(.secondary)
+                                            )
+                                    }
+                                    .padding(.vertical, 6)
+                                }
+                            }
+                            .foregroundColor(.primary)
+                            .frame(height: 60)
+                            NavigationLink {
+                                ChatView(viewModel: ChatViewModel(with: viewModel.rooms[index].jidString))
+                            } label: {
+                                EmptyView()
+                            }
+                                .opacity(0.0)
+                        }
+                    }
+                }
+                .listStyle(.plain)
+            }
+        }
+        .navigationTitle("Чаты")
+        .navigationBarTitleDisplayMode(.inline)
+        .toolbar {
+            ToolbarItem(placement: .navigationBarTrailing) {
+                Button {
+                    viewModel.fetchChatList()
+                } label: {
+                    Image(systemName: "arrow.counterclockwise")
+                }
+            }
+        }
+    }
+}
+
+struct RoomsListView_Previews: PreviewProvider {
+    static var viewModel: RoomsListViewModel = RoomsListViewModel()
+    static var previews: some View {
+        Group {
+            NavigationView {
+                RoomsListView(viewModel: viewModel)
+            }
+            NavigationView {
+                RoomsListView(viewModel: viewModel)
+            }
+            .preferredColorScheme(.dark)
+        }
+    }
+}

+ 8 - 8
Shared/ChatListViewModel.swift → Shared/Core/RoomsListViewModel.swift

@@ -1,18 +1,18 @@
 //
-//  ChatListViewModel.swift
+//  RoomsListViewModel.swift
 //  Chat
 //
-//  Created by Sergey Tarasov on 25.07.2022.
+//  Created by Sergey Tarasov on 10.08.2022.
 //
 
 import Foundation
 import XMPPFramework
 import XMPPFrameworkSwift
 
-class ChatListViewModel: ObservableObject {
-    let xmppController = XMPPController.shared
+class RoomsListViewModel: ObservableObject {
+    let manager = MUCManager()
 
-    @Published var jids: [XMPPJID] = []
+    @Published var rooms: [Room] = []
     var timer: Timer?
 
     init() {
@@ -27,9 +27,9 @@ class ChatListViewModel: ObservableObject {
 
     @objc
     public func fetchChatList() {
-        if jids.isEmpty {
-            let jids = xmppController.xmppRosterStorage.jids(for: self.xmppController.xmppStream)
-            self.jids = jids
+        if rooms.isEmpty {
+            self.manager.fetchRooms()
+            self.rooms = self.manager.rooms
         } else {
             self.timer?.invalidate()
         }

+ 36 - 0
Shared/Managers/MUCManager.swift

@@ -0,0 +1,36 @@
+//
+//  MUCManager.swift
+//  Chat (iOS)
+//
+//  Created by Sergey Tarasov on 17.08.2022.
+//
+
+import Foundation
+import XMPPFramework
+import XMPPFrameworkSwift
+
+class MUCManager: NSObject {
+    var muc: XMPPMUCLight
+
+    var rooms: [Room] = []
+
+    override init() {
+        self.muc = XMPPMUCLight()
+
+        super.init()
+
+        self.muc.activate(StreamManager.shared.stream)
+        self.muc.addDelegate(self, delegateQueue: .main)
+    }
+
+    func fetchRooms() {
+        _ = self.muc.discoverRooms(forServiceNamed: "chat.msg.sharix-app.org")
+    }
+}
+
+extension MUCManager: XMPPMUCLightDelegate {
+    func xmppMUCLight(_ sender: XMPPMUCLight, didDiscoverRooms rooms: [DDXMLElement], forServiceNamed serviceName: String) {
+        debugPrint(rooms)
+        self.rooms = rooms.map({ Room(jidString: $0.attributeStringValue(forName: "jid") ?? "", name: $0.attributeStringValue(forName: "name") ?? "") })
+    }
+}

+ 72 - 0
Shared/Managers/RoomManager.swift

@@ -0,0 +1,72 @@
+//
+//  RoomManager.swift
+//  Chat (iOS)
+//
+//  Created by Sergey Tarasov on 17.08.2022.
+//
+
+import Foundation
+import XMPPFramework
+import XMPPFrameworkSwift
+
+class RoomManager: NSObject {
+    var room: XMPPRoom
+    var storage: XMPPRoomMemoryStorage
+    var mam: XMPPMessageArchiveManagement
+
+    var messages: [XMPPMessage] = []
+
+    init(with jidString: String) {
+        self.storage = XMPPRoomMemoryStorage()
+        self.room = XMPPRoom(roomStorage: self.storage, jid: XMPPJID(string: jidString)!)
+        self.mam = XMPPMessageArchiveManagement()
+
+        super.init()
+
+        self.room.activate(StreamManager.shared.stream)
+        self.mam.activate(StreamManager.shared.stream)
+        self.room.join(usingNickname: StreamManager.shared.stream.myJID?.user ?? "Test", history: nil)
+
+        self.room.addDelegate(self, delegateQueue: DispatchQueue.main)
+        self.mam.addDelegate(self, delegateQueue: DispatchQueue.main)
+    }
+
+    func fetchMessages() {
+        self.messages.removeAll()
+
+        let field = DDXMLElement(name: "field")
+        field.addAttribute(withName: "var", stringValue: "with")
+        field.addChild(DDXMLElement(name: "value", stringValue: self.room.roomJID.bare))
+
+        self.mam.retrieveMessageArchive(at: nil, withFields: [field], with: nil)
+        let messages = self.storage.messages() as? [XMPPMessage]
+        debugPrint(messages ?? "Empty")
+    }
+}
+
+extension RoomManager: XMPPRoomMemoryStorageDelegate {
+    func xmppRoomMemoryStorage(
+        _ sender: XMPPRoomMemoryStorage!,
+        didReceiveMessage message: XMPPRoomMessageMemoryStorageObject!,
+        fromOccupant occupant: XMPPRoomOccupantMemoryStorageObject!,
+        at index: UInt,
+        in allMessages: [Any]!
+    ) {
+        debugPrint(message.debugDescription)
+    }
+}
+
+extension RoomManager: XMPPRoomDelegate {
+    func xmppRoomDidJoin(_ sender: XMPPRoom) {
+
+    }
+}
+
+extension RoomManager: XMPPMessageArchiveManagementDelegate {
+    func xmppMessageArchiveManagement(
+        _ xmppMessageArchiveManagement: XMPPMessageArchiveManagement,
+        didReceiveMAMMessage message: XMPPMessage
+    ) {
+        self.messages.append(message)
+    }
+}

+ 21 - 0
Shared/Managers/RosterManager.swift

@@ -0,0 +1,21 @@
+//
+//  RosterManager.swift
+//  Chat (iOS)
+//
+//  Created by Sergey Tarasov on 17.08.2022.
+//
+
+import Foundation
+import XMPPFramework
+import XMPPFrameworkSwift
+
+class RosterManager {
+    var roster: XMPPRoster
+    var storage: XMPPRosterMemoryStorage
+
+    init() {
+        self.storage = XMPPRosterMemoryStorage()
+        self.roster = XMPPRoster(rosterStorage: self.storage)
+        self.roster.activate(StreamManager.shared.stream)
+    }
+}

+ 54 - 0
Shared/Managers/StreamManager.swift

@@ -0,0 +1,54 @@
+//
+//  StreamManager.swift
+//  Chat (iOS)
+//
+//  Created by Sergey Tarasov on 17.08.2022.
+//
+
+import Foundation
+import XMPPFramework
+import XMPPFrameworkSwift
+
+final class StreamManager: NSObject {
+    static let shared = StreamManager()
+
+    var stream: XMPPStream
+    var delegate: XMPPStreamDelegate?
+
+    var password: String
+
+    override init() {
+        self.stream = XMPPStream()
+
+        self.password = ""
+
+        super.init()
+        
+        self.stream.addDelegate(self, delegateQueue: DispatchQueue.main)
+    }
+
+    func setupStream(with login: String, password: String) {
+        self.stream.hostName = "msg.sharix-app.org"
+        self.stream.hostPort = 5222
+        self.stream.myJID = XMPPJID(string: login)
+        self.stream.startTLSPolicy = XMPPStreamStartTLSPolicy.allowed
+        self.password = password
+    }
+
+    func connect() {
+        if !self.stream.isDisconnected { return }
+        try! self.stream.connect(withTimeout: XMPPStreamTimeoutNone)
+    }
+}
+
+extension StreamManager: XMPPStreamDelegate {
+    func xmppStreamDidConnect(_ stream: XMPPStream) {
+        print("Stream: Connected")
+        try! stream.authenticate(withPassword: self.password)
+    }
+
+    func xmppStreamDidAuthenticate(_ sender: XMPPStream) {
+        self.stream.send(XMPPPresence())
+        print("Stream: Authenticated")
+    }
+}

+ 171 - 0
Shared/Managers/XMPPController.swift

@@ -0,0 +1,171 @@
+//
+//  XMPPController.swift
+//  Chat
+//
+//  Created by Sergey Tarasov on 25.07.2022.
+//
+
+import Foundation
+import XMPPFramework
+import XMPPFrameworkSwift
+
+/*
+final class XMPPController: NSObject {
+    static let shared = XMPPController()
+
+    var xmppStream: XMPPStream
+    var xmppReconnect: XMPPReconnect
+    var xmppRoster: XMPPRoster
+    var xmppRosterStorage: XMPPRosterMemoryStorage
+    var xmppMUC: XMPPMUCLight
+    var xmppMAM: XMPPMessageArchiveManagement
+    var xmppCapabilities: XMPPCapabilities
+    var xmppCapabilitiesStorage: XMPPCapabilitiesStorage
+    var xmppPing: XMPPPing
+    var xmppTime: XMPPTime
+
+    var users: [XMPPJID] = []
+    var rooms: [XMPPRoom] = []
+    var messages: [String] = []
+
+    var password: String?
+
+    override init() {
+        self.xmppStream = XMPPStream()
+        self.xmppReconnect = XMPPReconnect()
+        self.xmppRosterStorage = XMPPRosterMemoryStorage()
+        self.xmppRoster = XMPPRoster(rosterStorage: self.xmppRosterStorage, dispatchQueue: DispatchQueue.main)
+        self.xmppMUC = XMPPMUCLight(dispatchQueue: DispatchQueue.main)
+        self.xmppMAM = XMPPMessageArchiveManagement(dispatchQueue: DispatchQueue.main)
+        self.xmppCapabilitiesStorage = XMPPCapabilitiesCoreDataStorage.sharedInstance()
+        self.xmppCapabilities = XMPPCapabilities(capabilitiesStorage: self.xmppCapabilitiesStorage)
+        self.xmppCapabilities.autoFetchHashedCapabilities = true
+        self.xmppCapabilities.autoFetchNonHashedCapabilities = false
+        self.xmppPing = XMPPPing()
+        self.xmppTime = XMPPTime()
+
+        self.xmppReconnect.activate(self.xmppStream)
+        self.xmppRoster.activate(self.xmppStream)
+        self.xmppMUC.activate(self.xmppStream)
+        self.xmppMAM.activate(self.xmppStream)
+        self.xmppCapabilities.activate(self.xmppStream)
+        self.xmppPing.activate(self.xmppStream)
+        self.xmppTime.activate(self.xmppStream)
+
+        super.init()
+
+        self.xmppStream.addDelegate(self, delegateQueue: DispatchQueue.main)
+        self.xmppReconnect.addDelegate(self, delegateQueue: DispatchQueue.main)
+        self.xmppMUC.addDelegate(self, delegateQueue: DispatchQueue.main)
+        self.xmppMAM.addDelegate(self, delegateQueue: DispatchQueue.main)
+        self.xmppCapabilities.addDelegate(self, delegateQueue: DispatchQueue.main)
+    }
+
+    func setupStream(with login: String?, password: String?) {
+        self.password = password ?? ""
+        self.xmppStream.hostName = "msg.sharix-app.org"
+        self.xmppStream.hostPort = 5222
+        self.xmppStream.myJID = XMPPJID(string: login ?? "test11@msg.sharix-app.org")
+        self.xmppStream.startTLSPolicy = XMPPStreamStartTLSPolicy.allowed
+    }
+
+    func connect() {
+        if !self.xmppStream.isDisconnected { return }
+        try! self.xmppStream.connect(withTimeout: XMPPStreamTimeoutNone)
+    }
+
+    func getUserList() {
+        self.users = self.xmppRosterStorage.jids(for: self.xmppStream)
+    }
+
+    func getRoomList() {
+        self.xmppMUC.discoverRooms(forServiceNamed: "chat.msg.sharix-app.org")
+    }
+
+    func getMessages(from jid: XMPPJID) {
+        self.messages.removeAll()
+
+        let field = DDXMLElement(name: "field")
+        field.addAttribute(withName: "var", stringValue: "with")
+        let fieldValue = DDXMLElement(name: "value", stringValue: jid.bare)
+
+        field.addChild(fieldValue)
+
+        self.xmppMAM.retrieveMessageArchive(withFields: nil, with: nil)
+    }
+}
+
+extension XMPPController: XMPPMessageArchiveManagementDelegate {
+    func xmppMessageArchiveManagement(
+        _ xmppMessageArchiveManagement: XMPPMessageArchiveManagement,
+        didReceiveFormFields iq: XMPPIQ
+    ) {
+        print(iq.debugDescription)
+    }
+
+    func xmppMessageArchiveManagement(
+        _ xmppMessageArchiveManagement: XMPPMessageArchiveManagement,
+        didReceiveMAMMessage message: XMPPMessage
+    ) {
+        print(message.debugDescription)
+        guard let messageText = message.body else { return }
+        self.messages.append(messageText)
+    }
+
+    func xmppMessageArchiveManagement(
+        _ xmppMessageArchiveManagement: XMPPMessageArchiveManagement,
+        didFinishReceivingMessagesWith resultSet: XMPPResultSet
+    ) {
+        print(resultSet.debugDescription)
+    }
+}
+
+extension XMPPController: XMPPReconnectDelegate {
+    func xmppReconnect(
+        _ sender: XMPPReconnect,
+        shouldAttemptAutoReconnect connectionFlags: SCNetworkConnectionFlags
+    ) -> Bool {
+        return true
+    }
+}
+
+extension XMPPController: XMPPStreamDelegate {
+    func xmppStreamDidConnect(_ stream: XMPPStream) {
+        print("Stream: Connected")
+        try! stream.authenticate(withPassword: self.password ?? "test11_-")
+    }
+
+    func xmppStreamDidAuthenticate(_ sender: XMPPStream) {
+        self.xmppStream.send(XMPPPresence())
+        print("Stream: Authenticated")
+    }
+
+    func xmppStream(_ sender: XMPPStream, willReceive message: XMPPMessage) -> XMPPMessage? {
+        guard let body = message.body else {
+            return nil
+        }
+        debugPrint(body)
+        return message
+    }
+}
+
+extension XMPPController: XMPPMUCLightDelegate {
+//    func xmppMUC(_ sender: XMPPMUC, didDiscoverRooms rooms: [Any], forServiceNamed serviceName: String) {
+//        let elements = rooms.map({ try! DDXMLElement(xmlString: String(describing: $0)) })
+//        let jids = elements.compactMap({ XMPPJID(string: $0.attributeStringValue(forName: "jid") ?? "") })
+//        self.rooms = jids
+//        print(self.rooms)
+//    }
+
+    func xmppMUCLight(
+        _ sender: XMPPMUCLight,
+        didDiscoverRooms rooms: [DDXMLElement],
+        forServiceNamed serviceName: String
+    ) {
+        let jids = rooms.compactMap({ XMPPJID(string: $0.attributeStringValue(forName: "jid") ?? "") })
+        let rooms = jids.map { XMPPRoom(roomStorage: XMPPRoomMemoryStorage(), jid: $0) }
+        self.rooms = rooms
+        print(self.rooms)
+    }
+}
+*/

+ 23 - 0
Shared/Models/Room.swift

@@ -0,0 +1,23 @@
+//
+//  Room.swift
+//  Chat (iOS)
+//
+//  Created by Sergey Tarasov on 17.08.2022.
+//
+
+import Foundation
+import XMPPFramework
+import XMPPFrameworkSwift
+
+struct Room {
+    let jidString: String
+    let name: String
+    var messages: [XMPPMessage] = []
+}
+
+extension Room {
+    var jid: XMPPJID {
+        guard let jid = XMPPJID(string: jidString) else { return XMPPJID() }
+        return jid
+    }
+}

+ 22 - 0
Shared/Models/User.swift

@@ -0,0 +1,22 @@
+//
+//  User.swift
+//  Chat (iOS)
+//
+//  Created by Sergey Tarasov on 17.08.2022.
+//
+
+import Foundation
+import XMPPFramework
+import XMPPFrameworkSwift
+
+struct User {
+    let jidString: String
+    let nickname: String
+}
+
+extension User {
+    var jid: XMPPJID {
+        guard let jid = XMPPJID(string: jidString) else { return XMPPJID() }
+        return jid
+    }
+}

+ 0 - 0
Shared/KeyboardReadable.swift → Shared/Other/KeyboardReadable.swift


+ 0 - 0
Shared/MessageView.swift → Shared/Other/MessageView.swift


+ 0 - 127
Shared/XMPPController.swift

@@ -1,127 +0,0 @@
-//
-//  XMPPController.swift
-//  Chat
-//
-//  Created by Sergey Tarasov on 25.07.2022.
-//
-
-import Foundation
-import XMPPFramework
-import XMPPFrameworkSwift
-
-final class XMPPController: NSObject {
-    static let shared = XMPPController()
-
-    var xmppStream: XMPPStream
-    var xmppReconnect: XMPPReconnect
-    var xmppRoster: XMPPRoster
-    var xmppRosterStorage: XMPPRosterMemoryStorage
-    var xmppCapabilities: XMPPCapabilities
-    var xmppCapabilitiesStorage: XMPPCapabilitiesStorage
-    var xmppPing: XMPPPing
-    var xmppTime: XMPPTime
-
-    var turnSockets: NSMutableArray
-
-    var password: String?
-
-    override init() {
-        self.xmppStream = XMPPStream()
-        self.xmppReconnect = XMPPReconnect()
-        self.xmppRosterStorage = XMPPRosterMemoryStorage()
-        self.xmppRoster = XMPPRoster(rosterStorage: self.xmppRosterStorage, dispatchQueue: DispatchQueue.main)
-        self.xmppCapabilitiesStorage = XMPPCapabilitiesCoreDataStorage.sharedInstance()
-        self.xmppCapabilities = XMPPCapabilities(capabilitiesStorage: self.xmppCapabilitiesStorage)
-        self.xmppCapabilities.autoFetchHashedCapabilities = true
-        self.xmppCapabilities.autoFetchNonHashedCapabilities = false
-        self.xmppPing = XMPPPing()
-        self.xmppTime = XMPPTime()
-
-        self.xmppReconnect.activate(self.xmppStream)
-        self.xmppRoster.activate(self.xmppStream)
-        self.xmppCapabilities.activate(self.xmppStream)
-        self.xmppPing.activate(self.xmppStream)
-        self.xmppTime.activate(self.xmppStream)
-
-        self.turnSockets = []
-
-        super.init()
-
-        self.xmppStream.addDelegate(self, delegateQueue: DispatchQueue.main)
-        self.xmppReconnect.addDelegate(self, delegateQueue: DispatchQueue.main)
-        self.xmppCapabilities.addDelegate(self, delegateQueue: DispatchQueue.main)
-    }
-
-    func setupStream(with login: String?, password: String?) {
-        self.password = password ?? ""
-        self.xmppStream.hostName = "msg.sharix-app.org"
-        self.xmppStream.hostPort = 5222
-        self.xmppStream.myJID = XMPPJID(string: login ?? "test11@msg.sharix-app.org")
-        self.xmppStream.startTLSPolicy = XMPPStreamStartTLSPolicy.allowed
-    }
-
-    func connect() {
-        if !self.xmppStream.isDisconnected { return }
-        try! self.xmppStream.connect(withTimeout: XMPPStreamTimeoutNone)
-    }
-}
-
-extension XMPPController: TURNSocketDelegate {
-    func connectViaXEP65(with jid: XMPPJID) {
-        let turnSocket = TURNSocket(stream: self.xmppStream, to: jid)
-        self.turnSockets.add(turnSocket)
-        turnSocket.start(with: self, delegateQueue: DispatchQueue.main)
-    }
-
-    func xmppStream(_ sender: XMPPStream, didReceive iq: XMPPIQ) -> Bool {
-        print("---------- xmppStream:didReceiveIQ: ----------")
-        if TURNSocket.isNewStartTURNRequest(iq) {
-            let turnSocket = TURNSocket(stream: self.xmppStream, incomingTURNRequest: iq)
-            self.turnSockets.add(turnSocket)
-            turnSocket.start(with: self, delegateQueue: DispatchQueue.main)
-            return true
-        }
-        return false
-    }
-
-    func turnSocket(_ sender: TURNSocket, didSucceed socket: GCDAsyncSocket) {
-        self.turnSockets.remove(sender)
-    }
-
-    func turnSocketDidFail(_ sender: TURNSocket) {
-        self.turnSockets.remove(sender)
-    }
-}
-
-extension XMPPController: XMPPReconnectDelegate {
-    func xmppReconnect(
-        _ sender: XMPPReconnect,
-        shouldAttemptAutoReconnect connectionFlags: SCNetworkConnectionFlags
-    ) -> Bool {
-        return true
-    }
-}
-
-extension XMPPController: XMPPCapabilitiesDelegate {
-    func xmppCapabilities(
-        _ sender: XMPPCapabilities,
-        didDiscoverCapabilities caps: DDXMLElement,
-        for jid: XMPPJID
-    ) {
-        print("---------- xmppCapabilities:didDiscoverCapabilities:forJID: ----------")
-        print("jid: " + jid.debugDescription)
-        print("capabilities: " + caps.debugDescription)
-    }
-}
-
-extension XMPPController: XMPPStreamDelegate {
-    func xmppStreamDidConnect(_ stream: XMPPStream) {
-        print("Stream: Connected")
-        try! stream.authenticate(withPassword: self.password ?? "test11_-")
-    }
-
-    func xmppStreamDidAuthenticate(_ sender: XMPPStream) {
-        self.xmppStream.send(XMPPPresence())
-        print("Stream: Authenticated")
-    }
-}