From 97b7939245348e3aad75caaf86eb17874faf580d Mon Sep 17 00:00:00 2001 From: AlexisG Date: Thu, 18 Jul 2024 15:09:18 +0200 Subject: [PATCH 01/14] fix: Fix some logs --- libs/common/src/platform/sync/default-sync.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/common/src/platform/sync/default-sync.service.ts b/libs/common/src/platform/sync/default-sync.service.ts index 6896adca5cd3..2f3a9f77d3e4 100644 --- a/libs/common/src/platform/sync/default-sync.service.ts +++ b/libs/common/src/platform/sync/default-sync.service.ts @@ -170,13 +170,13 @@ export class DefaultSyncService extends CoreSyncService { if (papersPromise.status === "fulfilled") { // eslint-disable-next-line no-console - console.log(`${papersPromise.value.length} contacts ciphers will be added`); + console.log(`${papersPromise.value.length} papers ciphers will be added`); cozyCiphers = cozyCiphers.concat(papersPromise.value); } if (contactsPromise.status === "fulfilled") { // eslint-disable-next-line no-console - console.log(`${contactsPromise.value.length} papers ciphers will be added`); + console.log(`${contactsPromise.value.length} contacts ciphers will be added`); cozyCiphers = cozyCiphers.concat(contactsPromise.value); } From f3bd00a99753c222216b755510ba5c1e41bcf7f8 Mon Sep 17 00:00:00 2001 From: AlexisG Date: Fri, 19 Jul 2024 17:25:52 +0200 Subject: [PATCH 02/14] feat: Add CozyClientService to OverlayBackground We'll need it to recover the cozy contact. --- .../browser/src/autofill/background/overlay.background.spec.ts | 3 +++ apps/browser/src/autofill/background/overlay.background.ts | 2 ++ apps/browser/src/background/main.background.ts | 1 + 3 files changed, 6 insertions(+) diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts index c042af77190a..68b8ff79af01 100644 --- a/apps/browser/src/autofill/background/overlay.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay.background.spec.ts @@ -35,6 +35,7 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { BrowserApi } from "../../platform/browser/browser-api"; import { BrowserPlatformUtilsService } from "../../platform/services/platform-utils/browser-platform-utils.service"; +import { CozyClientService } from "../../popup/services/cozyClient.service"; import { AutofillOverlayElement, AutofillOverlayPort, @@ -87,6 +88,7 @@ describe("OverlayBackground", () => { let platformUtilsService: MockProxy; let selectedThemeMock$: BehaviorSubject; let themeStateService: MockProxy; + let cozyClientService: MockProxy; let overlayBackground: OverlayBackground; let portKeyForTabSpy: Record; let pageDetailsForTabSpy: PageDetailsForTab; @@ -165,6 +167,7 @@ describe("OverlayBackground", () => { i18nService, platformUtilsService, themeStateService, + cozyClientService ); portKeyForTabSpy = overlayBackground["portKeyForTab"]; pageDetailsForTabSpy = overlayBackground["pageDetailsForTab"]; diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index e492396603fd..ea65463bf506 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -65,6 +65,7 @@ import { /* start Cozy imports */ /* eslint-disable */ import { nameToColor } from "cozy-ui/transpiled/react/Avatar/helpers"; +import { CozyClientService } from "../../popup/services/cozyClient.service"; /* eslint-enable */ /* end Cozy imports */ @@ -167,6 +168,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private themeStateService: ThemeStateService, + private cozyClientService: CozyClientService, ) { this.initOverlayEventObservables(); } diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index f6e8d4d90d8f..5146392edd52 100755 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -1234,6 +1234,7 @@ export default class MainBackground { this.i18nService, this.platformUtilsService, themeStateService, + this.cozyClientService ); } From c02274426ded6b32ef7c462d583a863fb4f677c9 Mon Sep 17 00:00:00 2001 From: AlexisG Date: Fri, 19 Jul 2024 17:26:12 +0200 Subject: [PATCH 03/14] feat: Add Back icon --- apps/browser/src/autofill/utils/svg-icons.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/autofill/utils/svg-icons.ts b/apps/browser/src/autofill/utils/svg-icons.ts index 2419f5d73fc3..0c15f0ed1212 100644 --- a/apps/browser/src/autofill/utils/svg-icons.ts +++ b/apps/browser/src/autofill/utils/svg-icons.ts @@ -33,4 +33,8 @@ const viewCipherIcon = ''; //*/ -export { logoIcon, logoLockedIcon, globeIcon, lockIcon, plusIcon, viewCipherIcon }; +// Cozy customization, Add back icon +const backIcon = '' +// Cozy customization end + +export { logoIcon, logoLockedIcon, globeIcon, lockIcon, plusIcon, viewCipherIcon, backIcon }; From 50d8ece2bffcd248a1ac2cf3fae253441462fec4 Mon Sep 17 00:00:00 2001 From: AlexisG Date: Sun, 21 Jul 2024 10:31:13 +0200 Subject: [PATCH 04/14] fix: Style of contact list --- .../pages/list/autofill-inline-menu-list.ts | 5 ++--- .../overlay/inline-menu/pages/list/list.scss | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts index 90317ff88477..b6b505d093a8 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts @@ -577,10 +577,9 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { // Cozy customization; add contact initials to autofill if (cipher.contact) { + cipherIcon.classList.remove("cipher-icon"); + cipherIcon.classList.add("contact-initials"); cipherIcon.style.backgroundColor = cipher.contact.initialsColor; - cipherIcon.style.padding = "8px"; - cipherIcon.style.borderRadius = "50%"; - cipherIcon.style.color = "white"; cipherIcon.textContent = cipher.contact.initials; return cipherIcon; } diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss b/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss index 7902d059475e..bda2464de393 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss @@ -58,6 +58,23 @@ body { } } +// Cozy customization; replace .cipher-icon +.contact-initials { + display: flex; + align-content: center; + align-items: center; + justify-content: center; + flex-shrink: 0; + width: 3.2rem; + height: 3.2rem; + margin: 0 1.6rem 0 0.7rem; + line-height: 0; + background-size: 2.6rem; + border-radius: 50%; + color: white; +} +// Cozy customization end + .inline-menu-list-button { display: flex; align-content: center; @@ -169,7 +186,7 @@ body { position: absolute; bottom: -0.1rem; left: 0; - width: calc(3.2rem + 2rem); + width: 6.4rem; height: 0.1rem; @include themify($themes) { background-color: themed("backgroundColor"); From adc5d505bf5af637fbaecc0965ea426859752744 Mon Sep 17 00:00:00 2001 From: AlexisG Date: Fri, 26 Jul 2024 12:42:48 +0200 Subject: [PATCH 05/14] feat: Add some types about ambiguous contact field --- apps/browser/src/autofill/types/index.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/apps/browser/src/autofill/types/index.ts b/apps/browser/src/autofill/types/index.ts index a14ef1330cc9..a43c1612a23b 100644 --- a/apps/browser/src/autofill/types/index.ts +++ b/apps/browser/src/autofill/types/index.ts @@ -1,3 +1,5 @@ +import { ContactAddress, ContactEmail, ContactPhone } from "cozy-client/types/types"; + import { Region } from "@bitwarden/common/platform/abstractions/environment.service"; import { VaultTimeoutAction } from "@bitwarden/common/src/enums/vault-timeout-action.enum"; import { VaultTimeout } from "@bitwarden/common/types/vault-timeout.type"; @@ -56,3 +58,15 @@ export type FormFieldElement = FillableFormFieldElement | HTMLSpanElement; export type FormElementWithAttribute = FormFieldElement & Record; export type AutofillCipherTypeId = CipherType.Login | CipherType.Card | CipherType.Identity; + +// Cozy customization +export type AmibuousContactFieldName = "phone" | "email" | "address" + +export type AmbiguousContactFields = { + phone?: ContactPhone[]; + email?: ContactEmail[]; + address?: ContactAddress[]; +} + +export type AmbiguousContactFieldValue = ContactPhone[] | ContactEmail[] | ContactAddress[]; +// Cozy customization end From e16e176cadce961976fbe29d523d76e22ad5b0fc Mon Sep 17 00:00:00 2001 From: AlexisG Date: Fri, 26 Jul 2024 12:43:45 +0200 Subject: [PATCH 06/14] feat: Add helper for get ambiguous fields in the contact --- apps/browser/src/autofill/utils/index.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/autofill/utils/index.ts b/apps/browser/src/autofill/utils/index.ts index a040fa501228..a0278e8c8c0a 100644 --- a/apps/browser/src/autofill/utils/index.ts +++ b/apps/browser/src/autofill/utils/index.ts @@ -1,5 +1,7 @@ +import { IOCozyContact } from "cozy-client/types/types"; + import { AutofillPort } from "../enums/autofill-port.enum"; -import { FillableFormFieldElement, FormElementWithAttribute, FormFieldElement } from "../types"; +import { AmbiguousContactFields, AmibuousContactFieldName, FillableFormFieldElement, FormElementWithAttribute, FormFieldElement } from "../types"; /** * Generates a random string of characters. @@ -352,3 +354,15 @@ export function throttle(callback: () => void, limit: number) { } }; } + +/** + * @param ambiguousFields + * @param contact + */ +export const getAmbiguousFieldsContact = ( + ambiguousFields: AmibuousContactFieldName[], + contact: IOCozyContact, +): AmbiguousContactFields => { + return ambiguousFields + .reduce((acc, field) => (contact[field].length > 0 ? {...acc, [field]: contact[field]} : acc), {}); +}; From 37a593afbc4bee0d595a2614c5343613abfa24e7 Mon Sep 17 00:00:00 2001 From: AlexisG Date: Fri, 26 Jul 2024 12:51:32 +0200 Subject: [PATCH 07/14] feat: Add object for mapping field name between bitwarden and cozy --- apps/browser/src/autofill/utils/index.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/browser/src/autofill/utils/index.ts b/apps/browser/src/autofill/utils/index.ts index a0278e8c8c0a..370236528fe9 100644 --- a/apps/browser/src/autofill/utils/index.ts +++ b/apps/browser/src/autofill/utils/index.ts @@ -1,5 +1,9 @@ import { IOCozyContact } from "cozy-client/types/types"; +// Cozy customization +import { AutofillFieldQualifierType } from "src/autofill/enums/autofill-field.enums"; +// Cozy customization end + import { AutofillPort } from "../enums/autofill-port.enum"; import { AmbiguousContactFields, AmibuousContactFieldName, FillableFormFieldElement, FormElementWithAttribute, FormFieldElement } from "../types"; @@ -355,6 +359,7 @@ export function throttle(callback: () => void, limit: number) { }; } +// Cozy customization /** * @param ambiguousFields * @param contact @@ -366,3 +371,11 @@ export const getAmbiguousFieldsContact = ( return ambiguousFields .reduce((acc, field) => (contact[field].length > 0 ? {...acc, [field]: contact[field]} : acc), {}); }; +export const bitwardenToCozy: Partial> = { + identityPhone: "phone", + identityEmail: "email", + identityAddress1: "address", + identityState: "address", +}; +export const ambiguousContactFieldNames: AmibuousContactFieldName[] = ["phone", "email", "address"]; +// Cozy customization end From 8ad3b517c3875e44059fed3a3ff7816ebec8b453 Mon Sep 17 00:00:00 2001 From: AlexisG Date: Fri, 26 Jul 2024 13:24:36 +0200 Subject: [PATCH 08/14] feat: Add handleContactClick message handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds a new behavior if the cipher is a contact. It intercepts the autofill if the contact has one or more values in its “ambiguous” fields (phone/address/email). --- .../abstractions/overlay.background.ts | 6 ++ .../autofill/background/overlay.background.ts | 75 ++++++++++++++++++- .../pages/list/autofill-inline-menu-list.ts | 30 ++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index 283f92c611a1..b8d4d38567cc 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -139,6 +139,9 @@ export type OverlayPortMessage = { direction?: string; inlineMenuCipherId?: string; addNewCipherType?: CipherType; + // Cozy customization; + fieldQualifier?: AutofillFieldQualifierType; + // Cozy customization end }; export type InlineMenuCipherData = { @@ -244,6 +247,9 @@ export type InlineMenuListPortMessageHandlers = { autofillInlineMenuBlurred: () => void; unlockVault: ({ port }: PortConnectionParam) => void; fillAutofillInlineMenuCipher: ({ message, port }: PortOnMessageHandlerParams) => void; + // Cozy customization; fill ambiguous contact + handleContactClick: ({ message, port }: PortOnMessageHandlerParams) => void; + // Cozy customization end addNewVaultItem: ({ message, port }: PortOnMessageHandlerParams) => void; viewSelectedCipher: ({ message, port }: PortOnMessageHandlerParams) => void; redirectAutofillInlineMenuFocusOut: ({ message, port }: PortOnMessageHandlerParams) => void; diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index ea65463bf506..fb9f66d2c058 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -37,7 +37,7 @@ import { MAX_SUB_FRAME_DEPTH, } from "../enums/autofill-overlay.enum"; import { AutofillService } from "../services/abstractions/autofill.service"; -import { generateRandomChars } from "../utils"; +import { ambiguousContactFieldNames, bitwardenToCozy, generateRandomChars, getAmbiguousFieldsContact } from "../utils"; import { LockedVaultPendingNotificationsData } from "./abstractions/notification.background"; import { @@ -64,6 +64,10 @@ import { /* start Cozy imports */ /* eslint-disable */ +import { Q } from "cozy-client"; +import { IOCozyContact } from "cozy-client/types/types"; +// @ts-ignore +import { CONTACTS_DOCTYPE } from "cozy-client/dist/models/contact"; import { nameToColor } from "cozy-ui/transpiled/react/Avatar/helpers"; import { CozyClientService } from "../../popup/services/cozyClient.service"; /* eslint-enable */ @@ -150,6 +154,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { autofillInlineMenuBlurred: () => this.checkInlineMenuButtonFocused(), unlockVault: ({ port }) => this.unlockVault(port), fillAutofillInlineMenuCipher: ({ message, port }) => this.fillInlineMenuCipher(message, port), + handleContactClick: ({ message, port }) => this.handleContactClick(message, port), addNewVaultItem: ({ message, port }) => this.getNewVaultItemDetails(message, port), viewSelectedCipher: ({ message, port }) => this.viewSelectedCipher(message, port), redirectAutofillInlineMenuFocusOut: ({ message, port }) => @@ -807,6 +812,74 @@ export class OverlayBackground implements OverlayBackgroundInterface { this.inlineMenuCiphers = new Map([[inlineMenuCipherId, cipher], ...this.inlineMenuCiphers]); } + /** + * @param inlineMenuCipherId - Cipher ID corresponding to the inlineMenuCiphers map. Does not correspond to the actual cipher's ID. + * @param sender - The sender of the port message + */ + private async handleContactClick(message: OverlayPortMessage, port: chrome.runtime.Port) { + const { inlineMenuCipherId } = message; + const client = await this.cozyClientService.getClientInstance(); + const cipher = this.inlineMenuCiphers.get(inlineMenuCipherId); + + // FIXME: Temporary way to query data. We want to avoid online request. + const { data: contact } = (await client.fetchQueryAndGetFromState({ + definition: Q(CONTACTS_DOCTYPE).getById(cipher.id), + options: { + as: `${CONTACTS_DOCTYPE}/${cipher.id}`, + singleDocData: true, + }, + })) as { data: IOCozyContact }; + + const ambiguousContactFields = getAmbiguousFieldsContact(ambiguousContactFieldNames, contact); + + const isFocusedFieldAmbigous = ambiguousContactFieldNames.includes( + bitwardenToCozy[this.focusedFieldData?.fieldQualifier], + ); + const hasMultipleAmbiguousValueInSameField = Object.values(ambiguousContactFields).some( + (item) => item.length > 1, + ); + const currentAmbiguousFieldValue = + ambiguousContactFields[bitwardenToCozy[this.focusedFieldData?.fieldQualifier]]; + + // On an ambiguous form field, the associated contact values are kept. + const ambiguousFormFieldsOfFocusedField = Object.fromEntries( + Object.entries(ambiguousContactFields).filter( + ([fieldName]) => fieldName === bitwardenToCozy[this.focusedFieldData?.fieldQualifier], + ), + ); + // On an unambiguous form field, we keep only the multiple values of an ambiguous contact field. + const unambiguousFormFieldsOfFocusedField = Object.fromEntries( + Object.entries(ambiguousContactFields).filter( + ([, fieldValue]) => Array.isArray(fieldValue) && fieldValue.length > 1, + ), + ); + + /* + On a form field other than ambiguous(phone/address/email): + - If the contact has one or less ambiguous value: autofill everything. + - If the contact has more than one ambiguous values: display a menu to choose which one. + On the ambiguous(phone/address/email) form field: + - If contact has one or less ambiguous value: display list with phone/address/email. + - If the contact has more than one ambiguous values: display list with the phone/address/email. + */ + if ( + (!isFocusedFieldAmbigous && hasMultipleAmbiguousValueInSameField) || + isFocusedFieldAmbigous && currentAmbiguousFieldValue?.length > 0 // TODO Part_2 Remove "currentAmbiguousFieldValue?.length > 0" condition + ) { + this.inlineMenuListPort?.postMessage({ + command: "ambiguousFieldList", + inlineMenuCipherId, + contactName: contact.displayName, + ambiguousFields: isFocusedFieldAmbigous + ? ambiguousFormFieldsOfFocusedField + : unambiguousFormFieldsOfFocusedField, + isFocusedFieldAmbigous, + }); + } else { + this.fillInlineMenuCipher(message, port); + } + } + /** * Checks if the inline menu is focused. Will check the inline menu list * if it is open, otherwise it will check the inline menu button. diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts index b6b505d093a8..3632c867af50 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts @@ -13,6 +13,12 @@ import { } from "../../abstractions/autofill-inline-menu-list"; import { AutofillInlineMenuPageElement } from "../shared/autofill-inline-menu-page-element"; +/* start Cozy imports */ +/* eslint-disable */ +import { AutofillFieldQualifierType } from "src/autofill/enums/autofill-field.enums"; +/* eslint-enable */ +/* end Cozy imports */ + export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { private inlineMenuListContainer: HTMLDivElement; private resizeObserver: ResizeObserver; @@ -23,6 +29,11 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { private cipherListScrollDebounceTimeout: number | NodeJS.Timeout; private currentCipherIndex = 0; private filledByCipherType: CipherType; + // Cozy customization + private lastFilledCipherId: string; + private fieldQualifier: AutofillFieldQualifierType; + private fieldValue: string; + // Cozy customization end private showInlineMenuAccountCreation: boolean; private readonly showCiphersPerPage = 6; private newItemButtonElement: HTMLButtonElement; @@ -76,6 +87,11 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { translations, portKey, ); + // Cozy customization + this.lastFilledCipherId = lastFilledCipherId; + this.fieldQualifier = fieldQualifier; + this.fieldValue = fieldValue; + // Cozy customization end this.filledByCipherType = filledByCipherType; @@ -431,6 +447,20 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { * @param cipher - The cipher to fill. */ private handleFillCipherClickEvent = (cipher: InlineMenuCipherData) => { + if (cipher.contact) { + return this.useEventHandlersMemo( + () => + this.postMessageToParent({ + command: "handleContactClick", + inlineMenuCipherId: cipher.id, + lastFilledCipherId: this.lastFilledCipherId, + fieldQualifier: this.fieldQualifier, + fieldValue: this.fieldValue, + }), + `${cipher.id}-fill-cipher-button-click-handler`, + ); + } + return this.useEventHandlersMemo( () => this.postMessageToParent({ From bb7ea8c0ff90a375d922911c7c1196f7f156119a Mon Sep 17 00:00:00 2001 From: AlexisG Date: Fri, 26 Jul 2024 14:02:00 +0200 Subject: [PATCH 09/14] feat: Add ambiguousFieldList handler in autofill menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds the new menu following the `ambiguousFieldList` message in the previous commit. When a contact has several values in the same “ambiguous” field (phone/address/email), these fields are often associated with a “work” or “home” label. In this case, on click we autofill with all the corresponding values. --- apps/browser/src/_locales/en/messages.json | 9 + apps/browser/src/_locales/fr/messages.json | 9 + .../abstractions/overlay.background.ts | 6 + .../autofill/background/overlay.background.ts | 36 ++- .../abstractions/autofill-inline-menu-list.ts | 17 + .../pages/list/autofill-inline-menu-list.ts | 303 +++++++++++++++++- .../overlay/inline-menu/pages/list/list.scss | 50 +++ apps/browser/src/autofill/types/index.ts | 4 +- apps/browser/src/autofill/utils/index.ts | 39 ++- 9 files changed, 462 insertions(+), 11 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index e06624fe6c82..d7754b4ef0e0 100755 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -4011,5 +4011,14 @@ }, "cipherContactMe": { "message": "me" + }, + "empty_ambiguous_email": { + "message": "No email found" + }, + "empty_ambiguous_phone": { + "message": "No phone found" + }, + "empty_ambiguous_address": { + "message": "No address found" } } diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 39e1480205b1..86cd6df5fa7e 100755 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -3973,5 +3973,14 @@ }, "cipherContactMe": { "message": "moi" + }, + "empty_ambiguous_email": { + "message": "Aucun email trouvé" + }, + "empty_ambiguous_phone": { + "message": "Aucun téléphone trouvé" + }, + "empty_ambiguous_address": { + "message": "Aucune adresse trouvée" } } diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index b8d4d38567cc..2e4b1affc3ae 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -9,6 +9,7 @@ import { LockedVaultPendingNotificationsData } from "./notification.background"; /* start Cozy imports */ /* eslint-disable */ import { AutofillFieldQualifierType } from "src/autofill/enums/autofill-field.enums"; +import { AmbiguousContactFieldValue } from "src/autofill/types"; /* eslint-enable */ /* end Cozy imports */ @@ -141,6 +142,7 @@ export type OverlayPortMessage = { addNewCipherType?: CipherType; // Cozy customization; fieldQualifier?: AutofillFieldQualifierType; + ambiguousValue?: AmbiguousContactFieldValue[0]; // Cozy customization end }; @@ -249,6 +251,10 @@ export type InlineMenuListPortMessageHandlers = { fillAutofillInlineMenuCipher: ({ message, port }: PortOnMessageHandlerParams) => void; // Cozy customization; fill ambiguous contact handleContactClick: ({ message, port }: PortOnMessageHandlerParams) => void; + fillAutofillInlineMenuCipherWithAmbiguousField: ({ + message, + port, + }: PortOnMessageHandlerParams) => void; // Cozy customization end addNewVaultItem: ({ message, port }: PortOnMessageHandlerParams) => void; viewSelectedCipher: ({ message, port }: PortOnMessageHandlerParams) => void; diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index fb9f66d2c058..c9827080e9ad 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -37,7 +37,12 @@ import { MAX_SUB_FRAME_DEPTH, } from "../enums/autofill-overlay.enum"; import { AutofillService } from "../services/abstractions/autofill.service"; -import { ambiguousContactFieldNames, bitwardenToCozy, generateRandomChars, getAmbiguousFieldsContact } from "../utils"; +import { + ambiguousContactFieldNames, + bitwardenToCozy, + generateRandomChars, + getAmbiguousFieldsContact, +} from "../utils"; import { LockedVaultPendingNotificationsData } from "./abstractions/notification.background"; import { @@ -70,6 +75,7 @@ import { IOCozyContact } from "cozy-client/types/types"; import { CONTACTS_DOCTYPE } from "cozy-client/dist/models/contact"; import { nameToColor } from "cozy-ui/transpiled/react/Avatar/helpers"; import { CozyClientService } from "../../popup/services/cozyClient.service"; +import { AmbiguousContactFieldValue } from "src/autofill/types"; /* eslint-enable */ /* end Cozy imports */ @@ -154,7 +160,11 @@ export class OverlayBackground implements OverlayBackgroundInterface { autofillInlineMenuBlurred: () => this.checkInlineMenuButtonFocused(), unlockVault: ({ port }) => this.unlockVault(port), fillAutofillInlineMenuCipher: ({ message, port }) => this.fillInlineMenuCipher(message, port), + // Cozy customization handleContactClick: ({ message, port }) => this.handleContactClick(message, port), + fillAutofillInlineMenuCipherWithAmbiguousField: ({ message, port }) => + this.fillAutofillInlineMenuCipherWithAmbiguousField(message, port), + // Cozy customization end addNewVaultItem: ({ message, port }) => this.getNewVaultItemDetails(message, port), viewSelectedCipher: ({ message, port }) => this.viewSelectedCipher(message, port), redirectAutofillInlineMenuFocusOut: ({ message, port }) => @@ -782,6 +792,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { private async fillInlineMenuCipher( { inlineMenuCipherId }: OverlayPortMessage, { sender }: chrome.runtime.Port, + ambiguousValue?: AmbiguousContactFieldValue[0], ) { const pageDetails = this.pageDetailsForTab[sender.tab.id]; if (!inlineMenuCipherId || !pageDetails?.size) { @@ -799,6 +810,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { pageDetails: Array.from(pageDetails.values()), fillNewPassword: true, allowTotpAutofill: true, + ...(ambiguousValue ? { cozyProfile: ambiguousValue } : {}), }); // Cozy customization @@ -812,10 +824,18 @@ export class OverlayBackground implements OverlayBackgroundInterface { this.inlineMenuCiphers = new Map([[inlineMenuCipherId, cipher], ...this.inlineMenuCiphers]); } + private async fillAutofillInlineMenuCipherWithAmbiguousField( + message: OverlayPortMessage, + port: chrome.runtime.Port, + ) { + const ambiguousValue = message.ambiguousValue; + this.fillInlineMenuCipher(message, port, ambiguousValue); + } + /** - * @param inlineMenuCipherId - Cipher ID corresponding to the inlineMenuCiphers map. Does not correspond to the actual cipher's ID. - * @param sender - The sender of the port message - */ + * @param inlineMenuCipherId - Cipher ID corresponding to the inlineMenuCiphers map. Does not correspond to the actual cipher's ID. + * @param sender - The sender of the port message + */ private async handleContactClick(message: OverlayPortMessage, port: chrome.runtime.Port) { const { inlineMenuCipherId } = message; const client = await this.cozyClientService.getClientInstance(); @@ -864,7 +884,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { */ if ( (!isFocusedFieldAmbigous && hasMultipleAmbiguousValueInSameField) || - isFocusedFieldAmbigous && currentAmbiguousFieldValue?.length > 0 // TODO Part_2 Remove "currentAmbiguousFieldValue?.length > 0" condition + (isFocusedFieldAmbigous && currentAmbiguousFieldValue?.length > 0) // TODO Part_2 Remove "currentAmbiguousFieldValue?.length > 0" condition ) { this.inlineMenuListPort?.postMessage({ command: "ambiguousFieldList", @@ -1452,6 +1472,12 @@ export class OverlayBackground implements OverlayBackgroundInterface { addNewIdentityItem: this.i18nService.translate("addNewIdentityItem"), cardNumberEndsWith: this.i18nService.translate("cardNumberEndsWith"), cipherContactMe: this.i18nService.translate("cipherContactMe"), + home: this.i18nService.translate("home"), + work: this.i18nService.translate("work"), + cell: this.i18nService.translate("cell"), + empty_ambiguous_email: this.i18nService.translate("empty_ambiguous_email"), + empty_ambiguous_phone: this.i18nService.translate("empty_ambiguous_phone"), + empty_ambiguous_address: this.i18nService.translate("empty_ambiguous_address"), }; } diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts index 1d978cae00bf..e892b9ff1a8a 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts @@ -5,6 +5,7 @@ import { InlineMenuCipherData } from "../../../background/abstractions/overlay.b /* start Cozy imports */ /* eslint-disable */ +import { AmbiguousContactFields } from "src/autofill/types"; import { AutofillFieldQualifierType } from "src/autofill/enums/autofill-field.enums"; /* eslint-enable */ /* end Cozy imports */ @@ -16,6 +17,15 @@ export type UpdateAutofillInlineMenuListCiphersMessage = AutofillInlineMenuListM showInlineMenuAccountCreation?: boolean; }; +// Cozy customization +export type UpdateAutofillInlineMenuListAmbiguousMessage = AutofillInlineMenuListMessage & { + inlineMenuCipherId: string; + contactName: string; + ambiguousFields: AmbiguousContactFields; + isFocusedFieldAmbigous: boolean; +}; +// Cozy customization end + export type InitAutofillInlineMenuListMessage = AutofillInlineMenuListMessage & { authStatus: AuthenticationStatus; styleSheetUrl: string; @@ -42,5 +52,12 @@ export type AutofillInlineMenuListWindowMessageHandlers = { }: { message: UpdateAutofillInlineMenuListCiphersMessage; }) => void; + // Cozy customization + ambiguousFieldList: ({ + message, + }: { + message: UpdateAutofillInlineMenuListAmbiguousMessage; + }) => void; + // Cozy customization end focusAutofillInlineMenuList: () => void; }; diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts index 3632c867af50..c7e5f9cd7269 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts @@ -5,8 +5,19 @@ import { EVENTS } from "@bitwarden/common/autofill/constants"; import { CipherType } from "@bitwarden/common/vault/enums"; import { InlineMenuCipherData } from "../../../../background/abstractions/overlay.background"; -import { buildSvgDomElement } from "../../../../utils"; -import { globeIcon, lockIcon, plusIcon, viewCipherIcon } from "../../../../utils/svg-icons"; +import { + bitwardenToCozy, + buildSvgDomElement, + getAmbiguousValueKey, + makeAmbiguousValueLabel, +} from "../../../../utils"; +import { + backIcon, + globeIcon, + lockIcon, + plusIcon, + viewCipherIcon, +} from "../../../../utils/svg-icons"; import { AutofillInlineMenuListWindowMessageHandlers, InitAutofillInlineMenuListMessage, @@ -15,7 +26,13 @@ import { AutofillInlineMenuPageElement } from "../shared/autofill-inline-menu-pa /* start Cozy imports */ /* eslint-disable */ +import uniqueId from "lodash/uniqueId"; import { AutofillFieldQualifierType } from "src/autofill/enums/autofill-field.enums"; +import { + AmbiguousContactFields, + AmbiguousContactFieldValue, + AmibuousContactFieldName, +} from "src/autofill/types"; /* eslint-enable */ /* end Cozy imports */ @@ -43,6 +60,13 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { checkAutofillInlineMenuListFocused: () => this.checkInlineMenuListFocused(), updateAutofillInlineMenuListCiphers: ({ message }) => this.updateListItems(message.ciphers, message.showInlineMenuAccountCreation), + ambiguousFieldList: ({ message }) => + this.ambiguousFieldList( + message.inlineMenuCipherId, + message.contactName, + message.ambiguousFields, + message.isFocusedFieldAmbigous, + ), focusAutofillInlineMenuList: () => this.focusInlineMenuList(), }; @@ -160,7 +184,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { */ private updateListItems( ciphers: InlineMenuCipherData[], - showInlineMenuAccountCreation?: boolean, + showInlineMenuAccountCreation?: boolean ) { this.ciphers = ciphers; this.currentCipherIndex = 0; @@ -198,6 +222,279 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { this.newItemButtonElement.addEventListener(EVENTS.KEYUP, this.handleNewItemButtonKeyUpEvent); } + /** + * @param inlineMenuCipherId + * @param contactName + * @param ambiguousKey + * @param ambiguousValue + * @param isAmbiguousFieldFocused + */ + private createAmbiguousListItem( + inlineMenuCipherId: string, + contactName: string, + ambiguousKey: AmibuousContactFieldName, + ambiguousValue: AmbiguousContactFieldValue[0], + isAmbiguousFieldFocused: boolean, + ) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const currentListItemValue = ambiguousValue[getAmbiguousValueKey(ambiguousKey)]; + + const listItem = document.createElement("li"); + listItem.setAttribute("role", "listitem"); + listItem.classList.add("inline-menu-list-actions-item"); + + const div = document.createElement("div"); + div.classList.add("cipher-container"); + + const fillButton = document.createElement("button"); + fillButton.setAttribute("tabindex", "-1"); + fillButton.classList.add("fill-cipher-button", "inline-menu-list-action"); + fillButton.setAttribute("aria-label", contactName); + fillButton.addEventListener( + EVENTS.CLICK, + this.handleFillCipherAmbiguousClickEvent(inlineMenuCipherId, ambiguousValue, uniqueId()), + ); + + const isAlreadySelected = this.fieldValue && currentListItemValue + .toLowerCase() + .includes(this.fieldValue.toLowerCase()); + const radio = document.createElement("input"); + radio.setAttribute("type", "radio"); + radio.setAttribute("name", "contact"); + radio.setAttribute("id", "contact"); + if (isAlreadySelected) { + radio.setAttribute("checked", "true"); + } + radio.style.marginRight = "2rem"; + + const detailsSpan = document.createElement("span"); + detailsSpan.classList.add("cipher-details"); + + const nameSpanText = makeAmbiguousValueLabel(ambiguousValue, isAmbiguousFieldFocused, this.getTranslation.bind(this)); + const nameSpan = document.createElement("span"); + nameSpan.setAttribute("title", nameSpanText); + nameSpan.textContent = nameSpanText; + nameSpan.classList.add("cipher-name"); + + const subNameSpan = document.createElement("span"); + subNameSpan.setAttribute("title", ambiguousKey); + subNameSpan.classList.add("cipher-subtitle"); + + // Reverse the class for the current ambiguous field + if (bitwardenToCozy[this.fieldQualifier] === ambiguousKey) { + subNameSpan.classList.replace("cipher-subtitle", "cipher-name"); + nameSpan.classList.replace("cipher-name", "cipher-subtitle"); + } + subNameSpan.textContent = currentListItemValue; + + detailsSpan.appendChild(nameSpan); + detailsSpan.appendChild(subNameSpan); + fillButton.appendChild(radio); + fillButton.appendChild(detailsSpan); + + div.appendChild(fillButton); + listItem.appendChild(div); + + return listItem; + } + + /** + * @param contactName + */ + private buildNewAmbiguousHeader(contactName: string) { + this.newItemButtonElement = globalThis.document.createElement("button"); + this.newItemButtonElement.tabIndex = -1; + this.newItemButtonElement.classList.add("inline-menu-list-ambiguous-header"); + + const span = globalThis.document.createElement("span"); + span.textContent = contactName; + span.setAttribute("title", contactName); + span.classList.add("ambiguous-header-text"); + this.newItemButtonElement.setAttribute("aria-label", contactName); + + if (this.fieldValue) { + this.newItemButtonElement.classList.add("inline-menu-list-ambiguous-header--without-back-icon"); + } else { + this.newItemButtonElement.append(buildSvgDomElement(backIcon)); + this.newItemButtonElement.addEventListener(EVENTS.CLICK, this.handleNewAmbiguousHeaderClick); + } + this.newItemButtonElement.appendChild(span); + + return this.buildAmbiguousHeaderContainer(this.newItemButtonElement); + } + + private handleNewAmbiguousHeaderClick = () => { + this.updateListItems(this.ciphers); + }; + + /** + * @param element + */ + private buildAmbiguousHeaderContainer(element: Element) { + const inlineMenuListButtonContainer = globalThis.document.createElement("div"); + inlineMenuListButtonContainer.classList.add("inline-menu-list-ambiguous-header-container"); + inlineMenuListButtonContainer.appendChild(element); + + return inlineMenuListButtonContainer; + } + + // TODO Part_2 => Uncomment for next step + // private createNewAmbiguousButton(inlineMenuCipherId: string, ambiguousKey: AmibuousContactFieldName) { + // const listItem = document.createElement("li"); + // listItem.setAttribute("role", "listitem"); + // listItem.classList.add("inline-menu-list-actions-item"); + + // const div = document.createElement("div"); + // div.classList.add("cipher-container"); + + // const fillButton = document.createElement("button"); + // fillButton.setAttribute("tabindex", "-1"); + // fillButton.classList.add("fill-cipher-button", "inline-menu-list-action"); + // fillButton.setAttribute("aria-label", ambiguousKey); + // // TODO Part_2 => fillButton.addEventListener(EVENTS.CLICK, this.handleFillCipherAmbiguousClickEvent(inlineMenuCipherId, ambiguousValue, uniqueId())); + + // const radio = document.createElement("input"); + // radio.setAttribute("type", "radio"); + // radio.setAttribute("name", "contact"); + // radio.setAttribute("id", "contact"); + // radio.style.marginRight = "2rem"; + + // const detailsSpan = document.createElement("span"); + // detailsSpan.classList.add("cipher-details"); + + // const nameSpanText = `New ${ambiguousKey}`; + // const nameSpan = document.createElement("span"); + // nameSpan.setAttribute("title", nameSpanText); + // nameSpan.textContent = nameSpanText; + // nameSpan.classList.add("cipher-name"); + + // detailsSpan.appendChild(nameSpan); + // fillButton.appendChild(radio); + // fillButton.appendChild(detailsSpan); + // div.appendChild(fillButton); + // listItem.appendChild(div); + + // return listItem; + // } + + /** + * @param ambiguousKey + */ + private createEmptyAmbiguousListItem(ambiguousKey: AmibuousContactFieldName) { + const listItem = document.createElement("li"); + listItem.setAttribute("role", "listitem"); + listItem.classList.add("inline-menu-list-actions-item", "disabled"); + + const div = document.createElement("div"); + div.classList.add("cipher-container"); + + const fillButton = document.createElement("button"); + fillButton.setAttribute("tabindex", "-1"); + fillButton.classList.add("fill-cipher-button", "inline-menu-list-action"); + fillButton.setAttribute("aria-label", this.getTranslation(`empty_ambiguous_${ambiguousKey}`)); + + const radio = document.createElement("input"); + radio.setAttribute("type", "radio"); + radio.setAttribute("name", "contact"); + radio.setAttribute("id", "contact"); + radio.style.marginRight = "2rem"; + + const detailsSpan = document.createElement("span"); + detailsSpan.classList.add("cipher-details"); + + const nameSpanText = this.getTranslation(`empty_ambiguous_${ambiguousKey}`); + const nameSpan = document.createElement("span"); + nameSpan.setAttribute("title", nameSpanText); + nameSpan.textContent = nameSpanText; + nameSpan.classList.add("cipher-name"); + + detailsSpan.appendChild(nameSpan); + fillButton.appendChild(radio); + fillButton.appendChild(detailsSpan); + div.appendChild(fillButton); + listItem.appendChild(div); + + return listItem; + } + + /** + * @param inlineMenuCipherId + * @param contactName + * @param ambiguousFields + * @param isAmbiguousFieldFocused + */ + private ambiguousFieldList( + inlineMenuCipherId: string, + contactName: string, + ambiguousFields: AmbiguousContactFields, + isAmbiguousFieldFocused: boolean, + ) { + this.inlineMenuListContainer.innerHTML = ""; + this.inlineMenuListContainer.classList.remove( + "inline-menu-list-container--with-new-item-button", + ); + + const addNewLoginButtonContainer = this.buildNewAmbiguousHeader(contactName); + + const ulElement = globalThis.document.createElement("ul"); + ulElement.classList.add("inline-menu-list-actions"); + ulElement.setAttribute("role", "list"); + ulElement.addEventListener(EVENTS.SCROLL, this.handleCiphersListScrollEvent, { + passive: true, + }); + + const firstAmbiguousFieldEntries = Object.entries(ambiguousFields)?.[0]; + const firstAmbiguousFieldName = firstAmbiguousFieldEntries?.[0] as AmibuousContactFieldName; // || bitwardenToCozy[this.fieldQualifier]; // TODO Part_2 To add for next step, The contact has no value in an ambiguous focus form field + + if (firstAmbiguousFieldEntries) { + for (const firstAmbiguousFieldValue of firstAmbiguousFieldEntries[1]) { + const li = this.createAmbiguousListItem( + inlineMenuCipherId, + contactName, + firstAmbiguousFieldName, + firstAmbiguousFieldValue, + isAmbiguousFieldFocused, + ); + ulElement.appendChild(li); + } + } else { + const emptyLi = this.createEmptyAmbiguousListItem(firstAmbiguousFieldName); + ulElement.appendChild(emptyLi); + } + // TODO Uncomment for next step, Add "New xxx" button for ambiguous field + // if (isAmbiguousFieldFocused) { + // const newButton = this.createNewAmbiguousButton(inlineMenuCipherId, firstAmbiguousFieldName); + // ulElement.appendChild(newButton); + // } + + this.inlineMenuListContainer.appendChild(addNewLoginButtonContainer); + this.inlineMenuListContainer.appendChild(ulElement); + + this.inlineMenuListContainer.classList.add("inline-menu-list-container--with-new-item-button"); + } + + /** + * @param inlineMenuCipherId + * @param ambiguousValue + * @param UID + */ + private handleFillCipherAmbiguousClickEvent = ( + inlineMenuCipherId: string, + ambiguousValue: AmbiguousContactFieldValue[0], + UID: string, + ) => { + return this.useEventHandlersMemo( + () => + this.postMessageToParent({ + command: "fillAutofillInlineMenuCipherWithAmbiguousField", + inlineMenuCipherId, + ambiguousValue, + }), + `${UID}-fill-cipher-button-click-handler`, + ); + }; + /** * Inline menu view that is presented when no ciphers are found for a given page. * Facilitates the ability to add a new vault item from the inline menu. diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss b/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss index bda2464de393..8b6078075fee 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss @@ -58,6 +58,56 @@ body { } } +// Cozy customization +.inline-menu-list-ambiguous-header-container { + width: 100%; + padding: 0.2rem; + border: 0; + background-color: #297ef2; +} +.ambiguous-header-text { + max-width: 16rem; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + font-size: 1.8rem; + line-height: 23px; + font-weight: 700; + font-family: $font-family-sans-serif; + color: white; +} +.inline-menu-list-ambiguous-header { + display: flex; + align-content: center; + justify-content: flex-start; + width: 100%; + text-align: left; + background: transparent; + border: none; + padding: 0.7rem 0; + margin: 0; + cursor: pointer; + border-radius: 0.4rem; + + svg { + position: relative; + top: 0.25rem; + width: 1.6rem; + height: 1.6rem; + margin-left: 1rem; + margin-right: 2rem; + + path { + fill: white; + } + } +} +.inline-menu-list-ambiguous-header--without-back-icon { + padding: 0.7rem 0 0.7rem 1.6rem; + cursor: default; +} +// Cozy customization end + // Cozy customization; replace .cipher-icon .contact-initials { display: flex; diff --git a/apps/browser/src/autofill/types/index.ts b/apps/browser/src/autofill/types/index.ts index a43c1612a23b..2b2a00f8b7e9 100644 --- a/apps/browser/src/autofill/types/index.ts +++ b/apps/browser/src/autofill/types/index.ts @@ -60,13 +60,13 @@ export type FormElementWithAttribute = FormFieldElement & Record { + switch (ambiguousKey) { + case "email": + return "address"; + case "phone": + return "number"; + case "address": + return "formattedAddress"; + default: + return ""; + } +}; + +export const makeAmbiguousValueLabel = ( + ambiguousValue: AmbiguousContactFieldValue[0], + isAmbiguousFieldFocused: boolean, + t: (key: string) => string, +) => { + const translatedType = ambiguousValue.type?.toLowerCase() === 'cell' ? t(ambiguousValue.type) : ambiguousValue.type + const translatedLabel = t(ambiguousValue.label) + + if (isAmbiguousFieldFocused) { + return ambiguousValue.label + ? `${translatedType ? `${translatedType} (${translatedLabel})` : translatedLabel}` + : ""; + } else { + return `${translatedLabel || ""}`; + } +}; // Cozy customization end From ffa58ff61b1c1395436f7f04910749b8b211de65 Mon Sep 17 00:00:00 2001 From: AlexisG Date: Fri, 26 Jul 2024 15:05:50 +0200 Subject: [PATCH 10/14] feat: Display a specific menu if the clicked field is already filled in If the form field is already filled in, we want to be able to display the menu with the choices corresponding to the field type and the autofill without changing the other fields. --- .../autofill/background/overlay.background.ts | 9 ++++--- .../abstractions/autofill-inline-menu-list.ts | 1 + .../pages/list/autofill-inline-menu-list.ts | 27 +++++++++++++++++-- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index c9827080e9ad..3f7467be8007 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -793,6 +793,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { { inlineMenuCipherId }: OverlayPortMessage, { sender }: chrome.runtime.Port, ambiguousValue?: AmbiguousContactFieldValue[0], + fieldHtmlIDToFill?: string, ) { const pageDetails = this.pageDetailsForTab[sender.tab.id]; if (!inlineMenuCipherId || !pageDetails?.size) { @@ -811,6 +812,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { fillNewPassword: true, allowTotpAutofill: true, ...(ambiguousValue ? { cozyProfile: ambiguousValue } : {}), + ...(fieldHtmlIDToFill ? { fillOnlyThisFieldHtmlID: fieldHtmlIDToFill } : {}), }); // Cozy customization @@ -828,8 +830,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { message: OverlayPortMessage, port: chrome.runtime.Port, ) { - const ambiguousValue = message.ambiguousValue; - this.fillInlineMenuCipher(message, port, ambiguousValue); + const { ambiguousValue, fieldHtmlIDToFill } = message; + this.fillInlineMenuCipher(message, port, ambiguousValue, fieldHtmlIDToFill); } /** @@ -837,7 +839,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { * @param sender - The sender of the port message */ private async handleContactClick(message: OverlayPortMessage, port: chrome.runtime.Port) { - const { inlineMenuCipherId } = message; + const { inlineMenuCipherId, fieldHtmlIDToFill } = message; const client = await this.cozyClientService.getClientInstance(); const cipher = this.inlineMenuCiphers.get(inlineMenuCipherId); @@ -894,6 +896,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { ? ambiguousFormFieldsOfFocusedField : unambiguousFormFieldsOfFocusedField, isFocusedFieldAmbigous, + fieldHtmlIDToFill, }); } else { this.fillInlineMenuCipher(message, port); diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts index e892b9ff1a8a..ecc06c08e4e1 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-list.ts @@ -23,6 +23,7 @@ export type UpdateAutofillInlineMenuListAmbiguousMessage = AutofillInlineMenuLis contactName: string; ambiguousFields: AmbiguousContactFields; isFocusedFieldAmbigous: boolean; + fieldHtmlIDToFill: string; }; // Cozy customization end diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts index c7e5f9cd7269..0ccd04a65c72 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts @@ -50,6 +50,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { private lastFilledCipherId: string; private fieldQualifier: AutofillFieldQualifierType; private fieldValue: string; + private fieldHtmlID: string; // Cozy customization end private showInlineMenuAccountCreation: boolean; private readonly showCiphersPerPage = 6; @@ -66,6 +67,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { message.contactName, message.ambiguousFields, message.isFocusedFieldAmbigous, + message.fieldHtmlIDToFill, ), focusAutofillInlineMenuList: () => this.focusInlineMenuList(), }; @@ -115,6 +117,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { this.lastFilledCipherId = lastFilledCipherId; this.fieldQualifier = fieldQualifier; this.fieldValue = fieldValue; + this.fieldHtmlID = fieldHtmlID; // Cozy customization end this.filledByCipherType = filledByCipherType; @@ -208,7 +211,22 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { passive: true, }); - this.loadPageOfCiphers(); + // On the contact ambiguous fields, if the field has a value, the corresponding menu is displayed directly. + if ( + this.fieldValue && + ambiguousContactFieldNames.includes(bitwardenToCozy[this.fieldQualifier]) + ) { + this.postMessageToParent({ + command: "handleContactClick", + inlineMenuCipherId: this.lastFilledCipherId, + lastFilledCipherId: this.lastFilledCipherId, + fieldQualifier: this.fieldQualifier, + fieldValue: this.fieldValue, + fieldHtmlIDToFill: this.fieldHtmlID, + }) + } else { + this.loadPageOfCiphers(); + } this.inlineMenuListContainer.appendChild(this.ciphersList); @@ -235,6 +253,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { ambiguousKey: AmibuousContactFieldName, ambiguousValue: AmbiguousContactFieldValue[0], isAmbiguousFieldFocused: boolean, + fieldHtmlIDToFill: string, ) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -253,7 +272,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { fillButton.setAttribute("aria-label", contactName); fillButton.addEventListener( EVENTS.CLICK, - this.handleFillCipherAmbiguousClickEvent(inlineMenuCipherId, ambiguousValue, uniqueId()), + this.handleFillCipherAmbiguousClickEvent(inlineMenuCipherId, ambiguousValue, fieldHtmlIDToFill, uniqueId()), ); const isAlreadySelected = this.fieldValue && currentListItemValue @@ -429,6 +448,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { contactName: string, ambiguousFields: AmbiguousContactFields, isAmbiguousFieldFocused: boolean, + fieldHtmlIDToFill: string, ) { this.inlineMenuListContainer.innerHTML = ""; this.inlineMenuListContainer.classList.remove( @@ -455,6 +475,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { firstAmbiguousFieldName, firstAmbiguousFieldValue, isAmbiguousFieldFocused, + fieldHtmlIDToFill, ); ulElement.appendChild(li); } @@ -482,6 +503,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { private handleFillCipherAmbiguousClickEvent = ( inlineMenuCipherId: string, ambiguousValue: AmbiguousContactFieldValue[0], + fieldHtmlIDToFill: string, UID: string, ) => { return this.useEventHandlersMemo( @@ -490,6 +512,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { command: "fillAutofillInlineMenuCipherWithAmbiguousField", inlineMenuCipherId, ambiguousValue, + fieldHtmlIDToFill, }), `${UID}-fill-cipher-button-click-handler`, ); From 52a7c11d372cfda52e59a507d320b9d6a39aed09 Mon Sep 17 00:00:00 2001 From: AlexisG Date: Fri, 26 Jul 2024 15:08:31 +0200 Subject: [PATCH 11/14] refactor: Fix prettier --- .../background/overlay.background.spec.ts | 2 +- .../pages/list/autofill-inline-menu-list.ts | 26 +++++++++++++------ apps/browser/src/autofill/utils/index.ts | 21 ++++++++++----- apps/browser/src/autofill/utils/svg-icons.ts | 3 ++- .../browser/src/background/main.background.ts | 2 +- 5 files changed, 37 insertions(+), 17 deletions(-) diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts index 68b8ff79af01..be55ad49e4c2 100644 --- a/apps/browser/src/autofill/background/overlay.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay.background.spec.ts @@ -167,7 +167,7 @@ describe("OverlayBackground", () => { i18nService, platformUtilsService, themeStateService, - cozyClientService + cozyClientService, ); portKeyForTabSpy = overlayBackground["portKeyForTab"]; pageDetailsForTabSpy = overlayBackground["pageDetailsForTab"]; diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts index 0ccd04a65c72..d850eb2261db 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts @@ -187,7 +187,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { */ private updateListItems( ciphers: InlineMenuCipherData[], - showInlineMenuAccountCreation?: boolean + showInlineMenuAccountCreation?: boolean, ) { this.ciphers = ciphers; this.currentCipherIndex = 0; @@ -223,7 +223,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { fieldQualifier: this.fieldQualifier, fieldValue: this.fieldValue, fieldHtmlIDToFill: this.fieldHtmlID, - }) + }); } else { this.loadPageOfCiphers(); } @@ -272,12 +272,16 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { fillButton.setAttribute("aria-label", contactName); fillButton.addEventListener( EVENTS.CLICK, - this.handleFillCipherAmbiguousClickEvent(inlineMenuCipherId, ambiguousValue, fieldHtmlIDToFill, uniqueId()), + this.handleFillCipherAmbiguousClickEvent( + inlineMenuCipherId, + ambiguousValue, + fieldHtmlIDToFill, + uniqueId(), + ), ); - const isAlreadySelected = this.fieldValue && currentListItemValue - .toLowerCase() - .includes(this.fieldValue.toLowerCase()); + const isAlreadySelected = + this.fieldValue && currentListItemValue.toLowerCase().includes(this.fieldValue.toLowerCase()); const radio = document.createElement("input"); radio.setAttribute("type", "radio"); radio.setAttribute("name", "contact"); @@ -290,7 +294,11 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { const detailsSpan = document.createElement("span"); detailsSpan.classList.add("cipher-details"); - const nameSpanText = makeAmbiguousValueLabel(ambiguousValue, isAmbiguousFieldFocused, this.getTranslation.bind(this)); + const nameSpanText = makeAmbiguousValueLabel( + ambiguousValue, + isAmbiguousFieldFocused, + this.getTranslation.bind(this), + ); const nameSpan = document.createElement("span"); nameSpan.setAttribute("title", nameSpanText); nameSpan.textContent = nameSpanText; @@ -333,7 +341,9 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { this.newItemButtonElement.setAttribute("aria-label", contactName); if (this.fieldValue) { - this.newItemButtonElement.classList.add("inline-menu-list-ambiguous-header--without-back-icon"); + this.newItemButtonElement.classList.add( + "inline-menu-list-ambiguous-header--without-back-icon", + ); } else { this.newItemButtonElement.append(buildSvgDomElement(backIcon)); this.newItemButtonElement.addEventListener(EVENTS.CLICK, this.handleNewAmbiguousHeaderClick); diff --git a/apps/browser/src/autofill/utils/index.ts b/apps/browser/src/autofill/utils/index.ts index 9c22c78f8e36..3e21aaae0351 100644 --- a/apps/browser/src/autofill/utils/index.ts +++ b/apps/browser/src/autofill/utils/index.ts @@ -375,16 +375,24 @@ export const getAmbiguousFieldsContact = ( ambiguousFields: AmibuousContactFieldName[], contact: IOCozyContact, ): AmbiguousContactFields => { - return ambiguousFields - .reduce((acc, field) => (contact[field].length > 0 ? {...acc, [field]: contact[field]} : acc), {}); + return ambiguousFields.reduce( + (acc, field) => (contact[field].length > 0 ? { ...acc, [field]: contact[field] } : acc), + {}, + ); }; -export const bitwardenToCozy: Partial> = { +export const bitwardenToCozy: Partial< + Record +> = { identityPhone: "phone", identityEmail: "email", identityAddress1: "address", identityState: "address", }; -export const ambiguousContactFieldNames: AmibuousContactFieldName[] = ["phone", "email", "address"]; +export const ambiguousContactFieldNames: AmbiguousContactFieldName[] = [ + "phone", + "email", + "address", +]; export const getAmbiguousValueKey = (ambiguousKey: AmibuousContactFieldName) => { switch (ambiguousKey) { @@ -404,8 +412,9 @@ export const makeAmbiguousValueLabel = ( isAmbiguousFieldFocused: boolean, t: (key: string) => string, ) => { - const translatedType = ambiguousValue.type?.toLowerCase() === 'cell' ? t(ambiguousValue.type) : ambiguousValue.type - const translatedLabel = t(ambiguousValue.label) + const translatedType = + ambiguousValue.type?.toLowerCase() === "cell" ? t(ambiguousValue.type) : ambiguousValue.type; + const translatedLabel = t(ambiguousValue.label); if (isAmbiguousFieldFocused) { return ambiguousValue.label diff --git a/apps/browser/src/autofill/utils/svg-icons.ts b/apps/browser/src/autofill/utils/svg-icons.ts index 0c15f0ed1212..58c5ecf3d0d8 100644 --- a/apps/browser/src/autofill/utils/svg-icons.ts +++ b/apps/browser/src/autofill/utils/svg-icons.ts @@ -34,7 +34,8 @@ const viewCipherIcon = //*/ // Cozy customization, Add back icon -const backIcon = '' +const backIcon = + ''; // Cozy customization end export { logoIcon, logoLockedIcon, globeIcon, lockIcon, plusIcon, viewCipherIcon, backIcon }; diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 5146392edd52..42cbe6e49bd1 100755 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -1234,7 +1234,7 @@ export default class MainBackground { this.i18nService, this.platformUtilsService, themeStateService, - this.cozyClientService + this.cozyClientService, ); } From 21c3cf7f836a2fc58385aec657dace31ef9cf59e Mon Sep 17 00:00:00 2001 From: AlexisG Date: Tue, 30 Jul 2024 16:50:48 +0200 Subject: [PATCH 12/14] feat: Add a search bar to the contact list --- apps/browser/src/_locales/en/messages.json | 3 ++ apps/browser/src/_locales/fr/messages.json | 3 ++ .../autofill/background/overlay.background.ts | 1 + .../pages/list/autofill-inline-menu-list.ts | 54 ++++++++++++++++--- .../overlay/inline-menu/pages/list/list.scss | 37 +++++++++++++ apps/browser/src/autofill/utils/svg-icons.ts | 14 ++++- 6 files changed, 104 insertions(+), 8 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index d7754b4ef0e0..3fe1e71743f6 100755 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -4012,6 +4012,9 @@ "cipherContactMe": { "message": "me" }, + "contactSearch": { + "message": "Search\u2026" + }, "empty_ambiguous_email": { "message": "No email found" }, diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 86cd6df5fa7e..3310213b42a6 100755 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -3974,6 +3974,9 @@ "cipherContactMe": { "message": "moi" }, + "contactSearch": { + "message": "Recherche\u2026" + }, "empty_ambiguous_email": { "message": "Aucun email trouvé" }, diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index 3f7467be8007..ebae73221f24 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -1478,6 +1478,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { home: this.i18nService.translate("home"), work: this.i18nService.translate("work"), cell: this.i18nService.translate("cell"), + contactSearch: this.i18nService.translate("contactSearch"), empty_ambiguous_email: this.i18nService.translate("empty_ambiguous_email"), empty_ambiguous_phone: this.i18nService.translate("empty_ambiguous_phone"), empty_ambiguous_address: this.i18nService.translate("empty_ambiguous_address"), diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts index d850eb2261db..683a209e9171 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts @@ -6,6 +6,7 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { InlineMenuCipherData } from "../../../../background/abstractions/overlay.background"; import { + ambiguousContactFieldNames, bitwardenToCozy, buildSvgDomElement, getAmbiguousValueKey, @@ -17,6 +18,7 @@ import { lockIcon, plusIcon, viewCipherIcon, + magnifier, } from "../../../../utils/svg-icons"; import { AutofillInlineMenuListWindowMessageHandlers, @@ -51,6 +53,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { private fieldQualifier: AutofillFieldQualifierType; private fieldValue: string; private fieldHtmlID: string; + private newItemInputSearchElement: HTMLInputElement; // Cozy customization end private showInlineMenuAccountCreation: boolean; private readonly showCiphersPerPage = 6; @@ -228,6 +231,14 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { this.loadPageOfCiphers(); } + const isContactCipherList = ciphers.every((cipher) => cipher.contact); + if (isContactCipherList) { + this.inlineMenuListContainer.appendChild(this.buildContactSearch()); + this.inlineMenuListContainer.classList.add( + "inline-menu-list-container--with-new-item-button", + ); + } + this.inlineMenuListContainer.appendChild(this.ciphersList); if (!this.showInlineMenuAccountCreation) { @@ -240,6 +251,32 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { this.newItemButtonElement.addEventListener(EVENTS.KEYUP, this.handleNewItemButtonKeyUpEvent); } + private buildContactSearch() { + const inputContainer = globalThis.document.createElement("div"); + inputContainer.classList.add("search-container"); + this.newItemInputSearchElement = globalThis.document.createElement("input"); + this.newItemInputSearchElement.type = "text"; + this.newItemInputSearchElement.placeholder = this.getTranslation("contactSearch"); + this.newItemInputSearchElement.tabIndex = -1; + this.newItemInputSearchElement.classList.add("contact-search-header"); + const iconElement = buildSvgDomElement(magnifier); + iconElement.classList.add("search-icon"); + + inputContainer.append(iconElement); + inputContainer.append(this.newItemInputSearchElement); + + this.newItemInputSearchElement.addEventListener(EVENTS.KEYUP, this.handleNewSearch); + + return this.buildAmbiguousHeaderContainer(inputContainer); + } + + private handleNewSearch = () => { + const filteredCiphers = this.ciphers.filter((c) => + c.name.toLowerCase().includes(this.newItemInputSearchElement.value), + ); + this.loadPageOfCiphers(filteredCiphers); + }; + /** * @param inlineMenuCipherId * @param contactName @@ -636,17 +673,20 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { /** * Loads a page of ciphers into the inline menu list container. */ - private loadPageOfCiphers() { - const lastIndex = Math.min( - this.currentCipherIndex + this.showCiphersPerPage, - this.ciphers.length, - ); + private loadPageOfCiphers(filteredCiphers?: InlineMenuCipherData[]) { + const ciphers = filteredCiphers || this.ciphers; + if (filteredCiphers) { + this.currentCipherIndex = 0; + this.ciphersList.innerHTML = ""; + } + const lastIndex = Math.min(this.currentCipherIndex + this.showCiphersPerPage, ciphers.length); + for (let cipherIndex = this.currentCipherIndex; cipherIndex < lastIndex; cipherIndex++) { - this.ciphersList.appendChild(this.buildInlineMenuListActionsItem(this.ciphers[cipherIndex])); + this.ciphersList.appendChild(this.buildInlineMenuListActionsItem(ciphers[cipherIndex])); this.currentCipherIndex++; } - if (this.currentCipherIndex >= this.ciphers.length) { + if (this.currentCipherIndex >= ciphers.length) { this.ciphersList.removeEventListener(EVENTS.SCROLL, this.handleCiphersListScrollEvent); } } diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss b/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss index 8b6078075fee..1c5d9afd1755 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss @@ -106,6 +106,43 @@ body { padding: 0.7rem 0 0.7rem 1.6rem; cursor: default; } +.search-container { + position: relative; + display: inline-block; + + .search-icon { + position: absolute; + left: 1.6rem; + top: 50%; + transform: translateY(-50%); + pointer-events: none; + color: rgb(170, 170, 170); + } + + .contact-search-header { + display: flex; + align-content: center; + justify-content: flex-start; + width: 100%; + text-align: left; + background: rgba(255, 255, 255, 0.16); + border: none; + padding: 0.7rem 0.8rem 0.7rem 4rem; + margin: 0.4rem; + cursor: pointer; + border-radius: 1.6rem; + color: rgb(255, 255, 255); + outline: none; + + &::placeholder { + color: rgba(255, 255, 255, 0.32); + } + &::-webkit-search-cancel-button { + fill: red; + color: blue; + } + } +} // Cozy customization end // Cozy customization; replace .cipher-icon diff --git a/apps/browser/src/autofill/utils/svg-icons.ts b/apps/browser/src/autofill/utils/svg-icons.ts index 58c5ecf3d0d8..7501025c5038 100644 --- a/apps/browser/src/autofill/utils/svg-icons.ts +++ b/apps/browser/src/autofill/utils/svg-icons.ts @@ -36,6 +36,18 @@ const viewCipherIcon = // Cozy customization, Add back icon const backIcon = ''; + +const magnifier = + ''; // Cozy customization end -export { logoIcon, logoLockedIcon, globeIcon, lockIcon, plusIcon, viewCipherIcon, backIcon }; +export { + logoIcon, + logoLockedIcon, + globeIcon, + lockIcon, + plusIcon, + viewCipherIcon, + backIcon, + magnifier, +}; From 83123a474bd9f4e1fa243fc3d325cbb19804253c Mon Sep 17 00:00:00 2001 From: AlexisG Date: Tue, 30 Jul 2024 16:57:53 +0200 Subject: [PATCH 13/14] fix: typing mistake --- .../pages/list/autofill-inline-menu-list.ts | 10 +++++----- apps/browser/src/autofill/types/index.ts | 2 +- apps/browser/src/autofill/utils/index.ts | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts index 683a209e9171..fbe82a4e154f 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts @@ -33,7 +33,7 @@ import { AutofillFieldQualifierType } from "src/autofill/enums/autofill-field.en import { AmbiguousContactFields, AmbiguousContactFieldValue, - AmibuousContactFieldName, + AmbiguousContactFieldName, } from "src/autofill/types"; /* eslint-enable */ /* end Cozy imports */ @@ -287,7 +287,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { private createAmbiguousListItem( inlineMenuCipherId: string, contactName: string, - ambiguousKey: AmibuousContactFieldName, + ambiguousKey: AmbiguousContactFieldName, ambiguousValue: AmbiguousContactFieldValue[0], isAmbiguousFieldFocused: boolean, fieldHtmlIDToFill: string, @@ -406,7 +406,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { } // TODO Part_2 => Uncomment for next step - // private createNewAmbiguousButton(inlineMenuCipherId: string, ambiguousKey: AmibuousContactFieldName) { + // private createNewAmbiguousButton(inlineMenuCipherId: string, ambiguousKey: AmbiguousContactFieldName) { // const listItem = document.createElement("li"); // listItem.setAttribute("role", "listitem"); // listItem.classList.add("inline-menu-list-actions-item"); @@ -447,7 +447,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { /** * @param ambiguousKey */ - private createEmptyAmbiguousListItem(ambiguousKey: AmibuousContactFieldName) { + private createEmptyAmbiguousListItem(ambiguousKey: AmbiguousContactFieldName) { const listItem = document.createElement("li"); listItem.setAttribute("role", "listitem"); listItem.classList.add("inline-menu-list-actions-item", "disabled"); @@ -512,7 +512,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { }); const firstAmbiguousFieldEntries = Object.entries(ambiguousFields)?.[0]; - const firstAmbiguousFieldName = firstAmbiguousFieldEntries?.[0] as AmibuousContactFieldName; // || bitwardenToCozy[this.fieldQualifier]; // TODO Part_2 To add for next step, The contact has no value in an ambiguous focus form field + const firstAmbiguousFieldName = firstAmbiguousFieldEntries?.[0] as AmbiguousContactFieldName; // || bitwardenToCozy[this.fieldQualifier]; // TODO Part_2 To add for next step, The contact has no value in an ambiguous focus form field if (firstAmbiguousFieldEntries) { for (const firstAmbiguousFieldValue of firstAmbiguousFieldEntries[1]) { diff --git a/apps/browser/src/autofill/types/index.ts b/apps/browser/src/autofill/types/index.ts index 2b2a00f8b7e9..f2ad9cb0fa12 100644 --- a/apps/browser/src/autofill/types/index.ts +++ b/apps/browser/src/autofill/types/index.ts @@ -60,7 +60,7 @@ export type FormElementWithAttribute = FormFieldElement & Record void, limit: number) { * @param contact */ export const getAmbiguousFieldsContact = ( - ambiguousFields: AmibuousContactFieldName[], + ambiguousFields: AmbiguousContactFieldName[], contact: IOCozyContact, ): AmbiguousContactFields => { return ambiguousFields.reduce( @@ -381,7 +381,7 @@ export const getAmbiguousFieldsContact = ( ); }; export const bitwardenToCozy: Partial< - Record + Record > = { identityPhone: "phone", identityEmail: "email", @@ -394,7 +394,7 @@ export const ambiguousContactFieldNames: AmbiguousContactFieldName[] = [ "address", ]; -export const getAmbiguousValueKey = (ambiguousKey: AmibuousContactFieldName) => { +export const getAmbiguousValueKey = (ambiguousKey: AmbiguousContactFieldName) => { switch (ambiguousKey) { case "email": return "address"; From 579fd2db1d37611d983f75e6df2160b3bde72c29 Mon Sep 17 00:00:00 2001 From: AlexisG Date: Wed, 31 Jul 2024 12:08:05 +0200 Subject: [PATCH 14/14] fix: Prevents use of a falsy label before retrieving its translation. --- apps/browser/src/autofill/utils/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/browser/src/autofill/utils/index.ts b/apps/browser/src/autofill/utils/index.ts index 0271e7fc4494..f66206d1fae7 100644 --- a/apps/browser/src/autofill/utils/index.ts +++ b/apps/browser/src/autofill/utils/index.ts @@ -414,10 +414,10 @@ export const makeAmbiguousValueLabel = ( ) => { const translatedType = ambiguousValue.type?.toLowerCase() === "cell" ? t(ambiguousValue.type) : ambiguousValue.type; - const translatedLabel = t(ambiguousValue.label); + const translatedLabel = ambiguousValue.label ? t(ambiguousValue.label) : null; if (isAmbiguousFieldFocused) { - return ambiguousValue.label + return translatedLabel ? `${translatedType ? `${translatedType} (${translatedLabel})` : translatedLabel}` : ""; } else {