Connecting NodeJS with React to create notification system - javascript

Good afternoon, I'm pretty new to React since mostly was doing NodeJS stuff before and I come with a problem that I currently don't know how to connect my backend with frontend parts together.
GOAL: I wan't to create a price alert which is going to send me a email notification.
I have a Dropdown Where I can write a Symbol Name and Price and Set it up. I wan't to create an alert when Price is getting bigger than writed in DropDown. F
Example: I Open a DropDown - write ADABTC in first textarea 500 click Set button and
I already have email notification script ready I was using - NodeMailer Package
I'm also using Binance API to receive the Price
Here is my ReactJS Code part.(Event.jsx)
import React, { useState, useEffect } from "react";
const assets = [
"ADABTC",
"AIONBTC",
"ALGOBTC",
"ARDRBTC",
"KAVABTC",
"ETHBTC",
"ETCBTC"
];
export default function App() {
const [queries, setQueries] = useState([]);
const [symbol, setSymbol] = useState("");
const [price, setPrice] = useState("");
const [dropdown, setDropdown] = useState([]);
const onChangeSymbol = e => {
setSymbol(e.target.value);
};
const onChangePrice = e => {
setPrice(e.target.value);
};
var today = new Date();
var date =
today.getFullYear() + "-" + (today.getMonth() + 1) + "-" + today.getDate();
var time =
today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
var dateTime = date + " " + time;
const onClick = () => {
if (symbol !== "" && price !== "") {
setQueries(queries => {
return [
...queries,
`${symbol}` + " " + `${price}` + " " + "(" + dateTime+")"
];
});
setSymbol("");
setPrice("");
}
};
useEffect(() => {
if (symbol !== "" && symbol !== assets.find(rec => rec === symbol)) {
setDropdown(assets.filter(rec => rec.indexOf(symbol) > -1));
} else {
setDropdown([]);
}
}, [symbol]);
return (
<div id="DropDownArea" className="DropDownField">
<div id="PriceAlertHistory">
<h6>Price Alert History</h6>
</div>
<ul>
{queries.map(query => (
<li>{query}</li>
))}
</ul>
<input
type="text"
placeholder="Symbol"
value={symbol}
onChange={onChangeSymbol}
/>
<input
type="number"
placeholder="Price"
value={price}
onChange={onChangePrice}
/>
<button type="submit" onClick={onClick}>
Set
</button>
<ul>
{dropdown.length !== 0
? dropdown.map(asset => {
return (
<li
onClick={() => {
setSymbol(asset);
setDropdown([]);
}}
>
{asset}
</li>
);
})
: false}
</ul>
</div>
);
}
Here is my NodeJS Code which is receiving data from Binance API.(Databases.js)
const getBTCData = async symbol => {
let data = await fetch(`https://api.binance.com/api/v3/klines?symbol=${symbol}&interval=1d&limit=1`).then(res => res.json());
const btcusdtdata = data.map(d => {
return {
Open: parseFloat(d[1]),
}
});
let volume = await fetch(`https://api.binance.com/api/v3/ticker/24hr?symbol=${symbol}`).then(res => res.json());
const volumedata = data.map(d => {
return {
Volume: parseFloat(volume.quoteVolume)
}
});
console.log(btcusdtdata[0]);
};
This is NodeMailer script(Email.js)
require('dotenv').config()
"use strict"; // This is ES6 specific. Help's to run code faster(IMPORTANT FOR NOTIFICATION SYSTEM)
const nodemailer = require("nodemailer");
async function main() {
let transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true, // true for 465, false for other ports
auth: {
user: process.env.DB_USER, // OUR ALARM EMAIL
pass: process.env.DB_PASS, // OUR ALARM PASSWORD
},
});
let info = await transporter.sendMail({
from: process.env.DB_USER, // sender address
to: process.env.DB_RECEIVER, // list of receivers
subject: `Asset is UP`, // Subject line
text: "ASSET IS UP", // plain text body
});
console.log("Message sent: %s", info.messageId);
}
main().catch(console.error);

