1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 16:23:44 +00:00

Merge branch 'master' into browser-ext-ui-update-test

This commit is contained in:
DanHillesheim
2022-10-27 09:45:30 -06:00
54 changed files with 270 additions and 509 deletions

View File

@@ -65,13 +65,15 @@ export class LockComponent extends BaseLockComponent {
// eslint-disable-next-line rxjs-angular/prefer-takeuntil // eslint-disable-next-line rxjs-angular/prefer-takeuntil
this.route.queryParams.subscribe((params) => { this.route.queryParams.subscribe((params) => {
if (this.supportsBiometric && params.promptBiometric && autoPromptBiometric) { setTimeout(async () => {
setTimeout(async () => { if (!params.promptBiometric || !this.supportsBiometric || !autoPromptBiometric) {
if (await ipcRenderer.invoke("windowVisible")) { return;
this.unlockBiometric(); }
}
}, 1000); if (await ipcRenderer.invoke("windowVisible")) {
} this.unlockBiometric();
}
}, 1000);
}); });
this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => { this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => {
this.ngZone.run(() => { this.ngZone.run(() => {

View File

@@ -0,0 +1,128 @@
import { Component, Input, OnChanges, OnInit } from "@angular/core";
import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";
import { Utils } from "@bitwarden/common/misc/utils";
@Component({
selector: "app-avatar",
template: `<img
*ngIf="src"
[src]="src"
title="{{ data }}"
[ngClass]="{ 'rounded-circle': circle }"
/>`,
})
export class AvatarComponent implements OnChanges, OnInit {
@Input() size = 45;
@Input() charCount = 2;
@Input() fontSize = 20;
@Input() dynamic = false;
@Input() circle = false;
@Input() color?: string;
@Input() id?: number;
@Input() text?: string;
private svgCharCount = 2;
private svgFontWeight = 300;
src: SafeResourceUrl;
constructor(public sanitizer: DomSanitizer) {}
ngOnInit() {
if (!this.dynamic) {
this.generate();
}
}
ngOnChanges() {
if (this.dynamic) {
this.generate();
}
}
private async generate() {
let chars: string = null;
const upperCaseText = this.text?.toUpperCase() ?? "";
chars = this.getFirstLetters(upperCaseText, this.svgCharCount);
if (chars == null) {
chars = this.unicodeSafeSubstring(upperCaseText, this.svgCharCount);
}
// If the chars contain an emoji, only show it.
if (chars.match(Utils.regexpEmojiPresentation)) {
chars = chars.match(Utils.regexpEmojiPresentation)[0];
}
let svg: HTMLElement;
let hexColor = this.color;
if (this.color != null) {
svg = this.createSvgElement(this.size, hexColor);
} else if (this.id != null) {
hexColor = Utils.stringToColor(this.id.toString());
svg = this.createSvgElement(this.size, hexColor);
} else {
hexColor = Utils.stringToColor(upperCaseText);
svg = this.createSvgElement(this.size, hexColor);
}
const charObj = this.createTextElement(chars, hexColor);
svg.appendChild(charObj);
const html = window.document.createElement("div").appendChild(svg).outerHTML;
const svgHtml = window.btoa(unescape(encodeURIComponent(html)));
this.src = this.sanitizer.bypassSecurityTrustResourceUrl(
"data:image/svg+xml;base64," + svgHtml
);
}
private getFirstLetters(data: string, count: number): string {
const parts = data.split(" ");
if (parts.length > 1) {
let text = "";
for (let i = 0; i < count; i++) {
text += this.unicodeSafeSubstring(parts[i], 1);
}
return text;
}
return null;
}
private createSvgElement(size: number, color: string): HTMLElement {
const svgTag = window.document.createElement("svg");
svgTag.setAttribute("xmlns", "http://www.w3.org/2000/svg");
svgTag.setAttribute("pointer-events", "none");
svgTag.setAttribute("width", size.toString());
svgTag.setAttribute("height", size.toString());
svgTag.style.backgroundColor = color;
svgTag.style.width = size + "px";
svgTag.style.height = size + "px";
return svgTag;
}
private createTextElement(character: string, color: string): HTMLElement {
const textTag = window.document.createElement("text");
textTag.setAttribute("text-anchor", "middle");
textTag.setAttribute("y", "50%");
textTag.setAttribute("x", "50%");
textTag.setAttribute("dy", "0.35em");
textTag.setAttribute("pointer-events", "auto");
textTag.setAttribute("fill", Utils.pickTextColorBasedOnBgColor(color, 135, true));
textTag.setAttribute(
"font-family",
'"Open Sans","Helvetica Neue",Helvetica,Arial,' +
'sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"'
);
textTag.textContent = character;
textTag.style.fontWeight = this.svgFontWeight.toString();
textTag.style.fontSize = this.fontSize + "px";
return textTag;
}
private unicodeSafeSubstring(str: string, count: number) {
const characters = str.match(/./gu);
return characters != null ? characters.slice(0, count).join("") : "";
}
}

View File

@@ -8,17 +8,18 @@
aria-controls="cdk-overlay-container" aria-controls="cdk-overlay-container"
[attr.aria-expanded]="isOpen" [attr.aria-expanded]="isOpen"
> >
<ng-container *ngIf="activeAccountEmail != null; else noActiveAccount"> <ng-container *ngIf="activeAccount?.email != null; else noActiveAccount">
<app-avatar <app-avatar
[data]="activeAccountEmail" [text]="activeAccount.name"
[id]="activeAccount.id"
size="25" size="25"
[circle]="true" [circle]="true"
[fontSize]="14" [fontSize]="14"
[dynamic]="true" [dynamic]="true"
*ngIf="activeAccountEmail != null" *ngIf="activeAccount.email != null"
aria-hidden="true" aria-hidden="true"
></app-avatar> ></app-avatar>
<span>{{ activeAccountEmail }}</span> <span>{{ activeAccount.email }}</span>
</ng-container> </ng-container>
<ng-template #noActiveAccount> <ng-template #noActiveAccount>
<span>{{ "switchAccount" | i18n }}</span> <span>{{ "switchAccount" | i18n }}</span>
@@ -58,7 +59,8 @@
attr.aria-label="{{ 'switchAccount' | i18n }}" attr.aria-label="{{ 'switchAccount' | i18n }}"
> >
<app-avatar <app-avatar
[data]="a.value.profile.email" [text]="a.value.profile.name ?? a.value.profile.email"
[id]="a.value.profile.userId"
size="25" size="25"
[circle]="true" [circle]="true"
[fontSize]="14" [fontSize]="14"
@@ -85,7 +87,7 @@
></i> ></i>
</button> </button>
</div> </div>
<ng-container *ngIf="activeAccountEmail != null"> <ng-container *ngIf="activeAccount?.email != null">
<div class="border" *ngIf="numberOfAccounts > 0"></div> <div class="border" *ngIf="numberOfAccounts > 0"></div>
<ng-container *ngIf="numberOfAccounts < 4"> <ng-container *ngIf="numberOfAccounts < 4">
<button type="button" class="add" routerLink="/login" (click)="addAccount()"> <button type="button" class="add" routerLink="/login" (click)="addAccount()">

View File

@@ -1,14 +1,22 @@
import { animate, state, style, transition, trigger } from "@angular/animations"; import { animate, state, style, transition, trigger } from "@angular/animations";
import { ConnectedPosition } from "@angular/cdk/overlay"; import { ConnectedPosition } from "@angular/cdk/overlay";
import { Component, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { concatMap, Subject, takeUntil } from "rxjs";
import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service";
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
import { StateService } from "@bitwarden/common/abstractions/state.service"; import { StateService } from "@bitwarden/common/abstractions/state.service";
import { TokenService } from "@bitwarden/common/abstractions/token.service";
import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus"; import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus";
import { Utils } from "@bitwarden/common/misc/utils"; import { Utils } from "@bitwarden/common/misc/utils";
import { Account } from "@bitwarden/common/models/domain/account"; import { Account } from "@bitwarden/common/models/domain/account";
type ActiveAccount = {
id: string;
name: string;
email: string;
};
export class SwitcherAccount extends Account { export class SwitcherAccount extends Account {
get serverUrl() { get serverUrl() {
return this.removeWebProtocolFromString( return this.removeWebProtocolFromString(
@@ -48,11 +56,12 @@ export class SwitcherAccount extends Account {
]), ]),
], ],
}) })
// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class AccountSwitcherComponent implements OnInit, OnDestroy {
export class AccountSwitcherComponent implements OnInit { private destroy$ = new Subject<void>();
isOpen = false; isOpen = false;
accounts: { [userId: string]: SwitcherAccount } = {}; accounts: { [userId: string]: SwitcherAccount } = {};
activeAccountEmail: string; activeAccount?: ActiveAccount;
serverUrl: string; serverUrl: string;
authStatus = AuthenticationStatus; authStatus = AuthenticationStatus;
overlayPostition: ConnectedPosition[] = [ overlayPostition: ConnectedPosition[] = [
@@ -65,7 +74,7 @@ export class AccountSwitcherComponent implements OnInit {
]; ];
get showSwitcher() { get showSwitcher() {
const userIsInAVault = !Utils.isNullOrWhitespace(this.activeAccountEmail); const userIsInAVault = !Utils.isNullOrWhitespace(this.activeAccount?.email);
const userIsAddingAnAdditionalAccount = Object.keys(this.accounts).length > 0; const userIsAddingAnAdditionalAccount = Object.keys(this.accounts).length > 0;
return userIsInAVault || userIsAddingAnAdditionalAccount; return userIsInAVault || userIsAddingAnAdditionalAccount;
} }
@@ -81,21 +90,39 @@ export class AccountSwitcherComponent implements OnInit {
constructor( constructor(
private stateService: StateService, private stateService: StateService,
private authService: AuthService, private authService: AuthService,
private messagingService: MessagingService private messagingService: MessagingService,
private tokenService: TokenService
) {} ) {}
async ngOnInit(): Promise<void> { async ngOnInit(): Promise<void> {
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.stateService.accounts
this.stateService.accounts.subscribe(async (accounts: { [userId: string]: Account }) => { .pipe(
for (const userId in accounts) { concatMap(async (accounts: { [userId: string]: Account }) => {
accounts[userId].profile.authenticationStatus = await this.authService.getAuthStatus( for (const userId in accounts) {
userId accounts[userId].profile.authenticationStatus = await this.authService.getAuthStatus(
); userId
} );
}
this.accounts = await this.createSwitcherAccounts(accounts); this.accounts = await this.createSwitcherAccounts(accounts);
this.activeAccountEmail = await this.stateService.getEmail(); try {
}); this.activeAccount = {
id: await this.tokenService.getUserId(),
name: (await this.tokenService.getName()) ?? (await this.tokenService.getEmail()),
email: await this.tokenService.getEmail(),
};
} catch {
this.activeAccount = undefined;
}
}),
takeUntil(this.destroy$)
)
.subscribe();
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
} }
toggle() { toggle() {

View File

@@ -10,6 +10,7 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { JslibModule } from "@bitwarden/angular/jslib.module"; import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AvatarComponent } from "../components/avatar.component";
import { ServicesModule } from "../services/services.module"; import { ServicesModule } from "../services/services.module";
@NgModule({ @NgModule({
@@ -25,6 +26,7 @@ import { ServicesModule } from "../services/services.module";
ScrollingModule, ScrollingModule,
ServicesModule, ServicesModule,
], ],
declarations: [AvatarComponent],
exports: [ exports: [
A11yModule, A11yModule,
BrowserAnimationsModule, BrowserAnimationsModule,
@@ -37,6 +39,7 @@ import { ServicesModule } from "../services/services.module";
ReactiveFormsModule, ReactiveFormsModule,
ScrollingModule, ScrollingModule,
ServicesModule, ServicesModule,
AvatarComponent,
], ],
providers: [DatePipe], providers: [DatePipe],
}) })

View File

@@ -6,12 +6,7 @@
[appA11yTitle]="'organizationPicker' | i18n" [appA11yTitle]="'organizationPicker' | i18n"
[bitMenuTriggerFor]="orgPickerMenu" [bitMenuTriggerFor]="orgPickerMenu"
> >
<app-avatar <bit-avatar [text]="activeOrganization.name" [id]="activeOrganization.id"></bit-avatar>
[data]="activeOrganization.name"
size="45"
[circle]="true"
[dynamic]="true"
></app-avatar>
<div class="tw-flex"> <div class="tw-flex">
<div class="org-name tw-ml-3"> <div class="org-name tw-ml-3">
<span>{{ activeOrganization.name }}</span> <span>{{ activeOrganization.name }}</span>

View File

@@ -54,13 +54,7 @@
*ngIf="name" *ngIf="name"
appStopProp appStopProp
> >
<app-avatar <bit-avatar [text]="name" [id]="userId" size="small"></bit-avatar>
[data]="name"
[email]="email"
size="25"
fontSize="14"
[circle]="true"
></app-avatar>
<div class="tw-ml-2 tw-block tw-overflow-hidden tw-whitespace-nowrap"> <div class="tw-ml-2 tw-block tw-overflow-hidden tw-whitespace-nowrap">
<span>{{ "loggedInAs" | i18n }}</span> <span>{{ "loggedInAs" | i18n }}</span>
<small class="tw-block tw-overflow-hidden tw-whitespace-nowrap tw-text-muted">{{ <small class="tw-block tw-overflow-hidden tw-whitespace-nowrap tw-text-muted">{{

View File

@@ -24,6 +24,7 @@ export class NavbarComponent implements OnInit {
name: string; name: string;
email: string; email: string;
providers: Provider[] = []; providers: Provider[] = [];
userId: string;
organizations$: Observable<Organization[]>; organizations$: Observable<Organization[]>;
constructor( constructor(
@@ -41,6 +42,7 @@ export class NavbarComponent implements OnInit {
async ngOnInit() { async ngOnInit() {
this.name = await this.tokenService.getName(); this.name = await this.tokenService.getName();
this.email = await this.tokenService.getEmail(); this.email = await this.tokenService.getEmail();
this.userId = await this.tokenService.getUserId();
if (this.name == null || this.name.trim() === "") { if (this.name == null || this.name.trim() === "") {
this.name = this.email; this.name = this.email;
} }

View File

@@ -41,14 +41,7 @@
</thead> </thead>
<tr *ngFor="let user of filteredUsers"> <tr *ngFor="let user of filteredUsers">
<td width="30"> <td width="30">
<app-avatar <bit-avatar [text]="user | userName" [id]="user.id" size="small"></bit-avatar>
[data]="user | userName"
[email]="user.email"
size="25"
[circle]="true"
[fontSize]="14"
>
</app-avatar>
</td> </td>
<td> <td>
{{ user.email }} {{ user.email }}
@@ -60,14 +53,7 @@
</tr> </tr>
<tr *ngFor="let user of excludedUsers"> <tr *ngFor="let user of excludedUsers">
<td width="30"> <td width="30">
<app-avatar <bit-avatar [text]="user | userName" [id]="user.id" size="small"></bit-avatar>
[data]="user | userName"
[email]="user.email"
size="25"
[circle]="true"
[fontSize]="14"
>
</app-avatar>
</td> </td>
<td> <td>
{{ user.email }} {{ user.email }}
@@ -89,14 +75,7 @@
</thead> </thead>
<tr *ngFor="let user of filteredUsers"> <tr *ngFor="let user of filteredUsers">
<td width="30"> <td width="30">
<app-avatar <bit-avatar [text]="user | userName" [id]="user.id" size="small"></bit-avatar>
[data]="user | userName"
[email]="user.email"
size="25"
[circle]="true"
[fontSize]="14"
>
</app-avatar>
</td> </td>
<td> <td>
{{ user.email }} {{ user.email }}

View File

@@ -33,14 +33,7 @@
</thead> </thead>
<tr *ngFor="let user of users"> <tr *ngFor="let user of users">
<td width="30"> <td width="30">
<app-avatar <bit-avatar [text]="user | userName" [id]="user.id" size="small"></bit-avatar>
[data]="user | userName"
[email]="user.email"
size="25"
[circle]="true"
[fontSize]="14"
>
</app-avatar>
</td> </td>
<td> <td>
{{ user.email }} {{ user.email }}
@@ -59,14 +52,7 @@
</thead> </thead>
<tr *ngFor="let user of users"> <tr *ngFor="let user of users">
<td width="30"> <td width="30">
<app-avatar <bit-avatar [text]="user | userName" [id]="user.id" size="small"></bit-avatar>
[data]="user | userName"
[email]="user.email"
size="25"
[circle]="true"
[fontSize]="14"
>
</app-avatar>
</td> </td>
<td> <td>
{{ user.email }} {{ user.email }}

View File

@@ -33,14 +33,7 @@
</thead> </thead>
<tr *ngFor="let user of users"> <tr *ngFor="let user of users">
<td width="30"> <td width="30">
<app-avatar <bit-avatar [text]="user | userName" [id]="user.id" size="small"></bit-avatar>
[data]="user | userName"
[email]="user.email"
size="25"
[circle]="true"
[fontSize]="14"
>
</app-avatar>
</td> </td>
<td> <td>
{{ user.email }} {{ user.email }}
@@ -59,14 +52,7 @@
</thead> </thead>
<tr *ngFor="let user of users"> <tr *ngFor="let user of users">
<td width="30"> <td width="30">
<app-avatar <bit-avatar [text]="user | userName" [id]="user.id" size="small"></bit-avatar>
[data]="user | userName"
[email]="user.email"
size="25"
[circle]="true"
[fontSize]="14"
>
</app-avatar>
</td> </td>
<td> <td>
{{ user.email }} {{ user.email }}

View File

@@ -28,13 +28,11 @@
</thead> </thead>
<tr *ngFor="let item of users"> <tr *ngFor="let item of users">
<td width="30"> <td width="30">
<app-avatar <bit-avatar
[data]="item.user | userName" [text]="item.user | userName"
[email]="item.user.email" [id]="item.user.id"
size="25" size="small"
[circle]="true" ></bit-avatar>
[fontSize]="14"
></app-avatar>
</td> </td>
<td> <td>
{{ item.user.email }} {{ item.user.email }}

View File

@@ -101,14 +101,7 @@
/> />
</td> </td>
<td width="30" (click)="check(u)"> <td width="30" (click)="check(u)">
<app-avatar <bit-avatar [text]="u | userName" [id]="u.id" size="small"></bit-avatar>
[data]="u | userName"
[email]="u.email"
size="25"
[circle]="true"
[fontSize]="14"
>
</app-avatar>
</td> </td>
<td> <td>
{{ u.email }} {{ u.email }}

View File

@@ -141,14 +141,7 @@
<input type="checkbox" [(ngModel)]="u.checked" appStopProp /> <input type="checkbox" [(ngModel)]="u.checked" appStopProp />
</td> </td>
<td width="30"> <td width="30">
<app-avatar <bit-avatar [text]="u | userName" [id]="u.userId" size="small"></bit-avatar>
[data]="u | userName"
[email]="u.email"
size="25"
[circle]="true"
[fontSize]="14"
>
</app-avatar>
</td> </td>
<td> <td>
<a href="#" appStopClick (click)="edit(u)">{{ u.email }}</a> <a href="#" appStopClick (click)="edit(u)">{{ u.email }}</a>

View File

@@ -63,7 +63,7 @@
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6">
<app-avatar data="{{ org.name }}" dynamic="true" size="75" fontSize="35"></app-avatar> <bit-avatar [text]="org.name" [id]="org.id" size="large"></bit-avatar>
</div> </div>
</div> </div>
<button type="submit" buttonType="primary" bitButton [loading]="form.loading"> <button type="submit" buttonType="primary" bitButton [loading]="form.loading">

View File

@@ -12,7 +12,7 @@
<tbody> <tbody>
<tr *ngFor="let p of providers"> <tr *ngFor="let p of providers">
<td width="30"> <td width="30">
<app-avatar [data]="p.name" size="25" [circle]="true" [fontSize]="14"></app-avatar> <bit-avatar [text]="p.name" [id]="p.id" size="small"></bit-avatar>
</td> </td>
<td> <td>
<a href="#" [routerLink]="['/providers', p.id]">{{ p.name }}</a> <a href="#" [routerLink]="['/providers', p.id]">{{ p.name }}</a>

View File

@@ -7,7 +7,7 @@
class="tw-flex tw-h-28 tw-bg-background-alt2 tw-text-center tw-text-primary-300" class="tw-flex tw-h-28 tw-bg-background-alt2 tw-text-center tw-text-primary-300"
[ngClass]="{ 'tw-grayscale': disabled }" [ngClass]="{ 'tw-grayscale': disabled }"
> >
<div class="tw-m-auto"><bit-icon [icon]="icon"></bit-icon></div> <div class="tw-m-auto"><bit-icon [icon]="icon" aria-hidden="true"></bit-icon></div>
</div> </div>
<div class="tw-p-5" [ngClass]="{ 'tw-grayscale': disabled }"> <div class="tw-p-5" [ngClass]="{ 'tw-grayscale': disabled }">
<h3 class="tw-mb-4 tw-text-xl tw-font-bold">{{ title }}</h3> <h3 class="tw-mb-4 tw-text-xl tw-font-bold">{{ title }}</h3>

View File

@@ -179,7 +179,7 @@
<span class="sr-only">{{ "loading" | i18n }}</span> <span class="sr-only">{{ "loading" | i18n }}</span>
</ng-container> </ng-container>
<ng-container *ngIf="loaded"> <ng-container *ngIf="loaded">
<img class="no-items-image" aria-hidden="true" /> <bit-icon [icon]="noItemIcon" aria-hidden="true"></bit-icon>
<p>{{ "noSendsInList" | i18n }}</p> <p>{{ "noSendsInList" | i18n }}</p>
<button (click)="addSend()" class="btn btn-outline-primary" [disabled]="disableSend"> <button (click)="addSend()" class="btn btn-outline-primary" [disabled]="disableSend">
<i class="bwi bwi-plus bwi-fw"></i>{{ "createSend" | i18n }} <i class="bwi bwi-plus bwi-fw"></i>{{ "createSend" | i18n }}

View File

@@ -11,6 +11,7 @@ import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.serv
import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { SendService } from "@bitwarden/common/abstractions/send.service"; import { SendService } from "@bitwarden/common/abstractions/send.service";
import { SendView } from "@bitwarden/common/models/view/send.view"; import { SendView } from "@bitwarden/common/models/view/send.view";
import { Icons } from "@bitwarden/components";
import { AddEditComponent } from "./add-edit.component"; import { AddEditComponent } from "./add-edit.component";
@@ -23,6 +24,7 @@ const BroadcasterSubscriptionId = "SendComponent";
export class SendComponent extends BaseSendComponent { export class SendComponent extends BaseSendComponent {
@ViewChild("sendAddEdit", { read: ViewContainerRef, static: true }) @ViewChild("sendAddEdit", { read: ViewContainerRef, static: true })
sendAddEditModalRef: ViewContainerRef; sendAddEditModalRef: ViewContainerRef;
noItemIcon = Icons.Search;
constructor( constructor(
sendService: SendService, sendService: SendService,

View File

@@ -34,14 +34,7 @@
<tbody> <tbody>
<tr *ngFor="let c of trustedContacts; let i = index"> <tr *ngFor="let c of trustedContacts; let i = index">
<td width="30"> <td width="30">
<app-avatar <bit-avatar [text]="c | userName" [id]="c.granteeId" size="small"></bit-avatar>
[data]="c | userName"
[email]="c.email"
size="25"
[circle]="true"
[fontSize]="14"
>
</app-avatar>
</td> </td>
<td> <td>
<a href="#" appStopClick (click)="edit(c)">{{ c.email }}</a> <a href="#" appStopClick (click)="edit(c)">{{ c.email }}</a>
@@ -149,14 +142,7 @@
<tbody> <tbody>
<tr *ngFor="let c of grantedContacts; let i = index"> <tr *ngFor="let c of grantedContacts; let i = index">
<td width="30"> <td width="30">
<app-avatar <bit-avatar [text]="c | userName" [id]="c.grantorId" size="small"></bit-avatar>
[data]="c | userName"
[email]="c.email"
size="25"
[circle]="true"
[fontSize]="14"
>
</app-avatar>
</td> </td>
<td> <td>
<span>{{ c.email }}</span> <span>{{ c.email }}</span>

View File

@@ -90,29 +90,6 @@
</div> </div>
<small class="form-text text-muted">{{ "faviconDesc" | i18n }}</small> <small class="form-text text-muted">{{ "faviconDesc" | i18n }}</small>
</div> </div>
<div class="form-group">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
id="enableGravatars"
name="enableGravatars"
[(ngModel)]="enableGravatars"
/>
<label class="form-check-label" for="enableGravatars">
{{ "enableGravatars" | i18n }}
</label>
<a
href="https://gravatar.com/"
target="_blank"
rel="noopener"
appA11yTitle="{{ 'learnMore' | i18n }}"
>
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
</a>
</div>
<small class="form-text text-muted">{{ "enableGravatarsDesc" | i18n }}</small>
</div>
<div class="form-group"> <div class="form-group">
<div class="form-check"> <div class="form-check">
<input <input

View File

@@ -17,7 +17,6 @@ import { Utils } from "@bitwarden/common/misc/utils";
export class PreferencesComponent implements OnInit { export class PreferencesComponent implements OnInit {
vaultTimeoutAction = "lock"; vaultTimeoutAction = "lock";
enableFavicons: boolean; enableFavicons: boolean;
enableGravatars: boolean;
enableFullWidth: boolean; enableFullWidth: boolean;
theme: ThemeType; theme: ThemeType;
locale: string; locale: string;
@@ -73,7 +72,6 @@ export class PreferencesComponent implements OnInit {
this.vaultTimeout.setValue(await this.vaultTimeoutSettingsService.getVaultTimeout()); this.vaultTimeout.setValue(await this.vaultTimeoutSettingsService.getVaultTimeout());
this.vaultTimeoutAction = await this.stateService.getVaultTimeoutAction(); this.vaultTimeoutAction = await this.stateService.getVaultTimeoutAction();
this.enableFavicons = !(await this.stateService.getDisableFavicon()); this.enableFavicons = !(await this.stateService.getDisableFavicon());
this.enableGravatars = await this.stateService.getEnableGravitars();
this.enableFullWidth = await this.stateService.getEnableFullWidth(); this.enableFullWidth = await this.stateService.getEnableFullWidth();
this.locale = (await this.stateService.getLocale()) ?? null; this.locale = (await this.stateService.getLocale()) ?? null;
@@ -98,7 +96,6 @@ export class PreferencesComponent implements OnInit {
this.vaultTimeoutAction this.vaultTimeoutAction
); );
await this.stateService.setDisableFavicon(!this.enableFavicons); await this.stateService.setDisableFavicon(!this.enableFavicons);
await this.stateService.setEnableGravitars(this.enableGravatars);
await this.stateService.setEnableFullWidth(this.enableFullWidth); await this.stateService.setEnableFullWidth(this.enableFullWidth);
this.messagingService.send("setFullWidth"); this.messagingService.send("setFullWidth");
if (this.theme !== this.startingTheme) { if (this.theme !== this.startingTheme) {

View File

@@ -33,14 +33,7 @@
</div> </div>
<div class="col-6"> <div class="col-6">
<div class="mb-3"> <div class="mb-3">
<app-avatar <bit-avatar [text]="profile | userName" [id]="profile.id" size="large"></bit-avatar>
data="{{ profile | userName }}"
[email]="profile.email"
dynamic="true"
size="75"
fontSize="35"
>
</app-avatar>
</div> </div>
<hr /> <hr />
<p *ngIf="fingerprint"> <p *ngIf="fingerprint">

View File

@@ -8,13 +8,14 @@ import { ToastrModule } from "ngx-toastr";
import { JslibModule } from "@bitwarden/angular/jslib.module"; import { JslibModule } from "@bitwarden/angular/jslib.module";
import { import {
AsyncActionsModule,
AvatarModule,
BadgeModule, BadgeModule,
ButtonModule, ButtonModule,
CalloutModule, CalloutModule,
FormFieldModule, FormFieldModule,
MenuModule,
IconModule, IconModule,
AsyncActionsModule, MenuModule,
} from "@bitwarden/components"; } from "@bitwarden/components";
// Register the locales for the application // Register the locales for the application
@@ -45,6 +46,7 @@ import "./locales";
MenuModule, MenuModule,
FormFieldModule, FormFieldModule,
IconModule, IconModule,
AvatarModule,
], ],
exports: [ exports: [
CommonModule, CommonModule,
@@ -64,6 +66,7 @@ import "./locales";
MenuModule, MenuModule,
FormFieldModule, FormFieldModule,
IconModule, IconModule,
AvatarModule,
], ],
providers: [DatePipe], providers: [DatePipe],
bootstrap: [], bootstrap: [],

View File

@@ -142,7 +142,7 @@
<span class="sr-only">{{ "loading" | i18n }}</span> <span class="sr-only">{{ "loading" | i18n }}</span>
</ng-container> </ng-container>
<ng-container *ngIf="loaded"> <ng-container *ngIf="loaded">
<img class="no-items-image" aria-hidden="true" /> <bit-icon [icon]="noItemIcon" aria-hidden="true"></bit-icon>
<p>{{ "noItemsInList" | i18n }}</p> <p>{{ "noItemsInList" | i18n }}</p>
<button (click)="addCipher()" class="btn btn-outline-primary" *ngIf="showAddNew"> <button (click)="addCipher()" class="btn btn-outline-primary" *ngIf="showAddNew">
<i class="bwi bwi-plus bwi-fw"></i>{{ "addItem" | i18n }} <i class="bwi bwi-plus bwi-fw"></i>{{ "addItem" | i18n }}

View File

@@ -17,6 +17,7 @@ import { CipherType } from "@bitwarden/common/enums/cipherType";
import { EventType } from "@bitwarden/common/enums/eventType"; import { EventType } from "@bitwarden/common/enums/eventType";
import { Organization } from "@bitwarden/common/models/domain/organization"; import { Organization } from "@bitwarden/common/models/domain/organization";
import { CipherView } from "@bitwarden/common/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/models/view/cipher.view";
import { Icons } from "@bitwarden/components";
const MaxCheckedCount = 500; const MaxCheckedCount = 500;
@@ -39,6 +40,7 @@ export class CiphersComponent extends BaseCiphersComponent implements OnDestroy
userHasPremiumAccess = false; userHasPremiumAccess = false;
organizations: Organization[] = []; organizations: Organization[] = [];
profileName: string; profileName: string;
noItemIcon = Icons.Search;
private didScroll = false; private didScroll = false;
private pagedCiphersCount = 0; private pagedCiphersCount = 0;

View File

@@ -1,34 +0,0 @@
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.7">
<g opacity="0.7">
<g clip-path="url(#clip0_44_9647)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.3599 73.2564C43.579 74.4366 47.0654 75.0822 50.7059 75.0822C66.9882 75.0822 80.1876 62.1696 80.1876 46.2411C80.1876 45.8578 80.1804 45.4762 80.1648 45.0966H108.891V84.6672H40.3599V73.2564Z" fill="#6e7689"/>
<path d="M21.5461 46.241C21.5461 62.1696 34.7456 75.0822 51.028 75.0822C67.3104 75.0822 80.5098 62.1696 80.5098 46.241C80.5098 30.3125 67.3104 17.4 51.028 17.4C34.7456 17.4 21.5461 30.3125 21.5461 46.241Z" stroke="#bac0ce" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M35.3603 70.5954C35.3603 69.933 34.823 69.3954 34.1603 69.3954C33.4976 69.3954 32.9603 69.933 32.9603 70.5954H35.3603ZM112.835 40.2387C114.169 40.2387 115.2 41.3027 115.2 42.5698H117.6C117.6 39.9762 115.493 37.8387 112.835 37.8387V40.2387ZM115.2 42.5698V88.6158H117.6V42.5698H115.2ZM115.2 88.6158C115.2 89.9094 114.142 90.9468 112.835 90.9468V93.3468C115.425 93.3468 117.6 91.2774 117.6 88.6158H115.2ZM112.835 90.9468H37.7256V93.3468H112.835V90.9468ZM37.7256 90.9468C36.3913 90.9468 35.3603 89.883 35.3603 88.6158H32.9603C32.9603 91.2096 35.0667 93.3468 37.7256 93.3468V90.9468ZM35.3603 88.6158V70.5954H32.9603V88.6158H35.3603ZM79.8684 40.2387H112.835V37.8387H79.8684V40.2387Z" fill="#bac0ce"/>
<path d="M79.9068 45.2867H109.021V84.8574H40.4873V73.0512" stroke="#bac0ce" stroke-width="2" stroke-linejoin="round"/>
<path d="M57.3565 102.56H93.2046" stroke="#bac0ce" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M68.9544 92.1468V102.56" stroke="#bac0ce" stroke-width="4" stroke-linejoin="round"/>
<path d="M80.553 92.1468V102.56" stroke="#bac0ce" stroke-width="4" stroke-linejoin="round"/>
<path d="M27.4398 64.9452L22.9296 69.4554L5.72134 86.6634C4.54976 87.8352 4.54976 89.7342 5.72133 90.906L6.95929 92.1438C8.13085 93.3156 10.0304 93.3156 11.202 92.1438L28.4102 74.9358L32.9204 70.4256" stroke="#bac0ce" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M101.293 53.1537H85.1784" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
<path d="M101.293 59.1966H90.2142" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
<path d="M85.1784 59.1966H77.625" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
<path d="M101.293 65.2392H94.2426" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
<path d="M88.7034 65.2392H73.0926" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
<path d="M101.293 71.2824H85.1784" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
<path d="M79.6392 71.2824H71.0784" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
<path d="M101.293 77.325H78.6318" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
<path d="M73.0926 77.325H59.9997" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
<path d="M54.4604 77.325H46.4032" stroke="#bac0ce" stroke-width="2" stroke-linecap="round"/>
<path d="M29.1638 33.0108H70.6926C72.0181 33.0108 73.0926 34.0853 73.0926 35.4108V41.6894C73.0926 43.0149 72.0181 44.0894 70.6926 44.0894H29.1638C27.8383 44.0894 26.7638 43.0149 26.7638 41.6894V35.4108C26.7638 34.0853 27.8383 33.0108 29.1638 33.0108Z" stroke="#bac0ce" stroke-width="4"/>
<path d="M22.7354 54.1609H57.0962C58.4217 54.1609 59.4962 55.2354 59.4962 56.5609V62.8392C59.4962 64.1652 58.4217 65.2392 57.0962 65.2392H28.7783" stroke="#bac0ce" stroke-width="4" stroke-linecap="round"/>
<path d="M79.1358 54.1609H72.975C71.6496 54.1609 70.575 55.2354 70.575 56.5609V62.9736C70.575 64.2252 71.5896 65.2392 72.8406 65.2392" stroke="#bac0ce" stroke-width="4" stroke-linecap="round"/>
</g>
</g>
</g>
<defs>
<clipPath id="clip0_44_9647">
<rect width="120" height="120" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -1,34 +0,0 @@
<svg width="120" height="120" viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.7">
<g opacity="0.7">
<g clip-path="url(#clip0_44_9647)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.3599 73.2564C43.579 74.4366 47.0654 75.0822 50.7059 75.0822C66.9882 75.0822 80.1876 62.1696 80.1876 46.2411C80.1876 45.8578 80.1804 45.4762 80.1648 45.0966H108.891V84.6672H40.3599V73.2564Z" fill="#ced4dc"/>
<path d="M21.5461 46.241C21.5461 62.1696 34.7456 75.0822 51.028 75.0822C67.3104 75.0822 80.5098 62.1696 80.5098 46.241C80.5098 30.3125 67.3104 17.4 51.028 17.4C34.7456 17.4 21.5461 30.3125 21.5461 46.241Z" stroke="#6d757e" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M35.3603 70.5954C35.3603 69.933 34.823 69.3954 34.1603 69.3954C33.4976 69.3954 32.9603 69.933 32.9603 70.5954H35.3603ZM112.835 40.2387C114.169 40.2387 115.2 41.3027 115.2 42.5698H117.6C117.6 39.9762 115.493 37.8387 112.835 37.8387V40.2387ZM115.2 42.5698V88.6158H117.6V42.5698H115.2ZM115.2 88.6158C115.2 89.9094 114.142 90.9468 112.835 90.9468V93.3468C115.425 93.3468 117.6 91.2774 117.6 88.6158H115.2ZM112.835 90.9468H37.7256V93.3468H112.835V90.9468ZM37.7256 90.9468C36.3913 90.9468 35.3603 89.883 35.3603 88.6158H32.9603C32.9603 91.2096 35.0667 93.3468 37.7256 93.3468V90.9468ZM35.3603 88.6158V70.5954H32.9603V88.6158H35.3603ZM79.8684 40.2387H112.835V37.8387H79.8684V40.2387Z" fill="#6d757e"/>
<path d="M79.9068 45.2867H109.021V84.8574H40.4873V73.0512" stroke="#6d757e" stroke-width="2" stroke-linejoin="round"/>
<path d="M57.3565 102.56H93.2046" stroke="#6d757e" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M68.9544 92.1468V102.56" stroke="#6d757e" stroke-width="4" stroke-linejoin="round"/>
<path d="M80.553 92.1468V102.56" stroke="#6d757e" stroke-width="4" stroke-linejoin="round"/>
<path d="M27.4398 64.9452L22.9296 69.4554L5.72134 86.6634C4.54976 87.8352 4.54976 89.7342 5.72133 90.906L6.95929 92.1438C8.13085 93.3156 10.0304 93.3156 11.202 92.1438L28.4102 74.9358L32.9204 70.4256" stroke="#6d757e" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M101.293 53.1537H85.1784" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
<path d="M101.293 59.1966H90.2142" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
<path d="M85.1784 59.1966H77.625" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
<path d="M101.293 65.2392H94.2426" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
<path d="M88.7034 65.2392H73.0926" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
<path d="M101.293 71.2824H85.1784" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
<path d="M79.6392 71.2824H71.0784" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
<path d="M101.293 77.325H78.6318" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
<path d="M73.0926 77.325H59.9997" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
<path d="M54.4604 77.325H46.4032" stroke="#6d757e" stroke-width="2" stroke-linecap="round"/>
<path d="M29.1638 33.0108H70.6926C72.0181 33.0108 73.0926 34.0853 73.0926 35.4108V41.6894C73.0926 43.0149 72.0181 44.0894 70.6926 44.0894H29.1638C27.8383 44.0894 26.7638 43.0149 26.7638 41.6894V35.4108C26.7638 34.0853 27.8383 33.0108 29.1638 33.0108Z" stroke="#6d757e" stroke-width="4"/>
<path d="M22.7354 54.1609H57.0962C58.4217 54.1609 59.4962 55.2354 59.4962 56.5609V62.8392C59.4962 64.1652 58.4217 65.2392 57.0962 65.2392H28.7783" stroke="#6d757e" stroke-width="4" stroke-linecap="round"/>
<path d="M79.1358 54.1609H72.975C71.6496 54.1609 70.575 55.2354 70.575 56.5609V62.9736C70.575 64.2252 71.5896 65.2392 72.8406 65.2392" stroke="#6d757e" stroke-width="4" stroke-linecap="round"/>
</g>
</g>
</g>
<defs>
<clipPath id="clip0_44_9647">
<rect width="120" height="120" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -1235,13 +1235,6 @@
"faviconDesc": { "faviconDesc": {
"message": "Show a recognizable image next to each login." "message": "Show a recognizable image next to each login."
}, },
"enableGravatars": {
"message": "Show Gravatars",
"description": "Use avatar images loaded from gravatar.com."
},
"enableGravatarsDesc": {
"message": "Use avatar images loaded from gravatar.com."
},
"enableFullWidth": { "enableFullWidth": {
"message": "Display full width layout", "message": "Display full width layout",
"description": "Allows scaling the web vault UI's width" "description": "Allows scaling the web vault UI's width"

View File

@@ -10,6 +10,4 @@ if (process.env.NODE_ENV === "production") {
} }
// Other polyfills // Other polyfills
require("whatwg-fetch");
require("webcrypto-shim");
require("date-input-polyfill"); require("date-input-polyfill");

View File

@@ -314,10 +314,4 @@ a i.bwi {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
text-align: center; text-align: center;
.no-items-image {
@include themify($themes) {
content: url("../images/search-web" + themed("svgSuffix"));
}
}
} }

View File

@@ -36,8 +36,8 @@
@import "~bootstrap/scss/_close"; @import "~bootstrap/scss/_close";
//@import "~bootstrap/scss/_toasts"; //@import "~bootstrap/scss/_toasts";
@import "~bootstrap/scss/_modal"; @import "~bootstrap/scss/_modal";
@import "~bootstrap/scss/_tooltip"; // @import "~bootstrap/scss/_tooltip";
@import "~bootstrap/scss/_popover"; // @import "~bootstrap/scss/_popover";
// @import "~bootstrap/scss/_carousel"; // @import "~bootstrap/scss/_carousel";
// @import "~bootstrap/scss/_spinners"; // @import "~bootstrap/scss/_spinners";
@import "~bootstrap/scss/_utilities"; @import "~bootstrap/scss/_utilities";

View File

@@ -243,7 +243,6 @@ const devServer =
https://www.paypalobjects.com https://www.paypalobjects.com
https://q.stripe.com https://q.stripe.com
https://haveibeenpwned.com https://haveibeenpwned.com
https://www.gravatar.com
;child-src ;child-src
'self' 'self'
https://js.stripe.com https://js.stripe.com

View File

@@ -23,7 +23,7 @@
<table class="table table-hover table-list"> <table class="table table-hover table-list">
<tr *ngFor="let o of organizations"> <tr *ngFor="let o of organizations">
<td width="30"> <td width="30">
<app-avatar [data]="o.name" size="25" [circle]="true" [fontSize]="14"></app-avatar> <bit-avatar [text]="o.name" [id]="o.id" size="small"></bit-avatar>
</td> </td>
<td> <td>
{{ o.name }} {{ o.name }}

View File

@@ -59,12 +59,7 @@
<tbody> <tbody>
<tr *ngFor="let o of searchedClients"> <tr *ngFor="let o of searchedClients">
<td width="30"> <td width="30">
<app-avatar <bit-avatar [text]="o.organizationName" [id]="o.id" size="small"></bit-avatar>
[data]="o.organizationName"
size="25"
[circle]="true"
[fontSize]="14"
></app-avatar>
</td> </td>
<td> <td>
<a [routerLink]="['/organizations', o.organizationId]">{{ o.organizationName }}</a> <a [routerLink]="['/organizations', o.organizationId]">{{ o.organizationName }}</a>

View File

@@ -124,14 +124,7 @@
<input type="checkbox" [(ngModel)]="u.checked" appStopProp /> <input type="checkbox" [(ngModel)]="u.checked" appStopProp />
</td> </td>
<td width="30"> <td width="30">
<app-avatar <bit-avatar [text]="u | userName" [id]="u.userId" size="small"></bit-avatar>
[data]="u | userName"
[email]="u.email"
size="25"
[circle]="true"
[fontSize]="14"
>
</app-avatar>
</td> </td>
<td> <td>
<a href="#" appStopClick (click)="edit(u)">{{ u.email }}</a> <a href="#" appStopClick (click)="edit(u)">{{ u.email }}</a>

View File

@@ -3,7 +3,7 @@
<div class="container d-flex"> <div class="container d-flex">
<div class="d-flex flex-column"> <div class="d-flex flex-column">
<div class="my-auto d-flex align-items-center pl-1"> <div class="my-auto d-flex align-items-center pl-1">
<app-avatar [data]="provider.name" size="45" [circle]="true"></app-avatar> <bit-avatar [text]="provider.name" [id]="provider.id"></bit-avatar>
<div class="org-name ml-3"> <div class="org-name ml-3">
<span>{{ provider.name }}</span> <span>{{ provider.name }}</span>
<small class="text-muted">{{ "provider" | i18n }}</small> <small class="text-muted">{{ "provider" | i18n }}</small>

View File

@@ -42,7 +42,7 @@
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6">
<app-avatar data="{{ provider.name }}" dynamic="true" size="75" fontSize="35"></app-avatar> <bit-avatar [text]="provider.name" [id]="provider.id" size="large"></bit-avatar>
</div> </div>
</div> </div>
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading"> <button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">

View File

@@ -1,127 +0,0 @@
import { Component, Input, OnChanges, OnInit } from "@angular/core";
import { DomSanitizer } from "@angular/platform-browser";
import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { Utils } from "@bitwarden/common/misc/utils";
@Component({
selector: "app-avatar",
template:
'<img *ngIf="src" [src]="sanitizer.bypassSecurityTrustResourceUrl(src)" title="{{data}}" ' +
"[ngClass]=\"{'rounded-circle': circle}\">",
})
export class AvatarComponent implements OnChanges, OnInit {
@Input() data: string;
@Input() email: string;
@Input() size = 45;
@Input() charCount = 2;
@Input() textColor = "#ffffff";
@Input() fontSize = 20;
@Input() fontWeight = 300;
@Input() dynamic = false;
@Input() circle = false;
src: string;
constructor(
public sanitizer: DomSanitizer,
private cryptoFunctionService: CryptoFunctionService,
private stateService: StateService
) {}
ngOnInit() {
if (!this.dynamic) {
this.generate();
}
}
ngOnChanges() {
if (this.dynamic) {
this.generate();
}
}
private async generate() {
const enableGravatars = await this.stateService.getEnableGravitars();
if (enableGravatars && this.email != null) {
const hashBytes = await this.cryptoFunctionService.hash(
this.email.toLowerCase().trim(),
"md5"
);
const hash = Utils.fromBufferToHex(hashBytes).toLowerCase();
this.src = "https://www.gravatar.com/avatar/" + hash + "?s=" + this.size + "&r=pg&d=retro";
} else {
let chars: string = null;
const upperData = this.data.toUpperCase();
if (this.charCount > 1) {
chars = this.getFirstLetters(upperData, this.charCount);
}
if (chars == null) {
chars = this.unicodeSafeSubstring(upperData, this.charCount);
}
// If the chars contain an emoji, only show it.
if (chars.match(Utils.regexpEmojiPresentation)) {
chars = chars.match(Utils.regexpEmojiPresentation)[0];
}
const charObj = this.getCharText(chars);
const color = Utils.stringToColor(upperData);
const svg = this.getSvg(this.size, color);
svg.appendChild(charObj);
const html = window.document.createElement("div").appendChild(svg).outerHTML;
const svgHtml = window.btoa(unescape(encodeURIComponent(html)));
this.src = "data:image/svg+xml;base64," + svgHtml;
}
}
private getFirstLetters(data: string, count: number): string {
const parts = data.split(" ");
if (parts.length > 1) {
let text = "";
for (let i = 0; i < count; i++) {
text += this.unicodeSafeSubstring(parts[i], 1);
}
return text;
}
return null;
}
private getSvg(size: number, color: string): HTMLElement {
const svgTag = window.document.createElement("svg");
svgTag.setAttribute("xmlns", "http://www.w3.org/2000/svg");
svgTag.setAttribute("pointer-events", "none");
svgTag.setAttribute("width", size.toString());
svgTag.setAttribute("height", size.toString());
svgTag.style.backgroundColor = color;
svgTag.style.width = size + "px";
svgTag.style.height = size + "px";
return svgTag;
}
private getCharText(character: string): HTMLElement {
const textTag = window.document.createElement("text");
textTag.setAttribute("text-anchor", "middle");
textTag.setAttribute("y", "50%");
textTag.setAttribute("x", "50%");
textTag.setAttribute("dy", "0.35em");
textTag.setAttribute("pointer-events", "auto");
textTag.setAttribute("fill", this.textColor);
textTag.setAttribute(
"font-family",
'"Open Sans","Helvetica Neue",Helvetica,Arial,' +
'sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"'
);
textTag.textContent = character;
textTag.style.fontWeight = this.fontWeight.toString();
textTag.style.fontSize = this.fontSize + "px";
return textTag;
}
private unicodeSafeSubstring(str: string, count: number) {
const characters = str.match(/./gu);
return characters != null ? characters.slice(0, count).join("") : "";
}
}

View File

@@ -2,7 +2,6 @@ import { CommonModule, DatePipe } from "@angular/common";
import { NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { AvatarComponent } from "./components/avatar.component";
import { CalloutComponent } from "./components/callout.component"; import { CalloutComponent } from "./components/callout.component";
import { ExportScopeCalloutComponent } from "./components/export-scope-callout.component"; import { ExportScopeCalloutComponent } from "./components/export-scope-callout.component";
import { IconComponent } from "./components/icon.component"; import { IconComponent } from "./components/icon.component";
@@ -46,7 +45,6 @@ import { PasswordStrengthComponent } from "./shared/components/password-strength
A11yTitleDirective, A11yTitleDirective,
ApiActionDirective, ApiActionDirective,
AutofocusDirective, AutofocusDirective,
AvatarComponent,
BoxRowDirective, BoxRowDirective,
CalloutComponent, CalloutComponent,
ColorPasswordCountPipe, ColorPasswordCountPipe,
@@ -74,7 +72,6 @@ import { PasswordStrengthComponent } from "./shared/components/password-strength
A11yTitleDirective, A11yTitleDirective,
ApiActionDirective, ApiActionDirective,
AutofocusDirective, AutofocusDirective,
AvatarComponent,
BitwardenToastModule, BitwardenToastModule,
BoxRowDirective, BoxRowDirective,
CalloutComponent, CalloutComponent,

View File

@@ -37,9 +37,11 @@ const kdf = 0;
const kdfIterations = 10000; const kdfIterations = 10000;
const userId = Utils.newGuid(); const userId = Utils.newGuid();
const masterPasswordHash = "MASTER_PASSWORD_HASH"; const masterPasswordHash = "MASTER_PASSWORD_HASH";
const name = "NAME";
const decodedToken = { const decodedToken = {
sub: userId, sub: userId,
name: name,
email: email, email: email,
premium: false, premium: false,
}; };
@@ -122,6 +124,7 @@ describe("LogInStrategy", () => {
...new AccountProfile(), ...new AccountProfile(),
...{ ...{
userId: userId, userId: userId,
name: name,
email: email, email: email,
hasPremiumPersonally: false, hasPremiumPersonally: false,
kdfIterations: kdfIterations, kdfIterations: kdfIterations,

View File

@@ -173,8 +173,6 @@ export abstract class StateService<T extends Account = Account> {
) => Promise<void>; ) => Promise<void>;
getEnableFullWidth: (options?: StorageOptions) => Promise<boolean>; getEnableFullWidth: (options?: StorageOptions) => Promise<boolean>;
setEnableFullWidth: (value: boolean, options?: StorageOptions) => Promise<void>; setEnableFullWidth: (value: boolean, options?: StorageOptions) => Promise<void>;
getEnableGravitars: (options?: StorageOptions) => Promise<boolean>;
setEnableGravitars: (value: boolean, options?: StorageOptions) => Promise<void>;
getEnableMinimizeToTray: (options?: StorageOptions) => Promise<boolean>; getEnableMinimizeToTray: (options?: StorageOptions) => Promise<boolean>;
setEnableMinimizeToTray: (value: boolean, options?: StorageOptions) => Promise<void>; setEnableMinimizeToTray: (value: boolean, options?: StorageOptions) => Promise<void>;
getEnableStartToTray: (options?: StorageOptions) => Promise<boolean>; getEnableStartToTray: (options?: StorageOptions) => Promise<boolean>;

View File

@@ -105,6 +105,7 @@ export abstract class LogInStrategy {
...new AccountProfile(), ...new AccountProfile(),
...{ ...{
userId: accountInformation.sub, userId: accountInformation.sub,
name: accountInformation.name,
email: accountInformation.email, email: accountInformation.email,
hasPremiumPersonally: accountInformation.premium, hasPremiumPersonally: accountInformation.premium,
kdfIterations: tokenResponse.kdfIterations, kdfIterations: tokenResponse.kdfIterations,

View File

@@ -175,6 +175,7 @@ export class AccountProfile {
apiKeyClientId?: string; apiKeyClientId?: string;
authenticationStatus?: AuthenticationStatus; authenticationStatus?: AuthenticationStatus;
convertAccountToKeyConnector?: boolean; convertAccountToKeyConnector?: boolean;
name?: string;
email?: string; email?: string;
emailVerified?: boolean; emailVerified?: boolean;
entityId?: string; entityId?: string;
@@ -219,7 +220,6 @@ export class AccountSettings {
enableAutoFillOnPageLoad?: boolean; enableAutoFillOnPageLoad?: boolean;
enableBiometric?: boolean; enableBiometric?: boolean;
enableFullWidth?: boolean; enableFullWidth?: boolean;
enableGravitars?: boolean;
environmentUrls: EnvironmentUrls = new EnvironmentUrls(); environmentUrls: EnvironmentUrls = new EnvironmentUrls();
equivalentDomains?: any; equivalentDomains?: any;
minimizeOnCopyToClipboard?: boolean; minimizeOnCopyToClipboard?: boolean;

View File

@@ -1228,27 +1228,6 @@ export class StateService<
); );
} }
async getEnableGravitars(options?: StorageOptions): Promise<boolean> {
return (
(
await this.getAccount(
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
)
)?.settings?.enableGravitars ?? false
);
}
async setEnableGravitars(value: boolean, options?: StorageOptions): Promise<void> {
const account = await this.getAccount(
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
);
account.settings.enableGravitars = value;
await this.saveAccount(
account,
this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())
);
}
async getEnableMinimizeToTray(options?: StorageOptions): Promise<boolean> { async getEnableMinimizeToTray(options?: StorageOptions): Promise<boolean> {
return ( return (
(await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions()))) (await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())))

View File

@@ -61,7 +61,6 @@ const v1Keys: { [key: string]: string } = {
enableBrowserIntegrationFingerprint: "enableBrowserIntegrationFingerprint", enableBrowserIntegrationFingerprint: "enableBrowserIntegrationFingerprint",
enableCloseToTray: "enableCloseToTray", enableCloseToTray: "enableCloseToTray",
enableFullWidth: "enableFullWidth", enableFullWidth: "enableFullWidth",
enableGravatars: "enableGravatars",
enableMinimizeToTray: "enableMinimizeToTray", enableMinimizeToTray: "enableMinimizeToTray",
enableStartToTray: "enableStartToTrayKey", enableStartToTray: "enableStartToTrayKey",
enableTray: "enableTray", enableTray: "enableTray",
@@ -305,9 +304,6 @@ export class StateMigrationService<
enableFullWidth: enableFullWidth:
(await this.get<boolean>(v1Keys.enableFullWidth)) ?? (await this.get<boolean>(v1Keys.enableFullWidth)) ??
defaultAccount.settings.enableFullWidth, defaultAccount.settings.enableFullWidth,
enableGravitars:
(await this.get<boolean>(v1Keys.enableGravatars)) ??
defaultAccount.settings.enableGravitars,
environmentUrls: globals.environmentUrls ?? defaultAccount.settings.environmentUrls, environmentUrls: globals.environmentUrls ?? defaultAccount.settings.environmentUrls,
equivalentDomains: equivalentDomains:
(await this.get<any>(v1Keys.equivalentDomains)) ?? (await this.get<any>(v1Keys.equivalentDomains)) ??

View File

@@ -17,9 +17,9 @@ const SizeClasses: Record<SizeTypes, string[]> = {
}) })
export class AvatarComponent implements OnChanges { export class AvatarComponent implements OnChanges {
@Input() border = false; @Input() border = false;
@Input() color: string; @Input() color?: string;
@Input() id: number; @Input() id?: string;
@Input() text: string; @Input() text?: string;
@Input() size: SizeTypes = "default"; @Input() size: SizeTypes = "default";
private svgCharCount = 2; private svgCharCount = 2;
@@ -42,7 +42,7 @@ export class AvatarComponent implements OnChanges {
private generate() { private generate() {
let chars: string = null; let chars: string = null;
const upperCaseText = this.text.toUpperCase(); const upperCaseText = this.text?.toUpperCase() ?? "";
chars = this.getFirstLetters(upperCaseText, this.svgCharCount); chars = this.getFirstLetters(upperCaseText, this.svgCharCount);
@@ -58,9 +58,9 @@ export class AvatarComponent implements OnChanges {
let svg: HTMLElement; let svg: HTMLElement;
let hexColor = this.color; let hexColor = this.color;
if (this.color != null) { if (!Utils.isNullOrWhitespace(this.color)) {
svg = this.createSvgElement(this.svgSize, hexColor); svg = this.createSvgElement(this.svgSize, hexColor);
} else if (this.id != null) { } else if (!Utils.isNullOrWhitespace(this.id)) {
hexColor = Utils.stringToColor(this.id.toString()); hexColor = Utils.stringToColor(this.id.toString());
svg = this.createSvgElement(this.svgSize, hexColor); svg = this.createSvgElement(this.svgSize, hexColor);
} else { } else {

View File

@@ -10,9 +10,7 @@ import { Icon, isIcon } from "./icon";
export class BitIconComponent { export class BitIconComponent {
@Input() icon: Icon; @Input() icon: Icon;
constructor(private domSanitizer: DomSanitizer) {} @HostBinding()
@HostBinding("innerHtml")
protected get innerHtml() { protected get innerHtml() {
if (!isIcon(this.icon)) { if (!isIcon(this.icon)) {
return ""; return "";
@@ -21,4 +19,6 @@ export class BitIconComponent {
const svg = this.icon.svg; const svg = this.icon.svg;
return this.domSanitizer.bypassSecurityTrustHtml(svg); return this.domSanitizer.bypassSecurityTrustHtml(svg);
} }
constructor(private domSanitizer: DomSanitizer) {}
} }

View File

@@ -1,4 +1 @@
// Put generic icons in this folder and export them here. export * from "./search";
// Note: Icons need to be in separate files for tree-shaking to work properly
export {}; // <- remove when adding icons in here

View File

@@ -0,0 +1,18 @@
import { svgIcon } from "../icon";
export const Search = svgIcon`
<svg width="120" height="120" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity=".49">
<path class="tw-fill-secondary-300" fill-rule="evenodd" clip-rule="evenodd" d="M40.36 73.256a30.004 30.004 0 0 0 10.346 1.826c16.282 0 29.482-12.912 29.482-28.84 0-.384-.008-.766-.023-1.145h28.726v39.57H40.36v-11.41Z" />
<path class="tw-stroke-secondary-500" d="M21.546 46.241c0 15.929 13.2 28.841 29.482 28.841S80.51 62.17 80.51 46.241c0-15.928-13.2-28.841-29.482-28.841S21.546 30.313 21.546 46.241Z" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" />
<path class="tw-fill-secondary-500" d="M35.36 70.595a1.2 1.2 0 0 0-2.4 0h2.4Zm77.475-30.356a2.343 2.343 0 0 1 2.365 2.33h2.4c0-2.593-2.107-4.73-4.765-4.73v2.4Zm2.365 2.33v46.047h2.4V42.57h-2.4Zm0 46.047c0 1.293-1.058 2.33-2.365 2.33v2.4c2.59 0 4.765-2.069 4.765-4.73h-2.4Zm-2.365 2.33h-75.11v2.4h75.11v-2.4Zm-75.11 0a2.343 2.343 0 0 1-2.365-2.33h-2.4c0 2.594 2.107 4.73 4.766 4.73v-2.4Zm-2.365-2.33v-18.02h-2.4v18.02h2.4Zm44.508-48.377h32.967v-2.4H79.868v2.4Z" />
<path class="tw-stroke-secondary-500" d="M79.907 45.287h29.114v39.57H40.487V73.051" stroke-width="2" stroke-linejoin="round" />
<path class="tw-stroke-secondary-500" d="M57.356 102.56h35.849" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" />
<path class="tw-stroke-secondary-500" d="M68.954 92.147v10.413m11.599-10.413v10.413" stroke-width="4" stroke-linejoin="round" />
<path class="tw-stroke-secondary-500" d="m27.44 64.945-4.51 4.51L5.72 86.663a3 3 0 0 0 0 4.243l1.238 1.238a3 3 0 0 0 4.243 0L28.41 74.936l4.51-4.51" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" />
<path class="tw-stroke-secondary-500" d="M101.293 53.154H85.178m16.115 6.043H90.214m-5.036 0h-7.553m23.668 6.043h-7.05m-5.54 0h-15.61m28.2 6.042H85.178m-5.538 0h-8.562m30.215 6.043H78.632m-5.539 0H60m-5.54 0h-8.057" stroke-width="2" stroke-linecap="round" />
<path class="tw-stroke-secondary-500" d="M29.164 33.01h41.529a2.4 2.4 0 0 1 2.4 2.4v6.28a2.4 2.4 0 0 1-2.4 2.4h-41.53a2.4 2.4 0 0 1-2.4-2.4v-6.28a2.4 2.4 0 0 1 2.4-2.4Z" stroke-width="4" />
<path class="tw-stroke-secondary-500" d="M22.735 54.16h34.361a2.4 2.4 0 0 1 2.4 2.4v6.28a2.4 2.4 0 0 1-2.4 2.4H28.778m50.358-11.08h-6.161a2.4 2.4 0 0 0-2.4 2.4v6.414a2.266 2.266 0 0 0 2.266 2.265" stroke-width="4" stroke-linecap="round" />
</g>
</svg>
`;

View File

@@ -1,15 +1,16 @@
export * from "./async-actions"; export * from "./async-actions";
export * from "./avatar";
export * from "./badge"; export * from "./badge";
export * from "./banner"; export * from "./banner";
export * from "./button"; export * from "./button";
export * from "./callout"; export * from "./callout";
export * from "./dialog";
export * from "./form-field"; export * from "./form-field";
export * from "./icon";
export * from "./icon-button"; export * from "./icon-button";
export * from "./icon";
export * from "./link";
export * from "./menu"; export * from "./menu";
export * from "./multi-select"; export * from "./multi-select";
export * from "./dialog";
export * from "./link";
export * from "./tabs"; export * from "./tabs";
export * from "./toggle-group"; export * from "./toggle-group";
export * from "./utils/i18n-mock.service"; export * from "./utils/i18n-mock.service";

50
package-lock.json generated
View File

@@ -63,7 +63,6 @@
"sweetalert2": "^10.16.6", "sweetalert2": "^10.16.6",
"tldts": "^5.7.84", "tldts": "^5.7.84",
"utf-8-validate": "^5.0.9", "utf-8-validate": "^5.0.9",
"whatwg-fetch": "^3.6.2",
"zone.js": "^0.11.4", "zone.js": "^0.11.4",
"zxcvbn": "^4.4.2" "zxcvbn": "^4.4.2"
}, },
@@ -103,11 +102,9 @@
"@types/papaparse": "^5.3.2", "@types/papaparse": "^5.3.2",
"@types/proper-lockfile": "^4.1.2", "@types/proper-lockfile": "^4.1.2",
"@types/retry": "^0.12.2", "@types/retry": "^0.12.2",
"@types/webcrypto": "^0.0.28",
"@types/zxcvbn": "^4.4.1", "@types/zxcvbn": "^4.4.1",
"@typescript-eslint/eslint-plugin": "^5.22.0", "@typescript-eslint/eslint-plugin": "^5.22.0",
"@typescript-eslint/parser": "^5.22.0", "@typescript-eslint/parser": "^5.22.0",
"@webcomponents/custom-elements": "^1.5.0",
"autoprefixer": "^10.4.7", "autoprefixer": "^10.4.7",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"chromatic": "^6.5.6", "chromatic": "^6.5.6",
@@ -167,7 +164,6 @@
"typescript": "4.6.4", "typescript": "4.6.4",
"url": "^0.11.0", "url": "^0.11.0",
"util": "^0.12.4", "util": "^0.12.4",
"webcrypto-shim": "^0.1.7",
"webpack": "^5.64.4", "webpack": "^5.64.4",
"webpack-cli": "^4.9.1", "webpack-cli": "^4.9.1",
"webpack-dev-server": "^4.9.3", "webpack-dev-server": "^4.9.3",
@@ -13245,12 +13241,6 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/webcrypto": {
"version": "0.0.28",
"resolved": "https://registry.npmjs.org/@types/webcrypto/-/webcrypto-0.0.28.tgz",
"integrity": "sha512-jzAoSUvqA+183nJO/Sc73CREQJsv+p77WJdn532GqA3YXQzlwRwHhClVa7U4O8iB2sJSR7G3v6f1mJFNkwA9YQ==",
"dev": true
},
"node_modules/@types/webpack": { "node_modules/@types/webpack": {
"version": "4.41.32", "version": "4.41.32",
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz",
@@ -13880,12 +13870,6 @@
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
"node_modules/@webcomponents/custom-elements": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.5.0.tgz",
"integrity": "sha512-c+7jPQCs9h/BYVcZ2Kna/3tsl3A/9EyXfvWjp5RiTDm1OpTcbZaCa1z4RNcTe/hUtXaqn64JjNW1yrWT+rZ8gg==",
"dev": true
},
"node_modules/@webpack-cli/configtest": { "node_modules/@webpack-cli/configtest": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
@@ -42229,12 +42213,6 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/webcrypto-shim": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/webcrypto-shim/-/webcrypto-shim-0.1.7.tgz",
"integrity": "sha512-JAvAQR5mRNRxZW2jKigWMjCMkjSdmP5cColRP1U/pTg69VgHXEi1orv5vVpJ55Zc5MIaPc1aaurzd9pjv2bveg==",
"dev": true
},
"node_modules/webidl-conversions": { "node_modules/webidl-conversions": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
@@ -42697,11 +42675,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/whatwg-fetch": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
"integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA=="
},
"node_modules/whatwg-mimetype": { "node_modules/whatwg-mimetype": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
@@ -53105,12 +53078,6 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/webcrypto": {
"version": "0.0.28",
"resolved": "https://registry.npmjs.org/@types/webcrypto/-/webcrypto-0.0.28.tgz",
"integrity": "sha512-jzAoSUvqA+183nJO/Sc73CREQJsv+p77WJdn532GqA3YXQzlwRwHhClVa7U4O8iB2sJSR7G3v6f1mJFNkwA9YQ==",
"dev": true
},
"@types/webpack": { "@types/webpack": {
"version": "4.41.32", "version": "4.41.32",
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz",
@@ -53605,12 +53572,6 @@
"@xtuc/long": "4.2.2" "@xtuc/long": "4.2.2"
} }
}, },
"@webcomponents/custom-elements": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.5.0.tgz",
"integrity": "sha512-c+7jPQCs9h/BYVcZ2Kna/3tsl3A/9EyXfvWjp5RiTDm1OpTcbZaCa1z4RNcTe/hUtXaqn64JjNW1yrWT+rZ8gg==",
"dev": true
},
"@webpack-cli/configtest": { "@webpack-cli/configtest": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
@@ -75589,12 +75550,6 @@
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
"dev": true "dev": true
}, },
"webcrypto-shim": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/webcrypto-shim/-/webcrypto-shim-0.1.7.tgz",
"integrity": "sha512-JAvAQR5mRNRxZW2jKigWMjCMkjSdmP5cColRP1U/pTg69VgHXEi1orv5vVpJ55Zc5MIaPc1aaurzd9pjv2bveg==",
"dev": true
},
"webidl-conversions": { "webidl-conversions": {
"version": "6.1.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
@@ -75906,11 +75861,6 @@
} }
} }
}, },
"whatwg-fetch": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
"integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA=="
},
"whatwg-mimetype": { "whatwg-mimetype": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",

View File

@@ -67,11 +67,9 @@
"@types/papaparse": "^5.3.2", "@types/papaparse": "^5.3.2",
"@types/proper-lockfile": "^4.1.2", "@types/proper-lockfile": "^4.1.2",
"@types/retry": "^0.12.2", "@types/retry": "^0.12.2",
"@types/webcrypto": "^0.0.28",
"@types/zxcvbn": "^4.4.1", "@types/zxcvbn": "^4.4.1",
"@typescript-eslint/eslint-plugin": "^5.22.0", "@typescript-eslint/eslint-plugin": "^5.22.0",
"@typescript-eslint/parser": "^5.22.0", "@typescript-eslint/parser": "^5.22.0",
"@webcomponents/custom-elements": "^1.5.0",
"autoprefixer": "^10.4.7", "autoprefixer": "^10.4.7",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"chromatic": "^6.5.6", "chromatic": "^6.5.6",
@@ -131,7 +129,6 @@
"typescript": "4.6.4", "typescript": "4.6.4",
"url": "^0.11.0", "url": "^0.11.0",
"util": "^0.12.4", "util": "^0.12.4",
"webcrypto-shim": "^0.1.7",
"webpack": "^5.64.4", "webpack": "^5.64.4",
"webpack-cli": "^4.9.1", "webpack-cli": "^4.9.1",
"webpack-dev-server": "^4.9.3", "webpack-dev-server": "^4.9.3",
@@ -187,7 +184,6 @@
"sweetalert2": "^10.16.6", "sweetalert2": "^10.16.6",
"tldts": "^5.7.84", "tldts": "^5.7.84",
"utf-8-validate": "^5.0.9", "utf-8-validate": "^5.0.9",
"whatwg-fetch": "^3.6.2",
"zone.js": "^0.11.4", "zone.js": "^0.11.4",
"zxcvbn": "^4.4.2" "zxcvbn": "^4.4.2"
}, },