Bladeren bron

update acces with self-signed

alqindiirsyam 1 jaar geleden
bovenliggende
commit
a93c7328f5
19 gewijzigde bestanden met toevoegingen van 330 en 139 verwijderingen
  1. 9 0
      appbuilder-ios/AppBuilder/AppBuilder/FirstTabViewController.swift
  2. 5 0
      appbuilder-ios/AppBuilder/AppBuilder/Info.plist
  3. 9 0
      appbuilder-ios/AppBuilder/AppBuilder/SecondTabViewController.swift
  4. 26 17
      appbuilder-ios/AppBuilder/AppBuilder/ThirdTabViewController.swift
  5. 37 33
      appbuilder-ios/AppBuilder/AppBuilder/ViewController.swift
  6. 15 2
      appbuilder-ios/NexilisLite/NexilisLite/Source/Download.swift
  7. 4 1
      appbuilder-ios/NexilisLite/NexilisLite/Source/Extension.swift
  8. 52 45
      appbuilder-ios/NexilisLite/NexilisLite/Source/FloatingButton/FloatingButton.swift
  9. 1 1
      appbuilder-ios/NexilisLite/NexilisLite/Source/IncomingThread.swift
  10. 12 0
      appbuilder-ios/NexilisLite/NexilisLite/Source/Model/Chat.swift
  11. 1 1
      appbuilder-ios/NexilisLite/NexilisLite/Source/Network.swift
  12. 21 8
      appbuilder-ios/NexilisLite/NexilisLite/Source/Nexilis.swift
  13. 8 1
      appbuilder-ios/NexilisLite/NexilisLite/Source/OutgoingThread.swift
  14. 8 2
      appbuilder-ios/NexilisLite/NexilisLite/Source/Utils.swift
  15. 53 12
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift
  16. 53 12
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift
  17. 4 1
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/EditorStarMessages.swift
  18. 3 3
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/MessageInfo.swift
  19. 9 0
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ContactChatViewController.swift

+ 9 - 0
appbuilder-ios/AppBuilder/AppBuilder/FirstTabViewController.swift

@@ -602,4 +602,13 @@ extension FirstTabViewController: WKUIDelegate, WKNavigationDelegate {
         }
         decisionHandler(.allow)
     }
+    func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
+            let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
+            completionHandler(.useCredential, credential)
+        }
+        else {
+            completionHandler(.cancelAuthenticationChallenge, nil)
+        }
+    }
 }

+ 5 - 0
appbuilder-ios/AppBuilder/AppBuilder/Info.plist

@@ -46,6 +46,11 @@
 	<string>Get File Photos from Local Dictionary for Send Message and Content Creation.</string>
 	<key>NSSpeechRecognitionUsageDescription</key>
 	<string>Used for speech recognition features.</string>
+    <key>NSAppTransportSecurity</key>
+    <dict>
+    <key>NSAllowsArbitraryLoads</key>
+    <true/>
+    </dict>
 	<key>NSUserActivityTypes</key>
 	<array>
 		<string>INSendMessageIntent</string>

+ 9 - 0
appbuilder-ios/AppBuilder/AppBuilder/SecondTabViewController.swift

@@ -340,6 +340,15 @@ class SecondTabViewController: UIViewController, UIScrollViewDelegate, UIGesture
                             self.groups.append(og)
                         }
                     }
+                    self.groups.sort { (a, b) -> Bool in
+                        if Int(a.official) == 1 {
+                            return true
+                        } else if Int(b.official) == 1 {
+                            return false
+                        } else {
+                            return Int(a.official) ?? 0 > Int(b.official) ?? 0
+                        }
+                    }
                     DispatchQueue.main.async {
                         self.tableView.reloadData()
                     }

+ 26 - 17
appbuilder-ios/AppBuilder/AppBuilder/ThirdTabViewController.swift

@@ -593,22 +593,31 @@ extension ThirdTabViewController: SFSpeechRecognizerDelegate {
 }
 
 extension ThirdTabViewController: WKUIDelegate {
-      func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
-            if self.viewIfLoaded?.window != nil {
-              if let urlStr = navigationAction.request.url?.absoluteString {
-                  collapseDocked()
-                  if urlStr.contains("nexilis/pages/tab1-main-only") || urlStr.contains("nexilis/pages/tab3-main-only") || urlStr.contains("nexilis/pages/tab1-main") || urlStr.contains("nexilis/pages/tab3-commerce") || urlStr.contains("nexilis/pages/tab1-video") || urlStr.contains("nexilis/pages/tab3-main") {
-                      ViewController.alwaysHideButton = false
-                      showTabBar()
-                      ThirdTabViewController.atFirstPage = true
-                  }
-//                  else if isUsingMyWebview() {
-//                      ViewController.alwaysHideButton = true
-//                      hideTabBar()
-//                      ThirdTabViewController.atFirstPage = false
-//                  }
+    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
+        if self.viewIfLoaded?.window != nil {
+          if let urlStr = navigationAction.request.url?.absoluteString {
+              collapseDocked()
+              if urlStr.contains("nexilis/pages/tab1-main-only") || urlStr.contains("nexilis/pages/tab3-main-only") || urlStr.contains("nexilis/pages/tab1-main") || urlStr.contains("nexilis/pages/tab3-commerce") || urlStr.contains("nexilis/pages/tab1-video") || urlStr.contains("nexilis/pages/tab3-main") {
+                  ViewController.alwaysHideButton = false
+                  showTabBar()
+                  ThirdTabViewController.atFirstPage = true
               }
-            }
-            decisionHandler(.allow)
-      }
+    //                  else if isUsingMyWebview() {
+    //                      ViewController.alwaysHideButton = true
+    //                      hideTabBar()
+    //                      ThirdTabViewController.atFirstPage = false
+    //                  }
+          }
+        }
+        decisionHandler(.allow)
+    }
+    func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
+            let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
+            completionHandler(.useCredential, credential)
+        }
+        else {
+            completionHandler(.cancelAuthenticationChallenge, nil)
+        }
+    }
 }

