mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-06 04:18:31 +00:00
Fixes #734 - Add a "View logs" option to the report a problem screen
Move bug report and log viewer responsibility to a separate flow coordinator. Reuse it as a child coordinator in the authentication, userSessionFlow and settings flow coordinators.
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
|
||||
struct BugReportFlowCoordinatorParameters {
|
||||
enum PresentationMode {
|
||||
case sheet(NavigationStackCoordinator)
|
||||
case push(NavigationStackCoordinator)
|
||||
}
|
||||
|
||||
let presentationMode: PresentationMode
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
let bugReportService: BugReportServiceProtocol
|
||||
let userID: String?
|
||||
let deviceID: String?
|
||||
}
|
||||
|
||||
class BugReportFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private let parameters: BugReportFlowCoordinatorParameters
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private var internalNavigationStackCoordinator: NavigationStackCoordinator?
|
||||
|
||||
private var isModallyPresented: Bool {
|
||||
switch parameters.presentationMode {
|
||||
case .sheet:
|
||||
return true
|
||||
case .push:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
init(parameters: BugReportFlowCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
}
|
||||
|
||||
func start() {
|
||||
presentBugReportScreen()
|
||||
}
|
||||
|
||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
func clearRoute(animated: Bool) {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func presentBugReportScreen() {
|
||||
let params = BugReportScreenCoordinatorParameters(bugReportService: parameters.bugReportService,
|
||||
userID: parameters.userID,
|
||||
deviceID: parameters.deviceID,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
screenshot: nil,
|
||||
isModallyPresented: isModallyPresented)
|
||||
let coordinator = BugReportScreenCoordinator(parameters: params)
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .cancel:
|
||||
dismiss()
|
||||
case .viewLogs:
|
||||
presentLogViewerScreen()
|
||||
case .finish:
|
||||
showSuccess(label: L10n.actionDone)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
switch parameters.presentationMode {
|
||||
case .sheet(let navigationStackCoordinator):
|
||||
let internalNavigationStackCoordinator = NavigationStackCoordinator()
|
||||
internalNavigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
navigationStackCoordinator.setSheetCoordinator(internalNavigationStackCoordinator)
|
||||
self.internalNavigationStackCoordinator = internalNavigationStackCoordinator
|
||||
case .push(let navigationStackCoordinator):
|
||||
internalNavigationStackCoordinator = navigationStackCoordinator
|
||||
navigationStackCoordinator.push(coordinator)
|
||||
}
|
||||
}
|
||||
|
||||
private func presentLogViewerScreen() {
|
||||
let coordinator = LogViewerScreenCoordinator(parameters: .init())
|
||||
coordinator.actions.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .done:
|
||||
internalNavigationStackCoordinator?.pop()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
internalNavigationStackCoordinator?.push(coordinator)
|
||||
}
|
||||
|
||||
private func dismiss() {
|
||||
switch parameters.presentationMode {
|
||||
case .push(let navigationStackCoordinator):
|
||||
navigationStackCoordinator.pop()
|
||||
case .sheet(let navigationStackCoordinator):
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func showSuccess(label: String) {
|
||||
parameters.userIndicatorController.submitIndicator(UserIndicator(title: label))
|
||||
}
|
||||
}
|
||||
@@ -91,6 +91,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
// MARK: - FlowCoordinatorProtocol
|
||||
|
||||
func start() {
|
||||
fatalError("This flow coordinator expect a route")
|
||||
}
|
||||
|
||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||
switch appRoute {
|
||||
case .room(let roomID):
|
||||
|
||||
@@ -48,6 +48,9 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
// periphery:ignore - retaining purpose
|
||||
private var appLockSetupFlowCoordinator: AppLockSetupFlowCoordinator?
|
||||
|
||||
// periphery:ignore - retaining purpose
|
||||
private var bugReportFlowCoordinator: BugReportFlowCoordinator?
|
||||
|
||||
private let actionsSubject: PassthroughSubject<SettingsFlowCoordinatorAction, Never> = .init()
|
||||
var actions: AnyPublisher<SettingsFlowCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
@@ -57,6 +60,10 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
self.parameters = parameters
|
||||
}
|
||||
|
||||
func start() {
|
||||
fatalError("Unavailable")
|
||||
}
|
||||
|
||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||
switch appRoute {
|
||||
case .settings:
|
||||
@@ -76,17 +83,17 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
func clearRoute(animated: Bool) { }
|
||||
func clearRoute(animated: Bool) {
|
||||
fatalError("Unavailable")
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func presentSettingsScreen(animated: Bool) {
|
||||
navigationStackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
let parameters = SettingsScreenCoordinatorParameters(userSession: parameters.userSession,
|
||||
appSettings: parameters.appSettings)
|
||||
|
||||
let settingsScreenCoordinator = SettingsScreenCoordinator(parameters: parameters)
|
||||
let settingsScreenCoordinator = SettingsScreenCoordinator(parameters: .init(userSession: parameters.userSession,
|
||||
appSettings: parameters.appSettings))
|
||||
|
||||
settingsScreenCoordinator.actions
|
||||
.sink { [weak self] action in
|
||||
@@ -94,9 +101,9 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
switch action {
|
||||
case .dismiss:
|
||||
self.parameters.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
parameters.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
case .logout:
|
||||
self.parameters.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
parameters.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
|
||||
// The settings sheet needs to be dismissed before the alert can be shown
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
@@ -113,7 +120,12 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
case .appLock:
|
||||
presentAppLockSetupFlow()
|
||||
case .bugReport:
|
||||
presentBugReportScreen()
|
||||
bugReportFlowCoordinator = BugReportFlowCoordinator(parameters: .init(presentationMode: .push(navigationStackCoordinator),
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
bugReportService: parameters.bugReportService,
|
||||
userID: parameters.userSession.userID,
|
||||
deviceID: parameters.userSession.deviceID))
|
||||
bugReportFlowCoordinator?.start()
|
||||
case .about:
|
||||
presentLegalInformationScreen()
|
||||
case .sessionVerification:
|
||||
@@ -132,7 +144,7 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
navigationStackCoordinator.setRootCoordinator(settingsScreenCoordinator, animated: animated)
|
||||
|
||||
self.parameters.navigationSplitCoordinator.setSheetCoordinator(navigationStackCoordinator) { [weak self] in
|
||||
parameters.navigationSplitCoordinator.setSheetCoordinator(navigationStackCoordinator) { [weak self] in
|
||||
guard let self else { return }
|
||||
|
||||
navigationStackCoordinator = nil
|
||||
@@ -187,28 +199,6 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
coordinator.start()
|
||||
}
|
||||
|
||||
private func presentBugReportScreen() {
|
||||
let params = BugReportScreenCoordinatorParameters(bugReportService: parameters.bugReportService,
|
||||
userID: parameters.userSession.userID,
|
||||
deviceID: parameters.userSession.deviceID,
|
||||
userIndicatorController: parameters.userIndicatorController,
|
||||
screenshot: nil,
|
||||
isModallyPresented: false)
|
||||
let coordinator = BugReportScreenCoordinator(parameters: params)
|
||||
coordinator.completion = { [weak self] result in
|
||||
switch result {
|
||||
case .finish:
|
||||
self?.showSuccess(label: L10n.actionDone)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
self?.navigationStackCoordinator.pop()
|
||||
}
|
||||
|
||||
navigationStackCoordinator.push(coordinator)
|
||||
}
|
||||
|
||||
private func presentLegalInformationScreen() {
|
||||
navigationStackCoordinator.push(LegalInformationScreenCoordinator(appSettings: parameters.appSettings))
|
||||
}
|
||||
@@ -269,10 +259,6 @@ class SettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
navigationStackCoordinator.push(coordinator)
|
||||
}
|
||||
|
||||
private func showSuccess(label: String) {
|
||||
parameters.userIndicatorController.submitIndicator(UserIndicator(title: label))
|
||||
}
|
||||
|
||||
// MARK: OIDC Account Management
|
||||
|
||||
private func presentAccountProfileURL() {
|
||||
|
||||
@@ -33,9 +33,14 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private let actionsSubject: PassthroughSubject<UserSessionFlowCoordinatorAction, Never> = .init()
|
||||
|
||||
private let stateMachine: UserSessionFlowCoordinatorStateMachine
|
||||
|
||||
private let roomFlowCoordinator: RoomFlowCoordinator
|
||||
|
||||
private let settingsFlowCoordinator: SettingsFlowCoordinator
|
||||
|
||||
// periphery:ignore - retaining purpose
|
||||
private var bugReportFlowCoordinator: BugReportFlowCoordinator?
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var migrationCancellable: AnyCancellable?
|
||||
|
||||
@@ -250,7 +255,12 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
break
|
||||
|
||||
case (.roomList, .feedbackScreen, .feedbackScreen):
|
||||
presentFeedbackScreen(animated: animated)
|
||||
bugReportFlowCoordinator = BugReportFlowCoordinator(parameters: .init(presentationMode: .sheet(sidebarNavigationStackCoordinator),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
bugReportService: bugReportService,
|
||||
userID: userSession.userID,
|
||||
deviceID: userSession.deviceID))
|
||||
bugReportFlowCoordinator?.start()
|
||||
case (.feedbackScreen, .dismissedFeedbackScreen, .roomList):
|
||||
break
|
||||
|
||||
@@ -483,30 +493,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
self?.stateMachine.processEvent(.dismissedStartChatScreen)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Bug reporting
|
||||
|
||||
private func presentFeedbackScreen(animated: Bool, for image: UIImage? = nil) {
|
||||
let feedbackNavigationStackCoordinator = NavigationStackCoordinator()
|
||||
|
||||
let parameters = BugReportScreenCoordinatorParameters(bugReportService: bugReportService,
|
||||
userID: userSession.userID,
|
||||
deviceID: userSession.deviceID,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
screenshot: image,
|
||||
isModallyPresented: true)
|
||||
let coordinator = BugReportScreenCoordinator(parameters: parameters)
|
||||
coordinator.completion = { [weak self] _ in
|
||||
self?.navigationSplitCoordinator.setSheetCoordinator(nil)
|
||||
}
|
||||
|
||||
feedbackNavigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
|
||||
navigationSplitCoordinator.setSheetCoordinator(feedbackNavigationStackCoordinator, animated: animated) { [weak self] in
|
||||
self?.stateMachine.processEvent(.dismissedFeedbackScreen)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Invites list
|
||||
|
||||
private func presentInvitesList(animated: Bool) {
|
||||
|
||||
Reference in New Issue
Block a user