Various tweaks (#1129)

* Fixes #1121 - Hide the loading indicator after the logout task finishes

* Manually restart the sync only when entering the `error` state

* Use stopSync instead of the roomListService directly on client deinit

* Replace WeakClientProxyWrapper with callback based delegate

* Fix homescreen user avatar not automatically updating

* Replace default Build SDK profile with reldbg, which is fast but also doesn't crash

* Always show the loading indicator when the room list is not in a `running` state

* Implement delayed user indicator presentations through the normal API

* Fix the unit tests

* Replace UserIndicatorController delayedIndicators dictionary with a plain set
This commit is contained in:
Stefan Ceriu
2023-06-22 15:04:20 +03:00
committed by GitHub
parent f64bd4be23
commit 030b1b0bfa
15 changed files with 103 additions and 89 deletions

View File

@@ -325,7 +325,6 @@
804C15D8ADE0EA7A5268F58A /* OverridableAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648DD1C10E4957CB791FE0B8 /* OverridableAvatarImage.swift */; };
80D00A7C62AAB44F54725C43 /* PermalinkBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F754E66A8970963B15B2A41E /* PermalinkBuilder.swift */; };
80DEA2A4B20F9E279EAE6B2B /* UserProfile+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */; };
8196A2E71ACC902DD69F24EE /* UserNotificationControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DE6C5C756E1393202BA95CD /* UserNotificationControllerTests.swift */; };
81A7C020CB5F6232242A8414 /* UserSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F36C0A6D59717193F49EA986 /* UserSessionTests.swift */; };
8285FF4B2C2331758C437FF7 /* ReportContentScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 713B48DBF65DE4B0DD445D66 /* ReportContentScreenViewModelProtocol.swift */; };
828EA5009557C2B9DCD4CA0F /* UserDiscoverySection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D071F86CD47582B9196C9D16 /* UserDiscoverySection.swift */; };
@@ -426,6 +425,7 @@
A14A9419105A1CD42F0511C4 /* UserIndicatorModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43005941B3A2C9671E23C85 /* UserIndicatorModalView.swift */; };
A17FAD2EBC53E17B5FD384DB /* InviteUsersScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22730A30C50AC2E3D5BA8642 /* InviteUsersScreenViewModelProtocol.swift */; };
A1D4033881320C9EB88196E6 /* ServerConfirmationScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE846DDA83BFD7EC5C03760B /* ServerConfirmationScreenUITests.swift */; };
A1DF0E1E526A981ED6D5DF44 /* UserIndicatorControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2429224EB0EEA34D35CE9249 /* UserIndicatorControllerTests.swift */; };
A216C83ADCF32BA5EF8A6FBC /* InviteUsersViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845DDBDE5A0887E73D38B826 /* InviteUsersViewModelTests.swift */; };
A23B8B27A1436A1049EEF68E /* InfoPlistReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A580295A56B55A856CC4084 /* InfoPlistReader.swift */; };
A2434D4DFB49A68E5CD0F53C /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */; };
@@ -775,7 +775,6 @@
0C88046D6A070D9827181C4D /* OnboardingUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingUITests.swift; sourceTree = "<group>"; };
0D0B159AFFBBD8ECFD0E37FA /* LoginScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenModels.swift; sourceTree = "<group>"; };
0D8F620C8B314840D8602E3F /* NSE.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = NSE.appex; sourceTree = BUILT_PRODUCTS_DIR; };
0DE6C5C756E1393202BA95CD /* UserNotificationControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationControllerTests.swift; sourceTree = "<group>"; };
0E8BDC092D817B68CD9040C5 /* UserSessionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStore.swift; sourceTree = "<group>"; };
0F19DBE940499D3E3DD405D8 /* RoomMemberDetailsScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreenUITests.swift; sourceTree = "<group>"; };
0F5567A7EF6F2AB9473236F6 /* DocumentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentPicker.swift; sourceTree = "<group>"; };
@@ -835,6 +834,7 @@
227AC5D71A4CE43512062243 /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = "<group>"; };
23AA3F4B285570805CB0CCDD /* MapTiler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTiler.swift; sourceTree = "<group>"; };
24227FF9A2797F6EA7F69CDD /* HomeScreenInvitesButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenInvitesButton.swift; sourceTree = "<group>"; };
2429224EB0EEA34D35CE9249 /* UserIndicatorControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorControllerTests.swift; sourceTree = "<group>"; };
248649EBA5BC33DB93698734 /* SessionVerificationControllerProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationControllerProxyMock.swift; sourceTree = "<group>"; };
24DEE0682C95F897B6C7CB0D /* ServerConfirmationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenViewModel.swift; sourceTree = "<group>"; };
24F5530B2212862FA4BEFF2D /* HomeScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelProtocol.swift; sourceTree = "<group>"; };
@@ -2363,7 +2363,7 @@
2AB2C848BB9A7A9B618B7B89 /* TextBasedRoomTimelineTests.swift */,
1734A445A58ED855B977A0A8 /* TracingConfigurationTests.swift */,
EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */,
0DE6C5C756E1393202BA95CD /* UserNotificationControllerTests.swift */,
2429224EB0EEA34D35CE9249 /* UserIndicatorControllerTests.swift */,
BA241DEEF7C8A7181C0AEDC9 /* UserPreferenceTests.swift */,
53280D2292E6C9C7821773FD /* UserSession */,
70C5B842301AC281DF374E41 /* Extensions */,
@@ -3874,8 +3874,8 @@
AF33B9044498211C3D82F1E1 /* UNTextInputNotificationResponse+Creator.swift in Sources */,
8D3E1FADD78E72504DE0E402 /* UserAgentBuilderTests.swift in Sources */,
E313BDD2B8813144139B2E00 /* UserDiscoveryServiceTest.swift in Sources */,
A1DF0E1E526A981ED6D5DF44 /* UserIndicatorControllerTests.swift in Sources */,
08248D02BACA75CDC3B39A96 /* UserNotificationCenterSpy.swift in Sources */,
8196A2E71ACC902DD69F24EE /* UserNotificationControllerTests.swift in Sources */,
04F17DE71A50206336749BAC /* UserPreferenceTests.swift in Sources */,
81A7C020CB5F6232242A8414 /* UserSessionTests.swift in Sources */,
99F8DA4CCC6772EE5FE68E24 /* ViewModelContext.swift in Sources */,

