Ver Fonte

add grouping image to editor group

alqindiirsyam há 2 anos atrás
pai
commit
51f5348ba7

BIN
appbuilder-ios/DigiXLite/DigiXLite.xcworkspace/xcuserdata/akhmadalqindiirsyam.xcuserdatad/UserInterfaceState.xcuserstate


+ 427 - 115
appbuilder-ios/DigiXLite/DigiXLite/Source/View/Chat/EditorGroup.swift

@@ -93,6 +93,8 @@ public class EditorGroup: UIViewController {
     var listViewOnSection: [UIView] = []
     var fakeProgMultip = 0
     let maxFakeProgMultip = 2
+    var groupImages: [String:[ImageGrouping]] = [:]
+    var titleText: String!
     
     public override func viewDidDisappear(_ animated: Bool) {
         if self.isMovingFromParent {
@@ -123,8 +125,9 @@ public class EditorGroup: UIViewController {
         if indexPath != nil {
             let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
             let isPinned = headerRect.origin.y <= tableChatView.contentOffset.y
-            if listViewOnSection.count != 0 && listViewOnSection.count - 1 <= indexPath!.section && isPinned {
-                let headerView = listViewOnSection[currentIndexpath!.section]
+            if listViewOnSection.count != 0 && listViewOnSection.count - 1 == indexPath!.section && isPinned {
+                let sect = listViewOnSection.count - 1 < currentIndexpath!.section ? listViewOnSection.count - 1 : currentIndexpath!.section
+                let headerView = listViewOnSection[sect]
                 headerView.isHidden = true
             }
         }
@@ -439,6 +442,7 @@ public class EditorGroup: UIViewController {
                 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 chat_id='\(dataTopic["chat_id"] as! String)' order by server_date asc"
             }
             if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: query) {
+                var tempImages: [ImageGrouping] = []
                 while cursorData.next() {
                     var row: [String: Any?] = [:]
                     row["message_id"] = cursorData.string(forColumnIndex: 0)
@@ -482,16 +486,60 @@ public class EditorGroup: UIViewController {
                         }
                     }
                     row["chat_date"] = chatDate(stringDate: row["server_date"] as! String)
+                    
+                    if (dataMessages.count == 0 || dataMessages.last!["f_pin"] as! String == row["f_pin"] as! String) && tempImages.count <= 30 && row["image_id"] != nil && !(row["image_id"] as! String).isEmpty && (row["message_text"] as! String).isEmpty && (row["reff_id"] as! String).isEmpty && (row["credential"] as! String) != "1" && (row["read_receipts"] as! String) != "8" {
+                        if tempImages.count != 0 && getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(tempImages.last!.time)!), end: Date.init(milliseconds: Int64(row["server_date"] as! String)!))/60 >= 11 {
+                            if tempImages.count >= 4 {
+                                groupImages[tempImages[0].messageId] = tempImages
+                                if let idxTemp = dataMessages.firstIndex(where: { $0["message_id"] as! String == tempImages[0].messageId }) {
+                                    for _ in 1..<tempImages.count {
+                                        dataMessages.remove(at: idxTemp + 1)
+                                    }
+                                }
+                            }
+                            tempImages.removeAll()
+                        }
+                        tempImages.append(ImageGrouping(messageId: row["message_id"] as! String, thumbId: row["thumb_id"] as! String, imageId: row["image_id"] as! String, status: row["status"] as! String, time: row["server_date"] as! String, lPin: row["l_pin"] as! String, dataMessage: row, dataPerson: [:], dataGroup: dataGroup, dataTopic: dataTopic))
+                    } else if tempImages.count >= 4 {
+                        groupImages[tempImages[0].messageId] = tempImages
+                        if let idxTemp = dataMessages.firstIndex(where: { $0["message_id"] as! String == tempImages[0].messageId }) {
+                            for _ in 1..<tempImages.count {
+                                dataMessages.remove(at: idxTemp + 1)
+                            }
+                        }
+                        tempImages.removeAll()
+                    } else if tempImages.count != 0 {
+                        tempImages.removeAll()
+                    }
                     dataMessages.append(row)
                 }
 //                if isHistoryCC {
 //                    dataMessages.remove(at: 0)
 //                }
+                if tempImages.count >= 4 {
+                    if tempImages.count > 30 {
+                        tempImages.removeSubrange(30..<tempImages.count)
+                    }
+                    groupImages[tempImages[0].messageId] = tempImages
+                    if let idxTemp = dataMessages.firstIndex(where: { $0["message_id"] as! String == tempImages[0].messageId }) {
+                        for _ in 1..<tempImages.count {
+                            dataMessages.remove(at: idxTemp + 1)
+                        }
+                    }
+                }
                 cursorData.close()
             }
         })
     }
     
+    func getSecondsDifferenceFromTwoDates(start: Date, end: Date) -> Int {
+        let diff = Int(end.timeIntervalSince1970 - start.timeIntervalSince1970)
+
+        let hours = diff / 3600
+        let seconds = (diff - hours * 3600)
+        return seconds
+    }
+    
     private func getRealStatus(messageId: String) -> String {
         var status = "1"
         Database.shared.database?.inTransaction({ (fmdb, rollback) in
@@ -580,6 +628,7 @@ public class EditorGroup: UIViewController {
             titleNavigation.font = UIFont.systemFont(ofSize: 12).bold
             
             navigationItem.titleView = viewAppBar
+            titleText = titleNavigation.text
         } else {
             searchBar = UISearchBar()
             searchBar.autocapitalizationType = .none
@@ -1528,7 +1577,7 @@ public class EditorGroup: UIViewController {
                             }
                         }
                     }
-                }  else if dataMessages[index]["video_id"] as? String != nil && !((dataMessages[index]["video_id"] as? String)!.isEmpty){
+                } else if dataMessages[index]["video_id"] as? String != nil && !((dataMessages[index]["video_id"] as? String)!.isEmpty){
                     Download().startHTTP(forKey: dataMessages[index]["video_id"] as! String) { (name, progress) in
                         guard progress == 100 else {
                             return
@@ -1555,7 +1604,8 @@ public class EditorGroup: UIViewController {
                             }
                         }
                     }
-                } else if dataMessages[index]["file_id"] as? String != nil && !((dataMessages[index]["file_id"] as? String)!.isEmpty) {
+                }
+                else if dataMessages[index]["file_id"] as? String != nil && !((dataMessages[index]["file_id"] as? String)!.isEmpty) {
                     Download().startHTTP(forKey: dataMessages[index]["file_id"] as! String) { (name, progress) in
                         guard progress == 100 else {
                             return
@@ -1601,7 +1651,7 @@ public class EditorGroup: UIViewController {
                 }
             }
             let indexPathFirst = tableChatView.indexPathsForVisibleRows?.first
-            if indexPathFirst != nil && listViewOnSection.count != 0 && listViewOnSection.count - 1 >= indexPathFirst!.section{
+            if indexPathFirst != nil && listViewOnSection.count != 0 && listViewOnSection.count - 1 >= indexPathFirst!.section {
                 let headerView = listViewOnSection[indexPathFirst!.section]
                 if headerView.isHidden {
                     headerView.isHidden = false
@@ -1707,9 +1757,9 @@ extension EditorGroup: UIDocumentPickerDelegate, DocumentPickerDelegate, QLPrevi
             if !FileManager.default.fileExists(atPath: fileURL.path) {
                 do {
                     try dataFile.write(to: fileURL)
-                    //print(("file saved")
+                    //print("file saved")
                 } catch {
-                    //print(("error saving file:", error)
+                    //print("error saving file:", error)
                 }
             }
             sendChat(message_text: "\(originaFileName)|", attachment_flag: "6", file_id: renamedNameFile, viewController: self)
@@ -2442,6 +2492,15 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
             let buttonGesture = UITapGestureRecognizer(target: self, action: #selector(sessionAction))
             button.isUserInteractionEnabled = true
             button.addGestureRecognizer(buttonGesture)
+            
+            let selectedMessage = dataMessages.filter({ $0["isSelected"] as! Bool == true })
+            if selectedMessage.count > 0 {
+                for i in 0..<selectedMessage.count {
+                    if let isGroupingImages = groupImages[selectedMessage[i]["message_id"] as! String] {
+                        title.text = "\(countSelected + (isGroupingImages.count - 1)) " + "Selected".localized()
+                    }
+                }
+            }
         } else {
             buttonUp = UIButton()
             container.addSubview(buttonUp)
@@ -2526,11 +2585,20 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
             }
             cancelAction()
         } else if forwardSession {
-            let dataMessages = self.dataMessages.filter({ $0["isSelected"] as! Bool == true })
+            var dataMessages = self.dataMessages.filter({ $0["isSelected"] as! Bool == true })
             let countSelected = dataMessages.count
             if countSelected == 0 {
                 return
             }
+            for i in 0..<countSelected {
+                if let groupingImages = groupImages[dataMessages[i]["message_id"] as! String] {
+                    var tempData = dataMessages
+                    tempData.remove(at: 0)
+                    var dataMessageInGrouping = (groupImages[dataMessages[i]["message_id"] as! String]!).map({ $0.dataMessage })
+                    tempData.insert(contentsOf: dataMessageInGrouping, at: i)
+                    dataMessages = tempData
+                }
+            }
             contactChatNav.modalPresentationStyle = .custom
             contactChatNav.navigationBar.tintColor = .white
             contactChatNav.navigationBar.barTintColor = .mainColor
@@ -2568,7 +2636,8 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
             let idMe = UserDefaults.standard.string(forKey: "me") as String?
             let dataFilterFpin = dataMessages.filter({ $0["f_pin"] as? String != idMe})
             let dataFilterLock = dataMessages.filter({ $0["lock"] as? String == "1"})
-            if dataFilterFpin.count == 0 && dataFilterLock.count == 0 {
+            let statusDataRead = dataMessages.filter({ Int($0["status"] as! String)! >= 4})
+            if dataFilterFpin.count == 0 && dataFilterLock.count == 0 && statusDataRead.count == 0 {
                 if let action = self.actionDelete(for: "everyone", title: "Delete".localized() + " \(countSelected) " + "For Everyone".localized(), dataMessages: dataMessages) {
                     alertController.addAction(action)
                 }
@@ -2677,16 +2746,53 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
         return UIAlertAction(title: title, style: .destructive) { [unowned self] _ in
             for i in 0..<dataMessages.count {
                 if (type == "me") {
-                    self.deleteMessage(l_pin: dataGroup["group_id"] as! String, message_id: dataMessages[i]["message_id"] as! String, scope: "4", type: "1", chat: dataTopic["chat_id"] as! String)
-                    let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[i]["message_id"] as? String})
-                    if idx != nil {
-                        self.dataMessages.remove(at: idx!)
-                        if (idx == self.dataMessages.count - 1) {
-                            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
+                    if let groupingImages = groupImages[dataMessages[i]["message_id"] as! String] {
+                        for i in 0..<groupingImages.count {
+                            self.deleteMessage(l_pin: dataGroup["group_id"] as! String, message_id: groupingImages[i].messageId, scope: "4", type: "1", chat: dataTopic["chat_id"] as! String)
+                            let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == groupingImages[i].messageId })
+                            if idx != nil {
+                                self.dataMessages.remove(at: idx!)
+                                if (idx == self.dataMessages.count - 1) {
+                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
+                                }
+                                for i in 0..<dataDates.count {
+                                    if self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[i] }).count == 0 {
+                                        dataDates.remove(at: i)
+                                    }
+                                }
+                            }
                         }
-                        for i in 0..<dataDates.count {
-                            if self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[i] }).count == 0 {
-                                dataDates.remove(at: i)
+                        self.groupImages.removeValue(forKey: groupingImages[0].messageId)
+                    } else {
+                        if let groupingImages = groupImages[dataMessages[i]["message_id"] as! String] {
+                            for i in 0..<groupingImages.count {
+                                self.deleteMessage(l_pin: dataGroup["group_id"] as! String, message_id: groupingImages[i].messageId, scope: "4", type: "2", chat: dataTopic["chat_id"] as! String)
+                                let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == groupingImages[i].messageId})
+                                if idx != nil {
+                                    self.dataMessages[idx!]["lock"] = "1"
+                                    self.dataMessages[idx!]["attachment_flag"] = "0"
+                                    self.dataMessages[idx!]["reff_id"] = ""
+                                }
+                            }
+                            if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == groupingImages[0].messageId}) {
+                                var dataMessageInGrouping = (groupImages[dataMessages[i]["message_id"] as! String]!).map({ $0.dataMessage })
+                                dataMessageInGrouping.remove(at: 0)
+                                self.dataMessages.insert(contentsOf: dataMessageInGrouping, at: idx+1)
+                                self.groupImages.removeValue(forKey: groupingImages[0].messageId)
+                            }
+                        } else {
+                            self.deleteMessage(l_pin: dataGroup["group_id"] as! String, message_id: dataMessages[i]["message_id"] as! String, scope: "4", type: "1", chat: dataTopic["chat_id"] as! String)
+                            let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[i]["message_id"] as? String})
+                            if idx != nil {
+                                self.dataMessages.remove(at: idx!)
+                                if (idx == self.dataMessages.count - 1) {
+                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
+                                }
+                                for i in 0..<dataDates.count {
+                                    if self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[i] }).count == 0 {
+                                        dataDates.remove(at: i)
+                                    }
+                                }
                             }
                         }
                     }
