import Component from '@ember/component'; // @ts-ignore: Ignore import of compiled template import template from '../../../templates/components/avatar/containers/avatar-main'; import { inject as service } from '@ember/service'; import { set } from '@ember/object'; import { task } from 'ember-concurrency'; import { tagName, layout } from '@ember-decorators/component'; import { ETask } from '@datahub/utils/types/concurrency'; import { PersonEntity } from '@datahub/data-models/entity/person/person-entity'; import DataModelsService from '@datahub/data-models/services/data-models'; import { Avatar } from '@datahub/shared/modules/avatars/avatar'; import { oneOrMany } from '@datahub/utils/array/one-or-many'; import { containerDataSource } from '@datahub/utils/api/data-source'; /** * The AvatarMainContainer is responsible for receiving information about a person and translating * it into an Avatar class object that can be passed into various components meant for rendering * avatars consistently. Because the data we receive in our application can vary, the goal of this * container will be to take inconsistent input and return consistent output. * * Possible parameters for this container are: entity, urn, username. * Input can be in the form of a single item or an array of items. The container only intends for * one kind of input given, if multiple inputs are provided then the priority will be: * entity > urn > username * * @example * {{!-- Basic use --}} * * {{#each avatars as |avatar|}} * * {{/each}} * * * {{!-- Would fetch data for the avatar object as we build it --}} * * ... * */ @tagName('') @layout(template) @containerDataSource('getAvatarsTask', ['entity', 'urn', 'username']) export default class AvatarMainContainer extends Component { /** * Injection of data models service to access the generic PersonEntity class */ @service dataModels!: DataModelsService; /** * Optional argument for a person entity, or a list of person entities, to underly the data * inside the avatar object */ entity?: PersonEntity | Array; /** * Optional argument for a person entity's urn, or list of urns, to reference the entity that * should underly the data inside the avatar object */ urn?: string | Array; /** * Optional argument for a person entity's username, or list of usernames, provided when their * urn or entity (the safer options) are not easily accessible */ username?: string | Array; /** * Whether or not we need to await the async operation of building the avatars. Setting this to * true will await the build() method on our avatars, allowing us to access additional underlying * data but will cause a delay in returning the information. This is useful when we need the * entirety of the Avatar information *before* showing anything on the UI. However, in most cases * we will want to show some UI initially and load additional data as needed, so this is default * to false * @default false */ shouldBuildAvatar?: boolean; /** * The avatar objects finally created from the avatar creation task, yielded from this container * to whatever component needs this data for rendering */ avatars?: Array; /** * Given a urn or username argument to the container, returns as a list of urns */ get urns(): Array { const { username, urn, dataModels } = this; const personEntityClass = dataModels.getModel(PersonEntity.displayName); if (!username && !urn) { return []; } return urn ? oneOrMany(urn) : oneOrMany(username).map((value: string): string => personEntityClass.urnFromUsername(value)); } /** * Regardless of the argument given to the container, this will return a list of entities that * should underly the avatar objects we create */ get entities(): Array { const { entity, dataModels } = this; const personEntityClass = dataModels.getModel(PersonEntity.displayName); return entity ? oneOrMany(entity) : this.urns.map((urn): PersonEntity => new personEntityClass(urn)); } /** * Sets the avatar information to be yielded by the container */ @task(function*(this: AvatarMainContainer): IterableIterator>> { const avatars: Array = Boolean(this.shouldBuildAvatar) ? (((yield Promise.all( this.entities.map((entity): Promise => Avatar.build(entity)) )) as unknown) as Array) : this.entities.map((entity): Avatar => new Avatar(entity)); set(this, 'avatars', avatars); }) getAvatarsTask!: ETask; }