diff --git a/ConversationsClassic/Helpers/Date+Extensions.swift b/ConversationsClassic/Helpers/Date+Extensions.swift new file mode 100644 index 0000000..0dde77a --- /dev/null +++ b/ConversationsClassic/Helpers/Date+Extensions.swift @@ -0,0 +1,9 @@ +import Foundation + +extension TimeInterval { + var minAndSec: String { + let minutes = Int(self) / 60 + let seconds = Int(self) % 60 + return String(format: "%02d:%02d", minutes, seconds) + } +} diff --git a/ConversationsClassic/View/Screens/Attachments/AttachmentFilesPickerView.swift b/ConversationsClassic/View/Screens/Attachments/AttachmentFilesPickerView.swift index 2777e50..4a7738f 100644 --- a/ConversationsClassic/View/Screens/Attachments/AttachmentFilesPickerView.swift +++ b/ConversationsClassic/View/Screens/Attachments/AttachmentFilesPickerView.swift @@ -5,9 +5,9 @@ struct AttachmentFilesPickerView: View { @State private var isPickerPresented = false var body: some View { - Button(action: { + Button { isPickerPresented = true - }) { + } label: { Text("Select Files") } .sheet(isPresented: $isPickerPresented) { diff --git a/ConversationsClassic/View/Screens/Attachments/AttachmentMediaPickerView.swift b/ConversationsClassic/View/Screens/Attachments/AttachmentMediaPickerView.swift index 2a257db..7ddefde 100644 --- a/ConversationsClassic/View/Screens/Attachments/AttachmentMediaPickerView.swift +++ b/ConversationsClassic/View/Screens/Attachments/AttachmentMediaPickerView.swift @@ -138,26 +138,49 @@ struct AttachmentMediaPickerView: View { private func fetchImages() { let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] - let assets = PHAsset.fetchAssets(with: .image, options: fetchOptions) + let assets = PHAsset.fetchAssets(with: fetchOptions) let manager = PHImageManager.default() let option = PHImageRequestOptions() option.isSynchronous = true assets.enumerateObjects { asset, _, _ in - manager.requestImage( - for: asset, - targetSize: PHImageManagerMaximumSize, - contentMode: .aspectFill, - options: option - ) { image, _ in - image?.scaleAndCropImage(toExampleSize: CGSize(width: gridSize, height: gridSize), completion: { image in - if let image { - DispatchQueue.main.async { - self.photos.append(PhotoView(image: image, gridSize: gridSize)) + if asset.mediaType == .image { + manager.requestImage( + for: asset, + targetSize: PHImageManagerMaximumSize, + contentMode: .aspectFill, + options: option + ) { image, _ in + image?.scaleAndCropImage(toExampleSize: CGSize(width: gridSize, height: gridSize), completion: { image in + if let image { + DispatchQueue.main.async { + self.photos.append(PhotoView(image: image, gridSize: gridSize)) + } + } + }) + } + } else if asset.mediaType == .video { + manager.requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in + if let avAsset { + let imageGenerator = AVAssetImageGenerator(asset: avAsset) + imageGenerator.appliesPreferredTrackTransform = true + let time = CMTimeMake(value: 1, timescale: 2) + do { + let imageRef = try imageGenerator.copyCGImage(at: time, actualTime: nil) + let thumbnail = UIImage(cgImage: imageRef) + thumbnail.scaleAndCropImage(toExampleSize: CGSize(width: gridSize, height: gridSize), completion: { image in + if let image { + DispatchQueue.main.async { + self.photos.append(PhotoView(image: image, gridSize: gridSize, duration: asset.duration.minAndSec)) + } + } + }) + } catch { + print("Failed to create thumbnail image") } } - }) + } } } } @@ -175,22 +198,39 @@ struct AttachmentMediaPickerView: View { private struct PhotoView: Identifiable, View { let id = UUID() let gridSize: CGFloat + let duration: String? @State private var image: UIImage @State private var ready = false - init(image: UIImage, gridSize: CGFloat) { + init(image: UIImage, gridSize: CGFloat, duration: String? = nil) { self.image = image self.gridSize = gridSize + self.duration = duration } var body: some View { if ready { - Image(uiImage: image) - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: gridSize, height: gridSize) - .clipped() + ZStack { + Image(uiImage: image) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: gridSize, height: gridSize) + .clipped() + if let duration { + VStack { + Spacer() + HStack { + Spacer() + Text(duration) + .foregroundColor(.Material.Text.white) + .font(.sub1) + .shadow(color: .black, radius: 2) + .padding(4) + } + } + } + } } else { ZStack { Rectangle()