diff --git a/apps/desktop/src/auth/login/login.component.ts b/apps/desktop/src/auth/login/login.component.ts index eb7b924362..a810a29a26 100644 --- a/apps/desktop/src/auth/login/login.component.ts +++ b/apps/desktop/src/auth/login/login.component.ts @@ -3,7 +3,6 @@ import { FormBuilder } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { Subject, takeUntil } from "rxjs"; -import { EnvironmentSelectorComponent } from "@bitwarden/angular/auth/components/environment-selector.component"; import { LoginComponent as BaseLoginComponent } from "@bitwarden/angular/auth/components/login.component"; import { FormValidationErrorsService } from "@bitwarden/angular/platform/abstractions/form-validation-errors.service"; import { ModalService } from "@bitwarden/angular/services/modal.service"; @@ -37,8 +36,6 @@ const BroadcasterSubscriptionId = "LoginComponent"; export class LoginComponent extends BaseLoginComponent implements OnDestroy { @ViewChild("environment", { read: ViewContainerRef, static: true }) environmentModal: ViewContainerRef; - @ViewChild("environmentSelector", { read: ViewContainerRef, static: true }) - environmentSelector: EnvironmentSelectorComponent; protected componentDestroyed$: Subject = new Subject(); webVaultHostname = ""; diff --git a/apps/web/src/app/admin-console/organizations/core/views/organization-user.view.ts b/apps/web/src/app/admin-console/organizations/core/views/organization-user.view.ts index ee263ec750..947ae9b13e 100644 --- a/apps/web/src/app/admin-console/organizations/core/views/organization-user.view.ts +++ b/apps/web/src/app/admin-console/organizations/core/views/organization-user.view.ts @@ -26,6 +26,10 @@ export class OrganizationUserView { twoFactorEnabled: boolean; usesKeyConnector: boolean; hasMasterPassword: boolean; + /** + * True if this organizaztion user has been granted access to Secrets Manager, false otherwise. + */ + accessSecretsManager: boolean; collections: CollectionAccessSelectionView[] = []; groups: string[] = []; diff --git a/apps/web/src/app/admin-console/organizations/members/people.component.ts b/apps/web/src/app/admin-console/organizations/members/people.component.ts index b2aedacc80..8a303ddfe5 100644 --- a/apps/web/src/app/admin-console/organizations/members/people.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/people.component.ts @@ -571,7 +571,8 @@ export class PeopleComponent } async bulkEnableSM() { - const users = this.getCheckedUsers(); + const users = this.getCheckedUsers().filter((ou) => !ou.accessSecretsManager); + if (users.length === 0) { this.platformUtilsService.showToast( "error", @@ -588,6 +589,7 @@ export class PeopleComponent await lastValueFrom(dialogRef.closed); this.selectAll(false); + await this.load(); } async events(user: OrganizationUserView) { diff --git a/apps/web/src/app/billing/organizations/organization-billing-tab.component.ts b/apps/web/src/app/billing/organizations/organization-billing-tab.component.ts deleted file mode 100644 index 25d4ea4892..0000000000 --- a/apps/web/src/app/billing/organizations/organization-billing-tab.component.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Component, OnInit } from "@angular/core"; -import { ActivatedRoute } from "@angular/router"; -import { map, Observable, switchMap } from "rxjs"; - -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; - -@Component({ - templateUrl: "organization-billing-tab.component.html", -}) -export class OrganizationBillingTabComponent implements OnInit { - showPaymentAndHistory$: Observable; - - constructor( - private route: ActivatedRoute, - private organizationService: OrganizationService, - private platformUtilsService: PlatformUtilsService, - ) {} - - ngOnInit() { - this.showPaymentAndHistory$ = this.route.params.pipe( - switchMap((params) => this.organizationService.get$(params.organizationId)), - map( - (org) => - !this.platformUtilsService.isSelfHost() && - org.canViewBillingHistory && - org.canEditPaymentMethods, - ), - ); - } -} diff --git a/apps/web/src/app/billing/organizations/organization-billing.module.ts b/apps/web/src/app/billing/organizations/organization-billing.module.ts index 141233aef3..490ebafbff 100644 --- a/apps/web/src/app/billing/organizations/organization-billing.module.ts +++ b/apps/web/src/app/billing/organizations/organization-billing.module.ts @@ -17,6 +17,7 @@ import { OrganizationSubscriptionSelfhostComponent } from "./organization-subscr import { SecretsManagerAdjustSubscriptionComponent } from "./sm-adjust-subscription.component"; import { SecretsManagerSubscribeStandaloneComponent } from "./sm-subscribe-standalone.component"; import { SubscriptionHiddenComponent } from "./subscription-hidden.component"; +import { SubscriptionStatusComponent } from "./subscription-status.component"; @NgModule({ imports: [ @@ -38,6 +39,7 @@ import { SubscriptionHiddenComponent } from "./subscription-hidden.component"; SecretsManagerAdjustSubscriptionComponent, SecretsManagerSubscribeStandaloneComponent, SubscriptionHiddenComponent, + SubscriptionStatusComponent, ], }) export class OrganizationBillingModule {} diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html index 5f767d85c4..b4fac65854 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.html @@ -12,51 +12,58 @@ > - - {{ "subscriptionCanceled" | i18n }} - -

{{ "subscriptionPendingCanceled" | i18n }}

- -
+ {{ "subscriptionCanceled" | i18n }} + +

{{ "subscriptionPendingCanceled" | i18n }}

+ +
-
-
{{ "billingPlan" | i18n }}
-
{{ sub.plan.name }}
- -
{{ "status" | i18n }}
-
- {{ - isSponsoredSubscription ? "sponsored" : subscription.status || "-" - }} - {{ - "pendingCancellation" | i18n - }} -
-
- {{ "subscriptionExpiration" | i18n }} -
-
- {{ nextInvoice ? (nextInvoice.date | date: "mediumDate") : "-" }} -
-
-
+
+
{{ "billingPlan" | i18n }}
+
{{ sub.plan.name }}
+ +
{{ "status" | i18n }}
+
+ {{ + isSponsoredSubscription ? "sponsored" : subscription.status || "-" + }} + {{ + "pendingCancellation" | i18n + }} +
+
+ {{ "subscriptionExpiration" | i18n }} +
+
+ {{ nextInvoice ? (nextInvoice.date | date: "mediumDate") : "-" }} +
+
+
+
+
{{ diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts index 2256a92756..0810f79b8e 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts @@ -1,6 +1,6 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; -import { concatMap, firstValueFrom, lastValueFrom, Subject, takeUntil } from "rxjs"; +import { concatMap, firstValueFrom, lastValueFrom, Observable, Subject, takeUntil } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; @@ -11,6 +11,8 @@ import { PlanType } from "@bitwarden/common/billing/enums"; import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response"; import { BillingSubscriptionItemResponse } from "@bitwarden/common/billing/models/response/subscription.response"; import { ProductType } from "@bitwarden/common/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 { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -41,6 +43,8 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy showSecretsManagerSubscribe = false; firstLoaded = false; loading: boolean; + locale: string; + showUpdatedSubscriptionStatusSection$: Observable; protected readonly teamsStarter = ProductType.TeamsStarter; @@ -55,6 +59,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy private organizationApiService: OrganizationApiServiceAbstraction, private route: ActivatedRoute, private dialogService: DialogService, + private configService: ConfigService, ) {} async ngOnInit() { @@ -74,6 +79,11 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy takeUntil(this.destroy$), ) .subscribe(); + + this.showUpdatedSubscriptionStatusSection$ = this.configService.getFeatureFlag$( + FeatureFlag.AC1795_UpdatedSubscriptionStatusSection, + false, + ); } ngOnDestroy() { @@ -86,6 +96,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy return; } this.loading = true; + this.locale = await firstValueFrom(this.i18nService.locale$); this.userOrg = await this.organizationService.get(this.organizationId); if (this.userOrg.canViewSubscription) { this.sub = await this.organizationApiService.getSubscription(this.organizationId); diff --git a/apps/web/src/app/billing/organizations/subscription-status.component.html b/apps/web/src/app/billing/organizations/subscription-status.component.html new file mode 100644 index 0000000000..4bb2c91b85 --- /dev/null +++ b/apps/web/src/app/billing/organizations/subscription-status.component.html @@ -0,0 +1,32 @@ + + +

{{ data.callout.body }}

+ +
+
+
{{ "billingPlan" | i18n }}
+
{{ planName }}
+ +
{{ data.status.label }}
+
+ + {{ displayedStatus }} + +
+
+ {{ data.date.label | titlecase }} +
+
+ {{ data.date.value | date: "mediumDate" }} +
+
+
+
diff --git a/apps/web/src/app/billing/organizations/subscription-status.component.ts b/apps/web/src/app/billing/organizations/subscription-status.component.ts new file mode 100644 index 0000000000..54af940be5 --- /dev/null +++ b/apps/web/src/app/billing/organizations/subscription-status.component.ts @@ -0,0 +1,184 @@ +import { DatePipe } from "@angular/common"; +import { Component, EventEmitter, Input, Output } from "@angular/core"; + +import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + +type ComponentData = { + status: { + label: string; + value: string; + }; + date: { + label: string; + value: string; + }; + callout?: { + severity: "danger" | "warning"; + header: string; + body: string; + showReinstatementButton: boolean; + }; +}; + +@Component({ + selector: "app-subscription-status", + templateUrl: "subscription-status.component.html", +}) +export class SubscriptionStatusComponent { + @Input({ required: true }) organizationSubscriptionResponse: OrganizationSubscriptionResponse; + @Output() reinstatementRequested = new EventEmitter(); + + constructor( + private datePipe: DatePipe, + private i18nService: I18nService, + ) {} + + get displayedStatus(): string { + const sponsored = this.subscription.items.some((item) => item.sponsoredSubscriptionItem); + return sponsored ? this.i18nService.t("sponsored") : this.data.status.value; + } + + get planName() { + return this.organizationSubscriptionResponse.plan.name; + } + + get status(): string { + return this.subscription.status != "canceled" && this.subscription.cancelAtEndDate + ? "pending_cancellation" + : this.subscription.status; + } + + get subscription() { + return this.organizationSubscriptionResponse.subscription; + } + + get data(): ComponentData { + const defaultStatusLabel = this.i18nService.t("status"); + + const nextChargeDateLabel = this.i18nService.t("nextCharge"); + const subscriptionExpiredDateLabel = this.i18nService.t("subscriptionExpired"); + const cancellationDateLabel = this.i18nService.t("cancellationDate"); + + switch (this.status) { + case "trialing": { + return { + status: { + label: defaultStatusLabel, + value: this.i18nService.t("trial"), + }, + date: { + label: nextChargeDateLabel, + value: this.subscription.periodEndDate, + }, + }; + } + case "active": { + return { + status: { + label: defaultStatusLabel, + value: this.i18nService.t("active"), + }, + date: { + label: nextChargeDateLabel, + value: this.subscription.periodEndDate, + }, + }; + } + case "past_due": { + const pastDueText = this.i18nService.t("pastDue"); + const suspensionDate = this.datePipe.transform( + this.subscription.suspensionDate, + "mediumDate", + ); + const calloutBody = + this.subscription.collectionMethod === "charge_automatically" + ? this.i18nService.t( + "pastDueWarningForChargeAutomatically", + this.subscription.gracePeriod, + suspensionDate, + ) + : this.i18nService.t( + "pastDueWarningForSendInvoice", + this.subscription.gracePeriod, + suspensionDate, + ); + return { + status: { + label: defaultStatusLabel, + value: pastDueText, + }, + date: { + label: subscriptionExpiredDateLabel, + value: this.subscription.unpaidPeriodEndDate, + }, + callout: { + severity: "warning", + header: pastDueText, + body: calloutBody, + showReinstatementButton: false, + }, + }; + } + case "unpaid": { + return { + status: { + label: defaultStatusLabel, + value: this.i18nService.t("unpaid"), + }, + date: { + label: subscriptionExpiredDateLabel, + value: this.subscription.unpaidPeriodEndDate, + }, + callout: { + severity: "danger", + header: this.i18nService.t("unpaidInvoice"), + body: this.i18nService.t("toReactivateYourSubscription"), + showReinstatementButton: false, + }, + }; + } + case "pending_cancellation": { + const pendingCancellationText = this.i18nService.t("pendingCancellation"); + return { + status: { + label: defaultStatusLabel, + value: pendingCancellationText, + }, + date: { + label: cancellationDateLabel, + value: this.subscription.periodEndDate, + }, + callout: { + severity: "warning", + header: pendingCancellationText, + body: this.i18nService.t("subscriptionPendingCanceled"), + showReinstatementButton: true, + }, + }; + } + case "incomplete_expired": + case "canceled": { + const canceledText = this.i18nService.t("canceled"); + return { + status: { + label: defaultStatusLabel, + value: canceledText, + }, + date: { + label: cancellationDateLabel, + value: this.subscription.periodEndDate, + }, + callout: { + severity: "danger", + header: canceledText, + body: this.i18nService.t("subscriptionCanceled"), + showReinstatementButton: false, + }, + }; + } + } + } + + requestReinstatement = () => this.reinstatementRequested.emit(); +} diff --git a/apps/web/src/app/core/router.service.ts b/apps/web/src/app/core/router.service.ts index 5a0d903ba7..caebb22733 100644 --- a/apps/web/src/app/core/router.service.ts +++ b/apps/web/src/app/core/router.service.ts @@ -1,14 +1,31 @@ import { Injectable } from "@angular/core"; import { Title } from "@angular/platform-browser"; import { ActivatedRoute, NavigationEnd, Router } from "@angular/router"; -import { filter } from "rxjs"; +import { filter, firstValueFrom } from "rxjs"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { + KeyDefinition, + ROUTER_DISK, + StateProvider, + GlobalState, +} from "@bitwarden/common/platform/state"; + +const DEEP_LINK_REDIRECT_URL = new KeyDefinition(ROUTER_DISK, "deepLinkRedirectUrl", { + deserializer: (value: string) => value, +}); @Injectable() export class RouterService { + /** + * The string value of the URL the user tried to navigate to while unauthenticated. + * + * Developed to allow users to deep link even when the navigation gets interrupted + * by the authentication process. + */ + private deepLinkRedirectUrlState: GlobalState; + private previousUrl: string = undefined; private currentUrl: string = undefined; @@ -16,9 +33,11 @@ export class RouterService { private router: Router, private activatedRoute: ActivatedRoute, private titleService: Title, - private stateService: StateService, + private stateProvider: StateProvider, i18nService: I18nService, ) { + this.deepLinkRedirectUrlState = this.stateProvider.getGlobal(DEEP_LINK_REDIRECT_URL); + this.currentUrl = this.router.url; router.events @@ -67,14 +86,14 @@ export class RouterService { * @param url URL being saved to the Global State */ async persistLoginRedirectUrl(url: string): Promise { - await this.stateService.setDeepLinkRedirectUrl(url); + await this.deepLinkRedirectUrlState.update(() => url); } /** * Fetch and clear persisted LoginRedirectUrl if present in state */ async getAndClearLoginRedirectUrl(): Promise | undefined { - const persistedPreLoginUrl = await this.stateService.getDeepLinkRedirectUrl(); + const persistedPreLoginUrl = await firstValueFrom(this.deepLinkRedirectUrlState.state$); if (!Utils.isNullOrEmpty(persistedPreLoginUrl)) { await this.persistLoginRedirectUrl(null); diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts index 09522beadc..cdf45d0669 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts @@ -41,7 +41,7 @@ export class BulkMoveDialogComponent implements OnInit { cipherIds: string[] = []; formGroup = this.formBuilder.group({ - folderId: ["", [Validators.required]], + folderId: ["", [Validators.nullValidator]], }); folders$: Observable; diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index b8e5a5ff4d..05697461a9 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -7678,5 +7678,57 @@ }, "subscriptionUpdateFailed": { "message": "Subscription update failed" + }, + "trial": { + "message": "Trial", + "description": "A subscription status label." + }, + "pastDue": { + "message": "Past due", + "description": "A subscription status label" + }, + "subscriptionExpired": { + "message": "Subscription expired", + "description": "The date header used when a subscription is past due." + }, + "pastDueWarningForChargeAutomatically": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date to maintain your subscription. Please resolve the past due invoices by $SUSPENSION_DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "11" + }, + "suspension_date": { + "content": "$2", + "example": "01/10/2024" + } + }, + "description": "A warning shown to the user when their subscription is past due and they are charged automatically." + }, + "pastDueWarningForSendInvoice": { + "message": "You have a grace period of $DAYS$ days from the date your first unpaid invoice is due to maintain your subscription. Please resolve the past due invoices by $SUSPENSION_DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "11" + }, + "suspension_date": { + "content": "$2", + "example": "01/10/2024" + } + }, + "description": "A warning shown to the user when their subscription is past due and they pay via invoice." + }, + "unpaidInvoice": { + "message": "Unpaid invoice", + "description": "The header of a warning box shown to a user whose subscription is unpaid." + }, + "toReactivateYourSubscription": { + "message": "To reactivate your subscription, please resolve the past due invoices.", + "description": "The body of a warning box shown to a user whose subscription is unpaid." + }, + "cancellationDate": { + "message": "Cancellation date", + "description": "The date header used when a subscription is cancelled." } } diff --git a/libs/angular/src/auth/components/environment-selector.component.html b/libs/angular/src/auth/components/environment-selector.component.html index 8ad0602385..a8dab8f121 100644 --- a/libs/angular/src/auth/components/environment-selector.component.html +++ b/libs/angular/src/auth/components/environment-selector.component.html @@ -1,79 +1,81 @@ -
- {{ "loggingInOn" | i18n }}: - -
- - -
- + + +
+
-
-
+ + diff --git a/libs/angular/src/auth/components/environment-selector.component.ts b/libs/angular/src/auth/components/environment-selector.component.ts index 838667e080..9e811d02af 100644 --- a/libs/angular/src/auth/components/environment-selector.component.ts +++ b/libs/angular/src/auth/components/environment-selector.component.ts @@ -36,11 +36,9 @@ import { }) export class EnvironmentSelectorComponent { @Output() onOpenSelfHostedSettings = new EventEmitter(); - isOpen = false; - showingModal = false; - selectedEnvironment: Region; - ServerEnvironmentType = Region; - overlayPosition: ConnectedPosition[] = [ + protected isOpen = false; + protected ServerEnvironmentType = Region; + protected overlayPosition: ConnectedPosition[] = [ { originX: "start", originY: "bottom", diff --git a/libs/common/src/billing/models/response/subscription.response.ts b/libs/common/src/billing/models/response/subscription.response.ts index 0a2cb2290e..a05a40624d 100644 --- a/libs/common/src/billing/models/response/subscription.response.ts +++ b/libs/common/src/billing/models/response/subscription.response.ts @@ -36,6 +36,10 @@ export class BillingSubscriptionResponse extends BaseResponse { status: string; cancelled: boolean; items: BillingSubscriptionItemResponse[] = []; + collectionMethod: string; + suspensionDate?: string; + unpaidPeriodEndDate?: string; + gracePeriod?: number; constructor(response: any) { super(response); @@ -51,6 +55,10 @@ export class BillingSubscriptionResponse extends BaseResponse { if (items != null) { this.items = items.map((i: any) => new BillingSubscriptionItemResponse(i)); } + this.collectionMethod = this.getResponseProperty("CollectionMethod"); + this.suspensionDate = this.getResponseProperty("SuspensionDate"); + this.unpaidPeriodEndDate = this.getResponseProperty("unpaidPeriodEndDate"); + this.gracePeriod = this.getResponseProperty("GracePeriod"); } } diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 9470db9447..9d427034bd 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -8,6 +8,7 @@ export enum FeatureFlag { FlexibleCollectionsMigration = "flexible-collections-migration", ShowPaymentMethodWarningBanners = "show-payment-method-warning-banners", EnableConsolidatedBilling = "enable-consolidated-billing", + AC1795_UpdatedSubscriptionStatusSection = "AC-1795_updated-subscription-status-section", } // Replace this with a type safe lookup of the feature flag values in PM-2282 diff --git a/libs/common/src/platform/abstractions/state.service.ts b/libs/common/src/platform/abstractions/state.service.ts index 4c876316cd..4971481381 100644 --- a/libs/common/src/platform/abstractions/state.service.ts +++ b/libs/common/src/platform/abstractions/state.service.ts @@ -243,18 +243,5 @@ export abstract class StateService { setVaultTimeoutAction: (value: string, options?: StorageOptions) => Promise; getApproveLoginRequests: (options?: StorageOptions) => Promise; setApproveLoginRequests: (value: boolean, options?: StorageOptions) => Promise; - /** - * fetches string value of URL user tried to navigate to while unauthenticated. - * @param options Defines the storage options for the URL; Defaults to session Storage. - * @returns route called prior to successful login. - */ - getDeepLinkRedirectUrl: (options?: StorageOptions) => Promise; - /** - * Store URL in session storage by default, but can be configured. Developed to handle - * unauthN interrupted navigation. - * @param url URL of route - * @param options Defines the storage options for the URL; Defaults to session Storage. - */ - setDeepLinkRedirectUrl: (url: string, options?: StorageOptions) => Promise; nextUpActiveUser: () => Promise; } diff --git a/libs/common/src/platform/models/domain/global-state.ts b/libs/common/src/platform/models/domain/global-state.ts index cb9e3f71b3..703a998d1c 100644 --- a/libs/common/src/platform/models/domain/global-state.ts +++ b/libs/common/src/platform/models/domain/global-state.ts @@ -4,5 +4,4 @@ export class GlobalState { vaultTimeoutAction?: string; enableBrowserIntegration?: boolean; enableBrowserIntegrationFingerprint?: boolean; - deepLinkRedirectUrl?: string; } diff --git a/libs/common/src/platform/services/state.service.ts b/libs/common/src/platform/services/state.service.ts index fb62af250b..a35659a7ac 100644 --- a/libs/common/src/platform/services/state.service.ts +++ b/libs/common/src/platform/services/state.service.ts @@ -1173,23 +1173,6 @@ export class StateService< ); } - async getDeepLinkRedirectUrl(options?: StorageOptions): Promise { - return ( - await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskOptions())) - )?.deepLinkRedirectUrl; - } - - async setDeepLinkRedirectUrl(url: string, options?: StorageOptions): Promise { - const globals = await this.getGlobals( - this.reconcileOptions(options, await this.defaultOnDiskOptions()), - ); - globals.deepLinkRedirectUrl = url; - await this.saveGlobals( - globals, - this.reconcileOptions(options, await this.defaultOnDiskOptions()), - ); - } - protected async getGlobals(options: StorageOptions): Promise { let globals: TGlobalState; if (this.useMemory(options.storageLocation)) { diff --git a/libs/common/src/platform/state/state-definitions.ts b/libs/common/src/platform/state/state-definitions.ts index 069525434b..1c4f38335d 100644 --- a/libs/common/src/platform/state/state-definitions.ts +++ b/libs/common/src/platform/state/state-definitions.ts @@ -38,6 +38,7 @@ export const BILLING_DISK = new StateDefinition("billing", "disk"); export const KEY_CONNECTOR_DISK = new StateDefinition("keyConnector", "disk"); export const ACCOUNT_MEMORY = new StateDefinition("account", "memory"); export const AVATAR_DISK = new StateDefinition("avatar", "disk", { web: "disk-local" }); +export const ROUTER_DISK = new StateDefinition("router", "disk"); export const LOGIN_EMAIL_DISK = new StateDefinition("loginEmail", "disk", { web: "disk-local", }); diff --git a/libs/components/src/menu/menu-divider.component.html b/libs/components/src/menu/menu-divider.component.html index c7c2c739d2..98048261cf 100644 --- a/libs/components/src/menu/menu-divider.component.html +++ b/libs/components/src/menu/menu-divider.component.html @@ -1,4 +1,5 @@ diff --git a/libs/components/src/menu/menu-trigger-for.directive.ts b/libs/components/src/menu/menu-trigger-for.directive.ts index 20ae0b1ce7..7e392f241f 100644 --- a/libs/components/src/menu/menu-trigger-for.directive.ts +++ b/libs/components/src/menu/menu-trigger-for.directive.ts @@ -88,6 +88,7 @@ export class MenuTriggerForDirective implements OnDestroy { } this.destroyMenu(); }); + this.menu.keyManager.setFirstItemActive(); this.keyDownEventsSub = this.menu.keyManager && this.overlayRef