Explorar el Código

update fix bugs

alqindiirsyam hace 7 meses
padre
commit
73a940a114

+ 3 - 0
.gitignore

@@ -47,3 +47,6 @@ Podfile.lock
 # Carthage/Build
 
 .DS_Store
+ExampleCode/ExampleCode.xcodeproj/xcshareddata/xcschemes/ExampleCode.xcscheme
+NexilisLite/.DS_Store
+ExampleCode/ExampleCode/Info.plist

+ 0 - 10
ExampleCode/ExampleCode/Info.plist

@@ -10,16 +10,6 @@
 	<array>
 		<string>INSendMessageIntent</string>
 	</array>
-	<key>UIAppFonts</key>
-	<array>
-		<string>Poppins-Light.ttf</string>
-		<string>Poppins-Medium.ttf</string>
-		<string>Poppins-SemiBoldItalic.ttf</string>
-		<string>Poppins-Regular.ttf</string>
-		<string>Poppins-LightItalic.ttf</string>
-		<string>Poppins-SemiBold.ttf</string>
-		<string>Poppins-MediumItalic.ttf</string>
-	</array>
 	<key>UIApplicationSceneManifest</key>
 	<dict>
 		<key>UIApplicationSupportsMultipleScenes</key>

+ 3 - 2
ExampleCode/ExampleCode/ViewController.swift

