alqindiirsyam 6 сар өмнө
parent
commit
d1a9da1b35

BIN
.DS_Store


+ 4 - 4
AppBuilder/AppBuilder.xcodeproj/project.pbxproj

@@ -100,7 +100,7 @@
 /* End PBXFileReference section */
 
 /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
-		CDE50D8B2D62E0B00094C30D /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
+		CD41C01A2D6423E300BBEAA8 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
 			isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
 			membershipExceptions = (
 				Info.plist,
@@ -110,7 +110,7 @@
 /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
 
 /* Begin PBXFileSystemSynchronizedRootGroup section */
-		CDE50D872D62E08C0094C30D /* AppBuilderShare */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (CDE50D8B2D62E0B00094C30D /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = AppBuilderShare; sourceTree = "<group>"; };
+		CD41C0162D6423D300BBEAA8 /* AppBuilderShare */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (CD41C01A2D6423E300BBEAA8 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = AppBuilderShare; sourceTree = "<group>"; };
 /* End PBXFileSystemSynchronizedRootGroup section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -136,7 +136,7 @@
 			isa = PBXGroup;
 			children = (
 				2401CE98275490DB00B323BB /* AppBuilder */,
-				CDE50D872D62E08C0094C30D /* AppBuilderShare */,
+				CD41C0162D6423D300BBEAA8 /* AppBuilderShare */,
 				D555D5E529C625D02EB38D49 /* Frameworks */,
 				6E32BCCF4DE50EE1A90E8AAE /* Pods */,
 				2401CE97275490DB00B323BB /* Products */,
@@ -238,7 +238,7 @@
 			dependencies = (
 			);
 			fileSystemSynchronizedGroups = (
-				CDE50D872D62E08C0094C30D /* AppBuilderShare */,
+				CD41C0162D6423D300BBEAA8 /* AppBuilderShare */,
 			);
 			name = AppBuilderShare;
 			productName = AppBuilderShare;

+ 11 - 8
AppBuilder/AppBuilder/FirstTabViewController.swift

@@ -34,6 +34,7 @@ class FirstTabViewController: UIViewController, UIScrollViewDelegate, UIGestureR
     var imageVideoPicker: ImageVideoPicker!
     var blockedCertificate = ""
     var allowedURLs = Set<String>()
+    var loadingURL = false
     
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -62,7 +63,6 @@ class FirstTabViewController: UIViewController, UIScrollViewDelegate, UIGestureR
         refreshControl.addTarget(self, action: #selector(reloadWebView(_:)), for: .valueChanged)
         webView.scrollView.addSubview(refreshControl)
         webView.scrollView.delegate = self
-        webView.navigationDelegate = self
         webView.allowsBackForwardNavigationGestures = true
         
         let contentController = self.webView.configuration.userContentController
@@ -685,25 +685,30 @@ extension FirstTabViewController: SFSpeechRecognizerDelegate {
     }
 }
 
-extension FirstTabViewController: WKUIDelegate, WKNavigationDelegate {
+extension FirstTabViewController: WKUIDelegate {
     func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping @MainActor (WKNavigationActionPolicy) -> Void) {
         guard let url = navigationAction.request.url else {
             decisionHandler(.cancel)
             return
         }
+        if loadingURL {
+            decisionHandler(.cancel)
+            return
+        }
+        loadingURL = true
         if allowedURLs.contains(url.absoluteString) {
             if ViewController.alwaysHideButton {
                 ViewController.alwaysHideButton = false
                 showTabBar()
             }
-            print("✅ URL already allowed: \(url)")
+            loadingURL = false
             decisionHandler(.allow)
             return
         }
         validateSSLCertificate(url: url) { isValid in
-            print("is VALID? : \(isValid)")
             if isValid {
                 self.allowedURLs.insert(url.absoluteString)
+                self.loadingURL = false
                 decisionHandler(.allow)
             } else {
                 let host = url.host ?? ""
@@ -726,9 +731,11 @@ extension FirstTabViewController: WKUIDelegate, WKNavigationDelegate {
                             }
                         }
                         self.allowedURLs.insert(url.absoluteString)
+                        self.loadingURL = false
                         decisionHandler(.allow)
                     }
                     let noAction = UIAlertAction(title: "No", style: .cancel) { _ in
+                        self.loadingURL = false
                         decisionHandler(.cancel)
                     }
                     alert.addAction(yesAction)
@@ -745,7 +752,6 @@ extension FirstTabViewController: WKUIDelegate, WKNavigationDelegate {
 
         let task = session.dataTask(with: request) { _, response, error in
             if let error = error {
-                print("SSL Validation Error: \(error.localizedDescription)")
                 completion(false)
                 return
             }
@@ -767,10 +773,8 @@ extension FirstTabViewController: URLSessionDelegate {
             if let jsonData = storedCertificate.data(using: .utf8),
                let certJson = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: String] {
                 if publicKeyHash == certJson[domain] {
-                    print("✅ Certificate Matched. Allowing Navigation.")
                     completionHandler(.useCredential, URLCredential(trust: serverTrust))
                 } else {
-                    print("❌ Certificate Mismatch! Blocking Navigation.")
                     blockedCertificate = publicKeyHash
                     completionHandler(.cancelAuthenticationChallenge, nil)
                 }
@@ -786,7 +790,6 @@ extension FirstTabViewController: URLSessionDelegate {
         
         var error: Unmanaged<CFError>?
         guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
-            print("❌ Failed to extract public key")
             return nil
         }
         

+ 11 - 8
AppBuilder/AppBuilder/ThirdTabViewController.swift

@@ -11,7 +11,7 @@ import NexilisLite
 import Speech
 import CommonCrypto
 
-class ThirdTabViewController: UIViewController, UIScrollViewDelegate, UIGestureRecognizerDelegate, WKNavigationDelegate, WKScriptMessageHandler, ImageVideoPickerDelegate  {
+class ThirdTabViewController: UIViewController, UIScrollViewDelegate, UIGestureRecognizerDelegate, WKScriptMessageHandler, ImageVideoPickerDelegate  {
 
     var webView: WKWebView!
     var address = ""
@@ -35,6 +35,7 @@ class ThirdTabViewController: UIViewController, UIScrollViewDelegate, UIGestureR
     var imageVideoPicker: ImageVideoPicker!
     var blockedCertificate = ""
     var allowedURLs = Set<String>()
+    var loadingURL = false
     
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -63,7 +64,6 @@ class ThirdTabViewController: UIViewController, UIScrollViewDelegate, UIGestureR
         refreshControl.addTarget(self, action: #selector(reloadWebView(_:)), for: .valueChanged)
         webView.scrollView.addSubview(refreshControl)
         webView.scrollView.delegate = self
-        webView.navigationDelegate = self
         webView.allowsBackForwardNavigationGestures = true
         
         let contentController = self.webView.configuration.userContentController
@@ -711,19 +711,24 @@ extension ThirdTabViewController: WKUIDelegate {
             decisionHandler(.cancel)
             return
         }
+        if loadingURL {
+            decisionHandler(.cancel)
+            return
+        }
+        loadingURL = true
         if allowedURLs.contains(url.absoluteString) {
             if ViewController.alwaysHideButton {
                 ViewController.alwaysHideButton = false
                 showTabBar()
             }
-            print("✅ URL already allowed: \(url)")
+            loadingURL = false
             decisionHandler(.allow)
             return
         }
         validateSSLCertificate(url: url) { isValid in
-            print("is VALID? : \(isValid)")
             if isValid {
                 self.allowedURLs.insert(url.absoluteString)
+                self.loadingURL = false
                 decisionHandler(.allow)
             } else {
                 let host = url.host ?? ""
@@ -746,9 +751,11 @@ extension ThirdTabViewController: WKUIDelegate {
                             }
                         }
                         self.allowedURLs.insert(url.absoluteString)
+                        self.loadingURL = false
                         decisionHandler(.allow)
                     }
                     let noAction = UIAlertAction(title: "No", style: .cancel) { _ in
+                        self.loadingURL = false
                         decisionHandler(.cancel)
                     }
                     alert.addAction(yesAction)
@@ -765,7 +772,6 @@ extension ThirdTabViewController: WKUIDelegate {
 
         let task = session.dataTask(with: request) { _, response, error in
             if let error = error {
-                print("SSL Validation Error: \(error.localizedDescription)")
                 completion(false)
                 return
             }
@@ -787,10 +793,8 @@ extension ThirdTabViewController: URLSessionDelegate {
             if let jsonData = storedCertificate.data(using: .utf8),
                let certJson = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: String] {
                 if publicKeyHash == certJson[domain] {
-                    print("✅ Certificate Matched. Allowing Navigation.")
                     completionHandler(.useCredential, URLCredential(trust: serverTrust))
                 } else {
-                    print("❌ Certificate Mismatch! Blocking Navigation.")
                     blockedCertificate = publicKeyHash
                     completionHandler(.cancelAuthenticationChallenge, nil)
                 }
@@ -806,7 +810,6 @@ extension ThirdTabViewController: URLSessionDelegate {
         
         var error: Unmanaged<CFError>?
         guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
-            print("❌ Failed to extract public key")
             return nil
         }
         

+ 1 - 0
AppBuilder/Podfile

@@ -4,5 +4,6 @@ platform :ios, '14.0'
 target 'AppBuilder' do
   use_frameworks!
   pod 'NexilisLite', :path => '../NexilisLite'
+  pod 'StreamShield', :path => '../StreamShield'
   pod 'NotificationBannerSwift', :git => 'https://github.com/Daltron/NotificationBanner.git', :tag => '4.0.0'
 end

BIN
NexilisLite/.DS_Store


+ 24 - 27
NexilisLite/NexilisLite/Source/APIS.swift

@@ -512,6 +512,9 @@ public class APIS: NSObject {
         vc.fromAPI = true
         Utils.addBackground(view: navigationController.view)
         navigationController.defaultStyle()
+        if UIApplication.shared.visibleViewController is UINavigationController && (UIApplication.shared.visibleViewController as! UINavigationController).rootViewController is SettingTableViewController {
+            return
+        }
         if UIApplication.shared.visibleViewController?.navigationController != nil {
             UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
         } else {
@@ -849,6 +852,7 @@ public class APIS: NSObject {
     
     public static var uuidCall: UUID?
     public static var fpinCall: String?
+    public static var listIdentifierNotif: [String] = []
     public static func showNotificationNexilis(_ userInfo: [AnyHashable : Any]) {
 //        let center = UNUserNotificationCenter.current()
 //        let content = UNMutableNotificationContent()
@@ -890,7 +894,10 @@ public class APIS: NSObject {
                                     DispatchQueue.global().async {
                                         _ = Nexilis.write(message: CoreMessage_TMessageBank.getAckMessage(messageId: message[CoreMessage_TMessageKey.MESSAGE_ID] ?? ""))
                                     }
-                                    addNotificationNexilis(messageToSave)
+                                    if !listIdentifierNotif.contains(message[CoreMessage_TMessageKey.MESSAGE_ID] ?? ""){
+                                        listIdentifierNotif.append(message[CoreMessage_TMessageKey.MESSAGE_ID] ?? "")
+                                        APIS.addNotificationNexilis(messageToSave)
+                                    }
                                 }
                             } else if code == "CL03" {
                                 let callFromName = data["call-from-name"] as? String ?? ""
@@ -1086,6 +1093,11 @@ public class APIS: NSObject {
             if let userInfo = response.notification.request.content.userInfo as? [String: String] {
                 let id = userInfo["id"] ?? ""
                 let type = userInfo["type"] ?? ""
+                if let navigationC = UIApplication.shared.visibleViewController as? UINavigationController {
+                    if navigationC.viewControllers[navigationC.viewControllers.count - 1] is EditorPersonal || navigationC.viewControllers[navigationC.viewControllers.count - 1] is EditorGroup {
+                        navigationC.popViewController(animated: true)
+                    }
+                }
                 if type == "0" {
                     if User.getDataCanNil(pin: id) == nil {
                         return
@@ -1106,14 +1118,8 @@ public class APIS: NSObject {
                     let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
                     navigationController.navigationBar.titleTextAttributes = textAttributes
                     if UIApplication.shared.visibleViewController?.navigationController != nil {
-                        if UIApplication.shared.visibleViewController?.navigationController?.presentedViewController == editorPersonalVC {
-                            return
-                        }
                         UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
                     } else {
-                        if UIApplication.shared.visibleViewController == editorPersonalVC {
-                            return
-                        }
                         UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
                     }
                 } else if type == "1" {
@@ -1152,14 +1158,8 @@ public class APIS: NSObject {
                     let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
                     navigationController.navigationBar.titleTextAttributes = textAttributes
                     if UIApplication.shared.visibleViewController?.navigationController != nil {
-                        if UIApplication.shared.visibleViewController?.navigationController?.presentedViewController == editorGroupVC {
-                            return
-                        }
                         UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
                     } else {
-                        if UIApplication.shared.visibleViewController == editorGroupVC {
-                            return
-                        }
                         UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
                     }
                 } else if type == "CL03" {
@@ -1242,19 +1242,16 @@ public class APIS: NSObject {
     }
     
     public static func enterForeground() {
-        do {
-            if !Nexilis.afterConnect {
-                var id = Utils.getConnectionID()
-                if id.isEmpty {
-                    let sDID = UIDevice.current.identifierForVendor?.uuidString ?? "UNK-DEVICE"
-                    id = String(sDID[sDID.index(sDID.endIndex, offsetBy: -5)...])
-                    Utils.setConnectionID(value: id)
+        DispatchQueue.global().async {
+            do {
+                if !Nexilis.afterConnect {
+                    var id = Utils.getConnectionID()
+                    try API.initConnection(sAPIK: Nexilis.sAPIKey, cbiI: Callback(), sTCPAddr: Nexilis.ADDRESS, nTCPPort: Nexilis.PORT, sUserID: id, sStartWH: "09:00")
                 }
-                try API.initConnection(sAPIK: Nexilis.sAPIKey, cbiI: Callback(), sTCPAddr: Nexilis.ADDRESS, nTCPPort: Nexilis.PORT, sUserID: id, sStartWH: "09:00")
-                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
+                listIdentifierNotif.removeAll()
+                Nexilis.afterConnect = false
+            } catch {
             }
-            Nexilis.afterConnect = false
-        } catch {
         }
         checkDataForShareExtension()
         UIApplication.shared.applicationIconBadgeNumber = 0
@@ -1356,9 +1353,6 @@ public class APIS: NSObject {
                                     }
                                     message = CoreMessage_TMessageBank.sendMessage(l_pin: groupId.isEmpty ? idContact : groupId, message_scope_id: scopeId, status: scopeId == "3" ? "1" : "2", message_text: data, credential: "0", attachment_flag: attachmentFlag, ex_blog_id: "", message_large_text: "", ex_format: "", image_id: imageId, audio_id: "", video_id: videoId, file_id: renamedFileId, thumb_id: thumb, reff_id: "", read_receipts: "4", chat_id: chatId, is_call_center: "0", call_center_id: "", opposite_pin: scopeId == "3" ? (User.getMyPin() ?? "") : idContact, gif_id: "", isForwarded: "0", isSecret: "0")
                                     Nexilis.addQueueMessage(message: message)
-                                    DispatchQueue.global().asyncAfter(deadline: .now() + 0.5, execute: {
-                                        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
-                                    })
                                     userDefaults.set("", forKey: "sharedItem")
                                     userDefaults.synchronize()
                                 }
@@ -1369,6 +1363,9 @@ public class APIS: NSObject {
                     }
                 }
             }
+            DispatchQueue.global().asyncAfter(deadline: .now() + 0.5, execute: {
+                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
+            })
         }
     }
     

+ 1 - 1
NexilisLite/NexilisLite/Source/FloatingButton/FloatingButton.swift

@@ -99,7 +99,7 @@ public class FloatingButton: UIView, UIGestureRecognizerDelegate {
             }
             var urlGif = URL(string: configModeFB == MODE_VERTICAL_ANIMATION ? Utils.getIconCenterAnim2() : Utils.getIconCenterAnim4())
             if (urlGif == nil) {
-                urlGif = Bundle.resourceBundle(for: Nexilis.self).url(forResource: configModeFB == MODE_VERTICAL_ANIMATION ? "pb_def_icon_mode2" : "pb_def_icon_mode4", withExtension: "gif")! //resourcesMediaBundle
+                urlGif = Bundle.resourceBundle(for: Nexilis.self).url(forResource: configModeFB == MODE_VERTICAL_ANIMATION ? "pb_def_icon_mode2" : "pb_def_icon_mode4", withExtension: "gif") ?? nil
                 if urlGif == nil {
                     urlGif = Bundle.resourcesMediaBundle(for: Nexilis.self).url(forResource: configModeFB == MODE_VERTICAL_ANIMATION ? "pb_def_icon_mode2" : "pb_def_icon_mode4", withExtension: "gif")!
                 }

+ 4 - 15
NexilisLite/NexilisLite/Source/IncomingThread.swift

@@ -218,7 +218,7 @@ class IncomingThread {
             _ = Nexilis.responseString(packetId: packetId, message: "00", timeout: 3000)
         }
         DispatchQueue.global().async {
-            var viewController = UIApplication.shared.visibleViewController
+            let viewController = UIApplication.shared.visibleViewController
             DispatchQueue.main.async {
                 if !CheckConnection.isConnectedToNetwork()  || API.nGetCLXConnState() == 0 {
                     let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
@@ -1301,20 +1301,9 @@ class IncomingThread {
         }
         DispatchQueue.main.async {
             if APIS.checkAppStateisBackground() {
-                UNUserNotificationCenter.current().getPendingNotificationRequests{ notificationsPending in
-                    let identifier = message.getBody(key : CoreMessage_TMessageKey.MESSAGE_ID, default_value : "")
-                    let matchingNotifications = notificationsPending.filter { $0.identifier == identifier }
-                    if matchingNotifications.isEmpty {
-                        UNUserNotificationCenter.current().getDeliveredNotifications { notifications in
-                            let identifier = message.getBody(key : CoreMessage_TMessageKey.MESSAGE_ID, default_value : "")
-                            let matchingNotifications = notifications.filter { $0.request.identifier == identifier }
-                            if matchingNotifications.isEmpty && message.getBody(key : CoreMessage_TMessageKey.LAST_EDIT, default_value : "0") == "0" {
-                                DispatchQueue.main.async {
-                                    APIS.addNotificationNexilis(message)
-                                }
-                            }
-                        }
-                    }
+                if !APIS.listIdentifierNotif.contains(message.getBody(key: CoreMessage_TMessageKey.MESSAGE_ID)){
+                    APIS.listIdentifierNotif.append(message.getBody(key: CoreMessage_TMessageKey.MESSAGE_ID))
+                    APIS.addNotificationNexilis(message)
                 }
             }
         }

+ 18 - 8
NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift

@@ -145,7 +145,7 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
         }
         updateProfile()
         let indexPath = tableChatView.indexPathsForVisibleRows?.first
-        if indexPath != nil {
+        if indexPath != nil && currentIndexpath != nil {
             let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
             let isPinned = headerRect.origin.y <= tableChatView.contentOffset.y
             if listViewOnSection.count != 0 && listViewOnSection.count - 1 == indexPath!.section && isPinned {
@@ -416,7 +416,7 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
         changeAppBar()
         getData()
         getCounter()
-        if counter > 0 {
+        if counter > 0 && dataMessages.count >= counter {
             markerCounter = dataMessages[dataMessages.count - counter]["message_id"] as? String
         }
         
@@ -485,7 +485,7 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
         let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
         tapGesture.cancelsTouchesInView = false
         tableChatView.addGestureRecognizer(tapGesture)
-        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.6, execute: {
             if self.tableChatView.alpha != 1.0 {
                 UIView.animate(withDuration: 0.5, animations: {
                     self.tableChatView.alpha = 1.0
@@ -1954,6 +1954,9 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
     }
     
     private func addButtonScrollToBottom() {
+        if tableChatView.alpha != 1 {
+            return
+        }
         self.view.addSubview(buttonScrollToBottom)
         buttonScrollToBottom.translatesAutoresizingMaskIntoConstraints = false
         NSLayoutConstraint.activate([
@@ -2499,6 +2502,8 @@ extension EditorGroup: UITextViewDelegate {
         if self.isEditingMessage && textView == editTextView {
             if textView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
                 buttonSendEdit.isEnabled = false
+            } else if !buttonSendEdit.isEnabled {
+                buttonSendEdit.isEnabled = true
             }
         }
     }
@@ -3949,16 +3954,21 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
     //    }
     
     public func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
-        if self.tableChatView.alpha != 1.0 {
-            UIView.animate(withDuration: 0.5, animations: {
-                self.tableChatView.alpha = 1.0
-            })
-        }
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.6, execute: {
+            if self.tableChatView.alpha != 1.0 {
+                UIView.animate(withDuration: 0.5, animations: {
+                    self.tableChatView.alpha = 1.0
+                })
+            }
+        })
     }
     
     public func scrollViewDidScroll(_ scrollView: UIScrollView) {
         lastY = scrollView.contentOffset.y
         DispatchQueue.main.async { [self] in
+            if tableChatView.alpha != 1 {
+                return
+            }
             checkNewMessage(tableView: self.tableChatView)
         }
     }

+ 21 - 12
NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift

@@ -148,7 +148,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         updateProfile()
         gettingDataMessage = false
         let indexPath = tableChatView.indexPathsForVisibleRows?.first
-        if indexPath != nil {
+        if indexPath != nil && currentIndexpath != nil {
             let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
             let isPinned = headerRect.origin.y <= tableChatView.contentOffset.y
             if listViewOnSection.count != 0 && listViewOnSection.count - 1 == indexPath!.section && isPinned {
@@ -567,7 +567,6 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                 markerCounter = dataMessages[dataMessages.count - counter]["message_id"] as? String
             }
             
-            tableChatView.alpha = 0
             if !referenceMessageId.isEmpty {
                 if dataMessages.firstIndex(where: {$0["message_id"] as? String == referenceMessageId} ) != 0 {
                     DispatchQueue.main.async {
@@ -633,13 +632,13 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         } else {
             tableChatView.scrollToBottom(isAnimated: false)
         }
-        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
-            if self.tableChatView.alpha != 1.0 {
+        if tableChatView.alpha != 1 {
+            DispatchQueue.main.asyncAfter(deadline: .now() + 0.6, execute: {
                 UIView.animate(withDuration: 0.5, animations: {
                     self.tableChatView.alpha = 1.0
                 })
-            }
-        })
+            })
+        }
         for data in listTimerCredential {
             if data.value > 0 {
                 var second = data.value
@@ -3310,6 +3309,9 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
     }
     
     private func addButtonScrollToBottom() {
+        if tableChatView.alpha != 1 {
+            return
+        }
         self.view.addSubview(buttonScrollToBottom)
         buttonScrollToBottom.translatesAutoresizingMaskIntoConstraints = false
         NSLayoutConstraint.activate([
@@ -3670,6 +3672,8 @@ extension EditorPersonal: UITextViewDelegate {
         if self.isEditingMessage && textView == editTextView {
             if textView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
                 buttonSendEdit.isEnabled = false
+            } else if !buttonSendEdit.isEnabled {
+                buttonSendEdit.isEnabled = true
             }
         }
     }
@@ -5066,9 +5070,11 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
 //    }
     
     public func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
-        if self.tableChatView.alpha != 1.0 {
-            UIView.animate(withDuration: 0.5, animations: {
-                self.tableChatView.alpha = 1.0
+        if tableChatView.alpha != 1 {
+            DispatchQueue.main.asyncAfter(deadline: .now() + 0.6, execute: {
+                UIView.animate(withDuration: 0.5, animations: {
+                    self.tableChatView.alpha = 1.0
+                })
             })
         }
     }
@@ -5076,6 +5082,9 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
     public func scrollViewDidScroll(_ scrollView: UIScrollView) {
         lastY = scrollView.contentOffset.y
         DispatchQueue.main.async { [self] in
+            if tableChatView.alpha != 1 {
+                return
+            }
             checkNewMessage(tableView: self.tableChatView)
         }
     }
@@ -5301,7 +5310,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
                 let containerButton = UIView()
                 cell.contentView.addSubview(containerButton)
                 containerButton.translatesAutoresizingMaskIntoConstraints = false
-                containerButton.topAnchor.constraint(equalTo: containerMessage.bottomAnchor, constant: 5).isActive = true
+                containerButton.topAnchor.constraint(equalTo: messageText.bottomAnchor, constant: 5).isActive = true
                 containerButton.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor, constant: -5).isActive = true
                 containerButton.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 15).isActive = true
                 containerButton.widthAnchor.constraint(equalToConstant: self.view!.frame.size.width * 0.9).isActive = true
@@ -7636,7 +7645,7 @@ extension UITableView {
     
     func scrollToBottom(isAnimated:Bool = true){
         
-        DispatchQueue.main.async {
+        DispatchQueue.main.asyncAfter(deadline: .now() + (isAnimated ? 0 : 0.6), execute: {
             if self.numberOfSections == 0 {
                 return
             }
@@ -7646,7 +7655,7 @@ extension UITableView {
             if indexPath.row != -1 {
                 self.scrollToRow(at: indexPath, at: .bottom, animated: isAnimated)
             }
-        }
+        })
     }
     
     func scrollToTop(isAnimated:Bool = true) {

BIN
StreamShield/.DS_Store


+ 1 - 1
StreamShield/StreamShield.podspec

@@ -22,7 +22,7 @@ Pod::Spec.new do |spec|
   spec.source_files = 'StreamShield/Source/**/*'
   spec.resource_bundles = { 'StreamShield' => ['StreamShield/Resource/**/*']}
   spec.swift_version = '5.5.1'
-  spec.dependency 'nuSDKService', '~> 3.0.1'
+  spec.dependency 'nuSDKService', '~> 4.0.5'
   spec.ios.vendored_frameworks = "StreamShield.framework"
   spec.pod_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64', 'ENABLE_BITCODE' => 'NO' }
   spec.user_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64', 'ENABLE_BITCODE' => 'NO' }

+ 37 - 12
StreamShield/StreamShield/Source/SecurityShield.swift

@@ -12,6 +12,7 @@ import AVFoundation
 import AVKit
 import CoreTelephony
 import CryptoKit
+import MachO
 
 public class SecurityShield: NSObject {
     
@@ -94,7 +95,6 @@ public class SecurityShield: NSObject {
 
 private class Process: NSObject {
     static func check(dataSS : String) {
-        print("dataSS = \(dataSS)")
         if !dataSS.isEmpty {
             if let jsonArray = try? JSONSerialization.jsonObject(with: dataSS.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
                 do {
@@ -234,7 +234,6 @@ private class Process: NSObject {
     private static var screen: UIView!
     @objc static func preventScreenRecording() {
         let isCaptured = UIScreen.main.isCaptured
-        //print("isCaptured: \(isCaptured)")
         if isCaptured {
             blurScreen()
         }
@@ -252,7 +251,6 @@ private class Process: NSObject {
         if let window = UIApplication.shared.windows.first {
             window.addSubview(screen)
         } else {
-            //print("window nil")
         }
     }
 
@@ -432,7 +430,6 @@ private class Process: NSObject {
                     return true
                 }
             } else {
-                print("Failed to compare versions")
             }
         }
         return false
@@ -703,9 +700,6 @@ private class Process: NSObject {
     }
     
     private static func isRooted() -> Bool {
-        #if arch(i386) || arch(x86_64)
-        return false
-        #else
         let fileManager = FileManager.default
         if fileManager.fileExists(atPath: "/Applications/Cydia.app") ||
             fileManager.fileExists(atPath: "/Library/MobileSubstrate/MobileSubstrate.dylib") ||
@@ -716,12 +710,20 @@ private class Process: NSObject {
             fileManager.fileExists(atPath: "/Applications/FakeApp.app") {
             return true
         }
-        #endif
         let filePath = "/private/var/mobile/Library/Preferences/com.apple.springboard.plist"
         if FileManager.default.fileExists(atPath: filePath) {
             return true
         }
         
+        let testPath = "/private/" + UUID().uuidString
+        do {
+            try "test".write(toFile: testPath, atomically: true, encoding: .utf8)
+            try FileManager.default.removeItem(atPath: testPath)
+            return true
+        } catch {
+            // Could not write outside sandbox
+        }
+        
         return false
     }
     
@@ -743,6 +745,33 @@ private class Process: NSObject {
         }
     }
     
+    private static func isHooked() -> Bool {
+        let suspiciousLibraries = [
+            "FridaGadget",
+            "libsubstrate.dylib",
+            "libcycript.dylib",
+            "cyinject.dylib",
+            "MobileSubstrate.dylib",
+            "SSLKillSwitch.dylib",
+            "CydiaSubstrate",
+            "TweakInject",
+            "0Shadow",
+            "shadow.dylib"
+        ]
+        
+        for i in 0..<_dyld_image_count() {
+            if let imageName = _dyld_get_image_name(i) {
+                let name = String(cString: imageName)
+                for library in suspiciousLibraries {
+                    if name.lowercased().contains(library.lowercased()) {
+                        return true
+                    }
+                }
+            }
+        }
+        return false
+    }
+    
     private static func isScreenCasting() -> Bool {
         return checkForExternalScreen()
     }
@@ -2010,7 +2039,6 @@ public class TMessageSS {
     public func toBytes() -> [UInt8] {
         let data:String = pack()
         var result: [UInt8] = Array(data.utf8)
-        //print("[bytes_processing] build bytes data:" + String(result.count) + ", media:" + String(getMedia().count))
         if (!getMedia().isEmpty) {
             for index in 0...getMedia().count - 1 {
                 result.append(getMedia()[index])
@@ -2080,7 +2108,6 @@ public class TMessageSS {
             result   = true
         }
         else {
-            //print("[bytes_processing] Invalid header length: " + String(headers.count))
         }
         return result
     }
@@ -2323,7 +2350,6 @@ public class SecureUserDefaultsSS {
         let encoder = JSONEncoder()
         guard let encodedData = try? encoder.encode(value),
               let encryptedData = try? encrypt(data: encodedData) else {
-            print("Failed to encrypt data")
             return
         }
         defaults.set(encryptedData, forKey: key)
@@ -2333,7 +2359,6 @@ public class SecureUserDefaultsSS {
     func value<T: Codable>(forKey key: String) -> T? {
         guard let encryptedData = defaults.data(forKey: key),
               let decryptedData = try? decrypt(data: encryptedData) else {
-            print("Failed to decrypt data")
             return nil
         }
         let decoder = JSONDecoder()