Flescio/add room image (#961)

* image media upload for room creation

* fix remove duplicate logic

* use Async Image, add Focusable Fields in create room

* remove alert message which doesn't have a copy

* add remove duplicates on crete room image

* add button style for preventing undesired touches

* add changelog, add error case for file too large

* Fix iPad sheet presentation

* add error media preprocess

* dismissing focus on image picker
This commit is contained in:
Flescio
2023-05-30 14:55:29 +02:00
committed by GitHub
parent eba0fe5ce3
commit 03ba211e6c
11 changed files with 255 additions and 32 deletions
@@ -20,7 +20,7 @@ import SwiftUI
struct StartChatScreenCoordinatorParameters {
let userSession: UserSessionProtocol
weak var userIndicatorController: UserIndicatorControllerProtocol?
let navigationStackCoordinator: NavigationStackCoordinator?
let navigationStackCoordinator: NavigationStackCoordinator
let userDiscoveryService: UserDiscoveryServiceProtocol
}
@@ -45,6 +45,14 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
selectedUsers.asCurrentValuePublisher()
}
private var navigationStackCoordinator: NavigationStackCoordinator {
parameters.navigationStackCoordinator
}
private var userIndicatorController: UserIndicatorControllerProtocol? {
parameters.userIndicatorController
}
var actions: AnyPublisher<StartChatScreenCoordinatorAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
@@ -60,12 +68,12 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
guard let self else { return }
switch action {
case .close:
self.actionsSubject.send(.close)
actionsSubject.send(.close)
case .createRoom:
// before creating a room we select the users we would like to invite in that room
self.presentInviteUsersScreen()
presentInviteUsersScreen()
case .openRoom(let identifier):
self.actionsSubject.send(.openRoom(withIdentifier: identifier))
actionsSubject.send(.openRoom(withIdentifier: identifier))
}
}
.store(in: &cancellables)
@@ -99,7 +107,7 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
}
.store(in: &cancellables)
parameters.navigationStackCoordinator?.push(coordinator) { [weak self] in
navigationStackCoordinator.push(coordinator) { [weak self] in
self?.createRoomParameters.send(.init())
self?.selectedUsers.send([])
}
@@ -107,27 +115,71 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
private func openCreateRoomScreen() {
let createParameters = CreateRoomCoordinatorParameters(userSession: parameters.userSession,
userIndicatorController: parameters.userIndicatorController,
userIndicatorController: userIndicatorController,
createRoomParameters: createRoomParametersPublisher,
selectedUsers: selectedUsersPublisher)
let coordinator = CreateRoomCoordinator(parameters: createParameters)
coordinator.actions.sink { [weak self] result in
guard let self else { return }
switch result {
case .deselectUser(let user):
self?.toggleUser(user)
self.toggleUser(user)
case .updateDetails(let details):
self?.createRoomParameters.send(details)
self.createRoomParameters.send(details)
case .openRoom(let identifier):
self?.actionsSubject.send(.openRoom(withIdentifier: identifier))
self.actionsSubject.send(.openRoom(withIdentifier: identifier))
case .displayMediaPickerWithSource(let source):
self.displayMediaPickerWithSource(source)
case .removeImage:
var parameters = self.createRoomParameters.value
parameters.avatarImageMedia = nil
self.createRoomParameters.send(parameters)
}
}
.store(in: &cancellables)
parameters.navigationStackCoordinator?.push(coordinator)
navigationStackCoordinator.push(coordinator)
}
// MARK: - Private
let mediaUploadingPreprocessor = MediaUploadingPreprocessor()
private func displayMediaPickerWithSource(_ source: MediaPickerScreenSource) {
let stackCoordinator = NavigationStackCoordinator()
let userIndicatorController = UserIndicatorController(rootCoordinator: stackCoordinator)
let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: userIndicatorController, source: source) { [weak self] action in
guard let self else { return }
switch action {
case .cancel:
navigationStackCoordinator.setSheetCoordinator(nil)
case .selectMediaAtURL(let url):
processAvatar(from: url)
}
}
stackCoordinator.setRootCoordinator(mediaPickerCoordinator)
navigationStackCoordinator.setSheetCoordinator(userIndicatorController)
}
private func processAvatar(from url: URL) {
navigationStackCoordinator.setSheetCoordinator(nil)
showLoadingIndicator()
Task { [weak self] in
guard let self else { return }
do {
let media = try await mediaUploadingPreprocessor.processMedia(at: url).get()
var parameters = createRoomParameters.value
parameters.avatarImageMedia = media
createRoomParameters.send(parameters)
} catch {
userIndicatorController?.alertInfo = AlertInfo(id: .init(), title: L10n.commonError, message: L10n.errorUnknown)
}
hideLoadingIndicator()
}
}
private func toggleUser(_ user: UserProfileProxy) {
var selectedUsers = selectedUsers.value
if let index = selectedUsers.firstIndex(where: { $0.userID == user.userID }) {
@@ -137,4 +189,19 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
}
self.selectedUsers.send(selectedUsers)
}
// MARK: Loading indicator
private static let loadingIndicatorIdentifier = "StartChatCoordinatorLoading"
private func showLoadingIndicator() {
userIndicatorController?.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
type: .modal,
title: L10n.commonLoading,
persistent: true))
}
private func hideLoadingIndicator() {
userIndicatorController?.retractIndicatorWithId(Self.loadingIndicatorIdentifier)
}
}