blob: 5c1c13dd30d1b334d270070eae7629447fa44a9c [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome-untrusted://personalization/polymer/v3_0/iron-list/iron-list.js';
import './setup.js';
import './styles.js';
import {html, PolymerElement} from 'chrome-untrusted://personalization/polymer/v3_0/polymer/polymer_bundled.min.js';
import {EventType} from '../common/constants.js';
import {selectCollection, selectLocalCollection, validateReceivedData} from '../common/iframe_api.js';
import {unguessableTokenToString} from '../common/utils.js';
/**
* @fileoverview Responds to |SendCollectionsEvent| from trusted. Handles user
* input and responds with |SelectCollectionEvent| when an image is selected.
*/
const kLocalCollectionId = 'local_';
/**
* A displayable type constructed from a LocalImage or a WallpaperImage.
* @typedef {{id: string, name: string, count: string, preview: ?url.mojom.Url}}
*/
let Tile;
/**
* Get the text to display for number of images.
* @param {?number|undefined} x
* @return {string}
*/
function getCountText(x) {
switch (x) {
case undefined:
case null:
return '';
case 0:
return loadTimeData.getString('zeroImages');
case 1:
return loadTimeData.getString('oneImage');
default:
if (typeof x !== 'number' || x < 0) {
console.error('Received an impossible value');
return '';
}
return loadTimeData.getStringF('multipleImages', x);
}
}
/**
* A common display format between local images and WallpaperCollection.
* Get the first displayable image with data from the list of possible images.
* TODO(b/184774974) display a collage of up to three images.
* @param {Array<!chromeos.personalizationApp.mojom.LocalImage>} localImages
* @param {Object<string, string>} localImageData
* @return {!Tile}
*/
function getLocalTile(localImages, localImageData) {
const name = loadTimeData.getString('myImagesLabel');
if (localImageData && Array.isArray(localImages)) {
for (const {id} of localImages) {
const key = unguessableTokenToString(id);
const data = localImageData[key];
if (!data) {
continue;
}
return {
name,
id: kLocalCollectionId,
count: getCountText(localImages.length),
preview: {url: data},
};
}
}
return {
name,
id: kLocalCollectionId,
count: getCountText(0),
preview: null,
};
}
class CollectionsGrid extends PolymerElement {
static get is() {
return 'collections-grid';
}
static get template() {
return html`{__html_template__}`;
}
static get properties() {
return {
/**
* @type {!Array<!chromeos.personalizationApp.mojom.WallpaperCollection>}
* @private
*/
collections_: {
type: Array,
value: [],
},
/**
* Mapping of collection id to number of images. Loads in progressively
* after collections_.
* @type {!Object<string, number>}
* @private
*/
imageCounts_: {
type: Object,
value: {},
},
/**
* @type {!Array<!chromeos.personalizationApp.mojom.LocalImage>}
* @private
*/
localImages_: {
type: Array,
value: [],
},
/**
* Stores a mapping of local image id to thumbnail data.
* @private
* @type {!Object<string, string>}
*/
localImageData_: {
type: Object,
value: {},
},
/**
* @type {!Array<!Tile>}
*/
tiles_: {
type: Array,
computed:
'computeTiles_(collections_, imageCounts_, localImages_, localImageData_)',
},
};
}
/** @override */
constructor() {
super();
this.onMessageReceived_ = this.onMessageReceived_.bind(this);
}
/** @override */
connectedCallback() {
super.connectedCallback();
window.addEventListener('message', this.onMessageReceived_);
}
/** @override */
disconnectedCallback() {
super.disconnectedCallback();
window.removeEventListener('message', this.onMessageReceived_);
}
/**
* @param {!Array<!chromeos.personalizationApp.mojom.WallpaperCollection>}
* collections
* @param {!Object<string, number>} imageCounts
* @param {!Array<!chromeos.personalizationApp.mojom.LocalImage>} localImages
*/
computeTiles_(collections, imageCounts, localImages, localImageData) {
const localTile = getLocalTile(localImages, localImageData);
const collectionTiles = collections.map(({name, id, preview}) => {
return {
name,
id,
count: getCountText(imageCounts[id]),
preview,
};
});
return [localTile, ...collectionTiles];
}
/**
* Handler for messages from trusted code. Expects only SendImagesEvent and
* will error on any other event.
* @param {!Event} message
* @private
*/
onMessageReceived_(message) {
switch (message.data.type) {
case EventType.SEND_COLLECTIONS:
try {
this.collections_ =
validateReceivedData(message, EventType.SEND_COLLECTIONS);
} catch (e) {
console.warn('Invalid collections received', e);
this.collections_ = [];
}
break;
case EventType.SEND_IMAGE_COUNTS:
this.imageCounts_ = message.data.counts;
break;
case EventType.SEND_LOCAL_IMAGES:
try {
this.localImages_ =
validateReceivedData(message, EventType.SEND_LOCAL_IMAGES);
} catch (e) {
console.warn('Invalid local images received', e);
this.localImages_ = [];
}
break;
case EventType.SEND_LOCAL_IMAGE_DATA:
this.localImageData_ = {
...this.localImageData_,
[unguessableTokenToString(message.data.id)]: message.data.data,
};
break;
default:
console.error(`Unexpected event type ${message.data.type}`);
break;
}
}
/**
* Notify trusted code that a user clicked on a collection.
* @private
* @param {!Event} e
*/
onCollectionClicked_(e) {
const id = e.currentTarget.dataset.id;
if (id === kLocalCollectionId) {
selectLocalCollection(window.parent);
return;
}
selectCollection(window.parent, id);
}
}
customElements.define(CollectionsGrid.is, CollectionsGrid);