Silence some warnings. (#4969)

This commit is contained in:
Doug
2026-01-19 15:16:16 +00:00
committed by GitHub
parent a30ae24d16
commit 1faf9125c1
12 changed files with 50 additions and 55 deletions
@@ -96,7 +96,6 @@ class SpacesTabFlowCoordinator: FlowCoordinatorProtocol {
// MARK: - Private // MARK: - Private
// swiftlint:disable:next cyclomatic_complexity
private func configureStateMachine() { private func configureStateMachine() {
stateMachine.addRoutes(event: .start, transitions: [.initial => .spacesScreen(selectedSpaceID: nil)]) { [weak self] _ in stateMachine.addRoutes(event: .start, transitions: [.initial => .spacesScreen(selectedSpaceID: nil)]) { [weak self] _ in
self?.presentSpacesScreen() self?.presentSpacesScreen()
@@ -307,7 +307,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
analytics: ServiceLocator.shared.analytics, analytics: ServiceLocator.shared.analytics,
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
viewModel.updateAvatar(fileURL: Bundle.main.url(forResource: "preview_avatar_room", withExtension: "jpg")!) viewModel.updateAvatar(fileURL: Bundle.main.url(forResource: "preview_avatar_room", withExtension: "jpg") ?? .picturesDirectory)
return viewModel return viewModel
}() }()
@@ -331,7 +331,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
analytics: ServiceLocator.shared.analytics, analytics: ServiceLocator.shared.analytics,
userIndicatorController: UserIndicatorControllerMock(), userIndicatorController: UserIndicatorControllerMock(),
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
viewModel.updateAvatar(fileURL: Bundle.main.url(forResource: "preview_avatar_room", withExtension: "jpg")!) viewModel.updateAvatar(fileURL: Bundle.main.url(forResource: "preview_avatar_room", withExtension: "jpg") ?? .picturesDirectory)
return viewModel return viewModel
}() }()
@@ -37,33 +37,36 @@ enum QRCodeLoginScreenMode {
struct QRCodeLoginScreenViewState: BindableState { struct QRCodeLoginScreenViewState: BindableState {
var state: QRCodeLoginState var state: QRCodeLoginState
let mode: QRCodeLoginScreenMode
/// Whether or not it is possible for the screen to start the manual sign in flow. This was added to avoid /// Whether or not it is possible for the screen to start the manual sign in flow. This was added to avoid
/// having to handle server configuration when ``AppSettings.allowOtherAccountProviders`` is false. /// having to handle server configuration when ``AppSettings.allowOtherAccountProviders`` is false.
let canSignInManually: Bool let canSignInManually: Bool
let isPresentedModally: Bool
let instructions = QRCodeLoginScreenInstructions() let instructions = QRCodeLoginScreenInstructions()
var bindings = QRCodeLoginScreenViewStateBindings() var bindings = QRCodeLoginScreenViewStateBindings()
var shouldDisplayCancelButton: Bool { var shouldDisplayCancelButton: Bool {
// TODO: Simplify/validate these assumptions. switch mode {
if isPresentedModally { case .login:
switch state { switch state {
case .loginInstructions, .scan, .error(.noCameraPermission): true case .loginInstructions, .scan, .error(.noCameraPermission): true
default: false case .error: false
case .linkDesktopInstructions, .displayCode, .displayQR, .confirmCode: false // Unreachable states.
} }
} else { case .linkDesktop, .linkMobile:
switch state { switch state {
case .displayCode, .confirmCode, .scan, .error(.noCameraPermission): true case .displayCode, .confirmCode, .scan, .error(.noCameraPermission): true
case .loginInstructions, .linkDesktopInstructions, .displayQR, .error: false case .linkDesktopInstructions, .displayQR, .error: false
case .loginInstructions: false // Unreachable state.
} }
} }
} }
var shouldDisplayBackButton: Bool { var shouldDisplayBackButton: Bool {
if isPresentedModally { switch mode {
false case .login:
} else { false // Login is presented modally, never show the back button.
case .linkDesktop, .linkMobile:
switch state { switch state {
case .loginInstructions, .linkDesktopInstructions, .displayQR: true case .loginInstructions, .linkDesktopInstructions, .displayQR: true
case .displayCode, .confirmCode, .scan, .error: false case .displayCode, .confirmCode, .scan, .error: false
@@ -12,7 +12,6 @@ import Foundation
typealias QRCodeLoginScreenViewModelType = StateStoreViewModel<QRCodeLoginScreenViewState, QRCodeLoginScreenViewAction> typealias QRCodeLoginScreenViewModelType = StateStoreViewModel<QRCodeLoginScreenViewState, QRCodeLoginScreenViewAction>
class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScreenViewModelProtocol { class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScreenViewModelProtocol {
private let mode: QRCodeLoginScreenMode
private let appMediator: AppMediatorProtocol private let appMediator: AppMediatorProtocol
private let actionsSubject: PassthroughSubject<QRCodeLoginScreenViewModelAction, Never> = .init() private let actionsSubject: PassthroughSubject<QRCodeLoginScreenViewModelAction, Never> = .init()
@@ -26,24 +25,19 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr
init(mode: QRCodeLoginScreenMode, init(mode: QRCodeLoginScreenMode,
canSignInManually: Bool, canSignInManually: Bool,
appMediator: AppMediatorProtocol) { appMediator: AppMediatorProtocol) {
self.mode = mode
self.appMediator = appMediator self.appMediator = appMediator
let initialState: QRCodeLoginScreenViewState = switch mode { let initialViewState: QRCodeLoginState = switch mode {
case .login: case .login: .loginInstructions
.init(state: .loginInstructions, canSignInManually: canSignInManually, isPresentedModally: true) case .linkDesktop: .linkDesktopInstructions
case .linkDesktop:
.init(state: .linkDesktopInstructions, canSignInManually: canSignInManually, isPresentedModally: false)
case .linkMobile(let progressPublisher): case .linkMobile(let progressPublisher):
switch progressPublisher.value { switch progressPublisher.value {
case .qrReady(let image): case .qrReady(let image): .displayQR(image)
.init(state: .displayQR(image), canSignInManually: canSignInManually, isPresentedModally: false) default: .error(.unknown)
default:
.init(state: .error(.unknown), canSignInManually: canSignInManually, isPresentedModally: false)
} }
} }
super.init(initialViewState: initialState) super.init(initialViewState: .init(state: initialViewState, mode: mode, canSignInManually: canSignInManually))
setupSubscriptions() setupSubscriptions()
if case .linkMobile(let progressPublisher) = mode { if case .linkMobile(let progressPublisher) = mode {
@@ -62,7 +56,7 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr
case .sendCheckCode: case .sendCheckCode:
Task { await sendCheckCode() } Task { await sendCheckCode() }
case .errorAction(.startOver): case .errorAction(.startOver):
switch mode { switch state.mode {
case .login: case .login:
Task { await startScanIfPossible() } Task { await startScanIfPossible() }
case .linkDesktop, .linkMobile: case .linkDesktop, .linkMobile:
@@ -88,7 +82,7 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak self] qrData in .sink { [weak self] qrData in
guard let self else { return } guard let self else { return }
switch mode { switch state.mode {
case .login(let qrCodeLoginService): case .login(let qrCodeLoginService):
handleScan(qrData: qrData, loginService: qrCodeLoginService) handleScan(qrData: qrData, loginService: qrCodeLoginService)
case .linkDesktop(let linkNewDeviceService): case .linkDesktop(let linkNewDeviceService):
@@ -152,8 +146,6 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr
} }
} }
// TODO: when user cancels in UI then the underlying login needs to be cancelled too. It's unclear if we have that exposed in the bindings yet.
private func handleScan(qrData: Data, linkService: LinkNewDeviceServiceProtocol) { private func handleScan(qrData: Data, linkService: LinkNewDeviceServiceProtocol) {
guard currentTask == nil else { return } guard currentTask == nil else { return }
@@ -314,24 +306,23 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr
} }
/// Only for mocking initial states /// Only for mocking initial states
fileprivate init(state: QRCodeLoginState, canSignInManually: Bool, isPresentedModally: Bool, checkCodeInput: String) { fileprivate init(state: QRCodeLoginState, mode: QRCodeLoginScreenMode, canSignInManually: Bool, checkCodeInput: String) {
mode = .login(QRCodeLoginServiceMock())
appMediator = AppMediatorMock.default appMediator = AppMediatorMock.default
super.init(initialViewState: .init(state: state, super.init(initialViewState: .init(state: state,
mode: mode,
canSignInManually: canSignInManually, canSignInManually: canSignInManually,
isPresentedModally: isPresentedModally,
bindings: .init(checkCodeInput: checkCodeInput))) bindings: .init(checkCodeInput: checkCodeInput)))
} }
} }
extension QRCodeLoginScreenViewModel { extension QRCodeLoginScreenViewModel {
static func mock(state: QRCodeLoginState, static func mock(state: QRCodeLoginState,
mode: QRCodeLoginScreenMode = .login(QRCodeLoginServiceMock()),
canSignInManually: Bool = true, canSignInManually: Bool = true,
isPresentedModally: Bool = true,
checkCodeInput: String = "") -> QRCodeLoginScreenViewModel { checkCodeInput: String = "") -> QRCodeLoginScreenViewModel {
QRCodeLoginScreenViewModel(state: state, QRCodeLoginScreenViewModel(state: state,
mode: mode,
canSignInManually: canSignInManually, canSignInManually: canSignInManually,
isPresentedModally: isPresentedModally,
checkCodeInput: checkCodeInput) checkCodeInput: checkCodeInput)
} }
} }
@@ -113,7 +113,7 @@ class AudioRecorder: AudioRecorderProtocol {
MXLog.info("setup audio session") MXLog.info("setup audio session")
try audioSession.setAllowHapticsAndSystemSoundsDuringRecording(true) try audioSession.setAllowHapticsAndSystemSoundsDuringRecording(true)
try audioSession.setCategory(.playAndRecord, mode: .default, options: [.allowBluetooth]) try audioSession.setCategory(.playAndRecord, mode: .default, options: [.allowBluetoothHFP])
try audioSession.setActive(true) try audioSession.setActive(true)
addObservers() addObservers()
} }
@@ -160,6 +160,9 @@ class AuthenticationService: AuthenticationServiceProtocol {
return progressSubject.asCurrentValuePublisher() return progressSubject.asCurrentValuePublisher()
} }
// At some stage the SDK will have a `qrCodeData.intent` which we should check before continuing here.
// Note the equivalent check will also happen for linking a device by QR in the LinkNewDeviceService.
guard let scannedServerName = qrData.serverName() else { guard let scannedServerName = qrData.serverName() else {
MXLog.error("The QR code is from a device that is not yet signed in.") MXLog.error("The QR code is from a device that is not yet signed in.")
progressSubject.send(completion: .failure(.qrCodeError(.deviceNotSignedIn))) progressSubject.send(completion: .failure(.qrCodeError(.deviceNotSignedIn)))
@@ -63,7 +63,7 @@ class LinkNewDeviceService: LinkNewDeviceServiceProtocol {
Task { Task {
do { do {
// TODO: we need a way to cancel the in progress grant if the user hit the cancel button // Note: The SDK doesn't provide us with a way to cancel the grant if the user hit the cancel button 🤷‍♂️
try await grantLoginHandler.generate(progressListener: listener) // The success state is handled by the listener. try await grantLoginHandler.generate(progressListener: listener) // The success state is handled by the listener.
} catch let error as HumanQrGrantLoginError { } catch let error as HumanQrGrantLoginError {
MXLog.error("QR code reciprocate error: \(error)") MXLog.error("QR code reciprocate error: \(error)")
@@ -98,12 +98,12 @@ class LinkNewDeviceService: LinkNewDeviceServiceProtocol {
return progressSubject.asCurrentValuePublisher() return progressSubject.asCurrentValuePublisher()
} }
#warning("Check intent/server name??") // At some stage the SDK will have a `qrCodeData.intent` which we should check before continuing here.
#warning("Check Element Pro here??") // Note the equivalent check will also happen for sign in with QR code in the AuthenticationService.
Task { Task {
do { do {
// TODO: it would be nice to be able to cancel the grant at the SDK level if the user hits the cancel button // Note: The SDK doesn't provide us with a way to cancel the grant if the user hit the cancel button 🤷‍♂️
try await grantLoginHandler.scan(qrCodeData: qrCodeData, progressListener: listener) // The success state is handled by the listener. try await grantLoginHandler.scan(qrCodeData: qrCodeData, progressListener: listener) // The success state is handled by the listener.
} catch let error as HumanQrGrantLoginError { } catch let error as HumanQrGrantLoginError {
MXLog.error("QR code reciprocate error: \(error)") MXLog.error("QR code reciprocate error: \(error)")
@@ -216,7 +216,9 @@ class CheckCodeSenderProxy: Equatable {
try await underlyingSender.send(code: code) try await underlyingSender.send(code: code)
} }
#warning("Waiting for an SDK update to use the underlying sender.") // Bypassed for now whilst we wait for an SDK update (however its worth noting that
// things should still fail if the wrong code is provided, just not necessarily with
// the right error being shown). https://github.com/matrix-org/matrix-rust-sdk/pull/5957
func validate(checkCode: UInt8) -> Bool { true } func validate(checkCode: UInt8) -> Bool { true }
} }
@@ -451,9 +451,6 @@ struct MediaUploadingPreprocessor {
try? FileManager.default.removeItem(at: outputURL) try? FileManager.default.removeItem(at: outputURL)
exportSession.outputURL = outputURL
exportSession.outputFileType = AVFileType.mp4
guard exportSession.supportedFileTypes.contains(AVFileType.mp4) else { guard exportSession.supportedFileTypes.contains(AVFileType.mp4) else {
throw .failedConvertingVideo throw .failedConvertingVideo
} }
@@ -463,9 +460,10 @@ struct MediaUploadingPreprocessor {
exportSession.fileLengthLimit = Int64(Double(targetFileSize) * 0.9) exportSession.fileLengthLimit = Int64(Double(targetFileSize) * 0.9)
} }
await exportSession.export() do {
try await exportSession.export(to: outputURL, as: .mp4)
guard exportSession.status == .completed else { } catch {
MXLog.error("Video conversion failed: \(error)")
throw .failedConvertingVideo throw .failedConvertingVideo
} }
+2 -3
View File
@@ -50,9 +50,8 @@ class DateTests: XCTestCase {
XCTAssertEqual(threeDaysAgo.formattedDateSeparator(), threeDaysAgo.formatted(.dateTime.weekday(.wide))) XCTAssertEqual(threeDaysAgo.formattedDateSeparator(), threeDaysAgo.formatted(.dateTime.weekday(.wide)))
// This test will fail during the first 6 days of the year. // This test will fail during the first 6 days of the year.
let startOfTheYear = calendar.date(bySetting: .dayOfYear, value: 1, of: startOfToday)! let startOfTheYear = calendar.dateInterval(of: .year, for: startOfToday)!.start
// FIXME: Uncomment on the 7th Jan. XCTAssertEqual(startOfTheYear.formattedDateSeparator(), startOfTheYear.formatted(.dateTime.weekday(.wide).day().month(.wide)))
// XCTAssertEqual(startOfTheYear.formattedDateSeparator(), startOfTheYear.formatted(.dateTime.weekday(.wide).day().month(.wide)))
let theMillennium = calendar.date(from: DateComponents(year: 2000, month: 1, day: 1))! let theMillennium = calendar.date(from: DateComponents(year: 2000, month: 1, day: 1))!
XCTAssertEqual(theMillennium.formattedDateSeparator(), theMillennium.formatted(.dateTime.weekday(.wide).day().month(.wide).year())) XCTAssertEqual(theMillennium.formattedDateSeparator(), theMillennium.formatted(.dateTime.weekday(.wide).day().month(.wide).year()))
+5 -5
View File
@@ -34,7 +34,7 @@ class LoggingTests: XCTestCase {
return return
} }
try XCTAssertTrue(String(contentsOf: logFile).contains(infoLog)) try XCTAssertTrue(String(contentsOf: logFile, encoding: .utf8).contains(infoLog))
} }
func testLogLevels() throws { func testLogLevels() throws {
@@ -48,7 +48,7 @@ class LoggingTests: XCTestCase {
return return
} }
try XCTAssertFalse(String(contentsOf: logFile).contains(verboseLog)) try XCTAssertFalse(String(contentsOf: logFile, encoding: .utf8).contains(verboseLog))
} }
/// This is meant to test the `Target.tests.configure(…)`, but at this stage the test is somewhat pointless /// This is meant to test the `Target.tests.configure(…)`, but at this stage the test is somewhat pointless
@@ -103,7 +103,7 @@ class LoggingTests: XCTestCase {
return return
} }
let content = try String(contentsOf: logFile) let content = try String(contentsOf: logFile, encoding: .utf8)
XCTAssertTrue(content.contains(roomSummary.id)) XCTAssertTrue(content.contains(roomSummary.id))
XCTAssertFalse(content.contains(roomName)) XCTAssertFalse(content.contains(roomName))
XCTAssertFalse(content.contains(lastMessage)) XCTAssertFalse(content.contains(lastMessage))
@@ -185,7 +185,7 @@ class LoggingTests: XCTestCase {
return return
} }
let content = try String(contentsOf: logFile) let content = try String(contentsOf: logFile, encoding: .utf8)
XCTAssertTrue(content.contains(textMessage.id.uniqueID.value)) XCTAssertTrue(content.contains(textMessage.id.uniqueID.value))
XCTAssertFalse(content.contains(textMessage.body)) XCTAssertFalse(content.contains(textMessage.body))
XCTAssertFalse(content.contains(textAttributedString)) XCTAssertFalse(content.contains(textAttributedString))
@@ -254,7 +254,7 @@ class LoggingTests: XCTestCase {
return return
} }
let content = try String(contentsOf: logFile) let content = try String(contentsOf: logFile, encoding: .utf8)
XCTAssertTrue(content.contains(String(describing: TextMessageContent.self))) XCTAssertTrue(content.contains(String(describing: TextMessageContent.self)))
XCTAssertFalse(content.contains(textString)) XCTAssertFalse(content.contains(textString))
@@ -226,7 +226,7 @@ final class NotificationManagerTests: XCTestCase {
} }
} }
extension NotificationManagerTests: NotificationManagerDelegate { extension NotificationManagerTests: @MainActor NotificationManagerDelegate {
func registerForRemoteNotifications() { func registerForRemoteNotifications() {
authorizationStatusWasGranted = true authorizationStatusWasGranted = true
registerForRemoteNotificationsDelegateCalled?() registerForRemoteNotificationsDelegateCalled?()
@@ -98,7 +98,7 @@ class RoomDetailsEditScreenViewModelTests: XCTestCase {
XCTAssertFalse(context.viewState.canSave) XCTAssertFalse(context.viewState.canSave)
XCTAssertNil(context.alertInfo) XCTAssertNil(context.alertInfo)
var deferred = deferFulfillment(viewModel.actions) { $0 == .cancel } let deferred = deferFulfillment(viewModel.actions) { $0 == .cancel }
context.send(viewAction: .cancel) context.send(viewAction: .cancel)
try await deferred.fulfill() try await deferred.fulfill()
XCTAssertNil(context.alertInfo) XCTAssertNil(context.alertInfo)