diff --git a/BitwardenShared/Core/Vault/Helpers/CipherMatchingHelper.swift b/BitwardenShared/Core/Vault/Helpers/CipherMatchingHelper.swift index 7d7495df7..3af0460d0 100644 --- a/BitwardenShared/Core/Vault/Helpers/CipherMatchingHelper.swift +++ b/BitwardenShared/Core/Vault/Helpers/CipherMatchingHelper.swift @@ -49,7 +49,7 @@ class CipherMatchingHelper { /// - ciphers: The list of ciphers to filter. /// - Returns: The list of ciphers that match the URI. /// - func ciphersMatching(uri: String?, ciphers: [CipherView]) async -> [CipherView] { + func ciphersMatching(uri: String?, ciphers: [CipherListView]) async -> [CipherListView] { guard let uri else { return [] } let matchURL = URL(string: uri) @@ -66,7 +66,7 @@ class CipherMatchingHelper { let defaultMatchType = await (try? stateService.getDefaultUriMatchType()) ?? .domain let matchingCiphers = ciphers.reduce( - into: (exact: [CipherView], fuzzy: [CipherView])([], []) + into: (exact: [CipherListView], fuzzy: [CipherListView])([], []) ) { result, cipher in let match = checkForCipherMatch( cipher: cipher, @@ -138,15 +138,14 @@ class CipherMatchingHelper { /// - Returns: The result of the match for the cipher and URI. /// private func checkForCipherMatch( // swiftlint:disable:this function_parameter_count - cipher: CipherView, + cipher: CipherListView, defaultMatchType: UriMatchType, isApp: Bool, matchUri: String, matchingDomains: Set, matchingFuzzyDomains: Set ) -> MatchResult { - guard cipher.type == .login, - let login = cipher.login, + guard case let .login(login) = cipher.type, let loginUris = login.uris, cipher.deletedDate == nil else { return .none diff --git a/BitwardenShared/Core/Vault/Models/Enum/VaultFilterType.swift b/BitwardenShared/Core/Vault/Models/Enum/VaultFilterType.swift index 44b39d6cd..319008eb8 100644 --- a/BitwardenShared/Core/Vault/Models/Enum/VaultFilterType.swift +++ b/BitwardenShared/Core/Vault/Models/Enum/VaultFilterType.swift @@ -46,7 +46,7 @@ extension VaultFilterType { /// - Parameter cipher: The `CipherView` to determine if it should be in the vault list. /// - Returns: Whether the cipher should be displayed in the vault list. /// - func cipherFilter(_ cipher: CipherView) -> Bool { + func cipherFilter(_ cipher: CipherListView) -> Bool { switch self { case .allVaults: true @@ -82,7 +82,7 @@ extension VaultFilterType { /// - ciphers: The `CipherView` objects used to determine if a folder is empty. /// - Returns: Whether the folder should be displayed in the vault list. /// - func folderFilter(_ folder: FolderView, ciphers: [CipherView]) -> Bool { + func folderFilter(_ folder: FolderView, ciphers: [CipherListView]) -> Bool { switch self { case .allVaults: return true diff --git a/BitwardenShared/Core/Vault/Repositories/VaultRepository.swift b/BitwardenShared/Core/Vault/Repositories/VaultRepository.swift index e66af858e..7ec2f2644 100644 --- a/BitwardenShared/Core/Vault/Repositories/VaultRepository.swift +++ b/BitwardenShared/Core/Vault/Repositories/VaultRepository.swift @@ -206,7 +206,7 @@ public protocol VaultRepository: AnyObject { /// /// - Returns: The list of a user's ciphers that can be used for autofill. func ciphersAutofillPublisher( - availableFido2CredentialsPublisher: AnyPublisher<[BitwardenSdk.CipherView]?, Error>, + availableFido2CredentialsPublisher: AnyPublisher<[BitwardenSdk.CipherListView]?, Error>, mode: AutofillListMode, rpID: String?, uri: String? @@ -235,7 +235,7 @@ public protocol VaultRepository: AnyObject { /// /// - Returns: A publisher for searching the user's ciphers for autofill. func searchCipherAutofillPublisher( - availableFido2CredentialsPublisher: AnyPublisher<[BitwardenSdk.CipherView]?, Error>, + availableFido2CredentialsPublisher: AnyPublisher<[BitwardenSdk.CipherListView]?, Error>, mode: AutofillListMode, filterType: VaultFilterType, rpID: String?, @@ -465,7 +465,7 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length /// - Returns: A list of `VaultListItem`s for the folders within a nested tree. /// private func folderVaultListItems( - activeCiphers: [CipherView], + activeCiphers: [CipherListView], folderTree: Tree, nestedFolderId: String? = nil ) -> [VaultListItem] { @@ -504,35 +504,33 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length searchText: String, filterType: VaultFilterType, isActive: Bool, - cipherFilter: ((CipherView) -> Bool)? = nil - ) async throws -> AnyPublisher<[CipherView], Error> { + cipherFilter: ((CipherListView) -> Bool)? = nil + ) async throws -> AnyPublisher<[CipherListView], Error> { let query = searchText.trimmingCharacters(in: .whitespacesAndNewlines) .lowercased() .folding(options: .diacriticInsensitive, locale: .current) - let isMatchingCipher: (CipherView) -> Bool = isActive + let isMatchingCipher: (CipherListView) -> Bool = isActive ? { $0.deletedDate == nil } : { $0.deletedDate != nil } let isSSHKeyVaultItemEnabled: Bool = await configService.getFeatureFlag(.sshKeyVaultItem) - let sshKeyFilter: (CipherView) -> Bool = { cipher in + let sshKeyFilter: (CipherListView) -> Bool = { cipher in cipher.type != .sshKey || isSSHKeyVaultItemEnabled } - return try await cipherService.ciphersPublisher().asyncTryMap { ciphers -> [CipherView] in + return try await cipherService.ciphersPublisher().asyncTryMap { ciphers -> [CipherListView] in // Convert the Ciphers to CipherViews and filter appropriately. - let matchingCiphers = try await ciphers.asyncMap { cipher in - try await self.clientService.vault().ciphers().decrypt(cipher: cipher) - } - .filter { cipher in - filterType.cipherFilter(cipher) && - isMatchingCipher(cipher) && - sshKeyFilter(cipher) && - (cipherFilter?(cipher) ?? true) - } + let matchingCiphers = try await self.clientService.vault().ciphers().decryptList(ciphers: ciphers) + .filter { cipher in + filterType.cipherFilter(cipher) && + isMatchingCipher(cipher) && + sshKeyFilter(cipher) && + (cipherFilter?(cipher) ?? true) + } - var matchedCiphers: [CipherView] = [] - var lowPriorityMatchedCiphers: [CipherView] = [] + var matchedCiphers: [CipherListView] = [] + var lowPriorityMatchedCiphers: [CipherListView] = [] // Search the ciphers. matchingCiphers.forEach { cipherView in @@ -541,10 +539,10 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length matchedCiphers.append(cipherView) } else if query.count >= 8, cipherView.id?.starts(with: query) == true { lowPriorityMatchedCiphers.append(cipherView) - } else if cipherView.subtitle?.lowercased() + } else if cipherView.subtitle.lowercased() .folding(options: .diacriticInsensitive, locale: nil).contains(query) == true { lowPriorityMatchedCiphers.append(cipherView) - } else if cipherView.login?.uris?.contains(where: { $0.uri?.contains(query) == true }) == true { + } else if case let .login(login) = cipherView.type, login.uris?.contains(where: { $0.uri?.contains(query) == true }) == true { lowPriorityMatchedCiphers.append(cipherView) } } @@ -563,7 +561,7 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length /// - Returns: A list of totpKey type items in the vault list. /// private func totpListItems( - from ciphers: [CipherView], + from ciphers: [CipherListView], filter: VaultFilterType? ) async throws -> [VaultListItem] { let hasPremiumFeaturesAccess = await (try? doesActiveAccountHavePremium()) ?? false @@ -571,11 +569,14 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length // Filter and sort the list. let activeCiphers = ciphers .filter(filter?.cipherFilter(_:) ?? { _ in true }) + .filter { $0.deletedDate == nil } .filter { cipher in - cipher.deletedDate == nil - && cipher.type == .login - && cipher.login?.totp != nil - && (hasPremiumFeaturesAccess || cipher.organizationUseTotp) + switch cipher.type { + case let .login(loginView): + return loginView.totp != nil && (hasPremiumFeaturesAccess || cipher.organizationUseTotp) + default: + return false + } } .sorted { $0.name.localizedStandardCompare($1.name) == .orderedAscending } @@ -592,10 +593,11 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length /// - Parameter cipherView: The cipher view that may have a TOTP key. /// - Returns: A `VaultListItem` if the CipherView supports TOTP. /// - private func totpItem(for cipherView: CipherView) async throws -> VaultListItem? { + private func totpItem(for cipherView: CipherListView) async throws -> VaultListItem? { guard let id = cipherView.id, - let login = cipherView.login, - let key = login.totp else { + case let .login(loginView) = cipherView.type, + let key = loginView.totp + else { return nil } guard let code = try? await clientService.vault().generateTOTPCode( @@ -609,19 +611,20 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length return nil } - let listModel = VaultListTOTP( - id: id, - loginView: login, - requiresMasterPassword: cipherView.reprompt == .password, - totpCode: code - ) - return VaultListItem( - id: id, - itemType: .totp( - name: cipherView.name, - totpModel: listModel - ) - ) + return nil + // let listModel = VaultListTOTP( + // id: id, + // loginView: login, + // requiresMasterPassword: cipherView.reprompt == .password, + // totpCode: code + // ) + // return VaultListItem( + // id: id, + // itemType: .totp( + // name: cipherView.name, + // totpModel: listModel + // ) + // ) } /// Returns a `VaultListSection` for the collection section, if one exists. @@ -637,7 +640,7 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length /// - Returns: A `VaultListSection` for the collection section, if one exists. /// private func vaultListCollectionSection( - activeCiphers: [CipherView], + activeCiphers: [CipherListView], collections: [Collection], filter: VaultFilterType, nestedCollectionId: String? = nil @@ -689,7 +692,7 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length /// - Returns: A `VaultListSection` for the folder section, if one exists. /// private func vaultListFolderSection( - activeCiphers: [CipherView], + activeCiphers: [CipherListView], group: VaultListGroup, filter: VaultFilterType, folders: [Folder] @@ -724,8 +727,8 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length /// - Returns: A `VaultListSection` for the vault items section. /// private func vaultListItemsSection( - activeCiphers: [CipherView], - deletedCiphers: [CipherView], + activeCiphers: [CipherListView], + deletedCiphers: [CipherListView], group: VaultListGroup, filter: VaultFilterType ) async throws -> VaultListSection { @@ -740,7 +743,9 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length case .identity: items = activeCiphers.filter { $0.type == .identity }.compactMap(VaultListItem.init) case .login: - items = activeCiphers.filter { $0.type == .login }.compactMap(VaultListItem.init) + items = activeCiphers.filter { if case .login = $0.type { + return true + } else { return false } }.compactMap(VaultListItem.init) case .noFolder: items = activeCiphers.filter { $0.folderId == nil }.compactMap(VaultListItem.init) case .secureNote: @@ -748,7 +753,8 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length case .sshKey: items = activeCiphers.filter { $0.type == .sshKey }.compactMap(VaultListItem.init) case .totp: - items = try await totpListItems(from: activeCiphers, filter: filter) + // items = try await totpListItems(from: activeCiphers, filter: filter) + items = [] case .trash: items = deletedCiphers.compactMap(VaultListItem.init) } @@ -774,11 +780,9 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length collections: [Collection], folders: [Folder] = [] ) async throws -> [VaultListSection] { - let ciphers = try await ciphers.asyncMap { cipher in - try await self.clientService.vault().ciphers().decrypt(cipher: cipher) - } - .filter(filter.cipherFilter) - .sorted { $0.name.localizedStandardCompare($1.name) == .orderedAscending } + let ciphers = try await clientService.vault().ciphers().decryptList(ciphers: ciphers) + .filter(filter.cipherFilter) + .sorted { $0.name.localizedStandardCompare($1.name) == .orderedAscending } let activeCiphers = ciphers.filter { $0.deletedDate == nil } let deletedCiphers = ciphers.filter { $0.deletedDate != nil } @@ -832,11 +836,9 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length folders: [Folder], filter: VaultFilterType ) async throws -> [VaultListSection] { - let ciphers = try await ciphers.asyncMap { cipher in - try await self.clientService.vault().ciphers().decrypt(cipher: cipher) - } - .filter(filter.cipherFilter) - .sorted { $0.name.localizedStandardCompare($1.name) == .orderedAscending } + let ciphers = try await clientService.vault().ciphers().decryptList(ciphers: ciphers) + .filter(filter.cipherFilter) + .sorted { $0.name.localizedStandardCompare($1.name) == .orderedAscending } let isSSHKeyVaultItemFlagEnabled: Bool = await configService.getFeatureFlag(.sshKeyVaultItem) let activeCiphers = ciphers.filter { cipher in @@ -891,7 +893,7 @@ class DefaultVaultRepository { // swiftlint:disable:this type_body_length let typesCardCount = activeCiphers.lazy.filter { $0.type == .card }.count let typesIdentityCount = activeCiphers.lazy.filter { $0.type == .identity }.count - let typesLoginCount = activeCiphers.lazy.filter { $0.type == .login }.count + let typesLoginCount = activeCiphers.lazy.filter { if case .login = $0.type { return true } else { return false } }.count let typesSecureNoteCount = activeCiphers.lazy.filter { $0.type == .secureNote }.count var types = [ @@ -1218,7 +1220,7 @@ extension DefaultVaultRepository: VaultRepository { } func ciphersAutofillPublisher( - availableFido2CredentialsPublisher: AnyPublisher<[BitwardenSdk.CipherView]?, Error>, + availableFido2CredentialsPublisher: AnyPublisher<[BitwardenSdk.CipherListView]?, Error>, mode: AutofillListMode, rpID: String?, uri: String? @@ -1228,9 +1230,8 @@ extension DefaultVaultRepository: VaultRepository { availableFido2CredentialsPublisher ) .asyncTryMap { ciphers, availableFido2Credentials in - let decryptedCiphers = try await ciphers.asyncMap { cipher in - try await self.clientService.vault().ciphers().decrypt(cipher: cipher) - } + let decryptedCiphers = try await self.clientService.vault().ciphers().decryptList(ciphers: ciphers) + let matchingCiphers = await CipherMatchingHelper( settingsService: self.settingsService, stateService: self.stateService @@ -1254,7 +1255,7 @@ extension DefaultVaultRepository: VaultRepository { } func searchCipherAutofillPublisher( - availableFido2CredentialsPublisher: AnyPublisher<[BitwardenSdk.CipherView]?, Error>, + availableFido2CredentialsPublisher: AnyPublisher<[BitwardenSdk.CipherListView]?, Error>, mode: AutofillListMode, filterType: VaultFilterType, rpID: String?, @@ -1266,7 +1267,7 @@ extension DefaultVaultRepository: VaultRepository { filterType: filterType, isActive: true ) { cipher in - cipher.type == .login + if case .login = cipher.type { return true } else { return false } }, availableFido2CredentialsPublisher ) @@ -1304,7 +1305,10 @@ extension DefaultVaultRepository: VaultRepository { case .identity: return cipher.type == .identity case .login: - return cipher.type == .login + if case .login = cipher.type { + return true + } + return false case .noFolder: return cipher.folderId == nil case .secureNote: @@ -1312,8 +1316,10 @@ extension DefaultVaultRepository: VaultRepository { case .sshKey: return cipher.type == .sshKey case .totp: - return cipher.type == .login - && cipher.login?.totp != nil + if case let .login(loginView) = cipher.type { + return loginView.totp != nil + } + return false case .trash: return cipher.deletedDate != nil } @@ -1378,8 +1384,8 @@ extension DefaultVaultRepository: VaultRepository { /// - searchText: The current search text. /// - Returns: The sections for the autofill list. private func createAutofillListSections( - availableFido2Credentials: [CipherView]?, - from ciphers: [CipherView], + availableFido2Credentials: [CipherListView]?, + from ciphers: [CipherListView], mode: AutofillListMode, rpID: String?, searchText: String? @@ -1394,15 +1400,17 @@ extension DefaultVaultRepository: VaultRepository { } var sections = [VaultListSection]() - if #available(iOSApplicationExtension 17.0, *), - let fido2Section = try await loadAutofillFido2Section( - availableFido2Credentials: availableFido2Credentials, - mode: mode, - rpID: rpID, - searchText: searchText, - searchResults: searchText != nil ? ciphers : nil - ) { - sections.append(fido2Section) + if #available(iOSApplicationExtension 17.0, *) { + // let fido2Section = try await loadAutofillFido2Section( + // availableFido2Credentials: availableFido2Credentials, + // mode: mode, + // rpID: rpID, + // searchText: searchText, + // searchResults: searchText != nil ? ciphers : nil + // ) { + // sections.append(fido2Section) + // + return [] } else if ciphers.isEmpty { return [] } @@ -1427,11 +1435,11 @@ extension DefaultVaultRepository: VaultRepository { /// - Parameter ciphers: Ciphers to load. /// - Returns: The section to display passwords + Fido2 credentials. private func createAutofillListCombinedSingleSection( - from ciphers: [CipherView] + from ciphers: [CipherListView] ) async throws -> VaultListSection { let vaultItems = try await ciphers .asyncMap { cipher in - guard cipher.hasFido2Credentials else { + guard case let .login(loginView) = cipher.type, loginView.hasFido2 else { return VaultListItem(cipherView: cipher) } return try await createFido2VaultListItem(from: cipher) @@ -1448,21 +1456,23 @@ extension DefaultVaultRepository: VaultRepository { /// Creates a `VaultListItem` from a `CipherView` with Fido2 credentials. /// - Parameter cipher: Cipher from which create the item. /// - Returns: The `VaultListItem` with the cipher and Fido2 credentials. - func createFido2VaultListItem(from cipher: CipherView) async throws -> VaultListItem? { - let decryptedFido2Credentials = try await clientService - .platform() - .fido2() - .decryptFido2AutofillCredentials(cipherView: cipher) - - guard let fido2CredentialAutofillView = decryptedFido2Credentials.first else { - errorReporter.log(error: Fido2Error.decryptFido2AutofillCredentialsEmpty) - return nil - } - - return VaultListItem( - cipherView: cipher, - fido2CredentialAutofillView: fido2CredentialAutofillView - ) + func createFido2VaultListItem(from cipher: CipherListView) async throws -> VaultListItem? { + nil + // let decryptedFido2Credentials = try await clientService + // .platform() + // .fido2() + // .decryptFido2AutofillCredentials(cipherView: cipher) + // + // guard let fido2CredentialAutofillView = decryptedFido2Credentials.first else { + // errorReporter.log(error: Fido2Error.decryptFido2AutofillCredentialsEmpty) + // return nil + // } + // + // return VaultListItem( + // cipherView: cipher, + // fido2CredentialAutofillView: fido2CredentialAutofillView + // ) + // } /// Gets the passwords vault list section name depending on the context. @@ -1501,7 +1511,7 @@ extension DefaultVaultRepository: VaultRepository { /// - searchResults: The search results. /// - Returns: The vault list section for Fido2 autofill if needed. private func loadAutofillFido2Section( - availableFido2Credentials: [CipherView]?, + availableFido2Credentials: [CipherListView]?, mode: AutofillListMode, rpID: String?, searchText: String? = nil, diff --git a/BitwardenShared/UI/Vault/Extensions/Alert+Vault.swift b/BitwardenShared/UI/Vault/Extensions/Alert+Vault.swift index bffba53ab..6b423712e 100644 --- a/BitwardenShared/UI/Vault/Extensions/Alert+Vault.swift +++ b/BitwardenShared/UI/Vault/Extensions/Alert+Vault.swift @@ -163,7 +163,7 @@ extension Alert { @MainActor static func moreOptions( // swiftlint:disable:this function_body_length function_parameter_count cyclomatic_complexity line_length canCopyTotp: Bool, - cipherView: CipherView, + cipherView: CipherListView, hasMasterPassword: Bool, id: String, showEdit: Bool, @@ -183,119 +183,118 @@ extension Alert { )) }) } - - // Add any additional actions for the type of cipher selected. - switch cipherView.type { - case .card: - if let number = cipherView.card?.number { - alertActions.append(AlertAction(title: Localizations.copyNumber, style: .default) { _, _ in - await action(.copy( - toast: Localizations.number, - value: number, - requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword, - logEvent: nil, - cipherId: nil - )) - }) - } - if let code = cipherView.card?.code { - alertActions.append(AlertAction(title: Localizations.copySecurityCode, style: .default) { _, _ in - await action(.copy( - toast: Localizations.securityCode, - value: code, - requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword, - logEvent: .cipherClientCopiedCardCode, - cipherId: cipherView.id - )) - }) - } - case .login: - if let username = cipherView.login?.username { - alertActions.append(AlertAction(title: Localizations.copyUsername, style: .default) { _, _ in - await action(.copy( - toast: Localizations.username, - value: username, - requiresMasterPasswordReprompt: false, - logEvent: nil, - cipherId: nil - )) - }) - } - if let password = cipherView.login?.password, - cipherView.viewPassword { - alertActions.append(AlertAction(title: Localizations.copyPassword, style: .default) { _, _ in - await action(.copy( - toast: Localizations.password, - value: password, - requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword, - logEvent: .cipherClientCopiedPassword, - cipherId: cipherView.id - )) - }) - } - if canCopyTotp, let totp = cipherView.login?.totp { - alertActions.append(AlertAction(title: Localizations.copyTotp, style: .default) { _, _ in - await action(.copyTotp( - totpKey: TOTPKeyModel(authenticatorKey: totp), - requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword - )) - }) - } - if let uri = cipherView.login?.uris?.first?.uri, - let url = URL(string: uri) { - alertActions - .append(AlertAction(title: Localizations.launch, style: .default) { _, _ in - await action(.launch(url: url)) - }) - } - case .identity: - // No-op: no extra options beyond view and edit. - break - case .secureNote: - if let notes = cipherView.notes { - alertActions.append(AlertAction(title: Localizations.copyNotes, style: .default) { _, _ in - await action(.copy( - toast: Localizations.notes, - value: notes, - requiresMasterPasswordReprompt: false, - logEvent: nil, - cipherId: nil - )) - }) - } - case .sshKey: - if let sshKey = cipherView.sshKey { - alertActions.append(AlertAction(title: Localizations.copyPublicKey, style: .default) { _, _ in - await action(.copy( - toast: Localizations.publicKey, - value: sshKey.publicKey, - requiresMasterPasswordReprompt: false, - logEvent: nil, - cipherId: cipherView.id - )) - }) - if cipherView.viewPassword { - alertActions.append(AlertAction(title: Localizations.copyPrivateKey, style: .default) { _, _ in - await action(.copy( - toast: Localizations.privateKey, - value: sshKey.privateKey, - requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword, - logEvent: nil, - cipherId: cipherView.id - )) - }) - } - alertActions.append(AlertAction(title: Localizations.copyFingerprint, style: .default) { _, _ in - await action(.copy( - toast: Localizations.fingerprint, - value: sshKey.fingerprint, - requiresMasterPasswordReprompt: false, - logEvent: nil, - cipherId: cipherView.id - )) - }) - } - } + // // Add any additional actions for the type of cipher selected. + // switch cipherView.type { + // case .card: + // if let number = cipherView.card?.number { + // alertActions.append(AlertAction(title: Localizations.copyNumber, style: .default) { _, _ in + // await action(.copy( + // toast: Localizations.number, + // value: number, + // requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword, + // logEvent: nil, + // cipherId: nil + // )) + // }) + // } + // if let code = cipherView.card?.code { + // alertActions.append(AlertAction(title: Localizations.copySecurityCode, style: .default) { _, _ in + // await action(.copy( + // toast: Localizations.securityCode, + // value: code, + // requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword, + // logEvent: .cipherClientCopiedCardCode, + // cipherId: cipherView.id + // )) + // }) + // } + // case .login: + // if let username = cipherView.login?.username { + // alertActions.append(AlertAction(title: Localizations.copyUsername, style: .default) { _, _ in + // await action(.copy( + // toast: Localizations.username, + // value: username, + // requiresMasterPasswordReprompt: false, + // logEvent: nil, + // cipherId: nil + // )) + // }) + // } + // if let password = cipherView.login?.password, + // cipherView.viewPassword { + // alertActions.append(AlertAction(title: Localizations.copyPassword, style: .default) { _, _ in + // await action(.copy( + // toast: Localizations.password, + // value: password, + // requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword, + // logEvent: .cipherClientCopiedPassword, + // cipherId: cipherView.id + // )) + // }) + // } + // if canCopyTotp, let totp = cipherView.login?.totp { + // alertActions.append(AlertAction(title: Localizations.copyTotp, style: .default) { _, _ in + // await action(.copyTotp( + // totpKey: TOTPKeyModel(authenticatorKey: totp), + // requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword + // )) + // }) + // } + // if let uri = cipherView.login?.uris?.first?.uri, + // let url = URL(string: uri) { + // alertActions + // .append(AlertAction(title: Localizations.launch, style: .default) { _, _ in + // await action(.launch(url: url)) + // }) + // } + // case .identity: + // // No-op: no extra options beyond view and edit. + // break + // case .secureNote: + // if let notes = cipherView.notes { + // alertActions.append(AlertAction(title: Localizations.copyNotes, style: .default) { _, _ in + // await action(.copy( + // toast: Localizations.notes, + // value: notes, + // requiresMasterPasswordReprompt: false, + // logEvent: nil, + // cipherId: nil + // )) + // }) + // } + // case .sshKey: + // if let sshKey = cipherView.sshKey { + // alertActions.append(AlertAction(title: Localizations.copyPublicKey, style: .default) { _, _ in + // await action(.copy( + // toast: Localizations.publicKey, + // value: sshKey.publicKey, + // requiresMasterPasswordReprompt: false, + // logEvent: nil, + // cipherId: cipherView.id + // )) + // }) + // if cipherView.viewPassword { + // alertActions.append(AlertAction(title: Localizations.copyPrivateKey, style: .default) { _, _ in + // await action(.copy( + // toast: Localizations.privateKey, + // value: sshKey.privateKey, + // requiresMasterPasswordReprompt: cipherView.reprompt == .password && hasMasterPassword, + // logEvent: nil, + // cipherId: cipherView.id + // )) + // }) + // } + // alertActions.append(AlertAction(title: Localizations.copyFingerprint, style: .default) { _, _ in + // await action(.copy( + // toast: Localizations.fingerprint, + // value: sshKey.fingerprint, + // requiresMasterPasswordReprompt: false, + // logEvent: nil, + // cipherId: cipherView.id + // )) + // }) + // } + // } // Return the alert. return Alert( diff --git a/BitwardenShared/UI/Vault/Helpers/VaultItemMoreOptionsHelper.swift b/BitwardenShared/UI/Vault/Helpers/VaultItemMoreOptionsHelper.swift index ccea2d1cb..237160ba4 100644 --- a/BitwardenShared/UI/Vault/Helpers/VaultItemMoreOptionsHelper.swift +++ b/BitwardenShared/UI/Vault/Helpers/VaultItemMoreOptionsHelper.swift @@ -154,12 +154,13 @@ class DefaultVaultItemMoreOptionsHelper: VaultItemMoreOptionsHelper { await generateAndCopyTotpCode(totpKey: totpKey, handleDisplayToast: handleDisplayToast) } case let .edit(cipherView, requiresMasterPasswordReprompt): + guard let id = cipherView.id else { return } if requiresMasterPasswordReprompt { presentMasterPasswordRepromptAlert { - self.coordinator.navigate(to: .editItem(cipherView), context: self) + self.coordinator.navigate(to: .editItemFrom(id: id), context: self) } } else { - coordinator.navigate(to: .editItem(cipherView), context: self) + coordinator.navigate(to: .editItemFrom(id: id), context: self) } case let .launch(url): handleOpenURL(url.sanitized) diff --git a/BitwardenShared/UI/Vault/PreviewContent/BitwardenSdk+VaultFixtures.swift b/BitwardenShared/UI/Vault/PreviewContent/BitwardenSdk+VaultFixtures.swift index 8ff4e7444..774c8b199 100644 --- a/BitwardenShared/UI/Vault/PreviewContent/BitwardenSdk+VaultFixtures.swift +++ b/BitwardenShared/UI/Vault/PreviewContent/BitwardenSdk+VaultFixtures.swift @@ -243,6 +243,48 @@ extension CipherView { } } +extension CipherListView { + static func fixture( + attachments: UInt32 = 0, + collectionIds: [String] = [], + creationDate: DateTime = Date(year: 2023, month: 11, day: 5, hour: 9, minute: 41), + deletedDate: Date? = nil, + edit: Bool = true, + favorite: Bool = false, + folderId: String? = nil, + id: String? = "1", + key: String? = nil, + name: String = "Bitwarden", + organizationId: String? = nil, + reprompt: BitwardenSdk.CipherRepromptType = .none, + organizationUseTotp: Bool = false, + revisionDate: Date = Date(year: 2023, month: 11, day: 5, hour: 9, minute: 41), + type: BitwardenSdk.CipherListViewType = .login(LoginListView(hasFido2: false, totp: nil, uris: [])), + viewPassword: Bool = true, + subtitle: String = "subtitle" + ) -> CipherListView { + CipherListView( + id: id, + organizationId: organizationId, + folderId: folderId, + collectionIds: collectionIds, + key: key, + name: name, + subtitle: subtitle, + type: type, + favorite: favorite, + reprompt: reprompt, + organizationUseTotp: organizationUseTotp, + edit: edit, + viewPassword: viewPassword, + attachments: attachments, + creationDate: creationDate, + deletedDate: deletedDate, + revisionDate: revisionDate + ) + } +} + extension Collection { static func fixture( id: String? = "", diff --git a/BitwardenShared/UI/Vault/Vault/AutofillList/VaultAutofillListProcessor.swift b/BitwardenShared/UI/Vault/Vault/AutofillList/VaultAutofillListProcessor.swift index c3191e9b5..734064de3 100644 --- a/BitwardenShared/UI/Vault/Vault/AutofillList/VaultAutofillListProcessor.swift +++ b/BitwardenShared/UI/Vault/Vault/AutofillList/VaultAutofillListProcessor.swift @@ -85,15 +85,17 @@ class VaultAutofillListProcessor: StateProcessor< case let .vaultItemTapped(vaultItem): switch vaultItem.itemType { case let .cipher(cipher, fido2CredentialAutofillView): - if #available(iOSApplicationExtension 17.0, *), - let fido2AppExtensionDelegate, - fido2CredentialAutofillView != nil || fido2AppExtensionDelegate.isCreatingFido2Credential { - await onCipherForFido2CredentialPicked(cipher: cipher) - } else { - await autofillHelper.handleCipherForAutofill(cipherView: cipher) { [weak self] toastText in - self?.state.toast = Toast(title: toastText) - } - } + // if #available(iOSApplicationExtension 17.0, *), + // let fido2AppExtensionDelegate, + // fido2CredentialAutofillView != nil || fido2AppExtensionDelegate.isCreatingFido2Credential { + // await onCipherForFido2CredentialPicked(cipher: cipher) + // } else { + // await autofillHelper.handleCipherForAutofill(cipherView: cipher) { [weak self] toastText in + // self?.state.toast = Toast(title: toastText) + // } + // } + // + break case .group: return case .totp: @@ -217,52 +219,54 @@ class VaultAutofillListProcessor: StateProcessor< state.showNoResults = false return } - do { - let searchResult = try await services.vaultRepository.searchCipherAutofillPublisher( - availableFido2CredentialsPublisher: services - .fido2UserInterfaceHelper - .availableCredentialsForAuthenticationPublisher(), - mode: autofillListMode, - filterType: .allVaults, - rpID: fido2AppExtensionDelegate?.rpID, - searchText: searchText - ) - for try await sections in searchResult { - state.ciphersForSearch = sections - state.showNoResults = sections.isEmpty - } - } catch { - state.ciphersForSearch = [] - coordinator.showAlert(.defaultAlert(title: Localizations.anErrorHasOccurred)) - services.errorReporter.log(error: error) - } + // do { + // let searchResult = try await services.vaultRepository.searchCipherAutofillPublisher( + // availableFido2CredentialsPublisher: services + // .fido2UserInterfaceHelper + // .availableCredentialsForAuthenticationPublisher(), + // mode: autofillListMode, + // filterType: .allVaults, + // rpID: fido2AppExtensionDelegate?.rpID, + // searchText: searchText + // ) + // for try await sections in searchResult { + // state.ciphersForSearch = sections + // state.showNoResults = sections.isEmpty + // } + // } catch { + // state.ciphersForSearch = [] + // coordinator.showAlert(.defaultAlert(title: Localizations.anErrorHasOccurred)) + // services.errorReporter.log(error: error) + // } + // } /// Streams the list of autofill items. /// private func streamAutofillItems() async { - do { - var uri = appExtensionDelegate?.uri - if let fido2AppExtensionDelegate, - fido2AppExtensionDelegate.isCreatingFido2Credential, - let rpID = fido2AppExtensionDelegate.rpID { - uri = "https://\(rpID)" - } - - for try await sections in try await services.vaultRepository.ciphersAutofillPublisher( - availableFido2CredentialsPublisher: services - .fido2UserInterfaceHelper - .availableCredentialsForAuthenticationPublisher(), - mode: autofillListMode, - rpID: fido2AppExtensionDelegate?.rpID, - uri: uri - ) { - state.vaultListSections = sections - } - } catch { - coordinator.showAlert(.defaultAlert(title: Localizations.anErrorHasOccurred)) - services.errorReporter.log(error: error) - } + // do { + // var uri = appExtensionDelegate?.uri + // if let fido2AppExtensionDelegate, + // fido2AppExtensionDelegate.isCreatingFido2Credential, + // let rpID = fido2AppExtensionDelegate.rpID { + // uri = "https://\(rpID)" + // } + // + // for try await sections in try await services.vaultRepository.ciphersAutofillPublisher( + // availableFido2CredentialsPublisher: services + // .fido2UserInterfaceHelper + // .availableCredentialsForAuthenticationPublisher(), + // mode: autofillListMode, + // rpID: fido2AppExtensionDelegate?.rpID, + // uri: uri + // ) { + // state.vaultListSections = sections + // } + // } catch { + // coordinator.showAlert(.defaultAlert(title: Localizations.anErrorHasOccurred)) + // services.errorReporter.log(error: error) + // } + // } } @@ -505,26 +509,27 @@ extension VaultAutofillListProcessor { for cipher: CipherView, fido2CreationOptions: BitwardenSdk.CheckUserOptions ) async { - do { - let result = try await services.fido2UserInterfaceHelper.checkUser( - userVerificationPreference: fido2CreationOptions.requireVerification, - credential: cipher, - shouldThrowEnforcingRequiredVerification: true - ) - - services.fido2UserInterfaceHelper.pickedCredentialForCreation( - result: .success( - CheckUserAndPickCredentialForCreationResult( - cipher: CipherViewWrapper(cipher: cipher), - checkUserResult: CheckUserResult(userPresent: true, userVerified: result.userVerified) - ) - ) - ) - } catch UserVerificationError.cancelled { - return - } catch { - coordinator.showAlert(.networkResponseError(error)) - services.errorReporter.log(error: error) - } + // do { + // let result = try await services.fido2UserInterfaceHelper.checkUser( + // userVerificationPreference: fido2CreationOptions.requireVerification, + // credential: cipher, + // shouldThrowEnforcingRequiredVerification: true + // ) + // + // services.fido2UserInterfaceHelper.pickedCredentialForCreation( + // result: .success( + // CheckUserAndPickCredentialForCreationResult( + // cipher: CipherViewWrapper(cipher: cipher), + // checkUserResult: CheckUserResult(userPresent: true, userVerified: result.userVerified) + // ) + // ) + // ) + // } catch UserVerificationError.cancelled { + // return + // } catch { + // coordinator.showAlert(.networkResponseError(error)) + // services.errorReporter.log(error: error) + // } + // } } // swiftlint:disable:this file_length diff --git a/BitwardenShared/UI/Vault/Vault/AutofillList/VaultAutofillListView.swift b/BitwardenShared/UI/Vault/Vault/AutofillList/VaultAutofillListView.swift index 043d3fb4f..2418021eb 100644 --- a/BitwardenShared/UI/Vault/Vault/AutofillList/VaultAutofillListView.swift +++ b/BitwardenShared/UI/Vault/Vault/AutofillList/VaultAutofillListView.swift @@ -265,7 +265,6 @@ private struct VaultAutofillListSearchableView: View { .init( cipherView: .fixture( id: String(id), - login: .fixture(), name: "Bitwarden" ) )! @@ -294,7 +293,6 @@ private struct VaultAutofillListSearchableView: View { .init( cipherView: .fixture( id: String(id), - login: .fixture(), name: "Bitwarden" ) )! @@ -322,7 +320,6 @@ private struct VaultAutofillListSearchableView: View { items: [ .init(cipherView: .fixture( id: "1", - login: .fixture(username: "user@bitwarden.com"), name: "Apple" ), fido2CredentialAutofillView: .fixture( rpId: "apple.com", @@ -330,12 +327,6 @@ private struct VaultAutofillListSearchableView: View { ))!, .init(cipherView: .fixture( id: "4", - login: .fixture( - fido2Credentials: [ - .fixture(), - ], - username: "user@bitwarden.com" - ), name: "myApp.com" ), fido2CredentialAutofillView: .fixture( rpId: "myApp.com", @@ -343,12 +334,6 @@ private struct VaultAutofillListSearchableView: View { ))!, .init(cipherView: .fixture( id: "5", - login: .fixture( - fido2Credentials: [ - .fixture(), - ], - username: "user@test.com" - ), name: "Testing something really long to see how it looks" ), fido2CredentialAutofillView: .fixture( rpId: "someApp", @@ -362,12 +347,10 @@ private struct VaultAutofillListSearchableView: View { items: [ .init(cipherView: .fixture( id: "1", - login: .fixture(username: "user@bitwarden.com"), name: "Apple" ))!, .init(cipherView: .fixture( id: "2", - login: .fixture(username: "user@bitwarden.com"), name: "Bitwarden" ))!, .init(cipherView: .fixture( diff --git a/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupView.swift b/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupView.swift index 90daaf808..6f114a5ab 100644 --- a/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupView.swift +++ b/BitwardenShared/UI/Vault/Vault/VaultGroup/VaultGroupView.swift @@ -287,12 +287,10 @@ struct VaultGroupView: View { items: [ .init(cipherView: .fixture( id: "1", - login: .fixture(username: "email@example.com"), name: "Example" ))!, .init(cipherView: .fixture( id: "2", - login: .fixture(username: "email2@example.com"), name: "Example 2" ))!, ], diff --git a/BitwardenShared/UI/Vault/Vault/VaultItemSelection/VaultItemSelectionProcessor.swift b/BitwardenShared/UI/Vault/Vault/VaultItemSelection/VaultItemSelectionProcessor.swift index 927b8e3b3..1d69517fd 100644 --- a/BitwardenShared/UI/Vault/Vault/VaultItemSelection/VaultItemSelectionProcessor.swift +++ b/BitwardenShared/UI/Vault/Vault/VaultItemSelection/VaultItemSelectionProcessor.swift @@ -192,28 +192,29 @@ class VaultItemSelectionProcessor: StateProcessor< /// to add the OTP key to. /// private func showEditForNewOtpKey(vaultListItem: VaultListItem) async { - guard case let .cipher(cipherView, _) = vaultListItem.itemType, - cipherView.type == .login, - let login = cipherView.login else { - coordinator.showAlert(.defaultAlert(title: Localizations.anErrorHasOccurred)) - return - } - - do { - if try await services.authRepository.shouldPerformMasterPasswordReprompt(reprompt: cipherView.reprompt) { - guard try await userVerificationHelper.verifyMasterPassword() == .verified else { - return - } - } - - let updatedCipherView = cipherView.update(login: login.update(totp: state.otpAuthModel.uri)) - coordinator.navigate(to: .editItem(updatedCipherView), context: self) - } catch UserVerificationError.cancelled { - // No-op: don't log or alert for cancellation errors. - } catch { - coordinator.showAlert(.defaultAlert(title: Localizations.anErrorHasOccurred)) - services.errorReporter.log(error: error) - } + // guard case let .cipher(cipherView, _) = vaultListItem.itemType, + // cipherView.type == .login, + // let login = cipherView.login else { + // coordinator.showAlert(.defaultAlert(title: Localizations.anErrorHasOccurred)) + // return + // } + // + // do { + // if try await services.authRepository.shouldPerformMasterPasswordReprompt(reprompt: cipherView.reprompt) { + // guard try await userVerificationHelper.verifyMasterPassword() == .verified else { + // return + // } + // } + // + // let updatedCipherView = cipherView.update(login: login.update(totp: state.otpAuthModel.uri)) + // coordinator.navigate(to: .editItem(updatedCipherView), context: self) + // } catch UserVerificationError.cancelled { + // // No-op: don't log or alert for cancellation errors. + // } catch { + // coordinator.showAlert(.defaultAlert(title: Localizations.anErrorHasOccurred)) + // services.errorReporter.log(error: error) + // } + // } /// Streams the list of vault items. diff --git a/BitwardenShared/UI/Vault/Vault/VaultItemSelection/VaultItemSelectionView.swift b/BitwardenShared/UI/Vault/Vault/VaultItemSelection/VaultItemSelectionView.swift index 36cd70691..813e8b293 100644 --- a/BitwardenShared/UI/Vault/Vault/VaultItemSelection/VaultItemSelectionView.swift +++ b/BitwardenShared/UI/Vault/Vault/VaultItemSelection/VaultItemSelectionView.swift @@ -277,15 +277,13 @@ private struct VaultItemSelectionSearchableView: View { #Preview("Matching Items") { NavigationView { - let ciphers: [CipherView] = [ + let ciphers: [CipherListView] = [ .fixture( id: "1", - login: .fixture(username: "user@bitwarden.com"), name: "Apple" ), .fixture( id: "2", - login: .fixture(username: "user@bitwarden.com"), name: "Bitwarden" ), .fixture( @@ -294,12 +292,10 @@ private struct VaultItemSelectionSearchableView: View { ), .fixture( id: "4", - login: .fixture(username: "user@bitwarden.com"), name: "Apple" ), .fixture( id: "5", - login: .fixture(username: "user@bitwarden.com"), name: "Bitwarden" ), .fixture( @@ -308,12 +304,10 @@ private struct VaultItemSelectionSearchableView: View { ), .fixture( id: "7", - login: .fixture(username: "user@bitwarden.com"), name: "Apple" ), .fixture( id: "8", - login: .fixture(username: "user@bitwarden.com"), name: "Bitwarden" ), .fixture( diff --git a/BitwardenShared/UI/Vault/Vault/VaultList/VaultListItem.swift b/BitwardenShared/UI/Vault/Vault/VaultList/VaultListItem.swift index fd9e3b105..17354dc7b 100644 --- a/BitwardenShared/UI/Vault/Vault/VaultList/VaultListItem.swift +++ b/BitwardenShared/UI/Vault/Vault/VaultList/VaultListItem.swift @@ -15,7 +15,7 @@ public struct VaultListItem: Equatable, Identifiable, Sendable, VaultItemWithDec /// - Fido2CredentialAutofillView: Additional data from the main Fido2 credential /// of the `CipherView` to be displayed when needed (Optional). /// - case cipher(CipherView, Fido2CredentialAutofillView? = nil) + case cipher(CipherListView, Fido2CredentialAutofillView? = nil) /// The wrapped item is a group of items. case group(VaultListGroup, Int) @@ -52,7 +52,7 @@ extension VaultListItem { /// /// - Parameter cipherView: The `CipherView` used to initialize the `VaultListItem`. /// - init?(cipherView: CipherView) { + init?(cipherView: CipherListView) { guard let id = cipherView.id else { return nil } self.init(id: id, itemType: .cipher(cipherView)) } @@ -61,8 +61,8 @@ extension VaultListItem { /// - Parameters: /// - cipherView: The `CipherView` used to initialize the `VaultListItem`. /// - fido2CredentialAutofillView: The main Fido2 credential of the `cipherView` prepared for UI display. - init?(cipherView: CipherView, fido2CredentialAutofillView: Fido2CredentialAutofillView) { - guard let id = cipherView.id, cipherView.type == .login else { return nil } + init?(cipherView: CipherListView, fido2CredentialAutofillView: Fido2CredentialAutofillView) { + guard let id = cipherView.id, case .login = cipherView.type else { return nil } self.init(id: id, itemType: .cipher(cipherView, fido2CredentialAutofillView)) } } @@ -148,7 +148,8 @@ extension VaultListItem { var loginView: BitwardenSdk.LoginView? { switch itemType { case let .cipher(cipherView, _): - cipherView.login + // cipherView.login + nil case .group: nil case let .totp(_, totpModel): diff --git a/BitwardenShared/UI/Vault/Vault/VaultList/VaultListProcessor.swift b/BitwardenShared/UI/Vault/Vault/VaultList/VaultListProcessor.swift index 15ed03809..d53f43eb2 100644 --- a/BitwardenShared/UI/Vault/Vault/VaultList/VaultListProcessor.swift +++ b/BitwardenShared/UI/Vault/Vault/VaultList/VaultListProcessor.swift @@ -376,7 +376,7 @@ enum MoreOptionsAction: Equatable { case copyTotp(totpKey: TOTPKeyModel, requiresMasterPasswordReprompt: Bool) /// Navigate to the view to edit the `cipherView`. - case edit(cipherView: CipherView, requiresMasterPasswordReprompt: Bool) + case edit(cipherView: CipherListView, requiresMasterPasswordReprompt: Bool) /// Launch the `url` in the device's browser. case launch(url: URL) diff --git a/BitwardenShared/UI/Vault/Vault/VaultList/VaultListView.swift b/BitwardenShared/UI/Vault/Vault/VaultList/VaultListView.swift index 29c4fec57..1b6fb10bc 100644 --- a/BitwardenShared/UI/Vault/Vault/VaultList/VaultListView.swift +++ b/BitwardenShared/UI/Vault/Vault/VaultList/VaultListView.swift @@ -460,7 +460,6 @@ struct VaultListView_Previews: PreviewProvider { items: [ .init(cipherView: .fixture( id: UUID().uuidString, - login: .fixture(username: "email@example.com"), name: "Example" ))!, .init(cipherView: .fixture( @@ -537,43 +536,36 @@ struct VaultListView_Previews: PreviewProvider { items: [ .init(cipherView: .fixture( id: UUID().uuidString, - login: .fixture(username: "email@example.com"), name: "Example", organizationId: "1" ))!, .init(cipherView: .fixture( id: UUID().uuidString, - login: .fixture(username: "email@example.com"), name: "Example", organizationId: "1" ))!, .init(cipherView: .fixture( id: UUID().uuidString, - login: .fixture(username: "email@example.com"), name: "Example", organizationId: "1" ))!, .init(cipherView: .fixture( id: UUID().uuidString, - login: .fixture(username: "email@example.com"), name: "Example", organizationId: "1" ))!, .init(cipherView: .fixture( id: UUID().uuidString, - login: .fixture(username: "email@example.com"), name: "Example", organizationId: "1" ))!, .init(cipherView: .fixture( id: UUID().uuidString, - login: .fixture(username: "email@example.com"), name: "Example", organizationId: "1" ))!, .init(cipherView: .fixture( id: UUID().uuidString, - login: .fixture(username: "email@example.com"), name: "Example", organizationId: "1" ))!, @@ -619,7 +611,6 @@ struct VaultListView_Previews: PreviewProvider { searchResults: [ .init(cipherView: .fixture( id: UUID().uuidString, - login: .fixture(username: "email@example.com"), name: "Example" ))!, ], @@ -640,17 +631,14 @@ struct VaultListView_Previews: PreviewProvider { searchResults: [ .init(cipherView: .fixture( id: UUID().uuidString, - login: .fixture(username: "email@example.com"), name: "Example" ))!, .init(cipherView: .fixture( id: UUID().uuidString, - login: .fixture(username: "email2@example.com"), name: "Example 2" ))!, .init(cipherView: .fixture( id: UUID().uuidString, - login: .fixture(username: "email3@example.com"), name: "Example 3" ))!, ], diff --git a/BitwardenShared/UI/Vault/Views/VaultListItemRow/VaultListItemRowView.swift b/BitwardenShared/UI/Vault/Views/VaultListItemRow/VaultListItemRowView.swift index 6a3088f30..89c2f3e9b 100644 --- a/BitwardenShared/UI/Vault/Views/VaultListItemRow/VaultListItemRowView.swift +++ b/BitwardenShared/UI/Vault/Views/VaultListItemRow/VaultListItemRowView.swift @@ -45,7 +45,7 @@ struct VaultListItemRowView: View { .accessibilityIdentifier("CipherInCollectionIcon") } - if cipherItem.attachments?.isEmpty == false { + if cipherItem.attachments > 0 { Asset.Images.paperclip16.swiftUIImage .imageStyle(.accessoryIcon( color: Asset.Colors.textSecondary.swiftUIColor, diff --git a/project.yml b/project.yml index 4008c7037..76cff1ec9 100644 --- a/project.yml +++ b/project.yml @@ -24,7 +24,7 @@ include: packages: BitwardenSdk: url: https://github.com/bitwarden/sdk-swift - revision: a0d9312fc1fc63f146064dae88b0a39cbaf7e0c5 + revision: 90e2ea1e1bad546612f951759e2741e1c7dac663 branch: unstable Firebase: url: https://github.com/firebase/firebase-ios-sdk