chromium/chrome/browser/resources/feed_internals/feed_internals.ts

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

import {assert} from 'chrome://resources/js/assert.js';
import {getRequiredElement} from 'chrome://resources/js/util.js';
import type {TimeDelta} from 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';

import type {LastFetchProperties, PageHandlerRemote} from './feed_internals.mojom-webui.js';
import {FeedOrder, PageHandler} from './feed_internals.mojom-webui.js';

/**
 * Reference to the backend.
 */
let pageHandler: PageHandlerRemote|null = null;

/**
 * Get and display general properties.
 */
function updatePageWithProperties() {
  assert(pageHandler);
  pageHandler.getGeneralProperties().then(response => {
    const properties = response.properties;
    getRequiredElement('is-feed-enabled').textContent =
        String(properties.isFeedEnabled);
    getRequiredElement('is-feed-visible').textContent =
        String(properties.isFeedVisible);
    getRequiredElement('is-feed-allowed').textContent =
        String(properties.isFeedAllowed);
    getRequiredElement('is-prefetching-enabled').textContent =
        String(properties.isPrefetchingEnabled);
    getRequiredElement('load-stream-status').textContent =
        properties.loadStreamStatus;
    getRequiredElement('feed-fetch-url').textContent =
        properties.feedFetchUrl.url;
    getRequiredElement('feed-actions-url').textContent =
        properties.feedActionsUrl.url;
    getRequiredElement<HTMLInputElement>('enable-webfeed-follow-intro-debug')
        .checked = properties.isWebFeedFollowIntroDebugEnabled;
    getRequiredElement<HTMLInputElement>('enable-webfeed-follow-intro-debug')
        .disabled = false;
    getRequiredElement<HTMLInputElement>('use-feed-query-requests').checked =
        properties.useFeedQueryRequests;

    switch (properties.followingFeedOrder) {
      case FeedOrder.kUnspecified:
        getRequiredElement<HTMLInputElement>('following-feed-order-unset')
            .checked = true;
        break;
      case FeedOrder.kGrouped:
        getRequiredElement<HTMLInputElement>('following-feed-order-grouped')
            .checked = true;
        break;
      case FeedOrder.kReverseChron:
        getRequiredElement<HTMLInputElement>(
            'following-feed-order-reverse-chron')
            .checked = true;
        break;
    }
    getRequiredElement<HTMLInputElement>('following-feed-order-grouped')
        .disabled = false;
    getRequiredElement<HTMLInputElement>('following-feed-order-reverse-chron')
        .disabled = false;
    getRequiredElement<HTMLInputElement>('following-feed-order-unset')
        .disabled = false;
  });
}

/**
 * Get and display last fetch data.
 */
function updatePageWithLastFetchProperties() {
  assert(pageHandler);
  pageHandler.getLastFetchProperties().then(response => {
    const properties: LastFetchProperties = response.properties;
    getRequiredElement('last-fetch-status').textContent =
        String(properties.lastFetchStatus);
    getRequiredElement('last-fetch-trigger').textContent =
        properties.lastFetchTrigger;
    getRequiredElement('last-fetch-time').textContent =
        toDateString(properties.lastFetchTime);
    getRequiredElement('refresh-suppress-time').textContent =
        toDateString(properties.refreshSuppressTime);
    getRequiredElement('last-fetch-bless-nonce').textContent =
        properties.lastBlessNonce;
    getRequiredElement('last-action-upload-status').textContent =
        String(properties.lastActionUploadStatus);
    getRequiredElement('last-action-upload-time').textContent =
        toDateString(properties.lastActionUploadTime);
  });
}

/**
 * Convert timeSinceEpoch to string for display.
 */
function toDateString(timeSinceEpoch: TimeDelta): string {
  const microseconds = Number(timeSinceEpoch.microseconds);
  return microseconds === 0 ? '' :
                              new Date(microseconds / 1000).toLocaleString();
}

/**
 * Hook up buttons to event listeners.
 */
