What is the proper way to get and display total_unread_count in React? - getstream-io

I'm trying to create a chat icon in a navigation bar with a message count (ala FB, Twitter, etc). I'm using ReactJS. The menu component is separate from the chat components. I'm kind of getting what I want, but not completely. I'm hoping someone can show me the correct way to listen for real time changes to total_unread_count in react.
Right now, if user a sends a new message to user b and user b is not on the chat screen, they will get badge with a message count. I can navigate all around and it'll keep that count. If I click on the chat view to navigate to the chat and click on the channel, then go back to any other screen, the notification count does not change. But, if I refresh the page manually, it'll clear the badge.
So, my expectation is that when a user has viewed the channel, that the state of the chat icon's badge count be updated to reflect that I've seen the message. Having to manually refresh the screen means I'm doing something wrong.
Here's my code so far:
useEffect(() => {
async function init() {
// set state initially because it appers that client.on events only happen a new event.
if (user != null) {
const response: any = await chatClient.connectUser({ id: user.uid }, token)
setUnreadCount(response.me.total_unread_count)
}
// update state when a new event occures.
chatClient.on((event) => {
if (event.unread_channels !== undefined) {
console.log("COUNT: ", event.total_unread_count)
setUnreadCount(event.total_unread_count)
}
})
}
init()
}, [])
I hope I'm communicating this well enough to make sense. I appreciate any help I can get.

Related

NodeJs,ExpressJS Running functions in background

