Tidy up Developer screen, add button to crash the app. (#1348)

* ID all the things
This commit is contained in:
Doug
2023-07-18 11:25:07 +01:00
committed by GitHub
parent 63935ceab3
commit c83a2bd729
5 changed files with 104 additions and 83 deletions
@@ -142,14 +142,14 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
} }
func shouldDisplayInAppNotification(_ service: NotificationManagerProtocol, content: UNNotificationContent) -> Bool { func shouldDisplayInAppNotification(_ service: NotificationManagerProtocol, content: UNNotificationContent) -> Bool {
guard let roomId = content.roomID else { guard let roomID = content.roomID else {
return true return true
} }
guard let userSessionFlowCoordinator else { guard let userSessionFlowCoordinator else {
// there is not a user session yet // there is not a user session yet
return false return false
} }
return !userSessionFlowCoordinator.isDisplayingRoomScreen(withRoomId: roomId) return !userSessionFlowCoordinator.isDisplayingRoomScreen(withRoomID: roomID)
} }
func notificationTapped(_ service: NotificationManagerProtocol, content: UNNotificationContent) async { func notificationTapped(_ service: NotificationManagerProtocol, content: UNNotificationContent) async {
@@ -179,10 +179,10 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
MXLog.info("[AppCoordinator] handle notification reply") MXLog.info("[AppCoordinator] handle notification reply")
guard let roomId = content.userInfo[NotificationConstants.UserInfoKey.roomIdentifier] as? String else { guard let roomID = content.userInfo[NotificationConstants.UserInfoKey.roomIdentifier] as? String else {
return return
} }
let roomProxy = await userSession.clientProxy.roomForIdentifier(roomId) let roomProxy = await userSession.clientProxy.roomForIdentifier(roomID)
switch await roomProxy?.sendMessage(replyText) { switch await roomProxy?.sendMessage(replyText) {
case .success: case .success:
break break
@@ -77,7 +77,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
switch action { switch action {
case .presentedRoom(let roomID): case .presentedRoom(let roomID):
self.analytics.signpost.beginRoomFlow(roomID) self.analytics.signpost.beginRoomFlow(roomID)
self.stateMachine.processEvent(.selectRoom(roomId: roomID)) self.stateMachine.processEvent(.selectRoom(roomID: roomID))
case .dismissedRoom: case .dismissedRoom:
self.stateMachine.processEvent(.deselectRoom) self.stateMachine.processEvent(.deselectRoom)
self.analytics.signpost.endRoomFlow() self.analytics.signpost.endRoomFlow()
@@ -100,8 +100,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
func stop() { } func stop() { }
func isDisplayingRoomScreen(withRoomId roomId: String) -> Bool { func isDisplayingRoomScreen(withRoomID roomID: String) -> Bool {
stateMachine.isDisplayingRoomScreen(withRoomId: roomId) stateMachine.isDisplayingRoomScreen(withRoomID: roomID)
} }
// MARK: - FlowCoordinatorProtocol // MARK: - FlowCoordinatorProtocol
@@ -202,8 +202,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
stateMachine.addTransitionHandler { [weak self] context in stateMachine.addTransitionHandler { [weak self] context in
switch context.toState { switch context.toState {
case .roomList(let selectedRoomId): case .roomList(let selectedRoomID):
self?.selectedRoomSubject.send(selectedRoomId) self?.selectedRoomSubject.send(selectedRoomID)
default: default:
break break
} }
@@ -259,8 +259,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
case .presentRoomDetails(let roomID): case .presentRoomDetails(let roomID):
self.roomFlowCoordinator.handleAppRoute(.roomDetails(roomID: roomID), animated: true) self.roomFlowCoordinator.handleAppRoute(.roomDetails(roomID: roomID), animated: true)
case .roomLeft(let roomID): case .roomLeft(let roomID):
if case .roomList(selectedRoomId: let selectedRoomId) = stateMachine.state, if case .roomList(selectedRoomID: let selectedRoomID) = stateMachine.state,
selectedRoomId == roomID { selectedRoomID == roomID {
self.roomFlowCoordinator.handleAppRoute(.roomList, animated: true) self.roomFlowCoordinator.handleAppRoute(.roomList, animated: true)
} }
case .presentSettingsScreen: case .presentSettingsScreen:
@@ -29,23 +29,23 @@ class UserSessionFlowCoordinatorStateMachine {
/// Showing the welcome screen. /// Showing the welcome screen.
case welcomeScreen case welcomeScreen
/// Showing the home screen. The `selectedRoomId` represents the timeline shown on the detail panel (if any) /// Showing the home screen. The `selectedRoomID` represents the timeline shown on the detail panel (if any)
case roomList(selectedRoomId: String?) case roomList(selectedRoomID: String?)
/// Showing the session verification flows /// Showing the session verification flows
case sessionVerificationScreen(selectedRoomId: String?) case sessionVerificationScreen(selectedRoomID: String?)
/// Showing the session verification flows /// Showing the session verification flows
case feedbackScreen(selectedRoomId: String?) case feedbackScreen(selectedRoomID: String?)
/// Showing the settings screen /// Showing the settings screen
case settingsScreen(selectedRoomId: String?) case settingsScreen(selectedRoomID: String?)
/// Showing the start chat screen /// Showing the start chat screen
case startChatScreen(selectedRoomId: String?) case startChatScreen(selectedRoomID: String?)
/// Showing invites list screen /// Showing invites list screen
case invitesScreen(selectedRoomId: String?) case invitesScreen(selectedRoomID: String?)
} }
struct EventUserInfo { struct EventUserInfo {
@@ -54,21 +54,26 @@ class UserSessionFlowCoordinatorStateMachine {
/// Events that can be triggered on the AppCoordinator state machine /// Events that can be triggered on the AppCoordinator state machine
enum Event: EventType { enum Event: EventType {
/// Start the user session flows /// Start the user session flows.
case start case start
/// Starts the user session flows with the welcome screen /// Starts the user session flows with the welcome screen.
/// **Note:** This is event is only for users who used the app before v1.1.8.
/// It can be removed once the older TestFlight builds have expired.
case startWithWelcomeScreen case startWithWelcomeScreen
/// Start the user session flows with a migration screen. /// Start the user session flows with a migration screen.
case startWithMigration case startWithMigration
/// Request to transition from the migration state to the home screen. /// Request to transition from the migration state to the home screen.
case completeMigration case completeMigration
/// Request presentation of the welcome screen.
case presentWelcomeScreen case presentWelcomeScreen
/// The welcome screen has been dismissed.
case dismissedWelcomeScreen case dismissedWelcomeScreen
/// Request presentation for a particular room /// Request presentation for a particular room
/// - Parameter roomId:the room identifier /// - Parameter roomID:the room identifier
case selectRoom(roomId: String) case selectRoom(roomID: String)
/// The room screen has been dismissed /// The room screen has been dismissed
case deselectRoom case deselectRoom
@@ -111,50 +116,50 @@ class UserSessionFlowCoordinatorStateMachine {
// swiftlint:disable:next cyclomatic_complexity // swiftlint:disable:next cyclomatic_complexity
private func configure() { private func configure() {
stateMachine.addRoutes(event: .start, transitions: [.initial => .roomList(selectedRoomId: nil)]) stateMachine.addRoutes(event: .start, transitions: [.initial => .roomList(selectedRoomID: nil)])
stateMachine.addRoutes(event: .startWithMigration, transitions: [.initial => .migration]) stateMachine.addRoutes(event: .startWithMigration, transitions: [.initial => .migration])
stateMachine.addRoutes(event: .startWithWelcomeScreen, transitions: [.initial => .welcomeScreen]) stateMachine.addRoutes(event: .startWithWelcomeScreen, transitions: [.initial => .welcomeScreen])
stateMachine.addRoutes(event: .completeMigration, transitions: [.migration => .roomList(selectedRoomId: nil)]) stateMachine.addRoutes(event: .completeMigration, transitions: [.migration => .roomList(selectedRoomID: nil)])
stateMachine.addRoutes(event: .dismissedWelcomeScreen, transitions: [.welcomeScreen => .roomList(selectedRoomId: nil)]) stateMachine.addRoutes(event: .dismissedWelcomeScreen, transitions: [.welcomeScreen => .roomList(selectedRoomID: nil)])
stateMachine.addRouteMapping { event, fromState, _ in stateMachine.addRouteMapping { event, fromState, _ in
switch (event, fromState) { switch (event, fromState) {
case (.selectRoom(let roomId), .roomList): case (.selectRoom(let roomID), .roomList):
return .roomList(selectedRoomId: roomId) return .roomList(selectedRoomID: roomID)
case (.selectRoom(let roomId), .invitesScreen): case (.selectRoom(let roomID), .invitesScreen):
return .invitesScreen(selectedRoomId: roomId) return .invitesScreen(selectedRoomID: roomID)
case (.deselectRoom, .roomList): case (.deselectRoom, .roomList):
return .roomList(selectedRoomId: nil) return .roomList(selectedRoomID: nil)
case (.deselectRoom, .invitesScreen): case (.deselectRoom, .invitesScreen):
return .invitesScreen(selectedRoomId: nil) return .invitesScreen(selectedRoomID: nil)
case (.showSettingsScreen, .roomList(let selectedRoomId)): case (.showSettingsScreen, .roomList(let selectedRoomID)):
return .settingsScreen(selectedRoomId: selectedRoomId) return .settingsScreen(selectedRoomID: selectedRoomID)
case (.dismissedSettingsScreen, .settingsScreen(let selectedRoomId)): case (.dismissedSettingsScreen, .settingsScreen(let selectedRoomID)):
return .roomList(selectedRoomId: selectedRoomId) return .roomList(selectedRoomID: selectedRoomID)
case (.feedbackScreen, .roomList(let selectedRoomId)): case (.feedbackScreen, .roomList(let selectedRoomID)):
return .feedbackScreen(selectedRoomId: selectedRoomId) return .feedbackScreen(selectedRoomID: selectedRoomID)
case (.dismissedFeedbackScreen, .feedbackScreen(let selectedRoomId)): case (.dismissedFeedbackScreen, .feedbackScreen(let selectedRoomID)):
return .roomList(selectedRoomId: selectedRoomId) return .roomList(selectedRoomID: selectedRoomID)
case (.showSessionVerificationScreen, .roomList(let selectedRoomId)): case (.showSessionVerificationScreen, .roomList(let selectedRoomID)):
return .sessionVerificationScreen(selectedRoomId: selectedRoomId) return .sessionVerificationScreen(selectedRoomID: selectedRoomID)
case (.dismissedSessionVerificationScreen, .sessionVerificationScreen(let selectedRoomId)): case (.dismissedSessionVerificationScreen, .sessionVerificationScreen(let selectedRoomID)):
return .roomList(selectedRoomId: selectedRoomId) return .roomList(selectedRoomID: selectedRoomID)
case (.showStartChatScreen, .roomList(let selectedRoomId)): case (.showStartChatScreen, .roomList(let selectedRoomID)):
return .startChatScreen(selectedRoomId: selectedRoomId) return .startChatScreen(selectedRoomID: selectedRoomID)
case (.dismissedStartChatScreen, .startChatScreen(let selectedRoomId)): case (.dismissedStartChatScreen, .startChatScreen(let selectedRoomID)):
return .roomList(selectedRoomId: selectedRoomId) return .roomList(selectedRoomID: selectedRoomID)
case (.showInvitesScreen, .roomList(let selectedRoomId)): case (.showInvitesScreen, .roomList(let selectedRoomID)):
return .invitesScreen(selectedRoomId: selectedRoomId) return .invitesScreen(selectedRoomID: selectedRoomID)
case (.showInvitesScreen, .invitesScreen(let selectedRoomId)): case (.showInvitesScreen, .invitesScreen(let selectedRoomID)):
return .invitesScreen(selectedRoomId: selectedRoomId) return .invitesScreen(selectedRoomID: selectedRoomID)
case (.closedInvitesScreen, .invitesScreen(let selectedRoomId)): case (.closedInvitesScreen, .invitesScreen(let selectedRoomID)):
return .roomList(selectedRoomId: selectedRoomId) return .roomList(selectedRoomID: selectedRoomID)
case (.presentWelcomeScreen, .roomList): case (.presentWelcomeScreen, .roomList):
return .welcomeScreen return .welcomeScreen
@@ -190,10 +195,10 @@ class UserSessionFlowCoordinatorStateMachine {
} }
/// Flag indicating the machine is displaying room screen with given room identifier /// Flag indicating the machine is displaying room screen with given room identifier
func isDisplayingRoomScreen(withRoomId roomId: String) -> Bool { func isDisplayingRoomScreen(withRoomID roomID: String) -> Bool {
switch stateMachine.state { switch stateMachine.state {
case .roomList(let selectedRoomId): case .roomList(let selectedRoomID):
return roomId == selectedRoomId return roomID == selectedRoomID
default: default:
return false return false
} }
@@ -22,7 +22,7 @@ struct DeveloperOptionsScreen: View {
var body: some View { var body: some View {
Form { Form {
Section { Section("Timeline") {
Toggle(isOn: $context.shouldCollapseRoomStateEvents) { Toggle(isOn: $context.shouldCollapseRoomStateEvents) {
Text("Collapse room state events") Text("Collapse room state events")
} }
@@ -30,29 +30,33 @@ struct DeveloperOptionsScreen: View {
context.send(viewAction: .changedShouldCollapseRoomStateEvents) context.send(viewAction: .changedShouldCollapseRoomStateEvents)
} }
Toggle(isOn: $context.userSuggestionsEnabled) {
Text("User suggestions")
}
.onChange(of: context.userSuggestionsEnabled) { _ in
context.send(viewAction: .changedUserSuggestionsEnabled)
}
Toggle(isOn: $context.readReceiptsEnabled) { Toggle(isOn: $context.readReceiptsEnabled) {
Text("Show read receipts") Text("Show read receipts")
Text("requires app reboot") Text("Requires app reboot")
} }
.onChange(of: context.readReceiptsEnabled) { _ in .onChange(of: context.readReceiptsEnabled) { _ in
context.send(viewAction: .changedReadReceiptsEnabled) context.send(viewAction: .changedReadReceiptsEnabled)
} }
}
Section("Notifications") {
Toggle(isOn: $context.isEncryptionSyncEnabled) { Toggle(isOn: $context.isEncryptionSyncEnabled) {
Text("Use notification encryption sync") Text("Use notification encryption sync")
Text("requires app reboot") Text("Requires app reboot")
} }
.onChange(of: context.isEncryptionSyncEnabled) { _ in .onChange(of: context.isEncryptionSyncEnabled) { _ in
context.send(viewAction: .changedIsEncryptionSyncEnabled) context.send(viewAction: .changedIsEncryptionSyncEnabled)
} }
Toggle(isOn: $context.notificationSettingsEnabled) {
Text("Show notification settings")
}
.onChange(of: context.notificationSettingsEnabled) { _ in
context.send(viewAction: .changedNotificationSettingsEnabled)
}
}
Section("Location sharing") {
Toggle(isOn: $context.locationEventsEnabled) { Toggle(isOn: $context.locationEventsEnabled) {
Text("Location events in timeline") Text("Location events in timeline")
} }
@@ -66,12 +70,14 @@ struct DeveloperOptionsScreen: View {
.onChange(of: context.shareLocationEnabled) { _ in .onChange(of: context.shareLocationEnabled) { _ in
context.send(viewAction: .changedShareLocationEnabled) context.send(viewAction: .changedShareLocationEnabled)
} }
}
Toggle(isOn: $context.notificationSettingsEnabled) {
Text("Show notification settings") Section("Room creation") {
Toggle(isOn: $context.userSuggestionsEnabled) {
Text("User suggestions")
} }
.onChange(of: context.notificationSettingsEnabled) { _ in .onChange(of: context.userSuggestionsEnabled) { _ in
context.send(viewAction: .changedNotificationSettingsEnabled) context.send(viewAction: .changedUserSuggestionsEnabled)
} }
} }
@@ -81,6 +87,14 @@ struct DeveloperOptionsScreen: View {
} label: { } label: {
Text("🥳") Text("🥳")
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.alignmentGuide(.listRowSeparatorLeading) { _ in 0 } // Fix separator alignment
}
Button {
fatalError("This crash is a test.")
} label: {
Text("💥")
.frame(maxWidth: .infinity)
} }
} }
@@ -118,8 +132,10 @@ struct DeveloperOptionsScreen: View {
// MARK: - Previews // MARK: - Previews
struct DeveloperOptionsScreen_Previews: PreviewProvider { struct DeveloperOptionsScreen_Previews: PreviewProvider {
static let viewModel = DeveloperOptionsScreenViewModel(appSettings: ServiceLocator.shared.settings)
static var previews: some View { static var previews: some View {
let viewModel = DeveloperOptionsScreenViewModel(appSettings: ServiceLocator.shared.settings) NavigationStack {
DeveloperOptionsScreen(context: viewModel.context) DeveloperOptionsScreen(context: viewModel.context)
}
} }
} }
@@ -149,8 +149,8 @@ class ClientProxy: ClientProxyProtocol {
func directRoomForUserID(_ userID: String) async -> Result<String?, ClientProxyError> { func directRoomForUserID(_ userID: String) async -> Result<String?, ClientProxyError> {
await Task.dispatch(on: clientQueue) { await Task.dispatch(on: clientQueue) {
do { do {
let roomId = try self.client.getDmRoom(userId: userID)?.id() let roomID = try self.client.getDmRoom(userId: userID)?.id()
return .success(roomId) return .success(roomID)
} catch { } catch {
return .failure(.failedRetrievingDirectRoom) return .failure(.failedRetrievingDirectRoom)
} }
@@ -182,8 +182,8 @@ class ClientProxy: ClientProxyProtocol {
preset: isRoomPrivate ? .privateChat : .publicChat, preset: isRoomPrivate ? .privateChat : .publicChat,
invite: userIDs, invite: userIDs,
avatar: avatarURL?.absoluteString) avatar: avatarURL?.absoluteString)
let roomId = try self.client.createRoom(request: parameters) let roomID = try self.client.createRoom(request: parameters)
return .success(roomId) return .success(roomID)
} catch { } catch {
return .failure(.failedCreatingRoom) return .failure(.failedCreatingRoom)
} }
@@ -208,16 +208,16 @@ class ClientProxy: ClientProxyProtocol {
} }
/// Await the room to be available in the room summary list /// Await the room to be available in the room summary list
/// - Parameter result: the result of a room creation Task with the roomId /// - Parameter result: the result of a room creation Task with the `roomID`.
private func waitForRoomSummary(with result: Result<String, ClientProxyError>, name: String?) async -> Result<String, ClientProxyError> { private func waitForRoomSummary(with result: Result<String, ClientProxyError>, name: String?) async -> Result<String, ClientProxyError> {
guard case .success(let roomId) = result else { return result } guard case .success(let roomID) = result else { return result }
let runner = ExpiringTaskRunner { [weak self] in let runner = ExpiringTaskRunner { [weak self] in
guard let roomLists = self?.roomSummaryProvider?.roomListPublisher.values else { guard let roomLists = self?.roomSummaryProvider?.roomListPublisher.values else {
return return
} }
// for every list of summaries, we check if we have a room summary with matching ID and name (if present) // for every list of summaries, we check if we have a room summary with matching ID and name (if present)
for await roomList in roomLists { for await roomList in roomLists {
guard let summary = roomList.first(where: { $0.id == roomId }) else { continue } guard let summary = roomList.first(where: { $0.id == roomID }) else { continue }
guard let name else { break } guard let name else { break }
if summary.name == name { if summary.name == name {
break break