@@ -3009,6 +3115,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
         
         let containerMessage = UIView()
         
+        let messageIdChat = (dataMessages[indexPath.row]["message_id"] as? String) ?? ""
         let thumbChat = dataMessages[indexPath.row]["thumb_id"] as! String
         let imageChat = dataMessages[indexPath.row]["image_id"] as! String
         let videoChat = dataMessages[indexPath.row]["video_id"] as! String
@@ -3260,8 +3367,8 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
             nameSender.textColor = .mainColor
         }
         
+        let imageStared = UIImageView()
         if dataMessages[indexPath.row]["is_stared"] as? String == "1" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as! String == "0") {
-            let imageStared = UIImageView()
             cellMessage.contentView.addSubview(imageStared)
             imageStared.translatesAutoresizingMaskIntoConstraints = false
             if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
@@ -3443,104 +3550,215 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
         let containerViewFile = UIView()
         
         if (thumbChat != "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as! String != "1")) {
-            topMarginText.constant = topMarginText.constant + ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.height
-            
-            containerMessage.addSubview(imageThumb)
-            imageThumb.translatesAutoresizingMaskIntoConstraints = false
-            let data = queryMessageReply(message_id: reffChat)
-            if (data.count == 0) {
-                imageThumb.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 32).isActive = true
-            }
-            imageThumb.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
-            imageThumb.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
-            imageThumb.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
-            imageThumb.widthAnchor.constraint(equalToConstant: ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.width).isActive = true
-            imageThumb.layer.cornerRadius = 5.0
-            imageThumb.clipsToBounds = true
-            imageThumb.contentMode = .scaleAspectFill
-            
-            let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
-            let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
-            let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
-            if let dirPath = paths.first {
-                let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumbChat)
-                let image    = UIImage(contentsOfFile: thumbURL.path)
-//                let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
-                imageThumb.image = image
-                
-                let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(videoChat)
-                let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(imageChat)
-                if !FileManager.default.fileExists(atPath: imageURL.path) || !FileManager.default.fileExists(atPath: videoURL.path){
-                    let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
+            if let listImages = groupImages[messageIdChat] {
+                timeMessage.isHidden = true
+                statusMessage.isHidden = true
+                imageStared.isHidden = true
+                topMarginText.constant = topMarginText.constant + 225
+                let listImageThumb: [UIImageView] = [UIImageView(), UIImageView(), UIImageView(), UIImageView()]
+                for i in 0..<4 {
+                    containerMessage.addSubview(listImageThumb[i])
+                    listImageThumb[i].layer.cornerRadius = 5.0
+                    listImageThumb[i].clipsToBounds = true
+                    listImageThumb[i].contentMode = .scaleAspectFill
+                    let widthHeightImage: CGFloat = 120
+                    switch i {
+                        case 0:
+                            listImageThumb[i].anchor(top: containerMessage.topAnchor, left: containerMessage.leftAnchor, paddingTop: 5, paddingLeft: 5, width: widthHeightImage, height: widthHeightImage)
+                        case 1:
+                            listImageThumb[i].anchor(top: containerMessage.topAnchor, left: listImageThumb[0].rightAnchor, right: containerMessage.rightAnchor, paddingTop: 5, paddingLeft: 5, paddingRight: 5, width: widthHeightImage, height: widthHeightImage)
+                        case 2:
+                            listImageThumb[i].anchor(left: containerMessage.leftAnchor, bottom: containerMessage.bottomAnchor, paddingLeft: 5, paddingBottom: 5, width: widthHeightImage, height: widthHeightImage)
+                        default:
+                            listImageThumb[i].anchor(left: listImageThumb[2].rightAnchor, bottom: containerMessage.bottomAnchor, right: containerMessage.rightAnchor, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, width: widthHeightImage, height: widthHeightImage)
+                    }
+                    let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
+                    let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
+                    let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
+                    if let dirPath = paths.first {
+                        let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(listImages[i].thumbId)
+                        let image    = UIImage(contentsOfFile: thumbURL.path)
+//                        let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
+                        listImageThumb[i].image = image
+
+                        let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(listImages[i].imageId)
+                        if !FileManager.default.fileExists(atPath: imageURL.path) {
+                            let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
+                            let blurEffectView = UIVisualEffectView(effect: blurEffect)
+                            blurEffectView.frame = CGRect(x: 0, y: 0, width: listImageThumb[i].frame.size.width, height: listImageThumb[i].frame.size.height)
+                            blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+                            listImageThumb[i].addSubview(blurEffectView)
+                        }
+
+                    }
+                    let containerTimeStatus = UIView()
+                    listImageThumb[i].addSubview(containerTimeStatus)
+                    containerTimeStatus.anchor(bottom: listImageThumb[i].bottomAnchor, right: listImageThumb[i].rightAnchor, height: 15)
+                    let widthcontainerTimeStatus = containerTimeStatus.widthAnchor.constraint(equalToConstant: 50)
+                    widthcontainerTimeStatus.isActive = true
+                    containerTimeStatus.layer.cornerRadius = 5.0
+                    containerTimeStatus.layer.masksToBounds = true
+                    containerTimeStatus.backgroundColor = .black.withAlphaComponent(0.15)
+                    
+                    let timeInImage = UILabel()
+                    containerTimeStatus.addSubview(timeInImage)
+                    let date = Date(milliseconds: Int64(listImages[i].time) ?? 100)
+                    let formatter = DateFormatter()
+                    formatter.dateFormat = "HH:mm"
+                    formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
+                    timeInImage.text = formatter.string(from: date as Date)
+                    timeInImage.textColor = .white
+                    timeInImage.font = UIFont.systemFont(ofSize: 10, weight: .medium)
+                    
+                    if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
+                        let statusInImage = UIImageView()
+                        containerTimeStatus.addSubview(statusInImage)
+                        statusInImage.anchor(right: containerTimeStatus.rightAnchor, centerY: containerTimeStatus.centerYAnchor, width: 15, height: 15)
+                        if listImages[i].status == "1" || listImages[i].status == "2"  {
+                            statusInImage.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: DigiX.self), with: nil)!.withTintColor(UIColor.white)
+                        } else if listImages[i].status == "3" {
+                            statusInImage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: DigiX.self), with: nil)!.withTintColor(UIColor.white)
+                        } else {
+                            statusInImage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: DigiX.self), with: nil)!.withTintColor(UIColor.systemBlue)
+                        }
+                        timeInImage.anchor(right: statusInImage.leftAnchor, centerY: containerTimeStatus.centerYAnchor, height: 15)
+                    } else {
+                        timeInImage.anchor(right: containerTimeStatus.rightAnchor, paddingRight: 5, centerY: containerTimeStatus.centerYAnchor, height: 15)
+                        widthcontainerTimeStatus.constant = widthcontainerTimeStatus.constant - 10
+                    }
+                    
+                    if listImages[i].dataMessage["is_stared"] as? String == "1" {
+                        let iconStar = UIImageView()
+                        containerTimeStatus.addSubview(iconStar)
+                        iconStar.anchor(right: timeInImage.leftAnchor, paddingRight: 2, centerY: containerTimeStatus.centerYAnchor, width: 15, height: 15)
+                        widthcontainerTimeStatus.constant = widthcontainerTimeStatus.constant + 15
+                        iconStar.image = UIImage(systemName: "star.fill")
+                        iconStar.tintColor = .white
+                    }
+                    
+                    if !copySession && !forwardSession && !deleteSession {
+                        let objectTap = ObjectGesture(target: self, action: #selector(imageGroupingTapped(_:)))
+                        listImageThumb[i].isUserInteractionEnabled = true
+                        listImageThumb[i].addGestureRecognizer(objectTap)
+                        objectTap.indexImageTapped = i
+                        objectTap.listImageFromGrouping = listImages
+                        objectTap.isInitiator = dataMessages[indexPath.row]["f_pin"] as? String == idMe
+                    }
+                }
+                if  listImages.count > 4 {
+                    let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.dark)
                     let blurEffectView = UIVisualEffectView(effect: blurEffect)
