mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 13:37:11 +00:00
Add a toggle in the developer options to optimise the media uploads. (#3408)
This commit is contained in:
parent
b1b2297972
commit
98a5ee5b48
@ -42,9 +42,9 @@ final class AppSettings {
|
||||
|
||||
// Feature flags
|
||||
case slidingSyncDiscovery
|
||||
case optimizeMediaUploads
|
||||
case publicSearchEnabled
|
||||
case fuzzyRoomListSearchEnabled
|
||||
case pinningEnabled
|
||||
case enableOnlySignedDeviceIsolationMode
|
||||
case identityPinningViolationNotificationsEnabled
|
||||
case knockingEnabled
|
||||
@ -281,6 +281,9 @@ final class AppSettings {
|
||||
@UserPreference(key: UserDefaultsKeys.slidingSyncDiscovery, defaultValue: .native, storageType: .userDefaults(store))
|
||||
var slidingSyncDiscovery: SlidingSyncDiscovery
|
||||
|
||||
@UserPreference(key: UserDefaultsKeys.optimizeMediaUploads, defaultValue: false, storageType: .userDefaults(store))
|
||||
var optimizeMediaUploads
|
||||
|
||||
@UserPreference(key: UserDefaultsKeys.identityPinningViolationNotificationsEnabled, defaultValue: isDevelopmentBuild, storageType: .userDefaults(store))
|
||||
var identityPinningViolationNotificationsEnabled
|
||||
|
||||
|
@ -810,6 +810,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
let roomDetailsEditParameters = RoomDetailsEditScreenCoordinatorParameters(roomProxy: roomProxy,
|
||||
mediaProvider: userSession.mediaProvider,
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings),
|
||||
navigationStackCoordinator: stackCoordinator,
|
||||
userIndicatorController: userIndicatorController,
|
||||
orientationManager: appMediator.windowManager)
|
||||
@ -895,7 +896,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
let parameters = MediaUploadPreviewScreenCoordinatorParameters(userIndicatorController: userIndicatorController,
|
||||
roomProxy: roomProxy,
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(),
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings),
|
||||
title: url.lastPathComponent,
|
||||
url: url)
|
||||
|
||||
|
@ -165,6 +165,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
let coordinator = UserDetailsEditScreenCoordinator(parameters: .init(orientationManager: parameters.windowManager,
|
||||
clientProxy: parameters.userSession.clientProxy,
|
||||
mediaProvider: parameters.userSession.mediaProvider,
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: parameters.appSettings),
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
userIndicatorController: parameters.userIndicatorController))
|
||||
|
||||
|
@ -542,7 +542,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
userSession: userSession,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
navigationStackCoordinator: startChatNavigationStackCoordinator,
|
||||
userDiscoveryService: userDiscoveryService)
|
||||
userDiscoveryService: userDiscoveryService,
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings))
|
||||
|
||||
let coordinator = StartChatScreenCoordinator(parameters: parameters)
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
|
@ -116,7 +116,7 @@ private class PreviewItem: NSObject, QLPreviewItem {
|
||||
struct MediaUploadPreviewScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = MediaUploadPreviewScreenViewModel(userIndicatorController: UserIndicatorControllerMock.default,
|
||||
roomProxy: JoinedRoomProxyMock(),
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(),
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||
title: "some random file name",
|
||||
url: URL.picturesDirectory)
|
||||
static var previews: some View {
|
||||
|
@ -11,6 +11,7 @@ import SwiftUI
|
||||
struct RoomDetailsEditScreenCoordinatorParameters {
|
||||
let roomProxy: JoinedRoomProxyProtocol
|
||||
let mediaProvider: MediaProviderProtocol
|
||||
let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||
weak var navigationStackCoordinator: NavigationStackCoordinator?
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
let orientationManager: OrientationManagerProtocol
|
||||
@ -35,6 +36,7 @@ final class RoomDetailsEditScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
viewModel = RoomDetailsEditScreenViewModel(roomProxy: parameters.roomProxy,
|
||||
mediaProvider: parameters.mediaProvider,
|
||||
mediaUploadingPreprocessor: parameters.mediaUploadingPreprocessor,
|
||||
userIndicatorController: parameters.userIndicatorController)
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe
|
||||
private let actionsSubject: PassthroughSubject<RoomDetailsEditScreenViewModelAction, Never> = .init()
|
||||
private let roomProxy: JoinedRoomProxyProtocol
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
private let mediaPreprocessor: MediaUploadingPreprocessor = .init()
|
||||
private let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||
|
||||
var actions: AnyPublisher<RoomDetailsEditScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
@ -22,8 +22,10 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe
|
||||
|
||||
init(roomProxy: JoinedRoomProxyProtocol,
|
||||
mediaProvider: MediaProviderProtocol,
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor,
|
||||
userIndicatorController: UserIndicatorControllerProtocol) {
|
||||
self.roomProxy = roomProxy
|
||||
self.mediaUploadingPreprocessor = mediaUploadingPreprocessor
|
||||
self.userIndicatorController = userIndicatorController
|
||||
|
||||
let roomAvatar = roomProxy.avatarURL
|
||||
@ -76,7 +78,7 @@ class RoomDetailsEditScreenViewModel: RoomDetailsEditScreenViewModelType, RoomDe
|
||||
title: L10n.commonLoading,
|
||||
persistent: true))
|
||||
|
||||
let mediaResult = await mediaPreprocessor.processMedia(at: url)
|
||||
let mediaResult = await mediaUploadingPreprocessor.processMedia(at: url)
|
||||
|
||||
switch mediaResult {
|
||||
case .success(.image):
|
||||
|
@ -154,6 +154,7 @@ struct RoomDetailsEditScreen_Previews: PreviewProvider, TestablePreview {
|
||||
|
||||
return RoomDetailsEditScreenViewModel(roomProxy: roomProxy,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||
userIndicatorController: UserIndicatorControllerMock.default)
|
||||
}()
|
||||
|
||||
@ -164,6 +165,7 @@ struct RoomDetailsEditScreen_Previews: PreviewProvider, TestablePreview {
|
||||
|
||||
return RoomDetailsEditScreenViewModel(roomProxy: roomProxy,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||
userIndicatorController: UserIndicatorControllerMock.default)
|
||||
}()
|
||||
|
||||
|
@ -46,6 +46,7 @@ protocol DeveloperOptionsProtocol: AnyObject {
|
||||
var hideUnreadMessagesBadge: Bool { get set }
|
||||
var fuzzyRoomListSearchEnabled: Bool { get set }
|
||||
var hideTimelineMedia: Bool { get set }
|
||||
var optimizeMediaUploads: Bool { get set }
|
||||
var enableOnlySignedDeviceIsolationMode: Bool { get set }
|
||||
var elementCallBaseURLOverride: URL? { get set }
|
||||
var identityPinningViolationNotificationsEnabled: Bool { get set }
|
||||
|
@ -62,6 +62,12 @@ struct DeveloperOptionsScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
Section("Media") {
|
||||
Toggle(isOn: $context.optimizeMediaUploads) {
|
||||
Text("Optimise for upload")
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
Toggle(isOn: $context.enableOnlySignedDeviceIsolationMode) {
|
||||
Text("Exclude insecure devices when sending/receiving messages")
|
||||
|
@ -12,6 +12,7 @@ struct UserDetailsEditScreenCoordinatorParameters {
|
||||
let orientationManager: OrientationManagerProtocol
|
||||
let clientProxy: ClientProxyProtocol
|
||||
let mediaProvider: MediaProviderProtocol
|
||||
let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||
weak var navigationStackCoordinator: NavigationStackCoordinator?
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
}
|
||||
@ -26,6 +27,7 @@ final class UserDetailsEditScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
viewModel = UserDetailsEditScreenViewModel(clientProxy: parameters.clientProxy,
|
||||
mediaProvider: parameters.mediaProvider,
|
||||
mediaUploadingPreprocessor: parameters.mediaUploadingPreprocessor,
|
||||
userIndicatorController: parameters.userIndicatorController)
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe
|
||||
private let actionsSubject: PassthroughSubject<UserDetailsEditScreenViewModelAction, Never> = .init()
|
||||
private let clientProxy: ClientProxyProtocol
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
private let mediaPreprocessor: MediaUploadingPreprocessor = .init()
|
||||
private let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||
|
||||
var actions: AnyPublisher<UserDetailsEditScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
@ -22,8 +22,10 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe
|
||||
|
||||
init(clientProxy: ClientProxyProtocol,
|
||||
mediaProvider: MediaProviderProtocol,
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor,
|
||||
userIndicatorController: UserIndicatorControllerProtocol) {
|
||||
self.clientProxy = clientProxy
|
||||
self.mediaUploadingPreprocessor = mediaUploadingPreprocessor
|
||||
self.userIndicatorController = userIndicatorController
|
||||
|
||||
super.init(initialViewState: UserDetailsEditScreenViewState(userID: clientProxy.userID,
|
||||
@ -88,7 +90,7 @@ class UserDetailsEditScreenViewModel: UserDetailsEditScreenViewModelType, UserDe
|
||||
title: L10n.commonLoading,
|
||||
persistent: true))
|
||||
|
||||
let mediaResult = await mediaPreprocessor.processMedia(at: url)
|
||||
let mediaResult = await mediaUploadingPreprocessor.processMedia(at: url)
|
||||
|
||||
switch mediaResult {
|
||||
case .success(.image):
|
||||
|
@ -117,6 +117,7 @@ struct UserDetailsEditScreen: View {
|
||||
struct UserDetailsEditScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = UserDetailsEditScreenViewModel(clientProxy: ClientProxyMock(.init(userID: "@stefan:matrix.org")),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaUploadingPreprocessor: .init(appSettings: ServiceLocator.shared.settings),
|
||||
userIndicatorController: UserIndicatorControllerMock.default)
|
||||
|
||||
static var previews: some View {
|
||||
|
@ -14,6 +14,7 @@ struct StartChatScreenCoordinatorParameters {
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
weak var navigationStackCoordinator: NavigationStackCoordinator?
|
||||
let userDiscoveryService: UserDiscoveryServiceProtocol
|
||||
let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||
}
|
||||
|
||||
enum StartChatScreenCoordinatorAction {
|
||||
@ -134,7 +135,6 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
let mediaUploadingPreprocessor = MediaUploadingPreprocessor()
|
||||
private func displayMediaPickerWithSource(_ source: MediaPickerScreenSource) {
|
||||
let stackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
@ -159,7 +159,7 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
|
||||
Task { [weak self] in
|
||||
guard let self else { return }
|
||||
do {
|
||||
let media = try await mediaUploadingPreprocessor.processMedia(at: url).get()
|
||||
let media = try await parameters.mediaUploadingPreprocessor.processMedia(at: url).get()
|
||||
var parameters = createRoomParameters.value
|
||||
parameters.avatarImageMedia = media
|
||||
createRoomParameters.send(parameters)
|
||||
|
@ -82,6 +82,8 @@ private struct VideoProcessingInfo {
|
||||
}
|
||||
|
||||
struct MediaUploadingPreprocessor {
|
||||
let appSettings: AppSettings
|
||||
|
||||
enum Constants {
|
||||
static let maximumThumbnailSize = CGSize(width: 800, height: 600)
|
||||
static let thumbnailCompressionQuality = 0.8
|
||||
@ -368,8 +370,9 @@ struct MediaUploadingPreprocessor {
|
||||
/// - Returns: the URL for the resulting video and its media info as a `VideoProcessingResult`
|
||||
private func convertVideoToMP4(_ url: URL, targetFileSize: UInt = 0) async -> Result<VideoProcessingInfo, MediaUploadingPreprocessorError> {
|
||||
let asset = AVURLAsset(url: url)
|
||||
let presetName = appSettings.optimizeMediaUploads ? AVAssetExportPreset640x480 : AVAssetExportPreset1920x1080
|
||||
|
||||
guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset1920x1080) else {
|
||||
guard let exportSession = AVAssetExportSession(asset: asset, presetName: presetName) else {
|
||||
return .failure(.failedConvertingVideo)
|
||||
}
|
||||
|
||||
|
@ -581,7 +581,8 @@ class MockScreen: Identifiable {
|
||||
userSession: userSession,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
userDiscoveryService: userDiscoveryMock)
|
||||
userDiscoveryService: userDiscoveryMock,
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings))
|
||||
let coordinator = StartChatScreenCoordinator(parameters: parameters)
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
return navigationStackCoordinator
|
||||
@ -595,7 +596,8 @@ class MockScreen: Identifiable {
|
||||
userSession: userSession,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
navigationStackCoordinator: navigationStackCoordinator,
|
||||
userDiscoveryService: userDiscoveryMock))
|
||||
userDiscoveryService: userDiscoveryMock,
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings)))
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
return navigationStackCoordinator
|
||||
case .createRoom:
|
||||
|
@ -10,7 +10,19 @@ import XCTest
|
||||
@testable import ElementX
|
||||
|
||||
final class MediaUploadingPreprocessorTests: XCTestCase {
|
||||
let mediaUploadingPreprocessor = MediaUploadingPreprocessor()
|
||||
var appSettings: AppSettings!
|
||||
var mediaUploadingPreprocessor: MediaUploadingPreprocessor!
|
||||
|
||||
override func setUp() {
|
||||
AppSettings.resetAllSettings()
|
||||
appSettings = AppSettings()
|
||||
ServiceLocator.shared.register(appSettings: appSettings)
|
||||
mediaUploadingPreprocessor = MediaUploadingPreprocessor(appSettings: appSettings)
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
AppSettings.resetAllSettings()
|
||||
}
|
||||
|
||||
func testAudioFileProcessing() async {
|
||||
guard let url = Bundle(for: Self.self).url(forResource: "test_audio.mp3", withExtension: nil) else {
|
||||
@ -70,6 +82,23 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
||||
XCTAssertEqual(videoInfo.thumbnailInfo?.size ?? 0, 34206, accuracy: 100)
|
||||
XCTAssertEqual(videoInfo.thumbnailInfo?.width, 800)
|
||||
XCTAssertEqual(videoInfo.thumbnailInfo?.height, 450)
|
||||
|
||||
// Repeat with optimised media setting
|
||||
appSettings.optimizeMediaUploads = true
|
||||
|
||||
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url),
|
||||
case let .video(_, _, optimizedVideoInfo) = optimizedResult else {
|
||||
XCTFail("Failed processing asset")
|
||||
return
|
||||
}
|
||||
|
||||
// Check optimised video info
|
||||
XCTAssertEqual(optimizedVideoInfo.mimetype, "video/mp4")
|
||||
XCTAssertEqual(optimizedVideoInfo.blurhash, "K32PJbx^I7jYaebHMvV?o$")
|
||||
XCTAssertEqual(optimizedVideoInfo.size ?? 0, 4_090_898, accuracy: 100) // Note: This is slightly stupid because it is larger now 🤦♂️
|
||||
XCTAssertEqual(optimizedVideoInfo.width, 640)
|
||||
XCTAssertEqual(optimizedVideoInfo.height, 360)
|
||||
XCTAssertEqual(optimizedVideoInfo.duration ?? 0, 30, accuracy: 100)
|
||||
}
|
||||
|
||||
func testPortraitMp4VideoProcessing() async {
|
||||
@ -110,6 +139,23 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
||||
XCTAssertEqual(videoInfo.thumbnailInfo?.size ?? 0, 83220, accuracy: 100)
|
||||
XCTAssertEqual(videoInfo.thumbnailInfo?.width, 337)
|
||||
XCTAssertEqual(videoInfo.thumbnailInfo?.height, 600)
|
||||
|
||||
// Repeat with optimised media setting
|
||||
appSettings.optimizeMediaUploads = true
|
||||
|
||||
guard case let .success(optimizedResult) = await mediaUploadingPreprocessor.processMedia(at: url),
|
||||
case let .video(_, _, optimizedVideoInfo) = optimizedResult else {
|
||||
XCTFail("Failed processing asset")
|
||||
return
|
||||
}
|
||||
|
||||
// Check optimised video info
|
||||
XCTAssertEqual(optimizedVideoInfo.mimetype, "video/mp4")
|
||||
XCTAssertEqual(optimizedVideoInfo.blurhash, "K7BDNJD*0L%#sl_2~C9ZE1")
|
||||
XCTAssertEqual(optimizedVideoInfo.size ?? 0, 6_520_897, accuracy: 100)
|
||||
XCTAssertEqual(optimizedVideoInfo.width, 360)
|
||||
XCTAssertEqual(optimizedVideoInfo.height, 640)
|
||||
XCTAssertEqual(optimizedVideoInfo.duration ?? 0, 30, accuracy: 100)
|
||||
}
|
||||
|
||||
func testLandscapeImageProcessing() async {
|
||||
@ -185,28 +231,10 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
||||
XCTAssertEqual(originalImage.size, convertedImage.size)
|
||||
|
||||
// Check that the GPS data has been stripped
|
||||
guard let imageSource = CGImageSourceCreateWithData(originalImageData as NSData, nil) else {
|
||||
XCTFail("Invalid test asset")
|
||||
return
|
||||
}
|
||||
|
||||
guard let originalMetadata: NSDictionary = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) else {
|
||||
XCTFail("Test asset is expected to contain metadata")
|
||||
return
|
||||
}
|
||||
|
||||
let originalMetadata = metadata(from: originalImageData)
|
||||
XCTAssertNotNil(originalMetadata.value(forKeyPath: "\(kCGImagePropertyGPSDictionary)"))
|
||||
|
||||
guard let convertedImageSource = CGImageSourceCreateWithData(convertedImageData as NSData, nil) else {
|
||||
XCTFail("Invalid converted asset")
|
||||
return
|
||||
}
|
||||
|
||||
guard let convertedMetadata: NSDictionary = CGImageSourceCopyPropertiesAtIndex(convertedImageSource, 0, nil) else {
|
||||
XCTFail("Test asset is expected to contain metadata")
|
||||
return
|
||||
}
|
||||
|
||||
let convertedMetadata = metadata(from: convertedImageData)
|
||||
XCTAssertNil(convertedMetadata.value(forKeyPath: "\(kCGImagePropertyGPSDictionary)"))
|
||||
|
||||
// Check that the thumbnail is generated correctly
|
||||
@ -223,5 +251,22 @@ final class MediaUploadingPreprocessorTests: XCTestCase {
|
||||
XCTAssert(thumbnail.size.width <= MediaUploadingPreprocessor.Constants.maximumThumbnailSize.height)
|
||||
XCTAssert(thumbnail.size.height <= MediaUploadingPreprocessor.Constants.maximumThumbnailSize.width)
|
||||
}
|
||||
|
||||
let thumbnailMetadata = metadata(from: thumbnailData)
|
||||
XCTAssertNil(thumbnailMetadata.value(forKeyPath: "\(kCGImagePropertyGPSDictionary)"))
|
||||
}
|
||||
|
||||
private func metadata(from imageData: Data) -> NSDictionary {
|
||||
guard let imageSource = CGImageSourceCreateWithData(imageData as NSData, nil) else {
|
||||
XCTFail("Invalid asset")
|
||||
return [:]
|
||||
}
|
||||
|
||||
guard let convertedMetadata: NSDictionary = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) else {
|
||||
XCTFail("Test asset is expected to contain metadata")
|
||||
return [:]
|
||||
}
|
||||
|
||||
return convertedMetadata
|
||||
}
|
||||
}
|
||||
|
@ -112,6 +112,7 @@ class RoomDetailsEditScreenViewModelTests: XCTestCase {
|
||||
userIndicatorController = UserIndicatorControllerMock.default
|
||||
viewModel = .init(roomProxy: JoinedRoomProxyMock(roomProxyConfiguration),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||
userIndicatorController: userIndicatorController)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user