1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-12 06:23:38 +00:00

Merge branch 'main' into km/pm-27331

This commit is contained in:
Thomas Avery
2026-02-11 11:37:46 -06:00
committed by GitHub
158 changed files with 3291 additions and 1005 deletions

View File

@@ -565,7 +565,7 @@ jobs:
uses: bitwarden/gh-actions/azure-logout@main
- name: Upload Sources
uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # v2.13.0
uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # v2.14.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}

View File

@@ -1007,7 +1007,7 @@ jobs:
node-version: ${{ env._NODE_VERSION }}
- name: Set up Python
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.14.2'
@@ -1247,7 +1247,7 @@ jobs:
node-version: ${{ env._NODE_VERSION }}
- name: Set up Python
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.14.2'
@@ -1522,7 +1522,7 @@ jobs:
node-version: ${{ env._NODE_VERSION }}
- name: Set up Python
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.14.2'
@@ -1873,7 +1873,7 @@ jobs:
uses: bitwarden/gh-actions/azure-logout@main
- name: Upload Sources
uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # v2.13.0
uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # v2.14.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}

View File

@@ -352,7 +352,7 @@ jobs:
- name: Scan Docker image
if: ${{ needs.setup.outputs.has_secrets == 'true' }}
id: container-scan
uses: anchore/scan-action@62b74fb7bb810d2c45b1865f47a77655621862a5 # v7.2.3
uses: anchore/scan-action@0d444ed77d83ee2ba7f5ced0d90d640a1281d762 # v7.3.0
with:
image: ${{ steps.image-name.outputs.name }}
fail-build: false
@@ -408,7 +408,7 @@ jobs:
uses: bitwarden/gh-actions/azure-logout@main
- name: Upload Sources
uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # v2.13.0
uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # v2.14.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}

View File

@@ -45,7 +45,7 @@ jobs:
uses: bitwarden/gh-actions/azure-logout@main
- name: Lint ${{ matrix.app.name }} config
uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # v2.13.0
uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # v2.14.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ matrix.app.project_id }}

View File

@@ -142,7 +142,7 @@ jobs:
run: cargo +nightly udeps --workspace --all-features --all-targets
- name: Install cargo-deny
uses: taiki-e/install-action@542cebaaed782771e619bd5609d97659d109c492 # v2.66.7
uses: taiki-e/install-action@887bc4e03483810873d617344dd5189cd82e7b8b # v2.67.11
with:
tool: cargo-deny@0.18.6

View File

@@ -331,7 +331,7 @@ jobs:
run: wget "https://github.com/bitwarden/clients/releases/download/${_RELEASE_TAG}/macos-build-number.json"
- name: Setup Ruby and Install Fastlane
uses: ruby/setup-ruby@708024e6c902387ab41de36e1669e43b5ee7085e # v1.283.0
uses: ruby/setup-ruby@90be1154f987f4dc0fe0dd0feedac9e473aa4ba8 # v1.286.0
with:
ruby-version: '3.4.7'
bundler-cache: false

View File

@@ -5966,6 +5966,9 @@
"cardNumberLabel": {
"message": "Card number"
},
"errorCannotDecrypt": {
"message": "Error: Cannot decrypt"
},
"removeMasterPasswordForOrgUserKeyConnector": {
"message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain."
},
@@ -6127,6 +6130,10 @@
"emailPlaceholder": {
"message": "user@bitwarden.com , user@acme.com"
},
"downloadBitwardenApps": {
"message": "Download Bitwarden apps"
},
"emailProtected": {
"message": "Email protected"
},
@@ -6134,4 +6141,4 @@
"message": "Individuals will need to enter the password to view this Send",
"description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated."
}
}
}

View File

