modules/messages.js

import { addElement, addText, get } from './dom.js'
/**
 * Fonctions pour gérer les messages utilisateur (feedback erreur|warning ou messages positifs)
 * @module
 */

/**
 * Les types possibles
 * @type {string[]}
 */
const types = ['info', 'warning', 'error', 'positive']

/**
 * Ajoute le feedback dans container
 * @param {HTMLElement} container
 * @param {Object} feedback
 * @param {string} feedback.message
 * @param {string} [feedback.type=error]
 * @param {string} [feedback.titre]
 * @return {HTMLElement} L'élément du feedback (déjà ajouté dans le container)
 */
export function addFeedback (container, { message, type = 'error', titre } = {}) {
  if (!types.includes(type)) {
    console.error(Error(`type de message inconnu : ${type}`))
    type = 'error'
  }
  if (!message) throw Error('Message obligatoire pour tout retour utilisateur')
  const cssDiv = type === 'info' ? '' : type
  const div = addElement(container, 'div', { className: `ui message ${cssDiv}` })
  const cssIcon = type === 'error'
    ? 'frown outline'
    : (type === 'warning')
        ? 'bullhorn'
        : 'bell outline' // info
  const iconClose = addElement(div, 'i', { className: 'close icon' })
  iconClose.addEventListener('click', () => div.remove())
  if (titre) {
    const divTitre = addElement(div, 'div', { className: 'header' })
    addElement(divTitre, 'i', { className: `${cssIcon} icon` })
    addText(divTitre, titre)
  }
  if (/<[a-zA-Z0-9_ "']+/.test(message)) div.innerHTML += message
  else addText(div, message)
  return div
}

/**
 * Ajoute un feedback (erreur ou encouragement)
 * @param {Object} feedback
 * @param {string} feedback.id id du div conteneur à utiliser
 * @param {string} feedback.message Le message à afficher
 * @param {string} feedback.type error|positive
 * @author Rémi ANGOT
 */
export function messageFeedback ({ id, message = '', type = 'error' } = {}) {
  if (!id || !message) return console.error(TypeError('arguments manquants'))
  const container = get(id)
  const div = addFeedback(container, { message, type })
  div.style.width = '400px'
  return div
}

export class UserFriendlyError extends Error {
  /**
   * Erreur destinée à être affichée à l'utilisateur
   * @param {string} [message] Si non fourni ce sera le texte générique 'Une erreur est survenue…
   * @param {Object} [options]
   * @param {string} [options.titre=Erreur interne]
   * @param {string} [options.type=error]
   */
  constructor (message, { titre, type } = {}) {
    if (!message) message = 'Une erreur est survenue.<br>Essayez de rafraichir la page.<br>Si l\'erreur persiste merci de contacter : <a href="mailto:contact@coopmaths.fr">contact@coopmaths.fr</a>'
    super(message)
    this.isUserFriendly = true // pour indiquer que le message peut être affiché tel quel à l'utilisateur
    this.titre = titre || 'Erreur interne'
    this.type = (types.includes(type) && type) || 'error'
  }
}

export const getInvalidModuleError = (path) => new UserFriendlyError(`${path} existe mais ne fournit pas les données attendues, impossible de charger cet exercice`)
export const getNoLatexError = (id) => new UserFriendlyError(`L'exercice ${id} n'a, pour l'instant, pas de version Latex`, { titre: 'Pas de contenu Latex pour cet exercice', type: 'warning' })
export const getUnknownError = (id) => new UserFriendlyError(`L'identifiant ${id} ne correspond à aucun exercice MathALEA.<br>Ceci est peut-être dû à un lien incomplet ou obsolète.`, { titre: 'Le code de l’exercice n’est pas valide' })

/**
 * Gestionnaire d'erreur générique (à mettre dans un catch). Il affichera l'erreur à l'utilisateur en cas de UserFriendlyError, ou le message d'erreur par défaut
 * @param {Error} error
 */
export function errorHandler (error) {
  // on sort toujours l'erreur d'origine en console
  console.error(error)
  // et on gère un retour utilisateur
  const container = get('containerErreur')
  if (!error.isUserFriendly) error = new UserFriendlyError() // le message d'erreur par défaut
  addFeedback(container, error)
}