Przeglądaj źródła

update nuSDKService and inprogress chat tabs WA like

alqindiirsyam 4 miesięcy temu
rodzic
commit
30ff5d61a0

BIN
.DS_Store


BIN
AppBuilder/.DS_Store


+ 7 - 2
AppBuilder/AppBuilder/ViewController.swift

@@ -34,7 +34,8 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
     var secondTab : SecondTabViewController?
     var thirdTab : ThirdTabViewController?
     var fourthTab : FourthTabViewController?
-    var callTab : CallLogVC?
+    var callTab : UINavigationController?
+    var chatWATab : UINavigationController?
     let emptyTab = EmptyTabViewController()
     public static var isTab1 = true
     public static var isTab2 = false
@@ -93,7 +94,8 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
         secondTab = storyboard?.instantiateViewController(withIdentifier: "secondTabVC") as? SecondTabViewController
         thirdTab = storyboard?.instantiateViewController(withIdentifier: "thirdTabVC") as? ThirdTabViewController
         fourthTab = storyboard?.instantiateViewController(withIdentifier: "fourthTabVC") as? FourthTabViewController
-        callTab = CallLogVC()
+        callTab = UINavigationController(rootViewController: CallLogVC())
+        chatWATab = UINavigationController(rootViewController: ChatWALikeVC())
         
         self.delegate = self
         
@@ -102,6 +104,7 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
         thirdTab?.tabBarItem = UITabBarItem(title: "", image: resizeImage(image: self.traitCollection.userInterfaceStyle == .dark ? UIImage(named: "tab_3_icon")!.withTintColor(.white) : UIImage(named: "tab_3_icon")!, targetSize: CGSize(width: 25, height: 25)).withRenderingMode(.alwaysOriginal), selectedImage: resizeImage(image: UIImage(named: "tab_3_icon")!, targetSize: CGSize(width: 25, height: 25)).withRenderingMode(.alwaysOriginal).withTintColor(self.traitCollection.userInterfaceStyle == .dark ? .lightGray : .mainColor))
         fourthTab?.tabBarItem = UITabBarItem(title: "", image: resizeImage(image: self.traitCollection.userInterfaceStyle == .dark ? UIImage(named: "tab_4_icon")!.withTintColor(.white) : UIImage(named: "tab_4_icon")!, targetSize: CGSize(width: 25, height: 25)).withRenderingMode(.alwaysOriginal), selectedImage: resizeImage(image: UIImage(named: "tab_4_icon")!, targetSize: CGSize(width: 25, height: 25)).withRenderingMode(.alwaysOriginal).withTintColor(self.traitCollection.userInterfaceStyle == .dark ? .lightGray : .mainColor))
         callTab?.tabBarItem = UITabBarItem(title: "", image: resizeImage(image: self.traitCollection.userInterfaceStyle == .dark ? UIImage(named: "tab_6_icon")!.withTintColor(.white) : UIImage(named: "tab_6_icon")!, targetSize: CGSize(width: 25, height: 25)).withRenderingMode(.alwaysOriginal), selectedImage: resizeImage(image: UIImage(named: "tab_6_icon")!, targetSize: CGSize(width: 25, height: 25)).withRenderingMode(.alwaysOriginal).withTintColor(self.traitCollection.userInterfaceStyle == .dark ? .lightGray : .mainColor))
+        chatWATab?.tabBarItem = UITabBarItem(title: "", image: resizeImage(image: self.traitCollection.userInterfaceStyle == .dark ? UIImage(named: "tab_2_icon")!.withTintColor(.white) : UIImage(named: "tab_2_icon")!, targetSize: CGSize(width: 25, height: 25)).withRenderingMode(.alwaysOriginal), selectedImage: resizeImage(image: UIImage(named: "tab_2_icon")!, targetSize: CGSize(width: 25, height: 25)).withRenderingMode(.alwaysOriginal).withTintColor(self.traitCollection.userInterfaceStyle == .dark ? .lightGray : .mainColor))
         var i = 0
         var j = 0
         while j < customTab.count {
@@ -121,6 +124,8 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
                     tabs.append(fourthTab!)
                 case "6":
                     tabs.append(callTab!)
+                case "18":
+                    tabs.append(chatWATab!)
                 default:
                     break
                 }

BIN
NexilisLite/.DS_Store


+ 1 - 1
NexilisLite/NexilisLite.podspec

@@ -24,7 +24,7 @@ Pod::Spec.new do |spec|
   spec.resource_bundles = { 'NexilisLite' => ['NexilisLite/Resource/**/*']}
   spec.swift_version = '5.5.1'
   spec.dependency 'FMDB', '~> 2.7.12'
-  spec.dependency 'nuSDKService', '4.0.18'
+  spec.dependency 'nuSDKService', '4.0.19'
   spec.dependency 'NotificationBannerSwift'
   spec.dependency 'Alamofire', '~> 5.10.2'
   spec.dependency 'SDWebImage', '~> 5.20.0'

+ 33 - 5
NexilisLite/NexilisLite.xcodeproj/project.pbxproj

@@ -217,6 +217,10 @@
 		CD46A0C52A0D0D5D009E4C87 /* MyArchive.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD46A0C42A0D0D5D009E4C87 /* MyArchive.swift */; };
 		CD5A73AE2A77642D000541A5 /* MessageInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD5A73AC2A77642D000541A5 /* MessageInfo.swift */; };
 		CD5A73AF2A77642D000541A5 /* ListGroupImages.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD5A73AD2A77642D000541A5 /* ListGroupImages.swift */; };
+		CD6312592DA925260088964E /* CallLogVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD6312582DA925260088964E /* CallLogVC.swift */; };
+		CD63125B2DA925320088964E /* ChatWALikeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD63125A2DA925320088964E /* ChatWALikeVC.swift */; };
+		CD63125E2DA925960088964E /* Group_NM.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD63125D2DA925960088964E /* Group_NM.swift */; };
+		CD63125F2DA925960088964E /* CallModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD63125C2DA925960088964E /* CallModel.swift */; };
 		CD637E3D2D9534ED004780FB /* NexilisLite.podspec in Resources */ = {isa = PBXBuildFile; fileRef = CD46A0BE2A0CE4FD009E4C87 /* NexilisLite.podspec */; };
 		CD7054EA2AD39DE2003741BF /* ChatGPTBotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD7054E92AD39DE2003741BF /* ChatGPTBotView.swift */; };
 		CD9551592A664BDA00AF6476 /* Poppins-LightItalic.otf in Resources */ = {isa = PBXBuildFile; fileRef = CD9551402A664BD400AF6476 /* Poppins-LightItalic.otf */; };