Related

Serum anchor initializeConfig() returns A raw constraint was violated

i am trying to create and initialize candy machine config account from web application but transaction failed with following error:
Are there some special need why every tutorial is based on node,js and just minting is on web?
Here is code snippet with following steps:
Create authority account and airdrop some lamports
Create candy machine config account
Initialize candy machine config account (fail)
Log:
Transaction simulation failed: Error processing Instruction 1: custom program error: 0x8f
Program 11111111111111111111111111111111 invoke [1]
Program 11111111111111111111111111111111 success
Program cndyAnrLdpjq1Ssp1z8xxDsB8dxe7u4HL5Nxi2K5WXZ invoke [1]
Program log: Custom program error: 0x8f
Program cndyAnrLdpjq1Ssp1z8xxDsB8dxe7u4HL5Nxi2K5WXZ consumed 6437 of 200000 compute units
Program cndyAnrLdpjq1Ssp1z8xxDsB8dxe7u4HL5Nxi2K5WXZ failed: custom program error: 0x8f
import { Keypair, PublicKey } from "#solana/web3.js"
import { useWorkspace } from "#/composables/useWorkspace"
import { SYSVAR_RENT_PUBKEY, CONFIG_ARRAY_START, CONFIG_LINE_SIZE, CANDY_MACHINE_PROGRAM_ID, SYSTEM_PROGRAM_ID } from "./constants"
import BN from "bn.js"
import * as anchor from '#project-serum/anchor';
let anchorWallet = null;
let solanaConnection = null;
let candyMachineProgram = null;
export const init = () => {
const { wallet, connection, candyMachine } = useWorkspace();
anchorWallet = wallet;
solanaConnection = connection;
candyMachineProgram = candyMachine;
}
const candyMachineAccountSpace = (numLines) => {
return CONFIG_ARRAY_START + 4 + numLines * CONFIG_LINE_SIZE + 4 + Math.ceil(numLines / 8.0)
};
export const mintNft = async (metadata) => {
let candyMachineConfig = Keypair.generate();
let authority = Keypair.generate();
await airdrop(authority.publicKey, 1);
await initializeCandyMachineAccount(metadata, candyMachineConfig, authority);
}
const createAccount = async (account) => {
let minimumAmountForRent = await solanaConnection.getMinimumBalanceForRentExemption(
candyMachineAccountSpace(1),
);
console.log("Account space need: " + candyMachineAccountSpace(1))
console.log("Minimum rent for config account: " + minimumAmountForRent);
console.log("From account: " + anchorWallet.value.publicKey);
console.log("To account: " + account.publicKey);
return anchor.web3.SystemProgram.createAccount({
fromPubkey: anchorWallet.value.publicKey,
newAccountPubkey: account.publicKey,
space: candyMachineAccountSpace(1),
lamports: minimumAmountForRent,
programId: new PublicKey(CANDY_MACHINE_PROGRAM_ID),
});
}
const airdrop = async (address, amount) => {
return await solanaConnection.requestAirdrop(address, amount);
}
const initializeCandyMachineAccount = async (metadata, candyMachineConfig, authority) => {
let cmuuid = candyMachineConfig.publicKey.toBase58().slice(0, 6) //;
console.log("cmuuid: " + cmuuid);
console.log("Symbol: " + metadata.symbol);
console.log("Seller fee: " + metadata.sellerFeeBasisPoints);
console.log("Rent: " + new PublicKey(SYSVAR_RENT_PUBKEY));
console.log("Wallet: ");
candyMachineProgram.value.rpc.initializeConfig(
{
uuid: cmuuid,
maxNumberOfLines: new BN(1),
symbol: metadata.symbol,
sellerFeeBasis: metadata.sellerFeeBasisPoints,
isMutable: true,
maxSupply: new BN(1),
retainAuthority: true,
creators: [
{
address: anchorWallet.value.publicKey,
share: 100
}
]
},
{
accounts: {
config: candyMachineConfig.publicKey,
authority: authority.publicKey,
payer: authority.publicKey,
systemProgram: SYSTEM_PROGRAM_ID,
rent: new PublicKey(SYSVAR_RENT_PUBKEY),
},
preInstructions: [
//await createAccount(authority), // created by airdrop
await createAccount(candyMachineConfig)
],
signers: [candyMachineConfig, authority],
}
)
}

