Make the client reload the page when it detects a server restart

If the client is not composing a message, we can just force a page
reload.  However, if he is composing a message, we must preserve that
message while still reloading as soon as possible.

We take the following approach: if the client has not completed the
composition after 5 minutes, do a compose-preserving reload
(described below).  If he sends the message before the timeout
expires, reload the page after a successful send.  If the send fails
(not due to server timeout), however, we do a compose-perserving
reload in case the error was due to the data format changing.  If the
send failed due to server timeout, we don't reload because the reload
will probably also fail.

In a compose-preserving reload, we redirect to an URI that has a
fragment indicating we are doing a reload and containing all the
necessary information for restoring the compose window to its
previous state.  On page load, we check the fragment to see if we
just did a compose-preserving reload, and, if we did, we restore the
compose window (or just try the send again in the case of send
failure).  The URI fragment looks like:

(imported from commit af4eeb3930c24118e088057d4da456748fbd2229)
This commit is contained in:
Zev Benjamin 2012-10-16 15:16:06 -04:00
parent 103bf321b4
commit fab64fd7b0
2 changed files with 88 additions and 3 deletions

View File

@ -11,8 +11,9 @@ var globals =
// compose.js
+ ' show_compose hide_compose toggle_compose clear_compose_box compose_button'
+ ' composing_message compose_stream_name validate_message'
+ ' status_classes'
+ ' composing_message composing_stream_message compose_stream_name'
+ ' compose_stream_name compose_subject compose_recipient compose_message'
+ ' validate_message status_classes'
// dom_access.js
+ ' get_first_visible get_last_visible get_next_visible get_prev_visible'
@ -45,6 +46,7 @@ var globals =
+ ' select_message select_message_by_id'
+ ' scroll_to_selected select_and_show_by_id'
+ ' selected_message selected_message_id'
+ ' reload_app reload_app_preserving_compose'
+ ' at_top_of_viewport at_bottom_of_viewport'
+ ' viewport'
;

View File

@ -5,6 +5,7 @@ var people_hash = {};
var selected_message_class = 'selected_message';
var viewport = $(window);
var app_needs_reload = false;
$(function () {
var i;
@ -19,8 +20,17 @@ $(function () {
send_status.hide();
hide_compose();
buttons.removeAttr('disabled');
if (app_needs_reload) {
reload_app();
return;
}
},
error: function (xhr) {
error: function (xhr, error_type) {
if (error_type !== 'timeout' && app_needs_reload) {
// The error might be due to the server changing
reload_app_preserving_compose(true);
return;
}
var response = "Error sending message";
if (xhr.status.toString().charAt(0) === "4") {
// Only display the error response for 4XX, where we've crafted
@ -501,6 +511,75 @@ function add_messages(data) {
update_autocomplete();
}
function reload_app() {
// If we can, reload the page immediately
if (! composing_message()) {
window.location.reload(true);
}
// If the user is composing a message, wait until he's done or
// until a timeout expires
app_needs_reload = true;
setTimeout(function () { reload_app_preserving_compose(false); },
1000 * 60 * 5); // 5 minutes
}
function reload_app_preserving_compose(send_after_reload) {
var url = "#reload:send_after_reload=" + Number(send_after_reload);
if (composing_stream_message()) {
url += "+msg_type=stream";
url += "+stream=" + encodeURIComponent(compose_stream_name());
url += "+subject=" + encodeURIComponent(compose_subject());
} else {
url += "+msg_type=huddle";
url += "+recipient=" + encodeURIComponent(compose_recipient());
}
url += "+msg="+ encodeURIComponent(compose_message());
window.location.replace(url);
window.location.reload(true);
}
// Check if we're doing a compose-preserving reload. This must be
// done before the first call to get_updates
$(function () {
var location = window.location.toString();
window.location = '#';
var fragment = location.substring(location.indexOf('#') + 1);
if (fragment.search("reload:") !== 0) {
return;
}
fragment = fragment.replace(/^reload:/, "");
var keyvals = fragment.split("+");
var vars = {};
$.each(keyvals, function (idx, str) {
var pair = str.split("=");
vars[pair[0]] = decodeURIComponent(pair[1]);
});
var tab;
var send_now = parseInt(vars.send_after_reload, 10);
if (vars.msg_type === "stream") {
if (! send_now) {
show_compose("stream", $("#new_message_content"));
}
compose_stream_name(vars.stream);
compose_subject(vars.subject);
} else {
if (! send_now) {
show_compose("huddle", $("#new_message_content"));
}
show_compose("huddle", $("#new_message_content"));
compose_recipient(vars.recipient);
}
compose_message(vars.msg);
if (send_now) {
$("#compose form").ajaxSubmit();
}
});
var get_updates_xhr;
var get_updates_timeout;
function get_updates() {
@ -514,6 +593,10 @@ function get_updates() {
received.failures = 0;
$('#connection-error').hide();
if (data.server_generation > server_generation) {
reload_app();
}
add_messages(data);
get_updates_timeout = setTimeout(get_updates, 0);
},