@@ -502,6 +506,10 @@
 		CD46A0C42A0D0D5D009E4C87 /* MyArchive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyArchive.swift; sourceTree = "<group>"; };
 		CD5A73AC2A77642D000541A5 /* MessageInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageInfo.swift; sourceTree = "<group>"; };
 		CD5A73AD2A77642D000541A5 /* ListGroupImages.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListGroupImages.swift; sourceTree = "<group>"; };
+		CD6312582DA925260088964E /* CallLogVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallLogVC.swift; sourceTree = "<group>"; };
+		CD63125A2DA925320088964E /* ChatWALikeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWALikeVC.swift; sourceTree = "<group>"; };
+		CD63125C2DA925960088964E /* CallModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallModel.swift; sourceTree = "<group>"; };
+		CD63125D2DA925960088964E /* Group_NM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Group_NM.swift; sourceTree = "<group>"; };
 		CD7054E92AD39DE2003741BF /* ChatGPTBotView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatGPTBotView.swift; sourceTree = "<group>"; };
 		CD9551332A664BD400AF6476 /* Poppins-SemiBoldItalic.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-SemiBoldItalic.otf"; sourceTree = "<group>"; };
 		CD9551342A664BD400AF6476 /* Poppins-ExtraBoldItalic.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-ExtraBoldItalic.otf"; sourceTree = "<group>"; };
@@ -676,13 +684,15 @@
 		CD1E715E2A0BA86100BF871F /* Model */ = {
 			isa = PBXGroup;
 			children = (
-				CD1E715F2A0BA86100BF871F /* Model.swift */,
-				CD1E71602A0BA86100BF871F /* Chat.swift */,
-				CD1E71612A0BA86100BF871F /* User.swift */,
+				CD63125C2DA925960088964E /* CallModel.swift */,
 				CD1E71622A0BA86100BF871F /* CategoryCC.swift */,
-				CD1E71632A0BA86100BF871F /* WorkingArea.swift */,
+				CD1E71602A0BA86100BF871F /* Chat.swift */,
 				CD1E71642A0BA86100BF871F /* Group.swift */,
+				CD63125D2DA925960088964E /* Group_NM.swift */,
+				CD1E715F2A0BA86100BF871F /* Model.swift */,
 				CD1E71652A0BA86100BF871F /* NotifSound.swift */,
+				CD1E71612A0BA86100BF871F /* User.swift */,
+				CD1E71632A0BA86100BF871F /* WorkingArea.swift */,
 			);
 			path = Model;
 			sourceTree = "<group>";
@@ -720,8 +730,8 @@
 		CD1E716D2A0BA86100BF871F /* Chat */ = {
 			isa = PBXGroup;
 			children = (
-				12C36CEA2D02995F0095BEC1 /* SecureFolderView.swift */,
 				CD7054E92AD39DE2003741BF /* ChatGPTBotView.swift */,
+				CD63125A2DA925320088964E /* ChatWALikeVC.swift */,
 				CD1E71742A0BA86100BF871F /* CustomTextView.swift */,
 				CD1E71722A0BA86100BF871F /* EditorGroup.swift */,
 				CD1E71702A0BA86100BF871F /* EditorPersonal.swift */,
@@ -731,6 +741,7 @@
 				CD5A73AC2A77642D000541A5 /* MessageInfo.swift */,
 				CD1E71732A0BA86100BF871F /* PreviewAttachmentImageVideo.swift */,
 				CD1E716F2A0BA86100BF871F /* PreviewAttachmentImageVideo.xib */,
+				12C36CEA2D02995F0095BEC1 /* SecureFolderView.swift */,
 			);
 			path = Chat;
 			sourceTree = "<group>";
@@ -756,6 +767,7 @@
 			children = (
 				CD1E717F2A0BA86100BF871F /* AudioViewController.swift */,
 				CD1E71872A0BA86100BF871F /* Call.swift */,
+				CD6312582DA925260088964E /* CallLogVC.swift */,
 				CD1E717D2A0BA86100BF871F /* CallManager.swift */,
 				CD1E71812A0BA86100BF871F /* CallProviderDelegate.swift */,
 				CD43C80A2D687DBF00297C6E /* CreateConferenceCallController.swift */,
@@ -1254,10 +1266,14 @@
 			inputFileListPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-NexilisLite/Pods-NexilisLite-resources-${CONFIGURATION}-input-files.xcfilelist",
 			);
+			inputPaths = (
+			);
 			name = "[CP] Copy Pods Resources";
 			outputFileListPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-NexilisLite/Pods-NexilisLite-resources-${CONFIGURATION}-output-files.xcfilelist",
 			);
+			outputPaths = (
+			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NexilisLite/Pods-NexilisLite-resources.sh\"\n";
@@ -1271,10 +1287,14 @@
 			inputFileListPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
 			);
+			inputPaths = (
+			);
 			name = "[CP] Embed Pods Frameworks";
 			outputFileListPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
 			);
+			outputPaths = (
+			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests-frameworks.sh\"\n";
@@ -1288,10 +1308,14 @@
 			inputFileListPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests-resources-${CONFIGURATION}-input-files.xcfilelist",
 			);
+			inputPaths = (
+			);
 			name = "[CP] Copy Pods Resources";
 			outputFileListPaths = (
 				"${PODS_ROOT}/Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests-resources-${CONFIGURATION}-output-files.xcfilelist",
 			);