View File

@@ -52,8 +52,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
private var appDelegateObserver: AnyCancellable?
private var userSessionObserver: AnyCancellable?
private var clientProxyObserver: AnyCancellable?
private var networkMonitorObserver: AnyCancellable?
private var initialSyncObserver: AnyCancellable?
private var backgroundRefreshSyncObserver: AnyCancellable?
let notificationManager: NotificationManagerProtocol
@@ -355,15 +355,12 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
showLoadingIndicator()
defer {
hideLoadingIndicator()
}
stopSync()
userSessionFlowCoordinator?.stop()
guard !isSoft else {
stateMachine.processEvent(.completedSigningOut(isSoft: isSoft))
hideLoadingIndicator()
return
}
@@ -380,6 +377,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
ServiceLocator.shared.analytics.resetConsentState()
stateMachine.processEvent(.completedSigningOut(isSoft: isSoft))
hideLoadingIndicator()
}
}
@@ -519,6 +518,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
backgroundAppRefreshTask?.setTaskCompleted(success: true)
backgroundAppRefreshTask = nil
clientProxyObserver = nil
}
private func startSync() {
@@ -532,15 +533,18 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
let identifier = "StaleDataIndicator"
ServiceLocator.shared.userIndicatorController.submitIndicator(.init(id: identifier, type: .toast(progress: .indeterminate), title: L10n.commonSyncing, persistent: true))
initialSyncObserver = userSession.clientProxy
clientProxyObserver = userSession.clientProxy
.callbacks
.receive(on: DispatchQueue.main)
.filter(\.isSyncUpdate)
.sink { [weak self] _ in
ServiceLocator.shared.userIndicatorController.retractIndicatorWithId(identifier)
self?.initialSyncObserver?.cancel()
.sink { action in
switch action {
case .startedUpdating:
ServiceLocator.shared.userIndicatorController.submitIndicator(.init(id: identifier, type: .toast(progress: .indeterminate), title: L10n.commonSyncing, persistent: true))
case .receivedSyncUpdate:
ServiceLocator.shared.userIndicatorController.retractIndicatorWithId(identifier)
default:
break
}
}
}

View File

@@ -1210,19 +1210,19 @@ class UserIndicatorControllerMock: UserIndicatorControllerProtocol {
//MARK: - submitIndicator
var submitIndicatorCallsCount = 0
var submitIndicatorCalled: Bool {
return submitIndicatorCallsCount > 0
var submitIndicatorDelayCallsCount = 0
var submitIndicatorDelayCalled: Bool {
return submitIndicatorDelayCallsCount > 0
}
var submitIndicatorReceivedIndicator: UserIndicator?
var submitIndicatorReceivedInvocations: [UserIndicator] = []
var submitIndicatorClosure: ((UserIndicator) -> Void)?
var submitIndicatorDelayReceivedArguments: (indicator: UserIndicator, delay: Duration?)?
var submitIndicatorDelayReceivedInvocations: [(indicator: UserIndicator, delay: Duration?)] = []
var submitIndicatorDelayClosure: ((UserIndicator, Duration?) -> Void)?
func submitIndicator(_ indicator: UserIndicator) {
submitIndicatorCallsCount += 1
submitIndicatorReceivedIndicator = indicator
submitIndicatorReceivedInvocations.append(indicator)
submitIndicatorClosure?(indicator)
func submitIndicator(_ indicator: UserIndicator, delay: Duration?) {
submitIndicatorDelayCallsCount += 1
submitIndicatorDelayReceivedArguments = (indicator: indicator, delay: delay)
submitIndicatorDelayReceivedInvocations.append((indicator: indicator, delay: delay))
submitIndicatorDelayClosure?(indicator, delay)
}
//MARK: - retractIndicatorWithId

View File

@@ -20,7 +20,7 @@ import Foundation
extension UserIndicatorControllerMock {
static var `default`: UserIndicatorControllerMock {
let mock = UserIndicatorControllerMock()
mock.submitIndicatorClosure = { _ in }
mock.submitIndicatorDelayClosure = { _, _ in }
mock.retractIndicatorWithIdClosure = { _ in }
mock.retractAllIndicatorsClosure = { }
return mock

View File

@@ -21,6 +21,7 @@ class UserIndicatorController: ObservableObject, UserIndicatorControllerProtocol
private var dismisalTimer: Timer?
private var displayTimes = [String: Date]()
private var delayedIndicators = Set<String>()
var nonPersistentDisplayDuration = 2.5
var minimumDisplayDuration = 0.5
@@ -51,12 +52,27 @@ class UserIndicatorController: ObservableObject, UserIndicatorControllerProtocol
)
}
func submitIndicator(_ indicator: UserIndicator) {
func submitIndicator(_ indicator: UserIndicator, delay: Duration?) {
if let index = indicatorQueue.firstIndex(where: { $0.id == indicator.id }) {
indicatorQueue[index] = indicator
} else {
retractIndicatorWithId(indicator.id)
indicatorQueue.append(indicator)
if let delay {
delayedIndicators.insert(indicator.id)
Timer.scheduledTimer(withTimeInterval: Double(delay.components.seconds), repeats: false) { [weak self] _ in
guard let self else { return }
guard delayedIndicators.contains(indicator.id) else {
return
}
retractIndicatorWithId(indicator.id)
indicatorQueue.append(indicator)
delayedIndicators.remove(indicator.id)
}
} else {
retractIndicatorWithId(indicator.id)
indicatorQueue.append(indicator)
}
}
displayTimes[indicator.id] = .now
@@ -69,6 +85,8 @@ class UserIndicatorController: ObservableObject, UserIndicatorControllerProtocol
}
func retractIndicatorWithId(_ id: String) {
delayedIndicators.remove(id)
guard let displayTime = displayTimes[id], abs(displayTime.timeIntervalSinceNow) <= minimumDisplayDuration else {
indicatorQueue.removeAll { $0.id == id }
return

View File

@@ -18,19 +18,14 @@ import Foundation
// sourcery: AutoMockable
protocol UserIndicatorControllerProtocol: CoordinatorProtocol {
func submitIndicator(_ indicator: UserIndicator)
func submitIndicator(_ indicator: UserIndicator, delay: Duration?)
func retractIndicatorWithId(_ id: String)
func retractAllIndicators()
var alertInfo: AlertInfo<UUID>? { get set }
}
extension UserIndicatorControllerProtocol {
/// Allows to submit a delayed indicator, this returns a Task so that it's also possible to cancel the action
func submitIndicator(_ indicator: UserIndicator, delay: Duration) -> Task<Void, Never> {
Task { @MainActor in
try? await Task.sleep(for: delay)
guard !Task.isCancelled else { return }
submitIndicator(indicator)
}
func submitIndicator(_ indicator: UserIndicator) {
submitIndicator(indicator, delay: nil)
}
}

View File

@@ -19,7 +19,7 @@ import SwiftUI
struct HomeScreenUserMenuButton: View {
@State private var showingLogoutConfirmation = false
let context: HomeScreenViewModel.Context
@ObservedObject var context: HomeScreenViewModel.Context
var body: some View {
Menu {

View File

@@ -26,7 +26,6 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
private let roomType: InviteUsersScreenRoomType
private weak var userIndicatorController: UserIndicatorControllerProtocol?
private let actionsSubject: PassthroughSubject<InviteUsersScreenViewModelAction, Never> = .init()
@CancellableTask private var showLoaderTask: Task<Void, Never>?
var actions: AnyPublisher<InviteUsersScreenViewModelAction, Never> {
actionsSubject.eraseToAnyPublisher()
@@ -165,11 +164,10 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
private let userIndicatorID = UUID().uuidString
private func showLoader() {
showLoaderTask = userIndicatorController?.submitIndicator(UserIndicator(id: userIndicatorID, type: .modal, title: L10n.commonLoading, persistent: true), delay: .milliseconds(200))
userIndicatorController?.submitIndicator(UserIndicator(id: userIndicatorID, type: .modal, title: L10n.commonLoading, persistent: true), delay: .milliseconds(200))
}
private func hideLoader() {
showLoaderTask = nil
userIndicatorController?.retractIndicatorWithId(userIndicatorID)
}
}

View File

@@ -21,7 +21,6 @@ typealias RoomMembersListScreenViewModelType = StateStoreViewModel<RoomMembersLi
class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMembersListScreenViewModelProtocol {
private let roomProxy: RoomProxyProtocol
private var members: [RoomMemberProxyProtocol] = []
@CancellableTask private var showLoaderTask: Task<Void, Never>?
var callback: ((RoomMembersListScreenViewModelAction) -> Void)?
@@ -109,11 +108,10 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
private let userIndicatorID = UUID().uuidString
private func showLoader() {
showLoaderTask = ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(id: userIndicatorID, type: .modal, title: L10n.commonLoading, persistent: true), delay: .milliseconds(200))
ServiceLocator.shared.userIndicatorController.submitIndicator(UserIndicator(id: userIndicatorID, type: .modal, title: L10n.commonLoading, persistent: true), delay: .milliseconds(200))
}
private func hideLoader() {
showLoaderTask = nil
ServiceLocator.shared.userIndicatorController.retractIndicatorWithId(userIndicatorID)
}
}

View File

@@ -30,7 +30,6 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
private let roomProxy: RoomProxyProtocol
private let timelineController: RoomTimelineControllerProtocol
private unowned let userIndicatorController: UserIndicatorControllerProtocol
private var loadingTask: Task<Void, Never>?
init(timelineController: RoomTimelineControllerProtocol,
mediaProvider: MediaProviderProtocol,
@@ -512,9 +511,8 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
private func handleTappedUser(userID: String) async {
// This is generally fast but it could take some time for rooms with thousands of users on first load
// Show a loader only if it takes more than 0.1 seconds
loadingTask = showLoadingIndicator(with: .milliseconds(100))
showLoadingIndicator(with: .milliseconds(100))
let result = await roomProxy.getMember(userID: userID)
loadingTask?.cancel()
hideLoadingIndicator()
switch result {
@@ -544,7 +542,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
private static let loadingIndicatorIdentifier = "RoomScreenLoadingIndicator"
private func showLoadingIndicator(with delay: Duration) -> Task<Void, Never> {
private func showLoadingIndicator(with delay: Duration) {
userIndicatorController.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier,
type: .modal(progress: .indeterminate, interactiveDismissDisabled: true),
title: L10n.commonLoading,

View File

@@ -19,26 +19,6 @@ import Foundation
import MatrixRustSDK
import UIKit
private class WeakClientProxyWrapper: ClientDelegate {
private weak var clientProxy: ClientProxy?
init(clientProxy: ClientProxy) {
self.clientProxy = clientProxy
}
// MARK: - ClientDelegate
func didReceiveAuthError(isSoftLogout: Bool) {
MXLog.error("Received authentication error, softlogout=\(isSoftLogout)")
clientProxy?.didReceiveAuthError(isSoftLogout: isSoftLogout)
}
func didRefreshTokens() {
MXLog.info("The session has updated tokens.")
clientProxy?.updateRestorationToken()
}
}
class ClientProxy: ClientProxyProtocol {
private let client: ClientProtocol
private let backgroundTaskService: BackgroundTaskServiceProtocol
@@ -67,10 +47,8 @@ class ClientProxy: ClientProxyProtocol {
private var visibleRoomsListProxyStateObservationToken: AnyCancellable?
deinit {
// These need to be inlined instead of using stopSync()
// as we can't call async methods safely from deinit
client.setDelegate(delegate: nil)
try? roomListService?.stopSync()
stopSync()
}
let callbacks = PassthroughSubject<ClientProxyCallback, Never>()
@@ -82,8 +60,11 @@ class ClientProxy: ClientProxyProtocol {
mediaLoader = MediaLoader(client: client, clientQueue: clientQueue)
let delegate = WeakClientProxyWrapper(clientProxy: self)
client.setDelegate(delegate: delegate)
client.setDelegate(delegate: ClientDelegateWrapper { [weak self] isSoftLogout in
self?.callbacks.send(.receivedAuthError(isSoftLogout: isSoftLogout))
} tokenRefreshCallback: { [weak self] in
self?.callbacks.send(.updateRestorationToken)
})
await configureRoomListService()
@@ -391,19 +372,25 @@ class ClientProxy: ClientProxyProtocol {
MXLog.info("Received room list update: \(state)")
// Restart the room list sync on every error for now
if state == .terminated {
if state == .error {
self.restartSync()
}
// The invites are available only when entering `running`
if state == .running {
self.callbacks.send(.receivedSyncUpdate)
Task {
// Subscribe to invites later as the underlying SlidingSync list is only added when entering AllRooms
await self.inviteSummaryProvider?.subscribeIfNecessary(entriesFunction: roomListService.invites(listener:),
entriesLoadingStateFunction: nil)
}
}
// Anything that's not `running` is interpreted as "Loading data"
if state == .running {
self.callbacks.send(.receivedSyncUpdate)
} else {
self.callbacks.send(.startedUpdating)
}
})
roomSummaryProvider = RoomSummaryProvider(roomListService: roomListService,
@@ -434,14 +421,6 @@ class ClientProxy: ClientProxyProtocol {
return (nil, nil)
}
}
fileprivate func updateRestorationToken() {
callbacks.send(.updateRestorationToken)
}
fileprivate func didReceiveAuthError(isSoftLogout: Bool) {
callbacks.send(.receivedAuthError(isSoftLogout: isSoftLogout))
}
}
extension ClientProxy: MediaLoaderProtocol {
@@ -469,3 +448,26 @@ private class RoomListStateListenerProxy: RoomListStateListener {
onUpdateClosure(state)
}
}
private class ClientDelegateWrapper: ClientDelegate {
private let authErrorCallback: (Bool) -> Void
private let tokenRefreshCallback: () -> Void
init(authErrorCallback: @escaping (Bool) -> Void,
tokenRefreshCallback: @escaping () -> Void) {
self.authErrorCallback = authErrorCallback
self.tokenRefreshCallback = tokenRefreshCallback
}
// MARK: - ClientDelegate
func didReceiveAuthError(isSoftLogout: Bool) {
MXLog.error("Received authentication error, softlogout=\(isSoftLogout)")
authErrorCallback(isSoftLogout)
}
func didRefreshTokens() {
MXLog.info("The session has updated tokens.")
tokenRefreshCallback()
}
}

View File

@@ -19,6 +19,7 @@ import Foundation
import MatrixRustSDK
enum ClientProxyCallback {
case startedUpdating
case receivedSyncUpdate
case receivedAuthError(isSoftLogout: Bool)
case updateRestorationToken

View File

@@ -25,7 +25,7 @@ struct BuildSDK: ParsableCommand {
var target: Target?
@Option(help: "The profile to use when building the SDK. Omit this option to build in debug mode.")
var profile: Profile = .debug
var profile: Profile = .reldbg
enum Error: LocalizedError {
case rustupOutputFailure

View File

@@ -185,7 +185,7 @@ class RoomScreenViewModelTests: XCTestCase {
// Test
viewModel.context.send(viewAction: .tappedOnUser(userID: "bob"))
await Task.yield()
XCTAssertFalse(userIndicatorControllerMock.submitIndicatorCalled)
XCTAssertFalse(userIndicatorControllerMock.submitIndicatorDelayCalled)
XCTAssert(roomProxyMock.getMemberUserIDCallsCount == 1)
XCTAssertEqual(roomProxyMock.getMemberUserIDReceivedUserID, "bob")
}
@@ -217,7 +217,7 @@ class RoomScreenViewModelTests: XCTestCase {
// Test
viewModel.context.send(viewAction: .tappedOnUser(userID: "bob"))
try? await Task.sleep(for: .milliseconds(300))
XCTAssert(userIndicatorControllerMock.submitIndicatorCallsCount == 1)
XCTAssert(userIndicatorControllerMock.submitIndicatorDelayCallsCount == 1)
XCTAssert(userIndicatorControllerMock.retractIndicatorWithIdCallsCount == 1)
XCTAssert(roomProxyMock.getMemberUserIDCallsCount == 1)
XCTAssertEqual(roomProxyMock.getMemberUserIDReceivedUserID, "bob")