function setupEventListeners() {
  getRequiredElement('refresh-for-you').addEventListener('click', function() {
    assert(pageHandler);
    pageHandler.refreshForYouFeed();
  });

  getRequiredElement('refresh-following').addEventListener('click', function() {
    assert(pageHandler);
    pageHandler.refreshFollowingFeed();
  });

  getRequiredElement('refresh-webfeed-suggestions')
      .addEventListener('click', () => {
        assert(pageHandler);
        pageHandler.refreshWebFeedSuggestions();
      });

  getRequiredElement('dump-feed-process-scope')
      .addEventListener('click', function() {
        assert(pageHandler);
        pageHandler.getFeedProcessScopeDump().then(response => {
          getRequiredElement('feed-process-scope-dump').textContent =
              response.dump;
          getRequiredElement<HTMLDetailsElement>('feed-process-scope-details')
              .open = true;
        });
      });

  getRequiredElement('load-feed-histograms')
      .addEventListener('click', function() {
        assert(pageHandler);
        pageHandler.getFeedHistograms().then(response => {
          getRequiredElement('feed-histograms-log').textContent = response.log;
          getRequiredElement<HTMLDetailsElement>('feed-histograms-details')
              .open = true;
        });
      });

  getRequiredElement('feed-host-override-apply')
      .addEventListener('click', function() {
        assert(pageHandler);
        pageHandler.overrideFeedHost({
          url: getRequiredElement<HTMLInputElement>('feed-host-override').value,
        });
      });

  getRequiredElement('discover-api-override-apply')
      .addEventListener('click', function() {
        assert(pageHandler);
        pageHandler.overrideDiscoverApiEndpoint({
          url: getRequiredElement<HTMLInputElement>('discover-api-override')
                   .value,
        });
      });

  getRequiredElement('feed-stream-data-override')
      .addEventListener('click', function() {
        assert(pageHandler);
        const file =
            getRequiredElement<HTMLInputElement>('feed-stream-data-file')
                .files![0];
        if (file && typeof pageHandler.overrideFeedStreamData === 'function') {
          const reader = new FileReader();
          reader.readAsArrayBuffer(file);
          reader.onload = function(e) {
            assert(pageHandler);
            const typedArray = new Uint8Array(e.target!.result as ArrayBuffer);
            pageHandler.overrideFeedStreamData([...typedArray]);
          };
        }
      });

  getRequiredElement('enable-webfeed-follow-intro-debug')
      .addEventListener('click', function() {
        assert(pageHandler);
        pageHandler.setWebFeedFollowIntroDebugEnabled(
            getRequiredElement<HTMLInputElement>(
                'enable-webfeed-follow-intro-debug')
                .checked);
        getRequiredElement<HTMLInputElement>(
            'enable-webfeed-follow-intro-debug')
            .disabled = true;
      });

  getRequiredElement('use-feed-query-requests')
      .addEventListener('click', function() {
        assert(pageHandler);
        pageHandler.setUseFeedQueryRequests(
            getRequiredElement<HTMLInputElement>('use-feed-query-requests')
                .checked);
      });

  const orderRadioClickListener = function(order: FeedOrder) {
    assert(pageHandler);
    getRequiredElement<HTMLInputElement>('following-feed-order-grouped')
        .disabled = true;
    getRequiredElement<HTMLInputElement>('following-feed-order-reverse-chron')
        .disabled = true;
    getRequiredElement<HTMLInputElement>('following-feed-order-unset')
        .disabled = true;
    pageHandler.setFollowingFeedOrder(order);
  };
  getRequiredElement('following-feed-order-unset')
      .addEventListener(
          'click', () => orderRadioClickListener(FeedOrder.kUnspecified));
  getRequiredElement('following-feed-order-grouped')
      .addEventListener(
          'click', () => orderRadioClickListener(FeedOrder.kGrouped));
  getRequiredElement('following-feed-order-reverse-chron')
      .addEventListener(
          'click', () => orderRadioClickListener(FeedOrder.kReverseChron));
}

function updatePage() {
  updatePageWithProperties();
  updatePageWithLastFetchProperties();
}

document.addEventListener('DOMContentLoaded', function() {
  // Setup backend mojo.
  pageHandler = PageHandler.getRemote();

  setInterval(updatePage, 2000);
  updatePage();

  setupEventListeners();
});