[AC-2520] Remove Unassigned Items Banner (#10042)

* chore: remove UnassignedItemsBanner feature flag, refs AC-2520

* chore: remove unassignedItemsBanner from web-header component, refs AC-2520

* chore: delete unassigned items banner service/api/spec, refs AC-2520

* chore: remove unassigned items banner messages (web), refs AC-2520

* chore: remove unassigned items banner messages (browser), refs AC-2520

* chore: remove unassigned items banner code from current tab (browser), refs AC-2520

* chore: remove state definition for unassigned items banner, refs AC-2520

* chore: revert state-definition removal, refs AC-2520
This commit is contained in:
Vincent Salucci 2024-07-12 11:06:02 -05:00 committed by GitHub
parent 486176a648
commit bce6e77514
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 0 additions and 287 deletions

View File

@ -3358,20 +3358,6 @@
"passkeyRemoved": { "passkeyRemoved": {
"message": "Passkey removed" "message": "Passkey removed"
}, },
"unassignedItemsBannerNotice": {
"message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console."
},
"unassignedItemsBannerSelfHostNotice": {
"message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console."
},
"unassignedItemsBannerCTAPartOne": {
"message": "Assign these items to a collection from the",
"description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerCTAPartTwo": {
"message": "to make them visible.",
"description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible."
},
"autofillSuggestions": { "autofillSuggestions": {
"message": "Auto-fill suggestions" "message": "Auto-fill suggestions"
}, },

View File

@ -36,44 +36,6 @@
</div> </div>
<ng-container *ngIf="loaded"> <ng-container *ngIf="loaded">
<app-vault-select (onVaultSelectionChanged)="load()"></app-vault-select> <app-vault-select (onVaultSelectionChanged)="load()"></app-vault-select>
<app-callout
*ngIf="
(unassignedItemsBannerEnabled$ | async) &&
(unassignedItemsBannerService.showBanner$ | async) &&
!(unassignedItemsBannerService.loading$ | async)
"
type="info"
>
<p>
{{ unassignedItemsBannerService.bannerText$ | async | i18n }}
{{ "unassignedItemsBannerCTAPartOne" | i18n }}
<a
[href]="unassignedItemsBannerService.adminConsoleUrl$ | async"
bitLink
linkType="contrast"
target="_blank"
rel="noreferrer"
>{{ "adminConsole" | i18n }}</a
>
{{ "unassignedItemsBannerCTAPartTwo" | i18n }}
<a
href="https://bitwarden.com/help/unassigned-vault-items-moved-to-admin-console"
bitLink
linkType="contrast"
target="_blank"
rel="noreferrer"
>{{ "learnMore" | i18n }}</a
>
</p>
<button
type="button"
class="btn primary callout-half"
appStopClick
(click)="unassignedItemsBannerService.hideBanner()"
>
{{ "gotIt" | i18n }}
</button>
</app-callout>
<div class="box list" *ngIf="loginCiphers"> <div class="box list" *ngIf="loginCiphers">
<h2 class="box-header"> <h2 class="box-header">
{{ "typeLogins" | i18n }} {{ "typeLogins" | i18n }}

View File

@ -3,14 +3,11 @@ import { Router } from "@angular/router";
import { Subject, firstValueFrom, from, Subscription } from "rxjs"; import { Subject, firstValueFrom, from, Subscription } from "rxjs";
import { debounceTime, switchMap, takeUntil } from "rxjs/operators"; import { debounceTime, switchMap, takeUntil } from "rxjs/operators";
import { UnassignedItemsBannerService } from "@bitwarden/angular/services/unassigned-items-banner.service";
import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { SearchService } from "@bitwarden/common/abstractions/search.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants"; import { AutofillOverlayVisibility } from "@bitwarden/common/autofill/constants";
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; 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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { Utils } from "@bitwarden/common/platform/misc/utils"; import { Utils } from "@bitwarden/common/platform/misc/utils";
@ -58,10 +55,6 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
private loadedTimeout: number; private loadedTimeout: number;
private searchTimeout: number; private searchTimeout: number;
protected unassignedItemsBannerEnabled$ = this.configService.getFeatureFlag$(
FeatureFlag.UnassignedItemsBanner,
);
constructor( constructor(
private platformUtilsService: PlatformUtilsService, private platformUtilsService: PlatformUtilsService,
private cipherService: CipherService, private cipherService: CipherService,
@ -78,8 +71,6 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
private organizationService: OrganizationService, private organizationService: OrganizationService,
private vaultFilterService: VaultFilterService, private vaultFilterService: VaultFilterService,
private vaultSettingsService: VaultSettingsService, private vaultSettingsService: VaultSettingsService,
private configService: ConfigService,
protected unassignedItemsBannerService: UnassignedItemsBannerService,
) {} ) {}
async ngOnInit() { async ngOnInit() {

View File

@ -1,31 +1,3 @@
<bit-banner
class="-tw-m-6 tw-flex tw-flex-col tw-pb-6"
(onClose)="unassignedItemsBannerService.hideBanner()"
*ngIf="
(unassignedItemsBannerEnabled$ | async) &&
(unassignedItemsBannerService.showBanner$ | async) &&
!(unassignedItemsBannerService.loading$ | async)
"
>
{{ unassignedItemsBannerService.bannerText$ | async | i18n }}
{{ "unassignedItemsBannerCTAPartOne" | i18n }}
<a
[href]="unassignedItemsBannerService.adminConsoleUrl$ | async"
bitLink
linkType="contrast"
rel="noreferrer"
>{{ "adminConsole" | i18n }}</a
>
{{ "unassignedItemsBannerCTAPartTwo" | i18n }}
<a
href="https://bitwarden.com/help/unassigned-vault-items-moved-to-admin-console"
bitLink
linkType="contrast"
target="_blank"
rel="noreferrer"
>{{ "learnMore" | i18n }}</a
>
</bit-banner>
<header <header
*ngIf="routeData$ | async as routeData" *ngIf="routeData$ | async as routeData"
class="-tw-m-6 tw-mb-3 tw-flex tw-flex-col tw-p-6" class="-tw-m-6 tw-mb-3 tw-flex tw-flex-col tw-p-6"

View File

@ -3,12 +3,9 @@ import { ActivatedRoute } from "@angular/router";
import { map, Observable } from "rxjs"; import { map, Observable } from "rxjs";
import { User } from "@bitwarden/angular/pipes/user-name.pipe"; import { User } from "@bitwarden/angular/pipes/user-name.pipe";
import { UnassignedItemsBannerService } from "@bitwarden/angular/services/unassigned-items-banner.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum"; import { VaultTimeoutAction } from "@bitwarden/common/enums/vault-timeout-action.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
@ -33,17 +30,12 @@ export class WebHeaderComponent {
protected canLock$: Observable<boolean>; protected canLock$: Observable<boolean>;
protected selfHosted: boolean; protected selfHosted: boolean;
protected hostname = location.hostname; protected hostname = location.hostname;
protected unassignedItemsBannerEnabled$ = this.configService.getFeatureFlag$(
FeatureFlag.UnassignedItemsBanner,
);
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private platformUtilsService: PlatformUtilsService, private platformUtilsService: PlatformUtilsService,
private vaultTimeoutSettingsService: VaultTimeoutSettingsService, private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
private messagingService: MessagingService, private messagingService: MessagingService,
protected unassignedItemsBannerService: UnassignedItemsBannerService,
private configService: ConfigService,
private accountService: AccountService, private accountService: AccountService,
) { ) {
this.routeData$ = this.route.data.pipe( this.routeData$ = this.route.data.pipe(

View File

@ -8161,23 +8161,6 @@
"restrictedGroupAccessDesc": { "restrictedGroupAccessDesc": {
"message": "You cannot add yourself to a group." "message": "You cannot add yourself to a group."
}, },
"unassignedItemsBannerSelfHost": {
"message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in your All Vaults view across devices and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerNotice": {
"message": "Notice: Unassigned organization items are no longer visible in your All Vaults view across devices and are now only accessible via the Admin Console."
},
"unassignedItemsBannerSelfHostNotice": {
"message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in your All Vaults view across devices and will only be accessible via the Admin Console."
},
"unassignedItemsBannerCTAPartOne": {
"message": "Assign these items to a collection from the",
"description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible."
},
"unassignedItemsBannerCTAPartTwo": {
"message": "to make them visible.",
"description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible."
},
"deleteProvider": { "deleteProvider": {
"message": "Delete provider" "message": "Delete provider"
}, },

View File

@ -1,19 +0,0 @@
import { Injectable } from "@angular/core";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
@Injectable({ providedIn: "root" })
export class UnassignedItemsBannerApiService {
constructor(private apiService: ApiService) {}
async getShowUnassignedCiphersBanner(): Promise<boolean> {
const r = await this.apiService.send(
"GET",
"/ciphers/has-unassigned-ciphers",
null,
true,
true,
);
return r;
}
}

View File

@ -1,65 +0,0 @@
import { MockProxy, mock } from "jest-mock-extended";
import { firstValueFrom, of } from "rxjs";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { FakeStateProvider, mockAccountServiceWith } from "@bitwarden/common/spec";
import { UserId } from "@bitwarden/common/types/guid";
import { UnassignedItemsBannerApiService } from "./unassigned-items-banner.api.service";
import { SHOW_BANNER_KEY, UnassignedItemsBannerService } from "./unassigned-items-banner.service";
describe("UnassignedItemsBanner", () => {
let stateProvider: FakeStateProvider;
let apiService: MockProxy<UnassignedItemsBannerApiService>;
let environmentService: MockProxy<EnvironmentService>;
let organizationService: MockProxy<OrganizationService>;
const sutFactory = () =>
new UnassignedItemsBannerService(
stateProvider,
apiService,
environmentService,
organizationService,
);
beforeEach(() => {
const fakeAccountService = mockAccountServiceWith("userId" as UserId);
stateProvider = new FakeStateProvider(fakeAccountService);
apiService = mock();
environmentService = mock();
environmentService.environment$ = of(null);
organizationService = mock();
organizationService.organizations$ = of([]);
});
it("shows the banner if showBanner local state is true", async () => {
const showBanner = stateProvider.activeUser.getFake(SHOW_BANNER_KEY);
showBanner.nextState(true);
const sut = sutFactory();
expect(await firstValueFrom(sut.showBanner$)).toBe(true);
expect(apiService.getShowUnassignedCiphersBanner).not.toHaveBeenCalled();
});
it("does not show the banner if showBanner local state is false", async () => {
const showBanner = stateProvider.activeUser.getFake(SHOW_BANNER_KEY);
showBanner.nextState(false);
const sut = sutFactory();
expect(await firstValueFrom(sut.showBanner$)).toBe(false);
expect(apiService.getShowUnassignedCiphersBanner).not.toHaveBeenCalled();
});
it("fetches from server if local state has not been set yet", async () => {
apiService.getShowUnassignedCiphersBanner.mockResolvedValue(true);
const showBanner = stateProvider.activeUser.getFake(SHOW_BANNER_KEY);
showBanner.nextState(undefined);
const sut = sutFactory();
expect(await firstValueFrom(sut.showBanner$)).toBe(true);
expect(apiService.getShowUnassignedCiphersBanner).toHaveBeenCalledTimes(1);
});
});

View File

@ -1,87 +0,0 @@
import { Injectable } from "@angular/core";
import { combineLatest, concatMap, map, startWith } from "rxjs";
import {
OrganizationService,
canAccessOrgAdmin,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import {
EnvironmentService,
Region,
} from "@bitwarden/common/platform/abstractions/environment.service";
import {
StateProvider,
UNASSIGNED_ITEMS_BANNER_DISK,
UserKeyDefinition,
} from "@bitwarden/common/platform/state";
import { UnassignedItemsBannerApiService } from "./unassigned-items-banner.api.service";
export const SHOW_BANNER_KEY = new UserKeyDefinition<boolean>(
UNASSIGNED_ITEMS_BANNER_DISK,
"showBanner",
{
deserializer: (b) => b,
clearOn: [],
},
);
/** Displays a banner that tells users how to move their unassigned items into a collection. */
@Injectable({ providedIn: "root" })
export class UnassignedItemsBannerService {
private _showBanner = this.stateProvider.getActive(SHOW_BANNER_KEY);
showBanner$ = this._showBanner.state$.pipe(
concatMap(async (showBannerState) => {
// null indicates that the user has not seen or dismissed the banner yet - get the flag from server
if (showBannerState == null) {
const showBannerResponse = await this.apiService.getShowUnassignedCiphersBanner();
await this._showBanner.update(() => showBannerResponse);
return showBannerResponse;
}
return showBannerState;
}),
);
private adminConsoleOrg$ = this.organizationService.organizations$.pipe(
map((orgs) => orgs.find((o) => canAccessOrgAdmin(o))),
);
adminConsoleUrl$ = combineLatest([
this.adminConsoleOrg$,
this.environmentService.environment$,
]).pipe(
map(([org, environment]) => {
if (org == null || environment == null) {
return "#";
}
return environment.getWebVaultUrl() + "/#/organizations/" + org.id;
}),
);
bannerText$ = this.environmentService.environment$.pipe(
map((e) =>
e?.getRegion() == Region.SelfHosted
? "unassignedItemsBannerSelfHostNotice"
: "unassignedItemsBannerNotice",
),
);
loading$ = combineLatest([this.adminConsoleUrl$, this.bannerText$]).pipe(
startWith(true),
map(() => false),
);
constructor(
private stateProvider: StateProvider,
private apiService: UnassignedItemsBannerApiService,
private environmentService: EnvironmentService,
private organizationService: OrganizationService,
) {}
async hideBanner() {
await this._showBanner.update(() => false);
}
}

View File

@ -11,7 +11,6 @@ export enum FeatureFlag {
ShowPaymentMethodWarningBanners = "show-payment-method-warning-banners", ShowPaymentMethodWarningBanners = "show-payment-method-warning-banners",
EnableConsolidatedBilling = "enable-consolidated-billing", EnableConsolidatedBilling = "enable-consolidated-billing",
AC1795_UpdatedSubscriptionStatusSection = "AC-1795_updated-subscription-status-section", AC1795_UpdatedSubscriptionStatusSection = "AC-1795_updated-subscription-status-section",
UnassignedItemsBanner = "unassigned-items-banner",
EnableDeleteProvider = "AC-1218-delete-provider", EnableDeleteProvider = "AC-1218-delete-provider",
ExtensionRefresh = "extension-refresh", ExtensionRefresh = "extension-refresh",
RestrictProviderAccess = "restrict-provider-access", RestrictProviderAccess = "restrict-provider-access",
@ -45,7 +44,6 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.ShowPaymentMethodWarningBanners]: FALSE, [FeatureFlag.ShowPaymentMethodWarningBanners]: FALSE,
[FeatureFlag.EnableConsolidatedBilling]: FALSE, [FeatureFlag.EnableConsolidatedBilling]: FALSE,
[FeatureFlag.AC1795_UpdatedSubscriptionStatusSection]: FALSE, [FeatureFlag.AC1795_UpdatedSubscriptionStatusSection]: FALSE,
[FeatureFlag.UnassignedItemsBanner]: FALSE,
[FeatureFlag.EnableDeleteProvider]: FALSE, [FeatureFlag.EnableDeleteProvider]: FALSE,
[FeatureFlag.ExtensionRefresh]: FALSE, [FeatureFlag.ExtensionRefresh]: FALSE,
[FeatureFlag.RestrictProviderAccess]: FALSE, [FeatureFlag.RestrictProviderAccess]: FALSE,