Configured and applied SwiftFormat

This commit is contained in:
Stefan Ceriu
2022-09-27 10:17:22 +03:00
committed by Stefan Ceriu
parent ff2e6ddfa7
commit 43c28d23b7
663 changed files with 2329 additions and 2840 deletions
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,15 +14,14 @@
// limitations under the License.
//
import SwiftUI
import Combine
import SwiftUI
typealias AllChatsOnboardingViewModelType = StateStoreViewModel<AllChatsOnboardingViewState,
Never,
AllChatsOnboardingViewAction>
Never,
AllChatsOnboardingViewAction>
class AllChatsOnboardingViewModel: AllChatsOnboardingViewModelType, AllChatsOnboardingViewModelProtocol {
// MARK: - Properties
// MARK: Private
@@ -34,7 +33,7 @@ class AllChatsOnboardingViewModel: AllChatsOnboardingViewModelType, AllChatsOnbo
// MARK: - Setup
static func makeAllChatsOnboardingViewModel() -> AllChatsOnboardingViewModelProtocol {
return AllChatsOnboardingViewModel()
AllChatsOnboardingViewModel()
}
private init() {
@@ -42,7 +41,7 @@ class AllChatsOnboardingViewModel: AllChatsOnboardingViewModelType, AllChatsOnbo
}
private static func defaultState() -> AllChatsOnboardingViewState {
return AllChatsOnboardingViewState(pages: [
AllChatsOnboardingViewState(pages: [
AllChatsOnboardingPageData(image: Asset.Images.allChatsOnboarding1.image,
title: VectorL10n.allChatsOnboardingPageTitle1,
message: VectorL10n.allChatsOnboardingPageMessage1),
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import Foundation
protocol AllChatsOnboardingViewModelProtocol {
var completion: ((AllChatsOnboardingViewModelResult) -> Void)? { get set }
static func makeAllChatsOnboardingViewModel() -> AllChatsOnboardingViewModelProtocol
var context: AllChatsOnboardingViewModelType.Context { get }
@@ -14,12 +14,11 @@
// limitations under the License.
//
import SwiftUI
import CommonKit
import SwiftUI
/// All Chats onboarding screen
final class AllChatsOnboardingCoordinator: NSObject, Coordinator, Presentable {
// MARK: - Properties
// MARK: Private
@@ -42,8 +41,8 @@ final class AllChatsOnboardingCoordinator: NSObject, Coordinator, Presentable {
let viewModel = AllChatsOnboardingViewModel.makeAllChatsOnboardingViewModel()
let view = AllChatsOnboarding(viewModel: viewModel.context)
self.viewModel = viewModel
self.hostingController = VectorHostingController(rootView: view)
self.indicatorPresenter = UserIndicatorTypePresenter(presentingViewController: hostingController)
hostingController = VectorHostingController(rootView: view)
indicatorPresenter = UserIndicatorTypePresenter(presentingViewController: hostingController)
super.init()
@@ -65,7 +64,7 @@ final class AllChatsOnboardingCoordinator: NSObject, Coordinator, Presentable {
}
func toPresentable() -> UIViewController {
return self.hostingController
hostingController
}
// MARK: - Private
@@ -87,9 +86,7 @@ final class AllChatsOnboardingCoordinator: NSObject, Coordinator, Presentable {
// MARK: - UIAdaptivePresentationControllerDelegate
extension AllChatsOnboardingCoordinator: UIAdaptivePresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
completion?()
}
}
@@ -26,7 +26,6 @@ import Foundation
/// Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator.
@objcMembers
final class AllChatsOnboardingCoordinatorBridgePresenter: NSObject {
// MARK: - Properties
// MARK: Private
@@ -53,7 +52,7 @@ final class AllChatsOnboardingCoordinatorBridgePresenter: NSObject {
}
func dismiss(animated: Bool, completion: (() -> Void)?) {
guard let coordinator = self.coordinator else {
guard let coordinator = coordinator else {
return
}
coordinator.toPresentable().dismiss(animated: animated) {
@@ -62,4 +61,3 @@ final class AllChatsOnboardingCoordinatorBridgePresenter: NSObject {
}
}
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct AllChatsOnboarding: View {
// MARK: - Properties
// MARK: Private
@@ -36,12 +35,12 @@ struct AllChatsOnboarding: View {
.foregroundColor(theme.colors.primaryContent)
.padding()
TabView(selection: $selectedTab) {
ForEach(viewModel.viewState.pages.indices) { index in
ForEach(viewModel.viewState.pages.indices, id: \.self) { index in
let page = viewModel.viewState.pages[index]
AllChatsOnboardingPage(image: page.image,
title: page.title,
message: page.message)
.tag(index)
.tag(index)
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .automatic))
@@ -61,7 +60,7 @@ struct AllChatsOnboarding: View {
// MARK: - Private
private func onCallToAction() {
if (selectedTab == viewModel.viewState.pages.count - 1) {
if selectedTab == viewModel.viewState.pages.count - 1 {
viewModel.send(viewAction: .cancel)
} else {
withAnimation {
@@ -1,4 +1,4 @@
//
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct AllChatsOnboardingPage: View {
// MARK: - Properties
let image: UIImage
@@ -55,7 +54,7 @@ struct AllChatsOnboardingPage_Previews: PreviewProvider {
preview.theme(.dark).preferredColorScheme(.dark)
}
static private var preview: some View {
private static var preview: some View {
AllChatsOnboardingPage(image: Asset.Images.allChatsOnboarding1.image,
title: VectorL10n.allChatsOnboardingPageTitle1,
message: VectorL10n.allChatsOnboardingPageMessage1)
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,7 +25,6 @@ import Foundation
/// Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator.
@objcMembers
final class RoomNotificationSettingsCoordinatorBridgePresenter: NSObject {
// MARK: - Properties
// MARK: Private
@@ -61,11 +60,11 @@ final class RoomNotificationSettingsCoordinatorBridgePresenter: NSObject {
viewController.present(navigationController, animated: animated, completion: nil)
roomNotificationSettingsCoordinator.start()
self.coordinator = roomNotificationSettingsCoordinator
coordinator = roomNotificationSettingsCoordinator
}
func dismiss(animated: Bool, completion: (() -> Void)?) {
guard let coordinator = self.coordinator else {
guard let coordinator = coordinator else {
return
}
coordinator.toPresentable().dismiss(animated: animated) {
@@ -79,22 +78,21 @@ final class RoomNotificationSettingsCoordinatorBridgePresenter: NSObject {
}
// MARK: - RoomNotificationSettingsCoordinatorDelegate
extension RoomNotificationSettingsCoordinatorBridgePresenter: RoomNotificationSettingsCoordinatorDelegate {
func roomNotificationSettingsCoordinatorDidCancel(_ coordinator: RoomNotificationSettingsCoordinatorType) {
self.delegate?.roomNotificationSettingsCoordinatorBridgePresenterDelegateDidComplete(self)
delegate?.roomNotificationSettingsCoordinatorBridgePresenterDelegateDidComplete(self)
}
func roomNotificationSettingsCoordinatorDidComplete(_ coordinator: RoomNotificationSettingsCoordinatorType) {
self.delegate?.roomNotificationSettingsCoordinatorBridgePresenterDelegateDidComplete(self)
delegate?.roomNotificationSettingsCoordinatorBridgePresenterDelegateDidComplete(self)
}
}
// MARK: - UIAdaptivePresentationControllerDelegate
extension RoomNotificationSettingsCoordinatorBridgePresenter: UIAdaptivePresentationControllerDelegate {
func roomNotificationSettingsCoordinatorDidComplete(_ presentationController: UIPresentationController) {
self.delegate?.roomNotificationSettingsCoordinatorBridgePresenterDelegateDidComplete(self)
delegate?.roomNotificationSettingsCoordinatorBridgePresenterDelegateDidComplete(self)
}
}
@@ -15,14 +15,14 @@
//
import Foundation
import UIKit
import SwiftUI
import UIKit
final class RoomNotificationSettingsCoordinator: RoomNotificationSettingsCoordinatorType {
// MARK: - Properties
// MARK: Private
private var roomNotificationSettingsViewModel: RoomNotificationSettingsViewModelType
private let roomNotificationSettingsViewController: UIViewController
@@ -47,34 +47,35 @@ final class RoomNotificationSettingsCoordinator: RoomNotificationSettingsCoordin
roomNotificationService: roomNotificationService,
avatarData: avatarData,
displayName: room.summary.displayname,
roomEncrypted: room.summary.isEncrypted)
roomEncrypted: room.summary.isEncrypted
)
let avatarService: AvatarServiceProtocol = AvatarService(mediaManager: room.mxSession.mediaManager)
let view = RoomNotificationSettings(viewModel: viewModel, presentedModally: presentedModally)
.addDependency(avatarService)
let viewController = VectorHostingController(rootView: view)
self.roomNotificationSettingsViewModel = viewModel
self.roomNotificationSettingsViewController = viewController
roomNotificationSettingsViewModel = viewModel
roomNotificationSettingsViewController = viewController
}
// MARK: - Public methods
func start() {
self.roomNotificationSettingsViewModel.coordinatorDelegate = self
func start() {
roomNotificationSettingsViewModel.coordinatorDelegate = self
}
func toPresentable() -> UIViewController {
return self.roomNotificationSettingsViewController
roomNotificationSettingsViewController
}
}
// MARK: - RoomNotificationSettingsViewModelCoordinatorDelegate
extension RoomNotificationSettingsCoordinator: RoomNotificationSettingsViewModelCoordinatorDelegate {
func roomNotificationSettingsViewModelDidComplete(_ viewModel: RoomNotificationSettingsViewModelType) {
self.delegate?.roomNotificationSettingsCoordinatorDidComplete(self)
delegate?.roomNotificationSettingsCoordinatorDidComplete(self)
}
func roomNotificationSettingsViewModelDidCancel(_ viewModel: RoomNotificationSettingsViewModelType) {
self.delegate?.roomNotificationSettingsCoordinatorDidCancel(self)
delegate?.roomNotificationSettingsCoordinatorDidCancel(self)
}
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -40,4 +40,3 @@ extension RoomNotificationSettingsViewState {
roomEncrypted ? VectorL10n.roomNotifsSettingsEncryptedRoomNotice : ""
}
}
@@ -26,4 +26,3 @@ protocol RoomNotificationSettingsViewStateType {
var avatarData: AvatarProtocol? { get }
var displayName: String? { get }
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,7 +25,7 @@ enum RoomNotificationState: Int {
extension RoomNotificationState: CaseIterable { }
extension RoomNotificationState: Identifiable {
var id: Int { self.rawValue }
var id: Int { rawValue }
}
extension RoomNotificationState {
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import Foundation
final class MXRoomNotificationSettingsService: RoomNotificationSettingsServiceType {
typealias Completion = () -> Void
// MARK: - Properties
@@ -50,11 +49,11 @@ final class MXRoomNotificationSettingsService: RoomNotificationSettingsServiceTy
// MARK: - Public
func observeNotificationState(listener: @escaping RoomNotificationStateCallback) {
let observer = NotificationCenter.default.addObserver(
forName: NSNotification.Name(rawValue: kMXNotificationCenterDidUpdateRules),
object: nil,
queue: OperationQueue.main) { [weak self] _ in
queue: OperationQueue.main
) { [weak self] _ in
guard let self = self else { return }
listener(self.room.notificationState)
}
@@ -88,7 +87,7 @@ final class MXRoomNotificationSettingsService: RoomNotificationSettingsServiceTy
}
guard let rule = room.overridePushRule else {
self.addPushRuleToMute(completion: completion)
addPushRuleToMute(completion: completion)
return
}
@@ -96,7 +95,7 @@ final class MXRoomNotificationSettingsService: RoomNotificationSettingsServiceTy
MXLog.debug("[RoomNotificationSettingsService] Request in progress: ignore push rule update")
completion()
return
}
}
// if the user defined one, use it
if rule.actionsContains(actionType: MXPushRuleActionTypeDontNotify) {
@@ -130,7 +129,7 @@ final class MXRoomNotificationSettingsService: RoomNotificationSettingsServiceTy
MXLog.debug("[MXRoom+Riot] Request in progress: ignore push rule update")
completion()
return
}
}
// if the user defined one, use it
if rule.actionsContains(actionType: MXPushRuleActionTypeDontNotify) {
@@ -140,11 +139,10 @@ final class MXRoomNotificationSettingsService: RoomNotificationSettingsServiceTy
self.addPushRuleToMentionOnly(completion: completion)
}
}
}
private func allMessages(completion: @escaping Completion) {
if !room.isMentionsOnly && !room.isMuted {
if !room.isMentionsOnly, !room.isMuted {
completion()
return
}
@@ -172,7 +170,8 @@ final class MXRoomNotificationSettingsService: RoomNotificationSettingsServiceTy
room.roomId,
notify: false,
sound: false,
highlight: false)
highlight: false
)
}
private func addPushRuleToMute(completion: @escaping Completion) {
@@ -207,18 +206,19 @@ final class MXRoomNotificationSettingsService: RoomNotificationSettingsServiceTy
private func enablePushRule(rule: MXPushRule, completion: @escaping Completion) {
handleUpdateCallback(completion) {
// No way to check whether this notification concerns the push rule. Consider the change is applied.
return true
true
}
handleFailureCallback(completion)
room.mxSession.notificationCenter.enableRule(rule, isEnabled: true)
}
private func handleUpdateCallback(_ completion: @escaping Completion, releaseCheck: @escaping () -> Bool) {
private func handleUpdateCallback(_ completion: @escaping Completion, releaseCheck: @escaping () -> Bool) {
notificationCenterDidUpdateObserver = NotificationCenter.default.addObserver(
forName: NSNotification.Name(rawValue: kMXNotificationCenterDidUpdateRules),
object: nil,
queue: OperationQueue.main) { [weak self] _ in
queue: OperationQueue.main
) { [weak self] _ in
guard let self = self else { return }
if releaseCheck() {
self.removeObservers()
@@ -231,7 +231,8 @@ final class MXRoomNotificationSettingsService: RoomNotificationSettingsServiceTy
notificationCenterDidFailObserver = NotificationCenter.default.addObserver(
forName: NSNotification.Name(rawValue: kMXNotificationCenterDidFailRulesUpdate),
object: nil,
queue: OperationQueue.main) { [weak self] _ in
queue: OperationQueue.main
) { [weak self] _ in
guard let self = self else { return }
self.removeObservers()
completion()
@@ -239,23 +240,23 @@ final class MXRoomNotificationSettingsService: RoomNotificationSettingsServiceTy
}
func removeObservers() {
if let observer = self.notificationCenterDidUpdateObserver {
if let observer = notificationCenterDidUpdateObserver {
NotificationCenter.default.removeObserver(observer)
self.notificationCenterDidUpdateObserver = nil
notificationCenterDidUpdateObserver = nil
}
if let observer = self.notificationCenterDidFailObserver {
if let observer = notificationCenterDidFailObserver {
NotificationCenter.default.removeObserver(observer)
self.notificationCenterDidFailObserver = nil
notificationCenterDidFailObserver = nil
}
}
}
extension MXRoom {
public var isMuted: Bool {
public extension MXRoom {
var isMuted: Bool {
// Check whether an override rule has been defined with the roomm id as rule id.
// This kind of rule is created to mute the room
guard let rule = self.overridePushRule,
guard let rule = overridePushRule,
rule.actionsContains(actionType: MXPushRuleActionTypeDontNotify),
rule.conditionIsEnabled(kind: .eventMatch, for: roomId) else {
return false
@@ -263,7 +264,7 @@ extension MXRoom {
return rule.enabled
}
public var isMentionsOnly: Bool {
var isMentionsOnly: Bool {
// Check push rules at room level
guard let rule = roomPushRule else { return false }
return rule.enabled && rule.actionsContains(actionType: MXPushRuleActionTypeDontNotify)
@@ -271,8 +272,7 @@ extension MXRoom {
}
// We could move these to their own file and make available in global namespace or move to sdk but they are only used here at the moment
fileprivate extension MXRoom {
private extension MXRoom {
typealias Completion = () -> Void
func getRoomRule(from rules: [Any]) -> MXPushRule? {
guard let pushRules = rules as? [MXPushRule] else {
@@ -285,19 +285,18 @@ fileprivate extension MXRoom {
var overridePushRule: MXPushRule? {
guard let overrideRules = mxSession.notificationCenter.rules.global.override else {
return nil
}
}
return getRoomRule(from: overrideRules)
}
var roomPushRule: MXPushRule? {
guard let roomRules = mxSession.notificationCenter.rules.global.room else {
return nil
}
}
return getRoomRule(from: roomRules)
}
var notificationState: RoomNotificationState {
if isMuted {
return .mute
}
@@ -306,10 +305,9 @@ fileprivate extension MXRoom {
}
return .all
}
}
fileprivate extension MXPushRule {
private extension MXPushRule {
func actionsContains(actionType: MXPushRuleActionType) -> Bool {
guard let actions = actions as? [MXPushRuleAction] else {
return false
@@ -323,8 +321,8 @@ fileprivate extension MXPushRule {
}
let ruleContainsCondition = conditions.contains { condition in
guard case kind = MXPushRuleConditionType(identifier: condition.kind),
let key = condition.parameters["key"] as? String,
let pattern = condition.parameters["pattern"] as? String
let key = condition.parameters["key"] as? String,
let pattern = condition.parameters["pattern"] as? String
else { return false }
return key == "room_id" && pattern == roomId
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import Foundation
class MockRoomNotificationSettingsService: RoomNotificationSettingsServiceType {
static let example = MockRoomNotificationSettingsService(initialState: .all)
var listener: RoomNotificationStateCallback?
@@ -32,7 +31,7 @@ class MockRoomNotificationSettingsService: RoomNotificationSettingsServiceType {
}
func update(state: RoomNotificationState, completion: @escaping UpdateRoomNotificationStateCompletion) {
self.notificationState = state
notificationState = state
completion()
listener?(state)
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,7 +20,6 @@ typealias UpdateRoomNotificationStateCompletion = () -> Void
typealias RoomNotificationStateCallback = (RoomNotificationState) -> Void
protocol RoomNotificationSettingsServiceType {
func observeNotificationState(listener: @escaping RoomNotificationStateCallback)
func update(state: RoomNotificationState, completion: @escaping UpdateRoomNotificationStateCompletion)
var notificationState: RoomNotificationState { get }
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct FormPickerItem: View {
typealias TapCallback = () -> Void
@Environment(\.theme) var theme: ThemeSwiftUI
@@ -53,7 +52,6 @@ struct FormPickerItem: View {
}
struct FormPickerItem_Previews: PreviewProvider {
static let items = ["Item 1", "Item 2", "Item 3"]
static var selected: String = items[0]
static var previews: some View {
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct FormSectionFooter: View {
@Environment(\.theme) var theme: ThemeSwiftUI
var text: String
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct FormSectionHeader: View {
@Environment(\.theme) var theme: ThemeSwiftUI
var text: String
@@ -17,7 +17,6 @@
import SwiftUI
struct RoomNotificationSettings: View {
@Environment(\.theme) var theme: ThemeSwiftUI
@ObservedObject var viewModel: RoomNotificationSettingsSwiftUIViewModel
@@ -42,7 +41,7 @@ struct RoomNotificationSettings: View {
var body: some View {
VectorForm {
if let avatarData = viewModel.viewState.avatarData as? AvatarInputProtocol {
if let avatarData = viewModel.viewState.avatarData as? AvatarInputProtocol {
RoomNotificationSettingsHeader(
avatarData: avatarData,
displayName: viewModel.viewState.displayName
@@ -74,7 +73,6 @@ struct RoomNotificationSettings: View {
}
struct RoomNotificationSettings_Previews: PreviewProvider {
static let mockViewModel = RoomNotificationSettingsSwiftUIViewModel(
roomNotificationService: MockRoomNotificationSettingsService.example,
avatarData: MockAvatarInput.example,
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct RoomNotificationSettingsHeader: View {
@Environment(\.theme) var theme: ThemeSwiftUI
var avatarData: AvatarInputProtocol
var displayName: String?
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct VectorForm<Content: View>: View {
@Environment(\.theme) var theme: ThemeSwiftUI
var content: () -> Content
@@ -38,12 +37,10 @@ struct VectorForm<Content: View>: View {
)
.background(theme.colors.system)
.edgesIgnoringSafeArea(.bottom)
}
}
struct VectorForm_Previews: PreviewProvider {
static var previews: some View {
Group {
VectorForm {
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,17 +14,16 @@
// limitations under the License.
//
import Foundation
import Combine
import Foundation
class RoomNotificationSettingsSwiftUIViewModel: RoomNotificationSettingsViewModel, ObservableObject {
@Published var viewState: RoomNotificationSettingsViewState
lazy var cancellables = Set<AnyCancellable>()
override init(roomNotificationService: RoomNotificationSettingsServiceType, initialState: RoomNotificationSettingsViewState) {
self.viewState = initialState
viewState = initialState
super.init(roomNotificationService: roomNotificationService, initialState: initialState)
}
@@ -16,11 +16,10 @@
limitations under the License.
*/
import Foundation
import Combine
import Foundation
class RoomNotificationSettingsViewModel: RoomNotificationSettingsViewModelType {
// MARK: - Properties
// MARK: Private
@@ -40,12 +39,10 @@ class RoomNotificationSettingsViewModel: RoomNotificationSettingsViewModelType {
// MARK: - Setup
init(
roomNotificationService: RoomNotificationSettingsServiceType,
initialState: RoomNotificationSettingsViewState
) {
init(roomNotificationService: RoomNotificationSettingsServiceType,
initialState: RoomNotificationSettingsViewState) {
self.roomNotificationService = roomNotificationService
self.state = initialState
state = initialState
self.roomNotificationService.observeNotificationState { [weak self] state in
guard let self = self else { return }
@@ -53,12 +50,10 @@ class RoomNotificationSettingsViewModel: RoomNotificationSettingsViewModelType {
}
}
convenience init(
roomNotificationService: RoomNotificationSettingsServiceType,
avatarData: AvatarProtocol?,
displayName: String?,
roomEncrypted: Bool
) {
convenience init(roomNotificationService: RoomNotificationSettingsServiceType,
avatarData: AvatarProtocol?,
displayName: String?,
roomEncrypted: Bool) {
let notificationState = Self.mapNotificationStateOnRead(encrypted: roomEncrypted, state: roomNotificationService.notificationState)
let initialState = RoomNotificationSettingsViewState(
@@ -71,16 +66,16 @@ class RoomNotificationSettingsViewModel: RoomNotificationSettingsViewModelType {
self.init(roomNotificationService: roomNotificationService, initialState: initialState)
}
// MARK: - Public
// MARK: - Public
func process(viewAction: RoomNotificationSettingsViewAction) {
switch viewAction {
case .load:
update(viewState: self.state)
update(viewState: state)
case .selectNotificationState(let state):
self.state.notificationState = state
case .save:
self.state.saving = true
state.saving = true
roomNotificationService.update(state: state.notificationState) { [weak self] in
guard let self = self else { return }
self.state.saving = false
@@ -103,6 +98,6 @@ class RoomNotificationSettingsViewModel: RoomNotificationSettingsViewModelType {
}
func update(viewState: RoomNotificationSettingsViewState) {
self.viewDelegate?.roomNotificationSettingsViewModel(self, didUpdateViewState: viewState)
viewDelegate?.roomNotificationSettingsViewModel(self, didUpdateViewState: viewState)
}
}
@@ -28,8 +28,7 @@ protocol RoomNotificationSettingsViewModelCoordinatorDelegate: AnyObject {
}
/// Protocol describing the view model used by `RoomNotificationSettingsViewController`
protocol RoomNotificationSettingsViewModelType {
protocol RoomNotificationSettingsViewModelType {
var viewDelegate: RoomNotificationSettingsViewModelViewDelegate? { get set }
var coordinatorDelegate: RoomNotificationSettingsViewModelCoordinatorDelegate? { get set }
@@ -15,8 +15,8 @@
//
import Foundation
import UIKit
import SwiftUI
import UIKit
struct PollEditFormCoordinatorParameters {
let room: MXRoom
@@ -24,7 +24,6 @@ struct PollEditFormCoordinatorParameters {
}
final class PollEditFormCoordinator: Coordinator, Presentable {
// MARK: - Properties
// MARK: Private
@@ -50,7 +49,7 @@ final class PollEditFormCoordinator: Coordinator, Presentable {
viewModel = PollEditFormViewModel(parameters: PollEditFormViewModelParameters(mode: .editing,
pollDetails: EditFormPollDetails(type: Self.pollKindKeyToDetailsType(pollContent.kind),
question: pollContent.question,
answerOptions: pollContent.answerOptions.map { $0.text })))
answerOptions: pollContent.answerOptions.map(\.text))))
} else {
viewModel = PollEditFormViewModel(parameters: PollEditFormViewModelParameters(mode: .creation, pollDetails: .default))
@@ -63,6 +62,7 @@ final class PollEditFormCoordinator: Coordinator, Presentable {
}
// MARK: - Public
func start() {
pollEditFormViewModel.completion = { [weak self] result in
guard let self = self else { return }
@@ -75,7 +75,7 @@ final class PollEditFormCoordinator: Coordinator, Presentable {
self.pollEditFormViewModel.startLoading()
self.parameters.room.sendPollStart(withContent: pollStartContent, threadId: nil, localEcho: nil) { [weak self] result in
self.parameters.room.sendPollStart(withContent: pollStartContent, threadId: nil, localEcho: nil) { [weak self] _ in
guard let self = self else { return }
self.pollEditFormViewModel.stopLoading()
@@ -103,7 +103,7 @@ final class PollEditFormCoordinator: Coordinator, Presentable {
self.parameters.room.sendPollUpdate(for: pollStartEvent,
oldContent: oldPollContent,
newContent: newPollContent, localEcho: nil) { [weak self] result in
newContent: newPollContent, localEcho: nil) { [weak self] _ in
guard let self = self else { return }
self.pollEditFormViewModel.stopLoading()
@@ -113,7 +113,7 @@ final class PollEditFormCoordinator: Coordinator, Presentable {
MXLog.error("Failed updating poll", context: error)
self.pollEditFormViewModel.stopLoading(errorAlertType: .failedUpdatingPoll)
}
}
}
}
}
@@ -121,7 +121,7 @@ final class PollEditFormCoordinator: Coordinator, Presentable {
// MARK: - Presentable
func toPresentable() -> UIViewController {
return pollEditFormHostingController
pollEditFormHostingController
}
// MARK: - Private
@@ -136,21 +136,20 @@ final class PollEditFormCoordinator: Coordinator, Presentable {
kind: Self.pollDetailsTypeToKindKey(details.type),
maxSelections: NSNumber(value: details.maxSelections),
answerOptions: options)
}
private static func pollDetailsTypeToKindKey(_ type: EditFormPollType) -> String {
let mapping = [EditFormPollType.disclosed : kMXMessageContentKeyExtensiblePollKindDisclosedMSC3381,
EditFormPollType.undisclosed : kMXMessageContentKeyExtensiblePollKindUndisclosedMSC3381]
let mapping = [EditFormPollType.disclosed: kMXMessageContentKeyExtensiblePollKindDisclosedMSC3381,
EditFormPollType.undisclosed: kMXMessageContentKeyExtensiblePollKindUndisclosedMSC3381]
return mapping[type] ?? kMXMessageContentKeyExtensiblePollKindDisclosedMSC3381
}
private static func pollKindKeyToDetailsType(_ key: String) -> EditFormPollType {
let mapping = [kMXMessageContentKeyExtensiblePollKindDisclosed : EditFormPollType.disclosed,
kMXMessageContentKeyExtensiblePollKindDisclosedMSC3381 : EditFormPollType.disclosed,
kMXMessageContentKeyExtensiblePollKindUndisclosed : EditFormPollType.undisclosed,
kMXMessageContentKeyExtensiblePollKindUndisclosedMSC3381 : EditFormPollType.undisclosed]
let mapping = [kMXMessageContentKeyExtensiblePollKindDisclosed: EditFormPollType.disclosed,
kMXMessageContentKeyExtensiblePollKindDisclosedMSC3381: EditFormPollType.disclosed,
kMXMessageContentKeyExtensiblePollKindUndisclosed: EditFormPollType.undisclosed,
kMXMessageContentKeyExtensiblePollKindUndisclosedMSC3381: EditFormPollType.undisclosed]
return mapping[key] ?? EditFormPollType.disclosed
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -82,14 +82,14 @@ struct PollEditFormViewState: BindableState {
var confirmationButtonEnabled: Bool {
!bindings.question.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty &&
bindings.answerOptions.filter({ !$0.text.isEmpty }).count >= minAnswerOptionsCount
bindings.answerOptions.filter { !$0.text.isEmpty }.count >= minAnswerOptionsCount
}
var addAnswerOptionButtonEnabled: Bool {
bindings.answerOptions.count < maxAnswerOptionsCount
}
var showLoadingIndicator: Bool = false
var showLoadingIndicator = false
}
struct PollEditFormViewStateBindings {
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,7 +24,7 @@ enum MockPollEditFormScreenState: MockScreenState, CaseIterable {
PollEditForm.self
}
var screenView: ([Any], AnyView) {
var screenView: ([Any], AnyView) {
let viewModel = PollEditFormViewModel(parameters: PollEditFormViewModelParameters(mode: .creation, pollDetails: .default))
return ([viewModel], AnyView(PollEditForm(viewModel: viewModel.context)))
}
@@ -14,20 +14,19 @@
// limitations under the License.
//
import SwiftUI
import Combine
import SwiftUI
struct PollEditFormViewModelParameters {
let mode: PollEditFormMode
let pollDetails: EditFormPollDetails
}
typealias PollEditFormViewModelType = StateStoreViewModel <PollEditFormViewState,
Never,
PollEditFormViewAction>
typealias PollEditFormViewModelType = StateStoreViewModel<PollEditFormViewState,
Never,
PollEditFormViewAction>
class PollEditFormViewModel: PollEditFormViewModelType, PollEditFormViewModelProtocol {
private struct Constants {
private enum Constants {
static let minAnswerOptionsCount = 2
static let maxAnswerOptionsCount = 20
static let maxQuestionLength = 340
@@ -102,11 +101,11 @@ class PollEditFormViewModel: PollEditFormViewModelType, PollEditFormViewModelPro
// MARK: - Private
private func buildPollDetails() -> EditFormPollDetails {
return EditFormPollDetails(type: state.bindings.type,
question: state.bindings.question.text.trimmingCharacters(in: .whitespacesAndNewlines),
answerOptions: state.bindings.answerOptions.compactMap({ answerOption in
let text = answerOption.text.trimmingCharacters(in: .whitespacesAndNewlines)
return text.isEmpty ? nil : text
}))
EditFormPollDetails(type: state.bindings.type,
question: state.bindings.question.text.trimmingCharacters(in: .whitespacesAndNewlines),
answerOptions: state.bindings.answerOptions.compactMap { answerOption in
let text = answerOption.text.trimmingCharacters(in: .whitespacesAndNewlines)
return text.isEmpty ? nil : text
})
}
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,8 +14,8 @@
// limitations under the License.
//
import XCTest
import RiotSwiftUI
import XCTest
class PollEditFormUITests: MockScreenTestCase {
func testInitialStateComponents() {
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,8 +14,8 @@
// limitations under the License.
//
import XCTest
import Combine
import XCTest
@testable import RiotSwiftUI
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct PollEditForm: View {
// MARK: - Properties
// MARK: Private
@@ -33,7 +32,6 @@ struct PollEditForm: View {
GeometryReader { proxy in
ScrollView {
VStack(alignment: .leading, spacing: 32.0) {
PollEditFormTypePicker(selectedType: $viewModel.type)
VStack(alignment: .leading, spacing: 16.0) {
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct PollEditFormAnswerOptionView: View {
@Environment(\.theme) private var theme: ThemeSwiftUI
@State private var focused = false
@@ -39,7 +38,7 @@ struct PollEditFormAnswerOptionView: View {
})
.textFieldStyle(BorderedInputFieldStyle(isEditing: focused))
Button(action: onDelete) {
Image(uiImage:Asset.Images.pollDeleteOptionIcon.image)
Image(uiImage: Asset.Images.pollDeleteOptionIcon.image)
}
.accessibilityIdentifier("Delete answer option")
}
@@ -50,12 +49,8 @@ struct PollEditFormAnswerOptionView: View {
struct PollEditFormAnswerOptionView_Previews: PreviewProvider {
static var previews: some View {
VStack(spacing: 32.0) {
PollEditFormAnswerOptionView(text: Binding.constant(""), index: 0) {
}
PollEditFormAnswerOptionView(text: Binding.constant("Test"), index: 5) {
}
PollEditFormAnswerOptionView(text: Binding.constant(""), index: 0) { }
PollEditFormAnswerOptionView(text: Binding.constant("Test"), index: 5) { }
}
}
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -43,7 +43,6 @@ private struct PollEditFormTypeButton: View {
selectedType = type
} label: {
HStack(alignment: .top, spacing: 8.0) {
Image(uiImage: selectionImage)
VStack(alignment: .leading, spacing: 2) {
@@ -26,7 +26,6 @@ enum RoomAccessCoordinatorCoordinatorAction {
@objcMembers
final class RoomAccessCoordinator: Coordinator {
// MARK: - Properties
// MARK: Private
@@ -35,7 +34,7 @@ final class RoomAccessCoordinator: Coordinator {
private var upgradedRoomId: String?
private var navigationRouter: NavigationRouterType {
return self.parameters.navigationRouter
parameters.navigationRouter
}
// MARK: Public
@@ -58,32 +57,31 @@ final class RoomAccessCoordinator: Coordinator {
init(parameters: RoomAccessCoordinatorParameters) {
self.parameters = parameters
}
}
// MARK: - Public
func start() {
MXLog.debug("[RoomAccessCoordinator] did start.")
let rootCoordinator = self.createRoomAccessTypeCoordinator()
let rootCoordinator = createRoomAccessTypeCoordinator()
rootCoordinator.start()
self.add(childCoordinator: rootCoordinator)
self.accessCoordinator = rootCoordinator
add(childCoordinator: rootCoordinator)
accessCoordinator = rootCoordinator
if self.navigationRouter.modules.isEmpty == false {
self.navigationRouter.push(rootCoordinator, animated: true, popCompletion: { [weak self] in
if navigationRouter.modules.isEmpty == false {
navigationRouter.push(rootCoordinator, animated: true, popCompletion: { [weak self] in
self?.remove(childCoordinator: rootCoordinator)
})
} else {
self.navigationRouter.setRootModule(rootCoordinator) { [weak self] in
navigationRouter.setRootModule(rootCoordinator) { [weak self] in
self?.remove(childCoordinator: rootCoordinator)
}
}
}
func toPresentable() -> UIViewController {
return self.navigationRouter.toPresentable()
navigationRouter.toPresentable()
}
// MARK: - Private
@@ -91,7 +89,7 @@ final class RoomAccessCoordinator: Coordinator {
func pushScreen(with coordinator: Coordinator & Presentable) {
add(childCoordinator: coordinator)
self.navigationRouter.push(coordinator, animated: true, popCompletion: { [weak self] in
navigationRouter.push(coordinator, animated: true, popCompletion: { [weak self] in
self?.remove(childCoordinator: coordinator)
})
@@ -103,13 +101,13 @@ final class RoomAccessCoordinator: Coordinator {
coordinator.toPresentable().modalPresentationStyle = .overFullScreen
coordinator.toPresentable().modalTransitionStyle = .crossDissolve
self.navigationRouter.present(coordinator, animated: true)
navigationRouter.present(coordinator, animated: true)
coordinator.start()
}
private func createRoomAccessTypeCoordinator() -> RoomAccessTypeChooserCoordinator {
let coordinator: RoomAccessTypeChooserCoordinator = RoomAccessTypeChooserCoordinator(parameters: RoomAccessTypeChooserCoordinatorParameters(roomId: parameters.room.roomId, allowsRoomUpgrade: parameters.allowsRoomUpgrade, session: parameters.room.mxSession))
let coordinator = RoomAccessTypeChooserCoordinator(parameters: RoomAccessTypeChooserCoordinatorParameters(roomId: parameters.room.roomId, allowsRoomUpgrade: parameters.allowsRoomUpgrade, session: parameters.room.mxSession))
coordinator.callback = { [weak self] result in
guard let self = self else { return }
@@ -132,7 +130,8 @@ final class RoomAccessCoordinator: Coordinator {
let paramaters = MatrixItemChooserCoordinatorParameters(
session: parameters.room.mxSession,
viewProvider: RoomRestrictedAccessSpaceChooserViewProvider(navTitle: VectorL10n.roomAccessSettingsScreenNavTitle),
itemsProcessor: RoomRestrictedAccessSpaceChooserItemsProcessor(roomId: roomId, session: parameters.room.mxSession))
itemsProcessor: RoomRestrictedAccessSpaceChooserItemsProcessor(roomId: roomId, session: parameters.room.mxSession)
)
let coordinator = MatrixItemChooserCoordinator(parameters: paramaters)
coordinator.completion = { [weak self] result in
guard let self = self else { return }
@@ -154,7 +153,8 @@ final class RoomAccessCoordinator: Coordinator {
session: parameters.room.mxSession,
roomId: roomId,
parentSpaceId: parameters.parentSpaceId,
versionOverride: versionOverride)
versionOverride: versionOverride
)
let coordinator = RoomUpgradeCoordinator(parameters: paramaters)
coordinator.completion = { [weak self] result in
@@ -1,4 +1,5 @@
//
import MatrixSDK
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,7 +15,6 @@
// limitations under the License.
//
import UIKit
import MatrixSDK
@objc protocol RoomAccessCoordinatorBridgePresenterDelegate {
func roomAccessCoordinatorBridgePresenterDelegate(_ coordinatorBridgePresenter: RoomAccessCoordinatorBridgePresenter, didCancelRoomWithId roomId: String)
@@ -27,7 +27,6 @@ import MatrixSDK
/// Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator.
@objcMembers
final class RoomAccessCoordinatorBridgePresenter: NSObject {
// MARK: - Properties
// MARK: Private
@@ -82,7 +81,7 @@ final class RoomAccessCoordinatorBridgePresenter: NSObject {
}
func dismiss(animated: Bool, completion: (() -> Void)?) {
guard let coordinator = self.coordinator else {
guard let coordinator = coordinator else {
return
}
coordinator.toPresentable().dismiss(animated: animated) {
@@ -98,13 +97,11 @@ final class RoomAccessCoordinatorBridgePresenter: NSObject {
// MARK: - UIAdaptivePresentationControllerDelegate
extension RoomAccessCoordinatorBridgePresenter: UIAdaptivePresentationControllerDelegate {
func roomNotificationSettingsCoordinatorDidComplete(_ presentationController: UIPresentationController) {
if let roomId = self.coordinator?.currentRoomId {
self.delegate?.roomAccessCoordinatorBridgePresenterDelegate(self, didCancelRoomWithId: roomId)
if let roomId = coordinator?.currentRoomId {
delegate?.roomAccessCoordinatorBridgePresenterDelegate(self, didCancelRoomWithId: roomId)
} else {
self.delegate?.roomAccessCoordinatorBridgePresenterDelegate(self, didCancelRoomWithId: self.room.roomId)
delegate?.roomAccessCoordinatorBridgePresenterDelegate(self, didCancelRoomWithId: room.roomId)
}
}
}
@@ -20,7 +20,6 @@ import Foundation
/// RoomAccessCoordinator input parameters
struct RoomAccessCoordinatorParameters {
/// The Matrix room
let room: MXRoom
@@ -23,7 +23,6 @@ struct RoomAccessTypeChooserCoordinatorParameters {
}
final class RoomAccessTypeChooserCoordinator: Coordinator, Presentable {
// MARK: - Properties
// MARK: Private
@@ -70,10 +69,10 @@ final class RoomAccessTypeChooserCoordinator: Coordinator, Presentable {
}
func toPresentable() -> UIViewController {
return self.roomAccessTypeChooserHostingController
roomAccessTypeChooserHostingController
}
func handleRoomUpgradeResult(_ result: RoomUpgradeCoordinatorResult) {
self.roomAccessTypeChooserViewModel.handleRoomUpgradeResult(result)
roomAccessTypeChooserViewModel.handleRoomUpgradeResult(result)
}
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
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.
enum MockRoomAccessTypeChooserScreenState: MockScreenState, CaseIterable {
@@ -42,7 +41,7 @@ enum MockRoomAccessTypeChooserScreenState: MockScreenState, CaseIterable {
service = MockRoomAccessTypeChooserService(accessItems: [
RoomAccessTypeChooserAccessItem(id: .private, isSelected: true, title: VectorL10n.private, detail: VectorL10n.roomAccessSettingsScreenPrivateMessage, badgeText: nil),
RoomAccessTypeChooserAccessItem(id: .restricted, isSelected: false, title: VectorL10n.createRoomTypeRestricted, detail: VectorL10n.roomAccessSettingsScreenRestrictedMessage, badgeText: VectorL10n.roomAccessSettingsScreenUpgradeRequired),
RoomAccessTypeChooserAccessItem(id: .public, isSelected: false, title: VectorL10n.public, detail: VectorL10n.roomAccessSettingsScreenPublicMessage, badgeText: nil),
RoomAccessTypeChooserAccessItem(id: .public, isSelected: false, title: VectorL10n.public, detail: VectorL10n.roomAccessSettingsScreenPublicMessage, badgeText: nil)
])
}
let viewModel = RoomAccessTypeChooserViewModel(roomAccessTypeChooserService: service)
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,14 +14,13 @@
// limitations under the License.
//
import SwiftUI
import Combine
import SwiftUI
typealias RoomAccessTypeChooserViewModelType = StateStoreViewModel<RoomAccessTypeChooserViewState,
RoomAccessTypeChooserStateAction,
RoomAccessTypeChooserViewAction>
RoomAccessTypeChooserStateAction,
RoomAccessTypeChooserViewAction>
class RoomAccessTypeChooserViewModel: RoomAccessTypeChooserViewModelType, RoomAccessTypeChooserViewModelProtocol {
// MARK: - Properties
// MARK: Private
@@ -43,7 +42,8 @@ class RoomAccessTypeChooserViewModel: RoomAccessTypeChooserViewModelType, RoomAc
private static func defaultState(roomAccessTypeChooserService: RoomAccessTypeChooserServiceProtocol) -> RoomAccessTypeChooserViewState {
let bindings = RoomAccessTypeChooserViewModelBindings(
showUpgradeRoomAlert: roomAccessTypeChooserService.roomUpgradeRequiredSubject.value,
waitingMessage: roomAccessTypeChooserService.waitingMessageSubject.value, isLoading: roomAccessTypeChooserService.waitingMessageSubject.value != nil)
waitingMessage: roomAccessTypeChooserService.waitingMessageSubject.value, isLoading: roomAccessTypeChooserService.waitingMessageSubject.value != nil
)
return RoomAccessTypeChooserViewState(accessItems: roomAccessTypeChooserService.accessItemsSubject.value, bindings: bindings)
}
@@ -130,7 +130,7 @@ class RoomAccessTypeChooserViewModel: RoomAccessTypeChooserViewModelType, RoomAc
private func didSelect(accessType: RoomAccessTypeChooserAccessType) {
roomAccessTypeChooserService.updateSelection(with: accessType)
if accessType == .restricted && !roomAccessTypeChooserService.roomUpgradeRequiredSubject.value {
if accessType == .restricted, !roomAccessTypeChooserService.roomUpgradeRequiredSubject.value {
callback?(.spaceSelection(roomAccessTypeChooserService.currentRoomId, .restricted))
}
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,12 +14,11 @@
// limitations under the License.
//
import Foundation
import Combine
import Foundation
import MatrixSDK
class RoomAccessTypeChooserService: RoomAccessTypeChooserServiceProtocol {
// MARK: - Properties
// MARK: Private
@@ -40,6 +39,7 @@ class RoomAccessTypeChooserService: RoomAccessTypeChooserServiceProtocol {
accessItemsSubject.send(accessItems)
}
}
private(set) var selectedType: RoomAccessTypeChooserAccessType = .private {
didSet {
for (index, item) in accessItems.enumerated() {
@@ -48,6 +48,7 @@ class RoomAccessTypeChooserService: RoomAccessTypeChooserServiceProtocol {
accessItemsSubject.send(accessItems)
}
}
private var roomJoinRule: MXRoomJoinRule = .private
private var currentOperation: MXHTTPOperation?
@@ -67,8 +68,8 @@ class RoomAccessTypeChooserService: RoomAccessTypeChooserServiceProtocol {
self.roomId = roomId
self.allowsRoomUpgrade = allowsRoomUpgrade
self.session = session
self.currentRoomId = roomId
self.versionOverride = session.homeserverCapabilitiesService.versionOverrideForFeature(.restricted)
currentRoomId = roomId
versionOverride = session.homeserverCapabilitiesService.versionOverrideForFeature(.restricted)
roomUpgradeRequiredSubject = CurrentValueSubject(false)
waitingMessageSubject = CurrentValueSubject(nil)
@@ -92,7 +93,7 @@ class RoomAccessTypeChooserService: RoomAccessTypeChooserServiceProtocol {
self.selectedType = selectedType
if selectedType == .restricted {
if roomUpgradeRequired && roomUpgradeRequiredSubject.value == false {
if roomUpgradeRequired, roomUpgradeRequiredSubject.value == false {
roomUpgradeRequiredSubject.send(true)
}
}
@@ -108,14 +109,14 @@ class RoomAccessTypeChooserService: RoomAccessTypeChooserServiceProtocol {
let _joinRule: MXRoomJoinRule?
switch self.selectedType {
switch selectedType {
case .private:
_joinRule = .invite
case .public:
_joinRule = .public
case .restricted:
_joinRule = nil
if roomUpgradeRequired && roomUpgradeRequiredSubject.value == false {
if roomUpgradeRequired, roomUpgradeRequiredSubject.value == false {
roomUpgradeRequiredSubject.send(true)
} else {
completion()
@@ -123,7 +124,7 @@ class RoomAccessTypeChooserService: RoomAccessTypeChooserServiceProtocol {
}
if let joinRule = _joinRule {
self.waitingMessageSubject.send(VectorL10n.roomAccessSettingsScreenSettingRoomAccess)
waitingMessageSubject.send(VectorL10n.roomAccessSettingsScreenSettingRoomAccess)
room.setJoinRule(joinRule) { [weak self] response in
guard let self = self else { return }
@@ -140,7 +141,7 @@ class RoomAccessTypeChooserService: RoomAccessTypeChooserServiceProtocol {
}
func updateRoomId(with roomId: String) {
self.currentRoomId = roomId
currentRoomId = roomId
readRoomState()
}
@@ -148,17 +149,17 @@ class RoomAccessTypeChooserService: RoomAccessTypeChooserServiceProtocol {
private func setupAccessItems() {
guard let spaceService = session.spaceService, let ancestors = spaceService.ancestorsPerRoomId[currentRoomId], !ancestors.isEmpty, allowsRoomUpgrade || !roomUpgradeRequired else {
self.accessItems = [
accessItems = [
RoomAccessTypeChooserAccessItem(id: .private, isSelected: false, title: VectorL10n.private, detail: VectorL10n.roomAccessSettingsScreenPrivateMessage, badgeText: nil),
RoomAccessTypeChooserAccessItem(id: .public, isSelected: false, title: VectorL10n.public, detail: VectorL10n.roomAccessSettingsScreenPublicMessage, badgeText: nil),
RoomAccessTypeChooserAccessItem(id: .public, isSelected: false, title: VectorL10n.public, detail: VectorL10n.roomAccessSettingsScreenPublicMessage, badgeText: nil)
]
return
}
self.accessItems = [
accessItems = [
RoomAccessTypeChooserAccessItem(id: .private, isSelected: false, title: VectorL10n.private, detail: VectorL10n.roomAccessSettingsScreenPrivateMessage, badgeText: nil),
RoomAccessTypeChooserAccessItem(id: .restricted, isSelected: false, title: VectorL10n.createRoomTypeRestricted, detail: VectorL10n.roomAccessSettingsScreenRestrictedMessage, badgeText: roomUpgradeRequired ? VectorL10n.roomAccessSettingsScreenUpgradeRequired : VectorL10n.roomAccessSettingsScreenEditSpaces),
RoomAccessTypeChooserAccessItem(id: .public, isSelected: false, title: VectorL10n.public, detail: VectorL10n.roomAccessSettingsScreenPublicMessage, badgeText: nil),
RoomAccessTypeChooserAccessItem(id: .public, isSelected: false, title: VectorL10n.public, detail: VectorL10n.roomAccessSettingsScreenPublicMessage, badgeText: nil)
]
accessItemsSubject.send(accessItems)
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,15 +14,14 @@
// limitations under the License.
//
import Foundation
import Combine
import Foundation
class MockRoomAccessTypeChooserService: RoomAccessTypeChooserServiceProtocol {
static let mockAccessItems: [RoomAccessTypeChooserAccessItem] = [
RoomAccessTypeChooserAccessItem(id: .private, isSelected: true, title: VectorL10n.private, detail: VectorL10n.roomAccessSettingsScreenPrivateMessage, badgeText: nil),
RoomAccessTypeChooserAccessItem(id: .restricted, isSelected: false, title: VectorL10n.createRoomTypeRestricted, detail: VectorL10n.roomAccessSettingsScreenRestrictedMessage, badgeText: VectorL10n.roomAccessSettingsScreenUpgradeRequired),
RoomAccessTypeChooserAccessItem(id: .public, isSelected: false, title: VectorL10n.public, detail: VectorL10n.roomAccessSettingsScreenPublicMessage, badgeText: nil),
RoomAccessTypeChooserAccessItem(id: .public, isSelected: false, title: VectorL10n.public, detail: VectorL10n.roomAccessSettingsScreenPublicMessage, badgeText: nil)
]
private(set) var accessItemsSubject: CurrentValueSubject<[RoomAccessTypeChooserAccessItem], Never>
@@ -31,9 +30,9 @@ class MockRoomAccessTypeChooserService: RoomAccessTypeChooserServiceProtocol {
private(set) var errorSubject: CurrentValueSubject<Error?, Never>
private(set) var selectedType: RoomAccessTypeChooserAccessType = .private
var currentRoomId: String = "!aaabaa:matrix.org"
var currentRoomId = "!aaabaa:matrix.org"
var versionOverride: String? {
return "9"
"9"
}
init(accessItems: [RoomAccessTypeChooserAccessItem] = mockAccessItems) {
@@ -44,18 +43,14 @@ class MockRoomAccessTypeChooserService: RoomAccessTypeChooserServiceProtocol {
}
func simulateUpdate(accessItems: [RoomAccessTypeChooserAccessItem]) {
self.accessItemsSubject.send(accessItems)
accessItemsSubject.send(accessItems)
}
func updateSelection(with selectedType: RoomAccessTypeChooserAccessType) {
}
func updateSelection(with selectedType: RoomAccessTypeChooserAccessType) { }
func updateRoomId(with roomId: String) {
currentRoomId = roomId
}
func applySelection(completion: @escaping () -> Void) {
}
func applySelection(completion: @escaping () -> Void) { }
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,8 +14,8 @@
// limitations under the License.
//
import Foundation
import Combine
import Foundation
protocol RoomAccessTypeChooserServiceProtocol {
var accessItemsSubject: CurrentValueSubject<[RoomAccessTypeChooserAccessItem], Never> { get }
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,8 +14,8 @@
// limitations under the License.
//
import XCTest
import RiotSwiftUI
import XCTest
class RoomAccessTypeChooserUITests: MockScreenTestCase {
// Tests to be implemented.
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,11 +14,9 @@
// limitations under the License.
//
import XCTest
import Combine
import XCTest
@testable import RiotSwiftUI
class RoomAccessTypeChooserViewModelTests: XCTestCase {
}
class RoomAccessTypeChooserViewModelTests: XCTestCase { }
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct RoomAccessTypeChooser: View {
// MARK: - Properties
// MARK: Private
@@ -55,7 +54,7 @@ struct RoomAccessTypeChooser: View {
@ViewBuilder
private var listContent: some View {
ScrollView{
ScrollView {
VStack(alignment: .leading) {
Text(VectorL10n.roomAccessSettingsScreenTitle)
.foregroundColor(theme.colors.primaryContent)
@@ -84,7 +83,6 @@ struct RoomAccessTypeChooser: View {
// MARK: - Previews
struct RoomAccessTypeChooser_Previews: PreviewProvider {
static let stateRenderer = MockRoomAccessTypeChooserScreenState.stateRenderer
static var previews: some View {
stateRenderer.screenGroup(addNavigation: true)
@@ -17,7 +17,6 @@
import SwiftUI
struct RoomAccessTypeChooserRow: View {
// MARK: - Properties
// MARK: Private
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
class RoomRestrictedAccessSpaceChooserViewProvider: MatrixItemChooserCoordinatorViewProvider {
private let navTitle: String?
init(navTitle: String?) {
@@ -25,6 +24,6 @@ class RoomRestrictedAccessSpaceChooserViewProvider: MatrixItemChooserCoordinator
}
func view(with viewModel: MatrixItemChooserViewModelType.Context) -> AnyView {
return AnyView(RoomRestrictedAccessSpaceChooserSelector(viewModel: viewModel, navTitle: navTitle))
AnyView(RoomRestrictedAccessSpaceChooserSelector(viewModel: viewModel, navTitle: navTitle))
}
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import Foundation
class RoomRestrictedAccessSpaceChooserItemsProcessor: MatrixItemChooserProcessorProtocol {
// MARK: Private
private let roomId: String
@@ -28,7 +27,7 @@ class RoomRestrictedAccessSpaceChooserItemsProcessor: MatrixItemChooserProcessor
init(roomId: String, session: MXSession) {
self.roomId = roomId
self.session = session
self.dataSource = MatrixItemChooserRoomRestrictedAllowedParentsDataSource(roomId: roomId)
dataSource = MatrixItemChooserRoomRestrictedAllowedParentsDataSource(roomId: roomId)
}
// MARK: MatrixItemChooserSelectionProcessorProtocol
@@ -50,7 +49,7 @@ class RoomRestrictedAccessSpaceChooserItemsProcessor: MatrixItemChooserProcessor
}
}
func isItemIncluded(_ item: (MatrixListItemData)) -> Bool {
return true
func isItemIncluded(_ item: MatrixListItemData) -> Bool {
true
}
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct RoomRestrictedAccessSpaceChooserSelector: View {
// MARK: Properties
@ObservedObject var viewModel: MatrixItemChooserViewModel.Context
@@ -51,5 +50,4 @@ struct RoomRestrictedAccessSpaceChooserSelector: View {
}
}
}
}
@@ -24,7 +24,6 @@ enum RoomSuggestionCoordinatorCoordinatorAction {
@objcMembers
final class RoomSuggestionCoordinator: Coordinator {
// MARK: - Properties
// MARK: Private
@@ -32,7 +31,7 @@ final class RoomSuggestionCoordinator: Coordinator {
private let parameters: RoomSuggestionCoordinatorParameters
private var navigationRouter: NavigationRouterType {
return self.parameters.navigationRouter
parameters.navigationRouter
}
// MARK: Public
@@ -46,31 +45,30 @@ final class RoomSuggestionCoordinator: Coordinator {
init(parameters: RoomSuggestionCoordinatorParameters) {
self.parameters = parameters
}
}
// MARK: - Public
func start() {
MXLog.debug("[RoomSuggestionCoordinator] did start.")
let rootCoordinator = self.createRoomSuggestionSpaceChooser()
let rootCoordinator = createRoomSuggestionSpaceChooser()
rootCoordinator.start()
self.add(childCoordinator: rootCoordinator)
add(childCoordinator: rootCoordinator)
if self.navigationRouter.modules.isEmpty == false {
self.navigationRouter.push(rootCoordinator, animated: true, popCompletion: { [weak self] in
if navigationRouter.modules.isEmpty == false {
navigationRouter.push(rootCoordinator, animated: true, popCompletion: { [weak self] in
self?.remove(childCoordinator: rootCoordinator)
})
} else {
self.navigationRouter.setRootModule(rootCoordinator) { [weak self] in
navigationRouter.setRootModule(rootCoordinator) { [weak self] in
self?.remove(childCoordinator: rootCoordinator)
}
}
}
func toPresentable() -> UIViewController {
return self.navigationRouter.toPresentable()
navigationRouter.toPresentable()
}
// MARK: - Private
@@ -78,7 +76,7 @@ final class RoomSuggestionCoordinator: Coordinator {
func pushScreen(with coordinator: Coordinator & Presentable) {
add(childCoordinator: coordinator)
self.navigationRouter.push(coordinator, animated: true, popCompletion: { [weak self] in
navigationRouter.push(coordinator, animated: true, popCompletion: { [weak self] in
self?.remove(childCoordinator: coordinator)
})
@@ -91,7 +89,8 @@ final class RoomSuggestionCoordinator: Coordinator {
title: VectorL10n.roomSuggestionSettingsScreenTitle,
detail: VectorL10n.roomSuggestionSettingsScreenMessage,
viewProvider: RoomSuggestionSpaceChooserViewProvider(navTitle: VectorL10n.roomAccessSettingsScreenNavTitle),
itemsProcessor: RoomSuggestionSpaceChooserItemsProcessor(roomId: parameters.room.roomId, session: parameters.room.mxSession))
itemsProcessor: RoomSuggestionSpaceChooserItemsProcessor(roomId: parameters.room.roomId, session: parameters.room.mxSession)
)
let coordinator = MatrixItemChooserCoordinator(parameters: paramaters)
coordinator.completion = { [weak self] result in
guard let self = self else { return }
@@ -107,5 +106,4 @@ final class RoomSuggestionCoordinator: Coordinator {
}
return coordinator
}
}
@@ -26,7 +26,6 @@ import UIKit
/// Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator.
@objcMembers
final class RoomSuggestionCoordinatorBridgePresenter: NSObject {
// MARK: - Properties
// MARK: Private
@@ -70,7 +69,7 @@ final class RoomSuggestionCoordinatorBridgePresenter: NSObject {
}
func dismiss(animated: Bool, completion: (() -> Void)?) {
guard let coordinator = self.coordinator else {
guard let coordinator = coordinator else {
return
}
coordinator.toPresentable().dismiss(animated: animated) {
@@ -86,9 +85,7 @@ final class RoomSuggestionCoordinatorBridgePresenter: NSObject {
// MARK: - UIAdaptivePresentationControllerDelegate
extension RoomSuggestionCoordinatorBridgePresenter: UIAdaptivePresentationControllerDelegate {
func roomNotificationSettingsCoordinatorDidComplete(_ presentationController: UIPresentationController) {
self.delegate?.roomSuggestionCoordinatorBridgePresenterDelegateDidCancel(self)
delegate?.roomSuggestionCoordinatorBridgePresenterDelegateDidCancel(self)
}
}
@@ -18,7 +18,6 @@ import Foundation
/// RoomSuggestionCoordinator input parameters
struct RoomSuggestionCoordinatorParameters {
/// The Matrix room
let room: MXRoom
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
class RoomSuggestionSpaceChooserViewProvider: MatrixItemChooserCoordinatorViewProvider {
private let navTitle: String?
init(navTitle: String?) {
@@ -25,6 +24,6 @@ class RoomSuggestionSpaceChooserViewProvider: MatrixItemChooserCoordinatorViewPr
}
func view(with viewModel: MatrixItemChooserViewModelType.Context) -> AnyView {
return AnyView(RoomSuggestionSpaceChooserSelector(viewModel: viewModel, navTitle: navTitle))
AnyView(RoomSuggestionSpaceChooserSelector(viewModel: viewModel, navTitle: navTitle))
}
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +22,6 @@ public enum RoomSuggestionSpaceChooserItemsProcessorError: Int, Error {
}
class RoomSuggestionSpaceChooserItemsProcessor: MatrixItemChooserProcessorProtocol {
// MARK: Private
private let roomId: String
@@ -35,7 +34,7 @@ class RoomSuggestionSpaceChooserItemsProcessor: MatrixItemChooserProcessorProtoc
init(roomId: String, session: MXSession) {
self.roomId = roomId
self.session = session
self.dataSource = MatrixItemChooserRoomDirectParentsDataSource(roomId: roomId, preselectionMode: .suggestedRoom)
dataSource = MatrixItemChooserRoomDirectParentsDataSource(roomId: roomId, preselectionMode: .suggestedRoom)
}
deinit {
@@ -43,6 +42,7 @@ class RoomSuggestionSpaceChooserItemsProcessor: MatrixItemChooserProcessorProtoc
NotificationCenter.default.removeObserver(observer)
}
}
// MARK: MatrixItemChooserSelectionProcessorProtocol
private(set) var dataSource: MatrixItemChooserDataSource
@@ -53,11 +53,11 @@ class RoomSuggestionSpaceChooserItemsProcessor: MatrixItemChooserProcessorProtoc
let unselectedItems: [String]
let selectedItems: [String]
if let preselectedItems = dataSource.preselectedItemIds {
unselectedItems = preselectedItems.compactMap({ itemId in
return !itemsIds.contains(itemId) ? itemId : nil
})
unselectedItems = preselectedItems.compactMap { itemId in
!itemsIds.contains(itemId) ? itemId : nil
}
selectedItems = itemsIds.compactMap { itemId in
return !preselectedItems.contains(itemId) ? itemId : nil
!preselectedItems.contains(itemId) ? itemId : nil
}
} else {
unselectedItems = []
@@ -78,7 +78,7 @@ class RoomSuggestionSpaceChooserItemsProcessor: MatrixItemChooserProcessorProtoc
if let firstError = self.computationErrorList.first {
completion(.failure(firstError))
} else {
self.didBuildSpaceGraphObserver = NotificationCenter.default.addObserver(forName: MXSpaceService.didBuildSpaceGraph, object: nil, queue: OperationQueue.main) { [weak self] notification in
self.didBuildSpaceGraphObserver = NotificationCenter.default.addObserver(forName: MXSpaceService.didBuildSpaceGraph, object: nil, queue: OperationQueue.main) { [weak self] _ in
guard let self = self else { return }
if let observer = self.didBuildSpaceGraphObserver {
@@ -93,8 +93,8 @@ class RoomSuggestionSpaceChooserItemsProcessor: MatrixItemChooserProcessorProtoc
}
}
func isItemIncluded(_ item: (MatrixListItemData)) -> Bool {
return true
func isItemIncluded(_ item: MatrixListItemData) -> Bool {
true
}
// MARK: - Private
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct RoomSuggestionSpaceChooserSelector: View {
// MARK: Properties
@ObservedObject var viewModel: MatrixItemChooserViewModel.Context
@@ -48,5 +47,4 @@ struct RoomSuggestionSpaceChooserSelector: View {
}
}
}
}
@@ -14,8 +14,8 @@
// limitations under the License.
//
import SwiftUI
import MatrixSDK
import SwiftUI
struct RoomUpgradeCoordinatorParameters {
let session: MXSession
@@ -25,7 +25,6 @@ struct RoomUpgradeCoordinatorParameters {
}
final class RoomUpgradeCoordinator: Coordinator, Presentable {
// MARK: - Properties
// MARK: Private
@@ -53,6 +52,7 @@ final class RoomUpgradeCoordinator: Coordinator, Presentable {
}
// MARK: - Public
func start() {
MXLog.debug("[RoomUpgradeCoordinator] did start.")
roomUpgradeViewModel.completion = { [weak self] result in
@@ -68,6 +68,6 @@ final class RoomUpgradeCoordinator: Coordinator, Presentable {
}
func toPresentable() -> UIViewController {
return self.roomUpgradeHostingController
roomUpgradeHostingController
}
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -36,7 +36,7 @@ enum MockRoomUpgradeScreenState: MockScreenState, CaseIterable {
}
/// Generate the view struct for the screen state.
var screenView: ([Any], AnyView) {
var screenView: ([Any], AnyView) {
let service: MockRoomUpgradeService
switch self {
case .initial:
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,14 +14,13 @@
// limitations under the License.
//
import SwiftUI
import Combine
import SwiftUI
typealias RoomUpgradeViewModelType = StateStoreViewModel<RoomUpgradeViewState,
Never,
RoomUpgradeViewAction>
Never,
RoomUpgradeViewAction>
class RoomUpgradeViewModel: RoomUpgradeViewModelType, RoomUpgradeViewModelProtocol {
// MARK: - Properties
// MARK: Private
@@ -35,7 +34,7 @@ class RoomUpgradeViewModel: RoomUpgradeViewModelType, RoomUpgradeViewModelProtoc
// MARK: - Setup
static func makeRoomUpgradeViewModel(roomUpgradeService: RoomUpgradeServiceProtocol) -> RoomUpgradeViewModelProtocol {
return RoomUpgradeViewModel(roomUpgradeService: roomUpgradeService)
RoomUpgradeViewModel(roomUpgradeService: roomUpgradeService)
}
private init(roomUpgradeService: RoomUpgradeServiceProtocol) {
@@ -45,7 +44,7 @@ class RoomUpgradeViewModel: RoomUpgradeViewModelType, RoomUpgradeViewModelProtoc
}
private static func defaultState(roomUpgradeService: RoomUpgradeServiceProtocol) -> RoomUpgradeViewState {
return RoomUpgradeViewState(waitingMessage: nil, isLoading: false, parentSpaceName: roomUpgradeService.parentSpaceName)
RoomUpgradeViewState(waitingMessage: nil, isLoading: false, parentSpaceName: roomUpgradeService.parentSpaceName)
}
private func setupObservers() {
@@ -53,7 +52,7 @@ class RoomUpgradeViewModel: RoomUpgradeViewModelType, RoomUpgradeViewModelProtoc
.upgradingSubject
.sink { [weak self] isUpgrading in
self?.state.isLoading = isUpgrading
self?.state.waitingMessage = isUpgrading ? VectorL10n.roomAccessSettingsScreenUpgradeAlertUpgrading: nil
self?.state.waitingMessage = isUpgrading ? VectorL10n.roomAccessSettingsScreenUpgradeAlertUpgrading : nil
}
.store(in: &cancellables)
}
@@ -65,7 +64,7 @@ class RoomUpgradeViewModel: RoomUpgradeViewModelType, RoomUpgradeViewModelProtoc
case .cancel:
completion?(.cancel(roomUpgradeService.currentRoomId))
case .done(let autoInviteUsers):
roomUpgradeService.upgradeRoom(autoInviteUsers: autoInviteUsers) { [weak self] success, roomId in
roomUpgradeService.upgradeRoom(autoInviteUsers: autoInviteUsers) { [weak self] success, _ in
guard let self = self else { return }
if success {
self.completion?(.done(self.roomUpgradeService.currentRoomId))
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import Foundation
protocol RoomUpgradeViewModelProtocol {
var completion: ((RoomUpgradeViewModelResult) -> Void)? { get set }
static func makeRoomUpgradeViewModel(roomUpgradeService: RoomUpgradeServiceProtocol) -> RoomUpgradeViewModelProtocol
var context: RoomUpgradeViewModelType.Context { get }
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,12 +14,11 @@
// limitations under the License.
//
import Foundation
import Combine
import Foundation
import MatrixSDK
class RoomUpgradeService: RoomUpgradeServiceProtocol {
// MARK: - Properties
// MARK: Private
@@ -37,7 +36,7 @@ class RoomUpgradeService: RoomUpgradeServiceProtocol {
private(set) var currentRoomId: String
var parentSpaceName: String? {
guard let parentId = self.parentSpaceId else {
guard let parentId = parentSpaceId else {
return nil
}
@@ -53,11 +52,11 @@ class RoomUpgradeService: RoomUpgradeServiceProtocol {
init(session: MXSession, roomId: String, parentSpaceId: String?, versionOverride: String) {
self.session = session
self.currentRoomId = roomId
currentRoomId = roomId
self.parentSpaceId = parentSpaceId
self.versionOverride = versionOverride
self.upgradingSubject = CurrentValueSubject(false)
self.errorSubject = CurrentValueSubject(nil)
upgradingSubject = CurrentValueSubject(false)
errorSubject = CurrentValueSubject(nil)
}
deinit {
@@ -70,18 +69,18 @@ class RoomUpgradeService: RoomUpgradeServiceProtocol {
func upgradeRoom(autoInviteUsers: Bool, completion: @escaping (Bool, String) -> Void) {
upgradingSubject.send(true)
if autoInviteUsers, let room = session.room(withRoomId: self.currentRoomId) {
self.currentOperation = room.members { [weak self] response in
if autoInviteUsers, let room = session.room(withRoomId: currentRoomId) {
currentOperation = room.members { [weak self] response in
guard let self = self else { return }
switch response {
case .success(let members):
let memberIds: [String] = members?.members.compactMap({ member in
let memberIds: [String] = members?.members.compactMap { member in
guard member.membership == .join, member.userId != self.session.myUserId else {
return nil
}
return member.userId
}) ?? []
} ?? []
self.upgradeRoom(to: self.versionOverride, inviteUsers: memberIds, completion: completion)
case .failure(let error):
self.upgradingSubject.send(false)
@@ -89,7 +88,7 @@ class RoomUpgradeService: RoomUpgradeServiceProtocol {
}
}
} else {
self.upgradeRoom(to: versionOverride, inviteUsers: [], completion: completion)
upgradeRoom(to: versionOverride, inviteUsers: [], completion: completion)
}
}
@@ -98,7 +97,7 @@ class RoomUpgradeService: RoomUpgradeServiceProtocol {
private func upgradeRoom(to versionOverride: String, inviteUsers userIds: [String], completion: @escaping (Bool, String) -> Void) {
// Need to disable graph update during this process as a lot of syncs will occure
session.spaceService.graphUpdateEnabled = false
currentOperation = session.matrixRestClient.upgradeRoom(withId: self.currentRoomId, to: versionOverride) { [weak self] response in
currentOperation = session.matrixRestClient.upgradeRoom(withId: currentRoomId, to: versionOverride) { [weak self] response in
guard let self = self else { return }
switch response {
@@ -108,7 +107,7 @@ class RoomUpgradeService: RoomUpgradeServiceProtocol {
let parentSpaces = self.session.spaceService.directParentIds(ofRoomWithId: oldRoomId)
self.moveRoom(from: oldRoomId, to: replacementRoomId, within: Array(parentSpaces), at: 0) {
self.session.spaceService.graphUpdateEnabled = true
self.didBuildSpaceGraphObserver = NotificationCenter.default.addObserver(forName: MXSpaceService.didBuildSpaceGraph, object: nil, queue: OperationQueue.main) { [weak self] notification in
self.didBuildSpaceGraphObserver = NotificationCenter.default.addObserver(forName: MXSpaceService.didBuildSpaceGraph, object: nil, queue: OperationQueue.main) { [weak self] _ in
guard let self = self else { return }
if let observer = self.didBuildSpaceGraphObserver {
@@ -144,7 +143,7 @@ class RoomUpgradeService: RoomUpgradeServiceProtocol {
}
space.moveChild(withRoomId: roomId, to: newRoomId) { [weak self] response in
guard let self = self else { return }
guard let self = self else { return }
if let error = response.error {
MXLog.warning("[RoomUpgradeService] moveRoom \(roomId) to \(newRoomId) within \(space.spaceId): failed due to error: \(error)")
@@ -158,13 +157,13 @@ class RoomUpgradeService: RoomUpgradeServiceProtocol {
/// Recurse to the next index once done.
private func inviteUser(from userIds: [String], at index: Int, completion: @escaping (Bool, String) -> Void) {
guard index < userIds.count else {
self.upgradingSubject.send(false)
upgradingSubject.send(false)
completion(true, currentRoomId)
return
}
currentOperation = session.matrixRestClient.invite(.userId(userIds[index]), toRoom: currentRoomId) { [weak self] response in
guard let self = self else { return }
guard let self = self else { return }
self.currentOperation = nil
if let error = response.error {
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,24 +14,22 @@
// limitations under the License.
//
import Foundation
import Combine
import Foundation
class MockRoomUpgradeService: RoomUpgradeServiceProtocol {
var currentRoomId: String = "!sfdlksjdflkfjds:matrix.org"
var currentRoomId = "!sfdlksjdflkfjds:matrix.org"
var errorSubject: CurrentValueSubject<Error?, Never>
var upgradingSubject: CurrentValueSubject<Bool, Never>
var parentSpaceName: String? {
return "Parent space name"
"Parent space name"
}
init() {
self.errorSubject = CurrentValueSubject(nil)
self.upgradingSubject = CurrentValueSubject(false)
errorSubject = CurrentValueSubject(nil)
upgradingSubject = CurrentValueSubject(false)
}
func upgradeRoom(autoInviteUsers: Bool, completion: @escaping (Bool, String) -> Void) {
}
func upgradeRoom(autoInviteUsers: Bool, completion: @escaping (Bool, String) -> Void) { }
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,8 +14,8 @@
// limitations under the License.
//
import Foundation
import Combine
import Foundation
protocol RoomUpgradeServiceProtocol {
var currentRoomId: String { get }
@@ -14,8 +14,8 @@
// limitations under the License.
//
import XCTest
import RiotSwiftUI
import XCTest
class RoomUpgradeUITests: MockScreenTestCase {
// Tests to be implemented.
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,8 +14,8 @@
// limitations under the License.
//
import XCTest
import Combine
import XCTest
@testable import RiotSwiftUI
@@ -24,7 +24,5 @@ class RoomUpgradeViewModelTests: XCTestCase {
var viewModel: RoomUpgradeViewModelProtocol!
var context: RoomUpgradeViewModelType.Context!
override func setUpWithError() throws {
}
override func setUpWithError() throws { }
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,10 +17,9 @@
import SwiftUI
struct RoomUpgrade: View {
// MARK: - Properties
@State var autoInviteUsers: Bool = true
@State var autoInviteUsers = true
// MARK: Private
@@ -86,13 +85,13 @@ struct RoomUpgrade: View {
.padding(.horizontal, 24)
.padding(.vertical, 16)
}
.background(RoundedRectangle.init(cornerRadius: 8).foregroundColor(theme.colors.background))
.background(RoundedRectangle(cornerRadius: 8).foregroundColor(theme.colors.background))
.padding(.horizontal, 20)
.frame(minWidth: 0, maxWidth: 500)
}
private func noteText(_ message: String) -> some View {
return Text(message)
Text(message)
.multilineTextAlignment(.center)
.font(theme.fonts.subheadline)
.foregroundColor(theme.colors.secondaryContent)
@@ -14,9 +14,9 @@
// limitations under the License.
//
import SwiftUI
import MatrixSDK
import Combine
import MatrixSDK
import SwiftUI
struct TimelinePollCoordinatorParameters {
let session: MXSession
@@ -25,7 +25,6 @@ struct TimelinePollCoordinatorParameters {
}
final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDelegate {
// MARK: - Properties
// MARK: Private
@@ -81,25 +80,24 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel
}
// MARK: - Public
func start() {
}
func start() { }
func toPresentable() -> UIViewController {
return VectorHostingController(rootView: TimelinePollView(viewModel: viewModel.context),
forceZeroSafeAreaInsets: true)
VectorHostingController(rootView: TimelinePollView(viewModel: viewModel.context),
forceZeroSafeAreaInsets: true)
}
func canEndPoll() -> Bool {
return pollAggregator.poll.isClosed == false
pollAggregator.poll.isClosed == false
}
func canEditPoll() -> Bool {
return pollAggregator.poll.isClosed == false && pollAggregator.poll.totalAnswerCount == 0
pollAggregator.poll.isClosed == false && pollAggregator.poll.totalAnswerCount == 0
}
func endPoll() {
parameters.room.sendPollEnd(for: parameters.pollStartEvent, threadId: nil, localEcho: nil, success: nil) { [weak self] error in
parameters.room.sendPollEnd(for: parameters.pollStartEvent, threadId: nil, localEcho: nil, success: nil) { [weak self] _ in
self?.viewModel.showClosingFailure()
}
}
@@ -110,17 +108,11 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel
viewModel.updateWithPollDetails(buildTimelinePollFrom(aggregator.poll))
}
func pollAggregatorDidStartLoading(_ aggregator: PollAggregator) {
}
func pollAggregatorDidStartLoading(_ aggregator: PollAggregator) { }
func pollAggregatorDidEndLoading(_ aggregator: PollAggregator) {
}
func pollAggregatorDidEndLoading(_ aggregator: PollAggregator) { }
func pollAggregator(_ aggregator: PollAggregator, didFailWithError: Error) {
}
func pollAggregator(_ aggregator: PollAggregator, didFailWithError: Error) { }
// MARK: - Private
@@ -129,19 +121,19 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel
func buildTimelinePollFrom(_ poll: PollProtocol) -> TimelinePollDetails {
let answerOptions = poll.answerOptions.map { pollAnswerOption in
TimelinePollAnswerOption(id: pollAnswerOption.id,
text: pollAnswerOption.text,
count: pollAnswerOption.count,
winner: pollAnswerOption.isWinner,
selected: pollAnswerOption.isCurrentUserSelection)
text: pollAnswerOption.text,
count: pollAnswerOption.count,
winner: pollAnswerOption.isWinner,
selected: pollAnswerOption.isCurrentUserSelection)
}
return TimelinePollDetails(question: poll.text,
answerOptions: answerOptions,
closed: poll.isClosed,
totalAnswerCount: poll.totalAnswerCount,
type: pollKindToTimelinePollType(poll.kind),
maxAllowedSelections: poll.maxAllowedSelections,
hasBeenEdited: poll.hasBeenEdited)
answerOptions: answerOptions,
closed: poll.isClosed,
totalAnswerCount: poll.totalAnswerCount,
type: pollKindToTimelinePollType(poll.kind),
maxAllowedSelections: poll.maxAllowedSelections,
hasBeenEdited: poll.hasBeenEdited)
}
private func pollKindToTimelinePollType(_ kind: PollKind) -> TimelinePollType {
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,9 +22,7 @@ class TimelinePollProvider {
var session: MXSession?
var coordinatorsForEventIdentifiers = [String: TimelinePollCoordinator]()
private init() {
}
private init() { }
/// Create or retrieve the poll timeline coordinator for this event and return
/// a view to be displayed in the timeline
@@ -49,6 +47,6 @@ class TimelinePollProvider {
/// Retrieve the poll timeline coordinator for the given event or nil if it hasn't been created yet
func timelinePollCoordinatorForEventIdentifier(_ eventIdentifier: String) -> TimelinePollCoordinator? {
return coordinatorsForEventIdentifiers[eventIdentifier]
coordinatorsForEventIdentifiers[eventIdentifier]
}
}
@@ -14,8 +14,8 @@
// limitations under the License.
//
import XCTest
import RiotSwiftUI
import XCTest
class TimelinePollUITests: MockScreenTestCase {
func testOpenDisclosedPoll() {
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,8 +14,8 @@
// limitations under the License.
//
import XCTest
import Combine
import XCTest
@testable import RiotSwiftUI
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,7 @@
import Foundation
import SwiftUI
typealias TimelinePollViewModelCallback = ((TimelinePollViewModelResult) -> Void)
typealias TimelinePollViewModelCallback = (TimelinePollViewModelResult) -> Void
enum TimelinePollViewAction {
case selectAnswerOptionWithIdentifier(String)
@@ -63,7 +63,7 @@ struct TimelinePollDetails {
var totalAnswerCount: UInt
var type: TimelinePollType
var maxAllowedSelections: UInt
var hasBeenEdited: Bool = true
var hasBeenEdited = true
init(question: String, answerOptions: [TimelinePollAnswerOption],
closed: Bool,
@@ -81,7 +81,7 @@ struct TimelinePollDetails {
}
var hasCurrentUserVoted: Bool {
answerOptions.filter { $0.selected == true}.count > 0
answerOptions.filter { $0.selected == true }.count > 0
}
var shouldDiscloseResults: Bool {
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,16 +27,16 @@ enum MockTimelinePollScreenState: MockScreenState, CaseIterable {
TimelinePollDetails.self
}
var screenView: ([Any], AnyView) {
var screenView: ([Any], AnyView) {
let answerOptions = [TimelinePollAnswerOption(id: "1", text: "First", count: 10, winner: false, selected: false),
TimelinePollAnswerOption(id: "2", text: "Second", count: 5, winner: false, selected: true),
TimelinePollAnswerOption(id: "3", text: "Third", count: 15, winner: true, selected: false)]
let poll = TimelinePollDetails(question: "Question",
answerOptions: answerOptions,
closed: (self == .closedDisclosed || self == .closedUndisclosed ? true : false),
closed: self == .closedDisclosed || self == .closedUndisclosed ? true : false,
totalAnswerCount: 20,
type: (self == .closedDisclosed || self == .openDisclosed ? .disclosed : .undisclosed),
type: self == .closedDisclosed || self == .openDisclosed ? .disclosed : .undisclosed,
maxAllowedSelections: 1,
hasBeenEdited: false)
@@ -14,14 +14,13 @@
// limitations under the License.
//
import SwiftUI
import Combine
import SwiftUI
typealias TimelinePollViewModelType = StateStoreViewModel<TimelinePollViewState,
Never,
TimelinePollViewAction>
Never,
TimelinePollViewAction>
class TimelinePollViewModel: TimelinePollViewModelType, TimelinePollViewModelProtocol {
// MARK: - Properties
// MARK: Private
@@ -40,14 +39,13 @@ class TimelinePollViewModel: TimelinePollViewModelType, TimelinePollViewModelPro
override func process(viewAction: TimelinePollViewAction) {
switch viewAction {
// Update local state. An update will be pushed from the coordinator once sent.
case .selectAnswerOptionWithIdentifier(let identifier):
guard !state.poll.closed else {
return
}
if (state.poll.maxAllowedSelections == 1) {
if state.poll.maxAllowedSelections == 1 {
updateSingleSelectPollLocalState(selectedAnswerIdentifier: identifier, callback: completion)
} else {
updateMultiSelectPollLocalState(&state, selectedAnswerIdentifier: identifier, callback: completion)
@@ -98,12 +96,12 @@ class TimelinePollViewModel: TimelinePollViewModelType, TimelinePollViewModelPro
let isDeselecting = selectedAnswerOptions.filter { $0.id == selectedAnswerIdentifier }.count > 0
if !isDeselecting && selectedAnswerOptions.count >= state.poll.maxAllowedSelections {
if !isDeselecting, selectedAnswerOptions.count >= state.poll.maxAllowedSelections {
return
}
state.poll.answerOptions.updateEach { answerOption in
if (answerOption.id != selectedAnswerIdentifier) {
if answerOption.id != selectedAnswerIdentifier {
return
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct TimelinePollAnswerOptionButton: View {
// MARK: - Properties
// MARK: Private
@@ -47,7 +46,6 @@ struct TimelinePollAnswerOptionButton: View {
var answerOptionLabel: some View {
VStack(alignment: .leading, spacing: 12.0) {
HStack(alignment: .top, spacing: 8.0) {
if !poll.closed {
Image(uiImage: answerOption.selected ? Asset.Images.pollCheckboxSelected.image : Asset.Images.pollCheckboxDefault.image)
}
@@ -56,7 +54,7 @@ struct TimelinePollAnswerOptionButton: View {
.font(theme.fonts.body)
.foregroundColor(theme.colors.primaryContent)
if poll.closed && answerOption.winner {
if poll.closed, answerOption.winner {
Spacer()
Image(uiImage: Asset.Images.pollWinnerIcon.image)
}
@@ -69,7 +67,7 @@ struct TimelinePollAnswerOptionButton: View {
.progressViewStyle(LinearProgressViewStyle())
.scaleEffect(x: 1.0, y: 1.2, anchor: .center)
if (poll.shouldDiscloseResults) {
if poll.shouldDiscloseResults {
Text(answerOption.count == 1 ? VectorL10n.pollTimelineOneVote : VectorL10n.pollTimelineVotesCount(Int(answerOption.count)))
.font(theme.fonts.footnote)
.foregroundColor(poll.closed && answerOption.winner ? theme.colors.accent : theme.colors.secondaryContent)
@@ -107,33 +105,33 @@ struct TimelinePollAnswerOptionButton_Previews: PreviewProvider {
VStack {
TimelinePollAnswerOptionButton(poll: buildPoll(closed: false, type: type),
answerOption: buildAnswerOption(selected: false),
action: {})
action: { })
TimelinePollAnswerOptionButton(poll: buildPoll(closed: false, type: type),
answerOption: buildAnswerOption(selected: true),
action: {})
action: { })
TimelinePollAnswerOptionButton(poll: buildPoll(closed: true, type: type),
answerOption: buildAnswerOption(selected: false, winner: false),
action: {})
action: { })
TimelinePollAnswerOptionButton(poll: buildPoll(closed: true, type: type),
answerOption: buildAnswerOption(selected: false, winner: true),
action: {})
action: { })
TimelinePollAnswerOptionButton(poll: buildPoll(closed: true, type: type),
answerOption: buildAnswerOption(selected: true, winner: false),
action: {})
action: { })
TimelinePollAnswerOptionButton(poll: buildPoll(closed: true, type: type),
answerOption: buildAnswerOption(selected: true, winner: true),
action: {})
action: { })
let longText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
TimelinePollAnswerOptionButton(poll: buildPoll(closed: true, type: type),
answerOption: buildAnswerOption(text: longText, selected: true, winner: true),
action: {})
action: { })
}
}
}
@@ -141,12 +139,12 @@ struct TimelinePollAnswerOptionButton_Previews: PreviewProvider {
static func buildPoll(closed: Bool, type: TimelinePollType) -> TimelinePollDetails {
TimelinePollDetails(question: "",
answerOptions: [],
closed: closed,
totalAnswerCount: 100,
type: type,
maxAllowedSelections: 1,
hasBeenEdited: false)
answerOptions: [],
closed: closed,
totalAnswerCount: 100,
type: type,
maxAllowedSelections: 1,
hasBeenEdited: false)
}
static func buildAnswerOption(text: String = "Test", selected: Bool, winner: Bool = false) -> TimelinePollAnswerOption {
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,6 @@
import SwiftUI
struct TimelinePollView: View {
// MARK: - Properties
// MARK: Private
@@ -32,7 +31,6 @@ struct TimelinePollView: View {
let poll = viewModel.viewState.poll
VStack(alignment: .leading, spacing: 16.0) {
Text(poll.question)
.font(theme.fonts.bodySB)
.foregroundColor(theme.colors.primaryContent) +
@@ -77,12 +75,12 @@ struct TimelinePollView: View {
return VectorL10n.pollTimelineTotalNoVotes
case 1:
return (poll.hasCurrentUserVoted || poll.type == .undisclosed ?
VectorL10n.pollTimelineTotalOneVote :
VectorL10n.pollTimelineTotalOneVoteNotVoted)
VectorL10n.pollTimelineTotalOneVote :
VectorL10n.pollTimelineTotalOneVoteNotVoted)
default:
return (poll.hasCurrentUserVoted || poll.type == .undisclosed ?
VectorL10n.pollTimelineTotalVotes(Int(poll.totalAnswerCount)) :
VectorL10n.pollTimelineTotalVotesNotVoted(Int(poll.totalAnswerCount)))
VectorL10n.pollTimelineTotalVotes(Int(poll.totalAnswerCount)) :
VectorL10n.pollTimelineTotalVotesNotVoted(Int(poll.totalAnswerCount)))
}
}
@@ -14,10 +14,10 @@
// limitations under the License.
//
import Foundation
import UIKit
import SwiftUI
import Combine
import Foundation
import SwiftUI
import UIKit
protocol UserSuggestionCoordinatorDelegate: AnyObject {
func userSuggestionCoordinator(_ coordinator: UserSuggestionCoordinator, didRequestMentionForMember member: MXRoomMember, textTrigger: String?)
@@ -30,7 +30,6 @@ struct UserSuggestionCoordinatorParameters {
}
final class UserSuggestionCoordinator: Coordinator, Presentable {
// MARK: - Properties
// MARK: Private
@@ -94,12 +93,11 @@ final class UserSuggestionCoordinator: Coordinator, Presentable {
}
// MARK: - Public
func start() {
}
func start() { }
func toPresentable() -> UIViewController {
return self.userSuggestionHostingController
userSuggestionHostingController
}
// MARK: - Private
@@ -131,13 +129,12 @@ final class UserSuggestionCoordinator: Coordinator, Presentable {
}
private class UserSuggestionCoordinatorRoomMemberProvider: RoomMembersProviderProtocol {
private let room: MXRoom
var roomMembers: [MXRoomMember] = []
init(room: MXRoom) {
self.room = room;
self.room = room
}
func fetchMembers(_ members: @escaping ([RoomMembersProviderMember]) -> Void) {
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,10 +24,9 @@ protocol UserSuggestionCoordinatorBridgeDelegate: AnyObject {
@objcMembers
final class UserSuggestionCoordinatorBridge: NSObject {
private var _userSuggestionCoordinator: Any? = nil
private var _userSuggestionCoordinator: Any?
fileprivate var userSuggestionCoordinator: UserSuggestionCoordinator {
return _userSuggestionCoordinator as! UserSuggestionCoordinator
_userSuggestionCoordinator as! UserSuggestionCoordinator
}
weak var delegate: UserSuggestionCoordinatorBridgeDelegate?
@@ -35,7 +34,7 @@ final class UserSuggestionCoordinatorBridge: NSObject {
init(mediaManager: MXMediaManager, room: MXRoom) {
let parameters = UserSuggestionCoordinatorParameters(mediaManager: mediaManager, room: room)
let userSuggestionCoordinator = UserSuggestionCoordinator(parameters: parameters)
self._userSuggestionCoordinator = userSuggestionCoordinator
_userSuggestionCoordinator = userSuggestionCoordinator
super.init()
@@ -43,11 +42,11 @@ final class UserSuggestionCoordinatorBridge: NSObject {
}
func processTextMessage(_ textMessage: String) {
return self.userSuggestionCoordinator.processTextMessage(textMessage)
userSuggestionCoordinator.processTextMessage(textMessage)
}
func toPresentable() -> UIViewController? {
return self.userSuggestionCoordinator.toPresentable()
userSuggestionCoordinator.toPresentable()
}
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,8 +14,8 @@
// limitations under the License.
//
import Foundation
import Combine
import Foundation
struct RoomMembersProviderMember {
var userId: String
@@ -34,7 +34,6 @@ struct UserSuggestionServiceItem: UserSuggestionItemProtocol {
}
class UserSuggestionService: UserSuggestionServiceProtocol {
// MARK: - Properties
// MARK: Private
@@ -58,7 +57,7 @@ class UserSuggestionService: UserSuggestionServiceProtocol {
init(roomMemberProvider: RoomMembersProviderProtocol, shouldDebounce: Bool = true) {
self.roomMemberProvider = roomMemberProvider
if (shouldDebounce) {
if shouldDebounce {
currentTextTriggerSubject
.debounce(for: 0.5, scheduler: RunLoop.main)
.removeDuplicates()
@@ -79,12 +78,12 @@ class UserSuggestionService: UserSuggestionServiceProtocol {
let lastComponent = textMessage.components(separatedBy: .whitespaces).last,
lastComponent.prefix(while: { $0 == "@" }).count == 1 // Partial username should start with one and only one "@" character
else {
self.items.send([])
self.currentTextTriggerSubject.send(nil)
items.send([])
currentTextTriggerSubject.send(nil)
return
}
self.currentTextTriggerSubject.send(lastComponent)
currentTextTriggerSubject.send(lastComponent)
}
// MARK: - Private
@@ -105,12 +104,12 @@ class UserSuggestionService: UserSuggestionServiceProtocol {
UserSuggestionServiceItem(userId: member.userId, displayName: member.displayName, avatarUrl: member.avatarUrl)
}
self.items.send(self.suggestionItems.filter({ userSuggestion in
self.items.send(self.suggestionItems.filter { userSuggestion in
let containedInUsername = userSuggestion.userId.lowercased().contains(partialName.lowercased())
let containedInDisplayName = (userSuggestion.displayName ?? "").lowercased().contains(partialName.lowercased())
return (containedInUsername || containedInDisplayName)
}))
})
}
}
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,8 +14,8 @@
// limitations under the License.
//
import Foundation
import Combine
import Foundation
protocol UserSuggestionItemProtocol: Avatarable {
var userId: String { get }
@@ -24,7 +24,6 @@ protocol UserSuggestionItemProtocol: Avatarable {
}
protocol UserSuggestionServiceProtocol {
var items: CurrentValueSubject<[UserSuggestionItemProtocol], Never> { get }
var currentTextTrigger: String? { get }
@@ -38,6 +37,7 @@ extension UserSuggestionItemProtocol {
var mxContentUri: String? {
avatarUrl
}
var matrixItemId: String {
userId
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,8 +14,8 @@
// limitations under the License.
//
import XCTest
import RiotSwiftUI
import XCTest
class UserSuggestionUITests: MockScreenTestCase {
func testUserSuggestionScreen() throws {
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,13 +14,12 @@
// limitations under the License.
//
import XCTest
import Combine
import XCTest
@testable import RiotSwiftUI
class UserSuggestionServiceTests: XCTestCase {
var service: UserSuggestionService?
override func setUp() {
@@ -107,12 +106,11 @@ class UserSuggestionServiceTests: XCTestCase {
extension UserSuggestionServiceTests: RoomMembersProviderProtocol {
func fetchMembers(_ members: @escaping ([RoomMembersProviderMember]) -> Void) {
let users = [("Alice", "@alice:matrix.org"),
("Bob", "@bob:matrix.org")]
members(users.map({ user in
members(users.map { user in
RoomMembersProviderMember(userId: user.1, displayName: user.0, avatarUrl: "")
}))
})
}
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,13 +20,13 @@ import SwiftUI
enum MockUserSuggestionScreenState: MockScreenState, CaseIterable {
case multipleResults
static private var members: [RoomMembersProviderMember]!
private static var members: [RoomMembersProviderMember]!
var screenType: Any.Type {
UserSuggestionList.self
}
var screenView: ([Any], AnyView) {
var screenView: ([Any], AnyView) {
let service = UserSuggestionService(roomMemberProvider: self)
let listViewModel = UserSuggestionViewModel(userSuggestionService: service)
@@ -37,7 +37,7 @@ enum MockUserSuggestionScreenState: MockScreenState, CaseIterable {
return (
[service, listViewModel],
AnyView(UserSuggestionListWithInput(viewModel: viewModel)
.addDependency(MockAvatarService.example))
.addDependency(MockAvatarService.example))
)
}
}
@@ -52,7 +52,7 @@ extension MockUserSuggestionScreenState: RoomMembersProviderProtocol {
}
private func generateUsersWithCount(_ count: UInt) -> [RoomMembersProviderMember] {
return (0..<count).map { _ in
(0..<count).map { _ in
let identifier = "@" + UUID().uuidString
return RoomMembersProviderMember(userId: identifier, displayName: identifier, avatarUrl: "mxc://matrix.org/VyNYAgahaiAzUoOeZETtQ")
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,15 +14,14 @@
// limitations under the License.
//
import SwiftUI
import Combine
import SwiftUI
typealias UserSuggestionViewModelType = StateStoreViewModel <UserSuggestionViewState,
Never,
UserSuggestionViewAction>
typealias UserSuggestionViewModelType = StateStoreViewModel<UserSuggestionViewState,
Never,
UserSuggestionViewAction>
class UserSuggestionViewModel: UserSuggestionViewModelType, UserSuggestionViewModelProtocol {
// MARK: - Properties
// MARK: Private
@@ -39,15 +38,15 @@ class UserSuggestionViewModel: UserSuggestionViewModelType, UserSuggestionViewMo
self.userSuggestionService = userSuggestionService
let items = userSuggestionService.items.value.map { suggestionItem in
return UserSuggestionViewStateItem(id: suggestionItem.userId, avatar: suggestionItem, displayName: suggestionItem.displayName)
UserSuggestionViewStateItem(id: suggestionItem.userId, avatar: suggestionItem, displayName: suggestionItem.displayName)
}
super.init(initialViewState: UserSuggestionViewState(items: items))
userSuggestionService.items.sink { [weak self] items in
self?.state.items = items.map({ item in
self?.state.items = items.map { item in
UserSuggestionViewStateItem(id: item.userId, avatar: item, displayName: item.displayName)
})
}
}.store(in: &cancellables)
}
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,7 +17,7 @@
import SwiftUI
struct UserSuggestionList: View {
private struct Constants {
private enum Constants {
static let topPadding: CGFloat = 8.0
static let listItemPadding: CGFloat = 4.0
static let lineSpacing: CGFloat = 10.0
@@ -57,7 +57,7 @@ struct UserSuggestionList: View {
userId: item.id
)
.padding(.bottom, Constants.listItemPadding)
.padding(.top, (viewModel.viewState.items.first?.id == item.id ? Constants.listItemPadding + Constants.topPadding : Constants.listItemPadding))
.padding(.top, viewModel.viewState.items.first?.id == item.id ? Constants.listItemPadding + Constants.topPadding : Constants.listItemPadding)
}
}
.listStyle(PlainListStyle())
@@ -76,7 +76,6 @@ struct UserSuggestionList: View {
}
private struct BackgroundView<Content: View>: View {
var content: () -> Content
@Environment(\.theme) private var theme: ThemeSwiftUI
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,13 +17,14 @@
import SwiftUI
struct UserSuggestionListItem: View {
// MARK: - Properties
// MARK: Private
@Environment(\.theme) private var theme: ThemeSwiftUI
// MARK: Public
let avatar: AvatarInputProtocol?
let displayName: String?
let userId: String
@@ -1,4 +1,4 @@
//
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,11 +18,10 @@ import SwiftUI
struct UserSuggestionListWithInputViewModel {
let listViewModel: UserSuggestionViewModel
let callback: (String)->()
let callback: (String) -> Void
}
struct UserSuggestionListWithInput: View {
// MARK: - Properties
// MARK: Private
@@ -30,14 +29,14 @@ struct UserSuggestionListWithInput: View {
// MARK: Public
var viewModel: UserSuggestionListWithInputViewModel
@State private var inputText: String = ""
@State private var inputText = ""
var body: some View {
VStack(spacing: 0.0) {
UserSuggestionList(viewModel: viewModel.listViewModel.context)
TextField("Search for user", text: $inputText)
.background(Color.white)
.onChange(of: inputText, perform:viewModel.callback)
.onChange(of: inputText, perform: viewModel.callback)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding([.leading, .trailing])
.onAppear {