mirror of
https://github.com/bitwarden/browser
synced 2025-12-13 14:53:33 +00:00
show toasts on copying, resolves #28
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import * as template from './password-generator-history.component.html';
|
import * as template from './password-generator-history.component.html';
|
||||||
|
|
||||||
|
import { ToasterService } from 'angular2-toaster';
|
||||||
import { Angulartics2 } from 'angulartics2';
|
import { Angulartics2 } from 'angulartics2';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -8,6 +9,7 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
|
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
|
||||||
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||||
|
|
||||||
import { PasswordHistory } from 'jslib/models/domain/passwordHistory';
|
import { PasswordHistory } from 'jslib/models/domain/passwordHistory';
|
||||||
@@ -17,10 +19,11 @@ import { PasswordHistory } from 'jslib/models/domain/passwordHistory';
|
|||||||
template: template,
|
template: template,
|
||||||
})
|
})
|
||||||
export class PasswordGeneratorHistoryComponent implements OnInit {
|
export class PasswordGeneratorHistoryComponent implements OnInit {
|
||||||
history: PasswordHistory[];
|
history: PasswordHistory[] = [];
|
||||||
|
|
||||||
constructor(private passwordGenerationService: PasswordGenerationService, private analytics: Angulartics2,
|
constructor(private passwordGenerationService: PasswordGenerationService, private analytics: Angulartics2,
|
||||||
private platformUtilsService: PlatformUtilsService) { }
|
private platformUtilsService: PlatformUtilsService, private i18nService: I18nService,
|
||||||
|
private toasterService: ToasterService) { }
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.history = await this.passwordGenerationService.getHistory();
|
this.history = await this.passwordGenerationService.getHistory();
|
||||||
@@ -34,5 +37,6 @@ export class PasswordGeneratorHistoryComponent implements OnInit {
|
|||||||
copy(password: string) {
|
copy(password: string) {
|
||||||
this.analytics.eventTrack.next({ action: 'Copied Historical Password' });
|
this.analytics.eventTrack.next({ action: 'Copied Historical Password' });
|
||||||
this.platformUtilsService.copyToClipboard(password);
|
this.platformUtilsService.copyToClipboard(password);
|
||||||
|
this.toasterService.popAsync('info', null, this.i18nService.t('valueCopied', this.i18nService.t('password')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
|
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
|
||||||
|
import { I18nService } from 'jslib/abstractions/i18n.service';
|
||||||
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -28,7 +29,8 @@ export class PasswordGeneratorComponent implements OnInit {
|
|||||||
avoidAmbiguous = false;
|
avoidAmbiguous = false;
|
||||||
|
|
||||||
constructor(private passwordGenerationService: PasswordGenerationService, private analytics: Angulartics2,
|
constructor(private passwordGenerationService: PasswordGenerationService, private analytics: Angulartics2,
|
||||||
private platformUtilsService: PlatformUtilsService) { }
|
private platformUtilsService: PlatformUtilsService, private i18nService: I18nService,
|
||||||
|
private toasterService: ToasterService) { }
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.options = await this.passwordGenerationService.getOptions();
|
this.options = await this.passwordGenerationService.getOptions();
|
||||||
@@ -73,6 +75,7 @@ export class PasswordGeneratorComponent implements OnInit {
|
|||||||
copy() {
|
copy() {
|
||||||
this.analytics.eventTrack.next({ action: 'Copied Generated Password' });
|
this.analytics.eventTrack.next({ action: 'Copied Generated Password' });
|
||||||
this.platformUtilsService.copyToClipboard(this.password);
|
this.platformUtilsService.copyToClipboard(this.password);
|
||||||
|
this.toasterService.popAsync('info', null, this.i18nService.t('valueCopied', this.i18nService.t('password')));
|
||||||
}
|
}
|
||||||
|
|
||||||
select() {
|
select() {
|
||||||
|
|||||||
@@ -212,21 +212,15 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
const menu = new remote.Menu();
|
const menu = new remote.Menu();
|
||||||
menu.append(new remote.MenuItem({
|
menu.append(new remote.MenuItem({
|
||||||
label: this.i18nService.t('view'),
|
label: this.i18nService.t('view'),
|
||||||
click: () => {
|
click: () => this.functionWithChangeDetection(() => {
|
||||||
this.ngZone.run(async () => {
|
this.viewCipher(cipher);
|
||||||
this.viewCipher(cipher);
|
}),
|
||||||
this.changeDetectorRef.detectChanges();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
menu.append(new remote.MenuItem({
|
menu.append(new remote.MenuItem({
|
||||||
label: this.i18nService.t('edit'),
|
label: this.i18nService.t('edit'),
|
||||||
click: () => {
|
click: () => this.functionWithChangeDetection(() => {
|
||||||
this.ngZone.run(async () => {
|
this.editCipher(cipher);
|
||||||
this.editCipher(cipher);
|
}),
|
||||||
this.changeDetectorRef.detectChanges();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
switch (cipher.type) {
|
switch (cipher.type) {
|
||||||
@@ -243,13 +237,13 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
if (cipher.login.username != null) {
|
if (cipher.login.username != null) {
|
||||||
menu.append(new remote.MenuItem({
|
menu.append(new remote.MenuItem({
|
||||||
label: this.i18nService.t('copyUsername'),
|
label: this.i18nService.t('copyUsername'),
|
||||||
click: () => this.platformUtilsService.copyToClipboard(cipher.login.username),
|
click: () => this.copyValue(cipher.login.username, 'username'),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
if (cipher.login.password != null) {
|
if (cipher.login.password != null) {
|
||||||
menu.append(new remote.MenuItem({
|
menu.append(new remote.MenuItem({
|
||||||
label: this.i18nService.t('copyPassword'),
|
label: this.i18nService.t('copyPassword'),
|
||||||
click: () => this.platformUtilsService.copyToClipboard(cipher.login.password),
|
click: () => this.copyValue(cipher.login.password, 'password'),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -260,13 +254,13 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
if (cipher.card.number != null) {
|
if (cipher.card.number != null) {
|
||||||
menu.append(new remote.MenuItem({
|
menu.append(new remote.MenuItem({
|
||||||
label: this.i18nService.t('copyNumber'),
|
label: this.i18nService.t('copyNumber'),
|
||||||
click: () => this.platformUtilsService.copyToClipboard(cipher.card.number),
|
click: () => this.copyValue(cipher.card.number, 'number'),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
if (cipher.card.code != null) {
|
if (cipher.card.code != null) {
|
||||||
menu.append(new remote.MenuItem({
|
menu.append(new remote.MenuItem({
|
||||||
label: this.i18nService.t('copySecurityCode'),
|
label: this.i18nService.t('copySecurityCode'),
|
||||||
click: () => this.platformUtilsService.copyToClipboard(cipher.card.code),
|
click: () => this.copyValue(cipher.card.code, 'securityCode'),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -488,8 +482,20 @@ export class VaultComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private addCipherWithChangeDetection(type: CipherType = null) {
|
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.ngZone.run(async () => {
|
||||||
this.addCipher(type);
|
func();
|
||||||
this.changeDetectorRef.detectChanges();
|
this.changeDetectorRef.detectChanges();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<i class="fa fa-lg fa-share-square-o"></i>
|
<i class="fa fa-lg fa-share-square-o"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}"
|
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}"
|
||||||
(click)="copy(cipher.login.uri, 'URI')">
|
(click)="copy(cipher.login.uri, 'uri', 'URI')">
|
||||||
<i class="fa fa-lg fa-clipboard"></i>
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick title="{{'copyUsername' | i18n}}"
|
<a class="row-btn" href="#" appStopClick title="{{'copyUsername' | i18n}}"
|
||||||
(click)="copy(cipher.login.username, 'Username')">
|
(click)="copy(cipher.login.username, 'username', 'Username')">
|
||||||
<i class="fa fa-lg fa-clipboard"></i>
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="row-btn" href="#" appStopClick title="{{'copyPassword' | i18n}}"
|
<a class="row-btn" href="#" appStopClick title="{{'copyPassword' | i18n}}"
|
||||||
(click)="copy(cipher.login.password, 'Password')">
|
(click)="copy(cipher.login.password, 'password', 'Password')">
|
||||||
<i class="fa fa-lg fa-clipboard"></i>
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}"
|
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}"
|
||||||
(click)="copy(totpCode, 'TOTP')">
|
(click)="copy(totpCode, 'verificationCodeTotp', 'TOTP')">
|
||||||
<i class="fa fa-lg fa-clipboard"></i>
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -95,7 +95,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick title="{{'copyNumber' | i18n}}"
|
<a class="row-btn" href="#" appStopClick title="{{'copyNumber' | i18n}}"
|
||||||
(click)="copy(cipher.card.number, 'Number')">
|
(click)="copy(cipher.card.number, 'number', 'Number')">
|
||||||
<i class="fa fa-lg fa-clipboard"></i>
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
<a class="row-btn" href="#" appStopClick title="{{'copySecurityCode' | i18n}}"
|
<a class="row-btn" href="#" appStopClick title="{{'copySecurityCode' | i18n}}"
|
||||||
(click)="copy(cipher.card.code, 'Security Code')">
|
(click)="copy(cipher.card.code, 'securityCode', 'Security Code')">
|
||||||
<i class="fa fa-lg fa-clipboard"></i>
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -207,7 +207,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}"
|
<a class="row-btn" href="#" appStopClick title="{{'copyValue' | i18n}}"
|
||||||
*ngIf="field.value && field.type !== fieldType.Boolean"
|
*ngIf="field.value && field.type !== fieldType.Boolean"
|
||||||
(click)="copy(field.value, 'Field')">
|
(click)="copy(field.value, 'value', 'Field')">
|
||||||
<i class="fa fa-lg fa-clipboard"></i>
|
<i class="fa fa-lg fa-clipboard"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -96,13 +96,15 @@ export class ViewComponent implements OnChanges, OnDestroy {
|
|||||||
this.platformUtilsService.launchUri(this.cipher.login.uri);
|
this.platformUtilsService.launchUri(this.cipher.login.uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(value: string, aType: string) {
|
copy(value: string, typeI18nKey: string, aType: string) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.analytics.eventTrack.next({ action: 'Copied ' + aType });
|
this.analytics.eventTrack.next({ action: 'Copied ' + aType });
|
||||||
this.platformUtilsService.copyToClipboard(value);
|
this.platformUtilsService.copyToClipboard(value);
|
||||||
|
this.toasterService.popAsync('info', null,
|
||||||
|
this.i18nService.t('valueCopied', this.i18nService.t(typeI18nKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
async downloadAttachment(attachment: AttachmentView) {
|
async downloadAttachment(attachment: AttachmentView) {
|
||||||
|
|||||||
@@ -941,5 +941,15 @@
|
|||||||
},
|
},
|
||||||
"quitBitwarden": {
|
"quitBitwarden": {
|
||||||
"message": "Quit Bitwarden"
|
"message": "Quit Bitwarden"
|
||||||
|
},
|
||||||
|
"valueCopied": {
|
||||||
|
"message": "$VALUE$ copied",
|
||||||
|
"description": "Value has been copied to the clipboard.",
|
||||||
|
"placeholders": {
|
||||||
|
"value": {
|
||||||
|
"content": "$1",
|
||||||
|
"example": "Password"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user