-                    blurEffectView.frame = CGRect(x: 0, y: 0, width: imageThumb.frame.size.width, height: imageThumb.frame.size.height)
+                    blurEffectView.frame = CGRect(x: 0, y: 0, width: listImageThumb[3].frame.size.width, height: listImageThumb[3].frame.size.height)
                     blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
-                    imageThumb.addSubview(blurEffectView)
-                    if !imageChat.isEmpty {
-                        let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 50, weight: .bold, scale: .default)))
+                    listImageThumb[3].addSubview(blurEffectView)
+                    
+                    let countRestImages = UILabel()
+                    listImageThumb[3].addSubview(countRestImages)
+                    countRestImages.anchor(centerX: listImageThumb[3].centerXAnchor, centerY: listImageThumb[3].centerYAnchor)
+                    countRestImages.font = UIFont.systemFont(ofSize: 30, weight: .medium)
+                    countRestImages.text = "+\(listImages.count - 3)"
+                    countRestImages.textColor = .white
+                }
+            } else {
+                topMarginText.constant = topMarginText.constant + ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.height
+                
+                containerMessage.addSubview(imageThumb)
+                imageThumb.translatesAutoresizingMaskIntoConstraints = false
+                let data = queryMessageReply(message_id: reffChat)
+                if (data.count == 0) {
+                    imageThumb.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 32).isActive = true
+                }
+                imageThumb.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
+                imageThumb.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
+                imageThumb.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
+                imageThumb.widthAnchor.constraint(equalToConstant: ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.width).isActive = true
+                imageThumb.layer.cornerRadius = 5.0
+                imageThumb.clipsToBounds = true
+                imageThumb.contentMode = .scaleAspectFill
+                
+                let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
+                let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
+                let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
+                if let dirPath = paths.first {
+                    let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumbChat)
+                    let image    = UIImage(contentsOfFile: thumbURL.path)
+    //                let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
+                    imageThumb.image = image
+                    
+                    let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(videoChat)
+                    let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(imageChat)
+                    if !FileManager.default.fileExists(atPath: imageURL.path) || !FileManager.default.fileExists(atPath: videoURL.path){
+                        let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
+                        let blurEffectView = UIVisualEffectView(effect: blurEffect)
+                        blurEffectView.frame = CGRect(x: 0, y: 0, width: imageThumb.frame.size.width, height: imageThumb.frame.size.height)
+                        blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
                         imageThumb.addSubview(blurEffectView)
-                        imageThumb.addSubview(imageDownload)
-                        imageDownload.tintColor = .black.withAlphaComponent(0.3)
-                        imageDownload.translatesAutoresizingMaskIntoConstraints = false
-                        imageDownload.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
-                        imageDownload.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
+                        if !imageChat.isEmpty {
+                            let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 50, weight: .bold, scale: .default)))
+                            imageThumb.addSubview(blurEffectView)
+                            imageThumb.addSubview(imageDownload)
+                            imageDownload.tintColor = .black.withAlphaComponent(0.3)
+                            imageDownload.translatesAutoresizingMaskIntoConstraints = false
+                            imageDownload.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
+                            imageDownload.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
+                        }
                     }
+                    
                 }
                 
-            }
-            
-            if (videoChat != "") {
-                let imagePlay = UIImageView(image: UIImage(systemName: "play.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .bold, scale: .default))?.imageWithInsets(insets: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))?.withTintColor(.white))
-                imagePlay.circle()
-                imageThumb.addSubview(imagePlay)
-                imagePlay.backgroundColor = .black.withAlphaComponent(0.3)
-                imagePlay.translatesAutoresizingMaskIntoConstraints = false
-                imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
-                imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
-            }
-            
-            if (dataMessages[indexPath.row]["progress"] as! Double != 100.0 && dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
-                let container = UIView()
-                imageThumb.addSubview(container)
-                container.translatesAutoresizingMaskIntoConstraints = false
-                container.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
-                container.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
-                container.widthAnchor.constraint(equalToConstant: 30).isActive = true
-                container.heightAnchor.constraint(equalToConstant: 30).isActive = true
-                container.backgroundColor = .white.withAlphaComponent(0.1)
-                let circlePath = UIBezierPath(arcCenter: CGPoint(x: 10, y: 20), radius: 15, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
-                let trackShape = CAShapeLayer()
-                trackShape.path = circlePath.cgPath
-                trackShape.fillColor = UIColor.black.withAlphaComponent(0.3).cgColor
-                trackShape.lineWidth = 3
-                trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
-                container.backgroundColor = .clear
-                container.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
-                container.layer.addSublayer(shapeLoading)
-                let imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
-                imageupload.tintColor = .white
-                container.addSubview(imageupload)
-                imageupload.translatesAutoresizingMaskIntoConstraints = false
-                imageupload.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
-                imageupload.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
-                imageupload.widthAnchor.constraint(equalToConstant: 20).isActive = true
-                imageupload.heightAnchor.constraint(equalToConstant: 20).isActive = true
-            }
-            
-            if !copySession && !forwardSession && !deleteSession {
-                let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
-                imageThumb.isUserInteractionEnabled = true
-                imageThumb.addGestureRecognizer(objectTap)
-                objectTap.image_id = imageChat
-                objectTap.video_id = videoChat
-                objectTap.imageView = imageThumb
-                objectTap.indexPath = indexPath
+                if (videoChat != "") {
+                    let imagePlay = UIImageView(image: UIImage(systemName: "play.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .bold, scale: .default))?.imageWithInsets(insets: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))?.withTintColor(.white))
+                    imagePlay.circle()
+                    imageThumb.addSubview(imagePlay)
+                    imagePlay.backgroundColor = .black.withAlphaComponent(0.3)
+                    imagePlay.translatesAutoresizingMaskIntoConstraints = false
+                    imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
+                    imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
+                }
+                
+                if (dataMessages[indexPath.row]["progress"] as! Double != 100.0 && dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
+                    let container = UIView()
+                    imageThumb.addSubview(container)
+                    container.translatesAutoresizingMaskIntoConstraints = false
+                    container.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
+                    container.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
+                    container.widthAnchor.constraint(equalToConstant: 30).isActive = true
+                    container.heightAnchor.constraint(equalToConstant: 30).isActive = true
+                    container.backgroundColor = .white.withAlphaComponent(0.1)
+                    let circlePath = UIBezierPath(arcCenter: CGPoint(x: 10, y: 20), radius: 15, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
+                    let trackShape = CAShapeLayer()
+                    trackShape.path = circlePath.cgPath
+                    trackShape.fillColor = UIColor.black.withAlphaComponent(0.3).cgColor
+                    trackShape.lineWidth = 3
+                    trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
+                    container.backgroundColor = .clear
+                    container.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
+                    container.layer.addSublayer(shapeLoading)
+                    let imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
+                    imageupload.tintColor = .white
+                    container.addSubview(imageupload)
+                    imageupload.translatesAutoresizingMaskIntoConstraints = false
+                    imageupload.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
+                    imageupload.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
+                    imageupload.widthAnchor.constraint(equalToConstant: 20).isActive = true
+                    imageupload.heightAnchor.constraint(equalToConstant: 20).isActive = true
+                }
+                
+                if !copySession && !forwardSession && !deleteSession {
+                    let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
+                    imageThumb.isUserInteractionEnabled = true
+                    imageThumb.addGestureRecognizer(objectTap)
+                    objectTap.image_id = imageChat
+                    objectTap.video_id = videoChat
+                    objectTap.imageView = imageThumb
+                    objectTap.indexPath = indexPath
+                }
             }
         }
         
@@ -3941,6 +4159,94 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
         return cellMessage
     }
     
+    @objc func imageGroupingTapped(_ sender: ObjectGesture) {
+        let listGroupingImages = ListGroupImages()
+        listGroupingImages.imageTapped = sender.indexImageTapped
+        listGroupingImages.listGroupingImages = sender.listImageFromGrouping
+        listGroupingImages.titleName = titleText
+        listGroupingImages.isInitiator = sender.isInitiator
+        listGroupingImages.isPersonal = false
+        listGroupingImages.updateEditor = { [self] updatedData, replyData, isUpdateDelete in
+            if replyData.count == 0 {
+                if updatedData.count != 0 && !isUpdateDelete {
+                    groupImages[sender.listImageFromGrouping[0].messageId] = updatedData
+                } else if updatedData.count > 0 {
+                    let deletedForEveryoneData = updatedData.filter({ $0.dataMessage["lock"] as? String == "1" })
+                    if deletedForEveryoneData.count != 0 {
+                        if groupImages[sender.listImageFromGrouping[0].messageId] != nil {
+                            var dataWillEmpty = updatedData
+                            while dataWillEmpty.count > 0 {
+                                if let lastIdx = dataWillEmpty.lastIndex(where: { $0.dataMessage["lock"] as? String == "1" }) {
+                                    if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.listImageFromGrouping[0].messageId }) {
+                                        if dataWillEmpty[lastIdx].messageId == sender.listImageFromGrouping[0].messageId {
+                                            self.dataMessages.remove(at: idx)
+                                            self.dataMessages.insert(dataWillEmpty[lastIdx].dataMessage, at: idx)
+                                        } else {
+                                            self.dataMessages.insert(dataWillEmpty[lastIdx].dataMessage, at: idx + 1)
+                                        }
+                                        let subData = Array(updatedData[lastIdx+1..<dataWillEmpty.count])
+                                        if subData.count >= 4 {
+                                            groupImages[subData[0].messageId] = subData
+                                            self.dataMessages.insert(subData[0].dataMessage, at: lastIdx + 1)
+                                        } else {
+                                            if subData.count > 0 {
+                                                self.dataMessages.insert(contentsOf: subData.map({ $0.dataMessage }), at: idx + (dataWillEmpty[lastIdx].messageId == sender.listImageFromGrouping[0].messageId ? 1 : 2))
+                                            }
+                                        }
+                                    }
+                                    dataWillEmpty.removeSubrange(lastIdx..<dataWillEmpty.count)
+                                } else if dataWillEmpty.count >= 4 {
+                                    groupImages[dataWillEmpty[0].messageId] = dataWillEmpty
+                                    dataWillEmpty.removeAll()
+                                } else {
+                                    if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.listImageFromGrouping[0].messageId }) {
+                                        self.dataMessages.remove(at: idx)
+                                        self.dataMessages.insert(contentsOf: dataWillEmpty.map({ $0.dataMessage }), at: idx)
+                                        groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
+                                    }
+                                    dataWillEmpty.removeAll()
+                                }
+                            }
+                        } else {
+                            
+                        }
+                    } else {
+                        if updatedData.count >= 4 {
+                            if updatedData[0].messageId == sender.listImageFromGrouping[0].messageId {
+                                groupImages[sender.listImageFromGrouping[0].messageId] = updatedData
+                            } else {
+                                if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.listImageFromGrouping[0].messageId }) {
+                                    self.dataMessages.remove(at: idx)
+                                    self.dataMessages.insert(updatedData[0].dataMessage, at: idx)
+                                    groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
+                                    groupImages[updatedData[0].messageId] = updatedData
+                                }
+                            }
+                        } else {
+                            if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.listImageFromGrouping[0].messageId }) {
+                                groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
+                                self.dataMessages.remove(at: idx)
+                                let dataMessageInGrouping = updatedData.map({ $0.dataMessage })
+                                self.dataMessages.insert(contentsOf: dataMessageInGrouping, at: idx)
+                            }
+                        }
+                    }
+                } else {
+                    groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
+                    if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.listImageFromGrouping[0].messageId }) {
+                        self.dataMessages.remove(at: idx)
+                    }
+                }
+                DispatchQueue.main.async { [self] in
+                    tableChatView.reloadData()
+                }
+            } else if replyData.count != 0 {
+                handleReply(indexPath: IndexPath(row: 0, section: 0), dataMessagesImage: replyData)
+            }
+        }
+        self.navigationController?.pushViewController(listGroupingImages, animated: true)
+    }
+    
     @objc func tapAck(_ sender: ObjectGesture) {
         let indexPath = sender.indexPath
         let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath.section]})
