Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions RIADigiDoc/Domain/Model/Error/NFC/DecryptError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Foundation
public enum DecryptError: Error {
case containerFileInvalid
case recipientsEmpty
case noCertLock
case cancelled
case unknown(Error)
}
Expand All @@ -33,6 +34,8 @@ extension DecryptError: LocalizedError {
return "Container file is invalid"
case .recipientsEmpty:
return "Person or company does not own a valid certificate"
case .noCertLock:
return "Failed to find lock for cert"
case .cancelled:
return "Operation cancelled by user"
case .unknown(let error):
Expand Down
7 changes: 4 additions & 3 deletions RIADigiDoc/Domain/NFC/NFCOperationBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,17 @@ public class NFCOperationBase: NSObject, Loggable, @MainActor NFCTagReaderSessio
nfcError = strings?.technicalErrorMessage ?? ""
}
}

func handleNoCertLockError(
error: Error,
session: NFCTagReaderSession
) {
Self.logger().error("NFC: Failed to find lock for cert")
Self.logger().error("NFC: Failed to find lock for cert, error: \(error)")
nfcError = strings?.wrongCardErrorMessage ?? ""
operationError = DecryptError.noCertLock
session.invalidate(errorMessage: nfcError)
}

func handleIdCardInternalError(
_ error: IdCardInternalError,
session: NFCTagReaderSession
Expand Down
1 change: 1 addition & 0 deletions RIADigiDoc/Domain/NFC/OperationReadCertAndSign.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public class OperationReadCertAndSign: NFCOperationBase, OperationReadCertAndSig

// MARK: - NFCTagReaderSessionDelegate

// swiftlint:disable:next cyclomatic_complexity
public override func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
Task { @MainActor in
defer {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ struct CryptoDataFilesLockedSection: View {
Text(verbatim: languageSettings.localized("Crypto files encrypted"))
.font(typography.bodyMedium)
.fixedSize(horizontal: false, vertical: true)
.lineLimit(4)
.truncationMode(.middle)
.lineLimit(nil)
.minimumScaleFactor(0.5)
.multilineTextAlignment(TextAlignment.leading)
.accessibilityLabel(
Text(
Expand Down
23 changes: 17 additions & 6 deletions RIADigiDoc/UI/Component/Container/Crypto/DecryptRootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import IdCardLib
import CommonsLib

struct DecryptRootView: View {
@Environment(\.accessibilityVoiceOverEnabled) private var voiceOverEnabled
@Environment(\.dismiss) private var dismiss
@Environment(LanguageSettings.self) private var languageSettings
@Environment(NavigationPathManager.self) private var pathManager
Expand All @@ -36,6 +37,10 @@ struct DecryptRootView: View {

private let sharedContainerViewModel: SharedContainerViewModelProtocol

private var containerSuccessfullyDecryptedMessage: String {
languageSettings.localized("Container successfully decrypted")
}

init() {
_viewModel = State(wrappedValue: Container.shared.decryptRootViewModel())
self.sharedContainerViewModel = Container.shared.sharedContainerViewModel()
Expand All @@ -59,9 +64,7 @@ struct DecryptRootView: View {
sharedContainerViewModel.removeLastContainer()
sharedContainerViewModel.setCryptoContainer(container)

Toast.show(languageSettings.localized(
"Container successfully decrypted"
), type: .success)
showContainerSuccessfullyDecryptedMessage()
}
)
}
Expand All @@ -78,9 +81,7 @@ struct DecryptRootView: View {
sharedContainerViewModel.removeLastContainer()
sharedContainerViewModel.setCryptoContainer(container)

Toast.show(languageSettings.localized(
"Container successfully decrypted"
), type: .success)
showContainerSuccessfullyDecryptedMessage()
}
)
}
Expand All @@ -96,6 +97,16 @@ struct DecryptRootView: View {
}
}
}

func showContainerSuccessfullyDecryptedMessage() {
Toast.show(languageSettings.localized(
containerSuccessfullyDecryptedMessage
), type: .success)

if voiceOverEnabled {
AccessibilityUtil.announceMessage(containerSuccessfullyDecryptedMessage)
}
}
}

#Preview {
Expand Down
26 changes: 18 additions & 8 deletions RIADigiDoc/UI/Component/Container/Crypto/EncryptView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import UtilsLib

struct EncryptView: View {
@Environment(\.presentationMode) var presentationMode
@Environment(\.accessibilityVoiceOverEnabled) private var voiceOverEnabled
@AppTheme private var theme
@AppTypography private var typography
@Environment(NavigationPathManager.self) private var pathManager
Expand Down Expand Up @@ -158,7 +159,7 @@ struct EncryptView: View {
private var containerExtension: String {
URL(fileURLWithPath: viewModel.containerName).pathExtension
}

private var containerIcon: String {
viewModel.isContainerDecrypted
? "ic_m3_encrypted_off_48pt_wght400"
Expand Down Expand Up @@ -513,18 +514,27 @@ struct EncryptView: View {
.animation(.easeInOut, value: showRenameModal)
.onChange(of: viewModel.errorMessage) { _, error in
guard let error else { return }
Toast.show(
languageSettings.localized(error.key, [error.args.joined(separator: ", ")])
)
let localizedMessage = languageSettings
.localized(error.key, [error.args.joined(separator: ", ")])
Toast.show(localizedMessage)

if voiceOverEnabled {
AccessibilityUtil.announceMessage(localizedMessage)
}

viewModel.resetErrorMessage()
encryptionButtonEnabled = true
}
.onChange(of: viewModel.successMessage) { _, message in
guard let message else { return }
Toast.show(
languageSettings.localized(message.key, [message.args.joined(separator: ", ")]),
type: .success
)
let localizedMessage = languageSettings
.localized(message.key, [message.args.joined(separator: ", ")])
Toast.show(localizedMessage, type: .success)

if voiceOverEnabled {
AccessibilityUtil.announceMessage(localizedMessage)
}

viewModel.resetSuccessMessage()
}
.onChange(of: viewModel.navigateToNestedSignedContainerView) { _, isNavigating in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import CommonsLib
import CryptoObjCWrapper

struct EncryptRecipientView: View {
@Environment(\.accessibilityVoiceOverEnabled) private var voiceOverEnabled
@Environment(\.dismiss) private var dismiss
@Environment(LanguageSettings.self) private var languageSettings

Expand All @@ -32,6 +33,9 @@ struct EncryptRecipientView: View {
@AppTheme private var theme
@AppTypography private var typography

@AccessibilityFocusState private var isTitleFocused: Bool
@AccessibilityFocusState private var isSearchFieldFocused: Bool
@AccessibilityFocusState private var focusedRecipientIndex: Int?
@FocusState private var isSearchFocused: Bool
@State private var isSearchExpanded = false

Expand Down Expand Up @@ -67,11 +71,11 @@ struct EncryptRecipientView: View {
var encryptLabel: String {
languageSettings.localized("Encrypt")
}

var noSearchResultsMessage: String {
languageSettings.localized("Person or company does not own a valid certificate")
}

private var addedRecipientsSection: some View {
VStack(alignment: .leading, spacing: Dimensions.Padding.ZeroPadding) {
if noSearchResults {
Expand Down Expand Up @@ -99,7 +103,7 @@ struct EncryptRecipientView: View {
.listRowSpacing(0)
.listSectionSpacing(.compact)
}

private var filteredRecipientsSection: some View {
VStack {
if #available(iOS 26.0, *) {
Expand Down Expand Up @@ -134,8 +138,16 @@ struct EncryptRecipientView: View {
.foregroundStyle(theme.onSurface)
.font(typography.headlineSmall)
.padding(.top, Dimensions.Padding.SPadding)
.minimumScaleFactor(0.5)
.accessibilityHeading(.h1)
.accessibilityAddTraits([.isHeader])
.accessibilityFocused($isTitleFocused)
.accessibilitySortPriority(3)
.onAppear {
if noSearchResults {
isTitleFocused = true
}
}
}
HStack {
Image(systemName: "magnifyingglass")
Expand All @@ -149,31 +161,56 @@ struct EncryptRecipientView: View {
.submitLabel(.done)
.textInputAutocapitalization(.never)
.autocorrectionDisabled(true)
.accessibilityFocused($isSearchFieldFocused)
.focused($isSearchFocused)
.onChange(of: isSearchFocused) { _, newValue in
isSearchExpanded = newValue
}
.onChange(of: viewModel.searchText) {
viewModel.handleSearchTextChange()
}
.onChange(of: filteredRecipients) { _, newValue in
guard !newValue.isEmpty else { return }

Task { @MainActor in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
focusedRecipientIndex = 0
}
}
}
.onSubmit {
if viewModel.searchText.allSatisfy(\.isNumber) &&
viewModel.searchText.count == 11 &&
!PersonalCodeValidator.isPersonalCodeValid(viewModel.searchText) {
Toast.show(languageSettings.localized("Personal code is not valid"))
let personalCodeNotValidMessage = languageSettings.localized(
"Personal code is not valid"
)

Toast.show(personalCodeNotValidMessage)

if voiceOverEnabled {
AccessibilityUtil.announceMessage(
personalCodeNotValidMessage
)
}
return
}

Task {
await viewModel.loadRecipients()

if noRecipients {
showNoRecipientsFoundMessage = true
}
await MainActor.run {
isTitleFocused = false

isSearchFocused = true
if voiceOverEnabled && noRecipients {
AccessibilityUtil.announceMessage(
languageSettings.localized("No recipients found")
)
}
}
}
}
.accessibilitySortPriority(1)

if isSearchExpanded {
Button(
Expand Down Expand Up @@ -204,6 +241,7 @@ struct EncryptRecipientView: View {
)
.padding(.top, Dimensions.Padding.LPadding)
.padding(.bottom, Dimensions.Padding.SPadding)

ScrollView {
if noSearchResults && !isSearchExpanded {
VStack {
Expand All @@ -218,7 +256,11 @@ struct EncryptRecipientView: View {
.scrollDisabled(true)
.scrollContentBackground(.hidden)
} else if showNoRecipientsFoundMessage {
emptyStateView(languageSettings.localized("Person or company does not own a valid certificate"))
emptyStateView(
languageSettings.localized(
"Person or company does not own a valid certificate"
)
)
} else {
filteredRecipientsSection
}
Expand All @@ -229,8 +271,10 @@ struct EncryptRecipientView: View {
addedRecipientsSection
}
}
.accessibilitySortPriority(filteredRecipients.isEmpty ? 2 : 0)
}
.padding(.horizontal, Dimensions.Padding.SPadding)
.accessibilityElement(children: .contain)

if showRemoveRecipientModal {
ConfirmModalView(
Expand Down Expand Up @@ -298,6 +342,8 @@ struct EncryptRecipientView: View {
.padding(Dimensions.Padding.MPadding)
.accessibilityElement(children: .ignore)
.accessibilityLabel(encryptLabel.lowercased())
.accessibilityAddTraits([.isButton])

.accessibilityIdentifier("bottomEncryptButton")
}
.onAppear {
Expand Down Expand Up @@ -338,6 +384,7 @@ struct EncryptRecipientView: View {
.contentShape(Rectangle())
.buttonStyle(.plain)
.background(theme.surface)
.accessibilityFocused($focusedRecipientIndex, equals: index)
}

private func addedRecipientRow(index: Int, item: Addressee) -> some View {
Expand All @@ -364,7 +411,7 @@ struct EncryptRecipientView: View {
.buttonStyle(.plain)
.background(theme.surface)
}

private func emptyStateView(_ text: String) -> some View {
ContentUnavailableView {
Text(verbatim: text)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,9 @@ struct RecipientsView: View {
.fixedSize(horizontal: false, vertical: true)
.multilineTextAlignment(.leading)
.accessibilityLabel({
if recipientIndex != 0 {
let prefix = languageSettings.localized("Recipient")
let name = nameText.lowercased()
return Text(verbatim: "\(prefix) \(recipientIndex), \(name)")
} else {
return Text(verbatim: nameText.lowercased())
}
let prefix = languageSettings.localized("Recipient")
let name = nameText.lowercased()
return Text(verbatim: "\(prefix) \(recipientIndex + 1), \(name)")
}())

let certType = recipientUtil.getRecipientCertTypeText(certType: recipient.certType)
Expand Down Expand Up @@ -142,7 +138,7 @@ struct RecipientsView: View {
.frame(width: Dimensions.Icon.IconSizeXXS, height: Dimensions.Icon.IconSizeXXS)
.foregroundStyle(theme.onSurface)
.accessibilityLabel(
Text(verbatim: languageSettings.localized("Remove container"))
Text(verbatim: languageSettings.localized("Remove recipient"))
)
})
}
Expand Down
Loading
Loading