<commit_before>import {
  action,
  reaction,
  computed,
  observable,
} from 'mobx';
import { debounce, remove } from 'lodash';
import ms from 'ms';
import { remote } from 'electron';

import Store from './lib/Store';
import Request from './lib/Request';
import CachedRequest from './lib/CachedRequest';
import { matchRoute } from '../helpers/routing-helpers';
import { gaEvent, statsEvent } from '../lib/analytics';
import { workspaceStore } from '../features/workspaces';
import { serviceLimitStore } from '../features/serviceLimit';
import { RESTRICTION_TYPES } from '../models/Service';

const debug = require('debug')('Franz:ServiceStore');

const { app } = remote;

export default class ServicesStore extends Store {
  @observable allServicesRequest = new CachedRequest(this.api.services, 'all');

  @observable createServiceRequest = new Request(this.api.services, 'create');

  @observable updateServiceRequest = new Request(this.api.services, 'update');

  @observable reorderServicesRequest = new Request(this.api.services, 'reorder');

  @observable deleteServiceRequest = new Request(this.api.services, 'delete');

  @observable clearCacheRequest = new Request(this.api.services, 'clearCache');

  @observable filterNeedle = null;

  constructor(...args) {
    super(...args);

    // Register action handlers
    this.actions.service.setActive.listen(this._setActive.bind(this));
    this.actions.service.blurActive.listen(this._blurActive.bind(this));
    this.actions.service.setActiveNext.listen(this._setActiveNext.bind(this));
    this.actions.service.setActivePrev.listen(this._setActivePrev.bind(this));
    this.actions.service.showAddServiceInterface.listen(this._showAddServiceInterface.bind(this));
    this.actions.service.createService.listen(this._createService.bind(this));
    this.actions.service.createFromLegacyService.listen(this._createFromLegacyService.bind(this));
    this.actions.service.updateService.listen(this._updateService.bind(this));
    this.actions.service.deleteService.listen(this._deleteService.bind(this));
    this.actions.service.clearCache.listen(this._clearCache.bind(this));
    this.actions.service.setWebviewReference.listen(this._setWebviewReference.bind(this));
    this.actions.service.detachService.listen(this._detachService.bind(this));
    this.actions.service.focusService.listen(this._focusService.bind(this));
    this.actions.service.focusActiveService.listen(this._focusActiveService.bind(this));
    this.actions.service.toggleService.listen(this._toggleService.bind(this));
    this.actions.service.handleIPCMessage.listen(this._handleIPCMessage.bind(this));
    this.actions.service.sendIPCMessage.listen(this._sendIPCMessage.bind(this));
    this.actions.service.sendIPCMessageToAllServices.listen(this._sendIPCMessageToAllServices.bind(this));
    this.actions.service.setUnreadMessageCount.listen(this._setUnreadMessageCount.bind(this));
    this.actions.service.openWindow.listen(this._openWindow.bind(this));
    this.actions.service.filter.listen(this._filter.bind(this));
    this.actions.service.resetFilter.listen(this._resetFilter.bind(this));
    this.actions.service.resetStatus.listen(this._resetStatus.bind(this));
    this.actions.service.reload.listen(this._reload.bind(this));
    this.actions.service.reloadActive.listen(this._reloadActive.bind(this));
    this.actions.service.reloadAll.listen(this._reloadAll.bind(this));
    this.actions.service.reloadUpdatedServices.listen(this._reloadUpdatedServices.bind(this));
    this.actions.service.reorder.listen(this._reorder.bind(this));
    this.actions.service.toggleNotifications.listen(this._toggleNotifications.bind(this));
    this.actions.service.toggleAudio.listen(this._toggleAudio.bind(this));
    this.actions.service.openDevTools.listen(this._openDevTools.bind(this));
    this.actions.service.openDevToolsForActiveService.listen(this._openDevToolsForActiveService.bind(this));
    this.actions.service.hibernate.listen(this._hibernate.bind(this));
    this.actions.service.awake.listen(this._awake.bind(this));
    this.actions.service.resetLastPollTimer.listen(this._resetLastPollTimer.bind(this));

    this.registerReactions([
      this._focusServiceReaction.bind(this),
      this._getUnreadMessageCountReaction.bind(this),
      this._mapActiveServiceToServiceModelReaction.bind(this),
      this._saveActiveService.bind(this),
      this._logoutReaction.bind(this),
      this._handleMuteSettings.bind(this),
      this._restrictServiceAccess.bind(this),
    ]);

    // Just bind this
    this._initializeServiceRecipeInWebview.bind(this);
  }

  setup() {
    // Single key reactions for the sake of your CPU
    reaction(
      () => this.stores.settings.app.enableSpellchecking,
      () => this._shareSettingsWithServiceProcess(),
    );

    reaction(
      () => this.stores.settings.app.spellcheckerLanguage,
      () => this._shareSettingsWithServiceProcess(),
    );
  }

  initialize() {
    super.initialize();

    // Check services to become hibernated
    this.serviceMaintenanceTick();
  }

  teardown() {
    super.teardown();

    // Stop checking services for hibernation
    this.serviceMaintenanceTick.cancel();
  }

  /**
   * Сheck for services to become hibernated.
   */
  serviceMaintenanceTick = debounce(() => {
    this._serviceMaintenance();
    this.serviceMaintenanceTick();
    debug('Service maintenance tick');
  }, ms('10s'));

  /**
   * Run various maintenance tasks on services
   */
  _serviceMaintenance() {
    this.all.forEach((service) => {
      // Defines which services should be hibernated.
      if (!service.isActive && (Date.now() - service.lastUsed > ms('5m'))) {
        // If service is stale for 5 min, hibernate it.
        this._hibernate({ serviceId: service.id });
      }

      if (service.lastPoll && (service.lastPoll - service.lastPollAnswer > ms('1m'))) {
        // If service did not reply for more than 1m try to reload.
        if (!service.isActive) {
          if (this.stores.app.isOnline && service.lostRecipeReloadAttempt < 3) {
            debug(`Reloading service: ${service.name} (${service.id}). Attempt: ${service.lostRecipeReloadAttempt}`);
            service.webview.reload();
            service.lostRecipeReloadAttempt += 1;

            service.lostRecipeConnection = false;
          }
        } else {
          debug(`Service lost connection: ${service.name} (${service.id}).`);
          service.lostRecipeConnection = true;
        }
      } else {
        service.lostRecipeConnection = false;
        service.lostRecipeReloadAttempt = 0;
      }
    });
  }

