mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-25 02:52:45 +02:00
+71
@@ -0,0 +1,71 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
/*
|
||||
Copyright 2021 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
final class SpaceCreationPostProcessCoordinator: Coordinator, Presentable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let parameters: SpaceCreationPostProcessCoordinatorParameters
|
||||
private let spaceCreationPostProcessHostingController: UIViewController
|
||||
private var spaceCreationPostProcessViewModel: SpaceCreationPostProcessViewModelProtocol
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
var callback: ((SpaceCreationPostProcessCoordinatorAction) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
init(parameters: SpaceCreationPostProcessCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
let viewModel = SpaceCreationPostProcessViewModel.makeSpaceCreationPostProcessViewModel(spaceCreationPostProcessService: SpaceCreationPostProcessService(session: parameters.session, creationParams: parameters.creationParams))
|
||||
let view = SpaceCreationPostProcess(viewModel: viewModel.context)
|
||||
.addDependency(AvatarService.instantiate(mediaManager: parameters.session.mediaManager))
|
||||
spaceCreationPostProcessViewModel = viewModel
|
||||
let hostingController = VectorHostingController(rootView: view)
|
||||
hostingController.hidesBackTitleWhenPushed = true
|
||||
spaceCreationPostProcessHostingController = hostingController
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
func start() {
|
||||
MXLog.debug("[SpaceCreationPostProcessCoordinator] did start.")
|
||||
spaceCreationPostProcessViewModel.completion = { [weak self] result in
|
||||
MXLog.debug("[SpaceCreationPostProcessCoordinator] SpaceCreationPostProcessViewModel did complete with result: \(result).")
|
||||
guard let self = self else { return }
|
||||
switch result {
|
||||
case .cancel:
|
||||
self.callback?(.cancel)
|
||||
case .done(let spaceId):
|
||||
self.callback?(.done(spaceId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
return self.spaceCreationPostProcessHostingController
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SpaceCreationPostProcessCoordinatorParameters {
|
||||
let session: MXSession
|
||||
let creationParams: SpaceCreationParameters
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum SpaceCreationPostProcessCoordinatorAction {
|
||||
case done(_ spaceId: String)
|
||||
case cancel
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum SpaceCreationPostProcessPresence {
|
||||
case online
|
||||
case idle
|
||||
case offline
|
||||
}
|
||||
|
||||
extension SpaceCreationPostProcessPresence {
|
||||
var title: String {
|
||||
switch self {
|
||||
case .online:
|
||||
return VectorL10n.roomParticipantsOnline
|
||||
case .idle:
|
||||
return VectorL10n.roomParticipantsIdle
|
||||
case .offline:
|
||||
return VectorL10n.roomParticipantsOffline
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SpaceCreationPostProcessPresence: CaseIterable { }
|
||||
|
||||
extension SpaceCreationPostProcessPresence: Identifiable {
|
||||
var id: Self { self }
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum SpaceCreationPostProcessStateAction {
|
||||
case updateTasks([SpaceCreationPostProcessTask])
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum SpaceCreationPostProcessTaskState: CaseIterable {
|
||||
static var allCases: [SpaceCreationPostProcessTaskState] = [.none, .started, .success, .failure]
|
||||
|
||||
case none
|
||||
case started
|
||||
case success
|
||||
case failure
|
||||
}
|
||||
|
||||
enum SpaceCreationPostProcessTaskType {
|
||||
case createSpace
|
||||
case uploadAvatar
|
||||
case createRoom(_ roomName: String)
|
||||
case addRooms
|
||||
case inviteUsersByEmail
|
||||
}
|
||||
|
||||
struct SpaceCreationPostProcessTask {
|
||||
let type: SpaceCreationPostProcessTaskType
|
||||
let title: String
|
||||
var state: SpaceCreationPostProcessTaskState
|
||||
var isFinished: Bool {
|
||||
return state == .failure || state == .success
|
||||
}
|
||||
var subTasks: [SpaceCreationPostProcessTask] = []
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum SpaceCreationPostProcessViewAction {
|
||||
case cancel
|
||||
case runTasks
|
||||
case retry
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum SpaceCreationPostProcessViewModelResult {
|
||||
case cancel
|
||||
case done(_ spaceId: String)
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct SpaceCreationPostProcessViewState: BindableState {
|
||||
var tasks: [SpaceCreationPostProcessTask]
|
||||
var isFinished: Bool
|
||||
var errorCount: Int
|
||||
}
|
||||
+330
@@ -0,0 +1,330 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import MatrixSDK
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
class SpaceCreationPostProcessService: SpaceCreationPostProcessServiceProtocol {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let creationParams: SpaceCreationParameters
|
||||
|
||||
private var tasks: [SpaceCreationPostProcessTask] = []
|
||||
private var currentTaskIndex = 0
|
||||
private var isRetry = false
|
||||
|
||||
private(set) var createdSpace: MXSpace? {
|
||||
didSet {
|
||||
createdSpaceId = createdSpace?.spaceId
|
||||
}
|
||||
}
|
||||
private(set) var createdSpaceId: String?
|
||||
private var createdRoomsByName: [String: MXRoom] = [:]
|
||||
|
||||
private var currentSubTaskIndex = 0
|
||||
|
||||
private var processingQueue = DispatchQueue(label: "org.matrix.sdk.MXSpace.processingQueue", attributes: .concurrent)
|
||||
|
||||
private lazy var stateEventBuilder: MXRoomInitialStateEventBuilder = {
|
||||
return MXRoomInitialStateEventBuilder()
|
||||
}()
|
||||
|
||||
private lazy var mediaUploader: MXMediaLoader = {
|
||||
return MXMediaManager.prepareUploader(withMatrixSession: session, initialRange: 0, andRange: 1.0)
|
||||
}()
|
||||
|
||||
// MARK: Public
|
||||
|
||||
private(set) var tasksSubject: CurrentValueSubject<[SpaceCreationPostProcessTask], Never>
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, creationParams: SpaceCreationParameters) {
|
||||
self.session = session
|
||||
self.creationParams = creationParams
|
||||
self.tasks = Self.tasks(with: creationParams)
|
||||
self.tasksSubject = CurrentValueSubject(tasks)
|
||||
}
|
||||
|
||||
deinit {
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func run() {
|
||||
self.isRetry = self.currentTaskIndex > 0
|
||||
self.currentTaskIndex = -1
|
||||
runNextTask()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private static func tasks(with creationParams: SpaceCreationParameters) -> [SpaceCreationPostProcessTask] {
|
||||
guard let spaceName = creationParams.name else {
|
||||
MXLog.error("[SpaceCreationPostProcessService] setupTasks: space name shouldn't be nil")
|
||||
return []
|
||||
}
|
||||
|
||||
var tasks = [SpaceCreationPostProcessTask(type: .createSpace, title: VectorL10n.spacesCreationPostProcessCreatingSpaceTask(spaceName), state: .none)]
|
||||
if creationParams.userSelectedAvatar != nil {
|
||||
tasks.append(SpaceCreationPostProcessTask(type: .uploadAvatar, title: VectorL10n.spacesCreationPostProcessUploadingAvatar, state: .none))
|
||||
}
|
||||
if creationParams.addedRoomIds.isEmpty {
|
||||
tasks.append(contentsOf: creationParams.newRooms.compactMap({ room in
|
||||
guard !room.name.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return SpaceCreationPostProcessTask(type: .createRoom(room.name), title: VectorL10n.spacesCreationPostProcessCreatingRoom(room.name), state: .none)
|
||||
}))
|
||||
} else {
|
||||
let subTasks = creationParams.addedRoomIds.map { roomId in
|
||||
SpaceCreationPostProcessTask(type: .addRooms, title: roomId, state: .none)
|
||||
}
|
||||
tasks.append(SpaceCreationPostProcessTask(type: .addRooms, title: VectorL10n.spacesCreationPostProcessAddingRooms("\(creationParams.addedRoomIds.count)"), state: .none, subTasks: subTasks))
|
||||
}
|
||||
|
||||
if creationParams.userIdInvites.isEmpty {
|
||||
let emailInviteCount = creationParams.userDefinedEmailInvites.count
|
||||
if emailInviteCount > 0 {
|
||||
let subTasks = creationParams.userDefinedEmailInvites.map { emailAddress in
|
||||
SpaceCreationPostProcessTask(type: .inviteUsersByEmail, title: emailAddress, state: .none)
|
||||
}
|
||||
|
||||
tasks.append(SpaceCreationPostProcessTask(type: .inviteUsersByEmail, title: VectorL10n.spacesCreationPostProcessInvitingUsers("\(creationParams.userDefinedEmailInvites.count)"), state: .none, subTasks: subTasks))
|
||||
}
|
||||
}
|
||||
|
||||
return tasks
|
||||
}
|
||||
|
||||
private func runNextTask() {
|
||||
currentTaskIndex += 1
|
||||
guard currentTaskIndex < tasks.count else {
|
||||
return
|
||||
}
|
||||
|
||||
let task = tasks[currentTaskIndex]
|
||||
|
||||
guard !task.isFinished || task.state == .failure else {
|
||||
runNextTask()
|
||||
return
|
||||
}
|
||||
|
||||
// fakeTaskExecution(task: task)
|
||||
// return
|
||||
|
||||
switch task.type {
|
||||
case .createSpace:
|
||||
createSpace(andUpdate: task)
|
||||
case .uploadAvatar:
|
||||
uploadAvatar(andUpdate: task)
|
||||
case .addRooms:
|
||||
addRooms(andUpdate: task)
|
||||
case .createRoom(let roomName):
|
||||
if let room = createdRoomsByName[roomName] {
|
||||
addToSpace(room: room)
|
||||
} else {
|
||||
createRoom(withName: roomName, andUpdate: task)
|
||||
}
|
||||
case .inviteUsersByEmail:
|
||||
inviteUsersByEmail(andUpdate: task)
|
||||
}
|
||||
}
|
||||
|
||||
private func createSpace(andUpdate task: SpaceCreationPostProcessTask) {
|
||||
let parameters = MXSpaceCreationParameters()
|
||||
parameters.name = creationParams.name
|
||||
parameters.topic = creationParams.topic
|
||||
parameters.preset = creationParams.isPublic ? kMXRoomPresetPublicChat : kMXRoomPresetPrivateChat
|
||||
parameters.visibility = creationParams.isPublic ? kMXRoomDirectoryVisibilityPublic : kMXRoomDirectoryVisibilityPrivate
|
||||
if creationParams.isPublic {
|
||||
var alias = creationParams.address
|
||||
if let userDefinedAlias = creationParams.userDefinedAddress {
|
||||
alias = userDefinedAlias
|
||||
}
|
||||
parameters.roomAlias = alias?.fullLocalAlias(with: session)
|
||||
let guestAccessStateEvent = self.stateEventBuilder.buildGuestAccessEvent(withAccess: .canJoin)
|
||||
parameters.addOrUpdateInitialStateEvent(guestAccessStateEvent)
|
||||
let historyVisibilityStateEvent = self.stateEventBuilder.buildHistoryVisibilityEvent(withVisibility: .worldReadable)
|
||||
parameters.addOrUpdateInitialStateEvent(historyVisibilityStateEvent)
|
||||
}
|
||||
parameters.inviteArray = creationParams.userIdInvites
|
||||
|
||||
updateCurrentTask(with: .started)
|
||||
session.spaceService.createSpace(with: parameters) { [weak self] response in
|
||||
guard let self = self else { return }
|
||||
if response.isFailure {
|
||||
self.updateCurrentTask(with: .failure)
|
||||
} else {
|
||||
self.updateCurrentTask(with: .success)
|
||||
self.createdSpace = response.value
|
||||
self.runNextTask()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func uploadAvatar(andUpdate task: SpaceCreationPostProcessTask) {
|
||||
self.updateCurrentTask(with: .started)
|
||||
|
||||
guard let avatar = creationParams.userSelectedAvatar, let spaceRoom = self.createdSpace?.room else {
|
||||
self.updateCurrentTask(with: .success)
|
||||
self.runNextTask()
|
||||
return
|
||||
}
|
||||
|
||||
let avatarUp = MXKTools.forceImageOrientationUp(avatar)
|
||||
|
||||
mediaUploader.uploadData(avatarUp?.jpegData(compressionQuality: 0.5), filename: nil, mimeType: "image/jpeg",
|
||||
success: { [weak self] (urlString) in
|
||||
guard let self = self else { return }
|
||||
guard let urlString = urlString else { return }
|
||||
guard let url = URL(string: urlString) else { return }
|
||||
|
||||
self.setAvatar(ofRoom: spaceRoom, withURL: url, andUpdate: task)
|
||||
},
|
||||
failure: { [weak self] (error) in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.updateCurrentTask(with: .failure)
|
||||
self.runNextTask()
|
||||
})
|
||||
}
|
||||
|
||||
private func setAvatar(ofRoom room: MXRoom, withURL url: URL, andUpdate task: SpaceCreationPostProcessTask) {
|
||||
room.setAvatar(url: url) { [weak self] (response) in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.updateCurrentTask(with: response.isSuccess ? .success: .failure)
|
||||
self.runNextTask()
|
||||
}
|
||||
}
|
||||
|
||||
private func createRoom(withName roomName: String, andUpdate task: SpaceCreationPostProcessTask) {
|
||||
let parameters = MXRoomCreationParameters()
|
||||
parameters.name = roomName
|
||||
parameters.visibility = creationParams.isPublic ? kMXRoomDirectoryVisibilityPublic : kMXRoomDirectoryVisibilityPrivate
|
||||
parameters.preset = creationParams.isPublic ? kMXRoomPresetPublicChat : kMXRoomPresetPrivateChat
|
||||
|
||||
updateCurrentTask(with: .started)
|
||||
session.createRoom(parameters: parameters) { [weak self] response in
|
||||
guard let self = self else { return }
|
||||
|
||||
guard response.isSuccess, let createdRoom = response.value else {
|
||||
self.updateCurrentTask(with: .failure)
|
||||
self.runNextTask()
|
||||
return
|
||||
}
|
||||
|
||||
self.createdRoomsByName[roomName] = createdRoom
|
||||
self.addToSpace(room: createdRoom)
|
||||
}
|
||||
}
|
||||
|
||||
private func addToSpace(room: MXRoom) {
|
||||
self.createdSpace?.addChild(roomId: room.matrixItemId, completion: { response in
|
||||
self.updateCurrentTask(with: response.isFailure ? .failure : .success)
|
||||
self.runNextTask()
|
||||
})
|
||||
}
|
||||
|
||||
private func addRooms(andUpdate task: SpaceCreationPostProcessTask) {
|
||||
updateCurrentTask(with: .started)
|
||||
currentSubTaskIndex = -1
|
||||
addNextExistingRoom()
|
||||
}
|
||||
|
||||
private func inviteUsersByEmail(andUpdate task: SpaceCreationPostProcessTask) {
|
||||
updateCurrentTask(with: .started)
|
||||
currentSubTaskIndex = -1
|
||||
inviteNextUserByEmail()
|
||||
}
|
||||
|
||||
private func inviteNextUserByEmail() {
|
||||
guard let createdSpace = self.createdSpace, let room = createdSpace.room else {
|
||||
updateCurrentTask(with: .failure)
|
||||
runNextTask()
|
||||
return
|
||||
}
|
||||
|
||||
currentSubTaskIndex += 1
|
||||
|
||||
guard currentSubTaskIndex < tasks[currentTaskIndex].subTasks.count else {
|
||||
let isSuccess = tasks[currentTaskIndex].subTasks.reduce(true, { $0 && $1.state == .success })
|
||||
updateCurrentTask(with: isSuccess ? .success : .failure)
|
||||
runNextTask()
|
||||
return
|
||||
}
|
||||
|
||||
room.invite(.email(creationParams.emailInvites[currentSubTaskIndex])) { [weak self] response in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.tasks[self.currentTaskIndex].subTasks[self.currentSubTaskIndex].state = response.isSuccess ? .success : .failure
|
||||
self.inviteNextUserByEmail()
|
||||
}
|
||||
}
|
||||
|
||||
private func addNextExistingRoom() {
|
||||
guard let createdSpace = self.createdSpace else {
|
||||
updateCurrentTask(with: .failure)
|
||||
runNextTask()
|
||||
return
|
||||
}
|
||||
|
||||
currentSubTaskIndex += 1
|
||||
|
||||
guard currentSubTaskIndex < tasks[currentTaskIndex].subTasks.count else {
|
||||
let isSuccess = tasks[currentTaskIndex].subTasks.reduce(true, { $0 && $1.state == .success })
|
||||
updateCurrentTask(with: isSuccess ? .success : .failure)
|
||||
runNextTask()
|
||||
return
|
||||
}
|
||||
|
||||
createdSpace.addChild(roomId: creationParams.addedRoomIds[currentSubTaskIndex], completion: { [weak self] response in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.tasks[self.currentTaskIndex].subTasks[self.currentSubTaskIndex].state = response.isSuccess ? .success : .failure
|
||||
self.addNextExistingRoom()
|
||||
})
|
||||
}
|
||||
|
||||
private func fakeTaskExecution(task: SpaceCreationPostProcessTask) {
|
||||
updateCurrentTask(with: .started)
|
||||
processingQueue.async {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
||||
self.updateCurrentTask(with: .success)
|
||||
self.runNextTask()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateCurrentTask(with state: SpaceCreationPostProcessTaskState) {
|
||||
guard currentTaskIndex < tasks.count else {
|
||||
return
|
||||
}
|
||||
|
||||
tasks[currentTaskIndex].state = state
|
||||
self.tasksSubject.send(tasks)
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
/// Using an enum for the screen allows you define the different state cases with
|
||||
/// the relevant associated data for each case.
|
||||
@available(iOS 14.0, *)
|
||||
enum MockSpaceCreationPostProcessScreenState: MockScreenState {
|
||||
static var screenStates: [MockScreenState] = [MockSpaceCreationPostProcessScreenState.tasks]
|
||||
|
||||
|
||||
// A case for each state you want to represent
|
||||
// with specific, minimal associated data that will allow you
|
||||
// mock that screen.
|
||||
case tasks
|
||||
|
||||
/// The associated screen
|
||||
var screenType: Any.Type {
|
||||
SpaceCreationPostProcess.self
|
||||
}
|
||||
|
||||
/// Generate the view struct for the screen state.
|
||||
var screenView: ([Any], AnyView) {
|
||||
let service: MockSpaceCreationPostProcessService
|
||||
switch self {
|
||||
case .tasks:
|
||||
service = MockSpaceCreationPostProcessService()
|
||||
}
|
||||
let viewModel = SpaceCreationPostProcessViewModel.makeSpaceCreationPostProcessViewModel(spaceCreationPostProcessService: service)
|
||||
|
||||
// can simulate service and viewModel actions here if needs be.
|
||||
|
||||
return (
|
||||
[service, viewModel],
|
||||
AnyView(SpaceCreationPostProcess(viewModel: viewModel.context)
|
||||
.addDependency(MockAvatarService.example))
|
||||
)
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
class MockSpaceCreationPostProcessService: SpaceCreationPostProcessServiceProtocol {
|
||||
|
||||
var tasksSubject: CurrentValueSubject<[SpaceCreationPostProcessTask], Never>
|
||||
private(set) var createdSpaceId: String?
|
||||
|
||||
init(
|
||||
tasks: [SpaceCreationPostProcessTask] = [
|
||||
SpaceCreationPostProcessTask(type: .createSpace, title: "Space creation", state: .success),
|
||||
SpaceCreationPostProcessTask(type: .createRoom("Room#1"), title: "Room#1 creation", state: .failure),
|
||||
SpaceCreationPostProcessTask(type: .createRoom("Room#2"), title: "Room#2 creation", state: .started),
|
||||
SpaceCreationPostProcessTask(type: .createRoom("Room#3"), title: "Room#3 creation", state: .none)
|
||||
]
|
||||
) {
|
||||
self.tasksSubject = CurrentValueSubject<[SpaceCreationPostProcessTask], Never>(tasks)
|
||||
}
|
||||
|
||||
func simulateUpdate(presence: SpaceCreationPostProcessPresence) {
|
||||
self.tasksSubject.send([
|
||||
SpaceCreationPostProcessTask(type: .createSpace, title: "Space creation", state: .success),
|
||||
SpaceCreationPostProcessTask(type: .createRoom("Room#1"), title: "Room#1 creation", state: .failure),
|
||||
SpaceCreationPostProcessTask(type: .createRoom("Room#2"), title: "Room#2 creation", state: .success),
|
||||
SpaceCreationPostProcessTask(type: .createRoom("Room#3"), title: "Room#3 creation", state: .started)
|
||||
])
|
||||
}
|
||||
|
||||
func run() {
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
protocol SpaceCreationPostProcessServiceProtocol: AnyObject {
|
||||
var tasksSubject: CurrentValueSubject<[SpaceCreationPostProcessTask], Never> { get }
|
||||
var createdSpaceId: String? { get }
|
||||
func run()
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import RiotSwiftUI
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
class SpaceCreationPostProcessUITests: MockScreenTest {
|
||||
|
||||
override class var screenType: MockScreenState.Type {
|
||||
return MockSpaceCreationPostProcessScreenState.self
|
||||
}
|
||||
|
||||
override class func createTest() -> MockScreenTest {
|
||||
return SpaceCreationPostProcessUITests(selector: #selector(verifySpaceCreationPostProcessScreen))
|
||||
}
|
||||
|
||||
func verifySpaceCreationPostProcessScreen() throws {
|
||||
guard let screenState = screenState as? MockSpaceCreationPostProcessScreenState else { fatalError("no screen") }
|
||||
switch screenState {
|
||||
case .presence(let presence):
|
||||
verifySpaceCreationPostProcessPresence(presence: presence)
|
||||
case .longDisplayName(let name):
|
||||
verifySpaceCreationPostProcessLongName(name: name)
|
||||
}
|
||||
}
|
||||
|
||||
func verifySpaceCreationPostProcessPresence(presence: SpaceCreationPostProcessPresence) {
|
||||
let presenceText = app.staticTexts["presenceText"]
|
||||
XCTAssert(presenceText.exists)
|
||||
XCTAssertEqual(presenceText.label, presence.title)
|
||||
}
|
||||
|
||||
func verifySpaceCreationPostProcessLongName(name: String) {
|
||||
let displayNameText = app.staticTexts["displayNameText"]
|
||||
XCTAssert(displayNameText.exists)
|
||||
XCTAssertEqual(displayNameText.label, name)
|
||||
}
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Combine
|
||||
|
||||
@testable import RiotSwiftUI
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
class SpaceCreationPostProcessViewModelTests: XCTestCase {
|
||||
private enum Constants {
|
||||
static let presenceInitialValue: SpaceCreationPostProcessPresence = .offline
|
||||
static let displayName = "Alice"
|
||||
}
|
||||
var service: MockSpaceCreationPostProcessService!
|
||||
var viewModel: SpaceCreationPostProcessViewModelProtocol!
|
||||
var context: SpaceCreationPostProcessViewModelType.Context!
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
override func setUpWithError() throws {
|
||||
service = MockSpaceCreationPostProcessService(displayName: Constants.displayName, presence: Constants.presenceInitialValue)
|
||||
viewModel = SpaceCreationPostProcessViewModel.makeSpaceCreationPostProcessViewModel(spaceCreationPostProcessService: service)
|
||||
context = viewModel.context
|
||||
}
|
||||
|
||||
func testInitialState() {
|
||||
XCTAssertEqual(context.viewState.displayName, Constants.displayName)
|
||||
XCTAssertEqual(context.viewState.presence, Constants.presenceInitialValue)
|
||||
}
|
||||
|
||||
func testFirstPresenceReceived() throws {
|
||||
let presencePublisher = context.$viewState.map(\.presence).removeDuplicates().collect(1).first()
|
||||
XCTAssertEqual(try xcAwait(presencePublisher), [Constants.presenceInitialValue])
|
||||
}
|
||||
|
||||
func testPresenceUpdatesReceived() throws {
|
||||
let presencePublisher = context.$viewState.map(\.presence).removeDuplicates().collect(3).first()
|
||||
let awaitDeferred = xcAwaitDeferred(presencePublisher)
|
||||
let newPresenceValue1: SpaceCreationPostProcessPresence = .online
|
||||
let newPresenceValue2: SpaceCreationPostProcessPresence = .idle
|
||||
service.simulateUpdate(presence: newPresenceValue1)
|
||||
service.simulateUpdate(presence: newPresenceValue2)
|
||||
XCTAssertEqual(try awaitDeferred(), [Constants.presenceInitialValue, newPresenceValue1, newPresenceValue2])
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
struct SpaceCreationPostProcess: View {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
|
||||
// MARK: Public
|
||||
|
||||
@ObservedObject var viewModel: SpaceCreationPostProcessViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Spacer()
|
||||
VStack(spacing: 13) {
|
||||
ProgressView()
|
||||
.isHidden(viewModel.viewState.isFinished)
|
||||
.scaleEffect(1.5, anchor: .center)
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: theme.colors.secondaryContent))
|
||||
Text(VectorL10n.spacesCreationPostProcessCreatingSpace)
|
||||
.font(theme.fonts.calloutSB)
|
||||
.foregroundColor(theme.colors.secondaryContent)
|
||||
}
|
||||
Spacer()
|
||||
VStack(alignment: .leading, spacing: 11) {
|
||||
ForEach(viewModel.viewState.tasks.indices) { index in
|
||||
SpaceCreationPostProcessItem(title: viewModel.viewState.tasks[index].title, state: viewModel.viewState.tasks[index].state)
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
HStack {
|
||||
ThemableButton(icon: nil, title: VectorL10n.done) {
|
||||
viewModel.send(viewAction: .cancel)
|
||||
}
|
||||
ThemableButton(icon: nil, title: VectorL10n.retry) {
|
||||
viewModel.send(viewAction: .retry)
|
||||
}
|
||||
}
|
||||
.isHidden(!viewModel.viewState.isFinished || viewModel.viewState.errorCount == 0)
|
||||
}
|
||||
.animation(.easeIn(duration: 0.2), value: viewModel.viewState.errorCount)
|
||||
.padding(EdgeInsets(top: 24, leading: 16, bottom: 24, trailing: 16))
|
||||
.navigationBarHidden(true)
|
||||
.background(theme.colors.background)
|
||||
.frame(maxHeight: .infinity)
|
||||
.onAppear() {
|
||||
viewModel.send(viewAction: .runTasks)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
struct SpaceCreationPostProcess_Previews: PreviewProvider {
|
||||
static let stateRenderer = MockSpaceCreationPostProcessScreenState.stateRenderer
|
||||
static var previews: some View {
|
||||
stateRenderer.screenGroup(addNavigation: true).theme(.light).preferredColorScheme(.light)
|
||||
stateRenderer.screenGroup(addNavigation: true).theme(.dark).preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
struct SpaceCreationPostProcessItem: View {
|
||||
// MARK: - Properties
|
||||
|
||||
let title: String
|
||||
let state: SpaceCreationPostProcessTaskState
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@Environment(\.theme) private var theme: ThemeSwiftUI
|
||||
private var tintColor: Color {
|
||||
switch state {
|
||||
case .none:
|
||||
return theme.colors.quinaryContent
|
||||
case .started:
|
||||
return theme.colors.primaryContent
|
||||
case .success:
|
||||
return theme.colors.tertiaryContent
|
||||
case .failure:
|
||||
return theme.colors.alert
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
switch state {
|
||||
case .none:
|
||||
Image(systemName: "circle").renderingMode(.template).foregroundColor(theme.colors.tertiaryContent)
|
||||
case .started:
|
||||
ProgressView().progressViewStyle(CircularProgressViewStyle(tint: theme.colors.secondaryContent)).scaleEffect(0.9, anchor: .center)
|
||||
Spacer().frame(width: 6)
|
||||
case .success:
|
||||
Image(systemName: "checkmark.circle.fill").renderingMode(.template).foregroundColor(theme.colors.tertiaryContent)
|
||||
case .failure:
|
||||
Image(systemName: "exclamationmark.circle.fill").renderingMode(.template).foregroundColor(theme.colors.alert)
|
||||
}
|
||||
Text(title)
|
||||
.font(theme.fonts.callout)
|
||||
.foregroundColor(state == .started ? theme.colors.primaryContent : theme.colors.tertiaryContent)
|
||||
}
|
||||
.opacity(state == .none ? 0.5 : 1)
|
||||
.animation(.easeOut(duration: 0.2), value: state)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
struct SpaceCreationPostProcessItem_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
SpaceCreationPostProcessItem(title: "failed task", state: .failure)
|
||||
SpaceCreationPostProcessItem(title: "not started", state: .none)
|
||||
SpaceCreationPostProcessItem(title: "on going task ", state: .started)
|
||||
SpaceCreationPostProcessItem(title: "succesful task", state: .success)
|
||||
}
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
SpaceCreationPostProcessItem(title: "failed task", state: .failure)
|
||||
SpaceCreationPostProcessItem(title: "not started", state: .none)
|
||||
SpaceCreationPostProcessItem(title: "on going task ", state: .started)
|
||||
SpaceCreationPostProcessItem(title: "succesful task", state: .success)
|
||||
}.theme(.dark).preferredColorScheme(.dark)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
|
||||
|
||||
@available(iOS 14, *)
|
||||
typealias SpaceCreationPostProcessViewModelType = StateStoreViewModel<SpaceCreationPostProcessViewState,
|
||||
SpaceCreationPostProcessStateAction,
|
||||
SpaceCreationPostProcessViewAction>
|
||||
@available(iOS 14, *)
|
||||
class SpaceCreationPostProcessViewModel: SpaceCreationPostProcessViewModelType, SpaceCreationPostProcessViewModelProtocol {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let spaceCreationPostProcessService: SpaceCreationPostProcessServiceProtocol
|
||||
private var updateNotificationObserver: Any?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var completion: ((SpaceCreationPostProcessViewModelResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
static func makeSpaceCreationPostProcessViewModel(spaceCreationPostProcessService: SpaceCreationPostProcessServiceProtocol) -> SpaceCreationPostProcessViewModelProtocol {
|
||||
return SpaceCreationPostProcessViewModel(spaceCreationPostProcessService: spaceCreationPostProcessService)
|
||||
}
|
||||
|
||||
private init(spaceCreationPostProcessService: SpaceCreationPostProcessServiceProtocol) {
|
||||
self.spaceCreationPostProcessService = spaceCreationPostProcessService
|
||||
super.init(initialViewState: Self.defaultState(spaceCreationPostProcessService: spaceCreationPostProcessService))
|
||||
setupTasksObserving()
|
||||
}
|
||||
|
||||
private static func defaultState(spaceCreationPostProcessService: SpaceCreationPostProcessServiceProtocol) -> SpaceCreationPostProcessViewState {
|
||||
let tasks = spaceCreationPostProcessService.tasksSubject.value
|
||||
return SpaceCreationPostProcessViewState(
|
||||
tasks: tasks,
|
||||
isFinished: tasks.first?.state == .failure || tasks.reduce(true, { result, task in result && task.isFinished }),
|
||||
errorCount: tasks.reduce(0, { result, task in result + (task.state == .failure ? 1 : 0) })
|
||||
)
|
||||
}
|
||||
|
||||
private func setupTasksObserving() {
|
||||
let tasksUpdatePublisher = spaceCreationPostProcessService.tasksSubject
|
||||
.map(SpaceCreationPostProcessStateAction.updateTasks)
|
||||
.eraseToAnyPublisher()
|
||||
dispatch(actionPublisher: tasksUpdatePublisher)
|
||||
updateNotificationObserver = NotificationCenter.default.addObserver(forName: SpaceCreationPostProcessViewModel.didUpdate, object: nil, queue: OperationQueue.main) { [weak self] notification in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let state = notification.userInfo?[SpaceCreationPostProcessViewModel.newStateKey] as? SpaceCreationPostProcessViewState else {
|
||||
return
|
||||
}
|
||||
|
||||
if state.isFinished && state.errorCount == 0 {
|
||||
guard let spaceId = self.spaceCreationPostProcessService.createdSpaceId else {
|
||||
self.cancel()
|
||||
return
|
||||
}
|
||||
|
||||
self.done(spaceId: spaceId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let updateNotificationObserver = self.updateNotificationObserver {
|
||||
NotificationCenter.default.removeObserver(updateNotificationObserver)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: SpaceCreationPostProcessViewAction) {
|
||||
switch viewAction {
|
||||
case .cancel:
|
||||
cancel()
|
||||
case .runTasks:
|
||||
runTasks()
|
||||
case .retry:
|
||||
runTasks()
|
||||
}
|
||||
}
|
||||
|
||||
override class func reducer(state: inout SpaceCreationPostProcessViewState, action: SpaceCreationPostProcessStateAction) {
|
||||
switch action {
|
||||
case .updateTasks(let tasks):
|
||||
state.tasks = tasks
|
||||
state.isFinished = tasks.first?.state == .failure || tasks.reduce(true, { result, task in result && task.isFinished })
|
||||
state.errorCount = tasks.reduce(0, { result, task in result + (task.state == .failure ? 1 : 0) })
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(name: SpaceCreationPostProcessViewModel.didUpdate, object: nil, userInfo: [SpaceCreationPostProcessViewModel.newStateKey : state])
|
||||
|
||||
UILog.debug("[SpaceCreationPostProcessViewModel] reducer with action \(action) produced state: \(state)")
|
||||
}
|
||||
|
||||
private func done(spaceId: String) {
|
||||
completion?(.done(spaceId))
|
||||
}
|
||||
|
||||
private func cancel() {
|
||||
completion?(.cancel)
|
||||
}
|
||||
|
||||
private func runTasks() {
|
||||
spaceCreationPostProcessService.run()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MXSpaceService notification constants
|
||||
@available(iOS 14, *)
|
||||
extension SpaceCreationPostProcessViewModel {
|
||||
/// Posted once the process is finished
|
||||
public static let didUpdate = Notification.Name("SpaceCreationPostProcessViewModelDidUpdate")
|
||||
|
||||
public static let newStateKey = "newState"
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// File created from SimpleUserProfileExample
|
||||
// $ createScreen.sh Spaces/SpaceCreation/SpaceCreationPostProcess SpaceCreationPostProcess
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol SpaceCreationPostProcessViewModelProtocol {
|
||||
|
||||
var completion: ((SpaceCreationPostProcessViewModelResult) -> Void)? { get set }
|
||||
@available(iOS 14, *)
|
||||
static func makeSpaceCreationPostProcessViewModel(spaceCreationPostProcessService: SpaceCreationPostProcessServiceProtocol) -> SpaceCreationPostProcessViewModelProtocol
|
||||
@available(iOS 14, *)
|
||||
var context: SpaceCreationPostProcessViewModelType.Context { get }
|
||||
}
|
||||
Reference in New Issue
Block a user