wip
This commit is contained in:
parent
bb502ba79a
commit
e21d1a1ce9
|
@ -9,4 +9,7 @@ enum FileAction: Stateable {
|
|||
case attachmentThumbnailCreated(messageId: String, thumbnailName: String)
|
||||
|
||||
case copyFileForUploading(messageId: String, fileData: Data, thumbnailData: Data?)
|
||||
|
||||
case fetchItemsFromGallery
|
||||
case itemsFromGalleryFetched(items: [SharingGalleryItem])
|
||||
}
|
||||
|
|
|
@ -13,9 +13,7 @@ enum SharingAction: Stateable {
|
|||
|
||||
case checkGalleryAccess
|
||||
case setGalleryAccess(Bool)
|
||||
case fetchGallery
|
||||
case galleryFetched([SharingGalleryItem])
|
||||
case thumbnailUpdated(data: Data, id: String)
|
||||
case galleryItemsUpdated(items: [SharingGalleryItem])
|
||||
|
||||
case cameraCaptured(media: Data, type: SharingCameraMediaType)
|
||||
case flushCameraCaptured
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import Foundation
|
||||
import Photos
|
||||
import UIKit
|
||||
|
||||
final class FileProcessing {
|
||||
|
@ -42,25 +43,104 @@ final class FileProcessing {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func scaleAndCropImage(_ img: UIImage, _ size: CGSize) -> UIImage? {
|
||||
let aspect = img.size.width / img.size.height
|
||||
let targetAspect = size.width / size.height
|
||||
var newWidth: CGFloat
|
||||
var newHeight: CGFloat
|
||||
if aspect < targetAspect {
|
||||
newWidth = size.width
|
||||
newHeight = size.width / aspect
|
||||
} else {
|
||||
newHeight = size.height
|
||||
newWidth = size.height * aspect
|
||||
func fetchGallery() -> [SharingGalleryItem] {
|
||||
let fetchOptions = PHFetchOptions()
|
||||
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
|
||||
let assets = PHAsset.fetchAssets(with: fetchOptions)
|
||||
var items: [SharingGalleryItem] = []
|
||||
assets.enumerateObjects { asset, _, _ in
|
||||
if asset.mediaType == .image {
|
||||
items.append(.init(id: asset.localIdentifier, type: .photo))
|
||||
} else if asset.mediaType == .video {
|
||||
items.append(.init(id: asset.localIdentifier, type: .video))
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
|
||||
img.draw(in: CGRect(x: (size.width - newWidth) / 2, y: (size.height - newHeight) / 2, width: newWidth, height: newHeight))
|
||||
let newImage = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
func fillGalleryItemsThumbnails(items: [SharingGalleryItem]) -> [SharingGalleryItem] {
|
||||
var result: [SharingGalleryItem] = []
|
||||
let ids = items
|
||||
.filter { $0.thumbnail == nil }
|
||||
.map { $0.id }
|
||||
|
||||
return newImage
|
||||
let assets = PHAsset.fetchAssets(withLocalIdentifiers: ids, options: nil)
|
||||
assets.enumerateObjects { asset, _, _ in
|
||||
if asset.mediaType == .image {
|
||||
PHImageManager.default().requestImage(
|
||||
for: asset,
|
||||
targetSize: PHImageManagerMaximumSize,
|
||||
contentMode: .aspectFill,
|
||||
options: nil
|
||||
) { image, _ in
|
||||
image?.scaleAndCropImage(toExampleSize: CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) { image in
|
||||
if let image {
|
||||
let data = image.jpegData(compressionQuality: 1.0) ?? Data()
|
||||
result.append(.init(id: asset.localIdentifier, type: .photo, thumbnail: data))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if asset.mediaType == .video {
|
||||
PHImageManager.default().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: Const.galleryGridSize, height: Const.galleryGridSize)) { image in
|
||||
if let image {
|
||||
let data = image.jpegData(compressionQuality: 1.0) ?? Data()
|
||||
result.append(.init(id: asset.localIdentifier, type: .video, thumbnail: data))
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("Failed to create thumbnail image")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private extension FileProcessing {
|
||||
func scaleAndCropImage(_ img: UIImage, _ size: CGSize) -> UIImage? {
|
||||
let aspect = img.size.width / img.size.height
|
||||
let targetAspect = size.width / size.height
|
||||
var newWidth: CGFloat
|
||||
var newHeight: CGFloat
|
||||
if aspect < targetAspect {
|
||||
newWidth = size.width
|
||||
newHeight = size.width / aspect
|
||||
} else {
|
||||
newHeight = size.height
|
||||
newWidth = size.height * aspect
|
||||
}
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
|
||||
img.draw(in: CGRect(x: (size.width - newWidth) / 2, y: (size.height - newHeight) / 2, width: newWidth, height: newHeight))
|
||||
let newImage = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
return newImage
|
||||
}
|
||||
|
||||
func syncEnumrate(_ ids: [String]? = nil) -> [PHAsset] {
|
||||
var result: [PHAsset] = []
|
||||
|
||||
let fetchOptions = PHFetchOptions()
|
||||
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
|
||||
if let ids {
|
||||
fetchOptions.predicate = NSPredicate(format: "localIdentifier IN %@", ids)
|
||||
}
|
||||
let assets = PHAsset.fetchAssets(with: fetchOptions)
|
||||
assets.enumerateObjects { asset, _, _ in
|
||||
result.append(asset)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ final class FileMiddleware {
|
|||
|
||||
func middleware(state _: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
||||
switch action {
|
||||
// MARK: - For incomig attachments
|
||||
case .conversationAction(.messagesUpdated(let messages)):
|
||||
return Future { [weak self] promise in
|
||||
guard let wSelf = self else {
|
||||
|
@ -62,6 +63,21 @@ final class FileMiddleware {
|
|||
}
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
// MARK: - For outgoing sharing
|
||||
case .fileAction(.fetchItemsFromGallery):
|
||||
return Future<AppAction, Never> { promise in
|
||||
let items = FileProcessing.shared.fetchGallery()
|
||||
promise(.success(.fileAction(.itemsFromGalleryFetched(items: items))))
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
case .fileAction(.itemsFromGalleryFetched(let items)):
|
||||
return Future { promise in
|
||||
let newItems = FileProcessing.shared.fillGalleryItemsThumbnails(items: items)
|
||||
promise(.success(.sharingAction(.galleryItemsUpdated(items: newItems))))
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
case .fileAction(.copyFileForUploading(let messageId, let data, let thumbnail)):
|
||||
print("=====")
|
||||
print("copyFileForUploading")
|
||||
|
|
|
@ -53,68 +53,9 @@ final class SharingMiddleware {
|
|||
}
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
case .sharingAction(.fetchGallery):
|
||||
return Future<AppAction, Never> { promise in
|
||||
let fetchOptions = PHFetchOptions()
|
||||
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
|
||||
let assets = PHAsset.fetchAssets(with: fetchOptions)
|
||||
var items: [SharingGalleryItem] = []
|
||||
assets.enumerateObjects { asset, _, _ in
|
||||
if asset.mediaType == .image {
|
||||
items.append(.init(id: asset.localIdentifier, type: .photo))
|
||||
} else if asset.mediaType == .video {
|
||||
items.append(.init(id: asset.localIdentifier, type: .video))
|
||||
}
|
||||
}
|
||||
promise(.success(.sharingAction(.galleryFetched(items))))
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
case .sharingAction(.galleryFetched(let items)):
|
||||
DispatchQueue.global().async {
|
||||
let ids = items
|
||||
.filter { $0.thumbnail == nil }
|
||||
.map { $0.id }
|
||||
let assets = PHAsset.fetchAssets(withLocalIdentifiers: ids, options: nil)
|
||||
assets.enumerateObjects { asset, _, _ in
|
||||
if asset.mediaType == .image {
|
||||
PHImageManager.default().requestImage(
|
||||
for: asset,
|
||||
targetSize: PHImageManagerMaximumSize,
|
||||
contentMode: .aspectFill,
|
||||
options: nil
|
||||
) { image, _ in
|
||||
image?.scaleAndCropImage(toExampleSize: CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) { image in
|
||||
if let image {
|
||||
let data = image.jpegData(compressionQuality: 1.0) ?? Data()
|
||||
store.dispatch(.sharingAction(.thumbnailUpdated(data: data, id: asset.localIdentifier)))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if asset.mediaType == .video {
|
||||
PHImageManager.default().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: Const.galleryGridSize, height: Const.galleryGridSize)) { image in
|
||||
if let image {
|
||||
let data = image.jpegData(compressionQuality: 1.0) ?? Data()
|
||||
store.dispatch(.sharingAction(.thumbnailUpdated(data: data, id: asset.localIdentifier)))
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("Failed to create thumbnail image")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Empty().eraseToAnyPublisher()
|
||||
case .fileAction(.itemsFromGalleryFetched(let items)):
|
||||
return Just(.sharingAction(.galleryItemsUpdated(items: items)))
|
||||
.eraseToAnyPublisher()
|
||||
|
||||
// MARK: - Sharing
|
||||
case .sharingAction(.shareMedia(let ids)):
|
||||
|
|
|
@ -20,15 +20,9 @@ extension SharingState {
|
|||
state.cameraCapturedMedia = Data()
|
||||
state.cameraCapturedMediaType = .photo
|
||||
|
||||
case .galleryFetched(let items):
|
||||
case .galleryItemsUpdated(let items):
|
||||
state.galleryItems = items
|
||||
|
||||
case .thumbnailUpdated(let thumbnailData, let id):
|
||||
guard let index = state.galleryItems.firstIndex(where: { $0.id == id }) else {
|
||||
return
|
||||
}
|
||||
state.galleryItems[index].thumbnail = thumbnailData
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ struct SharingMediaPickerView: View {
|
|||
}
|
||||
.onChange(of: store.state.sharingState.isGalleryAccessGranted) { granted in
|
||||
if granted {
|
||||
store.dispatch(.sharingAction(.fetchGallery))
|
||||
store.dispatch(.fileAction(.fetchItemsFromGallery))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue