Skip to content
This repository was archived by the owner on Oct 25, 2023. It is now read-only.

Commit ba25a32

Browse files
committed
Implemented universal scroller
Added keyboard hide/show Added enter slash button Added scroll to keyboard
1 parent f49f710 commit ba25a32

File tree

8 files changed

+358
-105
lines changed

8 files changed

+358
-105
lines changed

‎app/js/controllers.js

+23-3
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
995995
$scope.selectedFlush = selectedFlush;
996996
$scope.botStart = botStart;
997997

998+
$scope.replyKeyboardToggle = replyKeyboardToggle;
999+
9981000
$scope.toggleEdit = toggleEdit;
9991001
$scope.toggleMedia = toggleMedia;
10001002
$scope.returnToRecent = returnToRecent;
@@ -1393,7 +1395,17 @@ angular.module('myApp.controllers', ['myApp.i18n'])
13931395
}
13941396
console.log('update reply markup', peerID, replyKeyboard);
13951397
$scope.historyState.replyKeyboard = replyKeyboard;
1396-
$scope.$broadcast('ui_panel_update');
1398+
$scope.$broadcast('ui_keyboard_update');
1399+
}
1400+
1401+
function replyKeyboardToggle () {
1402+
var replyKeyboard = $scope.historyState.replyKeyboard;
1403+
if (!replyKeyboard) {
1404+
return;
1405+
}
1406+
replyKeyboard.pFlags.hidden = !replyKeyboard.pFlags.hidden;
1407+
console.log('toggle reply markup', peerID, replyKeyboard);
1408+
$scope.$broadcast('ui_keyboard_update');
13971409
}
13981410

13991411
function botStart () {
@@ -1859,7 +1871,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
18591871
$scope.$on('user_update', angular.noop);
18601872
})
18611873

1862-
.controller('AppImSendController', function ($scope, $timeout, MtpApiManager, Storage, AppProfileManager, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, MtpApiFileManager) {
1874+
.controller('AppImSendController', function ($scope, $timeout, MtpApiManager, Storage, AppProfileManager, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, MtpApiFileManager, RichTextProcessor) {
18631875

18641876
$scope.$watch('curDialog.peer', resetDraft);
18651877
$scope.$on('user_update', angular.noop);
@@ -1876,6 +1888,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
18761888
$scope.$watch('draftMessage.sticker', onStickerSelected);
18771889
$scope.$watch('draftMessage.command', onCommandSelected);
18781890

1891+
$scope.enterSlash = enterSlash;
1892+
18791893
function sendMessage (e) {
18801894
$scope.$broadcast('ui_message_before_send');
18811895

@@ -1974,7 +1988,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
19741988
commandsList.push({
19751989
botID: peerBot.id,
19761990
value: value,
1977-
description: description
1991+
rDescription: RichTextProcessor.wrapRichText(description, {noLinks: true, noLineBreaks: true})
19781992
});
19791993
SearchIndexManager.indexObject(value, botSearchText + ' ' + command + ' ' + description, commandsIndex);
19801994
})
@@ -2017,6 +2031,12 @@ angular.module('myApp.controllers', ['myApp.i18n'])
20172031
$scope.$broadcast('ui_peer_reply');
20182032
}
20192033

2034+
function enterSlash (event) {
2035+
$scope.draftMessage.text = '/';
2036+
$scope.$broadcast('ui_peer_draft');
2037+
return cancelEvent(event);
2038+
}
2039+
20202040
function onMessageChange(newVal) {
20212041
// console.log('ctrl text changed', newVal);
20222042
// console.trace('ctrl text changed', newVal);

‎app/js/directives.js

+20
Original file line numberDiff line numberDiff line change
@@ -414,9 +414,26 @@ angular.module('myApp.directives', ['myApp.filters'])
414414
};
415415

416416
function link ($scope, element, attrs) {
417+
var scrollable = $('.reply_markup', element);
418+
var scroller = new Scroller(scrollable, {
419+
classPrefix: 'reply_markup',
420+
maxHeight: 170
421+
});
417422
$scope.buttonSend = function (button) {
418423
$scope.$emit('reply_button_press', button);
419424
}
425+
426+
$scope.$on('ui_keyboard_update', function () {
427+
onContentLoaded(function () {
428+
scroller.updateHeight();
429+
scroller.scrollTo(0);
430+
$scope.$emit('ui_panel_update');
431+
})
432+
});
433+
onContentLoaded(function () {
434+
scroller.updateHeight();
435+
$scope.$emit('ui_panel_update');
436+
});
420437
}
421438

422439
})
@@ -1474,6 +1491,9 @@ angular.module('myApp.directives', ['myApp.filters'])
14741491
if (!Config.Navigator.touch) {
14751492
composer.focus();
14761493
}
1494+
onContentLoaded(function () {
1495+
composer.checkAutocomplete(true);
1496+
});
14771497
if (emojiTooltip) {
14781498
emojiTooltip.hide();
14791499
}

‎app/js/message_composer.js

+101-66
Original file line numberDiff line numberDiff line change
@@ -196,21 +196,6 @@ EmojiTooltip.prototype.onMouseLeave = function (triggerUnshow) {
196196
}
197197
};
198198

199-
EmojiTooltip.prototype.getScrollWidth = function() {
200-
var outer = $('<div>').css({
201-
position: 'absolute',
202-
width: 100,
203-
height: 100,
204-
overflow: 'scroll',
205-
top: -9999
206-
}).appendTo($(document.body));
207-
208-
var scrollbarWidth = outer[0].offsetWidth - outer[0].clientWidth;
209-
outer.remove();
210-
211-
return scrollbarWidth;
212-
};
213-
214199

215200

216201
EmojiTooltip.prototype.createTooltip = function () {
@@ -219,20 +204,12 @@ EmojiTooltip.prototype.createTooltip = function () {
219204
}
220205

221206
var self = this;
222-
this.tooltipEl = $('<div class="composer_emoji_tooltip noselect"><div class="composer_emoji_tooltip_tabs"></div><div class="composer_emoji_tooltip_content_wrap nano mobile_scrollable_wrap"><div class="composer_emoji_tooltip_content nano-content clearfix"></div></div><div class="composer_emoji_tooltip_footer"><a class="composer_emoji_tooltip_settings"></a></div><div class="composer_emoji_tooltip_tail"><i class="icon icon-tooltip-tail"></i></div></div>').appendTo(document.body);
223-
224-
this.tabsEl = $('.composer_emoji_tooltip_tabs', this.tooltip);
225-
this.contentWrapEl = $('.composer_emoji_tooltip_content_wrap', this.tooltip);
226-
this.contentEl = $('.composer_emoji_tooltip_content', this.tooltip);
227-
this.footerEl = $('.composer_emoji_tooltip_footer', this.tooltip);
228-
this.settingsEl = $('.composer_emoji_tooltip_settings', this.tooltip);
229-
230-
var scrollWidth = this.getScrollWidth();
231-
if (scrollWidth > 0) {
232-
this.tooltipEl.css({
233-
width: parseInt(this.tooltipEl.css('width')) + scrollWidth
234-
});
235-
}
207+
this.tooltipEl = $('<div class="composer_emoji_tooltip noselect"><div class="composer_emoji_tooltip_tabs"></div><div class="composer_emoji_tooltip_content clearfix"></div><div class="composer_emoji_tooltip_footer"><a class="composer_emoji_tooltip_settings"></a></div><div class="composer_emoji_tooltip_tail"><i class="icon icon-tooltip-tail"></i></div></div>').appendTo(document.body);
208+
209+
this.tabsEl = $('.composer_emoji_tooltip_tabs', this.tooltipEl);
210+
this.contentEl = $('.composer_emoji_tooltip_content', this.tooltipEl);
211+
this.footerEl = $('.composer_emoji_tooltip_footer', this.tooltipEl);
212+
this.settingsEl = $('.composer_emoji_tooltip_settings', this.tooltipEl);
236213

237214
angular.forEach(['recent', 'smile', 'flower', 'bell', 'car', 'grid', 'stickers'], function (tabName, tabIndex) {
238215
var tab = $('<a class="composer_emoji_tooltip_tab composer_emoji_tooltip_tab_' + tabName + '"></a>')
@@ -254,9 +231,7 @@ EmojiTooltip.prototype.createTooltip = function () {
254231
}
255232
});
256233

257-
if (!Config.Mobile) {
258-
this.contentWrapEl.nanoScroller({preventPageScrolling: true, tabIndex: -1});
259-
}
234+
this.scroller = new Scroller(this.contentEl, {classPrefix: 'composer_emoji_tooltip'});
260235

261236
this.contentEl.on('mousedown', function (e) {
262237
e = e.originalEvent || e;
@@ -323,13 +298,7 @@ EmojiTooltip.prototype.updateTabContents = function () {
323298

324299
var renderContent = function () {
325300
self.contentEl.html(html.join(''));
326-
327-
if (!Config.Mobile) {
328-
self.contentWrapEl.nanoScroller({scroll: 'top'});
329-
setTimeout(function () {
330-
self.contentWrapEl.nanoScroller();
331-
}, 100);
332-
}
301+
self.scroller.reinit();
333302
}
334303

335304
if (this.tab == 6) { // Stickers
@@ -488,12 +457,9 @@ function MessageComposer (textarea, options) {
488457
this.setUpInput();
489458

490459
this.autoCompleteWrapEl = $('<div class="composer_dropdown_wrap"></div>').appendTo(document.body);
491-
this.autoCompleteScrollerEl = $('<div class="composer_dropdown_scroller nano"></div>').appendTo(this.autoCompleteWrapEl);
492-
this.autoCompleteEl = $('<ul class="composer_dropdown dropdown-menu nano-content"></ul>').appendTo(this.autoCompleteScrollerEl);
460+
this.autoCompleteEl = $('<ul class="composer_dropdown dropdown-menu"></ul>').appendTo(this.autoCompleteWrapEl);
493461

494-
if (!Config.Mobile) {
495-
this.autoCompleteScrollerEl.nanoScroller({preventPageScrolling: true, tabIndex: -1});
496-
}
462+
this.scroller = new Scroller(this.autoCompleteEl, {maxHeight: 180});
497463

498464
var self = this;
499465
this.autoCompleteEl.on('mousedown', function (e) {
@@ -614,18 +580,14 @@ MessageComposer.prototype.onKeyEvent = function (e) {
614580
currentSelected.removeClass('composer_autocomplete_option_active');
615581
if (nextWrap) {
616582
$(nextWrap).find('a').addClass('composer_autocomplete_option_active');
617-
if (!Config.Mobile) {
618-
scrollToNode(this.autoCompleteEl[0], nextWrap, this.autoCompleteScrollerEl);
619-
}
583+
this.scroller.scrollToNode(nextWrap);
620584
return cancelEvent(e);
621585
}
622586
}
623587

624588
var childNodes = this.autoCompleteEl[0].childNodes;
625589
var nextWrap = childNodes[next ? 0 : childNodes.length - 1];
626-
if (!Config.Mobile) {
627-
scrollToNode(this.autoCompleteEl[0], nextWrap, this.autoCompleteScrollerEl);
628-
}
590+
this.scroller.scrollToNode(nextWrap);
629591
$(nextWrap).find('a').addClass('composer_autocomplete_option_active');
630592

631593
return cancelEvent(e);
@@ -714,7 +676,7 @@ MessageComposer.prototype.restoreSelection = function () {
714676

715677

716678

717-
MessageComposer.prototype.checkAutocomplete = function () {
679+
MessageComposer.prototype.checkAutocomplete = function (forceFull) {
718680
if (Config.Mobile) {
719681
return false;
720682
}
@@ -730,7 +692,9 @@ MessageComposer.prototype.checkAutocomplete = function () {
730692
var value = textarea.value;
731693
}
732694

733-
value = value.substr(0, pos);
695+
if (!forceFull) {
696+
value = value.substr(0, pos);
697+
}
734698

735699
var matches = value.match(this.autoCompleteRegEx);
736700
if (matches) {
@@ -1096,15 +1060,7 @@ MessageComposer.prototype.focus = function () {
10961060
MessageComposer.prototype.renderSuggestions = function (html) {
10971061
this.autoCompleteEl.html(html.join(''));
10981062
this.autoCompleteWrapEl.show();
1099-
1100-
var self = this;
1101-
if (!Config.Mobile) {
1102-
self.autoCompleteScrollerEl.nanoScroller({scroll: 'top'});
1103-
setTimeout(function () {
1104-
self.autoCompleteScrollerEl.nanoScroller();
1105-
}, 100);
1106-
}
1107-
1063+
this.scroller.reinit();
11081064
this.updatePosition();
11091065
this.autocompleteShown = true;
11101066
}
@@ -1161,7 +1117,7 @@ MessageComposer.prototype.showCommandsSuggestions = function (commands) {
11611117

11621118
for (i = 0; i < count; i++) {
11631119
command = commands[i];
1164-
html.push('<li><a class="composer_command_option" data-command="' + encodeEntities(command.value) + '"><span class="composer_user_photo" data-user-id="' + command.botID + '"></span><span class="composer_command_value">' + encodeEntities(command.value) + '</span><span class="composer_command_desc">' + encodeEntities(command.description) + '</span></a></li>');
1120+
html.push('<li><a class="composer_command_option" data-command="' + encodeEntities(command.value) + '"><span class="composer_user_photo" data-user-id="' + command.botID + '"></span><span class="composer_command_value">' + encodeEntities(command.value) + '</span><span class="composer_command_desc">' + command.rDescription + '</span></a></li>');
11651121
}
11661122

11671123
this.renderSuggestions(html);
@@ -1182,14 +1138,13 @@ MessageComposer.prototype.showCommandsSuggestions = function (commands) {
11821138
MessageComposer.prototype.updatePosition = function () {
11831139
var offset = (this.richTextareaEl || this.textareaEl).offset();
11841140
var width = (this.richTextareaEl || this.textareaEl).outerWidth();
1185-
var contentHeight = this.autoCompleteEl[0].firstChild.clientHeight * this.autoCompleteEl[0].childNodes.length;
1186-
var height = Math.min(180, contentHeight);
1141+
var height = this.scroller.updateHeight();
11871142
this.autoCompleteWrapEl.css({
11881143
top: offset.top - height,
11891144
left: offset.left,
1190-
width: width - 2,
1191-
height: height
1145+
width: width - 2
11921146
});
1147+
this.scroller.update();
11931148
}
11941149

11951150
MessageComposer.prototype.hideSuggestions = function () {
@@ -1202,3 +1157,83 @@ MessageComposer.prototype.resetTyping = function () {
12021157
this.lastLength = 0;
12031158
}
12041159

1160+
1161+
1162+
function Scroller(content, options) {
1163+
options = options || {};
1164+
var classPrefix = options.classPrefix || 'scroller';
1165+
1166+
this.content = $(content);
1167+
this.content.wrap('<div class="' + classPrefix + '_scrollable_container"><div class="' + classPrefix + '_scrollable_wrap"><div class="' + classPrefix + '_scrollable"></div></div></div>');
1168+
1169+
this.scrollable = $(this.content[0].parentNode);
1170+
this.scroller = $(this.scrollable[0].parentNode);
1171+
this.wrap = $(this.scroller[0].parentNode);
1172+
1173+
this.useNano = options.nano !== undefined ? options.nano : !Config.Mobile;
1174+
this.maxHeight = options.maxHeight;
1175+
1176+
if (this.useNano) {
1177+
this.scrollable.addClass('nano-content');
1178+
this.scroller.addClass('nano');
1179+
this.scroller.nanoScroller({preventPageScrolling: true, tabIndex: -1});
1180+
} else {
1181+
if (this.maxHeight) {
1182+
this.wrap.css({maxHeight: this.maxHeight});
1183+
}
1184+
}
1185+
this.updateHeight();
1186+
}
1187+
1188+
Scroller.prototype.update = function () {
1189+
if (this.useNano) {
1190+
$(this.scroller).nanoScroller();
1191+
}
1192+
}
1193+
1194+
Scroller.prototype.reinit = function () {
1195+
this.scrollTo(0);
1196+
if (this.useNano) {
1197+
setTimeout((function () {
1198+
this.updateHeight();
1199+
}).bind(this), 100)
1200+
}
1201+
}
1202+
1203+
Scroller.prototype.updateHeight = function () {
1204+
var height;
1205+
if (this.maxHeight) {
1206+
var contentHeight = this.content[0].offsetHeight;
1207+
height = Math.min(this.maxHeight, contentHeight);
1208+
this.wrap.css({height: height});
1209+
} else {
1210+
height = this.scroller[0].offsetHeight;
1211+
}
1212+
$(this.scroller).nanoScroller();
1213+
return height;
1214+
}
1215+
1216+
1217+
Scroller.prototype.scrollTo = function (scrollTop) {
1218+
this.scrollable[0].scrollTop = scrollTop;
1219+
if (this.useNano) {
1220+
$(this.scroller).nanoScroller({flash: true});
1221+
}
1222+
}
1223+
1224+
Scroller.prototype.scrollToNode = function (node) {
1225+
node = node[0] || node;
1226+
var elTop = node.offsetTop - 15,
1227+
elHeight = node.offsetHeight + 30,
1228+
scrollTop = this.scrollable[0].scrollTop,
1229+
viewportHeight = this.scrollable[0].clientHeight;
1230+
1231+
if (scrollTop > elTop) { // we are below the node to scroll
1232+
this.scrollTo(elTop);
1233+
}
1234+
else if (scrollTop < elTop + elHeight - viewportHeight) { // we are over the node to scroll
1235+
this.scrollTo(elTop + elHeight - viewportHeight);
1236+
}
1237+
}
1238+
1239+

0 commit comments

Comments
 (0)