diff --git a/tools/jslint/check-all.js b/tools/jslint/check-all.js index 6e0a0dcc5f..db06ab4434 100644 --- a/tools/jslint/check-all.js +++ b/tools/jslint/check-all.js @@ -30,7 +30,7 @@ var globals = + ' add_messages' + ' subject_dict people_dict' + ' keep_pointer_in_view move_pointer_at_page_top_and_bottom' - + ' respond_to_message recenter_view' + + ' respond_to_message recenter_view last_viewport_movement_direction' + ' scroll_to_selected disable_pointer_movement get_private_message_recipient' + ' load_old_messages' + ' at_top_of_viewport at_bottom_of_viewport within_viewport' diff --git a/zephyr/static/js/hotkey.js b/zephyr/static/js/hotkey.js index 1c26a5c85b..3fc2a327ae 100644 --- a/zephyr/static/js/hotkey.js +++ b/zephyr/static/js/hotkey.js @@ -4,6 +4,16 @@ var exports = {}; exports.in_scroll_caused_by_keypress = false; +function wrap_directional_key_with_movement_direction(arrow_key_func) { + if (arrow_key_func === rows.next_visible) { + last_viewport_movement_direction = 1; + } else if (arrow_key_func === rows.prev_visible) { + last_viewport_movement_direction = -1; + } + + return arrow_key_func; +} + var directional_hotkeys = { 40: rows.next_visible, // down arrow 106: rows.next_visible, // 'j' @@ -108,7 +118,8 @@ function process_hotkey(e) { } if (directional_hotkeys.hasOwnProperty(code)) { - next_row = directional_hotkeys[code](current_msg_list.selected_row()); + next_row = wrap_directional_key_with_movement_direction( + directional_hotkeys[code])(current_msg_list.selected_row()); if (next_row.length !== 0) { exports.in_scroll_caused_by_keypress = true; current_msg_list.select_id(rows.id(next_row), {then_scroll: true}); diff --git a/zephyr/static/js/ui.js b/zephyr/static/js/ui.js index 27d7570393..d353541a12 100644 --- a/zephyr/static/js/ui.js +++ b/zephyr/static/js/ui.js @@ -617,6 +617,7 @@ $(function () { // scroll handler, but when we're at the top or bottom of the // page, the pointer may still need to move. move_pointer_at_page_top_and_bottom(delta); + last_viewport_movement_direction = delta; }); $(window).mousewheel(function (e, delta) { diff --git a/zephyr/static/js/zephyr.js b/zephyr/static/js/zephyr.js index 2fc23fd6ef..f6d8e4ec81 100644 --- a/zephyr/static/js/zephyr.js +++ b/zephyr/static/js/zephyr.js @@ -27,6 +27,9 @@ var disable_pointer_movement = false; // when Home is next clicked by the user var recenter_pointer_on_display = false; var suppress_scroll_pointer_update = false; +// Includes both scroll and arrow events. Negative means scroll up, +// positive means scroll down. +var last_viewport_movement_direction = 1; var furthest_read = -1; var server_furthest_read = -1; @@ -88,7 +91,21 @@ function recenter_view(message) { // If this logic changes, above_view_threshold andd // below_view_threshold must also change. - var selected_row_top = current_msg_list.selected_row().offset().top; + var selected_row = current_msg_list.selected_row(); + var selected_row_top = selected_row.offset().top; + + if ((above_view_threshold(message, true) && + (last_viewport_movement_direction >= 0)) || + (below_view_threshold(message) && + (last_viewport_movement_direction <= 0))) { + // If the message you're trying to center on is already in view AND + // you're already trying to move in the direction of that message, + // don't try to recenter. This avoids disorienting jumps when the + // pointer has gotten itself outside the threshold (e.g. by + // autoscrolling). + return; + } + if (above_view_threshold(message, true)) { // We specifically say useTop=true here, because suppose you're using // the arrow keys to arrow up and you've moved up to a huge message.