From d6bbb656f2bb415ce56caf2ba55bd096b67dedc3 Mon Sep 17 00:00:00 2001 From: Robyn MacCallum Date: Tue, 3 May 2022 14:05:16 -0400 Subject: [PATCH] [SG-32] Add Ownership badge to vault items (#1623) * [feature] Base implementation of EUVR filter changes * [refactor] Relocated vault-filters to app/modules * [refactor] Reuse vault-filters component for organizations * [refactor] Remove unused org filter component * [bug] .gitmodules branch change * [bug] Load organization filters after sync during login * [refactor] Introduce a SharedModule * [refactor] Created a home for loose components * [refactor] Convert VaultComponent and OrgVaultComponent into a pair of modules * [refactor] Implement for organization filter actions * [feature] Improve a11y standards of the vault filters module * [bug] Recreate package-lock.json * Fix build issue * [bug] Remove duplicate this.go() call * Add organization owner badge to vault items * Fix capitalization * Re-organize new components into modules * Use tailwind css class Co-authored-by: addison Co-authored-by: Hinton --- src/app/modules/loose-components.module.ts | 4 +- .../pipes/get-organization-name.pipe.ts | 14 ++++ src/app/modules/pipes/pipes.module.ts | 10 +++ .../individual-vault.component.html | 1 + .../individual-vault.component.ts | 10 +++ .../organization-badge.module.ts | 12 ++++ .../organization-name-badge.component.html | 9 +++ .../organization-name-badge.component.ts | 69 +++++++++++++++++++ .../organizations/vault/ciphers.component.ts | 7 +- src/app/oss.module.ts | 6 ++ src/app/vault/ciphers.component.html | 16 ++--- src/app/vault/ciphers.component.ts | 12 +++- src/locales/en/messages.json | 3 + 13 files changed, 160 insertions(+), 13 deletions(-) create mode 100644 src/app/modules/pipes/get-organization-name.pipe.ts create mode 100644 src/app/modules/pipes/pipes.module.ts create mode 100644 src/app/modules/vault/modules/organization-badge/organization-badge.module.ts create mode 100644 src/app/modules/vault/modules/organization-badge/organization-name-badge.component.html create mode 100644 src/app/modules/vault/modules/organization-badge/organization-name-badge.component.ts diff --git a/src/app/modules/loose-components.module.ts b/src/app/modules/loose-components.module.ts index a7e64c75..db021cc4 100644 --- a/src/app/modules/loose-components.module.ts +++ b/src/app/modules/loose-components.module.ts @@ -156,13 +156,15 @@ import { CollectionsComponent } from "../vault/collections.component"; import { FolderAddEditComponent } from "../vault/folder-add-edit.component"; import { ShareComponent } from "../vault/share.component"; +import { PipesModule } from "./pipes/pipes.module"; import { SharedModule } from "./shared.module"; import { VaultFilterModule } from "./vault-filter/vault-filter.module"; +import { OrganizationBadgeModule } from "./vault/modules/organization-badge/organization-badge.module"; // Please do not add to this list of declarations - we should refactor these into modules when doing so makes sense until there are none left. // If you are building new functionality, please create or extend a feature module instead. @NgModule({ - imports: [SharedModule, VaultFilterModule], + imports: [SharedModule, VaultFilterModule, OrganizationBadgeModule, PipesModule], declarations: [ PremiumBadgeComponent, AcceptEmergencyComponent, diff --git a/src/app/modules/pipes/get-organization-name.pipe.ts b/src/app/modules/pipes/get-organization-name.pipe.ts new file mode 100644 index 00000000..5745933c --- /dev/null +++ b/src/app/modules/pipes/get-organization-name.pipe.ts @@ -0,0 +1,14 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +import { Organization } from "jslib-common/models/domain/organization"; + +@Pipe({ + name: "orgNameFromId", + pure: true, +}) +export class GetOrgNameFromIdPipe implements PipeTransform { + transform(value: string, organizations: Organization[]) { + const orgName = organizations.find((o) => o.id === value)?.name; + return orgName; + } +} diff --git a/src/app/modules/pipes/pipes.module.ts b/src/app/modules/pipes/pipes.module.ts new file mode 100644 index 00000000..19015bae --- /dev/null +++ b/src/app/modules/pipes/pipes.module.ts @@ -0,0 +1,10 @@ +import { NgModule } from "@angular/core"; + +import { GetOrgNameFromIdPipe } from "./get-organization-name.pipe"; + +@NgModule({ + imports: [], + declarations: [GetOrgNameFromIdPipe], + exports: [GetOrgNameFromIdPipe], +}) +export class PipesModule {} diff --git a/src/app/modules/vault/modules/individual-vault/individual-vault.component.html b/src/app/modules/vault/modules/individual-vault/individual-vault.component.html index 16969d24..ab10f22c 100644 --- a/src/app/modules/vault/modules/individual-vault/individual-vault.component.html +++ b/src/app/modules/vault/modules/individual-vault/individual-vault.component.html @@ -54,6 +54,7 @@ (onShareClicked)="shareCipher($event)" (onCollectionsClicked)="editCipherCollections($event)" (onCloneClicked)="cloneCipher($event)" + (onOrganzationBadgeClicked)="applyOrganizationFilter($event)" > diff --git a/src/app/modules/vault/modules/individual-vault/individual-vault.component.ts b/src/app/modules/vault/modules/individual-vault/individual-vault.component.ts index c619ca30..9989366e 100644 --- a/src/app/modules/vault/modules/individual-vault/individual-vault.component.ts +++ b/src/app/modules/vault/modules/individual-vault/individual-vault.component.ts @@ -159,6 +159,16 @@ export class IndividualVaultComponent implements OnInit, OnDestroy { this.go(); } + async applyOrganizationFilter(orgId: string) { + if (orgId == null) { + this.activeFilter.resetOrganization(); + this.activeFilter.myVaultOnly = true; + } else { + this.activeFilter.selectedOrganizationId = orgId; + } + await this.applyVaultFilter(this.activeFilter); + } + filterSearchText(searchText: string) { this.ciphersComponent.searchText = searchText; this.ciphersComponent.search(200); diff --git a/src/app/modules/vault/modules/organization-badge/organization-badge.module.ts b/src/app/modules/vault/modules/organization-badge/organization-badge.module.ts new file mode 100644 index 00000000..297b17d5 --- /dev/null +++ b/src/app/modules/vault/modules/organization-badge/organization-badge.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from "@angular/core"; + +import { SharedModule } from "../../../shared.module"; + +import { OrganizationNameBadgeComponent } from "./organization-name-badge.component"; + +@NgModule({ + imports: [SharedModule], + declarations: [OrganizationNameBadgeComponent], + exports: [OrganizationNameBadgeComponent], +}) +export class OrganizationBadgeModule {} diff --git a/src/app/modules/vault/modules/organization-badge/organization-name-badge.component.html b/src/app/modules/vault/modules/organization-badge/organization-name-badge.component.html new file mode 100644 index 00000000..1a2f9936 --- /dev/null +++ b/src/app/modules/vault/modules/organization-badge/organization-name-badge.component.html @@ -0,0 +1,9 @@ + diff --git a/src/app/modules/vault/modules/organization-badge/organization-name-badge.component.ts b/src/app/modules/vault/modules/organization-badge/organization-name-badge.component.ts new file mode 100644 index 00000000..7fa37a8b --- /dev/null +++ b/src/app/modules/vault/modules/organization-badge/organization-name-badge.component.ts @@ -0,0 +1,69 @@ +import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; + +import { I18nService } from "jslib-common/abstractions/i18n.service"; + +@Component({ + selector: "app-org-badge", + templateUrl: "organization-name-badge.component.html", +}) +export class OrganizationNameBadgeComponent implements OnInit { + @Input() organizationName: string; + @Input() color: string; + + @Output() onOrganizationClicked = new EventEmitter(); + + textColor: string; + + constructor(private i18nService: I18nService) {} + + ngOnInit(): void { + if (this.organizationName == null || this.organizationName === "") { + this.organizationName = this.i18nService.t("me"); + } + const upperData = this.organizationName.toUpperCase(); + if (this.color == null) { + this.color = this.stringToColor(upperData); + } + this.textColor = this.pickTextColorBasedOnBgColor(); + } + + // This value currently isn't stored anywhere, only calculated in the app-avatar component + // Once we are allowing org colors to be changed and saved, change this out + private stringToColor(str: string): string { + let hash = 0; + for (let i = 0; i < str.length; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + } + let color = "#"; + for (let i = 0; i < 3; i++) { + const value = (hash >> (i * 8)) & 0xff; + color += ("00" + value.toString(16)).substr(-2); + } + return color; + } + + // There are a few ways to calculate text color for contrast, this one seems to fit accessibility guidelines best. + // https://stackoverflow.com/a/3943023/6869691 + private pickTextColorBasedOnBgColor() { + const color = this.color.charAt(0) === "#" ? this.color.substring(1, 7) : this.color; + const r = parseInt(color.substring(0, 2), 16); // hexToR + const g = parseInt(color.substring(2, 4), 16); // hexToG + const b = parseInt(color.substring(4, 6), 16); // hexToB + + const uicolors = [r / 255, g / 255, b / 255]; + const c = uicolors.map((c) => { + if (c <= 0.03928) { + return c / 12.92; + } else { + return Math.pow((c + 0.055) / 1.055, 2.4); + } + }); + + const L = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]; + return L > 0.179 ? "black !important" : "white !important"; + } + + emitOnOrganizationClicked() { + this.onOrganizationClicked.emit(); + } +} diff --git a/src/app/organizations/vault/ciphers.component.ts b/src/app/organizations/vault/ciphers.component.ts index 6d1c2484..08e4b158 100644 --- a/src/app/organizations/vault/ciphers.component.ts +++ b/src/app/organizations/vault/ciphers.component.ts @@ -5,6 +5,7 @@ import { CipherService } from "jslib-common/abstractions/cipher.service"; import { EventService } from "jslib-common/abstractions/event.service"; import { I18nService } from "jslib-common/abstractions/i18n.service"; import { LogService } from "jslib-common/abstractions/log.service"; +import { OrganizationService } from "jslib-common/abstractions/organization.service"; import { PasswordRepromptService } from "jslib-common/abstractions/passwordReprompt.service"; import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service"; import { SearchService } from "jslib-common/abstractions/search.service"; @@ -37,7 +38,8 @@ export class CiphersComponent extends BaseCiphersComponent { totpService: TotpService, passwordRepromptService: PasswordRepromptService, logService: LogService, - stateService: StateService + stateService: StateService, + organizationService: OrganizationService ) { super( searchService, @@ -48,7 +50,8 @@ export class CiphersComponent extends BaseCiphersComponent { totpService, stateService, passwordRepromptService, - logService + logService, + organizationService ); } diff --git a/src/app/oss.module.ts b/src/app/oss.module.ts index fcb75ca3..15ef23dc 100644 --- a/src/app/oss.module.ts +++ b/src/app/oss.module.ts @@ -1,9 +1,11 @@ import { NgModule } from "@angular/core"; import { LooseComponentsModule } from "./modules/loose-components.module"; +import { PipesModule } from "./modules/pipes/pipes.module"; import { SharedModule } from "./modules/shared.module"; import { VaultFilterModule } from "./modules/vault-filter/vault-filter.module"; import { IndividualVaultModule } from "./modules/vault/modules/individual-vault/individual-vault.module"; +import { OrganizationBadgeModule } from "./modules/vault/modules/organization-badge/organization-badge.module"; import { OrganizationVaultModule } from "./modules/vault/modules/organization-vault/organization-vault.module"; @NgModule({ @@ -13,12 +15,16 @@ import { OrganizationVaultModule } from "./modules/vault/modules/organization-va IndividualVaultModule, OrganizationVaultModule, VaultFilterModule, + OrganizationBadgeModule, + PipesModule, ], exports: [ LooseComponentsModule, IndividualVaultModule, OrganizationVaultModule, VaultFilterModule, + OrganizationBadgeModule, + PipesModule, ], bootstrap: [], }) diff --git a/src/app/vault/ciphers.component.html b/src/app/vault/ciphers.component.html index f36366b5..d6132961 100644 --- a/src/app/vault/ciphers.component.html +++ b/src/app/vault/ciphers.component.html @@ -24,15 +24,6 @@ title="{{ 'editItem' | i18n }}" >{{ c.name }} - - - {{ "shared" | i18n }} - {{ c.subTitle }} + + +