Browse Source

update fix bugs for 5.0.33

alqindiirsyam 3 months ago
parent
commit
5e900dff40
21 changed files with 219 additions and 145 deletions
  1. 2 0
      AppBuilder/AppBuilder.xcodeproj/project.pbxproj
  2. 1 1
      AppBuilder/AppBuilder/FourthTabViewController.swift
  3. 2 0
      NexilisLite/NexilisLite/Resource/id.lproj/Localizable.strings
  4. 16 3
      NexilisLite/NexilisLite/Source/APIS.swift
  5. 8 7
      NexilisLite/NexilisLite/Source/Extension.swift
  6. 5 8
      NexilisLite/NexilisLite/Source/Nexilis.swift
  7. 12 4
      NexilisLite/NexilisLite/Source/OutgoingThread.swift
  8. 7 1
      NexilisLite/NexilisLite/Source/View/Call/QmeraVideoViewController.swift
  9. 0 4
      NexilisLite/NexilisLite/Source/View/Chat/ChatGPTBotView.swift
  10. 42 54
      NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift
  11. 27 20
      NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift
  12. 2 2
      NexilisLite/NexilisLite/Source/View/Chat/MessageInfo.swift
  13. 18 20
      NexilisLite/NexilisLite/Source/View/Control/BackupRestoreView.swift
  14. 12 3
      NexilisLite/NexilisLite/Source/View/Control/BroadcastViewController.swift
  15. 9 1
      NexilisLite/NexilisLite/Source/View/Control/GroupCreateViewController.swift
  16. 25 8
      NexilisLite/NexilisLite/Source/View/Control/GroupDetailViewController.swift
  17. 9 4
      NexilisLite/NexilisLite/Source/View/Control/GroupMemberViewController.swift
  18. 9 2
      NexilisLite/NexilisLite/Source/View/Control/GroupNameViewController.swift
  19. 8 1
      NexilisLite/NexilisLite/Source/View/Control/GroupTopicViewController.swift
  20. 4 1
      NexilisLite/NexilisLite/Source/View/Control/ProfileViewController.swift
  21. 1 1
      NexilisLite/NexilisLite/Source/View/Control/SettingTableViewController.swift

+ 2 - 0
AppBuilder/AppBuilder.xcodeproj/project.pbxproj

@@ -633,6 +633,7 @@
 				);
 				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
 				MARKETING_VERSION = 5.0.29;
+				OTHER_CFLAGS = "-fstack-protector-strong";
 				PRODUCT_BUNDLE_IDENTIFIER = io.nexilis.appbuilder.AppBuilderShare;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
@@ -671,6 +672,7 @@
 				);
 				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
 				MARKETING_VERSION = 5.0.29;
+				OTHER_CFLAGS = "-fstack-protector-strong";
 				PRODUCT_BUNDLE_IDENTIFIER = io.nexilis.appbuilder.AppBuilderShare;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;

+ 1 - 1
AppBuilder/AppBuilder/FourthTabViewController.swift

@@ -354,7 +354,7 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
             Item(icon: UIImage(systemName: "message"), title: "Notification Message(s)".localized()),
             Item(icon: UIImage(systemName: "message"), title: "Notification Message(s) Group".localized()),
             Item(icon: UIImage(systemName: "iphone.homebutton.radiowaves.left.and.right"), title: "Vibrate Mode".localized()),
-            Item(icon: UIImage(systemName: "photo.on.rectangle.angled"), title: "Save to Gallery".localized()),
+//            Item(icon: UIImage(systemName: "photo.on.rectangle.angled"), title: "Save to Gallery".localized()),
             Item(icon: UIImage(systemName: "arrow.down.square"), title: "Auto Download".localized()),
         ]
         Item.menus["Version"] = [

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

@@ -415,3 +415,5 @@
 "Tap to call back" = "Ketuk untuk menelepon kembali";
 "To place audio or video call, tap ⊕ at the top and select a contact." = "Untuk melakukan panggilan audio atau video, ketuk ⊕ di bagian atas dan pilih kontak.";
 "To place chats, tap ⊕ at the top and select a contact." = "Untuk memulai obrolan, ketuk ⊕ di bagian atas dan pilih kontak.";
+"Update Now" = "Perbarui Sekarang";
+"Later" = "Nanti";

+ 16 - 3
NexilisLite/NexilisLite/Source/APIS.swift

@@ -1550,6 +1550,19 @@ public class APIS: NSObject {
         checkDataForShareExtension()
         UIApplication.shared.applicationIconBadgeNumber = 0
         UNUserNotificationCenter.current().removeAllDeliveredNotifications()
+        DispatchQueue.global().async {
+            while API.nGetCLXConnState() == 0 {
+                Thread.sleep(forTimeInterval: 0.5)
+            }
+            if let vers = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.checkVersion()) {
+                let dataVersion = vers.getBody(key: CoreMessage_TMessageKey.DATA)
+                if dataVersion != "1" {
+                    DispatchQueue.main.async {
+                        showExpiredVersion()
+                    }
+                }
+            }
+        }
 //        afterEnterForeground = true
     }
     
