<commit_before>goog.provide 'app.ErrorReporter'

goog.require 'goog.Promise'
goog.require 'goog.debug.ErrorReporter'

class app.ErrorReporter

  ###*
    @param {app.Routes} routes
    @param {app.user.Store} userStore
    @constructor
  ###
  constructor: (@routes, @userStore) ->
    @alreadyReported_ = Object.create null

    if !goog.DEBUG
      @reporter = goog.debug.ErrorReporter.install @routes.api.clientErrors.url()

  ###*
    @param {string} action
    @param {*} reason
  ###
  report: (action, reason) ->
    # Cancellation is not considered as a real error. For example, este.Router
    # cancels loading when another route is requested before previous is done.
    if reason instanceof goog.Promise.CancellationError
      return

    # Don't report already reported reason.
    reasonAsString = reason.toString()
    return if @alreadyReported_[reasonAsString]
    @alreadyReported_[reasonAsString] = true

    if reason instanceof app.Xhr.OfflineError
      alert reason.message
      return

    # TODO: Show something more beautiful then alert with a button to reload
    # app. No, auto-reload isn't a good idea since it can cause looping.
    alert 'Application error. Please reload browser.'

    if @reporter
      @reporter.setAdditionalArguments
        action: action
        user: @userStore?.user?.displayName || ''

    # Don't swallow error since it can be handled by any other promise.
    # Throw also ensures error is shown in console and therefore catched by
    # goog.debug.ErrorReporter.
    throw reason<commit_msg>Refactor app.ErrorReporter.<commit_after>goog.provide 'app.ErrorReporter'

goog.require 'goog.Promise'
goog.require 'goog.debug.ErrorReporter'
goog.require 'app.Xhr.OfflineError'
goog.require 'goog.net.HttpStatus'

class app.ErrorReporter

  ###*
    @param {app.Routes} routes
    @param {app.user.Store} userStore
    @constructor
  ###
  constructor: (@routes, @userStore) ->
    @alreadyReported_ = Object.create null

    if !goog.DEBUG
      @reporter = goog.debug.ErrorReporter.install @routes.api.clientErrors.url()

  ###*
    @param {string} action
    @param {*} reason
  ###
  report: (action, reason) ->
    return if !@isWorthReport_ reason

    # TODO: Show something more beautiful then alert. Add button to reload app.
    # Auto-reload is bad idea because it can cause reload looping.
    alert 'Application error. Please reload browser.'

    if !@isAlreadyReported_ reason
      @reporter?.setAdditionalArguments
        action: action
        user: @userStore?.user?.displayName || ''

      # Propagate error to other promises. It also ensures reason is shown in
      # console therefore catched and reported by goog.debug.ErrorReporter.
      throw reason

  ###*
    @param {*} reason
    @return {boolean}
  ###
  isWorthReport_: (reason) ->
    # 404 is common.
    return false if reason == 404
    # Cancellation is ok. For example este.Router cancels aborted requests.
    return false if reason instanceof goog.Promise.CancellationError
    # User is offline. It happens.
    if reason instanceof app.Xhr.OfflineError
      alert reason.message
      return false
    true

  ###*
    Don't report already reported reason.
    @param {*} reason
    @return {boolean}
  ###
  isAlreadyReported_: (reason) ->
    stringReason = String reason
    if @alreadyReported_[stringReason]
      return true
    @alreadyReported_[stringReason] = true
    false
{'fraction_non_alphanumeric': 0.09490538573508006, 'fraction_numerical': 0.0017467248908296944, 'mean_word_length': 3.1598062953995156, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, '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': '3726431', 'n_tokens_mistral': 1024, 'n_tokens_neox': 964, 'n_words': 307}