diff --git a/src/app/vault/password-generator-history.component.ts b/src/app/vault/password-generator-history.component.ts index c8978c25fc..069dc20d37 100644 --- a/src/app/vault/password-generator-history.component.ts +++ b/src/app/vault/password-generator-history.component.ts @@ -1,5 +1,6 @@ import * as template from './password-generator-history.component.html'; +import { ToasterService } from 'angular2-toaster'; import { Angulartics2 } from 'angulartics2'; import { @@ -8,6 +9,7 @@ import { } from '@angular/core'; import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; import { PasswordHistory } from 'jslib/models/domain/passwordHistory'; @@ -17,10 +19,11 @@ import { PasswordHistory } from 'jslib/models/domain/passwordHistory'; template: template, }) export class PasswordGeneratorHistoryComponent implements OnInit { - history: PasswordHistory[]; + history: PasswordHistory[] = []; constructor(private passwordGenerationService: PasswordGenerationService, private analytics: Angulartics2, - private platformUtilsService: PlatformUtilsService) { } + private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, + private toasterService: ToasterService) { } async ngOnInit() { this.history = await this.passwordGenerationService.getHistory(); @@ -34,5 +37,6 @@ export class PasswordGeneratorHistoryComponent implements OnInit { copy(password: string) { this.analytics.eventTrack.next({ action: 'Copied Historical Password' }); this.platformUtilsService.copyToClipboard(password); + this.toasterService.popAsync('info', null, this.i18nService.t('valueCopied', this.i18nService.t('password'))); } } diff --git a/src/app/vault/password-generator.component.ts b/src/app/vault/password-generator.component.ts index bbbcf42229..1db13c791b 100644 --- a/src/app/vault/password-generator.component.ts +++ b/src/app/vault/password-generator.component.ts @@ -12,6 +12,7 @@ import { } from '@angular/core'; import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; @Component({ @@ -28,7 +29,8 @@ export class PasswordGeneratorComponent implements OnInit { avoidAmbiguous = false; constructor(private passwordGenerationService: PasswordGenerationService, private analytics: Angulartics2, - private platformUtilsService: PlatformUtilsService) { } + private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, + private toasterService: ToasterService) { } async ngOnInit() { this.options = await this.passwordGenerationService.getOptions(); @@ -73,6 +75,7 @@ export class PasswordGeneratorComponent implements OnInit { copy() { this.analytics.eventTrack.next({ action: 'Copied Generated Password' }); this.platformUtilsService.copyToClipboard(this.password); + this.toasterService.popAsync('info', null, this.i18nService.t('valueCopied', this.i18nService.t('password'))); } select() { diff --git a/src/app/vault/vault.component.ts b/src/app/vault/vault.component.ts index 203daad21b..ad56168414 100644 --- a/src/app/vault/vault.component.ts +++ b/src/app/vault/vault.component.ts @@ -212,21 +212,15 @@ export class VaultComponent implements OnInit, OnDestroy { const menu = new remote.Menu(); menu.append(new remote.MenuItem({ label: this.i18nService.t('view'), - click: () => { - this.ngZone.run(async () => { - this.viewCipher(cipher); - this.changeDetectorRef.detectChanges(); - }); - }, + click: () => this.functionWithChangeDetection(() => { + this.viewCipher(cipher); + }), })); menu.append(new remote.MenuItem({ label: this.i18nService.t('edit'), - click: () => { - this.ngZone.run(async () => { - this.editCipher(cipher); - this.changeDetectorRef.detectChanges(); - }); - }, + click: () => this.functionWithChangeDetection(() => { + this.editCipher(cipher); + }), })); switch (cipher.type) { @@ -243,13 +237,13 @@ export class VaultComponent implements OnInit, OnDestroy { if (cipher.login.username != null) { menu.append(new remote.MenuItem({ label: this.i18nService.t('copyUsername'), - click: () => this.platformUtilsService.copyToClipboard(cipher.login.username), + click: () => this.copyValue(cipher.login.username, 'username'), })); } if (cipher.login.password != null) { menu.append(new remote.MenuItem({ label: this.i18nService.t('copyPassword'), - click: () => this.platformUtilsService.copyToClipboard(cipher.login.password), + click: () => this.copyValue(cipher.login.password, 'password'), })); } break; @@ -260,13 +254,13 @@ export class VaultComponent implements OnInit, OnDestroy { if (cipher.card.number != null) { menu.append(new remote.MenuItem({ label: this.i18nService.t('copyNumber'), - click: () => this.platformUtilsService.copyToClipboard(cipher.card.number), + click: () => this.copyValue(cipher.card.number, 'number'), })); } if (cipher.card.code != null) { menu.append(new remote.MenuItem({ label: this.i18nService.t('copySecurityCode'), - click: () => this.platformUtilsService.copyToClipboard(cipher.card.code), + click: () => this.copyValue(cipher.card.code, 'securityCode'), })); } break; @@ -488,8 +482,20 @@ export class VaultComponent implements OnInit, OnDestroy { } private addCipherWithChangeDetection(type: CipherType = null) { + this.functionWithChangeDetection(() => this.addCipher(type)); + } + + private copyValue(value: string, labelI18nKey: string) { + this.functionWithChangeDetection(() => { + this.platformUtilsService.copyToClipboard(value); + this.toasterService.popAsync('info', null, + this.i18nService.t('valueCopied', this.i18nService.t(labelI18nKey))); + }); + } + + private functionWithChangeDetection(func: Function) { this.ngZone.run(async () => { - this.addCipher(type); + func(); this.changeDetectorRef.detectChanges(); }); } diff --git a/src/app/vault/view.component.html b/src/app/vault/view.component.html index d58923953d..5a0b0ddbdc 100644 --- a/src/app/vault/view.component.html +++ b/src/app/vault/view.component.html @@ -23,7 +23,7 @@ + (click)="copy(cipher.login.uri, 'uri', 'URI')"> @@ -35,7 +35,7 @@
+ (click)="copy(cipher.login.username, 'username', 'Username')">
@@ -53,7 +53,7 @@ [ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"> + (click)="copy(cipher.login.password, 'password', 'Password')"> @@ -76,7 +76,7 @@
+ (click)="copy(totpCode, 'verificationCodeTotp', 'TOTP')">
@@ -95,7 +95,7 @@
+ (click)="copy(cipher.card.number, 'number', 'Number')">
@@ -115,7 +115,7 @@
+ (click)="copy(cipher.card.code, 'securityCode', 'Security Code')">
@@ -207,7 +207,7 @@ + (click)="copy(field.value, 'value', 'Field')"> diff --git a/src/app/vault/view.component.ts b/src/app/vault/view.component.ts index 3867420c7d..f76497e532 100644 --- a/src/app/vault/view.component.ts +++ b/src/app/vault/view.component.ts @@ -96,13 +96,15 @@ export class ViewComponent implements OnChanges, OnDestroy { this.platformUtilsService.launchUri(this.cipher.login.uri); } - copy(value: string, aType: string) { + copy(value: string, typeI18nKey: string, aType: string) { if (value == null) { return; } this.analytics.eventTrack.next({ action: 'Copied ' + aType }); this.platformUtilsService.copyToClipboard(value); + this.toasterService.popAsync('info', null, + this.i18nService.t('valueCopied', this.i18nService.t(typeI18nKey))); } async downloadAttachment(attachment: AttachmentView) { diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 6ba5459ff7..3a973ec2c3 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -941,5 +941,15 @@ }, "quitBitwarden": { "message": "Quit Bitwarden" + }, + "valueCopied": { + "message": "$VALUE$ copied", + "description": "Value has been copied to the clipboard.", + "placeholders": { + "value": { + "content": "$1", + "example": "Password" + } + } } }