+			outputPaths = (
+			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NexilisLite-NexilisLiteTests/Pods-NexilisLite-NexilisLiteTests-resources.sh\"\n";
@@ -1403,6 +1427,10 @@
 				CD1E721B2A0BA86100BF871F /* Nexilis.swift in Sources */,
 				CD1E72102A0BA86100BF871F /* Extension.swift in Sources */,
 				CD1E724C2A0BA86100BF871F /* NotificationSound.swift in Sources */,
+				CD63125E2DA925960088964E /* Group_NM.swift in Sources */,
+				CD63125F2DA925960088964E /* CallModel.swift in Sources */,
+				CD6312592DA925260088964E /* CallLogVC.swift in Sources */,
+				CD63125B2DA925320088964E /* ChatWALikeVC.swift in Sources */,
 				CDDF467A2A2EF0A700049A19 /* ScreenSharingViewController.swift in Sources */,
 				CD1E72402A0BA86100BF871F /* ChangeNamePassswordViewController.swift in Sources */,
 				CD1E722E2A0BA86100BF871F /* AudioViewController.swift in Sources */,

+ 2 - 0
NexilisLite/NexilisLite/Resource/id.lproj/Localizable.strings

@@ -413,3 +413,5 @@
 "Missed video call" = "Panggilan video tak terjawab";
 "No answer" = "Tidak ada jawaban";
 "Tap to call back" = "Ketuk untuk menelepon kembali";
+"To place audio or video call, tap ⊕ at the top and select a contact." = "Untuk melakukan panggilan audio atau video, ketuk ⊕ di bagian atas dan pilih kontak.";
+"To place chats, tap ⊕ at the top and select a contact." = "Untuk memulai obrolan, ketuk ⊕ di bagian atas dan pilih kontak.";

+ 26 - 4
NexilisLite/NexilisLite/Source/Extension.swift

@@ -425,9 +425,13 @@ extension NSObject {
                     
                     DispatchQueue.main.async {
                         if tableView != nil {
-                            tableView!.beginUpdates()
-                            tableView!.reloadRows(at: [indexPath!], with: .none)
-                            tableView!.endUpdates()
+                            if let indexPath = indexPath,
+                               indexPath.section < tableView!.numberOfSections,
+                               indexPath.row < tableView!.numberOfRows(inSection: indexPath.section) {
+                                tableView!.beginUpdates()
+                                tableView!.reloadRows(at: [indexPath], with: .none)
+                                tableView!.endUpdates()
+                            }
                         }
                         if type(of: self).urlStore[tmpAddress] == name && tableView == nil {
                             if FileManager().fileExists(atPath: file.path) {
@@ -456,7 +460,13 @@ extension NSObject {
                 
                 DispatchQueue.main.async {
                     if tableView != nil {
-                        tableView!.reloadRows(at: [indexPath!], with: .none)
+                        if let indexPath = indexPath,
+                           indexPath.section < tableView!.numberOfSections,
+                           indexPath.row < tableView!.numberOfRows(inSection: indexPath.section) {
+                            tableView!.beginUpdates()
+                            tableView!.reloadRows(at: [indexPath], with: .none)
+                            tableView!.endUpdates()
+                        }
                     }
                 }
             }
@@ -552,6 +562,10 @@ extension UIColor {
         return renderColor(hex: "#F5F5F5")
     }
     
+    public static var grayTitleColor: UIColor {
+        return renderColor(hex: "#5A5A5A")
+    }
+    
     public static var docColor: UIColor {
         return renderColor(hex: "#798F9A")
     }
@@ -591,6 +605,14 @@ extension UIColor {
         return renderColor(hex: "#25D366")
     }
     
+    public static var whatsappGreenTitleColor: UIColor {
+        return renderColor(hex: "#295C3B")
+    }
+    
+    public static var whatsappGreenLightColor: UIColor {
+        return renderColor(hex: "#E5FCE4")
+    }
+    
     public class func renderColor(hex: String) -> UIColor {
         var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
 

+ 1 - 0
NexilisLite/NexilisLite/Source/Nexilis.swift

@@ -1673,6 +1673,7 @@ public class Nexilis: NSObject {
         })
         var dataMessage: [AnyHashable : Any] = [:]
         dataMessage["message_id"] = idCall
+        dataMessage["pin"] = pin
         NotificationCenter.default.post(name: NSNotification.Name(rawValue: "refreshCallLog"), object: nil, userInfo: dataMessage)
         NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
     }

+ 25 - 20
NexilisLite/NexilisLite/Source/View/Call/CallLogVC.swift

@@ -8,7 +8,7 @@
 import Foundation
 import UIKit
 
-public class CallLogVC: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchResultsUpdating {
+public class CallLogVC: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchResultsUpdating, UISearchBarDelegate {
     private let tableView = UITableView(frame: .zero, style: .plain)
     private let searchController = UISearchController(searchResultsController: nil)
         
@@ -23,6 +23,9 @@ public class CallLogVC: UIViewController, UITableViewDataSource, UITableViewDele
         tableView.sectionHeaderHeight = 0
         tableView.sectionFooterHeight = 0
         tableView.automaticallyAdjustsScrollIndicatorInsets = false
+        if #available(iOS 15.0, *) {
+            tableView.sectionHeaderTopPadding = 0
+        }
         
         setupTableView()
         refresh()
@@ -30,9 +33,9 @@ public class CallLogVC: UIViewController, UITableViewDataSource, UITableViewDele
     }
     
     public override func viewWillAppear(_ animated: Bool) {
-        tabBarController?.navigationItem.title = "Calls".localized()
-        tabBarController?.navigationItem.hidesSearchBarWhenScrolling = true
-        navigationController?.setNavigationBarHidden(false, animated: false)
+        navigationItem.title = "Calls".localized()
+        navigationItem.hidesSearchBarWhenScrolling = true
+        tabBarController?.navigationController?.setNavigationBarHidden(true, animated: false)
         navigationController?.navigationBar.prefersLargeTitles = true
         navigationController?.navigationItem.largeTitleDisplayMode = .always
         let attributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: self.traitCollection.userInterfaceStyle == .dark ? .white : UIColor.black, NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16)]
@@ -43,6 +46,8 @@ public class CallLogVC: UIViewController, UITableViewDataSource, UITableViewDele
         appearance.largeTitleTextAttributes = largeAttributes
         navigationController?.navigationBar.standardAppearance = appearance
         navigationController?.navigationBar.scrollEdgeAppearance = appearance
+        let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.black, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
+        UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
         
 //        let leftButton = UIButton(type: .system)
 //        let imageLeft = UIImage(systemName: "ellipsis", withConfiguration: UIImage.SymbolConfiguration(pointSize: 12, weight: .bold))
@@ -54,7 +59,7 @@ public class CallLogVC: UIViewController, UITableViewDataSource, UITableViewDele
 //        leftButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
 //        leftButton.addTarget(self, action: #selector(leftBarButtonTapped), for: .touchUpInside)
 //        let leftBarButtonItem = UIBarButtonItem(customView: leftButton)
-//        tabBarController?.navigationItem.leftBarButtonItem = leftBarButtonItem
+//        navigationItem.leftBarButtonItem = leftBarButtonItem
         
         let rightButton = UIButton(type: .system)
         let imageRight = UIImage(systemName: "plus", withConfiguration: UIImage.SymbolConfiguration(pointSize: 12, weight: .bold))
@@ -66,7 +71,19 @@ public class CallLogVC: UIViewController, UITableViewDataSource, UITableViewDele
         rightButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
         rightButton.addTarget(self, action: #selector(rightBarButtonTapped), for: .touchUpInside)
         let rightBarButtonItem = UIBarButtonItem(customView: rightButton)
-        tabBarController?.navigationItem.rightBarButtonItem = rightBarButtonItem
+        navigationItem.rightBarButtonItem = rightBarButtonItem
+        
+        searchController.searchResultsUpdater = self
+        searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Search".localized(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.gray, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)])
+        searchController.obscuresBackgroundDuringPresentation = false
+        searchController.hidesNavigationBarDuringPresentation = true
+        searchController.searchBar.delegate = self
+
+        navigationItem.searchController = searchController
+        definesPresentationContext = true
+        DispatchQueue.main.async {
+            self.navigationController?.navigationBar.sizeToFit()
+        }
     }
     
     private func refresh() {
@@ -76,16 +93,10 @@ public class CallLogVC: UIViewController, UITableViewDataSource, UITableViewDele
             if textCallEmpty.isDescendant(of: view){
                 textCallEmpty.removeFromSuperview()
             }
-            searchController.searchResultsUpdater = self
-            searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Search".localized(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.gray, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)])
-            searchController.obscuresBackgroundDuringPresentation = false
-            searchController.hidesNavigationBarDuringPresentation = true
-
-            tabBarController?.navigationItem.searchController = searchController
-            definesPresentationContext = true
-            
+            searchController.searchBar.isHidden = false
             tableView.reloadData()
         } else {
+            searchController.searchBar.isHidden = true
             textCallEmpty.numberOfLines = 0
             let fullText = "To place audio or video call, tap ⊕ at the top and select a contact.".localized()
             let attributedString = NSMutableAttributedString(string: fullText)
@@ -99,9 +110,6 @@ public class CallLogVC: UIViewController, UITableViewDataSource, UITableViewDele
             view.addSubview(textCallEmpty)
             textCallEmpty.anchor(left: view.leftAnchor, right: view.rightAnchor, paddingLeft: 20, paddingRight: 20, centerX: view.centerXAnchor, centerY: view.centerYAnchor)
         }
-        DispatchQueue.main.async {
-            self.navigationController?.navigationBar.sizeToFit()
-        }
     }
     
     @objc func onRefreshCallLog(notification: NSNotification) {
@@ -287,9 +295,6 @@ public class CallLogVC: UIViewController, UITableViewDataSource, UITableViewDele
     }
     
     public func updateSearchResults(for searchController: UISearchController) {
-        // Handle search updates here
-        let searchText = searchController.searchBar.text ?? ""
-        print("Searching for: \(searchText)")
     }
     
     private func typeCallLog(type: String, isVideo: Bool) -> NSMutableAttributedString {

+ 1039 - 0
NexilisLite/NexilisLite/Source/View/Chat/ChatWALikeVC.swift

@@ -0,0 +1,1039 @@
+//
+//  ChatWALikeVC.swift
+//  Pods
+//
+//  Created by Qindi on 11/04/25.
+//
+
+import Foundation
+import UIKit
+import AVFAudio
+import QuickLook
+
+public class ChatWALikeVC: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchResultsUpdating, QLPreviewControllerDataSource, UISearchBarDelegate {
+    private let tableView = UITableView(frame: .zero, style: .plain)
+    private let searchController = UISearchController(searchResultsController: nil)
+    
+    var chats: [Chat] = []
+    var chatGroupMaps: [String: [Chat]] = [:]
+    var loadingData = true
+    private let textChatEmpty = UILabel()
+    
+    let contAll = UIButton(type: .custom)
+    let textAll = UILabel()
+    let contUnread = UIButton(type: .custom)
+    let textUnread = UILabel()
+    let contGroups = UIButton(type: .custom)
+    let textGroups = UILabel()
+    
+    var fillteredData: [Any] = []
+    var fillteredMessages: [Chat] = []
+    
+    var isFiltering: Bool {
+        return !searchController.searchBar.text!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && searchController.searchBar.text!.count > 1
+    }
+    
+    var previewItem: NSURL?
+    var audioPlayer: AVAudioPlayer?
+    
+    let UNREAD_TAG = 10
+    let PHOTOS_TAG = 11
+    let DOCUMENTS_TAG = 12
+    let LINKS_TAG = 13
+    let VIDEOS_TAG = 14
+    let GIFS_TAG = 15
+    let AUDIOS_TAG = 16
+    
+    var selectedTag = 0
+    
+    public override func viewDidLoad() {
+        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellChatWA")
+        tableView.dataSource = self
+        tableView.delegate = self
+        tableView.tableFooterView = UIView()
+        tableView.sectionHeaderHeight = 0
+        tableView.sectionFooterHeight = 0
+        tableView.automaticallyAdjustsScrollIndicatorInsets = false
+        if #available(iOS 15.0, *) {
+            tableView.sectionHeaderTopPadding = 0
+        }
+        
+        setupTableView()
+    }
+    
+    public override func viewWillAppear(_ animated: Bool) {
+        navigationItem.title = "Chats".localized()
+        navigationItem.hidesSearchBarWhenScrolling = true
+        tabBarController?.navigationController?.setNavigationBarHidden(true, animated: false)
+        navigationController?.navigationBar.prefersLargeTitles = true
+        navigationController?.navigationItem.largeTitleDisplayMode = .always
+        let attributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: self.traitCollection.userInterfaceStyle == .dark ? .white : UIColor.black, NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16)]
+        let largeAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: self.traitCollection.userInterfaceStyle == .dark ? .white : UIColor.black, NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 34)]
+        let appearance = UINavigationBarAppearance()
+        appearance.configureWithTransparentBackground()
+        appearance.titleTextAttributes = attributes
+        appearance.largeTitleTextAttributes = largeAttributes
+        navigationController?.navigationBar.standardAppearance = appearance
+        navigationController?.navigationBar.scrollEdgeAppearance = appearance
+        let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.black, NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)]
+        UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
+        
+//        let leftButton = UIButton(type: .system)
+//        let imageLeft = UIImage(systemName: "ellipsis", withConfiguration: UIImage.SymbolConfiguration(pointSize: 12, weight: .bold))
+//        leftButton.setImage(imageLeft, for: .normal)
+//        leftButton.tintColor = .black
+//        leftButton.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
+//        leftButton.layer.cornerRadius = 15
+//        leftButton.clipsToBounds = true
+//        leftButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
+//        leftButton.addTarget(self, action: #selector(leftBarButtonTapped), for: .touchUpInside)
+//        let leftBarButtonItem = UIBarButtonItem(customView: leftButton)
+//        navigationItem.leftBarButtonItem = leftBarButtonItem
+        
+        let rightButton = UIButton(type: .system)
+        let imageRight = UIImage(systemName: "plus", withConfiguration: UIImage.SymbolConfiguration(pointSize: 12, weight: .bold))
+        rightButton.setImage(imageRight, for: .normal)
+        rightButton.tintColor = .white
+        rightButton.backgroundColor = .whatsappGreenColor
+        rightButton.layer.cornerRadius = 15
+        rightButton.clipsToBounds = true
+        rightButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
+        rightButton.addTarget(self, action: #selector(rightBarButtonTapped), for: .touchUpInside)
+        let rightBarButtonItem = UIBarButtonItem(customView: rightButton)
+        navigationItem.rightBarButtonItem = rightBarButtonItem
+        
+        searchController.searchResultsUpdater = self
+        searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Search".localized(), attributes: [NSAttributedString.Key.foregroundColor: UIColor.gray, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)])
+        searchController.obscuresBackgroundDuringPresentation = false
+        searchController.hidesNavigationBarDuringPresentation = true
+        searchController.searchBar.delegate = self
+
+        navigationItem.searchController = searchController
+        navigationItem.hidesSearchBarWhenScrolling = false
+        definesPresentationContext = true
+        DispatchQueue.main.async {
+            self.navigationController?.navigationBar.sizeToFit()
+        }
+    }
+    
+    private func refresh() {
+        getData { [self] in
+            if chats.count == 0 {
+                searchController.searchBar.isHidden = true
+                if textChatEmpty.isDescendant(of: view){
+                    textChatEmpty.removeFromSuperview()
+                }
+                textChatEmpty.numberOfLines = 0
+                let fullText = "To place chats, tap ⊕ at the top and select a contact.".localized()
+                let attributedString = NSMutableAttributedString(string: fullText)
+                attributedString.addAttribute(.font, value: UIFont.systemFont(ofSize: 25), range: NSRange(location: 0, length: attributedString.length))
+                if let plusRange = fullText.range(of: "⊕") {
+                    let nsRange = NSRange(plusRange, in: fullText)
+                    attributedString.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: 40), range: nsRange)
+                }
+                textChatEmpty.attributedText = attributedString
+                
+                view.addSubview(textChatEmpty)
+                textChatEmpty.anchor(left: view.leftAnchor, right: view.rightAnchor, paddingLeft: 20, paddingRight: 20, centerX: view.centerXAnchor, centerY: view.centerYAnchor)
+            } else {
+                searchController.searchBar.isHidden = false
+            }
+        }
+    }
+    
+    public override func viewDidAppear(_ animated: Bool) {
+        refresh()
+    }
+    
+    private func getData(completion: @escaping ()->()) {
+        getChats {
+            DispatchQueue.main.async {
+                self.tableView.reloadData()
+                self.loadingData = false
+                completion()
+            }
+        }
+    }
+    
+    func getChats(completion: @escaping ()->()) {
+        DispatchQueue.global().async {
+            self.chatGroupMaps.removeAll()
+            let previousChat = self.chats
+            let allChats = Chat.getData()
+            var tempChats: [Chat] = []
+
+            for singleChat in allChats {
+                guard !singleChat.groupId.isEmpty else {
+                    tempChats.append(singleChat)
+                    continue
+                }
+                
+                let chatParentInPreviousChats = previousChat.first { $0.isParent && $0.groupId == singleChat.groupId }
+                
+                if var existingGroup = self.chatGroupMaps[singleChat.groupId] {
+                    existingGroup.insert(singleChat, at: 0)
+                    self.chatGroupMaps[singleChat.groupId] = existingGroup
+                    
+                    if let parentChatIndex = tempChats.firstIndex(where: { $0.groupId == singleChat.groupId && $0.isParent }) {
+                        if let counterParent = Int(tempChats[parentChatIndex].counter), let counterSingle = Int(singleChat.counter) {
+                            tempChats[parentChatIndex].counter = "\(counterParent + counterSingle)"
+                        }
+                    }
+                    
+                    if let parentExist = chatParentInPreviousChats, parentExist.isSelected,
+                       let indexParent = tempChats.firstIndex(where: { $0.isParent && $0.groupId == singleChat.groupId }) {
+                        tempChats.insert(singleChat, at: min(indexParent + existingGroup.count, tempChats.count))
+                    }
+                } else {
+                    self.chatGroupMaps[singleChat.groupId] = [singleChat]
+                    let parentChat = Chat(profile: singleChat.profile, groupName: singleChat.groupName, counter: singleChat.counter, groupId: singleChat.groupId)
+                    parentChat.isParent = true
+                    
+                    if let parentExist = chatParentInPreviousChats, parentExist.isSelected {
+                        parentChat.isSelected = true
+                        tempChats.append(parentChat)
+                        tempChats.append(singleChat)
+                    } else {
+                        tempChats.append(parentChat)
+                    }
+                }
+            }
+
+            self.chats = tempChats
+            completion()
+        }
+    }
+    
+    @objc func leftBarButtonTapped() {
+        print("Left bar button tapped")
+    }
+    
+    @objc func rightBarButtonTapped() {
+        APIS.openChat()
+    }
+    
+    private func setupTableView() {
+        view.addSubview(tableView)
+        tableView.translatesAutoresizingMaskIntoConstraints = false
+        
+        NSLayoutConstraint.activate([
+            tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
+            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
+            tableView.leftAnchor.constraint(equalTo: view.leftAnchor),
+            tableView.rightAnchor.constraint(equalTo: view.rightAnchor)
+        ])
+    }
+    
+    public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
+        let header = UIView()
+        
+        if chats.count > 0 {
+            header.addSubview(contAll)
+            contAll.anchor(top: header.topAnchor, left: header.leftAnchor, bottom: header.bottomAnchor, paddingLeft: 20, height: 30)
+            contAll.layer.cornerRadius = 15
+            contAll.clipsToBounds = true
+            contAll.tag = 0
+            contAll.backgroundColor = .whatsappGreenLightColor
+            contAll.addTarget(self, action: #selector(selectFilter), for: .touchUpInside)
+            
+            contAll.addSubview(textAll)
+            textAll.text = "All".localized()
+            textAll.font = .boldSystemFont(ofSize: 14)
+            textAll.textColor = .whatsappGreenTitleColor
+            textAll.anchor(left: contAll.leftAnchor, right: contAll.rightAnchor, paddingLeft: 15, paddingRight: 15, centerX: contAll.centerXAnchor, centerY: contAll.centerYAnchor, height: 20)
+            
+            header.addSubview(contUnread)
+            contUnread.anchor(top: header.topAnchor, left: contAll.rightAnchor, bottom: header.bottomAnchor, paddingLeft: 8, height: 30)
+            contUnread.layer.cornerRadius = 15
+            contUnread.clipsToBounds = true
+            contUnread.tag = 1
+            contUnread.backgroundColor = .whiteBubbleColor
+            contUnread.addTarget(self, action: #selector(selectFilter), for: .touchUpInside)
+            
+            contUnread.addSubview(textUnread)
+            textUnread.text = "Unread".localized()
+            textUnread.font = .boldSystemFont(ofSize: 14)
+            textUnread.textColor = .grayTitleColor
+            textUnread.anchor(left: contUnread.leftAnchor, right: contUnread.rightAnchor, paddingLeft: 15, paddingRight: 15, centerX: contUnread.centerXAnchor, centerY: contUnread.centerYAnchor, height: 20)
+            
+            header.addSubview(contGroups)
+            contGroups.anchor(top: header.topAnchor, left: contUnread.rightAnchor, bottom: header.bottomAnchor, paddingLeft: 8, height: 30)
+            contGroups.layer.cornerRadius = 15
+            contGroups.clipsToBounds = true
+            contGroups.tag = 2
+            contGroups.backgroundColor = .whiteBubbleColor
+            contGroups.addTarget(self, action: #selector(selectFilter), for: .touchUpInside)
+            
+            contGroups.addSubview(textGroups)
+            textGroups.text = "Groups".localized()
+            textGroups.font = .boldSystemFont(ofSize: 14)
+            textGroups.textColor = .grayTitleColor
+            textGroups.anchor(left: contGroups.leftAnchor, right: contGroups.rightAnchor, paddingLeft: 15, paddingRight: 15, centerX: contGroups.centerXAnchor, centerY: contGroups.centerYAnchor, height: 20)
+        }
+        
+        return header
+    }
+    
+    @objc func selectFilter(_ sender: UIButton) {
+        if sender.tag == 0 && contAll.backgroundColor != .whatsappGreenLightColor {
+            contAll.backgroundColor = .whatsappGreenLightColor
+            textAll.textColor = .whatsappGreenTitleColor
+            
+            contUnread.backgroundColor = .whiteBubbleColor
+            textUnread.textColor = .grayTitleColor
+            
+            contGroups.backgroundColor = .whiteBubbleColor
+            textGroups.textColor = .grayTitleColor
+        } else if sender.tag == 1 && contUnread.backgroundColor != .whatsappGreenLightColor {
+            contUnread.backgroundColor = .whatsappGreenLightColor
+            textUnread.textColor = .whatsappGreenTitleColor
+            
+            contAll.backgroundColor = .whiteBubbleColor
+            textAll.textColor = .grayTitleColor
+            
+            contGroups.backgroundColor = .whiteBubbleColor
+            textGroups.textColor = .grayTitleColor
+        }  else if sender.tag == 2 && contGroups.backgroundColor != .whatsappGreenLightColor {
+            contGroups.backgroundColor = .whatsappGreenLightColor
+            textGroups.textColor = .whatsappGreenTitleColor
+            
+            contAll.backgroundColor = .whiteBubbleColor
+            textAll.textColor = .grayTitleColor
+            
+            contUnread.backgroundColor = .whiteBubbleColor
+            textUnread.textColor = .grayTitleColor
+        }
+    }
+    
+    public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
+        return 30
+    }
+    
+    public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        if fillteredData.count > 0 && selectedTag != UNREAD_TAG && selectedTag != 0 {
+            if selectedTag == LINKS_TAG {
+                return 160.0
+            }
+            return 130.0
+        }
+        return 75.0
+    }
+    
+    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
+        let sectionHeaderHeight: CGFloat = 30
+        if scrollView.contentOffset.y <= sectionHeaderHeight && scrollView.contentOffset.y >= 0 {
+            scrollView.contentInset.top = -scrollView.contentOffset.y
+        } else if scrollView.contentOffset.y >= sectionHeaderHeight {
+            scrollView.contentInset.top = -sectionHeaderHeight
+        }
+    }
+    
+    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        if isFiltering {
+            return fillteredData.count
+        }
+        return chats.count
+    }
+    
+    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: "cellChatWA", for: indexPath)
+        let content = cell.contentView
+        if content.subviews.count > 0 {
+            content.subviews.forEach { $0.removeFromSuperview() }
+        }
+        let data: Chat
+        if isFiltering {
+            data = fillteredData[indexPath.row] as! Chat
+        } else {
+            if chats.count == 0 || (indexPath.row > (chats.count - 1)) {
+                let labelNochat = UILabel()
+                labelNochat.text = loadingData ? "Loading Data...".localized() : "There are no conversations".localized()
+                labelNochat.font = .systemFont(ofSize: 13 + String.offset())
+                labelNochat.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
+                content.addSubview(labelNochat)
+                labelNochat.anchor(centerX: content.centerXAnchor, centerY: content.centerYAnchor)
+                cell.backgroundColor = .clear
+                cell.selectionStyle = .none
+                return cell
+            }
+            data = chats[indexPath.row]
+        }
+        if selectedTag != UNREAD_TAG && selectedTag != 0 {
+            let title = UILabel()
+            let subtitle = UILabel()
+            title.textColor = .black
+            subtitle.textColor = .gray
+            title.font = .systemFont(ofSize: 16 + String.offset(), weight: .medium)
+            subtitle.font = .systemFont(ofSize: 14 + String.offset())
+            content.addSubview(title)
+            content.addSubview(subtitle)
+            title.anchor(top: content.topAnchor, left: content.leftAnchor, paddingTop: 10, paddingLeft: 20)
+            subtitle.anchor(top: title.bottomAnchor, left: content.leftAnchor, right: content.rightAnchor, paddingLeft: 20, paddingRight: 20)
+            subtitle.numberOfLines = 2
+            title.text = data.name
+            
+            let imageArrowRight = UIImageView(image: UIImage(systemName: "chevron.right"))
+            content.addSubview(imageArrowRight)
+            imageArrowRight.tintColor = .gray
+            imageArrowRight.anchor(top: content.topAnchor, right: content.rightAnchor, paddingTop: 10, paddingRight: 20)
+            
+            let timeView = UILabel()
+            content.addSubview(timeView)
+            timeView.anchor(top: content.topAnchor, right: imageArrowRight.leftAnchor, paddingTop: 10, paddingRight: 20)
+            timeView.textColor = .gray
+            timeView.font = UIFont.systemFont(ofSize: 16 + String.offset())
+            
+            let date = Date(milliseconds: Int64(data.serverDate)!)
+            let calendar = Calendar.current
+            
+            if (calendar.isDateInToday(date)) {
+                let formatter = DateFormatter()
+                formatter.dateFormat = "HH:mm"
+                formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
+                timeView.text = formatter.string(from: date as Date)
+            } else {
+                let startOfNow = calendar.startOfDay(for: Date())
+                let startOfTimeStamp = calendar.startOfDay(for: date)
+                let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
+                let day = -(components.day!)
+                if day == 1 {
+                    timeView.text = "Yesterday".localized()
+                } else {
+                    if day < 7 {
+                        let formatter = DateFormatter()
+                        formatter.dateFormat = "EEEE"
+                        let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
+                        if lang == "id" {
+                            formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
+                        }
+                        timeView.text = formatter.string(from: date)
+                    } else {
+                        let formatter = DateFormatter()
+                        formatter.dateFormat = "M/dd/yy"
+                        formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
+                        let stringFormat = formatter.string(from: date as Date)
+                        timeView.text = stringFormat
+                    }
+                }
+            }
+            
+            cell.separatorInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 0)
+            
+            let container = UIView()
+            content.addSubview(container)
+            container.anchor(top: subtitle.bottomAnchor, left: content.leftAnchor, right: content.rightAnchor, paddingTop: 5, paddingLeft: 20, paddingRight: 20, height: selectedTag == LINKS_TAG ? 75 : 60)
+            container.backgroundColor = .lightGray.withAlphaComponent(0.3)
+            container.layer.cornerRadius = 15
+            container.clipsToBounds = true
+            container.isUserInteractionEnabled = true
+            
+            if selectedTag == DOCUMENTS_TAG {
+                subtitle.text = "📄 " + "Document".localized()
+                
+                let imageFile = UIImageView(image: UIImage(systemName: "doc.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 45)))
+                container.addSubview(imageFile)
+                imageFile.tintColor = .black
+                imageFile.anchor(top: container.topAnchor, left: container.leftAnchor, bottom: container.bottomAnchor, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, width: 45)
+                
+                let nameFile = UILabel()
+                container.addSubview(nameFile)
+                nameFile.font = .systemFont(ofSize: 12 + String.offset(), weight: .medium)
+                nameFile.textColor = .black
+                nameFile.numberOfLines = 2
+                nameFile.anchor(top: container.topAnchor, left: imageFile.rightAnchor, right: container.rightAnchor, paddingTop: 5, paddingLeft: 10, paddingRight: 5)
+                nameFile.text = data.messageText.components(separatedBy: "|")[0]
+                
+                let fileSub = UILabel()
+                let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
+                let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
+                let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
+                let arrExtFile = (data.messageText.components(separatedBy: "|")[0]).split(separator: ".")
+                let finalExtFile = arrExtFile[arrExtFile.count - 1]
+                if let dirPath = paths.first {
+                    let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(data.file)
+                    if FileManager.default.fileExists(atPath: fileURL.path) {
+                        if let dataFile = try? Data(contentsOf: fileURL) {
+                            var sizeOfFile = Int(dataFile.count / 1000000)
+                            if (sizeOfFile < 1) {
+                                sizeOfFile = Int(dataFile.count / 1000)
+                                if (finalExtFile.count > 4) {
+                                    fileSub.text = "\(sizeOfFile) kB \u{2022} TXT"
+                                }else {
+                                    fileSub.text = "\(sizeOfFile) kB \u{2022} \(finalExtFile.uppercased())"
+                                }
+                            } else {
+                                if (finalExtFile.count > 4) {
+                                    fileSub.text = "\(sizeOfFile) MB \u{2022} TXT"
+                                }else {
+                                    fileSub.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
+                                }
+                            }
+                        } else {
+                            fileSub.text = ""
+                        }
+                    }
+                    else if FileEncryption.shared.isSecureExists(filename: data.file) {
+                        if let dataFile = try? FileEncryption.shared.readSecure(filename: data.file) {
+                            var sizeOfFile = Int(dataFile.count / 1000000)
+                            if (sizeOfFile < 1) {
+                                sizeOfFile = Int(dataFile.count / 1000)
+                                if (finalExtFile.count > 4) {
+                                    fileSub.text = "\(sizeOfFile) kB \u{2022} TXT"
+                                }else {
+                                    fileSub.text = "\(sizeOfFile) kB \u{2022} \(finalExtFile.uppercased())"
+                                }
+                            } else {
+                                if (finalExtFile.count > 4) {
+                                    fileSub.text = "\(sizeOfFile) MB \u{2022} TXT"
+                                }else {
+                                    fileSub.text = "\(sizeOfFile) MB \u{2022} \(finalExtFile.uppercased())"
+                                }
+                            }
+                        } else {
+                            fileSub.text = ""
+                        }
+                    }
+                    container.addSubview(fileSub)
+                    fileSub.anchor(top: nameFile.bottomAnchor, left: imageFile.rightAnchor, bottom: container.bottomAnchor, paddingLeft: 10, paddingBottom: 5)
+                    fileSub.font = .systemFont(ofSize: 10 + String.offset())
+                    fileSub.textColor = .gray
+                    let objectTap = ObjectGesture(target: self, action: #selector(onContSearch(_:)))
+                    objectTap.file_id = data.file
+                    container.addGestureRecognizer(objectTap)
+                }
+            } else if selectedTag == LINKS_TAG {
+                var text = ""
+                var txtData = data.messageText
+                if txtData.contains("■"){
+                    txtData = txtData.components(separatedBy: "■")[0]
+                    txtData = txtData.trimmingCharacters(in: .whitespacesAndNewlines)
+                }
+                let listTextSplitBreak = txtData.components(separatedBy: "\n")
+                let indexFirstLinkSplitBreak = listTextSplitBreak.firstIndex(where: { $0.contains("www.") || $0.contains("http://") || $0.contains("https://") })
+                if indexFirstLinkSplitBreak != nil {
+                    let listTextSplitSpace = listTextSplitBreak[indexFirstLinkSplitBreak!].components(separatedBy: " ")
+                    let indexFirstLinkSplitSpace = listTextSplitSpace.firstIndex(where: { ($0.starts(with: "www.") && $0.components(separatedBy: ".").count > 2) || ($0.starts(with: "http://") && $0.components(separatedBy: ".").count > 1) || ($0.starts(with: "https://") && $0.components(separatedBy: ".").count > 1) })
+                    if indexFirstLinkSplitSpace != nil {
+                        text = listTextSplitSpace[indexFirstLinkSplitSpace!]
+                    }
+                }
+                var dataURL = ""
+                subtitle.text = txtData
+                Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                    do {
+                        if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select data_link from LINK_PREVIEW where link='\(text)'"), cursor.next() {
+                            if let data = cursor.string(forColumnIndex: 0) {
+                                dataURL = data
+                            }
+                            cursor.close()
+                        }
+                    } catch {
+                        rollback.pointee = true
+                        print("Access database error: \(error.localizedDescription)")
+                    }
+                })
+                
+                var title = ""
+                var description = ""
+                var imageUrl: String?
+                var link = text
+                
+                let objectTap = ObjectGesture(target: self, action: #selector(onContSearch(_:)))
+                objectTap.message_id = link
+                container.addGestureRecognizer(objectTap)
+                
+                let imagePreview = UIImageView()
+                container.addSubview(imagePreview)
+                imagePreview.translatesAutoresizingMaskIntoConstraints = false
+                imagePreview.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true
+                imagePreview.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true
+                imagePreview.topAnchor.constraint(equalTo: container.topAnchor).isActive = true
+                imagePreview.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
+                
+                imagePreview.image = UIImage(systemName: "link", withConfiguration: UIImage.SymbolConfiguration(pointSize: 45))
+                imagePreview.contentMode = .center
+                imagePreview.clipsToBounds = true
+                imagePreview.tintColor = .black
+                imagePreview.backgroundColor = .gray.withAlphaComponent(0.3)
+                
+                let titlePreview = UILabel()
+                container.addSubview(titlePreview)
+                titlePreview.translatesAutoresizingMaskIntoConstraints = false
+                titlePreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
+                titlePreview.topAnchor.constraint(equalTo: container.topAnchor, constant: 10.0).isActive = true
+                titlePreview.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -5.0).isActive = true
+                titlePreview.text = title
+                titlePreview.font = UIFont.systemFont(ofSize: 14.0 + String.offset(), weight: .bold)
+                titlePreview.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
+                
+                let descPreview = UILabel()
+                container.addSubview(descPreview)
+                descPreview.translatesAutoresizingMaskIntoConstraints = false
+                descPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
+                descPreview.topAnchor.constraint(equalTo: titlePreview.bottomAnchor).isActive = true
+                descPreview.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -5.0).isActive = true
+                descPreview.text = description
+                descPreview.font = UIFont.systemFont(ofSize: 12.0 + String.offset())
+                descPreview.textColor = .gray
+                descPreview.numberOfLines = 1
+                
+                let linkPreview = UILabel()
+                container.addSubview(linkPreview)
+                linkPreview.translatesAutoresizingMaskIntoConstraints = false
+                linkPreview.leadingAnchor.constraint(equalTo: imagePreview.trailingAnchor, constant: 5.0).isActive = true
+                linkPreview.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -5.0).isActive = true
+                linkPreview.font = UIFont.systemFont(ofSize: 10.0 + String.offset())
+                linkPreview.textColor = .gray
+                linkPreview.numberOfLines = 1
+                
+                if !dataURL.isEmpty {
+                    if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
+                        title = data["title"] as! String
+                        description = data["description"] as! String
+                        imageUrl = data["imageUrl"] as? String
+                        link = data["link"] as! String
+                        
+                        if imageUrl != nil {
+                            imagePreview.loadImageAsync(with: imageUrl)
+                            imagePreview.contentMode = .scaleToFill
+                            imagePreview.clipsToBounds = true
+                        }
+                        
+                        titlePreview.text = title
+                        descPreview.text = description
+                        linkPreview.text = link
+                        linkPreview.topAnchor.constraint(equalTo: descPreview.bottomAnchor, constant: 8.0).isActive = true
+                    }
+                } else {
+                    linkPreview.text = link
+                    linkPreview.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
+                }
+            } else if selectedTag == AUDIOS_TAG {
+                subtitle.text = "♫ " + "Audio".localized()
+                
+                let imageAudio = UIImageView()
+                imageAudio.image = UIImage(systemName: "music.note", withConfiguration: UIImage.SymbolConfiguration(pointSize: 35))
+                container.addSubview(imageAudio)
+                imageAudio.anchor(left: container.leftAnchor, paddingLeft: 10, centerY: container.centerYAnchor)
+                imageAudio.tintColor = .black
+                
+                let nameAudio = UILabel()
+                container.addSubview(nameAudio)
+                nameAudio.anchor(left: imageAudio.rightAnchor, right: container.rightAnchor, paddingLeft: 10, paddingRight: 10, centerY: container.centerYAnchor)
+                nameAudio.numberOfLines = 2
+                nameAudio.text = data.messageText.components(separatedBy: "|")[0]
+                nameAudio.font = .systemFont(ofSize: 16 + String.offset(), weight: .medium)
+                
+                let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
+                let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
+                let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
+                if let dirPath = paths.first {
+                    let audioURL = URL(fileURLWithPath: dirPath).appendingPathComponent(data.audio)
+                    if !FileManager.default.fileExists(atPath: audioURL.path) && !FileEncryption.shared.isSecureExists(filename: data.audio) {
+                        Download().startHTTP(forKey: data.audio, isImage: false) { (name, progress) in
+                            guard progress == 100 else {
+                                return
+                            }
+                            tableView.reloadRows(at: [indexPath], with: .none)
+                        }
+                    } else {
+                        let objectTap = ObjectGesture(target: self, action: #selector(onContSearch(_:)))
+                        objectTap.audio_id = data.audio
+                        container.addGestureRecognizer(objectTap)
+                    }
+                }
+            }
+            return cell
+        }
+        
+        let imageView = UIImageView()
+        content.addSubview(imageView)
+        imageView.translatesAutoresizingMaskIntoConstraints = false
+        NSLayoutConstraint.activate([
+            imageView.topAnchor.constraint(equalTo: content.topAnchor, constant: 10.0),
+            imageView.bottomAnchor.constraint(equalTo: content.bottomAnchor, constant: -10.0),
+            imageView.widthAnchor.constraint(equalToConstant: 55.0),
+            imageView.heightAnchor.constraint(equalToConstant: 55.0)
+        ])
+        var leadingAnchor = imageView.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 20.0)
+        if data.profile.isEmpty && data.pin != "-999" && data.pin != "-997" {
+            if data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL {
+                imageView.image = UIImage(named: "Profile---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+            } else {
+                imageView.image = UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+            }
+        } else if data.pin == "-997" {
+            imageView.frame = CGRect(x: 0, y: 0, width: 55.0, height: 55.0)
+            imageView.circle()
+            if let urlGif = Bundle.resourceBundle(for: Nexilis.self).url(forResource: "pb_gpt_bot", withExtension: "gif") {
+                imageView.sd_setImage(with: urlGif) { (image, error, cacheType, imageURL) in
+                    if error == nil {
+                        imageView.animationImages = image?.images
+                        imageView.animationDuration = image?.duration ?? 0.0
+                        imageView.animationRepeatCount = 0
+                        imageView.startAnimating()
+                    }
+                }
+            } else if let urlGif = Bundle.resourcesMediaBundle(for: Nexilis.self).url(forResource: "pb_gpt_bot", withExtension: "gif") {
+                imageView.sd_setImage(with: urlGif) { (image, error, cacheType, imageURL) in
+                    if error == nil {
+                        imageView.animationImages = image?.images
+                        imageView.animationDuration = image?.duration ?? 0.0
+                        imageView.animationRepeatCount = 0
+                        imageView.startAnimating()
+                    }
+                }
+            }
+        } else {
+            if !Utils.getIconDock().isEmpty && data.profile.isEmpty {
+                let urlString = Utils.getUrlDock()!
+                if let cachedImage = ImageCache.shared.image(forKey: urlString) {
+                    let imageData = cachedImage
+                    imageView.image = imageData
+                } else {
+                    DispatchQueue.global().async{
+                        Utils.fetchDataWithCookiesAndUserAgent(from: URL(string: urlString)!) { data, response, error in
+                            guard let data = data, error == nil else { return }
+                            DispatchQueue.main.async() {
+                                if UIImage(data: data) != nil {
+                                    let imageData = UIImage(data: data)!
+                                    imageView.image = imageData
+                                    ImageCache.shared.save(image: imageData, forKey: urlString)
+                                }
+                            }
+                        }
+                    }
+                }
+            } else {
+                if data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL || data.isParent || data.pin == "-999" {
+                    getImage(name: data.profile, placeholderImage: UIImage(named: data.pin == "-999" ? "pb_button" : (data.messageScope == MessageScope.WHISPER || data.messageScope == MessageScope.CALL || data.messageScope == MessageScope.MISSED_CALL) ? "Profile---Purple" : "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), isCircle: true, tableView: tableView, indexPath: indexPath, completion: { result, isDownloaded, image in
+                        imageView.image = image
+                    })
+                } else {
+                    leadingAnchor = imageView.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 40.0)
+                    let image = UIImage(named: "Conversation---Purple", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+                    imageView.image = image
+                }
+            }
+        }
+        leadingAnchor.isActive = true
+        
+        let titleView = UILabel()
+        content.addSubview(titleView)
+        titleView.translatesAutoresizingMaskIntoConstraints = false
+        NSLayoutConstraint.activate([
+            titleView.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 10.0),
+            titleView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -40.0),
+        ])
+        titleView.font = UIFont.systemFont(ofSize: 14 + String.offset(), weight: .medium)
+        
+        let timeView = UILabel()
+        let viewCounter = UIView()
+        
+        if data.counter != "0" {
+            timeView.textColor = .systemRed
+            content.addSubview(viewCounter)
+            viewCounter.translatesAutoresizingMaskIntoConstraints = false
+            NSLayoutConstraint.activate([
+                viewCounter.widthAnchor.constraint(greaterThanOrEqualToConstant: 20),
+                viewCounter.heightAnchor.constraint(equalToConstant: 20)
+            ])
+            viewCounter.backgroundColor = .systemRed
+            viewCounter.layer.cornerRadius = 10
+            viewCounter.clipsToBounds = true
+            viewCounter.layer.borderWidth = 0.5
+            viewCounter.layer.borderColor = UIColor.secondaryColor.cgColor
+
+            let labelCounter = UILabel()
+            viewCounter.addSubview(labelCounter)
+            labelCounter.translatesAutoresizingMaskIntoConstraints = false
+            NSLayoutConstraint.activate([
+                labelCounter.centerYAnchor.constraint(equalTo: viewCounter.centerYAnchor),
+                labelCounter.leadingAnchor.constraint(equalTo: viewCounter.leadingAnchor, constant: 2),
+                labelCounter.trailingAnchor.constraint(equalTo: viewCounter.trailingAnchor, constant: -2),
+            ])
+            labelCounter.font = UIFont.systemFont(ofSize: 11 + String.offset())
+            if Int(data.counter)! > 99 {
+                labelCounter.text = "99+"
+            } else {
+                labelCounter.text = data.counter
+            }
+            labelCounter.textColor = .secondaryColor
+            labelCounter.textAlignment = .center
+        }
+        
+        if !data.isParent {
+            titleView.topAnchor.constraint(equalTo: content.topAnchor, constant: 10.0).isActive = true
+            titleView.text = data.name
+            
+            content.addSubview(timeView)
+            timeView.translatesAutoresizingMaskIntoConstraints = false
+            NSLayoutConstraint.activate([
+                timeView.topAnchor.constraint(equalTo: content.topAnchor, constant: 10.0),
+                timeView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20.0),
+            ])
+            timeView.textColor = .gray
+            timeView.font = UIFont.systemFont(ofSize: 14 + String.offset())
+            
+            let date = Date(milliseconds: Int64(data.serverDate) ?? 0)
+            let calendar = Calendar.current
+            
+            if (calendar.isDateInToday(date)) {
+                let formatter = DateFormatter()
+                formatter.dateFormat = "HH:mm"
+                formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
+                timeView.text = formatter.string(from: date as Date)
+            } else {
+                let startOfNow = calendar.startOfDay(for: Date())
+                let startOfTimeStamp = calendar.startOfDay(for: date)
+                let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
+                let day = -(components.day!)
+                if day == 1 {
+                    timeView.text = "Yesterday".localized()
+                } else {
+                    if day < 7 {
+                        let formatter = DateFormatter()
+                        formatter.dateFormat = "EEEE"
+                        let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
+                        if lang == "id" {
+                            formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
+                        }
+                        timeView.text = formatter.string(from: date)
+                    } else {
+                        let formatter = DateFormatter()
+                        formatter.dateFormat = "M/dd/yy"
+                        formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
+                        let stringFormat = formatter.string(from: date as Date)
+                        timeView.text = stringFormat
+                    }
+                }
+            }
+            
+            let messageView = UILabel()
+            content.addSubview(messageView)
+            messageView.translatesAutoresizingMaskIntoConstraints = false
+            NSLayoutConstraint.activate([
+                messageView.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 10.0),
+                messageView.topAnchor.constraint(equalTo: titleView.bottomAnchor),
+                messageView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -40.0),
+            ])
+            messageView.textColor = .gray
+            if data.messageText.contains("■") {
+                data.messageText = data.messageText.components(separatedBy: "■")[0]
+                data.messageText = data.messageText.trimmingCharacters(in: .whitespacesAndNewlines)
+            }
+            let text = Utils.previewMessageText(chat: data)
+            let idMe = User.getMyPin() as String?
+            if let attributeText = text as? NSMutableAttributedString {
+                let stringMessage = NSMutableAttributedString(string: "")
+                if data.fpin == idMe {
+                    if data.lock == "1" {
+                        if data.messageScope == "4" {
+                            stringMessage.append(NSAttributedString(string: "You".localized() + ": ", attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12 + String.offset(), weight: .medium)]))
+                        }
+                        stringMessage.append(("🚫 _"+"You were deleted this message".localized()+"_").richText())
+                    } else {
+                        let imageStatus = NSTextAttachment()
+                        if data.messageScope != MessageScope.CALL && data.messageScope != MessageScope.MISSED_CALL {
+                            let status = getRealStatus(messageId: data.messageId)
+                            if status == "0" {
+                                imageStatus.image = UIImage(systemName: "xmark.circle")!.withTintColor(UIColor.red, renderingMode: .alwaysOriginal)
+                            } else if status == "1" {
+                                imageStatus.image = UIImage(systemName: "clock.arrow.circlepath")!.withTintColor(UIColor.lightGray, renderingMode: .alwaysOriginal)
+                            } else if status == "2" {
+                                imageStatus.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
+                            } else if (status == "3") {
+                                imageStatus.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.lightGray)
+                            } else if (status == "8") {
+                                imageStatus.image = UIImage(named: "message_status_ack", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withRenderingMode(.alwaysOriginal)
+                            } else {
+                                imageStatus.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.systemBlue)
+                            }
+                            imageStatus.bounds = CGRect(x: 0, y: -5, width: 15, height: 15)
+                            let imageStatusString = NSAttributedString(attachment: imageStatus)
+                            stringMessage.append(imageStatusString)
+                            stringMessage.append(NSAttributedString(string: " "))
+                        }
+                        if data.messageScope == "4" {
+                            stringMessage.append(NSAttributedString(string: "You".localized() + ": ", attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12 + String.offset(), weight: .medium)]))
+                        }
+                        stringMessage.append(attributeText)
+                    }
+                } else {
+                    if data.messageScope == "4" {
+                        var fullname = User.getData(pin: data.fpin, lPin: data.pin)!.fullName
+                        let components = fullname.split(separator: " ")
+                        if components.count >= 2 {
+                            fullname = components.prefix(2).joined(separator: " ")
+                        }
+                        stringMessage.append(NSAttributedString(string: fullname + ": ", attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12 + String.offset(), weight: .medium)]))
+                    }
+                    if data.lock == "1" {
+                        stringMessage.append(("🚫 _"+"This message was deleted".localized()+"_").richText())
+                    } else {
+                        stringMessage.append(attributeText)
+                    }
+                }
+                messageView.attributedText = stringMessage
+            }
+            messageView.numberOfLines = 2
+            
+            if data.counter != "0" {
+                viewCounter.topAnchor.constraint(equalTo: timeView.bottomAnchor, constant: 5.0).isActive = true
+                viewCounter.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20).isActive = true
+            }
+        } else {
+            titleView.centerYAnchor.constraint(equalTo: content.centerYAnchor).isActive = true
+            titleView.text = data.groupName
+            
+            let iconName = (data.isSelected) ? "chevron.up.circle" : "chevron.down.circle"
+            let imageView = UIImageView(image: UIImage(systemName: iconName))
+            imageView.tintColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
+            content.addSubview(imageView)
+            imageView.translatesAutoresizingMaskIntoConstraints = false
+            NSLayoutConstraint.activate([
+                imageView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20),
+                imageView.centerYAnchor.constraint(equalTo: content.centerYAnchor),
+                imageView.widthAnchor.constraint(equalToConstant: 20),
+                imageView.heightAnchor.constraint(equalToConstant: 20)
+            ])
+            
+            if data.counter != "0" {
+                viewCounter.trailingAnchor.constraint(equalTo: imageView.leadingAnchor, constant: -5).isActive = true
+                viewCounter.centerYAnchor.constraint(equalTo: content.centerYAnchor).isActive = true
+            }
+        }
+        cell.separatorInset = UIEdgeInsets(top: 0, left: 85, bottom: 0, right: 0)
+        return cell
+    }
+    
+    @objc func onContSearch(_ sender: ObjectGesture) {
+        if selectedTag == PHOTOS_TAG {
+            
+        } else if selectedTag == VIDEOS_TAG {
+            
+        } else if selectedTag == DOCUMENTS_TAG {
+            let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
+            let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
+            let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
+            if let dirPath = paths.first {
+                let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.file_id)
+                if FileManager.default.fileExists(atPath: fileURL.path) {
+                    self.previewItem = fileURL as NSURL
+                    let previewController = QLPreviewController()
+                    let rightBarButton = UIBarButtonItem()
+                    previewController.navigationItem.rightBarButtonItem = rightBarButton
+                    previewController.dataSource = self
+                    previewController.modalPresentationStyle = .custom
+                    
+                    self.present(previewController, animated: true)
+                } else if FileEncryption.shared.isSecureExists(filename: sender.file_id) {
+                    do {
+                        if let docData = try FileEncryption.shared.readSecure(filename: sender.file_id) {
+                            
+                            let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
+                            let tempPath = cachesDirectory.appendingPathComponent(sender.file_id)
+                            try docData.write(to: tempPath)
+                            self.previewItem = tempPath as NSURL
+                            let previewController = QLPreviewController()
+                            let rightBarButton = UIBarButtonItem()
+                            previewController.navigationItem.rightBarButtonItem = rightBarButton
+                            previewController.dataSource = self
+                            previewController.modalPresentationStyle = .custom
+                            self.present(previewController,animated: true)
+                        }
+                    }
+                    catch {
+                        
+                    }
+                }
+            }
+        } else if selectedTag == LINKS_TAG {
+            var stringURl = sender.message_id
+            if stringURl.lowercased().starts(with: "www.") {
+                stringURl = "https://" + stringURl.replacingOccurrences(of: "www.", with: "")
+            }
+            guard let url = URL(string: stringURl) else { return }
+            UIApplication.shared.open(url)
+        } else if selectedTag == AUDIOS_TAG {
+            let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
+            let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
+            let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
+            if let dirPath = paths.first {
+                let audioURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.audio_id)
+                if FileManager.default.fileExists(atPath: audioURL.path) {
+                    do {
+                        if audioPlayer == nil || audioPlayer?.url != audioURL {
+                            do {
+                                try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
+                                try AVAudioSession.sharedInstance().setActive(true)
+                            } catch {
+                                
+                            }
+                            audioPlayer = try AVAudioPlayer(contentsOf: audioURL)
+                            audioPlayer?.prepareToPlay()
+                            audioPlayer?.play()
+                        } else if audioPlayer!.isPlaying {
+                            audioPlayer?.pause()
+                        } else {
+                            audioPlayer?.play()
+                        }
+                    } catch {
+                        
+                    }
+                } else if FileEncryption.shared.isSecureExists(filename: sender.audio_id) {
+                    do {
+                        if let audioData = try FileEncryption.shared.readSecure(filename: sender.audio_id) {
+                            let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
+                            let tempPath = cachesDirectory.appendingPathComponent(sender.audio_id)
+                            try audioData.write(to: tempPath)
+                            if audioPlayer == nil || audioPlayer?.url != tempPath {
+                                do {
+                                    try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
+                                    try AVAudioSession.sharedInstance().setActive(true)
+                                } catch {
+                                    
+                                }
+                                audioPlayer = try AVAudioPlayer(contentsOf: tempPath)
+                                audioPlayer?.prepareToPlay()
+                                audioPlayer?.play()
+                            } else if audioPlayer!.isPlaying {
+                                audioPlayer?.pause()
+                            } else {
+                                audioPlayer?.play()
+                            }
+                        }
+                    } catch {
+                        
+                    }
+                }
+            }
+        }
+    }
+    
+    private func getRealStatus(messageId: String) -> String {
+        var status = "1"
+        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+            if let cursorStatus = Database.shared.getRecords(fmdb: fmdb, query: "SELECT status, f_pin FROM MESSAGE_STATUS WHERE message_id='\(messageId)'") {
+                var listStatus: [Int] = []
+                while cursorStatus.next() {
+                    listStatus.append(Int(cursorStatus.string(forColumnIndex: 0)!)!)
+                }
+                cursorStatus.close()
+                status = "\(listStatus.min() ?? 2)"
+            }
+        })
+        return status
+    }
+    
+    public func updateSearchResults(for searchController: UISearchController) {
+    }
+    
+    public func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
+        return self.previewItem != nil ? 1 : 0
+    }
+    
+    public func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> any QLPreviewItem {
+        return self.previewItem!
+    }
+}

