mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[PM-13811] Remove conditional code for extension refresh on web (#13145)
* Enable UI refresh on web by default Removing all conditional code around the `ExtensionRefresh`-feature-flag on the web-UI * Remove no longer needed extensRefresh helpers --------- Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
7e2e604439
commit
9ddaf96020
@@ -22,7 +22,7 @@
|
||||
[routerLink]="[]"
|
||||
[queryParams]="{ itemId: cipher.id, action: clickAction }"
|
||||
queryParamsHandling="merge"
|
||||
[replaceUrl]="extensionRefreshEnabled"
|
||||
[replaceUrl]="true"
|
||||
title="{{ 'editItemWithName' | i18n: cipher.name }}"
|
||||
type="button"
|
||||
appStopProp
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { CollectionView } from "@bitwarden/admin-console/common";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@@ -25,11 +22,6 @@ import { RowHeightClass } from "./vault-items.component";
|
||||
export class VaultCipherRowComponent implements OnInit {
|
||||
protected RowHeightClass = RowHeightClass;
|
||||
|
||||
/**
|
||||
* Flag to determine if the extension refresh feature flag is enabled.
|
||||
*/
|
||||
protected extensionRefreshEnabled = false;
|
||||
|
||||
@Input() disabled: boolean;
|
||||
@Input() cipher: CipherView;
|
||||
@Input() showOwner: boolean;
|
||||
@@ -61,19 +53,12 @@ export class VaultCipherRowComponent implements OnInit {
|
||||
];
|
||||
protected organization?: Organization;
|
||||
|
||||
constructor(
|
||||
private configService: ConfigService,
|
||||
private i18nService: I18nService,
|
||||
) {}
|
||||
constructor(private i18nService: I18nService) {}
|
||||
|
||||
/**
|
||||
* Lifecycle hook for component initialization.
|
||||
* Checks if the extension refresh feature flag is enabled to provide to template.
|
||||
*/
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.extensionRefreshEnabled = await firstValueFrom(
|
||||
this.configService.getFeatureFlag$(FeatureFlag.ExtensionRefresh),
|
||||
);
|
||||
if (this.cipher.organizationId != null) {
|
||||
this.organization = this.organizations.find((o) => o.id === this.cipher.organizationId);
|
||||
}
|
||||
@@ -83,7 +68,7 @@ export class VaultCipherRowComponent implements OnInit {
|
||||
if (this.cipher.decryptionFailure) {
|
||||
return "showFailedToDecrypt";
|
||||
}
|
||||
return this.extensionRefreshEnabled ? "view" : null;
|
||||
return "view";
|
||||
}
|
||||
|
||||
protected get showTotpCopyButton() {
|
||||
|
||||
@@ -143,11 +143,7 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit, On
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
const extensionRefreshEnabled = await firstValueFrom(
|
||||
this.configService.getFeatureFlag$(FeatureFlag.ExtensionRefresh),
|
||||
);
|
||||
|
||||
this.cardIsExpired = extensionRefreshEnabled && isCardExpired(this.cipher.card);
|
||||
this.cardIsExpired = isCardExpired(this.cipher.card);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
||||
@@ -69,88 +69,48 @@
|
||||
|
||||
<div *ngIf="filter.type !== 'trash'" class="tw-shrink-0">
|
||||
<div appListDropdown>
|
||||
<ng-container [ngSwitch]="extensionRefreshEnabled">
|
||||
<ng-container *ngSwitchCase="true">
|
||||
<button
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
type="button"
|
||||
[bitMenuTriggerFor]="addOptions"
|
||||
id="newItemDropdown"
|
||||
appA11yTitle="{{ 'new' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-plus-f" aria-hidden="true"></i>
|
||||
{{ "new" | i18n }}<i class="bwi tw-ml-2" aria-hidden="true"></i>
|
||||
</button>
|
||||
<bit-menu #addOptions aria-labelledby="newItemDropdown">
|
||||
<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>
|
||||
<button type="button" bitMenuItem (click)="addCipher(CipherType.SshKey)">
|
||||
<i class="bwi bwi-key" slot="start" aria-hidden="true"></i>
|
||||
{{ "typeSshKey" | 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" aria-hidden="true"></i>
|
||||
{{ "collection" | i18n }}
|
||||
</button>
|
||||
</bit-menu>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="false">
|
||||
<button
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
type="button"
|
||||
[bitMenuTriggerFor]="addOptions"
|
||||
id="newItemDropdown"
|
||||
appA11yTitle="{{ 'new' | i18n }}"
|
||||
>
|
||||
{{ "new" | i18n }}<i class="bwi bwi-angle-down tw-ml-2" aria-hidden="true"></i>
|
||||
</button>
|
||||
<bit-menu #addOptions aria-labelledby="newItemDropdown">
|
||||
<button type="button" bitMenuItem (click)="addCipher()">
|
||||
<i class="bwi bwi-fw bwi-globe" aria-hidden="true"></i>
|
||||
{{ "item" | i18n }}
|
||||
</button>
|
||||
<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" aria-hidden="true"></i>
|
||||
{{ "collection" | i18n }}
|
||||
</button>
|
||||
</bit-menu>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<button
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
type="button"
|
||||
[bitMenuTriggerFor]="addOptions"
|
||||
id="newItemDropdown"
|
||||
appA11yTitle="{{ 'new' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-plus-f" aria-hidden="true"></i>
|
||||
{{ "new" | i18n }}<i class="bwi tw-ml-2" aria-hidden="true"></i>
|
||||
</button>
|
||||
<bit-menu #addOptions aria-labelledby="newItemDropdown">
|
||||
<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>
|
||||
<button type="button" bitMenuItem (click)="addCipher(CipherType.SshKey)">
|
||||
<i class="bwi bwi-key" slot="start" aria-hidden="true"></i>
|
||||
{{ "typeSshKey" | 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" aria-hidden="true"></i>
|
||||
{{ "collection" | i18n }}
|
||||
</button>
|
||||
</bit-menu>
|
||||
</div>
|
||||
</div>
|
||||
</app-header>
|
||||
|
||||
@@ -9,13 +9,10 @@ import {
|
||||
OnInit,
|
||||
Output,
|
||||
} from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { Unassigned, CollectionView } from "@bitwarden/admin-console/common";
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
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";
|
||||
@@ -50,7 +47,6 @@ export class VaultHeaderComponent implements OnInit {
|
||||
protected All = All;
|
||||
protected CollectionDialogTabType = CollectionDialogTabType;
|
||||
protected CipherType = CipherType;
|
||||
protected extensionRefreshEnabled: boolean;
|
||||
|
||||
/**
|
||||
* Boolean to determine the loading state of the header.
|
||||
@@ -85,16 +81,9 @@ export class VaultHeaderComponent implements OnInit {
|
||||
/** Emits an event when the delete collection button is clicked in the header */
|
||||
@Output() onDeleteCollection = new EventEmitter<void>();
|
||||
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
private configService: ConfigService,
|
||||
) {}
|
||||
constructor(private i18nService: I18nService) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.extensionRefreshEnabled = await firstValueFrom(
|
||||
this.configService.getFeatureFlag$(FeatureFlag.ExtensionRefresh),
|
||||
);
|
||||
}
|
||||
async ngOnInit() {}
|
||||
|
||||
/**
|
||||
* The id of the organization that is currently being filtered on.
|
||||
|
||||
@@ -22,12 +22,7 @@
|
||||
<p class="tw-pl-1">
|
||||
{{ "onboardingImportDataDetailsPartOne" | i18n }}
|
||||
<button type="button" bitLink (click)="emitToAddCipher()">
|
||||
{{
|
||||
(extensionRefreshEnabled
|
||||
? "onboardingImportDataDetailsLoginLink"
|
||||
: "onboardingImportDataDetailsLink"
|
||||
) | i18n
|
||||
}}
|
||||
{{ "onboardingImportDataDetailsLoginLink" | i18n }}
|
||||
</button>
|
||||
<span>
|
||||
{{ "onboardingImportDataDetailsPartTwoNoOrgs" | i18n }}
|
||||
|
||||
@@ -7,7 +7,6 @@ import { Subject, of } from "rxjs";
|
||||
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
@@ -28,7 +27,6 @@ describe("VaultOnboardingComponent", () => {
|
||||
let mockStateProvider: Partial<StateProvider>;
|
||||
let setInstallExtLinkSpy: any;
|
||||
let individualVaultPolicyCheckSpy: any;
|
||||
let mockConfigService: MockProxy<ConfigService>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockPolicyService = mock<PolicyService>();
|
||||
@@ -47,7 +45,6 @@ describe("VaultOnboardingComponent", () => {
|
||||
}),
|
||||
),
|
||||
};
|
||||
mockConfigService = mock<ConfigService>();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
TestBed.configureTestingModule({
|
||||
@@ -60,7 +57,6 @@ describe("VaultOnboardingComponent", () => {
|
||||
{ provide: I18nService, useValue: mockI18nService },
|
||||
{ provide: ApiService, useValue: mockApiService },
|
||||
{ provide: StateProvider, useValue: mockStateProvider },
|
||||
{ provide: ConfigService, useValue: mockConfigService },
|
||||
],
|
||||
}).compileComponents();
|
||||
fixture = TestBed.createComponent(VaultOnboardingComponent);
|
||||
|
||||
@@ -18,8 +18,6 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums/cipher-type";
|
||||
import { VaultOnboardingMessages } from "@bitwarden/common/vault/enums/vault-onboarding.enum";
|
||||
@@ -58,14 +56,12 @@ export class VaultOnboardingComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
protected onboardingTasks$: Observable<VaultOnboardingTasks>;
|
||||
protected showOnboarding = false;
|
||||
protected extensionRefreshEnabled = false;
|
||||
|
||||
constructor(
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected policyService: PolicyService,
|
||||
private apiService: ApiService,
|
||||
private vaultOnboardingService: VaultOnboardingServiceAbstraction,
|
||||
private configService: ConfigService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -74,9 +70,6 @@ export class VaultOnboardingComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.setInstallExtLink();
|
||||
this.individualVaultPolicyCheck();
|
||||
this.checkForBrowserExtension();
|
||||
this.extensionRefreshEnabled = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.ExtensionRefresh,
|
||||
);
|
||||
}
|
||||
|
||||
async ngOnChanges(changes: SimpleChanges) {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { DialogRef } from "@angular/cdk/dialog";
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ViewContainerRef,
|
||||
} from "@angular/core";
|
||||
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit, ViewChild } from "@angular/core";
|
||||
import { ActivatedRoute, Params, Router } from "@angular/router";
|
||||
import {
|
||||
BehaviorSubject,
|
||||
@@ -42,7 +34,6 @@ import {
|
||||
Unassigned,
|
||||
} from "@bitwarden/admin-console/common";
|
||||
import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
@@ -57,9 +48,7 @@ import { OrganizationBillingServiceAbstraction } from "@bitwarden/common/billing
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||
import { EventType } from "@bitwarden/common/enums";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
@@ -71,7 +60,6 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi
|
||||
import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { ServiceUtils } from "@bitwarden/common/vault/service-utils";
|
||||
@@ -105,13 +93,11 @@ import { VaultItemEvent } from "../components/vault-items/vault-item-event";
|
||||
import { VaultItemsModule } from "../components/vault-items/vault-items.module";
|
||||
import { getNestedCollectionTree } from "../utils/collection-utils";
|
||||
|
||||
import { AddEditComponent } from "./add-edit.component";
|
||||
import {
|
||||
AttachmentDialogCloseResult,
|
||||
AttachmentDialogResult,
|
||||
AttachmentsV2Component,
|
||||
} from "./attachments-v2.component";
|
||||
import { AttachmentsComponent } from "./attachments.component";
|
||||
import {
|
||||
BulkDeleteDialogResult,
|
||||
openBulkDeleteDialog,
|
||||
@@ -160,15 +146,6 @@ const SearchTextDebounceInterval = 200;
|
||||
})
|
||||
export class VaultComponent implements OnInit, OnDestroy {
|
||||
@ViewChild("vaultFilter", { static: true }) filterComponent: VaultFilterComponent;
|
||||
@ViewChild("attachments", { read: ViewContainerRef, static: true })
|
||||
attachmentsModalRef: ViewContainerRef;
|
||||
@ViewChild("folderAddEdit", { read: ViewContainerRef, static: true })
|
||||
folderAddEditModalRef: ViewContainerRef;
|
||||
@ViewChild("cipherAddEdit", { read: ViewContainerRef, static: true })
|
||||
cipherAddEditModalRef: ViewContainerRef;
|
||||
@ViewChild("share", { read: ViewContainerRef, static: true }) shareModalRef: ViewContainerRef;
|
||||
@ViewChild("collectionsModal", { read: ViewContainerRef, static: true })
|
||||
collectionsModalRef: ViewContainerRef;
|
||||
|
||||
trashCleanupWarning: string = null;
|
||||
kdfIterations: number;
|
||||
@@ -193,7 +170,6 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
private searchText$ = new Subject<string>();
|
||||
private refresh$ = new BehaviorSubject<void>(null);
|
||||
private destroy$ = new Subject<void>();
|
||||
private extensionRefreshEnabled: boolean;
|
||||
private hasSubscription$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private vaultItemDialogRef?: DialogRef<VaultItemDialogResult> | undefined;
|
||||
@@ -260,7 +236,6 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
private router: Router,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private i18nService: I18nService,
|
||||
private modalService: ModalService,
|
||||
private dialogService: DialogService,
|
||||
private messagingService: MessagingService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
@@ -278,7 +253,6 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
private eventCollectionService: EventCollectionService,
|
||||
private searchService: SearchService,
|
||||
private searchPipe: SearchPipe,
|
||||
private configService: ConfigService,
|
||||
private apiService: ApiService,
|
||||
private billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
private toastService: ToastService,
|
||||
@@ -437,15 +411,15 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
firstSetup$
|
||||
.pipe(
|
||||
switchMap(() => this.route.queryParams),
|
||||
// Only process the queryParams if the dialog is not open (only when extension refresh is enabled)
|
||||
filter(() => this.vaultItemDialogRef == undefined || !this.extensionRefreshEnabled),
|
||||
// Only process the queryParams if the dialog is not open
|
||||
filter(() => this.vaultItemDialogRef == undefined),
|
||||
switchMap(async (params) => {
|
||||
const cipherId = getCipherIdFromParams(params);
|
||||
if (cipherId) {
|
||||
if (await this.cipherService.get(cipherId)) {
|
||||
let action = params.action;
|
||||
// Default to "view" if extension refresh is enabled
|
||||
if (action == null && this.extensionRefreshEnabled) {
|
||||
// Default to "view"
|
||||
if (action == null) {
|
||||
action = "view";
|
||||
}
|
||||
|
||||
@@ -544,11 +518,6 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
this.refreshing = false;
|
||||
},
|
||||
);
|
||||
|
||||
// Check if the extension refresh feature flag is enabled
|
||||
this.extensionRefreshEnabled = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.ExtensionRefresh,
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -642,8 +611,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
* Handles opening the attachments dialog for a cipher.
|
||||
* Runs several checks to ensure that the user has the correct permissions
|
||||
* and then opens the attachments dialog.
|
||||
* Uses the new AttachmentsV2Component if the extensionRefresh feature flag is enabled.
|
||||
*
|
||||
* Uses the new AttachmentsV2Component
|
||||
* @param cipher
|
||||
* @returns
|
||||
*/
|
||||
@@ -668,51 +636,20 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
const canEditAttachments = await this.canEditAttachments(cipher);
|
||||
const dialogRef = AttachmentsV2Component.open(this.dialogService, {
|
||||
cipherId: cipher.id as CipherId,
|
||||
});
|
||||
|
||||
let madeAttachmentChanges = false;
|
||||
const result: AttachmentDialogCloseResult = await lastValueFrom(dialogRef.closed);
|
||||
|
||||
if (this.extensionRefreshEnabled) {
|
||||
const dialogRef = AttachmentsV2Component.open(this.dialogService, {
|
||||
cipherId: cipher.id as CipherId,
|
||||
});
|
||||
|
||||
const result: AttachmentDialogCloseResult = await lastValueFrom(dialogRef.closed);
|
||||
|
||||
if (
|
||||
result.action === AttachmentDialogResult.Uploaded ||
|
||||
result.action === AttachmentDialogResult.Removed
|
||||
) {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
return;
|
||||
if (
|
||||
result.action === AttachmentDialogResult.Uploaded ||
|
||||
result.action === AttachmentDialogResult.Removed
|
||||
) {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
const [modal] = await this.modalService.openViewRef(
|
||||
AttachmentsComponent,
|
||||
this.attachmentsModalRef,
|
||||
(comp) => {
|
||||
comp.cipherId = cipher.id;
|
||||
comp.viewOnly = !canEditAttachments;
|
||||
comp.onUploadedAttachment
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(() => (madeAttachmentChanges = true));
|
||||
comp.onDeletedAttachment
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(() => (madeAttachmentChanges = true));
|
||||
comp.onReuploadedAttachment
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(() => (madeAttachmentChanges = true));
|
||||
},
|
||||
);
|
||||
|
||||
modal.onClosed.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
||||
if (madeAttachmentChanges) {
|
||||
this.refresh();
|
||||
}
|
||||
madeAttachmentChanges = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -751,48 +688,13 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
await this.go({ cipherId: null, itemId: null, action: null });
|
||||
}
|
||||
|
||||
async addCipher(cipherType?: CipherType) {
|
||||
const type = cipherType ?? this.activeFilter.cipherType;
|
||||
|
||||
if (this.extensionRefreshEnabled) {
|
||||
return this.addCipherV2(type);
|
||||
}
|
||||
|
||||
const component = (await this.editCipher(null)) as AddEditComponent;
|
||||
component.type = type;
|
||||
if (
|
||||
this.activeFilter.organizationId !== "MyVault" &&
|
||||
this.activeFilter.organizationId != null
|
||||
) {
|
||||
component.organizationId = this.activeFilter.organizationId;
|
||||
component.collections = (
|
||||
await firstValueFrom(this.vaultFilterService.filteredCollections$)
|
||||
).filter((c) => !c.readOnly && c.id != null);
|
||||
}
|
||||
const selectedColId = this.activeFilter.collectionId;
|
||||
if (selectedColId !== "AllCollections" && selectedColId != null) {
|
||||
const selectedCollection = (
|
||||
await firstValueFrom(this.vaultFilterService.filteredCollections$)
|
||||
).find((c) => c.id === selectedColId);
|
||||
component.organizationId = selectedCollection?.organizationId;
|
||||
if (!selectedCollection.readOnly) {
|
||||
component.collectionIds = [selectedColId];
|
||||
}
|
||||
}
|
||||
component.folderId = this.activeFilter.folderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the add cipher dialog.
|
||||
* @param cipherType The type of cipher to add.
|
||||
* @returns The dialog reference.
|
||||
*/
|
||||
async addCipherV2(cipherType?: CipherType) {
|
||||
const cipherFormConfig = await this.cipherFormConfigService.buildConfig(
|
||||
"add",
|
||||
null,
|
||||
cipherType,
|
||||
);
|
||||
async addCipher(cipherType?: CipherType) {
|
||||
const type = cipherType ?? this.activeFilter.cipherType;
|
||||
const cipherFormConfig = await this.cipherFormConfigService.buildConfig("add", null, type);
|
||||
const collectionId =
|
||||
this.activeFilter.collectionId !== "AllCollections" && this.activeFilter.collectionId != null
|
||||
? this.activeFilter.collectionId
|
||||
@@ -823,6 +725,12 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
return this.editCipherId(cipher?.id, cloneMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a cipher using the new VaultItemDialog.
|
||||
* @param id
|
||||
* @param cloneMode
|
||||
* @returns
|
||||
*/
|
||||
async editCipherId(id: string, cloneMode?: boolean) {
|
||||
const cipher = await this.cipherService.get(id);
|
||||
|
||||
@@ -836,49 +744,6 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.extensionRefreshEnabled) {
|
||||
await this.editCipherIdV2(cipher, cloneMode);
|
||||
return;
|
||||
}
|
||||
|
||||
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||
AddEditComponent,
|
||||
this.cipherAddEditModalRef,
|
||||
(comp) => {
|
||||
comp.cipherId = id;
|
||||
comp.collectionId = this.selectedCollection?.node.id;
|
||||
|
||||
comp.onSavedCipher.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
||||
modal.close();
|
||||
this.refresh();
|
||||
});
|
||||
comp.onDeletedCipher.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
||||
modal.close();
|
||||
this.refresh();
|
||||
});
|
||||
comp.onRestoredCipher.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
||||
modal.close();
|
||||
this.refresh();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
modal.onClosedPromise().then(() => {
|
||||
void this.go({ cipherId: null, itemId: null, action: null });
|
||||
});
|
||||
|
||||
return childComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a cipher using the new VaultItemDialog.
|
||||
*
|
||||
* @param cipher
|
||||
* @param cloneMode
|
||||
*/
|
||||
private async editCipherIdV2(cipher: Cipher, cloneMode?: boolean) {
|
||||
const cipherFormConfig = await this.cipherFormConfigService.buildConfig(
|
||||
cloneMode ? "clone" : "edit",
|
||||
cipher.id as CipherId,
|
||||
@@ -1076,11 +941,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
const component = await this.editCipher(cipher, true);
|
||||
|
||||
if (component != null) {
|
||||
component.cloneMode = true;
|
||||
}
|
||||
await this.editCipher(cipher, true);
|
||||
}
|
||||
|
||||
restore = async (c: CipherView): Promise<boolean> => {
|
||||
@@ -1331,15 +1192,6 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
this.refresh$.next();
|
||||
}
|
||||
|
||||
private async canEditAttachments(cipher: CipherView) {
|
||||
if (cipher.organizationId == null || cipher.edit) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const organization = this.allOrganizations.find((o) => o.id === cipher.organizationId);
|
||||
return organization.canEditAllCiphers;
|
||||
}
|
||||
|
||||
private async go(queryParams: any = null) {
|
||||
if (queryParams == null) {
|
||||
queryParams = {
|
||||
|
||||
@@ -104,8 +104,8 @@
|
||||
*ngIf="filter.type !== 'trash' && filter.collectionId !== Unassigned && organization"
|
||||
class="tw-shrink-0"
|
||||
>
|
||||
<!-- "New" menu is always shown for Extension Refresh unless the user cannot create a cipher -->
|
||||
<ng-container *ngIf="extensionRefreshEnabled && canCreateCipher; else nonRefresh">
|
||||
<!-- "New" menu is always shown unless the user cannot create a cipher -->
|
||||
<ng-container *ngIf="canCreateCipher">
|
||||
<div appListDropdown>
|
||||
<button
|
||||
bitButton
|
||||
@@ -145,56 +145,5 @@
|
||||
</bit-menu>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #nonRefresh>
|
||||
<!-- Show a menu when the user can create a cipher and collection -->
|
||||
<div *ngIf="canCreateCipher && canCreateCollection" appListDropdown>
|
||||
<button
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
type="button"
|
||||
[bitMenuTriggerFor]="addOptions"
|
||||
id="newItemDropdown"
|
||||
appA11yTitle="{{ 'new' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-plus-f" aria-hidden="true"></i>
|
||||
{{ "new" | i18n }}<i class="bwi tw-ml-2" aria-hidden="true"></i>
|
||||
</button>
|
||||
<bit-menu #addOptions aria-labelledby="newItemDropdown">
|
||||
<button type="button" bitMenuItem (click)="addCipher()">
|
||||
<i class="bwi bwi-fw bwi-globe" aria-hidden="true"></i>
|
||||
{{ "item" | i18n }}
|
||||
</button>
|
||||
<button type="button" bitMenuItem (click)="addCollection()">
|
||||
<i class="bwi bwi-fw bwi-collection" aria-hidden="true"></i>
|
||||
{{ "collection" | i18n }}
|
||||
</button>
|
||||
</bit-menu>
|
||||
</div>
|
||||
|
||||
<!-- Show a single button when the user can only create a cipher -->
|
||||
<button
|
||||
*ngIf="canCreateCipher && !canCreateCollection"
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
(click)="addCipher()"
|
||||
>
|
||||
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||
{{ "newItem" | i18n }}
|
||||
</button>
|
||||
|
||||
<!-- Show a single button when the user can only create a collection -->
|
||||
<button
|
||||
*ngIf="canCreateCollection && !canCreateCipher"
|
||||
type="button"
|
||||
bitButton
|
||||
buttonType="primary"
|
||||
(click)="addCollection()"
|
||||
>
|
||||
<i class="bwi bwi-plus bwi-fw" aria-hidden="true"></i>
|
||||
{{ "newCollection" | i18n }}
|
||||
</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</app-header>
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
@@ -90,11 +89,6 @@ export class VaultHeaderComponent implements OnInit {
|
||||
|
||||
protected CollectionDialogTabType = CollectionDialogTabType;
|
||||
|
||||
/**
|
||||
* Whether the extension refresh feature flag is enabled.
|
||||
*/
|
||||
protected extensionRefreshEnabled = false;
|
||||
|
||||
/** The cipher type enum. */
|
||||
protected CipherType = CipherType;
|
||||
|
||||
@@ -106,11 +100,7 @@ export class VaultHeaderComponent implements OnInit {
|
||||
private configService: ConfigService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.extensionRefreshEnabled = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.ExtensionRefresh,
|
||||
);
|
||||
}
|
||||
async ngOnInit() {}
|
||||
|
||||
get title() {
|
||||
const headerType = this.i18nService.t("collections").toLowerCase();
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { DialogRef } from "@angular/cdk/dialog";
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ViewContainerRef,
|
||||
} from "@angular/core";
|
||||
import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute, Params, Router } from "@angular/router";
|
||||
import {
|
||||
BehaviorSubject,
|
||||
@@ -37,12 +29,10 @@ import {
|
||||
import {
|
||||
CollectionAdminService,
|
||||
CollectionAdminView,
|
||||
CollectionService,
|
||||
CollectionView,
|
||||
Unassigned,
|
||||
} from "@bitwarden/admin-console/common";
|
||||
import { SearchPipe } from "@bitwarden/angular/pipes/search.pipe";
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
@@ -127,7 +117,6 @@ import {
|
||||
import { VaultHeaderComponent } from "../org-vault/vault-header/vault-header.component";
|
||||
import { getNestedCollectionTree } from "../utils/collection-utils";
|
||||
|
||||
import { AddEditComponent } from "./add-edit.component";
|
||||
import {
|
||||
BulkCollectionsDialogComponent,
|
||||
BulkCollectionsDialogResult,
|
||||
@@ -166,13 +155,6 @@ enum AddAccessStatusType {
|
||||
export class VaultComponent implements OnInit, OnDestroy {
|
||||
protected Unassigned = Unassigned;
|
||||
|
||||
@ViewChild("attachments", { read: ViewContainerRef, static: true })
|
||||
attachmentsModalRef: ViewContainerRef;
|
||||
@ViewChild("cipherAddEdit", { read: ViewContainerRef, static: true })
|
||||
cipherAddEditModalRef: ViewContainerRef;
|
||||
@ViewChild("collectionsModal", { read: ViewContainerRef, static: true })
|
||||
collectionsModalRef: ViewContainerRef;
|
||||
|
||||
trashCleanupWarning: string = null;
|
||||
activeFilter: VaultFilter = new VaultFilter();
|
||||
|
||||
@@ -210,7 +192,6 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
private refresh$ = new BehaviorSubject<void>(null);
|
||||
private destroy$ = new Subject<void>();
|
||||
protected addAccessStatus$ = new BehaviorSubject<AddAccessStatusType>(0);
|
||||
private extensionRefreshEnabled: boolean;
|
||||
private resellerManagedOrgAlert: boolean;
|
||||
private vaultItemDialogRef?: DialogRef<VaultItemDialogResult> | undefined;
|
||||
|
||||
@@ -249,7 +230,6 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private syncService: SyncService,
|
||||
private i18nService: I18nService,
|
||||
private modalService: ModalService,
|
||||
private dialogService: DialogService,
|
||||
private messagingService: MessagingService,
|
||||
private broadcasterService: BroadcasterService,
|
||||
@@ -265,7 +245,6 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
private eventCollectionService: EventCollectionService,
|
||||
private totpService: TotpService,
|
||||
private apiService: ApiService,
|
||||
private collectionService: CollectionService,
|
||||
private toastService: ToastService,
|
||||
private configService: ConfigService,
|
||||
private cipherFormConfigService: CipherFormConfigService,
|
||||
@@ -278,10 +257,6 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.extensionRefreshEnabled = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.ExtensionRefresh,
|
||||
);
|
||||
|
||||
this.resellerManagedOrgAlert = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.ResellerManagedOrgAlert,
|
||||
);
|
||||
@@ -555,7 +530,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
firstSetup$
|
||||
.pipe(
|
||||
switchMap(() => combineLatest([this.route.queryParams, allCipherMap$])),
|
||||
filter(() => this.vaultItemDialogRef == undefined || !this.extensionRefreshEnabled),
|
||||
filter(() => this.vaultItemDialogRef == undefined),
|
||||
switchMap(async ([qParams, allCiphersMap]) => {
|
||||
const cipherId = getCipherIdFromParams(qParams);
|
||||
|
||||
@@ -586,15 +561,15 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
// Default to "view" if extension refresh is enabled
|
||||
if (action == null && this.extensionRefreshEnabled) {
|
||||
// Default to "view"
|
||||
if (action == null) {
|
||||
action = "view";
|
||||
}
|
||||
|
||||
if (action === "view") {
|
||||
await this.viewCipherById(cipher);
|
||||
} else {
|
||||
await this.editCipherId(cipher, false);
|
||||
await this.editCipher(cipher, false);
|
||||
}
|
||||
} else {
|
||||
this.toastService.showToast({
|
||||
@@ -836,27 +811,8 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
/** Opens the Add/Edit Dialog */
|
||||
async addCipher(cipherType?: CipherType) {
|
||||
if (this.extensionRefreshEnabled) {
|
||||
return this.addCipherV2(cipherType);
|
||||
}
|
||||
|
||||
let collections: CollectionView[] = [];
|
||||
|
||||
// Admins limited to only adding items to collections they have access to.
|
||||
collections = await firstValueFrom(this.editableCollections$);
|
||||
|
||||
await this.editCipher(null, false, (comp) => {
|
||||
comp.type = cipherType || this.activeFilter.cipherType;
|
||||
comp.collections = collections;
|
||||
if (this.activeFilter.collectionId) {
|
||||
comp.collectionIds = [this.activeFilter.collectionId];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Opens the Add/Edit Dialog. Only to be used when the BrowserExtension feature flag is active */
|
||||
async addCipherV2(cipherType?: CipherType) {
|
||||
const cipherFormConfig = await this.cipherFormConfigService.buildConfig(
|
||||
"add",
|
||||
null,
|
||||
@@ -877,24 +833,8 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
* Edit the given cipher or add a new cipher
|
||||
* @param cipherView - When set, the cipher to be edited
|
||||
* @param cloneCipher - `true` when the cipher should be cloned.
|
||||
* Used in place of the `additionalComponentParameters`, as
|
||||
* the `editCipherIdV2` method has a differing implementation.
|
||||
* @param defaultComponentParameters - A method that takes in an instance of
|
||||
* the `AddEditComponent` to edit methods directly.
|
||||
*/
|
||||
async editCipher(
|
||||
cipher: CipherView | null,
|
||||
cloneCipher: boolean,
|
||||
additionalComponentParameters?: (comp: AddEditComponent) => void,
|
||||
) {
|
||||
return this.editCipherId(cipher, cloneCipher, additionalComponentParameters);
|
||||
}
|
||||
|
||||
async editCipherId(
|
||||
cipher: CipherView | null,
|
||||
cloneCipher: boolean,
|
||||
additionalComponentParameters?: (comp: AddEditComponent) => void,
|
||||
) {
|
||||
async editCipher(cipher: CipherView | null, cloneCipher: boolean) {
|
||||
if (
|
||||
cipher &&
|
||||
cipher.reprompt !== 0 &&
|
||||
@@ -905,55 +845,6 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.extensionRefreshEnabled) {
|
||||
await this.editCipherIdV2(cipher, cloneCipher);
|
||||
return;
|
||||
}
|
||||
|
||||
const defaultComponentParameters = (comp: AddEditComponent) => {
|
||||
comp.organization = this.organization;
|
||||
comp.organizationId = this.organization.id;
|
||||
comp.cipherId = cipher?.id;
|
||||
comp.collectionId = this.activeFilter.collectionId;
|
||||
comp.onSavedCipher.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
||||
modal.close();
|
||||
this.refresh();
|
||||
});
|
||||
comp.onDeletedCipher.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
||||
modal.close();
|
||||
this.refresh();
|
||||
});
|
||||
comp.onRestoredCipher.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
||||
modal.close();
|
||||
this.refresh();
|
||||
});
|
||||
};
|
||||
|
||||
const [modal, childComponent] = await this.modalService.openViewRef(
|
||||
AddEditComponent,
|
||||
this.cipherAddEditModalRef,
|
||||
additionalComponentParameters == null
|
||||
? defaultComponentParameters
|
||||
: (comp) => {
|
||||
defaultComponentParameters(comp);
|
||||
additionalComponentParameters(comp);
|
||||
},
|
||||
);
|
||||
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
modal.onClosedPromise().then(() => {
|
||||
this.go({ cipherId: null, itemId: null, action: null });
|
||||
});
|
||||
|
||||
return childComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a cipher using the new AddEditCipherDialogV2 component.
|
||||
* Only to be used behind the ExtensionRefresh feature flag.
|
||||
*/
|
||||
private async editCipherIdV2(cipher: CipherView | null, cloneCipher: boolean) {
|
||||
const cipherFormConfig = await this.cipherFormConfigService.buildConfig(
|
||||
cloneCipher ? "clone" : "edit",
|
||||
cipher?.id as CipherId | null,
|
||||
@@ -1038,16 +929,7 @@ export class VaultComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
let collections: CollectionView[] = [];
|
||||
|
||||
// Admins limited to only adding items to collections they have access to.
|
||||
collections = await firstValueFrom(this.editableCollections$);
|
||||
|
||||
await this.editCipher(cipher, true, (comp) => {
|
||||
comp.cloneMode = true;
|
||||
comp.collections = collections;
|
||||
comp.collectionIds = cipher.collectionIds;
|
||||
});
|
||||
await this.editCipher(cipher, true);
|
||||
}
|
||||
|
||||
restore = async (c: CipherView): Promise<boolean> => {
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import { TestBed } from "@angular/core/testing";
|
||||
import { Navigation, Router, UrlTree } from "@angular/router";
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
|
||||
import { extensionRefreshRedirect } from "./extension-refresh-redirect";
|
||||
|
||||
describe("extensionRefreshRedirect", () => {
|
||||
let configService: MockProxy<ConfigService>;
|
||||
let router: MockProxy<Router>;
|
||||
|
||||
beforeEach(() => {
|
||||
configService = mock<ConfigService>();
|
||||
router = mock<Router>();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{ provide: ConfigService, useValue: configService },
|
||||
{ provide: Router, useValue: router },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("returns true when ExtensionRefresh flag is disabled", async () => {
|
||||
configService.getFeatureFlag.mockResolvedValue(false);
|
||||
|
||||
const result = await TestBed.runInInjectionContext(() =>
|
||||
extensionRefreshRedirect("/redirect")(),
|
||||
);
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(configService.getFeatureFlag).toHaveBeenCalledWith(FeatureFlag.ExtensionRefresh);
|
||||
expect(router.parseUrl).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns UrlTree when ExtensionRefresh flag is enabled and preserves query params", async () => {
|
||||
configService.getFeatureFlag.mockResolvedValue(true);
|
||||
|
||||
const urlTree = new UrlTree();
|
||||
urlTree.queryParams = { test: "test" };
|
||||
|
||||
const navigation: Navigation = {
|
||||
extras: {},
|
||||
id: 0,
|
||||
initialUrl: new UrlTree(),
|
||||
extractedUrl: urlTree,
|
||||
trigger: "imperative",
|
||||
previousNavigation: undefined,
|
||||
};
|
||||
|
||||
router.getCurrentNavigation.mockReturnValue(navigation);
|
||||
|
||||
await TestBed.runInInjectionContext(() => extensionRefreshRedirect("/redirect")());
|
||||
|
||||
expect(configService.getFeatureFlag).toHaveBeenCalledWith(FeatureFlag.ExtensionRefresh);
|
||||
expect(router.createUrlTree).toHaveBeenCalledWith(["/redirect"], {
|
||||
queryParams: urlTree.queryParams,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
import { inject } from "@angular/core";
|
||||
import { UrlTree, Router } from "@angular/router";
|
||||
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
|
||||
/**
|
||||
* Helper function to redirect to a new URL based on the ExtensionRefresh feature flag.
|
||||
* @param redirectUrl - The URL to redirect to if the ExtensionRefresh flag is enabled.
|
||||
*/
|
||||
export function extensionRefreshRedirect(redirectUrl: string): () => Promise<boolean | UrlTree> {
|
||||
return async () => {
|
||||
const configService = inject(ConfigService);
|
||||
const router = inject(Router);
|
||||
|
||||
const shouldRedirect = await configService.getFeatureFlag(FeatureFlag.ExtensionRefresh);
|
||||
if (shouldRedirect) {
|
||||
const currentNavigation = router.getCurrentNavigation();
|
||||
const queryParams = currentNavigation?.extractedUrl?.queryParams || {};
|
||||
|
||||
// Preserve query params when redirecting as it is likely that the refreshed component
|
||||
// will be consuming the same query params.
|
||||
return router.createUrlTree([redirectUrl], { queryParams });
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { Type, inject } from "@angular/core";
|
||||
import { Route, Routes } from "@angular/router";
|
||||
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||
|
||||
import { componentRouteSwap } from "./component-route-swap";
|
||||
|
||||
/**
|
||||
* Helper function to swap between two components based on the ExtensionRefresh feature flag.
|
||||
* @param defaultComponent - The current non-refreshed component to render.
|
||||
* @param refreshedComponent - The new refreshed component to render.
|
||||
* @param options - The shared route options to apply to the default component, and to the alt component if altOptions is not provided.
|
||||
* @param altOptions - The alt route options to apply to the alt component.
|
||||
*/
|
||||
export function extensionRefreshSwap(
|
||||
defaultComponent: Type<any>,
|
||||
refreshedComponent: Type<any>,
|
||||
options: Route,
|
||||
altOptions?: Route,
|
||||
): Routes {
|
||||
return componentRouteSwap(
|
||||
defaultComponent,
|
||||
refreshedComponent,
|
||||
async () => {
|
||||
const configService = inject(ConfigService);
|
||||
return configService.getFeatureFlag(FeatureFlag.ExtensionRefresh);
|
||||
},
|
||||
options,
|
||||
altOptions,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user