alqindiirsyam 2 years ago
parent
commit
d4e1c23d28
25 changed files with 1708 additions and 769 deletions
  1. 12 6
      appbuilder-ios/AppBuilder/AppBuilder/FourthTabViewController.swift
  2. 1 0
      appbuilder-ios/NexilisLite/NexilisLite.podspec
  3. BIN
      appbuilder-ios/NexilisLite/NexilisLite/Resource/Assets.xcassets/ic_internal_cc.imageset/ic_internal_cc.png
  4. 15 0
      appbuilder-ios/NexilisLite/NexilisLite/Resource/Palio.storyboard
  5. 12 0
      appbuilder-ios/NexilisLite/NexilisLite/Resource/id.lproj/Localizable.strings
  6. 32 1
      appbuilder-ios/NexilisLite/NexilisLite/Source/CoreMessage_TMessageBank.swift
  7. 3 0
      appbuilder-ios/NexilisLite/NexilisLite/Source/CoreMessage_TMessageCode.swift
  8. 1 0
      appbuilder-ios/NexilisLite/NexilisLite/Source/CoreMessage_TMessageKey.swift
  9. 27 11
      appbuilder-ios/NexilisLite/NexilisLite/Source/Database.swift
  10. 39 277
      appbuilder-ios/NexilisLite/NexilisLite/Source/Extension.swift
  11. 49 40
      appbuilder-ios/NexilisLite/NexilisLite/Source/IncomingThread.swift
  12. 80 0
      appbuilder-ios/NexilisLite/NexilisLite/Source/Network.swift
  13. 45 0
      appbuilder-ios/NexilisLite/NexilisLite/Source/Units.swift
  14. 2 0
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift
  15. 21 21
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/FormEditor.swift
  16. 4 4
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/AudienceViewController.swift
  17. 85 0
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/BackupRestoreOption.swift
  18. 940 0
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/BackupRestoreView.swift
  19. 3 3
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/BroadcastModeViewController.swift
  20. 4 1
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ChangeDeviceViewController.swift
  21. 1 1
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ProfileViewController.swift
  22. 319 391
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/SettingTableViewController.swift
  23. 3 3
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/TypeViewController.swift
  24. 2 2
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Streaming/CreateViewController.swift
  25. 8 8
      appbuilder-ios/NexilisLite/NexilisLite/Source/View/Streaming/QmeraCreateStreamingViewController.swift

+ 12 - 6
appbuilder-ios/AppBuilder/AppBuilder/FourthTabViewController.swift

@@ -152,27 +152,28 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
                 }
                 if ( User.isInternal(userType: cursorUser.string(forColumnIndex: 0) ?? "") && position == "1") || User.isOfficial(official_account: cursorUser.string(forColumnIndex: 2) ?? "") || User.isOfficial(official_account: cursorUser.string(forColumnIndex: 2) ?? "") {
                     Item.menus["Personal"] = [
-                        Item(icon: UIImage(systemName: "person.fill"), title: "Personal Information".localized()),
+                        Item(icon: UIImage(systemName: "person"), title: "Personal Information".localized()),
                         Item(icon: UIImage(systemName: "textformat.abc"), title: "Change Language".localized()),
                         Item(icon: UIImage(systemName: "person.crop.rectangle"), title: "Change Admin / Internal Password".localized()),
                         Item(icon: UIImage(systemName: "laptopcomputer.and.iphone"), title: "Sign-In to Web".localized()),
                     ]
                 } else if User.isInternal(userType: cursorUser.string(forColumnIndex: 0) ?? "") || User.isCallCenter(userType: cursorUser.string(forColumnIndex: 0) ?? "") || User.isVerified(official_account: cursorUser.string(forColumnIndex: 2) ?? "") {
                     Item.menus["Personal"] = [
-                        Item(icon: UIImage(systemName: "person.fill"), title: "Personal Information".localized()),
+                        Item(icon: UIImage(systemName: "person"), title: "Personal Information".localized()),
                         Item(icon: UIImage(systemName: "textformat.abc"), title: "Change Language".localized()),
                         Item(icon: UIImage(systemName: "laptopcomputer.and.iphone"), title: "Sign-In to Web".localized()),
                     ]
                 } else {
                     Item.menus["Personal"] = [
-                        Item(icon: UIImage(systemName: "person.fill"), title: "Personal Information".localized()),
+                        Item(icon: UIImage(systemName: "person"), title: "Personal Information".localized()),
                             Item(icon: UIImage(systemName: "textformat.abc"), title: "Change Language".localized()),
-                        Item(icon: UIImage(systemName: "person.crop.rectangle"), title: "Access Admin / Internal Features".localized()),
+                        Item(icon: UIImage(named: "ic_internal", in: Bundle.resourceBundle(for: Nexilis.self), with: nil)!, title: "Access Admin / Internal Features".localized()),
                     ]
                 }
                 if !isChangeProfile {
                     Item.menus["Personal"]?.append(Item(icon: UIImage(systemName: "arrow.up.and.person.rectangle.portrait"), title: "Sign-Up/Sign-In".localized()))
                 } else if isChangeProfile {
+                    Item.menus["Personal"]?.append(Item(icon: UIImage(systemName: "arrow.clockwise.icloud"), title: "Backup & Restore".localized()))
                     Item.menus["Personal"]?.append(Item(icon: UIImage(systemName: "rectangle.portrait.and.arrow.right"), title: "Sign-Out".localized()))
                 }
                 let image = cursorUser.string(forColumnIndex: 1)
@@ -213,9 +214,9 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
                 cursorUser.close()
             } else {
                 Item.menus["Personal"] = [
-                    Item(icon: UIImage(systemName: "person.fill"), title: "Personal Information".localized()),
+                    Item(icon: UIImage(systemName: "person"), title: "Personal Information".localized()),
                         Item(icon: UIImage(systemName: "textformat.abc"), title: "Change Language".localized()),
-                    Item(icon: UIImage(systemName: "person.crop.rectangle"), title: "Access Admin / Internal Features".localized()),
+                    Item(icon: UIImage(named: ""), title: "Access Admin / Internal Features".localized()),
                     Item(icon: UIImage(systemName: "arrow.up.and.person.rectangle.portrait"), title: "Sign-Up/Sign-In".localized())
                 ]
                 if !imageSignIn.isEmpty {
@@ -347,6 +348,8 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
                 cell.accessoryType = .disclosureIndicator
             case "Notification Message(s)".localized():
                 cell.accessoryType = .disclosureIndicator
+            case "Backup & Restore".localized():
+                cell.accessoryType = .disclosureIndicator
             case "Notification Message(s) Group".localized():
                 cell.accessoryType = .disclosureIndicator
 //            case "Logout".localized():
@@ -629,6 +632,9 @@ public class FourthTabViewController: UIViewController, UITableViewDelegate, UIT
             navigationController.navigationBar.titleTextAttributes = textAttributes
             navigationController.view.backgroundColor = .mainColor
             self.present(navigationController, animated: true)
+        } else if item.title == "Backup & Restore".localized() {
+            let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "backupRestore") as! BackupRestoreView
+            navigationController?.show(controller, sender: nil)
         }
     }
     

+ 1 - 0
appbuilder-ios/NexilisLite/NexilisLite.podspec

@@ -30,6 +30,7 @@ Pod::Spec.new do |spec|
   spec.dependency 'Alamofire', '~> 5.5'
   spec.dependency 'SDWebImage', '~> 5.0'
   spec.dependency 'Toast-Swift', '~> 5.0.1'
+  spec.dependency 'Zip', '~> 2.1'
   # spec.dependency 'iOS-WebP'
   # spec.preserve_path = 'NexilisLite.framework'
   # spec.xcconfig = { 'OTHER_LDFLAGS' => '-framework NexilisLite' }

BIN
appbuilder-ios/NexilisLite/NexilisLite/Resource/Assets.xcassets/ic_internal_cc.imageset/ic_internal_cc.png


+ 15 - 0
appbuilder-ios/NexilisLite/NexilisLite/Resource/Palio.storyboard

@@ -2323,6 +2323,21 @@
             </objects>
             <point key="canvasLocation" x="2779.7101449275365" y="3004.6875"/>
         </scene>
+        <!--Backup Restore View-->
+        <scene sceneID="hPU-qg-gQ2">
+            <objects>
+                <viewController storyboardIdentifier="backupRestore" id="uSM-i8-u9D" customClass="BackupRestoreView" customModule="NexilisLite" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="4qZ-7U-qA8">
+                        <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <viewLayoutGuide key="safeArea" id="J5B-BM-rF4"/>
+                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="0Xk-4f-4yy" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="870" y="3008"/>
+        </scene>
         <!--Broadcast Navigation Controller-->
         <scene sceneID="Wbo-YH-Wwm">
             <objects>

+ 12 - 0
appbuilder-ios/NexilisLite/NexilisLite/Resource/id.lproj/Localizable.strings

@@ -261,3 +261,15 @@
 "You have requested Call Center, please wait for response." = "Anda telah meminta Pusat Panggilan, harap tunggu tanggapan.";
 "Audio Call Ended" = "Panggilan Suara telah berakhir";
 "Live Streaming session hasn\'t started yet" = "Sesi Siaran Langsung belum dimulai";
+"Backup & Restore" = "Cadangkan & Pulihkan";
+"Last Backup" = "Cadangan terakhir";
+"Total Size" = "Ukuran Keseluruhan";
+"Back up your chat history to server so if you lose your phone or switch to a new one or logout your account, your chat history is safe. You can restore your chat history when you relogin your account." = "Cadangkan riwayat obrolan Anda ke server jadi jika Anda kehilangan ponsel atau beralih ke yang baru atau keluar dari akun Anda, riwayat obrolan Anda aman. Anda dapat memulihkan riwayat obrolan saat Anda masuk kembali ke akun Anda.";
+"Back Up Now" = "Cadangkan Sekarang";
+"Backing Up..." = "Mencadangkan...";
+"Back Up Option" = "Opsi Pencadangan";
+"Restore Now" = "Pulihkan Sekarang";
+"a month" = "sebulan";
+"3 months" = "3 bulan";
+"a week" = "seminggu";
+"Forever" = "selamanya";

+ 32 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/CoreMessage_TMessageBank.swift

@@ -2091,6 +2091,37 @@ public class CoreMessage_TMessageBank {
         tmessage.mPIN = UserDefaults.standard.string(forKey: "me")!
         tmessage.mBodies[CoreMessage_TMessageKey.NAME] = p_name
         tmessage.mBodies[CoreMessage_TMessageKey.PASSWORD] = p_password
-        return tmessage;
+        return tmessage
+    }
+    
+    public static func getBackupAvailability() -> TMessage {
+        let tmessage = TMessage()
+        tmessage.mCode    = CoreMessage_TMessageCode.BACKUP_AVAILABILITY
+        tmessage.mStatus  = CoreMessage_TMessageUtil.getTID()
+        tmessage.mPIN     = UserDefaults.standard.string(forKey: "me")!
+        return tmessage
+    }
+    
+    public static func getBackupUploaded(option: String, fileid: String, filesize: String, recordSize: String) -> TMessage {
+        let tmessage = TMessage()
+        tmessage.mCode    = CoreMessage_TMessageCode.BACKUP_UPLOADED
+        tmessage.mStatus  = CoreMessage_TMessageUtil.getTID()
+        tmessage.mPIN     = UserDefaults.standard.string(forKey: "me")!
+        tmessage.mBodies[CoreMessage_TMessageKey.TYPE] =  option
+        tmessage.mBodies[CoreMessage_TMessageKey.FILE_ID] = fileid
+        tmessage.mBodies[CoreMessage_TMessageKey.FILE_SIZE] = filesize
+        tmessage.mBodies[CoreMessage_TMessageKey.CREATED_DATE] = "\(Date().currentTimeMillis())"
+        tmessage.mBodies[CoreMessage_TMessageKey.RECORD_SIZE] = recordSize
+        return tmessage
+    }
+    
+    public static func getBackupRestored(option: String , fileid: String) -> TMessage {
+        let tmessage = TMessage()
+        tmessage.mCode    = CoreMessage_TMessageCode.BACKUP_RESTORED
+        tmessage.mStatus  = CoreMessage_TMessageUtil.getTID()
+        tmessage.mPIN     = UserDefaults.standard.string(forKey: "me")!
+        tmessage.mBodies[CoreMessage_TMessageKey.TYPE] = option
+        tmessage.mBodies[CoreMessage_TMessageKey.FILE_ID] = fileid
+        return tmessage
     }
 }

+ 3 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/CoreMessage_TMessageCode.swift

@@ -729,4 +729,7 @@ public class CoreMessage_TMessageCode {
     public static let MOBILE_INQUIRY = "MINQ";
     
     public static let SIGN_UP_AND_SIGN_IN_API = "SUAI01";
+    public static let BACKUP_AVAILABILITY = "BUA2";
+    public static let BACKUP_UPLOADED = "BUU2";
+    public static let BACKUP_RESTORED = "BUR2";
 }

+ 1 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/CoreMessage_TMessageKey.swift

@@ -141,6 +141,7 @@ public class CoreMessage_TMessageKey {
     public static let PART_SIZE         = "A53A"
     public static let PART_OF           = "A53B"
     public static let FILE_SIZE         = "A53C"
+    public static let RECORD_SIZE       = "A53D"
     public static let OFFMP              = "A54"
     public static let NETWORK_TYPE         = "A55"
     public static let RADIUS             = "A56"

+ 27 - 11
appbuilder-ios/NexilisLite/NexilisLite/Source/Database.swift

@@ -369,23 +369,39 @@ public class Database {
         "'requester' text" +
         ")", values: nil)
         
-        try fmdb.executeUpdate("CREATE TABLE IF NOT EXISTS 'FORM' (" +
+        try fmdb.executeUpdate("CREATE TABLE IF NOT EXISTS 'FORM_DATA' (" +
         "'id' integer PRIMARY KEY AUTOINCREMENT NOT NULL," +
-        "'form_id' TEXT NOT NULL UNIQUE," +
-        "'name' text," +
-        "'created_date' text," +
+        "'reff_id' TEXT NOT NULL UNIQUE," +
+        "'form_id' TEXT," +
+        "'title' text," +
+        "'description' text," +
         "'created_by' text," +
-        "'sq_no' integer" +
+        "'created_date' text," +
+        "'target_date' text," +
+        "'status' integer" +
         ")", values: nil)
         
-        try fmdb.executeUpdate("CREATE TABLE IF NOT EXISTS 'FORM_ITEM' (" +
+        try fmdb.executeUpdate("CREATE TABLE IF NOT EXISTS 'TASK_PIC' (" +
         "'id' integer PRIMARY KEY AUTOINCREMENT NOT NULL," +
-        "'form_id' text," +
+        "'reff_id' text," +
+        "'sq_no' integer," +
+        "'ac' text," +
+        "'ac_desc' text," +
+        "'f_pin' text," +
+        "'submit_date' text," +
+        "'submit_type' text," +
+        "'submit_info' text," +
+        "'submit_id' text)", values: nil)
+        
+        try fmdb.executeUpdate("CREATE INDEX IF NOT EXISTS TASK_PIC_UK1 on TASK_PIC (reff_id,submit_date,f_pin,submit_info)", values: nil)
+        
+        try fmdb.executeUpdate("CREATE TABLE IF NOT EXISTS 'TASK_DETAIL' (" +
+        "'id' integer PRIMARY KEY AUTOINCREMENT NOT NULL," +
+        "'reff_id' text," +
         "'key' text," +
-        "'label' text," +
-        "'value' text," +
-        "'type' text," +
-        "'sq_no' integer)", values: nil)
+        "'value' text)", values: nil)
+        
+        try fmdb.executeUpdate("CREATE INDEX IF NOT EXISTS TASK_DETAIL_UK1 on TASK_DETAIL (reff_id,key)", values: nil)
         
         try fmdb.executeUpdate("CREATE TABLE IF NOT EXISTS 'SERVICE_BANK' (" +
         "'id' integer PRIMARY KEY AUTOINCREMENT NOT NULL," +

+ 39 - 277
appbuilder-ios/NexilisLite/NexilisLite/Source/Extension.swift

@@ -302,8 +302,9 @@ extension NSObject {
             _ = Database.shared.deleteRecord(fmdb: fmdb, table: "PULL_DB", _where: "")
             _ = Database.shared.deleteRecord(fmdb: fmdb, table: "PREFS", _where: "")
             _ = Database.shared.deleteRecord(fmdb: fmdb, table: "CALL_CENTER_HISTORY", _where: "")
-            _ = Database.shared.deleteRecord(fmdb: fmdb, table: "FORM", _where: "")
-            _ = Database.shared.deleteRecord(fmdb: fmdb, table: "FORM_ITEM", _where: "")
+            _ = Database.shared.deleteRecord(fmdb: fmdb, table: "FORM_DATA", _where: "")
+            _ = Database.shared.deleteRecord(fmdb: fmdb, table: "TASK_PIC", _where: "")
+            _ = Database.shared.deleteRecord(fmdb: fmdb, table: "TASK_DETAIL", _where: "")
         })
     }
     
@@ -878,6 +879,18 @@ extension UINavigationController {
     var rootViewController : UIViewController? {
         return viewControllers.first
     }
+    
+    func popViewController(animated: Bool, completion: @escaping () -> Void) {
+        popViewController(animated: animated)
+
+        if animated, let coordinator = transitionCoordinator {
+            coordinator.animate(alongsideTransition: nil) { _ in
+                completion()
+            }
+        } else {
+            completion()
+        }
+    }
 }
 
 extension UIImageView {
@@ -1075,278 +1088,27 @@ extension UISearchBar
     }
 }
 
