Commit 0bbf0f94 authored by Becca Hughes's avatar Becca Hughes Committed by Commit Bot

[Media Feeds] Add feed items to WebUI

This adds the feed items to the WebUI. You
can select a feed to show the content.

BUG=1058187

Change-Id: I5051f77b68cb3da6710a547d50109cb95a7b890f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2118758
Commit-Queue: Becca Hughes <beccahughes@chromium.org>
Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Cr-Commit-Position: refs/heads/master@{#753441}
parent 03e43a1c
...@@ -80,7 +80,7 @@ cr.define('cr.ui', function() { ...@@ -80,7 +80,7 @@ cr.define('cr.ui', function() {
key = k; key = k;
}); });
this.delegate_.insertDataField(td, data, key); this.delegate_.insertDataField(td, data, key, dataRow);
tr.appendChild(td); tr.appendChild(td);
}); });
}); });
...@@ -103,8 +103,10 @@ cr.define('cr.ui', function() { ...@@ -103,8 +103,10 @@ cr.define('cr.ui', function() {
* @param {Element} td * @param {Element} td
* @param {?Object} data * @param {?Object} data
* @param {string} key * @param {string} key
* @param {Object} dataRow This is the row itself in case we need extra
* data to render the field.
*/ */
insertDataField(td, data, key) {} insertDataField(td, data, key, dataRow) {}
/** /**
* Compares two objects based on |sortKey|. * Compares two objects based on |sortKey|.
......
...@@ -124,6 +124,8 @@ ...@@ -124,6 +124,8 @@
</th> </th>
<th data-key="logos"> <th data-key="logos">
Logos Logos
<th data-key="actions">
Actions
</th> </th>
</tr> </tr>
</thead> </thead>
...@@ -131,22 +133,71 @@ ...@@ -131,22 +133,71 @@
</tbody> </tbody>
</table> </table>
<template id="datarow"> <div id="feed-content" style="display:none;">
<tr> <hr>
<td class="id-cell"></td> <h2>Feed Contents: <span id="current-feed"></span></h2>
<td class="url-cell"></td> <table id="feed-items-table">
<td></td> <thead>
<td class="last-discovery-time-cell"></td> <tr>
<td></td> <th sort-key="type" class="sort-column" sort-reverse>
<td></td> Type
<td></td> </th>
<td></td> <th sort-key="name">
<td></td> Name
<td></td> </th>
<td></td> <th sort-key="author">
<td></td> Author
<td></td> </th>
</tr> <th sort-key="datePublished">
</template> Date Published
</th>
<th sort-key="isFamilyFriendly">
Family Friendly
</th>
<th sort-key="actionStatus">
Action Status
</th>
<th sort-key="action.url">
Action URL
</th>
<th sort-key="action.startTime">
Action Start Time (secs)
</th>
<th sort-key="interactionCounters">
Interaction Counters
</th>
<th sort-key="contentRatings">
Content Ratings
</th>
<th sort-key="genre">
Genre
</th>
<th sort-key="live">
Live Details
</th>
<th sort-key="tvEpisode">
TV Episode
</th>
<th sort-key="playNextCandidate">
Play Next Candidate
</th>
<th sort-key="identifiers">
Identifiers
</th>
<th sort-key="shownCount">
Shown Count
</th>
<th sort-key="clicked">
Clicked
</th>
<th sort-key="images">
Images
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</body> </body>
</html> </html>
...@@ -11,10 +11,16 @@ function whenPageIsPopulatedForTest() { ...@@ -11,10 +11,16 @@ function whenPageIsPopulatedForTest() {
return mediaFeedsPageIsPopulatedResolver.promise; return mediaFeedsPageIsPopulatedResolver.promise;
} }
const mediaFeedItemsPageIsPopulatedResolver = new PromiseResolver();
function whenFeedTableIsPopulatedForTest() {
return mediaFeedItemsPageIsPopulatedResolver.promise;
}
(function() { (function() {
let delegate = null; let delegate = null;
let feedsTable = null; let feedsTable = null;
let feedItemsTable = null;
let store = null; let store = null;
/** @implements {cr.ui.MediaDataTableDelegate} */ /** @implements {cr.ui.MediaDataTableDelegate} */
...@@ -25,8 +31,28 @@ class MediaFeedsTableDelegate { ...@@ -25,8 +31,28 @@ class MediaFeedsTableDelegate {
* @param {Element} td * @param {Element} td
* @param {?Object} data * @param {?Object} data
* @param {string} key * @param {string} key
* @param {Object} dataRow
*/ */
insertDataField(td, data, key) { insertDataField(td, data, key, dataRow) {
if (key == 'actions') {
const a = document.createElement('a');
a.href = '#feed-content';
a.textContent = 'Show Contents';
td.appendChild(a);
a.addEventListener('click', () => {
store.getItemsForMediaFeed(dataRow.id).then(response => {
feedItemsTable.setData(response.items);
// Show the feed items section.
$('current-feed').textContent = dataRow.url.url;
$('feed-content').style.display = 'block';
mediaFeedItemsPageIsPopulatedResolver.resolve();
});
});
}
if (data === undefined || data === null) { if (data === undefined || data === null) {
return; return;
} }
...@@ -36,7 +62,7 @@ class MediaFeedsTableDelegate { ...@@ -36,7 +62,7 @@ class MediaFeedsTableDelegate {
td.textContent = data.url; td.textContent = data.url;
} else if ( } else if (
key === 'lastDiscoveryTime' || key === 'lastFetchTime' || key === 'lastDiscoveryTime' || key === 'lastFetchTime' ||
key === 'cacheExpiryTime') { key === 'cacheExpiryTime' || key === 'datePublished') {
// Format a mojo time. // Format a mojo time.
td.textContent = td.textContent =
convertMojoTimeToJS(/** @type {mojoBase.mojom.Time} */ (data)) convertMojoTimeToJS(/** @type {mojoBase.mojom.Time} */ (data))
...@@ -74,7 +100,7 @@ class MediaFeedsTableDelegate { ...@@ -74,7 +100,7 @@ class MediaFeedsTableDelegate {
td.textContent = td.textContent =
contentTypes.length === 0 ? 'None' : contentTypes.join(','); contentTypes.length === 0 ? 'None' : contentTypes.join(',');
} else if (key === 'logos') { } else if (key === 'logos' || key === 'images') {
// Format an array of mojo media images. // Format an array of mojo media images.
data.forEach((image) => { data.forEach((image) => {
const a = document.createElement('a'); const a = document.createElement('a');
...@@ -84,6 +110,129 @@ class MediaFeedsTableDelegate { ...@@ -84,6 +110,129 @@ class MediaFeedsTableDelegate {
td.appendChild(a); td.appendChild(a);
td.appendChild(document.createElement('br')); td.appendChild(document.createElement('br'));
}); });
} else if (key == 'type') {
// Format a MediaFeedItemType.
switch (parseInt(data, 10)) {
case mediaFeeds.mojom.MediaFeedItemType.kVideo:
td.textContent = 'Video';
break;
case mediaFeeds.mojom.MediaFeedItemType.kTVSeries:
td.textContent = 'TV Series';
break;
case mediaFeeds.mojom.MediaFeedItemType.kMovie:
td.textContent = 'Movie';
break;
}
} else if (key == 'isFamilyFriendly' || key == 'clicked') {
// Format a boolean.
td.textContent = data ? 'Yes' : 'No';
} else if (key == 'actionStatus') {
// Format a MediaFeedItemActionStatus.
switch (parseInt(data, 10)) {
case mediaFeeds.mojom.MediaFeedItemActionStatus.kUnknown:
td.textContent = 'Unknown';
break;
case mediaFeeds.mojom.MediaFeedItemActionStatus.kActive:
td.textContent = 'Active';
break;
case mediaFeeds.mojom.MediaFeedItemActionStatus.kPotential:
td.textContent = 'Potential';
break;
case mediaFeeds.mojom.MediaFeedItemActionStatus.kCompleted:
td.textContent = 'Completed';
break;
}
} else if (key == 'startTime') {
// Format a start time.
td.textContent =
timeDeltaToSeconds(/** @type {mojoBase.mojom.TimeDelta} */ (data));
} else if (key == 'interactionCounters') {
// Format interaction counters.
const counters = [];
Object.keys(data).forEach((key) => {
let keyString = '';
switch (parseInt(key, 10)) {
case mediaFeeds.mojom.InteractionCounterType.kWatch:
keyString = 'Watch';
break;
case mediaFeeds.mojom.InteractionCounterType.kLike:
keyString = 'Like';
break;
case mediaFeeds.mojom.InteractionCounterType.kDislike:
keyString = 'Dislike';
break;
}
counters.push(keyString + '=' + data[key]);
});
td.textContent = counters.join(' ');
} else if (key == 'contentRatings') {
// Format content ratings.
const ratings = [];
data.forEach((rating) => {
ratings.push(rating.agency + ' ' + rating.value);
});
td.textContent = ratings.join(', ');
} else if (key == 'author') {
// Format a mojom author.
const a = document.createElement('a');
a.href = data.url;
a.textContent = data.name;
a.target = '_blank';
td.appendChild(a);
} else if (key == 'name' || key == 'genre') {
// Format a mojo string16.
td.textContent =
decodeString16(/** @type {mojoBase.mojom.String16} */ (data));
} else if (key == 'live') {
// Format LiveDetails.
td.textContent = 'Live';
if (data.startTime) {
td.textContent += ' ' +
'StartTime=' +
convertMojoTimeToJS(
/** @type {mojoBase.mojom.Time} */ (data.startTime))
.toString();
}
if (data.endTime) {
td.textContent += ' ' +
'EndTime=' +
convertMojoTimeToJS(
/** @type {mojoBase.mojom.Time} */ (data.endTime))
.toString();
}
} else if (key == 'tvEpisode') {
// Format a TV Episode.
td.textContent = data.name + ' EpisodeNumber=' + data.episodeNumber +
' SeasonNumber=' + data.seasonNumber + ' ' +
formatIdentifiers(/** @type {Array<mediaFeeds.mojom.Identifier>} */ (
data.identifiers));
} else if (key == 'playNextCandidate') {
// Format a Play Next Candidate.
td.textContent = data.name + ' EpisodeNumber=' + data.episodeNumber +
' SeasonNumber=' + data.seasonNumber + ' ' +
formatIdentifiers(
/** @type {Array<mediaFeeds.mojom.Identifier>} */ (
data.identifiers)) +
' ActionURL=' + data.action.url.url;
if (data.action.startTime) {
td.textContent +=
' ActionStartTimeSecs=' + timeDeltaToSeconds(data.action.startTime);
}
td.textContent += ' DurationSecs=' + timeDeltaToSeconds(data.duration);
} else if (key == 'identifiers') {
// Format identifiers.
td.textContent = formatIdentifiers(
/** @type {Array<mediaFeeds.mojom.Identifier>} */ (data));
} else { } else {
td.textContent = data; td.textContent = data;
} }
...@@ -121,6 +270,57 @@ class MediaFeedsTableDelegate { ...@@ -121,6 +270,57 @@ class MediaFeedsTableDelegate {
} }
} }
/**
* Convert a time delta to seconds.
* @param {mojoBase.mojom.TimeDelta} timeDelta
* @returns {number}
*/
function timeDeltaToSeconds(timeDelta) {
return timeDelta.microseconds / 1000 / 1000;
}
/**
* Formats an array of identifiers for display.
* @param {Array<mediaFeeds.mojom.Identifier>} mojoIdentifiers
* @returns {string}
*/
function formatIdentifiers(mojoIdentifiers) {
const identifiers = [];
mojoIdentifiers.forEach((identifier) => {
let keyString = '';
switch (identifier.type) {
case mediaFeeds.mojom.Identifier_Type.kTMSRootId:
keyString = 'TMSRootId';
break;
case mediaFeeds.mojom.Identifier_Type.kTMSId:
keyString = 'TMSId';
break;
case mediaFeeds.mojom.Identifier_Type.kPartnerId:
keyString = 'PartnerId';
break;
}
identifiers.push(keyString + '=' + identifier.value);
});
return identifiers.join(' ');
}
/**
* Parses utf16 coded string.
* @param {?mojoBase.mojom.String16} arr
* @return {string}
*/
function decodeString16(arr) {
if (arr == null) {
return '';
}
return arr.data.map(ch => String.fromCodePoint(ch)).join('');
}
/** /**
* Converts a mojo time to a JS time. * Converts a mojo time to a JS time.
* @param {mojoBase.mojom.Time} mojoTime * @param {mojoBase.mojom.Time} mojoTime
...@@ -160,6 +360,7 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -160,6 +360,7 @@ document.addEventListener('DOMContentLoaded', () => {
delegate = new MediaFeedsTableDelegate(); delegate = new MediaFeedsTableDelegate();
feedsTable = new cr.ui.MediaDataTable($('feeds-table'), delegate); feedsTable = new cr.ui.MediaDataTable($('feeds-table'), delegate);
feedItemsTable = new cr.ui.MediaDataTable($('feed-items-table'), delegate);
updateFeedsTable(); updateFeedsTable();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment