This question probably doesn't have an answer. But, I thought I'd give it a shot.
I wrote a great one-page application. When the application starts up, the open tab "registers" itself with the server, which stores is as an "active" tab.
If user A changes XYZ in the workspace, every tab opened on that workspace, by any user, receives a notification that XYZ was changed. That triggers a reload in the clients, which will magically be updated. At the moment, I am doing this by polling. However, when it all works I can use things like WS or Socket.io to make things even faster.
PROBLEM: every tab receives the notification. Even the tab that instigated it in the first place! (as a result, an already-updated screen gets updated)
I somehow need the server to know the tab ID of the tab making the request. Remember that a user might have 5 tabs open: if they change XYZ, all tabs should receive the notification, EXCEPT the one that actually triggered it!
At the moment, I am passing the workspace ID for every Ajax request ( a user might be logged in, and have access to several workspace at the same time).
Solution 1: append both workspace ID and tab ID for every request
Solution 2: only use the tab ID for every request. The app will work out the workspace ID from the tabID (which knows which workspace it belongs to)
Solution 3: ????? (Something that I am missing?)
Any ideas?
Instead of having the server worry about which tabs to send change notifications to you could have the tab that initiated the changes ignore the notification.
Two ways to do this come to mind:
After changing the content a tab will ingore all notifications for a brief period of time. (This will work fine unless changes on multiple tabs happen in a short amount of time.)
Have the tab create a "change id" that it sends to the server with the changes to xyz. The broadcast change notification contains this id and the sending tab recognizes it as the one it sent and ignores it.
You could experiment with HTML5 Visibility API with a fallback to window.onfocus & window.onblur events and suppress updating the page if it's currently visible/active.
Related
I am working on an angular web application where I want to show the user messages about different actions (like success, failure, etc.). These messages are set to clear automatically after 10 seconds, or when the user clicks the x button beside every alert. This is the easy part.
The difficult part is - I want to maintain these messages when the user navigates to different parts of the application. Something like facebook/google chat boxes.
I initially went through some ideas and decided on a solution where I stored the messages in an array in the local storage and every web page had code to look for these messages and display them if found.
But, I faced issues with the timing of the messages disappearing automatically (the timer reset to 10 seconds for all messages on page load). And also whenever the URL changed, the messages would go away and would load as new alerts after the new web page finished loading.
What is the correct way of doing this?
Whenever an alert appears in your application, you can store its timestamp together with the message. And on the page load call setTimeout for each alert with a callback of removing this message and the time difference between now and the timestamp+10sec.
Using your implementation, the timeout issue is easy to fix: when you store the alert, include an epiresAfter property as well. This property would be a timestamp af when you expect the the alert to no longer display.
Then you set up a timeout that either:
polls and checks the timeout, clearing it if the current time is past the expiration time
or a timeout that triggers after the difference between the expiration time and the current time.
I would consider treating your Message component as a singleton, i.e. it only ever has one instance. If the Message component is a child to other components, then it will be removed when it's parent component is removed from the DOM.
Consider moving your messages component to the root/app level (i.e. inside your AppComponent). This way as the user navigates around your app, the Messages component will always be visible. Further, the state of the notifications can be stored within this Messages component, so it's own timer wouldn't be affected by the rendering/removal of other components.
This all assumes you aren't using some sort of global store for your app state. If you are, consider Hyun Woo Krassilchikoff's answer which details implementing a global NGRX store.
The most efficient way is actually to use an NGRX store for your error message and a unique component in the main layout of your application rendering any message streamed from the store.
You'd need:
an action for pushing any kind of alert:. E.g. PushAlert
an action for clearing the alert. E.g ClearAlert
a reducer for managing both actions above
a selector to stream the portion of the state related the alerts
an effect which triggers ClearAlert 10 sec after detecting PushAlert
a component which displays the alert streamed by the store
Let's say we have a web app that only has one element, for example an image IMG1 and if an user clicks on it, it will change to another image IMG2 (this change should be visible only to the users that clicked and triggered the event).
Then, I have another event that triggers when a total of 100 users clicked on the image (or any other back-end related event), and this time I want to dynamically change the image to IMG1 (but now I want the change to happen and be visible to all the users of the website).
The confusion starts when I realise that for both events the function would be the same (changing the src of that HTML img element) yet I want it to have a different effect:
on the event of a user click change it for that user only.
on an outside event that doesn't involve a specific user, change it for all the users to see the same image.
How does this work? what is the thing that makes the difference between a HTML change that only affects the users locally (on their actions) and a change that has a global effect (to all the users).
UPDATE !!!
I should have been more specific with what I don't know.
I'm familiar with AJAX request and I already have the backend sorted.
In the frontend script I have an event listener for the event from the backend, and all my questions are actually about 'what and how to do it' after the event listener is triggered.
Now, what I want to do when this happens is to make some changes, the main one being to change that image IMG1 to IMG2 for all the users (as it would be a dynamic update to the website) but also:
I need that change to be permanent, so in a case of users reloading the page or new users coming in, they all should still see IMG2. (And the only time the image would change would be when the event listener on the frontend script will trigger again on the same backend event to change the image again (to IMG3) for example. And yes, in this example there is NO 'on click' request for the users to change the image, so ignore my example previous to the update.
Now to address your answers, I checked the web sockets stuff and it seems to be doing what I need if I run that 'on event' change of image to all sockets. Which only leaves me with 2 questions now:
1) Will this change that occurs on all sockets to change the image be permanent, so in a case of users reloading the page or new users coming in, they will all see the new image (IMG2) as a permanent change to the webpage ?
2) Regarding these type of permanent changes, isn't reactJs a way of doing such changes dynamically?
What would actually happen if on that event listener (for the backend event) I simply ignore all the web sockets stuff and run the same code of changing the src of the image ?
2.5) Because from how I see it, that event in the backend fires without any specific user input, thus is not linked with any user. So if I simply run the code on that event without websockets It should either do absolutely nothing (so no change for anyone) OR do the change for all the users (acting simply as a dynamic update to the webpage). How does this work?
I'm looking forward for your answers, and thank you all in advance!!!
The click event needs to be handled by an AJAX request, sending a message to the server and the server will handle that and respond. Upon the response, the first type of event is executed for the user.
On server-side you will need to have an event queue somewhere, maybe in the database. If you are using WebSockets, then you will have to execute the second type of event for all users if the request is met via WebSocket channels. If you are not using WebSockets, you will need to do polling from the browser. Anyway, you will need a counter on the server-side to be able to determine when the second type of event is met.
EDIT
Yes, WebSockets are the way to go unless there is a strong reason not to do so, like a boss saying he or she does not want the server to use WebSockets. With WebSockets you have a live channel between the server and the client browsers. You can use this channel to send the URL change to the client. On the other hand, the client will have to handle the change with Javascript, gathering the tags where the src is to be changed and change them. If you happen to have a class of changable for all such tags, then executing the change can be done with a function like this:
function changeSources(newSrc) {
var items = document.getElementsByClassName("changable");
for (var index = 0; index < items.length; index++)
items[index].src = newSrc;
}
However, this change will be effectuated only for the loaded page which was initially loaded and upon new loads, this, by itself will not use the new src. So you will have to solve that problem as well. A neat way to do it is to store the new src on the server before you send it out to the client via WebSocket and use this stored src as the src of those tags when the client requests for the HTML. So, your problem has two parts, the first is changing the src on already loaded pages and the second is making the change permanent.
ReactJS is a Framework. At this point we need to define the technical background, since ReactJS will use a possible solution from these.
WebSocket
https://www.npmjs.com/package/react-websocket
This is a WebSocket implementation. The best technical background here is to use WebSockets unless there is a very good reason not to do so.
Server notification system
https://www.npmjs.com/package/react-notifications
Server notification systems in general are one-way ticket roads. The server may send a notification, but the client has no such possibility.
Polling
The browser may periodically send HTTP requests to the server and this way it can receive the src change response. This is a good solution if WebSockets and server notification systems are not an option.
Forever frame
You can use an invisible iframe to be loaded forever, which will provide you with the possibility of sending real-time messages from the server to the client, but this is very hacky.
The difference may be between a front end, running in the browser, or the mobile app, of each user, which is local, and the back end, where you can share data between all users.
This can be implemented by, for example, firebase. Here is an example: Firebase - Multiple users simultaneously updating same object using its old value
This does not mean, obviously, that back end data is always shared... In many cases each user accesses his own copy of back end data that is stored in a database.
i have a logout function that sets the User offline in my DB (mysql), but if it just closes the browser, in my DB the User is still online despite it's not , How can i manage this? How can i set the User Offline without press the logout botton? Cheers in advance !
Ps: Yes, i'm using SESSION
You can do it in following ways.
1) send the Ajax request to server every 5 seconds to update the current time.
2) and where you want to show offline just get records where current time is more than 5 seconds ago.
HI the only reliable way is to set an interval that calls the server and logs it in a database
var timeout = 15000; //milliseconds
setInterval( function(){
$.post('yoursite/keepalive' );
}, timeout );
Then you check the session on the server side you need a simple database table with the user id and a timestamp of the last time keepalive was called, then you just get the current time an there id ( from the session ) and save that. Then you can check if its been more then say like 20 seconds you will know they are gone ( should be updated every 15 sec ). Obviously you would need to have this interval on every page of your site to accurately track a user.
Things such as checking the session time, and unload are not accurate enough,
Unload is fired when any page is closed, so for example,
we have a user that has 2 pages open, they close one of them. the other page is already loaded so there is no traffic between client and server, and no way to know that page is still open
for Session time we have a similar problem, say someone is reading a long post on your page, They need to use the facilities and leave the page open. 30 minutes go by the come back and continue reading the post for another 10 minutes. now maybe the session has expired maybe it hasn't the fact remains they are still looking at your site, and you have no way to know it.
An interval will continue as long as the page is open and there are no javascript issues. A disadvantage of this is it will also keep their session updated ( you can get around this by sending the user id along with the ajax and not using the session, but that has other complications ) because you have that 15 second update you can check anytime if it has been more then 15 seconds. Say you want to display a list of online users to your other forum users, you just query for everyone with a current timestamp from that table, easy beazy.
As for the amount of time for the interval, you have to strike a balance between performance ( network traffic ) and how granular you need to know the information, if it's ok to only know if they logged off within the last minute then use that, if you can wait 5 minutes to know etc....
Really the Crux of the problem is how the server, and a client communicate. Right there is no two way communication like if your on the phone. It's more like a walkies talky where you have to say 10-4 and let go of the button for the other guy to talk. Essentially a client will make a request, that request is fulfilled by the server. that is the end of the communication and the state. Subsequent request state is maintained by using session so the next request uses that session to 'remember' the client. other then that there is no communication between client and server. There is no way to know they hung up the phone, for example, but to ask them if they are still there. ( this is an oversimplification because you cant send a request from the server to ask, more like they have to tell you they are not there, unless you use node.js or something like that ).
As #David has mentioned you could track this based on last activity, for that you would just need to know when the session was last updated. One of the easiest ways is to move the session into a database handler via http://php.net/manual/en/function.session-set-save-handler.php that way you can access when they were last active.
Using this vs ajax really depends on what you need to know, and how accurately. There is also the content of your page to weigh in. If you have a site that makes requests frequently it would be a better approach because you save on network traffic, for example. However, if you have long post someone could be reading for 20-30 minutes but want to know more frequent then that use ajax.
You can do it in many ways:
Launch an AJAX call on onbeforeunload javascript event. Prompting for a confirmation "Windows is closing, are you sure? YES/NO" should give you enough time to set the flag in the db, just be sure that if the user clicks "NO" you should unset your flag
Check session time... Add a var in your PHP_SESSION that is updated at every user event. If it becomes older than a preset threshold (i.e. 5 minutes), you can safely assume the user is gone
Example for onbeforeunload
function myConfirmation() {
return 'Are you sure you want to quit?';
}
window.onbeforeunload = myConfirmation;
You can try the javascript beforeunload event:
window.onbeforeunload = function() {
// Some AJAX request to logout.php or whatever script handles the logout
}
It will trigger when the user attempts to close the current window.
Watch out though, even if the user closes a single tab (your page), the event will be triggered, so if there are other tabs opened, so the browser will be, and you'll still get your users logged out.
Also, if several tabs of your website are opened, and you close one of them, you'll get your users logged out, which may not be what you want, so you'll probably have to find a way around to fix it.
I have a website and currently I am handing the timeout on client side that is using Javascript, so that I no request is being made I log the user out, but I have seen people on SO suggesting the same approach , and I see a big lapse in it, suppose a user has 2 tabs open.
Tab 1:
www.MYSITE.com/welcome.php
Tab 2:
www.MYSITE.com/edit_profile.php
Now if user is on Tab 2 and he is editing the profile there, Tab 1 is idle that means user will be logged out/shown warning (the way you are handling Idle time).
So that doesn't seems to be consistent, in my thinking it should be on server side, is my approach correct?
One way could be,
In case of Ajax, Whenever you send any request to server, on server you can check if session expired using isset($_SESSION['variable']), send a response SESSION_TIMEOUT & then in ajax callback, check for this response.
If this is found, show user a message 'SESSION expired!' & redirect to initial page (may be login page).
I'm working on a messaging system like Facebook. I do have on left a list of conversation, and on right a box where i load the messages, just like facebook does.
The basic system is complete (PHP/MySQL), and here some information on how it is structured:
messages.php - Main page, based on url parameters. Rewrited with.htaccess:
Examples:
URL = http://www.domain.com/messages/ - Right Box: Display form to send new message.
URL = http://www.domain.com/messages/Username - Ajax call to getUserMessages.php to load Messages between Logged in user and
Username and show them on the Right Box.
getUserMessages.php - Get from database messages between Logged in user and user selected. It does Output HTML ready to be displayed.
Now the system is partially Ajaxified, and i want it to be, just like Facebook does.
At the moment the Ajaxified part is:
When a user is vieweing a conversation, it display automatically new messages, and also update the conversation list with the last message.
If the user is not viewing a conversation, it does get new messages received and update the conversation list.
This is done with a PUSH service, to give Real Time experience to users.
I want to improve this, and make it to act like that:
The user click on the Conversation List, and it load the messages on the right Box, and also change the URL on the Address Bar, withut reload the entire page.
I can easily do the part to load messages when user click a conversation, but before i start i have two question:
1. How i do change the Address URL while displaying a User Conversation WITHOUT reload the page?
I found the answer.
2. How i do cache the conversations ? So if a user switch between two conversation, it does not call again the php file and query the database for all the messages, but appending only new messages (Maybe via another php File to fetch only Unread Messages)
EDIT
I comed up with a solution:
When a user open a conversation, i cache the entire Ajax response (that is HTML) in a variable, like messages-n, Where n is the user_id of the conversation selected, then if the user click again on that conversation, i check if messages-n is set, if it is, i print it and run an ajax request to get only unread message and append them.
That's only in my mind i didn't made it to actual code.
Could work well?
Solved 1/2 :
1. To change Address URL i'm using the HTML5 .pushState() event.
Since HTML5 Browsers implement the pushState method in different way, to have a Cross-Browser solution, and have support for HTML4 browsers with hash Fallback, i used Hystory.js.
2. To cache messages, i haven't found a solution yet, nor i tried to do it for now.
But as #Christopher suggested, i changed the Ajax response from HTML to Json.
If i find it i will update my answer.