@@ -1602,15 +1615,15 @@ public class APIS: NSObject {
     }
     
     public static func showExpiredVersion() {
-        guard !isAlertPresented else { return }
-        isAlertPresented = true
         let alertController = LibAlertController(
             title: "Update Available".localized(),
             message: "A new version is now available. Please update to the latest version to enjoy new features and important improvements.".localized(),
             preferredStyle: .alert
         )
         
-        alertController.addAction(UIAlertAction(title: "OK".localized(), style: .default, handler: { _ in
+        alertController.addAction(UIAlertAction(title: "Later".localized(), style: .cancel, handler: nil))
+        
+        alertController.addAction(UIAlertAction(title: "Update Now".localized(), style: .default, handler: { _ in
             if APIS.appNm == "OneApp" {
                 let appStoreURL = URL(string: "https://apps.apple.com/app/id6741251571")!
                 UIApplication.shared.open(appStoreURL)

+ 8 - 7
NexilisLite/NexilisLite/Source/Extension.swift

@@ -448,13 +448,11 @@ extension NSObject {
                             }
                         }
                         guard let tableView = tableView else { return }
-                        tableView.beginUpdates()
                         if let indexPath = indexPath,
                            indexPath.section < tableView.numberOfSections,
                            indexPath.row < tableView.numberOfRows(inSection: indexPath.section) {
                             tableView.reloadRows(at: [indexPath], with: .none)
                         }
-                        tableView.endUpdates()
                     }
                 }
             }
@@ -467,13 +465,11 @@ extension NSObject {
                 
                 DispatchQueue.main.async {
                     guard let tableView = tableView else { return }
-                    tableView.beginUpdates()
                     if let indexPath = indexPath,
                        indexPath.section < tableView.numberOfSections,
                        indexPath.row < tableView.numberOfRows(inSection: indexPath.section) {
                         tableView.reloadRows(at: [indexPath], with: .none)
                     }
-                    tableView.endUpdates()
                 }
             }
         }
@@ -577,7 +573,7 @@ extension UIColor {
     }
     
     public static var mentionColor: UIColor {
-        return UIApplication.shared.visibleViewController?.traitCollection.userInterfaceStyle == .dark ? renderColor(hex: "#f6fcae") : renderColor(hex: "#25D366")
+        return UIApplication.shared.visibleViewController?.traitCollection.userInterfaceStyle == .dark ? renderColor(hex: "#f6fcae") : renderColor(hex: "#22C55E")
     }
     
     public static var blueBubbleColor: UIColor {
@@ -868,7 +864,10 @@ extension String {
             let username = (text.string as NSString).substring(with: range)
             if isEditing {
                 if let _ = listMentionInTextField.firstIndex(where: { $0.fullName == username }) {
-                    text.addAttribute(.foregroundColor, value: UIColor.mentionColor, range: match.range(at: 0))
+                    let fullRange = match.range(at: 0)
+                    text.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(location: fullRange.lowerBound, length: 1))
+                    text.addAttribute(.foregroundColor, value: UIColor.mentionColor, range: range)
+                    text.addAttribute(.font, value: UIFont.systemFont(ofSize: 12 + String.offset(), weight: .medium), range: fullRange)
                 }
             } else {
                 if let member = Member.getMember(f_pin: username) {
@@ -876,7 +875,9 @@ extension String {
                     text.replaceCharacters(in: range, with: fullName)
                     if !groupID.isEmpty, Member.getMemberInGroup(f_pin: username, group_id: groupID) != nil {
                         let newRange = (text.string as NSString).range(of: "@\(fullName)")
-                        text.addAttribute(.foregroundColor, value: UIColor.mentionColor, range: newRange)
+                        text.addAttribute(.foregroundColor, value: UIColor.gray, range: NSRange(location: newRange.lowerBound, length: 1))
+                        text.addAttribute(.foregroundColor, value: UIColor.mentionColor, range: NSRange(location: newRange.lowerBound + 1, length: fullName.count))
+                        text.addAttribute(.font, value: UIFont.systemFont(ofSize: 12 + String.offset(), weight: .medium), range: newRange)
                     }
                 }
             }

+ 5 - 8
NexilisLite/NexilisLite/Source/Nexilis.swift

@@ -244,6 +244,11 @@ public class Nexilis: NSObject {
                         })
                     } else if isShowForceSignIn && !Utils.getForceAnonymous() && !Utils.getSetProfile() {
                         DispatchQueue.main.async {
+                            do {
+                                try _ = MasterKeyUtil.shared.getMasterKey()
+                            } catch {
+                                
+                            }
                             showForceSignIn()
                         }
                     }
@@ -286,14 +291,6 @@ public class Nexilis: NSObject {
                         dialog.modalPresentationStyle = .overCurrentContext
                         UIApplication.shared.visibleViewController?.present(dialog, animated: true)
                     }
-                    DispatchQueue.global().async {
-                        if let vers = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.checkVersion()) {
-                            let dataVersion = vers.getBody(key: CoreMessage_TMessageKey.DATA)
-                            if dataVersion == "1" {
-                                APIS.showExpiredVersion()
-                            }
-                        }
-                    }
                 }
             } catch {
                 delegate.onFailed(error: "99:Something went wrong")

+ 12 - 4
NexilisLite/NexilisLite/Source/OutgoingThread.swift

@@ -167,7 +167,10 @@ class OutgoingThread {
                     if result, progress == 100 {
                         do {
                             do{
-                                try FileEncryption.shared.writeSecure(filename: fileName)
+                                let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
+                                let fileURL = documentsDirectory.appendingPathComponent(fileName)
+                                try FileEncryption.shared.writeSecure(filename: fileName, data: Data(contentsOf: fileURL))
+                                try FileManager.default.removeItem(atPath: fileURL.path)
                                 if var data = try FileEncryption.shared.readSecure(filename: fileName) {
                                     let dataDecrypt = FileEncryption.shared.decryptFileFromServer(data: data)
                                     if dataDecrypt != nil {
@@ -186,7 +189,6 @@ class OutgoingThread {
                                 }
                                 if progress == 100 {
                                     if let response = Nexilis.writeSync(message: message) {
-                                        print("sendChat", response.toLogString())
                                         let messageId = response.getBody(key: CoreMessage_TMessageKey.MESSAGE_ID)
                                         Database.shared.database?.inTransaction({ (fmdb, rollback) in
                                             do {
@@ -200,7 +202,10 @@ class OutgoingThread {
                                             }
                                         })
                                         do {
-                                            try FileEncryption.shared.writeSecure(filename: fileName)
+                                            let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
+                                            let fileURL = documentsDirectory.appendingPathComponent(fileName)
+                                            try FileEncryption.shared.writeSecure(filename: fileName, data: Data(contentsOf: fileURL))
+                                            try FileManager.default.removeItem(atPath: fileURL.path)
                                         } catch {
                                             
                                         }
@@ -240,7 +245,10 @@ class OutgoingThread {
                                     }
                                 })
                                 do{
-                                    try FileEncryption.shared.writeSecure(filename: fileName)
+                                    let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
+                                    let fileURL = documentsDirectory.appendingPathComponent(fileName)
+                                    try FileEncryption.shared.writeSecure(filename: fileName, data: Data(contentsOf: fileURL))
+                                    try FileManager.default.removeItem(atPath: fileURL.path)
                                 } catch {
                                     
                                 }

+ 7 - 1
NexilisLite/NexilisLite/Source/View/Call/QmeraVideoViewController.swift

@@ -37,6 +37,7 @@ class QmeraVideoViewController: UIViewController {
     var isPresent = false
     var callFCM = true
     var isNavigationHidden = false
+    var rotateMyView = false
     var listRemoteViewFix: [UIImageView] = [
         UIImageView(),
         UIImageView(),
@@ -1287,9 +1288,13 @@ class QmeraVideoViewController: UIViewController {
             }
         } else if (state == Nexilis.VIDEO_CALL_ZOOM) && self.dataPerson.count > 1 {
             DispatchQueue.main.async {
-                if arrayMessage[0] != arrayMessage[3] {
+                if arrayMessage[0] != arrayMessage[3] && !self.rotateMyView {
                     self.zoomView.transform   = CGAffineTransform.init(scaleX: 1.9, y: 2.0).rotated(by: (CGFloat.pi)/2)
                     self.zoomView.contentMode = .scaleAspectFit
+                } else {
+                    self.rotateMyView = true
+                    self.zoomView.transform   = CGAffineTransform.init(scaleX: 1.9, y: 2.0).rotated(by: (-CGFloat.pi)/2)
+                    self.zoomView.contentMode = .scaleAspectFit
                 }
             }
         } else if (state == Nexilis.VIDEO_CAMERA_PARAMS_CHANED){
@@ -1623,6 +1628,7 @@ class QmeraVideoViewController: UIViewController {
                     
                     if self.dataPerson.count == 1 {
                         self.transformZoomAfterNewUserMore2 = false
+                        self.rotateMyView = false
 //                        self.zoomView.transform   = CGAffineTransform.init(scaleX: 1.9, y: 2.0).rotated(by: (CGFloat.pi)/2)
                         
                         if !self.users[0].isConnected {

+ 0 - 4
NexilisLite/NexilisLite/Source/View/Chat/ChatGPTBotView.swift

@@ -851,7 +851,6 @@ public class ChatGPTBotView: UIViewController, UIGestureRecognizerDelegate {
                         if self.markerCounter != nil {
                             self.markerCounter = nil
                         }
-                        self.tableChatView.beginUpdates()
                         let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == lastMarkerCounter })
                         if indexMessage != nil {
                             let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"] as! String)
@@ -860,7 +859,6 @@ public class ChatGPTBotView: UIViewController, UIGestureRecognizerDelegate {
                                 self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
                             }
                         }
-                        self.tableChatView.endUpdates()
                     }
                     else if self.currentIndexpath == nil {
                         self.counter = 0
@@ -873,7 +871,6 @@ public class ChatGPTBotView: UIViewController, UIGestureRecognizerDelegate {
                         if !self.indicatorCounterBSTB.isDescendant(of: self.view) && self.buttonScrollToBottom.isDescendant(of: self.view) {
                             self.markerCounter = row["message_id"] as? String
                             self.addCounterAtButttonScrollToBottom()
-                            self.tableChatView.beginUpdates()
                             let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == self.markerCounter })
                             if indexMessage != nil {
                                 let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"] as! String)
@@ -882,7 +879,6 @@ public class ChatGPTBotView: UIViewController, UIGestureRecognizerDelegate {
                                     self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
                                 }
                             }
-                            self.tableChatView.endUpdates()
                         } else if self.indicatorCounterBSTB.isDescendant(of: self.view) {
                             self.labelCounter.text = "\(self.counter)"
                         }

+ 42 - 54
NexilisLite/NexilisLite/Source/View/Chat/EditorGroup.swift

@@ -1187,7 +1187,6 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
                         if self.markerCounter != nil {
                             self.markerCounter = nil
                         }
-                        self.tableChatView.beginUpdates()
                         let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == lastMarkerCounter })
                         if indexMessage != nil {
                             let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"]  as? String ?? "")
@@ -1196,7 +1195,6 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
                                 self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
                             }
                         }
-                        self.tableChatView.endUpdates()
                     }
                     else if self.currentIndexpath == nil {
                         self.counter = 0
@@ -1209,7 +1207,6 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
                         if !self.indicatorCounterBSTB.isDescendant(of: self.view) && self.buttonScrollToBottom.isDescendant(of: self.view) {
                             self.markerCounter = row["message_id"] as? String
                             self.addCounterAtButttonScrollToBottom()
-                            self.tableChatView.beginUpdates()
                             let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == self.markerCounter })
                             if indexMessage != nil {
                                 let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"]  as? String ?? "")
@@ -1218,7 +1215,6 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
                                     self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
                                 }
                             }
-                            self.tableChatView.endUpdates()
                         } else if self.indicatorCounterBSTB.isDescendant(of: self.view) {
                             self.labelCounter.text = "\(self.counter)"
                         }
@@ -1957,7 +1953,6 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
         if self.markerCounter != nil {
             let lastMarkerCounter = self.markerCounter
             self.markerCounter = nil
-            self.tableChatView.beginUpdates()
             let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == lastMarkerCounter })
             if indexMessage != nil {
                 let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"]  as? String ?? "")
@@ -1966,7 +1961,6 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
                     self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
                 }
             }
-            self.tableChatView.endUpdates()
         }
     }
     
@@ -2701,6 +2695,9 @@ extension EditorGroup: UITextViewDelegate, CustomTextViewPasteDelegate {
         
         if listMentionInTextField.count > 0 {
             for j in 0..<listMentionInTextField.count {
+                if j > listMentionInTextField.count - 1{
+                    break
+                }
                 let name = (listMentionInTextField[j].firstName + " " + listMentionInTextField[j].lastName).trimmingCharacters(in: .whitespaces)
                 if !textView.text.contains("@\(name)") {
                     listMentionInTextField.remove(at: j)
@@ -4461,46 +4458,38 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource, AVAudioPlayer
             }
             let fulltextForMention = nowTextField.text.substring(from: 0, to: lastPositionCursorMention - 1)
             let diff = nowTextField.text.count - fulltextForMention.count
-            var indexLastMention = fulltextForMention.lastIndex(of: "@")
-            if indexLastMention == nil {
-                if let spaceIndex = fulltextForMention.lastIndex(of: " ") {
-                    indexLastMention = fulltextForMention.index(after: spaceIndex)
-                } else if let breakIndex = fulltextForMention.lastIndex(of: "\n") {
-                    indexLastMention = fulltextForMention.index(after: breakIndex)
-                } else {
-                    indexLastMention = fulltextForMention.firstIndex(of: fulltextForMention.first!)
-                }
-            }
-            if let indexLastMention = indexLastMention {
-                listMentionInTextField.append(listMentionWithText[indexPath.row])
-                let indexIntMention = fulltextForMention.distance(from: fulltextForMention.startIndex, to: indexLastMention)
-                let rangeReplacement = NSRange(location: indexIntMention, length: lastPositionCursorMention - indexIntMention)
-                
-                var addSpaceAfterReplacement = ""
-                if diff == 0 {
-                    addSpaceAfterReplacement = " "
-                }
-                
-                var text = nowTextField.text ?? ""
-                let nameMention = (listMentionWithText[indexPath.row].firstName + " " + listMentionWithText[indexPath.row].lastName).trimmingCharacters(in: .whitespaces)
-                let replacementText = "@\(nameMention)"
-                
-                // Replace the old text with the new text using the replaceSubrange(_:with:) method
-                if let startIndex = text.index(text.startIndex, offsetBy: rangeReplacement.location, limitedBy: text.endIndex),
-                   let endIndex = text.index(startIndex, offsetBy: rangeReplacement.length, limitedBy: text.endIndex) {
-                    text.replaceSubrange(startIndex..<endIndex, with: replacementText + addSpaceAfterReplacement)
+            let lines = fulltextForMention.split(separator: "\n")
+            if let lastLineIndex = lines.lastIndex(where: { !$0.isEmpty }) {
+                let words = lines[lastLineIndex].split(separator: " ")
+                if let lastWordIndex = words.lastIndex(where: { !$0.isEmpty }) {
+                    let lastWord = words[lastWordIndex]
+                    if let rangeLastWord = fulltextForMention.range(of: lastWord, options: .backwards) {
+                        listMentionInTextField.append(listMentionWithText[indexPath.row])
+                        
+                        var addSpaceAfterReplacement = ""
+                        if diff == 0 {
+                            addSpaceAfterReplacement = " "
+                        }
+                        
+                        var text = nowTextField.text ?? ""
+                        let nameMention = (listMentionWithText[indexPath.row].firstName + " " + listMentionWithText[indexPath.row].lastName).trimmingCharacters(in: .whitespaces)
+                        let replacementText = "@\(nameMention)"
+                        
+                        // Replace the old text with the new text using the replaceSubrange(_:with:) method
+                        text.replaceSubrange(rangeLastWord, with: replacementText + addSpaceAfterReplacement)
+                        
+                        nowTextField.attributedText = text.richText(isEditing: true, group_id: self.dataGroup["group_id"]  as? String ?? "", listMentionInTextField: listMentionInTextField)
+                        
+                        let newPosition = nowTextField.position(from: nowTextField.beginningOfDocument, offset: nowTextField.text.count - diff)
+                        nowTextField.selectedTextRange = nowTextField.textRange(from: newPosition!, to: newPosition!)
+                        
+                        listMentionInTextField.last?.ex_block = "\(nowTextField.text.count - diff - addSpaceAfterReplacement.count)" //upperBound
+                        
+                        hideMention()
+                        return
+                    }
                 }
-                
-                nowTextField.attributedText = text.richText(isEditing: true, group_id: self.dataGroup["group_id"]  as? String ?? "", listMentionInTextField: listMentionInTextField)
-                
-                let newPosition = nowTextField.position(from: nowTextField.beginningOfDocument, offset: nowTextField.text.count - diff)
-                nowTextField.selectedTextRange = nowTextField.textRange(from: newPosition!, to: newPosition!)
-                
-                listMentionInTextField.last?.ex_block = "\(nowTextField.text.count - diff - addSpaceAfterReplacement.count)" //upperBound
-                
-                hideMention()
             }
-            return
         }
         let dataMessages = self.dataMessages.filter({ $0["chat_date"]  as? String ?? "" == dataDates[indexPath.section] })
         if copySession || forwardSession || deleteSession {
@@ -6517,13 +6506,12 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource, AVAudioPlayer
                 let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.file_id)
                 if FileManager.default.fileExists(atPath: fileURL.path) {
                     self.previewItem = fileURL as NSURL
-                    let previewController = QLPreviewController()
-                    let rightBarButton = UIBarButtonItem()
-                    previewController.navigationItem.rightBarButtonItem = rightBarButton
+                    let previewController = CustomQLPreviewController()
+//                            let rightBarButton = UIBarButtonItem()
+//                            previewController.navigationItem.rightBarButtonItem = rightBarButton
                     previewController.dataSource = self
-                    previewController.modalPresentationStyle = .custom
-                    
-                    self.present(previewController, animated: true)
+//                            previewController.modalPresentationStyle = .custom
+                    self.present(previewController,animated: true)
                 } else if FileEncryption.shared.isSecureExists(filename: sender.file_id) {
                     do {
                         if var docData = try FileEncryption.shared.readSecure(filename: sender.file_id) {
@@ -6535,11 +6523,11 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource, AVAudioPlayer
                             let tempPath = cachesDirectory.appendingPathComponent(sender.file_id)
                             try docData.write(to: tempPath)
                             self.previewItem = tempPath as NSURL
-                            let previewController = QLPreviewController()
-                            let rightBarButton = UIBarButtonItem()
-                            previewController.navigationItem.rightBarButtonItem = rightBarButton
+                            let previewController = CustomQLPreviewController()
+//                            let rightBarButton = UIBarButtonItem()
+//                            previewController.navigationItem.rightBarButtonItem = rightBarButton
                             previewController.dataSource = self
-                            previewController.modalPresentationStyle = .custom
+//                            previewController.modalPresentationStyle = .custom
                             self.present(previewController,animated: true)
                         }
                     }

+ 27 - 20
NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift

@@ -552,7 +552,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         if onGoingCC {
             SecureUserDefaults.shared.set(self.fPinContacCenter, forKey: "inEditorPersonal")
         } else {
-            SecureUserDefaults.shared.set(dataPerson["f_pin"]!, forKey: "inEditorPersonal")
+            SecureUserDefaults.shared.set(dataPerson["f_pin"] ?? "", forKey: "inEditorPersonal")
         }
         UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [dataPerson["f_pin"]!!])
         
@@ -1061,7 +1061,6 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                     if tempData.count != 0 && (dataMessages.firstIndex(where: { $0["message_id"] as? String == tempData[0]["message_id"] as? String }) == nil) {
                         let lastIndex = tempData.count - 1
                         for i in 0..<tempData.count {
-                            tableChatView.beginUpdates()
                             dataMessages.insert(tempData[lastIndex - i], at: 0)
                             if dataMessages.firstIndex(where: { $0["chat_date"] as? String == tempData[lastIndex - i]["chat_date"] as? String }) != nil {
                                 tableChatView.insertRows(at: [IndexPath(row: 0, section: currentIndexpath!.section)], with: .top)
@@ -1069,7 +1068,6 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                                 tableChatView.insertSections(IndexSet(integer: 0), with: .top)
                                 tableChatView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .top)
                             }
-                            tableChatView.endUpdates()
                         }
                     }
                     cursorData.close()
@@ -1748,7 +1746,6 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                         if self.markerCounter != nil {
                             self.markerCounter = nil
                         }
-                        self.tableChatView.beginUpdates()
                         let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == lastMarkerCounter })
                         if indexMessage != nil {
                             let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"]  as? String ?? "")
@@ -1757,7 +1754,6 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                                 self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
                             }
                         }
-                        self.tableChatView.endUpdates()
                     }
                     else if self.currentIndexpath == nil {
                         self.counter = 0
@@ -1770,7 +1766,6 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                         if !self.indicatorCounterBSTB.isDescendant(of: self.view) && self.buttonScrollToBottom.isDescendant(of: self.view) {
                             self.markerCounter = row["message_id"] as? String
                             self.addCounterAtButttonScrollToBottom()
-                            self.tableChatView.beginUpdates()
                             let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == self.markerCounter })
                             if indexMessage != nil {
                                 let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"]  as? String ?? "")
@@ -1779,7 +1774,6 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                                     self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
                                 }
                             }