@@ -4351,9 +4657,14 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
 //        return UISwipeActionsConfiguration(actions: [action])
 //    }
     
-    private func handleReply(indexPath: IndexPath) {
+    private func handleReply(indexPath: IndexPath, dataMessagesImage: [String: Any?] = [:]) {
         self.deleteReplyView()
-        self.textFieldSend.becomeFirstResponder()
+        var dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath.section]})
+        if dataMessages.count != 0 {
+            dataMessages = [dataMessagesImage]
+        } else {
+            self.textFieldSend.becomeFirstResponder()
+        }
         self.reffId = dataMessages[indexPath.row]["message_id"] as? String
         UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
             self.constraintTopTextField.constant = self.constraintTopTextField.constant + 50
@@ -4597,8 +4908,9 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
             if indexPath != nil {
                 let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
                 let isPinned = headerRect.origin.y <= scrollView.contentOffset.y
-                if listViewOnSection.count != 0 && listViewOnSection.count - 1 <= indexPath!.section && isPinned {
-                    let headerView = listViewOnSection[currentIndexpath!.section]
+                if listViewOnSection.count != 0 && listViewOnSection.count - 1 == indexPath!.section && isPinned {
+                    let sect = listViewOnSection.count - 1 < currentIndexpath!.section ? listViewOnSection.count - 1 : currentIndexpath!.section
+                    let headerView = listViewOnSection[sect]
                     headerView.isHidden = true
                 }
             }

+ 37 - 12
appbuilder-ios/DigiXLite/DigiXLite/Source/View/Chat/EditorPersonal.swift

@@ -143,8 +143,9 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         if indexPath != nil {
             let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
             let isPinned = headerRect.origin.y <= tableChatView.contentOffset.y
-            if listViewOnSection.count != 0 && listViewOnSection.count - 1 <= indexPath!.section && isPinned {
-                let headerView = listViewOnSection[currentIndexpath!.section]
+            if listViewOnSection.count != 0 && listViewOnSection.count - 1 == indexPath!.section && isPinned {
+                let sect = listViewOnSection.count - 1 < currentIndexpath!.section ? listViewOnSection.count - 1 : currentIndexpath!.section
+                let headerView = listViewOnSection[sect]
                 headerView.isHidden = true
             }
         }
@@ -1139,7 +1140,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                             }
                             tempImages.removeAll()
                         }
-                        tempImages.append(ImageGrouping(messageId: row["message_id"] as! String, thumbId: row["thumb_id"] as! String, imageId: row["image_id"] as! String, status: row["status"] as! String, time: row["server_date"] as! String, lPin: row["l_pin"] as! String, dataMessage: row, dataPerson: dataPerson))
+                        tempImages.append(ImageGrouping(messageId: row["message_id"] as! String, thumbId: row["thumb_id"] as! String, imageId: row["image_id"] as! String, status: row["status"] as! String, time: row["server_date"] as! String, lPin: row["l_pin"] as! String, dataMessage: row, dataPerson: dataPerson, dataGroup: [:], dataTopic: [:]))
                     } else if tempImages.count >= 4 {
                         groupImages[tempImages[0].messageId] = tempImages
                         if let idxTemp = dataMessages.firstIndex(where: { $0["message_id"] as! String == tempImages[0].messageId }) {
@@ -1767,7 +1768,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
             if !onGoingCC.isEmpty {
                 let officer = onGoingCC.isEmpty ? "" : onGoingCC.components(separatedBy: ",")[1]
                 if message.getBody(key: CoreMessage_TMessageKey.F_PIN) != officer {
-//                    //print(("RESET TIMER")
+                    //print("RESET TIMER")
 //                    timeoutCC.invalidate()
 //                    timeoutCC = Timer.scheduledTimer(withTimeInterval: 30.0, repeats: false, block: {_ in
 //                        let imageView = UIImageView(image: UIImage(systemName: "info.circle"))
@@ -2438,6 +2439,15 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                 self.tableChatView.endUpdates()
             }
         }
+//        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
+//            self.timerFakeProgress = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
+//                self.updateProgress(row as [AnyHashable : Any])
+//                if self.fakeProgMultip == self.maxFakeProgMultip {
+//                    self.timerFakeProgress?.invalidate()
+//                    self.fakeProgMultip = 0
+//                }
+//            }
+//        }
     }
     
     @objc func ccAction(sender: UIButton) {
@@ -3087,9 +3097,9 @@ extension EditorPersonal: UIDocumentPickerDelegate, DocumentPickerDelegate, QLPr
             if !FileManager.default.fileExists(atPath: fileURL.path) {
                 do {
                     try dataFile.write(to: fileURL)
-                    //print(("file saved")
+                    //print("file saved")
                 } catch {
-                    //print(("error saving file:", error)
+                    //print("error saving file:", error)
                 }
             }
             sendChat(message_text: "\(originaFileName)|", attachment_flag: "6", file_id: renamedNameFile, viewController: self)
@@ -4699,8 +4709,8 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
             timeMessage.leadingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: 8).isActive = true
         }
         