-//let alert = UIAlertController(title: "", message: "\n\n\n\n\n\n\n\n\n\n".localized(), preferredStyle: .alert)
-//let newWidth = UIScreen.main.bounds.width * 0.90 - 270
-//// update width constraint value for main view
-//if let viewWidthConstraint = alert.view.constraints.filter({ return $0.firstAttribute == .width }).first{
-//    viewWidthConstraint.constant = newWidth
-//}
-//// update width constraint value for container view
-//if let containerViewWidthConstraint = alert.view.subviews.first?.constraints.filter({ return $0.firstAttribute == .width }).first {
-//    containerViewWidthConstraint.constant = newWidth
-//}
-//let titleFont = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18), NSAttributedString.Key.foregroundColor: UIColor.black]
-//let titleAttrString = NSMutableAttributedString(string: m["MERNAM"]!.localized(), attributes: titleFont)
-//alert.setValue(titleAttrString, forKey: "attributedTitle")
-//alert.view.subviews.first?.subviews.first?.subviews.first?.backgroundColor = .lightGray
-//alert.view.tintColor = .black
-//if fileType != BroadcastViewController.FILE_TYPE_CHAT{
-//    let height:NSLayoutConstraint = NSLayoutConstraint(item: alert.view!, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: UIScreen.main.bounds.height * 0.6)
-//    alert.view.addConstraint(height)
-//}
-//
-//var containerView = UIView(frame: CGRect(x: 20, y: 60, width: alert.view.bounds.size.width * 0.9 - 40, height: 350))
-//if fileType == BroadcastViewController.FILE_TYPE_CHAT {
-//    containerView = UIView(frame: CGRect(x: 20, y: 60, width: alert.view.bounds.size.width * 0.9 - 40, height: 100))
-//}
-//alert.view.addSubview(containerView)
-//containerView.layer.cornerRadius = 10.0
-//containerView.clipsToBounds = true
-//
-//let buttonClose = UIButton(type: .close)
-//buttonClose.frame = CGRect(x: alert.view.bounds.size.width * 0.9 - 50, y: 15, width: 30, height: 30)
-//buttonClose.layer.cornerRadius = 15.0
-//buttonClose.clipsToBounds = true
-//buttonClose.backgroundColor = .secondaryColor.withAlphaComponent(0.5)
-//buttonClose.actionHandle(controlEvents: .touchUpInside,
-// ForAction:{() -> Void in
-//    alert.dismiss(animated: true, completion: nil)
-// })
-//alert.view.addSubview(buttonClose)
-//
-//let titleBroadcast = UILabel()
-//containerView.addSubview(titleBroadcast)
-//titleBroadcast.translatesAutoresizingMaskIntoConstraints = false
-//NSLayoutConstraint.activate([
-//    titleBroadcast.topAnchor.constraint(equalTo: containerView.topAnchor),
-//    titleBroadcast.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
-//    titleBroadcast.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
-//])
-//titleBroadcast.font = UIFont.systemFont(ofSize: 18)
-//titleBroadcast.numberOfLines = 0
-//titleBroadcast.text = m[CoreMessage_TMessageKey.TITLE]
-//titleBroadcast.textColor = .black
-//
-//let descBroadcast = UILabel()
-//containerView.addSubview(descBroadcast)
-//descBroadcast.translatesAutoresizingMaskIntoConstraints = false
-//NSLayoutConstraint.activate([
-//    descBroadcast.topAnchor.constraint(equalTo: titleBroadcast.bottomAnchor, constant: 10),
-//    descBroadcast.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
-//    descBroadcast.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
-//])
-//descBroadcast.font = UIFont.systemFont(ofSize: 15)
-//descBroadcast.numberOfLines = 0
-//descBroadcast.text = m[CoreMessage_TMessageKey.MESSAGE_TEXT_ENG]
-//descBroadcast.textColor = .black
-//
-//let stringLink = m[CoreMessage_TMessageKey.LINK] ?? ""
-//let linkBroadcast = UILabel()
-//if !stringLink.isEmpty {
-//    containerView.addSubview(linkBroadcast)
-//    linkBroadcast.translatesAutoresizingMaskIntoConstraints = false
-//    NSLayoutConstraint.activate([
-//        linkBroadcast.topAnchor.constraint(equalTo: descBroadcast.bottomAnchor, constant: 10),
-//        linkBroadcast.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
-//        linkBroadcast.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
-//    ])
-//    linkBroadcast.font = UIFont.systemFont(ofSize: 15)
-//    linkBroadcast.isUserInteractionEnabled = true
-//    linkBroadcast.numberOfLines = 0
-//    let attributedString = NSMutableAttributedString(string: stringLink, attributes:[NSAttributedString.Key.link: URL(string: stringLink)!])
-//    linkBroadcast.attributedText = attributedString
-//    let tap = ObjectGesture(target: self, action: #selector(tapLinkBroadcast))
-//    tap.message_id = stringLink
-//    linkBroadcast.addGestureRecognizer(tap)
-//}
-//
-//let dottedLine = UIView()
-//containerView.addSubview(dottedLine)
-//dottedLine.translatesAutoresizingMaskIntoConstraints = false
-//var constraintDottedLine = dottedLine.topAnchor.constraint(equalTo: descBroadcast.bottomAnchor, constant: 20)
-//if !stringLink.isEmpty{
-//    constraintDottedLine = dottedLine.topAnchor.constraint(equalTo: linkBroadcast.bottomAnchor, constant: 20)
-//}
-//NSLayoutConstraint.activate([
-//    constraintDottedLine,
-//    dottedLine.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
-//    dottedLine.widthAnchor.constraint(equalToConstant: alert.view.bounds.size.width * 0.9 - 40),
-//    dottedLine.heightAnchor.constraint(equalToConstant: 2)
-//])
-//dottedLine.backgroundColor = .black.withAlphaComponent(0.1)
-//let shapeLayer = CAShapeLayer()
-//shapeLayer.strokeColor = UIColor.black.withAlphaComponent(0.2).cgColor
-//shapeLayer.lineWidth = 2
-//// passing an array with the values [2,3] sets a dash pattern that alternates between a 2-user-space-unit-long painted segment and a 3-user-space-unit-long unpainted segment
-//shapeLayer.lineDashPattern = [2,3]
-//
-//let path = CGMutablePath()
-//path.addLines(between: [CGPoint(x: 0, y: 0),
-//                        CGPoint(x: alert.view.bounds.size.width * 0.9 - 20, y: 0)])
-//shapeLayer.path = path
-//dottedLine.layer.addSublayer(shapeLayer)
-//
-//let thumb = m[CoreMessage_TMessageKey.THUMB_ID] ?? ""
-//let image = m[CoreMessage_TMessageKey.IMAGE_ID] ?? ""
-//let video = m[CoreMessage_TMessageKey.VIDEO_ID] ?? ""
-//let file = m[CoreMessage_TMessageKey.FILE_ID] ?? ""
-//if fileType != BroadcastViewController.FILE_TYPE_CHAT {
-//    let imageBroadcast = UIImageView()
-//    containerView.addSubview(imageBroadcast)
-//    imageBroadcast.translatesAutoresizingMaskIntoConstraints = false
-//    NSLayoutConstraint.activate([
-//        imageBroadcast.topAnchor.constraint(equalTo: dottedLine.bottomAnchor, constant: 20),
-//        imageBroadcast.widthAnchor.constraint(equalToConstant: alert.view.bounds.size.width * 0.9 - 40),
-//        imageBroadcast.heightAnchor.constraint(equalToConstant: 250)
-//    ])
-//    imageBroadcast.layer.cornerRadius = 10.0
-//    imageBroadcast.clipsToBounds = true
-//    if fileType != BroadcastViewController.FILE_TYPE_DOCUMENT {
-//        imageBroadcast.contentMode = .scaleAspectFill
-//        imageBroadcast.setImage(name: thumb)
-//
-//        if fileType == BroadcastViewController.FILE_TYPE_VIDEO {
-//            let imagePlay = UIImageView(image: UIImage(systemName: "play.circle.fill"))
-//            imageBroadcast.addSubview(imagePlay)
-//            imagePlay.clipsToBounds = true
-//            imagePlay.translatesAutoresizingMaskIntoConstraints = false
-//            imagePlay.centerYAnchor.constraint(equalTo: imageBroadcast.centerYAnchor).isActive = true
-//            imagePlay.centerXAnchor.constraint(equalTo: imageBroadcast.centerXAnchor).isActive = true
-//            imagePlay.widthAnchor.constraint(equalToConstant: 60).isActive = true
-//            imagePlay.heightAnchor.constraint(equalToConstant: 60).isActive = true
-//            imagePlay.tintColor = .gray.withAlphaComponent(0.5)
-//        }
-//    } else {
-//        imageBroadcast.image = UIImage(systemName: "doc.fill")
-//        imageBroadcast.tintColor = .mainColor
-//        imageBroadcast.contentMode = .scaleAspectFit
-//    }
-//
-//    imageBroadcast.actionHandle(controlEvents: .touchUpInside,
-//     ForAction:{() -> Void in
-//        let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
-//        let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
-//        let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true)
-//        if fileType == BroadcastViewController.FILE_TYPE_IMAGE {
-//            if let dirPath = paths.first {
-//                let imageURL = URL(fileURLWithPath: dirPath).appendingPathComponent(image)
-//                if FileManager.default.fileExists(atPath: imageURL.path) {
-//                    let image    = UIImage(contentsOfFile: imageURL.path)
-//                    let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
-//                    previewImageVC.image = image
-//                    previewImageVC.isHiddenTextField = true
-//                    previewImageVC.modalPresentationStyle = .overFullScreen
-//                    previewImageVC.modalTransitionStyle  = .crossDissolve
-//                    let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
-//                    if checkViewController != nil {
-//                        UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.present(previewImageVC, animated: true, completion: nil)
-//                    } else {
-//                        UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.present(previewImageVC, animated: true, completion: nil)
-//                    }
-//                } else {
-//                    Download().start(forKey: image) { (name, progress) in
-//                        guard progress == 100 else {
-//                            return
-//                        }
-//
-//                        DispatchQueue.main.async {
-//                            let image    = UIImage(contentsOfFile: imageURL.path)
-//                            let previewImageVC = PreviewAttachmentImageVideo(nibName: "PreviewAttachmentImageVideo", bundle: Bundle.resourceBundle(for: Nexilis.self))
-//                            previewImageVC.image = image
-//                            previewImageVC.isHiddenTextField = true
-//                            previewImageVC.modalPresentationStyle = .overFullScreen
-//                            previewImageVC.modalTransitionStyle  = .crossDissolve
-//                            let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
-//                            if checkViewController != nil {
-//                                UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.present(previewImageVC, animated: true, completion: nil)
-//                            } else {
-//                                UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.present(previewImageVC, animated: true, completion: nil)
-//                            }
-//                        }
-//                    }
-//                }
-//            }
-//        } else if fileType == BroadcastViewController.FILE_TYPE_VIDEO {
-//            if let dirPath = paths.first {
-//                let videoURL = URL(fileURLWithPath: dirPath).appendingPathComponent(video)
-//                if FileManager.default.fileExists(atPath: videoURL.path) {
-//                    let player = AVPlayer(url: videoURL as URL)
-//                    let playerVC = AVPlayerViewController()
-//                    playerVC.player = player
-//                    playerVC.modalPresentationStyle = .custom
-//                    let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
-//                    if checkViewController != nil {
-//                        UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.present(playerVC, animated: true, completion: nil)
-//                    } else {
-//                        UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.present(playerVC, animated: true, completion: nil)
-//                    }
-//                } else {
-//                    Download().start(forKey: video) { (name, progress) in
-//                        DispatchQueue.main.async {
-//                            guard progress == 100 else {
-//                                return
-//                            }
-//                            let player = AVPlayer(url: videoURL as URL)
-//                            let playerVC = AVPlayerViewController()
-//                            playerVC.player = player
-//                            playerVC.modalPresentationStyle = .custom
-//                            let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
-//                            if checkViewController != nil {
-//                                UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.present(playerVC, animated: true, completion: nil)
-//                            } else {
-//                                UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.present(playerVC, animated: true, completion: nil)
-//                            }
-//                        }
-//                    }
-//                }
-//            }
-//        } else if fileType == BroadcastViewController.FILE_TYPE_DOCUMENT {
-//            if let dirPath = paths.first {
-//                let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(file)
-//                if FileManager.default.fileExists(atPath: fileURL.path) {
-//                    previewItem = fileURL as NSURL
-//                    let previewController = QLPreviewController()
-//                    let rightBarButton = UIBarButtonItem()
-//                    previewController.navigationItem.rightBarButtonItem = rightBarButton
-//                    previewController.dataSource = self
-//                    previewController.modalPresentationStyle = .overFullScreen
-//
-//                    let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
-//                    if checkViewController != nil {
-//                        UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.show(previewController, sender: nil)
-//                    } else {
-//                        UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.show(previewController, sender: nil)
-//                    }
-//                } else {
-//                    Download().start(forKey: file) { (name, progress) in
-//                        DispatchQueue.main.async {
-//                            guard progress == 100 else {
-//                                return
-//                            }
-//                            previewItem = fileURL as NSURL
-//                            let previewController = QLPreviewController()
-//                            let rightBarButton = UIBarButtonItem()
-//                            previewController.navigationItem.rightBarButtonItem = rightBarButton
-//                            previewController.dataSource = self
-//                            previewController.modalPresentationStyle = .overFullScreen
-//
-//                            let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
-//                            if checkViewController != nil {
-//                                UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.show(previewController, sender: nil)
-//                            } else {
-//                                UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.show(previewController, sender: nil)
-//                            }
-//                        }
-//                    }
-//                }
-//            }
-//        }
-//     })
-//}
-//
-//let checkViewController = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController
-//if checkViewController != nil {
-//    UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.presentedViewController?.present(alert, animated: true, completion: nil)
-//} else {
-//    UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController?.present(alert, animated: true, completion: nil)
-//}
+extension String {
+    init(unicodeScalar: UnicodeScalar) {
+        self.init(Character(unicodeScalar))
+    }
+
+
+    init?(unicodeCodepoint: Int) {
+        if let unicodeScalar = UnicodeScalar(unicodeCodepoint) {
+            self.init(unicodeScalar: unicodeScalar)
+        } else {
+            return nil
+        }
+    }
+
+
+    static func +(lhs: String, rhs: Int) -> String {
+        return lhs + String(unicodeCodepoint: rhs)!
+    }
+
+
+    static func +=(lhs: inout String, rhs: Int) {
+        lhs = lhs + rhs
+    }
+}

+ 49 - 40
appbuilder-ios/NexilisLite/NexilisLite/Source/IncomingThread.swift

