Bläddra i källkod

update fix bugs

alqindiirsyam 7 månader sedan
förälder
incheckning
27aedf056f
23 ändrade filer med 276 tillägg och 198 borttagningar
  1. 1 1
      AppBuilder/AppBuilder/FirstTabViewController.swift
  2. 1 1
      AppBuilder/AppBuilder/SecondTabViewController.swift
  3. 1 1
      AppBuilder/AppBuilder/ThirdTabViewController.swift
  4. 40 55
      AppBuilder/AppBuilder/ViewController.swift
  5. BIN
      NexilisLite/.DS_Store
  6. 18 18
      NexilisLite/NexilisLite/Source/APIS.swift
  7. 0 45
      NexilisLite/NexilisLite/Source/Extension.swift
  8. 24 0
      NexilisLite/NexilisLite/Source/Nexilis.swift
  9. 1 1
      NexilisLite/NexilisLite/Source/View/BNIView/BNIBookingWebView.swift
  10. 24 0
      NexilisLite/NexilisLite/Source/View/Call/QmeraAudioViewController.swift
  11. 8 8
      NexilisLite/NexilisLite/Source/View/Chat/ChatGPTBotView.swift
  12. 37 16
      NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift
  13. 50 26
      NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift
  14. 7 7
      NexilisLite/NexilisLite/Source/View/Chat/EditorStarMessages.swift
  15. 47 2
      NexilisLite/NexilisLite/Source/View/Chat/MessageInfo.swift
  16. 3 3
      NexilisLite/NexilisLite/Source/View/Contact/ContactCallViewController.swift
  17. 1 1
      NexilisLite/NexilisLite/Source/View/Control/BackupRestoreView.swift
  18. 5 5
      NexilisLite/NexilisLite/Source/View/Control/ContactChatViewController.swift
  19. 1 1
      NexilisLite/NexilisLite/Source/View/Control/GroupCreateViewController.swift
  20. 1 1
      NexilisLite/NexilisLite/Source/View/Control/GroupMemberViewController.swift
  21. 2 2
      NexilisLite/NexilisLite/Source/View/Control/ProfileViewController.swift
  22. 2 2
      NexilisLite/NexilisLite/Source/View/Streaming/CreateViewController.swift
  23. 2 2
      NexilisLite/NexilisLite/Source/View/Streaming/QmeraStreamingViewController.swift

+ 1 - 1
AppBuilder/AppBuilder/FirstTabViewController.swift

@@ -395,7 +395,7 @@ class FirstTabViewController: UIViewController, UIScrollViewDelegate, UIGestureR
                   let param1 = dict["param1"] as? String else {
                 return
             }
-            showToast(message: param1, controller: self.tabBarController!)
+            self.view.makeToast(param1, duration: 3)
         } else if message.name == "blockUser" {
             guard let dict = message.body as? [String: AnyObject],
                   let param1 = dict["param1"] as? String,

+ 1 - 1
AppBuilder/AppBuilder/SecondTabViewController.swift

@@ -1216,7 +1216,7 @@ extension SecondTabViewController: UITableViewDelegate, UITableViewDataSource {
                         viewController = self.parent
                     }
                     if let viewController = viewController as? ViewController {
-                        viewController.view.makeToast("You are not a member of this group".localized(), duration: 0.5)
+                        viewController.view.makeToast("You are not a member of this group".localized(), duration: 3)
                     }
                 }
             })

+ 1 - 1
AppBuilder/AppBuilder/ThirdTabViewController.swift

@@ -410,7 +410,7 @@ class ThirdTabViewController: UIViewController, UIScrollViewDelegate, UIGestureR
                   let param1 = dict["param1"] as? String else {
                 return
             }
-            showToast(message: param1, controller: self.tabBarController!)
+            self.view.makeToast(param1, duration: 3)
         } else if message.name == "blockUser" {
             guard let dict = message.body as? [String: AnyObject],
                   let param1 = dict["param1"] as? String,

+ 40 - 55
AppBuilder/AppBuilder/ViewController.swift

@@ -215,30 +215,6 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
                 }
             }
         }
-        if !Utils.getIndicatorTabImage().isEmpty {
-            let indicatorImage = Utils.getIndicatorTabImage()
-            let fullUrl = "\(PrefsUtil.getURLBase())get_file_from_path?img=\(indicatorImage)"
-            if let cachedImage = ImageCache.shared.image(forKey: fullUrl) {
-                DispatchQueue.main.async() { [self] in
-                    imageIndicator = cachedImage
-                    addCustomViewAboveTabBarItem(at: 0, image: imageIndicator)
-                }
-            } else {
-                Utils.fetchDataWithCookiesAndUserAgent(from: URL(string: fullUrl)!) { data, response, error in
-                    guard let data = data, error == nil else { return }
-                    // always update the UI from the main thread
-                    DispatchQueue.main.async() { [self] in
-                        if UIImage(data: data) != nil {
-                            imageIndicator = UIImage(data: data)!
-                            addCustomViewAboveTabBarItem(at: 0, image: imageIndicator)
-                            ImageCache.shared.save(image: UIImage(data: data)!, forKey: fullUrl)
-                        }
-                    }
-                }
-            }
-        } else {
-//            addCustomViewAboveTabBarItem(at: 0, image: imageIndicator)
-        }
         if((cpaasMode == PrefsUtil.CPAAS_MODE_DOCKED || cpaasMode == PrefsUtil.CPAAS_MODE_MIX)){
             createMidFloatingButton()
             navigationController?.setNavigationBarHidden(true, animated: false)
@@ -264,7 +240,31 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
         let center: NotificationCenter = NotificationCenter.default
         center.addObserver(self, selector: #selector(checkCounter), name: NSNotification.Name(rawValue: Nexilis.listenerReceiveChat), object: nil)
         center.addObserver(self, selector: #selector(checkCounter), name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil)
-        checkCounter()
+        if !Utils.getIndicatorTabImage().isEmpty {
+            let indicatorImage = Utils.getIndicatorTabImage()
+            let fullUrl = "\(PrefsUtil.getURLBase())get_file_from_path?img=\(indicatorImage)"
+            if let cachedImage = ImageCache.shared.image(forKey: fullUrl) {
+                DispatchQueue.main.async() { [self] in
+                    imageIndicator = cachedImage
+                    addCustomViewAboveTabBarItem(at: 0, image: imageIndicator)
+                }
+            } else {
+                Utils.fetchDataWithCookiesAndUserAgent(from: URL(string: fullUrl)!) { data, response, error in
+                    guard let data = data, error == nil else { return }
+                    // always update the UI from the main thread
+                    DispatchQueue.main.async() { [self] in
+                        if UIImage(data: data) != nil {
+                            imageIndicator = UIImage(data: data)!
+                            addCustomViewAboveTabBarItem(at: 0, image: imageIndicator)
+                            ImageCache.shared.save(image: UIImage(data: data)!, forKey: fullUrl)
+                        }
+                    }
+                }
+            }
+        }
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { [self] in
+            checkCounter()
+        })
         willappear()
 //        Nexilis.debugBroadcast()
     }
@@ -273,32 +273,14 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
         guard let tabBarItems = tabBar.items, index < tabBarItems.count else { return }
         let tabBarFrame = tabBar.frame
         let tabBarItemWidth = tabBarFrame.width / CGFloat(tabBarItems.count)
-        let xCenter = CGFloat(index) * tabBarItemWidth + (tabBarItemWidth / 2)
-        let yCenter = tabBarFrame.origin.y + (tabBarFrame.height / 2)
+        let multiplierValue: CGFloat = CGFloat(index + index + 1)
+        let xCenter = tabBarItemWidth * multiplierValue / 2 - 20
         if image != nil {
             indicatorImage = UIImageView(image: image)
             indicatorImage.contentMode = .scaleAspectFit
-            indicatorImage.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
-            indicatorImage.center = CGPoint(x: xCenter, y: yCenter)
-            view.addSubview(indicatorImage)
-        } else {
-            indicatorView = UIView()
-            indicatorView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
-            indicatorView.center = CGPoint(x: xCenter, y: yCenter)
-            let gradientLayer = CAGradientLayer()
-            gradientLayer.type = .radial
-            gradientLayer.colors = [
-                UIColor(white: 1.0, alpha: 0.0).cgColor,
-                UIColor(white: 1.0, alpha: 0.0).cgColor,
-                UIColor(red: 0.75, green: 0.71, blue: 0.71, alpha: 1.0).cgColor
-            ]
-            gradientLayer.locations = [0.0, 0.7, 1.0]
-            gradientLayer.bounds = CGRect(x: 0, y: 0, width: 90, height: 90)
-            gradientLayer.position = CGPoint(x: 45, y: 45)
-            indicatorView.layer.insertSublayer(gradientLayer, at: 0)
-            indicatorView.layer.cornerRadius = indicatorView.frame.width / 2
-            indicatorView.clipsToBounds = true
-            view.addSubview(indicatorView)
+            indicatorImage.frame = CGRect(x: xCenter, y: 5, width: 40, height: 40)
+            tabBar.addSubview(indicatorImage)
+            tabBar.sendSubviewToBack(indicatorImage)
         }
     }
     
