import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
import { connect, useSelector } from "react-redux";
import { FormContext, emptyObject, processIncludeVariables, processOptions, useIncludeVariables } from "../..";
import { appActions } from "../../redux";
import { Template } from "./";
import { useSpinnerContext, booleanString } from "../..";
const msSpinnerTimeout = window.config.CONSTANTS.MS_SPINNER_TIMEOUT || 500;
/**
* Componente que renderiza un template asincrónicamente, y lo actualiza cada cierto tiempo.
* @name Reactor.Components.Templates.LoadAsync
* @param {string} href Nombre de la operación que se va a llamar
* @param {JsonString?} data Datos que se quieran pasar a la operación
* @param {BooleanString} [include_form = "false"] Indica si se incluyen en la data una propiedad form con los datos del form que contiene el loadasync
* @param {string} auto_refresh Se puede establecer un intérvalo en ms para realizar un refresco automático
* @param {string} hook Gancho para refrescar el componente con un CallerAsync (si se usa hook auto_refresh no tiene efecto)
* @param {BooleanString} [use_token = "false"] Si es "true" envía el token del flujo actual a la operación
* @param {BooleanString} [load_on_render = "true"] Trae los datos en el primer renderizado, sin esperar el auto_refresh
* @param {BooleanString} [use_spinner = "false"] Indica si se muestra un spinner mientras se espera la respuesta del servidor
* @param {BooleanString} [debug = "false"] Envía informacion a la consola para depurar el componente
* @param {BooleanString} [refresh_on_change_data = "false"] Hace una llamada cada vez que alguno de los datos obtenidos de "data", "include_variables" o "include_form" se modifica
* @param {string} include_variables Lista de variables que se incluyen en data (separadas por espacio)
* @param {string} context Contexto contenedor de las variables que se van a incluir, es requerido si se incluyen variables
* @class
* @example
<load_async href="HomeMovimientos" data='{"idProducto":{{idProducto}}}' auto_refresh="{{refreshRate}}"/>
<load_async href="HomeMovimientos" data='{"idProducto":{{idProducto}}}' hook="NombreDelGancho" use_token="true" use_form="true"/>
*/
const _LoadAsync = ({
href,
data = false,
include_form = "false",
auto_refresh,
hook,
use_token = "false",
load_on_render = "true",
use_spinner = "false",
debug = "false",
refresh_on_change_data = "false",
include_variables,
context,
getAsyncContentUntoken,
loadAsyncHookRegister,
children,
}) => {
const { formInputData } = useContext(FormContext);
const inputRef = useRef();
const hookState = useSelector(state => (hook && state.app.hooks[hook]) || hookStateDefault);
const [ loaded, setLoaded ] = useState();
const [ loaded2, setLoaded2 ] = useState(false);
const spinner = useSpinnerContext();
const [ content, setContent ] = useState();
const variablesState = useSelector(state => context === "_" ? state.app.variables : state.app.variables[context] ) || null;
useEffect(()=>{
setTimeout(() => {
setLoaded2(true);
}, 10);
}, [])
useEffect(()=>{
if(hook){
loadAsyncHookRegister(hook);
}
}, [hook])
useEffect(()=>{
if(booleanString(load_on_render) && !loaded){
if (booleanString(debug)) {
console.log({event: "load on render", hookState, hook, load_on_render, loaded});
}
setLoaded(true)
loadData(hookState);
}
}, [hookState, load_on_render, hook, loaded])
useEffect(()=>{
if(loaded && hookState?.id !== null){
if (booleanString(debug)) {
console.log({event: "hook update", hookState, hook, loaded});
}
loadData(hookState);
}
}, [hookState, hook, loaded])
useEffect(()=>{
if (booleanString(debug)) {
console.log({auto_refresh, hook});
}
if (auto_refresh !== undefined && auto_refresh !== 0 && auto_refresh !== "0"){
const timer = setInterval(()=>loadData(), auto_refresh * 1);
return () => clearInterval(timer);
}
}, [hook, auto_refresh])
const token = useSelector(state => state.app.token) || null;
const variablesData = useIncludeVariables({variablesState, include_variables});
const formData = useMemo(() => (include_form === "true") ? {form: formInputData} : emptyObject, [formInputData, include_form]);
const formDataStr = useMemo(() => JSON.stringify(formData), [formData]);
const sendData = useMemo(() => ({...processOptions(data), ...JSON.parse(formDataStr), ...variablesData}), [data, formDataStr, variablesData]);
useEffect(() => {
if(loaded2 && booleanString(refresh_on_change_data)){
if (booleanString(debug)) {
console.log({event: "hook update change data", sendData});
}
loadData();
}
}, [refresh_on_change_data, sendData, loaded2])
const loadData = useCallback( ({silent, noisy} = {}) => {
const useSpinner = silent ? false : (
noisy ? true : (
use_spinner === "true"
)
);
const spinnerTimer = useSpinner && setTimeout(() => spinner.show(), msSpinnerTimeout);
getAsyncContentUntoken(href, sendData, use_token === "true" ? token : null)
.then(response => {
if (booleanString(debug)) {
console.log({response});
}
setContent(response.data.data);
})
.catch(reason =>
console.warn(reason)
)
.finally(()=>{
clearTimeout(spinnerTimer);
useSpinner && spinner.hide();
});
}, [use_spinner, href, use_token, token, sendData]);
return (
<>
{
include_form === "true" &&
<input type="hidden" ref={inputRef} />
}
{content && <Template content={content} ></Template>}
{children}
</>
)
}
const mapStateToProps = (state) => {
return emptyObject;
}
const hookStateDefault = {id: null, noisy: false, silent: false};
export const LoadAsync = connect(mapStateToProps, appActions)(_LoadAsync)