mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-13 19:51:37 +01: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:
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 { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { DialogService, ToastOptions, ToastService } from "@bitwarden/components";
|
||||
import { CredentialGeneratorHistoryDialogComponent } from "@bitwarden/generator-components";
|
||||
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
|
||||
import { KeyService, BiometricStateService } from "@bitwarden/key-management";
|
||||
|
||||
@ -324,10 +325,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
await this.deleteAccount();
|
||||
break;
|
||||
case "openPasswordHistory":
|
||||
await this.openModal<PasswordGeneratorHistoryComponent>(
|
||||
PasswordGeneratorHistoryComponent,
|
||||
this.passwordHistoryRef,
|
||||
);
|
||||
await this.openGeneratorHistory();
|
||||
break;
|
||||
case "showToast":
|
||||
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() {
|
||||
let updateRequest: MenuUpdateRequest;
|
||||
const stateAccounts = await firstValueFrom(this.accountService.accounts$);
|
||||
|
@ -2,6 +2,18 @@
|
||||
<span bitDialogTitle>{{ "generator" | i18n }}</span>
|
||||
<ng-container bitDialogContent>
|
||||
<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 bitDialogFooter>
|
||||
<button type="button" bitButton bitFormButton buttonType="secondary" bitDialogClose>
|
||||
|
@ -1,13 +1,37 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { ButtonModule, DialogModule } from "@bitwarden/components";
|
||||
import { GeneratorModule } from "@bitwarden/generator-components";
|
||||
import {
|
||||
ButtonModule,
|
||||
DialogModule,
|
||||
DialogService,
|
||||
ItemModule,
|
||||
LinkModule,
|
||||
} from "@bitwarden/components";
|
||||
import {
|
||||
CredentialGeneratorHistoryDialogComponent,
|
||||
GeneratorModule,
|
||||
} from "@bitwarden/generator-components";
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
selector: "credential-generator",
|
||||
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": {
|
||||
"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": {
|
||||
"message": "Clear",
|
||||
"description": "To clear something out. example: To clear browser history."
|
||||
@ -1378,6 +1387,15 @@
|
||||
"noPasswordsInList": {
|
||||
"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": {
|
||||
"message": "Undo"
|
||||
},
|
||||
|
@ -2,4 +2,16 @@
|
||||
|
||||
<bit-container>
|
||||
<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>
|
||||
|
@ -1,6 +1,10 @@
|
||||
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 { SharedModule } from "../../shared";
|
||||
@ -9,6 +13,12 @@ import { SharedModule } from "../../shared";
|
||||
standalone: true,
|
||||
selector: "credential-generator",
|
||||
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": {
|
||||
"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": {
|
||||
"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": {
|
||||
"message": "Clear",
|
||||
"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 { CredentialGeneratorHistoryDialogComponent } from "./credential-generator-history-dialog.component";
|
||||
export { EmptyCredentialHistoryComponent } from "./empty-credential-history.component";
|
||||
export { GeneratorModule } from "./generator.module";
|
||||
|
Loading…
Reference in New Issue
Block a user