I am using ReactJs to build a chat application with pubnub. The issue is my first message is sent 1 time, second is send twice and third is sent 3 times. What could be wrong, Here is my code. Do I need to unsubscribe at some point? The message is not just shown multiple times, it is actually sent.
class App extends Component {
constructor(props) {
super(props);
this.pubnub = new PubNub({
publishKey: 'demo',
subscribeKey: 'demo',
});
this.sendMessage = this.sendMessage.bind(this);
}
sendMessage() {
this.pubnub.publish({
channel: "simple-chat",
message: {
text: this.state.currentMesage,
sender: this.pubnub.getUUID()
}
});
this.setState({
currentMesage: ''
});
this.pubnub.subscribe({
channels: ['simple-chat'],
withPresence: true
});
this.pubnub.addListener({
message: (evt) => {
console.log('you have got a message:' + JSON.stringify(evt));
this.state.messages.push({
text: evt.message.text
})
this.setState({
messages: this.state.messages
});
}
});
}
export default App;
I believe that it's not a duplication of publish.
I think that you are 're-subscribing' to the same channel over and over again... while publishing 'again and again', so with every new subscribe you are receiving all of the messages that were published to that channel (like History)
So,
1st publish->subscribe-> 1 message
2nd publish->subscribe-> 2 messages
3rd publish->subscribe-> 3 messages
And so on...
Your PubNub init should be global and/or better management of the Subscribe/Publish calls.
Please use the UUID and set it with a static value
Best,
Avi.
Related
I have a simple chat component, as a part of a bigger app.
After logging in, userMessages are fetched from the backend and stored in useState.
const [userMessages, setUserMessages] = useState<ChatMessage[]>([]);
const [messages, setMessages] = useState<ChatMessage[]>([]);
userMessages - all the messages addressed to the user (from other users). Based on them, unread messages are displayed.
messages - messages belonging to a given conversation (between two users) are fetched when entering a given conversation, appear in the chat window.
When a user gets a new message while not being on chat, he gets notifications about unread messages (I used socket.io).
After clicking on the blue arrow icon, the current conversation is set (based on the message property - currentConversationId) and the messages belonging to this conversation are fetched from the database.
When they appear in the chat window each received message (only the green ones) is read...
...each message.tsx component has an useEffect that sends a request to the backend to change the status of a given message from unread to read and returns this message to the frontend, then the messages are updated using useState).
# message.tsx
useEffect(() => {
!own && !read && onReadMessage?.(_id);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
# communicationContext.tsx
const onReadMessage = async (id: string | undefined) => {
const updatedMessage = await CommunicationService.readMessage(id);
if (updatedMessage) {
let notification = {
receiverId: updatedMessage.sender?._id,
text: "read your message.",
silent: true,
read: false,
type: "message",
};
handleAddNotification?.(notification);
handleSendNotification?.({
...notification,
senderId: updatedMessage.sender?._id,
senderName: updatedMessage.sender?.name,
payload: { message: updatedMessage },
});
const updatedMessages = messages.map((message) =>
message._id === updatedMessage._id ? updatedMessage : message
);
setMessages(updatedMessages);
const updatedUserMessages = userMessages.map((message) =>
message._id === updatedMessage._id ? updatedMessage : message
);
setUserMessages(updatedUserMessages);
}
}
A request containing an updated message is also sent to the sender of the message via socket.io, then useState is also fired on the sender side and they see that the message has been read.
Up to this point everything works fine, but...
the problem arises when there are several unread messages at the same time.
In the database all messages are updated but the application status shows only 1-3 latest messages as read (depending on how many there are - sometimes only the last one is updated).
I know how useState works, so I expected this result, but I'm looking for a way around it and I'm out of ideas.
I need a solution that will update the entire state of the application, not just recent changes, without having to refresh the page.
I tried useReducer but got lost because there are too many useStates in communicationContext.tsx (here is a simplified version).
I suspect your onReadMessage handler should use functional state updates to eliminate race conditions or updating from stale state. It's a trivial change. The enqueued functional state updates will correctly update from the previous state versus whatever (likely stale) state value(s) are closed over in callback scope.
const onReadMessage = async (id: string | undefined) => {
const updatedMessage = await CommunicationService.readMessage(id);
if (updatedMessage) {
const notification = {
receiverId: updatedMessage.sender?._id,
text: "read your message.",
silent: true,
read: false,
type: "message",
};
handleAddNotification?.(notification);
handleSendNotification?.({
...notification,
senderId: updatedMessage.sender?._id,
senderName: updatedMessage.sender?.name,
payload: { message: updatedMessage },
});
const mapMessage = (message) => message._id === updatedMessage._id
? updatedMessage
: message;
setMessages(messages => messages.map(mapMessage));
setUserMessages(userMessages => userMessages.map(mapMessage));
}
};
So, basically I'm trying to receive a call from provider to my app. For that purpose Quickblox gives us a listener to receive the upcoming calls onCallListener. So here is my code snippet that should work but doesn't.
const calleesIds = [4104]
const sessionType = QB.webrtc.CallType.VIDEO
const additionalOptions = {}
let callSession = QB.webrtc.createNewSession(calleesIds, sessionType, null, additionalOptions)
console.log(callSession, "SESSION")
const mediaParams = {
audio: true,
video: true,
options: {
muted: true,
mirror: true,
},
elemId: "myVideoStream"
}
QB.webrtc.onCallListener = function(session: any, extension: object) {
callSession = session
console.log('asdasd')
// if you are going to take a call
session.getUserMedia(mediaParams, function (error: object, stream: object) {
if (error) {
console.error(error)
} else {
session.accept(extension)
session.attachMediaStream("videoStream", stream)
}
})
}
P.S. I also integrated chat which works perfect!
Found the solution by myself! Whenever you create a user and dialog id, search that user in the quickblox dashboard by the dialogId and change its settings: you will see that userId and providerId is the same which is wrong. So put your userId in the userId field and save that. After that you video calling listeners will work fine!)
P. S. also in the backend replace provider token with user token.
I'm currently working on a Android mobile App.
It's a kitchen recipes app. The app will send notification to the user during the day.
In the settings of the app, the user can choose how many and at what time he will receive the notifications (11 am to 7 pm for example)
This is where the problem begins;
I use the react-native-push-notification library with the following code:
static LocalNotif(string)
{
PushNotification.localNotification({
vibrate: true, // (optional) default: true
vibration: 300, // vibration length in milliseconds, ignored if vibrate=false, default: 1000
title: "VĂ©rifier vos produit", // (optional)
message: string, // (required)
largeIcon: "ic_launcher",
smallIcon: "ic_notification",
});
}
Next, I use the react-native-background-fetch to send a notification, even if the app is not running
static async backFetch(delay_to_next_notif)
{
BackgroundFetch.configure({
minimumFetchInterval: 3600
}, async (taskId) => {
// This is the fetch-event callback.
console.log("[BackgroundFetch] taskId: ", taskId);
// Use a switch statement to route task-handling.
switch (taskId) {
case 'com.foo.customtask':
this.LocalNotif("test")
break;
default:
console.log("Default fetch task");
}
// Finish, providing received taskId.
BackgroundFetch.finish(taskId);
});
// Step 2: Schedule a custom "oneshot" task "com.foo.customtask" to execute 5000ms from now.
BackgroundFetch.scheduleTask({
taskId: "com.foo.customtask",
forceAlarmManager: true,
delay: delay_to_next_notif// <-- milliseconds
});
}
The use of react-native-background-fetch is very strange. Sometime I never receive the notification.
Is it possible to use a push notification library and create a routine so that the user receives notifications at specific times during the day, even if the app is not running?
You can use Pushnptification.configure method and set your state if your app is in forground or background something like this
async componentDidMount() {
await this.requestUserPermission();
PushNotification.configure({
onNotification: (notification) => {
console.log('NOTIFICATION', notification);
if (notification.foreground === false) {
console.log('app is in background')
}
this.setState({
msg: notification.message.body
? notification.message.body
: notification.message,
forground: notification.foreground,
});
},
});
}
and in your return u can do something like this
{this.state.forground === true
? showMessage({
message: this.state.msg,
backgroundColor: '#1191cf',
type: 'default',
duration: 10000,
icon: 'success',
onPress: () => {
console.log('app is in forground')
},
})
: null}
I'm trying to integrate the backchannel and getting the values.
https://github.com/Microsoft/BotFramework-WebChat/tree/master/samples/15.d.backchannel-send-welcome-event
I also tried this. Get URL Referer and Origin header from Microsoft Bot Framework
I also tried deserializing the values still not able to get the data.
how can i get the language values?
here's my sample code:
var userinfo = {
id: 'user-id',
name: 'user name',
locale: 'es'
};
var botConnection = new BotChat.DirectLine({
token: 'mytoken',
user: userinfo,
locale: 'es'
});
BotChat.App({
botConnection : botConnection,
user: userinfo,
bot: { id: 'bot-id', name: 'bot name' },
}, document.getElementById('botDiv'));
botConnection
.postActivity({
from: userinfo,
name: 'ConversationUpdate',
type: 'event',
value: '',
})
.subscribe(function (id) {
console.log('"trigger ConversationUpdate" sent');
});
The purpose of this I want to pass the locale to my bot from my website.
just like in the emulator.
Thanks!
I would recommend adding the locale to the back channel event's channel data. That way on the bot side you can simply access the locale in the incoming activity without having to deserialize any JSON objects when you receive the event. Note, you can also use text or value in place of channelData. See the code snippets below.
BotChat Back Channel Event
// Send back channel event
botConnection.postActivity({
from: userinfo,
name: 'setLocale',
type: 'event',
channelData: "es"
}).subscribe(id => console.log(id));
Bot - C#
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
if (turnContext.Activity.Type == ActivityTypes.Message)
{
...
} else if (turnContext.Activity.Type == "event") {
// Check for `setLocale` events
if (turnContext.Activity.Name == "setLocale") {
await turnContext.SendActivityAsync($"Your locale is set to {turnContext.Activity.ChannelData}");
}
}
else
{
await turnContext.SendActivityAsync($"{turnContext.Activity.Type} event detected");
}
}
Hope this helps!
I am using react-native with socket.io to send and receive contact requests but my code is only emitting to the emitter and to no one else.
This is the server side:
users = []; // Each time a new user joins the server they are saved in this array
socket.on('create connection', function(data, callback) {
if(data.receiverId in users) { // If the user you want to add is online then callback is true else callback is false
// The underneath line is the one that I will be using but nothing happend
//io.sockets.in(data.receiverId).emit('save room', data);
// So I created this one to see if I actually was emitting something
socket.emit('save room', data); // I found out that the emitting was working but only with the emitter
callback(true);
}else
callback(false);
});
So my conclusion is that my server side is correct and the problem lies on my client side
This is my client side:
constructor() {
this.socket = SocketIOClient('http://192.168.15.4:8000');
this.socket.on('save room', function (data) { // This is where the server calls the emit. It was at first inside the connect function but I moved it to the constructor to see if that way all clients could get it, results are the same
Alert.alert(
'Accept Connection?',
'User: '+data.emitterId+' sent you a connection request',
[
{
text: 'Accept',
onPress: () => {},
},
{
text: 'Refuse',
onPress: () => {},
},
],
{cancellable: false}
);
})
}
#action connect(data, callback1) { // this access the function we had previously in the server
this.socket.emit('create connection', data, function (callback2) {
if(!callback2)
Alert.alert(
'User Offline',
'This user is currently offline try again later',
[
{
text: 'OK',
onPress: () => {callback1(false)},
},
],
{cancellable: false}
);
else {
callback1(true)
}
});
}
I think the problem lies on where I place the this.socket.on('save room') function, but in react-native I really do not know where to put it.
Thanks to #H. Tugkan kibar and #azium I realized that the problem was on my server side the correct way to emit to a specific user is like this:
socket.broadcast.to().emit();