@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import {
BehaviorSubject,
EmptyError,
@@ -79,7 +77,7 @@ export type BrowserFido2Message = { sessionId: string } & (
}
| {
type: typeof BrowserFido2MessageTypes.PickCredentialResponse;
cipherId?: string;
cipherId: string;
userVerified: boolean;
}
| {

View File

@@ -1,5 +1,6 @@
import {
BehaviorSubject,
catchError,
combineLatest,
combineLatestWith,
concatMap,
@@ -73,9 +74,25 @@ export class BadgeService {
map((evt) => evt.tab),
combineLatestWith(this.stateFunctions),
switchMap(([tab, dynamicStateFunctions]) => {
const functions = [...Object.values(dynamicStateFunctions), defaultTabStateFunction];
const functions = [
...Object.entries(dynamicStateFunctions),
["default" as string, defaultTabStateFunction] as const,
];
return combineLatest(functions.map((f) => f(tab).pipe(startWith(undefined)))).pipe(
return combineLatest(
functions.map(([name, f]) =>
f(tab).pipe(
startWith(undefined),
catchError((error: unknown) => {
this.logService.error(
`BadgeService: State function "${name}" threw an error`,
error,
);
return of(undefined);
}),
),
),
).pipe(
map((states) => ({
tab,
states: states.filter((s): s is BadgeStateSetting => s !== undefined),

View File

@@ -27,8 +27,8 @@
{{ button.label | i18n }}
</span>
</button>
<div *ngIf="button.showBerry" class="tw-absolute tw-top-1.5 tw-left-[calc(50%+5px)]">
<div class="tw-bg-notification-600 tw-size-2.5 tw-rounded-full"></div>
<div *ngIf="button.showBerry" class="tw-absolute tw-top-0 tw-left-[calc(50%+5px)]">
<bit-berry type="status" variant="danger"></bit-berry>
</div>
</li>
</ul>

View File

@@ -5,7 +5,7 @@ import { RouterModule } from "@angular/router";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { BitSvg } from "@bitwarden/assets/svg";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { SvgModule, LinkModule } from "@bitwarden/components";
import { SvgModule, LinkModule, BerryComponent } from "@bitwarden/components";
export type NavButton = {
label: string;
@@ -20,7 +20,7 @@ export type NavButton = {
@Component({
selector: "popup-tab-navigation",
templateUrl: "popup-tab-navigation.component.html",
imports: [CommonModule, LinkModule, RouterModule, JslibModule, SvgModule],
imports: [CommonModule, LinkModule, RouterModule, JslibModule, SvgModule, BerryComponent],
host: {
class: "tw-block tw-size-full tw-flex tw-flex-col",
},

View File

@@ -78,13 +78,13 @@ import { ExportBrowserV2Component } from "../tools/popup/settings/export/export-
import { ImportBrowserV2Component } from "../tools/popup/settings/import/import-browser-v2.component";
import { SettingsV2Component } from "../tools/popup/settings/settings-v2.component";
import { AtRiskPasswordsComponent } from "../vault/popup/components/at-risk-passwords/at-risk-passwords.component";
import { AddEditV2Component } from "../vault/popup/components/vault-v2/add-edit/add-edit-v2.component";
import { AssignCollections } from "../vault/popup/components/vault-v2/assign-collections/assign-collections.component";
import { AttachmentsV2Component } from "../vault/popup/components/vault-v2/attachments/attachments-v2.component";
import { IntroCarouselComponent } from "../vault/popup/components/vault-v2/intro-carousel/intro-carousel.component";
import { PasswordHistoryV2Component } from "../vault/popup/components/vault-v2/vault-password-history-v2/vault-password-history-v2.component";
import { VaultV2Component } from "../vault/popup/components/vault-v2/vault-v2.component";
import { ViewV2Component } from "../vault/popup/components/vault-v2/view-v2/view-v2.component";
import { AddEditComponent } from "../vault/popup/components/vault/add-edit/add-edit.component";
import { AssignCollections } from "../vault/popup/components/vault/assign-collections/assign-collections.component";
import { AttachmentsComponent } from "../vault/popup/components/vault/attachments/attachments.component";
import { IntroCarouselComponent } from "../vault/popup/components/vault/intro-carousel/intro-carousel.component";
import { PasswordHistoryComponent } from "../vault/popup/components/vault/vault-password-history/vault-password-history.component";
import { VaultComponent } from "../vault/popup/components/vault/vault.component";
import { ViewComponent } from "../vault/popup/components/vault/view/view.component";
import {
atRiskPasswordAuthGuard,
canAccessAtRiskPasswords,
@@ -93,13 +93,13 @@ import {
import { clearVaultStateGuard } from "../vault/popup/guards/clear-vault-state.guard";
import { IntroCarouselGuard } from "../vault/popup/guards/intro-carousel.guard";
import { AdminSettingsComponent } from "../vault/popup/settings/admin-settings.component";
import { AppearanceV2Component } from "../vault/popup/settings/appearance-v2.component";
import { AppearanceComponent } from "../vault/popup/settings/appearance.component";
import { ArchiveComponent } from "../vault/popup/settings/archive.component";
import { DownloadBitwardenComponent } from "../vault/popup/settings/download-bitwarden.component";
import { FoldersV2Component } from "../vault/popup/settings/folders-v2.component";
import { MoreFromBitwardenPageV2Component } from "../vault/popup/settings/more-from-bitwarden-page-v2.component";
import { FoldersComponent } from "../vault/popup/settings/folders.component";
import { MoreFromBitwardenPageComponent } from "../vault/popup/settings/more-from-bitwarden-page.component";
import { TrashComponent } from "../vault/popup/settings/trash.component";
import { VaultSettingsV2Component } from "../vault/popup/settings/vault-settings-v2.component";
import { VaultSettingsComponent } from "../vault/popup/settings/vault-settings.component";
import { RouteElevation } from "./app-routing.animations";
import {
@@ -214,7 +214,7 @@ const routes: Routes = [
},
{
path: "view-cipher",
component: ViewV2Component,
component: ViewComponent,
canActivate: [authGuard],
data: {
// Above "trash"
@@ -223,20 +223,20 @@ const routes: Routes = [
},
{
path: "cipher-password-history",
component: PasswordHistoryV2Component,
component: PasswordHistoryComponent,
canActivate: [authGuard],
data: { elevation: 4 } satisfies RouteDataProperties,
},
{
path: "add-cipher",
component: AddEditV2Component,
component: AddEditComponent,
canActivate: [authGuard, debounceNavigationGuard()],
data: { elevation: 1, resetRouterCacheOnTabChange: true } satisfies RouteDataProperties,
runGuardsAndResolvers: "always",
},
{
path: "edit-cipher",
component: AddEditV2Component,
component: AddEditComponent,
canActivate: [authGuard, debounceNavigationGuard()],
data: {
// Above "trash"
@@ -247,7 +247,7 @@ const routes: Routes = [
},
{
path: "attachments",
component: AttachmentsV2Component,
component: AttachmentsComponent,
canActivate: [authGuard, filePickerPopoutGuard()],
data: { elevation: 4 } satisfies RouteDataProperties,
},
@@ -301,13 +301,13 @@ const routes: Routes = [
},
{
path: "vault-settings",
component: VaultSettingsV2Component,
component: VaultSettingsComponent,
canActivate: [authGuard],
data: { elevation: 1 } satisfies RouteDataProperties,
},
{
path: "folders",
component: FoldersV2Component,
component: FoldersComponent,
canActivate: [authGuard],
data: { elevation: 2 } satisfies RouteDataProperties,
},
@@ -331,7 +331,7 @@ const routes: Routes = [
},
{
path: "appearance",
component: AppearanceV2Component,
component: AppearanceComponent,
canActivate: [authGuard],
data: { elevation: 1 } satisfies RouteDataProperties,
},
@@ -343,7 +343,7 @@ const routes: Routes = [
},
{
path: "clone-cipher",
component: AddEditV2Component,
component: AddEditComponent,
canActivate: [authGuard],
data: { elevation: 1 } satisfies RouteDataProperties,
},
@@ -635,7 +635,7 @@ const routes: Routes = [
},
{
path: "more-from-bitwarden",
component: MoreFromBitwardenPageV2Component,
component: MoreFromBitwardenPageComponent,
canActivate: [authGuard],
data: { elevation: 2 } satisfies RouteDataProperties,
},
@@ -696,7 +696,7 @@ const routes: Routes = [
},
{
path: "vault",
component: VaultV2Component,
component: VaultComponent,
canActivate: [authGuard],
canDeactivate: [clearVaultStateGuard],
data: { elevation: 0 } satisfies RouteDataProperties,

View File

@@ -1,19 +1,21 @@
<popup-page>
<bit-spotlight *ngIf="!(hasPremium$ | async)" persistent>
<span class="tw-text-xs"
>{{ "unlockFeaturesWithPremium" | i18n }}
<button
bitLink
buttonType="primary"
class="tw-text-xs"
type="button"
(click)="openUpgradeDialog()"
[title]="'upgradeNow' | i18n"
>
{{ "upgradeNow" | i18n }}
</button>
</span>
</bit-spotlight>
@if (!(hasPremium$ | async)) {
<bit-spotlight persistent>
<span class="tw-text-xs"
>{{ "unlockFeaturesWithPremium" | i18n }}
<button
bitLink
buttonType="primary"
class="tw-text-xs"
type="button"
(click)="openUpgradeDialog()"
[title]="'upgradeNow' | i18n"
>
{{ "upgradeNow" | i18n }}
</button>
</span>
</bit-spotlight>
}
<popup-header slot="header" pageTitle="{{ 'settings' | i18n }}">
<ng-container slot="end">
<app-pop-out></app-pop-out>
@@ -23,14 +25,14 @@
<bit-item-group>
<bit-item>
<a bit-item-content routerLink="/account-security">
<a bit-item-content routerLink="/account-security" [truncate]="false">
<i slot="start" class="bwi bwi-lock" aria-hidden="true"></i>
{{ "accountSecurity" | i18n }}
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
</a>
</bit-item>
<bit-item>
<a bit-item-content routerLink="/autofill">
<a bit-item-content routerLink="/autofill" [truncate]="false">
<i slot="start" class="bwi bwi-check-circle" aria-hidden="true"></i>
<div class="tw-flex tw-items-center tw-justify-center">
<p class="tw-pr-2">{{ "autofill" | i18n }}</p>
@@ -44,7 +46,7 @@
</a>
</bit-item>
<bit-item>
<a bit-item-content routerLink="/notifications">
<a bit-item-content routerLink="/notifications" [truncate]="false">
<i slot="start" class="bwi bwi-file-text" aria-hidden="true"></i>
{{ "notifications" | i18n }}
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
@@ -55,6 +57,7 @@
bit-item-content
routerLink="/vault-settings"
(click)="dismissBadge(NudgeType.EmptyVaultNudge)"
[truncate]="false"
>
<i slot="start" class="bwi bwi-vault" aria-hidden="true"></i>
<div class="tw-flex tw-items-center tw-justify-center">
@@ -63,20 +66,18 @@
Currently can be only 1 item for notification.
Will make this dynamic when more nudges are added
-->
<span
*ngIf="showVaultBadge$ | async"
bitBadge
variant="notification"
[attr.aria-label]="'nudgeBadgeAria' | i18n"
>1</span
>
@if (showVaultBadge$ | async) {
<span bitBadge variant="notification" [attr.aria-label]="'nudgeBadgeAria' | i18n"
>1</span
>
}
</div>
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
</a>
</bit-item>
<bit-item>
<a bit-item-content routerLink="/appearance">
<a bit-item-content routerLink="/appearance" [truncate]="false">
<i slot="start" class="bwi bwi-brush" aria-hidden="true"></i>
{{ "appearance" | i18n }}
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
@@ -85,7 +86,7 @@
@if (showAdminSettingsLink$ | async) {
<bit-item>
<a bit-item-content routerLink="/admin">
<a bit-item-content routerLink="/admin" [truncate]="false">
<i slot="start" class="bwi bwi-business" aria-hidden="true"></i>
<div class="tw-flex tw-items-center tw-justify-center">
<p class="tw-pr-2">{{ "admin" | i18n }}</p>
@@ -101,30 +102,28 @@
}
<bit-item>
<a bit-item-content routerLink="/about">
<a bit-item-content routerLink="/about" [truncate]="false">
<i slot="start" class="bwi bwi-info-circle" aria-hidden="true"></i>
{{ "about" | i18n }}
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
</a>
</bit-item>
<bit-item>
<a bit-item-content routerLink="/download-bitwarden">
<a bit-item-content routerLink="/download-bitwarden" [truncate]="false">
<i slot="start" class="bwi bwi-mobile" aria-hidden="true"></i>
<div class="tw-flex tw-items-center">
<p class="tw-pr-2">{{ "downloadBitwardenOnAllDevices" | i18n }}</p>
<span
*ngIf="showDownloadBitwardenNudge$ | async"
bitBadge
variant="notification"
[attr.aria-label]="'nudgeBadgeAria' | i18n"
>1
</span>
<p class="tw-pr-2">{{ "downloadBitwardenApps" | i18n }}</p>
@if (showDownloadBitwardenNudge$ | async) {
<span bitBadge variant="notification" [attr.aria-label]="'nudgeBadgeAria' | i18n"
>1
</span>
}
</div>
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
</a>
</bit-item>
<bit-item>
<a bit-item-content routerLink="/more-from-bitwarden">
<a bit-item-content routerLink="/more-from-bitwarden" [truncate]="false">
<i slot="start" class="bwi bwi-filter" aria-hidden="true"></i>
{{ "moreFromBitwarden" | i18n }}
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>

View File

@@ -41,24 +41,6 @@
</button>
<ng-container slot="end">
@if (isEditMode) {
@if ((archiveFlagEnabled$ | async) && isCipherArchived) {
<button
type="button"
[bitAction]="unarchive"
bitIconButton="bwi-unarchive"
[label]="'unarchive' | i18n"
></button>
}
@if ((userCanArchive$ | async) && canCipherBeArchived) {
<button
type="button"
[bitAction]="archive"
bitIconButton="bwi-archive"
[label]="'archiveVerb' | i18n"
></button>
}
}
@if (canDeleteCipher$ | async) {
<button
[bitAction]="delete"

View File

@@ -40,16 +40,16 @@ import BrowserPopupUtils from "../../../../../platform/browser/browser-popup-uti
import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.service";
import { PopupCloseWarningService } from "../../../../../popup/services/popup-close-warning.service";
import { AddEditV2Component } from "./add-edit-v2.component";
import { AddEditComponent } from "./add-edit.component";
// 'qrcode-parser' is used by `BrowserTotpCaptureService` but is an es6 module that jest can't compile.
// Mock the entire module here to prevent jest from throwing an error. I wasn't able to find a way to mock the
// `BrowserTotpCaptureService` where jest would not load the file in the first place.
jest.mock("qrcode-parser", () => {});
describe("AddEditV2Component", () => {
let component: AddEditV2Component;
let fixture: ComponentFixture<AddEditV2Component>;
describe("AddEditComponent", () => {
let component: AddEditComponent;
let fixture: ComponentFixture<AddEditComponent>;
let addEditCipherInfo$: BehaviorSubject<AddEditCipherInfo | null>;
let cipherServiceMock: MockProxy<CipherService>;
@@ -85,7 +85,7 @@ describe("AddEditV2Component", () => {
});
await TestBed.configureTestingModule({
imports: [AddEditV2Component],
imports: [AddEditComponent],
providers: [
provideNoopAnimations(),
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
@@ -143,7 +143,7 @@ describe("AddEditV2Component", () => {
})
.compileComponents();
fixture = TestBed.createComponent(AddEditV2Component);
fixture = TestBed.createComponent(AddEditComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
@@ -443,111 +443,6 @@ describe("AddEditV2Component", () => {
}));
});
describe("archive", () => {
it("calls archiveCipherUtilsService service to archive the cipher", async () => {
buildConfigResponse.originalCipher = { id: "222-333-444-5555", edit: true } as Cipher;
queryParams$.next({ cipherId: "222-333-444-5555" });
await fixture.whenStable();
await component.archive();
expect(component["archiveCipherUtilsService"].archiveCipher).toHaveBeenCalledWith(
expect.objectContaining({ id: "222-333-444-5555" }),
true,
);
});
});
describe("unarchive", () => {
it("calls archiveCipherUtilsService service to unarchive the cipher", async () => {
buildConfigResponse.originalCipher = {
id: "222-333-444-5555",
archivedDate: new Date(),
edit: true,
} as Cipher;
queryParams$.next({ cipherId: "222-333-444-5555" });
await component.unarchive();
expect(component["archiveCipherUtilsService"].unarchiveCipher).toHaveBeenCalledWith(
expect.objectContaining({ id: "222-333-444-5555" }),
);
});
});
describe("archive button", () => {
beforeEach(() => {
// prevent form from rendering
jest.spyOn(component as any, "loading", "get").mockReturnValue(true);
buildConfigResponse.originalCipher = { archivedDate: undefined, edit: true } as Cipher;
});
it("shows the archive button when the user can archive and the cipher can be archived", fakeAsync(() => {
cipherArchiveService.userCanArchive$.mockReturnValue(of(true));
queryParams$.next({ cipherId: "222-333-444-5555" });
tick();
fixture.detectChanges();
const archiveBtn = fixture.debugElement.query(By.css("button[biticonbutton='bwi-archive']"));
expect(archiveBtn).toBeTruthy();
}));
it("does not show the archive button when the user cannot archive", fakeAsync(() => {
cipherArchiveService.userCanArchive$.mockReturnValue(of(false));
queryParams$.next({ cipherId: "222-333-444-5555" });
tick();
fixture.detectChanges();
const archiveBtn = fixture.debugElement.query(By.css("button[biticonbutton='bwi-archive']"));
expect(archiveBtn).toBeFalsy();
}));
it("does not show the archive button when the cipher cannot be archived", fakeAsync(() => {
cipherArchiveService.userCanArchive$.mockReturnValue(of(true));
buildConfigResponse.originalCipher = { archivedDate: new Date(), edit: true } as Cipher;
queryParams$.next({ cipherId: "222-333-444-5555" });
tick();
fixture.detectChanges();
const archiveBtn = fixture.debugElement.query(By.css("button[biticonbutton='bwi-archive']"));
expect(archiveBtn).toBeFalsy();
}));
});
describe("unarchive button", () => {
beforeEach(() => {
// prevent form from rendering
jest.spyOn(component as any, "loading", "get").mockReturnValue(true);
buildConfigResponse.originalCipher = { edit: true } as Cipher;
});
it("shows the unarchive button when the cipher is archived", fakeAsync(() => {
buildConfigResponse.originalCipher = { archivedDate: new Date(), edit: true } as Cipher;
tick();
fixture.detectChanges();
const unarchiveBtn = fixture.debugElement.query(
By.css("button[biticonbutton='bwi-unarchive']"),
);
expect(unarchiveBtn).toBeTruthy();
}));
it("does not show the unarchive button when the cipher is not archived", fakeAsync(() => {
queryParams$.next({ cipherId: "222-333-444-5555" });
tick();
fixture.detectChanges();
const unarchiveBtn = fixture.debugElement.query(
By.css("button[biticonbutton='bwi-unarchive']"),
);
expect(unarchiveBtn).toBeFalsy();
}));
});
describe("delete", () => {
it("dialogService openSimpleDialog called when deleteBtn is hit", async () => {
const dialogSpy = jest

View File

@@ -157,8 +157,8 @@ export type AddEditQueryParams = Partial<Record<keyof QueryParams, string>>;
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
selector: "app-add-edit-v2",
templateUrl: "add-edit-v2.component.html",
selector: "app-add-edit",
templateUrl: "add-edit.component.html",
providers: [
{ provide: CipherFormConfigService, useClass: DefaultCipherFormConfigService },
{ provide: TotpCaptureService, useClass: BrowserTotpCaptureService },
@@ -182,7 +182,7 @@ export type AddEditQueryParams = Partial<Record<keyof QueryParams, string>>;
BadgeModule,
],
})
export class AddEditV2Component implements OnInit, OnDestroy {
export class AddEditComponent implements OnInit, OnDestroy {
readonly cipherFormComponent = viewChild(CipherFormComponent);
headerText: string;
config: CipherFormConfig;
@@ -201,14 +201,6 @@ export class AddEditV2Component implements OnInit, OnDestroy {
return new CipherView(this.config?.originalCipher);
}
get canCipherBeArchived(): boolean {
return this.cipher?.canBeArchived;
}
get isCipherArchived(): boolean {
return this.cipher?.isArchived;
}
private fido2PopoutSessionData$ = fido2PopoutSessionData$();
private fido2PopoutSessionData: Fido2SessionData;
@@ -370,10 +362,6 @@ export class AddEditV2Component implements OnInit, OnDestroy {
await BrowserApi.sendMessage("addEditCipherSubmitted");
}
get isEditMode(): boolean {
return ["edit", "partial-edit"].includes(this.config?.mode);
}
subscribeToParams(): void {
this.route.queryParams
.pipe(
@@ -487,40 +475,6 @@ export class AddEditV2Component implements OnInit, OnDestroy {
return this.i18nService.t(translation[type]);
}
/**
* Update the cipher in the form after archiving/unarchiving.
* @param revisionDate The new revision date.
* @param archivedDate The new archived date (null if unarchived).
**/
updateCipherFromArchive = (revisionDate: Date, archivedDate: Date | null) => {
this.cipherFormComponent().patchCipher((current) => {
current.revisionDate = revisionDate;
current.archivedDate = archivedDate;
return current;
});
};
archive = async () => {
const cipherResponse = await this.archiveCipherUtilsService.archiveCipher(this.cipher, true);
if (!cipherResponse) {
return;
}
this.updateCipherFromArchive(
new Date(cipherResponse.revisionDate),
new Date(cipherResponse.archivedDate),
);
};
unarchive = async () => {
const cipherResponse = await this.archiveCipherUtilsService.unarchiveCipher(this.cipher);
if (!cipherResponse) {
return;
}
this.updateCipherFromArchive(new Date(cipherResponse.revisionDate), null);
};
delete = async () => {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "deleteItem" },

View File

@@ -23,7 +23,7 @@ import { PopupFooterComponent } from "../../../../../platform/popup/layout/popup
import { PopupHeaderComponent } from "../../../../../platform/popup/layout/popup-header.component";
import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.service";
import { AttachmentsV2Component } from "./attachments-v2.component";
import { AttachmentsComponent } from "./attachments.component";
@Component({
selector: "popup-header",
@@ -44,9 +44,9 @@ class MockPopupFooterComponent {
readonly pageTitle = input<string>();
}
describe("AttachmentsV2Component", () => {
let component: AttachmentsV2Component;
let fixture: ComponentFixture<AttachmentsV2Component>;
describe("AttachmentsComponent", () => {
let component: AttachmentsComponent;
let fixture: ComponentFixture<AttachmentsComponent>;
const queryParams = new BehaviorSubject<{ cipherId: string }>({ cipherId: "5555-444-3333" });
let cipherAttachment: CipherAttachmentsComponent;
const navigate = jest.fn();
@@ -60,7 +60,7 @@ describe("AttachmentsV2Component", () => {
navigate.mockClear();
await TestBed.configureTestingModule({
imports: [AttachmentsV2Component],
imports: [AttachmentsComponent],
providers: [
{ provide: LogService, useValue: mock<LogService>() },
{ provide: ConfigService, useValue: mock<ConfigService>() },
@@ -83,7 +83,7 @@ describe("AttachmentsV2Component", () => {
{ provide: OrganizationService, useValue: mock<OrganizationService>() },
],
})
.overrideComponent(AttachmentsV2Component, {
.overrideComponent(AttachmentsComponent, {
remove: {
imports: [PopupHeaderComponent, PopupFooterComponent],
},
@@ -95,7 +95,7 @@ describe("AttachmentsV2Component", () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(AttachmentsV2Component);
fixture = TestBed.createComponent(AttachmentsComponent);
component = fixture.componentInstance;
fixture.detectChanges();

View File

@@ -20,8 +20,8 @@ import { PopupRouterCacheService } from "../../../../../platform/popup/view-cach
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
selector: "app-attachments-v2",
templateUrl: "./attachments-v2.component.html",
selector: "app-attachments",
templateUrl: "./attachments.component.html",
imports: [
CommonModule,
ButtonModule,
@@ -33,7 +33,7 @@ import { PopupRouterCacheService } from "../../../../../platform/popup/view-cach
PopOutComponent,
],
})
export class AttachmentsV2Component {
export class AttachmentsComponent {
/** The `id` tied to the underlying HTMLFormElement */
attachmentFormId = CipherAttachmentsComponent.attachmentFormID;

View File

@@ -35,7 +35,7 @@ import { PasswordRepromptService } from "@bitwarden/vault";
import { BrowserPremiumUpgradePromptService } from "../../../services/browser-premium-upgrade-prompt.service";
import { VaultPopupAutofillService } from "../../../services/vault-popup-autofill.service";
import { AddEditQueryParams } from "../add-edit/add-edit-v2.component";
import { AddEditQueryParams } from "../add-edit/add-edit.component";
import {
AutofillConfirmationDialogComponent,
AutofillConfirmationDialogResult,

View File

@@ -21,13 +21,13 @@ import { ButtonModule, DialogService, MenuModule, NoItemsModule } from "@bitward
import { BrowserApi } from "../../../../../platform/browser/browser-api";
import BrowserPopupUtils from "../../../../../platform/browser/browser-popup-utils";
import { NewItemDropdownV2Component, NewItemInitialValues } from "./new-item-dropdown-v2.component";
import { NewItemDropdownComponent, NewItemInitialValues } from "./new-item-dropdown.component";
describe("NewItemDropdownV2Component", () => {
let component: NewItemDropdownV2Component;
let fixture: ComponentFixture<NewItemDropdownV2Component>;
describe("NewItemDropdownComponent", () => {
let component: NewItemDropdownComponent;
let fixture: ComponentFixture<NewItemDropdownComponent>;
let dialogServiceMock: jest.Mocked<DialogService>;
let browserApiMock: jest.Mocked<typeof BrowserApi>;
const browserApiMock: jest.Mocked<typeof BrowserApi> = mock<typeof BrowserApi>();
let restrictedItemTypesServiceMock: jest.Mocked<RestrictedItemTypesService>;
const mockTab = { url: "https://example.com" };
@@ -62,7 +62,7 @@ describe("NewItemDropdownV2Component", () => {
ButtonModule,
MenuModule,
NoItemsModule,
NewItemDropdownV2Component,
NewItemDropdownComponent,
],
providers: [
{ provide: I18nService, useValue: { t: (key: string) => key } },
@@ -80,7 +80,7 @@ describe("NewItemDropdownV2Component", () => {
});
beforeEach(() => {
fixture = TestBed.createComponent(NewItemDropdownV2Component);
fixture = TestBed.createComponent(NewItemDropdownComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@@ -15,7 +15,7 @@ import { AddEditFolderDialogComponent } from "@bitwarden/vault";
import { BrowserApi } from "../../../../../platform/browser/browser-api";
import BrowserPopupUtils from "../../../../../platform/browser/browser-popup-utils";
import { AddEditQueryParams } from "../add-edit/add-edit-v2.component";
import { AddEditQueryParams } from "../add-edit/add-edit.component";
export interface NewItemInitialValues {
folderId?: string;
@@ -27,10 +27,10 @@ export interface NewItemInitialValues {
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
selector: "app-new-item-dropdown",
templateUrl: "new-item-dropdown-v2.component.html",
templateUrl: "new-item-dropdown.component.html",
imports: [NoItemsModule, JslibModule, CommonModule, ButtonModule, RouterLink, MenuModule],
})
export class NewItemDropdownV2Component implements OnInit {
export class NewItemDropdownComponent implements OnInit {
cipherType = CipherType;
private tab?: chrome.tabs.Tab;
/**

View File

@@ -1,6 +1,6 @@
<div class="tw-flex tw-gap-1 tw-items-center">
<div class="tw-flex-1">
<app-vault-v2-search></app-vault-v2-search>
<app-vault-search></app-vault-search>
</div>
<div class="tw-relative">
<button

View File

@@ -24,18 +24,18 @@ import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault
import { PasswordRepromptService } from "@bitwarden/vault";
import { AutofillService } from "../../../../../autofill/services/abstractions/autofill.service";
import { VaultPopupItemsService } from "../../../../../vault/popup/services/vault-popup-items.service";
import { VaultPopupItemsService } from "../../../services/vault-popup-items.service";
import {
PopupListFilter,
VaultPopupListFiltersService,
} from "../../../../../vault/popup/services/vault-popup-list-filters.service";
} from "../../../services/vault-popup-list-filters.service";
import { VaultPopupLoadingService } from "../../../services/vault-popup-loading.service";
import { VaultHeaderV2Component } from "./vault-header-v2.component";
import { VaultHeaderComponent } from "./vault-header.component";
describe("VaultHeaderV2Component", () => {
let component: VaultHeaderV2Component;
let fixture: ComponentFixture<VaultHeaderV2Component>;
describe("VaultHeaderComponent", () => {
let component: VaultHeaderComponent;
let fixture: ComponentFixture<VaultHeaderComponent>;
const emptyForm: PopupListFilter = {
organization: null,
@@ -57,7 +57,7 @@ describe("VaultHeaderV2Component", () => {
update.mockClear();
await TestBed.configureTestingModule({
imports: [VaultHeaderV2Component, CommonModule],
imports: [VaultHeaderComponent, CommonModule],
providers: [
{
provide: CipherService,
@@ -112,7 +112,7 @@ describe("VaultHeaderV2Component", () => {
],
}).compileComponents();
fixture = TestBed.createComponent(VaultHeaderV2Component);
fixture = TestBed.createComponent(VaultHeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@@ -13,17 +13,17 @@ import {
} from "@bitwarden/components";
import { runInsideAngular } from "../../../../../platform/browser/run-inside-angular.operator";
import { VaultPopupListFiltersService } from "../../../../../vault/popup/services/vault-popup-list-filters.service";
import { VaultPopupListFiltersService } from "../../../services/vault-popup-list-filters.service";
import { VaultListFiltersComponent } from "../vault-list-filters/vault-list-filters.component";
import { VaultV2SearchComponent } from "../vault-search/vault-v2-search.component";
import { VaultSearchComponent } from "../vault-search/vault-search.component";
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
selector: "app-vault-header-v2",
templateUrl: "vault-header-v2.component.html",
selector: "app-vault-header",
templateUrl: "vault-header.component.html",
imports: [
VaultV2SearchComponent,
VaultSearchComponent,
VaultListFiltersComponent,
DisclosureComponent,
IconButtonModule,
@@ -32,7 +32,7 @@ import { VaultV2SearchComponent } from "../vault-search/vault-v2-search.componen
JslibModule,
],
})
export class VaultHeaderV2Component {
export class VaultHeaderComponent {
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-signals
@ViewChild(DisclosureComponent) disclosure: DisclosureComponent;

View File

@@ -16,10 +16,10 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.service";
import { PasswordHistoryV2Component } from "./vault-password-history-v2.component";
import { PasswordHistoryComponent } from "./vault-password-history.component";
describe("PasswordHistoryV2Component", () => {
let fixture: ComponentFixture<PasswordHistoryV2Component>;
describe("PasswordHistoryComponent", () => {
let fixture: ComponentFixture<PasswordHistoryComponent>;
const params$ = new Subject();
const mockUserId = "acct-1" as UserId;
@@ -40,7 +40,7 @@ describe("PasswordHistoryV2Component", () => {
getCipher.mockClear();
await TestBed.configureTestingModule({
imports: [PasswordHistoryV2Component],
imports: [PasswordHistoryComponent],
providers: [
{ provide: WINDOW, useValue: window },
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
@@ -56,7 +56,7 @@ describe("PasswordHistoryV2Component", () => {
],
}).compileComponents();
fixture = TestBed.createComponent(PasswordHistoryV2Component);
fixture = TestBed.createComponent(PasswordHistoryComponent);
fixture.detectChanges();
});

View File

@@ -21,8 +21,8 @@ import { PopupRouterCacheService } from "../../../../../platform/popup/view-cach
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
selector: "vault-password-history-v2",
templateUrl: "vault-password-history-v2.component.html",
selector: "vault-password-history",
templateUrl: "vault-password-history.component.html",
imports: [
JslibModule,
PopupPageComponent,
@@ -32,7 +32,7 @@ import { PopupRouterCacheService } from "../../../../../platform/popup/view-cach
NgIf,
],
})
export class PasswordHistoryV2Component implements OnInit {
export class PasswordHistoryComponent implements OnInit {
protected cipher: CipherView;
constructor(

View File

@@ -11,18 +11,18 @@ import { SearchModule } from "@bitwarden/components";
import { VaultPopupItemsService } from "../../../services/vault-popup-items.service";
import { VaultPopupLoadingService } from "../../../services/vault-popup-loading.service";
import { VaultV2SearchComponent } from "./vault-v2-search.component";
import { VaultSearchComponent } from "./vault-search.component";
describe("VaultV2SearchComponent", () => {
let component: VaultV2SearchComponent;
let fixture: ComponentFixture<VaultV2SearchComponent>;
describe("VaultSearchComponent", () => {
let component: VaultSearchComponent;
let fixture: ComponentFixture<VaultSearchComponent>;
const searchText$ = new BehaviorSubject("");
const loading$ = new BehaviorSubject(false);
const applyFilter = jest.fn();
const createComponent = () => {
fixture = TestBed.createComponent(VaultV2SearchComponent);
fixture = TestBed.createComponent(VaultSearchComponent);
component = fixture.componentInstance;
fixture.detectChanges();
};
@@ -31,7 +31,7 @@ describe("VaultV2SearchComponent", () => {
applyFilter.mockClear();
await TestBed.configureTestingModule({
imports: [VaultV2SearchComponent, CommonModule, SearchModule, JslibModule, FormsModule],
imports: [VaultSearchComponent, CommonModule, SearchModule, JslibModule, FormsModule],
providers: [
{
provide: VaultPopupItemsService,

View File

@@ -24,10 +24,10 @@ import { VaultPopupLoadingService } from "../../../services/vault-popup-loading.
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
imports: [CommonModule, SearchModule, JslibModule, FormsModule],
selector: "app-vault-v2-search",
templateUrl: "vault-v2-search.component.html",
selector: "app-vault-search",
templateUrl: "vault-search.component.html",
})
export class VaultV2SearchComponent {
export class VaultSearchComponent {
searchText: string = "";
private searchText$ = new Subject<string>();

View File

@@ -74,7 +74,7 @@
</ul>
</bit-spotlight>
</div>
<app-vault-header-v2></app-vault-header-v2>
<app-vault-header></app-vault-header>
</ng-container>
</ng-container>

View File

@@ -50,10 +50,10 @@ import { AtRiskPasswordCalloutComponent } from "../at-risk-callout/at-risk-passw
import { AutofillVaultListItemsComponent } from "./autofill-vault-list-items/autofill-vault-list-items.component";
import { BlockedInjectionBanner } from "./blocked-injection-banner/blocked-injection-banner.component";
import { NewItemDropdownV2Component } from "./new-item-dropdown/new-item-dropdown-v2.component";
import { VaultHeaderV2Component } from "./vault-header/vault-header-v2.component";
import { NewItemDropdownComponent } from "./new-item-dropdown/new-item-dropdown.component";
import { VaultHeaderComponent } from "./vault-header/vault-header.component";
import { VaultListItemsContainerComponent } from "./vault-list-items-container/vault-list-items-container.component";
import { VaultV2Component } from "./vault-v2.component";
import { VaultComponent } from "./vault.component";
@Component({
selector: "popup-header",
@@ -66,12 +66,12 @@ export class PopupHeaderStubComponent {
}
@Component({
selector: "app-vault-header-v2",
selector: "app-vault-header",
standalone: true,
template: "",
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class VaultHeaderV2StubComponent {}
export class VaultHeaderStubComponent {}
@Component({
selector: "app-current-account",
@@ -158,8 +158,8 @@ const autoConfirmDialogSpy = jest
jest.spyOn(BrowserApi, "isPopupOpen").mockResolvedValue(false);
jest.spyOn(BrowserPopupUtils, "openCurrentPagePopout").mockResolvedValue();
describe("VaultV2Component", () => {
let component: VaultV2Component;
describe("VaultComponent", () => {
let component: VaultComponent;
interface FakeAccount {
id: string;
@@ -242,7 +242,7 @@ describe("VaultV2Component", () => {
beforeEach(async () => {
jest.clearAllMocks();
await TestBed.configureTestingModule({
imports: [VaultV2Component, RouterTestingModule],
imports: [VaultComponent, RouterTestingModule],
providers: [
provideNoopAnimations(),
{ provide: VaultPopupItemsService, useValue: itemsSvc },
@@ -298,13 +298,13 @@ describe("VaultV2Component", () => {
schemas: [NO_ERRORS_SCHEMA],
}).compileComponents();
TestBed.overrideComponent(VaultV2Component, {
TestBed.overrideComponent(VaultComponent, {
remove: {
imports: [
PopupHeaderComponent,
VaultHeaderV2Component,
VaultHeaderComponent,
CurrentAccountComponent,
NewItemDropdownV2Component,
NewItemDropdownComponent,
PopOutComponent,
BlockedInjectionBanner,
AtRiskPasswordCalloutComponent,
@@ -318,7 +318,7 @@ describe("VaultV2Component", () => {
add: {
imports: [
PopupHeaderStubComponent,
VaultHeaderV2StubComponent,
VaultHeaderStubComponent,
CurrentAccountStubComponent,
NewItemDropdownStubComponent,
PopOutStubComponent,
@@ -331,7 +331,7 @@ describe("VaultV2Component", () => {
},
});
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
component = fixture.componentInstance;
});
@@ -393,7 +393,7 @@ describe("VaultV2Component", () => {
});
it("passes popup-page scroll region element to scroll position service", fakeAsync(() => {
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
const component = fixture.componentInstance;
const readySubject$ = component["readySubject"] as unknown as BehaviorSubject<boolean>;
@@ -491,7 +491,7 @@ describe("VaultV2Component", () => {
of(type === NudgeType.PremiumUpgrade),
);
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
const component = fixture.componentInstance;
void component.ngOnInit();
@@ -524,7 +524,7 @@ describe("VaultV2Component", () => {
return of(type === NudgeType.EmptyVaultNudge);
});
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
fixture.detectChanges();
tick();
@@ -541,7 +541,7 @@ describe("VaultV2Component", () => {
return of(type === NudgeType.HasVaultItems);
});
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
fixture.detectChanges();
tick();
@@ -559,7 +559,7 @@ describe("VaultV2Component", () => {
return of(type === NudgeType.PremiumUpgrade);
});
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
fixture.detectChanges();
tick();
@@ -575,7 +575,7 @@ describe("VaultV2Component", () => {
return of(type === NudgeType.PremiumUpgrade);
});
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
fixture.detectChanges();
tick();
@@ -591,7 +591,7 @@ describe("VaultV2Component", () => {
return of(type === NudgeType.PremiumUpgrade);
});
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
fixture.detectChanges();
tick();
@@ -602,7 +602,7 @@ describe("VaultV2Component", () => {
it("does not render app-autofill-vault-list-items or favorites item container when hasSearchText$ is true", () => {
itemsSvc.hasSearchText$.next(true);
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
component = fixture.componentInstance;
const readySubject$ = component["readySubject"];
@@ -628,7 +628,7 @@ describe("VaultV2Component", () => {
itemsSvc.hasSearchText$.next(false);
loadingSvc.loading$.next(false);
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
component = fixture.componentInstance;
const readySubject$ = component["readySubject"];
@@ -655,7 +655,7 @@ describe("VaultV2Component", () => {
filtersSvc.numberOfAppliedFilters$.next(0);
loadingSvc.loading$.next(false);
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
component = fixture.componentInstance;
const readySubject$ = component["readySubject"];
@@ -679,7 +679,7 @@ describe("VaultV2Component", () => {
itemsSvc.hasSearchText$.next(true);
loadingSvc.loading$.next(false);
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
component = fixture.componentInstance;
const readySubject$ = component["readySubject"];
@@ -704,7 +704,7 @@ describe("VaultV2Component", () => {
filtersSvc.numberOfAppliedFilters$.next(1);
loadingSvc.loading$.next(false);
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
component = fixture.componentInstance;
const readySubject$ = component["readySubject"];
@@ -735,7 +735,7 @@ describe("VaultV2Component", () => {
}),
);
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
const component = fixture.componentInstance;
void component.ngOnInit();
@@ -754,7 +754,7 @@ describe("VaultV2Component", () => {
}),
);
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
const component = fixture.componentInstance;
void component.ngOnInit();
@@ -773,7 +773,7 @@ describe("VaultV2Component", () => {
}),
);
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
const component = fixture.componentInstance;
void component.ngOnInit();
@@ -792,7 +792,7 @@ describe("VaultV2Component", () => {
}),
);
const fixture = TestBed.createComponent(VaultV2Component);
const fixture = TestBed.createComponent(VaultComponent);
const component = fixture.componentInstance;
void component.ngOnInit();

View File

@@ -71,10 +71,10 @@ import { VaultLoadingSkeletonComponent } from "../vault-loading-skeleton/vault-l
import { BlockedInjectionBanner } from "./blocked-injection-banner/blocked-injection-banner.component";
import {
NewItemDropdownV2Component,
NewItemDropdownComponent,
NewItemInitialValues,
} from "./new-item-dropdown/new-item-dropdown-v2.component";
import { VaultHeaderV2Component } from "./vault-header/vault-header-v2.component";
} from "./new-item-dropdown/new-item-dropdown.component";
import { VaultHeaderComponent } from "./vault-header/vault-header.component";
import { AutofillVaultListItemsComponent, VaultListItemsContainerComponent } from ".";
@@ -90,7 +90,7 @@ type VaultState = UnionOfValues<typeof VaultState>;
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
selector: "app-vault",
templateUrl: "vault-v2.component.html",
templateUrl: "vault.component.html",
imports: [
BlockedInjectionBanner,
PopupPageComponent,
@@ -103,9 +103,9 @@ type VaultState = UnionOfValues<typeof VaultState>;
AutofillVaultListItemsComponent,
VaultListItemsContainerComponent,
ButtonModule,
NewItemDropdownV2Component,
NewItemDropdownComponent,
ScrollingModule,
VaultHeaderV2Component,
VaultHeaderComponent,
AtRiskPasswordCalloutComponent,
SpotlightComponent,
RouterModule,
@@ -116,7 +116,7 @@ type VaultState = UnionOfValues<typeof VaultState>;
],
providers: [{ provide: VaultItemsTransferService, useClass: DefaultVaultItemsTransferService }],
})
export class VaultV2Component implements OnInit, OnDestroy {
export class VaultComponent implements OnInit, OnDestroy {
NudgeType = NudgeType;
cipherType = CipherType;
private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);

View File

@@ -45,19 +45,19 @@ import {
import { BrowserApi } from "../../../../../platform/browser/browser-api";
import BrowserPopupUtils from "../../../../../platform/browser/browser-popup-utils";
import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.service";
import { VaultPopupAutofillService } from "../../../services/vault-popup-autofill.service";
import { VaultPopupScrollPositionService } from "../../../services/vault-popup-scroll-position.service";
import { VaultPopupAutofillService } from "./../../../services/vault-popup-autofill.service";
import { ViewV2Component } from "./view-v2.component";
import { ViewComponent } from "./view.component";
// 'qrcode-parser' is used by `BrowserTotpCaptureService` but is an es6 module that jest can't compile.
// Mock the entire module here to prevent jest from throwing an error. I wasn't able to find a way to mock the
// `BrowserTotpCaptureService` where jest would not load the file in the first place.
jest.mock("qrcode-parser", () => {});
describe("ViewV2Component", () => {
let component: ViewV2Component;
let fixture: ComponentFixture<ViewV2Component>;
describe("ViewComponent", () => {
let component: ViewComponent;
let fixture: ComponentFixture<ViewComponent>;
const params$ = new Subject();
const mockNavigate = jest.fn();
const collect = jest.fn().mockResolvedValue(null);
@@ -124,7 +124,7 @@ describe("ViewV2Component", () => {
cipherArchiveService.unarchiveWithServer.mockResolvedValue({ id: "122-333-444" } as CipherData);
await TestBed.configureTestingModule({
imports: [ViewV2Component],
imports: [ViewComponent],
providers: [
{ provide: Router, useValue: { navigate: mockNavigate } },
{ provide: CipherService, useValue: mockCipherService },
@@ -231,7 +231,7 @@ describe("ViewV2Component", () => {
})
.compileComponents();
fixture = TestBed.createComponent(ViewV2Component);
fixture = TestBed.createComponent(ViewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
(component as any).showFooter$ = of(true);

View File

@@ -56,17 +56,16 @@ import { sendExtensionMessage } from "../../../../../autofill/utils/index";
import { BrowserApi } from "../../../../../platform/browser/browser-api";
import BrowserPopupUtils from "../../../../../platform/browser/browser-popup-utils";
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";
import { PopupRouterCacheService } from "../../../../../platform/popup/view-cache/popup-router-cache.service";
import { BrowserPremiumUpgradePromptService } from "../../../services/browser-premium-upgrade-prompt.service";
import { BrowserViewPasswordHistoryService } from "../../../services/browser-view-password-history.service";
import { VaultPopupAutofillService } from "../../../services/vault-popup-autofill.service";
import { VaultPopupScrollPositionService } from "../../../services/vault-popup-scroll-position.service";
import { closeViewVaultItemPopout, VaultPopoutType } from "../../../utils/vault-popout-window";
import { ROUTES_AFTER_EDIT_DELETION } from "../add-edit/add-edit-v2.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";
import { VaultPopupAutofillService } from "./../../../services/vault-popup-autofill.service";
import { ROUTES_AFTER_EDIT_DELETION } from "../add-edit/add-edit.component";
/**
* The types of actions that can be triggered when loading the view vault item popout via the
@@ -83,8 +82,8 @@ type LoadAction =
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
selector: "app-view-v2",
templateUrl: "view-v2.component.html",
selector: "app-view",
templateUrl: "view.component.html",
imports: [
CommonModule,
SearchModule,
@@ -107,7 +106,7 @@ type LoadAction =
{ provide: ChangeLoginPasswordService, useClass: DefaultChangeLoginPasswordService },
],
})
export class ViewV2Component {
export class ViewComponent {
private activeUserId: UserId;
headerText: string;

View File

@@ -1,7 +1,7 @@
import { TestBed } from "@angular/core/testing";
import { RouterStateSnapshot } from "@angular/router";
import { VaultV2Component } from "../components/vault-v2/vault-v2.component";
import { VaultComponent } from "../components/vault/vault.component";
import { VaultPopupItemsService } from "../services/vault-popup-items.service";
import { VaultPopupListFiltersService } from "../services/vault-popup-list-filters.service";
@@ -42,7 +42,7 @@ describe("clearVaultStateGuard", () => {
const nextState = { url } as RouterStateSnapshot;
const result = TestBed.runInInjectionContext(() =>
clearVaultStateGuard({} as VaultV2Component, null, null, nextState),
clearVaultStateGuard({} as VaultComponent, null, null, nextState),
);
expect(result).toBe(true);
@@ -56,7 +56,7 @@ describe("clearVaultStateGuard", () => {
const nextState = { url } as RouterStateSnapshot;
const result = TestBed.runInInjectionContext(() =>
clearVaultStateGuard({} as VaultV2Component, null, null, nextState),
clearVaultStateGuard({} as VaultComponent, null, null, nextState),
);
expect(result).toBe(true);
@@ -67,7 +67,7 @@ describe("clearVaultStateGuard", () => {
it("should not clear vault state when not changing states", () => {
const result = TestBed.runInInjectionContext(() =>
clearVaultStateGuard({} as VaultV2Component, null, null, null),
clearVaultStateGuard({} as VaultComponent, null, null, null),
);
expect(result).toBe(true);

View File

@@ -1,7 +1,7 @@
import { inject } from "@angular/core";
import { CanDeactivateFn } from "@angular/router";
import { VaultV2Component } from "../components/vault-v2/vault-v2.component";
import { VaultComponent } from "../components/vault/vault.component";
import { VaultPopupItemsService } from "../services/vault-popup-items.service";
import { VaultPopupListFiltersService } from "../services/vault-popup-list-filters.service";
@@ -10,8 +10,8 @@ import { VaultPopupListFiltersService } from "../services/vault-popup-list-filte
* This ensures the search and filter state is reset when navigating between different tabs,
* except viewing or editing a cipher.
*/
export const clearVaultStateGuard: CanDeactivateFn<VaultV2Component> = (
component: VaultV2Component,
export const clearVaultStateGuard: CanDeactivateFn<VaultComponent> = (
component: VaultComponent,
currentRoute,
currentState,
nextState,

View File

@@ -7,7 +7,7 @@ import { firstValueFrom } from "rxjs";
import { DialogService } from "@bitwarden/components";
import { CipherFormGenerationService } from "@bitwarden/vault";
import { VaultGeneratorDialogComponent } from "../components/vault-v2/vault-generator-dialog/vault-generator-dialog.component";
import { VaultGeneratorDialogComponent } from "../components/vault/vault-generator-dialog/vault-generator-dialog.component";
@Injectable()
export class BrowserCipherFormGenerationService implements CipherFormGenerationService {

View File

@@ -69,7 +69,7 @@ describe("VaultPopupItemsService", () => {
const accountServiceMock = mockAccountServiceWith(userId);
const configServiceMock = mock<ConfigService>();
const cipherArchiveServiceMock = mock<CipherArchiveService>();
cipherArchiveServiceMock.userCanArchive$.mockReturnValue(of(true));
cipherArchiveServiceMock.hasArchiveFlagEnabled$ = of(true);
const restrictedItemTypesService = {
restricted$: new BehaviorSubject<RestrictedCipherType[]>([]),

View File

@@ -135,24 +135,23 @@ export class VaultPopupItemsService {
shareReplay({ refCount: true, bufferSize: 1 }),
);
private userCanArchive$ = this.activeUserId$.pipe(
switchMap((userId) => {
return this.cipherArchiveService.userCanArchive$(userId);
}),
);
private _activeCipherList$: Observable<PopupCipherViewLike[]> = this._allDecryptedCiphers$.pipe(
switchMap((ciphers) =>
combineLatest([this.organizations$, this.decryptedCollections$, this.userCanArchive$]).pipe(
map(([organizations, collections, canArchive]) => {
combineLatest([
this.organizations$,
this.decryptedCollections$,
this.cipherArchiveService.hasArchiveFlagEnabled$,
]).pipe(
map(([organizations, collections, archiveFlag]) => {
const orgMap = Object.fromEntries(organizations.map((org) => [org.id, org]));
const collectionMap = Object.fromEntries(collections.map((col) => [col.id, col]));
return ciphers
.filter(
(c) =>
!CipherViewLikeUtils.isDeleted(c) &&
(!canArchive || !CipherViewLikeUtils.isArchived(c)),
(!archiveFlag || !CipherViewLikeUtils.isArchived(c)),
)
.map((cipher) => {
(cipher as PopupCipherViewLike).collections = cipher.collectionIds?.map(
(colId) => collectionMap[colId as CollectionId],

View File

@@ -20,7 +20,7 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
import { PopupSizeService } from "../../../platform/popup/layout/popup-size.service";
import { VaultPopupCopyButtonsService } from "../services/vault-popup-copy-buttons.service";
import { AppearanceV2Component } from "./appearance-v2.component";
import { AppearanceComponent } from "./appearance.component";
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@@ -49,9 +49,9 @@ class MockPopupPageComponent {
@Input() loading: boolean;
}
describe("AppearanceV2Component", () => {
let component: AppearanceV2Component;
let fixture: ComponentFixture<AppearanceV2Component>;
describe("AppearanceComponent", () => {
let component: AppearanceComponent;
let fixture: ComponentFixture<AppearanceComponent>;
const showFavicons$ = new BehaviorSubject<boolean>(true);
const enableBadgeCounter$ = new BehaviorSubject<boolean>(true);
@@ -80,7 +80,7 @@ describe("AppearanceV2Component", () => {
setEnableRoutingAnimation.mockClear();
await TestBed.configureTestingModule({
imports: [AppearanceV2Component],
imports: [AppearanceComponent],
providers: [
{ provide: ConfigService, useValue: mock<ConfigService>() },
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
@@ -120,7 +120,7 @@ describe("AppearanceV2Component", () => {
},
],
})
.overrideComponent(AppearanceV2Component, {
.overrideComponent(AppearanceComponent, {
remove: {
imports: [PopupHeaderComponent, PopupPageComponent],
},
@@ -130,7 +130,7 @@ describe("AppearanceV2Component", () => {
})
.compileComponents();
fixture = TestBed.createComponent(AppearanceV2Component);
fixture = TestBed.createComponent(AppearanceComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@@ -36,7 +36,7 @@ import { VaultPopupCopyButtonsService } from "../services/vault-popup-copy-butto
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
templateUrl: "./appearance-v2.component.html",
templateUrl: "./appearance.component.html",
imports: [
CommonModule,
JslibModule,
@@ -52,7 +52,7 @@ import { VaultPopupCopyButtonsService } from "../services/vault-popup-copy-butto
PermitCipherDetailsPopoverComponent,
],
})
export class AppearanceV2Component implements OnInit {
export class AppearanceComponent implements OnInit {
private compactModeService = inject(PopupCompactModeService);
private copyButtonsService = inject(VaultPopupCopyButtonsService);
private popupSizeService = inject(PopupSizeService);

View File

@@ -42,7 +42,7 @@ import {
import { PopOutComponent } from "../../../platform/popup/components/pop-out.component";
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
import { ROUTES_AFTER_EDIT_DELETION } from "../components/vault-v2/add-edit/add-edit-v2.component";
import { ROUTES_AFTER_EDIT_DELETION } from "../components/vault/add-edit/add-edit.component";
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection

View File

@@ -19,7 +19,7 @@ import { AddEditFolderDialogComponent } from "@bitwarden/vault";
import { PopupFooterComponent } from "../../../platform/popup/layout/popup-footer.component";
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
import { FoldersV2Component } from "./folders-v2.component";
import { FoldersComponent } from "./folders.component";
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@@ -48,9 +48,9 @@ class MockPopupFooterComponent {
@Input() pageTitle: string = "";
}
describe("FoldersV2Component", () => {
let component: FoldersV2Component;
let fixture: ComponentFixture<FoldersV2Component>;
describe("FoldersComponent", () => {
let component: FoldersComponent;
let fixture: ComponentFixture<FoldersComponent>;
const folderViews$ = new BehaviorSubject<FolderView[]>([]);
const open = jest.spyOn(AddEditFolderDialogComponent, "open");
const mockDialogService = { open: jest.fn() };
@@ -59,7 +59,7 @@ describe("FoldersV2Component", () => {
open.mockClear();
await TestBed.configureTestingModule({
imports: [FoldersV2Component],
imports: [FoldersComponent],
providers: [
{ provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() },
{ provide: ConfigService, useValue: mock<ConfigService>() },
@@ -69,7 +69,7 @@ describe("FoldersV2Component", () => {
{ provide: AccountService, useValue: mockAccountServiceWith("UserId" as UserId) },
],
})
.overrideComponent(FoldersV2Component, {
.overrideComponent(FoldersComponent, {
remove: {
imports: [PopupHeaderComponent, PopupFooterComponent],
},
@@ -80,7 +80,7 @@ describe("FoldersV2Component", () => {
.overrideProvider(DialogService, { useValue: mockDialogService })
.compileComponents();
fixture = TestBed.createComponent(FoldersV2Component);
fixture = TestBed.createComponent(FoldersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@@ -25,7 +25,7 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
templateUrl: "./folders-v2.component.html",
templateUrl: "./folders.component.html",
imports: [
CommonModule,
JslibModule,
@@ -39,7 +39,7 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
AsyncActionsModule,
],
})
export class FoldersV2Component {
export class FoldersComponent {
folders$: Observable<FolderView[]>;
NoFoldersIcon = NoFolders;

View File

@@ -19,7 +19,7 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
templateUrl: "more-from-bitwarden-page-v2.component.html",
templateUrl: "more-from-bitwarden-page.component.html",
imports: [
CommonModule,
JslibModule,
@@ -30,7 +30,7 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
ItemModule,
],
})
export class MoreFromBitwardenPageV2Component {
export class MoreFromBitwardenPageComponent {
protected familySponsorshipAvailable$: Observable<boolean>;
protected isFreeFamilyPolicyEnabled$: Observable<boolean>;
protected hasSingleEnterpriseOrg$: Observable<boolean>;

View File

@@ -19,7 +19,7 @@ import { PopOutComponent } from "../../../platform/popup/components/pop-out.comp
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
import { VaultSettingsV2Component } from "./vault-settings-v2.component";
import { VaultSettingsComponent } from "./vault-settings.component";
@Component({
selector: "popup-header",
@@ -47,9 +47,9 @@ class MockPopOutComponent {
readonly show = input(true);
}
describe("VaultSettingsV2Component", () => {
let component: VaultSettingsV2Component;
let fixture: ComponentFixture<VaultSettingsV2Component>;
describe("VaultSettingsComponent", () => {
let component: VaultSettingsComponent;
let fixture: ComponentFixture<VaultSettingsComponent>;
let router: Router;
let mockCipherArchiveService: jest.Mocked<CipherArchiveService>;
@@ -90,11 +90,11 @@ describe("VaultSettingsV2Component", () => {
mockCipherArchiveService.hasArchiveFlagEnabled$ = mockHasArchiveFlagEnabled$.asObservable();
await TestBed.configureTestingModule({
imports: [VaultSettingsV2Component],
imports: [VaultSettingsComponent],
providers: [
provideRouter([
{ path: "archive", component: VaultSettingsV2Component },
{ path: "premium", component: VaultSettingsV2Component },
{ path: "archive", component: VaultSettingsComponent },
{ path: "premium", component: VaultSettingsComponent },
]),
{ provide: SyncService, useValue: mock<SyncService>() },
{ provide: ToastService, useValue: mock<ToastService>() },
@@ -117,7 +117,7 @@ describe("VaultSettingsV2Component", () => {
},
],
})
.overrideComponent(VaultSettingsV2Component, {
.overrideComponent(VaultSettingsComponent, {
remove: {
imports: [PopupHeaderComponent, PopupPageComponent, PopOutComponent],
},
@@ -127,7 +127,7 @@ describe("VaultSettingsV2Component", () => {
})
.compileComponents();
fixture = TestBed.createComponent(VaultSettingsV2Component);
fixture = TestBed.createComponent(VaultSettingsComponent);
component = fixture.componentInstance;
router = TestBed.inject(Router);
jest.spyOn(router, "navigate");

View File

@@ -23,7 +23,7 @@ import { BrowserPremiumUpgradePromptService } from "../services/browser-premium-
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
templateUrl: "vault-settings-v2.component.html",
templateUrl: "vault-settings.component.html",
imports: [
CommonModule,
JslibModule,
@@ -39,7 +39,7 @@ import { BrowserPremiumUpgradePromptService } from "../services/browser-premium-
{ provide: PremiumUpgradePromptService, useClass: BrowserPremiumUpgradePromptService },
],
})
export class VaultSettingsV2Component implements OnInit, OnDestroy {
export class VaultSettingsComponent implements OnInit, OnDestroy {
private readonly premiumBadgeComponent = viewChild(PremiumBadgeComponent);
lastSync = "--";

View File

@@ -83,7 +83,7 @@
"multer": "2.0.2",
"node-fetch": "2.7.0",
"node-forge": "1.3.2",
"open": "11.0.0",
"open": "8.4.2",
"papaparse": "5.5.3",
"proper-lockfile": "4.1.2",
"rxjs": "7.8.1",

View File

@@ -99,9 +99,6 @@ export class ArchiveCommand {
errorMessage: "Item is in the trash, the item must be restored before archiving.",
};
}
case cipher.organizationId != null: {
return { canArchive: false, errorMessage: "Cannot archive items in an organization." };
}
default:
return { canArchive: true };
}

View File

@@ -13,7 +13,7 @@ xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/re
IgnorableNamespaces="uap rescap com uap10 build"
xmlns:build="http://schemas.microsoft.com/developer/appx/2015/build">
<!-- use single quotes to avoid double quotes escaping in the publisher value -->
<Identity Name="${applicationId}"
<Identity Name="${identityName}"
ProcessorArchitecture="${arch}"
Publisher='${publisher}'
Version="${version}" />

View File

@@ -64,7 +64,7 @@
"customManifestPath": "./custom-appx-manifest.xml",
"applicationId": "BitwardenBeta",
"identityName": "8bitSolutionsLLC.BitwardenBeta",
"publisher": "CN=Bitwarden Inc., O=Bitwarden Inc., L=Santa Barbara, S=California, C=US, SERIALNUMBER=7654941, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US",
"publisher": "CN=14D52771-DE3C-4886-B8BF-825BA7690418",
"publisherDisplayName": "Bitwarden Inc",
"languages": [
"en-US",

View File

@@ -179,7 +179,7 @@
"customManifestPath": "./custom-appx-manifest.xml",
"applicationId": "bitwardendesktop",
"identityName": "8bitSolutionsLLC.bitwardendesktop",
"publisher": "CN=Bitwarden Inc., O=Bitwarden Inc., L=Santa Barbara, S=California, C=US, SERIALNUMBER=7654941, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US",
"publisher": "CN=14D52771-DE3C-4886-B8BF-825BA7690418",
"publisherDisplayName": "Bitwarden Inc",
"languages": [
"en-US",

View File

@@ -176,6 +176,7 @@ $translationMap = @{
'applicationId' = $builderConfig.appx.applicationId
'displayName' = $productName
'executable' = "app\${productName}.exe"
'identityName' = $builderConfig.appx.identityName
'publisher' = $builderConfig.appx.publisher
'publisherDisplayName' = $builderConfig.appx.publisherDisplayName
'version' = $version

View File

@@ -3,7 +3,7 @@ const child_process = require("child_process");
exports.default = async function (configuration) {
const ext = configuration.path.split(".").at(-1);
if (parseInt(process.env.ELECTRON_BUILDER_SIGN) === 1 && ["exe", "appx"].includes(ext)) {
if (parseInt(process.env.ELECTRON_BUILDER_SIGN) === 1 && ["exe"].includes(ext)) {
console.log(`[*] Signing file: ${configuration.path}`);
child_process.execFileSync(
"azuresigntool",

View File

@@ -273,7 +273,7 @@ export class AppComponent implements OnInit, OnDestroy {
this.loading = false;
break;
case "lockVault":
await this.lockService.lock(message.userId);
await this.lockService.lock(message.userId ?? this.activeUserId);
break;
case "lockAllVaults": {
await this.lockService.lockAll();

View File

@@ -4486,6 +4486,9 @@
"sessionTimeoutSettingsAction": {
"message": "Timeout action"
},
"errorCannotDecrypt": {
"message": "Error: Cannot decrypt"
},
"sessionTimeoutHeader": {
"message": "Session timeout"
},

View File

@@ -55,6 +55,11 @@ export class WindowMain {
// Perform a hard reload of the render process by crashing it. This is suboptimal but ensures that all memory gets
// cleared, as the process itself will be completely garbage collected.
ipcMain.on("reload-process", async () => {
if (isDev()) {
this.logService.info("Process reload requested, but skipping in development mode");
return;
}
this.logService.info("Reloading render process");
// User might have changed theme, ensure the window is updated.
this.win.setBackgroundColor(await this.getBackgroundColor());

View File

@@ -263,15 +263,12 @@ export class ItemFooterComponent implements OnInit, OnChanges {
this.userCanArchive = userCanArchive;
this.showArchiveButton =
cipherCanBeArchived &&
userCanArchive &&
(this.action === "view" || this.action === "edit") &&
!this.cipher.isArchived;
cipherCanBeArchived && userCanArchive && this.action === "view" && !this.cipher.isArchived;
// A user should always be able to unarchive an archived item
this.showUnarchiveButton =
hasArchiveFlagEnabled &&
(this.action === "view" || this.action === "edit") &&
this.action === "view" &&
this.cipher.isArchived &&
!this.cipher.isDeleted;
}

View File

@@ -0,0 +1,22 @@
<bit-simple-dialog hideIcon>
<div bitDialogContent>
<div class="tw-flex tw-justify-center">
<div class="tw-mt-1 tw-w-[273px]">
<bit-progress
[showText]="false"
size="default"
bgColor="primary"
[barWidth]="progressPercentage()"
></bit-progress>
</div>
</div>
<div class="tw-flex tw-flex-col tw-gap-2 tw-mt-6">
<h3 class="tw-font-semibold">
{{ "bulkReinviteProgressTitle" | i18n: progressCount() : allCount }}
</h3>
<span class="tw-text-sm">
{{ "bulkReinviteProgressSubtitle" | i18n }}
</span>
</div>
</div>
</bit-simple-dialog>

View File

@@ -0,0 +1,46 @@
import { DialogConfig, DialogRef } from "@angular/cdk/dialog";
import {
ChangeDetectionStrategy,
Component,
computed,
effect,
Inject,
Signal,
} from "@angular/core";
import { DIALOG_DATA, DialogService } from "@bitwarden/components";
export interface BulkProgressDialogParams {
progress: Signal<number>;
allCount: number;
}
@Component({
templateUrl: "bulk-progress-dialog.component.html",
selector: "member-bulk-progress-dialog",
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class BulkProgressDialogComponent {
protected allCount: string;
protected readonly progressCount: Signal<string>;
protected readonly progressPercentage: Signal<number>;
private readonly progressEffect = effect(() => {
if (this.progressPercentage() >= 100) {
this.dialogRef.close();
}
});
constructor(
public dialogRef: DialogRef,
@Inject(DIALOG_DATA) data: BulkProgressDialogParams,
) {
this.progressCount = computed(() => data.progress().toLocaleString());
this.allCount = data.allCount.toLocaleString();
this.progressPercentage = computed(() => (data.progress() / data.allCount) * 100);
}
static open(dialogService: DialogService, config: DialogConfig<BulkProgressDialogParams>) {
return dialogService.open(BulkProgressDialogComponent, config);
}
}

View File

@@ -0,0 +1,70 @@
<bit-dialog dialogSize="large">
@let failCount = dataSource().data.length;
<div bitDialogTitle>
@if (failCount > 1) {
{{ "bulkReinviteFailuresTitle" | i18n: failCount }}
} @else {
{{ "bulkReinviteFailureTitle" | i18n }}
}
</div>
<div bitDialogContent>
{{ "bulkReinviteFailureDescription" | i18n: failCount : totalCount }}
<a bitLink href="https://bitwarden.com/contact/" target="_blank" rel="noopener noreferrer">
{{ "contactSupportShort" | i18n | lowercase }}
<bit-icon name="bwi-external-link"></bit-icon>
</a>
<div class="tw-max-h-[304px] tw-overflow-auto tw-mt-4">
<bit-table [dataSource]="dataSource()">
<ng-container header>
<tr>
<th bitCell bitSortable="name">{{ "name" | i18n }}</th>
</tr>
</ng-container>
<ng-template body let-rows$>
@let rows = $any(rows$ | async);
@for (u of rows; track u.id) {
<tr bitRow class="tw-h-16">
<td bitCell>
<div class="tw-flex tw-items-center">
<bit-avatar
size="small"
[text]="u | userName"
[id]="u.userId"
[color]="u.avatarColor"
class="tw-mr-3"
></bit-avatar>
<div class="tw-flex tw-flex-col">
<div class="tw-flex tw-flex-row tw-gap-2">
<button type="button" bitLink>
{{ u.name ?? u.email }}
</button>
</div>
@if (u.name) {
<div class="tw-text-sm tw-text-muted">
{{ u.email }}
</div>
}
</div>
</div>
</td>
</tr>
}
</ng-template>
</bit-table>
</div>
</div>
<ng-container bitDialogFooter>
<button bitButton buttonType="primary" type="button" (click)="resendInvitations()">
{{ "bulkResendInvitations" | i18n }}
</button>
<button bitButton buttonType="secondary" type="button" (click)="cancel()">
{{ "cancel" | i18n }}
</button>
</ng-container>
</bit-dialog>

View File

@@ -0,0 +1,62 @@
import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { ChangeDetectionStrategy, Component, Inject, signal, WritableSignal } from "@angular/core";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { DialogService } from "@bitwarden/components";
import { MembersTableDataSource } from "@bitwarden/web-vault/app/admin-console/common/people-table-data-source";
import { OrganizationUserView } from "../../../core";
import {
BulkActionResult,
MemberActionsService,
} from "../../services/member-actions/member-actions.service";
export interface BulkReinviteFailureDialogParams {
result: BulkActionResult;
users: OrganizationUserView[];
organization: Organization;
}
@Component({
templateUrl: "bulk-reinvite-failure-dialog.component.html",
selector: "member-bulk-reinvite-failure-dialog",
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class BulkReinviteFailureDialogComponent {
private organization: Organization;
protected totalCount: string;
protected readonly dataSource: WritableSignal<MembersTableDataSource>;
constructor(
public dialogRef: DialogRef,
private memberActionsService: MemberActionsService,
@Inject(DIALOG_DATA) data: BulkReinviteFailureDialogParams,
environmentService: EnvironmentService,
) {
this.organization = data.organization;
this.totalCount = (data.users.length ?? 0).toLocaleString();
this.dataSource = signal(new MembersTableDataSource(environmentService));
this.dataSource().data = data.result.failed.map((failedUser) => {
const user = data.users.find((u) => u.id === failedUser.id);
if (user == null) {
throw new Error("Member not found");
}
return user;
});
}
async resendInvitations() {
await this.memberActionsService.bulkReinvite(this.organization, this.dataSource().data);
this.dialogRef.close();
}
async cancel() {
this.dialogRef.close();
}
static open(dialogService: DialogService, config: DialogConfig<BulkReinviteFailureDialogParams>) {
return dialogService.open(BulkReinviteFailureDialogComponent, config);
}
}

View File

@@ -444,10 +444,7 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
}
try {
const result = await this.memberActionsService.bulkReinvite(
organization,
filteredUsers.map((user) => user.id as UserId),
);
const result = await this.memberActionsService.bulkReinvite(organization, filteredUsers);
if (!result.successful) {
throw new Error();
@@ -472,7 +469,10 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
} else {
this.toastService.showToast({
variant: "success",
message: this.i18nService.t("bulkReinviteSuccessToast", invitedCount.toString()),
message:
invitedCount === 1
? this.i18nService.t("reinviteSuccessToast")
: this.i18nService.t("bulkReinviteSentToast", invitedCount.toString()),
});
}
} else {

View File

@@ -113,25 +113,24 @@
<th bitCell>{{ "policies" | i18n }}</th>
<th bitCell class="tw-w-10">
@if (showUserManagementControls()) {
<th bitCell>
<div class="tw-flex tw-flex-row tw-items-center tw-justify-end tw-gap-2">
<button
type="button"
bitIconButton="bwi-download"
size="small"
[bitAction]="exportMembers"
[disabled]="!firstLoaded"
label="{{ 'export' | i18n }}"
></button>
<button
[bitMenuTriggerFor]="headerMenu"
type="button"
bitIconButton="bwi-ellipsis-v"
size="small"
label="{{ 'options' | i18n }}"
></button>
</div>
</th>
<div class="tw-flex tw-flex-row tw-items-center tw-justify-end tw-gap-2">
<button
type="button"
bitIconButton="bwi-download"
size="small"
[bitAction]="exportMembers"
[disabled]="!firstLoaded"
label="{{ 'export' | i18n }}"
></button>
<button
[bitMenuTriggerFor]="headerMenu"
type="button"
bitIconButton="bwi-ellipsis-v"
size="small"
label="{{ 'options' | i18n }}"
*ngIf="showUserManagementControls()"
></button>
</div>
}
<bit-menu #headerMenu>

View File

@@ -519,7 +519,7 @@ describe("vNextMembersComponent", () => {
await component.bulkReinvite(mockOrg);
expect(mockMemberActionsService.bulkReinvite).toHaveBeenCalledWith(mockOrg, [invitedUser.id]);
expect(mockMemberActionsService.bulkReinvite).toHaveBeenCalledWith(mockOrg, [invitedUser]);
expect(mockMemberDialogManager.openBulkStatusDialog).toHaveBeenCalled();
});

Some files were not shown because too many files have changed in this diff Show More