Source: input/VoiceRecorder.js


import { blobToBase64, convertirWavAMP3, emptyObject, useVariables } from "../..";
import { useState } from "react";
import { useEffect } from "react";
import { createContext } from "react";
import { useContext } from "react";
import { useAudioRecorder } from '@sarafhbk/react-audio-recorder'


/**
 * Componente que permite grabar un audio desde el dispositivo
 * @name Reactor.Components.Input.VoiceRecorder
 * @param {string} name 
 *  name del control y de la variable donde se van a guardar los datos del control
 * @param {string} [default_value = ""] Url data con el contenido de un audio que inicie por defecto
 * @param {string} [context = "global"] Contexto donde se encuentra la variable
 * @param {BooleanString} [use_change = "false"] 
 *  establece si envia el evento change al generar el audio
 * @param {string?} max_seconds 
 *  Cantidad de segundos que durara como máximo la grabación
 *  Se puede convinar con el uso del componente voice_recorder_auto_submit
 * @class 
 * @example 

<p>Ejemplo de VoiceRecorder con submit manual</p>
<form action="EjemplosVoiceRecorder" to="EjemplosVoiceRecorder" data='{}'>
  <voice_recorder name="audio_prompt" context="gpt" use_change="true">
    <voice_recorder_start>
      <div class="btn btn-primary" href="#">Grabar</div>
    </voice_recorder_start>
    <voice_recorder_stop>
      <div class="btn btn-secondary" href="#">Grabando</div>
    </voice_recorder_stop>
    <stamp variable="audio_prompt.recordingTime" context="gpt"></stamp>
    <if variable="audio_prompt.urlData" context="gpt">
      <condition value="" operator="!=" type="text">
        <audio controls var-src="audio_prompt.urlData" context="gpt"></audio>
        <br/>
        <voice_recorder_delete>
          <div class="btn btn-warning" href="#">Cancelar audio</div>
        </voice_recorder_delete>
      </condition>
      <condition value="" operator="=" type="text">
        no  hay contenido
      </condition>
    </if>  
  </voice_recorder>
  <input type="submit" value="enviar" />
</form>

<p>Ejemplo de VoiceRecorder con submit automatico</p>
<form action="EjemplosVoiceRecorder" to="EjemplosVoiceRecorder" data='{}'>
  <voice_recorder name="audio_prompt2" context="gpt" use_change="true">
    <voice_recorder_auto_submit></voice_recorder_auto_submit>
    <voice_recorder_start>
      <div class="btn btn-primary" href="#">Grabar</div>
    </voice_recorder_start>
    <voice_recorder_stop>
      <div class="btn btn-secondary" href="#">Grabando</div>
    </voice_recorder_stop>
    <p><stamp variable="audio_prompt2.recordingTime" context="gpt"></stamp></p>
  </voice_recorder>
</form>

<p>Ejemplo de VoiceRecorder con submit automatico y tiempo maximo de grabacion</p>
<form action="EjemplosVoiceRecorder" to="EjemplosVoiceRecorder" data='{}'>
  <voice_recorder name="audio_prompt3" context="gpt" use_change="true" max_seconds="10">
    <voice_recorder_auto_submit></voice_recorder_auto_submit>
    <voice_recorder_start>
      <div class="btn btn-primary" href="#">Grabar</div>
    </voice_recorder_start>
    <voice_recorder_stop>
      <div class="btn btn-secondary" href="#">Grabando</div>
    </voice_recorder_stop>
    <p><stamp variable="audio_prompt3.recordingTime" context="gpt"></stamp></p>
    <p><stamp variable="audio_prompt3.recordingTimeRemaining" context="gpt"></stamp></p>
  </voice_recorder>
</form>

 */

export const VoiceRecorder = ({default_value = "", name, context = "global", children, use_change = "false", max_seconds}) => {

  const variables = useVariables({variable: name, context});
  const [state, setState] = useState(() => ({urlData: default_value, isPaused: false, isRecording: false, recordingTime: "", recordingTimeRemaining: ""}));
  const recorderControls = useAudioRecorder();
  const { status, startRecording, stopRecording } = recorderControls;
  const isPaused = status === "paused";
  const isRecording = status === "recording";
  const timer = recorderControls.timer;
  const recordingTime = isRecording ? new Date(timer * 1000).toISOString().substr(11, 8) : "";
  const recordingBlob = recorderControls.audioResult;
  const maxSeconds = max_seconds ? parseInt(max_seconds) : 0;
  const remaining = maxSeconds - timer;
  const recordingTimeRemaining = (isRecording && remaining >= 0) ? new Date((remaining) * 1000).toISOString().substr(11, 8) : ""

  useEffect(() => {
    setState(state => ({...state, isPaused, isRecording, recordingTime, status, recordingTimeRemaining}))
  }, [isPaused, isRecording, recordingTime, status, recordingTimeRemaining])

  useEffect(() => {
    variables.setData(state);
  }, [state])

  useEffect(() => {
    if(!isRecording && !!recordingBlob){
      fetch(recordingBlob).then(response => {
        response.blob().then(result => {
          blobToBase64(result).then(base64 => {
            setState(state => ({...state, urlData: base64}))
          })
        })
      })
    }
  }, [isRecording, recordingBlob])

  useEffect(() => {
    if(isRecording && maxSeconds > 0 && remaining < 0){
      stopRecording();
    }
  }, [isRecording, remaining, maxSeconds])

  const deleteData = () => {
    setState(state => ({...state, urlData: ""}))
  }

  const handleStopRecording = () => {
    isRecording && stopRecording();
  }

  return (
    <VoiceRecorderContext.Provider value={{state, startRecording, stopRecording: handleStopRecording, deleteData}}>
      <input type="hidden" name={name} value={state.urlData} use_change={use_change}/>
      {children}
    </VoiceRecorderContext.Provider>
  );
}

const VoiceRecorderContext = createContext({
  state: {urlData: "", isPaused: false, isRecording: false, recordingTime: "", recordingTimeRemaining: ""}, 
  startRecording: () => {}, 
  stopRecording: () => {}, 
  togglePauseResume: () => {},
  deleteData: () => {}, 
});

export const useVoiceRecorderContext = () => useContext(VoiceRecorderContext);