-                            self.tableChatView.endUpdates()
                         } else if self.indicatorCounterBSTB.isDescendant(of: self.view) {
                             self.labelCounter.text = "\(self.counter)"
                         }
@@ -2806,7 +2800,6 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         if self.markerCounter != nil {
             let lastMarkerCounter = self.markerCounter
             self.markerCounter = nil
-            self.tableChatView.beginUpdates()
             let indexMessage = self.dataMessages.firstIndex(where: { $0["message_id"] as? String == lastMarkerCounter })
             if indexMessage != nil {
                 let section = self.dataDates.firstIndex(of: self.dataMessages[indexMessage!]["chat_date"]  as? String ?? "")
@@ -2815,7 +2808,6 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                     self.tableChatView.reloadRows(at: [IndexPath(row: row!, section: section!)], with: .none)
                 }
             }
-            self.tableChatView.endUpdates()
         }
 //        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
 //            self.timerFakeProgress = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
@@ -3010,10 +3002,8 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
                     let categoryCC = dataMessages[dataMessages.count - 2]["category_cc"] as! [CategoryCC]
                     self.nowSelectedCategoryCC = categoryCC[0].parent
                 }
-                tableChatView.beginUpdates()
                 tableChatView.deleteRows(at: [IndexPath(row: dataMessages.count - 1, section: 0)], with: .none)
                 dataMessages.remove(at: dataMessages.count - 1)
