From 7bb8caed42595ff96a82c6813932310fd55bcd6c Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Wed, 8 May 2024 11:58:21 -0700 Subject: [PATCH 01/15] center secondary content (#9079) --- libs/auth/src/angular/anon-layout/anon-layout.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/auth/src/angular/anon-layout/anon-layout.component.html b/libs/auth/src/angular/anon-layout/anon-layout.component.html index 55da36d9bfa..5c3e7473316 100644 --- a/libs/auth/src/angular/anon-layout/anon-layout.component.html +++ b/libs/auth/src/angular/anon-layout/anon-layout.component.html @@ -13,7 +13,7 @@

{{ subtitle }}

-
+
From 37d409578afba1023342e1315149b581eb3b21c0 Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Wed, 8 May 2024 21:04:41 +0200 Subject: [PATCH 02/15] [PM-7740] Create notifications settings component (navigational changes) (#8919) * Move about.component into tools ownership * Split out account security settings Move settings.component.ts to auth/popup/settings and rename to account-security.component.ts Move controls from settings.component.html and create account-security.component.html Move settings.component.html to tools/popup/settings.component.html Create settings.component.ts under tools/popup/settings Fixup module imports and routing Add new strings to en/message.json * Move vault-timeout-input.component to auth * Move await-desktop-dialog.component to auth * Add transition for account-security * Create notifications settings component * Move excluded-domains component over to be owned by team-autofill * Add notifications entry to settings screen * Move excluded domains from settings to notifications settings screen --------- Co-authored-by: Daniel James Smith --- apps/browser/src/_locales/en/messages.json | 3 + .../settings/excluded-domains.component.html | 2 +- .../settings/excluded-domains.component.ts | 4 +- .../settings/notifications.component.html | 89 +++++++++++++++++++ .../popup/settings/notifications.component.ts | 53 +++++++++++ .../src/popup/app-routing.animations.ts | 10 ++- apps/browser/src/popup/app-routing.module.ts | 9 +- apps/browser/src/popup/app.module.ts | 4 +- .../popup/settings/settings.component.html | 8 +- 9 files changed, 170 insertions(+), 12 deletions(-) rename apps/browser/src/{ => autofill}/popup/settings/excluded-domains.component.html (98%) rename apps/browser/src/{ => autofill}/popup/settings/excluded-domains.component.ts (97%) create mode 100644 apps/browser/src/autofill/popup/settings/notifications.component.html create mode 100644 apps/browser/src/autofill/popup/settings/notifications.component.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index fcc80e5ff7d..9360524da1d 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -3035,6 +3035,9 @@ "accountSecurity": { "message": "Account security" }, + "notifications": { + "message": "Notifications" + }, "appearance": { "message": "Appearance" }, diff --git a/apps/browser/src/popup/settings/excluded-domains.component.html b/apps/browser/src/autofill/popup/settings/excluded-domains.component.html similarity index 98% rename from apps/browser/src/popup/settings/excluded-domains.component.html rename to apps/browser/src/autofill/popup/settings/excluded-domains.component.html index 652eb4aef08..8f78ac16404 100644 --- a/apps/browser/src/popup/settings/excluded-domains.component.html +++ b/apps/browser/src/autofill/popup/settings/excluded-domains.component.html @@ -1,7 +1,7 @@
- diff --git a/apps/browser/src/popup/settings/excluded-domains.component.ts b/apps/browser/src/autofill/popup/settings/excluded-domains.component.ts similarity index 97% rename from apps/browser/src/popup/settings/excluded-domains.component.ts rename to apps/browser/src/autofill/popup/settings/excluded-domains.component.ts index 1741fbaa659..5dad991dfa4 100644 --- a/apps/browser/src/popup/settings/excluded-domains.component.ts +++ b/apps/browser/src/autofill/popup/settings/excluded-domains.component.ts @@ -8,8 +8,8 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { BrowserApi } from "../../platform/browser/browser-api"; -import { enableAccountSwitching } from "../../platform/flags"; +import { BrowserApi } from "../../../platform/browser/browser-api"; +import { enableAccountSwitching } from "../../../platform/flags"; interface ExcludedDomain { uri: string; diff --git a/apps/browser/src/autofill/popup/settings/notifications.component.html b/apps/browser/src/autofill/popup/settings/notifications.component.html new file mode 100644 index 00000000000..89d83c9e480 --- /dev/null +++ b/apps/browser/src/autofill/popup/settings/notifications.component.html @@ -0,0 +1,89 @@ +
+
+ +
+

+ {{ "notifications" | i18n }} +

+
+ +
+
+
+
+
+
+ + +
+
+ +
+
+
+
+ + +
+
+ +
+
+
+
+ + +
+
+ +
+
+
+ +
+
+
diff --git a/apps/browser/src/autofill/popup/settings/notifications.component.ts b/apps/browser/src/autofill/popup/settings/notifications.component.ts new file mode 100644 index 00000000000..8e092192757 --- /dev/null +++ b/apps/browser/src/autofill/popup/settings/notifications.component.ts @@ -0,0 +1,53 @@ +import { Component, OnInit } from "@angular/core"; +import { firstValueFrom } from "rxjs"; + +import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service"; +import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; + +import { enableAccountSwitching } from "../../../platform/flags"; + +@Component({ + selector: "autofill-notification-settings", + templateUrl: "notifications.component.html", +}) +export class NotifcationsSettingsComponent implements OnInit { + enableAddLoginNotification = false; + enableChangedPasswordNotification = false; + enablePasskeys = true; + accountSwitcherEnabled = false; + + constructor( + private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction, + private vaultSettingsService: VaultSettingsService, + ) { + this.accountSwitcherEnabled = enableAccountSwitching(); + } + + async ngOnInit() { + this.enableAddLoginNotification = await firstValueFrom( + this.userNotificationSettingsService.enableAddedLoginPrompt$, + ); + + this.enableChangedPasswordNotification = await firstValueFrom( + this.userNotificationSettingsService.enableChangedPasswordPrompt$, + ); + + this.enablePasskeys = await firstValueFrom(this.vaultSettingsService.enablePasskeys$); + } + + async updateAddLoginNotification() { + await this.userNotificationSettingsService.setEnableAddedLoginPrompt( + this.enableAddLoginNotification, + ); + } + + async updateChangedPasswordNotification() { + await this.userNotificationSettingsService.setEnableChangedPasswordPrompt( + this.enableChangedPasswordNotification, + ); + } + + async updateEnablePasskeys() { + await this.vaultSettingsService.setEnablePasskeys(this.enablePasskeys); + } +} diff --git a/apps/browser/src/popup/app-routing.animations.ts b/apps/browser/src/popup/app-routing.animations.ts index 55d2687a436..96e5dbbe378 100644 --- a/apps/browser/src/popup/app-routing.animations.ts +++ b/apps/browser/src/popup/app-routing.animations.ts @@ -196,9 +196,6 @@ export const routerTransition = trigger("routerTransition", [ transition("vault-settings => sync", inSlideLeft), transition("sync => vault-settings", outSlideRight), - transition("tabs => excluded-domains", inSlideLeft), - transition("excluded-domains => tabs", outSlideRight), - transition("tabs => options", inSlideLeft), transition("options => tabs", outSlideRight), @@ -223,6 +220,13 @@ export const routerTransition = trigger("routerTransition", [ transition("tabs => edit-send, send-type => edit-send", inSlideUp), transition("edit-send => tabs, edit-send => send-type", outSlideDown), + // Notification settings + transition("tabs => notifications", inSlideLeft), + transition("notifications => tabs", outSlideRight), + + transition("notifications => excluded-domains", inSlideLeft), + transition("excluded-domains => notifications", outSlideRight), + transition("tabs => autofill", inSlideLeft), transition("autofill => tabs", outSlideRight), diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 8fb397fe240..1db2f92d3e5 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -27,6 +27,8 @@ import { TwoFactorOptionsComponent } from "../auth/popup/two-factor-options.comp import { TwoFactorComponent } from "../auth/popup/two-factor.component"; import { UpdateTempPasswordComponent } from "../auth/popup/update-temp-password.component"; import { AutofillComponent } from "../autofill/popup/settings/autofill.component"; +import { ExcludedDomainsComponent } from "../autofill/popup/settings/excluded-domains.component"; +import { NotifcationsSettingsComponent } from "../autofill/popup/settings/notifications.component"; import { PremiumComponent } from "../billing/popup/settings/premium.component"; import BrowserPopupUtils from "../platform/popup/browser-popup-utils"; import { GeneratorComponent } from "../tools/popup/generator/generator.component"; @@ -56,7 +58,6 @@ import { VaultSettingsComponent } from "../vault/popup/settings/vault-settings.c import { extensionRefreshRedirect, extensionRefreshSwap } from "./extension-refresh-route-utils"; import { debounceNavigationGuard } from "./services/debounce-navigation.service"; -import { ExcludedDomainsComponent } from "./settings/excluded-domains.component"; import { HelpAndFeedbackComponent } from "./settings/help-and-feedback.component"; import { OptionsComponent } from "./settings/options.component"; import { TabsV2Component } from "./tabs-v2.component"; @@ -256,6 +257,12 @@ const routes: Routes = [ canActivate: [AuthGuard], data: { state: "account-security" }, }, + { + path: "notifications", + component: NotifcationsSettingsComponent, + canActivate: [AuthGuard], + data: { state: "notifications" }, + }, { path: "vault-settings", component: VaultSettingsComponent, diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 4a310027c1b..cbc711f107f 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -37,6 +37,8 @@ import { TwoFactorOptionsComponent } from "../auth/popup/two-factor-options.comp import { TwoFactorComponent } from "../auth/popup/two-factor.component"; import { UpdateTempPasswordComponent } from "../auth/popup/update-temp-password.component"; import { AutofillComponent } from "../autofill/popup/settings/autofill.component"; +import { ExcludedDomainsComponent } from "../autofill/popup/settings/excluded-domains.component"; +import { NotifcationsSettingsComponent } from "../autofill/popup/settings/notifications.component"; import { PremiumComponent } from "../billing/popup/settings/premium.component"; import { HeaderComponent } from "../platform/popup/header.component"; import { PopupFooterComponent } from "../platform/popup/layout/popup-footer.component"; @@ -81,7 +83,6 @@ import { AppComponent } from "./app.component"; import { PopOutComponent } from "./components/pop-out.component"; import { UserVerificationComponent } from "./components/user-verification.component"; import { ServicesModule } from "./services/services.module"; -import { ExcludedDomainsComponent } from "./settings/excluded-domains.component"; import { HelpAndFeedbackComponent } from "./settings/help-and-feedback.component"; import { OptionsComponent } from "./settings/options.component"; import { TabsV2Component } from "./tabs-v2.component"; @@ -149,6 +150,7 @@ import "../platform/popup/locales"; LoginViaAuthRequestComponent, LoginDecryptionOptionsComponent, OptionsComponent, + NotifcationsSettingsComponent, AppearanceComponent, GeneratorComponent, PasswordGeneratorHistoryComponent, diff --git a/apps/browser/src/tools/popup/settings/settings.component.html b/apps/browser/src/tools/popup/settings/settings.component.html index 997bc557b67..7506a07da55 100644 --- a/apps/browser/src/tools/popup/settings/settings.component.html +++ b/apps/browser/src/tools/popup/settings/settings.component.html @@ -30,17 +30,17 @@
From 59c9606df3313f173e96404775aca83b9d2f944b Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Wed, 8 May 2024 15:38:56 -0400 Subject: [PATCH 03/15] Prevent calls to Billing Status endpoint when FF is off (#9032) --- apps/web/src/app/app.component.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 6e2761a9c4e..7d2b6d999aa 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -2,7 +2,17 @@ import { DOCUMENT } from "@angular/common"; import { Component, Inject, NgZone, OnDestroy, OnInit } from "@angular/core"; import { NavigationEnd, Router } from "@angular/router"; import * as jq from "jquery"; -import { Subject, filter, firstValueFrom, map, switchMap, takeUntil, timeout, timer } from "rxjs"; +import { + Subject, + combineLatest, + filter, + firstValueFrom, + map, + switchMap, + takeUntil, + timeout, + timer, +} from "rxjs"; import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; @@ -15,6 +25,7 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { PaymentMethodWarningsServiceAbstraction as PaymentMethodWarningService } from "@bitwarden/common/billing/abstractions/payment-method-warnings-service.abstraction"; +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 { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service"; @@ -241,8 +252,12 @@ export class AppComponent implements OnDestroy, OnInit { new SendOptionsPolicy(), ]); - this.paymentMethodWarningsRefresh$ + combineLatest([ + this.configService.getFeatureFlag$(FeatureFlag.ShowPaymentMethodWarningBanners), + this.paymentMethodWarningsRefresh$, + ]) .pipe( + filter(([showPaymentMethodWarningBanners]) => showPaymentMethodWarningBanners), switchMap(() => this.organizationService.memberOrganizations$), switchMap( async (organizations) => From 2227fd1190ed618fb3d2024697753a7bca1a9d44 Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Thu, 9 May 2024 12:29:51 +0200 Subject: [PATCH 04/15] Rename about.component to about-dialog.component (#9094) Co-authored-by: Daniel James Smith --- .../about-dialog.component.html} | 0 .../about-dialog.component.ts} | 4 ++-- apps/browser/src/tools/popup/settings/settings.component.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename apps/browser/src/tools/popup/settings/{about/about.component.html => about-dialog/about-dialog.component.html} (100%) rename apps/browser/src/tools/popup/settings/{about/about.component.ts => about-dialog/about-dialog.component.ts} (93%) diff --git a/apps/browser/src/tools/popup/settings/about/about.component.html b/apps/browser/src/tools/popup/settings/about-dialog/about-dialog.component.html similarity index 100% rename from apps/browser/src/tools/popup/settings/about/about.component.html rename to apps/browser/src/tools/popup/settings/about-dialog/about-dialog.component.html diff --git a/apps/browser/src/tools/popup/settings/about/about.component.ts b/apps/browser/src/tools/popup/settings/about-dialog/about-dialog.component.ts similarity index 93% rename from apps/browser/src/tools/popup/settings/about/about.component.ts rename to apps/browser/src/tools/popup/settings/about-dialog/about-dialog.component.ts index d7f98c1e7f7..0467debdfb5 100644 --- a/apps/browser/src/tools/popup/settings/about/about.component.ts +++ b/apps/browser/src/tools/popup/settings/about-dialog/about-dialog.component.ts @@ -9,11 +9,11 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { ButtonModule, DialogModule } from "@bitwarden/components"; @Component({ - templateUrl: "about.component.html", + templateUrl: "about-dialog.component.html", standalone: true, imports: [CommonModule, JslibModule, DialogModule, ButtonModule], }) -export class AboutComponent { +export class AboutDialogComponent { protected year = new Date().getFullYear(); protected version$: Observable; diff --git a/apps/browser/src/tools/popup/settings/settings.component.ts b/apps/browser/src/tools/popup/settings/settings.component.ts index 81727c442cd..d0c5d63092b 100644 --- a/apps/browser/src/tools/popup/settings/settings.component.ts +++ b/apps/browser/src/tools/popup/settings/settings.component.ts @@ -13,7 +13,7 @@ import { DialogService } from "@bitwarden/components"; import { BrowserApi } from "../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils"; -import { AboutComponent } from "./about/about.component"; +import { AboutDialogComponent } from "./about-dialog/about-dialog.component"; const RateUrls = { [DeviceType.ChromeExtension]: @@ -84,7 +84,7 @@ export class SettingsComponent implements OnInit { } about() { - this.dialogService.open(AboutComponent); + this.dialogService.open(AboutDialogComponent); } rate() { From da144410e8cba06a1305c6e9359a413ca95ca558 Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Thu, 9 May 2024 12:30:43 +0200 Subject: [PATCH 05/15] Remove settings from options.component which had been moved to notifications.component (#9093) Co-authored-by: Daniel James Smith --- .../src/popup/settings/options.component.html | 61 ------------------- .../src/popup/settings/options.component.ts | 32 ---------- 2 files changed, 93 deletions(-) diff --git a/apps/browser/src/popup/settings/options.component.html b/apps/browser/src/popup/settings/options.component.html index fa2b7514dbf..0382eb5b866 100644 --- a/apps/browser/src/popup/settings/options.component.html +++ b/apps/browser/src/popup/settings/options.component.html @@ -60,67 +60,6 @@
-
-
-
- - -
-
- -
-
-
-
- - -
-
- -
-
-
-
- - -
-
- -
diff --git a/apps/browser/src/popup/settings/options.component.ts b/apps/browser/src/popup/settings/options.component.ts index 0344362d36a..cfcc81bb22c 100644 --- a/apps/browser/src/popup/settings/options.component.ts +++ b/apps/browser/src/popup/settings/options.component.ts @@ -3,7 +3,6 @@ import { firstValueFrom } from "rxjs"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; -import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service"; import { ClearClipboardDelaySetting } from "@bitwarden/common/autofill/types"; import { UriMatchStrategy, @@ -25,9 +24,6 @@ export class OptionsComponent implements OnInit { autoFillOnPageLoadOptions: any[]; enableAutoTotpCopy = false; // TODO: Does it matter if this is set to false or true? enableContextMenuItem = false; - enableAddLoginNotification = false; - enableChangedPasswordNotification = false; - enablePasskeys = true; showCardsCurrentTab = false; showIdentitiesCurrentTab = false; showClearClipboard = true; @@ -36,13 +32,11 @@ export class OptionsComponent implements OnInit { clearClipboard: ClearClipboardDelaySetting; clearClipboardOptions: any[]; showGeneral = true; - showAutofill = true; showDisplay = true; accountSwitcherEnabled = false; constructor( private messagingService: MessagingService, - private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction, private autofillSettingsService: AutofillSettingsServiceAbstraction, private domainSettingsService: DomainSettingsService, i18nService: I18nService, @@ -82,14 +76,6 @@ export class OptionsComponent implements OnInit { this.autofillSettingsService.autofillOnPageLoadDefault$, ); - this.enableAddLoginNotification = await firstValueFrom( - this.userNotificationSettingsService.enableAddedLoginPrompt$, - ); - - this.enableChangedPasswordNotification = await firstValueFrom( - this.userNotificationSettingsService.enableChangedPasswordPrompt$, - ); - this.enableContextMenuItem = await firstValueFrom( this.autofillSettingsService.enableContextMenu$, ); @@ -101,8 +87,6 @@ export class OptionsComponent implements OnInit { this.enableAutoTotpCopy = await firstValueFrom(this.autofillSettingsService.autoCopyTotp$); - this.enablePasskeys = await firstValueFrom(this.vaultSettingsService.enablePasskeys$); - const defaultUriMatch = await firstValueFrom( this.domainSettingsService.defaultUriMatchStrategy$, ); @@ -111,22 +95,6 @@ export class OptionsComponent implements OnInit { this.clearClipboard = await firstValueFrom(this.autofillSettingsService.clearClipboardDelay$); } - async updateAddLoginNotification() { - await this.userNotificationSettingsService.setEnableAddedLoginPrompt( - this.enableAddLoginNotification, - ); - } - - async updateChangedPasswordNotification() { - await this.userNotificationSettingsService.setEnableChangedPasswordPrompt( - this.enableChangedPasswordNotification, - ); - } - - async updateEnablePasskeys() { - await this.vaultSettingsService.setEnablePasskeys(this.enablePasskeys); - } - async updateContextMenuItem() { await this.autofillSettingsService.setEnableContextMenu(this.enableContextMenuItem); this.messagingService.send("bgUpdateContextMenu"); From c69cc377162bf3072039047466a2555c2200cff9 Mon Sep 17 00:00:00 2001 From: Jake Fink Date: Thu, 9 May 2024 10:54:05 -0400 Subject: [PATCH 06/15] [PM-7747] add timeout to safari sendMessageWithResponse (#9082) * add timeout to safari sendMessageWithResponse * change to query views instead of sending message --- .../services/platform-utils/browser-platform-utils.service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts index 855492521bd..4163ca93107 100644 --- a/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts +++ b/apps/browser/src/platform/services/platform-utils/browser-platform-utils.service.ts @@ -163,6 +163,10 @@ export abstract class BrowserPlatformUtilsService implements PlatformUtilsServic * the view is open. */ async isViewOpen(): Promise { + if (this.isSafari()) { + // Query views on safari since chrome.runtime.sendMessage does not timeout and will hang. + return BrowserApi.isPopupOpen(); + } return Boolean(await BrowserApi.sendMessageWithResponse("checkVaultPopupHeartbeat")); } From 1180c60e913246bcf31527d51ba8be5e5f5a40cc Mon Sep 17 00:00:00 2001 From: KiruthigaManivannan <162679756+KiruthigaManivannan@users.noreply.github.com> Date: Thu, 9 May 2024 20:42:45 +0530 Subject: [PATCH 07/15] Pm 7843 two factor verification is empty on organization duo 2 fa (#9086) * PM-7843 Two Factor Verification is Empty on Organization duo 2fa * PM-7843 Addressed review comments * PM-7843 Bug fixed --- .../settings/two-factor-setup.component.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts b/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts index 8cf7ed313fe..c95ff754c45 100644 --- a/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts +++ b/apps/web/src/app/admin-console/organizations/settings/two-factor-setup.component.ts @@ -1,6 +1,6 @@ import { Component } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; -import { concatMap, takeUntil, map, lastValueFrom } from "rxjs"; +import { concatMap, takeUntil, map } from "rxjs"; import { tap } from "rxjs/operators"; import { ModalService } from "@bitwarden/angular/services/modal.service"; @@ -16,7 +16,6 @@ import { DialogService } from "@bitwarden/components"; import { TwoFactorDuoComponent } from "../../../auth/settings/two-factor-duo.component"; import { TwoFactorSetupComponent as BaseTwoFactorSetupComponent } from "../../../auth/settings/two-factor-setup.component"; -import { TwoFactorVerifyComponent } from "../../../auth/settings/two-factor-verify.component"; @Component({ selector: "app-two-factor-setup", @@ -66,17 +65,17 @@ export class TwoFactorSetupComponent extends BaseTwoFactorSetupComponent { async manage(type: TwoFactorProviderType) { switch (type) { case TwoFactorProviderType.OrganizationDuo: { - const twoFactorVerifyDialogRef = TwoFactorVerifyComponent.open(this.dialogService, { - data: { type: type, organizationId: this.organizationId }, - }); - const result: AuthResponse = await lastValueFrom( - twoFactorVerifyDialogRef.closed, + const result: AuthResponse = await this.callTwoFactorVerifyDialog( + TwoFactorProviderType.OrganizationDuo, ); + if (!result) { return; } const duoComp = await this.openModal(this.duoModalRef, TwoFactorDuoComponent); + duoComp.type = TwoFactorProviderType.OrganizationDuo; + duoComp.organizationId = this.organizationId; duoComp.auth(result); duoComp.onUpdated.pipe(takeUntil(this.destroy$)).subscribe((enabled: boolean) => { this.updateStatus(enabled, TwoFactorProviderType.OrganizationDuo); From 30ef66139e2b787b7615fdfdada6fa4afab7d971 Mon Sep 17 00:00:00 2001 From: vinith-kovan <156108204+vinith-kovan@users.noreply.github.com> Date: Thu, 9 May 2024 20:46:43 +0530 Subject: [PATCH 08/15] [PM 5007] migrate premium component (#8387) * premium component migration * premium component migration * premium component migration * premium component migration --- .../billing/individual/premium.component.html | 258 +++++++++--------- .../billing/individual/premium.component.ts | 116 ++++---- 2 files changed, 196 insertions(+), 178 deletions(-) diff --git a/apps/web/src/app/billing/individual/premium.component.html b/apps/web/src/app/billing/individual/premium.component.html index f962f7cfe13..e3afa7779b8 100644 --- a/apps/web/src/app/billing/individual/premium.component.html +++ b/apps/web/src/app/billing/individual/premium.component.html @@ -1,129 +1,143 @@ - -
-

{{ "goPremium" | i18n }}

-
- - {{ "alreadyPremiumFromOrg" | i18n }} - - -

{{ "premiumUpgradeUnlockFeatures" | i18n }}

-
    -
  • - - {{ "premiumSignUpStorage" | i18n }} -
  • -
  • - - {{ "premiumSignUpTwoStepOptions" | i18n }} -
  • -
  • - - {{ "premiumSignUpEmergency" | i18n }} -
  • -
  • - - {{ "premiumSignUpReports" | i18n }} -
  • -
  • - - {{ "premiumSignUpTotp" | i18n }} -
  • -
  • - - {{ "premiumSignUpSupport" | i18n }} -
  • -
  • - - {{ "premiumSignUpFuture" | i18n }} -
  • -
-

- {{ - "premiumPriceWithFamilyPlan" | i18n: (premiumPrice | currency: "$") : familyPlanMaxUserCount - }} - {{ - "bitwardenFamiliesPlan" | i18n - }} -

- +

{{ "goPremium" | i18n }}

+ - {{ "purchasePremium" | i18n }} -
-
- -

{{ "uploadLicenseFilePremium" | i18n }}

- -
- - - {{ - "licenseFileDesc" | i18n: "bitwarden_premium_license.json" - }} -
- + {{ this.licenseFile ? this.licenseFile.name : ("noFileChosen" | i18n) }} +
+ + {{ "licenseFileDesc" | i18n: "bitwarden_premium_license.json" }} + + - -
-

{{ "addons" | i18n }}

-
-
- - - {{ - "additionalStorageIntervalDesc" - | i18n: "1 GB" : (storageGbPrice | currency: "$") : ("year" | i18n) - }} + + + +

{{ "addons" | i18n }}

+
+ + {{ "additionalStorageGb" | i18n }} + + {{ + "additionalStorageIntervalDesc" + | i18n: "1 GB" : (storageGbPrice | currency: "$") : ("year" | i18n) + }} +
-
-

{{ "summary" | i18n }}

- {{ "premiumMembership" | i18n }}: {{ premiumPrice | currency: "$" }}
- {{ "additionalStorageGb" | i18n }}: {{ additionalStorage || 0 }} GB × - {{ storageGbPrice | currency: "$" }} = - {{ additionalStorageTotal | currency: "$" }} -
-

{{ "paymentInformation" | i18n }}

- - -
-
- {{ "planPrice" | i18n }}: {{ subtotal | currency: "USD $" }} -
- - {{ "estimatedTax" | i18n }}: {{ taxCharges | currency: "USD $" }} - + + +

{{ "summary" | i18n }}

+ {{ "premiumMembership" | i18n }}: {{ premiumPrice | currency: "$" }}
+ {{ "additionalStorageGb" | i18n }}: {{ additionalStorage || 0 }} GB × + {{ storageGbPrice | currency: "$" }} = + {{ additionalStorageTotal | currency: "$" }} +
+
+ +

{{ "paymentInformation" | i18n }}

+ + +
+
+ {{ "planPrice" | i18n }}: {{ subtotal | currency: "USD $" }} +
+ + {{ "estimatedTax" | i18n }}: {{ taxCharges | currency: "USD $" }} + +
+
+

+ {{ "total" | i18n }}: {{ total | currency: "USD $" }}/{{ "year" | i18n }} +

-
-

- {{ "total" | i18n }}: {{ total | currency: "USD $" }}/{{ "year" | i18n }} -

-
- {{ "paymentChargedAnnually" | i18n }} - +

{{ "paymentChargedAnnually" | i18n }}

+ + diff --git a/apps/web/src/app/billing/individual/premium.component.ts b/apps/web/src/app/billing/individual/premium.component.ts index 60536c17b50..79a8bad75ae 100644 --- a/apps/web/src/app/billing/individual/premium.component.ts +++ b/apps/web/src/app/billing/individual/premium.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit, ViewChild } from "@angular/core"; +import { FormControl, FormGroup, Validators } from "@angular/forms"; import { Router } from "@angular/router"; import { firstValueFrom, Observable } from "rxjs"; @@ -7,7 +8,6 @@ import { TokenService } from "@bitwarden/common/auth/abstractions/token.service" import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.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"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; @@ -26,11 +26,16 @@ export class PremiumComponent implements OnInit { premiumPrice = 10; familyPlanMaxUserCount = 6; storageGbPrice = 4; - additionalStorage = 0; cloudWebVaultUrl: string; + licenseFile: File = null; formPromise: Promise; - + protected licenseForm = new FormGroup({ + file: new FormControl(null, [Validators.required]), + }); + protected addonForm = new FormGroup({ + additionalStorage: new FormControl(0, [Validators.max(99), Validators.min(0)]), + }); constructor( private apiService: ApiService, private i18nService: I18nService, @@ -39,14 +44,17 @@ export class PremiumComponent implements OnInit { private router: Router, private messagingService: MessagingService, private syncService: SyncService, - private logService: LogService, private environmentService: EnvironmentService, private billingAccountProfileStateService: BillingAccountProfileStateService, ) { this.selfHosted = platformUtilsService.isSelfHost(); this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$; } - + protected setSelectedFile(event: Event) { + const fileInputEl = event.target; + const file: File = fileInputEl.files.length > 0 ? fileInputEl.files[0] : null; + this.licenseFile = file; + } async ngOnInit() { this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$); if (await firstValueFrom(this.billingAccountProfileStateService.hasPremiumPersonally$)) { @@ -56,13 +64,11 @@ export class PremiumComponent implements OnInit { return; } } - - async submit() { - let files: FileList = null; + submit = async () => { + this.licenseForm.markAllAsTouched(); + this.addonForm.markAllAsTouched(); if (this.selfHosted) { - const fileEl = document.getElementById("file") as HTMLInputElement; - files = fileEl.files; - if (files == null || files.length === 0) { + if (this.licenseFile == null) { this.platformUtilsService.showToast( "error", this.i18nService.t("errorOccurred"), @@ -72,53 +78,48 @@ export class PremiumComponent implements OnInit { } } - try { - if (this.selfHosted) { - // eslint-disable-next-line @typescript-eslint/no-misused-promises - if (!this.tokenService.getEmailVerified()) { - this.platformUtilsService.showToast( - "error", - this.i18nService.t("errorOccurred"), - this.i18nService.t("verifyEmailFirst"), - ); - return; - } - - const fd = new FormData(); - fd.append("license", files[0]); - this.formPromise = this.apiService.postAccountLicense(fd).then(() => { - return this.finalizePremium(); - }); - } else { - this.formPromise = this.paymentComponent - .createPaymentToken() - .then((result) => { - const fd = new FormData(); - fd.append("paymentMethodType", result[1].toString()); - if (result[0] != null) { - fd.append("paymentToken", result[0]); - } - fd.append("additionalStorageGb", (this.additionalStorage || 0).toString()); - fd.append("country", this.taxInfoComponent.taxInfo.country); - fd.append("postalCode", this.taxInfoComponent.taxInfo.postalCode); - return this.apiService.postPremium(fd); - }) - .then((paymentResponse) => { - if (!paymentResponse.success && paymentResponse.paymentIntentClientSecret != null) { - return this.paymentComponent.handleStripeCardPayment( - paymentResponse.paymentIntentClientSecret, - () => this.finalizePremium(), - ); - } else { - return this.finalizePremium(); - } - }); + if (this.selfHosted) { + // eslint-disable-next-line @typescript-eslint/no-misused-promises + if (!this.tokenService.getEmailVerified()) { + this.platformUtilsService.showToast( + "error", + this.i18nService.t("errorOccurred"), + this.i18nService.t("verifyEmailFirst"), + ); + return; } - await this.formPromise; - } catch (e) { - this.logService.error(e); + + const fd = new FormData(); + fd.append("license", this.licenseFile); + await this.apiService.postAccountLicense(fd).then(() => { + return this.finalizePremium(); + }); + } else { + await this.paymentComponent + .createPaymentToken() + .then((result) => { + const fd = new FormData(); + fd.append("paymentMethodType", result[1].toString()); + if (result[0] != null) { + fd.append("paymentToken", result[0]); + } + fd.append("additionalStorageGb", (this.additionalStorage || 0).toString()); + fd.append("country", this.taxInfoComponent.taxInfo.country); + fd.append("postalCode", this.taxInfoComponent.taxInfo.postalCode); + return this.apiService.postPremium(fd); + }) + .then((paymentResponse) => { + if (!paymentResponse.success && paymentResponse.paymentIntentClientSecret != null) { + return this.paymentComponent.handleStripeCardPayment( + paymentResponse.paymentIntentClientSecret, + () => this.finalizePremium(), + ); + } else { + return this.finalizePremium(); + } + }); } - } + }; async finalizePremium() { await this.apiService.refreshIdentityToken(); @@ -127,6 +128,9 @@ export class PremiumComponent implements OnInit { await this.router.navigate(["/settings/subscription/user-subscription"]); } + get additionalStorage(): number { + return this.addonForm.get("additionalStorage").value; + } get additionalStorageTotal(): number { return this.storageGbPrice * Math.abs(this.additionalStorage || 0); } From 0c2e8c15dc900ef522d93d1add30215100c21a4d Mon Sep 17 00:00:00 2001 From: vinith-kovan <156108204+vinith-kovan@users.noreply.github.com> Date: Thu, 9 May 2024 21:01:00 +0530 Subject: [PATCH 09/15] [PM 5012] migrate adjust subscription component (#8239) * adjust subscription component migration * adjust subscription component migration * adjust subscription component migration --- .../adjust-subscription.component.html | 96 +++++++++--------- .../adjust-subscription.component.ts | 99 ++++++++++++------- 2 files changed, 106 insertions(+), 89 deletions(-) diff --git a/apps/web/src/app/billing/organizations/adjust-subscription.component.html b/apps/web/src/app/billing/organizations/adjust-subscription.component.html index f0200da6384..9fe8d205407 100644 --- a/apps/web/src/app/billing/organizations/adjust-subscription.component.html +++ b/apps/web/src/app/billing/organizations/adjust-subscription.component.html @@ -1,65 +1,57 @@ -
-
-
-
- - - + +
+
+ + {{ "subscriptionSeats" | i18n }} + + {{ "total" | i18n }}: {{ additionalSeatCount || 0 }} × {{ seatPrice | currency: "$" }} = {{ adjustedSeatTotal | currency: "$" }} / - {{ interval | i18n }} - -
+ {{ interval | i18n }} +
-
-
-
- - -
- {{ "limitSubscriptionDesc" | i18n }} -
-
-
-
- +
+
+ + + {{ "limitSubscription" | i18n }} + {{ "limitSubscriptionDesc" | i18n }} + +
+
+
+ + {{ "maxSeatLimit" | i18n }} - + {{ "maxSeatCost" | i18n }}: {{ additionalMaxSeatCount || 0 }} × {{ seatPrice | currency: "$" }} = {{ maxSeatTotal | currency: "$" }} / - {{ interval | i18n }} - -
+ {{ interval | i18n }} +
-
+ diff --git a/apps/web/src/app/billing/organizations/adjust-subscription.component.ts b/apps/web/src/app/billing/organizations/adjust-subscription.component.ts index 4290a1281b0..b843c79cb95 100644 --- a/apps/web/src/app/billing/organizations/adjust-subscription.component.ts +++ b/apps/web/src/app/billing/organizations/adjust-subscription.component.ts @@ -1,77 +1,102 @@ -import { Component, EventEmitter, Input, Output } from "@angular/core"; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core"; +import { FormBuilder, Validators } from "@angular/forms"; +import { Subject, takeUntil } from "rxjs"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/organization-subscription-update.request"; 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"; @Component({ selector: "app-adjust-subscription", templateUrl: "adjust-subscription.component.html", }) -export class AdjustSubscription { +export class AdjustSubscription implements OnInit, OnDestroy { @Input() organizationId: string; @Input() maxAutoscaleSeats: number; @Input() currentSeatCount: number; @Input() seatPrice = 0; @Input() interval = "year"; @Output() onAdjusted = new EventEmitter(); + private destroy$ = new Subject(); - formPromise: Promise; - limitSubscription: boolean; - newSeatCount: number; - newMaxSeats: number; - + adjustSubscriptionForm = this.formBuilder.group({ + newSeatCount: [0, [Validators.min(0)]], + limitSubscription: [false], + newMaxSeats: [0, [Validators.min(0)]], + }); + get limitSubscription(): boolean { + return this.adjustSubscriptionForm.value.limitSubscription; + } constructor( private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, - private logService: LogService, private organizationApiService: OrganizationApiServiceAbstraction, + private formBuilder: FormBuilder, ) {} ngOnInit() { - this.limitSubscription = this.maxAutoscaleSeats != null; - this.newSeatCount = this.currentSeatCount; - this.newMaxSeats = this.maxAutoscaleSeats; + this.adjustSubscriptionForm.patchValue({ + newSeatCount: this.currentSeatCount, + limitSubscription: this.maxAutoscaleSeats != null, + newMaxSeats: this.maxAutoscaleSeats, + }); + this.adjustSubscriptionForm + .get("limitSubscription") + .valueChanges.pipe(takeUntil(this.destroy$)) + .subscribe((value: boolean) => { + if (value) { + this.adjustSubscriptionForm + .get("newMaxSeats") + .addValidators([ + Validators.min( + this.adjustSubscriptionForm.value.newSeatCount == null + ? 1 + : this.adjustSubscriptionForm.value.newSeatCount, + ), + Validators.required, + ]); + } + this.adjustSubscriptionForm.get("newMaxSeats").updateValueAndValidity(); + }); } - async submit() { - try { - const request = new OrganizationSubscriptionUpdateRequest( - this.additionalSeatCount, - this.newMaxSeats, - ); - this.formPromise = this.organizationApiService.updatePasswordManagerSeats( - this.organizationId, - request, - ); - - await this.formPromise; - - this.platformUtilsService.showToast( - "success", - null, - this.i18nService.t("subscriptionUpdated"), - ); - } catch (e) { - this.logService.error(e); + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + submit = async () => { + this.adjustSubscriptionForm.markAllAsTouched(); + if (this.adjustSubscriptionForm.invalid) { + return; } + const request = new OrganizationSubscriptionUpdateRequest( + this.additionalSeatCount, + this.adjustSubscriptionForm.value.newMaxSeats, + ); + await this.organizationApiService.updatePasswordManagerSeats(this.organizationId, request); + + this.platformUtilsService.showToast("success", null, this.i18nService.t("subscriptionUpdated")); + this.onAdjusted.emit(); - } + }; limitSubscriptionChanged() { - if (!this.limitSubscription) { - this.newMaxSeats = null; + if (!this.adjustSubscriptionForm.value.limitSubscription) { + this.adjustSubscriptionForm.value.newMaxSeats = null; } } get additionalSeatCount(): number { - return this.newSeatCount ? this.newSeatCount - this.currentSeatCount : 0; + return this.adjustSubscriptionForm.value.newSeatCount + ? this.adjustSubscriptionForm.value.newSeatCount - this.currentSeatCount + : 0; } get additionalMaxSeatCount(): number { - return this.newMaxSeats ? this.newMaxSeats - this.currentSeatCount : 0; + return this.adjustSubscriptionForm.value.newMaxSeats + ? this.adjustSubscriptionForm.value.newMaxSeats - this.currentSeatCount + : 0; } get adjustedSeatTotal(): number { From ff3b6f52ee518109e497c1d9f0b06016a66dd08d Mon Sep 17 00:00:00 2001 From: KiruthigaManivannan <162679756+KiruthigaManivannan@users.noreply.github.com> Date: Thu, 9 May 2024 21:11:17 +0530 Subject: [PATCH 10/15] PM-5017 Migrate Organization Plans component (#8448) * PM-5017 Migrated Organization plans component * PM-5017 Addressed all the review comments * PM-5017 Missed a minor change --------- Co-authored-by: vinith-kovan <156108204+vinith-kovan@users.noreply.github.com> --- .../organization-plans.component.html | 731 +++++++++--------- .../organization-plans.component.ts | 120 +-- 2 files changed, 444 insertions(+), 407 deletions(-) diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.html b/apps/web/src/app/billing/organizations/organization-plans.component.html index a77d42a3594..3c8ef2c9337 100644 --- a/apps/web/src/app/billing/organizations/organization-plans.component.html +++ b/apps/web/src/app/billing/organizations/organization-plans.component.html @@ -1,400 +1,428 @@ - {{ "loading" | i18n }} + {{ "loading" | i18n }} -

{{ "uploadLicenseFileOrg" | i18n }}

-
-
- - - {{ - "licenseFileDesc" | i18n: "bitwarden_organization_license.json" - }} -
- + {{ selectedFile?.name ?? ("noFileChosen" | i18n) }} +
+ + {{ "licenseFileDesc" | i18n: "bitwarden_organization_license.json" }} + +
- -

{{ "chooseYourPlan" | i18n }}

-
- -