dd support for deeplinking/navigating into the same room multiple times

- fix bugs around the view <-> view models going out of sync
- unwind the stack if the room is already presented
This commit is contained in:
Stefan Ceriu 2024-04-23 16:25:05 +03:00
parent f9163094ba
commit d949b17448
4 changed files with 39 additions and 7 deletions

View File

@ -19,6 +19,7 @@ import SwiftState
import SwiftUI
import UserNotifications
// swiftlint:disable file_length
enum RoomFlowCoordinatorAction: Equatable {
case presentRoom(roomID: String)
case presentCallScreen(roomProxy: RoomProxyProtocol)
@ -38,7 +39,7 @@ enum RoomFlowCoordinatorAction: Equatable {
}
}
// swiftlint:disable file_length
// swiftlint:disable:next type_body_length
class RoomFlowCoordinator: FlowCoordinatorProtocol {
private let roomID: String
private let userSession: UserSessionProtocol
@ -104,6 +105,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
}
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
guard stateMachine.state != .complete else {
fatalError("This flow coordinator is `finished` ☠️")
}
switch appRoute {
case .room(let roomID):
Task {
@ -324,7 +329,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
dismissFlow(animated: animated)
case (_, .presentRoom, .room):
Task { await self.presentRoom(animated: animated) }
Task { await self.presentRoom(fromState: context.fromState, animated: animated) }
case (_, .dismissFlow, .complete):
dismissFlow(animated: animated)
@ -463,11 +468,25 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
/// Updates the navigation stack so it displays the timeline for the given room
/// - Parameters:
/// - animated: whether it should animate the transition
private func presentRoom(animated: Bool) async {
private func presentRoom(fromState: State, animated: Bool) async {
// If any sheets are presented dismiss them, rely on their dismissal callbacks to transition the state machine
// through the correct states before presenting the room
navigationStackCoordinator.setSheetCoordinator(nil)
// Handle selecting the same room again
if !isChildFlow {
// First unwind the navigation stack
navigationStackCoordinator.popToRoot(animated: animated)
// And then decide if the room actually needs to be presented again
switch fromState {
case .initial, .roomDetails(isRoot: true), .joinRoomScreen:
break
default:
return // The room is already on the stack, no need to present it again
}
}
Task {
// Flag the room as read on entering, the timeline will take care of the read receipts
await roomProxy.flagAsUnread(false)
@ -649,7 +668,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
if isRoot {
navigationStackCoordinator.setRootCoordinator(coordinator, animated: animated) { [weak self] in
self?.stateMachine.tryEvent(.dismissFlow)
guard let self else { return }
if stateMachine.state != .room { // The root has been replaced by a room
stateMachine.tryEvent(.dismissFlow)
}
}
} else {
navigationStackCoordinator.push(coordinator, animated: animated) { [weak self] in

View File

@ -267,8 +267,14 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
presentHomeScreen()
attemptStartingOnboarding()
case(.roomList, .selectRoom(let roomID, let showingRoomDetails), .roomList):
Task { await self.startRoomFlow(roomID: roomID, showingRoomDetails: showingRoomDetails, animated: animated) }
case(.roomList(let selectedRoomID), .selectRoom(let roomID, let showingRoomDetails), .roomList):
if selectedRoomID == roomID {
if let roomFlowCoordinator {
roomFlowCoordinator.handleAppRoute(.room(roomID: roomID), animated: animated)
}
} else {
Task { await self.startRoomFlow(roomID: roomID, showingRoomDetails: showingRoomDetails, animated: animated) }
}
case(.roomList, .deselectRoom, .roomList):
dismissRoomFlow(animated: animated)

View File

@ -162,6 +162,8 @@ class RoomFlowCoordinatorTests: XCTestCase {
try await clearRoute(expectedActions: [.finished])
XCTAssertNil(navigationStackCoordinator.rootCoordinator)
await setupRoomFlowCoordinator(roomType: .invited(roomID: "InvitedRoomID"))
try await process(route: .room(roomID: "InvitedRoomID"))
XCTAssert(navigationStackCoordinator.rootCoordinator is JoinRoomScreenCoordinator)
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0)
@ -188,7 +190,8 @@ class RoomFlowCoordinatorTests: XCTestCase {
try await clearRoute(expectedActions: [.finished])
XCTAssertNil(navigationStackCoordinator.stackCoordinators.last, "A child room flow should remove the join room scren on dismissal")
navigationStackCoordinator.popToRoot()
await setupRoomFlowCoordinator(asChildFlow: true, roomType: .invited(roomID: "InvitedRoomID"))
navigationStackCoordinator.setRootCoordinator(BlankFormCoordinator())
try await process(route: .room(roomID: "InvitedRoomID"))
XCTAssertTrue(navigationStackCoordinator.rootCoordinator is BlankFormCoordinator, "A child room flow should push onto the stack, leaving the root alone.")

1
changelog.d/2653.bugfix Normal file
View File

@ -0,0 +1 @@
Fix deeplinking/navigating into the same room multiple times