Kaynağa Gözat

update add new request and release for 5.0.59

alqindiirsyam 4 hafta önce
ebeveyn
işleme
ba503fb9b4

+ 39 - 58
NexilisLite/NexilisLite/Source/Callback.swift

@@ -151,8 +151,8 @@ class NetworkMonitor {
         monitor.pathUpdateHandler = { [weak self] path in
             guard let self = self else { return }
             
-            self.canAccessGoogle { connected in
-                if connected {
+            DispatchQueue.main.async {
+                if path.status == .satisfied && API.nGetCLXConnState() != 0 {
                     // Cancel any pending "disconnected" work if connection is back
                     self.disconnectWorkItem?.cancel()
                     self.disconnectWorkItem = nil
@@ -198,26 +198,26 @@ class NetworkMonitor {
     private func handleDisconnected() {
         fromDisconnect = true
         DispatchQueue.main.async {
-            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()
+//            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()
             
-            if Utils.getSecureFolderEncrypt().isEmpty && Database.shared.database != nil {
+            if Utils.getSecureFolderOffline() == "0" && Database.shared.database != nil {
                 self.timerReloadData?.invalidate()
                 self.timerReloadData = Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in
                     Database.shared.database = nil
@@ -244,47 +244,28 @@ class NetworkMonitor {
                 self.timerReloadData?.invalidate()
                 self.timerReloadData = nil
                 
-                let imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
-                imageView.tintColor = .white
-                let banner = FloatingNotificationBanner(
-                    title: "You're Connected".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 imageView = UIImageView(image: UIImage(systemName: "checkmark.circle.fill"))
+//                imageView.tintColor = .white
+//                let banner = FloatingNotificationBanner(
+//                    title: "You're Connected".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()
             }
             if Database.shared.database == nil {
                 Nexilis.getFeatureAccess()
             }
         }
     }
-    
-    func canAccessGoogle(completion: @escaping (Bool) -> Void) {
-        guard let url = URL(string: "https://www.google.com") else {
-            completion(false)
-            return
-        }
-        
-        var request = URLRequest(url: url)
-        request.timeoutInterval = 5
-        
-        let task = URLSession.shared.dataTask(with: request) { _, response, _ in
-            if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
-                completion(true)
-            } else {
-                completion(false)
-            }
-        }
-        task.resume()
-    }
 }

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

@@ -3171,12 +3171,20 @@ extension EditorGroup: UITextViewDelegate, CustomTextViewPasteDelegate {
                         print("Access database error: \(error.localizedDescription)")
                     }
                 })
+                if !dataURL.isEmpty {
+                    if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
+                        let imageUrl = data["imageUrl"] as? String
+                        let link = data["link"]  as? String ?? ""
+                        if imageUrl == nil || (link.contains("youtube.com") && link.contains("watch?v=") && !imageUrl!.contains("img.youtube.com/vi/")) {
+                            dataURL = ""
+                        }
+                    }
+                }
                 if !dataURL.isEmpty {
                     if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
                         let title = data["title"]  as? String ?? ""
                         let description = data["description"]  as? String ?? ""
                         let imageUrl = data["imageUrl"] as? String
-                        let link = data["link"]  as? String ?? ""
                         if self.showingLink != text {
                             self.showingLink = text
                             self.deleteLinkPreview()
@@ -3195,9 +3203,19 @@ extension EditorGroup: UITextViewDelegate, CustomTextViewPasteDelegate {
                                        cache: DisabledCache.instance)
                     let preview = slp.preview(stringURl,
                                               onSuccess: { result in
-                        let title = result.title ?? "No Title"
-                        let description = stringURl.contains("google.com") ? "" : result.description
-                        let imageUrl = result.icon
+                        let title = result.title?.trimmingCharacters(in: .whitespacesAndNewlines)
+                                    .nilIfEmpty ?? URL(string: text)?.host ?? "Untitled"
+                        let description: String
+                        if text.contains("google.com") {
+                            description = "" // special rule for google
+                        } else {
+                            description = result.description?.trimmingCharacters(in: .whitespacesAndNewlines)
+                                .nilIfEmpty ?? "No description available"
+                        }
+                        let imageUrl = self.youtubeThumbnail(from: text)
+                            ?? result.image
+                            ?? result.icon
+                            ?? ""
                         Database.shared.database?.inTransaction({ (fmdb, rollback) in
                             do {
                                 var dataJson: [String: Any] = [:]
@@ -3227,7 +3245,7 @@ extension EditorGroup: UITextViewDelegate, CustomTextViewPasteDelegate {
                             }
                         }
                     },
-                                              onError: { error in
+                    onError: { error in
                         self.deleteLinkPreview()
                     })
                 }
@@ -3310,7 +3328,7 @@ extension EditorGroup: UITextViewDelegate, CustomTextViewPasteDelegate {
         } else {
             linkPreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor, constant: 5.0).isActive = true
         }
-        linkPreview.topAnchor.constraint(equalTo: descPreview.bottomAnchor, constant: 8.0).isActive = true
+        linkPreview.topAnchor.constraint(equalTo: descPreview.bottomAnchor).isActive = true
         linkPreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -80.0).isActive = true
         linkPreview.text = stringURl
         linkPreview.font = UIFont.systemFont(ofSize: 10.0 + offset())
@@ -3952,7 +3970,7 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
             } else if dataMessages[indexPath!.row]["attachment_flag"]  as? String ?? "" == "11" {
                 children = [reply, pin, delete]
             }
-            if ((Nexilis.checkingAccess(key: "secure_folder_forward") && dataMessages[indexPath!.row]["attachment_flag"] as? String ?? "" != "11") || (!(dataMessages[indexPath!.row][TypeDataMessage.message_text]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["image_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["video_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["file_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["audio_id"]  as? String ?? "").isEmpty) || (dataMessages[indexPath!.row][TypeDataMessage.spec_file] as? String ?? "").contains("forward")) && dataMessages[indexPath!.row]["read_receipts"] as? String != "8" {
+            if (Nexilis.checkingAccess(key: "secure_folder_forward") || (!(dataMessages[indexPath!.row][TypeDataMessage.message_text]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["image_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["video_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["file_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["audio_id"]  as? String ?? "").isEmpty) || (dataMessages[indexPath!.row][TypeDataMessage.spec_file] as? String ?? "").contains("forward")) && dataMessages[indexPath!.row]["read_receipts"] as? String != "8" && dataMessages[indexPath!.row]["attachment_flag"] as? String ?? "" != "11" {
                 children.insert(forward, at: 2)
             }
             if dataMessages[indexPath!.row]["f_pin"] as? String ?? "" != "-999" && dataMessages[indexPath!.row]["f_pin"] as? String != User.getMyPin() && dataMessages[indexPath!.row]["attachment_flag"]  as? String ?? "" != "11" {
@@ -3961,8 +3979,8 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
             if (dataMessages[indexPath!.row]["f_pin"]  as? String ?? "") == idMe {
                 children.insert(info, at: children.count - 1)
             }
-            if !(dataMessages[indexPath!.row][TypeDataMessage.message_text]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row][TypeDataMessage.attachment_flag] as? String ?? "") != "11" {
-                if (dataMessages[indexPath!.row]["f_pin"]  as? String ?? "") == idMe && ((dataMessages[indexPath!.row][TypeDataMessage.is_forwarded] as? Int) ?? 0) == 0 {
+            if !(dataMessages[indexPath!.row][TypeDataMessage.message_text]  as? String ?? "").isEmpty {
+                if (dataMessages[indexPath!.row]["f_pin"]  as? String ?? "") == idMe && ((dataMessages[indexPath!.row][TypeDataMessage.is_forwarded] as? Int) ?? 0) == 0 && (dataMessages[indexPath!.row][TypeDataMessage.attachment_flag] as? String ?? "") != "11" {
                     let date = Date(milliseconds: Int64(dataMessages[indexPath!.row][TypeDataMessage.server_date] as? String ?? "") ?? 0)
                     let pastDate = date.addingTimeInterval(-10 * 60)
                     let differenceInSeconds = date.timeIntervalSince(pastDate)
@@ -3970,7 +3988,9 @@ extension EditorGroup: UIContextMenuInteractionDelegate {
                         children.insert(edit, at: children.count - 1)
                     }
                 }
-                isMore = true
+                if (dataMessages[indexPath!.row][TypeDataMessage.attachment_flag] as? String ?? "") != "11" && (dataMessages[indexPath!.row]["image_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["video_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["file_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["audio_id"]  as? String ?? "").isEmpty{
+                    isMore = true
+                }
             }
         }
         
@@ -6432,6 +6452,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource, AVAudioPlayer
             }
             if !text.isEmpty {
                 isLoadingShowLink = true
+                var dataURL = ""
                 func showLink() {
                     if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
                         let title = data["title"] as? String
@@ -6462,7 +6483,8 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource, AVAudioPlayer
                             imagePreview.topAnchor.constraint(equalTo: containerLinkMessage.topAnchor).isActive = true
                             imagePreview.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
                             imagePreview.loadImageAsync(with: imageUrl)
-                            imagePreview.contentMode = .scaleToFill
+                            imagePreview.contentMode = .scaleAspectFill
+                            imagePreview.clipsToBounds = true
                         }
                         
                         let titlePreview = UILabel()
@@ -6476,7 +6498,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource, AVAudioPlayer
                         titlePreview.topAnchor.constraint(equalTo: containerLinkMessage.topAnchor, constant: 10.0).isActive = true
                         titlePreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -5.0).isActive = true
                         titlePreview.text = title
-                        titlePreview.font = UIFont.systemFont(ofSize: 14.0 + offset(), weight: .bold)
+                        titlePreview.font = UIFont.systemFont(ofSize: 12.0 + offset(), weight: .bold)
                         titlePreview.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
                         
                         let descPreview = UILabel()
@@ -6520,7 +6542,6 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource, AVAudioPlayer
                         }
                     }
                 }
-                var dataURL = ""
                 Database.shared.database?.inTransaction({ (fmdb, rollback) in
                     do {
                         if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select data_link from LINK_PREVIEW where link='\(text)'"), cursor.next() {
@@ -6534,6 +6555,24 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource, AVAudioPlayer
                         print("Access database error: \(error.localizedDescription)")
                     }
                 })
+                if !dataURL.isEmpty {
+                    if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
+                        let imageUrl = data["imageUrl"] as? String
+                        let link = data["link"]  as? String ?? ""
+                        if imageUrl == nil || (link.contains("youtube.com") && link.contains("watch?v=") && !imageUrl!.contains("img.youtube.com/vi/")) {
+                            dataURL = ""
+                        }
+                    }
+                }
+                if !dataURL.isEmpty {
+                    if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
+                        let imageUrl = data["imageUrl"] as? String
+                        let link = data["link"]  as? String ?? ""
+                        if imageUrl == nil || (link.contains("youtube.com") && link.contains("watch?v=") && !imageUrl!.contains("img.youtube.com/vi/")) {
+                            dataURL = ""
+                        }
+                    }
+                }
                 if dataURL.isEmpty {
                     let urlConfig = URLSessionConfiguration.default
                     let sessionDelegate = SelfSignedURLSessionDelegate()
@@ -6544,9 +6583,19 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource, AVAudioPlayer
                                                cache: DisabledCache.instance)
                     let preview = slp.preview(text,
                                               onSuccess: { result in
-                        let title = result.title ?? "No Title"
-                        let description = text.contains("google.com") ? "" : result.description
-                        let imageUrl = result.icon
+                        let title = result.title?.trimmingCharacters(in: .whitespacesAndNewlines)
+                                    .nilIfEmpty ?? URL(string: text)?.host ?? "Untitled"
+                        let description: String
+                        if text.contains("google.com") {
+                            description = "" // special rule for google
+                        } else {
+                            description = result.description?.trimmingCharacters(in: .whitespacesAndNewlines)
+                                .nilIfEmpty ?? "No description available"
+                        }
+                        let imageUrl = self.youtubeThumbnail(from: text)
+                            ?? result.image
+                            ?? result.icon
+                            ?? ""
                         Database.shared.database?.inTransaction({ (fmdb, rollback) in
                             do {
                                 var dataJson: [String: Any] = [:]
@@ -6823,6 +6872,24 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource, AVAudioPlayer
         return cellMessage
     }
     
+    func youtubeThumbnail(from url: String) -> String? {
+        guard let url = URL(string: url) else { return nil }
+        let host = url.host ?? ""
+        
+        if host.contains("youtube.com"),
+           let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems,
+           let videoId = queryItems.first(where: { $0.name == "v" })?.value {
+            return "https://img.youtube.com/vi/\(videoId)/hqdefault.jpg"
+        }
+        
+        if host.contains("youtu.be") {
+            let videoId = url.lastPathComponent
+            return "https://img.youtube.com/vi/\(videoId)/hqdefault.jpg"
+        }
+        
+        return nil
+    }
+    
     func playPauseAudio(indexPath: IndexPath, playButton: UIButton, progressSlider: UISlider, timeLabel: UILabel) {
         guard let audioPlayer = audioPlayers[indexPath] else { return }
 

+ 91 - 19
NexilisLite/NexilisLite/Source/View/Chat/EditorPersonal.swift

@@ -504,7 +504,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         ])
         let exblock = User.getDataCanNil(pin: self.dataPerson["f_pin"]!!)?.ex_block
         blocking = exblock == nil ? "0" : exblock!.isEmpty ? "0" : exblock!
-        if blocking == "1" && self.dataPerson["f_pin"]!! != "-999" {
+        if blocking == "1" && self.dataPerson["f_pin"]!! != "-999" && self.dataPerson["isOfficial"]!! != "1" {
             menu = UIMenu(title: "", children: [
                 actionSearch,
                 actionUnblock,
@@ -512,7 +512,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
             ])
             blockedView(blocked: "1")
         } else if blocking == "0" {
-            if self.dataPerson["f_pin"]!! != "-999" && complaintId.isEmpty {
+            if self.dataPerson["f_pin"]!! != "-999" && complaintId.isEmpty && self.dataPerson["isOfficial"]!! != "1" {
                 menu = UIMenu(title: "", children: [
                     actionSearch,
                     actionBlock,
@@ -545,7 +545,7 @@ public class EditorPersonal: UIViewController, ImageVideoPickerDelegate, UIGestu
         let buttonVideoCall = UIBarButtonItem(image: UIImage(systemName: "video", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular, scale: .default)), style: .plain, target: self, action: #selector(audioVideoCall(sender:)))
         buttonVideoCall.tag = 1
         let buttonAddRoom = UIBarButtonItem(image: UIImage(systemName: "plus.message", withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .regular, scale: .default)), style: .plain, target: self, action: #selector(addRoom(sender:)))
-        if dataPerson["f_pin"] != "-999" && !isContactCenter && blocking == "0" {
+        if dataPerson["f_pin"] != "-999" && !isContactCenter && blocking == "0" && self.dataPerson["isOfficial"]!! != "1" {
             navigationItem.rightBarButtonItems = [moreIcon,buttonAudioCall,buttonVideoCall]
         } else if !isContactCenter {
             navigationItem.rightBarButtonItem = moreIcon
@@ -4362,12 +4362,20 @@ extension EditorPersonal: UITextViewDelegate, CustomTextViewPasteDelegate {
                         print("Access database error: \(error.localizedDescription)")
                     }
                 })
+                if !dataURL.isEmpty {
+                    if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
+                        let imageUrl = data["imageUrl"] as? String
+                        let link = data["link"]  as? String ?? ""
+                        if imageUrl == nil || (link.contains("youtube.com") && link.contains("watch?v=") && !imageUrl!.contains("img.youtube.com/vi/")) {
+                            dataURL = ""
+                        }
+                    }
+                }
                 if !dataURL.isEmpty {
                     if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
                         let title = data["title"]  as? String ?? ""
                         let description = data["description"]  as? String ?? ""
                         let imageUrl = data["imageUrl"] as? String
-                        let link = data["link"]  as? String ?? ""
                         if self.showingLink != text {
                             self.showingLink = text
                             self.deleteLinkPreview()
@@ -4386,9 +4394,24 @@ extension EditorPersonal: UITextViewDelegate, CustomTextViewPasteDelegate {
                                        cache: DisabledCache.instance)
                     let preview = slp.preview(stringURl,
                                               onSuccess: { result in
-                        let title = result.title ?? "No Title"
-                        let description = stringURl.contains("google.com") ? "" : result.description
-                        let imageUrl = result.icon
+                        print("MASUK SINI KAH? :\(result)")
+                        if result.title == nil {
+                            self.checkLink(fullText: fullText)
+                            return
+                        }
+                        let title = result.title?.trimmingCharacters(in: .whitespacesAndNewlines)
+                                    .nilIfEmpty ?? URL(string: text)?.host ?? "Untitled"
+                        let description: String
+                        if text.contains("google.com") {
+                            description = "" // special rule for google
+                        } else {
+                            description = result.description?.trimmingCharacters(in: .whitespacesAndNewlines)
+                                .nilIfEmpty ?? ""
+                        }
+                        let imageUrl = self.youtubeThumbnail(from: text)
+                            ?? result.image
+                            ?? result.icon
+                            ?? ""
                         Database.shared.database?.inTransaction({ (fmdb, rollback) in
                             do {
                                 var dataJson: [String: Any] = [:]
@@ -4418,7 +4441,8 @@ extension EditorPersonal: UITextViewDelegate, CustomTextViewPasteDelegate {
                             }
                         }
                     },
-                                              onError: { error in
+                    onError: { error in
+                        print("onError? :\(error)")
                         self.deleteLinkPreview()
                     })
                 }
@@ -4428,6 +4452,24 @@ extension EditorPersonal: UITextViewDelegate, CustomTextViewPasteDelegate {
         }
     }
     
+    func youtubeThumbnail(from url: String) -> String? {
+        guard let url = URL(string: url) else { return nil }
+        let host = url.host ?? ""
+        
+        if host.contains("youtube.com"),
+           let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems,
+           let videoId = queryItems.first(where: { $0.name == "v" })?.value {
+            return "https://img.youtube.com/vi/\(videoId)/hqdefault.jpg"
+        }
+        
+        if host.contains("youtu.be") {
+            let videoId = url.lastPathComponent
+            return "https://img.youtube.com/vi/\(videoId)/hqdefault.jpg"
+        }
+        
+        return nil
+    }
+    
     private func buildPreviewLink(imageUrl: String?, title: String, description: String?, stringURl: String) {
         if !self.viewTextfield.subviews.contains(self.containerLink){
             UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
@@ -4498,7 +4540,7 @@ extension EditorPersonal: UITextViewDelegate, CustomTextViewPasteDelegate {
         } else {
             linkPreview.leadingAnchor.constraint(equalTo: self.containerLink.leadingAnchor, constant: 5.0).isActive = true
         }
-        linkPreview.topAnchor.constraint(equalTo: descPreview.bottomAnchor, constant: 8.0).isActive = true
+        linkPreview.topAnchor.constraint(equalTo: descPreview.bottomAnchor).isActive = true
         linkPreview.trailingAnchor.constraint(equalTo: self.containerLink.trailingAnchor, constant: -80.0).isActive = true
         linkPreview.text = stringURl
         linkPreview.font = UIFont.systemFont(ofSize: 10.0 + offset())
@@ -5096,14 +5138,14 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
             } else if dataMessages[indexPath!.row]["attachment_flag"]  as? String ?? "" == "11" {
                children = [reply, delete]
             }
-            if ((Nexilis.checkingAccess(key: "secure_folder_forward") && dataMessages[indexPath!.row]["attachment_flag"] as? String ?? "" != "11") || (!(dataMessages[indexPath!.row][TypeDataMessage.message_text]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["image_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["video_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["file_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["audio_id"]  as? String ?? "").isEmpty) || (dataMessages[indexPath!.row][TypeDataMessage.spec_file] as? String ?? "").contains("forward")) && dataMessages[indexPath!.row]["read_receipts"] as? String != "8" {
+            if ((Nexilis.checkingAccess(key: "secure_folder_forward") && dataMessages[indexPath!.row]["attachment_flag"] as? String ?? "" != "11") || (!(dataMessages[indexPath!.row][TypeDataMessage.message_text]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["image_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["video_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["file_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["audio_id"]  as? String ?? "").isEmpty) || (dataMessages[indexPath!.row][TypeDataMessage.spec_file] as? String ?? "").contains("forward")) && dataMessages[indexPath!.row]["read_receipts"] as? String != "8" && dataMessages[indexPath!.row]["read_receipts"] as? String != "8" && dataMessages[indexPath!.row]["attachment_flag"] as? String ?? "" != "11" {
                 children.insert(forward, at: 2)
             }
             if (dataMessages[indexPath!.row]["f_pin"]  as? String ?? "") == idMe {
                 children.insert(info, at: children.count - 1)
             }
-            if !(dataMessages[indexPath!.row][TypeDataMessage.message_text]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row][TypeDataMessage.attachment_flag] as? String ?? "") != "11" {
-                if (dataMessages[indexPath!.row]["f_pin"]  as? String ?? "") == idMe && ((dataMessages[indexPath!.row][TypeDataMessage.is_forwarded] as? Int) ?? 0) == 0 {
+            if !(dataMessages[indexPath!.row][TypeDataMessage.message_text]  as? String ?? "").isEmpty {
+                if (dataMessages[indexPath!.row]["f_pin"]  as? String ?? "") == idMe && ((dataMessages[indexPath!.row][TypeDataMessage.is_forwarded] as? Int) ?? 0) == 0 && (dataMessages[indexPath!.row][TypeDataMessage.attachment_flag] as? String ?? "") != "11" {
                     let date = Date(milliseconds: Int64(dataMessages[indexPath!.row][TypeDataMessage.server_date] as? String ?? "") ?? 0)
                     let pastDate = date.addingTimeInterval(-10 * 60)
                     let differenceInSeconds = date.timeIntervalSince(pastDate)
@@ -5111,7 +5153,9 @@ extension EditorPersonal: UIContextMenuInteractionDelegate {
                         children.insert(edit, at: children.count - 1)
                     }
                 }
-                isMore = true
+                if (dataMessages[indexPath!.row][TypeDataMessage.attachment_flag] as? String ?? "") != "11" && (dataMessages[indexPath!.row]["image_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["video_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["file_id"]  as? String ?? "").isEmpty && (dataMessages[indexPath!.row]["audio_id"]  as? String ?? "").isEmpty{
+                    isMore = true
+                }
             }
         }
         let mainMenu = UIMenu(title: "", options: [.displayInline],
@@ -7781,6 +7825,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource, AVAudioPla
             }
             if !text.isEmpty {
                 isLoadingShowLink = true
+                var dataURL = ""
                 func showLink() {
                     if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
                         let title = data["title"]  as? String ?? ""
@@ -7807,7 +7852,8 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource, AVAudioPla
                             imagePreview.topAnchor.constraint(equalTo: containerLinkMessage.topAnchor).isActive = true
                             imagePreview.widthAnchor.constraint(equalToConstant: 80.0).isActive = true
                             imagePreview.loadImageAsync(with: imageUrl)
-                            imagePreview.contentMode = .scaleToFill
+                            imagePreview.contentMode = .scaleAspectFill
+                            imagePreview.clipsToBounds = true
                         }
                         
                         let titlePreview = UILabel()
@@ -7821,7 +7867,7 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource, AVAudioPla
                         titlePreview.topAnchor.constraint(equalTo: containerLinkMessage.topAnchor, constant: 10.0).isActive = true
                         titlePreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -5.0).isActive = true
                         titlePreview.text = title
-                        titlePreview.font = UIFont.systemFont(ofSize: 14.0 + offset(), weight: .bold)
+                        titlePreview.font = UIFont.systemFont(ofSize: 12.0 + offset(), weight: .bold)
                         titlePreview.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
                         
                         let descPreview = UILabel()
@@ -7865,7 +7911,6 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource, AVAudioPla
                         }
                     }
                 }
-                var dataURL = ""
                 Database.shared.database?.inTransaction({ (fmdb, rollback) in
                     do {
                         if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select data_link from LINK_PREVIEW where link='\(text)'"), cursor.next() {
@@ -7879,6 +7924,15 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource, AVAudioPla
                         print("Access database error: \(error.localizedDescription)")
                     }
                 })
+                if !dataURL.isEmpty {
+                    if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
+                        let imageUrl = data["imageUrl"] as? String
+                        let link = data["link"]  as? String ?? ""
+                        if imageUrl == nil || (link.contains("youtube.com") && link.contains("watch?v=") && !imageUrl!.contains("img.youtube.com/vi/")) {
+                            dataURL = ""
+                        }
+                    }
+                }
                 if dataURL.isEmpty {
                     let urlConfig = URLSessionConfiguration.default
                     let sessionDelegate = SelfSignedURLSessionDelegate()
@@ -7889,9 +7943,19 @@ extension EditorPersonal: UITableViewDelegate, UITableViewDataSource, AVAudioPla
                                        cache: DisabledCache.instance)
                     let preview = slp.preview(text,
                                               onSuccess: { result in
-                        let title = result.title ?? "No Title"
-                        let description = text.contains("google.com") ? "" : result.description
-                        let imageUrl = result.icon
+                        let title = result.title?.trimmingCharacters(in: .whitespacesAndNewlines)
+                                    .nilIfEmpty ?? URL(string: text)?.host ?? "Untitled"
+                        let description: String
+                        if text.contains("google.com") {
+                            description = "" // special rule for google
+                        } else {
+                            description = result.description?.trimmingCharacters(in: .whitespacesAndNewlines)
+                                .nilIfEmpty ?? ""
+                        }
+                        let imageUrl = self.youtubeThumbnail(from: text)
+                            ?? result.image
+                            ?? result.icon
+                            ?? ""
                         Database.shared.database?.inTransaction({ (fmdb, rollback) in
                             do {
                                 var dataJson: [String: Any] = [:]
@@ -9510,3 +9574,11 @@ public class TypeDataMessage {
     public static let is_secret = "is_secret"
     public static let spec_file = "spec_file"
 }
+
+extension String {
+    var nilIfEmpty: String? {
+        let v = self.trimmingCharacters(in: .whitespacesAndNewlines)
+        return v.isEmpty ? nil : v
+    }
+}
+

+ 44 - 6
NexilisLite/NexilisLite/Source/View/Chat/EditorStarMessages.swift

@@ -758,6 +758,7 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
                 }
             }
             if !text.isEmpty {
+                var dataURL = ""
                 func showLink() {
                     if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
                         let title = data["title"] as! String
@@ -792,7 +793,8 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
                             } else {
                                 imagePreview.loadImageAsync(with: imageUrl)
                             }
-                            imagePreview.contentMode = .scaleToFill
+                            imagePreview.contentMode = .scaleAspectFill
+                            imagePreview.clipsToBounds = true
                         }
                         
                         let titlePreview = UILabel()
@@ -806,7 +808,7 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
                         titlePreview.topAnchor.constraint(equalTo: containerLinkMessage.topAnchor, constant: 25.0).isActive = true
                         titlePreview.trailingAnchor.constraint(equalTo: containerLinkMessage.trailingAnchor, constant: -80.0).isActive = true
                         titlePreview.text = title
-                        titlePreview.font = UIFont.systemFont(ofSize: 14.0 + offset(), weight: .bold)
+                        titlePreview.font = UIFont.systemFont(ofSize: 12.0 + offset(), weight: .bold)
                         titlePreview.textColor = self.traitCollection.userInterfaceStyle == .dark ? .white : .black
                         
                         let descPreview = UILabel()
@@ -844,7 +846,6 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
                         containerLinkMessage.addGestureRecognizer(objectTap)
                     }
                 }
-                var dataURL = ""
                 Database.shared.database?.inTransaction({ (fmdb, rollback) in
                     do {
                         if let cursor = Database.shared.getRecords(fmdb: fmdb, query: "select data_link from LINK_PREVIEW where link='\(text)'"), cursor.next() {
@@ -858,6 +859,15 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
                         print("Access database error: \(error.localizedDescription)")
                     }
                 })
+                if !dataURL.isEmpty {
+                    if let data = try! JSONSerialization.jsonObject(with: dataURL.data(using: String.Encoding.utf8)!, options: []) as? [String: Any] {
+                        let imageUrl = data["imageUrl"] as? String
+                        let link = data["link"]  as? String ?? ""
+                        if imageUrl == nil || (link.contains("youtube.com") && link.contains("watch?v=") && !imageUrl!.contains("img.youtube.com/vi/")) {
+                            dataURL = ""
+                        }
+                    }
+                }
                 if dataURL.isEmpty {
                     let urlConfig = URLSessionConfiguration.default
                     let sessionDelegate = SelfSignedURLSessionDelegate()
@@ -868,9 +878,19 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
                                        cache: DisabledCache.instance)
                     let preview = slp.preview(text,
                                               onSuccess: { result in
-                        let title = result.title ?? "No Title"
-                        let description = text.contains("google.com") ? "" : result.description
-                        let imageUrl = result.image
+                        let title = result.title?.trimmingCharacters(in: .whitespacesAndNewlines)
+                                    .nilIfEmpty ?? URL(string: text)?.host ?? "Untitled"
+                        let description: String
+                        if text.contains("google.com") {
+                            description = "" // special rule for google
+                        } else {
+                            description = result.description?.trimmingCharacters(in: .whitespacesAndNewlines)
+                                .nilIfEmpty ?? "No description available"
+                        }
+                        let imageUrl = self.youtubeThumbnail(from: text)
+                            ?? result.image
+                            ?? result.icon
+                            ?? ""
                         Database.shared.database?.inTransaction({ (fmdb, rollback) in
                             do {
                                 var dataJson: [String: Any] = [:]
@@ -908,6 +928,24 @@ public class EditorStarMessages: UIViewController, UITableViewDataSource, UITabl
         return cellMessage
     }
     
+    func youtubeThumbnail(from url: String) -> String? {
+        guard let url = URL(string: url) else { return nil }
+        let host = url.host ?? ""
+        
+        if host.contains("youtube.com"),
+           let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems,
+           let videoId = queryItems.first(where: { $0.name == "v" })?.value {
+            return "https://img.youtube.com/vi/\(videoId)/hqdefault.jpg"
+        }
+        
+        if host.contains("youtu.be") {
+            let videoId = url.lastPathComponent
+            return "https://img.youtube.com/vi/\(videoId)/hqdefault.jpg"
+        }
+        
+        return nil
+    }
+    
     @objc func tapAck(_ sender: ObjectGesture) {
         let indexPath = sender.indexPath
         let dataMessages = self.dataMessages.filter({ $0["chat_date"]  as? String ?? "" == dataDates[indexPath.section]})

+ 1 - 1
NexilisLite/NexilisLite/Source/View/Chat/PreviewAttachmentImageVideo.swift

@@ -828,7 +828,7 @@ class PreviewAttachmentImageVideo: UIViewController, UIScrollViewDelegate, UITex
             guard let tempImage = UIImage(data: compressedData) else { break }
             compressedData = tempImage.jpegData(compressionQuality: 0.5) ?? compressedData
             imageSizeMB = Double(compressedData.count) / (1024.0 * 1024.0)
-            print("Compressed to: \(imageSizeMB) MB")
+//            print("Compressed to: \(imageSizeMB) MB")
         }
         
         return compressedData