chromium/ash/webui/camera_app_ui/resources/js/views/camera/timertick.ts

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

import * as animate from '../../animation.js';
import * as dom from '../../dom.js';
import {play} from '../../sound.js';
import * as state from '../../state.js';
import {CanceledError} from '../../type.js';

/**
 * Handler to cancel the active running timer-ticks.
 */
let doCancel: (() => void)|null = null;

/**
 * Starts timer ticking if applicable.
 *
 * @return Promise for the operation.
 */
export async function start(): Promise<void> {
  doCancel = null;

  const isTimerOptionShown =
      getComputedStyle(dom.get('#open-timer-panel', HTMLElement)).display !==
      'none';
  if (!state.get(state.State.TIMER) || !isTimerOptionShown) {
    return Promise.resolve();
  }

  state.set(state.State.TIMER_TICK, true);
  try {
    await new Promise<void>((resolve, reject) => {
      let tickTimeout: number|null = null;
      const tickMsg = dom.get('#timer-tick-msg', HTMLElement);
      doCancel = () => {
        if (tickTimeout !== null) {
          clearTimeout(tickTimeout);
          tickTimeout = null;
        }
        animate.cancel(tickMsg);
        reject(new CanceledError('Timer tick is canceled'));
      };

      let tickCounter = state.get(state.State.TIMER_10SEC) ? 10 : 3;
      const sounds = new Map([
        [1, 'tickFinal'],
        [2, 'tickIncrement'],
        [3, 'tickIncrement'],
        [tickCounter, 'tickStart'],
      ] as const);

      function onTimerTick() {
        if (tickCounter === 0) {
          resolve();
        } else {
          const sound = sounds.get(tickCounter);
          if (sound !== undefined) {
            play(sound);
          }
          tickMsg.textContent = tickCounter + '';
          animate.play(tickMsg);
          tickTimeout = setTimeout(onTimerTick, 1000);
          tickCounter--;
        }
      }
      // First tick immediately in the next message loop cycle.
      tickTimeout = setTimeout(onTimerTick, 0);
    });
  } finally {
    state.set(state.State.TIMER_TICK, false);
  }
}

/**
 * Cancels active timer ticking if applicable.
 */
export function cancel(): void {
  doCancel?.();
  doCancel = null;
}