+        let imageStared = UIImageView()
         if dataMessages[indexPath.row]["is_stared"] as? String == "1" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as! String == "0") {
-            let imageStared = UIImageView()
             cell.contentView.addSubview(imageStared)
             imageStared.translatesAutoresizingMaskIntoConstraints = false
             if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
@@ -4939,6 +4949,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
             if let listImages = groupImages[messageIdChat] {
                 timeMessage.isHidden = true
                 statusMessage.isHidden = true
+                imageStared.isHidden = true
                 topMarginText.constant = topMarginText.constant + 225
                 let listImageThumb: [UIImageView] = [UIImageView(), UIImageView(), UIImageView(), UIImageView()]
                 for i in 0..<4 {
@@ -5009,8 +5020,18 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
                         timeInImage.anchor(right: statusInImage.leftAnchor, centerY: containerTimeStatus.centerYAnchor, height: 15)
                     } else {
                         timeInImage.anchor(right: containerTimeStatus.rightAnchor, paddingRight: 5, centerY: containerTimeStatus.centerYAnchor, height: 15)
-                        widthcontainerTimeStatus.constant = 40
+                        widthcontainerTimeStatus.constant = widthcontainerTimeStatus.constant - 10
                     }
+                    
+                    if listImages[i].dataMessage["is_stared"] as? String == "1" {
+                        let iconStar = UIImageView()
+                        containerTimeStatus.addSubview(iconStar)
+                        iconStar.anchor(right: timeInImage.leftAnchor, paddingRight: 2, centerY: containerTimeStatus.centerYAnchor, width: 15, height: 15)
+                        widthcontainerTimeStatus.constant = widthcontainerTimeStatus.constant + 15
+                        iconStar.image = UIImage(systemName: "star.fill")
+                        iconStar.tintColor = .white
+                    }
+                    
                     if !copySession && !forwardSession && !deleteSession {
                         let objectTap = ObjectGesture(target: self, action: #selector(imageGroupingTapped(_:)))
                         listImageThumb[i].isUserInteractionEnabled = true
@@ -6332,8 +6353,9 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource {
             if indexPath != nil {
                 let headerRect = tableChatView.rectForHeader(inSection: indexPath!.section)
                 let isPinned = headerRect.origin.y <= scrollView.contentOffset.y
-                if listViewOnSection.count != 0 && listViewOnSection.count - 1 <= indexPath!.section && isPinned {
-                    let headerView = listViewOnSection[currentIndexpath!.section]
+                if listViewOnSection.count != 0 && listViewOnSection.count - 1 == indexPath!.section && isPinned {
+                    let sect = listViewOnSection.count - 1 < currentIndexpath!.section ? listViewOnSection.count - 1 : currentIndexpath!.section
+                    let headerView = listViewOnSection[sect]
                     headerView.isHidden = true
                 }
             }
@@ -6427,9 +6449,11 @@ public class ImageGrouping {
     public var lPin = ""
     public var dataMessage: [String: Any?] = [:]
     public var dataPerson: [String: String?] = [:]
+    public var dataGroup: [String: Any?] = [:]
+    public var dataTopic: [String: Any?] = [:]
     public var isSelected = false
     
-    public init(messageId: String, thumbId: String, imageId: String, status: String, time: String, lPin: String, dataMessage: [String: Any?], dataPerson: [String: String?]) {
+    public init(messageId: String, thumbId: String, imageId: String, status: String, time: String, lPin: String, dataMessage: [String: Any?], dataPerson: [String: String?], dataGroup: [String: Any?], dataTopic: [String: Any?]) {
         self.messageId = messageId
         self.thumbId = thumbId
         self.imageId = imageId
@@ -6438,6 +6462,7 @@ public class ImageGrouping {
         self.lPin = lPin
         self.dataMessage = dataMessage
         self.dataPerson = dataPerson
+        self.dataGroup = dataGroup
+        self.dataTopic = dataTopic
     }
 }
-

+ 17 - 3
appbuilder-ios/DigiXLite/DigiXLite/Source/View/Chat/ListGroupImages.swift

@@ -26,6 +26,7 @@ class ListGroupImages: UIViewController, UITableViewDataSource, UITableViewDeleg
     var viewMultipleSelect = UIView()
     var constraintBottomViewMultipleSelect: NSLayoutConstraint!
     let centeredTitleView = CenteredTitleSubtitleView(frame: CGRect(x: 0, y: 0, width: 200, height: 44))
+    var isPersonal = true
 
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -268,7 +269,12 @@ class ListGroupImages: UIViewController, UITableViewDataSource, UITableViewDeleg
                 popover.dismiss()
                 let messageInfoVC = MessageInfo()
                 messageInfoVC.data = listGroupingImages[indexSelected].dataMessage
-                messageInfoVC.dataPerson = listGroupingImages[indexPath.row].dataPerson
+                if isPersonal {
+                    messageInfoVC.dataPerson = listGroupingImages[indexSelected].dataPerson
+                } else {
+                    messageInfoVC.dataGroup = listGroupingImages[indexSelected].dataGroup
+                    messageInfoVC.isPersonal = false
+                }
                 self.navigationController?.pushViewController(messageInfoVC, animated: true)
             default :
                 popover.dismiss()
@@ -519,10 +525,18 @@ class ListGroupImages: UIViewController, UITableViewDataSource, UITableViewDeleg
             let tempDataDelete = listGroupingImages
             for i in 0..<dataMessages.count {
                 if (type == "me") {
-                    self.deleteMessage(l_pin: dataMessages[i].lPin, message_id: dataMessages[i].messageId, scope: "3", type: "1", chat: "")
+                    if isPersonal {
+                        self.deleteMessage(l_pin: dataMessages[i].lPin, message_id: dataMessages[i].messageId, scope: "3", type: "1", chat: "")
+                    } else {
+                        self.deleteMessage(l_pin: dataMessages[i].dataGroup["group_id"] as! String, message_id: dataMessages[i].messageId, scope: "4", type: "1", chat: dataMessages[i].dataTopic["chat_id"] as! String)
+                    }
                     listGroupingImages.removeAll(where: { $0.messageId == dataMessages[i].messageId })
                 } else {
-                    self.deleteMessage(l_pin: dataMessages[i].lPin, message_id: dataMessages[i].messageId, scope: "3", type: "2", chat: "")
+                    if isPersonal {
+                        self.deleteMessage(l_pin: dataMessages[i].lPin, message_id: dataMessages[i].messageId, scope: "3", type: "2", chat: "")
+                    } else {
+                        self.deleteMessage(l_pin: dataMessages[i].dataGroup["group_id"] as! String, message_id: dataMessages[i].messageId, scope: "4", type: "2", chat: dataMessages[i].dataTopic["chat_id"] as! String)
+                    }
                     if let idxTemp = tempDataDelete!.firstIndex(where: { $0.messageId == dataMessages[i].messageId}) {
                         tempDataDelete![idxTemp].dataMessage["lock"] = "1"
                     }

+ 415 - 106
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift

@@ -93,6 +93,8 @@ public class EditorGroup: UIViewController {
     var listViewOnSection: [UIView] = []
     var fakeProgMultip = 0
     let maxFakeProgMultip = 2
+    var groupImages: [String:[ImageGrouping]] = [:]
+    var titleText: String!
     
     public override func viewDidDisappear(_ animated: Bool) {
         if self.isMovingFromParent {
@@ -440,6 +442,7 @@ public class EditorGroup: UIViewController {
                 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 chat_id='\(dataTopic["chat_id"] as! String)' order by server_date asc"
             }
             if let cursorData = Database.shared.getRecords(fmdb: fmdb, query: query) {
+                var tempImages: [ImageGrouping] = []
                 while cursorData.next() {
                     var row: [String: Any?] = [:]
                     row["message_id"] = cursorData.string(forColumnIndex: 0)
@@ -483,16 +486,60 @@ public class EditorGroup: UIViewController {
                         }
                     }
                     row["chat_date"] = chatDate(stringDate: row["server_date"] as! String)
+                    
+                    if (dataMessages.count == 0 || dataMessages.last!["f_pin"] as! String == row["f_pin"] as! String) && tempImages.count <= 30 && row["image_id"] != nil && !(row["image_id"] as! String).isEmpty && (row["message_text"] as! String).isEmpty && (row["reff_id"] as! String).isEmpty && (row["credential"] as! String) != "1" && (row["read_receipts"] as! String) != "8" {
+                        if tempImages.count != 0 && getSecondsDifferenceFromTwoDates(start: Date.init(milliseconds: Int64(tempImages.last!.time)!), end: Date.init(milliseconds: Int64(row["server_date"] as! String)!))/60 >= 11 {
+                            if tempImages.count >= 4 {
+                                groupImages[tempImages[0].messageId] = tempImages
+                                if let idxTemp = dataMessages.firstIndex(where: { $0["message_id"] as! String == tempImages[0].messageId }) {
+                                    for _ in 1..<tempImages.count {
+                                        dataMessages.remove(at: idxTemp + 1)
+                                    }
+                                }
+                            }
+                            tempImages.removeAll()
+                        }
+                        tempImages.append(ImageGrouping(messageId: row["message_id"] as! String, thumbId: row["thumb_id"] as! String, imageId: row["image_id"] as! String, status: row["status"] as! String, time: row["server_date"] as! String, lPin: row["l_pin"] as! String, dataMessage: row, dataPerson: [:], dataGroup: dataGroup, dataTopic: dataTopic))
+                    } else if tempImages.count >= 4 {
+                        groupImages[tempImages[0].messageId] = tempImages
+                        if let idxTemp = dataMessages.firstIndex(where: { $0["message_id"] as! String == tempImages[0].messageId }) {
+                            for _ in 1..<tempImages.count {
+                                dataMessages.remove(at: idxTemp + 1)
+                            }
+                        }
+                        tempImages.removeAll()
+                    } else if tempImages.count != 0 {
+                        tempImages.removeAll()
+                    }
                     dataMessages.append(row)
                 }
 //                if isHistoryCC {
 //                    dataMessages.remove(at: 0)
 //                }
+                if tempImages.count >= 4 {
+                    if tempImages.count > 30 {
+                        tempImages.removeSubrange(30..<tempImages.count)
+                    }
+                    groupImages[tempImages[0].messageId] = tempImages
+                    if let idxTemp = dataMessages.firstIndex(where: { $0["message_id"] as! String == tempImages[0].messageId }) {
+                        for _ in 1..<tempImages.count {
+                            dataMessages.remove(at: idxTemp + 1)
+                        }
+                    }
+                }
                 cursorData.close()
             }
         })
     }
     
+    func getSecondsDifferenceFromTwoDates(start: Date, end: Date) -> Int {
+        let diff = Int(end.timeIntervalSince1970 - start.timeIntervalSince1970)
+
+        let hours = diff / 3600
+        let seconds = (diff - hours * 3600)
+        return seconds
+    }
+    
     private func getRealStatus(messageId: String) -> String {
         var status = "1"
         Database.shared.database?.inTransaction({ (fmdb, rollback) in
@@ -581,6 +628,7 @@ public class EditorGroup: UIViewController {
             titleNavigation.font = UIFont.systemFont(ofSize: 12).bold
             
             navigationItem.titleView = viewAppBar
+            titleText = titleNavigation.text
         } else {
             searchBar = UISearchBar()
             searchBar.autocapitalizationType = .none
@@ -2444,6 +2492,15 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
             let buttonGesture = UITapGestureRecognizer(target: self, action: #selector(sessionAction))
             button.isUserInteractionEnabled = true
             button.addGestureRecognizer(buttonGesture)
+            
+            let selectedMessage = dataMessages.filter({ $0["isSelected"] as! Bool == true })
+            if selectedMessage.count > 0 {
+                for i in 0..<selectedMessage.count {
+                    if let isGroupingImages = groupImages[selectedMessage[i]["message_id"] as! String] {
+                        title.text = "\(countSelected + (isGroupingImages.count - 1)) " + "Selected".localized()
+                    }
+                }
+            }
         } else {
             buttonUp = UIButton()
             container.addSubview(buttonUp)
@@ -2528,11 +2585,20 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
             }
             cancelAction()
         } else if forwardSession {
-            let dataMessages = self.dataMessages.filter({ $0["isSelected"] as! Bool == true })
+            var dataMessages = self.dataMessages.filter({ $0["isSelected"] as! Bool == true })
             let countSelected = dataMessages.count
             if countSelected == 0 {
                 return
             }
+            for i in 0..<countSelected {
+                if let groupingImages = groupImages[dataMessages[i]["message_id"] as! String] {
+                    var tempData = dataMessages
+                    tempData.remove(at: 0)
+                    var dataMessageInGrouping = (groupImages[dataMessages[i]["message_id"] as! String]!).map({ $0.dataMessage })
+                    tempData.insert(contentsOf: dataMessageInGrouping, at: i)
+                    dataMessages = tempData
+                }
+            }
             contactChatNav.modalPresentationStyle = .custom
             contactChatNav.navigationBar.tintColor = .white
             contactChatNav.navigationBar.barTintColor = .mainColor
@@ -2570,7 +2636,8 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
             let idMe = UserDefaults.standard.string(forKey: "me") as String?
             let dataFilterFpin = dataMessages.filter({ $0["f_pin"] as? String != idMe})
             let dataFilterLock = dataMessages.filter({ $0["lock"] as? String == "1"})
-            if dataFilterFpin.count == 0 && dataFilterLock.count == 0 {
+            let statusDataRead = dataMessages.filter({ Int($0["status"] as! String)! >= 4})
+            if dataFilterFpin.count == 0 && dataFilterLock.count == 0 && statusDataRead.count == 0 {
                 if let action = self.actionDelete(for: "everyone", title: "Delete".localized() + " \(countSelected) " + "For Everyone".localized(), dataMessages: dataMessages) {
                     alertController.addAction(action)
                 }
@@ -2679,16 +2746,53 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
         return UIAlertAction(title: title, style: .destructive) { [unowned self] _ in
             for i in 0..<dataMessages.count {
                 if (type == "me") {
-                    self.deleteMessage(l_pin: dataGroup["group_id"] as! String, message_id: dataMessages[i]["message_id"] as! String, scope: "4", type: "1", chat: dataTopic["chat_id"] as! String)
-                    let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[i]["message_id"] as? String})
-                    if idx != nil {
-                        self.dataMessages.remove(at: idx!)
-                        if (idx == self.dataMessages.count - 1) {
-                            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
+                    if let groupingImages = groupImages[dataMessages[i]["message_id"] as! String] {
+                        for i in 0..<groupingImages.count {
+                            self.deleteMessage(l_pin: dataGroup["group_id"] as! String, message_id: groupingImages[i].messageId, scope: "4", type: "1", chat: dataTopic["chat_id"] as! String)
+                            let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == groupingImages[i].messageId })
+                            if idx != nil {
+                                self.dataMessages.remove(at: idx!)
+                                if (idx == self.dataMessages.count - 1) {
+                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
+                                }
+                                for i in 0..<dataDates.count {
+                                    if self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[i] }).count == 0 {
+                                        dataDates.remove(at: i)
+                                    }
+                                }
+                            }
                         }
-                        for i in 0..<dataDates.count {
-                            if self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[i] }).count == 0 {
-                                dataDates.remove(at: i)
+                        self.groupImages.removeValue(forKey: groupingImages[0].messageId)
+                    } else {
+                        if let groupingImages = groupImages[dataMessages[i]["message_id"] as! String] {
+                            for i in 0..<groupingImages.count {
+                                self.deleteMessage(l_pin: dataGroup["group_id"] as! String, message_id: groupingImages[i].messageId, scope: "4", type: "2", chat: dataTopic["chat_id"] as! String)
+                                let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == groupingImages[i].messageId})
+                                if idx != nil {
+                                    self.dataMessages[idx!]["lock"] = "1"
+                                    self.dataMessages[idx!]["attachment_flag"] = "0"
+                                    self.dataMessages[idx!]["reff_id"] = ""
+                                }
+                            }
+                            if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == groupingImages[0].messageId}) {
+                                var dataMessageInGrouping = (groupImages[dataMessages[i]["message_id"] as! String]!).map({ $0.dataMessage })
+                                dataMessageInGrouping.remove(at: 0)
+                                self.dataMessages.insert(contentsOf: dataMessageInGrouping, at: idx+1)
+                                self.groupImages.removeValue(forKey: groupingImages[0].messageId)
+                            }
+                        } else {
+                            self.deleteMessage(l_pin: dataGroup["group_id"] as! String, message_id: dataMessages[i]["message_id"] as! String, scope: "4", type: "1", chat: dataTopic["chat_id"] as! String)
+                            let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == dataMessages[i]["message_id"] as? String})
+                            if idx != nil {
+                                self.dataMessages.remove(at: idx!)
+                                if (idx == self.dataMessages.count - 1) {
+                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTabChats"), object: nil, userInfo: nil)
+                                }
+                                for i in 0..<dataDates.count {
+                                    if self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[i] }).count == 0 {
+                                        dataDates.remove(at: i)
+                                    }
+                                }
                             }
                         }
                     }
@@ -3011,6 +3115,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
         
         let containerMessage = UIView()
         
+        let messageIdChat = (dataMessages[indexPath.row]["message_id"] as? String) ?? ""
         let thumbChat = dataMessages[indexPath.row]["thumb_id"] as! String
         let imageChat = dataMessages[indexPath.row]["image_id"] as! String
         let videoChat = dataMessages[indexPath.row]["video_id"] as! String
@@ -3262,8 +3367,8 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
             nameSender.textColor = .mainColor
         }
         
+        let imageStared = UIImageView()
         if dataMessages[indexPath.row]["is_stared"] as? String == "1" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as! String == "0") {
-            let imageStared = UIImageView()
             cellMessage.contentView.addSubview(imageStared)
             imageStared.translatesAutoresizingMaskIntoConstraints = false
             if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
@@ -3445,104 +3550,215 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
         let containerViewFile = UIView()
         
         if (thumbChat != "" && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as! String != "1")) {
-            topMarginText.constant = topMarginText.constant + ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.height
-            
-            containerMessage.addSubview(imageThumb)
-            imageThumb.translatesAutoresizingMaskIntoConstraints = false
-            let data = queryMessageReply(message_id: reffChat)
-            if (data.count == 0) {
-                imageThumb.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 32).isActive = true
-            }
-            imageThumb.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
-            imageThumb.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
-            imageThumb.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
-            imageThumb.widthAnchor.constraint(equalToConstant: ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.width).isActive = true
-            imageThumb.layer.cornerRadius = 5.0
-            imageThumb.clipsToBounds = true
-            imageThumb.contentMode = .scaleAspectFill
-            
-            let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
-            let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
-            let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
-            if let dirPath = paths.first {
-                let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumbChat)
-                let image    = UIImage(contentsOfFile: thumbURL.path)
-//                let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
-                imageThumb.image = image
-                
-                let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(videoChat)
-                let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(imageChat)
-                if !FileManager.default.fileExists(atPath: imageURL.path) || !FileManager.default.fileExists(atPath: videoURL.path){
-                    let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
+            if let listImages = groupImages[messageIdChat] {
+                timeMessage.isHidden = true
+                statusMessage.isHidden = true
+                imageStared.isHidden = true
+                topMarginText.constant = topMarginText.constant + 225
+                let listImageThumb: [UIImageView] = [UIImageView(), UIImageView(), UIImageView(), UIImageView()]
+                for i in 0..<4 {
+                    containerMessage.addSubview(listImageThumb[i])
+                    listImageThumb[i].layer.cornerRadius = 5.0
+                    listImageThumb[i].clipsToBounds = true
+                    listImageThumb[i].contentMode = .scaleAspectFill
+                    let widthHeightImage: CGFloat = 120
+                    switch i {
+                        case 0:
+                            listImageThumb[i].anchor(top: containerMessage.topAnchor, left: containerMessage.leftAnchor, paddingTop: 5, paddingLeft: 5, width: widthHeightImage, height: widthHeightImage)
+                        case 1:
+                            listImageThumb[i].anchor(top: containerMessage.topAnchor, left: listImageThumb[0].rightAnchor, right: containerMessage.rightAnchor, paddingTop: 5, paddingLeft: 5, paddingRight: 5, width: widthHeightImage, height: widthHeightImage)
+                        case 2:
+                            listImageThumb[i].anchor(left: containerMessage.leftAnchor, bottom: containerMessage.bottomAnchor, paddingLeft: 5, paddingBottom: 5, width: widthHeightImage, height: widthHeightImage)
+                        default:
+                            listImageThumb[i].anchor(left: listImageThumb[2].rightAnchor, bottom: containerMessage.bottomAnchor, right: containerMessage.rightAnchor, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, width: widthHeightImage, height: widthHeightImage)
+                    }
+                    let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
+                    let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
+                    let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
+                    if let dirPath = paths.first {
+                        let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(listImages[i].thumbId)
+                        let image    = UIImage(contentsOfFile: thumbURL.path)
+//                        let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
+                        listImageThumb[i].image = image
+
+                        let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(listImages[i].imageId)
+                        if !FileManager.default.fileExists(atPath: imageURL.path) {
+                            let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
+                            let blurEffectView = UIVisualEffectView(effect: blurEffect)
+                            blurEffectView.frame = CGRect(x: 0, y: 0, width: listImageThumb[i].frame.size.width, height: listImageThumb[i].frame.size.height)
+                            blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
+                            listImageThumb[i].addSubview(blurEffectView)
+                        }
+
+                    }
+                    let containerTimeStatus = UIView()
+                    listImageThumb[i].addSubview(containerTimeStatus)
+                    containerTimeStatus.anchor(bottom: listImageThumb[i].bottomAnchor, right: listImageThumb[i].rightAnchor, height: 15)
+                    let widthcontainerTimeStatus = containerTimeStatus.widthAnchor.constraint(equalToConstant: 50)
+                    widthcontainerTimeStatus.isActive = true
+                    containerTimeStatus.layer.cornerRadius = 5.0
+                    containerTimeStatus.layer.masksToBounds = true
+                    containerTimeStatus.backgroundColor = .black.withAlphaComponent(0.15)
+                    
+                    let timeInImage = UILabel()
+                    containerTimeStatus.addSubview(timeInImage)
+                    let date = Date(milliseconds: Int64(listImages[i].time) ?? 100)
+                    let formatter = DateFormatter()
+                    formatter.dateFormat = "HH:mm"
+                    formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
+                    timeInImage.text = formatter.string(from: date as Date)
+                    timeInImage.textColor = .white
+                    timeInImage.font = UIFont.systemFont(ofSize: 10, weight: .medium)
+                    
+                    if (dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
+                        let statusInImage = UIImageView()
+                        containerTimeStatus.addSubview(statusInImage)
+                        statusInImage.anchor(right: containerTimeStatus.rightAnchor, centerY: containerTimeStatus.centerYAnchor, width: 15, height: 15)
+                        if listImages[i].status == "1" || listImages[i].status == "2"  {
+                            statusInImage.image = UIImage(named: "checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.white)
+                        } else if listImages[i].status == "3" {
+                            statusInImage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.white)
+                        } else {
+                            statusInImage.image = UIImage(named: "double-checklist", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!.withTintColor(UIColor.systemBlue)
+                        }
+                        timeInImage.anchor(right: statusInImage.leftAnchor, centerY: containerTimeStatus.centerYAnchor, height: 15)
+                    } else {
+                        timeInImage.anchor(right: containerTimeStatus.rightAnchor, paddingRight: 5, centerY: containerTimeStatus.centerYAnchor, height: 15)
+                        widthcontainerTimeStatus.constant = widthcontainerTimeStatus.constant - 10
+                    }
+                    
+                    if listImages[i].dataMessage["is_stared"] as? String == "1" {
+                        let iconStar = UIImageView()
+                        containerTimeStatus.addSubview(iconStar)
+                        iconStar.anchor(right: timeInImage.leftAnchor, paddingRight: 2, centerY: containerTimeStatus.centerYAnchor, width: 15, height: 15)
+                        widthcontainerTimeStatus.constant = widthcontainerTimeStatus.constant + 15
+                        iconStar.image = UIImage(systemName: "star.fill")
+                        iconStar.tintColor = .white
+                    }
+                    
+                    if !copySession && !forwardSession && !deleteSession {
+                        let objectTap = ObjectGesture(target: self, action: #selector(imageGroupingTapped(_:)))
+                        listImageThumb[i].isUserInteractionEnabled = true
+                        listImageThumb[i].addGestureRecognizer(objectTap)
+                        objectTap.indexImageTapped = i
+                        objectTap.listImageFromGrouping = listImages
+                        objectTap.isInitiator = dataMessages[indexPath.row]["f_pin"] as? String == idMe
+                    }
+                }
+                if  listImages.count > 4 {
+                    let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.dark)
                     let blurEffectView = UIVisualEffectView(effect: blurEffect)
-                    blurEffectView.frame = CGRect(x: 0, y: 0, width: imageThumb.frame.size.width, height: imageThumb.frame.size.height)
+                    blurEffectView.frame = CGRect(x: 0, y: 0, width: listImageThumb[3].frame.size.width, height: listImageThumb[3].frame.size.height)
                     blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
-                    imageThumb.addSubview(blurEffectView)
-                    if !imageChat.isEmpty {
-                        let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 50, weight: .bold, scale: .default)))
+                    listImageThumb[3].addSubview(blurEffectView)
+                    
+                    let countRestImages = UILabel()
+                    listImageThumb[3].addSubview(countRestImages)
+                    countRestImages.anchor(centerX: listImageThumb[3].centerXAnchor, centerY: listImageThumb[3].centerYAnchor)
+                    countRestImages.font = UIFont.systemFont(ofSize: 30, weight: .medium)
+                    countRestImages.text = "+\(listImages.count - 3)"
+                    countRestImages.textColor = .white
+                }
+            } else {
+                topMarginText.constant = topMarginText.constant + ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.height
+                
+                containerMessage.addSubview(imageThumb)
+                imageThumb.translatesAutoresizingMaskIntoConstraints = false
+                let data = queryMessageReply(message_id: reffChat)
+                if (data.count == 0) {
+                    imageThumb.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: 32).isActive = true
+                }
+                imageThumb.leadingAnchor.constraint(equalTo: containerMessage.leadingAnchor, constant: 15).isActive = true
+                imageThumb.bottomAnchor.constraint(equalTo: messageText.topAnchor, constant: -5).isActive = true
+                imageThumb.trailingAnchor.constraint(equalTo: containerMessage.trailingAnchor, constant: -15).isActive = true
+                imageThumb.widthAnchor.constraint(equalToConstant: ListGroupImages.getImageSize(image: thumbChat, screenWidth: self.view.frame.size.width * 0.6, screenHeight: 305)!.width).isActive = true
+                imageThumb.layer.cornerRadius = 5.0
+                imageThumb.clipsToBounds = true
+                imageThumb.contentMode = .scaleAspectFill
+                
+                let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
+                let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
+                let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
+                if let dirPath = paths.first {
+                    let thumbURL = URL(fileURLWithPath: dirPath).appendingPathComponent(thumbChat)
+                    let image    = UIImage(contentsOfFile: thumbURL.path)
+    //                let image = UIGraphicsRenderer.renderImageAt(url: thumbURL as NSURL, size: CGSize(width: 250, height: 250))
+                    imageThumb.image = image
+                    
+                    let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(videoChat)
+                    let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(imageChat)
+                    if !FileManager.default.fileExists(atPath: imageURL.path) || !FileManager.default.fileExists(atPath: videoURL.path){
+                        let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.light)
+                        let blurEffectView = UIVisualEffectView(effect: blurEffect)
+                        blurEffectView.frame = CGRect(x: 0, y: 0, width: imageThumb.frame.size.width, height: imageThumb.frame.size.height)
+                        blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
                         imageThumb.addSubview(blurEffectView)
-                        imageThumb.addSubview(imageDownload)
-                        imageDownload.tintColor = .black.withAlphaComponent(0.3)
-                        imageDownload.translatesAutoresizingMaskIntoConstraints = false
-                        imageDownload.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
-                        imageDownload.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
+                        if !imageChat.isEmpty {
+                            let imageDownload = UIImageView(image: UIImage(systemName: "arrow.down.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 50, weight: .bold, scale: .default)))
+                            imageThumb.addSubview(blurEffectView)
+                            imageThumb.addSubview(imageDownload)
+                            imageDownload.tintColor = .black.withAlphaComponent(0.3)
+                            imageDownload.translatesAutoresizingMaskIntoConstraints = false
+                            imageDownload.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
+                            imageDownload.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
+                        }
                     }
+                    
                 }
                 
-            }
-            
-            if (videoChat != "") {
-                let imagePlay = UIImageView(image: UIImage(systemName: "play.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .bold, scale: .default))?.imageWithInsets(insets: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))?.withTintColor(.white))
-                imagePlay.circle()
-                imageThumb.addSubview(imagePlay)
-                imagePlay.backgroundColor = .black.withAlphaComponent(0.3)
-                imagePlay.translatesAutoresizingMaskIntoConstraints = false
-                imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
-                imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
-            }
-            
-            if (dataMessages[indexPath.row]["progress"] as! Double != 100.0 && dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
-                let container = UIView()
-                imageThumb.addSubview(container)
-                container.translatesAutoresizingMaskIntoConstraints = false
-                container.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
-                container.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
-                container.widthAnchor.constraint(equalToConstant: 30).isActive = true
-                container.heightAnchor.constraint(equalToConstant: 30).isActive = true
-                container.backgroundColor = .white.withAlphaComponent(0.1)
-                let circlePath = UIBezierPath(arcCenter: CGPoint(x: 10, y: 20), radius: 15, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
-                let trackShape = CAShapeLayer()
-                trackShape.path = circlePath.cgPath
-                trackShape.fillColor = UIColor.black.withAlphaComponent(0.3).cgColor
-                trackShape.lineWidth = 3
-                trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
-                container.backgroundColor = .clear
-                container.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
-                container.layer.addSublayer(shapeLoading)
-                let imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
-                imageupload.tintColor = .white
-                container.addSubview(imageupload)
-                imageupload.translatesAutoresizingMaskIntoConstraints = false
-                imageupload.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
-                imageupload.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
-                imageupload.widthAnchor.constraint(equalToConstant: 20).isActive = true
-                imageupload.heightAnchor.constraint(equalToConstant: 20).isActive = true
-            }
-            
-            if !copySession && !forwardSession && !deleteSession {
-                let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
-                imageThumb.isUserInteractionEnabled = true
-                imageThumb.addGestureRecognizer(objectTap)
-                objectTap.image_id = imageChat
-                objectTap.video_id = videoChat
-                objectTap.imageView = imageThumb
-                objectTap.indexPath = indexPath
+                if (videoChat != "") {
+                    let imagePlay = UIImageView(image: UIImage(systemName: "play.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 20, weight: .bold, scale: .default))?.imageWithInsets(insets: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))?.withTintColor(.white))
+                    imagePlay.circle()
+                    imageThumb.addSubview(imagePlay)
+                    imagePlay.backgroundColor = .black.withAlphaComponent(0.3)
+                    imagePlay.translatesAutoresizingMaskIntoConstraints = false
+                    imagePlay.centerXAnchor.constraint(equalTo: imageThumb.centerXAnchor).isActive = true
+                    imagePlay.centerYAnchor.constraint(equalTo: imageThumb.centerYAnchor).isActive = true
+                }
+                
+                if (dataMessages[indexPath.row]["progress"] as! Double != 100.0 && dataMessages[indexPath.row]["f_pin"] as? String == idMe) {
+                    let container = UIView()
+                    imageThumb.addSubview(container)
+                    container.translatesAutoresizingMaskIntoConstraints = false
+                    container.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
+                    container.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
+                    container.widthAnchor.constraint(equalToConstant: 30).isActive = true
+                    container.heightAnchor.constraint(equalToConstant: 30).isActive = true
+                    container.backgroundColor = .white.withAlphaComponent(0.1)
+                    let circlePath = UIBezierPath(arcCenter: CGPoint(x: 10, y: 20), radius: 15, startAngle: -(.pi / 2), endAngle: .pi * 2, clockwise: true)
+                    let trackShape = CAShapeLayer()
+                    trackShape.path = circlePath.cgPath
+                    trackShape.fillColor = UIColor.black.withAlphaComponent(0.3).cgColor
+                    trackShape.lineWidth = 3
+                    trackShape.strokeColor = UIColor.blueBubbleColor.withAlphaComponent(0.3).cgColor
+                    container.backgroundColor = .clear
+                    container.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
+                    container.layer.addSublayer(shapeLoading)
+                    let imageupload = UIImageView(image: UIImage(systemName: "arrow.up", withConfiguration: UIImage.SymbolConfiguration(pointSize: 10, weight: .bold, scale: .default)))
+                    imageupload.tintColor = .white
+                    container.addSubview(imageupload)
+                    imageupload.translatesAutoresizingMaskIntoConstraints = false
+                    imageupload.bottomAnchor.constraint(equalTo: imageThumb.bottomAnchor, constant: -10).isActive = true
+                    imageupload.leadingAnchor.constraint(equalTo: imageThumb.leadingAnchor, constant: 10).isActive = true
+                    imageupload.widthAnchor.constraint(equalToConstant: 20).isActive = true
+                    imageupload.heightAnchor.constraint(equalToConstant: 20).isActive = true
+                }
+                
+                if !copySession && !forwardSession && !deleteSession {
+                    let objectTap = ObjectGesture(target: self, action: #selector(contentMessageTapped(_:)))
+                    imageThumb.isUserInteractionEnabled = true
+                    imageThumb.addGestureRecognizer(objectTap)
+                    objectTap.image_id = imageChat
+                    objectTap.video_id = videoChat
+                    objectTap.imageView = imageThumb
+                    objectTap.indexPath = indexPath
+                }
             }
         }
         
@@ -3943,6 +4159,94 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
         return cellMessage
     }
     
+    @objc func imageGroupingTapped(_ sender: ObjectGesture) {
+        let listGroupingImages = ListGroupImages()
+        listGroupingImages.imageTapped = sender.indexImageTapped
+        listGroupingImages.listGroupingImages = sender.listImageFromGrouping
+        listGroupingImages.titleName = titleText
+        listGroupingImages.isInitiator = sender.isInitiator
+        listGroupingImages.isPersonal = false
+        listGroupingImages.updateEditor = { [self] updatedData, replyData, isUpdateDelete in
+            if replyData.count == 0 {
+                if updatedData.count != 0 && !isUpdateDelete {
+                    groupImages[sender.listImageFromGrouping[0].messageId] = updatedData
+                } else if updatedData.count > 0 {
+                    let deletedForEveryoneData = updatedData.filter({ $0.dataMessage["lock"] as? String == "1" })
+                    if deletedForEveryoneData.count != 0 {
+                        if groupImages[sender.listImageFromGrouping[0].messageId] != nil {
+                            var dataWillEmpty = updatedData
+                            while dataWillEmpty.count > 0 {
+                                if let lastIdx = dataWillEmpty.lastIndex(where: { $0.dataMessage["lock"] as? String == "1" }) {
+                                    if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.listImageFromGrouping[0].messageId }) {
+                                        if dataWillEmpty[lastIdx].messageId == sender.listImageFromGrouping[0].messageId {
+                                            self.dataMessages.remove(at: idx)
+                                            self.dataMessages.insert(dataWillEmpty[lastIdx].dataMessage, at: idx)
+                                        } else {
+                                            self.dataMessages.insert(dataWillEmpty[lastIdx].dataMessage, at: idx + 1)
+                                        }
+                                        let subData = Array(updatedData[lastIdx+1..<dataWillEmpty.count])
+                                        if subData.count >= 4 {
+                                            groupImages[subData[0].messageId] = subData
+                                            self.dataMessages.insert(subData[0].dataMessage, at: lastIdx + 1)
+                                        } else {
+                                            if subData.count > 0 {
+                                                self.dataMessages.insert(contentsOf: subData.map({ $0.dataMessage }), at: idx + (dataWillEmpty[lastIdx].messageId == sender.listImageFromGrouping[0].messageId ? 1 : 2))
+                                            }
+                                        }
+                                    }
+                                    dataWillEmpty.removeSubrange(lastIdx..<dataWillEmpty.count)
+                                } else if dataWillEmpty.count >= 4 {
+                                    groupImages[dataWillEmpty[0].messageId] = dataWillEmpty
+                                    dataWillEmpty.removeAll()
+                                } else {
+                                    if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.listImageFromGrouping[0].messageId }) {
+                                        self.dataMessages.remove(at: idx)
+                                        self.dataMessages.insert(contentsOf: dataWillEmpty.map({ $0.dataMessage }), at: idx)
+                                        groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
+                                    }
+                                    dataWillEmpty.removeAll()
+                                }
+                            }
+                        } else {
+                            
+                        }
+                    } else {
+                        if updatedData.count >= 4 {
+                            if updatedData[0].messageId == sender.listImageFromGrouping[0].messageId {
+                                groupImages[sender.listImageFromGrouping[0].messageId] = updatedData
+                            } else {
+                                if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.listImageFromGrouping[0].messageId }) {
+                                    self.dataMessages.remove(at: idx)
+                                    self.dataMessages.insert(updatedData[0].dataMessage, at: idx)
+                                    groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
+                                    groupImages[updatedData[0].messageId] = updatedData
+                                }
+                            }
+                        } else {
+                            if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.listImageFromGrouping[0].messageId }) {
+                                groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
+                                self.dataMessages.remove(at: idx)
+                                let dataMessageInGrouping = updatedData.map({ $0.dataMessage })
+                                self.dataMessages.insert(contentsOf: dataMessageInGrouping, at: idx)
+                            }
+                        }
+                    }
+                } else {
+                    groupImages.removeValue(forKey: sender.listImageFromGrouping[0].messageId)
+                    if let idx = self.dataMessages.firstIndex(where: { $0["message_id"] as! String == sender.listImageFromGrouping[0].messageId }) {
+                        self.dataMessages.remove(at: idx)
+                    }
+                }
+                DispatchQueue.main.async { [self] in
+                    tableChatView.reloadData()
+                }
+            } else if replyData.count != 0 {
+                handleReply(indexPath: IndexPath(row: 0, section: 0), dataMessagesImage: replyData)
+            }
+        }
+        self.navigationController?.pushViewController(listGroupingImages, animated: true)
+    }
+    
     @objc func tapAck(_ sender: ObjectGesture) {
         let indexPath = sender.indexPath
         let dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath.section]})