+ 4 - 1
NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift

@@ -1911,7 +1911,10 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         DispatchQueue.main.async {
             let data:[AnyHashable : Any] = notification.userInfo!
             let messageId = data["message_id"]  as? String ?? ""
-            self.appendNewMessage(messageId: messageId)
+            let pin = data["pin"]  as? String ?? ""
+            if pin == self.dataPerson["f_pin"]!! {
+                self.appendNewMessage(messageId: messageId)
+            }
         }
     }
     

+ 1 - 1
NexilisLite/Podfile

@@ -7,7 +7,7 @@ target 'NexilisLite' do
 
   # Pods for NexilisLite
 
-  pod 'nuSDKService', '4.0.18'
+  pod 'nuSDKService', '4.0.19'
   pod 'FMDB', '~> 2.7.12'
   pod 'NotificationBannerSwift', :git => 'https://github.com/Daltron/NotificationBanner.git', :tag => '4.0.0'
   pod 'Alamofire', '~> 5.10.2'

+ 1 - 1
StreamShield/Podfile

@@ -6,6 +6,6 @@ target 'StreamShield' do
   use_frameworks!
 
   # Pods for StreamShield
-  pod 'nuSDKService', '~> 4.0.18'
+  pod 'nuSDKService', '~> 4.0.19'
 
 end

+ 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', '~> 4.0.18'
+  spec.dependency 'nuSDKService', '~> 4.0.19'
   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' }