mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-06 04:18:31 +00:00
Use the maxUploadSize in the media upload screen. (#4359)
* Fix a bug where some files sizes were sent as 0. * Make FileManger.sizeForItem(at:) a UInt. The docs specifically say that FileAttributeKey.size returns an unsigned long long so I have no idea why we were returning a Double here… * Add the maxMediaUploadSize property on ClientProxy. * Use the maxUploadSize to set a target video size and to show a file too big error. * Refactor media upload parameter order.
This commit is contained in:
@@ -240,6 +240,7 @@
|
|||||||
"common_poll_summary" = "Poll: %1$@";
|
"common_poll_summary" = "Poll: %1$@";
|
||||||
"common_poll_total_votes" = "Total votes: %1$@";
|
"common_poll_total_votes" = "Total votes: %1$@";
|
||||||
"common_poll_undisclosed_text" = "Results will show after the poll has ended";
|
"common_poll_undisclosed_text" = "Results will show after the poll has ended";
|
||||||
|
"common_preparing" = "Preparing…";
|
||||||
"common_privacy_policy" = "Privacy policy";
|
"common_privacy_policy" = "Privacy policy";
|
||||||
"common_reaction" = "Reaction";
|
"common_reaction" = "Reaction";
|
||||||
"common_reactions" = "Reactions";
|
"common_reactions" = "Reactions";
|
||||||
|
|||||||
@@ -859,6 +859,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
let stackCoordinator = NavigationStackCoordinator()
|
let stackCoordinator = NavigationStackCoordinator()
|
||||||
|
|
||||||
let roomDetailsEditParameters = RoomDetailsEditScreenCoordinatorParameters(roomProxy: roomProxy,
|
let roomDetailsEditParameters = RoomDetailsEditScreenCoordinatorParameters(roomProxy: roomProxy,
|
||||||
|
clientProxy: userSession.clientProxy,
|
||||||
mediaProvider: userSession.mediaProvider,
|
mediaProvider: userSession.mediaProvider,
|
||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings),
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings),
|
||||||
navigationStackCoordinator: stackCoordinator,
|
navigationStackCoordinator: stackCoordinator,
|
||||||
@@ -949,13 +950,14 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
animated: Bool) {
|
animated: Bool) {
|
||||||
let stackCoordinator = NavigationStackCoordinator()
|
let stackCoordinator = NavigationStackCoordinator()
|
||||||
|
|
||||||
let parameters = MediaUploadPreviewScreenCoordinatorParameters(timelineController: timelineController,
|
let parameters = MediaUploadPreviewScreenCoordinatorParameters(url: url,
|
||||||
userIndicatorController: userIndicatorController,
|
|
||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings),
|
|
||||||
title: url.lastPathComponent,
|
title: url.lastPathComponent,
|
||||||
url: url,
|
isRoomEncrypted: roomProxy.infoPublisher.value.isEncrypted,
|
||||||
shouldShowCaptionWarning: appSettings.shouldShowMediaCaptionWarning,
|
shouldShowCaptionWarning: appSettings.shouldShowMediaCaptionWarning,
|
||||||
isRoomEncrypted: roomProxy.infoPublisher.value.isEncrypted)
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings),
|
||||||
|
timelineController: timelineController,
|
||||||
|
clientProxy: userSession.clientProxy,
|
||||||
|
userIndicatorController: userIndicatorController)
|
||||||
|
|
||||||
let mediaUploadPreviewScreenCoordinator = MediaUploadPreviewScreenCoordinator(parameters: parameters)
|
let mediaUploadPreviewScreenCoordinator = MediaUploadPreviewScreenCoordinator(parameters: parameters)
|
||||||
|
|
||||||
|
|||||||
@@ -538,6 +538,8 @@ internal enum L10n {
|
|||||||
internal static func commonPollVotesCount(_ p1: Int) -> String {
|
internal static func commonPollVotesCount(_ p1: Int) -> String {
|
||||||
return L10n.tr("Localizable", "common_poll_votes_count", p1)
|
return L10n.tr("Localizable", "common_poll_votes_count", p1)
|
||||||
}
|
}
|
||||||
|
/// Preparing…
|
||||||
|
internal static var commonPreparing: String { return L10n.tr("Localizable", "common_preparing") }
|
||||||
/// Privacy policy
|
/// Privacy policy
|
||||||
internal static var commonPrivacyPolicy: String { return L10n.tr("Localizable", "common_privacy_policy") }
|
internal static var commonPrivacyPolicy: String { return L10n.tr("Localizable", "common_privacy_policy") }
|
||||||
/// Reaction
|
/// Reaction
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ struct ClientProxyMockConfiguration {
|
|||||||
|
|
||||||
var timelineMediaVisibility = TimelineMediaVisibility.always
|
var timelineMediaVisibility = TimelineMediaVisibility.always
|
||||||
var hideInviteAvatars = false
|
var hideInviteAvatars = false
|
||||||
|
|
||||||
|
var maxMediaUploadSize: UInt = 100 * 1024 * 1024
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ClientProxyMockError: Error {
|
enum ClientProxyMockError: Error {
|
||||||
@@ -101,5 +103,7 @@ extension ClientProxyMock {
|
|||||||
|
|
||||||
underlyingTimelineMediaVisibilityPublisher = CurrentValueSubject<TimelineMediaVisibility, Never>(configuration.timelineMediaVisibility).asCurrentValuePublisher()
|
underlyingTimelineMediaVisibilityPublisher = CurrentValueSubject<TimelineMediaVisibility, Never>(configuration.timelineMediaVisibility).asCurrentValuePublisher()
|
||||||
underlyingHideInviteAvatarsPublisher = CurrentValueSubject<Bool, Never>(configuration.hideInviteAvatars).asCurrentValuePublisher()
|
underlyingHideInviteAvatarsPublisher = CurrentValueSubject<Bool, Never>(configuration.hideInviteAvatars).asCurrentValuePublisher()
|
||||||
|
|
||||||
|
underlyingMaxMediaUploadSize = .success(configuration.maxMediaUploadSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2200,6 +2200,23 @@ class ClientProxyMock: ClientProxyProtocol, @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
var underlyingIsLiveKitRTCSupported: Bool!
|
var underlyingIsLiveKitRTCSupported: Bool!
|
||||||
var isLiveKitRTCSupportedClosure: (() async -> Bool)?
|
var isLiveKitRTCSupportedClosure: (() async -> Bool)?
|
||||||
|
var maxMediaUploadSizeCallsCount = 0
|
||||||
|
var maxMediaUploadSizeCalled: Bool {
|
||||||
|
return maxMediaUploadSizeCallsCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxMediaUploadSize: Result<UInt, ClientProxyError> {
|
||||||
|
get async {
|
||||||
|
maxMediaUploadSizeCallsCount += 1
|
||||||
|
if let maxMediaUploadSizeClosure = maxMediaUploadSizeClosure {
|
||||||
|
return await maxMediaUploadSizeClosure()
|
||||||
|
} else {
|
||||||
|
return underlyingMaxMediaUploadSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var underlyingMaxMediaUploadSize: Result<UInt, ClientProxyError>!
|
||||||
|
var maxMediaUploadSizeClosure: (() async -> Result<UInt, ClientProxyError>)?
|
||||||
|
|
||||||
//MARK: - isOnlyDeviceLeft
|
//MARK: - isOnlyDeviceLeft
|
||||||
|
|
||||||
|
|||||||
@@ -49,10 +49,10 @@ extension FileManager {
|
|||||||
/// Retrieve a file's disk size
|
/// Retrieve a file's disk size
|
||||||
/// - Parameter url: the file URL
|
/// - Parameter url: the file URL
|
||||||
/// - Returns: the size in bytes
|
/// - Returns: the size in bytes
|
||||||
func sizeForItem(at url: URL) throws -> Double {
|
func sizeForItem(at url: URL) throws -> UInt {
|
||||||
let attributes = try attributesOfItem(atPath: url.path())
|
let attributes = try attributesOfItem(atPath: url.path(percentEncoded: false))
|
||||||
|
|
||||||
guard let size = attributes[FileAttributeKey.size] as? Double else {
|
guard let size = attributes[FileAttributeKey.size] as? UInt else {
|
||||||
throw FileManagerError.invalidFileSize
|
throw FileManagerError.invalidFileSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class BugReportScreenViewModel: BugReportScreenViewModelType, BugReportScreenVie
|
|||||||
private static func validate(_ logFiles: [URL]) -> Bool {
|
private static func validate(_ logFiles: [URL]) -> Bool {
|
||||||
for fileURL in logFiles {
|
for fileURL in logFiles {
|
||||||
if let size = try? FileManager.default.sizeForItem(at: fileURL),
|
if let size = try? FileManager.default.sizeForItem(at: fileURL),
|
||||||
size > Double(1024 * 1024 * 1024) { // Consider anything over 1GB to be excessive.
|
size > 1024 * 1024 * 1024 { // Consider anything over 1GB to be excessive.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,12 +244,12 @@ enum TimelineMediaPreviewItem: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileSize: Double? {
|
var fileSize: UInt? {
|
||||||
previewItemURL.flatMap { try? FileManager.default.sizeForItem(at: $0) } ?? expectedFileSize
|
previewItemURL.flatMap { try? FileManager.default.sizeForItem(at: $0) } ?? expectedFileSize
|
||||||
}
|
}
|
||||||
|
|
||||||
private var expectedFileSize: Double? {
|
private var expectedFileSize: UInt? {
|
||||||
let fileSize: UInt? = switch timelineItem {
|
switch timelineItem {
|
||||||
case let audioItem as AudioRoomTimelineItem:
|
case let audioItem as AudioRoomTimelineItem:
|
||||||
audioItem.content.fileSize
|
audioItem.content.fileSize
|
||||||
case let fileItem as FileRoomTimelineItem:
|
case let fileItem as FileRoomTimelineItem:
|
||||||
@@ -261,8 +261,6 @@ enum TimelineMediaPreviewItem: Equatable {
|
|||||||
default:
|
default:
|
||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileSize.map(Double.init)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var caption: String? {
|
var caption: String? {
|
||||||
|
|||||||
+12
-10
@@ -9,13 +9,14 @@ import Combine
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct MediaUploadPreviewScreenCoordinatorParameters {
|
struct MediaUploadPreviewScreenCoordinatorParameters {
|
||||||
let timelineController: TimelineControllerProtocol
|
|
||||||
let userIndicatorController: UserIndicatorControllerProtocol
|
|
||||||
let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
|
||||||
let title: String?
|
|
||||||
let url: URL
|
let url: URL
|
||||||
let shouldShowCaptionWarning: Bool
|
let title: String?
|
||||||
let isRoomEncrypted: Bool
|
let isRoomEncrypted: Bool
|
||||||
|
let shouldShowCaptionWarning: Bool
|
||||||
|
let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||||
|
let timelineController: TimelineControllerProtocol
|
||||||
|
let clientProxy: ClientProxyProtocol
|
||||||
|
let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MediaUploadPreviewScreenCoordinatorAction {
|
enum MediaUploadPreviewScreenCoordinatorAction {
|
||||||
@@ -32,13 +33,14 @@ final class MediaUploadPreviewScreenCoordinator: CoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init(parameters: MediaUploadPreviewScreenCoordinatorParameters) {
|
init(parameters: MediaUploadPreviewScreenCoordinatorParameters) {
|
||||||
viewModel = MediaUploadPreviewScreenViewModel(timelineController: parameters.timelineController,
|
viewModel = MediaUploadPreviewScreenViewModel(url: parameters.url,
|
||||||
userIndicatorController: parameters.userIndicatorController,
|
|
||||||
mediaUploadingPreprocessor: parameters.mediaUploadingPreprocessor,
|
|
||||||
title: parameters.title,
|
title: parameters.title,
|
||||||
url: parameters.url,
|
isRoomEncrypted: parameters.isRoomEncrypted,
|
||||||
shouldShowCaptionWarning: parameters.shouldShowCaptionWarning,
|
shouldShowCaptionWarning: parameters.shouldShowCaptionWarning,
|
||||||
isRoomEncrypted: parameters.isRoomEncrypted)
|
mediaUploadingPreprocessor: parameters.mediaUploadingPreprocessor,
|
||||||
|
timelineController: parameters.timelineController,
|
||||||
|
clientProxy: parameters.clientProxy,
|
||||||
|
userIndicatorController: parameters.userIndicatorController)
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ struct MediaUploadPreviewScreenBindings: BindableState {
|
|||||||
var selectedRange = NSRange(location: 0, length: 0)
|
var selectedRange = NSRange(location: 0, length: 0)
|
||||||
|
|
||||||
var isPresentingMediaCaptionWarning = false
|
var isPresentingMediaCaptionWarning = false
|
||||||
|
var alertInfo: AlertInfo<MediaUploadPreviewAlertType>?
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MediaUploadPreviewAlertType: Hashable {
|
||||||
|
case maxUploadSizeUnknown
|
||||||
|
case maxUploadSizeExceeded(limit: UInt)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MediaUploadPreviewScreenViewAction {
|
enum MediaUploadPreviewScreenViewAction {
|
||||||
|
|||||||
+50
-14
@@ -20,6 +20,7 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
|||||||
|
|
||||||
private var processingTask: Task<Result<MediaInfo, MediaUploadingPreprocessorError>, Never>
|
private var processingTask: Task<Result<MediaInfo, MediaUploadingPreprocessorError>, Never>
|
||||||
private var requestHandle: SendAttachmentJoinHandleProtocol?
|
private var requestHandle: SendAttachmentJoinHandleProtocol?
|
||||||
|
private let clientProxy: ClientProxyProtocol
|
||||||
|
|
||||||
private var actionsSubject: PassthroughSubject<MediaUploadPreviewScreenViewModelAction, Never> = .init()
|
private var actionsSubject: PassthroughSubject<MediaUploadPreviewScreenViewModelAction, Never> = .init()
|
||||||
|
|
||||||
@@ -27,20 +28,22 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
|||||||
actionsSubject.eraseToAnyPublisher()
|
actionsSubject.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
init(timelineController: TimelineControllerProtocol,
|
init(url: URL,
|
||||||
userIndicatorController: UserIndicatorControllerProtocol,
|
|
||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor,
|
|
||||||
title: String?,
|
title: String?,
|
||||||
url: URL,
|
isRoomEncrypted: Bool,
|
||||||
shouldShowCaptionWarning: Bool,
|
shouldShowCaptionWarning: Bool,
|
||||||
isRoomEncrypted: Bool) {
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor,
|
||||||
self.timelineController = timelineController
|
timelineController: TimelineControllerProtocol,
|
||||||
self.userIndicatorController = userIndicatorController
|
clientProxy: ClientProxyProtocol,
|
||||||
self.mediaUploadingPreprocessor = mediaUploadingPreprocessor
|
userIndicatorController: UserIndicatorControllerProtocol) {
|
||||||
self.url = url
|
self.url = url
|
||||||
|
self.mediaUploadingPreprocessor = mediaUploadingPreprocessor
|
||||||
|
self.timelineController = timelineController
|
||||||
|
self.clientProxy = clientProxy
|
||||||
|
self.userIndicatorController = userIndicatorController
|
||||||
|
|
||||||
// Start processing the media whilst the user is reviewing it/adding a caption.
|
// Start processing the media whilst the user is reviewing it/adding a caption.
|
||||||
processingTask = Task { await mediaUploadingPreprocessor.processMedia(at: url) }
|
processingTask = Self.processMedia(at: url, preprocessor: mediaUploadingPreprocessor, clientProxy: clientProxy)
|
||||||
|
|
||||||
super.init(initialViewState: MediaUploadPreviewScreenViewState(url: url,
|
super.init(initialViewState: MediaUploadPreviewScreenViewState(url: url,
|
||||||
title: title,
|
title: title,
|
||||||
@@ -57,6 +60,8 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
|||||||
startLoading()
|
startLoading()
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
|
defer { stopLoading() }
|
||||||
|
|
||||||
switch await processingTask.value {
|
switch await processingTask.value {
|
||||||
case .success(let mediaInfo):
|
case .success(let mediaInfo):
|
||||||
switch await sendAttachment(mediaInfo: mediaInfo,
|
switch await sendAttachment(mediaInfo: mediaInfo,
|
||||||
@@ -67,12 +72,13 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
|||||||
MXLog.error("Failed sending attachment with error: \(error)")
|
MXLog.error("Failed sending attachment with error: \(error)")
|
||||||
showError(label: L10n.screenMediaUploadPreviewErrorFailedSending)
|
showError(label: L10n.screenMediaUploadPreviewErrorFailedSending)
|
||||||
}
|
}
|
||||||
|
case .failure(.maxUploadSizeUnknown):
|
||||||
stopLoading()
|
showAlert(.maxUploadSizeUnknown)
|
||||||
|
case .failure(.maxUploadSizeExceeded(let limit)):
|
||||||
|
showAlert(.maxUploadSizeExceeded(limit: limit))
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
MXLog.error("Failed processing media to upload with error: \(error)")
|
MXLog.error("Failed processing media to upload with error: \(error)")
|
||||||
showError(label: L10n.screenMediaUploadPreviewErrorFailedProcessing)
|
showError(label: L10n.screenMediaUploadPreviewErrorFailedProcessing)
|
||||||
stopLoading()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +94,15 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
|
private static func processMedia(at url: URL,
|
||||||
|
preprocessor: MediaUploadingPreprocessor,
|
||||||
|
clientProxy: ClientProxyProtocol) -> Task<Result<MediaInfo, MediaUploadingPreprocessorError>, Never> {
|
||||||
|
Task {
|
||||||
|
guard case let .success(maxUploadSize) = await clientProxy.maxMediaUploadSize else { return .failure(.maxUploadSizeUnknown) }
|
||||||
|
return await preprocessor.processMedia(at: url, maxUploadSize: maxUploadSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func sendAttachment(mediaInfo: MediaInfo, caption: String?) async -> Result<Void, TimelineControllerError> {
|
private func sendAttachment(mediaInfo: MediaInfo, caption: String?) async -> Result<Void, TimelineControllerError> {
|
||||||
let requestHandle: ((SendAttachmentJoinHandleProtocol) -> Void) = { [weak self] handle in
|
let requestHandle: ((SendAttachmentJoinHandleProtocol) -> Void) = { [weak self] handle in
|
||||||
self?.requestHandle = handle
|
self?.requestHandle = handle
|
||||||
@@ -124,8 +139,9 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
|||||||
private func startLoading() {
|
private func startLoading() {
|
||||||
userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
|
userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
|
||||||
type: .modal(progress: .indeterminate, interactiveDismissDisabled: false, allowsInteraction: true),
|
type: .modal(progress: .indeterminate, interactiveDismissDisabled: false, allowsInteraction: true),
|
||||||
title: L10n.commonSending,
|
title: L10n.commonPreparing,
|
||||||
persistent: true))
|
persistent: true),
|
||||||
|
delay: .milliseconds(100))
|
||||||
|
|
||||||
state.shouldDisableInteraction = true
|
state.shouldDisableInteraction = true
|
||||||
}
|
}
|
||||||
@@ -139,6 +155,26 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
|||||||
private func showError(label: String) {
|
private func showError(label: String) {
|
||||||
userIndicatorController.submitIndicator(UserIndicator(title: label))
|
userIndicatorController.submitIndicator(UserIndicator(title: label))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func showAlert(_ alertType: MediaUploadPreviewAlertType) {
|
||||||
|
switch alertType {
|
||||||
|
case .maxUploadSizeUnknown:
|
||||||
|
state.bindings.alertInfo = .init(id: alertType,
|
||||||
|
title: L10n.commonSomethingWentWrong,
|
||||||
|
message: L10n.screenMediaUploadPreviewErrorCouldNotBeUploaded,
|
||||||
|
primaryButton: .init(title: L10n.actionTryAgain) { [weak self] in
|
||||||
|
guard let self else { return }
|
||||||
|
processingTask = Self.processMedia(at: url, preprocessor: mediaUploadingPreprocessor, clientProxy: clientProxy)
|
||||||
|
process(viewAction: .send)
|
||||||
|
},
|
||||||
|
secondaryButton: .init(title: L10n.actionCancel, role: .cancel) { })
|
||||||
|
case .maxUploadSizeExceeded(let limit):
|
||||||
|
state.bindings.alertInfo = .init(id: alertType,
|
||||||
|
title: L10n.screenMediaUploadPreviewErrorTooLargeTitle,
|
||||||
|
message: L10n.screenMediaUploadPreviewErrorTooLargeMessage(limit.formatted(.byteCount(style: .file))),
|
||||||
|
primaryButton: .init(title: L10n.actionCancel, role: .cancel) { })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NSAttributedString {
|
extension NSAttributedString {
|
||||||
|
|||||||
+7
-5
@@ -39,6 +39,7 @@ struct MediaUploadPreviewScreen: View {
|
|||||||
.presentationBackground(.background) // Fix a bug introduced by the caption warning.
|
.presentationBackground(.background) // Fix a bug introduced by the caption warning.
|
||||||
.preferredColorScheme(colorSchemeOverride)
|
.preferredColorScheme(colorSchemeOverride)
|
||||||
.onAppear(perform: focusComposerIfHardwareKeyboardConnected)
|
.onAppear(perform: focusComposerIfHardwareKeyboardConnected)
|
||||||
|
.alert(item: $context.alertInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
@@ -227,13 +228,14 @@ struct MediaUploadPreviewScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
static let snapshotURL = URL.picturesDirectory
|
static let snapshotURL = URL.picturesDirectory
|
||||||
static let testURL = Bundle.main.url(forResource: "AppIcon60x60@2x", withExtension: "png")
|
static let testURL = Bundle.main.url(forResource: "AppIcon60x60@2x", withExtension: "png")
|
||||||
|
|
||||||
static let viewModel = MediaUploadPreviewScreenViewModel(timelineController: MockTimelineController(),
|
static let viewModel = MediaUploadPreviewScreenViewModel(url: snapshotURL,
|
||||||
userIndicatorController: UserIndicatorControllerMock.default,
|
|
||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
|
||||||
title: "App Icon.png",
|
title: "App Icon.png",
|
||||||
url: snapshotURL,
|
isRoomEncrypted: true,
|
||||||
shouldShowCaptionWarning: true,
|
shouldShowCaptionWarning: true,
|
||||||
isRoomEncrypted: true)
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||||
|
timelineController: MockTimelineController(),
|
||||||
|
clientProxy: ClientProxyMock(.init()),
|
||||||
|
userIndicatorController: UserIndicatorControllerMock.default)
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
MediaUploadPreviewScreen(context: viewModel.context)
|
MediaUploadPreviewScreen(context: viewModel.context)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import SwiftUI
|
|||||||
|
|
||||||
struct RoomDetailsEditScreenCoordinatorParameters {
|
struct RoomDetailsEditScreenCoordinatorParameters {
|
||||||
let roomProxy: JoinedRoomProxyProtocol
|
let roomProxy: JoinedRoomProxyProtocol
|
||||||
|
let clientProxy: ClientProxyProtocol
|
||||||
let mediaProvider: MediaProviderProtocol
|
let mediaProvider: MediaProviderProtocol
|
||||||
let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||||
weak var navigationStackCoordinator: NavigationStackCoordinator?
|
weak var navigationStackCoordinator: NavigationStackCoordinator?
|
||||||
@@ -35,6 +36,7 @@ final class RoomDetailsEditScreenCoordinator: CoordinatorProtocol {
|
|||||||
self.parameters = parameters
|
self.parameters = parameters
|
||||||
|
|
||||||
viewModel = RoomDetailsEditScreenViewModel(roomProxy: parameters.roomProxy,
|
viewModel = RoomDetailsEditScreenViewModel(roomProxy: parameters.roomProxy,
|
||||||
|
clientProxy: parameters.clientProxy,
|
||||||
mediaProvider: parameters.mediaProvider,
|
mediaProvider: parameters.mediaProvider,
|
||||||
mediaUploadingPreprocessor: parameters.mediaUploadingPreprocessor,
|
mediaUploadingPreprocessor: parameters.mediaUploadingPreprocessor,
|
||||||
userIndicatorController: parameters.userIndicatorController)
|
userIndicatorController: parameters.userIndicatorController)
|
||||||
|
|||||||
+10
-4
@@ -13,6 +13,7 @@ typealias RoomDetailsEditScreenViewModelType = StateStoreViewModel<RoomDetailsEd
|
|||||||
class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDetailsEditScreenViewModelProtocol {
|
class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDetailsEditScreenViewModelProtocol {
|
||||||
private let actionsSubject: PassthroughSubject<RoomDetailsEditScreenViewModelAction, Never> = .init()
|
private let actionsSubject: PassthroughSubject<RoomDetailsEditScreenViewModelAction, Never> = .init()
|
||||||
private let roomProxy: JoinedRoomProxyProtocol
|
private let roomProxy: JoinedRoomProxyProtocol
|
||||||
|
private let clientProxy: ClientProxyProtocol
|
||||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
private let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
private let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||||
|
|
||||||
@@ -21,10 +22,12 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
init(roomProxy: JoinedRoomProxyProtocol,
|
init(roomProxy: JoinedRoomProxyProtocol,
|
||||||
|
clientProxy: ClientProxyProtocol,
|
||||||
mediaProvider: MediaProviderProtocol,
|
mediaProvider: MediaProviderProtocol,
|
||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor,
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor,
|
||||||
userIndicatorController: UserIndicatorControllerProtocol) {
|
userIndicatorController: UserIndicatorControllerProtocol) {
|
||||||
self.roomProxy = roomProxy
|
self.roomProxy = roomProxy
|
||||||
|
self.clientProxy = clientProxy
|
||||||
self.mediaUploadingPreprocessor = mediaUploadingPreprocessor
|
self.mediaUploadingPreprocessor = mediaUploadingPreprocessor
|
||||||
self.userIndicatorController = userIndicatorController
|
self.userIndicatorController = userIndicatorController
|
||||||
|
|
||||||
@@ -72,15 +75,18 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe
|
|||||||
func didSelectMediaUrl(url: URL) {
|
func didSelectMediaUrl(url: URL) {
|
||||||
Task {
|
Task {
|
||||||
let userIndicatorID = UUID().uuidString
|
let userIndicatorID = UUID().uuidString
|
||||||
defer {
|
defer { userIndicatorController.retractIndicatorWithId(userIndicatorID) }
|
||||||
userIndicatorController.retractIndicatorWithId(userIndicatorID)
|
|
||||||
}
|
|
||||||
userIndicatorController.submitIndicator(UserIndicator(id: userIndicatorID,
|
userIndicatorController.submitIndicator(UserIndicator(id: userIndicatorID,
|
||||||
type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false),
|
type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false),
|
||||||
title: L10n.commonLoading,
|
title: L10n.commonLoading,
|
||||||
persistent: true))
|
persistent: true))
|
||||||
|
|
||||||
let mediaResult = await mediaUploadingPreprocessor.processMedia(at: url)
|
guard case let .success(maxUploadSize) = await clientProxy.maxMediaUploadSize else {
|
||||||
|
MXLog.error("Failed to get max upload size")
|
||||||
|
userIndicatorController.alertInfo = .init(id: .init())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let mediaResult = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize)
|
||||||
|
|
||||||
switch mediaResult {
|
switch mediaResult {
|
||||||
case .success(.image):
|
case .success(.image):
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ struct RoomDetailsEditScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
members: [.mockMeAdmin]))
|
members: [.mockMeAdmin]))
|
||||||
|
|
||||||
return RoomDetailsEditScreenViewModel(roomProxy: roomProxy,
|
return RoomDetailsEditScreenViewModel(roomProxy: roomProxy,
|
||||||
|
clientProxy: ClientProxyMock(.init()),
|
||||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||||
userIndicatorController: UserIndicatorControllerMock.default)
|
userIndicatorController: UserIndicatorControllerMock.default)
|
||||||
@@ -166,6 +167,7 @@ struct RoomDetailsEditScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
members: [.mockAlice]))
|
members: [.mockAlice]))
|
||||||
|
|
||||||
return RoomDetailsEditScreenViewModel(roomProxy: roomProxy,
|
return RoomDetailsEditScreenViewModel(roomProxy: roomProxy,
|
||||||
|
clientProxy: ClientProxyMock(.init()),
|
||||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||||
userIndicatorController: UserIndicatorControllerMock.default)
|
userIndicatorController: UserIndicatorControllerMock.default)
|
||||||
|
|||||||
+8
-5
@@ -82,21 +82,24 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe
|
|||||||
func didSelectMediaURL(url: URL) {
|
func didSelectMediaURL(url: URL) {
|
||||||
Task {
|
Task {
|
||||||
let userIndicatorID = UUID().uuidString
|
let userIndicatorID = UUID().uuidString
|
||||||
defer {
|
defer { userIndicatorController.retractIndicatorWithId(userIndicatorID) }
|
||||||
userIndicatorController.retractIndicatorWithId(userIndicatorID)
|
|
||||||
}
|
|
||||||
userIndicatorController.submitIndicator(UserIndicator(id: userIndicatorID,
|
userIndicatorController.submitIndicator(UserIndicator(id: userIndicatorID,
|
||||||
type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false),
|
type: .modal(progress: .indeterminate, interactiveDismissDisabled: true, allowsInteraction: false),
|
||||||
title: L10n.commonLoading,
|
title: L10n.commonLoading,
|
||||||
persistent: true))
|
persistent: true))
|
||||||
|
|
||||||
let mediaResult = await mediaUploadingPreprocessor.processMedia(at: url)
|
guard case let .success(maxUploadSize) = await clientProxy.maxMediaUploadSize else {
|
||||||
|
MXLog.error("Failed to get max upload size")
|
||||||
|
userIndicatorController.alertInfo = .init(id: .init())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let mediaResult = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize)
|
||||||
|
|
||||||
switch mediaResult {
|
switch mediaResult {
|
||||||
case .success(.image):
|
case .success(.image):
|
||||||
state.localMedia = try? mediaResult.get()
|
state.localMedia = try? mediaResult.get()
|
||||||
case .failure, .success:
|
case .failure, .success:
|
||||||
userIndicatorController.alertInfo = .init(id: .init(), title: L10n.commonError, message: L10n.errorUnknown)
|
userIndicatorController.alertInfo = .init(id: .init())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,12 +163,17 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
|
|||||||
Task { [weak self] in
|
Task { [weak self] in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
do {
|
do {
|
||||||
let media = try await parameters.mediaUploadingPreprocessor.processMedia(at: url).get()
|
guard case let .success(maxUploadSize) = await parameters.userSession.clientProxy.maxMediaUploadSize else {
|
||||||
|
MXLog.error("Failed to get max upload size")
|
||||||
|
parameters.userIndicatorController.alertInfo = AlertInfo(id: .init())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let media = try await parameters.mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize).get()
|
||||||
var parameters = createRoomParameters.value
|
var parameters = createRoomParameters.value
|
||||||
parameters.avatarImageMedia = media
|
parameters.avatarImageMedia = media
|
||||||
createRoomParameters.send(parameters)
|
createRoomParameters.send(parameters)
|
||||||
} catch {
|
} catch {
|
||||||
parameters.userIndicatorController.alertInfo = AlertInfo(id: .init(), title: L10n.commonError, message: L10n.errorUnknown)
|
parameters.userIndicatorController.alertInfo = AlertInfo(id: .init())
|
||||||
}
|
}
|
||||||
hideLoadingIndicator()
|
hideLoadingIndicator()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,6 +311,17 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var maxMediaUploadSize: Result<UInt, ClientProxyError> {
|
||||||
|
get async {
|
||||||
|
do {
|
||||||
|
return try await .success(UInt(client.getMaxMediaUploadSize()))
|
||||||
|
} catch {
|
||||||
|
MXLog.error("Failed checking the max media upload size with error: \(error)")
|
||||||
|
return .failure(.sdkError(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private(set) lazy var pusherNotificationClientIdentifier: String? = {
|
private(set) lazy var pusherNotificationClientIdentifier: String? = {
|
||||||
// NOTE: The result is stored as part of the restoration token. Any changes
|
// NOTE: The result is stored as part of the restoration token. Any changes
|
||||||
// here would require a migration to correctly match incoming notifications.
|
// here would require a migration to correctly match incoming notifications.
|
||||||
|
|||||||
@@ -130,6 +130,8 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
|
|||||||
|
|
||||||
var isLiveKitRTCSupported: Bool { get async }
|
var isLiveKitRTCSupported: Bool { get async }
|
||||||
|
|
||||||
|
var maxMediaUploadSize: Result<UInt, ClientProxyError> { get async }
|
||||||
|
|
||||||
func isOnlyDeviceLeft() async -> Result<Bool, ClientProxyError>
|
func isOnlyDeviceLeft() async -> Result<Bool, ClientProxyError>
|
||||||
|
|
||||||
func startSync()
|
func startSync()
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import UIKit
|
|||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
indirect enum MediaUploadingPreprocessorError: Error {
|
indirect enum MediaUploadingPreprocessorError: Error {
|
||||||
|
case maxUploadSizeUnknown
|
||||||
|
case maxUploadSizeExceeded(limit: UInt)
|
||||||
|
|
||||||
case failedProcessingMedia(Error)
|
case failedProcessingMedia(Error)
|
||||||
|
|
||||||
case failedProcessingImage(MediaUploadingPreprocessorError)
|
case failedProcessingImage(MediaUploadingPreprocessorError)
|
||||||
@@ -95,7 +98,7 @@ struct MediaUploadingPreprocessor {
|
|||||||
/// from images and retrieve associated media information
|
/// from images and retrieve associated media information
|
||||||
/// - Parameter url: the file URL
|
/// - Parameter url: the file URL
|
||||||
/// - Returns: a specific type of `MediaInfo` depending on the file type and its associated details
|
/// - Returns: a specific type of `MediaInfo` depending on the file type and its associated details
|
||||||
func processMedia(at url: URL) async -> Result<MediaInfo, MediaUploadingPreprocessorError> {
|
func processMedia(at url: URL, maxUploadSize: UInt) async -> Result<MediaInfo, MediaUploadingPreprocessorError> {
|
||||||
// Start by copying the file to a unique temporary location in order to avoid conflicts if processing it multiple times
|
// Start by copying the file to a unique temporary location in order to avoid conflicts if processing it multiple times
|
||||||
// All the other operations will be made relative to it
|
// All the other operations will be made relative to it
|
||||||
let uniqueFolder = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
let uniqueFolder = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||||
@@ -110,17 +113,17 @@ struct MediaUploadingPreprocessor {
|
|||||||
// Process unknown types as plain files
|
// Process unknown types as plain files
|
||||||
guard let type = UTType(filenameExtension: newURL.pathExtension),
|
guard let type = UTType(filenameExtension: newURL.pathExtension),
|
||||||
let mimeType = type.preferredMIMEType else {
|
let mimeType = type.preferredMIMEType else {
|
||||||
return processFile(at: newURL, mimeType: "application/octet-stream")
|
return processFile(at: newURL, mimeType: "application/octet-stream", maxUploadSize: maxUploadSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
if type.conforms(to: .image) {
|
if type.conforms(to: .image) {
|
||||||
return processImage(at: &newURL, type: type, mimeType: mimeType)
|
return processImage(at: &newURL, type: type, mimeType: mimeType, maxUploadSize: maxUploadSize)
|
||||||
} else if type.conforms(to: .movie) || type.conforms(to: .video) {
|
} else if type.conforms(to: .movie) || type.conforms(to: .video) {
|
||||||
return await processVideo(at: newURL)
|
return await processVideo(at: newURL, maxUploadSize: maxUploadSize)
|
||||||
} else if type.conforms(to: .audio) {
|
} else if type.conforms(to: .audio) {
|
||||||
return await processAudio(at: newURL, mimeType: mimeType)
|
return await processAudio(at: newURL, mimeType: mimeType, maxUploadSize: maxUploadSize)
|
||||||
} else {
|
} else {
|
||||||
return processFile(at: newURL, mimeType: mimeType)
|
return processFile(at: newURL, mimeType: mimeType, maxUploadSize: maxUploadSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +135,7 @@ struct MediaUploadingPreprocessor {
|
|||||||
/// - type: its UTType
|
/// - type: its UTType
|
||||||
/// - mimeType: the mimeType extracted from the UTType
|
/// - mimeType: the mimeType extracted from the UTType
|
||||||
/// - Returns: Returns a `MediaInfo.image` containing the URLs for the modified image and its thumbnail plus the corresponding `ImageInfo`
|
/// - Returns: Returns a `MediaInfo.image` containing the URLs for the modified image and its thumbnail plus the corresponding `ImageInfo`
|
||||||
private func processImage(at url: inout URL, type: UTType, mimeType: String) -> Result<MediaInfo, MediaUploadingPreprocessorError> {
|
private func processImage(at url: inout URL, type: UTType, mimeType: String, maxUploadSize: UInt) -> Result<MediaInfo, MediaUploadingPreprocessorError> {
|
||||||
do {
|
do {
|
||||||
try stripLocationFromImage(at: url, type: type)
|
try stripLocationFromImage(at: url, type: type)
|
||||||
|
|
||||||
@@ -161,18 +164,20 @@ struct MediaUploadingPreprocessor {
|
|||||||
return .failure(.failedProcessingImage(.failedStrippingLocationData))
|
return .failure(.failedProcessingImage(.failedStrippingLocationData))
|
||||||
}
|
}
|
||||||
|
|
||||||
let fileSize = (try? UInt64(FileManager.default.sizeForItem(at: url))) ?? 0
|
let fileSize = (try? FileManager.default.sizeForItem(at: url)) ?? 0
|
||||||
let thumbnailFileSize = (try? UInt64(FileManager.default.sizeForItem(at: thumbnailResult.url))) ?? 0
|
let thumbnailFileSize = (try? FileManager.default.sizeForItem(at: thumbnailResult.url)) ?? 0
|
||||||
|
|
||||||
|
guard fileSize < maxUploadSize, thumbnailFileSize < maxUploadSize else { return .failure(.maxUploadSizeExceeded(limit: maxUploadSize)) }
|
||||||
|
|
||||||
let thumbnailInfo = ThumbnailInfo(height: UInt64(thumbnailResult.height),
|
let thumbnailInfo = ThumbnailInfo(height: UInt64(thumbnailResult.height),
|
||||||
width: UInt64(thumbnailResult.width),
|
width: UInt64(thumbnailResult.width),
|
||||||
mimetype: thumbnailResult.mimeType,
|
mimetype: thumbnailResult.mimeType,
|
||||||
size: thumbnailFileSize)
|
size: UInt64(thumbnailFileSize))
|
||||||
|
|
||||||
let imageInfo = ImageInfo(height: UInt64(imageSize.height),
|
let imageInfo = ImageInfo(height: UInt64(imageSize.height),
|
||||||
width: UInt64(imageSize.width),
|
width: UInt64(imageSize.width),
|
||||||
mimetype: mimeType,
|
mimetype: mimeType,
|
||||||
size: fileSize,
|
size: UInt64(fileSize),
|
||||||
thumbnailInfo: thumbnailInfo,
|
thumbnailInfo: thumbnailInfo,
|
||||||
thumbnailSource: nil,
|
thumbnailSource: nil,
|
||||||
blurhash: thumbnailResult.blurhash,
|
blurhash: thumbnailResult.blurhash,
|
||||||
@@ -192,24 +197,26 @@ struct MediaUploadingPreprocessor {
|
|||||||
/// - type: its UTType
|
/// - type: its UTType
|
||||||
/// - mimeType: the mimeType extracted from the UTType
|
/// - mimeType: the mimeType extracted from the UTType
|
||||||
/// - Returns: Returns a `MediaInfo.video` containing the URLs for the modified video and its thumbnail plus the corresponding `VideoInfo`
|
/// - Returns: Returns a `MediaInfo.video` containing the URLs for the modified video and its thumbnail plus the corresponding `VideoInfo`
|
||||||
private func processVideo(at url: URL) async -> Result<MediaInfo, MediaUploadingPreprocessorError> {
|
private func processVideo(at url: URL, maxUploadSize: UInt) async -> Result<MediaInfo, MediaUploadingPreprocessorError> {
|
||||||
do {
|
do {
|
||||||
let result = try await convertVideoToMP4(url)
|
let result = try await convertVideoToMP4(url, targetFileSize: UInt(maxUploadSize))
|
||||||
let thumbnailResult = try await generateThumbnailForVideoAt(result.url)
|
let thumbnailResult = try await generateThumbnailForVideoAt(result.url)
|
||||||
|
|
||||||
let videoSize = (try? UInt64(FileManager.default.sizeForItem(at: result.url))) ?? 0
|
let videoSize = (try? FileManager.default.sizeForItem(at: result.url)) ?? 0
|
||||||
let thumbnailSize = (try? UInt64(FileManager.default.sizeForItem(at: thumbnailResult.url))) ?? 0
|
let thumbnailSize = (try? FileManager.default.sizeForItem(at: thumbnailResult.url)) ?? 0
|
||||||
|
|
||||||
|
guard videoSize < maxUploadSize, thumbnailSize < maxUploadSize else { return .failure(.maxUploadSizeExceeded(limit: maxUploadSize)) }
|
||||||
|
|
||||||
let thumbnailInfo = ThumbnailInfo(height: UInt64(thumbnailResult.height),
|
let thumbnailInfo = ThumbnailInfo(height: UInt64(thumbnailResult.height),
|
||||||
width: UInt64(thumbnailResult.width),
|
width: UInt64(thumbnailResult.width),
|
||||||
mimetype: thumbnailResult.mimeType,
|
mimetype: thumbnailResult.mimeType,
|
||||||
size: thumbnailSize)
|
size: UInt64(thumbnailSize))
|
||||||
|
|
||||||
let videoInfo = VideoInfo(duration: result.duration,
|
let videoInfo = VideoInfo(duration: result.duration,
|
||||||
height: UInt64(result.height),
|
height: UInt64(result.height),
|
||||||
width: UInt64(result.width),
|
width: UInt64(result.width),
|
||||||
mimetype: result.mimeType,
|
mimetype: result.mimeType,
|
||||||
size: videoSize,
|
size: UInt64(videoSize),
|
||||||
thumbnailInfo: thumbnailInfo,
|
thumbnailInfo: thumbnailInfo,
|
||||||
thumbnailSource: nil,
|
thumbnailSource: nil,
|
||||||
blurhash: thumbnailResult.blurhash)
|
blurhash: thumbnailResult.blurhash)
|
||||||
@@ -227,15 +234,17 @@ struct MediaUploadingPreprocessor {
|
|||||||
/// - url: The audio URL
|
/// - url: The audio URL
|
||||||
/// - mimeType: the mimeType extracted from the UTType
|
/// - mimeType: the mimeType extracted from the UTType
|
||||||
/// - Returns: Returns a `MediaInfo.audio` containing the file URL plus the corresponding `AudioInfo`
|
/// - Returns: Returns a `MediaInfo.audio` containing the file URL plus the corresponding `AudioInfo`
|
||||||
private func processAudio(at url: URL, mimeType: String?) async -> Result<MediaInfo, MediaUploadingPreprocessorError> {
|
private func processAudio(at url: URL, mimeType: String?, maxUploadSize: UInt) async -> Result<MediaInfo, MediaUploadingPreprocessorError> {
|
||||||
let fileSize = (try? UInt64(FileManager.default.sizeForItem(at: url))) ?? 0
|
let fileSize = (try? FileManager.default.sizeForItem(at: url)) ?? 0
|
||||||
|
|
||||||
|
guard fileSize < maxUploadSize else { return .failure(.maxUploadSizeExceeded(limit: maxUploadSize)) }
|
||||||
|
|
||||||
let asset = AVURLAsset(url: url)
|
let asset = AVURLAsset(url: url)
|
||||||
guard let durationInSeconds = try? await asset.load(.duration).seconds else {
|
guard let durationInSeconds = try? await asset.load(.duration).seconds else {
|
||||||
return .failure(.failedProcessingAudio)
|
return .failure(.failedProcessingAudio)
|
||||||
}
|
}
|
||||||
|
|
||||||
let audioInfo = AudioInfo(duration: durationInSeconds, size: fileSize, mimetype: mimeType)
|
let audioInfo = AudioInfo(duration: durationInSeconds, size: UInt64(fileSize), mimetype: mimeType)
|
||||||
return .success(.audio(audioURL: url, audioInfo: audioInfo))
|
return .success(.audio(audioURL: url, audioInfo: audioInfo))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,10 +254,12 @@ struct MediaUploadingPreprocessor {
|
|||||||
/// - type: its UTType
|
/// - type: its UTType
|
||||||
/// - mimeType: the mimeType extracted from the UTType
|
/// - mimeType: the mimeType extracted from the UTType
|
||||||
/// - Returns: Returns a `MediaInfo.file` containing the file URL plus the corresponding `FileInfo`
|
/// - Returns: Returns a `MediaInfo.file` containing the file URL plus the corresponding `FileInfo`
|
||||||
private func processFile(at url: URL, mimeType: String?) -> Result<MediaInfo, MediaUploadingPreprocessorError> {
|
private func processFile(at url: URL, mimeType: String?, maxUploadSize: UInt) -> Result<MediaInfo, MediaUploadingPreprocessorError> {
|
||||||
let fileSize = (try? UInt64(FileManager.default.sizeForItem(at: url))) ?? 0
|
let fileSize = (try? FileManager.default.sizeForItem(at: url)) ?? 0
|
||||||
|
|
||||||
let fileInfo = FileInfo(mimetype: mimeType, size: fileSize, thumbnailInfo: nil, thumbnailSource: nil)
|
guard fileSize < maxUploadSize else { return .failure(.maxUploadSizeExceeded(limit: maxUploadSize)) }
|
||||||
|
|
||||||
|
let fileInfo = FileInfo(mimetype: mimeType, size: UInt64(fileSize), thumbnailInfo: nil, thumbnailSource: nil)
|
||||||
return .success(.file(fileURL: url, fileInfo: fileInfo))
|
return .success(.file(fileURL: url, fileInfo: fileInfo))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,7 +399,7 @@ struct MediaUploadingPreprocessor {
|
|||||||
/// - url: the original video URL
|
/// - url: the original video URL
|
||||||
/// - targetFileSize: the maximum resulting file size. 90% of this will be used
|
/// - targetFileSize: the maximum resulting file size. 90% of this will be used
|
||||||
/// - Returns: the URL for the resulting video and its media info as a `VideoProcessingResult`
|
/// - Returns: the URL for the resulting video and its media info as a `VideoProcessingResult`
|
||||||
private func convertVideoToMP4(_ url: URL, targetFileSize: UInt = 0) async throws(MediaUploadingPreprocessorError) -> VideoProcessingInfo {
|
private func convertVideoToMP4(_ url: URL, targetFileSize: UInt) async throws(MediaUploadingPreprocessorError) -> VideoProcessingInfo {
|
||||||
let asset = AVURLAsset(url: url)
|
let asset = AVURLAsset(url: url)
|
||||||
let presetName = appSettings.optimizeMediaUploads ? AVAssetExportPreset1280x720 : AVAssetExportPreset1920x1080
|
let presetName = appSettings.optimizeMediaUploads ? AVAssetExportPreset1280x720 : AVAssetExportPreset1920x1080
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import XCTest
|
|||||||
@MainActor
|
@MainActor
|
||||||
class MediaUploadPreviewScreenViewModelTests: XCTestCase {
|
class MediaUploadPreviewScreenViewModelTests: XCTestCase {
|
||||||
var timelineProxy: TimelineProxyMock!
|
var timelineProxy: TimelineProxyMock!
|
||||||
|
var clientProxy: ClientProxyMock!
|
||||||
var viewModel: MediaUploadPreviewScreenViewModel!
|
var viewModel: MediaUploadPreviewScreenViewModel!
|
||||||
var context: MediaUploadPreviewScreenViewModel.Context { viewModel.context }
|
var context: MediaUploadPreviewScreenViewModel.Context { viewModel.context }
|
||||||
|
|
||||||
@@ -89,6 +90,54 @@ class MediaUploadPreviewScreenViewModelTests: XCTestCase {
|
|||||||
try await send()
|
try await send()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testUploadWithUnknownMaxUploadSize() async throws {
|
||||||
|
// Given an upload screen that is unable to fetch the max upload size.
|
||||||
|
setUpViewModel(url: imageURL, expectedCaption: nil, maxUploadSizeResult: .failure(.sdkError(ClientProxyMockError.generic)))
|
||||||
|
XCTAssertFalse(context.viewState.shouldDisableInteraction)
|
||||||
|
XCTAssertNil(context.alertInfo)
|
||||||
|
|
||||||
|
// When attempting to send the media.
|
||||||
|
let deferredAlert = deferFulfillment(context.observe(\.viewState.bindings.alertInfo)) { $0 != nil }
|
||||||
|
context.send(viewAction: .send)
|
||||||
|
|
||||||
|
XCTAssertTrue(context.viewState.shouldDisableInteraction, "The interaction should be disabled while sending.")
|
||||||
|
|
||||||
|
// Then alert should be shown to tell the user it failed.
|
||||||
|
try await deferredAlert.fulfill()
|
||||||
|
|
||||||
|
XCTAssertFalse(context.viewState.shouldDisableInteraction)
|
||||||
|
XCTAssertEqual(context.alertInfo?.id, .maxUploadSizeUnknown)
|
||||||
|
|
||||||
|
// When trying with the max upload size now available.
|
||||||
|
let deferredDismiss = deferFulfillment(viewModel.actions) { $0 == .dismiss }
|
||||||
|
clientProxy.underlyingMaxMediaUploadSize = .success(100 * 1024 * 1024)
|
||||||
|
context.alertInfo?.primaryButton.action?()
|
||||||
|
|
||||||
|
XCTAssertTrue(context.viewState.shouldDisableInteraction, "The interaction should be disabled while retrying.")
|
||||||
|
|
||||||
|
// Then the file should upload successfully.
|
||||||
|
try await deferredDismiss.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUploadExceedingMaxUploadSize() async throws {
|
||||||
|
// Given an upload screen with a really small max upload size.
|
||||||
|
setUpViewModel(url: imageURL, expectedCaption: nil, maxUploadSizeResult: .success(100))
|
||||||
|
XCTAssertFalse(context.viewState.shouldDisableInteraction)
|
||||||
|
XCTAssertNil(context.alertInfo)
|
||||||
|
|
||||||
|
// When attempting to send an image that is larger the limit.
|
||||||
|
let deferredAlert = deferFulfillment(context.observe(\.viewState.bindings.alertInfo)) { $0 != nil }
|
||||||
|
context.send(viewAction: .send)
|
||||||
|
|
||||||
|
XCTAssertTrue(context.viewState.shouldDisableInteraction, "The interaction should be disabled while sending.")
|
||||||
|
|
||||||
|
// Then an alert should be shown to inform the user of the max upload size.
|
||||||
|
try await deferredAlert.fulfill()
|
||||||
|
|
||||||
|
XCTAssertFalse(context.viewState.shouldDisableInteraction)
|
||||||
|
XCTAssertEqual(context.alertInfo?.id, .maxUploadSizeExceeded(limit: 100))
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
private var audioURL: URL { assertResourceURL(filename: "test_audio.mp3") }
|
private var audioURL: URL { assertResourceURL(filename: "test_audio.mp3") }
|
||||||
@@ -104,7 +153,7 @@ class MediaUploadPreviewScreenViewModelTests: XCTestCase {
|
|||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setUpViewModel(url: URL, expectedCaption: String?) {
|
private func setUpViewModel(url: URL, expectedCaption: String?, maxUploadSizeResult: Result<UInt, ClientProxyError>? = nil) {
|
||||||
timelineProxy = TimelineProxyMock(.init())
|
timelineProxy = TimelineProxyMock(.init())
|
||||||
timelineProxy.sendAudioUrlAudioInfoCaptionRequestHandleClosure = { [weak self] _, _, caption, _ in
|
timelineProxy.sendAudioUrlAudioInfoCaptionRequestHandleClosure = { [weak self] _, _, caption, _ in
|
||||||
self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
||||||
@@ -119,13 +168,19 @@ class MediaUploadPreviewScreenViewModelTests: XCTestCase {
|
|||||||
self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel = MediaUploadPreviewScreenViewModel(timelineController: MockTimelineController(timelineProxy: timelineProxy),
|
clientProxy = ClientProxyMock(.init())
|
||||||
userIndicatorController: UserIndicatorControllerMock(),
|
if let maxUploadSizeResult {
|
||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
clientProxy.underlyingMaxMediaUploadSize = maxUploadSizeResult
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel = MediaUploadPreviewScreenViewModel(url: url,
|
||||||
title: "Some File",
|
title: "Some File",
|
||||||
url: url,
|
isRoomEncrypted: true,
|
||||||
shouldShowCaptionWarning: true,
|
shouldShowCaptionWarning: true,
|
||||||
isRoomEncrypted: true)
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||||
|
timelineController: MockTimelineController(timelineProxy: timelineProxy),
|
||||||
|
clientProxy: clientProxy,
|
||||||
|
userIndicatorController: UserIndicatorControllerMock())
|
||||||
}
|
}
|
||||||
|
|
||||||
private func verifyCaption(_ caption: String?, expectedCaption: String?) -> Result<Void, TimelineProxyError> {
|
private func verifyCaption(_ caption: String?, expectedCaption: String?) -> Result<Void, TimelineProxyError> {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import XCTest
|
|||||||
@testable import ElementX
|
@testable import ElementX
|
||||||
|
|
||||||
final class MediaUploadingPreprocessorTests: XCTestCase {
|
final class MediaUploadingPreprocessorTests: XCTestCase {
|
||||||
|
let maxUploadSize: UInt = 100 * 1024 * 1024
|
||||||
var appSettings: AppSettings!
|
var appSettings: AppSettings!
|
||||||
var mediaUploadingPreprocessor: MediaUploadingPreprocessor!
|
var mediaUploadingPreprocessor: MediaUploadingPreprocessor!
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .audio(audioURL, audioInfo) = result else {
|
case let .audio(audioURL, audioInfo) = result else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -55,7 +56,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .video(videoURL, thumbnailURL, videoInfo) = result else {
|
case let .video(videoURL, thumbnailURL, videoInfo) = result else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -92,7 +93,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
// Repeat with optimised media setting
|
// Repeat with optimised media setting
|
||||||
appSettings.optimizeMediaUploads = true
|
appSettings.optimizeMediaUploads = true
|
||||||
|
|
||||||
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .video(optimizedVideoURL, _, optimizedVideoInfo) = optimizedResult else {
|
case let .video(optimizedVideoURL, _, optimizedVideoInfo) = optimizedResult else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -118,7 +119,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .video(videoURL, thumbnailURL, videoInfo) = result else {
|
case let .video(videoURL, thumbnailURL, videoInfo) = result else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -155,7 +156,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
// Repeat with optimised media setting
|
// Repeat with optimised media setting
|
||||||
appSettings.optimizeMediaUploads = true
|
appSettings.optimizeMediaUploads = true
|
||||||
|
|
||||||
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .video(optimizedVideoURL, _, optimizedVideoInfo) = optimizedResult else {
|
case let .video(optimizedVideoURL, _, optimizedVideoInfo) = optimizedResult else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -178,7 +179,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .image(convertedImageURL, thumbnailURL, imageInfo) = result else {
|
case let .image(convertedImageURL, thumbnailURL, imageInfo) = result else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -202,7 +203,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
// Repeat with optimised media setting
|
// Repeat with optimised media setting
|
||||||
appSettings.optimizeMediaUploads = true
|
appSettings.optimizeMediaUploads = true
|
||||||
|
|
||||||
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .image(optimizedImageURL, thumbnailURL, optimizedImageInfo) = optimizedResult else {
|
case let .image(optimizedImageURL, thumbnailURL, optimizedImageInfo) = optimizedResult else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -224,7 +225,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .image(convertedImageURL, thumbnailURL, imageInfo) = result else {
|
case let .image(convertedImageURL, thumbnailURL, imageInfo) = result else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -248,7 +249,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
// Repeat with optimised media setting
|
// Repeat with optimised media setting
|
||||||
appSettings.optimizeMediaUploads = true
|
appSettings.optimizeMediaUploads = true
|
||||||
|
|
||||||
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .image(optimizedImageURL, thumbnailURL, optimizedImageInfo) = optimizedResult else {
|
case let .image(optimizedImageURL, thumbnailURL, optimizedImageInfo) = optimizedResult else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -270,7 +271,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .image(convertedImageURL, _, imageInfo) = result else {
|
case let .image(convertedImageURL, _, imageInfo) = result else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -296,7 +297,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
// Repeat with optimised media setting
|
// Repeat with optimised media setting
|
||||||
appSettings.optimizeMediaUploads = true
|
appSettings.optimizeMediaUploads = true
|
||||||
|
|
||||||
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .image(optimizedImageURL, _, optimizedImageInfo) = optimizedResult else {
|
case let .image(optimizedImageURL, _, optimizedImageInfo) = optimizedResult else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -321,7 +322,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .image(convertedImageURL, thumbnailURL, imageInfo) = result else {
|
case let .image(convertedImageURL, thumbnailURL, imageInfo) = result else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -349,7 +350,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
// Repeat with optimised media setting
|
// Repeat with optimised media setting
|
||||||
appSettings.optimizeMediaUploads = true
|
appSettings.optimizeMediaUploads = true
|
||||||
|
|
||||||
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .image(optimizedImageURL, thumbnailURL, optimizedImageInfo) = optimizedResult else {
|
case let .image(optimizedImageURL, thumbnailURL, optimizedImageInfo) = optimizedResult else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -379,7 +380,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .image(convertedImageURL, _, imageInfo) = result else {
|
case let .image(convertedImageURL, _, imageInfo) = result else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -405,7 +406,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
// Repeat with optimised media setting
|
// Repeat with optimised media setting
|
||||||
appSettings.optimizeMediaUploads = true
|
appSettings.optimizeMediaUploads = true
|
||||||
|
|
||||||
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .image(optimizedImageURL, _, optimizedImageInfo) = optimizedResult else {
|
case let .image(optimizedImageURL, _, optimizedImageInfo) = optimizedResult else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -429,7 +430,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(result) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .image(convertedImageURL, thumbnailURL, imageInfo) = result else {
|
case let .image(convertedImageURL, thumbnailURL, imageInfo) = result else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
@@ -449,7 +450,7 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
|||||||
// Repeat with optimised media setting
|
// Repeat with optimised media setting
|
||||||
appSettings.optimizeMediaUploads = true
|
appSettings.optimizeMediaUploads = true
|
||||||
|
|
||||||
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url),
|
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url, maxUploadSize: maxUploadSize),
|
||||||
case let .image(optimizedImageURL, thumbnailURL, optimizedImageInfo) = optimizedResult else {
|
case let .image(optimizedImageURL, thumbnailURL, optimizedImageInfo) = optimizedResult else {
|
||||||
XCTFail("Failed processing asset")
|
XCTFail("Failed processing asset")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ class RoomDetailsEditScreenViewModelTests: XCTestCase {
|
|||||||
private func setupViewModel(roomProxyConfiguration: JoinedRoomProxyMockConfiguration) {
|
private func setupViewModel(roomProxyConfiguration: JoinedRoomProxyMockConfiguration) {
|
||||||
userIndicatorController = UserIndicatorControllerMock.default
|
userIndicatorController = UserIndicatorControllerMock.default
|
||||||
viewModel = .init(roomProxy: JoinedRoomProxyMock(roomProxyConfiguration),
|
viewModel = .init(roomProxy: JoinedRoomProxyMock(roomProxyConfiguration),
|
||||||
|
clientProxy: ClientProxyMock(.init()),
|
||||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||||
userIndicatorController: userIndicatorController)
|
userIndicatorController: userIndicatorController)
|
||||||
|
|||||||
Reference in New Issue
Block a user