Remember scroll offset of lanes when refreshing board view

When the board object in the board detail view is reloaded (which is
quite a regular occurrence to help prevent stale data being used), the
scroll position of lanes is lost.

This is a minor inconvenience for small boards with only a few cards
and a short distance to scroll, but is frustrating for large boards
with many cards per lane, since it can be hard to find the old scroll
location. The issue is compounded by long load times of large boards
making it seem like the lane scrollbars reset with no apparent reason.

This commit solves this issue by adding support to remember the scroll
position of each lane when updating the board object, and re-scrolling
the newly rendered lanes to the correct position.

Change-Id: I75a6703b2f43bed8cdc4643f6a103f95e1fa61ae
This commit is contained in:
Adam Coldrick 2019-02-18 19:09:42 +00:00
parent 884ca37c39
commit 1c048042ed
4 changed files with 49 additions and 4 deletions

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2015-2016 Codethink Limited
* Copyright (c) 2019 Adam Coldrick
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
@ -22,6 +23,8 @@ angular.module('sb.board').controller('BoardDetailController',
BoardHelper, DueDate, $document, User, $q, moment) {
'use strict';
$scope.board = new Board();
/**
* Load the board. If onlyContents is true then assume $scope.board
* is a board and reload its contents.
@ -35,6 +38,7 @@ angular.module('sb.board').controller('BoardDetailController',
};
});
Board.get(params, function(board) {
var offsets = BoardHelper.recordLaneScrollbars($scope.board);
$scope.board = board;
$scope.owners = [];
$scope.users = [];
@ -44,6 +48,7 @@ angular.module('sb.board').controller('BoardDetailController',
angular.forEach(board.users, function(id) {
$scope.users.push(User.get({id: id}));
});
BoardHelper.scrollLanes(board, offsets);
});
}

View File

@ -18,7 +18,7 @@
* A service to help with use of a kanban board.
*/
angular.module('sb.board').factory('BoardHelper',
function($document, $window, Worklist) {
function($document, $window, $timeout, Worklist) {
'use strict';
/**
@ -73,9 +73,47 @@ angular.module('sb.board').factory('BoardHelper',
});
}
/**
* Function to record scrollbar positions for the lanes of a board.
*
* This is used to track where a user has scrolled each lane to before
* refreshing the board UI. It returns a mapping of lane IDs to scroll
* offsets which can later be used to re-scroll to the correct point.
*/
function recordLaneScrollbars(board) {
var scrollbars = {};
angular.forEach(board.lanes, function(lane) {
var elem = $document[0].getElementById('lane-' + lane.id);
if (!!elem) {
scrollbars[lane.id] = elem.scrollTop;
}
});
return scrollbars;
}
/**
* Function to scroll lanes to a previously recorded position.
*
* Takes a board and a mapping of lane IDs to scroll offsets as
* produced by `recordLaneScrollbars` and scrolls the corresponding
* lane containers by the given offsets.
*/
function scrollLanes(board, scrollbars) {
angular.forEach(board.lanes, function(lane) {
$timeout(function() {
var elem = $document[0].getElementById('lane-' + lane.id);
if (!!elem) {
elem.scrollTop = scrollbars[lane.id];
}
});
});
}
return {
maybeScrollContainer: scrollFunction,
moveCard: moveCard
moveCard: moveCard,
recordLaneScrollbars: recordLaneScrollbars,
scrollLanes: scrollLanes
};
}
);

View File

@ -15,7 +15,8 @@
-->
<div as-sortable="cardsSortable"
ng-model="lane.worklist.items"
class="kanban-lane-contents">
class="kanban-lane-contents"
id="lane-{{ lane.id }}">
<div class="kanban-card"
ng-class="{'kanban-card-due': isDue(item), 'kanban-card-late': isLate(item)}"
as-sortable-item

View File

@ -13,7 +13,8 @@
~ License for the specific language governing permissions and limitations
~ under the License.
-->
<div class="kanban-lane-contents-readonly">
<div class="kanban-lane-contents-readonly"
id="lane-{{ lane.id }}">
<div class="kanban-card-readonly"
ng-class="{'kanban-card-due': isDue(item), 'kanban-card-late': isLate(item)}"
ng-repeat="item in lane.worklist.items"