Set parameters from UI to SQL editor, React/JS

the idea of this stuff is that user can add parameters to the SQL editor from two inputs, one for the parameter itself and the other one for its value
and if user writes in SQL editor, automatically adds inputs that are the parameter and the value.
From SQL editor to inputs it works fine, cause I'm sending a regex. The trouble is located where I try to send the inputs to SQL Editor. I think is more like a logic problem, but I still don't find the solution.
The point is that by adding one parameter from the input, it adds more than required, even though I always clean it before adding, but apparently that doesn't affect.
This is the code
import React, { useEffect, useState} from 'react';
import SQLContainerInput from '../Components/SQLContainerInput';
........
function arrayParamsExec(stringSql) {
const paramsQueryText = [...stringSql.matchAll(/{{(\w+)}}/ig)];
const newArray = paramsQueryText.map(item => item[1]);
return newArray;
}
const initalStateCurrentChartInfo = {
SQLQuery: '',
dataType: 'TABLE',
columns: [],
};
const CustomSQLEditor = ({
fromQuery, // del Redux
}) = {
const [currentChartInfo, setCurrentChartInfo] = useState(
initalStateCurrentChartInfo,
);
const [params, setParams] = useState([]);
const [textSql, setTextSql] = useState('');
useEffect(() => {
....
let sqlDefaultString = '';
sqlDefaultString = fromQuery.internal_name
? `SELECT * FROM \`${fromQuery.internal_name}__${fromQuery.items[0]}\` LIMIT 20`
: '';
setCurrentChartInfo({
...currentChartInfo,
SQLQuery: `${sqlQuery}`,
});
},[fromQuery]);
// ------------------params---------------------
const addProperty = () => {
setParams([
...params,
{ name: '', value: '' },
]);
};
const updateProperty = (event, index, key) => {
const newProperties = [...params];
newProperties[index][key] = event?.target?.value;
// agregar parĂ¡metros al editor SQL
let sqlParams = textSql;
if (key === 'name') {
params.forEach(p => {
if (p.name && /^\w+$/i.test(p.name)) {
sqlParams += `{{${p.name}}}`;
}
});
setTextSql('');
setTextSql(`${sqlParams}`);
}
setParams(newProperties);
};
const deleteProperty = index => {
const newProperties = [...params];
newProperties.splice(index, 1);
const newTextSQL = replaceAll(textSql, `{{${params[index]?.name}}}`, '');
setTextSql(newTextSQL);
setParams(newProperties);
};
// ------------------end params---------------------
const changeTextEditor = (valueEditor) => {
const namesParams = arrayParamsExec(valueEditor);
const newProperties = namesParams.map((pName) => {
const valueNew = params.find(p => p.name === pName);
return {name: pName, value: valueNew?.value || ''};
});
setParams(newProperties);
setTextSql(valueEditor);
}
return (
<>
<SQLContainerInput
button={{
onClick: handleSubmit,
}}
input={{
value: `${textSql}\n`,
onChange: changeTextEditor,
}}
/>
<DymanicKeyValueInputInput
properties={params}
updateProperty={updateProperty}
deleteProperty={deleteProperty}
addProperty={addProperty}
/>
</>
);
}
Then, I tried as a solution set another value which is textSql, that takes care of placing the concatenated string, and the string coming from redux is fromQuery. The redux string is set in the sqlParams variable, when is added concatenates with the params and then, I clean textSql
......
const updateProperty = (event, index, key) => {
const newProperties = [...params];
newProperties[index][key] = event?.target?.value;
// agregar parĂ¡metros al editor SQL
let sqlParams = currentChartInfo.SQLQuery;
if (key === 'name') {
params.forEach(p => {
if (p.name && /^\w+$/i.test(p.name)) {
sqlParams += `{{${p.name}}}`;
}
});
setTextSql('');
setTextSql(`${sqlParams}`);
}
setParams(newProperties);
};
......
The trouble in there is that if I directly write from SQL editor, it resets the whole string, I mean, everything that has been written but there it works when I put the params and it's not repeated. I don't find a way to do that, so I'm sorry for the ignorance if I'm doing something wrong.
For example, when I write a large SQL text.
When a parameter is added from the input, it resets.
Video with the error demo: https://www.youtube.com/watch?v=rQBPOPyeXlI
Repo's url: https://gitlab.com/albert925/parametrosui-a-editor-sql
try this:
import { useState, useEffect } from 'react';
import DymanicKeyValueInputInput from './components/DymanicKeyValueInputInput';
import SQLContainerInput from './components/SQLContainerInput';
import { fromQuery } from './bd/data';
import { replaceAll, arrayParamsExec } from './utils/strings';
import { Content, ContentAddButton } from './stylesApp';
const initalStateCurrentChartInfo = {
SQLQuery: '',
columns: [],
};
function App() {
const [currentChartInfo, setCurrentChartInfo] = useState(
initalStateCurrentChartInfo,
);
const [params, setParams] = useState([]);
const [textSql, setTextSql] = useState('');
const [endSql, setendSql] = useState('');
useEffect(() => {
let sqlQuery = '';
sqlQuery = fromQuery.internal_name
? `SELECT * FROM \`${fromQuery.internal_name}__${fromQuery.items[0]}\` LIMIT 20`
: '';
setCurrentChartInfo({
...currentChartInfo,
SQLQuery: `${sqlQuery}`,
});
setTextSql(sqlQuery);
},[fromQuery]);
useEffect(()=>{
const endSql = replaceAll(textSql, '\n', '');
setendSql('')
setendSql(endSql);
},[textSql]);
const cleanSQLString = (string) => {
//corto la cadena en {{
//reemplazo {{ por "" en cada item
//devuelvo el array con los reemplazos
return string.split("{{").map(item=>item.replace("}}",""));
}
// ------------------params---------------------
const addProperty = () => {
setParams([
...params,
{ name: '', value: '' },
]);
};
const updateProperty = (event, index, key) => {
const newProperties = [...params];
newProperties[index][key] = event?.target?.value;
let currentSQL = cleanSQLString(endSql);
// clean the string so that then add parameters to sql
if (key === 'name' && /^\w+$/i.test(event?.target?.value)) {
let sqlParams;
if(currentSQL.length > 1){
sqlParams = `${currentSQL[0]}`;
}else{
sqlParams = `${endSql}`;
}
params.forEach(p => {
if (p.name && /^\w+$/i.test(p.name)) {
sqlParams += `{{${p.name}}}`;
}
});
setTextSql('');
setTextSql(`${sqlParams}`);
/*if(currentSQL.length > 1){
sqlParams = `${currentSQL[0]}`;
}else{
sqlParams = `${endSql}`;
}
console.log(sqlParams)
// get parameter positions by regular expression
const searchParamRegedix = arrayParamsExec(endSql);
console.log(searchParamRegedix, 'searchParamRegedix')
if (searchParamRegedix.paramsQueryText.length > 0) {
console.log(111)
// get the position
const searchPosition = searchParamRegedix.paramsQueryText[index]?.index;
// remove the old word in braces
const deleteOldParam = replaceAll(sqlParams, `${searchParamRegedix.list[index]}`, '');
// the remaining string is removed from the obtained position
const deleteFirtsLetter = deleteOldParam.substr(searchPosition + 2);
// the string is removed from the beginning to the position obtained
const deleteRemaining = deleteOldParam.substr(0, searchPosition + 2)
// the string of the beginning and end is combined with its parameter
const contantString = deleteRemaining + event?.target?.value + deleteFirtsLetter;
// the entire string is overwritten in the state
setTextSql(contantString);
}
else{
params.forEach(p => {
if (p.name && /^\w+$/i.test(p.name)) {
sqlParams += `{{${p.name}}}`;
}
});
setTextSql('');
setTextSql(`${sqlParams}`);
}*/
}
setParams(newProperties);
};
const deleteProperty = index => {
const newProperties = [...params];
newProperties.splice(index, 1);
const newTextSQL = replaceAll(textSql, `{{${params[index]?.name}}}`, '');
setTextSql(newTextSQL);
setParams(newProperties);
};
// ------------------end params---------------------
const changeTextEditor = (valueEditor) => {
const namesParams = arrayParamsExec(valueEditor);
// keep the value with the parameter with respect to adding a new parameter in the sql editor,
// but if the parameter word is changed, the value is deleted
const newProperties = namesParams.list.map((pName) => {
const valueNew = params.find(p => p.name === pName);
return {name: pName, value: valueNew?.value || ''};
});
setParams(newProperties);
setTextSql(valueEditor);
}
return (
<Content>
<div>
<SQLContainerInput
input={{
value: `${endSql}\n`,
onChange: changeTextEditor,
}}
/>
</div>
<div>
<h2>PARAMS</h2>
<ContentAddButton>
<DymanicKeyValueInputInput
properties={params}
updateProperty={updateProperty}
deleteProperty={deleteProperty}
addProperty={addProperty}
id="sqlapiparams"
className="isMultipleInpustSelects"
tilesColumns={["", ""]}
inputsPlaceholder={["My_Parameter", "Type params value"]}
isIconTransfer
isIconError
/>
</ContentAddButton>
</div>
</Content>
);
}
export default App;