@@ -13,6 +13,7 @@ import StreamShield
 class ViewController: UIViewController, ConnectDelegate {
     func onSuccess(userId: String) {
         print("SUCCESS \(userId)")
+//        SecurityShield.check(appName: "OneApp", apiKey: "38747683290F62E9667A018F490396EAE47BC16ADECD85B7E865C733E6DBD6A2")
     }
     
     func onFailed(error: String) {
@@ -23,7 +24,7 @@ class ViewController: UIViewController, ConnectDelegate {
     override func viewDidLoad() {
         super.viewDidLoad()
         
-        APIS.connect(appName: "SecureComm" ,apiKey: "8E784770B239FA3044A1DB44E276FB575B9BF3800ED11E200F4ACDD8FB476ABA", delegate: self, showButton: false)
+        APIS.connect(appName: "ps29ios" ,apiKey: "31D080A9F46FBCAAE18F122B8C621CAC2994151978DD3D9BEBD01EB927EE840C", delegate: self, showButton: true)
         //"OneApp","38747683290F62E9667A018F490396EAE47BC16ADECD85B7E865C733E6DBD6A2"
     }
     
@@ -33,7 +34,7 @@ class ViewController: UIViewController, ConnectDelegate {
                 APIS.openContactCenter()
             }),
             UIAction(title: "Chat".localized(), handler: {(_) in
-                APIS.openChat()
+                APIS.openConversation()
             }),
             UIAction(title: "Notification Center".localized(), handler: {(_) in
                 APIS.openNotificationCenter()

+ 55 - 0
NexilisLite/NexilisLite/Source/CoreMessage_TMessageBank.swift

@@ -201,6 +201,61 @@ public class CoreMessage_TMessageBank {
         return tmessage
     }
     
+    public static func editMessage(message_id: String = "", l_pin: String, message_scope_id: String, status: String, message_text: String, credential: String, attachment_flag: String, ex_blog_id: String, message_large_text: String, ex_format: String, image_id: String, audio_id: String, video_id: String, file_id: String, thumb_id: String, reff_id: String, read_receipts: String, chat_id: String, is_call_center: String, call_center_id: String, opposite_pin: String, last_edit: Int64 = 0) -> TMessage {
+        let me = User.getMyPin()!
+        let tmessage = TMessage()
+        tmessage.mCode = CoreMessage_TMessageCode.EDIT_MESSAGE
+        tmessage.mStatus = me + CoreMessage_TMessageUtil.getTID()
+        tmessage.mPIN = me
+        tmessage.mL_PIN = l_pin
+        tmessage.mBodies[CoreMessage_TMessageKey.MESSAGE_ID] = message_id.isEmpty ? me + CoreMessage_TMessageUtil.getTID() : message_id
+        tmessage.mBodies[CoreMessage_TMessageKey.F_PIN] = me
+        tmessage.mBodies[CoreMessage_TMessageKey.L_PIN] = l_pin
+        tmessage.mBodies[CoreMessage_TMessageKey.SERVER_DATE] = String(Date().currentTimeMillis())
+        tmessage.mBodies[CoreMessage_TMessageKey.LOCAL_TIMESTAMP] = String(Date().currentTimeMillis())
+        tmessage.mBodies[CoreMessage_TMessageKey.MESSAGE_SCOPE_ID] = message_scope_id
+        tmessage.mBodies[CoreMessage_TMessageKey.STATUS] = status
+        tmessage.mBodies[CoreMessage_TMessageKey.MESSAGE_TEXT] = message_text.toStupidString()
+        tmessage.mBodies[CoreMessage_TMessageKey.CREDENTIAL] = credential
+        tmessage.mBodies[CoreMessage_TMessageKey.ATTACHMENT_FLAG] = attachment_flag
+        tmessage.mBodies[CoreMessage_TMessageKey.BLOG_ID] = ex_blog_id
+        tmessage.mBodies[CoreMessage_TMessageKey.BODY] = message_large_text
+        tmessage.mBodies[CoreMessage_TMessageKey.CONNECTED] = "1"
+        tmessage.mBodies[CoreMessage_TMessageKey.FORMAT] = ex_format
+        tmessage.mBodies[CoreMessage_TMessageKey.IS_CALL_CENTER] = is_call_center
+        tmessage.mBodies[CoreMessage_TMessageKey.CALL_CENTER_ID] = call_center_id
+        tmessage.mBodies[CoreMessage_TMessageKey.F_USER_ID] = me
+        tmessage.mBodies[CoreMessage_TMessageKey.QUANTITY] = "1"
+        if !opposite_pin.isEmpty {
+            tmessage.mBodies[CoreMessage_TMessageKey.OPPOSITE_PIN] = opposite_pin
+        }
+        
+        if !image_id.isEmpty {
+            tmessage.mBodies[CoreMessage_TMessageKey.IMAGE_ID] = image_id
+        }
+        if !audio_id.isEmpty {
+            tmessage.mBodies[CoreMessage_TMessageKey.AUDIO_ID] = audio_id
+        }
+        if !video_id.isEmpty {
+            tmessage.mBodies[CoreMessage_TMessageKey.VIDEO_ID] = video_id
+        }
+        if !file_id.isEmpty {
+            tmessage.mBodies[CoreMessage_TMessageKey.FILE_ID] = file_id
+        }
+        if !thumb_id.isEmpty {
+            tmessage.mBodies[CoreMessage_TMessageKey.THUMB_ID] = thumb_id
+        }
+        if !reff_id.isEmpty {
+            tmessage.mBodies[CoreMessage_TMessageKey.REF_ID] = reff_id
+        }
+        tmessage.mBodies[CoreMessage_TMessageKey.READ_RECEIPTS] = read_receipts
+        if !chat_id.isEmpty {
+            tmessage.mBodies[CoreMessage_TMessageKey.CHAT_ID] = chat_id
+        }
+        tmessage.mBodies[CoreMessage_TMessageKey.LAST_EDIT] = "\(last_edit)"
+        return tmessage
+    }
+    
     public static func getUpdateRead(p_chat_id: String, p_f_pin: String, p_scope_id: String, qty: Int) -> TMessage {
         let me = User.getMyPin()!
         let tmessage = TMessage()

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

@@ -367,6 +367,7 @@ public class CoreMessage_TMessageCode {
     public static let UPDATE_PENDING_SID       = "C45";
     public static let CHECKIN       = "C46";
     public static let SEND_VERSION_STATE = "VRS"; //S
+    public static let EDIT_MESSAGE = "EM01";
     
     
     public static let GET_BALANCE   = "C35";

+ 4 - 3
NexilisLite/NexilisLite/Source/Database.swift

@@ -53,12 +53,12 @@ public class Database {
             
             database?.inDatabase({(fmdb) in
                 fmdb.setKey(key)
-                print("Open Done")
+//                print("Open Done")
             })
             database?.inTransaction({(fmdb, rollback) in
                 do {
                     try createDatabase(fmdb: fmdb)
-                    print("Create Done")
+//                    print("Create Done")
                 } catch {
                 }
             })
@@ -241,7 +241,8 @@ public class Database {
                                 "'local_timestamp' TEXT," +
                                 "'is_consult' INTEGER DEFAULT 0," +
                                 "'is_call_center' INTEGER DEFAULT 0," +
-                                "'call_center_id' TEXT" +
+                                "'call_center_id' TEXT," +
+                                "'last_edited' INTEGER DEFAULT 0" +
                                 ")", values: nil)
         
         try fmdb.executeUpdate("CREATE INDEX IF NOT EXISTS index_m_opposite on MESSAGE (opposite_pin, chat_id)", values: nil)

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

@@ -764,7 +764,7 @@ extension String {
         let boldItalicFont = UIFont.systemFont(ofSize: 12, weight: .semibold)
         let textUTF8 = String(self.utf8)
         let finalText = NSMutableAttributedString(string: textUTF8, attributes: [NSAttributedString.Key.font: font])
-        finalText.addAttribute(.foregroundColor, value: UIApplication.shared.visibleViewController?.traitCollection.userInterfaceStyle == .dark ? UIColor.white : UIColor.black, range: NSRange(location: 0, length: finalText.string.count))
+        finalText.addAttribute(.foregroundColor, value: UIApplication.shared.visibleViewController?.traitCollection.userInterfaceStyle == .dark ? UIColor.white : UIColor.black, range: NSRange(location: 0, length: textUTF8.count + textUTF8.countEmojiCharacter()))
         let boldSign: Character = "*"
         let italicSign: Character = "_"
         let underlineSign: Character = "^"

+ 16 - 8
NexilisLite/NexilisLite/Source/FloatingButton/FloatingButton.swift

@@ -246,16 +246,24 @@ public class FloatingButton: UIView {
         if configModeFB != MODE_VERTICAL_FLOATING_BUTTON {
             return
         }
-        let dataImage = try? Data(contentsOf: URL(string: (isDocked ? Utils.getUrlDock() : Utils.getIconCenter())!)!)
-        if dataImage != nil {
-            if let image = UIImage(data: dataImage!) {
-                nexilis_button.image = image
-            } else {
-                nexilis_button.image = UIImage(named: Utils.getFBIconBg() == "1" ? "pb_button" : "pb_ball", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+        let task = URLSession.shared.dataTask(with: URL(string: (isDocked ? Utils.getUrlDock() : Utils.getIconCenter())!)!) { dataImage, response, error in
+            if let error = error {
+                print("Failed to load data: \(error)")
+                return
+            }
+            DispatchQueue.main.async { [self] in
+                if dataImage != nil && UIImage(data: dataImage!) != nil {
+                    if let image = UIImage(data: dataImage!) {
+                        nexilis_button.image = image
+                    } else {
+                        nexilis_button.image = UIImage(named: Utils.getFBIconBg() == "1" ? "pb_button" : "pb_ball", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+                    }
+                } else {
+                    nexilis_button.image = UIImage(named: Utils.getFBIconBg() == "1" ? "pb_button" : "pb_ball", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
+                }
             }
-        } else {
-            nexilis_button.image = UIImage(named: Utils.getFBIconBg() == "1" ? "pb_button" : "pb_ball", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)
         }
+        task.resume()
     }
     
     private func checkDelayAnimation() {

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

@@ -84,7 +84,7 @@ class IncomingThread {
             onInitForumInfoBatch(message: message)
         } else if message.getCode() == CoreMessage_TMessageCode.UPLOAD_FILE {
             uploadFile(message: message)
-        } else if message.getCode() == CoreMessage_TMessageCode.SEND_CHAT {
+        } else if message.getCode() == CoreMessage_TMessageCode.SEND_CHAT || message.getCode() == CoreMessage_TMessageCode.EDIT_MESSAGE {
             receiveMessage(message: message)
         } else if message.getCode() == CoreMessage_TMessageCode.UPDATE_CTEXT {
             receiveMessageStatus(message: message)

+ 112 - 88
NexilisLite/NexilisLite/Source/Nexilis.swift

@@ -249,7 +249,6 @@ public class Nexilis: NSObject {
                             addFB(viewController: viewController!, fromMAB: fromMAB)
                         }
                         if let rootViewController = viewController {
-                            // Check if dark mode is enabled
                             let isDarkMode = rootViewController.traitCollection.userInterfaceStyle == .dark
                             if isDarkMode {
                                 UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).defaultTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
@@ -366,44 +365,42 @@ public class Nexilis: NSObject {
     }
     
     private static func getPullGroupNoMember() {
-        DispatchQueue.global().async {
-            if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.pullGroupNoMember(), timeout: 30 * 1000), response.isOk() {
-                let data = response.getBody(key: CoreMessage_TMessageKey.DATA)
-                //print("KUACAU \(data)")
-                if !data.isEmpty {
-                    if let jsonArray = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
-                        Database.shared.database?.inTransaction({ (fmdb, rollback) in
-                            do {
-                                for json in jsonArray {
-                                    let group_id = CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.GROUP_ID)
-                                    _ = try Database.shared.insertRecord(fmdb: fmdb, table: "GROUP_NM", cvalues: [
-                                        "group_id" : group_id,
-                                        "f_name" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.GROUP_NAME),
-                                        "scope_id" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.MESSAGE_SCOPE_ID),
-                                        "image_id": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.THUMB_ID),
-                                        "quote": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.QUOTE),
-                                        "last_update" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.LAST_UPDATE),
-                                        "created_by" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.CREATED_BY),
-                                        "created_date" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.CREATED_DATE),
-                                        "ex_block" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.BLOCK),
-                                        "folder_id" : "",
-                                        "chat_modifier" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.CHAT_MODIFIER),
-                                        "group_type" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.IS_ORGANIZATION),
-                                        "parent" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.PARENT_ID),
-                                        "level" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.LEVEL),
-                                        "is_open" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.IS_OPEN),
-                                        "official" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.OFFICIAL_ACCOUNT),
-                                        "level_edu" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.LEVEL_EDU),
-                                        "materi_edu" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.MATERI_EDU),
-                                        "is_education" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.IS_EDUCATION)
-                                    ], replace: true)
-                                }
-                            } catch {
-                                rollback.pointee = true
-                                print("Access database error: \(error.localizedDescription)")
+        if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.pullGroupNoMember(), timeout: 30 * 1000), response.isOk() {
+            let data = response.getBody(key: CoreMessage_TMessageKey.DATA)
+            //print("KUACAU \(data)")
+            if !data.isEmpty {
+                if let jsonArray = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
+                    Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                        do {
+                            for json in jsonArray {
+                                let group_id = CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.GROUP_ID)
+                                _ = try Database.shared.insertRecord(fmdb: fmdb, table: "GROUP_NM", cvalues: [
+                                    "group_id" : group_id,
+                                    "f_name" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.GROUP_NAME),
+                                    "scope_id" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.MESSAGE_SCOPE_ID),
+                                    "image_id": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.THUMB_ID),
+                                    "quote": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.QUOTE),
+                                    "last_update" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.LAST_UPDATE),
+                                    "created_by" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.CREATED_BY),
+                                    "created_date" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.CREATED_DATE),
+                                    "ex_block" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.BLOCK),
+                                    "folder_id" : "",
+                                    "chat_modifier" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.CHAT_MODIFIER),
+                                    "group_type" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.IS_ORGANIZATION),
+                                    "parent" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.PARENT_ID),
+                                    "level" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.LEVEL),
+                                    "is_open" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.IS_OPEN),
+                                    "official" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.OFFICIAL_ACCOUNT),
+                                    "level_edu" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.LEVEL_EDU),
+                                    "materi_edu" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.MATERI_EDU),
+                                    "is_education" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.IS_EDUCATION)
+                                ], replace: true)
                             }
-                        })
-                    }
+                        } catch {
+                            rollback.pointee = true
+                            print("Access database error: \(error.localizedDescription)")
+                        }
+                    })
                 }
             }
         }
@@ -499,34 +496,32 @@ public class Nexilis: NSObject {
     }
     
     private static func getPullWorkingArea() {
-        DispatchQueue.global().asyncAfter(deadline: .now(), execute: {
-            if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getWorkingAreaContactCenter(), timeout: 30 * 1000), response.isOk() {
-                let data = response.getBody(key: CoreMessage_TMessageKey.DATA)
-                if !data.isEmpty {
-                    if let jsonArray = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
-                        Database.shared.database?.inTransaction({ (fmdb, rollback) in
-                            do {
-                                for json in jsonArray {
-                                    var parent = CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.PARENT_ID)
-                                    if parent.isEmpty {
-                                        parent = "-99"
-                                    }
-                                    _ = try Database.shared.insertRecord(fmdb: fmdb, table: "WORKING_AREA", cvalues: [
-                                        "area_id" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.WORKING_AREA),
-                                        "name" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.NAME),
-                                        "parent" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.PARENT_ID),
-                                        "level" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.LEVEL)
-                                    ], replace: true)
+        if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getWorkingAreaContactCenter(), timeout: 30 * 1000), response.isOk() {
+            let data = response.getBody(key: CoreMessage_TMessageKey.DATA)
+            if !data.isEmpty {
+                if let jsonArray = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
+                    Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                        do {
+                            for json in jsonArray {
+                                var parent = CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.PARENT_ID)
+                                if parent.isEmpty {
+                                    parent = "-99"
                                 }
-                            } catch {
-                                rollback.pointee = true
-                                print("Access database error: \(error.localizedDescription)")
+                                _ = try Database.shared.insertRecord(fmdb: fmdb, table: "WORKING_AREA", cvalues: [
+                                    "area_id" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.WORKING_AREA),
+                                    "name" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.NAME),
+                                    "parent" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.PARENT_ID),
+                                    "level" : CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.LEVEL)
+                                ], replace: true)
                             }
-                        })
-                    }
+                        } catch {
+                            rollback.pointee = true
+                            print("Access database error: \(error.localizedDescription)")
+                        }
+                    })
                 }
             }