@@ -4353,9 +4657,14 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource {
 //        return UISwipeActionsConfiguration(actions: [action])
 //    }
     
-    private func handleReply(indexPath: IndexPath) {
+    private func handleReply(indexPath: IndexPath, dataMessagesImage: [String: Any?] = [:]) {
         self.deleteReplyView()
-        self.textFieldSend.becomeFirstResponder()
+        var dataMessages = self.dataMessages.filter({ $0["chat_date"] as! String == dataDates[indexPath.section]})
+        if dataMessages.count != 0 {
+            dataMessages = [dataMessagesImage]
+        } else {
+            self.textFieldSend.becomeFirstResponder()
+        }
         self.reffId = dataMessages[indexPath.row]["message_id"] as? String
         UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
             self.constraintTopTextField.constant = self.constraintTopTextField.constant + 50

+ 6 - 2
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift

@@ -1140,7 +1140,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                             }
                             tempImages.removeAll()
                         }
-                        tempImages.append(ImageGrouping(messageId: row["message_id"] as! String, thumbId: row["thumb_id"] as! String, imageId: row["image_id"] as! String, status: row["status"] as! String, time: row["server_date"] as! String, lPin: row["l_pin"] as! String, dataMessage: row, dataPerson: dataPerson))
+                        tempImages.append(ImageGrouping(messageId: row["message_id"] as! String, thumbId: row["thumb_id"] as! String, imageId: row["image_id"] as! String, status: row["status"] as! String, time: row["server_date"] as! String, lPin: row["l_pin"] as! String, dataMessage: row, dataPerson: dataPerson, dataGroup: [:], dataTopic: [:]))
                     } else if tempImages.count >= 4 {
                         groupImages[tempImages[0].messageId] = tempImages
                         if let idxTemp = dataMessages.firstIndex(where: { $0["message_id"] as! String == tempImages[0].messageId }) {
@@ -6449,9 +6449,11 @@ public class ImageGrouping {
     public var lPin = ""
     public var dataMessage: [String: Any?] = [:]
     public var dataPerson: [String: String?] = [:]
+    public var dataGroup: [String: Any?] = [:]
+    public var dataTopic: [String: Any?] = [:]
     public var isSelected = false
     
-    public init(messageId: String, thumbId: String, imageId: String, status: String, time: String, lPin: String, dataMessage: [String: Any?], dataPerson: [String: String?]) {
+    public init(messageId: String, thumbId: String, imageId: String, status: String, time: String, lPin: String, dataMessage: [String: Any?], dataPerson: [String: String?], dataGroup: [String: Any?], dataTopic: [String: Any?]) {
         self.messageId = messageId
         self.thumbId = thumbId
         self.imageId = imageId
@@ -6460,5 +6462,7 @@ public class ImageGrouping {
         self.lPin = lPin
         self.dataMessage = dataMessage
         self.dataPerson = dataPerson
+        self.dataGroup = dataGroup
+        self.dataTopic = dataTopic
     }
 }

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

@@ -26,6 +26,7 @@ class ListGroupImages: UIViewController, UITableViewDataSource, UITableViewDeleg
     var viewMultipleSelect = UIView()
     var constraintBottomViewMultipleSelect: NSLayoutConstraint!
     let centeredTitleView = CenteredTitleSubtitleView(frame: CGRect(x: 0, y: 0, width: 200, height: 44))
+    var isPersonal = true
 
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -268,7 +269,12 @@ class ListGroupImages: UIViewController, UITableViewDataSource, UITableViewDeleg
                 popover.dismiss()
                 let messageInfoVC = MessageInfo()
                 messageInfoVC.data = listGroupingImages[indexSelected].dataMessage
-                messageInfoVC.dataPerson = listGroupingImages[indexPath.row].dataPerson
+                if isPersonal {
+                    messageInfoVC.dataPerson = listGroupingImages[indexSelected].dataPerson
+                } else {
+                    messageInfoVC.dataGroup = listGroupingImages[indexSelected].dataGroup
+                    messageInfoVC.isPersonal = false
+                }
                 self.navigationController?.pushViewController(messageInfoVC, animated: true)
             default :
                 popover.dismiss()
@@ -519,10 +525,18 @@ class ListGroupImages: UIViewController, UITableViewDataSource, UITableViewDeleg
             let tempDataDelete = listGroupingImages
             for i in 0..<dataMessages.count {
                 if (type == "me") {
-                    self.deleteMessage(l_pin: dataMessages[i].lPin, message_id: dataMessages[i].messageId, scope: "3", type: "1", chat: "")
+                    if isPersonal {
+                        self.deleteMessage(l_pin: dataMessages[i].lPin, message_id: dataMessages[i].messageId, scope: "3", type: "1", chat: "")
+                    } else {
+                        self.deleteMessage(l_pin: dataMessages[i].dataGroup["group_id"] as! String, message_id: dataMessages[i].messageId, scope: "4", type: "1", chat: dataMessages[i].dataTopic["chat_id"] as! String)
+                    }
                     listGroupingImages.removeAll(where: { $0.messageId == dataMessages[i].messageId })
                 } else {
-                    self.deleteMessage(l_pin: dataMessages[i].lPin, message_id: dataMessages[i].messageId, scope: "3", type: "2", chat: "")
+                    if isPersonal {
+                        self.deleteMessage(l_pin: dataMessages[i].lPin, message_id: dataMessages[i].messageId, scope: "3", type: "2", chat: "")
+                    } else {
+                        self.deleteMessage(l_pin: dataMessages[i].dataGroup["group_id"] as! String, message_id: dataMessages[i].messageId, scope: "4", type: "2", chat: dataMessages[i].dataTopic["chat_id"] as! String)
+                    }
                     if let idxTemp = tempDataDelete!.firstIndex(where: { $0.messageId == dataMessages[i].messageId}) {
                         tempDataDelete![idxTemp].dataMessage["lock"] = "1"
                     }