@@ -147,6 +147,8 @@ class IncomingThread {
             mobileInquiry(message: message)
         } else if message.getCode() == CoreMessage_TMessageCode.INQUIRY {
             inquiry(message: message)
+        } else if message.getCode() == CoreMessage_TMessageCode.BACKUP_AVAILABILITY {
+            backupAvailability(message: message)
         } else {
             print("unprocessed code", message.getCode())
             ack(message: message)
@@ -160,6 +162,13 @@ class IncomingThread {
      *
      */
     
+    private func backupAvailability(message: TMessage) {
+        var dataMessage: [AnyHashable : Any] = [:]
+        dataMessage["message"] = message
+        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "backupAvailability"), object: nil, userInfo: dataMessage)
+        ack(message: message)
+    }
+    
     private func makeNotifAddFriend(message: TMessage) {
         let data  = message.getBody(key: CoreMessage_TMessageKey.DATA)
         if let jsonArray = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
@@ -1667,46 +1676,46 @@ class IncomingThread {
     }
     
     private static func onPushForm(message: TMessage) {
-        var form = Form()
-        form.formId = message.getBody(key: CoreMessage_TMessageKey.FORM_ID)
-        form.title = message.getBody(key: CoreMessage_TMessageKey.TITLE)
-        form.createdDate = message.getBodyAsLong(key: CoreMessage_TMessageKey.CREATED_DATE, default_value: 0)
-        form.createdBy = message.getBody(key: CoreMessage_TMessageKey.CREATED_BY)
-        form.sqNo = message.getBodyAsInteger(key: CoreMessage_TMessageKey.SEQUENCE, default_value: 0)
-        Database.shared.database?.inTransaction({ (fmdb, rollback) in
-            _ = Database.shared.deleteRecord(fmdb: fmdb, table: "FORM", _where: "form_id = '\(form.formId)'")
-            _ = Database.shared.deleteRecord(fmdb: fmdb, table: "FORM_ITEM", _where: "form_id = '\(form.formId)'")
-        })
-        if(message.getBody(key: CoreMessage_TMessageKey.STATUS) == "0"){
-            return
-        }
-        Database.shared.database?.inTransaction({ (fmdb, rollback) in
-            _ = try? Database.shared.insertRecord(fmdb: fmdb, table: "FORM", cvalues: [
-                    "form_id" : form.formId,
-                    "name": form.title,
-                    "created_date": form.createdDate,
-                    "created_by": form.createdBy,
-                    "sq_no": form.sqNo
-                ],
-                replace: true)
-        })
-        let data = message.getBody(key: CoreMessage_TMessageKey.DATA)
-        if let jsonArray = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
-            Database.shared.database?.inTransaction({ (fmdb, rollback) in
-                for json in jsonArray{
-                    _ = try? Database.shared.insertRecord(fmdb: fmdb, table: "FORM_ITEM",
-                        cvalues: [
-                            "form_id" : form.formId,
-                            "label": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.LABEL),
-                            "value": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.VALUE),
-                            "key": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.KEY),
-                            "type": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.TYPE),
-                            "sq_no": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.SEQUENCE)
-                        ],
-                        replace: false)
-                }
-            })
-        }
+//        var form = Form()
+//        form.formId = message.getBody(key: CoreMessage_TMessageKey.FORM_ID)
+//        form.title = message.getBody(key: CoreMessage_TMessageKey.TITLE)
+//        form.createdDate = message.getBodyAsLong(key: CoreMessage_TMessageKey.CREATED_DATE, default_value: 0)
+//        form.createdBy = message.getBody(key: CoreMessage_TMessageKey.CREATED_BY)
+//        form.sqNo = message.getBodyAsInteger(key: CoreMessage_TMessageKey.SEQUENCE, default_value: 0)
+//        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+//            _ = Database.shared.deleteRecord(fmdb: fmdb, table: "FORM", _where: "form_id = '\(form.formId)'")
+//            _ = Database.shared.deleteRecord(fmdb: fmdb, table: "FORM_ITEM", _where: "form_id = '\(form.formId)'")
+//        })
+//        if(message.getBody(key: CoreMessage_TMessageKey.STATUS) == "0"){
+//            return
+//        }
+//        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+//            _ = try? Database.shared.insertRecord(fmdb: fmdb, table: "FORM", cvalues: [
+//                    "form_id" : form.formId,
+//                    "name": form.title,
+//                    "created_date": form.createdDate,
+//                    "created_by": form.createdBy,
+//                    "sq_no": form.sqNo
+//                ],
+//                replace: true)
+//        })
+//        let data = message.getBody(key: CoreMessage_TMessageKey.DATA)
+//        if let jsonArray = try! JSONSerialization.jsonObject(with: data.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [AnyObject] {
+//            Database.shared.database?.inTransaction({ (fmdb, rollback) in
+//                for json in jsonArray{
+//                    _ = try? Database.shared.insertRecord(fmdb: fmdb, table: "FORM_ITEM",
+//                        cvalues: [
+//                            "form_id" : form.formId,
+//                            "label": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.LABEL),
+//                            "value": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.VALUE),
+//                            "key": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.KEY),
+//                            "type": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.TYPE),
+//                            "sq_no": CoreMessage_TMessageUtil.getString(json: json, key: CoreMessage_TMessageKey.SEQUENCE)
+//                        ],
+//                        replace: false)
+//                }
+//            })
+//        }
         
     }
     

+ 80 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/Network.swift

@@ -102,6 +102,86 @@ public class Network {
         }
     }
     
+    public func upload(fileUrl: URL, completion: @escaping (Bool, Double)->()) {
+        DispatchQueue(label: "Network").async {
+            do {
+                if FileManager.default.fileExists(atPath: fileUrl.path) {
+                    let path = fileUrl.path
+                    let attrib = try FileManager.default.attributesOfItem(atPath: path)
+                    let fileSize = attrib[.size] as! Int
+                    let fileName = (path as NSString).lastPathComponent
+                    print("file exists: \(path) -> \(fileSize)")
+                    if (fileSize > self.CHUNK_SIZE) {
+                        Nexilis.putUploadFile(forKey: fileName, uploader: self)
+                        print("[bytes_processing] Size: " + String(fileSize))
+                        var totalPart = fileSize / self.CHUNK_SIZE
+                        if (fileSize % self.CHUNK_SIZE > 0) {
+                            totalPart += 1
+                        }
+                        
+                        do {
+                            let outputFileHandle = try FileHandle(forReadingFrom: URL(fileURLWithPath: path))
+                            var data = outputFileHandle.readData(ofLength: self.CHUNK_SIZE)
+                            var index = 0
+                            while !data.isEmpty {
+                                if self.isCancel {
+                                    completion(false, Double(0))
+                                    break
+                                }
+                                self.uploadGroup.enter()
+                                print("[bytes_processing] Sending bytes part #" + String(index + 1) + " of " + String(totalPart) + " -->  " + String(data.count))
+                                
+                                let message = CoreMessage_TMessageBank.getUploadFile(p_image_id: fileName, file_size: String(fileSize), part_of: String(index), part_size: String(totalPart), p_file: [UInt8] (data))
+                                
+                                if let response = Nexilis.write(message: message), response.isEmpty {
+                                    completion(false, self.progress)
+                                    break
+                                }
+                                print("[bytes_processing] part #" + String(index + 1) + " of " + String(totalPart) + " uploading...")
+                                
+                                let wait = self.uploadGroup.wait(timeout: .now() + 30)
+                                print("[bytes_processing] part #" + String(index + 1) + " of " + String(totalPart) + " wait!", wait)
+                                if wait == DispatchTimeoutResult.timedOut {
+                                    completion(false, self.progress)
+                                    Nexilis.removeUploadFile(forKey: fileName)
+                                    self.uploadGroup.leave()
+                                    break
+                                }
+                                self.progress = Double(index + 1) / Double(totalPart) * 100
+                                completion(true, self.progress)
+                                
+                                print("[bytes_processing] part #" + String(index + 1) + " of " + String(totalPart) + " uploaded!")
+                                data = outputFileHandle.readData(ofLength: self.CHUNK_SIZE)
+                                index = index + 1
+                            }
+                            outputFileHandle.closeFile()
+                            _ = Nexilis.removeUploadFile(forKey: fileName)
+                        } catch {
+                            print(error.localizedDescription)
+                        }
+                    }
+                    else {
+                        let data = try Data(contentsOf: URL(fileURLWithPath: path))
+                        
+                        let message = CoreMessage_TMessageBank.getUploadFile(p_image_id: fileName, file_size: String(fileSize), part_of: "0", part_size: "0", p_file: [UInt8] (data))
+                        
+                        guard let response = Nexilis.write(message: message), !response.isEmpty else {
+                            completion(false, self.progress)
+                            return
+                        }
+                        print("[bytes_processing] File uploaded!")
+                        completion(response.count > 1, 100)
+                    }
+                } else {
+                    print("file not exists \(fileUrl)")
+                    completion(false, 0)
+                }
+            } catch {
+                print(error.localizedDescription)
+            }
+        }
+    }
+    
     public func cancel() {
         self.isCancel = true
     }

+ 45 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/Units.swift

@@ -0,0 +1,45 @@
+//
+//  Units.swift
+//  NexilisLite
+//
+//  Created by Akhmad Al Qindi Irsyam on 20/02/23.
+//
+
+import Foundation
+
+public struct Units {
+  
+  public let bytes: Int64
+  
+  public var kilobytes: Double {
+    return Double(bytes) / 1_024
+  }
+  
+  public var megabytes: Double {
+    return kilobytes / 1_024
+  }
+  
+  public var gigabytes: Double {
+    return megabytes / 1_024
+  }
+  
+  public init(bytes: Int64) {
+    self.bytes = bytes
+  }
+  
+  public func getReadableUnit() -> String {
+    
+    switch bytes {
+    case 0..<1_024:
+      return "\(bytes) Bytes"
+    case 1_024..<(1_024 * 1_024):
+      return "\(String(format: "%.2f", kilobytes)) KB"
+    case 1_024..<(1_024 * 1_024 * 1_024):
+      return "\(String(format: "%.2f", megabytes)) MB"
+    case (1_024 * 1_024 * 1_024)...Int64.max:
+      return "\(String(format: "%.2f", gigabytes)) GB"
+    default:
+      return "\(bytes) Bytes"
+    }
+  }
+}

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

