diff --git a/Linphone/Assets.xcassets/image-broken.imageset/Contents.json b/Linphone/Assets.xcassets/image-broken.imageset/Contents.json new file mode 100644 index 000000000..c850a9b36 --- /dev/null +++ b/Linphone/Assets.xcassets/image-broken.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "image-broken.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/image-broken.imageset/image-broken.svg b/Linphone/Assets.xcassets/image-broken.imageset/image-broken.svg new file mode 100644 index 000000000..c51df7fe6 --- /dev/null +++ b/Linphone/Assets.xcassets/image-broken.imageset/image-broken.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/UI/Main/Contacts/Fragments/ContactsListFragment.swift b/Linphone/UI/Main/Contacts/Fragments/ContactsListFragment.swift index c8db8a7c6..9c57fb39d 100644 --- a/Linphone/UI/Main/Contacts/Fragments/ContactsListFragment.swift +++ b/Linphone/UI/Main/Contacts/Fragments/ContactsListFragment.swift @@ -87,7 +87,7 @@ struct ContactsListFragment: View { withAnimation { contactViewModel.indexDisplayedFriend = index } - if contactsManager.lastSearch[index].friend != nil && contactsManager.lastSearch[index].friend!.address != nil { + if index < contactsManager.lastSearch.count && contactsManager.lastSearch[index].friend != nil && contactsManager.lastSearch[index].friend!.address != nil { startCallFunc(contactsManager.lastSearch[index].friend!.address!) } } diff --git a/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift b/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift index 9767bccfb..38d796517 100644 --- a/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift +++ b/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift @@ -187,46 +187,62 @@ struct ChatBubbleView: View { if message.attachments.first!.type == .image || message.attachments.first!.type == .video { if #available(iOS 16.0, *) { - AsyncImage(url: message.attachments.first!.thumbnail) { image in - ZStack { - image - .resizable() - .interpolation(.medium) - .aspectRatio(contentMode: .fill) - - if message.attachments.first!.type == .video { - Image("play-fill") - .renderingMode(.template) + AsyncImage(url: message.attachments.first!.thumbnail) { phase in + switch phase { + case .empty: + ProgressView() + case .success(let image): + ZStack { + image .resizable() - .foregroundStyle(.white) - .frame(width: 40, height: 40, alignment: .leading) + .interpolation(.medium) + .aspectRatio(contentMode: .fill) + + if message.attachments.first!.type == .video { + Image("play-fill") + .renderingMode(.template) + .resizable() + .foregroundStyle(.white) + .frame(width: 40, height: 40, alignment: .leading) + } } + case .failure: + Image("image-broken") + @unknown default: + EmptyView() } - } placeholder: { - ProgressView() } .layoutPriority(-1) + .clipShape(RoundedRectangle(cornerRadius: 4)) } else { - AsyncImage(url: message.attachments.first!.thumbnail) { image in - ZStack { - image - .resizable() - .interpolation(.medium) - .aspectRatio(contentMode: .fill) - - if message.attachments.first!.type == .video { - Image("play-fill") - .renderingMode(.template) + AsyncImage(url: message.attachments.first!.thumbnail) { phase in + switch phase { + case .empty: + ProgressView() + case .success(let image): + ZStack { + image .resizable() - .foregroundStyle(.white) - .frame(width: 40, height: 40, alignment: .leading) + .interpolation(.medium) + .aspectRatio(contentMode: .fill) + + if message.attachments.first!.type == .video { + Image("play-fill") + .renderingMode(.template) + .resizable() + .foregroundStyle(.white) + .frame(width: 40, height: 40, alignment: .leading) + } } + case .failure: + Image("image-broken") + @unknown default: + EmptyView() } - } placeholder: { - ProgressView() } - .id(UUID()) .layoutPriority(-1) + .clipShape(RoundedRectangle(cornerRadius: 4)) + .id(UUID()) } } else if message.attachments.first!.type == .gif { if #available(iOS 16.0, *) { @@ -319,7 +335,7 @@ struct ChatBubbleView: View { return orientation != nil && orientation == 6 ? (pixelHeight ?? 0, pixelWidth ?? 0) : (pixelWidth ?? 0, pixelHeight ?? 0) } } - return (0, 0) + return (100, 100) } } diff --git a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift index fc797a25f..50a952660 100644 --- a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift +++ b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift @@ -139,6 +139,7 @@ class ConversationViewModel: ObservableObject { } else { if content.type != "video" { let path = URL(string: self.getNewFilePath(name: content.name ?? "")) + if path != nil { let attachment = Attachment( @@ -151,6 +152,7 @@ class ConversationViewModel: ObservableObject { } else if content.type == "video" { let path = URL(string: self.getNewFilePath(name: content.name ?? "")) let pathThumbnail = URL(string: self.generateThumbnail(name: content.name ?? "")) + if path != nil && pathThumbnail != nil { let attachment = Attachment( @@ -240,6 +242,7 @@ class ConversationViewModel: ObservableObject { } else { if content.type != "video" { let path = URL(string: self.getNewFilePath(name: content.name ?? "")) + if path != nil { let attachment = Attachment( @@ -252,6 +255,7 @@ class ConversationViewModel: ObservableObject { } else if content.type == "video" { let path = URL(string: self.getNewFilePath(name: content.name ?? "")) let pathThumbnail = URL(string: self.generateThumbnail(name: content.name ?? "")) + if path != nil && pathThumbnail != nil { let attachment = Attachment( @@ -339,6 +343,7 @@ class ConversationViewModel: ObservableObject { } else if content.name != nil && !content.name!.isEmpty { if content.type != "video" { let path = URL(string: self.getNewFilePath(name: content.name ?? "")) + if path != nil { let attachment = Attachment( @@ -350,6 +355,7 @@ class ConversationViewModel: ObservableObject { } } else if content.type == "video" { let path = URL(string: self.getNewFilePath(name: content.name ?? "")) + let pathThumbnail = URL(string: self.generateThumbnail(name: content.name ?? "")) if path != nil && pathThumbnail != nil { let attachment = @@ -512,7 +518,8 @@ class ConversationViewModel: ObservableObject { if message != nil { let path = FileManager.default.temporaryDirectory.appendingPathComponent((attachment.full.lastPathComponent.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "")) - let newPath = URL(string: "file://" + Factory.Instance.getDownloadDir(context: nil) + (attachment.full.lastPathComponent.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "")) + let newPath = URL(string: FileUtil.sharedContainerUrl().appendingPathComponent("Library/Images").absoluteString + + (attachment.full.lastPathComponent.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "")) /* let data = try Data(contentsOf: path) let decodedData: () = try data.write(to: path) @@ -581,7 +588,7 @@ class ConversationViewModel: ObservableObject { if contentName != nil { let isImage = FileUtil.isExtensionImage(path: contentName!) let file = FileUtil.getFileStoragePath(fileName: contentName ?? "", isImage: isImage) - content.filePath = file + content.filePath = String(file.dropFirst(7)) Log.info( "[ConversationViewModel] File \(contentName) will be downloaded at \(content.filePath)" ) @@ -593,13 +600,14 @@ class ConversationViewModel: ObservableObject { } func getNewFilePath(name: String) -> String { - return "file://" + Factory.Instance.getDownloadDir(context: nil) + (name.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "") + let groupName = "group.\(Bundle.main.bundleIdentifier ?? "").linphoneExtension" + return FileUtil.sharedContainerUrl().appendingPathComponent("Library/Images").absoluteString + (name.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "") } func generateThumbnail(name: String, pathThumbnail: URL? = nil) -> String { do { let path = pathThumbnail == nil - ? URL(string: "file://" + Factory.Instance.getDownloadDir(context: nil) + (name.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "")) + ? URL(string: "file://" + FileUtil.sharedContainerUrl().appendingPathComponent("Library/Images").absoluteString + (name.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "")) : pathThumbnail!.appendingPathComponent((name.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "")) let asset = AVURLAsset(url: path!, options: nil) let imgGenerator = AVAssetImageGenerator(asset: asset) @@ -612,7 +620,12 @@ class ConversationViewModel: ObservableObject { } let urlName = pathThumbnail == nil - ? URL(string: "file://" + Factory.Instance.getDownloadDir(context: nil) + "preview_" + (name.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "") + ".png") + ? URL(string: "file://" + + FileUtil.sharedContainerUrl().appendingPathComponent("Library/Images").absoluteString + + "preview_" + + (name.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "") + + ".png" + ) : pathThumbnail!.appendingPathComponent("preview_" + (name.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "") + ".png") if urlName != nil {