I am building a simple chat app and want to greet new users with a welcome mesage, but all i see is [object Object]. The connection is working. What to do?
This is the messages object that i am using to model the messages
const generateMessage = (text) => {
return {
text,
createdAt: new Date().getTime()
}
}
module.exports = {
generateMessage
}
Here is my connection
io.on("connection", (socket) => {
console.log("New socket connection")
socket.emit("message", generateMessage("Welcome to the chat"))
socket.broadcast.emit("message", "A new user has joined",)
socket.on("sendMessage", (message, callback) => {
io.emit("message", message)
callback()
})
socket.on("disconnect", () => {
io.emit("message", "A user has left")
})
})
And here is the html where i render the messages to the browser
<body>
Chat app
<div id="messages"></div>
<form id="message-form">
<input name="message" placeholder="Message">
<button>Send</button>
</form>
<template id="message-template">
<p></p>
</template>
<script src="/socket.io/socket.io.js"></script>
<script src="../chat/chat2.js"></script>
</body>
okay first thing that generateMessage function is not returning proper key value
return {
text: text,
createdAt: new Date().getTime()
}
and the second thing you are trying to render the whole object but you should render
socket.emit("message", generateMessage("Welcome to the chat").text)
In your client (the html file) you should create the message accessing text property of object:
const htmlMessageText = messageObjReceivedFromSocket.text
Not pass the object itself.
It'll easier help you if post how you handling the socket events on client.
Related
I am creating a project where I need to take a user input - pass it through a function and return the new value to the user - seems simple enough. I am new to async functions and have read everything I possibly can, and can't works out if there's a more fundamental issue I am missing. I will show the basic code, and then what I wish to achieve. I believe the issue, is that I am returning back the status of the function rather than the value, but just can't work it out.
Basic Code:
ipcMain.on('gpt3', (event, args) => {
async function gpt3(args) {
generateResponse('james', 'hello world'); // Takes a user's name & input and recieves a response from a python file.
event.reply('textRecieve', 'hello world'); // Sends 'Hello World' to the user (ipcRenderer 'textRecieve')
}
gpt3(args);
})
async function generateResponse(name, text) {
let testshell = new PythonShell('./python/text_echo.py', { mode: 'text', args: [name, text]});
let content = "";
try {
testshell.on('message', function (message) {
console.log(message); // prints the output from the python file 'Python File: james Text: hello world'
return message; // attempting to return the 'Message' from the python file
});
} catch (error) {
console.log("You've f*cked it somewhere my friend");
console.log(error);
}
}
Python Script:
import sys
name = sys.argv[1]
text = sys.argv[2]
print(f'Python File: {name} Text: {text}')
sys.stdout.flush()
Returns: (as expected)
> Executing task: npm run start <
> electron-quick-start#1.0.0 start
> electron .
Python File: james Text: hello world
What I'd Like it to do:
ipcMain.on('gpt3', (event, args) => {
async function gpt3(args) {
message = generateResponse('james', 'hello world'); // Takes a user's name & input and recieves a response from a python file, retunring the message to the 'message' variable.
console.log(message);
event.reply('textRecieve', 'message would send here'); // Sends the 'Message' to the user (ipcRenderer 'textRecieve')
}
gpt3(args);
})
async function generateResponse(name, text) {
let testshell = new PythonShell('./python/text_echo.py', { mode: 'text', args: [name, text]});
let content = ""
try {
testshell.on('message', function (message) {
console.log(message); // prints the output from the python file 'Python File: james Text: hello world'
return message; // attempting to return the 'Message' from the python file
});
} catch (error) {
console.log("You've f*cked it somewhere my friend")
console.log(error)
}
return content; // content needs to be message instead due to async nature it returns empty string
}
Returns:
> Executing task: npm run start <
> electron-quick-start#1.0.0 start
> electron .
Promise { '' }
Python File: james Text: hello world
TLDR; I would like to take the 'message' generated through 'generateResponse()' and pass it through to my 'event.reply()'. Instead, I am receiving what I believe to be the status of the Promise. Any help would be greatly appreciated. Thanks
You should resolve the promise first.
ipcMain.on('gpt3', (event, args) => {
async function gpt3(args) {
const message = await generateResponse('james', 'hello world');
console.log(message);
event.reply('textRecieve', 'message would send here'); // Sends the 'Message' to the user (ipcRenderer 'textRecieve')
}
gpt3(args);
})
async function generateResponse(name, text) {
let testshell = new PythonShell('./python/text_echo.py', { mode: 'text', args: [name, text]});
let content = ""
try {
testshell.on('message', function (message) {
console.log(message); // prints the output from the python file 'Python File: james Text: hello world'
content = message;
});
} catch (error) {
console.log("You've f*cked it somewhere my friend")
console.log(error)
}
return content; // content needs to be message instead due to async nature it returns empty string
}
Okay, so there were a few problems here... but the main was node.js 'non-ability' to pass variables around when 'asynchronous'. with node.js being new to me, I can't lie and say I was confused. Hopefully, the following link to a great workaround/method and my working code will be able to help someone:
https://stackoverflow.com/a/23667087/10246221
Code:
ipcMain - nested within app.whenReady().
ipcMain.on('gpt3', (event, input) => {
gpt3Async(event, input, function(result) {
event.reply('textRecieve', result);
console.log('gpt3Async: '+ result);
})
})
Code:
Generic 'nested' Function - free-floating around 'main.js' or 'index.js'.
function gpt3Async(event, input, callback) {
console.log('input: ' + input)
let testshell = new PythonShell('./python/text_echo.py', { mode: 'text', args: ['elliott' ,input]});
testshell.on('message', function (message) {
callback(message);
});
}
Code: Python Script 'text_echo.py' - in my case within a 'python' subdirectory.
import sys
name = sys.argv[1]
text = sys.argv[2]
print(f'Python File: {name} Text: {text}')
#sys.stdout.flush()
sys.stdout.flush()
For anyone working on a project where you need input and output for python scripts, this will help you out. also make sure you turn on the following:
webPreferences: {
//preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
sandbox: false,
},
BUT!, please be aware of the security implications this will have on your code, More info is available here: https://stackoverflow.com/a/57507392 & https://electronjs.org/docs/tutorial/security#3-enable-context-isolation-for-remote-content & much more so do some reading if this is an important project...
Okay, An explainer, or at least something that blew my mind as a beginner... . The way I finally understood it was through the example link:
https://stackoverflow.com/a/23667087/10246221
for some reason, it hadn't clicked with me that functions could be nested within functions like this, all in one line. For someone who is used to JS or node.js this may seem fundamental, but seeing as this is a first-time project to me, and maybe others - if still using python code. Hopefully, this may help!
ipcMain.on('gpt3', (event, input) => { gpt3Async(event, input, function(result) { event.reply('textRecieve', result); console.log('gpt3Async: '+ result);})})
I want to give user possibility to choose his own value of Ether. How to do this?
How efficiently change the network when pressing connect to metamask (for example when user will press this button metamask should change network to binance smart chain (ID 56)
here is my code:
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/web3#latest/dist/web3.min.js"></script>
</head>
<body>
<div>
<button class="enableEthereumButton btn">Enable Ethereum</button>
<button class="sendEthButton btn">Send Eth</button>
<div id="status"></div>
</div>
<script type="text/javascript">
// Running on the page, in the browser
if (typeof window.ethereum !== 'undefined') {
console.log('MetaMask is installed!');
}
if (!ethereum || !ethereum.isMetaMask) {
throw new Error('Please install MetaMask.')
}
/*********************************************************/
/* Handle chain (network) and chainChanged, per EIP 1193 */
/*********************************************************/
// Do this:
ethereum.on('chainChanged', (chainId) => {
/* handle the chainId */
});
const ethereumButton = document.querySelector('.enableEthereumButton');
const sendEthButton = document.querySelector('.sendEthButton');
let accounts = [];
//Sending Ethereum to an address
sendEthButton.addEventListener('click', () => {
ethereum
.request({
method: 'eth_sendTransaction',
params: [
{
from: accounts[0],
to: '0x6adress.................',
value: '0x00',
gasPrice: '0x0000001F6EA08600',
gas: '0x0001ADB0',
},
],
})
.then((txHash) => console.log(txHash))
.catch((error) => console.error);
});
ethereumButton.addEventListener('click', () => {
getAccount();
});
async function getAccount() {
accounts = await ethereum.request({ method: 'eth_requestAccounts' });
}
</script>
</body>
</html>
Metamask screenshot
Add an input field for the value and pass it in to your params. This is basic html form + javascript interactions, not specific to web3, so for more info on how to do that I would look here https://www.w3schools.com/html/html_forms.asp
To read the network the user is connecting with in your app you can listen for a chain change event: https://docs.metamask.io/guide/ethereum-provider.html#events
Then if they are not connected to a network your app supports you should show the user a notice.
I'm working on a chat style app using React and socket.io, and I want to implement something where a client can see the input being typed by the other client in realtime. I'm using onChange on the input to update the state and emit the message at the same time, but that only sends messages one letter at a time to the server, and deletions don't work at all.
Here's the frontend with extra code omitted:
this.state = {
text: '',
name: '',
messages: []
}
componentDidMount() {
socket.on('RECIEVE_MESSAGE', function(msg) {
this.setState({
messages: [...this.state.messages, msg]
})
})
}
onInputChange(event) {
this.setState({
text: event.target.value
})
socket.emit('example_message', this.state.text);
this.setState({
messages: [...this.state.messages, this.state.text]
})
}
return (
<div>
<form type="text" onSubmit={this.handleSubmit}>
<input
type="text"
className="text-input"
id="name-input"
name="text-input"
required="required"
placeholder="text"
//used to save end result
value={this.state.text}
onChange={this.onInputChange}></input>
{/* <button className="next-btn"> NEXT </button> */}
<button onClick={this.sendSocketIO}>Send Socket.io</button>
</form>
<p>{this.state.text}</p>
<p>{this.state.messages}. </p>
</div>
)
And the backend:
io.on('connection', client => {
console.log('Socket connected: ', client.id);
//recieves the message from the client, and then re-emits to everyone
client.on('SEND_MESSAGE', data => {
data.message = validate.blacklist(data.message, ['<','>','&','"','/']);
io.emit('RECEIVE_MESSAGE', data);
});
I'd like to render to the DOM a live feed of what the other client is typing, and update it with every character and character deletion.
You are emitting with:
socket.emit('example_message', this.state.text)
and handling with:
client.on('SEND_MESSAGE', data...
Use the same name for the message to emit and then handle on the server.
I am using Javascript SDK for 1-1 chat in Quickblox, but somehow I am not able to store the chat history.
I am following this link.
var message = {
body: text,
type: 'chat',
extension: {
nick: chatUser.email,
// token from session is set on window object
token: window.token,
// MyChat is a custom class_name
class_name: 'MyChat'
}
};
I am passing the class_name and token since I saw the android sdk following the same pattern.
private Message createMsgWithAdditionalInfo(int userId, String body, Map<?, ?> addinfoParams){
Message message = new Message(QBChatUtils.getChatLoginFull(userId), Message.Type.chat);
String addInfo = ToStringHelper.toString(addinfoParams, "", Consts.ESCAPED_AMPERSAND);
//
MessageExtension messageExtension = new MessageExtension(Consts.QB_INFO, "");
try {
messageExtension.setValue("token", QBAuth.getBaseService().getToken());
messageExtension.setValue("class_name", "ChatMessage");
messageExtension.setValue("additional", addInfo);
} catch (BaseServiceException e) {
e.printStackTrace();
}
message.addExtension(messageExtension);
message.setBody(body);
return message;
}
Also in instructions I see this.
<message id="123" type="chat" to="291-92#chat.quickblox.com" from="292-92#chat.quickblox.com"><body>Hi there</body><quickblox xmlns=""><token>848d4bf336d99532deff6bf7c8bb4b7e7b1a71f9</token><class_name>ChatMessage</class_name></quickblox></message>
Here also I see token & class passed so I am guessing how to I structure in my message object so that I get it to work.
The way I have created chatService is this.
chatService = new QBChat(params);
// to send message I am using sendMessage function
// message object is same as defined above.
chatService.sendMessage(recipientID, message);
This is an old and deprecated method to store chat history
Look at this guide http://quickblox.com/developers/Chat#Server-side_chat_history
var msg = {
body: "Hey",
extension: {
save_to_history: 1
},
senderId: currentUser.id,
};
You have to use 'save_to_history' to store a message
You can use this branch as a basis
https://github.com/QuickBlox/quickblox-javascript-sdk/tree/develop.chat/samples/chat
This question already has an answer here:
Get Meteor Method Return Value Inside Helper Function
(1 answer)
Closed 7 years ago.
I am working on a meteor project but having trouble with the asynchronous functionality. I would like to be able to reliably display information through a mongo collection find query that occurs on the server. The query should be located in a meteor method on the server, callable by the client. However, due to the asynchronous nature of meteor, the Template Helper returns undefined before the method call finishes. How can I reliably wait or restructure the code (trigger an event to display the information coming back from the query)?
What I want the html page to look like after loading:
<head>
<title>appMongoTest</title>
</head>
<body>
<h3>appMongoTest</h3>
hello world<br />
blah blah blah<br />
more example test<br />
</body>
html file: appMongoTest/client/appMongoTest.html
<!-- appMongoTest.html -->
<head>
<title>appMongoTest</title>
</head>
<body>
<h3>appMongoTest</h3>
{{> example}}
</body>
<template name='example'>
<!-- DEBUG
{{testString}}<br />
{{#each testArray}}
{{text}}<br />
{{/each}}
END DEBUG -->
{{#each getResults}}
{{content}}<br />
{{/each}}
</template>
client js: appMongoTest/client/client.js
// client.js
// results() doesn't need to be seperated from the helpers
// seperated for clarity while debugging
function results() {
/* Should I be using Meteor.apply instead with the
onResultReceived or wait option? If so, how do I use that?
I have been unable to figure out the syntax for the
onResultReceived or wait options. */
Meteor.call('findText', function(error, result) {
if (error) { console.log(error); }
else {
// DEBUG console.log(result);
// results is correctly getting the objects
return result;
}
});
}
Template.example.helpers({
testString: 'testString working',
testArray: [
{ text: 'one' },
{ text: 'two' },
{ text: 'three' }
],
getResults: function() {
var res = results();
// DEBUG console.log('what results() returns');
// DEBUG console.log(res);
// I am getting back undefined rather than the objects
return res;
}
});
server js: appMongoTest/server/server.js
// server.js
function addExample(name, type, content) {
exampleCollection.update(
{ name: name },
{ $set: {
type: type,
content: content
} },
{ upsert: true }
);
}
Meteor.startup(function() {
// DEBUG console.log('Hello Server!');
exampleCollection = new Meteor.Collection('example');
addExample('text0', 'text', 'hello world');
addExample('text1', 'text', 'blah blah blah');
addExample('text2', 'text', 'more example text');
});
Meteor.methods({
'findText': function() {
// DEBUG console.log('findingText');
var results = exampleCollection.find(
{ type: 'text' },
{ fields: { content: true } }
).fetch();
// DEBUG console.log(results);
// DEBUG console.log(results[0].content);
return results;
}
})
This is a ccommon problem in Meteor. You should use some defensive coding, I.e.
if(results) return results;
and in your template
{{#if results}}
...
{{else}}
Loading
{{/if}}
This should stop your browser from throwing errors, and give feedback to the user if needed.