chromium/ash/webui/personalization_app/resources/js/ambient/album_list_element.ts

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview The element for displaying a list of albums.
 */

import 'chrome://resources/ash/common/personalization/common.css.js';
import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';

import {assert} from 'chrome://resources/js/assert.js';
import {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';

import {AmbientModeAlbum, TopicSource} from '../../personalization_app.mojom-webui.js';
import {WithPersonalizationStore} from '../personalization_store.js';
import {getCountText, isRecentHighlightsAlbum} from '../utils.js';

import {getTemplate} from './album_list_element.html.js';

export interface AlbumListElement {
  $: {grid: IronListElement};
}

export type AlbumSelectedChangedEvent = CustomEvent<{album: AmbientModeAlbum}>;

declare global {
  interface HTMLElementEventMap {
    'album_selected_changed': AlbumSelectedChangedEvent;
  }
}

export class AlbumListElement extends WithPersonalizationStore {
  static get is() {
    return 'album-list';
  }

  static get template() {
    return getTemplate();
  }

  static get properties() {
    return {
      topicSource: TopicSource,
      /**
       * List of albums received from the client.
       */
      albums: {
        type: Array,
        value: null,
        observer: 'onAlbumsChanged_',
      },
      /**
       * List of albums used for iron-list rendering.
       */
      albumsForDisplay_: {
        type: Array,
        value: [],
      },
    };
  }

  topicSource: TopicSource;
  albums: AmbientModeAlbum[]|null;
  private albumsForDisplay_: AmbientModeAlbum[];

  private onAlbumsChanged_(albums: AlbumListElement['albums']) {
    if (!albums) {
      return;
    }

    // `albumsForDisplay_` is updated in place to avoid complete re-rendering of
    // iron-list, which would cause the tabindex to reset. See b/291123326.
    this.updateList(
        /*propertyPath=*/ 'albumsForDisplay_',
        /*identityGetter=*/
        (album: AmbientModeAlbum) => album.id,
        /*newList=*/ albums,
        /*identityBasedUpdate=*/ true,
    );
  }

  /** Invoked on selection of an album. */
  private onAlbumSelected_(e: Event&{model: {album: AmbientModeAlbum}}) {
    // Retrieve the actual instance of selected album from `albums`.
    const albumIndex =
        this.albums!.findIndex(album => album.id === e.model.album.id);
    assert(albumIndex >= 0);
    const albumChanged = this.albums![albumIndex];

    if (this.topicSource === TopicSource.kVideo && albumChanged.checked) {
      // De-selecting a selected video album is a no-op. Selecting a different
      // video album will unselect the other video albums in the client.
      return;
    }

    albumChanged.checked = !albumChanged.checked;
    this.dispatchEvent(new CustomEvent(
        'album_selected_changed',
        {bubbles: true, composed: true, detail: {album: albumChanged}}));
  }

  private isAlbumSelected_(
      changedAlbum: AmbientModeAlbum|null,
      albums: AmbientModeAlbum[]|null): boolean {
    if (!changedAlbum) {
      return false;
    }
    const album = albums!.find(album => album.id === changedAlbum.id);
    return !!album && album.checked;
  }

  private getAlbumItemClass_(
      album: AmbientModeAlbum|null, albums: AmbientModeAlbum[]|null): string {
    return album && this.isAlbumSelected_(album, albums) ?
        'album album-selected' :
        'album';
  }

  /** Returns the secondary text to display for the specified |album|. */
  private getSecondaryText_(
      album: AmbientModeAlbum|null, topicSource: TopicSource): string {
    if (!album) {
      return '';
    }
    if (topicSource === TopicSource.kGooglePhotos) {
      if (isRecentHighlightsAlbum(album)) {
        return this.i18n('ambientModeAlbumsSubpageRecentHighlightsDesc');
      }
      return getCountText(album.numberOfPhotos);
    }
    if (this.topicSource === TopicSource.kArtGallery ||
        this.topicSource === TopicSource.kVideo) {
      return album.description;
    }
    return '';
  }

  private getAriaIndex_(index: number): number {
    return index + 1;
  }

  private isGooglePhotos_(topicSource: TopicSource): boolean {
    return topicSource === TopicSource.kGooglePhotos;
  }

  private isVideo_(topicSource: TopicSource): boolean {
    return topicSource === TopicSource.kVideo;
  }
}

customElements.define(AlbumListElement.is, AlbumListElement);