mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 06:13:38 +00:00
[PM-13667] Add button to open credential history on web (#12100)
* Create CredentialGeneratorHistoryDialogComponent to be re-used on web and desktop * Add button to open credential histpry on web * Add button to open credential history on desktop (#12101) - Register route to open new CredentialGeneratorHistoryDialogComponent when FeatureFlag/GeneratorToolsModernization is enabled - Add button to credential generator - Add missing keys to en/messages.json Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com> --------- Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
7eb18b8e1a
commit
03aa4fd4d8
@@ -62,6 +62,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi
|
|||||||
import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
import { DialogService, ToastOptions, ToastService } from "@bitwarden/components";
|
import { DialogService, ToastOptions, ToastService } from "@bitwarden/components";
|
||||||
|
import { CredentialGeneratorHistoryDialogComponent } from "@bitwarden/generator-components";
|
||||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||||
import { KeyService, BiometricStateService } from "@bitwarden/key-management";
|
import { KeyService, BiometricStateService } from "@bitwarden/key-management";
|
||||||
|
|
||||||
@@ -324,10 +325,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
await this.deleteAccount();
|
await this.deleteAccount();
|
||||||
break;
|
break;
|
||||||
case "openPasswordHistory":
|
case "openPasswordHistory":
|
||||||
await this.openModal<PasswordGeneratorHistoryComponent>(
|
await this.openGeneratorHistory();
|
||||||
PasswordGeneratorHistoryComponent,
|
|
||||||
this.passwordHistoryRef,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case "showToast":
|
case "showToast":
|
||||||
this.toastService._showToast(message);
|
this.toastService._showToast(message);
|
||||||
@@ -558,6 +556,21 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async openGeneratorHistory() {
|
||||||
|
const isGeneratorSwapEnabled = await this.configService.getFeatureFlag(
|
||||||
|
FeatureFlag.GeneratorToolsModernization,
|
||||||
|
);
|
||||||
|
if (isGeneratorSwapEnabled) {
|
||||||
|
await this.dialogService.open(CredentialGeneratorHistoryDialogComponent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.openModal<PasswordGeneratorHistoryComponent>(
|
||||||
|
PasswordGeneratorHistoryComponent,
|
||||||
|
this.passwordHistoryRef,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private async updateAppMenu() {
|
private async updateAppMenu() {
|
||||||
let updateRequest: MenuUpdateRequest;
|
let updateRequest: MenuUpdateRequest;
|
||||||
const stateAccounts = await firstValueFrom(this.accountService.accounts$);
|
const stateAccounts = await firstValueFrom(this.accountService.accounts$);
|
||||||
|
|||||||
@@ -2,6 +2,18 @@
|
|||||||
<span bitDialogTitle>{{ "generator" | i18n }}</span>
|
<span bitDialogTitle>{{ "generator" | i18n }}</span>
|
||||||
<ng-container bitDialogContent>
|
<ng-container bitDialogContent>
|
||||||
<tools-credential-generator />
|
<tools-credential-generator />
|
||||||
|
<bit-item>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
bitLink
|
||||||
|
bit-item-content
|
||||||
|
aria-haspopup="true"
|
||||||
|
(click)="openHistoryDialog()"
|
||||||
|
>
|
||||||
|
{{ "generatorHistory" | i18n }}
|
||||||
|
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</bit-item>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container bitDialogFooter>
|
<ng-container bitDialogFooter>
|
||||||
<button type="button" bitButton bitFormButton buttonType="secondary" bitDialogClose>
|
<button type="button" bitButton bitFormButton buttonType="secondary" bitDialogClose>
|
||||||
|
|||||||
@@ -1,13 +1,37 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { ButtonModule, DialogModule } from "@bitwarden/components";
|
import {
|
||||||
import { GeneratorModule } from "@bitwarden/generator-components";
|
ButtonModule,
|
||||||
|
DialogModule,
|
||||||
|
DialogService,
|
||||||
|
ItemModule,
|
||||||
|
LinkModule,
|
||||||
|
} from "@bitwarden/components";
|
||||||
|
import {
|
||||||
|
CredentialGeneratorHistoryDialogComponent,
|
||||||
|
GeneratorModule,
|
||||||
|
} from "@bitwarden/generator-components";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
standalone: true,
|
||||||
selector: "credential-generator",
|
selector: "credential-generator",
|
||||||
templateUrl: "credential-generator.component.html",
|
templateUrl: "credential-generator.component.html",
|
||||||
imports: [DialogModule, ButtonModule, JslibModule, GeneratorModule],
|
imports: [
|
||||||
|
DialogModule,
|
||||||
|
ButtonModule,
|
||||||
|
JslibModule,
|
||||||
|
GeneratorModule,
|
||||||
|
ItemModule,
|
||||||
|
ButtonModule,
|
||||||
|
LinkModule,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class CredentialGeneratorComponent {}
|
export class CredentialGeneratorComponent {
|
||||||
|
constructor(private dialogService: DialogService) {}
|
||||||
|
|
||||||
|
openHistoryDialog = () => {
|
||||||
|
// open history dialog
|
||||||
|
this.dialogService.open(CredentialGeneratorHistoryDialogComponent);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1371,6 +1371,15 @@
|
|||||||
"passwordHistory": {
|
"passwordHistory": {
|
||||||
"message": "Password history"
|
"message": "Password history"
|
||||||
},
|
},
|
||||||
|
"generatorHistory": {
|
||||||
|
"message": "Generator history"
|
||||||
|
},
|
||||||
|
"clearGeneratorHistoryTitle": {
|
||||||
|
"message": "Clear generator history"
|
||||||
|
},
|
||||||
|
"cleargGeneratorHistoryDescription": {
|
||||||
|
"message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?"
|
||||||
|
},
|
||||||
"clear": {
|
"clear": {
|
||||||
"message": "Clear",
|
"message": "Clear",
|
||||||
"description": "To clear something out. example: To clear browser history."
|
"description": "To clear something out. example: To clear browser history."
|
||||||
@@ -1378,6 +1387,15 @@
|
|||||||
"noPasswordsInList": {
|
"noPasswordsInList": {
|
||||||
"message": "There are no passwords to list."
|
"message": "There are no passwords to list."
|
||||||
},
|
},
|
||||||
|
"clearHistory": {
|
||||||
|
"message": "Clear history"
|
||||||
|
},
|
||||||
|
"nothingToShow": {
|
||||||
|
"message": "Nothing to show"
|
||||||
|
},
|
||||||
|
"nothingGeneratedRecently": {
|
||||||
|
"message": "You haven't generated anything recently"
|
||||||
|
},
|
||||||
"undo": {
|
"undo": {
|
||||||
"message": "Undo"
|
"message": "Undo"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,4 +2,16 @@
|
|||||||
|
|
||||||
<bit-container>
|
<bit-container>
|
||||||
<tools-credential-generator />
|
<tools-credential-generator />
|
||||||
|
<bit-item>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
bitLink
|
||||||
|
bit-item-content
|
||||||
|
aria-haspopup="true"
|
||||||
|
(click)="openHistoryDialog()"
|
||||||
|
>
|
||||||
|
{{ "generatorHistory" | i18n }}
|
||||||
|
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</bit-item>
|
||||||
</bit-container>
|
</bit-container>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
import { GeneratorModule } from "@bitwarden/generator-components";
|
import { ButtonModule, DialogService, ItemModule, LinkModule } from "@bitwarden/components";
|
||||||
|
import {
|
||||||
|
CredentialGeneratorHistoryDialogComponent,
|
||||||
|
GeneratorModule,
|
||||||
|
} from "@bitwarden/generator-components";
|
||||||
|
|
||||||
import { HeaderModule } from "../../layouts/header/header.module";
|
import { HeaderModule } from "../../layouts/header/header.module";
|
||||||
import { SharedModule } from "../../shared";
|
import { SharedModule } from "../../shared";
|
||||||
@@ -9,6 +13,12 @@ import { SharedModule } from "../../shared";
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
selector: "credential-generator",
|
selector: "credential-generator",
|
||||||
templateUrl: "credential-generator.component.html",
|
templateUrl: "credential-generator.component.html",
|
||||||
imports: [SharedModule, HeaderModule, GeneratorModule],
|
imports: [SharedModule, HeaderModule, GeneratorModule, ItemModule, ButtonModule, LinkModule],
|
||||||
})
|
})
|
||||||
export class CredentialGeneratorComponent {}
|
export class CredentialGeneratorComponent {
|
||||||
|
constructor(private dialogService: DialogService) {}
|
||||||
|
|
||||||
|
openHistoryDialog = () => {
|
||||||
|
this.dialogService.open(CredentialGeneratorHistoryDialogComponent);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1642,9 +1642,27 @@
|
|||||||
"passwordHistory": {
|
"passwordHistory": {
|
||||||
"message": "Password history"
|
"message": "Password history"
|
||||||
},
|
},
|
||||||
|
"generatorHistory": {
|
||||||
|
"message": "Generator history"
|
||||||
|
},
|
||||||
|
"clearGeneratorHistoryTitle": {
|
||||||
|
"message": "Clear generator history"
|
||||||
|
},
|
||||||
|
"cleargGeneratorHistoryDescription": {
|
||||||
|
"message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?"
|
||||||
|
},
|
||||||
"noPasswordsInList": {
|
"noPasswordsInList": {
|
||||||
"message": "There are no passwords to list."
|
"message": "There are no passwords to list."
|
||||||
},
|
},
|
||||||
|
"clearHistory": {
|
||||||
|
"message": "Clear history"
|
||||||
|
},
|
||||||
|
"nothingToShow": {
|
||||||
|
"message": "Nothing to show"
|
||||||
|
},
|
||||||
|
"nothingGeneratedRecently": {
|
||||||
|
"message": "You haven't generated anything recently"
|
||||||
|
},
|
||||||
"clear": {
|
"clear": {
|
||||||
"message": "Clear",
|
"message": "Clear",
|
||||||
"description": "To clear something out. Example: To clear browser history."
|
"description": "To clear something out. Example: To clear browser history."
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<bit-dialog #dialog background="alt">
|
||||||
|
<span bitDialogTitle>{{ "generatorHistory" | i18n }}</span>
|
||||||
|
<ng-container bitDialogContent>
|
||||||
|
<bit-empty-credential-history *ngIf="!(hasHistory$ | async)" style="display: contents" />
|
||||||
|
<bit-credential-generator-history *ngIf="hasHistory$ | async" />
|
||||||
|
</ng-container>
|
||||||
|
<ng-container bitDialogFooter>
|
||||||
|
<button
|
||||||
|
[disabled]="!(hasHistory$ | async)"
|
||||||
|
bitButton
|
||||||
|
type="submit"
|
||||||
|
buttonType="primary"
|
||||||
|
(click)="clear()"
|
||||||
|
>
|
||||||
|
{{ "clearHistory" | i18n }}
|
||||||
|
</button>
|
||||||
|
</ng-container>
|
||||||
|
</bit-dialog>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component } from "@angular/core";
|
||||||
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
|
import { BehaviorSubject, distinctUntilChanged, firstValueFrom, map, switchMap } from "rxjs";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components";
|
||||||
|
import { GeneratorHistoryService } from "@bitwarden/generator-history";
|
||||||
|
|
||||||
|
import { CredentialGeneratorHistoryComponent as CredentialGeneratorHistoryToolsComponent } from "./credential-generator-history.component";
|
||||||
|
import { EmptyCredentialHistoryComponent } from "./empty-credential-history.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "credential-generator-history-dialog.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
ButtonModule,
|
||||||
|
CommonModule,
|
||||||
|
JslibModule,
|
||||||
|
DialogModule,
|
||||||
|
CredentialGeneratorHistoryToolsComponent,
|
||||||
|
EmptyCredentialHistoryComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class CredentialGeneratorHistoryDialogComponent {
|
||||||
|
protected readonly hasHistory$ = new BehaviorSubject<boolean>(false);
|
||||||
|
protected readonly userId$ = new BehaviorSubject<UserId>(null);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private accountService: AccountService,
|
||||||
|
private history: GeneratorHistoryService,
|
||||||
|
private dialogService: DialogService,
|
||||||
|
) {
|
||||||
|
this.accountService.activeAccount$
|
||||||
|
.pipe(
|
||||||
|
takeUntilDestroyed(),
|
||||||
|
map(({ id }) => id),
|
||||||
|
distinctUntilChanged(),
|
||||||
|
)
|
||||||
|
.subscribe(this.userId$);
|
||||||
|
|
||||||
|
this.userId$
|
||||||
|
.pipe(
|
||||||
|
takeUntilDestroyed(),
|
||||||
|
switchMap((id) => id && this.history.credentials$(id)),
|
||||||
|
map((credentials) => credentials.length > 0),
|
||||||
|
)
|
||||||
|
.subscribe(this.hasHistory$);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear = async () => {
|
||||||
|
const confirmed = await this.dialogService.openSimpleDialog({
|
||||||
|
title: { key: "clearGeneratorHistoryTitle" },
|
||||||
|
content: { key: "cleargGeneratorHistoryDescription" },
|
||||||
|
type: "warning",
|
||||||
|
acceptButtonText: { key: "clearHistory" },
|
||||||
|
cancelButtonText: { key: "cancel" },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (confirmed) {
|
||||||
|
await this.history.clear(await firstValueFrom(this.userId$));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export { CredentialGeneratorHistoryComponent } from "./credential-generator-history.component";
|
export { CredentialGeneratorHistoryComponent } from "./credential-generator-history.component";
|
||||||
|
export { CredentialGeneratorHistoryDialogComponent } from "./credential-generator-history-dialog.component";
|
||||||
export { EmptyCredentialHistoryComponent } from "./empty-credential-history.component";
|
export { EmptyCredentialHistoryComponent } from "./empty-credential-history.component";
|
||||||
export { GeneratorModule } from "./generator.module";
|
export { GeneratorModule } from "./generator.module";
|
||||||
|
|||||||
Reference in New Issue
Block a user