-                tableChatView.endUpdates()
             } else {
                 return
             }
@@ -4705,7 +4695,7 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
             
             let tapGesture = ObjectGesture(target: self, action: #selector(dismissEditVC))
             tapGesture.message_id = oldText
-            view.addGestureRecognizer(tapGesture)
+            blurView.addGestureRecognizer(tapGesture)
             
             editTextView = CustomTextView()
             editTextView.layer.cornerRadius = textFieldSend.maxCornerRadius()
@@ -7922,11 +7912,11 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource, AVAudioPla
                 let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(sender.file_id)
                 if FileManager.default.fileExists(atPath: fileURL.path) {
                     self.previewItem = fileURL as NSURL
-                    let previewController = QLPreviewController()
-                    let rightBarButton = UIBarButtonItem()
-                    previewController.navigationItem.rightBarButtonItem = rightBarButton
+                    let previewController = CustomQLPreviewController()
+//                    let rightBarButton = UIBarButtonItem()
+//                    previewController.navigationItem.rightBarButtonItem = rightBarButton
                     previewController.dataSource = self
-                    previewController.modalPresentationStyle = .custom
+//                    previewController.modalPresentationStyle = .custom
                     
                     self.present(previewController, animated: true)
                 } else if FileEncryption.shared.isSecureExists(filename: sender.file_id) {
@@ -7940,11 +7930,11 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource, AVAudioPla
                             let tempPath = cachesDirectory.appendingPathComponent(sender.file_id)
                             try docData.write(to: tempPath)
                             self.previewItem = tempPath as NSURL
-                            let previewController = QLPreviewController()
-                            let rightBarButton = UIBarButtonItem()
-                            previewController.navigationItem.rightBarButtonItem = rightBarButton
+                            let previewController = CustomQLPreviewController()
+//                            let rightBarButton = UIBarButtonItem()
+//                            previewController.navigationItem.rightBarButtonItem = rightBarButton
                             previewController.dataSource = self
-                            previewController.modalPresentationStyle = .custom
+//                            previewController.modalPresentationStyle = .custom
                             self.present(previewController,animated: true)
                         }
                     }
