mirror of
https://github.com/bitwarden/browser
synced 2025-12-31 23:53:37 +00:00
[PS-1092] Organization Service Observables (#3462)
* Update imports * Implement observables in a few places * Add tests * Get all clients working * Use _destroy * Address PR feedback * Address PR feedback * Address feedback
This commit is contained in:
@@ -16,7 +16,7 @@ import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarde
|
||||
import { LogService as LogServiceAbstraction } from "@bitwarden/common/abstractions/log.service";
|
||||
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
|
||||
import { OrganizationService as OrganizationServiceAbstraction } from "@bitwarden/common/abstractions/organization.service";
|
||||
import { OrganizationService as OrganizationServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { PasswordGenerationService as PasswordGenerationServiceAbstraction } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyApiServiceAbstraction } from "@bitwarden/common/abstractions/policy/policy-api.service.abstraction";
|
||||
@@ -27,6 +27,7 @@ import { SendService as SendServiceAbstraction } from "@bitwarden/common/abstrac
|
||||
import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
|
||||
import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/abstractions/sync/sync.service.abstraction";
|
||||
import { SyncNotifierService as SyncNotifierServiceAbstraction } from "@bitwarden/common/abstractions/sync/syncNotifier.service.abstraction";
|
||||
import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/abstractions/system.service";
|
||||
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/abstractions/token.service";
|
||||
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/abstractions/totp.service";
|
||||
@@ -58,7 +59,7 @@ import { FolderApiService } from "@bitwarden/common/services/folder/folder-api.s
|
||||
import { KeyConnectorService } from "@bitwarden/common/services/keyConnector.service";
|
||||
import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service";
|
||||
import { NotificationsService } from "@bitwarden/common/services/notifications.service";
|
||||
import { OrganizationService } from "@bitwarden/common/services/organization.service";
|
||||
import { OrganizationService } from "@bitwarden/common/services/organization/organization.service";
|
||||
import { PasswordGenerationService } from "@bitwarden/common/services/passwordGeneration.service";
|
||||
import { PolicyApiService } from "@bitwarden/common/services/policy/policy-api.service";
|
||||
import { PolicyService } from "@bitwarden/common/services/policy/policy.service";
|
||||
@@ -68,6 +69,7 @@ import { SendService } from "@bitwarden/common/services/send.service";
|
||||
import { SettingsService } from "@bitwarden/common/services/settings.service";
|
||||
import { StateMigrationService } from "@bitwarden/common/services/stateMigration.service";
|
||||
import { SyncService } from "@bitwarden/common/services/sync/sync.service";
|
||||
import { SyncNotifierService } from "@bitwarden/common/services/sync/syncNotifier.service";
|
||||
import { SystemService } from "@bitwarden/common/services/system.service";
|
||||
import { TokenService } from "@bitwarden/common/services/token.service";
|
||||
import { TotpService } from "@bitwarden/common/services/totp.service";
|
||||
@@ -158,6 +160,7 @@ export default class MainBackground {
|
||||
folderApiService: FolderApiServiceAbstraction;
|
||||
policyApiService: PolicyApiServiceAbstraction;
|
||||
userVerificationApiService: UserVerificationApiServiceAbstraction;
|
||||
syncNotifierService: SyncNotifierServiceAbstraction;
|
||||
|
||||
// Passed to the popup for Safari to workaround issues with theming, downloading, etc.
|
||||
backgroundWindow = window;
|
||||
@@ -298,7 +301,8 @@ export default class MainBackground {
|
||||
this.cryptoFunctionService,
|
||||
this.stateService
|
||||
);
|
||||
this.organizationService = new OrganizationService(this.stateService);
|
||||
this.syncNotifierService = new SyncNotifierService();
|
||||
this.organizationService = new OrganizationService(this.stateService, this.syncNotifierService);
|
||||
this.policyService = new PolicyService(this.stateService, this.organizationService);
|
||||
this.policyApiService = new PolicyApiService(
|
||||
this.policyService,
|
||||
@@ -388,9 +392,9 @@ export default class MainBackground {
|
||||
this.logService,
|
||||
this.keyConnectorService,
|
||||
this.stateService,
|
||||
this.organizationService,
|
||||
this.providerService,
|
||||
this.folderApiService,
|
||||
this.syncNotifierService,
|
||||
logoutCallback
|
||||
);
|
||||
this.eventService = new EventService(
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { OrganizationService as AbstractOrganizationService } from "@bitwarden/common/abstractions/organization.service";
|
||||
import { OrganizationService } from "@bitwarden/common/services/organization.service";
|
||||
import { OrganizationService as AbstractOrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { OrganizationService } from "@bitwarden/common/services/organization/organization.service";
|
||||
|
||||
import { FactoryOptions, CachedServices, factory } from "./factory-options";
|
||||
import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory";
|
||||
import {
|
||||
syncNotifierServiceFactory,
|
||||
SyncNotifierServiceInitOptions,
|
||||
} from "./sync-notifier-service.factory";
|
||||
|
||||
type OrganizationServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type OrganizationServiceInitOptions = OrganizationServiceFactoryOptions &
|
||||
SyncNotifierServiceInitOptions &
|
||||
StateServiceInitOptions;
|
||||
|
||||
export function organizationServiceFactory(
|
||||
@@ -17,6 +22,10 @@ export function organizationServiceFactory(
|
||||
cache,
|
||||
"organizationService",
|
||||
opts,
|
||||
async () => new OrganizationService(await stateServiceFactory(cache, opts))
|
||||
async () =>
|
||||
new OrganizationService(
|
||||
await stateServiceFactory(cache, opts),
|
||||
await syncNotifierServiceFactory(cache, opts)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { SyncNotifierService as AbstractSyncNotifierService } from "@bitwarden/common/abstractions/sync/syncNotifier.service.abstraction";
|
||||
import { SyncNotifierService } from "@bitwarden/common/services/sync/syncNotifier.service";
|
||||
|
||||
import { FactoryOptions, CachedServices, factory } from "./factory-options";
|
||||
|
||||
type SyncNotifierServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type SyncNotifierServiceInitOptions = SyncNotifierServiceFactoryOptions;
|
||||
|
||||
export function syncNotifierServiceFactory(
|
||||
cache: { syncNotifierService?: AbstractSyncNotifierService } & CachedServices,
|
||||
opts: SyncNotifierServiceInitOptions
|
||||
): Promise<AbstractSyncNotifierService> {
|
||||
return factory(cache, "syncNotifierService", opts, () =>
|
||||
Promise.resolve(new SyncNotifierService())
|
||||
);
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector
|
||||
import { LogService as LogServiceAbstraction } from "@bitwarden/common/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from "@bitwarden/common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
|
||||
@@ -12,7 +12,7 @@ import { FolderService } from "@bitwarden/common/abstractions/folder/folder.serv
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { PasswordRepromptService } from "@bitwarden/common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
||||
|
||||
@@ -10,7 +10,7 @@ import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
|
||||
import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
import { CipherType } from "@bitwarden/common/enums/cipherType";
|
||||
@@ -78,7 +78,7 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On
|
||||
|
||||
async ngOnInit() {
|
||||
this.searchTypeSearch = !this.platformUtilsService.isSafari();
|
||||
this.showOrganizations = await this.organizationService.hasOrganizations();
|
||||
this.showOrganizations = this.organizationService.hasOrganizations();
|
||||
this.vaultFilter = this.vaultFilterService.getVaultFilter();
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
this.route.queryParams.pipe(first()).subscribe(async (params) => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Router } from "@angular/router";
|
||||
import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service";
|
||||
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { PasswordRepromptService } from "@bitwarden/common/abstractions/passwordReprompt.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { SearchService } from "@bitwarden/common/abstractions/search.service";
|
||||
@@ -219,7 +219,7 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
||||
const otherTypes: CipherType[] = [];
|
||||
const dontShowCards = await this.stateService.getDontShowCardsCurrentTab();
|
||||
const dontShowIdentities = await this.stateService.getDontShowIdentitiesCurrentTab();
|
||||
this.showOrganizations = await this.organizationService.hasOrganizations();
|
||||
this.showOrganizations = this.organizationService.hasOrganizations();
|
||||
if (!dontShowCards) {
|
||||
otherTypes.push(CipherType.Card);
|
||||
}
|
||||
|
||||
@@ -1,70 +1,76 @@
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<header>
|
||||
<div class="left">
|
||||
<button type="button" (click)="cancel()">{{ "cancel" | i18n }}</button>
|
||||
</div>
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "moveToOrganization" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button
|
||||
type="submit"
|
||||
[disabled]="form.loading || !canSave"
|
||||
*ngIf="organizations && organizations.length"
|
||||
>
|
||||
<span [hidden]="form.loading">{{ "move" | i18n }}</span>
|
||||
<i class="bwi bwi-spinner bwi-lg bwi-spin" [hidden]="!form.loading" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<main tabindex="-1">
|
||||
<div class="box">
|
||||
<div class="box-content" *ngIf="!organizations || !organizations.length">
|
||||
<div class="box-content-row padded no-hover">
|
||||
{{ "noOrganizationsList" | i18n }}
|
||||
</div>
|
||||
<ng-container *ngIf="organizations$ | async as organizations">
|
||||
<header>
|
||||
<div class="left">
|
||||
<button type="button" (click)="cancel()">{{ "cancel" | i18n }}</button>
|
||||
</div>
|
||||
<div class="box-content" *ngIf="organizations && organizations.length">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="organization">{{ "organization" | i18n }}</label>
|
||||
<select
|
||||
id="organization"
|
||||
name="OrganizationId"
|
||||
[(ngModel)]="organizationId"
|
||||
(change)="filterCollections()"
|
||||
>
|
||||
<option *ngFor="let o of organizations" [ngValue]="o.id">{{ o.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "moveToOrgDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" *ngIf="organizations && organizations.length">
|
||||
<h2 class="box-header">
|
||||
{{ "collections" | i18n }}
|
||||
</h2>
|
||||
<div class="box-content" *ngIf="!collections || !collections.length">
|
||||
<div class="box-content-row padded no-hover">
|
||||
{{ "noCollectionsInList" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content" *ngIf="collections && collections.length">
|
||||
<div
|
||||
class="box-content-row box-content-row-checkbox"
|
||||
*ngFor="let c of collections; let i = index"
|
||||
appBoxRow
|
||||
<h1 class="center">
|
||||
<span class="title">{{ "moveToOrganization" | i18n }}</span>
|
||||
</h1>
|
||||
<div class="right">
|
||||
<button
|
||||
type="submit"
|
||||
[disabled]="form.loading || !canSave"
|
||||
*ngIf="organizations && organizations.length"
|
||||
>
|
||||
<label for="collection_{{ i }}">{{ c.name }}</label>
|
||||
<input
|
||||
id="collection_{{ i }}"
|
||||
type="checkbox"
|
||||
[(ngModel)]="c.checked"
|
||||
name="Collection[{{ i }}].Checked"
|
||||
/>
|
||||
<span [hidden]="form.loading">{{ "move" | i18n }}</span>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-lg bwi-spin"
|
||||
[hidden]="!form.loading"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<main tabindex="-1">
|
||||
<div class="box">
|
||||
<div class="box-content" *ngIf="!organizations || !organizations.length">
|
||||
<div class="box-content-row padded no-hover">
|
||||
{{ "noOrganizationsList" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content" *ngIf="organizations && organizations.length">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="organization">{{ "organization" | i18n }}</label>
|
||||
<select
|
||||
id="organization"
|
||||
name="OrganizationId"
|
||||
[(ngModel)]="organizationId"
|
||||
(change)="filterCollections()"
|
||||
>
|
||||
<option *ngFor="let o of organizations" [ngValue]="o.id">{{ o.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
{{ "moveToOrgDesc" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<div class="box" *ngIf="organizations && organizations.length">
|
||||
<h2 class="box-header">
|
||||
{{ "collections" | i18n }}
|
||||
</h2>
|
||||
<div class="box-content" *ngIf="!collections || !collections.length">
|
||||
<div class="box-content-row padded no-hover">
|
||||
{{ "noCollectionsInList" | i18n }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content" *ngIf="collections && collections.length">
|
||||
<div
|
||||
class="box-content-row box-content-row-checkbox"
|
||||
*ngFor="let c of collections; let i = index"
|
||||
appBoxRow
|
||||
>
|
||||
<label for="collection_{{ i }}">{{ c.name }}</label>
|
||||
<input
|
||||
id="collection_{{ i }}"
|
||||
type="checkbox"
|
||||
[(ngModel)]="c.checked"
|
||||
name="Collection[{{ i }}].Checked"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</ng-container>
|
||||
</form>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,71 +1,70 @@
|
||||
<div class="content org-filter-content" *ngIf="loaded && show">
|
||||
<button
|
||||
#toggleVaults
|
||||
class="org-filter"
|
||||
(click)="openOverlay()"
|
||||
aria-haspopup="menu"
|
||||
aria-controls="cdk-overlay-container"
|
||||
[attr.aria-expanded]="isOpen"
|
||||
[attr.aria-label]="vaultFilterDisplay"
|
||||
>
|
||||
{{ vaultFilterDisplay | ellipsis: 45 }}
|
||||
<i
|
||||
class="bwi bwi-sm"
|
||||
aria-hidden="true"
|
||||
[ngClass]="{ 'bwi-angle-down': !isOpen, 'bwi-chevron-up': isOpen }"
|
||||
></i>
|
||||
</button>
|
||||
<ng-template class="vault-select-container" #vaultSelectorTemplate>
|
||||
<div
|
||||
class="vault-select"
|
||||
[@transformPanel]="'open'"
|
||||
cdkTrapFocus
|
||||
cdkTrapFocusAutoCapture
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<ng-container *ngIf="loaded && organizations$ | async as organizations">
|
||||
<div class="content org-filter-content" *ngIf="loaded && shouldShow(organizations)">
|
||||
<ng-container *ngIf="selectedVault$ | async as vaultFilterDisplay">
|
||||
<button
|
||||
appStopClick
|
||||
(click)="selectAllVaults()"
|
||||
[ngClass]="{ active: !myVaultOnly && !selectOrganizationId }"
|
||||
appA11yTitle="{{ 'vault' | i18n }}: {{ 'allVaults' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-filter" aria-hidden="true"></i>
|
||||
{{ "allVaults" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
*ngIf="!enforcePersonalOwnwership"
|
||||
appStopClick
|
||||
(click)="selectMyVault()"
|
||||
appA11yTitle="{{ 'vault' | i18n }}: {{ 'myVault' | i18n }}"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-user" aria-hidden="true"></i>
|
||||
{{ "myVault" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
*ngFor="let organization of organizations"
|
||||
appStopClick
|
||||
(click)="selectOrganization(organization)"
|
||||
appA11yTitle="{{ 'vault' | i18n }}: {{ organization.name }}"
|
||||
#toggleVaults
|
||||
class="org-filter"
|
||||
(click)="openOverlay()"
|
||||
aria-haspopup="menu"
|
||||
aria-controls="cdk-overlay-container"
|
||||
[attr.aria-expanded]="isOpen"
|
||||
[attr.aria-label]="vaultFilterDisplay"
|
||||
>
|
||||
{{ vaultFilterDisplay | ellipsis: 45 }}
|
||||
<i
|
||||
*ngIf="organization.planProductType !== 1"
|
||||
class="bwi bwi-fw bwi-business"
|
||||
class="bwi bwi-sm"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="organization.planProductType === 1"
|
||||
class="bwi bwi-fw bwi-family"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span> {{ organization.name | ellipsis: (organization.enabled ? 21 : 18):true }}</span>
|
||||
<i
|
||||
*ngIf="!organization.enabled"
|
||||
class="bwi bwi-fw bwi-exclamation-triangle text-danger"
|
||||
aria-label="{{ 'organizationIsDisabled' | i18n }}"
|
||||
appA11yTitle="{{ 'organizationIsDisabled' | i18n }}"
|
||||
[ngClass]="{ 'bwi-angle-down': !isOpen, 'bwi-chevron-up': isOpen }"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template class="vault-select-container" #vaultSelectorTemplate>
|
||||
<div
|
||||
class="vault-select"
|
||||
[@transformPanel]="'open'"
|
||||
cdkTrapFocus
|
||||
cdkTrapFocusAutoCapture
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<button
|
||||
appStopClick
|
||||
(click)="selectAllVaults()"
|
||||
[ngClass]="{ active: !myVaultOnly && !selectOrganizationId }"
|
||||
>
|
||||
<i class="bwi bwi-fw bwi-filter" aria-hidden="true"></i>
|
||||
{{ "allVaults" | i18n }}
|
||||
</button>
|
||||
<button *ngIf="!enforcePersonalOwnwership" appStopClick (click)="selectMyVault()">
|
||||
<i class="bwi bwi-fw bwi-user" aria-hidden="true"></i>
|
||||
{{ "myVault" | i18n }}
|
||||
</button>
|
||||
<button
|
||||
*ngFor="let organization of organizations"
|
||||
appStopClick
|
||||
(click)="selectOrganization(organization)"
|
||||
>
|
||||
<i
|
||||
*ngIf="organization.planProductType !== 1"
|
||||
class="bwi bwi-fw bwi-business"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<i
|
||||
*ngIf="organization.planProductType === 1"
|
||||
class="bwi bwi-fw bwi-family"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span
|
||||
> {{ organization.name | ellipsis: (organization.enabled ? 21 : 18):true }}</span
|
||||
>
|
||||
<i
|
||||
*ngIf="!organization.enabled"
|
||||
class="bwi bwi-fw bwi-exclamation-triangle text-danger"
|
||||
aria-label="{{ 'organizationIsDisabled' | i18n }}"
|
||||
appA11yTitle="{{ 'organizationIsDisabled' | i18n }}"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
@@ -5,19 +5,19 @@ import {
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
NgZone,
|
||||
OnInit,
|
||||
Output,
|
||||
TemplateRef,
|
||||
ViewChild,
|
||||
ViewContainerRef,
|
||||
HostListener,
|
||||
OnDestroy,
|
||||
} from "@angular/core";
|
||||
import { merge } from "rxjs";
|
||||
import { BehaviorSubject, concatMap, map, merge, Observable, Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model";
|
||||
import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
|
||||
@@ -47,20 +47,22 @@ import { VaultFilterService } from "../../services/vaultFilter.service";
|
||||
]),
|
||||
],
|
||||
})
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
export class VaultSelectComponent implements OnInit {
|
||||
export class VaultSelectComponent implements OnInit, OnDestroy {
|
||||
@Output() onVaultSelectionChanged = new EventEmitter();
|
||||
|
||||
@ViewChild("toggleVaults", { read: ElementRef })
|
||||
buttonRef: ElementRef<HTMLButtonElement>;
|
||||
@ViewChild("vaultSelectorTemplate", { read: TemplateRef }) templateRef: TemplateRef<HTMLElement>;
|
||||
|
||||
private _selectedVault = new BehaviorSubject<string>(null);
|
||||
|
||||
isOpen = false;
|
||||
loaded = false;
|
||||
organizations: Organization[];
|
||||
organizations$: Observable<Organization[]>;
|
||||
selectedVault$: Observable<string> = this._selectedVault.asObservable();
|
||||
|
||||
vaultFilter: VaultFilter = new VaultFilter();
|
||||
vaultFilterDisplay = "";
|
||||
enforcePersonalOwnwership = false;
|
||||
enforcePersonalOwnership = false;
|
||||
overlayPostition: ConnectedPosition[] = [
|
||||
{
|
||||
originX: "start",
|
||||
@@ -71,22 +73,22 @@ export class VaultSelectComponent implements OnInit {
|
||||
];
|
||||
|
||||
private overlayRef: OverlayRef;
|
||||
private _destroy = new Subject<void>();
|
||||
|
||||
get show() {
|
||||
shouldShow(organizations: Organization[]): boolean {
|
||||
return (
|
||||
(this.organizations.length > 0 && !this.enforcePersonalOwnwership) ||
|
||||
(this.organizations.length > 1 && this.enforcePersonalOwnwership)
|
||||
(organizations.length > 0 && !this.enforcePersonalOwnership) ||
|
||||
(organizations.length > 1 && this.enforcePersonalOwnership)
|
||||
);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private vaultFilterService: VaultFilterService,
|
||||
private i18nService: I18nService,
|
||||
private ngZone: NgZone,
|
||||
private broadcasterService: BroadcasterService,
|
||||
private overlay: Overlay,
|
||||
private viewContainerRef: ViewContainerRef,
|
||||
private platformUtilsService: PlatformUtilsService
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private organizationService: OrganizationService
|
||||
) {}
|
||||
|
||||
@HostListener("document:keydown.escape", ["$event"])
|
||||
@@ -98,46 +100,45 @@ export class VaultSelectComponent implements OnInit {
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await this.load();
|
||||
this.broadcasterService.subscribe(this.constructor.name, (message: any) => {
|
||||
this.ngZone.run(async () => {
|
||||
switch (message.command) {
|
||||
case "syncCompleted":
|
||||
await this.load();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
this.organizations$ = this.organizationService.organizations$
|
||||
.pipe(takeUntil(this._destroy))
|
||||
.pipe(map((orgs) => orgs.sort((a, b) => a.name.localeCompare(b.name))));
|
||||
|
||||
this.organizations$
|
||||
.pipe(
|
||||
concatMap(async (organizations) => {
|
||||
this.enforcePersonalOwnership =
|
||||
await this.vaultFilterService.checkForPersonalOwnershipPolicy();
|
||||
|
||||
if (this.shouldShow(organizations)) {
|
||||
if (this.enforcePersonalOwnership && !this.vaultFilter.myVaultOnly) {
|
||||
const firstOrganization = organizations[0];
|
||||
this._selectedVault.next(firstOrganization.name);
|
||||
this.vaultFilterService.setVaultFilter(firstOrganization.id);
|
||||
this.vaultFilter.selectedOrganizationId = firstOrganization.id;
|
||||
} else if (this.vaultFilter.myVaultOnly) {
|
||||
this._selectedVault.next(this.i18nService.t(this.vaultFilterService.myVault));
|
||||
} else if (this.vaultFilter.selectedOrganizationId != null) {
|
||||
const selectedOrganization = organizations.find(
|
||||
(o) => o.id === this.vaultFilter.selectedOrganizationId
|
||||
);
|
||||
this._selectedVault.next(selectedOrganization.name);
|
||||
} else {
|
||||
this._selectedVault.next(this.i18nService.t(this.vaultFilterService.allVaults));
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
.pipe(takeUntil(this._destroy))
|
||||
.subscribe();
|
||||
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.vaultFilter = this.vaultFilterService.getVaultFilter();
|
||||
this.organizations = (await this.vaultFilterService.buildOrganizations()).sort((a, b) =>
|
||||
a.name.localeCompare(b.name)
|
||||
);
|
||||
this.enforcePersonalOwnwership =
|
||||
await this.vaultFilterService.checkForPersonalOwnershipPolicy();
|
||||
|
||||
if (this.show) {
|
||||
if (this.enforcePersonalOwnwership && !this.vaultFilter.myVaultOnly) {
|
||||
this.vaultFilterService.setVaultFilter(this.organizations[0].id);
|
||||
this.vaultFilter.selectedOrganizationId = this.organizations[0].id;
|
||||
this.vaultFilterDisplay = this.organizations.find(
|
||||
(o) => o.id === this.vaultFilter.selectedOrganizationId
|
||||
).name;
|
||||
} else if (this.vaultFilter.myVaultOnly) {
|
||||
this.vaultFilterDisplay = this.i18nService.t(this.vaultFilterService.myVault);
|
||||
} else if (this.vaultFilter.selectedOrganizationId != null) {
|
||||
this.vaultFilterDisplay = this.organizations.find(
|
||||
(o) => o.id === this.vaultFilter.selectedOrganizationId
|
||||
).name;
|
||||
} else {
|
||||
this.vaultFilterDisplay = this.i18nService.t(this.vaultFilterService.allVaults);
|
||||
}
|
||||
}
|
||||
this.loaded = true;
|
||||
ngOnDestroy(): void {
|
||||
this._destroy.next();
|
||||
this._destroy.complete();
|
||||
this._selectedVault.complete();
|
||||
}
|
||||
|
||||
openOverlay() {
|
||||
@@ -191,20 +192,20 @@ export class VaultSelectComponent implements OnInit {
|
||||
this.i18nService.t("disabledOrganizationFilterError")
|
||||
);
|
||||
} else {
|
||||
this.vaultFilterDisplay = organization.name;
|
||||
this._selectedVault.next(organization.name);
|
||||
this.vaultFilterService.setVaultFilter(organization.id);
|
||||
this.onVaultSelectionChanged.emit();
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
selectAllVaults() {
|
||||
this.vaultFilterDisplay = this.i18nService.t(this.vaultFilterService.allVaults);
|
||||
this._selectedVault.next(this.i18nService.t(this.vaultFilterService.allVaults));
|
||||
this.vaultFilterService.setVaultFilter(this.vaultFilterService.allVaults);
|
||||
this.onVaultSelectionChanged.emit();
|
||||
this.close();
|
||||
}
|
||||
selectMyVault() {
|
||||
this.vaultFilterDisplay = this.i18nService.t(this.vaultFilterService.myVault);
|
||||
this._selectedVault.next(this.i18nService.t(this.vaultFilterService.myVault));
|
||||
this.vaultFilterService.setVaultFilter(this.vaultFilterService.myVault);
|
||||
this.onVaultSelectionChanged.emit();
|
||||
this.close();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { VaultFilterService as BaseVaultFilterService } from "@bitwarden/angular
|
||||
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
|
||||
import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
|
||||
import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipherView";
|
||||
|
||||
Reference in New Issue
Block a user