-        })
+        }
     }
     
     public static func showForceSignIn() {
@@ -587,8 +582,8 @@ public class Nexilis: NSObject {
             SecureUserDefaults.shared.removeValue(forKey: "onGoingCC")
             SecureUserDefaults.shared.removeValue(forKey: "membersCC")
             SecureUserDefaults.shared.removeValue(forKey: "startTimeCC")
-            if UIApplication.shared.applicationState == .active {
-                DispatchQueue.main.async {
+            DispatchQueue.main.async {
+                if UIApplication.shared.applicationState == .active {
                     let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
                     imageView.tintColor = .white
                     let banner = FloatingNotificationBanner(title: "Call Center Session has ended".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .info, colors: nil, iconPosition: .center)
@@ -662,9 +657,13 @@ public class Nexilis: NSObject {
         API.terminateCall(sParty: nil)
     }
     
-    public static func addQueueMessage(message: TMessage) {
+    public static func addQueueMessage(message: TMessage, isEditMessage: Bool = false) {
 //        OutgoingThread.default.addQueue(message: message)
-        InquiryThread.default.addQueue(message: message)
+        if isEditMessage {
+            OutgoingThread.default.addQueue(message: message)
+        } else {
+            InquiryThread.default.addQueue(message: message)
+        }
     }
     
     public static func deleteQueueMessage(message: TMessage) {
@@ -1260,6 +1259,7 @@ public class Nexilis: NSObject {
     }
     
     static func saveMessage(message: TMessage, withStatus: Bool = true) {
+        print("save message \(message.toLogString())")
         guard let me = User.getMyPin() else {
             return
         }
@@ -1273,13 +1273,13 @@ public class Nexilis: NSObject {
         }
         Database.shared.database?.inTransaction({ (fmdb, rollback) in
             do {
-                if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select message_id from MESSAGE where message_id = '\(message_id)'") {
-                    if cursor.next() {
-                        //print("message exist")
-                        return
-                    }
-                    cursor.close()
-                }
+//                if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select message_id from MESSAGE where message_id = '\(message_id)'") {
+//                    if cursor.next() {
+//                        //print("message exist")
+//                        return
+//                    }
+//                    cursor.close()
+//                }
                 let l_pin = message.getBody(key : CoreMessage_TMessageKey.L_PIN, default_value : "")
                 let scope = message.getBody(key : CoreMessage_TMessageKey.MESSAGE_SCOPE_ID, default_value : "3")
                 let status = message.getBody(key : CoreMessage_TMessageKey.STATUS, default_value : "")
@@ -1287,6 +1287,7 @@ public class Nexilis: NSObject {
                 let broadcast_flag = message.getBody(key: CoreMessage_TMessageKey.BROADCAST_FLAG, default_value: "0")
                 let is_call_center = message.getBody(key: CoreMessage_TMessageKey.IS_CALL_CENTER, default_value: "0")
                 let call_center_id = message.getBody(key: CoreMessage_TMessageKey.CALL_CENTER_ID, default_value: "")
+                let last_edited = message.getBodyAsLong(key: CoreMessage_TMessageKey.LAST_EDIT, default_value: 0)
                 //print("prepare save db")
                 do {
                     _ = try Database.shared.insertRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
@@ -1317,7 +1318,8 @@ public class Nexilis: NSObject {
                         "local_timestamp" : message.getBody(key: CoreMessage_TMessageKey.LOCAL_TIMESTAMP, default_value : String(Date().currentTimeMillis())),
                         "broadcast_flag" : broadcast_flag,
                         "is_call_center" : is_call_center,
-                        "call_center_id" : call_center_id
+                        "call_center_id" : call_center_id,
+                        "last_edited" : last_edited
                     ], replace: true)
                 } catch {
                     rollback.pointee = true
@@ -1825,13 +1827,20 @@ public class Nexilis: NSObject {
         case .denied:
             permissionCheck = false
         case .undetermined:
+            Nexilis.dispatch = DispatchGroup()
+            Nexilis.dispatch?.enter()
             AVAudioSession.sharedInstance().requestRecordPermission({ (granted) in
                 if granted {
                     permissionCheck = true
                 } else {
                     permissionCheck = false
                 }
+                if let dispatch = Nexilis.dispatch {
+                    dispatch.leave()
+                }
             })
+            Nexilis.dispatch?.wait()
+            Nexilis.dispatch = nil
         default:
             break
         }
@@ -1923,28 +1932,38 @@ extension UIFont {
     static let FONT_SELECT = 0
 
     @objc class func libSystemFont(ofSize size: CGFloat) -> UIFont? {
-        jbs_registerFont(withFilenameString: LibFontName.regular)
+        if UIFont(name: LibFontName.regular, size: size) == nil {
+            jbs_registerFont(withFilenameString: LibFontName.regular)
+        }
         return UIFont(name: LibFontName.regular, size: size)
     }
     
     @objc class func libSystemFontWeight(ofSize size: CGFloat, weight: UIFont.Weight) -> UIFont? {
         if weight == .medium {
-            jbs_registerFont(withFilenameString: LibFontName.medium)
+            if UIFont(name: LibFontName.medium, size: size) == nil {
+                jbs_registerFont(withFilenameString: LibFontName.medium)
+            }
             return UIFont(name: LibFontName.medium, size: size)
         } else if weight == .semibold {
-            jbs_registerFont(withFilenameString: LibFontName.boldItalic)
+            if UIFont(name: LibFontName.boldItalic, size: size) == nil {
+                jbs_registerFont(withFilenameString: LibFontName.boldItalic)
+            }
             return UIFont(name: LibFontName.boldItalic, size: size)
         }
         return UIFont(name: LibFontName.regular, size: size)
     }
 
     @objc class func libBoldSystemFont(ofSize size: CGFloat) -> UIFont? {
-        jbs_registerFont(withFilenameString: LibFontName.bold)
+        if UIFont(name: LibFontName.bold, size: size) == nil {
+            jbs_registerFont(withFilenameString: LibFontName.bold)
+        }
         return UIFont(name: LibFontName.bold, size: size)
     }
 
     @objc class func libItalicSystemFont(ofSize size: CGFloat) -> UIFont? {
-        jbs_registerFont(withFilenameString: LibFontName.italic)
+        if UIFont(name: LibFontName.italic, size: size) == nil {
+            jbs_registerFont(withFilenameString: LibFontName.italic)
+        }
         return UIFont(name: LibFontName.italic, size: size)
     }
 
@@ -2789,19 +2808,21 @@ extension Nexilis: MessageDelegate {
                         } else if AVCaptureDevice.authorizationStatus(for: .video) ==  .denied {
                             permissionCheck = 0
                         } else {
+                            Nexilis.dispatch = DispatchGroup()
+                            Nexilis.dispatch?.enter()
                             AVCaptureDevice.requestAccess(for: .video, completionHandler: { (granted: Bool) -> Void in
                                 if granted == true {
                                     permissionCheck = 1
                                 } else {
                                     permissionCheck = 0
                                 }
+                                if let dispatch = Nexilis.dispatch {
+                                    dispatch.leave()
+                                }
                             })
+                            Nexilis.dispatch?.wait()
+                            Nexilis.dispatch = nil
                         }
-                        
-                        while permissionCheck == -1 {
-                            sleep(1)
-                        }
-                        
                         if permissionCheck == 0 {
                             let alert = LibAlertController(title: "Attention!".localized(), message: "Please allow camera permission in your settings".localized(), preferredStyle: .alert)
                             alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: { _ in
@@ -3153,17 +3174,20 @@ extension Nexilis: MessageDelegate {
                         } else if AVCaptureDevice.authorizationStatus(for: .video) ==  .denied {
                             permissionCheck = 0
                         } else {
+                            Nexilis.dispatch = DispatchGroup()
+                            Nexilis.dispatch?.enter()
                             AVCaptureDevice.requestAccess(for: .video, completionHandler: { (granted: Bool) -> Void in
                                 if granted == true {
                                     permissionCheck = 1
                                 } else {
                                     permissionCheck = 0
                                 }
+                                if let dispatch = Nexilis.dispatch {
+                                    dispatch.leave()
+                                }
                             })
-                        }
-                        
-                        while permissionCheck == -1 {
-                            sleep(1)
+                            Nexilis.dispatch?.wait()
+                            Nexilis.dispatch = nil
                         }
                         
                         if permissionCheck == 0 {

+ 2 - 2
NexilisLite/NexilisLite/Source/OutgoingThread.swift

@@ -125,12 +125,12 @@ class OutgoingThread {
     }
     
     private func process(message: TMessage) {
-        //print("outgoing process", message.toLogString())
+        print("outgoing process", message.toLogString())
         if self.isWait {
             queue.append(message)
             return
         }
-        if message.getCode() == CoreMessage_TMessageCode.SEND_CHAT {
+        if message.getCode() == CoreMessage_TMessageCode.SEND_CHAT || message.getCode() == CoreMessage_TMessageCode.EDIT_MESSAGE {
             sendChat(message: message)
         } else if message.getCode() == CoreMessage_TMessageCode.DELETE_CTEXT {
             deleteMessage(message: message)

+ 12 - 6
NexilisLite/NexilisLite/Source/View/Call/QmeraAudioViewController.swift

@@ -621,12 +621,6 @@ class QmeraAudioViewController: UIViewController {
     }
     
     @objc func didEnd(sender: Any?) {
-        if self.buttonWB.isDescendant(of: self.view){
-            self.buttonWB.removeFromSuperview()
-        }
-        if self.buttonChat.isDescendant(of: self.view){
-            self.buttonChat.removeFromSuperview()
-        }
         poweredByView.isHidden = true
         let onGoingCC: String = SecureUserDefaults.shared.value(forKey: "onGoingCC") ?? ""
         if !onGoingCC.isEmpty {
@@ -870,6 +864,12 @@ class QmeraAudioViewController: UIViewController {
                         let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
                         if pin == requester || pin == officer {
                             DispatchQueue.main.async {
+                                if self.buttonWB.isDescendant(of: self.view){
+                                    self.buttonWB.removeFromSuperview()
+                                }
+                                if self.buttonChat.isDescendant(of: self.view){
+                                    self.buttonChat.removeFromSuperview()
+                                }
                                 if self.viewIfLoaded?.window != nil {
                                     let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
                                     imageView.tintColor = .white
@@ -888,6 +888,12 @@ class QmeraAudioViewController: UIViewController {
                         }
                     } else if !onGoingCC.isEmpty && users.count == 0 {
                         DispatchQueue.main.async {
+                            if self.buttonWB.isDescendant(of: self.view){
+                                self.buttonWB.removeFromSuperview()
+                            }
+                            if self.buttonChat.isDescendant(of: self.view){
+                                self.buttonChat.removeFromSuperview()
+                            }
                             if self.viewIfLoaded?.window != nil {
                                 let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
                                 imageView.tintColor = .white

+ 119 - 119
NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift

@@ -2830,7 +2830,7 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
             })
             let message = CoreMessage_TMessageBank.sendMessage(message_id: messageId,
                                                                l_pin: dataMessages[indexPath!.row][TypeDataMessage.l_pin] as! String,
-                                                               message_scope_id: dataMessages[indexPath!.row][TypeDataMessage.l_pin] as! String,
+                                                               message_scope_id: dataMessages[indexPath!.row][TypeDataMessage.message_scope_id] as! String,
                                                                status: "1",
                                                                message_text: dataMessages[indexPath!.row][TypeDataMessage.message_text] as! String,
                                                                credential: dataMessages[indexPath!.row][TypeDataMessage.credential] as! String,
@@ -4461,9 +4461,9 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
                         }
                     }
                 }
-                else if FileEncryption.shared.isSecureExists(filename: fileURL.lastPathComponent) {
+                else if FileEncryption.shared.isSecureExists(filename: fileChat) {
                     do {
-                        if let dataFile = try FileEncryption.shared.readSecure(filename: fileURL.lastPathComponent) {
+                        if let dataFile = try FileEncryption.shared.readSecure(filename: fileChat) {
                             var sizeOfFile = Int(dataFile.count / 1000000)
                             if (sizeOfFile < 1) {
                                 sizeOfFile = Int(dataFile.count / 1000)
@@ -4495,7 +4495,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
             containerViewFile.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
             containerViewFile.bottomAnchor.constraint(equalTo:messageText.topAnchor, constant: -5).isActive = true
             containerViewFile.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
-            containerViewFile.heightAnchor.constraint(equalToConstant: 50).isActive = true
+//            containerViewFile.heightAnchor.constraint(equalToConstant: 50).isActive = true
             containerViewFile.backgroundColor = .black.withAlphaComponent(0.2)
             containerViewFile.layer.cornerRadius = 5.0
             containerViewFile.clipsToBounds = true
@@ -5224,129 +5224,129 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
                         }
                     }
                 }
-            } else if (sender.file_id != "") {
-                if let dirPath = paths.first {
-                    let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.file_id)
-                    if FileManager.default.fileExists(atPath: fileURL.path) {
-                        self.previewItem = fileURL as NSURL
-                        let previewController = QLPreviewController()
-                        let rightBarButton = UIBarButtonItem()
-                        previewController.navigationItem.rightBarButtonItem = rightBarButton
-                        previewController.dataSource = self
-                        previewController.modalPresentationStyle = .custom
-                        
-                        self.present(previewController, animated: true)
-                    } else if FileEncryption.shared.isSecureExists(filename: sender.file_id) {
-                        do {
-                            if let docData = try FileEncryption.shared.readSecure(filename: sender.file_id) {
-                                
-                                let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
-                                let tempPath = cachesDirectory.appendingPathComponent(sender.file_id)
-                                try docData.write(to: tempPath)
-                                self.previewItem = tempPath as NSURL
-                                let previewController = QLPreviewController()
-                                let rightBarButton = UIBarButtonItem()
-                                previewController.navigationItem.rightBarButtonItem = rightBarButton
-                                previewController.dataSource = self
-                                previewController.modalPresentationStyle = .custom
-                                self.present(previewController,animated: true)
-                            }
-                        }
-                        catch {
+            }
+        } else if (sender.file_id != "") {
+            if let dirPath = paths.first {
+                let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.file_id)
+                if FileManager.default.fileExists(atPath: fileURL.path) {
+                    self.previewItem = fileURL as NSURL
+                    let previewController = QLPreviewController()
+                    let rightBarButton = UIBarButtonItem()
+                    previewController.navigationItem.rightBarButtonItem = rightBarButton
+                    previewController.dataSource = self
+                    previewController.modalPresentationStyle = .custom
+                    
+                    self.present(previewController, animated: true)
+                } else if FileEncryption.shared.isSecureExists(filename: sender.file_id) {
+                    do {
+                        if let docData = try FileEncryption.shared.readSecure(filename: sender.file_id) {
                             
+                            let cachesDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
+                            let tempPath = cachesDirectory.appendingPathComponent(sender.file_id)
+                            try docData.write(to: tempPath)
+                            self.previewItem = tempPath as NSURL
+                            let previewController = QLPreviewController()
+                            let rightBarButton = UIBarButtonItem()
+                            previewController.navigationItem.rightBarButtonItem = rightBarButton
+                            previewController.dataSource = self
+                            previewController.modalPresentationStyle = .custom
+                            self.present(previewController,animated: true)
                         }
-                    } else {
-                        for view in sender.containerFile.subviews {
-                            if !(view is UIImageView) && !(view is UILabel) {
-                                view.removeFromSuperview()
-                            }
-                        }
-                        let containerLoading = UIView()
-                        sender.containerFile.addSubview(containerLoading)
-                        containerLoading.translatesAutoresizingMaskIntoConstraints = false
-                        containerLoading.centerYAnchor.constraint(equalTo: sender.containerFile.centerYAnchor).isActive = true
-                        containerLoading.leadingAnchor.constraint(equalTo: sender.labelFile.trailingAnchor, constant: 5).isActive = true
-                        containerLoading.trailingAnchor.constraint(equalTo: sender.containerFile.trailingAnchor, constant: -5).isActive = true
-                        containerLoading.widthAnchor.constraint(equalToConstant: 30).isActive = true
-                        containerLoading.heightAnchor.constraint(equalToConstant: 30).isActive = true
-                        let circlePath = UIBezierPath(arcCenter: CGPoint(x: 15, y: 15), radius: 10, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
-                        let trackShape = CAShapeLayer()
-                        trackShape.path = circlePath.cgPath
-                        trackShape.fillColor = UIColor.clear.cgColor
-                        trackShape.lineWidth = 5
-                        trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
-                        containerLoading.layer.addSublayer(trackShape)
-                        let shapeLoading = CAShapeLayer()
-                        shapeLoading.path = circlePath.cgPath
-                        shapeLoading.fillColor = UIColor.clear.cgColor
-                        shapeLoading.lineWidth = 3
-                        shapeLoading.strokeEnd = 0
-                        shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
-                        containerLoading.layer.addSublayer(shapeLoading)
-                        let imageupload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
-                        imageupload.tintColor = .white
-                        containerLoading.addSubview(imageupload)
-                        imageupload.translatesAutoresizingMaskIntoConstraints = false
-                        imageupload.centerYAnchor.constraint(equalTo: containerLoading.centerYAnchor).isActive = true
-                        imageupload.centerXAnchor.constraint(equalTo: containerLoading.centerXAnchor).isActive = true
+                    }
+                    catch {
                         
-                        Download().startHTTP(forKey: sender.file_id) { (name, progress) in
-                            DispatchQueue.main.async {
-                                guard progress == 100 else {
-                                    shapeLoading.strokeEnd = CGFloat(progress / 100)
-                                    return
-                                }
-                                do {
-                                    try FileEncryption.shared.writeSecure(filename: name)
-                                } catch {
-                                    
-                                }
-                                let idx = self.dataMessages.firstIndex(where: { $0["file_id"] as! String == sender.file_id})
-                                if idx != nil {
-                                    self.dataMessages[idx!]["progress"] = progress
-                                    self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
-                                }
+                    }
+                } else {
+                    for view in sender.containerFile.subviews {
+                        if !(view is UIImageView) && !(view is UILabel) {
+                            view.removeFromSuperview()
+                        }
+                    }
+                    let containerLoading = UIView()
+                    sender.containerFile.addSubview(containerLoading)
+                    containerLoading.translatesAutoresizingMaskIntoConstraints = false
+                    containerLoading.centerYAnchor.constraint(equalTo: sender.containerFile.centerYAnchor).isActive = true
+                    containerLoading.leadingAnchor.constraint(equalTo: sender.labelFile.trailingAnchor, constant: 5).isActive = true
+                    containerLoading.trailingAnchor.constraint(equalTo: sender.containerFile.trailingAnchor, constant: -5).isActive = true
+                    containerLoading.widthAnchor.constraint(equalToConstant: 30).isActive = true
+                    containerLoading.heightAnchor.constraint(equalToConstant: 30).isActive = true
+                    let circlePath = UIBezierPath(arcCenter: CGPoint(x: 15, y: 15), radius: 10, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
+                    let trackShape = CAShapeLayer()
+                    trackShape.path = circlePath.cgPath
+                    trackShape.fillColor = UIColor.clear.cgColor
+                    trackShape.lineWidth = 5
+                    trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
+                    containerLoading.layer.addSublayer(trackShape)
+                    let shapeLoading = CAShapeLayer()
+                    shapeLoading.path = circlePath.cgPath
+                    shapeLoading.fillColor = UIColor.clear.cgColor
+                    shapeLoading.lineWidth = 3
+                    shapeLoading.strokeEnd = 0
+                    shapeLoading.strokeColor = UIColor.blueBubbleColor.cgColor
+                    containerLoading.layer.addSublayer(shapeLoading)
+                    let imageupload = UIImageView(image: UIImage(systemName: "arrow.down", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
+                    imageupload.tintColor = .white
+                    containerLoading.addSubview(imageupload)
+                    imageupload.translatesAutoresizingMaskIntoConstraints = false
+                    imageupload.centerYAnchor.constraint(equalTo: containerLoading.centerYAnchor).isActive = true
+                    imageupload.centerXAnchor.constraint(equalTo: containerLoading.centerXAnchor).isActive = true
+                    
+                    Download().startHTTP(forKey: sender.file_id) { (name, progress) in
+                        DispatchQueue.main.async {
+                            guard progress == 100 else {
+                                shapeLoading.strokeEnd = CGFloat(progress / 100)
+                                return
+                            }
+                            do {
+                                try FileEncryption.shared.writeSecure(filename: name)
+                            } catch {
+                                
+                            }
+                            let idx = self.dataMessages.firstIndex(where: { $0["file_id"] as! String == sender.file_id})
+                            if idx != nil {
+                                self.dataMessages[idx!]["progress"] = progress
+                                self.tableChatView.reloadRows(at: [sender.indexPath], with: .none)
                             }
                         }
                     }
                 }
-            } else {
-                DispatchQueue.main.async {
-                    let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.message_id})
-                    if idx == nil {
-                        return
-                    }
-                    let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
-                    if section == nil {
-                        return
-                    }
-                    let row = self.dataMessages.filter({ $0["chat_date"] as! String == self.dataDates[section!]}).firstIndex(where: { $0["message_id"] as! String == self.dataMessages[idx!]["message_id"] as! String})
-                    if row == nil {
-                        return
-                    }
-                    let indexPath = IndexPath(row: row!, section: section!)
-                    self.tableChatView.scrollToRow(at: indexPath, at: .middle, animated: true)
-                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
-                        if let cell = self.tableChatView.cellForRow(at: indexPath) {
-                            let containerMessage = cell.contentView.subviews[1]
-                            let idMe = User.getMyPin() as String?
-                            if (self.dataMessages[idx!]["f_pin"] as? String == idMe) {
-                                containerMessage.backgroundColor = .blueBubbleColor.withAlphaComponent(0.3)
-                                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
-                                    if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
-                                        containerMessage.backgroundColor = .clear
-                                    } else {
-                                        containerMessage.backgroundColor = .blueBubbleColor
-                                    }
+            }
+        } else {
+            DispatchQueue.main.async {
+                let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.message_id})
+                if idx == nil {
+                    return
+                }
+                let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
+                if section == nil {
+                    return
+                }
+                let row = self.dataMessages.filter({ $0["chat_date"] as! String == self.dataDates[section!]}).firstIndex(where: { $0["message_id"] as! String == self.dataMessages[idx!]["message_id"] as! String})
+                if row == nil {
+                    return
+                }
+                let indexPath = IndexPath(row: row!, section: section!)
+                self.tableChatView.scrollToRow(at: indexPath, at: .middle, animated: true)
+                DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+                    if let cell = self.tableChatView.cellForRow(at: indexPath) {
+                        let containerMessage = cell.contentView.subviews[1]
+                        let idMe = User.getMyPin() as String?
+                        if (self.dataMessages[idx!]["f_pin"] as? String == idMe) {
+                            containerMessage.backgroundColor = .blueBubbleColor.withAlphaComponent(0.3)
+                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
+                                if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
+                                    containerMessage.backgroundColor = .clear
+                                } else {
+                                    containerMessage.backgroundColor = .blueBubbleColor
                                 }
-                            } else {
-                                containerMessage.backgroundColor = .whiteBubbleColor.withAlphaComponent(0.3)
-                                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
-                                    if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
-                                        containerMessage.backgroundColor = .clear
-                                    } else {
-                                        containerMessage.backgroundColor = .whiteBubbleColor
-                                    }
+                            }
+                        } else {
+                            containerMessage.backgroundColor = .whiteBubbleColor.withAlphaComponent(0.3)
+                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
+                                if (self.dataMessages[idx!]["attachment_flag"] as? String == "11") {
+                                    containerMessage.backgroundColor = .clear
+                                } else {
+                                    containerMessage.backgroundColor = .whiteBubbleColor
                                 }
                             }
                         }

+ 94 - 37
NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift

@@ -838,7 +838,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
     private func addDataMessage() {
         multipleOffsetUp += 1
         let queryCount = "SELECT COUNT(*) FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND (message_scope_id = '3' OR message_scope_id = '18') AND is_call_center = 0"
-        let query = "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared, blog_id, credential, is_call_center, call_center_id, opposite_pin FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND (message_scope_id = '3' OR message_scope_id = '18') AND is_call_center = 0 order by server_date asc LIMIT CASE WHEN (\(queryCount))-\(dataMessages.count)>=20 THEN 20*\(multipleOffsetUp-1) ELSE (\(queryCount))-\(dataMessages.count) END OFFSET CASE WHEN (\(queryCount))>=\(20*multipleOffsetUp) THEN (\(queryCount))-\(20*multipleOffsetUp) ELSE 0 END"
+        let query = "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared, blog_id, credential, is_call_center, call_center_id, opposite_pin, last_edited FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND (message_scope_id = '3' OR message_scope_id = '18') AND is_call_center = 0 order by server_date asc LIMIT CASE WHEN (\(queryCount))-\(dataMessages.count)>=20 THEN 20*\(multipleOffsetUp-1) ELSE (\(queryCount))-\(dataMessages.count) END OFFSET CASE WHEN (\(queryCount))>=\(20*multipleOffsetUp) THEN (\(queryCount))-\(20*multipleOffsetUp) ELSE 0 END"
         Database.shared.database?.inTransaction({ (fmdb, rollback) in
             do {
                 if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: query) {
@@ -868,6 +868,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                         row[TypeDataMessage.is_call_center] = cursorData.string(forColumnIndex: 20)
                         row[TypeDataMessage.call_center_id] = cursorData.string(forColumnIndex: 21)
                         row[TypeDataMessage.opposite_pin] = cursorData.string(forColumnIndex: 22)
+                        row[TypeDataMessage.last_edit] = cursorData.longLongInt(forColumnIndex: 23)
                         if let cursorStatus = Database.shared.getRecords(fmdb: fmdb, query: "SELECT status FROM MESSAGE_STATUS WHERE message_id='\(row["message_id"] as! String)'") {
                             while cursorStatus.next() {
                                 row["status"] = cursorStatus.string(forColumnIndex: 0)
@@ -979,7 +980,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
     private func getData() {
 //        let queryCount = "SELECT COUNT(*) FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND (message_scope_id = '3' OR message_scope_id = '18') AND is_call_center = 0"
 //        var query = "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared, blog_id, credential FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND (message_scope_id = '3' OR message_scope_id = '18') AND is_call_center = 0 order by server_date asc LIMIT CASE WHEN (\(queryCount))-\(dataMessages.count)>=20 THEN 20 ELSE (\(queryCount))-\(dataMessages.count) END OFFSET CASE WHEN (\(queryCount))>=\(20*multipleOffsetUp) THEN (\(queryCount))-\(20*multipleOffsetUp) ELSE 0 END"
-        var query = "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared, blog_id, credential, is_call_center, call_center_id, opposite_pin FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND (message_scope_id = '3' OR message_scope_id = '18') AND is_call_center = 0 order by server_date asc"
+        var query = "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared, blog_id, credential, is_call_center, call_center_id, opposite_pin, last_edited FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND (message_scope_id = '3' OR message_scope_id = '18') AND is_call_center = 0 order by server_date asc"
         if isContactCenter {
             if complaintId.isEmpty {
                 query = "SELECT message_id, f_pin, l_pin, message_scope_id, server_date, status, message_text, audio_id, video_id, image_id, thumb_id, read_receipts, chat_id, file_id, attachment_flag, reff_id, lock, is_stared FROM MESSAGE where (f_pin='\(dataPerson["f_pin"]!!)' or l_pin='\(dataPerson["f_pin"]!!)') AND message_scope_id = '5' AND broadcast_flag = 0 AND is_call_center = 1 order by server_date asc"
@@ -1043,6 +1044,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                         row[TypeDataMessage.is_call_center] = cursorData.string(forColumnIndex: 20)
                         row[TypeDataMessage.call_center_id] = cursorData.string(forColumnIndex: 21)
                         row[TypeDataMessage.opposite_pin] = cursorData.string(forColumnIndex: 22)
+                        row[TypeDataMessage.last_edit] = cursorData.longLongInt(forColumnIndex: 23)
                         if let cursorStatus = Database.shared.getRecords(fmdb: fmdb, query: "SELECT status FROM MESSAGE_STATUS WHERE message_id='\(row["message_id"] as! String)'") {
                             while cursorStatus.next() {
                                 row["status"] = cursorStatus.string(forColumnIndex: 0)
@@ -1480,6 +1482,18 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                     if chatData[CoreMessage_TMessageKey.F_PIN] == nil {
                         return
                     }
+                    let idx = self.dataMessages.firstIndex(where: { $0[TypeDataMessage.message_id] as? String == chatData[CoreMessage_TMessageKey.MESSAGE_ID]})
+                    if idx != nil {
+                        self.dataMessages[idx!][TypeDataMessage.message_text] = chatData[CoreMessage_TMessageKey.MESSAGE_TEXT]
+                        self.dataMessages[idx!][TypeDataMessage.last_edit] = chatData[CoreMessage_TMessageKey.LAST_EDIT]
+                        self.dataMessages[idx!][TypeDataMessage.status] = chatData[CoreMessage_TMessageKey.STATUS]
+                        let section = self.dataDates.firstIndex(of: self.dataMessages[idx!]["chat_date"] as! String)
+                        let row = self.dataMessages.filter({ $0["chat_date"] as! String == self.dataMessages[idx!]["chat_date"] as! String}).firstIndex(where: { $0["message_id"] as? String == self.dataMessages[idx!]["message_id"] as? String })
+                        if row != nil && section != nil  {
+                            self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
+                        }
+                        return
+                    }
                     var row: [String: Any?] = [:]
                     row["message_id"] = chatData[CoreMessage_TMessageKey.MESSAGE_ID]
                     row["f_pin"] = chatData[CoreMessage_TMessageKey.F_PIN]
@@ -2425,6 +2439,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         row["credential"] = credential
         row["chat_id"] = chat_id
         row["file_id"] = file_id
+        row["blog_id"] = ex_blog_id
         row["attachment_flag"] = attachment_flag
         row["reff_id"] = reff_id
         row["progress"] = 0.0
@@ -3748,33 +3763,9 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
                 self.tableChatView.reloadData()
             }
         })
-//        let edit = UIAction(title: "Edit".localized(), image: UIImage(systemName: "pencil"), handler: {(_) in
-//            if self.removed {
-//                return
-//            }
-//            if self.isSearching {
-//                self.cancelAction()
-//            }
-//            if self.reffId != nil {
-//                self.deleteReplyView()
-//            }
-//            self.isEditAction = true
-//            DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
-//                let nextInteraction = UIContextMenuInteraction(delegate: self)
-//                interaction.view!.addInteraction(nextInteraction)
-//                guard let interaction = interaction.view!.interactions.first,
-//                      let data = Data(base64Encoded: "X3ByZXNlbnRNZW51QXRMb2NhdGlvbjo="),
-//                      let str = String(data: data, encoding: .utf8)
-//                else {
-//                    return
-//                }
-//                let selector = NSSelectorFromString(str)
-//                guard interaction.responds(to: selector) else {
-//                    return
-//                }
-//                nextInteraction.perform(selector, with: self.view)
-//            }
-//        })
+        let edit = UIAction(title: "Edit".localized(), image: UIImage(systemName: "pencil"), handler: {(_) in
+            self.showEditMessageAlert(at: indexPath!)
+        })
         let info = UIAction(title: "Info".localized(), image: UIImage(systemName: "info.circle.fill"), handler: {(_) in
             if self.removed {
                 return
@@ -3855,7 +3846,7 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
             })
             let message = CoreMessage_TMessageBank.sendMessage(message_id: messageId,
                                                                l_pin: dataMessages[indexPath!.row][TypeDataMessage.l_pin] as! String,
-                                                               message_scope_id: dataMessages[indexPath!.row][TypeDataMessage.l_pin] as! String,
+                                                               message_scope_id: dataMessages[indexPath!.row][TypeDataMessage.message_scope_id] as! String,
                                                                status: "1",
                                                                message_text: dataMessages[indexPath!.row][TypeDataMessage.message_text] as! String,
                                                                credential: dataMessages[indexPath!.row][TypeDataMessage.credential] as! String,
@@ -3880,12 +3871,11 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
         var children: [UIMenuElement] = [star, reply, forward, copy, delete]
 //        let copyOption = self.copyOption(indexPath: indexPath!)
         let idMe = User.getMyPin() as String?
+        print("LOHE \(isContactCenter)")
         if dataMessages[indexPath!.row]["status"] as! String == "0" {
             children = [resend, delete]
         } else if isContactCenter {
-            if (groupImages[dataMessages[indexPath!.row]["message_id"] as! String] != nil) {
-                children = [reply, copy]
-            }
+            children = [reply, copy]
         } else if (dataMessages[indexPath!.row]["lock"] != nil && dataMessages[indexPath!.row]["lock"] as! String == "1") || dataMessages[indexPath!.row]["message_scope_id"] as! String == "18" || dataPerson["f_pin"] == "-999" || dataMessages[indexPath!.row]["credential"] as! String == "1" {
             children = [delete]
         } else if (groupImages[dataMessages[indexPath!.row]["message_id"] as! String] != nil) {
@@ -3915,6 +3905,9 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
             if (dataMessages[indexPath!.row]["f_pin"] as! String) == idMe {
                 children.insert(info, at: children.count - 1)
             }
+//            if !(dataMessages[indexPath!.row][TypeDataMessage.message_text] as! String).isEmpty && (dataMessages[indexPath!.row]["f_pin"] as! String) == idMe {
+//                children.insert(edit, at: children.count - 1)
+//            }
         }
         if isEditAction {
             return UIContextMenuConfiguration(identifier: nil,
@@ -3931,6 +3924,52 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
         }
     }
     
+    func showEditMessageAlert(at indexPath: IndexPath) {
+        let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath.section]})
+        let oldText = dataMessages[indexPath.row][TypeDataMessage.message_text] as! String
+        let alertController = UIAlertController(title: "Edit".localized(), message: nil, preferredStyle: .alert)
+        alertController.addTextField { textField in
+            textField.text = oldText
+        }
+        
+        let saveAction = UIAlertAction(title: "Save", style: .default) { [weak self] _ in
+            if let textField = alertController.textFields?.first, let newText = textField.text {
+                if newText.trimmingCharacters(in: .whitespacesAndNewlines) != oldText {
+                    let lastEdited = 1 + ((dataMessages[indexPath.row][TypeDataMessage.last_edit] as? Int64) ?? 0)
+                    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 {
+                        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+                            do {
+                                _ = Database.shared.updateRecord(fmdb: fmdb, table: "MESSAGE", cvalues: [
+                                    "message_text" : newText,
+                                    "last_edited" : lastEdited
+                                ], _where: "message_id = '\(dataMessages[indexPath.row]["message_id"] as! String)'")
+                                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
+                            } catch {
+                                rollback.pointee = true
+                                print("Access database error: \(error.localizedDescription)")
+                            }
+                        })
+                    }
+                    let idx = self?.dataMessages.firstIndex(where: { $0[TypeDataMessage.message_id] as? String == dataMessages[indexPath.row][TypeDataMessage.message_id] as? String})
+                    if idx != nil{
+                        self?.dataMessages[idx!][TypeDataMessage.message_text] = newText
+                        self?.dataMessages[idx!][TypeDataMessage.last_edit] = lastEdited
+                        self?.tableChatView.reloadRows(at: [indexPath], with: .none)
+                    }
+                }
+            }
+        }
+        
+        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
+        
+        alertController.addAction(saveAction)
+        alertController.addAction(cancelAction)
+        
+        present(alertController, animated: true, completion: nil)
+    }
+    
     @objc func cancelAction() {
         DispatchQueue.main.async {
             if self.copySession {
@@ -5176,6 +5215,21 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
             }
         }
         
+        if dataMessages[indexPath.row][TypeDataMessage.last_edit] != nil && dataMessages[indexPath.row][TypeDataMessage.last_edit] as! Int64 != 0 {
+            let editedText = UILabel()
+            editedText.text = "Edited".localized()
+            editedText.font = UIFont.systemFont(ofSize: 10, weight: .medium)
+            editedText.textColor = .lightGray
+            cell.contentView.addSubview(editedText)
+            editedText.translatesAutoresizingMaskIntoConstraints = false
+            if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
+                editedText.trailingAnchor.constraint(equalTo: timeMessage.leadingAnchor, constant: -2).isActive = true
+            } else {
+                editedText.leadingAnchor.constraint(equalTo: timeMessage.trailingAnchor, constant: 2).isActive = true
+            }
+            editedText.bottomAnchor.constraint(equalTo: containerMessage.bottomAnchor).isActive = true
+        }
+        
         let messageText = UILabel()
         messageText.numberOfLines = 0
         messageText.lineBreakMode = .byWordWrapping
@@ -5237,6 +5291,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
         }
         
         let imageSticker = UIImageView()
+        var stringLS = ""
         if let attachmentFlag = dataMessages[indexPath.row]["attachment_flag"], let attachmentFlag = attachmentFlag as? String {
             if attachmentFlag == "27" || attachmentFlag == "26" { // live streaming
                 let data = textChat
@@ -5253,10 +5308,11 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
                     }
                     if let c = User.getData(pin: by) {
                         let name = c.fullName
-                        messageText.attributedText = "\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm")) \nBroadcaster: \(name)".richText()
+                        stringLS = "\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm")) \nBroadcaster: \(name)"
                     } else {
-                        messageText.attributedText = ("\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm"))").richText()
+                        stringLS = ("\(type) \nTitle: \(title) \nDescription: \(description) \nStart: \(Date(milliseconds: start).format(dateFormat: "dd/MM/yyyy HH:mm"))")
                     }
+                    messageText.attributedText = stringLS.richText()
                 }
             }
             else if attachmentFlag == "11" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as! String != "1") && (dataMessages[indexPath.row]["lock"] as? String != "2") {
@@ -5364,7 +5420,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
         }
         
         if isSearching && textSearch.count > 1 {
-            messageText.attributedText = textChat.richText(isSearching: true, textSearch: textSearch)
+            messageText.attributedText = stringLS.isEmpty ? textChat.richText(isSearching: true, textSearch: textSearch) : stringLS.richText(isSearching: true, textSearch: textSearch)
             if textChat.lowercased().contains(textSearch) {
                 countMatchesSearch += 1
             }
@@ -5694,7 +5750,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
             containerViewFile.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
             containerViewFile.bottomAnchor.constraint(equalTo:messageText.topAnchor, constant: -5).isActive = true
             containerViewFile.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
-            containerViewFile.heightAnchor.constraint(equalToConstant: 50).isActive = true
+//            containerViewFile.heightAnchor.constraint(equalToConstant: 50).isActive = true
             containerViewFile.backgroundColor = .black.withAlphaComponent(0.2)
             containerViewFile.layer.cornerRadius = 5.0
             containerViewFile.clipsToBounds = true
@@ -7159,4 +7215,5 @@ public class TypeDataMessage {
     public static let is_call_center = "is_call_center"
     public static let call_center_id = "call_center_id"
     public static let opposite_pin = "opposite_pin"
+    public static let last_edit = "last_edit"
 }