Skip to content

Commit

Permalink
finalize item copy actions single item copy
Browse files Browse the repository at this point in the history
  • Loading branch information
jaasen-livefront committed Feb 22, 2025
1 parent 6acd671 commit 2b11013
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,16 @@
<ng-template #loginCopyMenu>
<bit-item-action>
<button
*ngIf="singleCopiableLogin"
type="button"
bitIconButton="bwi-clone"
size="small"
[appA11yTitle]="
'copyFieldValue' | i18n: singleCopiableLogin.field : singleCopiableLogin.value
'copyFieldValue' | i18n: singleCopiableLogin.key : singleCopiableLogin.value
"
[appCopyClick]="singleCopiableLogin.value"
[valueLabel]="singleCopiableLogin.field"
*ngIf="singleCopiableLogin"
[valueLabel]="singleCopiableLogin.key"
showToast
></button>
<ng-container *ngIf="!singleCopiableLogin">
<button
Expand Down Expand Up @@ -105,52 +106,78 @@
<ng-template #cardCopyMenu>
<bit-item-action>
<button
*ngIf="singleCopiableCard"
type="button"
bitIconButton="bwi-clone"
size="small"
[appA11yTitle]="
hasCardValues ? ('copyInfoTitle' | i18n: cipher.name) : ('noValuesToCopy' | i18n)
"
[disabled]="!hasCardValues"
[bitMenuTriggerFor]="cardOptions"
[appA11yTitle]="'copyFieldValue' | i18n: singleCopiableCard.key : singleCopiableCard.value"
[appCopyClick]="singleCopiableCard.value"
[valueLabel]="singleCopiableCard.key"
showToast
></button>
<bit-menu #cardOptions>
<button type="button" bitMenuItem appCopyField="cardNumber" [cipher]="cipher">
{{ "copyNumber" | i18n }}
</button>
<button type="button" bitMenuItem appCopyField="securityCode" [cipher]="cipher">
{{ "copySecurityCode" | i18n }}
</button>
</bit-menu>
<ng-container *ngIf="!singleCopiableCard">
<button
type="button"
bitIconButton="bwi-clone"
size="small"
[appA11yTitle]="
hasCardValues ? ('copyInfoTitle' | i18n: cipher.name) : ('noValuesToCopy' | i18n)
"
[disabled]="!hasCardValues"
[bitMenuTriggerFor]="cardOptions"
></button>
<bit-menu #cardOptions>
<button type="button" bitMenuItem appCopyField="cardNumber" [cipher]="cipher">
{{ "copyNumber" | i18n }}
</button>
<button type="button" bitMenuItem appCopyField="securityCode" [cipher]="cipher">
{{ "copySecurityCode" | i18n }}
</button>
</bit-menu>
</ng-container>
</bit-item-action>
</ng-template>
</ng-container>

<bit-item-action *ngIf="cipher.type === CipherType.Identity">
<button
*ngIf="singleCopiableIdentity"
type="button"
bitIconButton="bwi-clone"
size="small"
[appA11yTitle]="
hasIdentityValues ? ('copyInfoTitle' | i18n: cipher.name) : ('noValuesToCopy' | i18n)
'copyFieldValue' | i18n: singleCopiableIdentity.key : singleCopiableIdentity.value
"
[disabled]="!hasIdentityValues"
[bitMenuTriggerFor]="identityOptions"
[appCopyClick]="singleCopiableIdentity.value"
[valueLabel]="singleCopiableIdentity.key"
showToast
></button>
<bit-menu #identityOptions>
<button type="button" bitMenuItem appCopyField="username" [cipher]="cipher">
{{ "copyUsername" | i18n }}
</button>
<button type="button" bitMenuItem appCopyField="email" [cipher]="cipher">
{{ "copyEmail" | i18n }}
</button>
<button type="button" bitMenuItem appCopyField="phone" [cipher]="cipher">
{{ "copyPhone" | i18n }}
</button>
<button type="button" bitMenuItem appCopyField="address" [cipher]="cipher">
{{ "copyAddress" | i18n }}
</button>
</bit-menu>
<ng-container *ngIf="!singleCopiableIdentity">
<button
type="button"
bitIconButton="bwi-clone"
size="small"
[appA11yTitle]="
hasIdentityValues ? ('copyInfoTitle' | i18n: cipher.name) : ('noValuesToCopy' | i18n)
"
[disabled]="!hasIdentityValues"
[bitMenuTriggerFor]="identityOptions"
></button>
<bit-menu #identityOptions>
<button type="button" bitMenuItem appCopyField="username" [cipher]="cipher">
{{ "copyUsername" | i18n }}
</button>
<button type="button" bitMenuItem appCopyField="email" [cipher]="cipher">
{{ "copyEmail" | i18n }}
</button>
<button type="button" bitMenuItem appCopyField="phone" [cipher]="cipher">
{{ "copyPhone" | i18n }}
</button>
<button type="button" bitMenuItem appCopyField="address" [cipher]="cipher">
{{ "copyAddress" | i18n }}
</button>
</bit-menu>
</ng-container>
</bit-item-action>

