mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-11 10:10:25 +01:00
[PM-8277] - Password generator history component (#10921)
This commit is contained in:
parent
ed4d481e4d
commit
9e45e32c7d
@ -1796,6 +1796,15 @@
|
|||||||
"noPasswordsInList": {
|
"noPasswordsInList": {
|
||||||
"message": "There are no passwords to list."
|
"message": "There are no passwords to list."
|
||||||
},
|
},
|
||||||
|
"clearHistory": {
|
||||||
|
"message": "Clear history"
|
||||||
|
},
|
||||||
|
"noPasswordsToShow": {
|
||||||
|
"message": "No passwords to show"
|
||||||
|
},
|
||||||
|
"noRecentlyGeneratedPassword": {
|
||||||
|
"message": "You haven't generated a password recently"
|
||||||
|
},
|
||||||
"remove": {
|
"remove": {
|
||||||
"message": "Remove"
|
"message": "Remove"
|
||||||
},
|
},
|
||||||
|
@ -53,6 +53,7 @@ import { PremiumV2Component } from "../billing/popup/settings/premium-v2.compone
|
|||||||
import { PremiumComponent } from "../billing/popup/settings/premium.component";
|
import { PremiumComponent } from "../billing/popup/settings/premium.component";
|
||||||
import BrowserPopupUtils from "../platform/popup/browser-popup-utils";
|
import BrowserPopupUtils from "../platform/popup/browser-popup-utils";
|
||||||
import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service";
|
import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service";
|
||||||
|
import { CredentialGeneratorHistoryComponent } from "../tools/popup/generator/credential-generator-history.component";
|
||||||
import { CredentialGeneratorComponent } from "../tools/popup/generator/credential-generator.component";
|
import { CredentialGeneratorComponent } from "../tools/popup/generator/credential-generator.component";
|
||||||
import { GeneratorComponent } from "../tools/popup/generator/generator.component";
|
import { GeneratorComponent } from "../tools/popup/generator/generator.component";
|
||||||
import { PasswordGeneratorHistoryComponent } from "../tools/popup/generator/password-generator-history.component";
|
import { PasswordGeneratorHistoryComponent } from "../tools/popup/generator/password-generator-history.component";
|
||||||
@ -276,12 +277,11 @@ const routes: Routes = [
|
|||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { state: "generator" },
|
data: { state: "generator" },
|
||||||
},
|
},
|
||||||
{
|
...extensionRefreshSwap(PasswordGeneratorHistoryComponent, CredentialGeneratorHistoryComponent, {
|
||||||
path: "generator-history",
|
path: "generator-history",
|
||||||
component: PasswordGeneratorHistoryComponent,
|
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
data: { state: "generator-history" },
|
data: { state: "generator-history" },
|
||||||
},
|
}),
|
||||||
...extensionRefreshSwap(ImportBrowserComponent, ImportBrowserV2Component, {
|
...extensionRefreshSwap(ImportBrowserComponent, ImportBrowserV2Component, {
|
||||||
path: "import",
|
path: "import",
|
||||||
canActivate: [authGuard],
|
canActivate: [authGuard],
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
<popup-page>
|
||||||
|
<popup-header slot="header" [pageTitle]="'passwordHistory' | i18n" showBackButton>
|
||||||
|
<ng-container slot="end">
|
||||||
|
<app-pop-out></app-pop-out>
|
||||||
|
</ng-container>
|
||||||
|
</popup-header>
|
||||||
|
<bit-empty-credential-history *ngIf="!(hasHistory$ | async)" style="display: contents" />
|
||||||
|
<bit-credential-generator-history *ngIf="hasHistory$ | async" />
|
||||||
|
<popup-footer slot="footer">
|
||||||
|
<button
|
||||||
|
[disabled]="!(hasHistory$ | async)"
|
||||||
|
bitButton
|
||||||
|
type="submit"
|
||||||
|
buttonType="primary"
|
||||||
|
(click)="clear()"
|
||||||
|
>
|
||||||
|
{{ "clearHistory" | i18n }}
|
||||||
|
</button>
|
||||||
|
</popup-footer>
|
||||||
|
</popup-page>
|
@ -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, ContainerComponent } from "@bitwarden/components";
|
||||||
|
import {
|
||||||
|
CredentialGeneratorHistoryComponent as CredentialGeneratorHistoryToolsComponent,
|
||||||
|
EmptyCredentialHistoryComponent,
|
||||||
|
} from "@bitwarden/generator-components";
|
||||||
|
import { GeneratorHistoryService } from "@bitwarden/generator-history";
|
||||||
|
|
||||||
|
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
|
||||||
|
import { PopupFooterComponent } from "../../../platform/popup/layout/popup-footer.component";
|
||||||
|
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
|
||||||
|
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-credential-generator-history",
|
||||||
|
templateUrl: "credential-generator-history.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
ButtonModule,
|
||||||
|
CommonModule,
|
||||||
|
ContainerComponent,
|
||||||
|
JslibModule,
|
||||||
|
PopOutComponent,
|
||||||
|
PopupHeaderComponent,
|
||||||
|
PopupPageComponent,
|
||||||
|
CredentialGeneratorHistoryToolsComponent,
|
||||||
|
EmptyCredentialHistoryComponent,
|
||||||
|
PopupFooterComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class CredentialGeneratorHistoryComponent {
|
||||||
|
protected readonly hasHistory$ = new BehaviorSubject<boolean>(false);
|
||||||
|
protected readonly userId$ = new BehaviorSubject<UserId>(null);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private accountService: AccountService,
|
||||||
|
private history: GeneratorHistoryService,
|
||||||
|
) {
|
||||||
|
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 () => {
|
||||||
|
await this.history.clear(await firstValueFrom(this.userId$));
|
||||||
|
};
|
||||||
|
}
|
@ -243,6 +243,10 @@ import { FolderService } from "@bitwarden/common/vault/services/folder/folder.se
|
|||||||
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
|
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
|
||||||
import { VaultSettingsService } from "@bitwarden/common/vault/services/vault-settings/vault-settings.service";
|
import { VaultSettingsService } from "@bitwarden/common/vault/services/vault-settings/vault-settings.service";
|
||||||
import { ToastService } from "@bitwarden/components";
|
import { ToastService } from "@bitwarden/components";
|
||||||
|
import {
|
||||||
|
GeneratorHistoryService,
|
||||||
|
LocalGeneratorHistoryService,
|
||||||
|
} from "@bitwarden/generator-history";
|
||||||
import {
|
import {
|
||||||
legacyPasswordGenerationServiceFactory,
|
legacyPasswordGenerationServiceFactory,
|
||||||
legacyUsernameGenerationServiceFactory,
|
legacyUsernameGenerationServiceFactory,
|
||||||
@ -592,6 +596,11 @@ const safeProviders: SafeProvider[] = [
|
|||||||
StateProvider,
|
StateProvider,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
safeProvider({
|
||||||
|
provide: GeneratorHistoryService,
|
||||||
|
useClass: LocalGeneratorHistoryService,
|
||||||
|
deps: [EncryptService, CryptoServiceAbstraction, StateProvider],
|
||||||
|
}),
|
||||||
safeProvider({
|
safeProvider({
|
||||||
provide: UsernameGenerationServiceAbstraction,
|
provide: UsernameGenerationServiceAbstraction,
|
||||||
useFactory: legacyUsernameGenerationServiceFactory,
|
useFactory: legacyUsernameGenerationServiceFactory,
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
<bit-card *ngFor="let credential of credentials$ | async" class="tw-mb-2">
|
||||||
|
<div class="tw-flex tw-justify-between tw-items-center">
|
||||||
|
<div class="tw-flex tw-flex-col">
|
||||||
|
<h2 bitTypography="h6">{{ credential.credential }}</h2>
|
||||||
|
<span class="tw-text-muted" bitTypography="body1">{{
|
||||||
|
credential.generationDate | date: "medium"
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
bitIconButton="bwi-clone"
|
||||||
|
bitSuffix
|
||||||
|
size="small"
|
||||||
|
type="button"
|
||||||
|
[appCopyClick]="credential.credential"
|
||||||
|
[ariaLabel]="'copyPassword' | i18n"
|
||||||
|
[appA11yTitle]="'copyPassword' | i18n"
|
||||||
|
showToast
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
</bit-card>
|
@ -0,0 +1,58 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { Component } from "@angular/core";
|
||||||
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
|
import { RouterLink } from "@angular/router";
|
||||||
|
import { BehaviorSubject, distinctUntilChanged, 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 {
|
||||||
|
CardComponent,
|
||||||
|
IconButtonModule,
|
||||||
|
NoItemsModule,
|
||||||
|
SectionComponent,
|
||||||
|
SectionHeaderComponent,
|
||||||
|
} from "@bitwarden/components";
|
||||||
|
import { GeneratedCredential, GeneratorHistoryService } from "@bitwarden/generator-history";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: "bit-credential-generator-history",
|
||||||
|
templateUrl: "credential-generator-history.component.html",
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
IconButtonModule,
|
||||||
|
NoItemsModule,
|
||||||
|
JslibModule,
|
||||||
|
RouterLink,
|
||||||
|
CardComponent,
|
||||||
|
SectionComponent,
|
||||||
|
SectionHeaderComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class CredentialGeneratorHistoryComponent {
|
||||||
|
protected readonly userId$ = new BehaviorSubject<UserId>(null);
|
||||||
|
protected readonly credentials$ = new BehaviorSubject<GeneratedCredential[]>([]);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private accountService: AccountService,
|
||||||
|
private history: GeneratorHistoryService,
|
||||||
|
) {
|
||||||
|
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),
|
||||||
|
)
|
||||||
|
.subscribe(this.credentials$);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
<div class="tw-flex tw-flex-col tw-h-full tw-justify-center">
|
||||||
|
<div class="tw-text-center">
|
||||||
|
<bit-icon [icon]="noCredentialsIcon" aria-hidden="true"></bit-icon>
|
||||||
|
<h2 bitTypography="h4" class="tw-mt-3">{{ "noPasswordsToShow" | i18n }}</h2>
|
||||||
|
<div>{{ "noRecentlyGeneratedPassword" | i18n }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,18 @@
|
|||||||
|
import { Component } from "@angular/core";
|
||||||
|
|
||||||
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
|
import { IconModule, TypographyModule } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { NoCredentialsIcon } from "./icons/no-credentials.icon";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: "bit-empty-credential-history",
|
||||||
|
templateUrl: "empty-credential-history.component.html",
|
||||||
|
imports: [JslibModule, IconModule, TypographyModule],
|
||||||
|
})
|
||||||
|
export class EmptyCredentialHistoryComponent {
|
||||||
|
noCredentialsIcon = NoCredentialsIcon;
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
import { svgIcon } from "@bitwarden/components";
|
||||||
|
|
||||||
|
export const NoCredentialsIcon = svgIcon`
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="119" height="114" viewBox="0 0 119 114" fill="none">
|
||||||
|
<g clip-path="url(#clip0_201_7924)">
|
||||||
|
<path fill="#020F66" fill-rule="evenodd" clip-rule="evenodd" d="M35.2098 52.2486C35.9068 52.2486 36.4719 52.8137 36.4719 53.5107V58.2685C36.4719 58.9655 35.9068 59.5306 35.2098 59.5306C34.5128 59.5306 33.9478 58.9655 33.9478 58.2685V53.5107C33.9478 52.8137 34.5128 52.2486 35.2098 52.2486Z" />
|
||||||
|
<path fill="#020F66" fill-rule="evenodd" clip-rule="evenodd" d="M40.9963 56.4125C41.2091 57.0762 40.8437 57.7868 40.18 57.9997L35.5951 59.4703C34.9314 59.6832 34.2208 59.3177 34.0079 58.654C33.795 57.9903 34.1605 57.2797 34.8242 57.0668L39.409 55.5962C40.0727 55.3833 40.7834 55.7487 40.9963 56.4125Z" />
|
||||||
|
<path fill="#020F66" fill-rule="evenodd" clip-rule="evenodd" d="M34.471 57.2455C35.036 56.8374 35.8249 56.9647 36.233 57.5297L39.0445 61.4225C39.4526 61.9876 39.3254 62.7765 38.7603 63.1846C38.1952 63.5927 37.4063 63.4654 36.9982 62.9004L34.1868 59.0076C33.7787 58.4425 33.9059 57.6536 34.471 57.2455Z" />
|
||||||
|
<path fill="#020F66" fill-rule="evenodd" clip-rule="evenodd" d="M35.94 57.2401C36.508 57.6441 36.6411 58.432 36.2371 59.0001L33.4689 62.8928C33.065 63.4609 32.277 63.5939 31.709 63.19C31.141 62.786 31.0079 61.9981 31.4119 61.43L34.1801 57.5373C34.584 56.9692 35.3719 56.8362 35.94 57.2401Z" />
|
||||||
|
<path fill="#020F66" fill-rule="evenodd" clip-rule="evenodd" d="M29.4665 56.4091C29.6812 55.746 30.3929 55.3825 31.056 55.5972L35.5976 57.0679C36.2607 57.2826 36.6242 57.9942 36.4095 58.6573C36.1947 59.3205 35.4831 59.684 34.82 59.4692L30.2784 57.9986C29.6153 57.7839 29.2518 57.0723 29.4665 56.4091Z" />
|
||||||
|
<path fill="#020F66" fill-rule="evenodd" clip-rule="evenodd" d="M50.6932 52.2487C51.3902 52.2487 51.9553 52.8137 51.9553 53.5107V58.2686C51.9553 58.9656 51.3902 59.5306 50.6932 59.5306C49.9962 59.5306 49.4312 58.9656 49.4312 58.2686V53.5107C49.4312 52.8137 49.9962 52.2487 50.6932 52.2487Z" />
|
||||||
|
<path fill="#020F66" fill-rule="evenodd" clip-rule="evenodd" d="M56.4353 56.4088C56.6501 57.072 56.2866 57.7836 55.6234 57.9983L51.0819 59.4689C50.4187 59.6837 49.7071 59.3202 49.4924 58.657C49.2777 57.9939 49.6412 57.2823 50.3043 57.0676L54.8458 55.5969C55.509 55.3822 56.2206 55.7457 56.4353 56.4088Z" />
|
||||||
|
<path fill="#020F66" fill-rule="evenodd" clip-rule="evenodd" d="M49.9544 57.2452C50.5194 56.8371 51.3083 56.9643 51.7164 57.5294L54.5279 61.4221C54.936 61.9872 54.8087 62.7761 54.2437 63.1842C53.6786 63.5923 52.8897 63.4651 52.4816 62.9L49.6702 59.0072C49.2621 58.4422 49.3893 57.6533 49.9544 57.2452Z" />
|
||||||
|
<path fill="#020F66" fill-rule="evenodd" clip-rule="evenodd" d="M51.4331 57.2452C51.9982 57.6533 52.1254 58.4422 51.7173 59.0072L48.9059 62.9C48.4978 63.4651 47.7089 63.5923 47.1438 63.1842C46.5788 62.7761 46.4515 61.9872 46.8596 61.4221L49.6711 57.5294C50.0792 56.9643 50.8681 56.8371 51.4331 57.2452Z" />
|
||||||
|
<path fill="#020F66" fill-rule="evenodd" clip-rule="evenodd" d="M44.9514 56.4088C45.1661 55.7457 45.8777 55.3822 46.5409 55.5969L51.0824 57.0676C51.7455 57.2823 52.109 57.9939 51.8943 58.657C51.6796 59.3202 50.968 59.6837 50.3048 59.4689L45.7633 57.9983C45.1001 57.7836 44.7366 57.072 44.9514 56.4088Z" />
|
||||||
|
<path fill="#020F66" fill-rule="evenodd" clip-rule="evenodd" d="M60.5229 62.3772C60.5229 61.6802 61.088 61.1151 61.785 61.1151H70.7935C71.4905 61.1151 72.0556 61.6802 72.0556 62.3772C72.0556 63.0742 71.4905 63.6392 70.7935 63.6392H61.785C61.088 63.6392 60.5229 63.0742 60.5229 62.3772Z" />
|
||||||
|
<path fill="#020F66" fill-rule="evenodd" clip-rule="evenodd" d="M75.9663 62.3772C75.9663 61.6802 76.5314 61.1151 77.2284 61.1151H86.2369C86.9339 61.1151 87.4989 61.6802 87.4989 62.3772C87.4989 63.0742 86.9339 63.6392 86.2369 63.6392H77.2284C76.5314 63.6392 75.9663 63.0742 75.9663 62.3772Z" />
|
||||||
|
<path fill="#15C0CB" fill-rule="evenodd" clip-rule="evenodd" d="M20.1396 57.9313C20.1396 50.6126 26.0726 44.6796 33.3914 44.6796H86.3982C93.7169 44.6796 99.6499 50.6126 99.6499 57.9313C99.6499 65.25 93.7169 71.183 86.3982 71.183H33.3914C26.0726 71.183 20.1396 65.25 20.1396 57.9313ZM33.3914 47.2037C27.4667 47.2037 22.6638 52.0066 22.6638 57.9313C22.6638 63.856 27.4667 68.6589 33.3914 68.6589H86.3982C92.3229 68.6589 97.1258 63.856 97.1258 57.9313C97.1258 52.0066 92.3229 47.2037 86.3982 47.2037H33.3914Z"/>
|
||||||
|
<path fill="#020F66" fill-rule="evenodd" clip-rule="evenodd" d="M40.8279 11.8469C41.4764 12.1023 41.7952 12.835 41.5398 13.4836L37.3784 24.0525C37.123 24.701 36.3902 25.0198 35.7417 24.7644C35.0931 24.509 34.7744 23.7762 35.0298 23.1277L38.0204 15.5323C35.2016 16.9889 32.4865 18.7508 29.92 20.8232C9.44808 37.3546 6.25361 67.3517 22.785 87.8236C27.3496 93.4763 32.9382 97.8098 39.0683 100.775C39.6957 101.079 39.9583 101.834 39.6547 102.461C39.3512 103.089 38.5964 103.351 37.969 103.048C31.5107 99.9231 25.6247 95.3579 20.8212 89.4094C3.414 67.8529 6.77771 36.2666 28.3342 18.8594C31.1318 16.6003 34.0994 14.6905 37.1838 13.1248L29.3343 10.0341C28.6857 9.77875 28.367 9.04598 28.6223 8.39742C28.8777 7.74886 29.6105 7.43012 30.259 7.68548L40.8279 11.8469ZM84.1129 15.392C84.4739 14.7958 85.2499 14.6051 85.8462 14.9661C90.6935 17.901 95.1212 21.7125 98.8842 26.3725C116.291 47.929 112.928 79.5153 91.3711 96.9224C90.3117 97.7779 89.2278 98.5834 88.1224 99.339L96.3064 101.382C96.9827 101.551 97.394 102.236 97.2252 102.912C97.0564 103.588 96.3713 104 95.6951 103.831L84.6746 101.08C83.9984 100.911 83.587 100.226 83.7558 99.5498L86.5067 88.5294C86.6755 87.8531 87.3606 87.4417 88.0368 87.6105C88.7131 87.7794 89.1245 88.4644 88.9557 89.1407L86.9784 97.0621C87.9316 96.4005 88.8679 95.6994 89.7853 94.9586C110.257 78.4273 113.452 48.4302 96.9203 27.9583C93.3439 23.5293 89.1393 19.9108 84.5388 17.1253C83.9426 16.7643 83.7519 15.9883 84.1129 15.392Z" />
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_201_7924">
|
||||||
|
<rect width="119" height="114" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
`;
|
@ -1,2 +1,4 @@
|
|||||||
export { PassphraseSettingsComponent } from "./passphrase-settings.component";
|
export { PassphraseSettingsComponent } from "./passphrase-settings.component";
|
||||||
|
export { CredentialGeneratorHistoryComponent } from "./credential-generator-history.component";
|
||||||
|
export { EmptyCredentialHistoryComponent } from "./empty-credential-history.component";
|
||||||
export { PasswordSettingsComponent } from "./password-settings.component";
|
export { PasswordSettingsComponent } from "./password-settings.component";
|
||||||
|
Loading…
Reference in New Issue
Block a user