Files
ios-mail/Modules/InboxCoreUI/Sources/Lock/LockScreenStore.swift
T
Maciej Gomółka 4727fca432 Merge branch 'main' into release/5.1.0-main
# Conflicts:
#	Modules/App/Sources/UI/Actions/Views/MailboxActionBar/MailboxActionBarView.swift
#	Modules/App/Sources/UI/Screens/ConversationDetail/Cells/ExpandedMessageViews/MessageDetailsView.swift
#	Modules/App/Sources/UI/Screens/ConversationDetail/ConversationDetailScreen.swift
#	Modules/App/Tests/Doubles/MailSessionSpy.swift
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithArchiveLocationNoLabelsLayoutsCorrectly.collapsed_dark.png
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithArchiveLocationNoLabelsLayoutsCorrectly.collapsed_light.png
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithArchiveLocationNoLabelsLayoutsCorrectly.expanded_dark.png
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithArchiveLocationNoLabelsLayoutsCorrectly.expanded_light.png
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithCustomLocationAndLabelsLayoutsCorrectly.collapsed_dark.png
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithCustomLocationAndLabelsLayoutsCorrectly.collapsed_light.png
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithCustomLocationAndLabelsLayoutsCorrectly.expanded_dark.png
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithCustomLocationAndLabelsLayoutsCorrectly.expanded_light.png
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithInboxLocationLayoutsCorrectly.collapsed_dark.png
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithInboxLocationLayoutsCorrectly.collapsed_light.png
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithInboxLocationLayoutsCorrectly.expanded_dark.png
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithInboxLocationLayoutsCorrectly.expanded_light.png
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithOutboxLocationLayoutsCorrectly.dark.png
#	Modules/App/Tests/Tests/Snapshots/Screens/ConversationDetails/__Snapshots__/MessageDetailsViewSnapshotTests/testMessageDetailsWithOutboxLocationLayoutsCorrectly.light.png
#	Modules/InboxContacts/Sources/Generated/Rust.generated.swift
#	ProtonPackages/et-protoncore
#	mail-sdk-version
2025-07-15 11:36:55 +02:00

119 lines
3.9 KiB
Swift

// Copyright (c) 2025 Proton Technologies AG
//
// This file is part of Proton Mail.
//
// Proton Mail is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail. If not, see https://www.gnu.org/licenses/.
import Combine
import InboxCore
import proton_app_uniffi
class LockScreenStore: StateStore {
@Published var state: LockScreenState
private let pinVerifier: PINVerifier
private let biometricAuthorizationNotifier: () -> BiometricAuthorizationNotifier
private let signOutService: SignOutService
private let dismissLock: () -> Void
init(
state: LockScreenState,
pinVerifier: PINVerifier,
mailSession: @escaping @Sendable () -> MailSessionProtocol,
biometricAuthorizationNotifier: @escaping @Sendable () -> BiometricAuthorizationNotifier,
dismissLock: @escaping () -> Void
) {
self.state = state
self.pinVerifier = pinVerifier
self.signOutService = .init(mailSession: mailSession)
self.biometricAuthorizationNotifier = biometricAuthorizationNotifier
self.dismissLock = dismissLock
}
@MainActor
func handle(action: LockScreenAction) async {
switch action {
case .biometric(let output):
switch output {
case .authenticated:
biometricAuthorizationNotifier().biometricsCheckPassed()
dismissLock()
case .logOut:
await signOutAllAccounts()
}
case .pin(let output):
switch output {
case .logOut:
await signOutAllAccounts()
case .pin(let pin):
await verify(pin: pin)
}
case .pinScreenLoaded:
let pinAttemptsRemaining = await readNumberOfAttempts()
if pinAttemptsRemaining <= 3 {
handlePinAuthenticationError(attemptsLeft: pinAttemptsRemaining)
}
}
}
@MainActor
private func signOutAllAccounts() async {
do {
try await signOutService.signOutAllAccounts()
dismissLock()
} catch {
AppLogger.log(error: error, category: .appSettings)
}
}
@MainActor
private func handlePinAuthenticationError(attemptsLeft: Int) {
switch attemptsLeft {
case 0:
dismissLock()
case 1...3:
state =
state
.copy(\.pinAuthenticationError, to: .attemptsRemaining(attemptsLeft))
default:
state =
state
.copy(\.pinAuthenticationError, to: .custom(L10n.PINLock.invalidPIN.string))
}
}
@MainActor
private func verify(pin: PIN) async {
switch await pinVerifier.verifyPinCode(pin: pin.digits) {
case .ok:
dismissLock()
case .error(let error) where error == .reason(.tooFrequentAttempts):
state = state.copy(\.pinAuthenticationError, to: .tooFrequentAttempts)
case .error:
let pinAttemptsRemaining = await readNumberOfAttempts()
handlePinAuthenticationError(attemptsLeft: pinAttemptsRemaining)
}
}
@MainActor
private func readNumberOfAttempts() async -> Int {
do {
let attempts = try await pinVerifier.remainingPinAttempts().get() ?? 0
return Int(attempts)
} catch {
AppLogger.log(error: error, category: .appSettings)
return 0
}
}
}