Email sending via cron, pubsub

I get stuck with creating a new trigger to send only one email the next day, after changing the status in orders to 'DELIVERED', that is, create a query in the sendStatusEmails trigger that retrieves orders by status and id. Who can help with this problem or give an example?
Here's cron dailyEmails.js
module.exports = functions.pubsub
.schedule("0 0 12 * *")
.timeZone("")
.onRun(async () => {
await checkUnsentOrders();
await gameMailUnsentOrders();
await sendStatusEmails();
]);
});
example of trigger gameMailUnsentOrders.js
const processGamerStatusReminderEmail = async (gamerOrders: OrderData[]) => {
const firstOrder = _.head(gamerOrders);
const gamerName = firstOrder
? firstOrder.gamerName
? firstOrder.gamerName
: ""
: "";
const gamerId = firstOrder
? firstGameOrder.gamerId
? firstGameOrder.gamerId
: ""
: "";
const gamerData = await (await gamer.doc(gamerId).get()).data();
const gamerReminderEmail = gamerData.reminderEmail;
if (gamerReminderEmail === "" || !gamerReminderEmail) {
console.log(
` ${gamerName} email is not finded`
);
return;
}
let rows = gamerOrders
.map(
(doc: OrderData) =>
`...`
)
let view = `<strong>Status: </strong> ${status}(
`;
return await sentGameHtmlEmail(
gamerReminderEmail
);
};
module.exports = async () => {
const startDate = startOfDate();
const endDate = endOfDate();
const dateBefore = dateBeforeGameDay();
const finalizedStatusDayBefore = [
...finalisedOrdersStatusIds,
constants.DELIVERED.id,
];
let listGameOrders = await getGameOrdersMailing(startDate, endDate);
let ordersStatus = listGameOrders.filter((gameOrder) => {
const gameDate = DateTime.fromJSDate(gameOrder.gamePDate)
.toJSDate();
if (dateBefore.getDay() === gameDate.getDay()) {
return !finalizedStatusDayBefore.includes(gameOrder.gameStatus.id);
}
return !finalisedOrdersStatusIds.includes(gameOrder.gameStatus.id);
});
let orderedByGamerId = _.orderBy(
ordersStatus.map((order) => order),
"gamerId"
);
return await Promise.all(
_.map(orderedByGamerId, (gameOrders: OrderData[]) => {
return processGamerReminderEmail(gameOrders);
})
);
};

