When you enter a prompt on chatgpt.com, the view position moves to the top of the window so that it generates text down the page instead of constantly moving and scrolling as you are trying to read! how can i impliment this in vanila js they are setting dynamic height but i dont how these working
How to reinitialize MailerLite embedded form on route change without page refresh?
I’m working on a React project where I have a common component used across multiple static pages (e.g., About Us, FAQs, Terms & Conditions, etc.).
Inside the footer, I include a MailerLiteEmbed component to show a newsletter subscription form using MailerLite’s embedded script.
Here’s the component:
import React, { useEffect } from 'react';
const MailerLiteEmbed = () => {
useEffect(() => {
// Inject MailerLite script
(function (w, d, e, u, f, l, n) {
w[f] = w[f] || function () {
(w[f].q = w[f].q || []).push(arguments);
};
l = d.createElement(e);
l.async = 1;
l.src = u;
n = d.getElementsByTagName(e)[0];
n.parentNode.insertBefore(l, n);
})(window, document, 'script', 'https://assets.mailerlite.com/js/universal.js', 'ml');
// Initialize with account ID
window.ml('account', 'YOUR_ACCOUNT_ID');
}, []);
return (
<div className="ml-embedded" data-form="YOUR_FORM_ID"></div>
);
};
export default MailerLiteEmbed;
Problem:
The form loads correctly only on the first page load or after a hard refresh. When navigating between routes using React Router, the component stays mounted and doesn’t reinitialize the MailerLite form.
What I’ve Tried:
-
Passing a unique key to the MailerLiteEmbed component on each route change.
-
Manually removing the and reinserting it (didn’t help).
-
Clearing the .ml-embedded container content manually.
Question:
How can I force the MailerLite script to reinitialize and render the embedded form on every route change in a React app without refreshing the page?
javascript async fetch response wait – blob – recursive download – videos fine images not fine
async function downloadFileAsync(url, filename, ext) {
try {
// Fetch the file as a Blob
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
//console.log("downloadFileAsync:ASYNC response OK for:" + filename + ext);
const blob = await response.blob();
// Create a temporary URL for the Blob
//const fileURL = URL.createObjectURL(blob)
const _type = getTypeByEXT(ext);
const fileURL = URL.createObjectURL(new Blob([blob], { type: _type }));
// Create a link element to trigger the download
const link = document.createElement('a');
link.href = fileURL;
link.download = filename + ext; // Suggests a filename for the download
link.style.display = 'none'; // Hide the link
// Programmatically click the link to initiate download
document.body.appendChild(link);
link.click();
// Remove the link from the body
document.body.removeChild(link); // Clean up the link element
// Revoke the object URL to free up memory
URL.revokeObjectURL(fileURL);
console.log(`${filename}${ext} downloaded successfully.`);
} catch (error) {
console.error(`Error downloading ${filename}${ext}:`, error);
}
}
Line 14 from snippet above:
- const fileURL = URL.createObjectURL(blob);
and - const fileURL = URL.createObjectURL(new Blob([blob], { type: ext }));
Both work fine, if I am downloading a video.
Images never download.
Hence why I used MIME ‘image/jpg’ as type and the new Blob(…)
I used 1) first, always worked for videos, but not images.
I did research and found that I may have to use 2) instead.
Still no luck.
An aside is that I also use timing as well, so I do get the downloads.
setTimeout(() => {
// Recursively call the function with the rest of the array (excluding the first element)
processDownloadArrayWithDelay(arr.slice(1), delay);
}, delay);
and also with
downloadFileAsync(url, fn, ext);
The reason why I use recursion and setTimeout is that Promises wasn’t working. Promises left unfulfilled. Like I said, videos are downloading fine, images never…
pc.oniceconnectionstatechange = () => { state disconnected
i am making the streamign app in react native so when host start the streamign and user user join the stream user able to see host stream but user start the stream host not able see user see that time show the ice state disconnected
in that function
const connectToStreamer = async (streamerId) => {
if (streamerId === socket.id || peerConnections.current[streamerId]) {
console.log(Skipping connection to ${streamerId}: Already connected or self);
return;
}
try {
const pc = new RTCPeerConnection(iceServers);
peerConnections.current[streamerId] = pc;
// Decide direction based on whether you're sending a stream
const isSendingStream = !!localStreamRef.current;
if (isSendingStream) {
localStreamRef.current.getTracks().forEach(track => {
pc.addTrack(track, localStreamRef.current); // send
});
} else {
pc.addTransceiver('video', { direction: 'recvonly' }); // receive
pc.addTransceiver('audio', { direction: 'recvonly' }); // receive
}
pc.ontrack = event => {
if (event.streams[0]) {
console.log(`Received stream from ${streamerId}:`, event.streams[0]);
setRemoteStreams(prev => new Map(prev).set(streamerId, event.streams[0]));
}
};
pc.onicecandidate = event => {
if (event.candidate) {
console.log(`Sending ICE candidate to ${streamerId}`);
socket.emit('ice-candidate', { target: streamerId, candidate: event.candidate });
}
};
pc.oniceconnectionstatechange = () => {
socket.emit('Errorlogs','pc.oniceconnectionstatechange',`ICE connection state changed for ${streamerId}: ${pc.iceConnectionState}`);
console.log(`ICE state for ${streamerId}: ${pc.iceConnectionState}`);
if (pc.iceConnectionState === 'failed' || pc.iceConnectionState === 'disconnected') {
setTimeout(() => {
if (pc.iceConnectionState !== 'connected' && peerConnections.current[streamerId]) {
console.log(`Retrying connection to ${streamerId}`);
connectToStreamer(streamerId);
}
}, 5000);
} else if (pc.iceConnectionState === 'closed') {
delete peerConnections.current[streamerId];
setRemoteStreams(prev => {
const newStreams = new Map(prev);
newStreams.delete(streamerId);
return newStreams;
});
}
};
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
console.log(`Sending offer to ${streamerId}`);
socket.emit('offer', { target: streamerId, sdp: offer });
} catch (err) {
socket.emit('Errorlogs',err)
console.error(`Error connecting to streamer ${streamerId}:`, err);
delete peerConnections.current[streamerId];
}
};
show hte host joined all suers streams and all suers shwo the each other stream in that rooms
How to get eventFilters to trigger on a Firestore onUpdate in Firebase Functions v2 when nested value is null or undefined
I’m trying to trigger a Firebase Cloud Function (v2) only when a specific nested field is added to a Firestore document. That means that the value for that specific field should be undefined in the event.data.before and defined in the event.data.after. According to multiple GPTs, eventFilters should allow this, but it’s not firing as expected.
Let’s pretend my firestore document’s shape is as follows:
{
foo?: {
bar?: string;
}
}
Here’s my firebase function code that I wish would trigger if and only if bar is undefined or foo is undefined (and thus bar is also undefined) before and bar is defined afterwards.
Here’s my attempts:
export const onBarAdded = onDocumentUpdated(
{
document: 'users/{uid}',
eventFilters: {
'oldValue.fields.foo.bar': 'null',
'value.fields.foo.bar.stringValue': '*'
},
export const onBarAdded = onDocumentUpdated(
{
document: "users/{uid}",
eventFilters: {
"data.before.foo.bar": "== null",
"data.after.foo.bar": "!= null",
},
},
I can’t find good documentation either. I find the online documentation sparse and the documentation in the code is a TODO:
// firebase-functions/lib/v2/options.d.ts
// ...
/**
* Additional fields that can be set on any event-handling function.
*/
export interface EventHandlerOptions extends Omit<GlobalOptions, "enforceAppCheck"> {
/** Type of the event. Valid values are TODO */
eventType?: string;
/** TODO */
eventFilters?: Record<string, string | Expression<string>>;
// ...
Feel free to tell me it’s not possible or, maybe, what industry standard is.
Kee Credit loan App Customer. care. Helpline. NUMBER//-8016358379//8945//5776//91 @call nsh
Why am I experiencing a charging issue with a Lenovo IdeaPad 3? [closed]Why am I experiencing a charging issue with a Lenovo IdeaPad 3? [closed]Why am I experiencing a charging issue with a Lenovo IdeaPad 3? [closed]
Why do I get “typeerror: _ is null” on a Javascript line with no underscores?
In a Vue-based JavaScript project that uses the autocomplete-js package, I am hitting the following error when I try to instantiate the autocomplete object:
TypeError: _ is null
However, there is no underscore on the line that shows up in the debugger:
invariant(containerElement.tagName !== 'INPUT', 'The `container` option does not support `input` elements. You need to change the container to a `div`.');
This code is inside a subpackage of autocomplete-js called invariant. The invariant function being called in that package also contains no underscores. Also, the containerElement is not an INPUT type, so that invariant line shouldn’t be doing anything anyway.
How is this error happening, and how might I get past it?
Secure User Registration System in PHP with Input Validation and MySQL Integration [closed]
You are tasked with developing a user registration system using PHP and MySQL. The system must collect a user’s full name, email, password, and contact number. Upon submission, the PHP script should validate the inputs (check if email is valid, password is at least 8 characters, and contact number is numeric and 10 digits). If validation passes, the data must be stored in a MySQL database with the password hashed using password_hash(). Additionally, if the email is already registered, the user should be shown an appropriate error message. Write a PHP script to handle the form submission, input validation, error handling, and successful data insertion into the database. Also, describe how you would prevent SQL injection in this scenario.
Add Prism highlight to peace of codes in a text
Hy guys. I am trying to apply code highlight to a question made by a user and I am not sure how to do.
For a better understanding, when the user post a question similar to stack overflow he can write text and also include code. If he include code it’s added inside the code block like: “`code here´´´ and the adding code part works fine as you can see in this first picture.
the plain text and code snippet
As you can see in the picture, I am able to highlight the code but is add everything inside the highlighted block, and I am able only to specify the first language, in this example the first block of code is HTML so it is applying the highlight, but at the bottom you can see that the java part is not working because I am not able to add two languages to be recognized and the API does not recognized the codes part automatically.
So, my real question is how could I get the code part that is in a text than I can use the tag selected by the user to add as the language and then insert the code found in the text inside my
<pre><code class=language-{{tags[0]}}></code></pre> then print the rest of the plain text and once another peace of code is identified I add again inside <pre><code class=language-{{tags[1]}}></code></pre> until the end of the text.
To make clear this tags new to the language will be selected when the user add a question so I use to highlight with the correct format.
Here is an example that I hard coded only so you can have an idea what I am trying to do, but will not work the way I did because it apply for all question made even if there is no code. I made only so you can understand what I am trying to accomplish.
So in this last picture you can see that I highlighted the code parts only with their respective languages tag.
He is the code but only to visualized how i did but I know it is not the correct way I am new in jinja2 and front end so I appreciate some help from you guys.
{% set question = post.Question.split('```') %}
<div>
<span class="text-dark">
{{question[0]}}
</span>
<pre class="row">
<code class="language-{{tags[0]}}">
{{question[1]}}
</code>
</pre>
<span class="text-dark">
{{question[2]}}
</span>
<pre class="row">
<code class="language-{{tags[2]}}">
{{question[3]}}
</code>
</pre>
</div>
So if anyone has any idea how I could to that in Jinja2 or JS would be very nice, thanks in advance for you all.
Javascript/Ajax question from a confluence macro
I’m trying to debug a confluence macro that stores the value of a checkbox when it is clicked – but can’t seem to get it to even react to user interaction.
Shown below is the code that is being executed on the page (from the dev tools elements tab).
<input class="checkbox dynCheckbox" type="checkbox" name="dynCheckboxone" id="dynCheckboxone" value="checked" resolved="">
<label for="dynCheckboxone">one</label>
//<![CDATA[
AJS.toInit(function(){
AJS.$('.dynCheckboxone').click(function(){
AJS.${d}.ajax({
type: 'GET',
data: {
submit: 'true',
checkbox: AJS.$(this).attr('id'),
value: AJS.$(this).is(':checked') ? 'checked' : ''
},
url: "/location/for/this/stuff",
success: function(data) {
console.log(data);
},
error: function() {
console.log(data);
}
});
});
});
//]]>
This page should initially load with the checkbox unticked, and then when ticked, store the value and if the page is reloaded (due to the nature of confluence) it should remain ticked
Ever need a function to check your random or alike functions for output statistics
Here is a function to check a random function for outputs over ‘n’ no.of iterations, this is a useful function to check you algo or luck based random function this returns the return value of your function over hundred’s or thousand’s of iterations and arrange the results statistically like if you have set of expected outputs it shows like 1 returned 10 times in 100 times execution, 2 returned 20 times in 100 times execution and it also lists two other :
- Unexpected outputs
- Expected but not resulted outputs
- The execution time taken by code using epoch ms differences(Date object)
This function is useful over sectors like data analysis like chatGPT said or any other
Code :
//entering time
enter = new Date().valueOf();
//No need to modify this
function formatMsg(count,obj){
let status=false;
console.log(" In "+count+" times execution of your function, ");
for(a of Object.entries(obj)){
console.log(""+a[0]+" "+a[1]);
status=true;
}
}
function randomCheck(func,values,count=1000)
{
let list=[];
let unknown=[];
let expect=false,expected=[];
for(let i=0;i<count;i++){
list.push(func());
}
let obj = {};
var sum,sum2=0;
for(let j of values){
sum=0;
list.forEach((a)=>{
if(j == a) sum++;
});
obj[""+j+" resulted"] = "" + sum + " times";
sum2+=sum;
}
obj[" Total : "]=sum2;
//for values which are not resulted but expected
values.forEach((a)=>{
if(!list.includes(a)){
expect = true;
expected.push(a);
}
});
if(expect){obj[" Values which are expected to occur but didn't occur over "+count]=" executions";
expected.forEach((a)=>{
obj[" "+a]="";
});
}
//for values which are not expected but resulted
if(sum2!=count){
list.forEach((a)=>{
if(!values.includes(a)){
unknown.push(a)
}
});
obj[" Unknown values occured, "]="";
if(!unknown.length==0){
let temp={};
unknown.forEach(a=>{
if(Object.keys(temp).includes(""+a)){
temp[a]+=1;
}else{
temp[a]=1;
}
});
for(let itr of Object.entries(temp)){
obj[""+itr[0]+" resulted"]=" "+itr[1]+" times";
}
}
}
formatMsg(count,obj);
}
// update myfunc with your function code
myfunc=()=>{
return (Math.floor(Math.random()*100 + 1))
}
//update outputs with your array of outputs
let Outputs = [];
for(let i=1;i<=100;i++)
Outputs.push(i);
randomCheck(myfunc,Outputs,100);
/*This is the function you have to modify with your function and output here i have taken 1 .. 100 as outputs and my func is adjusted random to get 1 to 100 values and you can also specify the no.of iterations through count or by default the count will be 1000 */
//exiting time
exit = new Date().valueOf();
console.log("Overall execution time taken is approximately : "+(exit-enter)+" milliseconds...");
In node.js and npm, how can I prevent environment configs being passed on to required files?
I have a series of JS files in my node application that both perform a function (in this case Cypress tests) and export an object that can be used in other JS files.
I want to send an environment config variable when I run each file, eg
npm run main-tests --test=home-banner
The idea is that I can run a whole suite of tests by running npm run main-tests or just one test that belongs to that suite by running npm run main-tests --test=test-name.
In my main-tests.js file I have something like:
//main-tests.js
const homePageTests = require('./homePage/homePageTests.js');
const prodPageTest = require('./prodPages/prodPageTest.js');
const files = {...homePageTests.files, ...prodPageTest.files}; // <-all .spec.js file paths from subtests
let specs = Object.values(files);
if (process.env.npm_config_test) {
const testSpec = files[process.env.npm_config_test];
if (testSpec) {
specs = testSpec;
}
else {
console.error(`Test specification "${process.env.npm_config_test}" not found.`);
process.exit(1);
}
}
// do the tests.....
Then in my ‘sub-test’ JS files, eg homePageTests.js I have something similar:
//homePageTests.js
const files = {
"home-banner":"./homeBanner.spec.js",
"home-menu":"./homeMenu.spec.js",
};
let specs = Object.values(files);
if (process.env.npm_config_test) {
const testSpec = files[process.env.npm_config_test];
if (testSpec) {
specs = testSpec;
}
else {
console.error(`Test specification "${process.env.npm_config_test}" not found.`);
process.exit(1);
}
}
// do the tests.....
module.exports = {files};
Now, this all works fine, but for one thing: if I run npm run main-tests --test=home-banner the home-page config variable is also sent to prodPageTest.js and nothing gets run as home-page does not exist in the files object in prodPageTest.js and the conditional fails, hitting process.exit(1);
Obviously, this is something to do with the way I am ‘requiring’ the files (const prodPageTest = require('./prodPages/prodPageTest.js');).
Is there a way I can just include the files object from the subtest files, without including or running the rest of the code on that file?
Cannot mute microphone on Twilio call
I’m implementing a screen to make calls from the Twilio service:
At the moment, calls connect and I can hang up. The problem is that I can’t mute the microphone or the call (the button does nothing), and the “Status (Estado)” text only updates from “Disconnected (Desconectado)” to “On Call (En llamada.)”. Here’s the front-end code:
<?php
include 'php/conexion.php';
/**Validacion de Session**/
session_start();
if(!isset($_SESSION['user']['ID_Usuario'])){
header("Location: ../");
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<?php include_once 'include/head.php';?>
<!--<script src="js/funciones.js"></script>-->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="js/toast.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.umd.min.js"></script>
<!-- <script src="https://media.twiliocdn.com/sdk/js/voice/releases/2.13.0/twilio-voice.min.js"></script> -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
<title>Llamadas en linea</title>
<style>
#contenido_cliente table td {
vertical-align: top;
padding: 0.25rem 0.5rem;
}
#contenido_cliente td {
padding: 4px 8px;
vertical-align: top;
}
</style>
</head>
<?php include_once 'include/navbar.php'; ?>
<!-- Modal de espera -->
<div class="container-fluid">
<div class="row">
<?php include_once 'include/slide.php'; ?>
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<div
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2 fw-bold">
Llamadas en linea
</h1>
</div>
<div class="card mt-4">
<div class="card-header bg-info text-white">
<h5 class="mb-0">Llamadas Telefónicas</h5>
</div>
<div class="card-body">
<div class="row">
<!-- COLUMNA IZQUIERDA: Teléfono + Dialpad -->
<div class="col-md-4">
<!-- Input + Botones -->
<div class="mb-3">
<label for="numero_llamar" class="form-label">Número a llamar</label>
<input type="text" class="form-control" id="numero_llamar" placeholder="(000) 000 0000">
</div>
<div class="mb-3">
<button class="btn btn-success me-2" id="llamar" onclick="realizarLlamada()">Llamar</button>
<button class="btn btn-danger me-2" id="colgar" onclick="colgarLlamada()">Colgar</button>
<button class="btn btn-warning me-2" id="mute" onclick="toggleMute()">Silenciar</button>
</div>
<div class="mb-3">
<strong>Estado:</strong> <span id="estado_llamada">Desconectado</span><br>
<strong>Duración:</strong> <span id="duracion_llamada">00:00</span>
</div>
<!-- Dialpad -->
<div class="border rounded p-3 mt-4">
<div class="col-md-12 text-center">
<div class="d-grid gap-2 mb-2" style="max-width: 200px; margin: auto;">
<div class="row">
<div class="col"><button class="btn btn-outline-dark w-100" onclick="agregarNumero('1')">1</button></div>
<div class="col"><button class="btn btn-outline-dark w-100" onclick="agregarNumero('2')">2</button></div>
<div class="col"><button class="btn btn-outline-dark w-100" onclick="agregarNumero('3')">3</button></div>
</div>
<div class="row">
<div class="col"><button class="btn btn-outline-dark w-100" onclick="agregarNumero('4')">4</button></div>
<div class="col"><button class="btn btn-outline-dark w-100" onclick="agregarNumero('5')">5</button></div>
<div class="col"><button class="btn btn-outline-dark w-100" onclick="agregarNumero('6')">6</button></div>
</div>
<div class="row">
<div class="col"><button class="btn btn-outline-dark w-100" onclick="agregarNumero('7')">7</button></div>
<div class="col"><button class="btn btn-outline-dark w-100" onclick="agregarNumero('8')">8</button></div>
<div class="col"><button class="btn btn-outline-dark w-100" onclick="agregarNumero('9')">9</button></div>
</div>
<div class="row">
<div class="col"><button class="btn btn-outline-dark w-100" onclick="agregarNumero('*')">*</button></div>
<div class="col"><button class="btn btn-outline-dark w-100" onclick="agregarNumero('0')">0</button></div>
<div class="col"><button class="btn btn-outline-dark w-100" onclick="agregarNumero('#')">#</button></div>
</div>
<button class="btn btn-secondary mt-2" onclick="borrarUltimoDigito()">Borrar último</button>
<button class="btn btn-danger mt-1" onclick="borrarTodoNumero()">Borrar todo</button>
</div>
</div>
</div>
</div>
<!-- COLUMNA CENTRO: Agenda -->
<div class="col-md-4">
<div class="mb-2">
<input type="text" class="form-control" id="filtro_contactos" placeholder="Buscar contacto por nombre..." onkeyup="filtrarContactos()">
</div>
<div style="max-height: 560px; overflow-y: auto;">
<table class="table table-bordered table-hover mb-0">
<thead class="table-light sticky-top bg-white" style="top: 0; z-index: 1;">
<tr>
<th>Nombre</th>
<th>Celular</th>
<th>Estatus numero</th>
</tr>
</thead>
<tbody id="tabla_contactos">
<!-- Contactos aquí -->
</tbody>
</table>
</div>
</div>
<!-- COLUMNA DERECHA: Información del Cliente -->
<div class="col-md-4">
<div id="detalle_cliente" class="border rounded p-3 bg-light">
<h5 class="mb-3">Información del Cliente</h5>
<div id="contenido_cliente" style="max-height: 560px; overflow-y: auto;" class="text-muted">Seleccione un contacto para ver los detalles.</div>
</div>
</div>
</div>
</div>
</div>
<?php include_once 'include/footer.php';?>
</main>
</div>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/@twilio/[email protected]/dist/twilio.min.js"></script>
<script src="../js/llamadas.js"></script>
</html>
Here’s the script:
let device;
let currentConnection;
let isMuted = false;
let callStartTime;
let durationInterval;
function actualizarEstado(estado) {
document.getElementById("estado_llamada").textContent = estado;
}
function actualizarDuracion() {
if (!callStartTime) return;
const now = new Date();
const diff = Math.floor((now - callStartTime) / 1000);
const minutes = String(Math.floor(diff / 60)).padStart(2, '0');
const seconds = String(diff % 60).padStart(2, '0');
document.getElementById("duracion_llamada").textContent = `${minutes}:${seconds}`;
}
let listaContactos = [];
function cargarContactos() {
fetch('php/llamadas.php?accion=contactos')
.then(res => res.json())
.then(clientes => {
listaContactos = clientes; // Guardamos para filtrar después
renderizarContactos(clientes);
})
.catch(err => {
console.error("Error al cargar contactos:", err);
});
}
function renderizarContactos(clientes) {
const tabla = document.getElementById("tabla_contactos");
tabla.innerHTML = "";
const agrupado = {};
// Agrupar por ID_Cliente
clientes.forEach(c => {
if (!agrupado[c.ID_Cliente]) {
agrupado[c.ID_Cliente] = [];
}
agrupado[c.ID_Cliente].push(c);
});
// Dibujar por cada grupo
Object.values(agrupado).forEach(grupo => {
grupo.forEach(c => {
const fila = document.createElement("tr");
fila.style.cursor = "pointer";
let numero = '';
let etiqueta = '';
if (c.Estatus === "1") {
numero = c.Telefono;
etiqueta = 'Actual';
} else if (c.Estatus === "0") {
numero = c.Telefono;
etiqueta = 'Anterior';
} else {
numero = c.Celular;
etiqueta = ''; // Sin etiqueta visible
}
// Acción al hacer clic
fila.addEventListener("click", () => seleccionarNumero(numero));
fila.innerHTML = `
<td>${c.NombreCompleto}</td>
<td>${numero}</td>
<td>${etiqueta}</td>
`;
tabla.appendChild(fila);
});
});
if (tabla.innerHTML.trim() === "") {
const fila = document.createElement("tr");
fila.innerHTML = `<td colspan="3" class="text-center">Información del cliente no disponible</td>`;
tabla.appendChild(fila);
}
}
function filtrarContactos() {
const filtro = document.getElementById("filtro_contactos").value.toLowerCase();
const filtrados = listaContactos.filter(c =>
c.NombreCompleto.toLowerCase().includes(filtro)
);
renderizarContactos(filtrados);
}
function seleccionarNumero(numero) {
document.getElementById("numero_llamar").value = numero;
const contenedor = document.getElementById("contenido_cliente");
contenedor.innerHTML = `<div class="text-info">Cargando información del cliente...</div>`;
fetch(`php/llamadas.php?accion=reporte_contratos&telefono=${numero}`)
.then(res => res.json())
.then(data => {
if (!Array.isArray(data) || data.length === 0) {
contenedor.innerHTML = `<div class="text-warning">Información del cliente no disponible.</div>`;
return;
}
contenedor.innerHTML = ""; // Limpiar contenido
// Si hay más de un contrato, usar pestañas
if (data.length > 1) {
const navTabs = document.createElement("ul");
navTabs.classList.add("nav", "nav-tabs");
navTabs.role = "tablist";
const tabContent = document.createElement("div");
tabContent.classList.add("tab-content");
data.forEach((contrato, index) => {
const contratoId = contrato.ID_Contrato || `Contrato ${index + 1}`;
const tabId = `contrato-${index}`;
// Crear pestaña
const tab = document.createElement("li");
tab.classList.add("nav-item");
tab.innerHTML = `
<a class="nav-link ${index === 0 ? 'active' : ''}" data-bs-toggle="tab" href="#${tabId}" role="tab">${contratoId}</a>
`;
navTabs.appendChild(tab);
// Crear contenido de la pestaña
const tabPane = document.createElement("div");
tabPane.classList.add("tab-pane", "fade");
if (index === 0) {
tabPane.classList.add("show", "active");
}
tabPane.id = tabId;
tabPane.role = "tabpanel";
tabPane.appendChild(crearTablaCliente(contrato));
tabContent.appendChild(tabPane);
});
contenedor.appendChild(navTabs);
contenedor.appendChild(tabContent);
} else {
// Solo un contrato
contenedor.appendChild(crearTablaCliente(data[0]));
}
})
.catch(err => {
console.error("Error al obtener reporte del cliente:", err);
contenedor.innerHTML = `<div class="text-danger">Ocurrió un error al obtener la información del cliente.</div>`;
});
}
function crearTablaCliente(data) {
const tabla = document.createElement("table");
tabla.classList.add("table", "table-bordered", "table-sm", "mb-0", "w-100");
tabla.style.tableLayout = "fixed";
for (const [key, value] of Object.entries(data)) {
const fila = document.createElement("tr");
const celdaClave = document.createElement("td");
celdaClave.classList.add("fw-bold", "text-end", "bg-light");
celdaClave.style.width = "45%"; // Aumentamos para dejar más espacio
celdaClave.style.paddingRight = "1rem";
celdaClave.style.whiteSpace = "nowrap";
celdaClave.textContent = key;
const celdaValor = document.createElement("td");
celdaValor.classList.add("text-start");
celdaValor.style.width = "55%";
celdaValor.style.paddingLeft = "1rem";
celdaValor.textContent = formatearValor(key, value);
fila.appendChild(celdaClave);
fila.appendChild(celdaValor);
tabla.appendChild(fila);
}
return tabla;
}
function formatearValor(key, valor) {
if (!valor) return "";
// Fechas (YYYY-MM-DD)
if (/fecha/i.test(key) && /^d{4}-d{2}-d{2}/.test(valor)) {
const fecha = new Date(valor);
if (!isNaN(fecha)) {
return fecha.toLocaleDateString("es-MX", {
day: '2-digit',
month: 'long',
year: 'numeric'
}).replace(/^(d+)sdes(w+)sdes(d{4})$/, (_, d, m, y) => `${d}-${m.charAt(0).toUpperCase() + m.slice(1)}-${y}`);
}
}
// Monto / dinero
if (/monto|precio|cuota|total|costo|pago|importe/i.test(key)) {
const num = parseFloat(valor);
if (!isNaN(num)) {
return num.toLocaleString('es-MX', {
style: 'currency',
currency: 'MXN',
minimumFractionDigits: 2
});
}
}
return valor;
}
function inicializarTwilio() {
fetch('{Servidor}/Twilio/example/token.php')
.then(response => response.json())
.then(data => {
device = new Twilio.Device(data.token, { debug: true });
device.on('ready', () => {
actualizarEstado('Listo para llamar');
});
device.on('error', (error) => {
actualizarEstado(`Error: ${error.message}`);
});
device.on('connect', (conn) => {
currentConnection = conn;
conn.on('disconnect', () => currentConnection = null);
});
device.on('disconnect', () => currentConnection = null);
})
.catch(err => {
actualizarEstado('Error al obtener token');
console.error(err);
});
}
function realizarLlamada() {
const numero = document.getElementById("numero_llamar").value;
if (!device) {
alert("Dispositivo aún no inicializado.");
return;
}
fetch('{Servidor}/Twilio/example/voice.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `To=+52${encodeURIComponent(numero)}`
}).then(() => {
device.connect();
callStartTime = new Date();
durationInterval = setInterval(actualizarDuracion, 1000);
actualizarEstado('En llamada');
document.getElementById("llamar").disabled = true;
}).catch(err => {
console.error("Error al enviar número al backend:", err);
alert("No se pudo iniciar la llamada.");
});
}
function colgarLlamada() {
if (device) device.disconnectAll();
callStartTime = null;
clearInterval(durationInterval);
document.getElementById("duracion_llamada").textContent = '00:00';
actualizarEstado('Desconectado');
document.getElementById("llamar").disabled = false;
}
function toggleMute() {
if (!currentConnection) return;
const localStream = currentConnection.mediaStream?.stream;
if (localStream) {
const audioTrack = localStream.getAudioTracks()[0];
if (audioTrack) {
isMuted = !isMuted;
audioTrack.enabled = !isMuted;
actualizarEstado(isMuted ? "Silenciado (micrófono apagado)" : "En llamada");
}
}
}
function agregarNumero(digito) {
const input = document.getElementById("numero_llamar");
input.value += digito;
}
function borrarUltimoDigito() {
const input = document.getElementById("numero_llamar");
input.value = input.value.slice(0, -1);
}
function llenarNumero(numero) {
document.getElementById("numero_llamar").value = numero;
}
function borrarTodoNumero() {
document.getElementById("numero_llamar").value = '';
}
window.addEventListener('DOMContentLoaded', () => {
inicializarTwilio();
});
document.addEventListener("DOMContentLoaded", () => {
cargarContactos();
});
In the Twilio API section, I have two files configured. The first is “token.php,” where the connection is configured:
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
header('Access-Control-Allow-Headers: Content-Type');
require(__DIR__.'/../src/Twilio/autoload.php');
// Cargar credenciales desde la ruta específica
$config = require 'C:\conf_twilio\config.php';
use TwilioJwtAccessToken;
use TwilioJwtGrantsVoiceGrant;
$identity = 'Isilisi'; // Puede ser dinámico (ej: $_SESSION['user_id'])
try {
$token = new AccessToken(
$config['TWILIO_ACCOUNT_SID'],
$config['TWILIO_API_KEY'],
$config['TWILIO_API_SECRET'],
3600,
$identity
);
$voiceGrant = new VoiceGrant();
$voiceGrant->setOutgoingApplicationSid($config['TWIML_APP_SID']);
$voiceGrant->setIncomingAllow(true);
$token->addGrant($voiceGrant);
echo json_encode([
'identity' => $identity,
'token' => $token->toJWT(),
'callerId' => $config['TWILIO_CALLER_ID']
]);
} catch (Exception $e) {
header('HTTP/1.1 500 Internal Server Error');
echo json_encode(['error' => 'Error al generar token: ' . $e->getMessage()]);
}
?>
And finally, “voice.php,” where the request to connect the call is made. Both files exist at the same API level:
<?php
header('Access-Control-Allow-Origin: *'); // Permite peticiones desde cualquier origen
header('Access-Control-Allow-Methods: GET, POST');
header('Access-Control-Allow-Headers: Content-Type');
// voice.php
header('Content-Type: text/xml');
$to = $_POST['To'] ?? null;
if ($to) {
// Guardar temporalmente en archivo
file_put_contents(__DIR__ . '/numero.txt', $to);
exit; // Terminar si solo estamos guardando
}
// Si no vino por POST, entonces Twilio está solicitando el TwiML
$numero = @file_get_contents(__DIR__ . '/numero.txt');
echo '<?xml version="1.0" encoding="UTF-8"?>';
?>
<Response>
<?php if ($numero): ?>
<Dial callerId="+523341711661">
<Number><?php echo htmlspecialchars($numero); ?></Number>
</Dial>
<?php else: ?>
<Say>No se proporcionó un número para llamar.</Say>
<?php endif; ?>
</Response>
The main thing I need to fix is the microphone not muting, but I’m open to any other code improvements you might suggest. Thanks a bunch for your help!
babel-plugin-require-context-hook error: __requireContext is not defined
I was trying to use babel-plugin-require-context-hook to make jest to digest require.context()
, and, as a result of that, my tests finally started running smoothly.
At the same time though, my expo app itself started failing with the below error:
Metro error: __requireContext is not defined
Call Stack
factory
node_modules/expo-router/_ctx-html.js
loadModuleImplementation
node_modules/metro-runtime/src/polyfills/require.js
guardedLoadModule
node_modules/metro-runtime/src/polyfills/require.js
require
node_modules/metro-runtime/src/polyfills/require.js
factory
node_modules/expo-router/build/static/getRootComponent.js
loadModuleImplementation
node_modules/metro-runtime/src/polyfills/require.js
guardedLoadModule
node_modules/metro-runtime/src/polyfills/require.js
require
node_modules/metro-runtime/src/polyfills/require.js
factory
node_modules/expo-router/build/static/renderStaticContent.js
loadModuleImplementation
node_modules/metro-runtime/src/polyfills/require.js
I have this line in my source js file where require.context() is:
require('babel-plugin-require-context-hook/register')()
Making it conditional, doesn’t help either:
if (process.env.NODE_ENV === 'test') {
require('babel-plugin-require-context-hook/register')();
}
Would appreciate any help.
babel.config.js:
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
"plugins": [
[
"@babel/plugin-proposal-optional-chaining-assign",
{
"version": "2023-07"
}
],
["require-context-hook"]
]
};
};
package.json
"dependencies": {
"babel-plugin-require-context-hook": "^1.0.0",
...
Why does “Rendered more hooks than during the previous render” occur when generating components inline in the return statement?
I have a component that renders 100 instances of another component which internally uses hooks.
When I use this logic inline inside the return statement, like this:
export const MyComponent: StoryFn<typeof ParentComponent> = () => (
<someAnotherComponent>
{Array.from({ length: 100 }).map((_, index) => (
<currentComponent.Reaction
counter={index}
mine={index % 3 !== 0}
/>
))}
</someAnotherComponent>
);
gives this:
Rendered more hooks than during the previous render.
But when I add key={index} it solves the issue:
export const MyComponent: StoryFn<typeof ParentComponent> = () => (
<someAnotherComponent>
{Array.from({ length: 100 }).map((_, index) => (
<currentComponent.Reaction
key={index}
counter={index}
mine={index % 3 !== 0}
/>
))}
</someAnotherComponent>
);
This doesn’t give the error. Can someone explain me why?