@@ -2087,6 +2087,8 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         var opposite_pin = ""
         if isContactCenter {
             opposite_pin = fPinContacCenter
+        } else {
+            opposite_pin = l_pin
         }
         var credential = credential
         if isConfidential {

+ 21 - 21
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Chat/FormEditor.swift

@@ -61,13 +61,13 @@ class FormEditor: UIViewController {
     
     private func checkForm(json: [String: Any]) -> Bool {
         var result = false
-        Database.shared.database?.inTransaction({ (fmdb, rollback) in
-            if let dataForm = Database().getRecords(fmdb: fmdb, query: "select sq_no, created_date from FORM where form_id = '\(dataMessage["reff_id"] as! String)'"), dataForm.next() {
-                result = true
-                isAorR = Int(dataForm.int(forColumnIndex: 0))
-                dateApproved = dataForm.string(forColumnIndex: 1)!
-            }
-        })
+//        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+//            if let dataForm = Database().getRecords(fmdb: fmdb, query: "select sq_no, created_date from FORM where form_id = '\(dataMessage["reff_id"] as! String)'"), dataForm.next() {
+//                result = true
+//                isAorR = Int(dataForm.int(forColumnIndex: 0))
+//                dateApproved = dataForm.string(forColumnIndex: 1)!
+//            }
+//        })
         return result
     }
     
@@ -307,20 +307,20 @@ class FormEditor: UIViewController {
             let idMe = UserDefaults.standard.string(forKey: "me")!
             let resp = Nexilis.writeAndWait(message: CoreMessage_TMessageBank.getFormApproval(p_f_pin: idMe, p_ref_id: refId ?? "", p_approve: "\(isApprove)", p_note: "", p_sign: ""))
             if resp != nil {
-                Database.shared.database?.inTransaction({ (fmdb, rollback) in
-                    do {
-                        _ = try Database.shared.insertRecord(fmdb: fmdb, table: "FORM", cvalues: [
-                            "form_id" : refId ?? "",
-                            "name" : "",
-                            "created_date" : "\(Date().currentTimeMillis())",
-                            "created_by" : dataPerson["f_pin"]!!,
-                            "sq_no" : isApprove,
-                        ], replace: true)
-                    } catch {
-                        rollback.pointee = true
-                        print(error)
-                    }
-                })
+//                Database.shared.database?.inTransaction({ (fmdb, rollback) in
+//                    do {
+//                        _ = try Database.shared.insertRecord(fmdb: fmdb, table: "FORM", cvalues: [
+//                            "form_id" : refId ?? "",
+//                            "name" : "",
+//                            "created_date" : "\(Date().currentTimeMillis())",
+//                            "created_by" : dataPerson["f_pin"]!!,
+//                            "sq_no" : isApprove,
+//                        ], replace: true)
+//                    } catch {
+//                        rollback.pointee = true
+//                        print(error)
+//                    }
+//                })
                 DispatchQueue.main.async {
                     self.view.subviews.forEach({ $0.removeFromSuperview() })
                     self.viewDidLoad()

+ 4 - 4
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/AudienceViewController.swift

@@ -9,7 +9,7 @@ import UIKit
 
 class AudienceViewController: UITableViewController {
 
-    static var data: [String] = ["Customer".localized(), "Team".localized(), "All User".localized(), "Group".localized(), "User".localized(), "Merchant Member".localized()]
+    var data: [String] = ["Customer".localized(), "Team".localized(), "All User".localized(), "Group".localized(), "User".localized(), "Merchant Member".localized()]
     
     var selected: String?
     var isBroadcast = false
@@ -22,14 +22,14 @@ class AudienceViewController: UITableViewController {
     
     override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         if(!isBroadcast){
-            return AudienceViewController.data.count - 1
+            return data.count - 1
         }
-        return AudienceViewController.data.count
+        return data.count
     }
     
     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell", for: indexPath )
-        let selectedCell = AudienceViewController.data[indexPath.row]
+        let selectedCell = data[indexPath.row]
         cell.textLabel?.text = selectedCell
         cell.accessoryType = selected == selectedCell ? .checkmark : .none
         cell.tag = indexPath.row

+ 85 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/BackupRestoreOption.swift

@@ -0,0 +1,85 @@
+//
+//  BackupRestoreOption.swift
+//  NexilisLite
+//
+//  Created by Akhmad Al Qindi Irsyam on 20/02/23.
+//
+
+import UIKit
+
+class BackupRestoreOption: UITableViewController {
+    var data: [String] = ["Forever".localized(), "3 months".localized(), "a month".localized(), "a week".localized()]
+    
+    var selected = "M"
+    
+    var isSelected: ((String) -> ())?
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellBackupRestoreOption")
+    }
+    
+    private func selectedOption(text: String) -> Bool {
+        if selected == "F" && text == data[0] {
+            return true
+        } else if selected == "MMM" && text == data[1] {
+            return true
+        } else if selected == "M" && text == data[2] {
+            return true
+        } else if selected == "W" && text == data[3] {
+            return true
+        }
+        return false
+    }
+    
+    private func convertSelectedOption(text: String) -> String {
+        if text == data[0] {
+            return "F"
+        } else if text == data[1] {
+            return "MMM"
+        } else if text == data[2] {
+            return "M"
+        }
+        return "W"
+    }
+    
+    public func convertSelectedOptionWithCode(code: String) -> String {
+        if code == "F" {
+            return data[0]
+        } else if code == "MMM" {
+            return data[1]
+        } else if code == "M" {
+            return data[2]
+        }
+        return data[3]
+    }
+
+
+    // MARK: - Table view data source
+
+    override func numberOfSections(in tableView: UITableView) -> Int {
+        // #warning Incomplete implementation, return the number of sections
+        return 1
+    }
+
+    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        // #warning Incomplete implementation, return the number of rows
+        return data.count
+    }
+    
+    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: "cellBackupRestoreOption", for: indexPath )
+        cell.textLabel?.text = data[indexPath.row]
+        cell.accessoryType = selectedOption(text: data[indexPath.row]) ? .checkmark : .none
+        return cell
+    }
+    
+    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        selected = convertSelectedOption(text: data[indexPath.row])
+        tableView.reloadData()
+        navigationController?.popViewController(animated: true, completion: { [self] in
+            isSelected?(selected)
+        })
+    }
+
+}

+ 940 - 0
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/BackupRestoreView.swift

@@ -0,0 +1,940 @@
+//
+//  BackupRestoreView.swift
+//  NexilisLite
+//
+//  Created by Akhmad Al Qindi Irsyam on 16/02/23.
+//
+
+import UIKit
+import QuickLook
+import Zip
+
+public class BackupRestoreView: UIViewController, UITableViewDataSource, UITableViewDelegate {
+    private var tableView: UITableView!
+    var centerLogo = UIImageView()
+    var centerLogoIsRotated = false
+    let activityIndicatorBackup = UIActivityIndicatorView(style: .medium)
+    let activityIndicatorRestore = UIActivityIndicatorView(style: .medium)
+    let titleBackup = UILabel()
+    let titleRestore = UILabel()
+    let titleLastBackup = UILabel()
+    let titleTotalSize = UILabel()
+    let labelRestoring = UILabel()
+    let labelPreparing = UILabel()
+    
+    var isBackupStart = false
+    var isRestoreStart = false
+    
+    var valueLastBackup = ""
+    var valuesizeBackup = ""
+    var dayLastBackup = ""
+    var timeLastBackup = ""
+    var choosenOption = "M"
+    
+    var fileIdBackup = ""
+    var recordSizeBackup = ""
+    var optionBackup = ""
+    
+    let separator = String(unicodeCodepoint: 0x06) ?? ""
+
+    public override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        navigationController?.navigationBar.topItem?.backButtonTitle = "Back".localized()
+        
+        tableView = UITableView()
+        view.addSubview(tableView)
+        tableView.anchor(top: view.safeAreaLayoutGuide.topAnchor, left: view.leftAnchor, bottom: view.safeAreaLayoutGuide.bottomAnchor, right: view.rightAnchor)
+        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellBackupRestore")
+        tableView.dataSource = self
+        tableView.delegate = self
+        
+        if #available(iOS 15.0, *) {
+            tableView.sectionHeaderTopPadding = 0
+        }
+        
+        
+        let center: NotificationCenter = NotificationCenter.default
+        center.addObserver(self, selector: #selector(downloadFile(notification:)), name: NSNotification.Name(rawValue: "backupAvailability"), object: nil)
+        requestBackupAvailability()
+    }
+    
+    public override func viewDidAppear(_ animated: Bool) {
+        self.navigationController?.navigationBar.topItem?.title = "Backup & Restore".localized()
+        self.navigationController?.navigationBar.setNeedsLayout()
+        self.title = "Backup & Restore".localized()
+    }
+    
+    @objc private func downloadFile(notification: NSNotification) {
+        DispatchQueue.main.async { [self] in
+            let data:[AnyHashable : Any] = notification.userInfo!
+            if let message = data["message"] as? TMessage {
+                fileIdBackup = message.getBody(key: CoreMessage_TMessageKey.FILE_ID, default_value: "")
+                recordSizeBackup = message.getBody(key: CoreMessage_TMessageKey.RECORD_SIZE, default_value: "0")
+                optionBackup = message.getBody(key: CoreMessage_TMessageKey.TYPE, default_value: "")
+                let filesize = message.getBody(key: CoreMessage_TMessageKey.FILE_SIZE, default_value: "0")
+                let createdDate = message.getBody(key: CoreMessage_TMessageKey.CREATED_DATE, default_value: "\(Date().currentTimeMillis())")
+                if optionBackup != "AUTO" {
+                    let date = Date(milliseconds: Int64(createdDate)!)
+                    let calendar = Calendar.current
+                    
+                    if (calendar.isDateInToday(date)) {
+                        dayLastBackup = "Today".localized()
+                    } else {
+                        let startOfNow = calendar.startOfDay(for: Date())
+                        let startOfTimeStamp = calendar.startOfDay(for: date)
+                        let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
+                        let day = -(components.day!)
+                        if day == 1{
+                            dayLastBackup = "Yesterday".localized()
+                        } else {
+                            let formatter = DateFormatter()
+                            formatter.dateFormat = "dd MMMM yyyy"
+                            formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
+                            let stringFormat = formatter.string(from: date as Date)
+                            dayLastBackup = stringFormat
+                        }
+                    }
+                    
+                    let formatter = DateFormatter()
+                    formatter.dateFormat = "HH:mm"
+                    formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
+                    timeLastBackup = formatter.string(from: date as Date)
+                    
+                    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()
+                }
+            }
+        }
+    }
+    
+    private func requestBackupAvailability() {
+        DispatchQueue.global().async {
+            _ = Nexilis.write(message: CoreMessage_TMessageBank.getBackupAvailability())
+        }
+    }
+    
+    private func getFileName(option: String = "", fileId: String = "", withoutZIP: Bool = false) -> String {
+        if !fileId.isEmpty {
+            if withoutZIP {
+                return "\(User.getMyPin()!)_\(option)_\(fileId)"
+            }
+            return "\(User.getMyPin()!)_\(option)_\(fileId).zip"
+        }
+        return "\(User.getMyPin()!).zip"
+    }
+    
+    public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
+        if section == 2 && isBackupStart {
+            let container = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 20))
+            container.addSubview(labelPreparing)
+            labelPreparing.anchor(left: container.leftAnchor, paddingLeft: 10, centerY: container.centerYAnchor)
+            labelPreparing.textColor = .gray
+            labelPreparing.font = .systemFont(ofSize: 12)
+            return container
+        } else if section == 3 && isRestoreStart {
+            let container = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 20))
+            container.addSubview(labelRestoring)
+            labelRestoring.anchor(left: container.leftAnchor, paddingLeft: 10, centerY: container.centerYAnchor)
+            labelRestoring.textColor = .gray
+            labelRestoring.font = .systemFont(ofSize: 12)
+            return container
+        }
+        return UIView()
+    }
+    
+    public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
+        if section == 2 && isBackupStart {
+            return 30
+        } else if section == 3 && isRestoreStart {
+            return 30
+        }
+        return 20
+    }
+    
+    public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
+        .leastNormalMagnitude
+    }
+    
+    public func numberOfSections(in tableView: UITableView) -> Int {
+        return 4
+    }
+    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        if section == 1 {
+            return 2
+        } else if section == 3 {
+            return 0
+        }
+        return 1
+    }
+    
+    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        let cell = tableView.dequeueReusableCell(withIdentifier: "cellBackupRestore", for: indexPath as IndexPath)
+        cell.backgroundColor = .secondaryColor
+        makeViewBackup(cell: cell, indexPath: indexPath)
+        return cell
+    }
+    
+    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        tableView.deselectRow(at: indexPath, animated: true)
+        if indexPath.section == 1 && indexPath.row == 0 {
+            let controller = BackupRestoreOption()
+            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 {
+            if isBackupStart || isRestoreStart {
+                return
+            }
+            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 {
+            if isBackupStart || isRestoreStart || valueLastBackup == "-" {
+                return
+            }
+            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)
+            if let dirPath = paths.first {
+                let fileURL = URL(fileURLWithPath: dirPath).appendingPathComponent(getFileName(option: optionBackup, fileId: fileIdBackup))
+                if !FileManager.default.fileExists(atPath: fileURL.path) {
+                    Download().start(forKey: getFileName(option: optionBackup, fileId: fileIdBackup)) { (name, progress) in
+                        DispatchQueue.main.async { [self] in
+                            guard progress == 100 else {
+                                labelRestoring.text = "Downloading...".localized() + "  \(progress)%"
+                                tableView.reloadSections(IndexSet(integer: 3), with: .none)
+                                return
+                            }
+                            labelRestoring.text = "Restoring...".localized()
+                            tableView.reloadSections(IndexSet(integer: 3), with: .none)
+                            restoreData(file: fileURL, dirPath: dirPath, indexPath: indexPath)
+                        }
+                    }
+                } else {
+                    labelRestoring.text = "Restoring...".localized()
+                    tableView.reloadSections(IndexSet(integer: 3), with: .none)
+                    restoreData(file: fileURL, dirPath: dirPath, indexPath: indexPath)
+                }
+            }
+        }
+    }
+    
+    private func animateBackup() {
+        UIView.animate(withDuration: 2, animations: { [self] in
+            centerLogo.transform = centerLogo.transform.rotated(by: .pi)
+        })
+        UIView.animate(withDuration: 2, animations: { [self] in
+            centerLogo.transform = centerLogo.transform.rotated(by: .pi)
+        })
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { [self] in
+            if isBackupStart {
+                animateBackup()
+            }
+        })
+    }
+    
+    private func enableButtonBackup() {
+        titleBackup.text = "Back Up Now".localized()
+        titleBackup.textColor = .systemBlue
+        activityIndicatorBackup.stopAnimating()
+    }
+    
+    private func disableButtonBackup(isRestore: Bool = false) {
+        titleBackup.textColor = .gray
+        if !isRestore{
+            titleBackup.text = "Backing Up...".localized()
+            activityIndicatorBackup.startAnimating()
+        }
+    }
+    
+    private func enableButtonRestore() {
+        titleRestore.text = "Restore Now".localized()
+        titleRestore.textColor = .systemBlue
+        activityIndicatorRestore.stopAnimating()
+    }
+    
+    private func disableButtonRestore(isBackup: Bool = false) {
+        titleRestore.textColor = .gray
+        if !isBackup && !activityIndicatorRestore.isAnimating {
+            titleRestore.text = "Restoring...".localized()
+            activityIndicatorRestore.startAnimating()
+        } else {
+            titleRestore.text = "Restore Now".localized()
+            activityIndicatorRestore.stopAnimating()
+        }
+    }
+    
+    private func makeViewBackup(cell: UITableViewCell, indexPath: IndexPath) {
+        cell.contentView.subviews.forEach { $0.removeFromSuperview() }
+        if indexPath.section == 0 {
+            cell.selectionStyle = .none
+            cell.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: UIScreen.main.bounds.width)
+            
+            let container = UIView()
+            container.addTopBorder(with: .lightGray.withAlphaComponent(0.5), andWidth: 1)
+            container.addBottomBorder(with: .lightGray.withAlphaComponent(0.5), andWidth: 1)
+            let content = cell.contentView
+            content.addSubview(container)
+            container.anchor(top: content.topAnchor, left: content.leftAnchor, bottom: content.bottomAnchor, right: content.rightAnchor)
+            
+            let containerLogo = UIView()
+            containerLogo.layer.borderWidth = 1
+            containerLogo.layer.borderColor = UIColor.lightGray.cgColor
+            containerLogo.layer.cornerRadius = 8
+            containerLogo.clipsToBounds = true
+            container.addSubview(containerLogo)
+            containerLogo.anchor(top: container.topAnchor, left: container.leftAnchor, paddingTop: 10, paddingLeft: 10, width: 70, height: 70)
+            
+            let logo = UIImageView()
+            logo.image = UIImage(systemName: "icloud")
+            logo.contentMode = .scaleAspectFit
+            logo.tintColor = .systemBlue
+            containerLogo.addSubview(logo)
+            logo.anchor(centerX: containerLogo.centerXAnchor, centerY: containerLogo.centerYAnchor, width: 60, height: 60)
+            
+            centerLogo.image = UIImage(systemName: "arrow.clockwise")
+            centerLogo.contentMode = .scaleAspectFit
+            centerLogo.tintColor = .systemBlue
+            if !isBackupStart {
+                if !centerLogoIsRotated{
+                    centerLogoIsRotated = true
+                    centerLogo.transform = centerLogo.transform.rotated(by: .pi / 2)
+                    print("LOHE \(centerLogo.transform)")
+                }
+            }
+            logo.addSubview(centerLogo)
+            centerLogo.anchor(top: logo.topAnchor, left: logo.leftAnchor, paddingTop: 22, paddingLeft: 23)
+            
+            container.addSubview(titleLastBackup)
+            titleLastBackup.anchor(top: container.topAnchor, left: containerLogo.rightAnchor, paddingTop: 25, paddingLeft: 10)
+            titleLastBackup.text = "Last Backup".localized() + ": " + valueLastBackup
+            titleLastBackup.textColor = .gray
+            titleLastBackup.font = .systemFont(ofSize: 12)
+            
+            container.addSubview(titleTotalSize)
+            titleTotalSize.anchor(top: titleLastBackup.bottomAnchor, left: containerLogo.rightAnchor, paddingLeft: 10)
+            titleTotalSize.text = "Total Size".localized() + ": " + valuesizeBackup
+            titleTotalSize.textColor = .gray
+            titleTotalSize.font = .systemFont(ofSize: 12)
+            
+            let descBackup = UILabel()
+            container.addSubview(descBackup)
+            descBackup.anchor(top: containerLogo.bottomAnchor, left: container.leftAnchor, bottom: container.bottomAnchor, right: container.rightAnchor, paddingTop: 2, paddingLeft: 10, paddingBottom: 10, paddingRight: 10)
+            descBackup.text = "Back up your chat history to server so if you lose your phone or switch to a new one or logout your account, your chat history is safe. You can restore your chat history when you relogin your account.".localized()
+            descBackup.numberOfLines = 0
+            descBackup.textColor = .black
+            descBackup.font = .systemFont(ofSize: 12)
+        } else if indexPath.section == 1 {
+            if indexPath.row == 0 {
+                let container = UIView()
+                container.addTopBorder(with: .lightGray.withAlphaComponent(0.5), andWidth: 1)
+                let content = cell.contentView
+                content.addSubview(container)
+                container.anchor(top: content.topAnchor, left: content.leftAnchor, bottom: content.bottomAnchor, right: content.rightAnchor)
+                
+                let titleBackupOption = UILabel()
+                container.addSubview(titleBackupOption)
+                titleBackupOption.anchor(left: container.leftAnchor, paddingLeft: 10, centerY: container.centerYAnchor)
+                titleBackupOption.text = "Back Up Option".localized()
+                titleBackupOption.textColor = .systemBlue
+                titleBackupOption.font = .systemFont(ofSize: 14)
+                
+                let arrowRight = UIImageView()
+                arrowRight.tintColor = .gray
+                arrowRight.image = UIImage(systemName: "chevron.right")
+                container.addSubview(arrowRight)
+                arrowRight.anchor(right: container.rightAnchor, paddingRight: 10, centerY: container.centerYAnchor)
+                
+                let titleChoosenOption = UILabel()
+                container.addSubview(titleChoosenOption)
+                titleChoosenOption.anchor(right: arrowRight.leftAnchor, paddingRight: 10, centerY: container.centerYAnchor)
+                titleChoosenOption.text = BackupRestoreOption().convertSelectedOptionWithCode(code: choosenOption)
+                titleChoosenOption.textColor = .lightGray
+                titleChoosenOption.font = .systemFont(ofSize: 14)
+                
+            } else {
+                cell.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: UIScreen.main.bounds.width)
+                
+                let container = UIView()
+                container.addBottomBorder(with: .lightGray.withAlphaComponent(0.5), andWidth: 1)
+                let content = cell.contentView
+                content.addSubview(container)
+                container.anchor(top: content.topAnchor, left: content.leftAnchor, bottom: content.bottomAnchor, right: content.rightAnchor)
+                
+                container.addSubview(titleBackup)
+                titleBackup.anchor(left: container.leftAnchor, paddingLeft: 10, centerY: container.centerYAnchor)
+                titleBackup.font = .systemFont(ofSize: 14)
+                
+                container.addSubview(activityIndicatorBackup)
+                activityIndicatorBackup.anchor(right: container.rightAnchor, paddingRight: 10, centerY: container.centerYAnchor)
+                
+                if isBackupStart || isRestoreStart {
+                    cell.selectionStyle = .none
+                    disableButtonBackup(isRestore: isRestoreStart)
+                } else {
+                    cell.selectionStyle = .default
+                    enableButtonBackup()
+                }
+            }
+        } else {
+            cell.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: UIScreen.main.bounds.width)
+            
+            let container = UIView()
+            container.addTopBorder(with: .lightGray.withAlphaComponent(0.5), andWidth: 1)
+            container.addBottomBorder(with: .lightGray.withAlphaComponent(0.5), andWidth: 1)
+            let content = cell.contentView
+            content.addSubview(container)
+            container.anchor(top: content.topAnchor, left: content.leftAnchor, bottom: content.bottomAnchor, right: content.rightAnchor)
+            
+            container.addSubview(titleRestore)
+            titleRestore.anchor(left: container.leftAnchor, paddingLeft: 10, centerY: container.centerYAnchor)
+            titleRestore.font = .systemFont(ofSize: 14)
+            
+            container.addSubview(activityIndicatorRestore)
+            activityIndicatorRestore.anchor(right: container.rightAnchor, paddingRight: 10, centerY: container.centerYAnchor)
+            
+            if isBackupStart || isRestoreStart || valueLastBackup == "-" {
+                cell.selectionStyle = .none
+                disableButtonRestore(isBackup: isBackupStart || valueLastBackup == "-")
+            } else {
+                cell.selectionStyle = .default
+                enableButtonRestore()
+            }
+        }
+    }
+    
+    private func restoreMessage(nameColumn: [String], message: [String]) {
+        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+            do {
+                var cValues: [String: Any] = [:]
+                var columnNameMessage: [String] = []
+                if let tableInfo = Database.shared.getRecords(fmdb: fmdb,query: "PRAGMA table_info(MESSAGE)") {
+                    while tableInfo.next() {
+                        columnNameMessage.append(tableInfo.string(forColumn: "name")!)
+                    }
+                    tableInfo.close()
+                }
+                for i in 0..<message.count {
+                    if i > nameColumn.count - 1 {
+                        continue
+                    }
+                    if columnNameMessage.contains(nameColumn[i]) {
+                        cValues[nameColumn[i]] = message[i] == "<empty>" || message[i] == "null" ? "" : message[i]
+                    }
+                }
+                _ = try Database.shared.insertRecord(fmdb: fmdb, table: "MESSAGE", cvalues: cValues, replace: true)
+            } catch {
+                rollback.pointee = true
+                print(error)
+            }
+        })
+    }
+    
+    private func restoreUcList(dataUcList: [String]) {
+        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+            do {
+                _ = try Database.shared.insertRecord(fmdb: fmdb, table: "MESSAGE_SUMMARY", cvalues: [
+                    "l_pin" : dataUcList[0],
+                    "message_id" : dataUcList[1],
+                    "counter" : 0
+                ], replace: true)
+            } catch {
+                rollback.pointee = true
+                print(error)
+            }
+        })
+    }
+    
+    private func restoreFormData(nameColumn: [String], data: [String]) {
+        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+            do {
+                var cValues: [String: Any] = [:]
+                var columnNameMessage: [String] = []
+                if let tableInfo = Database.shared.getRecords(fmdb: fmdb,query: "PRAGMA table_info(FORM_DATA)") {
+                    while tableInfo.next() {
+                        columnNameMessage.append(tableInfo.string(forColumn: "name")!)
+                    }
+                    tableInfo.close()
+                }
+                for i in 0..<data.count {
+                    if columnNameMessage.contains(nameColumn[i]) {
+                        cValues[nameColumn[i]] = data[i] == "<empty>" || data[i] == "null" ? "" : data[i]
+                    }
+                }
+                _ = try Database.shared.insertRecord(fmdb: fmdb, table: "FORM_DATA", cvalues: cValues, replace: true)
+            } catch {
+                rollback.pointee = true
+                print(error)
+            }
+        })
+    }
+    
+    private func restoreTaskPIC(nameColumn: [String], data: [String]) {
+        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+            do {
+                var cValues: [String: Any] = [:]
+                var columnNameMessage: [String] = []
+                if let tableInfo = Database.shared.getRecords(fmdb: fmdb,query: "PRAGMA table_info(TASK_PIC)") {
+                    while tableInfo.next() {
+                        columnNameMessage.append(tableInfo.string(forColumn: "name")!)
+                    }
+                    tableInfo.close()
+                }
+                for i in 0..<data.count {
+                    if columnNameMessage.contains(nameColumn[i]) {
+                        cValues[nameColumn[i]] = data[i] == "<empty>" || data[i] == "null" ? "" : data[i]
+                    }
+                }
+                _ = try Database.shared.insertRecord(fmdb: fmdb, table: "TASK_PIC", cvalues: cValues, replace: true)
+            } catch {
+                rollback.pointee = true
+                print(error)
+            }
+        })
+    }
+    
+    private func restoreTasDetail(nameColumn: [String], data: [String]) {
+        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+            do {
+                var cValues: [String: Any] = [:]
+                var columnNameMessage: [String] = []
+                if let tableInfo = Database.shared.getRecords(fmdb: fmdb,query: "PRAGMA table_info(TASK_DETAIL)") {
+                    while tableInfo.next() {
+                        columnNameMessage.append(tableInfo.string(forColumn: "name")!)
+                    }
+                    tableInfo.close()
+                }
+                for i in 0..<data.count {
+                    if columnNameMessage.contains(nameColumn[i]) {
+                        cValues[nameColumn[i]] = data[i] == "<empty>" || data[i] == "null" ? "" : data[i]
+                    }
+                }
+                _ = try Database.shared.insertRecord(fmdb: fmdb, table: "TASK_DETAIL", cvalues: cValues, replace: true)
+            } catch {
+                rollback.pointee = true
+                print(error)
+            }
+        })
+    }
+    
+    private func restoreData(file: URL, dirPath: String, indexPath: IndexPath) {
+        let fileManager = FileManager()
+        var destinationURL = URL(fileURLWithPath: dirPath)
+        destinationURL.appendPathComponent("unzipItem\(Date().currentTimeMillis())")
+        do {
+            try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: nil)
+            try Zip.unzipFile(file, destination: destinationURL, overwrite: true, password: nil)
+            
+            let files = try FileManager.default.contentsOfDirectory(atPath: destinationURL.path)
+            for file in files {
+                let nameFile = (file as NSString).lastPathComponent
+                let fileURL = destinationURL.appendingPathComponent(nameFile)
+                var newFileExt: URL?
+                newFileExt = fileURL.deletingPathExtension().appendingPathExtension("txt")
+                try FileManager.default.moveItem(at: fileURL, to: newFileExt!)
+                var textReading = try String(contentsOf: newFileExt ?? fileURL, encoding: .utf8)
+                textReading = textReading.replacingOccurrences(of: "<NL>", with: "\n").replacingOccurrences(of: "<CR>", with: "\r")
+                let valueText = textReading.components(separatedBy: "\n")
+                if nameFile.trimmingCharacters(in: .whitespacesAndNewlines) == "MESSAGE" {
+                    let nameColumn = valueText[0].components(separatedBy: separator)
+                    for i in 1..<valueText.count - 1 {
+                        let dataMessage = valueText[i].components(separatedBy: separator)
+                        restoreMessage(nameColumn: nameColumn, message: dataMessage)
+                    }
+                } else if nameFile.trimmingCharacters(in: .whitespacesAndNewlines) == "UC_LIST" {
+                    for i in 1..<valueText.count - 1 {
+                        let dataUcList = valueText[i].components(separatedBy: separator)
+                        restoreUcList(dataUcList: dataUcList)
+                    }
+                } else if nameFile.trimmingCharacters(in: .whitespacesAndNewlines) == "FORM_DATA" {
+                    let nameColumn = valueText[0].components(separatedBy: separator)
+                    for i in 1..<valueText.count - 1 {
+                        let dataFormData = valueText[i].components(separatedBy: separator)
+                        restoreFormData(nameColumn: nameColumn, data: dataFormData)
+                    }
+                } else if nameFile.trimmingCharacters(in: .whitespacesAndNewlines) == "TASK_PIC" {
+                    let nameColumn = valueText[0].components(separatedBy: separator)
+                    for i in 1..<valueText.count - 1 {
+                        let dataTaskPIC = valueText[i].components(separatedBy: separator)
+                        restoreFormData(nameColumn: nameColumn, data: dataTaskPIC)
+                    }
+                }
+                else if nameFile.trimmingCharacters(in: .whitespacesAndNewlines) == "TASK_DETAIL" {
+                    let nameColumn = valueText[0].components(separatedBy: separator)
+                    for i in 1..<valueText.count - 1 {
+                        let dataTaskDetail = valueText[i].components(separatedBy: separator)
+                        restoreFormData(nameColumn: nameColumn, data: dataTaskDetail)
+                    }
+                }
+            }
+            DispatchQueue.global().async { [self] in
+                _ = Nexilis.write(message: CoreMessage_TMessageBank.getBackupRestored(option: optionBackup, fileid: fileIdBackup))
+            }
+            DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { [self] in
+                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)
+            self.view.makeToast("\(error)", duration: 0.5)
+            DispatchQueue.global().async { [self] in
+                _ = Nexilis.write(message: CoreMessage_TMessageBank.getBackupRestored(option: optionBackup, fileid: fileIdBackup))
+            }
+            DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: { [self] in
+                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()
+            })
+        }
+    }
+    
+    private func backupData(indexPath: IndexPath) {
+        Database.shared.database?.inTransaction({ (fmdb, rollback) in
+            let documentDirectoryUrl = try! FileManager.default.url(
+                for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true
+            )
+            var recordSize: Int64 = 0
+            
+            //Make File MESSAGE
+            let file_message = documentDirectoryUrl.appendingPathComponent("MESSAGE").appendingPathExtension("")
+            if let tableInfo = Database.shared.getRecords(fmdb: fmdb,query: "PRAGMA table_info(MESSAGE)") {
+                var text_message = ""
+                while tableInfo.next() {
+                    if text_message.isEmpty {
+                        text_message.append(tableInfo.string(forColumn: "name")!)
+                    } else {
+                        text_message.append(separator)
+                        text_message.append(tableInfo.string(forColumn: "name")!)
+                    }
+                }
+                text_message.append("\n")
+                tableInfo.close()
+                
+                if let cursorData = Database.shared.getRecords(fmdb: fmdb,query: "SELECT * FROM MESSAGE") {
+                    let columnCount = cursorData.columnCount
+                    var value_m = ""
+                    while cursorData.next() {
+                        for i in 0..<columnCount {
+                            value_m.append(cursorData.string(forColumnIndex: i) == nil ? "null" : cursorData.string(forColumnIndex: i)!.isEmpty ? "<empty>" : cursorData.string(forColumnIndex: i)!)
+                            value_m.append(separator)
+                        }
+                        value_m.append("\n")
+                        recordSize += 1
+                    }
+                    text_message.append(value_m)
+                    cursorData.close()
+                }
+                do {
+                    try text_message.write(to: file_message, atomically: true, encoding: .utf8)
+                }
+                catch {print(error)}
+            }
+            
+            //Make File UC_LIST
+            let file_uc_list = documentDirectoryUrl.appendingPathComponent("UC_LIST").appendingPathExtension("")
+            if let tableInfo = Database.shared.getRecords(fmdb: fmdb,query: "PRAGMA table_info(MESSAGE_SUMMARY)") {
+                var text_uc_list = ""
+                while tableInfo.next() {
+                    if tableInfo.string(forColumn: "name")! == "counter" {
+                        continue
+                    }
+                    if text_uc_list.isEmpty {
+                        text_uc_list.append(tableInfo.string(forColumn: "name")! == "l_pin" ? "opposite" : tableInfo.string(forColumn: "name")!)
+                    } else {
+                        text_uc_list.append(separator)
+                        text_uc_list.append(tableInfo.string(forColumn: "name")! == "l_pin" ? "opposite" : tableInfo.string(forColumn: "name")!)
+                    }
+                }
+                text_uc_list.append("\n")
+                tableInfo.close()
+                
+                if let cursorData = Database.shared.getRecords(fmdb: fmdb,query: "SELECT * FROM MESSAGE_SUMMARY") {
+                    let columnCount = cursorData.columnCount
+                    var value_m = ""
+                    while cursorData.next() {
+                        for i in 0..<columnCount - 1 {
+                            value_m.append(cursorData.string(forColumnIndex: i) == nil ? "null" : cursorData.string(forColumnIndex: i)!.isEmpty ? "<empty>" : cursorData.string(forColumnIndex: i)!)
+                            value_m.append(separator)
+                        }
+                        value_m.append("\n")
+                        recordSize += 1
+                    }
+                    text_uc_list.append(value_m)
+                    cursorData.close()
+                }
+                do {
+                    try text_uc_list.write(to: file_uc_list, atomically: true, encoding: .utf8)
+                }
+                catch {print(error)}
+            }
+            
+            //Make File FORM_DATA
+            let file_form_data = documentDirectoryUrl.appendingPathComponent("FORM_DATA").appendingPathExtension("")
+            if let tableInfo = Database.shared.getRecords(fmdb: fmdb,query: "PRAGMA table_info(FORM_DATA)") {
+                var text_form_data = ""
+                while tableInfo.next() {
+                    if text_form_data.isEmpty {
+                        text_form_data.append(tableInfo.string(forColumn: "name")!)
+                    } else {
+                        text_form_data.append(separator)
+                        text_form_data.append(tableInfo.string(forColumn: "name")!)
+                    }
+                }
+                text_form_data.append("\n")
+                tableInfo.close()
+                
+                if let cursorData = Database.shared.getRecords(fmdb: fmdb,query: "SELECT * FROM FORM_DATA") {
+                    let columnCount = cursorData.columnCount
+                    var value_m = ""
+                    while cursorData.next() {
+                        for i in 0..<columnCount - 1 {
+                            value_m.append(cursorData.string(forColumnIndex: i) == nil ? "null" : cursorData.string(forColumnIndex: i)!.isEmpty ? "<empty>" : cursorData.string(forColumnIndex: i)!)
+                            value_m.append(separator)
+                        }
+                        value_m.append("\n")
+                        recordSize += 1
+                    }
+                    text_form_data.append(value_m)
+                    cursorData.close()
+                }
+                do {
+                    try text_form_data.write(to: file_form_data, atomically: true, encoding: .utf8)
+                }
+                catch {print(error)}
+            }
+            
+            //Make File TASK_PIC
+            let file_task_pic = documentDirectoryUrl.appendingPathComponent("TASK_PIC").appendingPathExtension("")
+            if let tableInfo = Database.shared.getRecords(fmdb: fmdb,query: "PRAGMA table_info(TASK_PIC)") {
+                var text_task_pic = ""
+                while tableInfo.next() {
+                    if text_task_pic.isEmpty {
+                        text_task_pic.append(tableInfo.string(forColumn: "name")!)
+                    } else {
+                        text_task_pic.append(separator)
+                        text_task_pic.append(tableInfo.string(forColumn: "name")!)
+                    }
+                }
+                text_task_pic.append("\n")
+                tableInfo.close()
+                
+                if let cursorData = Database.shared.getRecords(fmdb: fmdb,query: "SELECT * FROM TASK_PIC") {
+                    let columnCount = cursorData.columnCount
+                    var value_m = ""
+                    while cursorData.next() {
+                        for i in 0..<columnCount - 1 {
+                            value_m.append(cursorData.string(forColumnIndex: i) == nil ? "null" : cursorData.string(forColumnIndex: i)!.isEmpty ? "<empty>" : cursorData.string(forColumnIndex: i)!)
+                            value_m.append(separator)
+                        }
+                        value_m.append("\n")
+                        recordSize += 1
+                    }
+                    text_task_pic.append(value_m)
+                    cursorData.close()
+                }
+                do {
+                    try text_task_pic.write(to: file_task_pic, atomically: true, encoding: .utf8)
+                }
+                catch {print(error)}
+            }
+            
+            //Make File TASK_DETAIL
+            let file_task_detail = documentDirectoryUrl.appendingPathComponent("TASK_DETAIL").appendingPathExtension("")
+            if let tableInfo = Database.shared.getRecords(fmdb: fmdb,query: "PRAGMA table_info(TASK_DETAIL)") {
+                var text_task_detail = ""
+                while tableInfo.next() {
+                    if text_task_detail.isEmpty {
+                        text_task_detail.append(tableInfo.string(forColumn: "name")!)
+                    } else {
+                        text_task_detail.append(separator)
+                        text_task_detail.append(tableInfo.string(forColumn: "name")!)
+                    }
+                }
+                text_task_detail.append("\n")
+                tableInfo.close()
+                
+                if let cursorData = Database.shared.getRecords(fmdb: fmdb,query: "SELECT * FROM TASK_DETAIL") {
+                    let columnCount = cursorData.columnCount
+                    var value_m = ""
+                    while cursorData.next() {
+                        for i in 0..<columnCount - 1 {
+                            value_m.append(cursorData.string(forColumnIndex: i) == nil ? "null" : cursorData.string(forColumnIndex: i)!.isEmpty ? "<empty>" : cursorData.string(forColumnIndex: i)!)
+                            value_m.append(separator)
+                        }
+                        value_m.append("\n")
+                        recordSize += 1
+                    }
+                    text_task_detail.append(value_m)
+                    cursorData.close()
+                }
+                do {
+                    try text_task_detail.write(to: file_task_detail, atomically: true, encoding: .utf8)
+                }
+                catch {print(error)}
+            }
+            
+            //ZIP ALL FILES
+            let fileManager = FileManager()
+            var destinationURL = documentDirectoryUrl
+            destinationURL.appendPathComponent("zipItem\(Date().currentTimeMillis())")
+            let listFiles: [URL] = [file_message, file_uc_list, file_form_data, file_task_pic, file_task_detail]
+            do {
+                try fileManager.createDirectory(at: destinationURL, withIntermediateDirectories: true, attributes: nil)
+                let zipFiles = destinationURL.appendingPathComponent(getFileName(option: choosenOption, fileId: fileIdBackup, withoutZIP: true)).appendingPathExtension("zip")
+                try Zip.zipFiles(paths: listFiles, zipFilePath: zipFiles, password: nil, progress: {progress in
+                    self.labelPreparing.text = "Preparing...".localized() + " \(progress * 100)%"
+                })
+                self.labelPreparing.text = "Uploading...".localized()
+                Network().upload(fileUrl: zipFiles, completion: { result,progress in
+                    if result {
+                        DispatchQueue.main.async { [self] in
+                            labelPreparing.text = "Uploading...".localized() + " \(progress)%"
+                            if progress == 100 {
+                                do {
+                                    let path = zipFiles.path
+                                    let attrib = try FileManager.default.attributesOfItem(atPath: path)
+                                    let fileSize = attrib[.size] as! Int64
+                                    DispatchQueue.global().async { [self] in
+                                        _ = Nexilis.write(message: CoreMessage_TMessageBank.getBackupUploaded(option: choosenOption, fileid: fileIdBackup, filesize: String(fileSize), recordSize: String(recordSize)))
+                                    }
+                                    let date = Date()
+                                    let calendar = Calendar.current
+                                    
+                                    if (calendar.isDateInToday(date)) {
+                                        dayLastBackup = "Today".localized()
+                                    } else {
+                                        let startOfNow = calendar.startOfDay(for: Date())
+                                        let startOfTimeStamp = calendar.startOfDay(for: date)
+                                        let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
+                                        let day = -(components.day!)
+                                        if day == 1{
+                                            dayLastBackup = "Yesterday".localized()
+                                        } else {
+                                            let formatter = DateFormatter()
+                                            formatter.dateFormat = "dd MMMM yyyy"
+                                            formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
+                                            let stringFormat = formatter.string(from: date as Date)
+                                            dayLastBackup = stringFormat
+                                        }
+                                    }
+                                    
+                                    let formatter = DateFormatter()
+                                    formatter.dateFormat = "HH:mm"
+                                    formatter.locale = NSLocale(localeIdentifier: "id") as Locale?
+                                    timeLastBackup = formatter.string(from: date as Date)
+                                    
+                                    valueLastBackup = dayLastBackup.localized() + ", " + timeLastBackup
+                                    valuesizeBackup = Units(bytes: fileSize).getReadableUnit()
+                                    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 {}
+                            }
+                        }
+                    }
+                })
+            } catch {print(error)}
+        })
+    }
+
+}
+
+extension UIView {
+    func addTopBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {
+        let border = UIView()
+        border.backgroundColor = color
+        border.autoresizingMask = [.flexibleWidth, .flexibleBottomMargin]
+        border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: borderWidth)
+        addSubview(border)
+    }
+
+    func addBottomBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {
+        let border = UIView()
+        border.backgroundColor = color
+        border.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
+        border.frame = CGRect(x: 0, y: frame.size.height - borderWidth, width: frame.size.width, height: borderWidth)
+        addSubview(border)
+    }
+
+    func addLeftBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {
+        let border = UIView()
+        border.backgroundColor = color
+        border.frame = CGRect(x: 0, y: 0, width: borderWidth, height: frame.size.height)
+        border.autoresizingMask = [.flexibleHeight, .flexibleRightMargin]
+        addSubview(border)
+    }
+
+    func addRightBorder(with color: UIColor?, andWidth borderWidth: CGFloat) {
+        let border = UIView()
+        border.backgroundColor = color
+        border.autoresizingMask = [.flexibleHeight, .flexibleLeftMargin]
+        border.frame = CGRect(x: frame.size.width - borderWidth, y: 0, width: borderWidth, height: frame.size.height)
+        addSubview(border)
+    }
+}
+
+extension String {
+    func appendLineToURL(fileURL: URL) throws {
+         try (self + "\n").appendToURL(fileURL: fileURL)
+     }
+
+     func appendToURL(fileURL: URL) throws {
+         let data = self.data(using: String.Encoding.utf8)!
+         try data.append(fileURL: fileURL)
+     }
+ }
+
+ extension Data {
+     func append(fileURL: URL) throws {
+         if let fileHandle = FileHandle(forWritingAtPath: fileURL.path) {
+             defer {
+                 fileHandle.closeFile()
+             }
+             fileHandle.seekToEndOfFile()
+             fileHandle.write(self)
+         }
+         else {
+             try write(to: fileURL, options: .atomic)
+         }
+     }
+ }