+ 37 - 33
appbuilder-ios/AppBuilder/AppBuilder/ViewController.swift

@@ -71,6 +71,7 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
     }
     
     func startView() {
+        navigationController?.view.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .black : .white
         Utils.addBackground(view: self.navigationController?.view)
         let topBorder = CALayer()
         topBorder.backgroundColor = UIColor.gray.withAlphaComponent(0.5).cgColor
@@ -223,32 +224,34 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
     }
     
     @objc func checkCounter() {
-        DispatchQueue.main.async { [self] in
-            if self.viewControllers?.firstIndex(of: secondTab!) != nil {
-                let counter = queryCountCounter()
-                let indexSecondTab = self.viewControllers?.firstIndex(of: secondTab!)
-                let viewSecondTab = self.tabBar.items?[indexSecondTab!].value(forKey: "view") as! UIView
-                if counter > 0 {
-                    if !indicatorCounterFB.isDescendant(of: viewSecondTab) {
-                        let viewSecondTab = self.tabBar.items?[indexSecondTab!].value(forKey: "view") as! UIView
-                        indicatorCounterFB.backgroundColor = .red
-                        indicatorCounterFB.layer.cornerRadius = 7.5
-                        indicatorCounterFB.clipsToBounds = true
-                        viewSecondTab.addSubview(indicatorCounterFB)
-                        indicatorCounterFB.anchor(top: viewSecondTab.topAnchor, right: viewSecondTab.rightAnchor, paddingRight: 20, height: 15, minWidth: 15, maxWidth: 20)
-                        indicatorCounterFB.addSubview(labelCounterFB)
-                        labelCounterFB.anchor(left: indicatorCounterFB.leftAnchor, right: indicatorCounterFB.rightAnchor, paddingLeft: 5, paddingRight: 5, centerX: indicatorCounterFB.centerXAnchor, centerY: indicatorCounterFB.centerYAnchor)
-                        labelCounterFB.font = .systemFont(ofSize: 10)
-                        labelCounterFB.textColor = .white
-                    }
-                    if counter > 99 {
-                        labelCounterFB.text = "99+"
+        DispatchQueue.global().async {
+            DispatchQueue.main.async { [self] in
+                if self.viewControllers?.firstIndex(of: secondTab!) != nil {
+                    let counter = queryCountCounter()
+                    let indexSecondTab = self.viewControllers?.firstIndex(of: secondTab!)
+                    let viewSecondTab = self.tabBar.items?[indexSecondTab!].value(forKey: "view") as! UIView
+                    if counter > 0 {
+                        if !indicatorCounterFB.isDescendant(of: viewSecondTab) {
+                            let viewSecondTab = self.tabBar.items?[indexSecondTab!].value(forKey: "view") as! UIView
+                            indicatorCounterFB.backgroundColor = .red
+                            indicatorCounterFB.layer.cornerRadius = 7.5
+                            indicatorCounterFB.clipsToBounds = true
+                            viewSecondTab.addSubview(indicatorCounterFB)
+                            indicatorCounterFB.anchor(top: viewSecondTab.topAnchor, right: viewSecondTab.rightAnchor, paddingRight: 45, height: 15, minWidth: 15, maxWidth: 20)
+                            indicatorCounterFB.addSubview(labelCounterFB)
+                            labelCounterFB.anchor(left: indicatorCounterFB.leftAnchor, right: indicatorCounterFB.rightAnchor, paddingLeft: 5, paddingRight: 5, centerX: indicatorCounterFB.centerXAnchor, centerY: indicatorCounterFB.centerYAnchor)
+                            labelCounterFB.font = .systemFont(ofSize: 10)
+                            labelCounterFB.textColor = .white
+                        }
+                        if counter > 99 {
+                            labelCounterFB.text = "99+"
+                        } else {
+                            labelCounterFB.text = "\(counter)"
+                        }
                     } else {
-                        labelCounterFB.text = "\(counter)"
-                    }
-                } else {
-                    if indicatorCounterFB.isDescendant(of: viewSecondTab) {
-                        indicatorCounterFB.removeFromSuperview()
+                        if indicatorCounterFB.isDescendant(of: viewSecondTab) {
+                            indicatorCounterFB.removeFromSuperview()
+                        }
                     }
                 }
             }
@@ -257,13 +260,11 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
     
     private func queryCountCounter() -> Int32 {
         var counter: Int32?
-        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
-            Database.shared.database?.inTransaction({ (fmdb, rollback) in
-                if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "SELECT SUM(counter) FROM MESSAGE_SUMMARY"), cursor.next() {
-                    counter = cursor.int(forColumnIndex: 0)
-                    cursor.close()
-                }
-            })
+        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+            if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "SELECT SUM(counter) FROM MESSAGE_SUMMARY"), cursor.next() {
+                counter = cursor.int(forColumnIndex: 0)
+                cursor.close()
+            }
         })
         return counter ?? 0
     }
@@ -1285,7 +1286,10 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
     }
     
     public static func getDataImageFromUrl(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
-        URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
+        let urlConfig = URLSessionConfiguration.default
+        let sessionDelegate = SelfSignedURLSessionDelegate()
+        let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
+        session.dataTask(with: url, completionHandler: completion).resume()
     }
 
 }

+ 15 - 2
appbuilder-ios/NexilisLite/NexilisLite/Source/Download.swift

