-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Duplicate incident tabs is bad #92
Comments
Yes, using HTML5 Local Storage (see e.g. http://www.w3schools.com/html/html5_webstorage.asp) Basically, you can have a cache of incidentIds -> incident JSON objects that is shared across tabs that are accessing the same domain. You can attach js event listeners on updates to the cache. See e.g. the following regarding using event listeners
Note that this doesn't handle a (I assume related) problem: if my partner and I both have the incident open in a tab on each of our consoles, my updates will not be seen by her, nor vice versa, so we could clobber each other's state. ExampleHere's a sketch of an example update scenario for local tab sharing. First off, I haven't examined your code so am not familiar with the architecture you've set up. I assume you've got a JS method (let's call it Also, I apologize in advance but my jQuery and etc. is pretty rusty; I'm more of a Java guy. This probably has both syntax and logic errors but my javascript is very seat-of-the-pants flying. This method can look something like this: void sendNewData(event) {
userState = {};
userState["summary"] = $("#summary").value();
... // build a JSON object containing a complete view of the local state
// Before waiting for a response from the server, immediately update the local cache with
// our new data for this incident
localStorage.setItem("incident#" + myIncidentId, userState);
// Then send the server our view of the incident.
$.post({
url: "http://" + myServer + "/v1/incidents/" + myIncidentId, // or whatever the POST target is.
data: userState, // send this json map as the POST payload
success: handleAjaxUpdate, // a method that handles latest state from server
error: handleError, // a method that handles a 401 or other err response from server
... // other args to the ajax post method
});
}
/**
* Re-render all fields that need rendering based on a complete view of an incident
* @param incident a hash of data that represents the current incident.
*/
function updateWindowFromData(incident) {
$("#summary").value(incident["summary"]);
// and set other local fields as appropriate.
}
/**
* Handle a response from the server.
* @param data a hash of data from the server that represents the current incident.
*/
function handleAjaxUpdate(data) {
// set local fields based on the incoming data. The server takes precedence over local state.
updateWindowFromData(data);
// finally, since the server's word is law, update our local cache.
localStorage.setItem("incident#" + myIncidentId, data);
} You need to add a listener to request a function fires when local storage is updated. Do something like this onLoad (e.g., in a jQuery initializer): /** Make this method called onLoad */
function initPage() {
// TODO: I think there's a better jquery-ish way to do this.
window.addEventListener('storage', handleStorageUpdate, false);
}
/** Fired any time the localStorage is updated by any tab. */
function handleStorageUpdate() {
// Get our incident...
cachedIncident = localStorage.getItem("incident#" + myIncidentId);
// ...and update the view.
updateWindowFromData(cachedIncident);
} |
I think the above is actually a mild oversimplification; I'm pretty sure you can't actually use an Similarly, |
Multi-User SynchronizationAs I alluded to above, this won't handle the situation where Operators Hubcap and Bucket both have the same incident open in their terminal. For that, you'll need to
As a disclaimer, I have no idea how the current code works, so apologies if some of this is redundant to your current implementation. I honestly haven't looked at it, which is a dangerous place to propose solution implementations from ;) Partial Updates on the client sideYour update method(s) that are fired e.g. function updateSummary() {
data = {}
data["id"] = myIncidentId;
data["summary"] = $("#summary").value();
$.ajax({ // Use $.ajax, not $.post to control http method.
method: "PATCH", // Use PATCH not POST to, in a REST-ful way, indicate that you are sending
// a partial update and not a complete object representation.
data: data, // Use our data hash created above: { id: 123, summary: "Something bad happened" }
success: handleAjaxUpdate, // Use method from above; server responds w/ complete object
error: handleError, // Handle a 4xx or 5xx response from server
... // other arguments as required
});
}
// And add methods like addNewRanger(), addIncidentType(), removeIncidentType(),
// addComment(), etc as required. Server-side updatesLeft as an exercise to the reader. Basically, don't persist the incoming object literally, you must This should rely on the HTTP PATCH method, not POST, since it's a partial update. Since a partial This should return the complete source-of-truth view of the incident to the caller as a json object. Polling for updatesHubcap sends an update. How does Bucket get it? In a jQuery initializer or other method fired function updateTimerHandler() {
$.ajax({
method: "GET",
url: "http://" + myServer + "/v1/incidents/" + myIncidentId, // n.b., https is better.
success: handleAjaxUpdate, // as before
error: handleError, // as before...
... // other args.
} Handling race conditionsAt this point, there may be a race between server-side data input and client-side data input. For bonus points, the localStorage could contain a hash containing the current incident AND the timestamp at which that incident object was created. You should update the localStorage iff your new incident's timestamp is greater than the timestamp associated with the incident currently in storage. Final thoughts
|
Other approaches:
Both of these approaches work equally well for multiple tabs in the same browser or multiple users on different browsers. In either case you can poll periodically if you want to warn users in advance, but polling isn't necessary in either case. |
@kimballa Thanks for all the references, that will help me ramp up on a few key things. Note the server doesn't generate events yet, but thats #46. What I had intended for this ticket to be for, though, is that if you click on incident 43 in the dispatch queue, which opens a tab, and then click it again, what I'd like is that to that take you to the already open tab instead of creating a new tab for incident 43. I don't know how to do that. |
I did a quick google search for that one too, since it also seems like a useful improvement. That having been said, I also open new tabs and url-hack my way to things I need to see when I can't navigate a UI quickly enough, so please bear in mind that it will likely be impossible to prevent duplicate tabs in a client's browser :) |
@flwyd these are both good suggestions too. I think the main difference is the question "should stale data merging/patching be done client-side or server-side?" The logic required will be effectively the same, the question boils down to where to perform it. From an idempotence standpoint,the consistency token could also be a hash (md5, sha1, etc) of the data payload, which is less sensitive to jitter than timestamps |
@wsanchez I think you're looking for Note that this means you have to use onclick handlers rather than letting actual links do their job, which can be annoying when someone wants to do actual linky things like right-click-copy or open two copies of an incident to compare old and new values. You might be able to use actual |
Oh, wait. |
@flwyd: Oh, cool… I thought that only the reserved words ( |
OK I've adopted a library called lscache, which is has a memcache-like interface and seems appropriate at least for caching personnel and incident types. It has the nice property that I don't have to write any cache expiration time logic myself. I tested it with personnel and seems to work well enough to close #95. I'm not sure I can use it for incidents if I also want to be able to subscribe to change events, though. |
Back to this bug, the code to change is here, which is to say, we need to specify a context there. |
Duplicate incident tabs is bad because out of sync.
Is it possible to prevent this?
The text was updated successfully, but these errors were encountered: