<commit_before>import M from '../Messages';
import { Phase } from './Timer';
import { pomodoroCount } from '../Filters';
import * as Sounds from '../Sounds';
import Notification from './Notification';
import { ExpirationPage } from './Expiration';
import Mutex from '../Mutex';
import createTimerSound from '../TimerSound';

class BadgeObserver
{
  onTimerStart(phase, nextPhase, elapsed, remaining) {
    this.updateBadge({ phase, minutes: Math.round(remaining / 60) });
  }

  onTimerTick(phase, nextPhase, elapsed, remaining) {
    this.updateBadge({ phase, minutes: Math.round(remaining / 60) });
  }

  onTimerStop(phase, nextPhase) {
    this.removeBadge();
  }

  onTimerPause(phase, nextPhase) {
    this.updateBadge({ phase, text: '—', tooltip: M.timer_paused });
  }

  onTimerResume(phase, nextPhase, elapsed, remaining) {
    this.updateBadge({ phase, minutes: Math.round(remaining / 60) });
  }

  onTimerExpire(phase, nextPhase) {
    this.removeBadge();
  }

  updateBadge({ phase, minutes, tooltip, text }) {
    let title = {
      [Phase.Focus]: M.focus_title,
      [Phase.ShortBreak]: M.short_break_title,
      [Phase.LongBreak]: M.long_break_title
    }[phase];

    if (minutes != null) {
      text = minutes < 1 ? M.less_than_minute : M.n_minutes(minutes);
      tooltip = M.browser_action_tooltip(title, M.time_remaining(text));
    } else {
      tooltip = M.browser_action_tooltip(title, tooltip);
    }

    let color = phase === Phase.Focus ? '#bb0000' : '#11aa11';
    chrome.browserAction.setTitle({ title: tooltip });
    chrome.browserAction.setBadgeText({ text });
    chrome.browserAction.setBadgeBackgroundColor({ color });
  }

  removeBadge() {
    chrome.browserAction.setTitle({ title: '' });
    chrome.browserAction.setBadgeText({ text: '' });
  }
}

class TimerSoundObserver
{
  constructor(settings) {
    this.settings = settings;
    this.mutex = new Mutex();
    this.timerSound = null;
  }

  async onTimerStart(phase) {
    if (phase !== Phase.Focus) {
      return;
    }

    let timerSoundSettings = this.settings.focus.timerSound;
    if (timerSoundSettings) {
      await this.mutex.exclusive(async () => {
        this.timerSound && await this.timerSound.close();
        this.timerSound = await createTimerSound(timerSoundSettings);
        this.timerSound.start();
      });
    }
  }

  async onTimerStop() {
    await this.mutex.exclusive(async () => {
      this.timerSound && await this.timerSound.close();
    });
  }

  async onTimerPause() {
    await this.mutex.exclusive(async () => {
      this.timerSound && await this.timerSound.stop();
    });
  }

  async onTimerResume() {
    await this.mutex.exclusive(async () => {
      this.timerSound && await this.timerSound.start();
    });
  }

  async onTimerExpire() {
    await this.mutex.exclusive(async () => {
      this.timerSound && await this.timerSound.close();
    });
  }
}

class ExpirationSoundObserver
{
  constructor(settings) {
    this.settings = settings;<commit_msg>Fix timer sound observer when changing phases.<commit_after>import M from '../Messages';
import { Phase } from './Timer';
import { pomodoroCount } from '../Filters';
import * as Sounds from '../Sounds';
import Notification from './Notification';
import { ExpirationPage } from './Expiration';
import Mutex from '../Mutex';
import createTimerSound from '../TimerSound';

class BadgeObserver
{
  onTimerStart(phase, nextPhase, elapsed, remaining) {
    this.updateBadge({ phase, minutes: Math.round(remaining / 60) });
  }

  onTimerTick(phase, nextPhase, elapsed, remaining) {
    this.updateBadge({ phase, minutes: Math.round(remaining / 60) });
  }

  onTimerStop(phase, nextPhase) {
    this.removeBadge();
  }

  onTimerPause(phase, nextPhase) {
    this.updateBadge({ phase, text: '—', tooltip: M.timer_paused });
  }

  onTimerResume(phase, nextPhase, elapsed, remaining) {
    this.updateBadge({ phase, minutes: Math.round(remaining / 60) });
  }

  onTimerExpire(phase, nextPhase) {
    this.removeBadge();
  }

  updateBadge({ phase, minutes, tooltip, text }) {
    let title = {
      [Phase.Focus]: M.focus_title,
      [Phase.ShortBreak]: M.short_break_title,
      [Phase.LongBreak]: M.long_break_title
    }[phase];

    if (minutes != null) {
      text = minutes < 1 ? M.less_than_minute : M.n_minutes(minutes);
      tooltip = M.browser_action_tooltip(title, M.time_remaining(text));
    } else {
      tooltip = M.browser_action_tooltip(title, tooltip);
    }

    let color = phase === Phase.Focus ? '#bb0000' : '#11aa11';
    chrome.browserAction.setTitle({ title: tooltip });
    chrome.browserAction.setBadgeText({ text });
    chrome.browserAction.setBadgeBackgroundColor({ color });
  }

  removeBadge() {
    chrome.browserAction.setTitle({ title: '' });
    chrome.browserAction.setBadgeText({ text: '' });
  }
}

class TimerSoundObserver
{
  constructor(settings) {
    this.settings = settings;
    this.mutex = new Mutex();
    this.timerSound = null;
  }

  async onTimerStart(phase) {
    let timerSoundSettings = this.settings.focus.timerSound;
    await this.mutex.exclusive(async () => {
      // Cleanup any existing timer sound.
      this.timerSound && await this.timerSound.close();

      if (phase === Phase.Focus && timerSoundSettings) {
        this.timerSound = await createTimerSound(timerSoundSettings);
        this.timerSound.start();
      } else {
        this.timerSound = null;
      }
    });
  }

  async onTimerStop() {
    await this.mutex.exclusive(async () => {
      this.timerSound && await this.timerSound.close();
    });
  }

  async onTimerPause() {
    await this.mutex.exclusive(async () => {
      this.timerSound && await this.timerSound.stop();
    });
  }

  async onTimerResume() {
    await this.mutex.exclusive(async () => {
      this.timerSound && await this.timerSound.start();
    });
  }

  async onTimerExpire() {
    await this.mutex.exclusive(async () => {
      this.timerSound && await this.timerSound.close();
    });
  }
}

class ExpirationSoundObserver
{
  constructor(settings) {
    this.settings = settings;
{'fraction_non_alphanumeric': 0.14602132895816242, 'fraction_numerical': 0.004922067268252666, 'mean_word_length': 3.2778947368421054, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2732627', 'n_tokens_mistral': 1959, 'n_tokens_neox': 1881, 'n_words': 383}