1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-28 02:23:25 +00:00

Merge remote-tracking branch 'origin' into auth/pm-19877/notification-processing

This commit is contained in:
Patrick Pimentel
2025-07-17 09:32:56 -04:00
27 changed files with 246 additions and 299 deletions

View File

@@ -1,6 +1,6 @@
<ng-container *ngIf="show">
<ng-container [ngSwitch]="displayMode">
<ng-container *ngSwitchCase="'personalOwnershipPolicy'">
<ng-container *ngSwitchCase="'organizationDataOwnershipPolicy'">
<div class="filter-heading" [ngClass]="{ active: !hasActiveFilter }">
<button
type="button"

View File

@@ -103,48 +103,11 @@
*ngIf="filter.type !== 'trash' && filter.collectionId !== Unassigned && organization"
class="tw-shrink-0"
>
<!-- "New" menu is always shown unless the user cannot create a cipher and cannot create a collection-->
<ng-container *ngIf="canCreateCipher || canCreateCollection">
<div appListDropdown>
<button
bitButton
buttonType="primary"
type="button"
[bitMenuTriggerFor]="addOptions"
id="newItemDropdown"
appA11yTitle="{{ 'new' | i18n }}"
>
<i class="bwi bwi-plus" aria-hidden="true"></i>
{{ "new" | i18n }}<i class="bwi tw-ml-2" aria-hidden="true"></i>
</button>
<bit-menu #addOptions aria-labelledby="newItemDropdown">
<ng-container *ngIf="canCreateCipher">
<button type="button" bitMenuItem (click)="addCipher(CipherType.Login)">
<i class="bwi bwi-globe" slot="start" aria-hidden="true"></i>
{{ "typeLogin" | i18n }}
</button>
<button type="button" bitMenuItem (click)="addCipher(CipherType.Card)">
<i class="bwi bwi-credit-card" slot="start" aria-hidden="true"></i>
{{ "typeCard" | i18n }}
</button>
<button type="button" bitMenuItem (click)="addCipher(CipherType.Identity)">
<i class="bwi bwi-id-card" slot="start" aria-hidden="true"></i>
{{ "typeIdentity" | i18n }}
</button>
<button type="button" bitMenuItem (click)="addCipher(CipherType.SecureNote)">
<i class="bwi bwi-sticky-note" slot="start" aria-hidden="true"></i>
{{ "note" | i18n }}
</button>
</ng-container>
<ng-container *ngIf="canCreateCollection">
<bit-menu-divider *ngIf="canCreateCipher"></bit-menu-divider>
<button type="button" bitMenuItem (click)="addCollection()">
<i class="bwi bwi-fw bwi-collection-shared" aria-hidden="true"></i>
{{ "collection" | i18n }}
</button>
</ng-container>
</bit-menu>
</div>
</ng-container>
<vault-new-cipher-menu
[canCreateCipher]="canCreateCipher"
[canCreateCollection]="canCreateCollection"
(cipherAdded)="addCipher($event)"
(collectionAdded)="addCollection()"
/>
</div>
</app-header>

View File

@@ -25,6 +25,7 @@ import {
SearchModule,
SimpleDialogOptions,
} from "@bitwarden/components";
import { NewCipherMenuComponent } from "@bitwarden/vault";
import { HeaderModule } from "../../../../layouts/header/header.module";
import { SharedModule } from "../../../../shared";
@@ -45,6 +46,7 @@ import { CollectionDialogTabType } from "../../shared/components/collection-dial
HeaderModule,
SearchModule,
JslibModule,
NewCipherMenuComponent,
],
})
export class VaultHeaderComponent {

View File

@@ -16,8 +16,7 @@ export default {
component: ReportCardComponent,
decorators: [
moduleMetadata({
imports: [JslibModule, BadgeModule, IconModule, RouterTestingModule],
declarations: [PremiumBadgeComponent],
imports: [JslibModule, BadgeModule, IconModule, RouterTestingModule, PremiumBadgeComponent],
}),
applicationConfig({
providers: [importProvidersFrom(PreloadedEnglishI18nModule)],

View File

@@ -18,8 +18,8 @@ export default {
component: ReportListComponent,
decorators: [
moduleMetadata({
imports: [JslibModule, BadgeModule, RouterTestingModule, IconModule],
declarations: [PremiumBadgeComponent, ReportCardComponent],
imports: [JslibModule, BadgeModule, RouterTestingModule, IconModule, PremiumBadgeComponent],
declarations: [ReportCardComponent],
}),
applicationConfig({
providers: [importProvidersFrom(PreloadedEnglishI18nModule)],

View File

@@ -42,10 +42,8 @@ import { WeakPasswordsReportComponent as OrgWeakPasswordsReportComponent } from
import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component";
import { HeaderModule } from "../layouts/header/header.module";
import { PremiumBadgeComponent } from "../vault/components/premium-badge.component";
import { FolderAddEditComponent } from "../vault/individual-vault/folder-add-edit.component";
import { OrganizationBadgeModule } from "../vault/individual-vault/organization-badge/organization-badge.module";
import { PipesModule } from "../vault/individual-vault/pipes/pipes.module";
import { PurgeVaultComponent } from "../vault/settings/purge-vault.component";
import { AccountFingerprintComponent } from "./components/account-fingerprint/account-fingerprint.component";
import { SharedModule } from "./shared.module";
@@ -68,6 +66,7 @@ import { SharedModule } from "./shared.module";
OrganizationLayoutComponent,
VerifyRecoverDeleteOrgComponent,
VaultTimeoutInputComponent,
PremiumBadgeComponent,
],
declarations: [
AcceptFamilySponsorshipComponent,
@@ -76,7 +75,6 @@ import { SharedModule } from "./shared.module";
EmergencyAccessConfirmComponent,
EmergencyAccessTakeoverComponent,
EmergencyAccessViewComponent,
FolderAddEditComponent,
OrgEventsComponent,
OrgExposedPasswordsReportComponent,
OrgInactiveTwoFactorReportComponent,
@@ -84,8 +82,6 @@ import { SharedModule } from "./shared.module";
OrgUnsecuredWebsitesReportComponent,
OrgUserConfirmComponent,
OrgWeakPasswordsReportComponent,
PremiumBadgeComponent,
PurgeVaultComponent,
RecoverDeleteComponent,
RecoverTwoFactorComponent,
RemovePasswordComponent,
@@ -106,7 +102,6 @@ import { SharedModule } from "./shared.module";
EmergencyAccessConfirmComponent,
EmergencyAccessTakeoverComponent,
EmergencyAccessViewComponent,
FolderAddEditComponent,
OrganizationLayoutComponent,
OrgEventsComponent,
OrgExposedPasswordsReportComponent,
@@ -116,7 +111,6 @@ import { SharedModule } from "./shared.module";
OrgUserConfirmComponent,
OrgWeakPasswordsReportComponent,
PremiumBadgeComponent,
PurgeVaultComponent,
RecoverDeleteComponent,
RecoverTwoFactorComponent,
RemovePasswordComponent,

View File

@@ -1,6 +1,8 @@
import { Component } from "@angular/core";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { BadgeModule } from "@bitwarden/components";
@Component({
selector: "app-premium-badge",
@@ -9,7 +11,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
{{ "premium" | i18n }}
</button>
`,
standalone: false,
imports: [JslibModule, BadgeModule],
})
export class PremiumBadgeComponent {
constructor(private messagingService: MessagingService) {}

View File

@@ -510,7 +510,7 @@ export class VaultItemsComponent {
private compareNames(a: VaultItem, b: VaultItem): number {
const getName = (item: VaultItem) => item.collection?.name || item.cipher?.name;
return getName(a).localeCompare(getName(b));
return getName(a)?.localeCompare(getName(b)) ?? -1;
}
/**

View File

@@ -1,32 +0,0 @@
<form [bitSubmit]="submitAndClose" [formGroup]="formGroup">
<bit-dialog>
<span bitDialogTitle>
{{ title }}
</span>
<span bitDialogContent>
<bit-form-field>
<bit-label>{{ "name" | i18n }}</bit-label>
<input bitInput id="name" formControlName="name" />
</bit-form-field>
</span>
<ng-container bitDialogFooter>
<button bitButton buttonType="primary" bitFormButton type="submit">
<span>{{ "save" | i18n }}</span>
</button>
<button bitButton buttonType="secondary" bitDialogClose type="button">
{{ "cancel" | i18n }}
</button>
<div class="tw-m-0 tw-ml-auto">
<button
buttonType="danger"
bitIconButton="bwi-trash"
bitFormButton
type="button"
appA11yTitle="{{ 'delete' | i18n }}"
*ngIf="editMode"
[bitAction]="deleteAndClose"
></button>
</div>
</ng-container>
</bit-dialog>
</form>

View File

@@ -1,139 +0,0 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, Inject } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { firstValueFrom } from "rxjs";
import { FolderAddEditComponent as BaseFolderAddEditComponent } from "@bitwarden/angular/vault/components/folder-add-edit.component";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values";
import {
DIALOG_DATA,
DialogConfig,
DialogRef,
DialogService,
ToastService,
} from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management";
@Component({
selector: "app-folder-add-edit",
templateUrl: "folder-add-edit.component.html",
standalone: false,
})
export class FolderAddEditComponent extends BaseFolderAddEditComponent {
protected override componentName = "app-folder-add-edit";
constructor(
folderService: FolderService,
folderApiService: FolderApiServiceAbstraction,
protected accountSerivce: AccountService,
protected keyService: KeyService,
i18nService: I18nService,
platformUtilsService: PlatformUtilsService,
logService: LogService,
dialogService: DialogService,
formBuilder: FormBuilder,
protected toastService: ToastService,
protected dialogRef: DialogRef<FolderAddEditDialogResult>,
@Inject(DIALOG_DATA) params: FolderAddEditDialogParams,
) {
super(
folderService,
folderApiService,
accountSerivce,
keyService,
i18nService,
platformUtilsService,
logService,
dialogService,
formBuilder,
toastService,
);
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
params?.folderId ? (this.folderId = params.folderId) : null;
}
deleteAndClose = async () => {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "deleteFolder" },
content: { key: "deleteFolderConfirmation" },
type: "warning",
});
if (!confirmed) {
return;
}
try {
await this.folderApiService.delete(this.folder.id, await firstValueFrom(this.activeUserId$));
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t("deletedFolder"),
});
} catch (e) {
this.logService.error(e);
}
this.dialogRef.close(FolderAddEditDialogResult.Deleted);
};
submitAndClose = async () => {
this.folder.name = this.formGroup.controls.name.value;
if (this.folder.name == null || this.folder.name === "") {
this.formGroup.controls.name.markAsTouched();
return;
}
try {
const activeAccountId = await firstValueFrom(this.activeUserId$);
const userKey = await this.keyService.getUserKeyWithLegacySupport(activeAccountId);
const folder = await this.folderService.encrypt(this.folder, userKey);
this.formPromise = this.folderApiService.save(folder, activeAccountId);
await this.formPromise;
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(this.editMode ? "editedFolder" : "addedFolder"),
});
this.onSavedFolder.emit(this.folder);
this.dialogRef.close(FolderAddEditDialogResult.Saved);
} catch (e) {
this.logService.error(e);
}
return;
};
}
export interface FolderAddEditDialogParams {
folderId: string;
}
export const FolderAddEditDialogResult = {
Deleted: "deleted",
Canceled: "canceled",
Saved: "saved",
} as const;
export type FolderAddEditDialogResult = UnionOfValues<typeof FolderAddEditDialogResult>;
/**
* Strongly typed helper to open a FolderAddEdit dialog
* @param dialogService Instance of the dialog service that will be used to open the dialog
* @param config Optional configuration for the dialog
*/
export function openFolderAddEditDialog(
dialogService: DialogService,
config?: DialogConfig<FolderAddEditDialogParams>,
) {
return dialogService.open<FolderAddEditDialogResult, FolderAddEditDialogParams>(
FolderAddEditComponent,
config,
);
}

View File

@@ -68,35 +68,13 @@
</ng-container>
<div *ngIf="filter.type !== 'trash'" class="tw-shrink-0">
<div appListDropdown>
<button
bitButton
buttonType="primary"
type="button"
[bitMenuTriggerFor]="addOptions"
id="newItemDropdown"
appA11yTitle="{{ 'new' | i18n }}"
>
<i class="bwi bwi-plus" aria-hidden="true"></i>
{{ "new" | i18n }}<i class="bwi tw-ml-2" aria-hidden="true"></i>
</button>
<bit-menu #addOptions aria-labelledby="newItemDropdown">
@for (item of cipherMenuItems$ | async; track item.type) {
<button type="button" bitMenuItem (click)="addCipher(item.type)">
<i class="bwi {{ item.icon }}" slot="start" aria-hidden="true"></i>
{{ item.labelKey | i18n }}
</button>
}
<bit-menu-divider />
<button type="button" bitMenuItem (click)="addFolder()">
<i class="bwi bwi-fw bwi-folder" aria-hidden="true"></i>
{{ "folder" | i18n }}
</button>
<button *ngIf="canCreateCollections" type="button" bitMenuItem (click)="addCollection()">
<i class="bwi bwi-fw bwi-collection-shared" aria-hidden="true"></i>
{{ "collection" | i18n }}
</button>
</bit-menu>
</div>
<vault-new-cipher-menu
[canCreateCipher]="true"
[canCreateFolder]="true"
[canCreateCollection]="canCreateCollections"
(cipherAdded)="addCipher($event)"
(folderAdded)="addFolder()"
(collectionAdded)="addCollection()"
/>
</div>
</app-header>

View File

@@ -3,7 +3,7 @@
import { CommonModule } from "@angular/common";
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from "@angular/core";
import { Router } from "@angular/router";
import { firstValueFrom, map, shareReplay } from "rxjs";
import { firstValueFrom } from "rxjs";
import {
Unassigned,
@@ -18,13 +18,13 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { CipherType } from "@bitwarden/common/vault/enums";
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service";
import {
BreadcrumbsModule,
DialogService,
MenuModule,
SimpleDialogOptions,
} from "@bitwarden/components";
import { NewCipherMenuComponent } from "@bitwarden/vault";
import { CollectionDialogTabType } from "../../../admin-console/organizations/shared/components/collection-dialog";
import { HeaderModule } from "../../../layouts/header/header.module";
@@ -46,6 +46,7 @@ import {
HeaderModule,
PipesModule,
JslibModule,
NewCipherMenuComponent,
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
@@ -54,21 +55,6 @@ export class VaultHeaderComponent {
protected All = All;
protected CollectionDialogTabType = CollectionDialogTabType;
protected CipherType = CipherType;
protected allCipherMenuItems = [
{ type: CipherType.Login, icon: "bwi-globe", labelKey: "typeLogin" },
{ type: CipherType.Card, icon: "bwi-credit-card", labelKey: "typeCard" },
{ type: CipherType.Identity, icon: "bwi-id-card", labelKey: "typeIdentity" },
{ type: CipherType.SecureNote, icon: "bwi-sticky-note", labelKey: "note" },
{ type: CipherType.SshKey, icon: "bwi-key", labelKey: "typeSshKey" },
];
protected cipherMenuItems$ = this.restrictedItemTypesService.restricted$.pipe(
map((restrictedTypes) => {
return this.allCipherMenuItems.filter((item) => {
return !restrictedTypes.some((restrictedType) => restrictedType.cipherType === item.type);
});
}),
shareReplay({ bufferSize: 1, refCount: true }),
);
/**
* Boolean to determine the loading state of the header.
@@ -109,7 +95,6 @@ export class VaultHeaderComponent {
private dialogService: DialogService,
private router: Router,
private configService: ConfigService,
private restrictedItemTypesService: RestrictedItemTypesService,
) {}
/**

View File

@@ -18,14 +18,16 @@ import {
ToastService,
} from "@bitwarden/components";
import { UserVerificationModule } from "../../auth/shared/components/user-verification";
import { SharedModule } from "../../shared";
export interface PurgeVaultDialogData {
organizationId: string;
}
@Component({
selector: "app-purge-vault",
templateUrl: "purge-vault.component.html",
standalone: false,
imports: [SharedModule, UserVerificationModule],
})
export class PurgeVaultComponent {
organizationId: string = null;

View File

@@ -647,6 +647,9 @@
"typeSecureNote": {
"message": "Secure note"
},
"typeNote": {
"message": "Note"
},
"typeSshKey": {
"message": "SSH key"
},
@@ -8928,7 +8931,7 @@
},
"uriMatchDefaultStrategyHint": {
"message": "URI match detection is how Bitwarden identifies autofill suggestions.",
"description": "Explains to the user that URI match detection determines how Bitwarden suggests autofill options, and clarifies that this default strategy applies when no specific match detection is set for a login item."
"description": "Explains to the user that URI match detection determines how Bitwarden suggests autofill options, and clarifies that this default strategy applies when no specific match detection is set for a login item."
},
"regExAdvancedOptionWarning": {
"message": "\"Regular expression\" is an advanced option with increased risk of exposing credentials.",