Clearing Message Input in a Socket React App when using Rooms

I'm working on a react app and after following a tutorial on rooms I can not longer get the input to clear when the message is inputed. The commented out sections are things I've tried. I think what I want to do us somehow refer to messageRef in the socket.js file and set it to an empty string. Alternatively I think I might want to move the room functions to Chat.js but the way I learned to set it up was in a seperate file.
For Clientside I have this Chat.js component and the socket.js in the utils folder
import React, { useEffect, useState, useRef } from "react";
import {initiateSocket, disconnectSocket, subscribeToChat, handleTyping, sendMessage } from "../../utils/socket/socket";
import "./listStyle.css";
var user = "User";
var store = "Store"
var storeMessage = user + " is going to " + store;
function Chat() {
const rooms = ['1', '2'];
let messageRef = useRef();
const [room, setRoom] = useState(rooms[0]);
const [typing, setTyping] = useState("")
const [message, setMessage] = useState("");
const [chat, setChat] = useState([]);
//keeps inputing for every letter
// const clearInput = (input) =>
// {
// //input = "";
// console.log("input cleared")
// }
// const handleSendMessage = () => {
// sendMessage(room, user + ": " + messageRef.current.value)
// messageRef.current.value = "";
// }
//const handleTyping = () => { socket.emit("typing", user + ": is typing") }
useEffect(() => {
if (room) initiateSocket(room);
subscribeToChat((err, data) => {
if (err) return;
setChat(oldChats => [...oldChats, data])
setTyping("")
});
return () => {
disconnectSocket();
}
}, [room]);
return (
<div>
<h1>Group: {room}</h1>
{ rooms.map((r, i) =>
<button onClick={() => setRoom(r)} key={i}>{r}</button>)}
<h1>{storeMessage}</h1>
<div id="list-chat">
<div id="chat-window">
<div id="output" >
{chat.map((m, i) => <p key={i}>{m}</p>)}
</div>
<div id="feedback" >{typing}</div>
</div>
<input id="message" autoComplete="off" type="text"
defaultValue = ""
placeholder="message"
//value={message}
onChange={() => setMessage(messageRef.current.value)} ref={messageRef}
//onChange={() => handleTyping(room, user + ": is typing.")}
/>
<button id="send"
onClick={() => sendMessage(room, user + ": " + messageRef.current.value)}
//onClick={() => console.log("test")}
//onClick={() => sendMessage(room, user + ": " + messageRef.current.value)}
//onchange={e => messageRef.current.value = ""}
//onClick={handleSendMessage()}
//onChange={() => handleTyping(room, user + ": is typing." )}
//only clears current room
//have to comment out on startup
//onChange={console.log("test")}
//onSubmit={messageRef.current.value = ""}
>Send</button>
</div>
</div>
)
}
export default Chat;
socket.js
The message is sent using the sendMessage function
(these 3 are from before rooms were implemented
// import {io} from "socket.io-client";
// const socket = io.connect(process.env.PORT || "http://localhost:3001");
// export default socket;
import io from 'socket.io-client';
let socket;
export const initiateSocket = (room) => {
socket = io('http://localhost:3000');
console.log(`Connecting socket...`);
if (socket && room) socket.emit('join', room);
}
export const disconnectSocket = () => {
console.log('Disconnecting socket...');
if(socket) socket.disconnect();
}
export const subscribeToChat = (cb) => {
if (!socket) return(true);
socket.on('chat', msg => {
console.log('Websocket event received!');
return cb(null, msg);
});
}
export const handleSendMessage = () =>{
}
export const sendMessage = (room, message) => {
if (socket) socket.emit('chat', {room, message });
console.log("message sent")
//message = "";
}
export const handleTyping = (room, message) =>{
if (socket) socket.emit('typing', {room, message});
//message = "";
console.log("typing")
}
and my server side file (seperate from the actual server.js)
const socketio = require('socket.io');
//let rooms = ["room1", "room2"]
const initializeSocketio = (server) => {
const io = socketio(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
allowedHeaders: ["my-custom-header"],
credentials: true
}
});
io.on('connection', function (socket) {
console.log("made socket connection", socket.id)
// socket.on('chat', function (data) {
// io.sockets.emit('chat', data)
// });
//doesn't do anything right now
// socket.on('start-shop', function (data) {
// io.sockets.emit('start-shop', data)
// });
// socket.on('typing', function (data) {
// socket.broadcast.emit('typing', data)
// });
//room test
// room1 = "room1";
// socket.on('join1', function (room1) {
// //socket.join(room1);
// console.log(room1)
// });
// room2 = "room2";
// socket.on('join2', function (room2) {
// //socket.join(room2);
// console.log(room2)
// });
//socket.in(room).emit('message', 'what is going on, party people?');
// socket.in('foobar').emit('message', 'anyone in this room yet?');
socket.on('disconnect', () =>
console.log(`Disconnected: ${socket.id}`));
socket.on('join', (room) => {
console.log(`Socket ${socket.id} joining ${room}`);
socket.join(room);
});
socket.on('chat', (data) => {
const { message, room } = data;
console.log(`msg: ${message}, room: ${room}`);
io.to(room).emit('chat', message);
});
// socket.on('typing', (data) => {
// const { typing, room } = data;
// console.log(`msg: ${typing}, room: ${room}`);
// io.to(room).broadcast('typing', typing);
// });
});
}
module.exports = initializeSocketio;
Before I implemented rooms I was using List.js which did delete the input when the message was sent in the handleSentMessage function and it worked.
import React, { useEffect, useState, useRef } from "react";
import socket from "../../utils/socket/socket";
import "./listStyle.css";
var user = "User";
var store = "Store"
var storeMessage = user + " is going to " + store;
//room test
var room1 = "room1";
var room2 = "room2";
function List() {
const messageRef = useRef();
const [messages, setMessages] = useState([]);
const [typing, setTyping] = useState("")
const [room, setRoom] = useState("");
const handleSendMessage = () => {
socket.emit("chat", user + ": " + messageRef.current.value)
messageRef.current.value = ""
}
//room test
//connect room 1
const connectRoom1 = () => {
console.log("button 1 clicked")
// socket.on('join1', function (room1) {
// socket.emit('room1', room1)
// console.log("connected to room1")
// //need to do something with this
// setRoom()
// });
socket.emit('join1', "anything1")
}
//connect room 2
const connectRoom2 = () => {
// console.log("button 2 clicked")
// socket.on('join2', function () {
// socket.emit('room2', room2)
// console.log("connected to room2")
// //need to do something with this
// setRoom()
// });
socket.emit('join2', "anything2")
}
//commented out for now
// socket.on('connect', function(){
// socket.emit('room1', room1)
// });
// socket.on('connect', function(){
// socket.emit('room2', room2)
// });
//room test
socket.on('message', function (data) {
console.log('Incoming message:', data);
});
const handleTyping = () => { socket.emit("typing", user + ": is typing") }
useEffect(() => {
socket.on("chat", (data) => {
setMessages((prevMessages) => ([...prevMessages, data]));
setTyping("")
});
socket.on("typing", (data) => {
setTyping(data)
console.log(data)
});
return () => {
socket.off("chat");
}
// dependency array, put user object in array if we want the callback function to fire everytime userobject sees a change
}, [])
return (
<div>
{/* connect to room 1 */}
<button onClick={connectRoom1}> Room 1 </button>
<br></br>
{/* connect to room 2 */}
<button onClick={connectRoom2}> Room 2 </button>
<h1>{storeMessage}</h1>
<div id="list-chat">
<div id="chat-window">
<div id="output">
{messages.map((message, i) => (
<p key={i}>{message}</p>
))}
</div>
<div id="feedback">{typing}</div>
</div>
<input id="message" autoComplete="off" type="text" placeholder="message" ref={messageRef} onChange={handleTyping} />
<button id="send" onClick={handleSendMessage}>Send</button>
</div>
</div>
)
}
export default List;
any ideas on what I need to do to get the input deleted when the message is sent.
I also want to try to reimplement the user: is typing message but that's a secondary priority.
Any hints or help to get pointed in the right direction would be greatly appreciated.
Maybe you should use a state to hold your input value, and you don't to use a ref for that, try something like:
const [inputValue, setInputValue] = useState("")
const handleSendMessage = (e) => {
socket.emit("chat", user + ": " + e.target.value)
setInputValue("")
}
....
<input id="message" autoComplete="off" type="text" placeholder="message"
ref={messageRef} onChange={handleTyping} value={inputValue}/>
To the typing feature, I would suggest a debounce, to avoid unnecessary events emitted. Take a look at https://github.com/xnimorz/use-debounce, for me one of the easiest debounce solution.
I found a solution to my first part.
const [myList, setList] = useState([]);
const messageRef = useRef(null);
------
const handleSubmit = (e) => {
e.preventDefault();
setList([...myList, messageRef.current.value]);
// you can manually set the value of your input by the node ref
messageRef.current.value = "";
// Or you can reset your form element to its default values
// e.currentTarget.reset();
}
---
<form onSubmit={handleSubmit}>
</form>
I haven't tried the second part yet.

audio is shown disable in twilio video chat

I am trying to create a video chat with twilio. I could turn the webcam and run the video, however i could not make the audio work. When i select the control, i get to enlarge the video and picture to picture mode but not control the audio.
This is how seen
Here is the code
function App() {
let localMediaRef = React.useRef(null);;
const [data, setIdentity] = React.useState({
identity: null,
token: null
});
const [room, setRoom] = React.useState({
activeRoom: null,
localMediaAvailable: null,
hasJoinedRoom: null
});
async function fetchToken() {
try {
const response = await fetch("/token");
const jsonResponse = await response.json();
const { identity, token } = jsonResponse;
setIdentity({
identity,
token
});
} catch (e) {
console.error("e", e);
}
}
React.useEffect(() => {
fetchToken();
}, []);
const attachTracks = (tracks, container) => {
tracks.forEach(track => {
container.appendChild(track.attach());
});
};
// Attaches a track to a specified DOM container
const attachParticipantTracks = (participant, container) => {
const tracks = Array.from(participant.tracks.values());
attachTracks(tracks, container);
};
const roomJoined = room => {
// Called when a participant joins a room
console.log("Joined as '" + data.identity + "'");
setRoom({
activeRoom: room,
localMediaAvailable: true,
hasJoinedRoom: true
});
// Attach LocalParticipant's Tracks, if not already attached.
const previewContainer = localMediaRef.current;
if (!previewContainer.querySelector("video")) {
attachParticipantTracks(room.localParticipant, previewContainer);
}
};
const joinRoom = () => {
let connectOptions = {
name: "Interview Testing"
};
let settings = {
audio: true
}
console.log('data', data, data.token)
Video.connect(
data.token,
connectOptions,
settings
).then(roomJoined, error => {
alert("Could not connect to Twilio: " + error.message);
});
};
return (
<div className="App">
<FeatureGrid>
<span onClick={joinRoom}>Webcam</span>
</FeatureGrid>
<PanelGrid>
{room.localMediaAvailable ? (
<VideoPanels>
<VideoPanel ref={localMediaRef} />
</VideoPanels>
) : (
""
)}
</PanelGrid>
</div>
);
}
export default App;
How do i enable audio too? Also the settings of video is shown only after right click. can't we show this by default?
UPDATE
its a LocalAudioTrack
this is remoteaudiotrack

Categories

Resources