@@ -72,7 +72,7 @@ public class Download {
             ]
             //print("HEADER: \(headers)")
             do {
-                let downloadRequest = AF.download(fullURL, headers: headers)
+                let downloadRequest = SessionManager.shared.session.download(fullURL, headers: headers)
                 .downloadProgress(queue: downloadBufferQueue) { progress in
                     let frac = progress.fractionCompleted*100
                     if frac != 100.0 {
@@ -94,7 +94,7 @@ public class Download {
                     }
                     else {
                         let statusCode = result.response?.statusCode
-                        //print("Response fail: \(statusCode)")
+                        print("Response fail: \(result.debugDescription)")
                         completion(filename,0)
                     }
                 }
@@ -132,3 +132,16 @@ public class Download {
         return result
     }
 }
+
+public class SessionManager {
+    static let shared = SessionManager()
+    let session: Session
+
+    private init() {
+        let sessionConfiguration = URLSessionConfiguration.default
+        sessionConfiguration.timeoutIntervalForRequest = 60
+        let serverTrustManager = ServerTrustManager(allHostsMustBeEvaluated: false,
+                                                    evaluators: [Utils.getURLBase().components(separatedBy: "/")[2]: DisabledTrustEvaluator()])
+        self.session = Session(configuration: sessionConfiguration, serverTrustManager: serverTrustManager)
+    }
+}

+ 4 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/Extension.swift

@@ -1110,7 +1110,10 @@ extension UIImageView {
 
         let url = URL(string: urlString)!
         currentURL = url
-        let task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
+        let urlConfig = URLSessionConfiguration.default
+        let sessionDelegate = SelfSignedURLSessionDelegate()
+        let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
+        let task = session.dataTask(with: url) { [weak self] data, response, error in
             self?.currentTask = nil
 
             //error handling

+ 52 - 45
appbuilder-ios/NexilisLite/NexilisLite/Source/FloatingButton/FloatingButton.swift

@@ -295,11 +295,11 @@ public class FloatingButton: UIView {
                         let newButton = UIButton()
                         newButton.heightAnchor.constraint(equalToConstant: defaultWidthHeightMenuFB).isActive = true
                         newButton.translatesAutoresizingMaskIntoConstraints = false
-                        DispatchQueue.global().async {
-                            let data = try? Data(contentsOf: URL(string: Utils.getURLBase() + "get_file_from_path?img=\(icon)")!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
-                            DispatchQueue.main.async {
-                                if data != nil {
-                                    if let image = UIImage(data: data!) {
+                        DispatchQueue.global().async {[self] in
+                            getDataImageFromUrl(from: URL(string: Utils.getURLBase() + "get_file_from_path?img=" + icon)!) { data, response, error in
+                                guard let data = data, error == nil else { return }
+                                DispatchQueue.main.async {
+                                    if let image = UIImage(data: data) {
                                         newButton.setImage(image, for: .normal)
                                     }
                                 }
@@ -375,11 +375,11 @@ public class FloatingButton: UIView {
                                             newButton.setImage(UIImage(named: mode == MODE_HORIZONTAL_SIDE_TAB ? "pb_button_hrz_more" : mode == MODE_HORIZONTAL_ANIMATION ? "pb_button_hrz_anim_more" : "pb_button_others", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), for: .normal)
                                         }
                                         if !icon.isEmpty {
-                                            DispatchQueue.global().async {
-                                                let data = try? Data(contentsOf: URL(string: "https://nexilis.io/get_file?account=\(Nexilis.sAPIKey)&image=\(icon)")!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
-                                                DispatchQueue.main.async {
-                                                    if data != nil {
-                                                        newButton.setImage(UIImage(data: data!), for: .normal)
+                                            DispatchQueue.global().async { [self] in
+                                                getDataImageFromUrl(from: URL(string: Utils.getURLBase() + "get_file_from_path?img=" + icon)!) { data, response, error in
+                                                    guard let data = data, error == nil else { return }
+                                                    DispatchQueue.main.async {
+                                                        newButton.setImage(UIImage(data: data), for: .normal)
                                                     }
                                                 }
                                             }
@@ -404,6 +404,13 @@ public class FloatingButton: UIView {
         }
     }
     
+    private func getDataImageFromUrl(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
+        let urlConfig = URLSessionConfiguration.default
+        let sessionDelegate = SelfSignedURLSessionDelegate()
+        let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
+        session.dataTask(with: url, completionHandler: completion).resume()
+    }
+    
     func getDefaultButton() {
         let data = [Nexilis.IDX_NOTIF_CENTER, Nexilis.IDX_CC, Nexilis.IDX_CONVERSATION, Nexilis.IDX_CALL, Nexilis.IDX_STREAM]
         for i in 0..<data.count {
@@ -502,35 +509,37 @@ public class FloatingButton: UIView {
     }
     
     @objc func checkCounter() {
-        let counter = queryCountCounter()
-        if counter > 0 {
-            DispatchQueue.main.async { [self] in
-                if button_fb2 != nil && !indicatorCounterFB.isDescendant(of: button_fb2) {
-                    button_fb2.addSubview(indicatorCounterFB)
-                    indicatorCounterFB.layer.cornerRadius = 7.5
-                    indicatorCounterFB.layer.masksToBounds = true
-                    indicatorCounterFB.backgroundColor = .systemRed
-                    indicatorCounterFB.anchor(top: button_fb2.topAnchor, left: button_fb2.leftAnchor, height: 15, minWidth: 15, maxWidth: 20)
-                    indicatorCounterFB.addSubview(labelCounterFB)
-                    labelCounterFB.anchor(left: indicatorCounterFB.leftAnchor, right: indicatorCounterFB.rightAnchor, paddingLeft: 5, paddingRight: 5, centerX: indicatorCounterFB.centerXAnchor, centerY: indicatorCounterFB.centerYAnchor)
-                    labelCounterFB.font = .systemFont(ofSize: 10)
-                    labelCounterFB.textColor = .white
-                }
-                if !indicatorCounterFBBig.isDescendant(of: nexilis_button){
-                    nexilis_button.addSubview(indicatorCounterFBBig)
-                    indicatorCounterFBBig.tintColor = .systemRed
-                    indicatorCounterFBBig.image = UIImage(systemName: "staroflife.circle.fill")
-                    indicatorCounterFBBig.anchor(top: nexilis_button.topAnchor, left: nexilis_button.leftAnchor, paddingTop: 5, paddingLeft: 5, width: 15, height: 15)
-                }
-                labelCounterFB.text = "\(counter)"
-            }
-        } else {
-            DispatchQueue.main.async { [self] in
-                if button_fb2 != nil && indicatorCounterFB.isDescendant(of: button_fb2) {
-                    indicatorCounterFB.removeFromSuperview()
+        DispatchQueue.global().async { [self] in
+            let counter = queryCountCounter()
+            if counter > 0 {
+                DispatchQueue.main.async { [self] in
+                    if button_fb2 != nil && !indicatorCounterFB.isDescendant(of: button_fb2) {
+                        button_fb2.addSubview(indicatorCounterFB)
+                        indicatorCounterFB.layer.cornerRadius = 7.5
+                        indicatorCounterFB.layer.masksToBounds = true
+                        indicatorCounterFB.backgroundColor = .systemRed
+                        indicatorCounterFB.anchor(top: button_fb2.topAnchor, left: button_fb2.leftAnchor, height: 15, minWidth: 15, maxWidth: 20)
+                        indicatorCounterFB.addSubview(labelCounterFB)
+                        labelCounterFB.anchor(left: indicatorCounterFB.leftAnchor, right: indicatorCounterFB.rightAnchor, paddingLeft: 5, paddingRight: 5, centerX: indicatorCounterFB.centerXAnchor, centerY: indicatorCounterFB.centerYAnchor)
+                        labelCounterFB.font = .systemFont(ofSize: 10)
+                        labelCounterFB.textColor = .white
+                    }
+                    if !indicatorCounterFBBig.isDescendant(of: nexilis_button){
+                        nexilis_button.addSubview(indicatorCounterFBBig)
+                        indicatorCounterFBBig.tintColor = .systemRed
+                        indicatorCounterFBBig.image = UIImage(systemName: "staroflife.circle.fill")
+                        indicatorCounterFBBig.anchor(top: nexilis_button.topAnchor, left: nexilis_button.leftAnchor, paddingTop: 5, paddingLeft: 5, width: 15, height: 15)
+                    }
+                    labelCounterFB.text = "\(counter)"
                 }
-                if indicatorCounterFBBig.isDescendant(of: nexilis_button) {
-                    indicatorCounterFBBig.removeFromSuperview()
+            } else {
+                DispatchQueue.main.async { [self] in
+                    if button_fb2 != nil && indicatorCounterFB.isDescendant(of: button_fb2) {
+                        indicatorCounterFB.removeFromSuperview()
+                    }
+                    if indicatorCounterFBBig.isDescendant(of: nexilis_button) {
+                        indicatorCounterFBBig.removeFromSuperview()
+                    }
                 }
             }
         }
@@ -538,13 +547,11 @@ public class FloatingButton: UIView {
     
     private func queryCountCounter() -> Int32 {
         var counter: Int32?
-        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
-            Database.shared.database?.inTransaction({ (fmdb, rollback) in
-                if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "SELECT SUM(counter) FROM MESSAGE_SUMMARY"), cursor.next() {
-                    counter = cursor.int(forColumnIndex: 0)
-                    cursor.close()
-                }
-            })
+        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+            if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "SELECT SUM(counter) FROM MESSAGE_SUMMARY"), cursor.next() {
+                counter = cursor.int(forColumnIndex: 0)
+                cursor.close()
+            }
         })
         return counter ?? 0
     }

+ 1 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/IncomingThread.swift

@@ -60,7 +60,7 @@ class IncomingThread {
     }
     
     private func process(message: TMessage) {
-        print("incoming process", message.toLogString())
+        //print("incoming process", message.toLogString())
         if message.getCode() == CoreMessage_TMessageCode.LOGIN_FILE {
             loginFile(message: message)
         } else if message.getCode() == CoreMessage_TMessageCode.PUSH_MYSELF || message.getCode() == CoreMessage_TMessageCode.PUSH_MYSELF_ACK || message.getCode() == CoreMessage_TMessageCode.PULL_MYSELF {

+ 12 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/Model/Chat.swift

@@ -90,6 +90,18 @@ public class Chat: Model {
         return messages
     }
     
+//    public static func getUcList() {
+//        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+//            let query = " select ms.message_id from MESSAGE_SUMMARY ms"
+//            if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: query) {
+//                while cursorData.next() {
+//                    print("HMMKAMPRET2 \(cursorData.string(forColumnIndex: 0))")
+//                }
+//                cursorData.close()
+//            }
+//        })
+//    }
+    
     public static func getData(messageId: String = "") -> [Chat] {
         var chats: [Chat] = []
         Database.shared.database?.inTransaction({ (fmdb, rollback) in

+ 1 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/Network.swift

@@ -227,7 +227,7 @@ public class Network {
         ]
         //print("HEADER: \(headers)")
         
-        let uploadRequest = AF.upload(multipartFormData: { (multipartFormData: MultipartFormData) in
+        let uploadRequest = SessionManager.shared.session.upload(multipartFormData: { (multipartFormData: MultipartFormData) in
             for (key, value) in parameters {
                 multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
                 //print(multipartFormData)

+ 21 - 8
appbuilder-ios/NexilisLite/NexilisLite/Source/Nexilis.swift

@@ -713,11 +713,12 @@ public class Nexilis: NSObject {
         var result = [String]()
         let url = URL(string: "https://nexilis.io/dipp/NuN1v3rs3/Qm3r4i0/get_ip?account=\(apiKey)")!
         let urlConfig = URLSessionConfiguration.default
+        let sessionDelegate = SelfSignedURLSessionDelegate()
         urlConfig.requestCachePolicy = .returnCacheDataElseLoad
         urlConfig.timeoutIntervalForRequest = 10.0
         urlConfig.timeoutIntervalForResource = 10.0
         let semaphore = DispatchSemaphore(value: 0)
-        let task = URLSession(configuration: urlConfig).dataTask(with: url) {(data, response, error) in
+        let task = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil).dataTask(with: url) {(data, response, error) in
             guard let data = data else {
                 semaphore.signal()
                 return
@@ -759,16 +760,17 @@ public class Nexilis: NSObject {
         
         let url = URL(string: "\(Utils.getDomainOpr())dipp/NuN1v3rs3/Qm3r4i0/get_ip_domain?account=\(apiKey)")!
         let urlConfig = URLSessionConfiguration.default
+        let sessionDelegate = SelfSignedURLSessionDelegate()
         urlConfig.requestCachePolicy = .returnCacheDataElseLoad
         urlConfig.timeoutIntervalForRequest = 10.0
         urlConfig.timeoutIntervalForResource = 10.0
         let semaphore = DispatchSemaphore(value: 0)
-        let task = URLSession(configuration: urlConfig).dataTask(with: url) {(data, response, error) in
+        let task = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil).dataTask(with: url) {(data, response, error) in
             guard let data = data,
                 let url = response?.url,
                 let httpResponse = response as? HTTPURLResponse,
                 let fields = httpResponse.allHeaderFields as? [String: String] else {
-                //print("MASUK SINI0 \(url)")
+                print("MASUK SINI0 \(url)")
                 semaphore.signal()
                 return
             }
@@ -781,12 +783,12 @@ public class Nexilis: NSObject {
             }
 
             let dataEncode = String(data: data, encoding: .utf8)!
-//            //print("dataEncode \(dataEncode.trimmingCharacters(in: .whitespacesAndNewlines))")
+            print("dataEncode \(dataEncode.trimmingCharacters(in: .whitespacesAndNewlines))")
 //            //print("decrypt \(Utils.decrypt(str: "4=sm<wmpm1ir==>wtxxl"))")
             if !dataEncode.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
                 let dataDecodeBase64 = String(data: Data(base64Encoded: dataEncode)!, encoding: .utf8)!
                 let dataRealDecode = Utils.decrypt(str: dataDecodeBase64)
-//                //print("dataRealDecode \(dataRealDecode)")
+                print("dataRealDecode \(dataRealDecode)")
                 do {
                     if let jsonData = dataRealDecode.data(using: .utf8), let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
                         var newDomain = jsonObject["domain"] as! String
@@ -821,11 +823,12 @@ public class Nexilis: NSObject {
         var result = false
         let url = URL(string: "\(newDomain)dipp/NuN1v3rs3/Qm3r4i0/get_ip_domain?account=\(Nexilis.sAPIKey)")!
         let urlConfig = URLSessionConfiguration.default
+        let sessionDelegate = SelfSignedURLSessionDelegate()
         urlConfig.requestCachePolicy = .returnCacheDataElseLoad
         urlConfig.timeoutIntervalForRequest = 10.0
         urlConfig.timeoutIntervalForResource = 10.0
         let semaphore = DispatchSemaphore(value: 0)
-        let task = URLSession(configuration: urlConfig).dataTask(with: url) {(data, response, error) in
+        let task = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil).dataTask(with: url) {(data, response, error) in
             if let httpResponse = response as? HTTPURLResponse {
                 if httpResponse.statusCode == 200 {
                     guard let url = response?.url,
@@ -1290,7 +1293,6 @@ public class Nexilis: NSObject {
             let broadcast_flag = message.getBody(key: CoreMessage_TMessageKey.BROADCAST_FLAG, default_value: "0")
             let is_call_center = message.getBody(key: CoreMessage_TMessageKey.IS_CALL_CENTER, default_value: "0")
             let call_center_id = message.getBody(key: CoreMessage_TMessageKey.CALL_CENTER_ID, default_value: "")
-            let opposite_pin = message.getBody(key: CoreMessage_TMessageKey.OPPOSITE_PIN, default_value: "")
             //print("prepare save db")
             do {
                 _ = try Database.shared.insertRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
@@ -1380,7 +1382,7 @@ public class Nexilis: NSObject {
                 do {
                     var queryGetLastMessageId = "SELECT message_id FROM MESSAGE where f_pin = '\(pin)' OR l_pin = '\(pin)' order by server_date desc LIMIT 1"
                     if scope == "4" {
-                        queryGetLastMessageId = "SELECT message_id FROM MESSAGE where l_pin = '\(pin)' order by server_date desc LIMIT 1"
+                        queryGetLastMessageId = "SELECT message_id FROM MESSAGE where l_pin = '\(pin)' AND chat_id = '' order by server_date desc LIMIT 1"
                         if !chat_id.isEmpty {
                             queryGetLastMessageId = "SELECT message_id FROM MESSAGE where chat_id = '\(pin)' order by server_date desc LIMIT 1"
                         }
@@ -3882,3 +3884,14 @@ extension Nexilis: QLPreviewControllerDataSource {
         return previewItem!
     }
 }
+
+public class SelfSignedURLSessionDelegate: NSObject, URLSessionTaskDelegate, URLSessionDataDelegate {
+    public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
+            if let serverTrust = challenge.protectionSpace.serverTrust {
+                let credential = URLCredential(trust: serverTrust)
+                completionHandler(.useCredential, credential)
+            }
+        }
+    }
+}

+ 8 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/OutgoingThread.swift

@@ -342,12 +342,19 @@ class OutgoingThread {
                 _ = Database.shared.deleteRecord(fmdb: fmdb, table: "MESSAGE_SUMMARY", _where: "message_id = '\(messageId)'")
                 let l_pin = message.getBody(key: CoreMessage_TMessageKey.L_PIN)
                 let chat = message.getBody(key: CoreMessage_TMessageKey.CHAT_ID)
+                let scope = message.getBody(key: CoreMessage_TMessageKey.SCOPE_ID)
                 do {
                     var pin = l_pin
                     if !chat.isEmpty {
                         pin = chat
                     }
-                    let queryGetLastMessageId = "SELECT message_id FROM MESSAGE where opposite_pin = '\(pin)' OR l_pin = '\(pin)' order by server_date desc LIMIT 1"
+                    var queryGetLastMessageId = "SELECT message_id FROM MESSAGE where f_pin = '\(pin)' OR l_pin = '\(pin)' order by server_date desc LIMIT 1"
+                    if scope == "4" {
+                        queryGetLastMessageId = "SELECT message_id FROM MESSAGE where l_pin = '\(pin)' AND chat_id = '' order by server_date desc LIMIT 1"
+                        if !chat.isEmpty {
+                            queryGetLastMessageId = "SELECT message_id FROM MESSAGE where chat_id = '\(pin)' order by server_date desc LIMIT 1"
+                        }
+                    }
                     var messageId = ""
                     if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: queryGetLastMessageId), cursorData.next() {
                         messageId = cursorData.string(forColumnIndex: 0) ?? ""

+ 8 - 2
appbuilder-ios/NexilisLite/NexilisLite/Source/Utils.swift

@@ -357,7 +357,10 @@ public final class Utils {
         request.setValue(Utils.getUserAgent(), forHTTPHeaderField: "User-Agent")
         request.setValue(Utils.getCookiesMobile(), forHTTPHeaderField: "Cookie")
         //print("DATA SEND MOBILE \(Utils.getUserAgent()) <> \(Utils.getCookiesMobile())")
-        let task = URLSession.shared.dataTask(with: request, completionHandler: completion)
+        let urlConfig = URLSessionConfiguration.default
+        let sessionDelegate = SelfSignedURLSessionDelegate()
+        let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
+        let task = session.dataTask(with: request, completionHandler: completion)
         task.resume()
     }
     
@@ -380,7 +383,10 @@ public final class Utils {
         request.setValue("application/json", forHTTPHeaderField: "Accept")
         request.httpBody = jsonData
         //print("DATA SEND MOBILE \(Utils.getUserAgent()) <> \(Utils.getCookiesMobile())")
-        let task = URLSession.shared.dataTask(with: request, completionHandler: completion)
+        let urlConfig = URLSessionConfiguration.default
+        let sessionDelegate = SelfSignedURLSessionDelegate()
+        let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
+        let task = session.dataTask(with: request, completionHandler: completion)
         task.resume()
     }
     

+ 53 - 12
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift

@@ -539,7 +539,7 @@ public class EditorGroup: UIViewController {
                     row["blog_id"] = cursorData.string(forColumnIndex: 18)
                     row["credential"] = cursorData.string(forColumnIndex: 19)
                     row["isSelected"] = false
-                    if row["credential"] as! String == "1" {
+                    if row["credential"] != nil && row["credential"] as! String == "1" {
                         let idMe = UserDefaults.standard.string(forKey: "me")!
                         if row["f_pin"] as! String == idMe {
                             let second = getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(row["server_date"] as! String)!), end: Date())
@@ -943,13 +943,13 @@ public class EditorGroup: UIViewController {
                     }
                     row["chat_date"] = "Today".localized()
                     row["blog_id"] = chatData[CoreMessage_TMessageKey.BLOG_ID]
-                    if row["credential"] as! String == "1" {
+                    if row["credential"] != nil && row["credential"] as! String == "1" {
                         self.listTimerCredential[row["message_id"] as! String] = 60
                     }
                     self.counter += 1
                     self.dataMessages.append(row)
                     self.tableChatView.insertRows(at: [IndexPath(row: self.dataMessages.filter({ $0["chat_date"] as! String == self.dataDates[self.dataDates.count - 1]}).count - 1, section: self.dataDates.count - 1)], with: .fade)
-                    if row["credential"] as! String == "1" {
+                    if row["credential"] != nil && row["credential"] as! String == "1" {
                         var timer = Timer()
                         var minute = 60
                         self.timerCredential[row["message_id"] as! String] = timer
@@ -1917,15 +1917,22 @@ public class EditorGroup: UIViewController {
     
     private func checkNewMessage(tableView: UITableView) {
         currentIndexpath = tableView.indexPathsForVisibleRows?.last
-        if currentIndexpath != nil {
-            let dataMessages = dataMessages.filter({ $0["chat_date"] as! String == dataDates[currentIndexpath!.section] })
+        let indexFirst = tableView.indexPathsForVisibleRows?.first
+        if indexFirst != nil {
+            let dataMessages = dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexFirst!.section] })
             if dataMessages.count == 0 {
                 return
             }
-            if currentIndexpath!.section == dataDates.count - 1 && currentIndexpath!.row != dataMessages.count - 1 && currentIndexpath!.row != dataMessages.count - 2 && !buttonScrollToBottom.isDescendant(of: self.view) {
-                addButtonScrollToBottom()
-                addCounterAtButttonScrollToBottom()
-            } else if currentIndexpath!.section == dataDates.count - 1 && currentIndexpath!.row == dataMessages.count - 1 {
+            let contentHeight = tableView.contentSize.height
+            let scrollViewHeight = tableView.frame.height
+            let fullContentOffset = contentHeight - scrollViewHeight
+            let contentOffsetY = tableView.contentOffset.y
+            if ((currentIndexpath!.section == dataDates.count - 1 && indexFirst!.row != dataMessages.count - 1) || indexFirst!.section != dataDates.count - 1) && fullContentOffset - contentOffsetY > 100 {
+                if !buttonScrollToBottom.isDescendant(of: self.view) {
+                    addButtonScrollToBottom()
+                    addCounterAtButttonScrollToBottom()
+                }
+            } else if (indexFirst!.section == dataDates.count - 1 && indexFirst!.row == dataMessages.count - 1) || fullContentOffset - contentOffsetY < 50 {
                 if buttonScrollToBottom.isDescendant(of: self.view) {
                     buttonScrollToBottom.removeConstraints(buttonScrollToBottom.constraints)
                     buttonScrollToBottom.removeFromSuperview()
@@ -2264,7 +2271,10 @@ extension EditorGroup: UITextViewDelegate {
                         }
                     }
                 } else {
-                    let slp = SwiftLinkPreview(session: URLSession.shared,
+                    let urlConfig = URLSessionConfiguration.default
+                    let sessionDelegate = SelfSignedURLSessionDelegate()
+                    let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
+                    let slp = SwiftLinkPreview(session: session,
                                    workQueue: SwiftLinkPreview.defaultWorkQueue,
                                    responseQueue: DispatchQueue.main,
                                        cache: DisabledCache.instance)
@@ -3910,6 +3920,20 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
         }
         
         func modifyText() {
+            
+            func ranges(of word: String, in string: String) -> [NSRange] {
+                var result: [NSRange] = []
+                var startIndex = string.startIndex
+                
+                while let range = string[startIndex...].range(of: word) {
+                    let nsRange = NSRange(range, in: string)
+                    result.append(nsRange)
+                    startIndex = range.upperBound
+                }
+                
+                return result
+            }
+            
             messageText.isUserInteractionEnabled = false
             if !textChat!.isEmpty {
                 if textChat!.contains("■"){
@@ -3919,12 +3943,26 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
                 let listTextEnter = textChat!.split(separator: "\n")
                 let finalAtribute = textChat!.richText(group_id: self.dataGroup["group_id"] as! String)
                 var containsLink = false
+                var listRange: [NSRange] = []
                 for j in 0...listTextEnter.count - 1 {
                     let listText = listTextEnter[j].split(separator: " ")
                     if listText.count > 0 {
                         for i in 0...listText.count - 1 {
                             if listText[i].lowercased().checkStartWithLink() {
-                                let rangeTapLink = (finalAtribute.string as NSString).range(of: String(listText[i]))
+                                var rangeTapLink = (finalAtribute.string as NSString).range(of: String(listText[i]))
+                                func checkContainsRange() {
+                                    if listRange.contains(rangeTapLink) {
+                                        let allRanges = ranges(of: String(listText[i]), in: finalAtribute.string)
+                                        for allRange in allRanges {
+                                            if !listRange.contains(allRange) {
+                                                rangeTapLink = allRange
+                                                break
+                                            }
+                                        }
+                                    }
+                                    listRange.append(rangeTapLink)
+                                }
+                                checkContainsRange()
                                 finalAtribute.addAttributes([.foregroundColor: UIColor.blue, .underlineStyle: NSUnderlineStyle.single.rawValue], range: rangeTapLink)
                                 if !containsLink {
                                     containsLink = true
@@ -4436,7 +4474,10 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
                     }
                 })
                 if dataURL.isEmpty {
-                    let slp = SwiftLinkPreview(session: URLSession.shared,
+                    let urlConfig = URLSessionConfiguration.default
+                    let sessionDelegate = SelfSignedURLSessionDelegate()
+                    let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
+                    let slp = SwiftLinkPreview(session: session,
                                    workQueue: SwiftLinkPreview.defaultWorkQueue,
                                    responseQueue: DispatchQueue.main,
                                        cache: DisabledCache.instance)

+ 53 - 12
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift

@@ -861,7 +861,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                     }
                     row["chat_date"] = chatDate(stringDate: row["server_date"] as! String)
                     row["isSelected"] = false
-                    if row["credential"] as! String == "1" {
+                    if row["credential"] != nil && row["credential"] as! String == "1" {
                         let idMe = UserDefaults.standard.string(forKey: "me")!
                         if row["f_pin"] as! String == idMe {
                             let second = getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(row["server_date"] as! String)!), end: Date())
@@ -1019,7 +1019,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                     }
                     row["chat_date"] = chatDate(stringDate: row["server_date"] as! String)
                     row["isSelected"] = false
-                    if row["credential"] as! String == "1" {
+                    if row["credential"] != nil && row["credential"] as! String == "1" {
                         let idMe = UserDefaults.standard.string(forKey: "me")!
                         if row["f_pin"] as! String == idMe {
                             let second = getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(row["server_date"] as! String)!), end: Date())
@@ -1468,12 +1468,12 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                     row["chat_date"] = "Today".localized()
                     row["blog_id"] = chatData[CoreMessage_TMessageKey.BLOG_ID]
                     self.counter += 1
-                    if row["credential"] as! String == "1" {
+                    if row["credential"] != nil && row["credential"] as! String == "1" {
                         self.listTimerCredential[row["message_id"] as! String] = 60
                     }
                     self.dataMessages.append(row)
                     self.tableChatView.insertRows(at: [IndexPath(row: self.dataMessages.filter({ $0["chat_date"] as! String == self.dataDates[self.dataDates.count - 1]}).count - 1, section: self.dataDates.count - 1)], with: .none)
-                    if row["credential"] as! String == "1" {
+                    if row["credential"] != nil && row["credential"] as! String == "1" {
                         var timer = Timer()
                         var minute = 60
                         self.timerCredential[row["message_id"] as! String] = timer
@@ -3011,15 +3011,22 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
 //            }
 //        }
         currentIndexpath = tableView.indexPathsForVisibleRows?.last
-        if currentIndexpath != nil {
+        let indexFirst = tableView.indexPathsForVisibleRows?.first
+        if indexFirst != nil {
             let dataMessages = dataMessages.filter({ $0["chat_date"] as! String == dataDates[currentIndexpath!.section] })
             if dataMessages.count == 0 || dataMessages.count - 1 < currentIndexpath!.row {
                 return
             }
-            if currentIndexpath!.section == dataDates.count - 1 && currentIndexpath!.row != dataMessages.count - 1 && currentIndexpath!.row != dataMessages.count - 2 && !buttonScrollToBottom.isDescendant(of: self.view) {
-                addButtonScrollToBottom()
-                addCounterAtButttonScrollToBottom()
-            } else if currentIndexpath!.section == dataDates.count - 1 && currentIndexpath!.row == dataMessages.count - 1 {
+            let contentHeight = tableView.contentSize.height
+            let scrollViewHeight = tableView.frame.height
+            let fullContentOffset = contentHeight - scrollViewHeight
+            let contentOffsetY = tableView.contentOffset.y
+            if ((currentIndexpath!.section == dataDates.count - 1 && indexFirst!.row != dataMessages.count - 1) || indexFirst!.section != dataDates.count - 1) && fullContentOffset - contentOffsetY > 100 {
+                if !buttonScrollToBottom.isDescendant(of: self.view) {
+                    addButtonScrollToBottom()
+                    addCounterAtButttonScrollToBottom()
+                }
+            } else if (indexFirst!.section == dataDates.count - 1 && indexFirst!.row == dataMessages.count - 1) || fullContentOffset - contentOffsetY < 50 {
                 if buttonScrollToBottom.isDescendant(of: self.view) {
                     buttonScrollToBottom.removeConstraints(buttonScrollToBottom.constraints)
                     buttonScrollToBottom.removeFromSuperview()
@@ -3243,7 +3250,10 @@ extension EditorPersonal: UITextViewDelegate {
                         }
                     }
                 } else {
-                    let slp = SwiftLinkPreview(session: URLSession.shared,
+                    let urlConfig = URLSessionConfiguration.default
+                    let sessionDelegate = SelfSignedURLSessionDelegate()
+                    let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
+                    let slp = SwiftLinkPreview(session: session,
                                    workQueue: SwiftLinkPreview.defaultWorkQueue,
                                    responseQueue: DispatchQueue.main,
                                        cache: DisabledCache.instance)
@@ -5034,6 +5044,20 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
         }
         
         func modifyText() {
+            
+            func ranges(of word: String, in string: String) -> [NSRange] {
+                var result: [NSRange] = []
+                var startIndex = string.startIndex
+                
+                while let range = string[startIndex...].range(of: word) {
+                    let nsRange = NSRange(range, in: string)
+                    result.append(nsRange)
+                    startIndex = range.upperBound
+                }
+                
+                return result
+            }
+            
             messageText.isUserInteractionEnabled = false
             if !textChat.isEmpty {
                 if textChat.contains("■"){
@@ -5043,12 +5067,26 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
                 let listTextEnter = textChat.split(separator: "\n")
                 let finalAtribute = textChat.richText()
                 var containsLink = false
+                var listRange: [NSRange] = []
                 for j in 0...listTextEnter.count - 1 {
                     let listText = listTextEnter[j].split(separator: " ")
                     if listText.count > 0 {
                         for i in 0...listText.count - 1 {
                             if listText[i].lowercased().checkStartWithLink() {
-                                let rangeTapLink = (finalAtribute.string as NSString).range(of: String(listText[i]))
+                                var rangeTapLink = (finalAtribute.string as NSString).range(of: String(listText[i]))
+                                func checkContainsRange() {
+                                    if listRange.contains(rangeTapLink) {
+                                        let allRanges = ranges(of: String(listText[i]), in: finalAtribute.string)
+                                        for allRange in allRanges {
+                                            if !listRange.contains(allRange) {
+                                                rangeTapLink = allRange
+                                                break
+                                            }
+                                        }
+                                    }
+                                    listRange.append(rangeTapLink)
+                                }
+                                checkContainsRange()
                                 finalAtribute.addAttributes([.foregroundColor: UIColor.blue, .underlineStyle: NSUnderlineStyle.single.rawValue], range: rangeTapLink)
                                 if !containsLink {
                                     containsLink = true
@@ -5559,7 +5597,10 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
                     }
                 })
                 if dataURL.isEmpty {
-                    let slp = SwiftLinkPreview(session: URLSession.shared,
+                    let urlConfig = URLSessionConfiguration.default
+                    let sessionDelegate = SelfSignedURLSessionDelegate()
+                    let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
+                    let slp = SwiftLinkPreview(session: session,
                                    workQueue: SwiftLinkPreview.defaultWorkQueue,
                                    responseQueue: DispatchQueue.main,
                                        cache: DisabledCache.instance)

+ 4 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/EditorStarMessages.swift

@@ -773,7 +773,10 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
                     }
                 })
                 if dataURL.isEmpty {
-                    let slp = SwiftLinkPreview(session: URLSession.shared,
+                    let urlConfig = URLSessionConfiguration.default
+                    let sessionDelegate = SelfSignedURLSessionDelegate()
+                    let session = URLSession(configuration: urlConfig, delegate: sessionDelegate, delegateQueue: nil)
+                    let slp = SwiftLinkPreview(session: session,
                                    workQueue: SwiftLinkPreview.defaultWorkQueue,
                                    responseQueue: DispatchQueue.main,
                                        cache: DisabledCache.instance)

+ 3 - 3
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/MessageInfo.swift

@@ -20,7 +20,7 @@ class MessageInfo: UIViewController, UITableViewDelegate, UITableViewDataSource
         super.viewDidLoad()
 
         title = "Message Info".localized()
-        view.backgroundColor = .white
+        view.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .white
         navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
         navigationController?.navigationBar.topItem?.backButtonTitle = "Back".localized()
         
@@ -201,7 +201,7 @@ class MessageInfo: UIViewController, UITableViewDelegate, UITableViewDataSource
                 let dataStatusRead = dataStatus.filter({ ($0["status"] as! String) == "4" })
                 let dataStatusDelivered = dataStatus.filter({ ($0["status"] as! String) == "3" })
                 
-                cell.backgroundColor = .white
+                cell.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .white
                 
                 var content = cell.defaultContentConfiguration()
                 content.textProperties.font = UIFont.systemFont(ofSize: 14)
@@ -339,7 +339,7 @@ class MessageInfo: UIViewController, UITableViewDelegate, UITableViewDataSource
         }
         
         if indexPath.row != 0 {
-            cell.backgroundColor = .white
+            cell.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : .white
             var content = cell.defaultContentConfiguration()
             content.textProperties.font = UIFont.systemFont(ofSize: 14)
             content.imageProperties.maximumSize = CGSize(width: 24, height: 24)

+ 9 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ContactChatViewController.swift

@@ -229,6 +229,15 @@ class ContactChatViewController: UITableViewController {
                             self.groups.append(og)
                         }
                     }
+                    self.groups.sort { (a, b) -> Bool in
+                        if Int(a.official) == 1 {
+                            return true
+                        } else if Int(b.official) == 1 {
+                            return false
+                        } else {
+                            return Int(a.official) ?? 0 > Int(b.official) ?? 0
+                        }
+                    }
                     DispatchQueue.main.async {
                         self.tableView.reloadData()
                     }