<bit-item-action *ngIf="cipher.type === CipherType.SecureNote">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ import { CommonModule } from "@angular/common";
import { Component, Input, inject } from "@angular/core";

import { JslibModule } from "@bitwarden/angular/jslib.module";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";

Check warning on line 7 in apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts#L7

Added line #L7 was not covered by tests
import { CipherType } from "@bitwarden/common/vault/enums";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { IconButtonModule, ItemModule, MenuModule } from "@bitwarden/components";
import { CopyCipherFieldDirective } from "@bitwarden/vault";

import { VaultPopupCopyButtonsService } from "../../../services/vault-popup-copy-buttons.service";

type CipherItem = {
value: string;
key: string;
};

@Component({
standalone: true,
selector: "app-item-copy-actions",
Expand Down Expand Up @@ -38,61 +44,47 @@ export class ItemCopyActionsComponent {
}

get singleCopiableLogin() {
const { username, password, hasTotp, totp } = this.cipher.login;
// If there is both a username and password but the password is not viewable, then the username is the only copiable value
if (username && password && !this.cipher.viewPassword) {
return {
value: username,
field: "username",
};
}
if (username && !password && !hasTotp) {
return {
value: username,
field: "username",
};
}
if (!username && password && !hasTotp) {
return {
value: password,
field: "password",
};
}
if (!username && !password && hasTotp) {
return {
value: totp,
field: "totp",
};
const loginItems: CipherItem[] = [

Check warning on line 47 in apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts#L47

Added line #L47 was not covered by tests
{ value: this.cipher.login.username, key: "username" },
{ value: this.cipher.login.password, key: "password" },
{ value: this.cipher.login.totp, key: "totp" },
];
// If both the password and username are visible but the password is hidden, return the username
if (!this.cipher.viewPassword && this.cipher.login.username && this.cipher.login.password) {
return { value: this.cipher.login.username, key: this.i18nService.t("username") };

Check warning on line 54 in apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts#L54

Added line #L54 was not covered by tests
}
return null;
return this.findSingleCopiableItem(loginItems);

Check warning on line 56 in apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts#L56

Added line #L56 was not covered by tests
}

get singleCopiableCardValue() {
const { code, number } = this.cipher.card;
if (code && !number) {
return code;
}
if (!code && number) {
return number;
}
return null;
get singleCopiableCard() {
const cardItems: CipherItem[] = [

Check warning on line 60 in apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts#L60

Added line #L60 was not covered by tests
{ value: this.cipher.card.code, key: "code" },
{ value: this.cipher.card.number, key: "number" },
];
return this.findSingleCopiableItem(cardItems);

Check warning on line 64 in apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts#L64

Added line #L64 was not covered by tests
}

get singleCopiableIdentityValue() {
const { fullAddressForCopy, email, username, phone } = this.cipher.identity;
if (fullAddressForCopy && !email && !username && !phone) {
return fullAddressForCopy;
}
if (!fullAddressForCopy && email && !username && !phone) {
return email;
}
if (!fullAddressForCopy && !email && username && !phone) {
return username;
}
if (!fullAddressForCopy && !email && !username && phone) {
return phone;
}
return null;
get singleCopiableIdentity() {
const identityItems: CipherItem[] = [

Check warning on line 68 in apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts#L68

Added line #L68 was not covered by tests
{ value: this.cipher.identity.fullAddressForCopy, key: "address" },
{ value: this.cipher.identity.email, key: "email" },
{ value: this.cipher.identity.username, key: "username" },
{ value: this.cipher.identity.phone, key: "phone" },
];
return this.findSingleCopiableItem(identityItems);

Check warning on line 74 in apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts#L74

Added line #L74 was not covered by tests
}

/*
* Given a list of CipherItems, if there is only one item with a value,
* return it with the translated key. Otherwise return null
*/
findSingleCopiableItem(items: { value: string; key: string }[]): CipherItem | null {
const singleItemWithValue = items.find(

Check warning on line 82 in apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts

View check run for this annotation

Codecov / codecov/patch

apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts#L82

Added line #L82 was not covered by tests
(key) => key.value && items.every((f) => f === key || !f.value),
);
return singleItemWithValue
? { value: singleItemWithValue.value, key: this.i18nService.t(singleItemWithValue.key) }
: null;
}

get hasCardValues() {
Expand Down Expand Up @@ -120,5 +112,5 @@ export class ItemCopyActionsComponent {
);
}

constructor() {}
constructor(private i18nService: I18nService) {}
}

0 comments on commit 2b11013

Please sign in to comment.