Browse Source

Improved append message animation, improved dialog switch speed

master
Igor Zhukov 11 years ago
parent
commit
36eaccf950
  1. 10
      app/css/app.css
  2. 36
      app/js/controllers.js
  3. 54
      app/js/directives.js
  4. 65
      app/js/services.js
  5. 2
      app/partials/im.html
  6. 6
      gulpfile.js

10
app/css/app.css

@ -834,6 +834,16 @@ a.im_dialog:hover .im_dialog_date { @@ -834,6 +834,16 @@ a.im_dialog:hover .im_dialog_date {
bottom: 0;
width: 100%;
}
.im_history_appending {
-webkit-transition: all 0.2s;
-moz-transition: all 0.2s;
-ms-transition: all 0.2s;
-o-transition: all 0.2s;
transition: all 0.2s;
}
.im_history {
/*padding: 20px 0 0 3px;*/
padding: 20px 0 0 0;

36
app/js/controllers.js

@ -223,9 +223,7 @@ angular.module('myApp.controllers', []) @@ -223,9 +223,7 @@ angular.module('myApp.controllers', [])
var offset = 0,
maxID = 0,
hasMore = false,
startLimit = 20,
limit = 100;
hasMore = false;
MtpApiManager.invokeApi('account.updateStatus', {offline: false});
$scope.$on('dialogs_need_more', function () {
@ -278,14 +276,14 @@ angular.module('myApp.controllers', []) @@ -278,14 +276,14 @@ angular.module('myApp.controllers', [])
maxID = 0;
hasMore = false;
AppMessagesManager.getDialogs($scope.search.query, maxID, startLimit).then(function (dialogsResult) {
AppMessagesManager.getDialogs($scope.search.query, maxID).then(function (dialogsResult) {
$scope.dialogs = [];
if (dialogsResult.dialogs.length) {
offset += startLimit;
offset += dialogsResult.dialogs.length;
maxID = dialogsResult.dialogs[dialogsResult.dialogs.length - 1].top_message;
hasMore = offset < dialogsResult.count;
hasMore = dialogsResult.count === null || offset < dialogsResult.count;
angular.forEach(dialogsResult.dialogs, function (dialog) {
$scope.dialogs.push(AppMessagesManager.wrapForDialog(dialog.top_message, dialog.unread_count));
@ -295,7 +293,7 @@ angular.module('myApp.controllers', []) @@ -295,7 +293,7 @@ angular.module('myApp.controllers', [])
$scope.$broadcast('ui_dialogs_change');
if (!$scope.search.query) {
AppMessagesManager.getDialogs('', maxID, limit);
AppMessagesManager.getDialogs('', maxID);
}
}, function (error) {
@ -312,10 +310,10 @@ angular.module('myApp.controllers', []) @@ -312,10 +310,10 @@ angular.module('myApp.controllers', [])
return;
}
AppMessagesManager.getDialogs($scope.search.query, maxID, limit).then(function (dialogsResult) {
offset += limit;
AppMessagesManager.getDialogs($scope.search.query, maxID).then(function (dialogsResult) {
offset += dialogsResult.dialogs.length;
maxID = dialogsResult.dialogs[dialogsResult.dialogs.length - 1].top_message;
hasMore = offset < dialogsResult.count;
hasMore = dialogsResult.count === null || offset < dialogsResult.count;
angular.forEach(dialogsResult.dialogs, function (dialog) {
$scope.dialogs.push(AppMessagesManager.wrapForDialog(dialog.top_message, dialog.unread_count));
@ -359,8 +357,6 @@ angular.module('myApp.controllers', []) @@ -359,8 +357,6 @@ angular.module('myApp.controllers', [])
offset = 0,
hasMore = false,
maxID = 0,
startLimit = 20,
limit = 50,
inputMediaFilters = {
photos: 'inputMessagesFilterPhotos',
video: 'inputMessagesFilterVideo',
@ -421,12 +417,12 @@ angular.module('myApp.controllers', []) @@ -421,12 +417,12 @@ angular.module('myApp.controllers', [])
var inputMediaFilter = $scope.mediaType && {_: inputMediaFilters[$scope.mediaType]},
getMessagesPromise = inputMediaFilter
? AppMessagesManager.getSearch($scope.curDialog.inputPeer, '', inputMediaFilter, maxID, limit)
: AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID, limit);
? AppMessagesManager.getSearch($scope.curDialog.inputPeer, '', inputMediaFilter, maxID)
: AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID);
getMessagesPromise.then(function (historyResult) {
offset += limit;
hasMore = offset < historyResult.count;
offset += historyResult.history.length;
hasMore = historyResult.count === null || offset < historyResult.count;
maxID = historyResult.history[historyResult.history.length - 1];
angular.forEach(historyResult.history, function (id) {
@ -447,7 +443,7 @@ angular.module('myApp.controllers', []) @@ -447,7 +443,7 @@ angular.module('myApp.controllers', [])
var curJump = ++jump,
inputMediaFilter = $scope.mediaType && {_: inputMediaFilters[$scope.mediaType]},
getMessagesPromise = inputMediaFilter
? AppMessagesManager.getSearch($scope.curDialog.inputPeer, '', inputMediaFilter, maxID, startLimit)
? AppMessagesManager.getSearch($scope.curDialog.inputPeer, '', inputMediaFilter, maxID)
: AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID);
$scope.historyEmpty = false;
@ -458,7 +454,7 @@ angular.module('myApp.controllers', []) @@ -458,7 +454,7 @@ angular.module('myApp.controllers', [])
offset += historyResult.history.length;
$scope.historyEmpty = !historyResult.count;
hasMore = offset < historyResult.count;
hasMore = historyResult.count === null || offset < historyResult.count;
maxID = historyResult.history[historyResult.history.length - 1];
updateHistoryPeer();
@ -702,7 +698,7 @@ angular.module('myApp.controllers', []) @@ -702,7 +698,7 @@ angular.module('myApp.controllers', [])
$timeout(function () {
var text = $scope.draftMessage.text;
if (!text.length) {
if (!angular.isString(text) || !text.length) {
return false;
}
@ -982,7 +978,7 @@ angular.module('myApp.controllers', []) @@ -982,7 +978,7 @@ angular.module('myApp.controllers', [])
photo: {_: 'inputChatPhotoEmpty'}
}).then(function (updateResult) {
onStatedMessage(updateResult);
}).['finally'](function () {
})['finally'](function () {
$scope.photo.updating = false;
});
};

54
app/js/directives.js

@ -149,6 +149,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -149,6 +149,7 @@ angular.module('myApp.directives', ['myApp.filters'])
function link (scope, element, attrs) {
var historyWrap = $('.im_history_wrap', element)[0],
historyMessagesEl = $('.im_history_messages', element)[0],
historyEl = $('.im_history', element)[0],
scrollableWrap = $('.im_history_scrollable_wrap', element)[0],
scrollable = $('.im_history_scrollable', element)[0],
@ -173,8 +174,19 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -173,8 +174,19 @@ angular.module('myApp.directives', ['myApp.filters'])
}, delay || 0);
}
var animated = true,
var transform = false,
trs = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform'],
i = 0;
for (i = 0; i < trs.length; i++) {
if (trs[i] in historyMessagesEl.style) {
transform = trs[i];
break;
}
}
var animated = transform ? true : false,
curAnimation = false;
scope.$on('ui_history_append', function (e, options) {
if (!atBottom && !options.my) {
return;
@ -186,26 +198,29 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -186,26 +198,29 @@ angular.module('myApp.directives', ['myApp.filters'])
$(scrollableWrap).addClass('im_history_to_bottom');
}
var wasH = scrollableWrap.scrollHeight;
onContentLoaded(function () {
if (animated) {
curAnimation = true;
$(scrollableWrap).stop().animate({
scrollTop: scrollableWrap.scrollHeight - scrollableWrap.clientHeight
}, {
duration: 200,
always: function () {
updateScroller();
$(historyMessagesEl).removeClass('im_history_appending');
scrollableWrap.scrollTop = scrollableWrap.scrollHeight;
$(historyMessagesEl).css(transform, 'translate(0px, ' + (scrollableWrap.scrollHeight - wasH) + 'px)');
setTimeout(function () {
$(historyMessagesEl).addClass('im_history_appending');
$(historyMessagesEl).css(transform, 'translate(0px, 0px)');
setTimeout(function () {
curAnimation = false;
}
});
updateScroller();
$(historyMessagesEl).removeClass('im_history_appending');
updateBottomizer();
}, 300);
}, 0);
} else {
$(scrollableWrap).removeClass('im_history_to_bottom');
$(scrollable).css({bottom: ''});
scrollableWrap.scrollTop = scrollableWrap.scrollHeight;
$(historyWrap).nanoScroller();
updateBottomizer();
}
$(historyWrap).nanoScroller();
});
});
@ -253,6 +268,10 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -253,6 +268,10 @@ angular.module('myApp.directives', ['myApp.filters'])
updateScroller();
moreNotified = false;
$timeout(function () {
$(scrollableWrap).trigger('scroll');
})
});
});
@ -302,6 +321,9 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -302,6 +321,9 @@ angular.module('myApp.directives', ['myApp.filters'])
minHeight: historyH - 44
});
updateBottomizer();
if (heightOnly == true) return;
if (atBottom) {
onContentLoaded(function () {
@ -312,6 +334,14 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -312,6 +334,14 @@ angular.module('myApp.directives', ['myApp.filters'])
updateScroller(100);
}
function updateBottomizer () {
$(historyMessagesEl).css({marginTop: 0});
if (historyMessagesEl.offsetHeight <= scrollableWrap.offsetHeight) {
$(historyMessagesEl).css({marginTop: (scrollableWrap.offsetHeight - historyMessagesEl.offsetHeight - 20 - 44) + 'px'});
}
$(historyWrap).nanoScroller();
}
$($window).on('resize', updateSizes);
updateSizes();

65
app/js/services.js

@ -690,19 +690,18 @@ angular.module('myApp.services', []) @@ -690,19 +690,18 @@ angular.module('myApp.services', [])
}
}
if (curDialogStorage.count !== null && (
curDialogStorage.dialogs.length >= offset + limit ||
curDialogStorage.dialogs.length == curDialogStorage.count
)) {
if (curDialogStorage.count !== null && curDialogStorage.dialogs.length == curDialogStorage.count ||
curDialogStorage.dialogs.length >= offset + (limit || 1)
) {
return $q.when({
count: curDialogStorage.count,
dialogs: curDialogStorage.dialogs.slice(offset, offset + limit)
dialogs: curDialogStorage.dialogs.slice(offset, offset + (limit || 20))
});
}
var deferred = $q.defer();
limit = limit || 20;
MtpApiManager.invokeApi('messages.getDialogs', {
return MtpApiManager.invokeApi('messages.getDialogs', {
offset: offset,
limit: limit,
max_id: maxID || 0
@ -735,17 +734,17 @@ angular.module('myApp.services', []) @@ -735,17 +734,17 @@ angular.module('myApp.services', [])
top_message: dialog.top_message,
unread_count: dialog.unread_count
});
if (historiesStorage[peerID] === undefined) {
historiesStorage[peerID] = {count: null, history: [dialog.top_message], pending: []}
}
});
deferred.resolve({
return {
count: curDialogStorage.count,
dialogs: curDialogStorage.dialogs.slice(offset, offset + limit)
};
});
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
}
function fillHistoryStorage (inputPeer, maxID, fullLimit, historyStorage) {
@ -807,11 +806,8 @@ angular.module('myApp.services', []) @@ -807,11 +806,8 @@ angular.module('myApp.services', [])
var foundDialog = getDialogByPeerID(peerID);
if (foundDialog && foundDialog[0] && foundDialog[0].unread_count > 1) {
unreadLimit = Math.min(1000, foundDialog[0].unread_count);
limit = Math.max(20, unreadLimit + 2);
}
limit = unreadLimit;
}
if (!limit) {
limit = 20;
}
if (maxID > 0) {
@ -822,17 +818,22 @@ angular.module('myApp.services', []) @@ -822,17 +818,22 @@ angular.module('myApp.services', [])
}
}
if (historyStorage.count !== null && (
historyStorage.history.length >= offset + limit ||
historyStorage.history.length == historyStorage.count
)) {
if (historyStorage.count !== null && historyStorage.history.length == historyStorage.count ||
historyStorage.history.length >= offset + (limit || 1)
) {
return $q.when({
count: historyStorage.count,
history: resultPending.concat(historyStorage.history.slice(offset, offset + limit)),
history: resultPending.concat(historyStorage.history.slice(offset, offset + (limit || 20))),
unreadLimit: unreadLimit
});
}
if (unreadLimit) {
limit = Math.max(20, unreadLimit + 2);
}
limit = limit || 20;
return fillHistoryStorage(inputPeer, maxID, limit, historyStorage).then(function () {
offset = 0;
if (maxID > 0) {
@ -858,7 +859,7 @@ angular.module('myApp.services', []) @@ -858,7 +859,7 @@ angular.module('myApp.services', [])
filter: inputFilter || {_: 'inputMessagesFilterEmpty'},
min_date: 0,
max_date: 0,
limit: limit,
limit: limit || 20,
max_id: maxID || 0
}).then(function (searchResult) {
AppUsersManager.saveApiUsers(searchResult.users);
@ -922,7 +923,7 @@ angular.module('myApp.services', []) @@ -922,7 +923,7 @@ angular.module('myApp.services', [])
if (!foundDialog[0] || !foundDialog[0].unread_count) {
if (!historyStorage && !historyStorage.history.length) {
if (!historyStorage || !historyStorage.history.length) {
return false;
}
@ -2098,18 +2099,10 @@ angular.module('myApp.services', []) @@ -2098,18 +2099,10 @@ angular.module('myApp.services', [])
return urlPromises[url];
}
var deferred = $q.defer();
$http.get(url, {responseType: 'blob', transformRequest: null})
.then(
function (response) {
deferred.resolve(window.webkitURL.createObjectURL(response.data));
}, function (error) {
deferred.reject(error);
}
);
return urlPromises[url] = deferred.promise;
return urlPromises[url] = $http.get(url, {responseType: 'blob', transformRequest: null})
.then(function (response) {
return window.webkitURL.createObjectURL(response.data);
});
}
return {

2
app/partials/im.html

@ -88,8 +88,10 @@ @@ -88,8 +88,10 @@
<div class="im_history" ng-class="{im_history_selectable: selectActions}">
<div class="im_history_empty" ng-show="historyEmpty &amp;&amp; !history.length">No messages to display</div>
<div class="im_history_messages">
<div class="im_history_message_wrap" my-message ng-repeat="historyMessage in history"></div>
</div>
</div>
<div class="im_history_typing_wrap">
<div class="im_history_typing" ng-show="typing.user">

6
gulpfile.js

@ -27,12 +27,18 @@ gulp.task('usemin', ['templates', 'enable-production'], function() { @@ -27,12 +27,18 @@ gulp.task('usemin', ['templates', 'enable-production'], function() {
.pipe(gulp.dest('dist'));
});
// ulimit -n 10240 on OS X
gulp.task('imagemin', function() {
return gulp.src(['app/img/**/*', '!app/img/screenshot*', '!app/img/*.wav'])
.pipe($.imagemin())
.pipe(gulp.dest('dist/img'));
});
gulp.task('copy-images', function() {
return gulp.src(['app/img/**/*', '!app/img/screenshot*', '!app/img/*.wav'])
.pipe(gulp.dest('dist/img'));
});
gulp.task('copy', function() {
return es.concat(
gulp.src(['app/favicon.ico', 'app/favicon_unread.ico', 'app/manifest.webapp', 'app/manifest.json', 'app/**/*worker.js'])

Loading…
Cancel
Save