  // Computed props
  @computed get all() {
    if (this.stores.user.isLoggedIn) {<commit_msg>reload test<commit_after>import {
  action,
  reaction,
  computed,
  observable,
} from 'mobx';
import { debounce, remove } from 'lodash';
import ms from 'ms';
import { remote } from 'electron';

import Store from './lib/Store';
import Request from './lib/Request';
import CachedRequest from './lib/CachedRequest';
import { matchRoute } from '../helpers/routing-helpers';
import { gaEvent, statsEvent } from '../lib/analytics';
import { workspaceStore } from '../features/workspaces';
import { serviceLimitStore } from '../features/serviceLimit';
import { RESTRICTION_TYPES } from '../models/Service';

const debug = require('debug')('Franz:ServiceStore');

const { app } = remote;

export default class ServicesStore extends Store {
  @observable allServicesRequest = new CachedRequest(this.api.services, 'all');

  @observable createServiceRequest = new Request(this.api.services, 'create');

  @observable updateServiceRequest = new Request(this.api.services, 'update');

  @observable reorderServicesRequest = new Request(this.api.services, 'reorder');

  @observable deleteServiceRequest = new Request(this.api.services, 'delete');

  @observable clearCacheRequest = new Request(this.api.services, 'clearCache');

  @observable filterNeedle = null;

  constructor(...args) {
    super(...args);

    // Register action handlers
    this.actions.service.setActive.listen(this._setActive.bind(this));
    this.actions.service.blurActive.listen(this._blurActive.bind(this));
    this.actions.service.setActiveNext.listen(this._setActiveNext.bind(this));
    this.actions.service.setActivePrev.listen(this._setActivePrev.bind(this));
    this.actions.service.showAddServiceInterface.listen(this._showAddServiceInterface.bind(this));
    this.actions.service.createService.listen(this._createService.bind(this));
    this.actions.service.createFromLegacyService.listen(this._createFromLegacyService.bind(this));
    this.actions.service.updateService.listen(this._updateService.bind(this));
    this.actions.service.deleteService.listen(this._deleteService.bind(this));
    this.actions.service.clearCache.listen(this._clearCache.bind(this));
    this.actions.service.setWebviewReference.listen(this._setWebviewReference.bind(this));
    this.actions.service.detachService.listen(this._detachService.bind(this));
    this.actions.service.focusService.listen(this._focusService.bind(this));
    this.actions.service.focusActiveService.listen(this._focusActiveService.bind(this));
    this.actions.service.toggleService.listen(this._toggleService.bind(this));
    this.actions.service.handleIPCMessage.listen(this._handleIPCMessage.bind(this));
    this.actions.service.sendIPCMessage.listen(this._sendIPCMessage.bind(this));
    this.actions.service.sendIPCMessageToAllServices.listen(this._sendIPCMessageToAllServices.bind(this));
    this.actions.service.setUnreadMessageCount.listen(this._setUnreadMessageCount.bind(this));
    this.actions.service.openWindow.listen(this._openWindow.bind(this));
    this.actions.service.filter.listen(this._filter.bind(this));
    this.actions.service.resetFilter.listen(this._resetFilter.bind(this));
    this.actions.service.resetStatus.listen(this._resetStatus.bind(this));
    this.actions.service.reload.listen(this._reload.bind(this));
    this.actions.service.reloadActive.listen(this._reloadActive.bind(this));
    this.actions.service.reloadAll.listen(this._reloadAll.bind(this));
    this.actions.service.reloadUpdatedServices.listen(this._reloadUpdatedServices.bind(this));
    this.actions.service.reorder.listen(this._reorder.bind(this));
    this.actions.service.toggleNotifications.listen(this._toggleNotifications.bind(this));
    this.actions.service.toggleAudio.listen(this._toggleAudio.bind(this));
    this.actions.service.openDevTools.listen(this._openDevTools.bind(this));
    this.actions.service.openDevToolsForActiveService.listen(this._openDevToolsForActiveService.bind(this));
    this.actions.service.hibernate.listen(this._hibernate.bind(this));
    this.actions.service.awake.listen(this._awake.bind(this));
    this.actions.service.resetLastPollTimer.listen(this._resetLastPollTimer.bind(this));

    this.registerReactions([
      this._focusServiceReaction.bind(this),
      this._getUnreadMessageCountReaction.bind(this),
      this._mapActiveServiceToServiceModelReaction.bind(this),
      this._saveActiveService.bind(this),
      this._logoutReaction.bind(this),
      this._handleMuteSettings.bind(this),
      this._restrictServiceAccess.bind(this),
    ]);

    // Just bind this
    this._initializeServiceRecipeInWebview.bind(this);
  }

  setup() {
    // Single key reactions for the sake of your CPU
    reaction(
      () => this.stores.settings.app.enableSpellchecking,
      () => this._shareSettingsWithServiceProcess(),
    );

    reaction(
      () => this.stores.settings.app.spellcheckerLanguage,
      () => this._shareSettingsWithServiceProcess(),
    );
  }

  initialize() {
    super.initialize();

    // Check services to become hibernated
    this.serviceMaintenanceTick();
  }

  teardown() {
    super.teardown();

    // Stop checking services for hibernation
    this.serviceMaintenanceTick.cancel();
  }

  /**
   * Сheck for services to become hibernated.
   */
  serviceMaintenanceTick = debounce(() => {
    this._serviceMaintenance();
    this.serviceMaintenanceTick();
    debug('Service maintenance tick');
  }, ms('10s'));

  /**
   * Run various maintenance tasks on services
   */
  _serviceMaintenance() {
    this.all.forEach((service) => {
      // Defines which services should be hibernated.
      if (!service.isActive && (Date.now() - service.lastUsed > ms('5m'))) {
        // If service is stale for 5 min, hibernate it.
        this._hibernate({ serviceId: service.id });
      }

      if (service.lastPoll && (service.lastPoll - service.lastPollAnswer > ms('1m'))) {
        // If service did not reply for more than 1m try to reload.
        if (!service.isActive) {
          if (this.stores.app.isOnline && service.lostRecipeReloadAttempt < 3) {
            debug(`Reloading service: ${service.name} (${service.id}). Attempt: ${service.lostRecipeReloadAttempt}`);
            // service.webview.reload();
            service.lostRecipeReloadAttempt += 1;

            service.lostRecipeConnection = false;
          }
        } else {
          debug(`Service lost connection: ${service.name} (${service.id}).`);
          service.lostRecipeConnection = true;
        }
      } else {
        service.lostRecipeConnection = false;
        service.lostRecipeReloadAttempt = 0;
      }
    });
  }

  // Computed props
  @computed get all() {
    if (this.stores.user.isLoggedIn) {
{'fraction_non_alphanumeric': 0.13387998509131568, 'fraction_numerical': 0.001341781587774879, 'mean_word_length': 4.1186570011446015, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 19, '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': '5496565', 'n_tokens_mistral': 3979, 'n_tokens_neox': 3882, 'n_words': 563}