@@ -346,15 +328,18 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
                 if self.viewControllers?.firstIndex(of: secondTab!) != nil {
                     let counter = queryCountCounter()
                     let indexSecondTab = self.viewControllers?.firstIndex(of: secondTab!)
-                    let viewSecondTab = self.tabBar.items?[indexSecondTab!].value(forKey: "view") as! UIView
                     if counter > 0 {
-                        if !indicatorCounterFB.isDescendant(of: viewSecondTab) {
-                            let viewSecondTab = self.tabBar.items?[indexSecondTab!].value(forKey: "view") as! UIView
+                        if !indicatorCounterFB.isDescendant(of: tabBar) {
+                            guard let tabBarItems = tabBar.items, let index = indexSecondTab else { return }
+                            let tabBarFrame = tabBar.frame
+                            let tabBarItemWidth = tabBarFrame.width / CGFloat(tabBarItems.count)
+                            let multiplierValue: CGFloat = CGFloat(index + index + 1)
+                            let xCenter = tabBarItemWidth * multiplierValue / 2 + 10
                             indicatorCounterFB.backgroundColor = .red
-                            indicatorCounterFB.layer.cornerRadius = 7.5
+                            indicatorCounterFB.layer.cornerRadius = 6.5
                             indicatorCounterFB.clipsToBounds = true
-                            viewSecondTab.addSubview(indicatorCounterFB)
-                            indicatorCounterFB.anchor(top: viewSecondTab.topAnchor, right: viewSecondTab.rightAnchor, paddingRight: 45, height: 15, minWidth: 15, maxWidth: 20)
+                            indicatorCounterFB.frame = CGRect(x: xCenter, y: 5, width: counter > 99 ? 30 : counter > 9 ? 23 : 13, height: 13)
+                            tabBar.addSubview(indicatorCounterFB)
                             indicatorCounterFB.addSubview(labelCounterFB)
                             labelCounterFB.anchor(left: indicatorCounterFB.leftAnchor, right: indicatorCounterFB.rightAnchor, paddingLeft: 5, paddingRight: 5, centerX: indicatorCounterFB.centerXAnchor, centerY: indicatorCounterFB.centerYAnchor)
                             labelCounterFB.font = .systemFont(ofSize: 10)
@@ -366,7 +351,7 @@ class ViewController: UITabBarController, UITabBarControllerDelegate, SettingMAB
                             labelCounterFB.text = "\(counter)"
                         }
                     } else {
-                        if indicatorCounterFB.isDescendant(of: viewSecondTab) {
+                        if indicatorCounterFB.isDescendant(of: tabBar) {
                             indicatorCounterFB.removeFromSuperview()
                         }
                     }

BIN
NexilisLite/.DS_Store


+ 18 - 18
NexilisLite/NexilisLite/Source/APIS.swift

@@ -78,7 +78,7 @@ public class APIS: NSObject {
         } else {
             if media != nil || (media == nil && category != nil) {
                 if media == nil || media! < 0 || media! > 2 {
-                    UIApplication.shared.visibleViewController?.view.makeToast("108:Invalid Contact Center media parameter (0:Chat, 1:Audio Call, 2:Video Call)".localized(), duration: 2)
+                    UIApplication.shared.visibleViewController?.view.makeToast("108:Invalid Contact Center media parameter (0:Chat, 1:Audio Call, 2:Video Call)".localized(), duration: 3)
                     return
                 }
             }
@@ -86,12 +86,12 @@ public class APIS: NSObject {
                 if category != 0 {
                     let service = CategoryCC.getDataFromServiceId(service_id: "\(category!)")
                     if service == nil {
-                        UIApplication.shared.visibleViewController?.view.makeToast("109:Invalid Contact Center category parameter".localized(), duration: 2)
+                        UIApplication.shared.visibleViewController?.view.makeToast("109:Invalid Contact Center category parameter".localized(), duration: 3)
                         return
                     }
                     let serviceChilds = CategoryCC.getDatafromParent(parent: service!.service_id)
                     if serviceChilds.count > 0 {
-                        UIApplication.shared.visibleViewController?.view.makeToast("109:Invalid Contact Center category parameter".localized(), duration: 2)
+                        UIApplication.shared.visibleViewController?.view.makeToast("109:Invalid Contact Center category parameter".localized(), duration: 3)
                         return
                     }
                 }
@@ -194,7 +194,7 @@ public class APIS: NSObject {
         }
         let user = User.getDataFromNameCanNil(name: name)
         if user == nil {
-            UIApplication.shared.visibleViewController?.view.makeToast("91:Invalid name or you must add Username to your contact first".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("91:Invalid name or you must add Username to your contact first".localized(), duration: 3)
             return
         }
         let editorPersonalVC = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "editorPersonalVC") as! EditorPersonal
@@ -284,12 +284,12 @@ public class APIS: NSObject {
     
     public static func startAudioCall(name: String) {
         if name.isEmpty {
-            UIApplication.shared.visibleViewController?.view.makeToast("92:Username is empty".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("92:Username is empty".localized(), duration: 3)
             return
         }
         let user = User.getDataFromNameCanNil(name: name)
         if user == nil {
-            UIApplication.shared.visibleViewController?.view.makeToast("91:Invalid name or you must add Username to your contact first".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("91:Invalid name or you must add Username to your contact first".localized(), duration: 3)
             return
         }
         if !CheckConnection.isConnectedToNetwork() {
@@ -329,12 +329,12 @@ public class APIS: NSObject {
     
     public static func startVideoCall(name: String) {
         if name.isEmpty {
-            UIApplication.shared.visibleViewController?.view.makeToast("92:Username is empty".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("92:Username is empty".localized(), duration: 3)
             return
         }
         let user = User.getDataFromNameCanNil(name: name)
         if user == nil {
-            UIApplication.shared.visibleViewController?.view.makeToast("91:Invalid name or you must add Username to your contact first".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("91:Invalid name or you must add Username to your contact first".localized(), duration: 3)
             return
         }
         if !CheckConnection.isConnectedToNetwork() {
@@ -530,12 +530,12 @@ public class APIS: NSObject {
     
     public static func startWhiteboard(name: String) {
         if name.isEmpty {
-            UIApplication.shared.visibleViewController?.view.makeToast("92:Username is empty".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("92:Username is empty".localized(), duration: 3)
             return
         }
         let user = User.getDataFromNameCanNil(name: name)
         if user == nil {
-            UIApplication.shared.visibleViewController?.view.makeToast("91:Invalid name or you must add Username to your contact first".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("91:Invalid name or you must add Username to your contact first".localized(), duration: 3)
             return
         }
         if !CheckConnection.isConnectedToNetwork() {
@@ -575,12 +575,12 @@ public class APIS: NSObject {
     
     public static func startScreenSharing(name: String) {
         if name.isEmpty {
-            UIApplication.shared.visibleViewController?.view.makeToast("92:Username is empty".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("92:Username is empty".localized(), duration: 3)
             return
         }
         let user = User.getDataFromNameCanNil(name: name)
         if user == nil {
-            UIApplication.shared.visibleViewController?.view.makeToast("91:Invalid name or you must add Username to your contact first".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("91:Invalid name or you must add Username to your contact first".localized(), duration: 3)
             return
         }
         if !CheckConnection.isConnectedToNetwork() {
@@ -621,7 +621,7 @@ public class APIS: NSObject {
     
     public static func signInAdmin(password: String) {
         if password.isEmpty {
-            UIApplication.shared.visibleViewController?.view.makeToast("113:Password is empty".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("113:Password is empty".localized(), duration: 3)
             return
         }
         let isChangeProfile = Utils.getSetProfile()
@@ -631,7 +631,7 @@ public class APIS: NSObject {
         }
         let isAdmin = User.isAdmin()
         if isAdmin {
-            UIApplication.shared.visibleViewController?.view.makeToast("112:You already login or registered as Admin".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("112:You already login or registered as Admin".localized(), duration: 3)
             return
         }
         if !CheckConnection.isConnectedToNetwork()  || API.nGetCLXConnState() == 0 {
@@ -703,7 +703,7 @@ public class APIS: NSObject {
         }
         let isAdmin = User.isAdmin()
         if !isAdmin {
-            UIApplication.shared.visibleViewController?.view.makeToast("111:You must Sign In as Admin to use this feature".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("111:You must Sign In as Admin to use this feature".localized(), duration: 3)
             return
         }
         let controller = SetInternalCSAccount()
@@ -752,15 +752,15 @@ public class APIS: NSObject {
         }
         let finalUname = uname.replacingOccurrences(of: "[\\n\\r\\t~%()\"]", with: "", options: .regularExpression)
         if finalUname == User.getData(pin: User.getMyPin())?.fullName {
-            UIApplication.shared.visibleViewController?.view.makeToast("102:Duplicate username".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("102:Duplicate username".localized(), duration: 3)
             return
         }
         if finalUname.count == 0 {
-            UIApplication.shared.visibleViewController?.view.makeToast("103:Username is empty".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("103:Username is empty".localized(), duration: 3)
             return
         }
         if finalUname.count < 3 {
-            UIApplication.shared.visibleViewController?.view.makeToast("104:Username length is too short".localized(), duration: 2)
+            UIApplication.shared.visibleViewController?.view.makeToast("104:Username length is too short".localized(), duration: 3)
             return
         }
         let a = finalUname.split(separator: " ", maxSplits: 1)

+ 0 - 45
NexilisLite/NexilisLite/Source/Extension.swift

@@ -642,51 +642,6 @@ extension String {
 
 extension UIViewController {
     
-    public func showToast(message : String, font: UIFont = UIFont.systemFont(ofSize: 12, weight: .medium), controller: UIViewController) {
-        
-        let toastContainer = UIView(frame: CGRect())
-        toastContainer.backgroundColor = self.traitCollection.userInterfaceStyle == .dark ? .white.withAlphaComponent(0.6) : UIColor.mainColor.withAlphaComponent(0.6)
-        toastContainer.alpha = 0.0
-        toastContainer.layer.cornerRadius = 25;
-        toastContainer.clipsToBounds  =  true
-        
-        let toastLabel = UILabel(frame: CGRect())
-        toastLabel.textColor = self.traitCollection.userInterfaceStyle == .dark ? .blackDarkMode : UIColor.white
-        toastLabel.textAlignment = .center;
-        toastLabel.font = font
-        toastLabel.text = message
-        toastLabel.clipsToBounds  =  true
-        toastLabel.numberOfLines = 0
-        
-        toastContainer.addSubview(toastLabel)
-        controller.view.addSubview(toastContainer)
-        controller.view.bringSubviewToFront(toastContainer)
-        
-        toastLabel.translatesAutoresizingMaskIntoConstraints = false
-        toastContainer.translatesAutoresizingMaskIntoConstraints = false
-        
-        let a1 = NSLayoutConstraint(item: toastLabel, attribute: .leading, relatedBy: .equal, toItem: toastContainer, attribute: .leading, multiplier: 1, constant: 15)
-        let a2 = NSLayoutConstraint(item: toastLabel, attribute: .trailing, relatedBy: .equal, toItem: toastContainer, attribute: .trailing, multiplier: 1, constant: -15)
-        let a3 = NSLayoutConstraint(item: toastLabel, attribute: .bottom, relatedBy: .equal, toItem: toastContainer, attribute: .bottom, multiplier: 1, constant: -15)
-        let a4 = NSLayoutConstraint(item: toastLabel, attribute: .top, relatedBy: .equal, toItem: toastContainer, attribute: .top, multiplier: 1, constant: 15)
-        toastContainer.addConstraints([a1, a2, a3, a4])
-        
-        let c1 = NSLayoutConstraint(item: toastContainer, attribute: .leading, relatedBy: .equal, toItem: controller.view, attribute: .leading, multiplier: 1, constant: 65)
-        let c2 = NSLayoutConstraint(item: toastContainer, attribute: .trailing, relatedBy: .equal, toItem: controller.view, attribute: .trailing, multiplier: 1, constant: -65)
-        let c3 = NSLayoutConstraint(item: toastContainer, attribute: .bottom, relatedBy: .equal, toItem: controller.view, attribute: .bottom, multiplier: 1, constant: -75)
-        controller.view.addConstraints([c1, c2, c3])
-        
-        UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveEaseIn, animations: {
-            toastContainer.alpha = 1.0
-        }, completion: { _ in
-            UIView.animate(withDuration: 0.5, delay: 1.5, options: .curveEaseOut, animations: {
-                toastContainer.alpha = 0.0
-            }, completion: {_ in
-                toastContainer.removeFromSuperview()
-            })
-        })
-    }
-    
     public func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
         UIGraphicsBeginImageContextWithOptions(targetSize, false, 0.0);
         image.draw(in: CGRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height))

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

@@ -1525,6 +1525,9 @@ public class Nexilis: NSObject {
             return
         }
         let status = message.getBody(key : CoreMessage_TMessageKey.STATUS, default_value : "")
+        let latitude = message.getBody(key : CoreMessage_TMessageKey.LATITUDE, default_value : "")
+        let longitude = message.getBody(key : CoreMessage_TMessageKey.LONGITUDE, default_value : "")
+        let desc = message.getBody(key : CoreMessage_TMessageKey.DESCRIPTION, default_value : "")
         guard !status.isEmpty else {
             return
         }
@@ -1546,17 +1549,26 @@ public class Nexilis: NSObject {
                                 if status == "3" {
                                     _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE_STATUS", cvalues: [
                                         "status" : status,
+                                        "longitude" : longitude,
+                                        "latitude" : latitude,
+                                        "location" : desc,
                                         "time_delivered" : String(Date().currentTimeMillis()),
                                         "last_update" : String(Date().currentTimeMillis())], _where: "message_id = '\(t)' and f_pin = '\(l_pin)'")
                                 } else if status == "4" {
                                     _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE_STATUS", cvalues: [
                                         "status" : status,
                                         "time_read" : String(Date().currentTimeMillis()),
+                                        "longitude" : longitude,
+                                        "latitude" : latitude,
+                                        "location" : desc,
                                         "last_update" : String(Date().currentTimeMillis())], _where: "message_id = '\(t)' and f_pin = '\(l_pin)'")
                                 } else if status == "8" {
                                     _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE_STATUS", cvalues: [
                                         "status" : status,
                                         "time_ack" : String(Date().currentTimeMillis()),
+                                        "longitude" : longitude,
+                                        "latitude" : latitude,
+                                        "location" : desc,
                                         "last_update" : String(Date().currentTimeMillis())], _where: "message_id = '\(t)' and f_pin = '\(l_pin)'")
                                 } else {
                                     _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE_STATUS", cvalues: [
@@ -1575,20 +1587,32 @@ public class Nexilis: NSObject {
                                 _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE_STATUS", cvalues: [
                                     "status" : status,
                                     "time_delivered" : String(Date().currentTimeMillis()),
+                                    "longitude" : longitude,
+                                    "latitude" : latitude,
+                                    "location" : desc,
                                     "last_update" : String(Date().currentTimeMillis())], _where: "message_id = '\(message_id)' and f_pin = '\(l_pin)'")
                             } else if status == "4" {
                                 _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE_STATUS", cvalues: [
                                     "status" : status,
                                     "time_read" : String(Date().currentTimeMillis()),
+                                    "longitude" : longitude,
+                                    "latitude" : latitude,
+                                    "location" : desc,
                                     "last_update" : String(Date().currentTimeMillis())], _where: "message_id = '\(message_id)' and f_pin = '\(l_pin)'")
                             } else if status == "8" {
                                 _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE_STATUS", cvalues: [
                                     "status" : status,
                                     "time_ack" : String(Date().currentTimeMillis()),
+                                    "longitude" : longitude,
+                                    "latitude" : latitude,
+                                    "location" : desc,
                                     "last_update" : String(Date().currentTimeMillis())], _where: "message_id = '\(message_id)' and f_pin = '\(l_pin)'")
                             } else {
                                 _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE_STATUS", cvalues: [
                                     "status" : status,
+                                    "longitude" : longitude,
+                                    "latitude" : latitude,
+                                    "location" : desc,
                                     "last_update" : String(Date().currentTimeMillis())], _where: "message_id = '\(message_id)' and f_pin = '\(l_pin)'")
                             }
                         }

+ 1 - 1
NexilisLite/NexilisLite/Source/View/BNIView/BNIBookingWebView.swift

@@ -240,7 +240,7 @@ public class BNIBookingWebView: UIViewController, WKNavigationDelegate, UIScroll
                   let param1 = dict["param1"] as? String else {
                 return
             }
-            showToast(message: param1, controller: self.tabBarController!)
+            self.view.makeToast(param1, duration: 3)
         } else if message.name == "blockUser" {
             guard let dict = message.body as? [String: AnyObject],
                   let param1 = dict["param1"] as? String,

+ 24 - 0
NexilisLite/NexilisLite/Source/View/Call/QmeraAudioViewController.swift

@@ -76,6 +76,8 @@ class QmeraAudioViewController: UIViewController {
     
     private var isSpeaker: Bool = false
     
+    private var isMuted: Bool = false
+    
     var listRemoteViewFix: [UIImageView] = [
         UIImageView(),
         UIImageView(),
@@ -183,6 +185,20 @@ class QmeraAudioViewController: UIViewController {
         return button
     }()
     
+    let mic: UIButton = {
+        let button = UIButton()
+        button.setImage(UIImage(systemName: "mic")?.withTintColor(.mainColor, renderingMode: .alwaysOriginal), for: .normal)
+        button.setImage(UIImage(systemName: "mic.slash")?.withTintColor(.white, renderingMode: .alwaysOriginal), for: .selected)
+        button.imageView?.contentMode = .scaleAspectFit
+        button.setBackgroundColor(.white, for: .normal)
+        button.setBackgroundColor(.mainColor, for: .highlighted)
+        button.setBackgroundColor(.mainColor, for: .selected)
+        button.contentVerticalAlignment = .fill
+        button.contentHorizontalAlignment = .fill
+        button.imageEdgeInsets = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
+        return button
+    }()
+    
     let stack: UIStackView = {
         let stackView = UIStackView()
         stackView.axis = .horizontal
@@ -334,6 +350,7 @@ class QmeraAudioViewController: UIViewController {
         accept.circle()
         invite.circle()
         speaker.circle()
+        mic.circle()
     }
     
     private func getUserData(completion: @escaping (User?) -> ()) {
@@ -396,10 +413,12 @@ class QmeraAudioViewController: UIViewController {
         stack.addArrangedSubview(invite)
         stack.addArrangedSubview(end)
         stack.addArrangedSubview(speaker)
+        stack.addArrangedSubview(mic)
         
         invite.addTarget(self, action: #selector(didInvite(sender:)), for: .touchUpInside)
         end.addTarget(self, action: #selector(didPressEnd(sender:)), for: .touchUpInside)
         speaker.addTarget(self, action: #selector(didSpeaker(sender:)), for: .touchUpInside)
+        mic.addTarget(self, action: #selector(didMute(sender:)), for: .touchUpInside)
         
         if !ticketId.isEmpty {
             self.view.addSubview(self.stackViewToolbar2)
@@ -574,6 +593,11 @@ class QmeraAudioViewController: UIViewController {
         Nexilis.setSpeaker(isSpeaker)
     }
     
+    @objc func didMute(sender: Any?) {
+        isMuted = !isMuted
+        mic.isSelected = isMuted
+    }
+    
     @objc func didInvite(sender: Any?) {
         let controller = QmeraCallContactViewController()
         controller.isDismiss = { user in

+ 8 - 8
NexilisLite/NexilisLite/Source/View/Chat/ChatGPTBotView.swift

@@ -297,7 +297,7 @@ class ChatGPTBotView: UIViewController, UIGestureRecognizerDelegate {
         if viewController is ChatGPTBotView {
             if ((textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) == "Send message".localized() && textFieldSend.textColor == UIColor.lightGray && attachment_flag != "11") || textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ) {
                 dismissKeyboard()
-                viewController.showToast(message: "Write Messages".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                viewController.view.makeToast("Write Messages".localized(), duration: 3)
                 if (textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) != "Send message".localized()) {
                     textFieldSend.text = ""
                 }
@@ -1555,7 +1555,7 @@ extension ChatGPTBotView: UIContextMenuInteractionDelegate {
             text = text + "\n\n\nchat " + "Powered by Nexilis".localized()
             DispatchQueue.main.async {
                 UIPasteboard.general.string = text
-                self.showToast(message: "Text coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
             }
             cancelAction()
         } else if deleteSession {
@@ -1639,14 +1639,14 @@ extension ChatGPTBotView: UIContextMenuInteractionDelegate {
                                         if FileManager.default.fileExists(atPath: imageURL.path) {
                                             let image    = UIImage(contentsOfFile: imageURL.path)
                                             UIPasteboard.general.image = image
-                                            self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                            self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                         }
                                         else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
                                             do {
                                                 if let imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
                                                     let image = UIImage(data: imageData)
                                                     UIPasteboard.general.image = image
-                                                    self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                                    self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                                 }
                                             } catch {
                                                 
@@ -1660,7 +1660,7 @@ extension ChatGPTBotView: UIContextMenuInteractionDelegate {
                         if (index == 0) {
                             DispatchQueue.main.async {
                                 UIPasteboard.general.string = dataMessages[indexPath.row]["message_text"] as? String
-                                self.showToast(message: "Text coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
                             }
                         } else {
                             DispatchQueue.main.async {
@@ -1673,14 +1673,14 @@ extension ChatGPTBotView: UIContextMenuInteractionDelegate {
                                         if FileManager.default.fileExists(atPath: imageURL.path) {
                                             let image    = UIImage(contentsOfFile: imageURL.path)
                                             UIPasteboard.general.image = image
-                                            self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                            self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                         }
                                         else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
                                             do {
                                                 if let imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
                                                     let image = UIImage(data: imageData)
                                                     UIPasteboard.general.image = image
-                                                    self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                                    self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                                 }
                                             } catch {
                                                 
@@ -2180,7 +2180,7 @@ extension ChatGPTBotView: UITableViewDelegate, UITableViewDataSource {
                         if row != nil && section != nil {
                             self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
                         }
-                        self.showToast(message: "Confirmation Success.".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                        self.view.makeToast("Confirmation Success.".localized(), duration: 3)
                     }
                 }
             }

+ 37 - 16
NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift

@@ -14,7 +14,7 @@ import NotificationBannerSwift
 import nuSDKService
 import SwiftLinkPreview
 
-public class EditorGroup: UIViewController {
+public class EditorGroup: UIViewController, CLLocationManagerDelegate {
     @IBOutlet var viewButton: UIView!
     @IBOutlet var constraintViewTextField: NSLayoutConstraint!
     @IBOutlet var buttonVoice: UIButton!
@@ -105,6 +105,9 @@ public class EditorGroup: UIViewController {
     var constraintBottomeditTextView: NSLayoutConstraint!
     var constraintHeighteditTextView: NSLayoutConstraint!
     var constraintBottomSendEditTV: NSLayoutConstraint!
+    let locationManager = CLLocationManager()
+    var longitude = ""
+    var latitude = ""
     
     public override func viewDidDisappear(_ animated: Bool) {
         if self.isMovingFromParent {
@@ -216,6 +219,17 @@ public class EditorGroup: UIViewController {
         center.addObserver(self, selector: #selector(onMemberTopic(notification:)), name: NSNotification.Name(rawValue: "onTopic"), object: nil)
         center.addObserver(self, selector: #selector(onFailedSendMessage(notification:)), name: NSNotification.Name(rawValue: Nexilis.failedSendMessage), object: nil)
         
+        locationManager.delegate = self
+        locationManager.requestWhenInUseAuthorization()
+        
+        // Check if location services are enabled
+        if CLLocationManager.locationServicesEnabled() {
+            locationManager.desiredAccuracy = kCLLocationAccuracyBest
+            locationManager.startUpdatingLocation()
+        } else {
+            print("Location services are not enabled.")
+        }
+        
         if dataMessageForward != nil {
             for i in 0..<dataMessageForward!.count {
                 sendChat(message_scope_id: "4", status: "2", message_text: dataMessageForward![i]["message_text"] as! String, credential: "0", attachment_flag: dataMessageForward![i]["attachment_flag"] as! String, ex_blog_id: "", message_large_text: "", ex_format: "", image_id: dataMessageForward![i]["image_id"] as! String, audio_id: dataMessageForward![i]["audio_id"] as! String, video_id: dataMessageForward![i]["video_id"] as! String, file_id: dataMessageForward![i]["file_id"] as! String, thumb_id: dataMessageForward![i]["thumb_id"] as! String, reff_id: "", read_receipts: "", is_call_center: "0", call_center_id: "", viewController: self)
@@ -228,6 +242,13 @@ public class EditorGroup: UIViewController {
         tableMention.contentInset = UIEdgeInsets(top: -25, left: 0, bottom: 0, right: 0)
     }
     
+    public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
+        guard let location = locations.last else { return }
+        latitude = "\(location.coordinate.latitude)"
+        longitude = "\(location.coordinate.longitude)"
+        locationManager.stopUpdatingLocation()
+    }
+    
     public func afterUnfriend() {
         DispatchQueue.main.async {
             SecureUserDefaults.shared.removeValue(forKey: "inEditorGroup")
@@ -1645,7 +1666,7 @@ public class EditorGroup: UIViewController {
         if viewController is EditorGroup && file_id == "" && dataMessageForward == nil {
             if ((textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) == "Send message".localized() && textFieldSend.textColor == UIColor.lightGray && attachment_flag != "11") || textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ) {
                 dismissKeyboard()
-                viewController.showToast(message: "Write Messages".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                viewController.view.makeToast("Write Messages".localized(), duration: 3)
                 if (textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) != "Send message".localized()) {
                     textFieldSend.text = ""
                 }
@@ -2821,7 +2842,7 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
             self.showEditMessageView(at: indexPath!)
         })
         let translate = UIAction(title: "Translate".localized(), image: UIImage(systemName: "t.bubble"), handler: {(_) in
-            self.showToast(message: "Translating...".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+            self.view.makeToast("Translating...".localized(), duration: 3)
             var translation: String = "English"
             let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
             if lang == "id" {
@@ -2841,7 +2862,7 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
                     let response = response as? HTTPURLResponse
                     if response?.statusCode != 200 || error != nil {
                         DispatchQueue.main.async {
-                            self.showToast(message: "There is an error occurred while translating your message. Please try again or check your network connection.".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                            self.view.makeToast("There is an error occurred while translating your message. Please try again or check your network connection.".localized(), duration: 3)
                         }
                         return
                     }
@@ -2861,7 +2882,7 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
             }
         })
         let gcs = UIAction(title: "Get Chat Suggestion".localized(), image: UIImage(systemName: "exclamationmark.bubble"), handler: {(_) in
-            self.showToast(message: "Getting chat suggestion...".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+            self.view.makeToast("Getting chat suggestion...".localized(), duration: 3)
             let payload: [String : Any] = [
                 "role": "user",
                 "content": dataMessages[indexPath!.row][TypeDataMessage.message_text]!!
@@ -2876,7 +2897,7 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
                     let response = response as? HTTPURLResponse
                     if response?.statusCode != 200 || error != nil {
                         DispatchQueue.main.async {
-                            self.showToast(message: "There is an error occurred while translating your message. Please try again or check your network connection.".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                            self.view.makeToast("There is an error occurred while getting chat suggestion for you. Please try again or check your network connection.".localized(), duration: 3)
                         }
                         return
                     }
@@ -3102,7 +3123,7 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
              ForAction:{() -> Void in
                 let newText = self.editTextView.text ?? ""
                 if newText.trimmingCharacters(in: .whitespacesAndNewlines) != oldText {
-                    let lastEdited = 1 + ((dataMessages[indexPath.row][TypeDataMessage.last_edit] as? Int64) ?? 0)
+                    let lastEdited = Int64(Date().currentTimeMillis())
                     let message = CoreMessage_TMessageBank.editMessage(message_id: dataMessages[indexPath.row][TypeDataMessage.message_id] as! String, l_pin: dataMessages[indexPath.row][TypeDataMessage.l_pin] as! String, message_scope_id: dataMessages[indexPath.row][TypeDataMessage.message_scope_id] as! String, status: "1", message_text: newText, credential: dataMessages[indexPath.row][TypeDataMessage.credential] as! String, attachment_flag: dataMessages[indexPath.row][TypeDataMessage.attachment_flag] as! String, ex_blog_id: dataMessages[indexPath.row][TypeDataMessage.blog_id] as! String, message_large_text: "", ex_format: "", image_id: dataMessages[indexPath.row][TypeDataMessage.image_id] as! String, audio_id: dataMessages[indexPath.row][TypeDataMessage.audio_id] as! String, video_id: dataMessages[indexPath.row][TypeDataMessage.video_id] as! String, file_id: dataMessages[indexPath.row][TypeDataMessage.file_id] as! String, thumb_id: dataMessages[indexPath.row][TypeDataMessage.thumb_id] as! String, reff_id: dataMessages[indexPath.row][TypeDataMessage.reff_id] as! String, read_receipts: dataMessages[indexPath.row][TypeDataMessage.read_receipts] as! String, chat_id: dataMessages[indexPath.row][TypeDataMessage.chat_id] as! String, is_call_center: dataMessages[indexPath.row][TypeDataMessage.is_call_center] as! String, call_center_id: dataMessages[indexPath.row][TypeDataMessage.call_center_id] as! String, opposite_pin: dataMessages[indexPath.row][TypeDataMessage.opposite_pin] as! String, last_edit: lastEdited)
                     Nexilis.addQueueMessage(message: message, isEditMessage: true)
                     DispatchQueue.global().async {
@@ -3388,7 +3409,7 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
             text = text + "\n\n\nchat " + "Powered by Nexilis".localized()
             DispatchQueue.main.async {
                 UIPasteboard.general.string = text
-                self.showToast(message: "Text coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
             }
             cancelAction()
         } else if forwardSession {
@@ -3514,14 +3535,14 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
                                     if FileManager.default.fileExists(atPath: imageURL.path) {
                                         let image    = UIImage(contentsOfFile: imageURL.path)
                                         UIPasteboard.general.image = image
-                                        self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                        self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                     }
                                     else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
                                         do {
                                             if let imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
                                                 let image = UIImage(data: imageData)
                                                 UIPasteboard.general.image = image
-                                                self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                                self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                             }
                                         } catch {
                                             
@@ -3534,7 +3555,7 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
                         if (index == 0) {
                             DispatchQueue.main.async {
                                 UIPasteboard.general.string = dataMessages[indexPath.row]["message_text"] as? String
-                                self.showToast(message: "Text coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
                             }
                         } else {
                             DispatchQueue.main.async {
@@ -3546,13 +3567,13 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
                                     if FileManager.default.fileExists(atPath: imageURL.path) {
                                         let image    = UIImage(contentsOfFile: imageURL.path)
                                         UIPasteboard.general.image = image
-                                        self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                        self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                     } else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
                                         do {
                                             if let imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
                                                 let image = UIImage(data: imageData)
                                                 UIPasteboard.general.image = image
-                                                self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                                self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                             }
                                         } catch {
                                             
@@ -5341,7 +5362,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
             if (self.dataTopic["chat_id"] as! String != "") {
                 opposite_pin = self.dataTopic["chat_id"] as! String
             }
-            let result = Nexilis.write(message: CoreMessage_TMessageBank.getAckLocationMessage(f_pin: dataMessages[indexPath.row]["f_pin"] as! String, message_id: dataMessages[indexPath.row]["message_id"] as! String, l_pin: opposite_pin, server_date: "\(Date().currentTimeMillis())", message_scope_id: dataMessages[indexPath.row]["message_scope_id"] as! String, longitude: "", latitude: "", description: ""))
+            let result = Nexilis.write(message: CoreMessage_TMessageBank.getAckLocationMessage(f_pin: dataMessages[indexPath.row]["f_pin"] as! String, message_id: dataMessages[indexPath.row]["message_id"] as! String, l_pin: opposite_pin, server_date: "\(Date().currentTimeMillis())", message_scope_id: dataMessages[indexPath.row]["message_scope_id"] as! String, longitude: self.longitude, latitude: self.latitude, description: ""))
             if result != nil {
                 Database.shared.database?.inTransaction({ (fmdb, rollback) in
                     do {
@@ -5361,7 +5382,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
                         if row != nil && section != nil {
                             self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
                         }
-                        self.showToast(message: "Confirmation Success.".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                        self.view.makeToast("Confirmation Success.".localized(), duration: 3)
                     }
                 }
             }
@@ -5777,7 +5798,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
                         label.attributedText = highlightedText(for: text, in: range, label: label)
                         timerCheckLink = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: {_ in
                             UIPasteboard.general.string = word
-                            self.showToast(message: "Link Copied".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                            self.view.makeToast("Link Copied".localized(), duration: 3)
                             label.attributedText = self.removeHighlightedText(for: text, in: range, label: label)
                         })
                     }

+ 50 - 26
NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift

@@ -14,7 +14,7 @@ import Photos
 import nuSDKService
 import SwiftLinkPreview
 
-public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestureRecognizerDelegate {
+public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestureRecognizerDelegate, CLLocationManagerDelegate {
     @IBOutlet var viewButton: UIView!
     @IBOutlet var constraintViewTextField: NSLayoutConstraint!
     @IBOutlet var buttonVoice: UIButton!
@@ -115,6 +115,9 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
     var constraintBottomeditTextView: NSLayoutConstraint!
     var constraintHeighteditTextView: NSLayoutConstraint!
     var constraintBottomSendEditTV: NSLayoutConstraint!
+    let locationManager = CLLocationManager()
+    var longitude = ""
+    var latitude = ""
     
     public override func viewDidDisappear(_ animated: Bool) {
         if self.isMovingFromParent {
@@ -234,6 +237,17 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         center.addObserver(self, selector: #selector(onTyping(notification:)), name: NSNotification.Name(rawValue: Nexilis.listenerTypingChat), object: nil)
         center.addObserver(self, selector: #selector(onFailedSendMessage(notification:)), name: NSNotification.Name(rawValue: Nexilis.failedSendMessage), object: nil)
         
+        locationManager.delegate = self
+        locationManager.requestWhenInUseAuthorization()
+        
+        // Check if location services are enabled
+        if CLLocationManager.locationServicesEnabled() {
+            locationManager.desiredAccuracy = kCLLocationAccuracyBest
+            locationManager.startUpdatingLocation()
+        } else {
+            print("Location services are not enabled.")
+        }
+        
         if dataMessageForward != nil {
             for i in 0..<dataMessageForward!.count {
                 sendChat(message_scope_id: "3", status: "2", message_text: dataMessageForward![i]["message_text"] as! String, credential: "0", attachment_flag: dataMessageForward![i]["attachment_flag"] as! String, ex_blog_id: "", message_large_text: "", ex_format: "", image_id: dataMessageForward![i]["image_id"] as! String, audio_id: dataMessageForward![i]["audio_id"] as! String, video_id: dataMessageForward![i]["video_id"] as! String, file_id: dataMessageForward![i]["file_id"] as! String, thumb_id: dataMessageForward![i]["thumb_id"] as! String, reff_id: "", read_receipts: dataMessageForward![i]["read_receipts"] as! String, chat_id: "", is_call_center: "0", call_center_id: "", viewController: self)
@@ -290,6 +304,13 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
 //        }
     }
     
+    public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
+        guard let location = locations.last else { return }
+        latitude = "\(location.coordinate.latitude)"
+        longitude = "\(location.coordinate.longitude)"
+        locationManager.stopUpdatingLocation()
+    }
+    
     public func afterUnfriend() {
         DispatchQueue.main.async {
             for timer in self.timerCredential.values {
@@ -1610,7 +1631,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                             self.timeoutCC.invalidate()
                         } else {
                             if !self.showToast30s {
-                                self.showToast(message: "Please reply within 30 seconds so the call center session doesn't end.".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                self.view.makeToast("Please reply within 30 seconds so the call center session doesn't end.".localized(), duration: 3)
                                 sendTyping(l_pin: fPinContacCenter, isTyping: true)
                                 self.showToast30s = true
                             }
@@ -2175,7 +2196,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
     @objc func audioVideoCall(sender: UIBarButtonItem) {
         if sender.tag == 0 {
             if !Nexilis.checkingAccess(key: "audio_call") {
-                showToast(message: "Feature disabled..".localized(), font: UIFont.systemFont(ofSize: 12), controller: self)
+                self.view.makeToast("Feature disabled..".localized(), duration: 3)
                 return
             }
             let goAudioCall = Nexilis.checkMicPermission()
@@ -2205,7 +2226,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
             }
         } else {
             if !Nexilis.checkingAccess(key: "video_call") {
-                showToast(message: "Feature disabled..".localized(), font: UIFont.systemFont(ofSize: 12), controller: self)
+                self.view.makeToast("Feature disabled..".localized(), duration: 3)
                 return
             }
             let goAudioCall = Nexilis.checkMicPermission()
@@ -2377,7 +2398,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         if viewController is EditorPersonal && file_id == "" && dataMessageForward == nil && !isAutoSendCC{
             if ((textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) == "Send message".localized() && textFieldSend.textColor == UIColor.lightGray && attachment_flag != "11") || textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ) {
                 dismissKeyboard()
-                viewController.showToast(message: "Write Messages".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                viewController.view.makeToast("Write Messages".localized(), duration: 3)
                 if (textFieldSend.text!.trimmingCharacters(in: .whitespacesAndNewlines) != "Send message".localized()) {
                     textFieldSend.text = ""
                 }
@@ -2406,7 +2427,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                     textFieldSend.text = ""
                 }
                 dismissKeyboard()
-                viewController.showToast(message: "Unable to send message. Waiting for the officer to accept your request".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                self.view.makeToast("Unable to send message. Waiting for the officer to accept your request".localized(), duration: 3)
                 return
             }
             is_call_center = "1"
@@ -2554,7 +2575,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
     @objc func ccAction(sender: UIButton) {
         if self.nowSelectedCategoryCC == "CantReturn" {
             if sender.tag == 503 {
-                self.showToast(message: "You can't request Call Center more than one".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                self.view.makeToast("You can't request Call Center more than one".localized(), duration: 3)
             } else if sender.tag == 504 {
                 busyCCAction(sender: sender)
             }
@@ -2572,7 +2593,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         var row: [String: Any?] = [:]
         if nowSelectedCategoryCC.isEmpty || level > levelNow {
             if Utils.getDefaultCC() == "No" && !showToastTwiceClick {
-                self.showToast(message: "You can press your choice again to change category".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                self.view.makeToast("You can press your choice again to change category".localized(), duration: 3)
                 showToastTwiceClick = true
             }
             row["message_id"] = ""
@@ -2791,7 +2812,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                     let email = response.getBody(key: CoreMessage_TMessageKey.EMAIL, default_value: "")
                     let officer = response.getBody(key: CoreMessage_TMessageKey.L_PIN, default_value: "")
                     if email.isEmpty {
-                        self.showToast(message: "Invalid Email Address".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                        self.view.makeToast("Invalid Email Address".localized(), duration: 3)
                         return
                     }
                     // TODO: check if mail available
@@ -3809,7 +3830,7 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
             self.showEditMessageView(at: indexPath!)
         })
         let translate = UIAction(title: "Translate".localized(), image: UIImage(systemName: "t.bubble"), handler: {(_) in
-            self.showToast(message: "Translating...".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+            self.view.makeToast("Translating...".localized(), duration: 3)
             var translation: String = "English"
             let lang: String = SecureUserDefaults.shared.value(forKey: "i18n_language") ?? "en"
             if lang == "id" {
@@ -3829,7 +3850,7 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
                     let response = response as? HTTPURLResponse
                     if response?.statusCode != 200 || error != nil {
                         DispatchQueue.main.async {
-                            self.showToast(message: "There is an error occurred while translating your message. Please try again or check your network connection.".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                            self.view.makeToast("There is an error occurred while translating your message. Please try again or check your network connection.".localized(), duration: 3)
                         }
                         return
                     }
@@ -3849,7 +3870,7 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
             }
         })
         let gcs = UIAction(title: "Get Chat Suggestion".localized(), image: UIImage(systemName: "exclamationmark.bubble"), handler: {(_) in
-            self.showToast(message: "Getting chat suggestion...".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+            self.view.makeToast("Getting chat suggestion...".localized(), duration: 3)
             let payload: [String : Any] = [
                 "role": "user",
                 "content": dataMessages[indexPath!.row][TypeDataMessage.message_text]!!
@@ -3864,7 +3885,7 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
                     let response = response as? HTTPURLResponse
                     if response?.statusCode != 200 || error != nil {
                         DispatchQueue.main.async {
-                            self.showToast(message: "There is an error occurred while translating your message. Please try again or check your network connection.".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                            self.view.makeToast("There is an error occurred while getting chat suggestion for you. Please try again or check your network connection.".localized(), duration: 3)
                         }
                         return
                     }
@@ -4095,7 +4116,7 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
              ForAction:{() -> Void in
                 let newText = self.editTextView.text ?? ""
                 if newText.trimmingCharacters(in: .whitespacesAndNewlines) != oldText {
-                    let lastEdited = 1 + ((dataMessages[indexPath.row][TypeDataMessage.last_edit] as? Int64) ?? 0)
+                    let lastEdited = Int64(Date().currentTimeMillis())
                     let message = CoreMessage_TMessageBank.editMessage(message_id: dataMessages[indexPath.row][TypeDataMessage.message_id] as! String, l_pin: dataMessages[indexPath.row][TypeDataMessage.l_pin] as! String, message_scope_id: dataMessages[indexPath.row][TypeDataMessage.message_scope_id] as! String, status: "1", message_text: newText, credential: dataMessages[indexPath.row][TypeDataMessage.credential] as! String, attachment_flag: dataMessages[indexPath.row][TypeDataMessage.attachment_flag] as! String, ex_blog_id: dataMessages[indexPath.row][TypeDataMessage.blog_id] as! String, message_large_text: "", ex_format: "", image_id: dataMessages[indexPath.row][TypeDataMessage.image_id] as! String, audio_id: dataMessages[indexPath.row][TypeDataMessage.audio_id] as! String, video_id: dataMessages[indexPath.row][TypeDataMessage.video_id] as! String, file_id: dataMessages[indexPath.row][TypeDataMessage.file_id] as! String, thumb_id: dataMessages[indexPath.row][TypeDataMessage.thumb_id] as! String, reff_id: dataMessages[indexPath.row][TypeDataMessage.reff_id] as! String, read_receipts: dataMessages[indexPath.row][TypeDataMessage.read_receipts] as! String, chat_id: dataMessages[indexPath.row][TypeDataMessage.chat_id] as! String, is_call_center: dataMessages[indexPath.row][TypeDataMessage.is_call_center] as! String, call_center_id: dataMessages[indexPath.row][TypeDataMessage.call_center_id] as! String, opposite_pin: dataMessages[indexPath.row][TypeDataMessage.opposite_pin] as! String, last_edit: lastEdited)
                     Nexilis.addQueueMessage(message: message, isEditMessage: true)
                     DispatchQueue.global().async {
@@ -4393,7 +4414,7 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
             text = text + "\n\n\nchat " + "Powered by Nexilis".localized()
             DispatchQueue.main.async {
                 UIPasteboard.general.string = text
-                self.showToast(message: "Text coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
             }
             cancelAction()
         } else if forwardSession {
@@ -4538,13 +4559,13 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
                                     if FileManager.default.fileExists(atPath: imageURL.path) {
                                         let image    = UIImage(contentsOfFile: imageURL.path)
                                         UIPasteboard.general.image = image
-                                        self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                        self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                     } else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
                                         do {
                                             if let imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
                                                 let image = UIImage(data: imageData)
                                                 UIPasteboard.general.image = image
-                                                self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                                self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                             }
                                         } catch {
                                             
@@ -4557,7 +4578,7 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
                         if (index == 0) {
                             DispatchQueue.main.async {
                                 UIPasteboard.general.string = dataMessages[indexPath.row]["message_text"] as? String
-                                self.showToast(message: "Text coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
                             }
                         } else {
                             DispatchQueue.main.async {
@@ -4569,13 +4590,13 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
                                     if FileManager.default.fileExists(atPath: imageURL.path) {
                                         let image    = UIImage(contentsOfFile: imageURL.path)
                                         UIPasteboard.general.image = image
-                                        self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                        self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                     } else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
                                         do {
                                             if let imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
                                                 let image = UIImage(data: imageData)
                                                 UIPasteboard.general.image = image
-                                                self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                                self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                             }
                                         } catch {
                                             
@@ -5632,7 +5653,10 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
             }
         }
         
-        let stringDate = (dataMessages[indexPath.row]["server_date"] as? String) ?? ""
+        var stringDate = (dataMessages[indexPath.row]["server_date"] as? String) ?? ""
+        if dataMessages[indexPath.row][TypeDataMessage.last_edit] != nil && dataMessages[indexPath.row][TypeDataMessage.last_edit] as! Int64 != 0 {
+            stringDate = "\(dataMessages[indexPath.row][TypeDataMessage.last_edit] as! Int64)"
+        }
         if !stringDate.isEmpty {
             if (dataMessages[indexPath.row]["credential"] as? String) == "1" && dataMessages[indexPath.row]["lock"] as? String != "2" {
                 if dataTimer! >= 10 {
@@ -6476,11 +6500,11 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
     
     @objc func tapAck(_ sender: ObjectGesture) {
         if blocking == "1" {
-            self.view.makeToast("You blocked this user".localized())
+            self.view.makeToast("You blocked this user".localized(), duration: 3)
             return
         }
         if blocking == "-1" {
-            self.view.makeToast("You have been blocked by this user".localized())
+            self.view.makeToast("You have been blocked by this user".localized(), duration: 3)
             return
         }
         let indexPath = sender.indexPath
@@ -6496,7 +6520,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
             return
         }
         DispatchQueue.global().async {
-            let result = Nexilis.write(message: CoreMessage_TMessageBank.getAckLocationMessage(f_pin: dataMessages[indexPath.row]["f_pin"] as! String, message_id: dataMessages[indexPath.row]["message_id"] as! String, l_pin: dataMessages[indexPath.row]["l_pin"] as! String, server_date: "\(Date().currentTimeMillis())", message_scope_id: dataMessages[indexPath.row]["message_scope_id"] as! String, longitude: "", latitude: "", description: ""))
+            let result = Nexilis.write(message: CoreMessage_TMessageBank.getAckLocationMessage(f_pin: dataMessages[indexPath.row]["f_pin"] as! String, message_id: dataMessages[indexPath.row]["message_id"] as! String, l_pin: dataMessages[indexPath.row]["l_pin"] as! String, server_date: "\(Date().currentTimeMillis())", message_scope_id: dataMessages[indexPath.row]["message_scope_id"] as! String, longitude: self.latitude, latitude: self.longitude, description: ""))
             if result != nil {
                 Database.shared.database?.inTransaction({ (fmdb, rollback) in
                     do {
@@ -6516,7 +6540,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
                         if row != nil && section != nil {
                             self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
                         }
-                        self.showToast(message: "Confirmation Success.".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                        self.view.makeToast("Confirmation Success.".localized(), duration: 3)
                     }
                 }
             }
@@ -6990,7 +7014,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
                     label.attributedText = highlightedText(for: text, in: range, label: label)
                     timerCheckLink = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: {_ in
                         UIPasteboard.general.string = word
-                        self.showToast(message: "Link Copied".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                        self.view.makeToast("Link Copied".localized(), duration: 3)
                         label.attributedText = self.removeHighlightedText(for: text, in: range, label: label)
                     })
                 }

+ 7 - 7
NexilisLite/NexilisLite/Source/View/Chat/EditorStarMessages.swift

@@ -908,7 +908,7 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
                     label.attributedText = highlightedText(for: text, in: range, label: label)
                     timerCheckLink = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: {_ in
                         UIPasteboard.general.string = word
-                        self.showToast(message: "Link Copied".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                        self.view.makeToast("Link Copied".localized(), duration: 3)
                         label.attributedText = self.removeHighlightedText(for: text, in: range, label: label)
                     })
                 }
@@ -1517,7 +1517,7 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
                     text = text + "\n\n\nchat " + "Powered by Nexilis".localized()
                     DispatchQueue.main.async {
                         UIPasteboard.general.string = text
-                        self.showToast(message: "Text coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                        self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
                     }
                 }
             } else {
@@ -1530,13 +1530,13 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
                         if FileManager.default.fileExists(atPath: imageURL.path) {
                             let image    = UIImage(contentsOfFile: imageURL.path)
                             UIPasteboard.general.image = image
-                            self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                            self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                         } else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
                             do {
                                 if let imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
                                     let image    = UIImage(data: imageData)
                                     UIPasteboard.general.image = image
-                                    self.showToast(message: "Image coppied to clipboard".localized(), seconds: 1)
+                                    self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                 }
                             } catch {
                                 
@@ -1575,7 +1575,7 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
                     handler: {(_) in if (index == 0) {
                         DispatchQueue.main.async {
                             UIPasteboard.general.string = dataMessages[indexPath.row]["message_text"] as? String
-                            self.showToast(message: "Text coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                            self.view.makeToast("Text coppied to clipboard".localized(), duration: 3)
                         }
                     } else {
                         DispatchQueue.main.async {
@@ -1587,13 +1587,13 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
                                 if FileManager.default.fileExists(atPath: imageURL.path) {
                                     let image    = UIImage(contentsOfFile: imageURL.path)
                                     UIPasteboard.general.image = image
-                                    self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                    self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                 } else if FileEncryption.shared.isSecureExists(filename: imageURL.lastPathComponent) {
                                     do {
                                         if let imageData = try FileEncryption.shared.readSecure(filename: imageURL.lastPathComponent) {
                                             let image    = UIImage(data: imageData)
                                             UIPasteboard.general.image = image
-                                            self.showToast(message: "Image coppied to clipboard".localized(), font: UIFont.systemFont(ofSize: 12, weight: .medium), controller: self)
+                                            self.view.makeToast("Image coppied to clipboard".localized(), duration: 3)
                                         }
                                     } catch {
                                         

+ 47 - 2
NexilisLite/NexilisLite/Source/View/Chat/MessageInfo.swift

@@ -6,15 +6,19 @@
 //
 
 import UIKit
+import CoreLocation
 
-class MessageInfo: UIViewController, UITableViewDelegate, UITableViewDataSource {
+class MessageInfo: UIViewController, UITableViewDelegate, UITableViewDataSource, CLLocationManagerDelegate {
     var data: [String: Any?] = [:]
     var dataStatus: [[String: Any?]] = []
+    var dataLocation: [String] = []
     private var tableStatus: UITableView!
     var dateMessage = ""
     var dataPerson: [String: String?] = [:]
     var dataGroup: [String: Any?] = [:]
     var isPersonal = true
+    var finishGetLocation = false
+    let locationManager = CLLocationManager()
 
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -34,6 +38,13 @@ class MessageInfo: UIViewController, UITableViewDelegate, UITableViewDataSource
         getData()
         dateMessage = chatDate(stringDate: data["server_date"] as! String)
         
+        locationManager.delegate = self
+        locationManager.requestWhenInUseAuthorization()
+        if CLLocationManager.locationServicesEnabled() {
+            locationManager.desiredAccuracy = kCLLocationAccuracyBest
+            locationManager.startUpdatingLocation()
+        }
+        
         tableStatus.reloadData()
         
         view.addSubview(tableStatus)
@@ -52,7 +63,7 @@ class MessageInfo: UIViewController, UITableViewDelegate, UITableViewDataSource
     private func getData() {
         Database.shared.database?.inTransaction({ (fmdb, rollback) in
             do {
-                if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: "SELECT f_pin, status, time_delivered, time_read, time_ack FROM MESSAGE_STATUS where message_id='\(data["message_id"]!!)'") {
+                if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: "SELECT f_pin, status, time_delivered, time_read, time_ack, longitude, latitude FROM MESSAGE_STATUS where message_id='\(data["message_id"]!!)'") {
                     var listStatus: [Int] = []
                     while cursorData.next() {
                         var row: [String: Any?] = [:]
@@ -61,6 +72,8 @@ class MessageInfo: UIViewController, UITableViewDelegate, UITableViewDataSource
                         row["time_delivered"] = cursorData.string(forColumnIndex: 2) ?? ""
                         row["time_read"] = cursorData.string(forColumnIndex: 3) ?? ""
                         row["time_ack"] = cursorData.string(forColumnIndex: 4) ?? ""
+                        row["longitude"] = cursorData.string(forColumnIndex: 5) ?? ""
+                        row["latitude"] = cursorData.string(forColumnIndex: 6) ?? ""
                         dataStatus.append(row)
                         listStatus.append(Int(row["status"] as! String)!)
                     }
@@ -74,6 +87,38 @@ class MessageInfo: UIViewController, UITableViewDelegate, UITableViewDataSource
         })
     }
     
+    func getAllLocationDesc() {
+        for data in dataStatus {
+            let latitude = CLLocationDegrees(data["latitude"] as? String ?? "")!
+            let longitude = CLLocationDegrees(data["longitude"] as? String ?? "")!
+            let location = CLLocation(latitude: latitude, longitude: longitude)
+            let geocoder = CLGeocoder()
+            print("SEKUTT \(location.coordinate.latitude), \(location.coordinate.longitude)")
+            geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
+                var result = ""
+                if let error = error {
+                    print("Error fetching address: \(error.localizedDescription)")
+                } else if let placemark = placemarks?.first {
+                    if let locality = placemark.locality {
+                        result += locality + ", "
+                    }
+                    if let administrativeArea = placemark.administrativeArea {
+                        result += administrativeArea + ", "
+                    }
+                    if let country = placemark.country {
+                        result += country
+                    }
+                    print("MANTAB \(result)")
+                }
+            }
+        }
+    }
+    
+    public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
+        self.getAllLocationDesc()
+        locationManager.stopUpdatingLocation()
+    }
+    
     func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
         let containerViewStatus = UIView()
         containerViewStatus.backgroundColor = .darkGray

+ 3 - 3
NexilisLite/NexilisLite/Source/View/Contact/ContactCallViewController.swift

@@ -34,7 +34,7 @@ class ContactCallViewController: UIViewController {
     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
         if (segue.identifier == "videoVC") {
             if !Nexilis.checkingAccess(key: "video_call") {
-                showToast(message: "Feature disabled..".localized(), font: UIFont.systemFont(ofSize: 12), controller: self)
+                self.view.makeToast("Feature disabled..".localized(), duration: 3)
                 return
             }
             if !CheckConnection.isConnectedToNetwork() {
@@ -333,7 +333,7 @@ extension ContactCallViewController: UITableViewDataSource {
     
     @objc func call(sender: Any) {
         if !Nexilis.checkingAccess(key: "audio_call") {
-            showToast(message: "Feature disabled..".localized(), font: UIFont.systemFont(ofSize: 12), controller: self)
+            self.view.makeToast("Feature disabled..".localized(), duration: 3)
             return
         }
         let index = sender as! UIButton
@@ -355,7 +355,7 @@ extension ContactCallViewController: UITableViewDataSource {
     
     @objc func videoCall(sender: Any) {
         if !Nexilis.checkingAccess(key: "video_call") {
-            showToast(message: "Feature disabled..".localized(), font: UIFont.systemFont(ofSize: 12), controller: self)
+            self.view.makeToast("Feature disabled..".localized(), duration: 3)
             return
         }
         let index = sender as! UIButton

+ 1 - 1
NexilisLite/NexilisLite/Source/View/Control/BackupRestoreView.swift

@@ -646,7 +646,7 @@ public class BackupRestoreView: UIViewController, UITableViewDataSource, UITable
             })
         } catch {
             //print(error)
-            self.view.makeToast("Backup files are corrupted".localized(), duration: 0.5)
+            self.view.makeToast("Backup files are corrupted".localized(), duration: 3)
             DispatchQueue.global().async { [self] in
                 _ = Nexilis.write(message: CoreMessage_TMessageBank.getBackupRestored(option: optionBackup, fileid: fileIdBackup))
             }

+ 5 - 5
NexilisLite/NexilisLite/Source/View/Control/ContactChatViewController.swift

@@ -613,9 +613,9 @@ extension ContactChatViewController {
                 exblock = exblock == nil ? "0" : exblock!.isEmpty ? "0" : exblock!
                 if exblock != "0" {
                     if exblock == "1" {
-                        self.view.makeToast("You blocked this user".localized())
+                        self.view.makeToast("You blocked this user".localized(), duration: 3)
                     } else {
-                        self.view.makeToast("You have been blocked by this user".localized())
+                        self.view.makeToast("You have been blocked by this user".localized(), duration: 3)
                     }
                     return
                 }
@@ -642,9 +642,9 @@ extension ContactChatViewController {
                     exblock = exblock == nil ? "0" : exblock!.isEmpty ? "0" : exblock!
                     if exblock != "0" {
                         if exblock == "1" {
-                            self.view.makeToast("You blocked this user".localized())
+                            self.view.makeToast("You blocked this user".localized(), duration: 3)
                         } else {
-                            self.view.makeToast("You have been blocked by this user".localized())
+                            self.view.makeToast("You have been blocked by this user".localized(), duration: 3)
                         }
                         return
                     }
@@ -796,7 +796,7 @@ extension ContactChatViewController {
                         navigationController?.show(editorGroupVC, sender: nil)
                         cursorMember.close()
                     } else {
-                        self.view.makeToast("You are not a member of this group".localized(), duration: 0.5)
+                        self.view.makeToast("You are not a member of this group".localized(), duration: 3)
                     }
                 } catch {
                     rollback.pointee = true

+ 1 - 1
NexilisLite/NexilisLite/Source/View/Control/GroupCreateViewController.swift

@@ -66,7 +66,7 @@ class GroupCreateViewController: UITableViewController {
                     self.navigationController?.dismiss(animated: true, completion: nil)
                     self.isDismiss?(self.id)
                 } else {
-                    self.showToast(message: "Server busy, please try again later".localized(), seconds: 3)
+                    self.view.makeToast("Server busy, please try again later".localized(), duration: 3)
                 }
             }
         }

+ 1 - 1
NexilisLite/NexilisLite/Source/View/Control/GroupMemberViewController.swift

@@ -105,7 +105,7 @@ class GroupMemberViewController: UITableViewController {
                                 self.isDismiss?()
                             })
                         } else {
-                            self.showToast(message: "Server busy".localized(), seconds: 3)
+                            self.view.makeToast("Server busy, please try again later".localized(), duration: 3)
                         }
                     }
                 } catch {

+ 2 - 2
NexilisLite/NexilisLite/Source/View/Control/ProfileViewController.swift

@@ -420,7 +420,7 @@ public class ProfileViewController: UITableViewController {
     
     @objc func call(sender: Any) {
         if !Nexilis.checkingAccess(key: "audio_call") {
-            showToast(message: "Feature disabled..".localized(), font: UIFont.systemFont(ofSize: 12), controller: self)
+            self.view.makeToast("Feature disabled..".localized(), duration: 3)
             return
         }
         let myData = User.getData(pin: self.data)
@@ -453,7 +453,7 @@ public class ProfileViewController: UITableViewController {
     
     @objc func video(sender: Any) {
         if !Nexilis.checkingAccess(key: "video_call") {
-            showToast(message: "Feature disabled..".localized(), font: UIFont.systemFont(ofSize: 12), controller: self)
+            self.view.makeToast("Feature disabled..".localized(), duration: 3)
             return
         }
         let myData = User.getData(pin: self.data)

+ 2 - 2
NexilisLite/NexilisLite/Source/View/Streaming/CreateViewController.swift

@@ -134,10 +134,10 @@ class CreateViewController: UITableViewController {
             
             if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.createLS(title: "1~\(streamingTitle)", type: type, category: "3", tagline: streamingTag.text ?? "", notifType: notif, blogId: id, data: json)) {
                 if response.getBody(key: CoreMessage_TMessageKey.ERRCOD) != "00" {
-                    showToast(message: "Server Busy. Please try again.".localized(), font: UIFont.systemFont(ofSize: 12), controller: self)
+                    self.view.makeToast("Server Busy. Please try again.".localized(), duration: 3)
                 }
             } else {
-                showToast(message: "No Network. Please try again.".localized(), font: UIFont.systemFont(ofSize: 12), controller: self)
+                self.view.makeToast("No Network. Please try again.".localized(), duration: 3)
             }
             controller.data = User.getMyPin()!
             controller.streamingData = data

+ 2 - 2
NexilisLite/NexilisLite/Source/View/Streaming/QmeraStreamingViewController.swift

@@ -480,7 +480,7 @@ class QmeraStreamingViewController: UIViewController {
             let submitAction = UIAlertAction(title: "Submit".localized(), style: .default, handler: { (action) -> Void in
                 let textField = self.alert?.textFields![0]
                 if textField!.text!.isEmpty {
-                    self.showToast(message: (type == 0 ? "Title".localized() : "Tagline".localized()) + " " + "can't be empty".localized(), controller: self)
+                    self.view.makeToast((type == 0 ? "Title".localized() : "Tagline".localized()) + " " + "can't be empty".localized(), duration: 3)
                     return
                 }
                 if textField!.text! == (type == 0 ? (self.streamingData["title"] as! String) : (self.streamingData["tagline"] as! String)) {
@@ -505,7 +505,7 @@ class QmeraStreamingViewController: UIViewController {
                         } else {
                             self.tagline.text = textField!.text!
                         }
-                        self.showToast(message: "Successfully changed".localized(), controller: self)
+                        self.view.makeToast("Successfully changed".localized(), duration: 3)
                     }
                 }
             })