im asking this question because i dont know what to look for right now and my googling wasnt great so far.
I am making nodejs,express,sql app that scrape website. It takes 30 to 120 seconds to scrape whole category. How to make that function run in the background without blocking website. Frontend template engine is eJS. If its not possible to do with eJS which framework,library should i use then? I imagine it work like that
User go to /scrape
Choose category and send to server by clicking button
Some container on /scrape gets greyed out with circle rotating or
other % or smth
User can freely leave /scrape and click around webiste or just stay
on /scrape waiting for result
When user cames back to /scrape the results are there or when he
stayed results shows up with or without reloading the page
Getting full respond to these questions will be very helpfull. But just keywords for me to look up also are very helpfull
Sorry for bad english
For your case here you could use redis, or just store the data you scrape on an data structrue that you like (in my opinion, because of the category, hashmaps (js objects) are the best here) directly in nodejs. The process would then look like this:
User goes to /scrape and selects a category
Backend checks if that category was already scraped (e.g. checks for the data in the hashmap with the category name as key)
If the data exists (just check if the key is defined), then send the data to the user, else (if the data isn't stored, e.g. key == undefined), send the user a message that the data is beign scraped and just run the scrape funtion in the backround. The scrape function than scrapes the data, and if it is done, it pushes the data with the category key to the hashmap. To avoid the same categorys beign scraped at the same time, you could add a "pending" property to the hashmap. So if the user accesses the /scrape route, you check in the hashmap if the category key exsists, if yes and pending is false, send data, if yes and pending is true, send wait alert, if the key doesn't exists, start the scrape function and send a wait alter.
Additionally, to make the whole thing "live", you could use socket.io (https://socket.io/) to implement websockets. You could then send the scraped data to the user without the user having to reload the page to check if the scrape process is done.
I made a little exmaple, that doesn't implement scraping, but should make the whole logic here a little bit easier to understand. I also added some explenation to the code in form of comments.
const express = require("express");
const app = express()
// the data hashmap
const data = {};
// scrape function
const scrape = async (id) => {
// set pending to true to prevent multiple scraped on same category
data[id] = { pending: true, data: {} }
// this would be your scrape functio, I used a promise here that
// resolves after 5 seconds with an random number just for
// simplicity
const a = await new Promise((res, rej) => {
setTimeout(() => { res(Math.floor(Math.random()*1000)) }, 5000)
})
// if the data was scraped, set pending to false and add the data
data[id].pending = false;
data[id].data = { id: a }
}
// "scrape" route
app.get("/:id", async (req, res) => {
const { id } = req.params; // if would represent category
// check if id (category) is not in hashmap, if not, then
// start the scrape process and send a wait alert
if (data[id] == undefined) {
scrape(id);
res.send("scraping...")
// if the data is already beign scraped, send a wait alert
// the pending property prvents that multiple people trigger
// the scrape of the same category
} else if (data[id].pending == true) {
res.send("still scraping...")
// lastly, if the data is defined, and is not pending, then
// you could just send it
} else {
res.send(data[id].data)
}
})
// to test this, go to the root with any id, could be string, number,
// whatever (e.g. /1337 or /helloworld), wait for 5 seceonds (or
// leave and come back after 5 seconds), refresh the page and you can
// see the random number. If you now go to an other route (e.g /test)
// and go back to the last one, you still can see the data, if you again
// wait for 5 seconds and then go back to /test, you can see the data.
// You can also open multiple tabs at the same time, which means the
// scraping is asynchronous, so you don't have to wait for the
// one category to be scraped to scrape the next
app.listen(5000)

How to load data from a different component after submititng form from another component in ReactJs

I have an Event Model. in my event, people can indicate if they are attending an event by clicking a button, and when they indicate that they are attending an event by clicking a button, a Modal dialog will pop up to ask them if they also want to bring friends along to the event, if they say Yes, it will ask them to choose how many people they want to bring and they will choose from a drop-down list of numbers and submit. after submitting everything it will plus people that are coming and how many people the person is coming with then show them together.
Note: the modal dialog is from another component that I am requiring inside my Event component.
the problem that I am facing now is that my view only updates the people that are coming but doesn't update the number of the people the person is bringing along unless I refresh the page. this was working when I was using class-based-component but after changing my code to function based component it stopped working.
How do I make the browser to update it automatically without refreshing the page?
This is my Sandbox link https://codesandbox.io/s/compassionate-sanne-pmmx6?file=/event-comments.component.js
please use this URL inside sandbox https://pmmx6.csb.app/events/
Just add events after the URL provided by sandbox in other to load the events data because the link to the event is not showing inside the sandbox (I don't know why)
Maybe you can solve this by using event emitter
Use
EventEmitter.subscribe('refresh', (event)=>this.refresh(event))
to create the event
And when you want to execute it you use
EventEmitter.dispatch('refresh', event)
Step by step to create a eventEmitter:
Create a eventEmitter.js file:
const EventEmitter = {
events: {},
dispatch: function (event, data) {
if (!this.events[event]) return;
this.events[event].forEach(callback => callback(data))
console.log("Dispatched: " + event)
},
subscribe: function (event, callback) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(callback)
console.log("Subscribed: " + event)
},
unsubscribe: function(event) {
if (this.events[event]) {
delete this.events[event]
}
console.log("Unsubscribed: " + event)
}
};
module.exports = {EventEmitter};
In the component you want to refresh first you import the eventEmitter.js. For example:
import {EventEmitter} from "./tools/eventEmitter";
Then you use subscribe() to create a event:
EventEmitter.subscribe('refresh', (event)=>this.refresh(event))
where refresh is the function will execute when you dispatch the event
Finally, when you want to refresh the component, use .dispatch():
EventEmitter.dispatch('refresh', event)
This will execute the this.refresh() first component funcion
If you want to look https://medium.com/#lolahef/react-event-emitter-9a3bb0c719 is a good event emitter's page
Thanks, Everyone, I have solved the problem by just adding events after my API call. I saw the example on the React Facebook page. This method reloads the data anytime there is a change.
const [events, setEventData] = useState([]);
useEffect(() => {
axios
.get(
"https://cryptic-shelf-72177.herokuapp.com/events/" +
props.match.params.id +
"/eventcomments"
)
.then((response) => {
setEventData(response.data);
})
.catch(function (error) {
console.log(error);
});
}, [events]);

What Would I Want To Use To Have A Persistent Notification Show Up When I Access Specific Pages?

I want to make an extension that, when you go to some sites, you have a timer pop up on your screen.
I've been looking at the documentation, but I wouldn't say that its been particularly useful for a new extension builder like myself.
What I've gathered is that I would want to use a chrome.notification - which I've implemented. However, instead of the notification being persistent and being modified on the spot, it spams notifications, which is not what I want. Would I want to use a popup, notification, something else, or is this not even possible?
let test = 0
var myfunc = setInterval(function() {
// code goes here
test += 1
notification = new Notification("Test Notification", {
icon: '48.png',
body: test
});
}, 1000)

Is it possible to open widgets.getsitecontrol.com/ javascript from azure bot v4?

I want to open widgets.getsitecontrol.com/ javascript page that I have implemented on my website. Whenever I type 'Help' inside my bot, the widget should open. Is it possible to open it? Thanks. I am using node js version. If it is possible, please provide me an approach to solve this issue.
I'm not sure exactly how your widget functions, but when the user sends a 'help' message to the bot, you can send a back channel event to WebChat to trigger opening the widget. Take a look at the code snippets below.
Bot Code - NodeJs
When the bot receives a 'help' message from the user, the bot can send an event by sending an activity with the type set to 'event'. We can also give the outgoing activity a name attribute so we can send mutltiple types of events to WebChat. In this case, we are going to name the out going activity 'helpEvent'.
async onTurn(turnContext) {
if(turnContext.activity.type === ActivityTypes.Message) {
if (turnContext.activity.text.toLowerCase() === 'help') {
// Send Back Channel Help Event
await turnContext.sendActivity({ type: 'event', name: 'helpEvent'});
}
...
}
}
WebChat Custom Middleware
In WebChat, we are going to create a custom middleware to check incoming activities. When we encounter an activity that has a name and type that we recognize, trigger your event on the webpage. In the example below, I just alerted the use that they asked for help, but here is where you launch your widget.
const store = window.WebChat.createStore(
{},
({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
const { name, type } = action.payload.activity;
if (type === 'event' && name === 'helpEvent') {
// Activate Widget
alert("You asked for help.");
}
}
return next(action);
}
);
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
store,
}, document.getElementById('webchat'));
For more details on back channel events and creating a custom middleware in WebChat, checkout this sample in the WebChat Repo.
Hope this helps!