@@ -8524,3 +8514,20 @@ public class TypeDataMessage {
     public static let is_forwarded = "is_forwarded"
     public static let is_secret = "is_secret"
 }
+
+public final class CustomQLPreviewController: QLPreviewController {
+    public override func viewDidLoad() {
+        super.viewDidLoad()
+        navigationItem.rightBarButtonItem = UIBarButtonItem()
+    }
+
+    public override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+        (children[0] as? UINavigationController)?.setToolbarHidden(true, animated: false)
+    }
+
+    public override func viewDidLayoutSubviews() {
+        super.viewDidLayoutSubviews()
+        (children[0] as? UINavigationController)?.setToolbarHidden(true, animated: false)
+    }
+}

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

@@ -300,7 +300,7 @@ class MessageInfo: UIViewController, UITableViewDelegate, UITableViewDataSource,
                             })
                             
                             content.text = dataProfile["name"]!
-                            if dataLocation.count > 0 {
+                            if dataLocation.count > 0 && indexPath.row <= dataLocation.count - 1 {
                                 content.text = dataProfile["name"]! + " at (\(dataLocation[indexPath.row]))"
                             }
                             
@@ -1145,7 +1145,7 @@ class MessageInfo: UIViewController, UITableViewDelegate, UITableViewDataSource,
     }
     
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        if !data.isEmpty && data["read_receipts"] as? String == "8" {
+        if !data.isEmpty && data["read_receipts"] as? String == "8" && indexPath.section == 1 && indexPath.row <= dataLocation.count - 1 {
             if !isPersonal {
                 if let latitude = CLLocationDegrees(dataStatus[indexPath.row]["latitude"] as? String ?? "") {
                     if let longitude = CLLocationDegrees(dataStatus[indexPath.row]["longitude"] as? String ?? "") {

+ 18 - 20
NexilisLite/NexilisLite/Source/View/Control/BackupRestoreView.swift

@@ -112,15 +112,11 @@ public class BackupRestoreView: UIViewController, UITableViewDataSource, UITable
                     valueLastBackup = dayLastBackup.localized() + ", " + timeLastBackup
                     valuesizeBackup = Units(bytes: Int64(filesize)!).getReadableUnit()
                     
-                    tableView.beginUpdates()
                     tableView.reloadRows(at: [IndexPath(row: 0, section: 0)], with: .none)
-                    tableView.endUpdates()
                 } else {
                     valueLastBackup = "-"
                     valuesizeBackup = "-"
-                    tableView.beginUpdates()
                     tableView.reloadRows(at: [IndexPath(row: 0, section: 0), IndexPath(row: 0, section: 2)], with: .none)
-                    tableView.endUpdates()
                 }
             }
         }
@@ -199,9 +195,7 @@ public class BackupRestoreView: UIViewController, UITableViewDataSource, UITable
             controller.selected = choosenOption
             controller.isSelected = { choosen in
                 self.choosenOption = choosen
-                tableView.beginUpdates()
                 tableView.reloadRows(at: [indexPath], with: .none)
-                tableView.endUpdates()
             }
             navigationController?.show(controller, sender: nil)
         } else if indexPath.section == 1 && indexPath.row == 1 {
@@ -217,10 +211,8 @@ public class BackupRestoreView: UIViewController, UITableViewDataSource, UITable
             }
             isBackupStart = true
             labelPreparing.text = "Preparing...".localized();
-            tableView.beginUpdates()
             tableView.reloadRows(at: [indexPath], with: .none)
             tableView.reloadSections(IndexSet(integer: 2), with: .none)
-            tableView.endUpdates()
             animateBackup()
             backupData(indexPath: indexPath)
         } else if indexPath.section == 2 {
@@ -236,10 +228,8 @@ public class BackupRestoreView: UIViewController, UITableViewDataSource, UITable
             }
             isRestoreStart = true
             labelRestoring.text = "Downloading...".localized();
