2024-07-10 17:49:36 +00:00
|
|
|
import AVFoundation
|
2024-07-10 14:13:47 +00:00
|
|
|
import Combine
|
|
|
|
import Foundation
|
2024-07-10 17:49:36 +00:00
|
|
|
import Photos
|
|
|
|
import UIKit
|
2024-07-10 14:13:47 +00:00
|
|
|
|
|
|
|
final class SharingMiddleware {
|
|
|
|
static let shared = SharingMiddleware()
|
|
|
|
|
2024-07-14 13:00:14 +00:00
|
|
|
// swiftlint:disable:next function_body_length
|
2024-07-14 10:48:04 +00:00
|
|
|
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
2024-07-10 14:13:47 +00:00
|
|
|
switch action {
|
2024-07-14 13:00:14 +00:00
|
|
|
// MARK: - Camera and Gallery Access
|
2024-07-10 17:49:36 +00:00
|
|
|
case .sharingAction(.checkCameraAccess):
|
|
|
|
return Future<AppAction, Never> { promise in
|
|
|
|
let status = AVCaptureDevice.authorizationStatus(for: .video)
|
|
|
|
switch status {
|
|
|
|
case .authorized:
|
|
|
|
promise(.success(.sharingAction(.setCameraAccess(true))))
|
|
|
|
|
|
|
|
case .notDetermined:
|
|
|
|
AVCaptureDevice.requestAccess(for: .video) { granted in
|
|
|
|
promise(.success(.sharingAction(.setCameraAccess(granted))))
|
|
|
|
}
|
|
|
|
|
|
|
|
case .denied, .restricted:
|
|
|
|
promise(.success(.sharingAction(.setCameraAccess(false))))
|
|
|
|
|
|
|
|
@unknown default:
|
|
|
|
promise(.success(.sharingAction(.setCameraAccess(false))))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
|
|
|
|
case .sharingAction(.checkGalleryAccess):
|
|
|
|
return Future<AppAction, Never> { promise in
|
|
|
|
let status = PHPhotoLibrary.authorizationStatus()
|
|
|
|
switch status {
|
|
|
|
case .authorized, .limited:
|
|
|
|
promise(.success(.sharingAction(.setGalleryAccess(true))))
|
|
|
|
|
|
|
|
case .notDetermined:
|
|
|
|
PHPhotoLibrary.requestAuthorization { status in
|
|
|
|
promise(.success(.sharingAction(.setGalleryAccess(status == .authorized))))
|
|
|
|
}
|
|
|
|
|
|
|
|
case .denied, .restricted:
|
|
|
|
promise(.success(.sharingAction(.setGalleryAccess(false))))
|
|
|
|
|
|
|
|
@unknown default:
|
|
|
|
promise(.success(.sharingAction(.setGalleryAccess(false))))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
|
2024-07-14 13:42:51 +00:00
|
|
|
case .fileAction(.itemsFromGalleryFetched(let items)):
|
|
|
|
return Just(.sharingAction(.galleryItemsUpdated(items: items)))
|
|
|
|
.eraseToAnyPublisher()
|
2024-07-10 17:49:36 +00:00
|
|
|
|
2024-07-14 13:00:14 +00:00
|
|
|
// MARK: - Sharing
|
|
|
|
case .sharingAction(.shareMedia(let ids)):
|
|
|
|
return Future<AppAction, Never> { promise in
|
|
|
|
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
|
|
|
|
if let data = image?.jpegData(compressionQuality: 1.0) {
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
let newMessageId = UUID().uuidString
|
|
|
|
store.dispatch(.fileAction(.copyFileForUploading(
|
|
|
|
messageId: newMessageId,
|
|
|
|
fileData: data,
|
|
|
|
thumbnailData: store.state.sharingState.galleryItems.first(where: { $0.id == asset.localIdentifier })?.thumbnail
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if asset.mediaType == .video {
|
|
|
|
let options = PHVideoRequestOptions()
|
|
|
|
options.version = .original
|
|
|
|
options.deliveryMode = .highQualityFormat
|
|
|
|
PHImageManager.default().requestAVAsset(forVideo: asset, options: options) { avAsset, _, _ in
|
|
|
|
guard let urlAsset = avAsset as? AVURLAsset else { return }
|
|
|
|
let exporter = AVAssetExportSession(asset: urlAsset, presetName: AVAssetExportPresetHighestQuality)
|
|
|
|
exporter?.outputFileType = .mp4
|
|
|
|
let outputURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString + ".mp4")
|
|
|
|
exporter?.outputURL = outputURL
|
|
|
|
exporter?.exportAsynchronously {
|
|
|
|
switch exporter?.status {
|
|
|
|
case .completed:
|
|
|
|
if let data = try? Data(contentsOf: outputURL) {
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
let newMessageId = UUID().uuidString
|
|
|
|
store.dispatch(.fileAction(.copyFileForUploading(
|
|
|
|
messageId: newMessageId,
|
|
|
|
fileData: data,
|
|
|
|
thumbnailData: nil
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
promise(.success(.empty))
|
|
|
|
}
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
|
|
|
|
case .sharingAction(.cameraCaptured(let media, let type)):
|
|
|
|
print("Camera captured: \(media.count)")
|
|
|
|
return Empty().eraseToAnyPublisher()
|
|
|
|
|
2024-07-14 10:48:04 +00:00
|
|
|
case .sharingAction(.shareLocation(let lat, let lon)):
|
|
|
|
if let chat = state.conversationsState.currentChat {
|
|
|
|
let msg = "geo:\(lat),\(lon)"
|
|
|
|
return Just(.conversationAction(.sendMessage(from: chat.account, to: chat.participant, body: msg)))
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
} else {
|
|
|
|
return Empty().eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2024-07-14 13:00:14 +00:00
|
|
|
case .sharingAction(.shareDocuments(let data)):
|
|
|
|
print("Sharing documents: \(data.count)")
|
|
|
|
return Empty().eraseToAnyPublisher()
|
|
|
|
|
|
|
|
case .sharingAction(.shareContact(let jid)):
|
|
|
|
if let chat = state.conversationsState.currentChat {
|
|
|
|
let msg = "contact:\(jid)"
|
|
|
|
return Just(.conversationAction(.sendMessage(from: chat.account, to: chat.participant, body: msg)))
|
|
|
|
.eraseToAnyPublisher()
|
|
|
|
} else {
|
|
|
|
return Empty().eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
|
2024-07-10 14:13:47 +00:00
|
|
|
default:
|
|
|
|
return Empty().eraseToAnyPublisher()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|