Botbuilder Dialogues dont support debounce or separate loops

How to achieve the following?
There are two types of listeners in botframework
one to the root dialog / where luis handler is attached to.
another is Prompts where an input from user is sought.
In both occassions. it is possible that user enters the inputs in a series of utterances and not necessarily only one.
how can Prompts in botbuilder collect in debounce pattern, esp when in middle of seeking inputs from user? and how can these be directed to luis while in middle of dialog?
e.g.
1.
bot: please enter your name
user: 1 sec
user: ok, it is Smith.
2.
bot: fetching the details from server... ( 5 sec pause makes user lose patience)
user: u there?
// This should not break the current dialog ( i.e. dialogue handler is preparing a response).
bot: yes, I am there.still waiting for a response from server. pls hold on. (after few secs...)
bot: got the details. here you go..
third example.
bot: what was your experience?
user: well, where shall I begin?
user: it was kind of ok, but...
user: not very good..
user: but would recommend
the prompts should be able to collect these before reacting to each input...
Here is what Im doing: using the library "debounce-queue" Im queueing the user messages in the bot.use({receive}) middleware. When the array of events get debounced, I proceed to merge the events (text, attachments, etc). After thats done I proceed to execute the next() middleware callback. Here is some code (also using lodash _), you might need to adapt to yours:
var debounces = {};
bot.use({
receive: (event, next) => {
if (event.type === 'message') {
if (!debounces[event.address.user.id]) {
debounces[event.address.user.id] = debounce(events => {
var last = events[events.length-1];
var event = last.event;
_.reverse(events.splice(0, events.length - 1)).forEach(debounced => {
event.text = debounced.event.text + ' ' + event.text;
event.attachments = _.concat(event.attachments, debounced.event.attachments);
})
last.next()
})
}, 2000)
}
debounces[event.address.user.id]({ event, next });
} else {
next()
}
}
})

Resources