-            tableView.beginUpdates()
             tableView.reloadRows(at: [indexPath, IndexPath(row: 1, section: 1)], with: .none)
             tableView.reloadSections(IndexSet(integer: 3), with: .none)
-            tableView.endUpdates()
             let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
             let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
             let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
@@ -664,10 +654,8 @@ public class BackupRestoreView: UIViewController, UITableViewDataSource, UITable
                 isRestoreStart = false
                 valueLastBackup = "-"
                 valuesizeBackup = "-"
-                tableView.beginUpdates()
                 tableView.reloadRows(at: [indexPath, IndexPath(row: 1, section: 1), IndexPath(row: 0, section: 0), IndexPath(row: 0, section: 2)], with: .none)
                 tableView.reloadSections(IndexSet(integer: 3), with: .none)
-                tableView.endUpdates()
             })
         } catch {
             //print(error)
@@ -679,10 +667,8 @@ public class BackupRestoreView: UIViewController, UITableViewDataSource, UITable
                 isRestoreStart = false
                 valueLastBackup = "-"
                 valuesizeBackup = "-"
-                tableView.beginUpdates()
                 tableView.reloadRows(at: [indexPath, IndexPath(row: 1, section: 1), IndexPath(row: 0, section: 0), IndexPath(row: 0, section: 2)], with: .none)
                 tableView.reloadSections(IndexSet(integer: 3), with: .none)
-                tableView.endUpdates()
             })
         }
     }
@@ -895,11 +881,14 @@ public class BackupRestoreView: UIViewController, UITableViewDataSource, UITable
     //                    self.labelPreparing.text = "Preparing...".localized() + " \(progress * 100)%"
     //                })
                     let unzipProgress = Progress()
-                    let observation = unzipProgress.observe(\.fractionCompleted) { progress, _ in
-                        self.labelPreparing.text = "Preparing...".localized() + " \(progress.fractionCompleted * 100)%"
+                    let _ = unzipProgress.observe(\.fractionCompleted) { progress, _ in
+                        DispatchQueue.main.async {
+                            self.labelPreparing.text = "Preparing...".localized() + " \(progress.fractionCompleted * 100)%"
+                        }
                     }
                     try fileManager.zipItem(at: file_message, to: zipFiles, progress: unzipProgress)
                     guard let archive = Archive(url: zipFiles, accessMode: .update) else  {
+                        print("Failed Archive")
                         return
                     }
                     do {
@@ -908,9 +897,11 @@ public class BackupRestoreView: UIViewController, UITableViewDataSource, UITable
                         try archive.addEntry(with: file_task_pic.lastPathComponent, relativeTo: file_task_pic.deletingLastPathComponent())
                         try archive.addEntry(with: file_task_detail.lastPathComponent, relativeTo: file_task_detail.deletingLastPathComponent())
                     } catch {
-                        //print("Adding entry to ZIP archive failed with error:\(error)")
+                        print("Adding entry to ZIP archive failed with error:\(error)")
+                    }
+                    DispatchQueue.main.async {
+                        self.labelPreparing.text = "Uploading...".localized()
                     }
-                    self.labelPreparing.text = "Uploading...".localized()
                     Network().uploadHTTP(fileUrl: zipFiles, completion: { result,progress in
                         if result {
                             DispatchQueue.main.async { [self] in
@@ -958,14 +949,21 @@ public class BackupRestoreView: UIViewController, UITableViewDataSource, UITable
                                         labelPreparing.text = "Successfully Backup Data".localized()
                                         DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { [self] in
                                             isBackupStart = false
-                                            tableView.beginUpdates()
                                             tableView.reloadRows(at: [indexPath, IndexPath(row: 0, section: 0)], with: .none)
                                             tableView.reloadSections(IndexSet(integer: 2), with: .none)
-                                            tableView.endUpdates()
                                         })
                                     } catch {}
                                 }
                             }
+                        }  else {
+                            DispatchQueue.main.async { [self] in
+                                labelPreparing.text = "Failed Upload Backup Data".localized()
+                                DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { [self] in
+                                    isBackupStart = false
+                                    tableView.reloadRows(at: [indexPath, IndexPath(row: 0, section: 0)], with: .none)
+                                    tableView.reloadSections(IndexSet(integer: 2), with: .none)
+                                })
+                            }
                         }
                     })
                 } catch {

+ 12 - 3
NexilisLite/NexilisLite/Source/View/Control/BroadcastViewController.swift

@@ -413,7 +413,10 @@ class BroadcastViewController: UITableViewController, UITextFieldDelegate, UITex
                 if result1 {
                     if progress == 100 {
                         do {
-                            try FileEncryption.shared.writeSecure(filename: String(self.thumbId))
+                            let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
+                            let fileURL = documentsDirectory.appendingPathComponent(self.thumbId)
+                            try FileEncryption.shared.writeSecure(filename: self.thumbId, data: Data(contentsOf: fileURL))
+                            try FileManager.default.removeItem(atPath: fileURL.path)
                         } catch {
                             
                         }
@@ -422,7 +425,10 @@ class BroadcastViewController: UITableViewController, UITextFieldDelegate, UITex
                                 if progress == 100 {
                                     self.sendMsg(startTime: startTime, endTime: endTime)
                                     do {
-                                        try FileEncryption.shared.writeSecure(filename: self.fileId)
+                                        let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
+                                        let fileURL = documentsDirectory.appendingPathComponent(self.fileId)
+                                        try FileEncryption.shared.writeSecure(filename: self.fileId, data: Data(contentsOf: fileURL))
+                                        try FileManager.default.removeItem(atPath: fileURL.path)
                                     } catch {
                                         
                                     }
@@ -438,7 +444,10 @@ class BroadcastViewController: UITableViewController, UITextFieldDelegate, UITex
                     if progress == 100 {
                         self.sendMsg(startTime: startTime, endTime: endTime)
                         do {
-                            try FileEncryption.shared.writeSecure(filename: self.fileId)
+                            let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
+                            let fileURL = documentsDirectory.appendingPathComponent(self.fileId)
+                            try FileEncryption.shared.writeSecure(filename: self.fileId, data: Data(contentsOf: fileURL))
+                            try FileManager.default.removeItem(atPath: fileURL.path)
                         } catch {
                             
                         }

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

@@ -7,7 +7,7 @@
 
 import UIKit
 
-class GroupCreateViewController: UITableViewController {
+class GroupCreateViewController: UITableViewController, UITextFieldDelegate {
 
     @IBOutlet weak var name: UITextField!
     @IBOutlet weak var subGroup: UITextField!
@@ -34,10 +34,18 @@ class GroupCreateViewController: UITableViewController {
         navigationItem.rightBarButtonItem?.isEnabled = false
         name.placeholder = "Title".localized() + "*"
         subGroup.placeholder = "Sub Group".localized() + " " + "(" + "Optional".localized() + ")"
+        name.delegate = self
+        subGroup.delegate = self
         
         name.addTarget(self, action: #selector(onTextChanged(sender:)), for: .editingChanged)
     }
     
+    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
+        guard let currentText = textField.text else { return true }
+        let newLength = currentText.count + string.count - range.length
+        return newLength <= 100
+    }
+    
     func submit(completion: @escaping (Bool) -> ()) {
         let groupName = self.name.text!
         DispatchQueue.global().async {

+ 25 - 8
NexilisLite/NexilisLite/Source/View/Control/GroupDetailViewController.swift

@@ -9,7 +9,7 @@ import UIKit
 import NotificationBannerSwift
 import nuSDKService
 
-class GroupDetailViewController: UITableViewController {
+class GroupDetailViewController: UITableViewController, UITextFieldDelegate {
     
     enum Flag {
         case edit
@@ -58,6 +58,9 @@ class GroupDetailViewController: UITableViewController {
     
     var fromNotification = false
     
+    var alertTextFieldSubGroup: UITextField?
+    var alertTextFieldTopic: UITextField?
+    
     override func viewDidLoad() {
         super.viewDidLoad()
         
@@ -157,6 +160,8 @@ class GroupDetailViewController: UITableViewController {
         self.textFields.removeAll()
         self.alert2?.addTextField{ (texfield) in
             texfield.placeholder = "Group's Name".localized()
+            texfield.delegate = self
+            self.alertTextFieldSubGroup = texfield
             texfield.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControl.Event.editingChanged)
         }
         let submitAction = UIAlertAction(title: "Create".localized(), style: .default, handler: { (action) -> Void in
@@ -197,6 +202,19 @@ class GroupDetailViewController: UITableViewController {
         self.present(self.alert2!, animated: true, completion: nil)
     }
     
+    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
+        if textField == alertTextFieldSubGroup || textField == alertTextFieldTopic {
+            guard let currentText = textField.text,
+                  let stringRange = Range(range, in: currentText) else {
+                return false
+            }
+
+            let updatedText = currentText.replacingCharacters(in: stringRange, with: string)
+            return updatedText.count <= (textField == alertTextFieldSubGroup ? 100 : 50)
+        }
+        return false
+    }
+    
     func edit() {
         let controller = AppStoryBoard.Palio.instance.instantiateViewController(identifier: "groupNameView") as! GroupNameViewController
         controller.data = self.data
@@ -404,6 +422,8 @@ class GroupDetailViewController: UITableViewController {
                     self.alert2?.addTextField{ (texfield) in
                         texfield.text = topic.title
                         texfield.placeholder = "Topic's Name"
+                        texfield.delegate = self
+                        self.alertTextFieldTopic = texfield
                         texfield.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControl.Event.editingChanged)
                     }
                     let submitAction = UIAlertAction(title: "Rename".localized(), style: .default, handler: { (action) -> Void in
@@ -465,10 +485,8 @@ class GroupDetailViewController: UITableViewController {
                                 data["topicId"] = topic.chatId
                                 NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onTopic"), object: nil, userInfo: data)
                                 DispatchQueue.main.async {
-                                    tableView.beginUpdates()
                                     tableView.deleteRows(at: [indexPath], with: .none)
                                     g.topics.remove(at: index)
-                                    tableView.endUpdates()
                                 }
                             }
                         }
@@ -574,10 +592,8 @@ class GroupDetailViewController: UITableViewController {
                             self.changePosition(pin: member.pin, isAdmin: member.position == "0") { result in
                                 if result {
                                     DispatchQueue.main.async {
-                                        tableView.beginUpdates()
                                         member.position = member.position == "1" ? "0" : "1"
                                         tableView.reloadRows(at: [indexPath], with: .none)
-                                        tableView.endUpdates()
                                     }
                                 }
                             }
@@ -594,10 +610,8 @@ class GroupDetailViewController: UITableViewController {
                             self.exitGroup(pin: member.pin) { result in
                                 if result, let index = g.members.firstIndex(of: member) {
                                     DispatchQueue.main.async {
-                                        tableView.beginUpdates()
                                         tableView.deleteRows(at: [indexPath], with: .none)
                                         g.members.remove(at: index)
-                                        tableView.endUpdates()
                                     }
                                 }
                             }
@@ -1068,7 +1082,10 @@ extension GroupDetailViewController: ImageVideoPickerDelegate {
                             return
                         }
                         do {
-                            try FileEncryption.shared.writeSecure(filename: fileDir.lastPathComponent)
+                            let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
+                            let fileURL = documentsDirectory.appendingPathComponent(fileDir.lastPathComponent)
+                            try FileEncryption.shared.writeSecure(filename: fileDir.lastPathComponent, data: Data(contentsOf: fileURL))
+                            try FileManager.default.removeItem(atPath: fileURL.path)
                         } catch {
                             
                         }

+ 9 - 4
NexilisLite/NexilisLite/Source/View/Control/GroupMemberViewController.swift

@@ -66,6 +66,7 @@ class GroupMemberViewController: UITableViewController {
     }
     
     @objc func add(sender: Any) {
+        Nexilis.showLoader()
         DispatchQueue.global().async {
             Database.shared.database?.inTransaction({ fmdb, rollback in
                 do {
@@ -101,11 +102,15 @@ class GroupMemberViewController: UITableViewController {
                     
                     DispatchQueue.main.async {
                         if self.userSelected.count == result {
-                            self.navigationController?.dismiss(animated: true, completion: {
-                                self.isDismiss?()
-                            })
+                            Nexilis.hideLoader() {
+                                self.navigationController?.dismiss(animated: true, completion: {
+                                    self.isDismiss?()
+                                })
+                            }
                         } else {
-                            self.view.makeToast("Server busy, please try again later".localized(), duration: 3)
+                            Nexilis.hideLoader() {
+                                self.view.makeToast("Server busy, please try again later".localized(), duration: 3)
+                            }
                         }
                     }
                 } catch {

+ 9 - 2
NexilisLite/NexilisLite/Source/View/Control/GroupNameViewController.swift

@@ -7,7 +7,7 @@
 
 import UIKit
 
-class GroupNameViewController: UITableViewController {
+class GroupNameViewController: UITableViewController, UITextFieldDelegate {
 
     @IBOutlet weak var textField: UITextField!
     
@@ -27,10 +27,17 @@ class GroupNameViewController: UITableViewController {
         navigationItem.rightBarButtonItem?.isEnabled = false
         
         textField.text = name
+        textField.delegate = self
         textField.addTarget(self, action: #selector(didChanged(sender:)), for: .editingChanged)
         textField.placeholder = "Name".localized()
         
     }
+    
+    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
+        guard let currentText = textField.text else { return true }
+        let newLength = currentText.count + string.count - range.length
+        return newLength <= 100
+    }
 
     @objc func didChanged(sender: Any) {
         if let text = textField.text, text.trimmingCharacters(in: .whitespaces).isEmpty || text.lowercased() == name?.lowercased() {
@@ -44,7 +51,7 @@ class GroupNameViewController: UITableViewController {
         if let text = textField.text {
             DispatchQueue.global().async {
                 if let resp = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getChangeGroupInfo(p_group_id: self.data, p_name: text)) {
-                    if resp.isOk() {
+                    if resp.isOk() && !self.data.isEmpty {
                         Database.shared.database?.inTransaction({ fmdb, rollback in
                             do {
                                 _ = Database.shared.updateRecord(fmdb: fmdb, table: "GROUPZ", cvalues: ["f_name": text], _where: "group_id = '\(self.data)'")

+ 8 - 1
NexilisLite/NexilisLite/Source/View/Control/GroupTopicViewController.swift

@@ -7,7 +7,7 @@
 
 import UIKit
 
-class GroupTopicViewController: UITableViewController {
+class GroupTopicViewController: UITableViewController, UITextFieldDelegate {
 
     @IBOutlet weak var topic: UITextField!
     
@@ -26,6 +26,13 @@ class GroupTopicViewController: UITableViewController {
         
         topic.addTarget(self, action: #selector(didChanged(sender:)), for: .editingChanged)
         topic.placeholder = "Name".localized()
+        topic.delegate = self
+    }
+    
+    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
+        guard let currentText = textField.text else { return true }
+        let newLength = currentText.count + string.count - range.length
+        return newLength <= 50
     }
 
     @objc func didChanged(sender: Any) {

+ 4 - 1
NexilisLite/NexilisLite/Source/View/Control/ProfileViewController.swift

@@ -898,7 +898,10 @@ extension ProfileViewController: ImageVideoPickerDelegate {
                             return
                         }
                         do {
-                            try FileEncryption.shared.writeSecure(filename: fileDir.lastPathComponent)
+                            let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
+                            let fileURL = documentsDirectory.appendingPathComponent(fileDir.lastPathComponent)
+                            try FileEncryption.shared.writeSecure(filename: fileDir.lastPathComponent, data: Data(contentsOf: fileURL))
+                            try FileManager.default.removeItem(atPath: fileURL.path)
                         } catch {
                             
                         }

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

@@ -324,7 +324,7 @@ public class SettingTableViewController: UITableViewController, UIGestureRecogni
             Item(icon: UIImage(systemName: "message"), title: "Notification Message(s)".localized()),
             Item(icon: UIImage(systemName: "message"), title: "Notification Message(s) Group".localized()),
             Item(icon: UIImage(systemName: "iphone.homebutton.radiowaves.left.and.right"), title: "Vibrate Mode".localized()),
-            Item(icon: UIImage(systemName: "photo.on.rectangle.angled"), title: "Save to Gallery".localized()),
+//            Item(icon: UIImage(systemName: "photo.on.rectangle.angled"), title: "Save to Gallery".localized()),
             Item(icon: UIImage(systemName: "arrow.down.square"), title: "Auto Download".localized()),
         ]
         Item.menus["Version"] = [