+ 3 - 3
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/BroadcastModeViewController.swift

@@ -9,7 +9,7 @@ import UIKit
 
 class BroadcastModeViewController: UITableViewController {
 
-    static var data: [String] = ["One Time".localized(), "Daily".localized(), "Weekly".localized(), "Monthly".localized()]
+    var data: [String] = ["One Time".localized(), "Daily".localized(), "Weekly".localized(), "Monthly".localized()]
     
     var selected: String?
     
@@ -18,12 +18,12 @@ class BroadcastModeViewController: UITableViewController {
     }
     
     override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-        return BroadcastModeViewController.data.count
+        return data.count
     }
     
     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell", for: indexPath )
-        let selectedCell = BroadcastModeViewController.data[indexPath.row]
+        let selectedCell = data[indexPath.row]
         cell.textLabel?.text = selectedCell
         cell.accessoryType = selected == selectedCell ? .checkmark : .none
         cell.tag = indexPath.row

+ 4 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ChangeDeviceViewController.swift

@@ -245,8 +245,10 @@ public class ChangeDeviceViewController: UIViewController {
                     self.deleteAllRecordDatabase()
                     let id = response.getBody(key: CoreMessage_TMessageKey.F_PIN, default_value: "")
                     let thumb = response.getBody(key: CoreMessage_TMessageKey.THUMB_ID, default_value: "")
+                    let device_id = response.getBody(key: CoreMessage_TMessageKey.IMEI, default_value: id)
                     if(!id.isEmpty) {
-                        Nexilis.changeUser(f_pin: id)
+                        Nexilis.changeUser(f_pin: device_id)
+                        UserDefaults.standard.setValue(device_id, forKey: "device_id")
                         Utils.setProfile(value: true)
                         UserDefaults.standard.synchronize()
                         // pos registration
@@ -257,6 +259,7 @@ public class ChangeDeviceViewController: UIViewController {
                                 imageView.tintColor = .white
                                 let banner = FloatingNotificationBanner(title: "Successfully Sign-In".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
                                 banner.show()
+                                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onRefreshWebView"), object: nil, userInfo: nil)
                                 if self.fromChangeNamePass{
                                     var vc = self.navigationController?.presentingViewController
                                     while vc?.presentingViewController != nil {

+ 1 - 1
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/ProfileViewController.swift

@@ -205,6 +205,7 @@ public class ProfileViewController: UITableViewController {
         labelChangePassword.text = "Change Password".localized()
         labelAcceptCall.text = "Accept Call".localized()
         buttonHistoryCC.setTitle("Call Center History".localized(), for: .normal)
+        navigationController?.navigationBar.topItem?.backButtonTitle = "Back".localized()
         if let me = UserDefaults.standard.string(forKey: "me"), me == data || flag == Flag.me {
             buttonGroup.removeFromSuperview()
             navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Edit".localized(), style: .plain, target: self, action: #selector(didTapEdit(sender:)))
@@ -240,7 +241,6 @@ public class ProfileViewController: UITableViewController {
                 message.isEnabled = false
                 buttonGroup.removeFromSuperview()
             }
-            navigationController?.navigationBar.topItem?.backButtonTitle = "Back".localized()
             myViewGroup.removeFromSuperview()
         }
     }

+ 319 - 391
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/SettingTableViewController.swift

@@ -10,7 +10,7 @@ import NotificationBannerSwift
 import nuSDKService
 import Photos
 
-public class SettingTableViewController: UITableViewController {
+public class SettingTableViewController: UITableViewController, UIGestureRecognizerDelegate {
     
     var language: [[String: String]] = [["Indonesia": "id"],["English": "en"]]
     var alert: UIAlertController?
@@ -20,12 +20,16 @@ public class SettingTableViewController: UITableViewController {
     var switchSaveToGallery = UISwitch()
     var switchAutoDownload = UISwitch()
     
+    var notInTab = false
+    
     public override func viewDidLoad() {
         super.viewDidLoad()
         
-        title = "Settings".localized()
-        
-        makeMenu()
+        tableView.delegate = self
+        tableView.dataSource = self
+        tableView.layoutMargins = .init(top: 0, left: 5, bottom: 0, right: 5)
+//        tableView.separatorColor = .gray
+        tableView.separatorStyle = .none
         
         switchVibrateMode.tintColor = .gray
         switchSaveToGallery.tintColor = .gray
@@ -49,11 +53,12 @@ public class SettingTableViewController: UITableViewController {
         switchSaveToGallery.addTarget(self, action: #selector(saveToGallerySwitch), for: .valueChanged)
         switchAutoDownload.addTarget(self, action: #selector(autoDownloadSwitch), for: .valueChanged)
         
-        navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(didTapCancel))
+//        navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(didTapCancel))
     }
     
-    @objc func didTapCancel(sender: AnyObject) {
-        navigationController?.dismiss(animated: true, completion: nil)
+    public override func viewDidAppear(_ animated: Bool) {
+        self.navigationController?.navigationBar.topItem?.title = "Settings".localized()
+        self.navigationController?.navigationBar.setNeedsLayout()
     }
     
     @objc func vibrateModeSwitch() {
@@ -80,114 +85,123 @@ public class SettingTableViewController: UITableViewController {
         UserDefaults.standard.set(switchAutoDownload.isOn, forKey: "autoDownload")
     }
     
-    func makeMenu(imageSignIn: String = "") {
+    func makeMenu(imageSignIn: String = ""){
         let isChangeProfile = Utils.getSetProfile()
         Database.shared.database?.inTransaction({ fmdb, rollback in
-            if !isChangeProfile {
-                Item.menus["Personal"] = [
-                    Item(icon: UIImage(systemName: "person.crop.circle.badge.plus"), title: "Sign-Up (Change profile)".localized()),
-                    Item(icon: UIImage(systemName: "arrow.up.and.person.rectangle.portrait"), title: "Sign-In (Change Device)".localized())
-                ]
-            } else {
-                Item.menus["Personal"] = [
-                    Item(icon: UIImage(systemName: "person.fill"), title: "User Profile Management".localized()),
-//                    Item(icon: UIImage(systemName: "mail"), title: "Email".localized()),
-                    Item(icon: UIImage(systemName: "qrcode.viewfinder"), title: "Sign-In to Nexilis Web".localized()),
-                    Item(icon: UIImage(systemName: "rectangle.portrait.and.arrow.right"), title: "Sign-Out".localized())
-                ]
-                let idMe = UserDefaults.standard.string(forKey: "me") as String?
-                if let cursorUser = Database.shared.getRecords(fmdb: fmdb, query: "SELECT user_type, image_id FROM BUDDY where f_pin='\(idMe!)'"), cursorUser.next() {
-                    var groupId = ""
-                    if let cursorGroup = Database.shared.getRecords(fmdb: fmdb, query: "SELECT group_id FROM GROUPZ where group_type = 1 AND official = 1"), cursorGroup.next() {
-                        groupId = cursorGroup.string(forColumnIndex: 0) ?? ""
-                        cursorGroup.close()
-                    }
-                    var position = ""
-                    if let cursorIsAdmin = Database.shared.getRecords(fmdb: fmdb, query: "SELECT position FROM GROUPZ_MEMBER where group_id = '\(groupId)' AND f_pin = '\(idMe!)'"), cursorIsAdmin.next() {
-                        position = cursorIsAdmin.string(forColumnIndex: 0) ?? ""
-                        cursorIsAdmin.close()
-                    }
-                    if cursorUser.string(forColumnIndex: 0) == "23" && position == "1" {
-                        Item.menus["Personal"]?.insert(Item(icon: UIImage(systemName: "person.crop.rectangle"), title: "Change Admin / Internal Password".localized()), at: 1)
-                    } else if cursorUser.string(forColumnIndex: 0) != "23" && cursorUser.string(forColumnIndex: 0) != "24" {
-                        Item.menus["Personal"]?.insert(Item(icon: UIImage(systemName: "person.crop.rectangle"), title: "Sign-In Admin / Internal".localized()), at: 1)
-                    }
-//                    Item.menus["Personal"]?.insert(Item(icon: UIImage(named: "pb_button", in: Bundle.resourceBundle(for: Nexilis.self), with: nil), title: "Change Image FB Become Photo Profile".localized()), at: 2)
-                    let image = cursorUser.string(forColumnIndex: 1)
-                    if image != nil {
-                        if !image!.isEmpty {
-                            do {
-                                let documentDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
-                                let file = documentDir.appendingPathComponent(image!)
-                                if FileManager().fileExists(atPath: file.path) {
-                                    let image = UIImage(contentsOfFile: file.path)
-                                    Item.menus["Personal"]?[0].icon = image?.circleMasked
-                                    if !imageSignIn.isEmpty {
-                                        var dataImage: [AnyHashable : Any] = [:]
-                                        dataImage["name"] = imageSignIn
-                                        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
-                                    }
-                                } else {
-                                    Download().start(forKey: image!) { (name, progress) in
-                                        guard progress == 100 else {
-                                            return
-                                        }
-
-                                        DispatchQueue.main.async {
-                                            let image = UIImage(contentsOfFile: file.path)
-                                            Item.menus["Personal"]?[0].icon = image?.circleMasked
-                                            self.tableView.reloadData()
-                                            if !imageSignIn.isEmpty {
-                                                var dataImage: [AnyHashable : Any] = [:]
-                                                dataImage["name"] = imageSignIn
-                                                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
-                                            }
-                                        }
-                                    }
-                                }
-                            } catch {}
-                        }
-                    }
-                    cursorUser.close()
+            let idMe = UserDefaults.standard.string(forKey: "me") as String?
+            if let cursorUser = Database.shared.getRecords(fmdb: fmdb, query: "SELECT user_type, image_id, official_account FROM BUDDY where f_pin='\(idMe!)'"), cursorUser.next() {
+                var groupId = ""
+                if let cursorGroup = Database.shared.getRecords(fmdb: fmdb, query: "SELECT group_id FROM GROUPZ where group_type = 1 AND official = 1"), cursorGroup.next() {
+                    groupId = cursorGroup.string(forColumnIndex: 0) ?? ""
+                    cursorGroup.close()
+                }
+                var position = ""
+                if let cursorIsAdmin = Database.shared.getRecords(fmdb: fmdb, query: "SELECT position FROM GROUPZ_MEMBER where group_id = '\(groupId)' AND f_pin = '\(idMe!)'"), cursorIsAdmin.next() {
+                    position = cursorIsAdmin.string(forColumnIndex: 0) ?? ""
+                    cursorIsAdmin.close()
+                }
+                if ( User.isInternal(userType: cursorUser.string(forColumnIndex: 0) ?? "") && position == "1") || User.isOfficial(official_account: cursorUser.string(forColumnIndex: 2) ?? "") || User.isOfficial(official_account: cursorUser.string(forColumnIndex: 2) ?? "") {
+                    Item.menus["Personal"] = [
+                        Item(icon: UIImage(systemName: "person.fill"), title: "Personal Information".localized()),
+                        Item(icon: UIImage(systemName: "textformat.abc"), title: "Change Language".localized()),
+                        Item(icon: UIImage(systemName: "person.crop.rectangle"), title: "Change Admin / Internal Password".localized()),
+                        Item(icon: UIImage(systemName: "laptopcomputer.and.iphone"), title: "Sign-In to Web".localized()),
+                    ]
+                } else if User.isInternal(userType: cursorUser.string(forColumnIndex: 0) ?? "") || User.isCallCenter(userType: cursorUser.string(forColumnIndex: 0) ?? "") || User.isVerified(official_account: cursorUser.string(forColumnIndex: 2) ?? "") {
+                    Item.menus["Personal"] = [
+                        Item(icon: UIImage(systemName: "person.fill"), title: "Personal Information".localized()),
+                        Item(icon: UIImage(systemName: "textformat.abc"), title: "Change Language".localized()),
+                        Item(icon: UIImage(systemName: "laptopcomputer.and.iphone"), title: "Sign-In to Web".localized()),
+                    ]
                 } else {
-                    if !imageSignIn.isEmpty {
+                    Item.menus["Personal"] = [
+                        Item(icon: UIImage(systemName: "person.fill"), title: "Personal Information".localized()),
+                            Item(icon: UIImage(systemName: "textformat.abc"), title: "Change Language".localized()),
+                        Item(icon: UIImage(systemName: "person.crop.rectangle"), title: "Access Admin / Internal Features".localized()),
+                    ]
+                }
+                if !isChangeProfile {
+                    Item.menus["Personal"]?.append(Item(icon: UIImage(systemName: "arrow.up.and.person.rectangle.portrait"), title: "Sign-Up/Sign-In".localized()))
+                } else if isChangeProfile {
+                    Item.menus["Personal"]?.append(Item(icon: UIImage(systemName: "arrow.counterclockwise.icloud.fill"), title: "Backup & Restore".localized()))
+                    Item.menus["Personal"]?.append(Item(icon: UIImage(systemName: "rectangle.portrait.and.arrow.right"), title: "Sign-Out".localized()))
+                }
+                let image = cursorUser.string(forColumnIndex: 1)
+                if image != nil {
+                    if !image!.isEmpty {
                         do {
                             let documentDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
-                            let file = documentDir.appendingPathComponent(imageSignIn)
+                            let file = documentDir.appendingPathComponent(image!)
                             if FileManager().fileExists(atPath: file.path) {
                                 let image = UIImage(contentsOfFile: file.path)
                                 Item.menus["Personal"]?[0].icon = image?.circleMasked
-                                var dataImage: [AnyHashable : Any] = [:]
-                                dataImage["name"] = imageSignIn
-                                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
+                                if !imageSignIn.isEmpty {
+                                    var dataImage: [AnyHashable : Any] = [:]
+                                    dataImage["name"] = imageSignIn
+                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
+                                }
                             } else {
-                                Download().start(forKey: imageSignIn) { (name, progress) in
+                                Download().start(forKey: image!) { (name, progress) in
                                     guard progress == 100 else {
                                         return
                                     }
+
                                     DispatchQueue.main.async {
                                         let image = UIImage(contentsOfFile: file.path)
                                         Item.menus["Personal"]?[0].icon = image?.circleMasked
                                         self.tableView.reloadData()
-                                        var dataImage: [AnyHashable : Any] = [:]
-                                        dataImage["name"] = imageSignIn
-                                        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
+                                        if !imageSignIn.isEmpty {
+                                            var dataImage: [AnyHashable : Any] = [:]
+                                            dataImage["name"] = imageSignIn
+                                            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
+                                        }
                                     }
                                 }
                             }
                         } catch {}
                     }
                 }
+                cursorUser.close()
+            } else {
+                Item.menus["Personal"] = [
+                    Item(icon: UIImage(systemName: "person.fill"), title: "Personal Information".localized()),
+                        Item(icon: UIImage(systemName: "textformat.abc"), title: "Change Language".localized()),
+                    Item(icon: UIImage(systemName: "person.crop.rectangle"), title: "Access Admin / Internal Features".localized()),
+                    Item(icon: UIImage(systemName: "arrow.up.and.person.rectangle.portrait"), title: "Sign-Up/Sign-In".localized())
+                ]
+                if !imageSignIn.isEmpty {
+                    do {
+                        let documentDir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
+                        let file = documentDir.appendingPathComponent(imageSignIn)
+                        if FileManager().fileExists(atPath: file.path) {
+                            let image = UIImage(contentsOfFile: file.path)
+                            Item.menus["Personal"]?[0].icon = image?.circleMasked
+                            var dataImage: [AnyHashable : Any] = [:]
+                            dataImage["name"] = imageSignIn
+                            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
+                        } else {
+                            Download().start(forKey: imageSignIn) { (name, progress) in
+                                guard progress == 100 else {
+                                    return
+                                }
+                                DispatchQueue.main.async {
+                                    let image = UIImage(contentsOfFile: file.path)
+                                    Item.menus["Personal"]?[0].icon = image?.circleMasked
+                                    self.tableView.reloadData()
+                                    var dataImage: [AnyHashable : Any] = [:]
+                                    dataImage["name"] = imageSignIn
+                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
+                                }
+                            }
+                        }
+                    } catch {}
+                }
             }
         })
         
-//        Item.menus["Language"] = [
-//            Item(icon: UIImage(systemName: "textformat.abc"), title: "Change Language".localized()),
-//        ]
-        
         Item.menus["Call"] = [
-            Item(icon: UIImage(systemName: "message"), title: "Incoming Message(s)".localized()),
-            Item(icon: UIImage(systemName: "phone"), title: "Incoming Call(s)".localized()),
+            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: "arrow.down.square"), title: "Auto Download".localized()),
@@ -198,12 +212,40 @@ public class SettingTableViewController: UITableViewController {
         ]
     }
     
+    override public func viewWillAppear(_ animated: Bool) {
+        self.navigationController?.navigationBar.topItem?.title = ""
+        self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black]
+        let navBarAppearance = UINavigationBarAppearance()
+        navBarAppearance.configureWithTransparentBackground()
+        navigationController?.navigationBar.standardAppearance = navBarAppearance
+        navigationController?.navigationBar.scrollEdgeAppearance = navBarAppearance
+        let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.black]
+        UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
+        navigationController?.navigationBar.backgroundColor = .clear
+        navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
+        navigationController?.navigationBar.shadowImage = UIImage()
+        navigationController?.navigationBar.isTranslucent = true
+        navigationController?.setNavigationBarHidden(false, animated: false)
+        navigationController?.navigationBar.overrideUserInterfaceStyle = .light
+        navigationController?.navigationBar.barStyle = .default
+        navigationController?.navigationBar.tintColor = .black
+        tabBarController?.navigationItem.leftBarButtonItem = nil
+        tabBarController?.navigationItem.searchController = nil
+        tabBarController?.navigationItem.rightBarButtonItem = nil
+        makeMenu()
+        tableView.reloadData()
+    }
+    
     // MARK: - Table view data source
     
     public override func numberOfSections(in tableView: UITableView) -> Int {
         return Item.sections.count
     }
     
+    public override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
+        return 1
+    }
+    
     public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         return Item.menuFor(section: section).count
     }
@@ -211,7 +253,7 @@ public class SettingTableViewController: UITableViewController {
     public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
         cell.accessoryType = .none
-        cell.selectionStyle = .gray
+        cell.indentationLevel = 0
         var content = cell.defaultContentConfiguration()
         content.textProperties.font = UIFont.systemFont(ofSize: 14)
         content.secondaryTextProperties.font = UIFont.systemFont(ofSize: 14)
@@ -221,33 +263,39 @@ public class SettingTableViewController: UITableViewController {
         if let arr = Item.menus[section] {
             let menu = arr[indexPath.row]
             content.image = menu.icon
-            content.imageProperties.tintColor = .mainColor
+            content.imageProperties.tintColor = .black
             content.imageProperties.maximumSize = CGSize(width: 24, height: 24)
             content.text = menu.title
             cell.accessoryView = nil
+            cell.separatorInset = UIEdgeInsets(top: .greatestFiniteMagnitude, left: 0, bottom: 0, right: .greatestFiniteMagnitude)
             switch menu.title {
-            case "User Profile Management".localized():
-                cell.accessoryType = .disclosureIndicator
-            case "Sign-In Admin / Internal".localized():
+            case "Personal Information".localized():
                 cell.accessoryType = .disclosureIndicator
-            case "Change Admin / Internal Password".localized():
+            case "Access Admin / Internal Features".localized():
                 cell.accessoryType = .disclosureIndicator
-            case "Change Image FB Become Photo Profile".localized():
+            case "Sign-In to Web".localized():
                 cell.accessoryType = .disclosureIndicator
-            case "Set Internal / Officer Account".localized():
+            case "Sign-Up/Sign-In".localized():
                 cell.accessoryType = .disclosureIndicator
-            case "Pulsa".localized():
+            case "Backup & Restore".localized():
                 cell.accessoryType = .disclosureIndicator
-            case "Sign-Up (Change profile)".localized():
+            case "Notification Message(s)".localized():
                 cell.accessoryType = .disclosureIndicator
-            case "Sign-In (Change Device)".localized():
+            case "Notification Message(s) Group".localized():
                 cell.accessoryType = .disclosureIndicator
-            case "Sign-In to Nexilis Web".localized():
+//            case "Logout".localized():
+            case "Change Admin / Internal Password".localized():
                 cell.accessoryType = .disclosureIndicator
             case "Change Language".localized():
                 cell.accessoryType = .disclosureIndicator
             case "Version".localized():
-                content.secondaryText = UIApplication.appVersion
+                let accessoryButton = UIButton(type: .custom)
+                accessoryButton.setTitle(UIApplication.appVersion, for: .normal)
+                accessoryButton.setTitleColor(.black, for: .normal)
+                accessoryButton.contentHorizontalAlignment = .right;
+                accessoryButton.frame = CGRect(x: 0, y: 0, width: 100, height: 40)
+                accessoryButton.contentMode = .scaleAspectFit
+                cell.accessoryView = accessoryButton as UIView
             case "Vibrate Mode".localized():
                 cell.accessoryView = switchVibrateMode
             case "Save to Gallery".localized():
@@ -262,12 +310,45 @@ public class SettingTableViewController: UITableViewController {
         return cell
     }
     
+    private func checkIsChangePerson() -> Bool {
+        let isChangeProfile = Utils.getSetProfile()
+        if !isChangeProfile {
+            let alert = UIAlertController(title: "Change Profile".localized(), message: "You must change your name to use this feature".localized(), preferredStyle: .alert)
+            alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler: {(_) in
+                let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "signupsignin") as! SignUpSignIn
+                controller.forceLogin = true
+                let navigationController = UINavigationController(rootViewController: controller)
+                navigationController.modalPresentationStyle = .fullScreen
+                navigationController.navigationBar.tintColor = .white
+                navigationController.navigationBar.barTintColor = .mainColor
+                navigationController.navigationBar.isTranslucent = false
+                navigationController.navigationBar.overrideUserInterfaceStyle = .dark
+                navigationController.navigationBar.barStyle = .black
+                let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white]
+                UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
+                let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
+                navigationController.navigationBar.titleTextAttributes = textAttributes
+                navigationController.view.backgroundColor = .mainColor
+                if UIApplication.shared.visibleViewController?.navigationController != nil {
+                    UIApplication.shared.visibleViewController?.navigationController?.present(navigationController, animated: true, completion: nil)
+                } else {
+                    UIApplication.shared.visibleViewController?.present(navigationController, animated: true, completion: nil)
+                }
+            }))
+            if UIApplication.shared.visibleViewController?.navigationController != nil {
+                UIApplication.shared.visibleViewController?.navigationController?.present(alert, animated: true, completion: nil)
+            } else {
+                UIApplication.shared.visibleViewController?.present(alert, animated: true, completion: nil)
+            }
+            return false
+        }
+        return true
+    }
+    
     public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        tableView.deselectRow(at: indexPath, animated: false)
         let item = Item.menuFor(section: indexPath.section)[indexPath.row]
-        if item.title == "User Profile Management".localized() {
-            let isChangeProfile = Utils.getSetProfile()
-            if isChangeProfile {
+        if item.title == "Personal Information".localized() {
+            if(checkIsChangePerson()){
                 let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "profileView") as! ProfileViewController
                 controller.data = UserDefaults.standard.string(forKey: "me")!
                 controller.flag = .me
@@ -280,14 +361,35 @@ public class SettingTableViewController: UITableViewController {
                 }
                 navigationController?.show(controller, sender: nil)
             }
-        } else if item.title == "Sign-Up (Change profile)".localized() {
-            let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "changeNS") as! ChangeNamePassswordViewController
-            controller.fromSetting = true
-            controller.isSuccess = {
-                self.makeMenu()
-                self.tableView.reloadData()
+        } else if item.title == "Access Admin / Internal Features".localized() || item.title == "Change Admin / Internal Password".localized() {
+            if(checkIsChangePerson()){
+                if !CheckConnection.isConnectedToNetwork()  || API.nGetCLXConnState() == 0 {
+                    let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
+                    imageView.tintColor = .white
+                    let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                    banner.show()
+                    return
+                }
+                let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
+                if(item.title.contains("Change")){
+                    if let action = self.actionChangePassword(for: "admin", title: "Change Admin Password".localized()) {
+                        alertController.addAction(action)
+                    }
+                    if let action = self.actionChangePassword(for: "internal", title: "Change Internal Password".localized()) {
+                        alertController.addAction(action)
+                    }
+                }
+                else {
+                    if let action = self.actionLogin(for: "admin", title: "Access Admin Features".localized()) {
+                        alertController.addAction(action)
+                    }
+                    if let action = self.actionLogin(for: "internal", title: "Access Internal Features".localized()) {
+                        alertController.addAction(action)
+                    }
+                }
+                alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
+                self.present(alertController, animated: true)
             }
-            navigationController?.show(controller, sender: nil)
         } else if item.title == "Change Language".localized() {
             let vc = UIViewController()
             vc.preferredContentSize = CGSize(width: UIScreen.main.bounds.width - 10, height: 150)
@@ -313,20 +415,30 @@ public class SettingTableViewController: UITableViewController {
             alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: { (UIAlertAction) in
             }))
             
-            alert.addAction(UIAlertAction(title: "Select", style: .default, handler: { (UIAlertAction) in
+            alert.addAction(UIAlertAction(title: "Select".localized(), style: .default, handler: { (UIAlertAction) in
                 let selectedIndex = pickerView.selectedRow(inComponent: 0)
                 let lang = self.language[selectedIndex].values.first
                 UserDefaults.standard.set(lang, forKey: "i18n_language")
-                self.viewDidLoad()
+                self.navigationController?.navigationBar.topItem?.title = "Settings".localized();
+                self.navigationController?.navigationBar.setNeedsLayout()
+                self.makeMenu()
+                self.tableView.reloadData()
             }))
             self.present(alert, animated: true, completion: nil)
-        } else if item.title == "Sign-In (Change Device)".localized() {
+        } else if item.title == "Sign-In".localized() {
             let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "changeDevice") as! ChangeDeviceViewController
             controller.isDismiss = { newThumb in
                 self.makeMenu(imageSignIn: newThumb)
                 self.tableView.reloadData()
             }
             navigationController?.show(controller, sender: nil)
+        } else if item.title == "Sign-Up/Sign-In".localized() {
+            let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "signupsignin") as! SignUpSignIn
+            controller.isDismiss = { newThumb in
+                self.makeMenu(imageSignIn: newThumb)
+                self.tableView.reloadData()
+            }
+            navigationController?.show(controller, sender: nil)
         } else if item.title == "Sign-Out".localized() {
             let alert = UIAlertController(title: "Sign-Out".localized(), message: "Are you sure want to logout?".localized(), preferredStyle: .alert)
             alert.addAction(UIAlertAction(title: "Cancel".localized(), style: UIAlertAction.Style.default, handler: nil))
@@ -338,9 +450,8 @@ public class SettingTableViewController: UITableViewController {
                     banner.show()
                     return
                 }
+                Nexilis.showLoader()
                 DispatchQueue.global().async {
-                    self.deleteAllRecordDatabase()
-                    Nexilis.destroyAll()
                     let apiKey = Nexilis.sAPIKey
                     var id = UIDevice.current.identifierForVendor?.uuidString ?? "UNK-DEVICE"
                     if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getSignUpApi(api: apiKey, p_pin: id), timeout: 30 * 1000) {
@@ -350,59 +461,47 @@ public class SettingTableViewController: UITableViewController {
                             UserDefaults.standard.setValue(id, forKey: "me")
                             Utils.setProfile(value: false)
                             UserDefaults.standard.synchronize()
-                            // pos registration
-                            _ = Nexilis.write(message: CoreMessage_TMessageBank.getPostRegistration(p_pin: id))
+                            if Utils.getForceAnonymous() {
+                                self.deleteAllRecordDatabase()
+                                UserDefaults.standard.removeObject(forKey: "device_id")
+                                Nexilis.destroyAll()
+                                _ = Nexilis.write(message: CoreMessage_TMessageBank.getPostRegistration(p_pin: id))
+                            }
                             DispatchQueue.main.async {
-                                let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
+                                Nexilis.hideLoader(completion: {
+                                    let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
+                                    imageView.tintColor = .white
+                                    let banner = FloatingNotificationBanner(title: "Successfully Sign-Out".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
+                                    banner.show()
+                                    var dataImage: [AnyHashable : Any] = [:]
+                                    dataImage["name"] = ""
+                                    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
+                                    self.makeMenu()
+                                    self.tableView.reloadData()
+                                })
+                            }
+                        } else {
+                            Nexilis.hideLoader(completion: {
+                                let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                                 imageView.tintColor = .white
-                                let banner = FloatingNotificationBanner(title: "Successfully Sign-Out".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
+                                let banner = FloatingNotificationBanner(title: "Unable to access servers. Try again later".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
                                 banner.show()
-                                var dataImage: [AnyHashable : Any] = [:]
-                                dataImage["name"] = ""
-                                NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageFBUpdate"), object: nil, userInfo: dataImage)
-                                self.makeMenu()
-                                self.tableView.reloadData()
-                            }
+                            })
                         }
                     } else {
                         DispatchQueue.main.async {
-                            let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                            imageView.tintColor = .white
-                            let banner = FloatingNotificationBanner(title: "Unable to access servers. Try again later".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                            banner.show()
+                            Nexilis.hideLoader(completion: {
+                                let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
+                                imageView.tintColor = .white
+                                let banner = FloatingNotificationBanner(title: "Unable to access servers. Try again later".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                                banner.show()
+                            })
                         }
                     }
                 }
             }))
             self.present(alert, animated: true, completion: nil)
-        } else if item.title == "Sign-In Admin / Internal".localized() || item.title == "Change Admin / Internal Password".localized() {
-            if !CheckConnection.isConnectedToNetwork()  || API.nGetCLXConnState() == 0 {
-                let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                imageView.tintColor = .white
-                let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                banner.show()
-                return
-            }
-            let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
-            if(item.title.contains("Change")){
-                if let action = self.actionChangePassword(for: "admin", title: "Change Admin Password".localized()) {
-                    alertController.addAction(action)
-                }
-                if let action = self.actionChangePassword(for: "internal", title: "Change Internal Password".localized()) {
-                    alertController.addAction(action)
-                }
-            }
-            else {
-                if let action = self.actionLogin(for: "admin", title: "Sign-In as Admin".localized()) {
-                    alertController.addAction(action)
-                }
-                if let action = self.actionLogin(for: "internal", title: "Sign-In as Internal Team".localized()) {
-                    alertController.addAction(action)
-                }
-            }
-            alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
-            self.present(alertController, animated: true)
-        } else if item.title == "Sign-In to Nexilis Web".localized() {
+        } else if item.title == "Sign-In to Web".localized() {
             var permissionCheck = -1
             if AVCaptureDevice.authorizationStatus(for: .video) ==  .authorized {
                 permissionCheck = 1
@@ -441,229 +540,44 @@ public class SettingTableViewController: UITableViewController {
             navigationController.navigationBar.tintColor = .white
             navigationController.navigationBar.barTintColor = .mainColor
             navigationController.navigationBar.isTranslucent = false
-            navigationController.navigationBar.overrideUserInterfaceStyle = .dark
-            navigationController.navigationBar.barStyle = .black
             let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white]
             UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
             let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
             navigationController.navigationBar.titleTextAttributes = textAttributes
             navigationController.view.backgroundColor = .mainColor
+            navigationController.navigationBar.overrideUserInterfaceStyle = .dark
+            navigationController.navigationBar.barStyle = .black
             navigationController.modalPresentationStyle = .custom
             self.present(navigationController, animated: true)
-        } else if item.title == "Pulsa".localized() {
-            self.alert = UIAlertController(title: "Data warning in (IDR)".localized(), message: nil, preferredStyle: .alert)
-            self.textFields.removeAll()
-            self.alert?.addTextField{ (texfield) in
-                texfield.placeholder = "Saldo"
-                texfield.keyboardType = .numberPad
-                texfield.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControl.Event.editingChanged)
+        } else if item.title == "Notification Message(s)".localized() || item.title == "Notification Message(s) Group".localized() {
+            let controller = NotificationSound()
+            if item.title != "Notification Message(s)".localized() {
+                controller.isPersonal = false
             }
-            let submitAction = UIAlertAction(title: "Submit".localized(), style: .default, handler: { (action) -> Void in
-                let textField = self.alert?.textFields![0]
-                if !CheckConnection.isConnectedToNetwork()  || API.nGetCLXConnState() == 0 {
-                    let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                    imageView.tintColor = .white
-                    let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                    banner.show()
-                    return
-                }
-                DispatchQueue.global().async {
-                    let _ = Nexilis.writeSync(message: CoreMessage_TMessageBank.isiPulsaBNI(value: textField!.text!), timeout: 30 * 1000)
-                }
-                
-            })
-            submitAction.isEnabled = false
-            self.alert?.addAction(submitAction)
-            self.alert?.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
-            
-            self.present(self.alert!, animated: true, completion: nil)
-        } else if item.title == "Set Internal / Officer Account".localized() {
-            let controller = QmeraCallContactViewController()
-            controller.isDismiss = { user in
-                if user.userType != "23" && user.userType != "24" {
-                    let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
-                    alertController.addAction(UIAlertAction(title: "Set \(user.fullName) as Internal".localized(), style: .default, handler: { _ in
-                        self.setAsInternalAccount(user: user)
-                    }))
-                    alertController.addAction(UIAlertAction(title: "Set \(user.fullName) as Call Center".localized(), style: .default, handler: { _ in
-                        let viewSetOfficer = SetOfficerBNI()
-                        viewSetOfficer.f_pin = user.pin
-                        viewSetOfficer.name = user.fullName
-                        viewSetOfficer.modalTransitionStyle = .crossDissolve
-                        viewSetOfficer.modalPresentationStyle = .custom
-                        self.present(viewSetOfficer, animated: true)
-                    }))
-                    alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
-                    self.present(alertController, animated: true)
-                } else if user.userType == "24" {
-                    self.removeFromCCAccount(user: user)
-                } else {
-                    let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
-                    alertController.addAction(UIAlertAction(title: "Remove \(user.fullName) from Internal".localized(), style: .default, handler: { _ in
-                        self.removeFromInternalAccount(user: user)
-                    }))
-                    alertController.addAction(UIAlertAction(title: "Set \(user.fullName) as Call Center".localized(), style: .default, handler: { _ in
-                        let viewSetOfficer = SetOfficerBNI()
-                        viewSetOfficer.f_pin = user.pin
-                        viewSetOfficer.name = user.fullName
-                        viewSetOfficer.modalTransitionStyle = .crossDissolve
-                        viewSetOfficer.modalPresentationStyle = .custom
-                        self.present(viewSetOfficer, animated: true)
-                    }))
-                    alertController.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
-                    self.present(alertController, animated: true)
-                }
-            }
-            present(UINavigationController(rootViewController: controller), animated: true, completion: nil)
-        } else if item.title == "Change Image FB Become Photo Profile".localized() {
-            
+            let navigationController = UINavigationController(rootViewController: controller)
+            navigationController.navigationBar.tintColor = .white
+            navigationController.navigationBar.barTintColor = .mainColor
+            navigationController.navigationBar.isTranslucent = false
+            navigationController.navigationBar.overrideUserInterfaceStyle = .dark
+            navigationController.navigationBar.barStyle = .black
+            let cancelButtonAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: UIColor.white]
+            UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes, for: .normal)
+            let textAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
+            navigationController.navigationBar.titleTextAttributes = textAttributes
+            navigationController.view.backgroundColor = .mainColor
+            self.present(navigationController, animated: true)
         }
     }
     
-    private func setAsInternalAccount(user: User) {
-        self.alert = UIAlertController(title: "Set Internal Account".localized(), message: "Are you sure want to add \(user.fullName) to Internal Account?", preferredStyle: .alert)
-        self.alert?.addAction(UIAlertAction(title: "Yes".localized(), style: .default, handler: { (action) -> Void in
-            if !CheckConnection.isConnectedToNetwork()  || API.nGetCLXConnState() == 0 {
-                let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                imageView.tintColor = .white
-                let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                banner.show()
-                return
-            }
-            if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getManagementContactCenter(user_type: "3", l_pin: user.pin)) {
-                if response.isOk() {
-                    DispatchQueue.main.async {
-                        let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
-                        imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Successfully changed".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
-                        banner.show()
-                    }
-                }
-                DispatchQueue.main.async {
-                    if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "11" {
-                        let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                        imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Username or password does not match".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                        banner.show()
-                    } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "20" {
-                        let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                        imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Invalid password".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                        banner.show()
-                    }
-                }
-            } else {
-                DispatchQueue.main.async {
-                    let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                    imageView.tintColor = .white
-                    let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                    banner.show()
-                }
-            }
-        }))
-        self.alert?.addAction(UIAlertAction(title: "No".localized(), style: .cancel, handler: nil))
-        self.present(self.alert!, animated: true, completion: nil)
-    }
-    
-    private func removeFromCCAccount(user: User) {
-        self.alert = UIAlertController(title: "Remove Officer Account".localized(), message: "Are you sure want to remove \(user.fullName) from Officer Account?", preferredStyle: .alert)
-        self.alert?.addAction(UIAlertAction(title: "Yes".localized(), style: .default, handler: { (action) -> Void in
-            if !CheckConnection.isConnectedToNetwork()  || API.nGetCLXConnState() == 0 {
-                let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                imageView.tintColor = .white
-                let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                banner.show()
-                return
-            }
-            if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getManagementContactCenter(user_type: "0", l_pin: user.pin)) {
-                if response.isOk() {
-                    DispatchQueue.main.async {
-                        let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
-                        imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Successfully changed".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
-                        banner.show()
-                    }
-                }
-                DispatchQueue.main.async {
-                    if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "11" {
-                        let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                        imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Username or password does not match".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                        banner.show()
-                    } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "20" {
-                        let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                        imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Invalid password".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                        banner.show()
-                    }
-                }
-            } else {
-                DispatchQueue.main.async {
-                    let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                    imageView.tintColor = .white
-                    let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                    banner.show()
-                }
-            }
-        }))
-        self.alert?.addAction(UIAlertAction(title: "No".localized(), style: .cancel, handler: nil))
-        self.present(self.alert!, animated: true, completion: nil)
-    }
-    
-    private func removeFromInternalAccount(user: User) {
-        self.alert = UIAlertController(title: "Remove Officer Account".localized(), message: "Are you sure want to remove \(user.fullName) from Internal Account?", preferredStyle: .alert)
-        self.alert?.addAction(UIAlertAction(title: "Yes".localized(), style: .default, handler: { (action) -> Void in
-            if !CheckConnection.isConnectedToNetwork()  || API.nGetCLXConnState() == 0 {
-                let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                imageView.tintColor = .white
-                let banner = FloatingNotificationBanner(title: "Check your connection".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                banner.show()
-                return
-            }
-            if let response = Nexilis.writeSync(message: CoreMessage_TMessageBank.getManagementContactCenter(user_type: "2", l_pin: user.pin)) {
-                if response.isOk() {
-                    DispatchQueue.main.async {
-                        let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
-                        imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Successfully changed".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
-                        banner.show()
-                    }
-                }
-                DispatchQueue.main.async {
-                    if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "11" {
-                        let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                        imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Username or password does not match".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                        banner.show()
-                    } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "20" {
-                        let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                        imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Invalid password".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                        banner.show()
-                    }
-                }
-            } else {
-                DispatchQueue.main.async {
-                    let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
-                    imageView.tintColor = .white
-                    let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
-                    banner.show()
-                }
-            }
-        }))
-        self.alert?.addAction(UIAlertAction(title: "No".localized(), style: .cancel, handler: nil))
-        self.present(self.alert!, animated: true, completion: nil)
-    }
-    
     private func actionLogin(for type: String, title: String) -> UIAlertAction? {
         return UIAlertAction(title: title, style: .default) { _ in
-            self.alert = UIAlertController(title: "Sign-In as Admin".localized(), message: nil, preferredStyle: .alert)
+            self.alert = UIAlertController(title:"Access Admin Features".localized(), message: nil, preferredStyle: .alert)
             if type == "internal" {
-                self.alert = UIAlertController(title: "Sign-In as Internal Team".localized(), message: nil, preferredStyle: .alert)
+                self.alert = UIAlertController(title: "Access Internal Features".localized(), message: nil, preferredStyle: .alert)
             }
             self.textFields.removeAll()
             self.alert?.addTextField{ (texfield) in
-                texfield.placeholder = "Password"
+                texfield.placeholder = "Password".localized()
                 texfield.isSecureTextEntry = true
                 texfield.addPadding(.right(40))
                 texfield.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControl.Event.editingChanged)
@@ -685,17 +599,23 @@ public class SettingTableViewController: UITableViewController {
                     banner.show()
                     return
                 }
+                Nexilis.showLoader()
                 if type == "admin" {
                     self.signInAdmin(password: textField!.text!, completion: { result in
                         if result {
                             DispatchQueue.main.async {
-                                let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
-                                imageView.tintColor = .white
-                                let banner = FloatingNotificationBanner(title: "Successfully Sign-In Admin".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
-                                banner.show()
-                                let itemCP = Item(icon: UIImage(systemName: "person.crop.rectangle"), title: "Change Admin / Internal Password".localized())
-                                Item.menus["Personal"]?[1] = itemCP
-                                self.tableView.reloadData()
+                                Nexilis.hideLoader {
+                                    self.makeMenu()
+                                    self.tableView.reloadData()
+                                    let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
+                                    imageView.tintColor = .white
+                                    let banner = FloatingNotificationBanner(title: "Successfully Sign-In Admin".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
+                                    banner.show()
+                                }
+                            }
+                        } else {
+                            DispatchQueue.main.async {
+                                Nexilis.hideLoader {}
                             }
                         }
                     })
@@ -703,12 +623,18 @@ public class SettingTableViewController: UITableViewController {
                     self.signInInternal(password: textField!.text!, completion: { result in
                         if result {
                             DispatchQueue.main.async {
-                                let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
-                                imageView.tintColor = .white
-                                let banner = FloatingNotificationBanner(title: "Successfully Sign-In Internal Team".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
-                                banner.show()
-                                Item.menus["Personal"]?.remove(at: 1)
-                                self.tableView.reloadData()
+                                Nexilis.hideLoader {
+                                    self.makeMenu()
+                                    self.tableView.reloadData()
+                                    let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
+                                    imageView.tintColor = .white
+                                    let banner = FloatingNotificationBanner(title: "Successfully Sign-In Internal Team".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .success, colors: nil, iconPosition: .center)
+                                    banner.show()
+                                }
+                            }
+                        } else {
+                            DispatchQueue.main.async {
+                                Nexilis.hideLoader {}
                             }
                         }
                     })
@@ -815,6 +741,8 @@ public class SettingTableViewController: UITableViewController {
     
     @objc func alertTextFieldDidChange(_ sender: UITextField) {
         if(!textFields.isEmpty){
+            print("text count 0: \(textFields[0].text!.count)")
+            print("text count 1: \(textFields[1].text!.count)")
             alert?.actions[0].isEnabled = textFields[0].text!.count > 0 && textFields[1].text!.count > 0
         }
         else {
@@ -836,12 +764,12 @@ public class SettingTableViewController: UITableViewController {
                     if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "11" {
                         let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                         imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Username or password does not match".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                        let banner = FloatingNotificationBanner(title: "Username or password does not match".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .top)
                         banner.show()
                     } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "20" {
                         let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                         imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Invalid password".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                        let banner = FloatingNotificationBanner(title: "Invalid password".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .top)
                         banner.show()
                     }
                 }
@@ -849,7 +777,7 @@ public class SettingTableViewController: UITableViewController {
                 DispatchQueue.main.async {
                     let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                     imageView.tintColor = .white
-                    let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                    let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .top)
                     banner.show()
                 }
             }
@@ -871,12 +799,12 @@ public class SettingTableViewController: UITableViewController {
                     if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "11" {
                         let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                         imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Username or password does not match".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                        let banner = FloatingNotificationBanner(title: "Username or password does not match".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .top)
                         banner.show()
                     } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "20" {
                         let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                         imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Invalid password".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                        let banner = FloatingNotificationBanner(title: "Invalid password".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .top)
                         banner.show()
                     }
                 }
@@ -884,7 +812,7 @@ public class SettingTableViewController: UITableViewController {
                 DispatchQueue.main.async {
                     let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                     imageView.tintColor = .white
-                    let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                    let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .top)
                     banner.show()
                 }
             }
@@ -908,12 +836,12 @@ public class SettingTableViewController: UITableViewController {
                     if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "11" {
                         let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                         imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Username or password does not match".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                        let banner = FloatingNotificationBanner(title: "Username or password does not match".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .top)
                         banner.show()
                     } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "20" {
                         let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                         imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Invalid password".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                        let banner = FloatingNotificationBanner(title: "Invalid password".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .top)
                         banner.show()
                     }
                 }
@@ -921,7 +849,7 @@ public class SettingTableViewController: UITableViewController {
                 DispatchQueue.main.async {
                     let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                     imageView.tintColor = .white
-                    let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                    let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .top)
                     banner.show()
                 }
             }
@@ -945,12 +873,12 @@ public class SettingTableViewController: UITableViewController {
                     if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "11" {
                         let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                         imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Username or password does not match".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                        let banner = FloatingNotificationBanner(title: "Username or password does not match".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .top)
                         banner.show()
                     } else if response.getBody(key: CoreMessage_TMessageKey.ERRCOD, default_value: "99") == "20" {
                         let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                         imageView.tintColor = .white
-                        let banner = FloatingNotificationBanner(title: "Invalid password".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                        let banner = FloatingNotificationBanner(title: "Invalid password".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .top)
                         banner.show()
                     }
                 }
@@ -958,7 +886,7 @@ public class SettingTableViewController: UITableViewController {
                 DispatchQueue.main.async {
                     let imageView = UIImageView(image: UIImage(systemName: "xmark.circle.fill"))
                     imageView.tintColor = .white
-                    let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .center)
+                    let banner = FloatingNotificationBanner(title: "Unable to access servers".localized(), subtitle: nil, titleFont: UIFont.systemFont(ofSize: 16), titleColor: nil, titleTextAlign: .left, subtitleFont: nil, subtitleColor: nil, subtitleTextAlign: nil, leftView: imageView, rightView: nil, style: .danger, colors: nil, iconPosition: .top)
                     banner.show()
                 }
             }

+ 3 - 3
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Control/TypeViewController.swift

@@ -9,7 +9,7 @@ import UIKit
 
 class TypeViewController: UITableViewController {
     
-    static var data: [String] = ["Notification".localized(), "In App".localized()]
+    var data: [String] = ["Notification".localized(), "In App".localized()]
     
     var selected: String?
     
@@ -20,12 +20,12 @@ class TypeViewController: UITableViewController {
     }
     
     override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-        return TypeViewController.data.count
+        return data.count
     }
     
     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell = tableView.dequeueReusableCell(withIdentifier: "reuseCell", for: indexPath )
-        let selectedCell = TypeViewController.data[indexPath.row]
+        let selectedCell = data[indexPath.row]
         cell.textLabel?.text = selectedCell
         cell.accessoryType = selected == selectedCell ? .checkmark : .none
         cell.tag = indexPath.row

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

@@ -45,10 +45,10 @@ class CreateViewController: UITableViewController {
             streamingDesc.isEditable = false
             streamingTag.isEnabled = false
             if let a = data["type"] as? String {
-                audience.text = AudienceViewController.data[getTypeIndex(value: a)]
+                audience.text = AudienceViewController().data[getTypeIndex(value: a)]
             }
             if let b = data["broadcast_type"] as? String {
-                type.text = TypeViewController.data[getBroadcastIndex(value: b)]
+                type.text = TypeViewController().data[getBroadcastIndex(value: b)]
             }
             if let c = data["title"] as? String {
                 streamingTitle.text = c

+ 8 - 8
appbuilder-ios/NexilisLite/NexilisLite/Source/View/Streaming/QmeraCreateStreamingViewController.swift

@@ -32,8 +32,8 @@ public class QmeraCreateStreamingViewController: UITableViewController {
     ]
     
     private var chooser: [Chooser] = [
-        Chooser(title: "Target Audience".localized(), value: AudienceViewController.data.first),
-        Chooser(title: "Promotion Type".localized(), value: TypeViewController.data.first)
+        Chooser(title: "Target Audience".localized(), value: AudienceViewController().data.first),
+        Chooser(title: "Promotion Type".localized(), value: TypeViewController().data.first)
     ]
     
     private var users: [User] = [] {
@@ -107,10 +107,10 @@ public class QmeraCreateStreamingViewController: UITableViewController {
             descriptionView.isEditable = false
             taglineView.isEnabled = false
             if let a = data["type"] as? String {
-                chooser[0].value = AudienceViewController.data[getTypeIndex(value: a)]
+                chooser[0].value = AudienceViewController().data[getTypeIndex(value: a)]
             }
             if let b = data["broadcast_type"] as? String {
-                chooser[1].value = TypeViewController.data[getBroadcastIndex(value: b)]
+                chooser[1].value = TypeViewController().data[getBroadcastIndex(value: b)]
             }
             if let c = data["title"] as? String {
                 titleView.text = c
@@ -127,10 +127,10 @@ public class QmeraCreateStreamingViewController: UITableViewController {
             descriptionView.isEditable = false
             taglineView.isEnabled = false
             if let a = data["type"] as? String {
-                chooser[0].value = AudienceViewController.data[getTypeIndex(value: a)]
+                chooser[0].value = AudienceViewController().data[getTypeIndex(value: a)]
             }
             if let b = data["broadcast_type"] as? String {
-                chooser[1].value = TypeViewController.data[getBroadcastIndex(value: b)]
+                chooser[1].value = TypeViewController().data[getBroadcastIndex(value: b)]
             }
             if let c = data["title"] as? String {
                 titleView.text = c
@@ -404,7 +404,7 @@ public class QmeraCreateStreamingViewController: UITableViewController {
                 controller.selected = chooser.value
                 controller.isDismiss = { [weak self] index in
                     chooser.id = index
-                    chooser.value = AudienceViewController.data[index]
+                    chooser.value = AudienceViewController().data[index]
                     guard let sec = self?.sections else {
                         return
                     }
@@ -442,7 +442,7 @@ public class QmeraCreateStreamingViewController: UITableViewController {
                 controller.selected = chooser.value
                 controller.isDismiss = { index in
                     chooser.id = index
-                    chooser.value = TypeViewController.data[index]
+                    chooser.value = TypeViewController().data[index]
                     DispatchQueue.main.async {
                         tableView.reloadRows(at: [indexPath], with: .automatic)
                     }