mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Switch callbacks to combine (#1710)
* #750 - Convert the SoftLogoutScreen to combine * #750 - Convert the UserSessionFlowCoordinator to Combine * #750 - Convert the AnalyticsPromptScreen to Combine * #750 - Convert the LoginScreen to Combine * #750 - Convert the ServerSelectionScreen to Combine * #750 - Convert the EmojiPickerScreen to Combine * #750 - Convert the HomeScreen to Combine * #750 - Convert the MediaUploadPreviewScreen to Combine * #750 - Convert the OnboardingScreen to Combine * Rename `Onboarding` to `OnboardingScreen` * #750 - Convert the ReportContentScreen to Combine * #750 - Convert the RoomDetailsSscreen to Combine * #750 - Convert the RoomMemberDetailsScreen to Combine * #750 - Convert the RoomMembersListScreen to Combine * #750 - Convert the SessionVerificationScreen to Combine * #750 - Convert the SettingsScreen to Combine * #750 - Convert the AdvancedSettingsScreen to Combine * #750 - Convert the DeveloperOptionsScreen to Combine * Fix the unit tests * Use .sink action and the same cancellables constructor everywhere * Cleanup cancellables when setting up tests
This commit is contained in:
@@ -51,7 +51,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
|
||||
private var appDelegateObserver: AnyCancellable?
|
||||
private var userSessionObserver: AnyCancellable?
|
||||
private var clientProxyObserver: AnyCancellable?
|
||||
private var networkMonitorObserver: AnyCancellable?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
let notificationManager: NotificationManagerProtocol
|
||||
|
||||
@@ -374,15 +374,20 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
|
||||
keyBackupNeeded: false,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
let coordinator = SoftLogoutScreenCoordinator(parameters: parameters)
|
||||
coordinator.callback = { result in
|
||||
switch result {
|
||||
case .signedIn(let session):
|
||||
self.userSession = session
|
||||
self.stateMachine.processEvent(.createdUserSession)
|
||||
case .clearAllData:
|
||||
self.stateMachine.processEvent(.signOut(isSoft: false))
|
||||
|
||||
coordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .signedIn(let session):
|
||||
self.userSession = session
|
||||
stateMachine.processEvent(.createdUserSession)
|
||||
case .clearAllData:
|
||||
stateMachine.processEvent(.signOut(isSoft: false))
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationRootCoordinator.setRootCoordinator(coordinator)
|
||||
}
|
||||
@@ -401,14 +406,18 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
|
||||
appSettings: appSettings,
|
||||
analytics: ServiceLocator.shared.analytics)
|
||||
|
||||
userSessionFlowCoordinator.callback = { [weak self] action in
|
||||
switch action {
|
||||
case .signOut:
|
||||
self?.stateMachine.processEvent(.signOut(isSoft: false))
|
||||
case .clearCache:
|
||||
self?.stateMachine.processEvent(.clearCache)
|
||||
userSessionFlowCoordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .signOut:
|
||||
stateMachine.processEvent(.signOut(isSoft: false))
|
||||
case .clearCache:
|
||||
stateMachine.processEvent(.clearCache)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
userSessionFlowCoordinator.start()
|
||||
|
||||
@@ -528,7 +537,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
|
||||
|
||||
private func observeNetworkState() {
|
||||
let reachabilityNotificationIdentifier = "io.element.elementx.reachability.notification"
|
||||
networkMonitorObserver = ServiceLocator.shared.networkMonitor
|
||||
ServiceLocator.shared.networkMonitor
|
||||
.reachabilityPublisher
|
||||
.removeDuplicates()
|
||||
.sink { reachability in
|
||||
@@ -542,6 +551,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
|
||||
persistent: true))
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func handleAppRoute(_ appRoute: AppRoute) {
|
||||
|
||||
@@ -36,7 +36,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
private let stateMachine: StateMachine<State, Event> = .init(state: .initial)
|
||||
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private let actionsSubject: PassthroughSubject<RoomFlowCoordinatorAction, Never> = .init()
|
||||
var actions: AnyPublisher<RoomFlowCoordinatorAction, Never> {
|
||||
@@ -456,16 +456,22 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
roomProxy: roomProxy,
|
||||
userIndicatorController: userIndicatorController)
|
||||
let coordinator = ReportContentScreenCoordinator(parameters: parameters)
|
||||
coordinator.callback = { [weak self] completion in
|
||||
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
|
||||
switch completion {
|
||||
case .cancel:
|
||||
break
|
||||
case .finish:
|
||||
userIndicatorController.submitIndicator(UserIndicator(title: L10n.commonReportSubmitted, iconName: "checkmark"))
|
||||
|
||||
coordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
|
||||
switch action {
|
||||
case .cancel:
|
||||
break
|
||||
case .finish:
|
||||
userIndicatorController.submitIndicator(UserIndicator(title: L10n.commonReportSubmitted, iconName: "checkmark"))
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationCoordinator.setRootCoordinator(coordinator)
|
||||
navigationStackCoordinator.setSheetCoordinator(userIndicatorController) { [weak self] in
|
||||
self?.stateMachine.tryEvent(.dismissReportContent)
|
||||
@@ -508,12 +514,18 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
title: url.lastPathComponent,
|
||||
url: url)
|
||||
|
||||
let mediaUploadPreviewScreenCoordinator = MediaUploadPreviewScreenCoordinator(parameters: parameters) { [weak self] action in
|
||||
switch action {
|
||||
case .dismiss:
|
||||
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
let mediaUploadPreviewScreenCoordinator = MediaUploadPreviewScreenCoordinator(parameters: parameters)
|
||||
|
||||
mediaUploadPreviewScreenCoordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .dismiss:
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
stackCoordinator.setRootCoordinator(mediaUploadPreviewScreenCoordinator)
|
||||
|
||||
@@ -526,18 +538,22 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
let params = EmojiPickerScreenCoordinatorParameters(emojiProvider: emojiProvider,
|
||||
itemID: itemID, selectedEmojis: selectedEmoji)
|
||||
let coordinator = EmojiPickerScreenCoordinator(parameters: params)
|
||||
coordinator.callback = { [weak self] action in
|
||||
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case let .emojiSelected(emoji: emoji, itemID: itemID):
|
||||
MXLog.debug("Selected \(emoji) for \(itemID)")
|
||||
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
Task {
|
||||
await self?.timelineController?.toggleReaction(emoji, to: itemID)
|
||||
await self.timelineController?.toggleReaction(emoji, to: itemID)
|
||||
}
|
||||
case .dismiss:
|
||||
self?.navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationStackCoordinator.setSheetCoordinator(coordinator) { [weak self] in
|
||||
self?.stateMachine.tryEvent(.dismissEmojiPicker)
|
||||
|
||||
@@ -29,11 +29,12 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private let roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol
|
||||
private let appSettings: AppSettings
|
||||
private let analytics: AnalyticsService
|
||||
private let actionsSubject: PassthroughSubject<UserSessionFlowCoordinatorAction, Never> = .init()
|
||||
|
||||
private let stateMachine: UserSessionFlowCoordinatorStateMachine
|
||||
private let roomFlowCoordinator: RoomFlowCoordinator
|
||||
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var migrationCancellable: AnyCancellable?
|
||||
|
||||
private let sidebarNavigationStackCoordinator: NavigationStackCoordinator
|
||||
@@ -41,7 +42,9 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
private let selectedRoomSubject = CurrentValueSubject<String?, Never>(nil)
|
||||
|
||||
var callback: ((UserSessionFlowCoordinatorAction) -> Void)?
|
||||
var actions: AnyPublisher<UserSessionFlowCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(userSession: UserSessionProtocol,
|
||||
navigationSplitCoordinator: NavigationSplitCoordinator,
|
||||
@@ -263,34 +266,36 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
navigationStackCoordinator: detailNavigationStackCoordinator,
|
||||
selectedRoomPublisher: selectedRoomSubject.asCurrentValuePublisher())
|
||||
let coordinator = HomeScreenCoordinator(parameters: parameters)
|
||||
|
||||
coordinator.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .presentRoom(let roomID):
|
||||
self.roomFlowCoordinator.handleAppRoute(.room(roomID: roomID), animated: true)
|
||||
case .presentRoomDetails(let roomID):
|
||||
self.roomFlowCoordinator.handleAppRoute(.roomDetails(roomID: roomID), animated: true)
|
||||
case .roomLeft(let roomID):
|
||||
if case .roomList(selectedRoomID: let selectedRoomID) = stateMachine.state,
|
||||
selectedRoomID == roomID {
|
||||
self.roomFlowCoordinator.handleAppRoute(.roomList, animated: true)
|
||||
|
||||
coordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .presentRoom(let roomID):
|
||||
roomFlowCoordinator.handleAppRoute(.room(roomID: roomID), animated: true)
|
||||
case .presentRoomDetails(let roomID):
|
||||
roomFlowCoordinator.handleAppRoute(.roomDetails(roomID: roomID), animated: true)
|
||||
case .roomLeft(let roomID):
|
||||
if case .roomList(selectedRoomID: let selectedRoomID) = stateMachine.state,
|
||||
selectedRoomID == roomID {
|
||||
roomFlowCoordinator.handleAppRoute(.roomList, animated: true)
|
||||
}
|
||||
case .presentSettingsScreen:
|
||||
stateMachine.processEvent(.showSettingsScreen)
|
||||
case .presentFeedbackScreen:
|
||||
stateMachine.processEvent(.feedbackScreen)
|
||||
case .presentSessionVerificationScreen:
|
||||
stateMachine.processEvent(.showSessionVerificationScreen)
|
||||
case .presentStartChatScreen:
|
||||
stateMachine.processEvent(.showStartChatScreen)
|
||||
case .signOut:
|
||||
actionsSubject.send(.signOut)
|
||||
case .presentInvitesScreen:
|
||||
stateMachine.processEvent(.showInvitesScreen)
|
||||
}
|
||||
case .presentSettingsScreen:
|
||||
self.stateMachine.processEvent(.showSettingsScreen)
|
||||
case .presentFeedbackScreen:
|
||||
self.stateMachine.processEvent(.feedbackScreen)
|
||||
case .presentSessionVerificationScreen:
|
||||
self.stateMachine.processEvent(.showSessionVerificationScreen)
|
||||
case .presentStartChatScreen:
|
||||
self.stateMachine.processEvent(.showStartChatScreen)
|
||||
case .signOut:
|
||||
self.callback?(.signOut)
|
||||
case .presentInvitesScreen:
|
||||
self.stateMachine.processEvent(.showInvitesScreen)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
sidebarNavigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
}
|
||||
@@ -324,18 +329,22 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
notificationSettings: userSession.clientProxy.notificationSettings,
|
||||
appSettings: appSettings)
|
||||
let settingsScreenCoordinator = SettingsScreenCoordinator(parameters: parameters)
|
||||
settingsScreenCoordinator.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .dismiss:
|
||||
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
case .logout:
|
||||
self.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
self.callback?(.signOut)
|
||||
case .clearCache:
|
||||
self.callback?(.clearCache)
|
||||
|
||||
settingsScreenCoordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .dismiss:
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
case .logout:
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
actionsSubject.send(.signOut)
|
||||
case .clearCache:
|
||||
actionsSubject.send(.clearCache)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
settingsNavigationStackCoordinator.setRootCoordinator(settingsScreenCoordinator, animated: animated)
|
||||
|
||||
@@ -355,9 +364,16 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
let coordinator = SessionVerificationScreenCoordinator(parameters: parameters)
|
||||
|
||||
coordinator.callback = { [weak self] in
|
||||
self?.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
coordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .done:
|
||||
navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationSplitCoordinator.setSheetCoordinator(coordinator, animated: animated) { [weak self] in
|
||||
self?.stateMachine.processEvent(.dismissedSessionVerificationScreen)
|
||||
|
||||
+27
-16
@@ -14,13 +14,22 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
enum AnalyticsPromptScreenCoordinatorAction {
|
||||
case done
|
||||
}
|
||||
|
||||
final class AnalyticsPromptScreenCoordinator: CoordinatorProtocol {
|
||||
private let analytics: AnalyticsService
|
||||
private var viewModel: AnalyticsPromptScreenViewModel
|
||||
|
||||
var callback: (@MainActor () -> Void)?
|
||||
private var viewModel: AnalyticsPromptScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<AnalyticsPromptScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<AnalyticsPromptScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(analytics: AnalyticsService, termsURL: URL) {
|
||||
self.analytics = analytics
|
||||
@@ -30,20 +39,22 @@ final class AnalyticsPromptScreenCoordinator: CoordinatorProtocol {
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
viewModel.callback = { [weak self] result in
|
||||
guard let self else { return }
|
||||
|
||||
switch result {
|
||||
case .enable:
|
||||
MXLog.info("Enable Analytics")
|
||||
analytics.optIn()
|
||||
self.callback?()
|
||||
case .disable:
|
||||
MXLog.info("Disable Analytics")
|
||||
analytics.optOut()
|
||||
self.callback?()
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .enable:
|
||||
MXLog.info("Enable Analytics")
|
||||
analytics.optIn()
|
||||
actionsSubject.send(.done)
|
||||
case .disable:
|
||||
MXLog.info("Disable Analytics")
|
||||
analytics.optOut()
|
||||
actionsSubject.send(.done)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
|
||||
@@ -20,7 +20,11 @@ import SwiftUI
|
||||
typealias AnalyticsPromptScreenViewModelType = StateStoreViewModel<AnalyticsPromptScreenViewState, AnalyticsPromptScreenViewAction>
|
||||
|
||||
class AnalyticsPromptScreenViewModel: AnalyticsPromptScreenViewModelType, AnalyticsPromptScreenViewModelProtocol {
|
||||
var callback: (@MainActor (AnalyticsPromptScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<AnalyticsPromptScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<AnalyticsPromptScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
/// Initialize a view model with the specified prompt type and app display name.
|
||||
init(termsURL: URL) {
|
||||
@@ -33,9 +37,9 @@ class AnalyticsPromptScreenViewModel: AnalyticsPromptScreenViewModelType, Analyt
|
||||
override func process(viewAction: AnalyticsPromptScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .enable:
|
||||
callback?(.enable)
|
||||
actionsSubject.send(.enable)
|
||||
case .disable:
|
||||
callback?(.disable)
|
||||
actionsSubject.send(.disable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol AnalyticsPromptScreenViewModelProtocol {
|
||||
var callback: (@MainActor (AnalyticsPromptScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<AnalyticsPromptScreenViewModelAction, Never> { get }
|
||||
var context: AnalyticsPromptScreenViewModelType.Context { get }
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class AuthenticationCoordinator: CoordinatorProtocol {
|
||||
private let analytics: AnalyticsService
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
|
||||
private var cancellables: Set<AnyCancellable> = []
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
weak var delegate: AuthenticationCoordinatorDelegate?
|
||||
|
||||
@@ -57,15 +57,18 @@ class AuthenticationCoordinator: CoordinatorProtocol {
|
||||
// MARK: - Private
|
||||
|
||||
private func showOnboarding() {
|
||||
let coordinator = OnboardingCoordinator()
|
||||
|
||||
coordinator.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .login:
|
||||
Task { await self.startAuthentication() }
|
||||
let coordinator = OnboardingScreenCoordinator()
|
||||
|
||||
coordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .login:
|
||||
Task { await self.startAuthentication() }
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
}
|
||||
@@ -92,29 +95,31 @@ class AuthenticationCoordinator: CoordinatorProtocol {
|
||||
isModallyPresented: isModallyPresented)
|
||||
let coordinator = ServerSelectionScreenCoordinator(parameters: parameters)
|
||||
|
||||
coordinator.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .updated:
|
||||
if isModallyPresented {
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
} else {
|
||||
// We are here because the default server failed to respond.
|
||||
if authenticationService.homeserver.value.loginMode == .password {
|
||||
// Add the password login screen directly to the flow, its fine.
|
||||
showLoginScreen()
|
||||
coordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .updated:
|
||||
if isModallyPresented {
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
} else {
|
||||
// OIDC is presented from the confirmation screen so replace the
|
||||
// server selection screen which was inserted to handle the failure.
|
||||
navigationStackCoordinator.pop()
|
||||
showServerConfirmationScreen()
|
||||
// We are here because the default server failed to respond.
|
||||
if authenticationService.homeserver.value.loginMode == .password {
|
||||
// Add the password login screen directly to the flow, its fine.
|
||||
showLoginScreen()
|
||||
} else {
|
||||
// OIDC is presented from the confirmation screen so replace the
|
||||
// server selection screen which was inserted to handle the failure.
|
||||
navigationStackCoordinator.pop()
|
||||
showServerConfirmationScreen()
|
||||
}
|
||||
}
|
||||
case .dismiss:
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
case .dismiss:
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
if isModallyPresented {
|
||||
navigationCoordinator.setRootCoordinator(coordinator)
|
||||
@@ -178,20 +183,22 @@ class AuthenticationCoordinator: CoordinatorProtocol {
|
||||
userIndicatorController: userIndicatorController)
|
||||
let coordinator = LoginScreenCoordinator(parameters: parameters)
|
||||
|
||||
coordinator.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
coordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .signedIn(let userSession):
|
||||
userHasSignedIn(userSession: userSession)
|
||||
case .configuredForOIDC:
|
||||
// Pop back to the confirmation screen for OIDC login to continue.
|
||||
navigationStackCoordinator.pop(animated: false)
|
||||
case .isOnWaitlist(let credentials):
|
||||
showWaitlistScreen(for: credentials)
|
||||
switch action {
|
||||
case .signedIn(let userSession):
|
||||
userHasSignedIn(userSession: userSession)
|
||||
case .configuredForOIDC:
|
||||
// Pop back to the confirmation screen for OIDC login to continue.
|
||||
navigationStackCoordinator.pop(animated: false)
|
||||
case .isOnWaitlist(let credentials):
|
||||
showWaitlistScreen(for: credentials)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationStackCoordinator.push(coordinator)
|
||||
}
|
||||
|
||||
@@ -228,10 +235,18 @@ class AuthenticationCoordinator: CoordinatorProtocol {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
|
||||
let coordinator = AnalyticsPromptScreenCoordinator(analytics: analytics, termsURL: appSettings.analyticsConfiguration.termsURL)
|
||||
coordinator.callback = {
|
||||
completion()
|
||||
}
|
||||
|
||||
coordinator.actions
|
||||
.sink { action in
|
||||
switch action {
|
||||
case .done:
|
||||
completion()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationStackCoordinator.push(coordinator)
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,12 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService }
|
||||
|
||||
var callback: (@MainActor (LoginScreenCoordinatorAction) -> Void)?
|
||||
private let actionsSubject: PassthroughSubject<LoginScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<LoginScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
@@ -56,18 +61,20 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
viewModel.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .parseUsername(let username):
|
||||
parseUsername(username)
|
||||
case .forgotPassword:
|
||||
showForgotPasswordScreen()
|
||||
case .login(let username, let password):
|
||||
login(username: username, password: password)
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .parseUsername(let username):
|
||||
parseUsername(username)
|
||||
case .forgotPassword:
|
||||
showForgotPasswordScreen()
|
||||
case .login(let username, let password):
|
||||
login(username: username, password: password)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func stop() {
|
||||
@@ -126,7 +133,7 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
|
||||
initialDeviceName: UIDevice.current.initialDeviceName,
|
||||
deviceID: nil) {
|
||||
case .success(let userSession):
|
||||
callback?(.signedIn(userSession))
|
||||
actionsSubject.send(.signedIn(userSession))
|
||||
parameters.analytics.signpost.endLogin()
|
||||
stopLoading()
|
||||
case .failure(let error):
|
||||
@@ -134,11 +141,11 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
|
||||
parameters.analytics.signpost.endLogin()
|
||||
switch error {
|
||||
case .isOnWaitlist:
|
||||
callback?(.isOnWaitlist(.init(username: username,
|
||||
password: password,
|
||||
initialDeviceName: UIDevice.current.initialDeviceName,
|
||||
deviceID: nil,
|
||||
homeserver: authenticationService.homeserver.value)))
|
||||
actionsSubject.send(.isOnWaitlist(.init(username: username,
|
||||
password: password,
|
||||
initialDeviceName: UIDevice.current.initialDeviceName,
|
||||
deviceID: nil,
|
||||
homeserver: authenticationService.homeserver.value)))
|
||||
default:
|
||||
handleError(error)
|
||||
}
|
||||
@@ -159,7 +166,7 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
|
||||
case .success:
|
||||
stopLoading()
|
||||
if authenticationService.homeserver.value.loginMode == .oidc {
|
||||
callback?(.configuredForOIDC)
|
||||
actionsSubject.send(.configuredForOIDC)
|
||||
} else {
|
||||
updateViewModel()
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias LoginScreenViewModelType = StateStoreViewModel<LoginScreenViewState, LoginScreenViewAction>
|
||||
@@ -21,7 +22,11 @@ typealias LoginScreenViewModelType = StateStoreViewModel<LoginScreenViewState, L
|
||||
class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtocol {
|
||||
private let slidingSyncLearnMoreURL: URL
|
||||
|
||||
var callback: (@MainActor (LoginScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<LoginScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<LoginScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(homeserver: LoginHomeserver, slidingSyncLearnMoreURL: URL) {
|
||||
self.slidingSyncLearnMoreURL = slidingSyncLearnMoreURL
|
||||
@@ -34,11 +39,11 @@ class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtoc
|
||||
override func process(viewAction: LoginScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .parseUsername:
|
||||
callback?(.parseUsername(state.bindings.username))
|
||||
actionsSubject.send(.parseUsername(state.bindings.username))
|
||||
case .forgotPassword:
|
||||
callback?(.forgotPassword)
|
||||
actionsSubject.send(.forgotPassword)
|
||||
case .next:
|
||||
callback?(.login(username: state.bindings.username, password: state.bindings.password))
|
||||
actionsSubject.send(.login(username: state.bindings.username, password: state.bindings.password))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -14,11 +14,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol LoginScreenViewModelProtocol {
|
||||
var callback: (@MainActor (LoginScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<LoginScreenViewModelAction, Never> { get }
|
||||
var context: LoginScreenViewModelType.Context { get }
|
||||
|
||||
/// Update the view to reflect that a new homeserver is being loaded.
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ final class ServerConfirmationScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: ServerConfirmationScreenCoordinatorParameters
|
||||
private var viewModel: ServerConfirmationScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<ServerConfirmationScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<ServerConfirmationScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
|
||||
+19
-11
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct ServerSelectionScreenCoordinatorParameters {
|
||||
@@ -35,7 +36,12 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol {
|
||||
private var viewModel: ServerSelectionScreenViewModelProtocol
|
||||
private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService }
|
||||
|
||||
var callback: (@MainActor (ServerSelectionScreenCoordinatorAction) -> Void)?
|
||||
private let actionsSubject: PassthroughSubject<ServerSelectionScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<ServerSelectionScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: ServerSelectionScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
@@ -48,16 +54,18 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol {
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
viewModel.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .confirm(let homeserverAddress):
|
||||
self.useHomeserver(homeserverAddress)
|
||||
case .dismiss:
|
||||
self.callback?(.dismiss)
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .confirm(let homeserverAddress):
|
||||
self.useHomeserver(homeserverAddress)
|
||||
case .dismiss:
|
||||
actionsSubject.send(.dismiss)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func stop() {
|
||||
@@ -88,7 +96,7 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol {
|
||||
switch await authenticationService.configure(for: homeserverAddress) {
|
||||
case .success:
|
||||
MXLog.info("Selected homeserver: \(homeserverAddress)")
|
||||
callback?(.updated)
|
||||
actionsSubject.send(.updated)
|
||||
stopLoading()
|
||||
case .failure(let error):
|
||||
MXLog.info("Invalid homeserver: \(homeserverAddress)")
|
||||
|
||||
+8
-3
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias ServerSelectionScreenViewModelType = StateStoreViewModel<ServerSelectionScreenViewState, ServerSelectionScreenViewAction>
|
||||
@@ -21,7 +22,11 @@ typealias ServerSelectionScreenViewModelType = StateStoreViewModel<ServerSelecti
|
||||
class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, ServerSelectionScreenViewModelProtocol {
|
||||
private let slidingSyncLearnMoreURL: URL
|
||||
|
||||
var callback: (@MainActor (ServerSelectionScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<ServerSelectionScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<ServerSelectionScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(homeserverAddress: String, slidingSyncLearnMoreURL: URL, isModallyPresented: Bool) {
|
||||
self.slidingSyncLearnMoreURL = slidingSyncLearnMoreURL
|
||||
@@ -35,9 +40,9 @@ class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, Server
|
||||
override func process(viewAction: ServerSelectionScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .confirm:
|
||||
callback?(.confirm(homeserverAddress: state.bindings.homeserverAddress))
|
||||
actionsSubject.send(.confirm(homeserverAddress: state.bindings.homeserverAddress))
|
||||
case .dismiss:
|
||||
callback?(.dismiss)
|
||||
actionsSubject.send(.dismiss)
|
||||
case .clearFooterError:
|
||||
clearFooterError()
|
||||
}
|
||||
|
||||
+2
-2
@@ -14,11 +14,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol ServerSelectionScreenViewModelProtocol {
|
||||
var callback: (@MainActor (ServerSelectionScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<ServerSelectionScreenViewModelAction, Never> { get }
|
||||
var context: ServerSelectionScreenViewModelType.Context { get }
|
||||
|
||||
/// Displays an error to the user.
|
||||
|
||||
+23
-16
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct SoftLogoutScreenCoordinatorParameters {
|
||||
@@ -43,10 +44,14 @@ enum SoftLogoutScreenCoordinatorResult: CustomStringConvertible {
|
||||
final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: SoftLogoutScreenCoordinatorParameters
|
||||
private var viewModel: SoftLogoutScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<SoftLogoutScreenCoordinatorResult, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private var authenticationService: AuthenticationServiceProxyProtocol { parameters.authenticationService }
|
||||
|
||||
var callback: (@MainActor (SoftLogoutScreenCoordinatorResult) -> Void)?
|
||||
var actions: AnyPublisher<SoftLogoutScreenCoordinatorResult, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
@MainActor init(parameters: SoftLogoutScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
@@ -60,21 +65,23 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
viewModel.callback = { [weak self] result in
|
||||
guard let self else { return }
|
||||
MXLog.info("[SoftLogoutCoordinator] SoftLogoutViewModel did complete with result: \(result).")
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
MXLog.info("[SoftLogoutCoordinator] SoftLogoutViewModel did complete with result: \(action).")
|
||||
|
||||
switch result {
|
||||
case .login(let password):
|
||||
self.login(withPassword: password)
|
||||
case .forgotPassword:
|
||||
self.showForgotPasswordScreen()
|
||||
case .clearAllData:
|
||||
self.callback?(.clearAllData)
|
||||
case .continueWithOIDC:
|
||||
self.continueWithOIDC(presentationAnchor: viewModel.context.viewState.window)
|
||||
switch action {
|
||||
case .login(let password):
|
||||
login(withPassword: password)
|
||||
case .forgotPassword:
|
||||
showForgotPasswordScreen()
|
||||
case .clearAllData:
|
||||
actionsSubject.send(.clearAllData)
|
||||
case .continueWithOIDC:
|
||||
continueWithOIDC(presentationAnchor: viewModel.context.viewState.window)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func stop() {
|
||||
@@ -119,7 +126,7 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
||||
initialDeviceName: UIDevice.current.initialDeviceName,
|
||||
deviceID: parameters.credentials.deviceID) {
|
||||
case .success(let userSession):
|
||||
callback?(.signedIn(userSession))
|
||||
actionsSubject.send(.signedIn(userSession))
|
||||
stopLoading()
|
||||
case .failure(let error):
|
||||
stopLoading()
|
||||
@@ -146,7 +153,7 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
||||
presentationAnchor: presentationAnchor)
|
||||
switch await presenter.authenticate(using: oidcData) {
|
||||
case .success(let userSession):
|
||||
callback?(.signedIn(userSession))
|
||||
actionsSubject.send(.signedIn(userSession))
|
||||
case .failure(let error):
|
||||
handleError(error)
|
||||
}
|
||||
|
||||
+10
-5
@@ -14,12 +14,17 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias SoftLogoutScreenViewModelType = StateStoreViewModel<SoftLogoutScreenViewState, SoftLogoutScreenViewAction>
|
||||
|
||||
class SoftLogoutScreenViewModel: SoftLogoutScreenViewModelType, SoftLogoutScreenViewModelProtocol {
|
||||
var callback: (@MainActor (SoftLogoutScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<SoftLogoutScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<SoftLogoutScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(credentials: SoftLogoutScreenCredentials,
|
||||
homeserver: LoginHomeserver,
|
||||
@@ -36,13 +41,13 @@ class SoftLogoutScreenViewModel: SoftLogoutScreenViewModelType, SoftLogoutScreen
|
||||
override func process(viewAction: SoftLogoutScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .login:
|
||||
callback?(.login(state.bindings.password))
|
||||
actionsSubject.send(.login(state.bindings.password))
|
||||
case .forgotPassword:
|
||||
callback?(.forgotPassword)
|
||||
actionsSubject.send(.forgotPassword)
|
||||
case .clearAllData:
|
||||
callback?(.clearAllData)
|
||||
actionsSubject.send(.clearAllData)
|
||||
case .continueWithOIDC:
|
||||
callback?(.continueWithOIDC)
|
||||
actionsSubject.send(.continueWithOIDC)
|
||||
case .updateWindow(let window):
|
||||
guard state.window != window else { return }
|
||||
Task { state.window = window }
|
||||
|
||||
+2
-2
@@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
protocol SoftLogoutScreenViewModelProtocol {
|
||||
var callback: (@MainActor (SoftLogoutScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<SoftLogoutScreenViewModelAction, Never> { get }
|
||||
var context: SoftLogoutScreenViewModelType.Context { get }
|
||||
|
||||
/// Display an error to the user.
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ final class WaitlistScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: WaitlistScreenCoordinatorParameters
|
||||
private var viewModel: WaitlistScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<WaitlistScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var refreshCancellable: AnyCancellable?
|
||||
|
||||
var actions: AnyPublisher<WaitlistScreenCoordinatorAction, Never> {
|
||||
|
||||
@@ -35,7 +35,7 @@ struct BugReportScreenCoordinatorParameters {
|
||||
final class BugReportScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: BugReportScreenCoordinatorParameters
|
||||
private var viewModel: BugReportScreenViewModelProtocol
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var completion: ((BugReportScreenCoordinatorResult) -> Void)?
|
||||
|
||||
@@ -54,10 +54,10 @@ final class BugReportScreenCoordinator: CoordinatorProtocol {
|
||||
func start() {
|
||||
viewModel
|
||||
.actions
|
||||
.sink { [weak self] result in
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
MXLog.info("BugReportViewModel did complete with result: \(result).")
|
||||
switch result {
|
||||
MXLog.info("BugReportViewModel did complete with result: \(action).")
|
||||
switch action {
|
||||
case .cancel:
|
||||
self.completion?(.cancel)
|
||||
case let .submitStarted(progressPublisher):
|
||||
|
||||
@@ -28,7 +28,7 @@ final class CreatePollScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: CreatePollScreenCoordinatorParameters
|
||||
private var viewModel: CreatePollScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<CreatePollScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<CreatePollScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
|
||||
@@ -36,7 +36,7 @@ final class CreateRoomCoordinator: CoordinatorProtocol {
|
||||
private let parameters: CreateRoomCoordinatorParameters
|
||||
private var viewModel: CreateRoomViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<CreateRoomCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<CreateRoomCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct EmojiPickerScreenCoordinatorParameters {
|
||||
@@ -31,7 +32,12 @@ final class EmojiPickerScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: EmojiPickerScreenCoordinatorParameters
|
||||
private var viewModel: EmojiPickerScreenViewModelProtocol
|
||||
|
||||
var callback: ((EmojiPickerScreenCoordinatorAction) -> Void)?
|
||||
private let actionsSubject: PassthroughSubject<EmojiPickerScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<EmojiPickerScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: EmojiPickerScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
@@ -40,16 +46,18 @@ final class EmojiPickerScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
|
||||
func start() {
|
||||
viewModel.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case let .emojiSelected(emoji: emoji):
|
||||
self.callback?(.emojiSelected(emoji: emoji, itemID: self.parameters.itemID))
|
||||
case .dismiss:
|
||||
self.callback?(.dismiss)
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case let .emojiSelected(emoji: emoji):
|
||||
actionsSubject.send(.emojiSelected(emoji: emoji, itemID: self.parameters.itemID))
|
||||
case .dismiss:
|
||||
actionsSubject.send(.dismiss)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
|
||||
@@ -14,12 +14,17 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias EmojiPickerScreenViewModelType = StateStoreViewModel<EmojiPickerScreenViewState, EmojiPickerScreenViewAction>
|
||||
|
||||
class EmojiPickerScreenViewModel: EmojiPickerScreenViewModelType, EmojiPickerScreenViewModelProtocol {
|
||||
var callback: ((EmojiPickerScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<EmojiPickerScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<EmojiPickerScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
private let emojiProvider: EmojiProviderProtocol
|
||||
|
||||
@@ -40,9 +45,9 @@ class EmojiPickerScreenViewModel: EmojiPickerScreenViewModelType, EmojiPickerScr
|
||||
state.categories = convert(emojiCategories: categories)
|
||||
}
|
||||
case let .emojiTapped(emoji: emoji):
|
||||
callback?(.emojiSelected(emoji: emoji.value))
|
||||
actionsSubject.send(.emojiSelected(emoji: emoji.value))
|
||||
case .dismiss:
|
||||
callback?(.dismiss)
|
||||
actionsSubject.send(.dismiss)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol EmojiPickerScreenViewModelProtocol {
|
||||
var callback: ((EmojiPickerScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<EmojiPickerScreenViewModelAction, Never> { get }
|
||||
var context: EmojiPickerScreenViewModelType.Context { get }
|
||||
}
|
||||
|
||||
@@ -41,9 +41,12 @@ final class HomeScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: HomeScreenCoordinatorParameters
|
||||
private var viewModel: HomeScreenViewModelProtocol
|
||||
|
||||
private let actionsSubject: PassthroughSubject<HomeScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var callback: ((HomeScreenCoordinatorAction) -> Void)?
|
||||
var actions: AnyPublisher<HomeScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: HomeScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
@@ -55,30 +58,32 @@ final class HomeScreenCoordinator: CoordinatorProtocol {
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
|
||||
viewModel.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .presentRoom(let roomIdentifier):
|
||||
self.callback?(.presentRoom(roomIdentifier: roomIdentifier))
|
||||
case .presentRoomDetails(roomIdentifier: let roomIdentifier):
|
||||
self.callback?(.presentRoomDetails(roomIdentifier: roomIdentifier))
|
||||
case .roomLeft(roomIdentifier: let roomIdentifier):
|
||||
self.callback?(.roomLeft(roomIdentifier: roomIdentifier))
|
||||
case .presentFeedbackScreen:
|
||||
self.callback?(.presentFeedbackScreen)
|
||||
case .presentSettingsScreen:
|
||||
self.callback?(.presentSettingsScreen)
|
||||
case .presentSessionVerificationScreen:
|
||||
self.callback?(.presentSessionVerificationScreen)
|
||||
case .signOut:
|
||||
self.callback?(.signOut)
|
||||
case .presentStartChatScreen:
|
||||
self.callback?(.presentStartChatScreen)
|
||||
case .presentInvitesScreen:
|
||||
self.callback?(.presentInvitesScreen)
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .presentRoom(let roomIdentifier):
|
||||
actionsSubject.send(.presentRoom(roomIdentifier: roomIdentifier))
|
||||
case .presentRoomDetails(roomIdentifier: let roomIdentifier):
|
||||
actionsSubject.send(.presentRoomDetails(roomIdentifier: roomIdentifier))
|
||||
case .roomLeft(roomIdentifier: let roomIdentifier):
|
||||
actionsSubject.send(.roomLeft(roomIdentifier: roomIdentifier))
|
||||
case .presentFeedbackScreen:
|
||||
actionsSubject.send(.presentFeedbackScreen)
|
||||
case .presentSettingsScreen:
|
||||
actionsSubject.send(.presentSettingsScreen)
|
||||
case .presentSessionVerificationScreen:
|
||||
actionsSubject.send(.presentSessionVerificationScreen)
|
||||
case .signOut:
|
||||
actionsSubject.send(.signOut)
|
||||
case .presentStartChatScreen:
|
||||
actionsSubject.send(.presentStartChatScreen)
|
||||
case .presentInvitesScreen:
|
||||
actionsSubject.send(.presentInvitesScreen)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@@ -32,7 +32,11 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
private var visibleItemRangeObservationToken: AnyCancellable?
|
||||
private let visibleItemRangePublisher = CurrentValueSubject<(range: Range<Int>, isScrolling: Bool), Never>((0..<0, false))
|
||||
|
||||
var callback: ((HomeScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<HomeScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<HomeScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(userSession: UserSessionProtocol,
|
||||
attributedStringBuilder: AttributedStringBuilderProtocol,
|
||||
@@ -92,9 +96,9 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
override func process(viewAction: HomeScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .selectRoom(let roomIdentifier):
|
||||
callback?(.presentRoom(roomIdentifier: roomIdentifier))
|
||||
actionsSubject.send(.presentRoom(roomIdentifier: roomIdentifier))
|
||||
case .showRoomDetails(roomIdentifier: let roomIdentifier):
|
||||
callback?(.presentRoomDetails(roomIdentifier: roomIdentifier))
|
||||
actionsSubject.send(.presentRoomDetails(roomIdentifier: roomIdentifier))
|
||||
case .leaveRoom(roomIdentifier: let roomIdentifier):
|
||||
startLeaveRoomProcess(roomId: roomIdentifier)
|
||||
case .confirmLeaveRoom(roomIdentifier: let roomIdentifier):
|
||||
@@ -102,22 +106,22 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
case .userMenu(let action):
|
||||
switch action {
|
||||
case .feedback:
|
||||
callback?(.presentFeedbackScreen)
|
||||
actionsSubject.send(.presentFeedbackScreen)
|
||||
case .settings:
|
||||
callback?(.presentSettingsScreen)
|
||||
actionsSubject.send(.presentSettingsScreen)
|
||||
case .signOut:
|
||||
callback?(.signOut)
|
||||
actionsSubject.send(.signOut)
|
||||
}
|
||||
case .verifySession:
|
||||
callback?(.presentSessionVerificationScreen)
|
||||
actionsSubject.send(.presentSessionVerificationScreen)
|
||||
case .skipSessionVerification:
|
||||
state.showSessionVerificationBanner = false
|
||||
case .updateVisibleItemRange(let range, let isScrolling):
|
||||
visibleItemRangePublisher.send((range, isScrolling))
|
||||
case .startChat:
|
||||
callback?(.presentStartChatScreen)
|
||||
actionsSubject.send(.presentStartChatScreen)
|
||||
case .selectInvites:
|
||||
callback?(.presentInvitesScreen)
|
||||
actionsSubject.send(.presentInvitesScreen)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +130,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
title: L10n.crashDetectionDialogContent(InfoPlistReader.main.bundleDisplayName),
|
||||
primaryButton: .init(title: L10n.actionNo, action: nil),
|
||||
secondaryButton: .init(title: L10n.actionYes) { [weak self] in
|
||||
self?.callback?(.presentFeedbackScreen)
|
||||
self?.actionsSubject.send(.presentFeedbackScreen)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -335,7 +339,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
type: .modal(progress: .none, interactiveDismissDisabled: false, allowsInteraction: false),
|
||||
title: L10n.commonCurrentUserLeftRoom,
|
||||
iconName: "checkmark"))
|
||||
callback?(.roomLeft(roomIdentifier: roomId))
|
||||
actionsSubject.send(.roomLeft(roomIdentifier: roomId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol HomeScreenViewModelProtocol {
|
||||
var callback: ((HomeScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<HomeScreenViewModelAction, Never> { get }
|
||||
|
||||
var context: HomeScreenViewModelType.Context { get }
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ final class InviteUsersScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: InviteUsersScreenCoordinatorParameters
|
||||
private let viewModel: InviteUsersScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<InviteUsersScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<InviteUsersScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
|
||||
@@ -29,7 +29,7 @@ final class InvitesScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: InvitesScreenCoordinatorParameters
|
||||
private var viewModel: InvitesScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<InvitesScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<InvitesScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
|
||||
@@ -31,7 +31,7 @@ final class StaticLocationScreenCoordinator: CoordinatorProtocol {
|
||||
let viewModel: StaticLocationScreenViewModelProtocol
|
||||
|
||||
private let actionsSubject: PassthroughSubject<StaticLocationScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<StaticLocationScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
|
||||
+18
-10
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct MediaUploadPreviewScreenCoordinatorParameters {
|
||||
@@ -30,11 +31,14 @@ enum MediaUploadPreviewScreenCoordinatorAction {
|
||||
|
||||
final class MediaUploadPreviewScreenCoordinator: CoordinatorProtocol {
|
||||
private var viewModel: MediaUploadPreviewScreenViewModelProtocol
|
||||
private let callback: (MediaUploadPreviewScreenCoordinatorAction) -> Void
|
||||
private let actionsSubject: PassthroughSubject<MediaUploadPreviewScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(parameters: MediaUploadPreviewScreenCoordinatorParameters, callback: @escaping (MediaUploadPreviewScreenCoordinatorAction) -> Void) {
|
||||
self.callback = callback
|
||||
|
||||
var actions: AnyPublisher<MediaUploadPreviewScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: MediaUploadPreviewScreenCoordinatorParameters) {
|
||||
viewModel = MediaUploadPreviewScreenViewModel(userIndicatorController: parameters.userIndicatorController,
|
||||
roomProxy: parameters.roomProxy,
|
||||
mediaUploadingPreprocessor: parameters.mediaUploadingPreprocessor,
|
||||
@@ -43,14 +47,18 @@ final class MediaUploadPreviewScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
|
||||
func start() {
|
||||
viewModel.callback = { [weak self] action in
|
||||
switch action {
|
||||
case .dismiss:
|
||||
self?.callback(.dismiss)
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .dismiss:
|
||||
actionsSubject.send(.dismiss)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(MediaUploadPreviewScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
+7
-3
@@ -31,7 +31,11 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
||||
}
|
||||
}
|
||||
|
||||
var callback: ((MediaUploadPreviewScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<MediaUploadPreviewScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<MediaUploadPreviewScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(userIndicatorController: UserIndicatorControllerProtocol?,
|
||||
roomProxy: RoomProxyProtocol,
|
||||
@@ -58,7 +62,7 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
||||
case .success(let mediaInfo):
|
||||
switch await sendAttachment(mediaInfo: mediaInfo, progressSubject: progressSubject) {
|
||||
case .success:
|
||||
callback?(.dismiss)
|
||||
actionsSubject.send(.dismiss)
|
||||
case .failure(let error):
|
||||
MXLog.error("Failed sending attachment with error: \(error)")
|
||||
showError(label: L10n.screenMediaUploadPreviewErrorFailedSending)
|
||||
@@ -74,7 +78,7 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
||||
|
||||
case .cancel:
|
||||
requestHandle?.cancel()
|
||||
callback?(.dismiss)
|
||||
actionsSubject.send(.dismiss)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol MediaUploadPreviewScreenViewModelProtocol {
|
||||
var callback: ((MediaUploadPreviewScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<MediaUploadPreviewScreenViewModelAction, Never> { get }
|
||||
var context: MediaUploadPreviewScreenViewModelType.Context { get }
|
||||
}
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ final class MessageForwardingScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: MessageForwardingScreenCoordinatorParameters
|
||||
private var viewModel: MessageForwardingScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<MessageForwardingScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<MessageForwardingScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
|
||||
@@ -19,7 +19,7 @@ import SwiftUI
|
||||
|
||||
final class MigrationScreenCoordinator: CoordinatorProtocol {
|
||||
private var viewModel: MigrationScreenViewModelProtocol
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init() {
|
||||
viewModel = MigrationScreenViewModel()
|
||||
|
||||
+19
-12
@@ -14,28 +14,35 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
final class OnboardingCoordinator: CoordinatorProtocol {
|
||||
private var viewModel: OnboardingViewModelProtocol
|
||||
|
||||
var callback: ((OnboardingCoordinatorAction) -> Void)?
|
||||
final class OnboardingScreenCoordinator: CoordinatorProtocol {
|
||||
private var viewModel: OnboardingScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<OnboardingScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<OnboardingScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init() {
|
||||
viewModel = OnboardingViewModel()
|
||||
viewModel = OnboardingScreenViewModel()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
viewModel.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .login:
|
||||
self.callback?(.login)
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .login:
|
||||
actionsSubject.send(.login)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
+5
-5
@@ -18,23 +18,23 @@ import SwiftUI
|
||||
|
||||
// MARK: - Coordinator
|
||||
|
||||
enum OnboardingCoordinatorAction {
|
||||
enum OnboardingScreenCoordinatorAction {
|
||||
case login
|
||||
}
|
||||
|
||||
/// The content displayed in a single screen page.
|
||||
struct OnboardingPageContent {
|
||||
struct OnboardingScreenPageContent {
|
||||
let title: AttributedString
|
||||
let message: String
|
||||
let image: ImageAsset
|
||||
}
|
||||
|
||||
enum OnboardingViewModelAction {
|
||||
enum OnboardingScreenViewModelAction {
|
||||
case login
|
||||
}
|
||||
|
||||
struct OnboardingViewState: BindableState { }
|
||||
struct OnboardingScreenViewState: BindableState { }
|
||||
|
||||
enum OnboardingViewAction {
|
||||
enum OnboardingScreenViewAction {
|
||||
case login
|
||||
}
|
||||
+12
-8
@@ -17,19 +17,23 @@
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias OnboardingViewModelType = StateStoreViewModel<OnboardingViewState, OnboardingViewAction>
|
||||
typealias OnboardingScreenViewModelType = StateStoreViewModel<OnboardingScreenViewState, OnboardingScreenViewAction>
|
||||
|
||||
class OnboardingViewModel: OnboardingViewModelType, OnboardingViewModelProtocol {
|
||||
var callback: ((OnboardingViewModelAction) -> Void)?
|
||||
|
||||
init() {
|
||||
super.init(initialViewState: OnboardingViewState())
|
||||
class OnboardingScreenViewModel: OnboardingScreenViewModelType, OnboardingScreenViewModelProtocol {
|
||||
private var actionsSubject: PassthroughSubject<OnboardingScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<OnboardingScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
override func process(viewAction: OnboardingViewAction) {
|
||||
init() {
|
||||
super.init(initialViewState: OnboardingScreenViewState())
|
||||
}
|
||||
|
||||
override func process(viewAction: OnboardingScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .login:
|
||||
callback?(.login)
|
||||
actionsSubject.send(.login)
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol OnboardingViewModelProtocol {
|
||||
var callback: ((OnboardingViewModelAction) -> Void)? { get set }
|
||||
var context: OnboardingViewModelType.Context { get }
|
||||
protocol OnboardingScreenViewModelProtocol {
|
||||
var actions: AnyPublisher<OnboardingScreenViewModelAction, Never> { get }
|
||||
var context: OnboardingScreenViewModelType.Context { get }
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import SwiftUI
|
||||
struct OnboardingScreen: View {
|
||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||
|
||||
@ObservedObject var context: OnboardingViewModel.Context
|
||||
@ObservedObject var context: OnboardingScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
@@ -45,7 +45,7 @@ struct OnboardingScreen: View {
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
.background {
|
||||
OnboardingBackgroundImage()
|
||||
OnboardingScreenBackgroundImage()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ struct OnboardingScreen: View {
|
||||
// MARK: - Previews
|
||||
|
||||
struct OnboardingScreen_Previews: PreviewProvider {
|
||||
static let viewModel = OnboardingViewModel()
|
||||
static let viewModel = OnboardingScreenViewModel()
|
||||
|
||||
static var previews: some View {
|
||||
OnboardingScreen(context: viewModel.context)
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
import SwiftUI
|
||||
|
||||
/// The background gradient shown on the launch, splash and onboarding screens.
|
||||
struct OnboardingBackgroundImage: View {
|
||||
struct OnboardingScreenBackgroundImage: View {
|
||||
var body: some View {
|
||||
Image(asset: Asset.Images.launchBackground)
|
||||
.resizable()
|
||||
@@ -32,9 +32,12 @@ enum ReportContentScreenCoordinatorAction {
|
||||
final class ReportContentScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: ReportContentScreenCoordinatorParameters
|
||||
private var viewModel: ReportContentScreenViewModelProtocol
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private let actionsSubject: PassthroughSubject<ReportContentScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var callback: ((ReportContentScreenCoordinatorAction) -> Void)?
|
||||
var actions: AnyPublisher<ReportContentScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: ReportContentScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
@@ -48,17 +51,18 @@ final class ReportContentScreenCoordinator: CoordinatorProtocol {
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .submitStarted:
|
||||
self.startLoading()
|
||||
startLoading()
|
||||
case let .submitFailed(error):
|
||||
self.stopLoading()
|
||||
self.showError(description: error.localizedDescription)
|
||||
stopLoading()
|
||||
showError(description: error.localizedDescription)
|
||||
case .submitFinished:
|
||||
self.stopLoading()
|
||||
self.callback?(.finish)
|
||||
stopLoading()
|
||||
actionsSubject.send(.finish)
|
||||
case .cancel:
|
||||
self.callback?(.cancel)
|
||||
actionsSubject.send(.cancel)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ final class RoomDetailsEditScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: RoomDetailsEditScreenCoordinatorParameters
|
||||
private var viewModel: RoomDetailsEditScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<RoomDetailsEditScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<RoomDetailsEditScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
|
||||
@@ -41,7 +41,7 @@ final class RoomDetailsScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
|
||||
private let actionsSubject: PassthroughSubject<RoomDetailsScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<RoomDetailsScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
@@ -60,22 +60,24 @@ final class RoomDetailsScreenCoordinator: CoordinatorProtocol {
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
viewModel.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .requestMemberDetailsPresentation:
|
||||
self.presentRoomMembersList()
|
||||
case .requestInvitePeoplePresentation:
|
||||
self.presentInviteUsersScreen()
|
||||
case .leftRoom:
|
||||
self.actionsSubject.send(.leftRoom)
|
||||
case .requestEditDetailsPresentation(let accountOwner):
|
||||
self.presentRoomDetailsEditScreen(accountOwner: accountOwner)
|
||||
case .requestNotificationSettingsPresentation:
|
||||
self.presentNotificationSettings()
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .requestMemberDetailsPresentation:
|
||||
presentRoomMembersList()
|
||||
case .requestInvitePeoplePresentation:
|
||||
presentInviteUsersScreen()
|
||||
case .leftRoom:
|
||||
actionsSubject.send(.leftRoom)
|
||||
case .requestEditDetailsPresentation(let accountOwner):
|
||||
presentRoomDetailsEditScreen(accountOwner: accountOwner)
|
||||
case .requestNotificationSettingsPresentation:
|
||||
presentNotificationSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func stop() {
|
||||
@@ -94,12 +96,16 @@ final class RoomDetailsScreenCoordinator: CoordinatorProtocol {
|
||||
roomProxy: parameters.roomProxy)
|
||||
let coordinator = RoomMembersListScreenCoordinator(parameters: params)
|
||||
|
||||
coordinator.callback = { [weak self] action in
|
||||
switch action {
|
||||
case .invite:
|
||||
self?.presentInviteUsersScreen()
|
||||
coordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .invite:
|
||||
presentInviteUsersScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
navigationStackCoordinator?.push(coordinator)
|
||||
}
|
||||
@@ -115,10 +121,10 @@ final class RoomDetailsScreenCoordinator: CoordinatorProtocol {
|
||||
let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters)
|
||||
inviteUsersStackCoordinator.setRootCoordinator(coordinator)
|
||||
|
||||
coordinator.actions.sink { [weak self] result in
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch result {
|
||||
switch action {
|
||||
case .cancel:
|
||||
navigationStackCoordinator?.setSheetCoordinator(nil)
|
||||
case .proceed:
|
||||
|
||||
@@ -32,7 +32,11 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
|
||||
|
||||
private var dmRecipient: RoomMemberProxyProtocol?
|
||||
|
||||
var callback: ((RoomDetailsScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<RoomDetailsScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<RoomDetailsScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(accountUserID: String,
|
||||
roomProxy: RoomProxyProtocol,
|
||||
@@ -75,9 +79,9 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
|
||||
override func process(viewAction: RoomDetailsScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .processTapPeople:
|
||||
callback?(.requestMemberDetailsPresentation)
|
||||
actionsSubject.send(.requestMemberDetailsPresentation)
|
||||
case .processTapInvite:
|
||||
callback?(.requestInvitePeoplePresentation)
|
||||
actionsSubject.send(.requestInvitePeoplePresentation)
|
||||
case .processTapLeave:
|
||||
guard state.joinedMembersCount > 1 else {
|
||||
state.bindings.leaveRoomAlertItem = LeaveRoomAlertItem(roomId: roomProxy.id, state: .empty)
|
||||
@@ -95,7 +99,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
|
||||
MXLog.error("Missing account owner when presenting the room's edit details screen")
|
||||
return
|
||||
}
|
||||
callback?(.requestEditDetailsPresentation(accountOwner))
|
||||
actionsSubject.send(.requestEditDetailsPresentation(accountOwner))
|
||||
case .ignoreConfirmed:
|
||||
Task { await ignore() }
|
||||
case .unignoreConfirmed:
|
||||
@@ -104,7 +108,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
|
||||
if state.notificationSettingsState.isError {
|
||||
fetchNotificationSettings()
|
||||
} else {
|
||||
callback?(.requestNotificationSettingsPresentation)
|
||||
actionsSubject.send(.requestNotificationSettingsPresentation)
|
||||
}
|
||||
case .processToogleMuteNotifications:
|
||||
Task { await toggleMuteNotifications() }
|
||||
@@ -239,7 +243,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr
|
||||
case .failure:
|
||||
state.bindings.alertInfo = AlertInfo(id: .unknown)
|
||||
case .success:
|
||||
callback?(.leftRoom)
|
||||
actionsSubject.send(.leftRoom)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol RoomDetailsScreenViewModelProtocol {
|
||||
var callback: ((RoomDetailsScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<RoomDetailsScreenViewModelAction, Never> { get }
|
||||
var context: RoomDetailsScreenViewModelType.Context { get }
|
||||
func stop()
|
||||
}
|
||||
|
||||
+7
-1
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct RoomMemberDetailsScreenCoordinatorParameters {
|
||||
@@ -29,7 +30,12 @@ final class RoomMemberDetailsScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: RoomMemberDetailsScreenCoordinatorParameters
|
||||
private var viewModel: RoomMemberDetailsScreenViewModelProtocol
|
||||
|
||||
var callback: ((RoomMemberDetailsScreenCoordinatorAction) -> Void)?
|
||||
private let actionsSubject: PassthroughSubject<RoomMemberDetailsScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<RoomMemberDetailsScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: RoomMemberDetailsScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
|
||||
+6
-1
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias RoomMemberDetailsScreenViewModelType = StateStoreViewModel<RoomMemberDetailsScreenViewState, RoomMemberDetailsScreenViewAction>
|
||||
@@ -24,7 +25,11 @@ class RoomMemberDetailsScreenViewModel: RoomMemberDetailsScreenViewModelType, Ro
|
||||
private let mediaProvider: MediaProviderProtocol
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
|
||||
var callback: ((RoomMemberDetailsScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<RoomMemberDetailsScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<RoomMemberDetailsScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(roomProxy: RoomProxyProtocol,
|
||||
roomMemberProxy: RoomMemberProxyProtocol,
|
||||
|
||||
+2
-2
@@ -14,11 +14,11 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol RoomMemberDetailsScreenViewModelProtocol {
|
||||
var callback: ((RoomMemberDetailsScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<RoomMemberDetailsScreenViewModelAction, Never> { get }
|
||||
var context: RoomMemberDetailsScreenViewModelType.Context { get }
|
||||
func stop()
|
||||
}
|
||||
|
||||
+18
-10
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct RoomMembersListScreenCoordinatorParameters {
|
||||
@@ -33,7 +34,12 @@ final class RoomMembersListScreenCoordinator: CoordinatorProtocol {
|
||||
parameters.navigationStackCoordinator
|
||||
}
|
||||
|
||||
var callback: ((RoomMembersListScreenCoordinatorAction) -> Void)?
|
||||
private let actionsSubject: PassthroughSubject<RoomMembersListScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<RoomMembersListScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: RoomMembersListScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
@@ -44,16 +50,18 @@ final class RoomMembersListScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
|
||||
func start() {
|
||||
viewModel.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case let .selectMember(member):
|
||||
self.selectMember(member)
|
||||
case .invite:
|
||||
callback?(.invite)
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case let .selectMember(member):
|
||||
selectMember(member)
|
||||
case .invite:
|
||||
actionsSubject.send(.invite)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias RoomMembersListScreenViewModelType = StateStoreViewModel<RoomMembersListScreenViewState, RoomMembersListScreenViewAction>
|
||||
@@ -24,7 +25,11 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
|
||||
|
||||
private var members: [RoomMemberProxyProtocol] = []
|
||||
|
||||
var callback: ((RoomMembersListScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<RoomMembersListScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<RoomMembersListScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(roomProxy: RoomProxyProtocol,
|
||||
mediaProvider: MediaProviderProtocol,
|
||||
@@ -47,9 +52,9 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
|
||||
MXLog.error("Selected member \(id) not found")
|
||||
return
|
||||
}
|
||||
callback?(.selectMember(member))
|
||||
actionsSubject.send(.selectMember(member))
|
||||
case .invite:
|
||||
callback?(.invite)
|
||||
actionsSubject.send(.invite)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol RoomMembersListScreenViewModelProtocol {
|
||||
var callback: ((RoomMembersListScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<RoomMembersListScreenViewModelAction, Never> { get }
|
||||
var context: RoomMembersListScreenViewModelType.Context { get }
|
||||
}
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ final class RoomNotificationSettingsScreenCoordinator: CoordinatorProtocol {
|
||||
private var viewModel: RoomNotificationSettingsScreenViewModelProtocol
|
||||
|
||||
private let actionsSubject: PassthroughSubject<RoomNotificationSettingsScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<RoomNotificationSettingsScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
|
||||
@@ -106,10 +106,10 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
.store(in: &cancellables)
|
||||
|
||||
composerViewModel.actions
|
||||
.sink { [weak self] composerAction in
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
viewModel.process(composerAction: composerAction)
|
||||
viewModel.process(composerAction: action)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ class TimelineTableViewController: UIViewController {
|
||||
|
||||
/// The table's diffable data source.
|
||||
private var dataSource: UITableViewDiffableDataSource<TimelineSection, String>?
|
||||
private var cancellables: Set<AnyCancellable> = []
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
/// A publisher used to throttle back pagination requests.
|
||||
///
|
||||
|
||||
+20
-8
@@ -14,8 +14,13 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
enum SessionVerificationScreenCoordinatorAction {
|
||||
case done
|
||||
}
|
||||
|
||||
struct SessionVerificationScreenCoordinatorParameters {
|
||||
let sessionVerificationControllerProxy: SessionVerificationControllerProxyProtocol
|
||||
}
|
||||
@@ -24,7 +29,12 @@ final class SessionVerificationScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: SessionVerificationScreenCoordinatorParameters
|
||||
private var viewModel: SessionVerificationScreenViewModelProtocol
|
||||
|
||||
var callback: (() -> Void)?
|
||||
private let actionsSubject: PassthroughSubject<SessionVerificationScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<SessionVerificationScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: SessionVerificationScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
@@ -35,14 +45,16 @@ final class SessionVerificationScreenCoordinator: CoordinatorProtocol {
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
viewModel.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .finished:
|
||||
self.callback?()
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .finished:
|
||||
actionsSubject.send(.done)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
|
||||
+14
-9
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias SessionVerificationViewModelType = StateStoreViewModel<SessionVerificationScreenViewState, SessionVerificationScreenViewAction>
|
||||
@@ -23,7 +24,11 @@ class SessionVerificationScreenViewModel: SessionVerificationViewModelType, Sess
|
||||
|
||||
private var stateMachine: SessionVerificationScreenStateMachine
|
||||
|
||||
var callback: ((SessionVerificationScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<SessionVerificationScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<SessionVerificationScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(sessionVerificationControllerProxy: SessionVerificationControllerProxyProtocol,
|
||||
initialState: SessionVerificationScreenViewState = SessionVerificationScreenViewState()) {
|
||||
@@ -79,7 +84,7 @@ class SessionVerificationScreenViewModel: SessionVerificationViewModelType, Sess
|
||||
return
|
||||
}
|
||||
|
||||
callback?(.finished)
|
||||
actionsSubject.send(.finished)
|
||||
case .accept:
|
||||
stateMachine.processEvent(.acceptChallenge)
|
||||
case .decline:
|
||||
@@ -93,24 +98,24 @@ class SessionVerificationScreenViewModel: SessionVerificationViewModelType, Sess
|
||||
stateMachine.addTransitionHandler { [weak self] context in
|
||||
guard let self else { return }
|
||||
|
||||
self.state.verificationState = context.toState
|
||||
state.verificationState = context.toState
|
||||
|
||||
switch (context.fromState, context.event, context.toState) {
|
||||
case (.initial, .requestVerification, .requestingVerification):
|
||||
self.requestVerification()
|
||||
requestVerification()
|
||||
case (.verificationRequestAccepted, .startSasVerification, .startingSasVerification):
|
||||
self.startSasVerification()
|
||||
startSasVerification()
|
||||
case (.showingChallenge, .acceptChallenge, .acceptingChallenge):
|
||||
self.acceptChallenge()
|
||||
acceptChallenge()
|
||||
case (.showingChallenge, .declineChallenge, .decliningChallenge):
|
||||
self.declineChallenge()
|
||||
declineChallenge()
|
||||
case (_, .cancel, .cancelling):
|
||||
self.cancelVerification()
|
||||
cancelVerification()
|
||||
case (_, _, .verified):
|
||||
// Dismiss the success screen automatically.
|
||||
Task {
|
||||
try? await Task.sleep(for: .seconds(2))
|
||||
self.callback?(.finished)
|
||||
self.actionsSubject.send(.finished)
|
||||
}
|
||||
default:
|
||||
break
|
||||
|
||||
+2
-2
@@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol SessionVerificationScreenViewModelProtocol {
|
||||
var callback: ((SessionVerificationScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<SessionVerificationScreenViewModelAction, Never> { get }
|
||||
var context: SessionVerificationViewModelType.Context { get }
|
||||
}
|
||||
|
||||
+7
-1
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
enum AdvancedSettingsScreenCoordinatorAction {
|
||||
@@ -23,7 +24,12 @@ enum AdvancedSettingsScreenCoordinatorAction {
|
||||
final class AdvancedSettingsScreenCoordinator: CoordinatorProtocol {
|
||||
private var viewModel: AdvancedSettingsScreenViewModelProtocol
|
||||
|
||||
var callback: ((AdvancedSettingsScreenCoordinatorAction) -> Void)?
|
||||
private let actionsSubject: PassthroughSubject<AdvancedSettingsScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<AdvancedSettingsScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init() {
|
||||
viewModel = AdvancedSettingsScreenViewModel(advancedSettings: ServiceLocator.shared.settings)
|
||||
|
||||
+6
-1
@@ -14,12 +14,17 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias AdvancedSettingsScreenViewModelType = StateStoreViewModel<AdvancedSettingsScreenViewState, AdvancedSettingsScreenViewAction>
|
||||
|
||||
class AdvancedSettingsScreenViewModel: AdvancedSettingsScreenViewModelType, AdvancedSettingsScreenViewModelProtocol {
|
||||
var callback: ((AdvancedSettingsScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<AdvancedSettingsScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<AdvancedSettingsScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(advancedSettings: AdvancedSettingsProtocol) {
|
||||
let bindings = AdvancedSettingsScreenViewStateBindings(advancedSettings: advancedSettings)
|
||||
|
||||
+2
-2
@@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol AdvancedSettingsScreenViewModelProtocol {
|
||||
var callback: ((AdvancedSettingsScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<AdvancedSettingsScreenViewModelAction, Never> { get }
|
||||
var context: AdvancedSettingsScreenViewModelType.Context { get }
|
||||
}
|
||||
|
||||
+18
-7
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
enum DeveloperOptionsScreenCoordinatorAction {
|
||||
@@ -23,18 +24,28 @@ enum DeveloperOptionsScreenCoordinatorAction {
|
||||
final class DeveloperOptionsScreenCoordinator: CoordinatorProtocol {
|
||||
private var viewModel: DeveloperOptionsScreenViewModelProtocol
|
||||
|
||||
var callback: ((DeveloperOptionsScreenCoordinatorAction) -> Void)?
|
||||
private let actionsSubject: PassthroughSubject<DeveloperOptionsScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<DeveloperOptionsScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init() {
|
||||
viewModel = DeveloperOptionsScreenViewModel(developerOptions: ServiceLocator.shared.settings)
|
||||
viewModel.callback = { [weak self] action in
|
||||
switch action {
|
||||
case .clearCache:
|
||||
self?.callback?(.clearCache)
|
||||
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .clearCache:
|
||||
actionsSubject.send(.clearCache)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(DeveloperOptionsScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
+7
-2
@@ -14,12 +14,17 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias DeveloperOptionsScreenViewModelType = StateStoreViewModel<DeveloperOptionsScreenViewState, DeveloperOptionsScreenViewAction>
|
||||
|
||||
class DeveloperOptionsScreenViewModel: DeveloperOptionsScreenViewModelType, DeveloperOptionsScreenViewModelProtocol {
|
||||
var callback: ((DeveloperOptionsScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<DeveloperOptionsScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<DeveloperOptionsScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(developerOptions: DeveloperOptionsProtocol) {
|
||||
let bindings = DeveloperOptionsScreenViewStateBindings(developerOptions: developerOptions)
|
||||
@@ -31,7 +36,7 @@ class DeveloperOptionsScreenViewModel: DeveloperOptionsScreenViewModelType, Deve
|
||||
override func process(viewAction: DeveloperOptionsScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .clearCache:
|
||||
callback?(.clearCache)
|
||||
actionsSubject.send(.clearCache)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol DeveloperOptionsScreenViewModelProtocol {
|
||||
var callback: ((DeveloperOptionsScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<DeveloperOptionsScreenViewModelAction, Never> { get }
|
||||
var context: DeveloperOptionsScreenViewModelType.Context { get }
|
||||
}
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ final class NotificationSettingsEditScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: NotificationSettingsEditScreenCoordinatorParameters
|
||||
private var viewModel: NotificationSettingsEditScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<NotificationSettingsEditScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<NotificationSettingsEditScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ final class NotificationSettingsScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: NotificationSettingsScreenCoordinatorParameters
|
||||
private var viewModel: NotificationSettingsScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<NotificationSettingsScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private var navigationStackCoordinator: NavigationStackCoordinator? {
|
||||
parameters.navigationStackCoordinator
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsScreenCoordinatorParameters {
|
||||
@@ -34,8 +35,13 @@ enum SettingsScreenCoordinatorAction {
|
||||
final class SettingsScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: SettingsScreenCoordinatorParameters
|
||||
private var viewModel: SettingsScreenViewModelProtocol
|
||||
|
||||
var callback: ((SettingsScreenCoordinatorAction) -> Void)?
|
||||
|
||||
private let actionsSubject: PassthroughSubject<SettingsScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<SettingsScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
@@ -43,34 +49,37 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
|
||||
self.parameters = parameters
|
||||
|
||||
viewModel = SettingsScreenViewModel(userSession: parameters.userSession, appSettings: ServiceLocator.shared.settings)
|
||||
viewModel.callback = { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .close:
|
||||
callback?(.dismiss)
|
||||
case .accountProfile:
|
||||
presentAccountProfileURL()
|
||||
case .analytics:
|
||||
presentAnalyticsScreen()
|
||||
case .reportBug:
|
||||
presentBugReportScreen()
|
||||
case .about:
|
||||
presentLegalInformationScreen()
|
||||
case .sessionVerification:
|
||||
verifySession()
|
||||
case .accountSessionsList:
|
||||
presentAccountSessionsListURL()
|
||||
case .notifications:
|
||||
presentNotificationSettings()
|
||||
case .advancedSettings:
|
||||
self.presentAdvancedSettings()
|
||||
case .developerOptions:
|
||||
presentDeveloperOptions()
|
||||
case .logout:
|
||||
callback?(.logout)
|
||||
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .close:
|
||||
actionsSubject.send(.dismiss)
|
||||
case .accountProfile:
|
||||
presentAccountProfileURL()
|
||||
case .analytics:
|
||||
presentAnalyticsScreen()
|
||||
case .reportBug:
|
||||
presentBugReportScreen()
|
||||
case .about:
|
||||
presentLegalInformationScreen()
|
||||
case .sessionVerification:
|
||||
verifySession()
|
||||
case .accountSessionsList:
|
||||
presentAccountSessionsListURL()
|
||||
case .notifications:
|
||||
presentNotificationSettings()
|
||||
case .advancedSettings:
|
||||
self.presentAdvancedSettings()
|
||||
case .developerOptions:
|
||||
presentDeveloperOptions()
|
||||
case .logout:
|
||||
actionsSubject.send(.logout)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
@@ -152,10 +161,17 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
|
||||
let verificationParameters = SessionVerificationScreenCoordinatorParameters(sessionVerificationControllerProxy: sessionVerificationController)
|
||||
let coordinator = SessionVerificationScreenCoordinator(parameters: verificationParameters)
|
||||
|
||||
coordinator.callback = { [weak self] in
|
||||
self?.parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
|
||||
}
|
||||
|
||||
coordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .done:
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
parameters.navigationStackCoordinator?.setSheetCoordinator(coordinator) { [weak self] in
|
||||
self?.parameters.navigationStackCoordinator?.setSheetCoordinator(nil)
|
||||
}
|
||||
@@ -179,12 +195,16 @@ final class SettingsScreenCoordinator: CoordinatorProtocol {
|
||||
private func presentDeveloperOptions() {
|
||||
let coordinator = DeveloperOptionsScreenCoordinator()
|
||||
|
||||
coordinator.callback = { [weak self] action in
|
||||
switch action {
|
||||
case .clearCache:
|
||||
self?.callback?(.clearCache)
|
||||
coordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .clearCache:
|
||||
actionsSubject.send(.clearCache)
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
parameters.navigationStackCoordinator?.push(coordinator)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,11 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
|
||||
private let userSession: UserSessionProtocol
|
||||
private let appSettings: AppSettings
|
||||
|
||||
var callback: ((SettingsScreenViewModelAction) -> Void)?
|
||||
private var actionsSubject: PassthroughSubject<SettingsScreenViewModelAction, Never> = .init()
|
||||
|
||||
var actions: AnyPublisher<SettingsScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(userSession: UserSessionProtocol, appSettings: AppSettings) {
|
||||
self.userSession = userSession
|
||||
@@ -74,27 +78,27 @@ class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewMo
|
||||
override func process(viewAction: SettingsScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .close:
|
||||
callback?(.close)
|
||||
actionsSubject.send(.close)
|
||||
case .accountProfile:
|
||||
callback?(.accountProfile)
|
||||
actionsSubject.send(.accountProfile)
|
||||
case .analytics:
|
||||
callback?(.analytics)
|
||||
actionsSubject.send(.analytics)
|
||||
case .reportBug:
|
||||
callback?(.reportBug)
|
||||
actionsSubject.send(.reportBug)
|
||||
case .about:
|
||||
callback?(.about)
|
||||
actionsSubject.send(.about)
|
||||
case .logout:
|
||||
callback?(.logout)
|
||||
actionsSubject.send(.logout)
|
||||
case .sessionVerification:
|
||||
callback?(.sessionVerification)
|
||||
actionsSubject.send(.sessionVerification)
|
||||
case .notifications:
|
||||
callback?(.notifications)
|
||||
actionsSubject.send(.notifications)
|
||||
case .accountSessionsList:
|
||||
callback?(.accountSessionsList)
|
||||
actionsSubject.send(.accountSessionsList)
|
||||
case .advancedSettings:
|
||||
callback?(.advancedSettings)
|
||||
actionsSubject.send(.advancedSettings)
|
||||
case .developerOptions:
|
||||
callback?(.developerOptions)
|
||||
actionsSubject.send(.developerOptions)
|
||||
|
||||
case .updateWindow(let window):
|
||||
Task {
|
||||
|
||||
+2
-2
@@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
@MainActor
|
||||
protocol SettingsScreenViewModelProtocol {
|
||||
var callback: ((SettingsScreenViewModelAction) -> Void)? { get set }
|
||||
var actions: AnyPublisher<SettingsScreenViewModelAction, Never> { get }
|
||||
var context: SettingsScreenViewModelType.Context { get }
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: StartChatScreenCoordinatorParameters
|
||||
private var viewModel: StartChatScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<StartChatScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private var createRoomParameters = CurrentValueSubject<CreateRoomFlowParameters, Never>(.init())
|
||||
private var createRoomParametersPublisher: CurrentValuePublisher<CreateRoomFlowParameters, Never> {
|
||||
@@ -97,10 +97,10 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
|
||||
mediaProvider: parameters.userSession.mediaProvider,
|
||||
userDiscoveryService: parameters.userDiscoveryService)
|
||||
let coordinator = InviteUsersScreenCoordinator(parameters: inviteParameters)
|
||||
coordinator.actions.sink { [weak self] result in
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch result {
|
||||
switch action {
|
||||
case .cancel:
|
||||
break // Not shown in this flow.
|
||||
case .proceed:
|
||||
@@ -125,9 +125,9 @@ final class StartChatScreenCoordinator: CoordinatorProtocol {
|
||||
createRoomParameters: createRoomParametersPublisher,
|
||||
selectedUsers: selectedUsersPublisher)
|
||||
let coordinator = CreateRoomCoordinator(parameters: createParameters)
|
||||
coordinator.actions.sink { [weak self] result in
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
switch result {
|
||||
switch action {
|
||||
case .deselectUser(let user):
|
||||
self.toggleUser(user)
|
||||
case .updateDetails(let details):
|
||||
|
||||
@@ -26,7 +26,7 @@ struct WelcomeScreen: View {
|
||||
} bottomContent: {
|
||||
button
|
||||
}
|
||||
.background(OnboardingBackgroundImage())
|
||||
.background(OnboardingScreenBackgroundImage())
|
||||
.environment(\.backgroundStyle, AnyShapeStyle(Color.clear))
|
||||
.onAppear {
|
||||
context.send(viewAction: .appeared)
|
||||
|
||||
@@ -24,7 +24,7 @@ enum WelcomeScreenScreenCoordinatorAction {
|
||||
final class WelcomeScreenScreenCoordinator: CoordinatorProtocol {
|
||||
private var viewModel: WelcomeScreenScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<WelcomeScreenScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables: Set<AnyCancellable> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<WelcomeScreenScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
|
||||
@@ -71,7 +71,7 @@ class MockScreen: Identifiable {
|
||||
let id: UITestsScreenIdentifier
|
||||
|
||||
private var retainedState = [Any]()
|
||||
private var cancellables: Set<AnyCancellable> = []
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(id: UITestsScreenIdentifier) {
|
||||
self.id = id
|
||||
@@ -229,7 +229,7 @@ class MockScreen: Identifiable {
|
||||
isModallyPresented: false)
|
||||
return NotificationSettingsScreenCoordinator(parameters: parameters)
|
||||
case .onboarding:
|
||||
return OnboardingCoordinator()
|
||||
return OnboardingScreenCoordinator()
|
||||
case .roomPlainNoAvatar:
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: RoomProxyMock(with: .init(displayName: "Some room name", avatarURL: nil)),
|
||||
|
||||
Reference in New Issue
Block a user