Browse Source

Merge branch 'master' into voice_recorder

master
Jovan Gerodetti 9 years ago
parent
commit
c24bfc640d
  1. 1
      app/img/Telegram.svg
  2. 8
      app/js/app.js
  3. 137
      app/js/controllers.js
  4. 240
      app/js/directives.js
  5. 3
      app/js/filters.js
  6. 4
      app/js/lib/config.js
  7. 12
      app/js/lib/mtproto_wrapper.js
  8. 14
      app/js/lib/ng_utils.js
  9. 26
      app/js/lib/utils.js
  10. 3
      app/js/locales/de-de.json
  11. 3
      app/js/locales/it-it.json
  12. 3
      app/js/locales/pt-br.json
  13. 9
      app/js/locales/ru-ru.json
  14. 266
      app/js/message_composer.js
  15. 87
      app/js/messages_manager.js
  16. 518
      app/js/services.js
  17. 335
      app/less/app.less
  18. 7
      app/less/desktop.less
  19. 27
      app/partials/desktop/composer_dropdown.html
  20. 11
      app/partials/desktop/full_gif.html
  21. 36
      app/partials/desktop/im.html
  22. 53
      app/partials/desktop/inline_results.html
  23. 4
      app/partials/desktop/message.html

1
app/img/Telegram.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.2 KiB

8
app/js/app.js

@ -7,6 +7,12 @@ @@ -7,6 +7,12 @@
'use strict';
var extraModules = [];
if (Config.Modes.animations) {
extraModules.push('ngAnimate');
}
// Declare app level module which depends on filters, and services
angular.module('myApp', [
'ngRoute',
@ -24,7 +30,7 @@ angular.module('myApp', [ @@ -24,7 +30,7 @@ angular.module('myApp', [
PRODUCTION_ONLY_END*/
'myApp.directives',
'myApp.controllers'
]).
].concat(extraModules)).
config(['$locationProvider', '$routeProvider', '$compileProvider', 'StorageProvider', function($locationProvider, $routeProvider, $compileProvider, StorageProvider) {
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|file|blob|filesystem|chrome-extension|app):|data:image\//);

137
app/js/controllers.js

@ -1058,6 +1058,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1058,6 +1058,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.selectedReply = selectedReply;
$scope.selectedCancel = selectedCancel;
$scope.selectedFlush = selectedFlush;
$scope.selectInlineBot = selectInlineBot;
$scope.startBot = startBot;
$scope.cancelBot = cancelBot;
@ -1195,7 +1196,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1195,7 +1196,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.historyState.typing.splice(0, $scope.historyState.typing.length);
$scope.$broadcast('ui_peer_change');
$scope.$broadcast('ui_history_change');
safeReplaceObject($scope.state, {loaded: true, empty: !peerHistory.messages.length});
safeReplaceObject($scope.state, {loaded: true, empty: !peerHistory.messages.length, mayBeHasMore: true});
updateBotActions();
updateChannelActions();
@ -1299,14 +1300,14 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1299,14 +1300,14 @@ angular.module('myApp.controllers', ['myApp.i18n'])
return;
}
lessPending = false;
lessActive = true;
$scope.state.lessActive = lessActive = true;
var curJump = jump,
curLessJump = ++lessJump,
limit = 0,
backLimit = 20;
AppMessagesManager.getHistory($scope.curDialog.peerID, minID, limit, backLimit).then(function (historyResult) {
lessActive = false;
$scope.state.lessActive = lessActive = false;
if (curJump != jump || curLessJump != lessJump) return;
var i, id;
@ -1347,7 +1348,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1347,7 +1348,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
return;
}
morePending = false;
moreActive = true;
$scope.state.moreActive = moreActive = true;
var curJump = jump,
curMoreJump = ++moreJump,
@ -1358,7 +1359,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1358,7 +1359,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
: AppMessagesManager.getHistory($scope.curDialog.peerID, maxID, limit);
getMessagesPromise.then(function (historyResult) {
moreActive = false;
$scope.state.moreActive = moreActive = false;
if (curJump != jump || curMoreJump != moreJump) return;
angular.forEach(historyResult.history, function (id) {
@ -1405,9 +1406,9 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1405,9 +1406,9 @@ angular.module('myApp.controllers', ['myApp.i18n'])
limit = 10;
}
moreActive = false;
$scope.state.moreActive = moreActive = false;
morePending = false;
lessActive = false;
$scope.state.lessActive = lessActive = false;
lessPending = false;
var prerenderedLen = peerHistory.messages.length;
@ -1550,23 +1551,31 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1550,23 +1551,31 @@ angular.module('myApp.controllers', ['myApp.i18n'])
var target = $event.target;
while (target) {
if (target.className.indexOf('im_message_outer_wrap') != -1) {
if (target instanceof SVGElement) {
target = target.parentNode;
continue;
}
if (target.className && target.className.indexOf('im_message_outer_wrap') != -1) {
if (Config.Mobile) {
return false;
}
break;
}
if (target.className &&
target.className.indexOf('im_message_date') != -1) {
if ($scope.historyState.canReply) {
selectedReply(messageID);
} else {
selectedForward(messageID);
}
return false;
}
if (Config.Mobile &&
target.className &&
target.className.indexOf('im_message_body') != -1) {
break;
}
if (target.tagName == 'A' ||
target.onclick ||
target.getAttribute('ng-click')) {
return false;
}
var events = $._data(target, 'events');
if (events && (events.click || events.mousedown)) {
if (target.tagName == 'A' || hasOnlick(target)) {
return false;
}
target = target.parentNode;
@ -1651,6 +1660,11 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1651,6 +1660,11 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.$broadcast('messages_select');
}
function selectInlineBot (botID, $event) {
$scope.$broadcast('inline_bot_select', botID);
return cancelEvent($event);
}
function selectedCancel (noBroadcast) {
$scope.selectedMsgs = {};
$scope.selectedCount = 0;
@ -2069,7 +2083,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2069,7 +2083,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.$on('user_update', angular.noop);
})
.controller('AppImSendController', function ($scope, $timeout, MtpApiManager, Storage, AppProfileManager, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, MtpApiFileManager, RichTextProcessor) {
.controller('AppImSendController', function ($scope, $timeout, MtpApiManager, Storage, AppProfileManager, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, AppInlineBotsManager, MtpApiFileManager, RichTextProcessor) {
$scope.$watch('curDialog.peer', resetDraft);
$scope.$on('user_update', angular.noop);
@ -2091,6 +2105,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2091,6 +2105,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.$watch('draftMessage.files', onFilesSelected);
$scope.$watch('draftMessage.sticker', onStickerSelected);
$scope.$watch('draftMessage.command', onCommandSelected);
$scope.$watch('draftMessage.inlineResultID', onInlineResultSelected);
$scope.$on('history_reply_markup', function (e, peerData) {
if (peerData.peerID == $scope.curDialog.peerID) {
@ -2098,6 +2113,12 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2098,6 +2113,12 @@ angular.module('myApp.controllers', ['myApp.i18n'])
}
});
$scope.$on('inline_bot_select', function (e, botID) {
var bot = AppUsersManager.getUser(botID);
$scope.draftMessage.text = '@' + bot.username + ' ';;
$scope.$broadcast('ui_peer_draft', {focus: true});
});
$scope.replyKeyboardToggle = replyKeyboardToggle;
$scope.toggleSlash = toggleSlash;
@ -2250,6 +2271,9 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2250,6 +2271,9 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.draftMessage.text = '';
$scope.$broadcast('ui_peer_draft');
}
delete $scope.draftMessage.inlineProgress;
$scope.$broadcast('inline_results', false);
}
function applyDraftAttachment (e, attachment) {
@ -2394,6 +2418,60 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2394,6 +2418,60 @@ angular.module('myApp.controllers', ['myApp.i18n'])
Storage.remove('draft' + $scope.curDialog.peerID);
// console.log(dT(), 'draft delete', 'draft' + $scope.curDialog.peerID);
}
checkInlinePattern(newVal);
}
var inlineUsernameRegex = /^@([a-zA-Z\d_]{1,32})( | )([\s\S]*)$/;
var getInlineResultsTO = false;
var jump = 0;
function checkInlinePattern (message) {
if (getInlineResultsTO) {
$timeout.cancel(getInlineResultsTO);
}
var curJump = ++jump;
if (!message || !message.length) {
delete $scope.draftMessage.inlineProgress;
$scope.$broadcast('inline_results', false);
return;
}
var matches = message.match(inlineUsernameRegex);
if (!matches) {
delete $scope.draftMessage.inlineProgress;
$scope.$broadcast('inline_results', false);
return;
}
var username = matches[1];
$scope.draftMessage.inlineProgress = true;
AppPeersManager.resolveInlineMention(username).then(function (inlineBot) {
if (curJump != jump) {
return;
}
$scope.$broadcast('inline_placeholder', {
prefix: '@' + username + matches[2],
placeholder: inlineBot.placeholder
});
if (getInlineResultsTO) {
$timeout.cancel(getInlineResultsTO);
}
getInlineResultsTO = $timeout(function () {
AppInlineBotsManager.getInlineResults(inlineBot.id, matches[3], '').then(function (botResults) {
getInlineResultsTO = false;
if (curJump != jump) {
return;
}
botResults.text = message;
$scope.$broadcast('inline_results', botResults);
delete $scope.draftMessage.inlineProgress;
}, function () {
$scope.$broadcast('inline_results', false);
delete $scope.draftMessage.inlineProgress;
});
}, 500);
}, function () {
$scope.$broadcast('inline_results', false);
delete $scope.draftMessage.inlineProgress;
});
}
function onTyping () {
@ -2455,7 +2533,6 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2455,7 +2533,6 @@ angular.module('myApp.controllers', ['myApp.i18n'])
fwdsSend();
}
delete $scope.draftMessage.sticker;
resetDraft();
}
function onCommandSelected (command) {
@ -2467,6 +2544,25 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2467,6 +2544,25 @@ angular.module('myApp.controllers', ['myApp.i18n'])
delete $scope.draftMessage.sticker;
delete $scope.draftMessage.text;
delete $scope.draftMessage.command;
delete $scope.draftMessage.inlineResultID;
$scope.$broadcast('ui_message_send');
$scope.$broadcast('ui_peer_draft');
}
function onInlineResultSelected (qID) {
if (!qID) {
return;
}
var options = {
replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.mid
};
AppInlineBotsManager.sendInlineResult($scope.curDialog.peerID, qID, options);
fwdsSend();
resetDraft();
delete $scope.draftMessage.sticker;
delete $scope.draftMessage.text;
delete $scope.draftMessage.command;
delete $scope.draftMessage.inlineResultID;
$scope.$broadcast('ui_message_send');
$scope.$broadcast('ui_peer_draft');
}
@ -4654,11 +4750,14 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -4654,11 +4750,14 @@ angular.module('myApp.controllers', ['myApp.i18n'])
.controller('StickersetModalController', function ($scope, $rootScope, $modalInstance, MtpApiManager, RichTextProcessor, AppStickersManager, AppDocsManager, AppMessagesManager, LocationParamsService) {
$scope.slice = {limit: 20, limitDelta: 20};
var fullSet;
AppStickersManager.getStickerset($scope.inputStickerset).then(function (result) {
$scope.$broadcast('ui_height');
$scope.stickersetLoaded = true;
fullSet = result;
$scope.stickerset = result.set;
$scope.stickersetInstalled = result.installed;
$scope.stickersetInstalled = result.set.pFlags.installed == true;
$scope.documents = result.documents;
$scope.stickerEmojis = {};
@ -4673,7 +4772,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -4673,7 +4772,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
});
$scope.toggleInstalled = function (installed) {
AppStickersManager.installStickerset($scope.stickerset, !installed).then(function () {
AppStickersManager.installStickerset(fullSet, !installed).then(function () {
$scope.stickersetInstalled = installed;
})
};

240
app/js/directives.js

@ -1099,6 +1099,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -1099,6 +1099,7 @@ angular.module('myApp.directives', ['myApp.filters'])
historyEl = $('.im_history', element)[0],
scrollableWrap = $('.im_history_scrollable_wrap', element)[0],
scrollable = $('.im_history_scrollable', element)[0],
emptyWrapEl = $('.im_history_empty_wrap', element)[0],
bottomPanelWrap = $('.im_bottom_panel_wrap', element)[0],
sendFormWrap = $('.im_send_form_wrap', element)[0],
headWrap = $('.tg_page_head')[0],
@ -1412,7 +1413,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -1412,7 +1413,7 @@ angular.module('myApp.directives', ['myApp.filters'])
$(historyMessagesEl).css({marginTop: 0});
var marginTop = scrollableWrap.offsetHeight
- historyMessagesEl.offsetHeight
- 20
- emptyWrapEl.offsetHeight
- (Config.Mobile ? 0 : 39);
if (historyMessagesEl.offsetHeight > 0 && marginTop > 0) {
@ -1429,8 +1430,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -1429,8 +1430,7 @@ angular.module('myApp.directives', ['myApp.filters'])
})
.directive('mySendForm', function (_, $window, $compile, $modalStack, $http, $interpolate, Storage, AppStickersManager, AppDocsManager, ErrorService, shouldFocusOnInteraction) {
.directive('mySendForm', function (_, $timeout, $compile, $modalStack, $http, $interpolate, Storage, AppStickersManager, AppDocsManager, ErrorService, AppInlineBotsManager, shouldFocusOnInteraction) {
return {
link: link,
scope: {
@ -1497,6 +1497,11 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -1497,6 +1497,11 @@ angular.module('myApp.directives', ['myApp.filters'])
}
});
$scope.$on('stickers_changed', function () {
emojiTooltip.onStickersChanged();
});
var composerEmojiPanel;
if (emojiPanel) {
composerEmojiPanel = new EmojiPanel(emojiPanel, {
@ -1506,9 +1511,6 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -1506,9 +1511,6 @@ angular.module('myApp.directives', ['myApp.filters'])
});
}
var peerPhotoCompiled = $compile('<span class="composer_user_photo" my-peer-photolink="peerID" img-class="composer_user_photo"></span>');
var cachedPeerPhotos = {};
var composer = new MessageComposer(messageField, {
onTyping: function () {
$scope.$emit('ui_typing');
@ -1516,21 +1518,17 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -1516,21 +1518,17 @@ angular.module('myApp.directives', ['myApp.filters'])
getSendOnEnter: function () {
return sendOnEnter;
},
getPeerImage: function (element, peerID, noReplace) {
if (cachedPeerPhotos[peerID] && !noReplace) {
element.replaceWith(cachedPeerPhotos[peerID]);
return;
}
dropdownDirective: function (element, callback) {
var scope = $scope.$new(true);
scope.peerID = peerID;
peerPhotoCompiled(scope, function (clonedElement) {
cachedPeerPhotos[peerID] = clonedElement;
var clonedElement = $compile('<div><div my-composer-dropdown></div></div>')(scope, function (clonedElement, scope) {
element.replaceWith(clonedElement);
callback(scope, clonedElement);
});
},
mentions: $scope.mentions,
commands: $scope.commands,
onMessageSubmit: onMessageSubmit,
onInlineResultSend: onInlineResultSend,
onFilePaste: onFilePaste,
onCommandSend: function (command) {
$scope.$apply(function () {
@ -1544,6 +1542,21 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -1544,6 +1542,21 @@ angular.module('myApp.directives', ['myApp.filters'])
$(richTextarea).on('keydown keyup', updateHeight);
}
$scope.$on('inline_results', function (e, inlineResults) {
if (inlineResults) {
var w = ((richTextarea || messageField).offsetWidth || 382) - 2;
var h = 80;
AppInlineBotsManager.regroupWrappedResults(inlineResults.results, w, h);
setZeroTimeout(function () {
composer.setInlineSuggestions(inlineResults);
});
}
});
$scope.$on('inline_placeholder', function(e, data) {
composer.setInlinePlaceholder(data.prefix, data.placeholder);
});
fileSelects.on('change', function () {
var self = this;
$scope.$apply(function () {
@ -1659,6 +1672,12 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -1659,6 +1672,12 @@ angular.module('myApp.directives', ['myApp.filters'])
return cancelEvent(e);
}
function onInlineResultSend (qID) {
$scope.$apply(function () {
$scope.draftMessage.inlineResultID = qID;
});
}
function updateValue () {
if (richTextarea) {
composer.onChange();
@ -2063,7 +2082,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -2063,7 +2082,7 @@ angular.module('myApp.directives', ['myApp.filters'])
})
.directive('myLoadGif', function(AppDocsManager) {
.directive('myLoadGif', function(AppDocsManager, $timeout) {
return {
link: link,
@ -2082,6 +2101,15 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -2082,6 +2101,15 @@ angular.module('myApp.directives', ['myApp.filters'])
$scope.isActive = false;
// Demo
// $scope.document.progress = {enabled: true, percent: 30};
// $timeout(function () {
// $scope.document.progress.percent = 60;
// }, 3000);
// $timeout(function () {
// $scope.document.progress.percent = 100;
// }, 10000);
$scope.toggle = function (e) {
if (e && checkClick(e, true)) {
AppDocsManager.saveDocFile($scope.document.id);
@ -2092,6 +2120,16 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -2092,6 +2120,16 @@ angular.module('myApp.directives', ['myApp.filters'])
onContentLoaded(function () {
$scope.isActive = !$scope.isActive;
$scope.$emit('ui_height');
var video = $('video', element)[0];
if (video) {
if (!$scope.isActive) {
video.pause();
video.currentTime = 0;
} else {
video.play();
}
}
})
return;
}
@ -2105,8 +2143,9 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -2105,8 +2143,9 @@ angular.module('myApp.directives', ['myApp.filters'])
downloadPromise = AppDocsManager.downloadDoc($scope.document.id);
downloadPromise.then(function () {
$scope.isActive = true;
$scope.$emit('ui_height');
$timeout(function () {
$scope.isActive = true;
}, 200);
})
}
@ -2330,8 +2369,8 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -2330,8 +2369,8 @@ angular.module('myApp.directives', ['myApp.filters'])
var src = 'https://maps.googleapis.com/maps/api/staticmap?sensor=false&center=' + $scope.point['lat'] + ',' + $scope.point['long'] + '&zoom=15&size='+width+'x'+height+'&scale=2&key=' + apiKey;
ExternalResourcesManager.downloadImage(src).then(function (url) {
element.attr('src', url);
ExternalResourcesManager.downloadByURL(src).then(function (url) {
element.attr('src', url.valueOf());
});
}
@ -2831,7 +2870,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -2831,7 +2870,7 @@ angular.module('myApp.directives', ['myApp.filters'])
}
};
if (element[0].tagName == 'A') {
if (element[0].tagName == 'A' && !hasOnlick(element[0])) {
element.on('click', function () {
if (peerID > 0) {
AppUsersManager.openUser(peerID, override);
@ -3317,44 +3356,67 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -3317,44 +3356,67 @@ angular.module('myApp.directives', ['myApp.filters'])
})
.directive('myArcProgress', function () {
var html = '<svg class="progress-arc" viewPort="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg"><circle class="progress-arc-circle" fill="transparent" stroke-dashoffset="0"></circle><circle class="progress-arc-bar" fill="transparent" stroke-dashoffset="0"></circle></svg>';
var html =
'<svg class="progress-arc" viewPort="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg">\
<defs>\
<linearGradient id="grad_intermediate%id%" x1="0%" y1="0%" x2="100%" y2="0%">\
<stop offset="0%" class="stop0" />\
<stop offset="60%" class="stop60" />\
<stop offset="100%" class="stop100"/>\
</linearGradient>\
</defs>\
<circle class="progress-arc-bar"></circle>\
</svg>';
function updateProgress (bar, progress, fullLen) {
progress = Math.max(0.0, Math.min(progress, 1.0));
var minProgress = 0.2;
progress = minProgress + (1 - minProgress) * progress;
bar.css({strokeDasharray: (progress * fullLen) + ', ' + ((1 - progress) * fullLen)});
}
var num = 0;
return {
scope: {
progress: '=myArcProgress'
},
link: function ($scope, element, attrs) {
element
.html(html)
.addClass('progress-arc-wrap');
var intermediate = !attrs.myArcProgress;
var width = attrs.width || element.width() || 40;
var stroke = attrs.stroke || (width / 2 * 0.14);
var center = width / 2;
var radius = center - (stroke / 2);
var svgEl = element[0].firstChild;
var circle = $('.progress-arc-circle', element);
var bar = $('.progress-arc-bar', element);
// Doesn't work without unique id for every gradient
var curNum = ++num;
var width = attrs.width || 40;
var radius = width * 0.86;
var stroke = width * 0.14;
var center = width / 2;
element
.html(html.replace('%id%', curNum))
.addClass('progress-arc-wrap')
.addClass(intermediate ? 'progress-arc-intermediate' : 'progress-arc-percent')
.css({width: width, height: width});
$(svgEl).attr('width', width);
$(svgEl).attr('height', width);
circle.attr('cx', center);
circle.attr('cy', center);
circle.attr('r', radius);
circle.css({strokeWidth: stroke});
$(element[0].firstChild)
.attr('width', width)
.attr('height', width);
bar.attr('cx', center);
bar.attr('cy', center);
bar.attr('r', radius);
bar.css({strokeWidth: stroke});
var bar = $('.progress-arc-bar', element);
bar
.attr('cx', center)
.attr('cy', center)
.attr('r', radius)
.css({strokeWidth: stroke});
var fullLen = 2 * Math.PI * radius;
$scope.$watch('progress', function (newProgress) {
var progress = newProgress / 100.0;
progress = Math.max(0.0, Math.min(progress, 1.0));
bar.css({strokeDasharray: (progress * fullLen) + ', ' + ((1 - progress) * fullLen)});
});
if (intermediate) {
updateProgress(bar, 0.3, fullLen);
bar.css({stroke: 'url(#grad_intermediate' + curNum + ')'});
} else {
$scope.$watch('progress', function (newProgress) {
updateProgress(bar, newProgress / 100.0, fullLen);
});
}
}
}
})
@ -3380,3 +3442,87 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -3380,3 +3442,87 @@ angular.module('myApp.directives', ['myApp.filters'])
};
})
.directive('myComposerDropdown', function () {
return {
templateUrl: templateUrl('composer_dropdown')
}
})
.directive('myEmojiSuggestions', function () {
return {
link: function($scope, element, attrs) {
$scope.$watchCollection('emojiCodes', function (codes) {
// var codes = $scope.$eval(attrs.myEmojiSuggestions);
var html = [];
var iconSize = Config.Mobile ? 26 : 20;
var emoticonCode, emoticonData, spritesheet, pos, categoryIndex;
var count = Math.min(5, codes.length);
var i, x, y;
for (i = 0; i < count; i++) {
emoticonCode = codes[i];
if (emoticonCode.code) {
emoticonCode = emoticonCode.code;
}
if (emoticonData = Config.Emoji[emoticonCode]) {
spritesheet = EmojiHelper.spritesheetPositions[emoticonCode];
categoryIndex = spritesheet[0];
pos = spritesheet[1];
x = iconSize * spritesheet[3];
y = iconSize * spritesheet[2];
html.push('<li><a class="composer_emoji_option" data-code="' + encodeEntities(emoticonCode) + '"><i class="emoji emoji-w', iconSize, ' emoji-spritesheet-' + categoryIndex + '" style="background-position: -' + x + 'px -' + y + 'px;"></i><span class="composer_emoji_shortcut">:' + encodeEntities(emoticonData[1][0]) + ':</span></a></li>');
}
}
// onContentLoaded(function () {
element.html(html.join(''));
console.log(dT(), 'emoji done');
// });
});
}
};
})
.directive('myInlineResults', function (AppPhotosManager, ExternalResourcesManager, AppDocsManager) {
return {
templateUrl: templateUrl('inline_results'),
scope: {
botResults: '=myInlineResults'
},
link: function ($scope, element, attrs) {
$scope.$watch('botResults.results', function (results) {
angular.forEach(results, function (result) {
if (result.thumb_url && !result.thumbUrl) {
ExternalResourcesManager.downloadByURL(result.thumb_url).then(function (url) {
result.thumbUrl = url;
});
}
if (result.type == 'gif' && result.content_url && !result.contentUrl) {
ExternalResourcesManager.downloadByURL(result.content_url).then(function (url) {
result.contentUrl = url;
});
}
if (result.type == 'gif' && result.document) {
AppDocsManager.downloadDoc(result.document.id);
}
if (result.type == 'photo' && result.photo) {
var photoSize = AppPhotosManager.choosePhotoSize(result.photo, result.thumbW, result.thumbH),
dim = calcImageInBox(photoSize.w, photoSize.h, result.thumbW, result.thumbH);
result.thumb = {
width: dim.w,
height: dim.h,
location: photoSize.location,
size: photoSize.size
};
}
})
});
}
}
})

3
app/js/filters.js

@ -187,6 +187,9 @@ angular.module('myApp.filters', ['myApp.i18n']) @@ -187,6 +187,9 @@ angular.module('myApp.filters', ['myApp.i18n'])
.filter('formatSizeProgress', function ($filter, _) {
var formatSizeFilter = $filter('formatSize');
return function (progress) {
if (!progress.total) {
return '';
}
var done = formatSizeFilter(progress.done, true),
doneParts = done.split(' '),
total = formatSizeFilter(progress.total),

4
app/js/lib/config.js

@ -36,7 +36,9 @@ Config.Modes = { @@ -36,7 +36,9 @@ Config.Modes = {
webcrypto: location.search.indexOf('webcrypto=0')== -1,
packed: location.protocol == 'app:' || location.protocol == 'chrome-extension:',
ios_standalone: window.navigator.standalone && navigator.userAgent.match(/iOS|iPhone|iPad/),
chrome_packed: window.chrome && chrome.app && chrome.app.window && true || false
chrome_packed: window.chrome && chrome.app && chrome.app.window && true || false,
animations: true,
memory_only: false
};
Config.Navigator = {

12
app/js/lib/mtproto_wrapper.js

@ -384,11 +384,13 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) @@ -384,11 +384,13 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
}
function getFileStorage () {
if (TmpfsFileStorage.isAvailable()) {
return TmpfsFileStorage;
}
if (IdbFileStorage.isAvailable()) {
return IdbFileStorage;
if (!Config.Modes.memory_only) {
if (TmpfsFileStorage.isAvailable()) {
return TmpfsFileStorage;
}
if (IdbFileStorage.isAvailable()) {
return IdbFileStorage;
}
}
return MemoryFileStorage;
}

14
app/js/lib/ng_utils.js

@ -963,10 +963,10 @@ angular.module('izhukov.utils', []) @@ -963,10 +963,10 @@ angular.module('izhukov.utils', [])
};
})
.service('ExternalResourcesManager', function ($q, $http) {
.service('ExternalResourcesManager', function ($q, $http, $sce) {
var urlPromises = {};
function downloadImage (url) {
function downloadByURL (url) {
if (urlPromises[url] !== undefined) {
return urlPromises[url];
}
@ -974,12 +974,18 @@ angular.module('izhukov.utils', []) @@ -974,12 +974,18 @@ angular.module('izhukov.utils', [])
return urlPromises[url] = $http.get(url, {responseType: 'blob', transformRequest: null})
.then(function (response) {
window.URL = window.URL || window.webkitURL;
return window.URL.createObjectURL(response.data);
var url = window.URL.createObjectURL(response.data);
return $sce.trustAsResourceUrl(url);
}, function (error) {
if (!Config.modes.chrome_packed) {
return $q.when($sce.trustAsResourceUrl(url));
}
return $q.reject(error);
});
}
return {
downloadImage: downloadImage
downloadByURL: downloadByURL
}
})

26
app/js/lib/utils.js

@ -60,6 +60,18 @@ function cancelEvent (event) { @@ -60,6 +60,18 @@ function cancelEvent (event) {
return false;
}
function hasOnlick (element) {
if (element.onclick ||
element.getAttribute('ng-click')) {
return true;
}
var events = $._data(element, 'events');
if (events && (events.click || events.mousedown)) {
return true;
}
return false;
}
function getScrollWidth() {
var outer = $('<div>').css({
position: 'absolute',
@ -289,7 +301,21 @@ function scrollToNode (scrollable, node, scroller) { @@ -289,7 +301,21 @@ function scrollToNode (scrollable, node, scroller) {
}
}
if (Config.Modes.animations &&
typeof window.requestAnimationFrame == 'function') {
window.onAnimationFrameCallback = function (cb) {
return (function () {
window.requestAnimationFrame(cb);
});
};
} else {
window.onAnimationFrameCallback = function (cb) {
return cb;
};
}
function onContentLoaded (cb) {
cb = onAnimationFrameCallback(cb);
setZeroTimeout(cb);
}

3
app/js/locales/de-de.json

@ -253,6 +253,7 @@ @@ -253,6 +253,7 @@
"conversation_media_video": "Video",
"conversation_media_document": "Datei",
"conversation_media_sticker": "Sticker",
"conversation_media_gif": "GIF",
"conversation_media_audio": "Audio",
"conversation_media_location": "Standort",
"conversation_media_contact": "Kontakt",
@ -472,7 +473,9 @@ @@ -472,7 +473,9 @@
"password_recover_submit": "Absenden",
"login_controller_unknown_country": "Unbekannt",
"message_forwarded_message": "Weitergeleitete Nachricht",
"message_via_bot": "via {bot}",
"message_forwarded_message_mobile": "Weitergeleitet von {from}, {date}",
"message_forwarded_via_message_mobile": "Weitergeleitet von {from} via {bot}, {date}",
"message_attach_audio_message": "Sprachnachricht",
"message_attach_audio_play": "Abspielen",
"message_attach_document_open": "Öffnen",

3
app/js/locales/it-it.json

@ -253,6 +253,7 @@ @@ -253,6 +253,7 @@
"conversation_media_video": "Video",
"conversation_media_document": "File",
"conversation_media_sticker": "Sticker",
"conversation_media_gif": "GIF",
"conversation_media_audio": "Audio",
"conversation_media_location": "Posizione",
"conversation_media_contact": "Contatto",
@ -472,7 +473,9 @@ @@ -472,7 +473,9 @@
"password_recover_submit": "Invia",
"login_controller_unknown_country": "Sconosciuto",
"message_forwarded_message": "Messaggio inoltrato",
"message_via_bot": "via {bot}",
"message_forwarded_message_mobile": "Inoltrato da {from},{date}",
"message_forwarded_via_message_mobile": "Inoltrato da {from} via {bot}, {date}",
"message_attach_audio_message": "Nota vocale",
"message_attach_audio_play": "Riproduci",
"message_attach_document_open": "Apri",

3
app/js/locales/pt-br.json

@ -253,6 +253,7 @@ @@ -253,6 +253,7 @@
"conversation_media_video": "Vídeo",
"conversation_media_document": "Arquivo",
"conversation_media_sticker": "Sticker",
"conversation_media_gif": "GIF",
"conversation_media_audio": "Áudio",
"conversation_media_location": "Localização",
"conversation_media_contact": "Contato",
@ -472,7 +473,9 @@ @@ -472,7 +473,9 @@
"password_recover_submit": "Enviar",
"login_controller_unknown_country": "Desconhecido",
"message_forwarded_message": "Encaminhar mensagem",
"message_via_bot": "via {bot}",
"message_forwarded_message_mobile": "Encaminhado de {from}, {date}",
"message_forwarded_via_message_mobile": "Encaminhado de {from} via {bot}, {date}",
"message_attach_audio_message": "Mensagem de voz",
"message_attach_audio_play": "Tocar",
"message_attach_document_open": "Abrir",

9
app/js/locales/ru-ru.json

@ -251,8 +251,9 @@ @@ -251,8 +251,9 @@
"conversation_you": "Вы",
"conversation_media_photo": "Фотография",
"conversation_media_video": "Видео",
"conversation_media_document": "File",
"conversation_media_document": "Файл",
"conversation_media_sticker": "Стикер",
"conversation_media_gif": "GIF",
"conversation_media_audio": "Аудио",
"conversation_media_location": "Местоположение",
"conversation_media_contact": "Контакт",
@ -357,7 +358,7 @@ @@ -357,7 +358,7 @@
"head_edit_messages": "Редактировать сообщения",
"head_media_photos": "Фотографии",
"head_media_video": "Видео",
"head_media_documents": "Files",
"head_media_documents": "Файлы",
"head_media_audio": "Голосовые сообщения",
"head_about": "О приложении",
"head_clear_all": "Очистить всё",
@ -393,7 +394,7 @@ @@ -393,7 +394,7 @@
"im_media": "Медиа",
"im_media_photos": "Фотографии",
"im_media_video": "Видео",
"im_media_documents": "Files",
"im_media_documents": "Файлы",
"im_media_audio": "Голосовые сообщения",
"im_pluralize_participants": "{'one': '{} участник', 'few': '{} участника', 'many': '{} участников', 'other': '{} участников'}",
"im_show_recent_messages": "Показать последние сообщения",
@ -472,7 +473,9 @@ @@ -472,7 +473,9 @@
"password_recover_submit": "Отправить",
"login_controller_unknown_country": "Неизвестно",
"message_forwarded_message": "Пересланное сообщение",
"message_via_bot": "via {bot}",
"message_forwarded_message_mobile": "Переслано от {from}, {date}",
"message_forwarded_via_message_mobile": "Forwarded from {from} via {bot}, {date}",
"message_attach_audio_message": "Запись голоса",
"message_attach_audio_play": "Воспроизвести",
"message_attach_document_open": "Открыть",

266
app/js/message_composer.js

@ -364,7 +364,7 @@ EmojiTooltip.prototype.createTooltip = function () { @@ -364,7 +364,7 @@ EmojiTooltip.prototype.createTooltip = function () {
EmojiTooltip.prototype.selectCategory = function (cat, force) {
if (this.cat === cat && !force) {
if (!this.tab && this.cat === cat && !force) {
return false;
}
$('.active', this.categoriesEl).removeClass('active');
@ -566,6 +566,12 @@ EmojiTooltip.prototype.onStickersScroll = function (scrollable, scrollTop) { @@ -566,6 +566,12 @@ EmojiTooltip.prototype.onStickersScroll = function (scrollable, scrollTop) {
this.activateStickerCategory();
};
EmojiTooltip.prototype.onStickersChanged = function () {
if (this.tab) {
this.updateStickersContents(true);
}
};
EmojiTooltip.prototype.activateStickerCategory = function () {
var categoriesEl = this.categoriesEl[1];
var categoryEl = categoriesEl.childNodes[this.cat];
@ -668,38 +674,19 @@ EmojiPanel.prototype.update = function () { @@ -668,38 +674,19 @@ EmojiPanel.prototype.update = function () {
function MessageComposer (textarea, options) {
var self = this;
this.textareaEl = $(textarea);
this.setUpInput();
this.autoCompleteWrapEl = $('<div class="composer_dropdown_wrap"></div>').appendTo(document.body);
this.autoCompleteEl = $('<ul class="composer_dropdown dropdown-menu"></ul>').appendTo(this.autoCompleteWrapEl);
var autoCompleteEl = $('<div></div>').appendTo(this.autoCompleteWrapEl);
this.scroller = new Scroller(this.autoCompleteEl, {maxHeight: 180});
var self = this;
this.autoCompleteEl.on('mousedown', function (e) {
e = e.originalEvent || e;
var target = $(e.target), mention, code, command;
if (target[0].tagName != 'A') {
target = $(target[0].parentNode);
}
if (code = target.attr('data-code')) {
if (self.onEmojiSelected) {
self.onEmojiSelected(code, true);
}
EmojiHelper.pushPopularEmoji(code);
}
if (mention = target.attr('data-mention')) {
self.onMentionSelected(mention);
}
if (command = target.attr('data-command')) {
if (self.onCommandSelected) {
self.onCommandSelected(command);
}
self.hideSuggestions();
}
return cancelEvent(e);
options.dropdownDirective(autoCompleteEl, function (scope, newAutoCompleteEl) {
self.autoCompleteEl = newAutoCompleteEl;
self.autoCompleteScope = scope;
self.setUpAutoComplete();
});
this.isActive = false;
@ -708,16 +695,20 @@ function MessageComposer (textarea, options) { @@ -708,16 +695,20 @@ function MessageComposer (textarea, options) {
this.onMessageSubmit = options.onMessageSubmit;
this.getSendOnEnter = options.getSendOnEnter;
this.onFilePaste = options.onFilePaste;
this.onCommandSend = options.onCommandSend;
this.onInlineResultSend = options.onInlineResultSend;
this.mentions = options.mentions;
this.commands = options.commands;
this.getPeerImage = options.getPeerImage;
this.onCommandSend = options.onCommandSend;
}
MessageComposer.autoCompleteRegEx = /(\s|^)(:|@|\/)([A-Za-z0-9\-\+\*@_]*)$/;
MessageComposer.prototype.setUpInput = function () {
this.inlinePlaceholderWrap = $('<div class="im_inline_placeholder_wrap"></div>').prependTo(this.textareaEl[0].parentNode);
this.inlinePlaceholderPrefixEl = $('<span class="im_inline_placeholder_prefix"></span>').appendTo(this.inlinePlaceholderWrap);
this.inlinePlaceholderEl = $('<span class="im_inline_placeholder"></span>').appendTo(this.inlinePlaceholderWrap);
if ('contentEditable' in document.body) {
this.setUpRich();
} else {
@ -732,6 +723,56 @@ MessageComposer.prototype.setUpInput = function () { @@ -732,6 +723,56 @@ MessageComposer.prototype.setUpInput = function () {
}
}
MessageComposer.prototype.setInlinePlaceholder = function (prefix, placeholder) {
this.inlinePlaceholderPrefix = prefix
this.inlinePlaceholderPrefixEl.html(encodeEntities(prefix));
this.inlinePlaceholderEl.html(encodeEntities(placeholder));
this.onChange();
}
MessageComposer.prototype.updateInlinePlaceholder = function () {
var prefix = this.inlinePlaceholderPrefix;
if (prefix) {
var value = this.textareaEl.val();
this.inlinePlaceholderWrap.toggleClass('active', value == prefix);
}
}
MessageComposer.prototype.setUpAutoComplete = function () {
this.scroller = new Scroller(this.autoCompleteEl, {maxHeight: 180});
var self = this;
this.autoCompleteEl.on('mousedown', function (e) {
e = e.originalEvent || e;
var target = $(e.target), mention, code, command, inlineID;
if (target[0].tagName != 'A') {
target = $(target[0].parentNode);
}
if (code = target.attr('data-code')) {
if (self.onEmojiSelected) {
self.onEmojiSelected(code, true);
}
EmojiHelper.pushPopularEmoji(code);
}
if (mention = target.attr('data-mention')) {
self.onMentionSelected(mention);
}
if (command = target.attr('data-command')) {
if (self.onCommandSelected) {
self.onCommandSelected(command);
}
self.hideSuggestions();
}
if (inlineID = target.attr('data-inlineid')) {
if (self.onInlineResultSend) {
self.onInlineResultSend(inlineID);
}
self.hideSuggestions();
}
return cancelEvent(e);
});
}
MessageComposer.prototype.setUpRich = function () {
this.textareaEl.hide();
this.richTextareaEl = $('<div class="composer_rich_textarea" contenteditable="true" dir="auto"></div>');
@ -763,7 +804,7 @@ MessageComposer.prototype.onKeyEvent = function (e) { @@ -763,7 +804,7 @@ MessageComposer.prototype.onKeyEvent = function (e) {
if (this.keyupStarted === undefined) {
this.keyupStarted = now;
}
if (now - this.keyupStarted > 10000) {
if (now - this.keyupStarted > 3000 || true) {
this.onChange();
}
else {
@ -771,6 +812,8 @@ MessageComposer.prototype.onKeyEvent = function (e) { @@ -771,6 +812,8 @@ MessageComposer.prototype.onKeyEvent = function (e) {
if (this.wasEmpty != !length) {
this.wasEmpty = !this.wasEmpty;
this.onChange();
} else if (this.inlinePlaceholderPrefix) {
this.onChange();
} else {
this.updateValueTO = setTimeout(this.onChange.bind(this), 1000);
}
@ -797,48 +840,57 @@ MessageComposer.prototype.onKeyEvent = function (e) { @@ -797,48 +840,57 @@ MessageComposer.prototype.onKeyEvent = function (e) {
if (this.autocompleteShown) {
if (e.keyCode == 38 || e.keyCode == 40) { // UP / DOWN
var next = e.keyCode == 40;
var currentSelected = $(this.autoCompleteEl).find('.composer_autocomplete_option_active');
if (currentSelected.length) {
var currentSelectedWrap = currentSelected[0].parentNode;
var nextWrap = currentSelectedWrap[next ? 'nextSibling' : 'previousSibling'];
currentSelected.removeClass('composer_autocomplete_option_active');
if (nextWrap) {
$(nextWrap).find('a').addClass('composer_autocomplete_option_active');
this.scroller.scrollToNode(nextWrap);
var currentSel = $(this.autoCompleteEl).find('li.composer_autocomplete_option_active');
var allLIs = Array.prototype.slice.call($(this.autoCompleteEl).find('li'));
var nextSel;
if (currentSel.length) {
var pos = allLIs.indexOf(currentSel[0]);
var nextPos = pos + (next ? 1 : -1);
nextSel = allLIs[nextPos];
currentSel.removeClass('composer_autocomplete_option_active');
if (nextSel) {
$(nextSel).addClass('composer_autocomplete_option_active');
this.scroller.scrollToNode(nextSel);
return cancelEvent(e);
}
}
var childNodes = this.autoCompleteEl[0].childNodes;
var nextWrap = childNodes[next ? 0 : childNodes.length - 1];
this.scroller.scrollToNode(nextWrap);
$(nextWrap).find('a').addClass('composer_autocomplete_option_active');
nextSel = allLIs[next ? 0 : allLIs.length - 1];
this.scroller.scrollToNode(nextSel);
$(nextSel).addClass('composer_autocomplete_option_active');
return cancelEvent(e);
}
if (e.keyCode == 13 || e.keyCode == 9) { // Enter or Tab
var currentSelected = $(this.autoCompleteEl).find('.composer_autocomplete_option_active');
if (!currentSelected.length && e.keyCode == 9) {
currentSelected = $(this.autoCompleteEl[0].childNodes[0]).find('a');
var currentSel = $(this.autoCompleteEl).find('li.composer_autocomplete_option_active');
if (!currentSel.length && e.keyCode == 9) {
currentSel = $(this.autoCompleteEl).find('li:first');
}
var code, mention, command;
if (code = currentSelected.attr('data-code')) {
currentSel = currentSel.find('a:first');
var code, mention, command, inlineID;
if (code = currentSel.attr('data-code')) {
this.onEmojiSelected(code, true);
EmojiHelper.pushPopularEmoji(code);
return cancelEvent(e);
}
if (mention = currentSelected.attr('data-mention')) {
if (mention = currentSel.attr('data-mention')) {
this.onMentionSelected(mention);
return cancelEvent(e);
}
if (command = currentSelected.attr('data-command')) {
if (command = currentSel.attr('data-command')) {
if (this.onCommandSelected) {
this.onCommandSelected(command, e.keyCode == 9);
}
return cancelEvent(e);
}
if (inlineID = currentSel.attr('data-inlineid')) {
if (self.onInlineResultSend) {
self.onInlineResultSend(inlineID);
}
return cancelEvent(e);
}
checkSubmit = true;
}
}
@ -918,6 +970,13 @@ MessageComposer.prototype.checkAutocomplete = function (forceFull) { @@ -918,6 +970,13 @@ MessageComposer.prototype.checkAutocomplete = function (forceFull) {
var value = textarea.value;
}
if (value &&
this.curInlineResults &&
this.curInlineResults.text == value) {
this.showInlineSuggestions(this.curInlineResults);
return;
};
if (!forceFull) {
value = value.substr(0, pos);
}
@ -1268,6 +1327,7 @@ MessageComposer.prototype.onChange = function (e) { @@ -1268,6 +1327,7 @@ MessageComposer.prototype.onChange = function (e) {
delete this.keyupStarted;
this.textareaEl.val(getRichValue(this.richTextareaEl[0])).trigger('change');
}
this.updateInlinePlaceholder();
}
MessageComposer.prototype.getEmojiHtml = function (code, emoji) {
@ -1347,8 +1407,7 @@ MessageComposer.prototype.blur = function () { @@ -1347,8 +1407,7 @@ MessageComposer.prototype.blur = function () {
}
}
MessageComposer.prototype.renderSuggestions = function (html) {
this.autoCompleteEl.html(html.join(''));
MessageComposer.prototype.renderSuggestions = function () {
this.autoCompleteWrapEl.show();
this.scroller.reinit();
this.updatePosition();
@ -1356,75 +1415,66 @@ MessageComposer.prototype.renderSuggestions = function (html) { @@ -1356,75 +1415,66 @@ MessageComposer.prototype.renderSuggestions = function (html) {
}
MessageComposer.prototype.showEmojiSuggestions = function (codes) {
var html = [];
var iconSize = Config.Mobile ? 26 : 20;
var emoticonCode, emoticonData, spritesheet, pos, categoryIndex;
var count = Math.min(5, codes.length);
var i, x, y;
for (i = 0; i < count; i++) {
emoticonCode = codes[i];
if (emoticonCode.code) {
emoticonCode = emoticonCode.code;
}
if (emoticonData = Config.Emoji[emoticonCode]) {
spritesheet = EmojiHelper.spritesheetPositions[emoticonCode];
categoryIndex = spritesheet[0];
pos = spritesheet[1];
x = iconSize * spritesheet[3];
y = iconSize * spritesheet[2];
html.push('<li><a class="composer_emoji_option" data-code="' + encodeEntities(emoticonCode) + '"><i class="emoji emoji-w', iconSize, ' emoji-spritesheet-' + categoryIndex + '" style="background-position: -' + x + 'px -' + y + 'px;"></i><span class="composer_emoji_shortcut">:' + encodeEntities(emoticonData[1][0]) + ':</span></a></li>');
}
}
this.renderSuggestions(html);
var self = this;
setZeroTimeout(function () {
self.autoCompleteScope.$apply(function () {
self.autoCompleteScope.type = 'emoji';
self.autoCompleteScope.emojiCodes = codes;
});
onContentLoaded(function () {
self.renderSuggestions();
});
});
}
MessageComposer.prototype.showMentionSuggestions = function (users) {
var html = [];
var user;
var count = users.length;
var i;
for (i = 0; i < count; i++) {
user = users[i];
html.push('<li><a class="composer_mention_option" data-mention="' + user.username + '"><span class="composer_user_photo" data-user-id="' + user.id + '"></span><span class="composer_user_name">' + user.rFullName + '</span><span class="composer_user_mention">@' + user.username + '</span></a></li>');
}
this.renderSuggestions(html);
var self = this;
this.autoCompleteEl.find('.composer_user_photo').each(function (k, element) {
self.getPeerImage($(element), element.getAttribute('data-user-id'));
setZeroTimeout(function () {
self.autoCompleteScope.$apply(function () {
self.autoCompleteScope.type = 'mentions';
self.autoCompleteScope.mentionUsers = users;
});
onContentLoaded(function () {
self.renderSuggestions();
});
});
}
MessageComposer.prototype.showCommandsSuggestions = function (commands) {
var html = [];
var command;
var count = Math.min(200, commands.length);
var i;
var self = this;
setZeroTimeout(function () {
self.autoCompleteScope.$apply(function () {
self.autoCompleteScope.type = 'commands';
self.autoCompleteScope.commands = commands;
});
onContentLoaded(function () {
self.renderSuggestions();
});
});
}
for (i = 0; i < count; i++) {
command = commands[i];
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>');
MessageComposer.prototype.showInlineSuggestions = function (botResults) {
if (!botResults || !botResults.results.length) {
this.hideSuggestions();
return;
}
this.renderSuggestions(html);
var self = this;
var usedImages = {};
this.autoCompleteEl.find('.composer_user_photo').each(function (k, element) {
var noReplace = true;
var botID = element.getAttribute('data-user-id');
if (!usedImages[botID]) {
usedImages[botID] = true;
noReplace = false;
}
self.getPeerImage($(element), botID, noReplace);
setZeroTimeout(function () {
self.autoCompleteScope.$apply(function () {
self.autoCompleteScope.type = 'inline';
self.autoCompleteScope.botResults = botResults;
});
onContentLoaded(function () {
self.renderSuggestions();
});
});
}
MessageComposer.prototype.setInlineSuggestions = function (botResults) {
this.curInlineResults = botResults;
this.checkAutocomplete();
}
MessageComposer.prototype.updatePosition = function () {
var offset = (this.richTextareaEl || this.textareaEl).offset();
var height = this.scroller.updateHeight();
@ -1438,6 +1488,8 @@ MessageComposer.prototype.updatePosition = function () { @@ -1438,6 +1488,8 @@ MessageComposer.prototype.updatePosition = function () {
}
MessageComposer.prototype.hideSuggestions = function () {
// console.trace();
// return;
this.autoCompleteWrapEl.hide();
delete this.autocompleteShown;
}

87
app/js/messages_manager.js

@ -181,7 +181,10 @@ angular.module('myApp.services') @@ -181,7 +181,10 @@ angular.module('myApp.services')
}
NotificationsManager.savePeerSettings(peerID, dialog.notify_settings);
ApiUpdatesManager.addChannelState(channelID, dialog.pts);
if (dialog.pts) {
ApiUpdatesManager.addChannelState(channelID, dialog.pts);
}
}
function getTopMessages (limit) {
@ -1291,10 +1294,12 @@ angular.module('myApp.services') @@ -1291,10 +1294,12 @@ angular.module('myApp.services')
isChannel = AppPeersManager.isChannel(peerID),
isMegagroup = isChannel && AppPeersManager.isMegagroup(peerID),
asChannel = isChannel && !isMegagroup ? true : false,
entities = [],
entities = options.entities || [],
message;
text = RichTextProcessor.parseMarkdown(text, entities);
if (!options.viaBotID) {
text = RichTextProcessor.parseMarkdown(text, entities);
}
if (historyStorage === undefined) {
historyStorage = historiesStorage[peerID] = {count: null, history: [], pending: []};
@ -1328,6 +1333,7 @@ angular.module('myApp.services') @@ -1328,6 +1333,7 @@ angular.module('myApp.services')
message: text,
random_id: randomIDS,
reply_to_msg_id: replyToMsgID,
via_bot_id: options.viaBotID,
entities: entities,
views: asChannel && 1,
pending: true
@ -1359,21 +1365,34 @@ angular.module('myApp.services') @@ -1359,21 +1365,34 @@ angular.module('myApp.services')
if (replyToMsgID) {
flags |= 1;
}
if (entities.length) {
flags |= 8;
}
if (asChannel) {
flags |= 16;
}
var apiPromise;
if (options.viaBotID) {
apiPromise = MtpApiManager.invokeApi('messages.sendInlineBotResult', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
random_id: randomID,
reply_to_msg_id: getMessageLocalID(replyToMsgID),
query_id: options.queryID,
id: options.resultID
}, sentRequestOptions);
} else {
if (entities.length) {
flags |= 8;
}
apiPromise = MtpApiManager.invokeApi('messages.sendMessage', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
message: text,
random_id: randomID,
reply_to_msg_id: getMessageLocalID(replyToMsgID),
entities: entities
}, sentRequestOptions)
}
// console.log(flags, entities);
MtpApiManager.invokeApi('messages.sendMessage', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
message: text,
random_id: randomID,
reply_to_msg_id: getMessageLocalID(replyToMsgID),
entities: entities
}, sentRequestOptions).then(function (updates) {
apiPromise.then(function (updates) {
if (updates._ == 'updateShortSentMessage') {
message.flags = updates.flags;
message.date = updates.date;
@ -1637,7 +1656,8 @@ angular.module('myApp.services') @@ -1637,7 +1656,8 @@ angular.module('myApp.services')
case 'inputMediaPhoto':
media = {
_: 'messageMediaPhoto',
photo: AppPhotosManager.getPhoto(inputMedia.id.id)
photo: AppPhotosManager.getPhoto(inputMedia.id.id),
caption: inputMedia.caption || ''
};
break;
@ -1648,9 +1668,14 @@ angular.module('myApp.services') @@ -1648,9 +1668,14 @@ angular.module('myApp.services')
};
media = {
_: 'messageMediaDocument',
'document': doc
'document': doc,
caption: inputMedia.caption || ''
};
break;
case 'messageMediaPending':
media = inputMedia;
break;
}
var flags = 0;
@ -1684,6 +1709,7 @@ angular.module('myApp.services') @@ -1684,6 +1709,7 @@ angular.module('myApp.services')
media: media,
random_id: randomIDS,
reply_to_msg_id: replyToMsgID,
via_bot_id: options.viaBotID,
views: asChannel && 1,
pending: true
};
@ -1718,13 +1744,26 @@ angular.module('myApp.services') @@ -1718,13 +1744,26 @@ angular.module('myApp.services')
sentRequestOptions.afterMessageID = pendingAfterMsgs[peerID].messageID;
}
MtpApiManager.invokeApi('messages.sendMedia', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
media: inputMedia,
random_id: randomID,
reply_to_msg_id: getMessageLocalID(replyToMsgID)
}, sentRequestOptions).then(function (updates) {
var apiPromise;
if (options.viaBotID) {
apiPromise = MtpApiManager.invokeApi('messages.sendInlineBotResult', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
random_id: randomID,
reply_to_msg_id: getMessageLocalID(replyToMsgID),
query_id: options.queryID,
id: options.resultID
}, sentRequestOptions);
} else {
apiPromise = MtpApiManager.invokeApi('messages.sendMedia', {
flags: flags,
peer: AppPeersManager.getInputPeerByID(peerID),
media: inputMedia,
random_id: randomID,
reply_to_msg_id: getMessageLocalID(replyToMsgID)
}, sentRequestOptions);
}
apiPromise.then(function (updates) {
ApiUpdatesManager.processUpdateMessage(updates);
}, function (error) {
toggleError(true);
@ -3007,7 +3046,7 @@ angular.module('myApp.services') @@ -3007,7 +3046,7 @@ angular.module('myApp.services')
};
}
})
})
});
return {
getConversations: getConversations,

518
app/js/services.js

@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
.service('AppUsersManager', function ($rootScope, $modal, $modalStack, $filter, $q, qSync, MtpApiFileManager, MtpApiManager, RichTextProcessor, ErrorService, Storage, _) {
.service('AppUsersManager', function ($rootScope, $modal, $modalStack, $filter, $q, qSync, MtpApiManager, RichTextProcessor, ErrorService, Storage, _) {
var users = {},
usernames = {},
userAccess = {},
@ -575,7 +575,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -575,7 +575,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
})
.service('AppChatsManager', function ($q, $rootScope, $modal, _, MtpApiFileManager, MtpApiManager, AppUsersManager, AppPhotosManager, RichTextProcessor) {
.service('AppChatsManager', function ($q, $rootScope, $modal, _, MtpApiManager, AppUsersManager, AppPhotosManager, RichTextProcessor) {
var chats = {},
usernames = {},
channelAccess = {},
@ -833,7 +833,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -833,7 +833,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}
})
.service('AppPeersManager', function (qSync, AppUsersManager, AppChatsManager, MtpApiManager) {
.service('AppPeersManager', function ($q, qSync, AppUsersManager, AppChatsManager, MtpApiManager) {
function getInputPeer (peerString) {
var firstChar = peerString.charAt(0),
@ -944,6 +944,24 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -944,6 +944,24 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
});
}
function resolveInlineMention (username) {
return resolveUsername(username).then(function (peerID) {
if (peerID > 0) {
var bot = AppUsersManager.getUser(peerID);
if (bot.pFlags.bot && bot.bot_inline_placeholder !== undefined) {
return qSync.when({
id: peerID,
placeholder: bot.bot_inline_placeholder
});
}
}
return $q.reject();
}, function (error) {
error.handled = true;
return $q.reject(error);
});
}
function getPeerID (peerString) {
if (angular.isObject(peerString)) {
return peerString.user_id
@ -990,6 +1008,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -990,6 +1008,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
getPeer: getPeer,
getPeerPhoto: getPeerPhoto,
resolveUsername: resolveUsername,
resolveInlineMention: resolveInlineMention,
isChannel: isChannel,
isMegagroup: isMegagroup,
isBot: isBot
@ -1222,6 +1241,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1222,6 +1241,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
});
break;
}
return $q.reject(error);
});
}
@ -1907,7 +1927,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1907,7 +1927,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}
})
.service('AppDocsManager', function ($sce, $rootScope, $modal, $window, $q, RichTextProcessor, MtpApiFileManager, FileManager, qSync) {
.service('AppDocsManager', function ($sce, $rootScope, $modal, $window, $q, $timeout, RichTextProcessor, MtpApiFileManager, FileManager, qSync) {
var docs = {},
docsForHistory = {},
windowW = $(window).width(),
@ -2096,13 +2116,18 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2096,13 +2116,18 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
});
downloadPromise.then(function (blob) {
delete historyDoc.progress;
if (blob) {
FileManager.getFileCorrectUrl(blob, doc.mime_type).then(function (url) {
historyDoc.url = $sce.trustAsResourceUrl(url);
var trustedUrl = $sce.trustAsResourceUrl(url);
historyDoc.url = trustedUrl;
doc.url = trustedUrl;
})
historyDoc.downloaded = true;
}
historyDoc.progress.percent = 100;
$timeout(function () {
delete historyDoc.progress;
});
console.log('file save done');
}, function (e) {
console.log('document download failed', e);
@ -2274,24 +2299,81 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2274,24 +2299,81 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}
})
.service('AppStickersManager', function ($q, $rootScope, $modal, _, FileManager, MtpApiManager, MtpApiFileManager, AppDocsManager, Storage) {
.service('AppStickersManager', function ($q, $rootScope, $modal, _, FileManager, MtpApiManager, AppDocsManager, Storage, ApiUpdatesManager) {
var currentStickers = [];
var currentStickersets = [];
var installedStickersets = {};
var stickersetItems = {};
var applied = false;
var started = false;
var applied = false;
var currentStickerSets = [];
$rootScope.$on('apiUpdate', function (e, update) {
if (update._ != 'updateStickerSets' &&
update._ != 'updateNewStickerSet' &&
update._ != 'updateDelStickerSet' &&
update._ != 'updateStickerSetsOrder') {
return false;
}
return Storage.get('all_stickers').then(function (stickers) {
if (!stickers ||
stickers.layer != Config.Schema.API.layer) {
$rootScope.$broadcast('stickers_changed');
}
switch (update._) {
case 'updateNewStickerSet':
var fullSet = update.stickerset;
var set = fullSet.set;
var pos = false;
for (var i = 0, len = stickers.sets.length; i < len; i++) {
if (stickers.sets[i].id == set.id) {
pos = i;
break;
}
}
if (pos !== false) {
stickers.sets.splice(pos, 1);
}
set.pFlags.installed = true;
stickers.sets.unshift(set);
stickers.fullSets[set.id] = fullSet;
break;
case 'updateDelStickerSet':
var set;
for (var i = 0, len = stickers.sets.length; i < len; i++) {
set = stickers.sets[i];
if (set.id == update.id) {
set.pFlags.installed = false;
stickers.sets.splice(i, 1);
break;
}
}
delete stickers.fullSets[update.id];
break;
case 'updateStickerSetsOrder':
var order = update.order;
stickers.sets.sort(function (a, b) {
return order.indexOf(a.id) - order.indexOf(b.id);
});
break;
}
stickers.hash = getStickerSetsHash(stickers.sets);
stickers.date = 0;
Storage.set({all_stickers: stickers}).then(function () {
$rootScope.$broadcast('stickers_changed');
});
});
});
return {
start: start,
getStickers: getStickers,
openStickersetLink: openStickersetLink,
openStickerset: openStickerset,
installStickerset: installStickerset,
pushPopularSticker: pushPopularSticker,
getStickers: getStickers,
getStickerset: getStickerset,
getStickersImages: getStickersImages
getStickerset: getStickerset
};
function start () {
@ -2301,104 +2383,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2301,104 +2383,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}
}
function getPopularStickers () {
return Storage.get('stickers_popular').then(function (popStickers) {
var result = [];
var i, len, docID;
if (popStickers && popStickers.length) {
for (i = 0, len = popStickers.length; i < len; i++) {
docID = popStickers[i][0];
if (AppDocsManager.hasDoc(docID)) {
result.push({id: docID, rate: popStickers[i][1]});
}
}
};
return result;
});
}
function pushPopularSticker (id) {
getPopularStickers().then(function (popularStickers) {
var exists = false;
var count = popularStickers.length;
var result = [];
for (var i = 0; i < count; i++) {
if (popularStickers[i].id == id) {
exists = true;
popularStickers[i].rate++;
}
result.push([popularStickers[i].id, popularStickers[i].rate]);
}
if (exists) {
result.sort(function (a, b) {
return b[1] - a[1];
});
} else {
if (result.length > 15) {
result = result.slice(0, 15);
}
result.push([id, 1]);
}
ConfigStorage.set({stickers_popular: result});
});
}
function processRawStickers(stickers) {
if (applied !== stickers.hash) {
applied = stickers.hash;
var i, j, len1, len2, doc, set, setItems, fullSet;
currentStickersets = [];
currentStickers = [];
len1 = stickers.sets.length;
for (i = 0; i < len1; i++) {
set = stickers.sets[i];
fullSet = stickers.fullSets[set.id];
len2 = fullSet.documents.length;
setItems = [];
for (j = 0; j < len2; j++) {
doc = fullSet.documents[j];
AppDocsManager.saveDoc(doc);
currentStickers.push(doc.id);
setItems.push(doc.id);
}
currentStickersets.push({
id: set.id,
title: set.title,
short_name: set.short_name,
installed: (set.flags & (1 << 0)) > 0,
disabled: (set.flags & (1 << 1)) > 0,
official: (set.flags & (1 << 2)) > 0,
docIDs: setItems
});
installedStickersets[set.id] = true;
}
}
return getPopularStickers().then(function (popularStickers) {
var resultStickersets = currentStickersets;
if (popularStickers.length) {
resultStickersets = currentStickersets.slice();
var setItems = [];
var i, len;
for (i = 0, len = popularStickers.length; i < len; i++) {
setItems.push(popularStickers[i].id);
}
resultStickersets.unshift({
id: 0,
title: _('im_stickers_tab_recent_raw'),
short_name: '',
installed: true,
disabled: false,
official: false,
docIDs: setItems
})
}
return resultStickersets;
});
}
function getStickers (force) {
return Storage.get('all_stickers').then(function (stickers) {
var layer = Config.Schema.API.layer;
@ -2424,7 +2408,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2424,7 +2408,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
return processRawStickers(newStickers);
}
return getStickerSets(newStickers).then(function () {
return getStickerSets(newStickers, stickers && stickers.fullSets).then(function () {
Storage.set({all_stickers: newStickers});
return processRawStickers(newStickers);
});
@ -2433,9 +2417,54 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2433,9 +2417,54 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
})
}
function getStickerSets (allStickers) {
function processRawStickers(stickers) {
if (applied !== stickers.hash) {
applied = stickers.hash;
var i, j, len1, len2, doc, set, docIDs, documents;
currentStickerSets = [];
len1 = stickers.sets.length;
for (i = 0; i < len1; i++) {
set = stickers.sets[i];
if (set.pFlags.disabled) {
continue;
}
documents = stickers.fullSets[set.id].documents;
len2 = documents.length;
docIDs = [];
for (j = 0; j < len2; j++) {
doc = documents[j];
AppDocsManager.saveDoc(doc);
docIDs.push(doc.id);
}
set.docIDs = docIDs;
currentStickerSets.push(set);
}
}
return getPopularStickers().then(function (popularStickers) {
var resultStickersets = currentStickerSets;
if (popularStickers.length) {
resultStickersets = currentStickerSets.slice();
var docIDs = [];
var i, len;
for (i = 0, len = popularStickers.length; i < len; i++) {
docIDs.push(popularStickers[i].id);
}
resultStickersets.unshift({
id: 0,
title: _('im_stickers_tab_recent_raw'),
short_name: '',
docIDs: docIDs
});
}
return resultStickersets;
});
}
function getStickerSets (allStickers, prevCachedSets) {
var promises = [];
var cachedSets = allStickers.fullSets || {};
var cachedSets = prevCachedSets || allStickers.fullSets || {};
allStickers.fullSets = {};
angular.forEach(allStickers.sets, function (shortSet) {
var fullSet = cachedSets[shortSet.id];
@ -2457,24 +2486,46 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2457,24 +2486,46 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
return $q.all(promises);
}
function downloadStickerThumb (docID) {
var doc = AppDocsManager.getDoc(docID);
var thumbLocation = angular.copy(doc.thumb.location);
thumbLocation.sticker = true;
return MtpApiFileManager.downloadSmallFile(thumbLocation).then(function (blob) {
return {
id: doc.id,
src: FileManager.getUrl(blob, 'image/webp')
function getPopularStickers () {
return Storage.get('stickers_popular').then(function (popStickers) {
var result = [];
var i, len, docID;
if (popStickers && popStickers.length) {
for (i = 0, len = popStickers.length; i < len; i++) {
docID = popStickers[i][0];
if (AppDocsManager.hasDoc(docID)) {
result.push({id: docID, rate: popStickers[i][1]});
}
}
};
return result;
});
}
function getStickersImages () {
var promises = [];
angular.forEach(currentStickers, function (docID) {
promises.push(downloadStickerThumb (docID));
function pushPopularSticker (id) {
getPopularStickers().then(function (popularStickers) {
var exists = false;
var count = popularStickers.length;
var result = [];
for (var i = 0; i < count; i++) {
if (popularStickers[i].id == id) {
exists = true;
popularStickers[i].rate++;
}
result.push([popularStickers[i].id, popularStickers[i].rate]);
}
if (exists) {
result.sort(function (a, b) {
return b[1] - a[1];
});
} else {
if (result.length > 15) {
result = result.slice(0, 15);
}
result.push([id, 1]);
}
ConfigStorage.set({stickers_popular: result});
});
return $q.all(promises);
}
function getStickerset (inputStickerset) {
@ -2484,31 +2535,32 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2484,31 +2535,32 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
for (var i = 0; i < result.documents.length; i++) {
AppDocsManager.saveDoc(result.documents[i]);
}
result.installed = installedStickersets[result.set.id] !== undefined;
return result;
});
}
function installStickerset (set, uninstall) {
function installStickerset (fullSet, uninstall) {
var method = uninstall
? 'messages.uninstallStickerSet'
: 'messages.installStickerSet';
var inputStickerset = {
_: 'inputStickerSetID',
id: set.id,
access_hash: set.access_hash
id: fullSet.set.id,
access_hash: fullSet.set.access_hash
};
return MtpApiManager.invokeApi(method, {
stickerset: inputStickerset,
disabled: false
}).then(function (result) {
var update;
if (uninstall) {
delete installedStickersets[set.id];
update = {_: 'updateDelStickerSet', id: fullSet.set.id};
} else {
installedStickersets[set.id] = true;
update = {_: 'updateNewStickerSet', stickerset: fullSet};
}
Storage.remove('all_stickers').then(function () {
getStickers(true);
ApiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: update
});
});
}
@ -2530,6 +2582,186 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2530,6 +2582,186 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
windowClass: 'stickerset_modal_window mobile_modal'
});
}
function getStickerSetsHash (stickerSets) {
var acc = 0, set;
for (var i = 0; i < stickerSets.length; i++) {
set = stickerSets[i];
if (set.pFlags.disabled || !set.pFlags.installed) {
continue;
}
acc = ((acc * 20261) + 0x80000000 + set.hash) % 0x80000000;
}
return acc;
}
})
.service('AppInlineBotsManager', function (MtpApiManager, AppMessagesManager, AppDocsManager, AppPhotosManager, RichTextProcessor, AppUsersManager) {
var inlineResults = {};
return {
sendInlineResult: sendInlineResult,
regroupWrappedResults: regroupWrappedResults,
getInlineResults: getInlineResults
};
function getInlineResults (botID, query, offset) {
return MtpApiManager.invokeApi('messages.getInlineBotResults', {
bot: AppUsersManager.getUserInput(botID),
query: query,
offset: offset
}, {timeout: 1, stopTime: -1, noErrorBox: true}).then(function(botResults) {
var queryID = botResults.query_id;
delete botResults._;
delete botResults.flags;
delete botResults.query_id;
angular.forEach(botResults.results, function (result) {
var qID = queryID + '_' + result.id;
result.qID = qID;
result.botID = botID;
result.rTitle = RichTextProcessor.wrapRichText(result.title, {noLinebreaks: true, noLinks: true});
result.rDescription = RichTextProcessor.wrapRichText(result.description, {noLinebreaks: true, noLinks: true});
result.initials = (result.url || result.title || result.type || '').substr(0, 1)
if (result.document) {
AppDocsManager.saveDoc(result.document);
}
if (result.photo) {
AppPhotosManager.savePhoto(result.photo);
}
inlineResults[qID] = result;
});
return botResults;
});
}
function regroupWrappedResults (results, rowW, rowH) {
if (!results ||
!results[0] ||
results[0].type != 'photo' && results[0].type != 'gif') {
return;
}
var ratios = [];
angular.forEach(results, function (result) {
var w, h;
if (result._ == 'botInlineMediaResultDocument') {
w = result.document.w;
h = result.document.h;
}
else if (result._ == 'botInlineMediaResultPhoto') {
var photoSize = (result.photo.sizes || [])[0];
w = photoSize && photoSize.w;
h = photoSize && photoSize.h;
}
else {
w = result.w;
h = result.h;
}
if (!w || !h) {
w = h = 1;
}
ratios.push(w / h);
});
var rows = [];
var curCnt = 0;
var curW = 0;
angular.forEach(ratios, function (ratio) {
var w = ratio * rowH;
curW += w;
if (!curCnt || curCnt < 4 && curW < (rowW * 1.1)) {
curCnt++;
} else {
rows.push(curCnt);
curCnt = 1;
curW = w;
}
});
if (curCnt) {
rows.push(curCnt);
}
var i = 0;
var thumbs = [];
var lastRowI = rows.length - 1;
angular.forEach(rows, function (rowCnt, rowI) {
var lastRow = rowI == lastRowI;
var curRatios = ratios.slice(i, i + rowCnt);
var sumRatios = 0;
angular.forEach(curRatios, function (ratio) {
sumRatios += ratio;
});
angular.forEach(curRatios, function (ratio, j) {
var thumbH = rowH;
var thumbW = rowW * ratio / sumRatios;
var realW = thumbH * ratio;
if (lastRow && thumbW > realW) {
thumbW = realW;
}
var result = results[i + j];
result.thumbW = Math.floor(thumbW);
result.thumbH = Math.floor(thumbH);
});
i += rowCnt;
});
}
function sendInlineResult (peerID, qID, options) {
var inlineResult = inlineResults[qID];
if (inlineResult === undefined) {
return false;
}
var splitted = qID.split('_');
var queryID = splitted.shift();
var resultID = splitted.join('_');
options = options || {};
options.viaBotID = inlineResult.botID;
options.queryID = queryID;
options.resultID = resultID;
if (inlineResult.send_message._ == 'botInlineMessageText') {
options.entities = inlineResult.send_message.entities;
AppMessagesManager.sendText(peerID, inlineResult.send_message.message, options);
} else {
var caption = '';
if (inlineResult.send_message._ == 'botInlineMessageMediaAuto') {
caption = inlineResult.send_message.caption;
}
var inputMedia = false;
if (inlineResult._ == 'botInlineMediaResultDocument') {
var doc = inlineResult.document;
inputMedia = {
_: 'inputMediaDocument',
id: {_: 'inputDocument', id: doc.id, access_hash: doc.access_hash},
caption: caption
};
}
else if (inlineResult._ == 'botInlineMediaResultPhoto') {
var photo = inlineResult.photo;
inputMedia = {
_: 'inputMediaPhoto',
id: {_: 'inputPhoto', id: photo.id, access_hash: photo.access_hash},
caption: caption
};
}
if (!inputMedia) {
inputMedia = {
_: 'messageMediaPending',
type: inlineResult.type,
file_name: inlineResult.title || inlineResult.content_url || inlineResult.url,
size: 0,
progress: {percent: 30, total: 0}
};
}
AppMessagesManager.sendOther(peerID, inputMedia, options);
}
}
})
.service('ApiUpdatesManager', function ($rootScope, MtpNetworkerFactory, AppUsersManager, AppChatsManager, AppPeersManager, MtpApiManager) {
@ -2828,6 +3060,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2828,6 +3060,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}
function addChannelState (channelID, pts) {
if (!pts) {
throw new Error('Add channel state without pts ' + channelID);
}
if (channelStates[channelID] === undefined) {
channelStates[channelID] = {
pts: pts,
@ -2842,9 +3077,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2842,9 +3077,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
function getChannelState (channelID, pts) {
if (channelStates[channelID] === undefined) {
if (!pts) {
throw new Error('Get channel empty state without pts ' + channelID);
}
addChannelState(channelID, pts);
}
return channelStates[channelID];
@ -3997,7 +4229,11 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -3997,7 +4229,11 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
type: 'JUMP_EXT_URL',
url: url
}).then(function () {
window.open(url, '_blank');
var target = '_blank';
if (url.search('https://telegram.me/') === 0) {
target = '_self';
}
window.open(url, target);
});
return true;
}

335
app/less/app.less

@ -420,20 +420,56 @@ a { @@ -420,20 +420,56 @@ a {
display: block;
border-radius: 100%;
}
.progress-arc circle {
stroke-dashoffset: 0;
stroke: rgba(0,0,0,0.3);
stroke-width: 3px;
}
.progress-arc .progress-arc-bar {
stroke: #FFF;
stroke-dashoffset: 0;
transform-origin: center center;
transition: stroke-dasharray 500ms linear;
fill: transparent;
.progress-arc-intermediate & {
stroke: #68a4d1;
-webkit-animation: infinite_rotation 0.8s linear infinite;
-moz-animation: infinite_rotation 0.8s linear infinite;
-ms-animation: infinite_rotation 0.8s linear infinite;
animation: infinite_rotation 0.8s linear infinite;
}
-webkit-animation: infinite_rotation 2s linear infinite;
-moz-animation: infinite_rotation 2s linear infinite;
-ms-animation: infinite_rotation 2s linear infinite;
animation: infinite_rotation 2s linear infinite;
.composer_progress_icon & {
stroke: rgba(0,0,0,0.3);
}
.progress-arc-percent & {
stroke: #FFF;
stroke: rgba(255,255,255,0.95);
transition: stroke-dasharray 500ms linear;
-webkit-animation: infinite_rotation 2s linear infinite;
-moz-animation: infinite_rotation 2s linear infinite;
-ms-animation: infinite_rotation 2s linear infinite;
animation: infinite_rotation 2s linear infinite;
}
}
.stop0 {
stop-opacity: 1.0;
stop-color: #68a4d1;
.composer_progress_icon & {
stop-color: rgba(0,0,0,0.3);
}
}
.stop60 {
stop-opacity: 1.0;
stop-color: #68a4d1;
.composer_progress_icon & {
stop-color: rgba(0,0,0,0.3);
}
}
.stop100 {
stop-opacity: 0.0;
stop-color: #68a4d1;
.composer_progress_icon & {
stop-color: rgba(0,0,0,0.3);
}
}
/* Infinite rotation */
@ -926,13 +962,14 @@ a.tg_radio_on:hover i.icon-radio { @@ -926,13 +962,14 @@ a.tg_radio_on:hover i.icon-radio {
background-position: -5px -10px;
}
.icon-tg-title {
width: 63px;
height: 16px;
width: 62px;
height: 14px;
display: inline-block;
vertical-align: middle;
.image-2x('../img/Telegram.png', 63px, 16px);
background-image: url('../img/Telegram.svg');
background-repeat: no-repeat;
background-position: 0 0;
margin-top: 1px;
}
.login_head_submit_wrap,
.login_head_submit_progress {
@ -1392,7 +1429,8 @@ a.im_dialog_selected { @@ -1392,7 +1429,8 @@ a.im_dialog_selected {
}
.im_history {
padding: 20px 0 0 0;
// padding: 20px 0 0 0;
padding: 0;
position: relative;
}
.im_message_unread_split {
@ -1403,12 +1441,14 @@ a.im_dialog_selected { @@ -1403,12 +1441,14 @@ a.im_dialog_selected {
margin: 10px 0;
}
.im_message_author,
.im_message_fwd_author {
.im_message_fwd_author,
.im_message_via_author {
color: #3a6d99;
font-weight: bold;
}
.non_osx .im_message_author,
.non_osx .im_message_fwd_author {
.non_osx .im_message_fwd_author,
.non_osx .im_message_via_author {
font-size: 12px;
}
.im_message_author_via {
@ -1505,6 +1545,9 @@ div.im_message_video_thumb { @@ -1505,6 +1545,9 @@ div.im_message_video_thumb {
.image-2x('../img/icons/IconsetW.png', 42px, 1171px);
background-position: 0 -590px;
.is_2x & {
background-position: 0 -591px;
}
}
.im_message_geopoint {
@ -1999,6 +2042,10 @@ img.im_message_document_thumb { @@ -1999,6 +2042,10 @@ img.im_message_document_thumb {
padding: 0 0 20px 10px;
}
.im_message_date {
cursor: pointer;
}
div.im_message_author,
div.im_message_body {
display: block;
@ -2245,6 +2292,37 @@ a.im_message_fwd_photo { @@ -2245,6 +2292,37 @@ a.im_message_fwd_photo {
display: none;
}
}
.im_history_loading {
width: 60px;
margin: 0 auto;
visibility: hidden;
&.vertical-aligned {
visibility: visible;
}
}
.im_history_loading_more {
display: block;
width: 26px;
margin: 0 auto;
padding: 20px 0 0;
visibility: hidden;
&.im_history_loading_more_active {
visibility: visible;
}
}
.im_history_loading_less {
display: block;
width: 26px;
margin: 0 auto;
visibility: hidden;
&.im_history_loading_less_active {
visibility: visible;
}
}
.im_send_panel_wrap {
margin: 0 auto;
@ -2293,6 +2371,22 @@ textarea.im_message_field { @@ -2293,6 +2371,22 @@ textarea.im_message_field {
height: 50px;
resize: none;
}
.im_inline_placeholder_wrap {
position: absolute;
margin-top: 2px;
white-space: nowrap;
pointer-events: none;
display: none;
}
.im_inline_placeholder_wrap.active {
display: block;
}
.im_inline_placeholder_prefix {
visibility: hidden;
}
.im_inline_placeholder {
color: #9aa2ab;
}
.icon-online {
background: #6ec26d;
@ -2423,7 +2517,27 @@ img.img_fullsize { @@ -2423,7 +2517,27 @@ img.img_fullsize {
}
/* Message composer */
.composer_progress_icon {
display: block;
opacity: 0;
position: absolute;
right: 3px;
top: 2px;
cursor: pointer;
padding: 0;
width: 22px;
height: 22px;
margin-top: 1px;
transition: opacity cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.2s;
pointer-events: none;
.composer_progress_enabled & {
opacity: 1;
}
}
.composer_emoji_insert_btn {
opacity: 1;
display: block;
position: absolute;
right: 3px;
@ -2434,7 +2548,13 @@ img.img_fullsize { @@ -2434,7 +2548,13 @@ img.img_fullsize {
width: 22px;
height: 22px;
margin-top: 1px;
transition: opacity cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.2s;
.composer_progress_enabled & {
opacity: 0;
}
}
.icon-emoji {
display: inline-block;
width: 22px;
@ -2757,6 +2877,7 @@ a.composer_emoji_btn { @@ -2757,6 +2877,7 @@ a.composer_emoji_btn {
border-radius: 0;
margin-top: -5px;
margin-left: -1px;
width: 180px;
}
.composer_dropdown {
@ -2780,9 +2901,10 @@ a.composer_emoji_btn { @@ -2780,9 +2901,10 @@ a.composer_emoji_btn {
}
li a:hover,
li a.composer_autocomplete_option_active {
li.composer_autocomplete_option_active a {
color: #52719a;
background: #f2f6fa;
text-decoration: none;
}
}
@ -2802,7 +2924,7 @@ a.composer_emoji_btn { @@ -2802,7 +2924,7 @@ a.composer_emoji_btn {
.composer_dropdown {
li a:hover .composer_user_mention,
li a.composer_autocomplete_option_active .composer_user_mention {
li.composer_autocomplete_option_active a .composer_user_mention {
color: #698192;
}
}
@ -2861,7 +2983,7 @@ a.composer_emoji_btn { @@ -2861,7 +2983,7 @@ a.composer_emoji_btn {
text-overflow: ellipsis;
}
a.composer_command_option:hover .composer_command_desc,
a.composer_command_option.composer_autocomplete_option_active .composer_command_desc {
.composer_autocomplete_option_active a.composer_command_option .composer_command_desc {
color: #698192;
}
.composer_command_desc .emoji {
@ -3031,6 +3153,64 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before { @@ -3031,6 +3153,64 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before {
}
}
.inline_results_wrap {
line-height: 0;
}
.inline_result_wrap {
display: block;
}
.inline_result_gif,
.inline_result_photo {
display: inline-block;
}
.inline_result_article {
display: block;
}
.inline_article_thumb_wrap {
width: 50px;
height: 50px;
margin-right: 10px;
pointer-events: none;
}
.inline_article_thumb {
max-width: 50px;
max-height: 50px;
line-height: 0;
}
.inline_article_thumb_initials {
background: rgba(0,0,0, 0.05);
line-height: 50px;
text-align: center;
font-size: 25px;
text-transform: uppercase;
}
.inline_article_content_wrap {
overflow: hidden;
pointer-events: none;
}
.inline_article_title {
font-weight: bold;
}
.composer_dropdown > li.inline_result_gif > a,
.composer_dropdown > li.inline_result_photo > a {
padding: 0;
line-height: 0;
display: block;
overflow: hidden;
}
.inline_result_gif .img_gif_video,
.inline_result_photo .inline_result_photo_image {
object-fit: cover;
}
.inline_result_gif_mtproto,
.inline_result_gif_http,
.inline_result_photo_mtproto,
.inline_result_photo_http {
pointer-events: none;
}
.error_modal_window {
.modal-dialog {
@ -3553,64 +3733,83 @@ h5 { @@ -3553,64 +3733,83 @@ h5 {
}
/* Gif documents */
.img_gif_with_progress_wrap {
.img_gif_image_wrap {
position: relative;
overflow: hidden;
float: left;
margin-top: 3px;
max-width: 100%;
}
.img_gif_meta {
background: rgba(0,0,0,0.3);
width: 40px;
height: 40px;
line-height: 0;
position: absolute;
z-index: 2;
border-radius: 50%;
overflow: hidden;
margin: 0 auto;
top: 50%;
left: 50%;
margin-left: -20px;
margin-top: -20px;
pointer-events: none;
}
.img_gif_label {
font-weight: bold;
color: #FFF;
display: block;
line-height: 40px;
font-size: 13px;
text-align: center;
}
.icon-cancel {
position: absolute;
top: 50%;
left: 50%;
margin-left: -9px;
margin-top: -1px;
.icon-bar {
display: block;
width: 18px;
height: 2px;
background: #FFF;
transform-origin: 50% 50%;
&:first-child {
.transform(rotate(-45deg));
}
&:last-child {
.transform(translate3d(0,-2px,0) rotate(45deg));
}
}
}
.img_gif_thumb {
-webkit-filter: blur(3px);
-moz-filter: blur(3px);
-o-filter: blur(3px);
-ms-filter: blur(3px);
filter: blur(3px);
-webkit-filter: blur(2px);
-moz-filter: blur(2px);
-o-filter: blur(2px);
-ms-filter: blur(2px);
filter: blur(2px);
filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius='3');
margin: -1px;
padding: 1px;
transform-origin: center center;
-webkit-transform-origin: center center;
-webkit-transform: scale(1.02, 1.02);
transform: scale(1.02, 1.02);
max-width: 100%;
height: auto;
}
.img_gif_image {
max-width: 100%;
}
.img_gif_info_wrap {
color: #fff;
font-size: 10px;
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 4px;
.img_gif_meta_contents {
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.2s;
}
.img_gif_label,
.img_gif_size {
padding: 1px 8px;
background: rgba(0,0,0,0.5);
border-radius: 3px;
overflow: hidden;
.img_gif_meta_contents.ng-leave.ng-leave-active,
.img_gif_meta_contents.ng-enter {
opacity: 0;
}
.img_gif_progress_wrap {
position: absolute;
bottom: 0;
left: 0;
right: 0;
.tg_progress {
background: rgba(0,0,0, 0.6);
border-color: rgba(0,0,0, 0.6);
border-width: 8px;
height: 18px;
border-radius: 0;
}
.progress-bar {
background: #fff;
height: 2px;
}
.img_gif_meta_contents.ng-leave,
.img_gif_meta_contents.ng-enter.ng-enter-active {
opacity: 1;
}
.countries_modal_window {

7
app/less/desktop.less

@ -748,7 +748,6 @@ a.footer_link.active:active { @@ -748,7 +748,6 @@ a.footer_link.active:active {
line-height: 18px;
width: 100%;
height: 39px;
padding: 13px 0 8px;
overflow: hidden;
-webkit-user-select: none;
}
@ -758,7 +757,7 @@ a.footer_link.active:active { @@ -758,7 +757,7 @@ a.footer_link.active:active {
color: #999;
max-width: 556px;
margin: 0 auto;
padding: 0 81px 0 85px;
padding: 13px 81px 8px 85px;
a.im_history_typing_author {
color: #999;
@ -767,6 +766,10 @@ a.footer_link.active:active { @@ -767,6 +766,10 @@ a.footer_link.active:active {
}
}
.im_history_loading_less {
margin-top: 5px;
}
/* Contacts modal */
.contacts_modal {
&_window {

27
app/partials/desktop/composer_dropdown.html

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
<div ng-switch="type">
<ul ng-switch-when="mentions" class="composer_dropdown">
<li ng-repeat="user in mentionUsers">
<a class="composer_mention_option" data-mention="{{user.username}}">
<span class="composer_user_photo" my-peer-photolink="user.id" img-class="composer_user_photo"></span>
<span class="composer_user_name" ng-bind-html="user.rFullName"></span>
<span class="composer_user_mention" ng-bind="'@' + user.username"></span>
</a>
</li>
</ul>
<ul ng-switch-when="commands" class="composer_dropdown">
<li ng-repeat="command in commands track by (command.botID + command.value)">
<a class="composer_command_option" data-command="{{command.value}}">
<span class="composer_user_photo" my-peer-photolink="command.botID" img-class="composer_user_photo"></span>
<span class="composer_command_value" ng-bind="command.value"></span>
<span class="composer_command_desc" ng-bind-html="command.rDescription"></span>
</a>
</li>
</ul>
<ul ng-switch-when="emoji" my-emoji-suggestions="emojiCodes" class="composer_dropdown"></ul>
<div ng-switch-when="inline" my-inline-results="botResults"></div>
</div>

11
app/partials/desktop/full_gif.html

@ -3,11 +3,14 @@ @@ -3,11 +3,14 @@
<div class="img_gif_image_wrap">
<div class="img_gif_meta" ng-show="!isActive" ng-switch="document.progress.enabled">
<div ng-switch-when="true" my-arc-progress="document.progress.percent"></div>
<div ng-switch-default class="img_gif_info_wrap">
<div class="img_gif_label pull-left">GIF</div>
<div ng-if="!document.downloaded" class="img_gif_size pull-right" ng-bind="::document.size | formatSize"></div>
<div ng-switch-when="true" class="img_gif_meta_contents">
<i class="icon icon-cancel">
<i class="icon icon-bar"></i>
<i class="icon icon-bar"></i>
</i>
<div my-arc-progress="document.progress.percent"></div>
</div>
<div ng-switch-default class="img_gif_label noselect img_gif_meta_contents">GIF</div>
</div>
<div ng-if="document.url" ng-show="document.downloaded &amp;&amp; isActive" ng-switch="document.mime_type == 'video/mp4'">

36
app/partials/desktop/im.html

@ -90,8 +90,8 @@ @@ -90,8 +90,8 @@
<div ng-switch-default class="im_history_not_selected" my-vertical-position="0.35" padding="true" my-i18n="im_select_a_chat"></div>
</div>
<div ng-show="!state.notSelected &amp;&amp; !state.loaded" class="im_history_not_selected" my-vertical-position="0.35" padding="true">
<my-i18n msgid="im_loading_history"></my-i18n><span my-loading-dots></span>
<div ng-show="!state.notSelected &amp;&amp; !state.loaded" class="im_history_loading" my-vertical-position="0.35" padding="true">
<div my-arc-progress stroke="5" width="60"></div>
</div>
<div ng-show="state.loaded">
@ -103,14 +103,18 @@ @@ -103,14 +103,18 @@
<div class="im_history_scrollable">
<div class="im_history" ng-class="{im_history_selectable: !historyState.botActions, im_history_select_active: historyState.selectActions}">
<div ng-if="state.empty" class="im_history_empty" ng-switch="state.mayBeHasMore" my-vertical-position="0.25" padding="true">
<span ng-switch-when="true">
<my-i18n msgid="im_loading_history"></my-i18n><span my-loading-dots></span>
</span>
<span ng-switch-default my-i18n="im_no_messages"></span>
<div class="im_history_empty_wrap" ng-show="state.empty" ng-switch="state.mayBeHasMore">
<div ng-switch-when="true" class="im_history_loading" my-vertical-position="0.25" padding="true">
<div my-arc-progress stroke="5" width="60"></div>
</div>
<div ng-switch-default class="im_history_empty" my-vertical-position="0.25" padding="true" my-i18n="im_no_messages"></div>
</div>
<div class="im_history_messages" ng-class="{im_history_messages_group: historyPeer.id < 0}">
<div class="im_history_loading_more" ng-class="{im_history_loading_more_active: state.moreActive}">
<div my-arc-progress stroke="3" width="26"></div>
</div>
<div class="im_history_messages_peer" ng-show="peerHistory.peerID == historyPeer.id" ng-repeat="peerHistory in peerHistories">
<div class="im_history_message_wrap" my-message ng-repeat="historyMessage in peerHistory.messages"></div>
</div>
@ -118,9 +122,12 @@ @@ -118,9 +122,12 @@
</div>
<div class="im_history_typing_wrap">
<div class="im_history_typing_wrap" ng-switch="historyState.skipped">
<div class="im_history_typing" ng-show="historyState.typing.length > 0 &amp;&amp; !historyFilter.mediaType &amp;&amp; !state.empty" ng-switch="historyState.typing.length" my-i18n>
<div ng-switch-when="true" class="im_history_loading_less" ng-class="{im_history_loading_less_active: state.lessActive}">
<div my-arc-progress stroke="3" width="26"></div>
</div>
<div ng-switch-default class="im_history_typing" ng-show="historyState.typing.length > 0 &amp;&amp; !historyFilter.mediaType &amp;&amp; !state.empty" ng-switch="historyState.typing.length" my-i18n>
<span ng-switch-when="1" my-i18n-format="im_one_typing"></span>
<span ng-switch-when="2" my-i18n-format="im_two_typing"></span>
<span ng-switch-default my-i18n-format="im_many_typing"></span>
@ -175,7 +182,9 @@ @@ -175,7 +182,9 @@
</a>
<a class="pull-left im_panel_own_photo" my-peer-photolink="draftMessage.isBroadcast ? historyPeer.id : ownID" img-class="im_panel_own_photo" watch="true" ng-click="openSettings()" no-open="true"></a>
<form my-send-form draft-message="draftMessage" mentions="mentions" commands="commands" class="im_send_form" ng-class="{im_send_form_empty: !draftMessage.text.length}">
<form my-send-form draft-message="draftMessage" mentions="mentions" commands="commands" class="im_send_form" ng-class="{im_send_form_empty: !draftMessage.text.length, composer_progress_enabled: draftMessage.inlineProgress}">
<div class="im_send_form_inline_results" my-inline-results="inlineResults"></div>
<div class="im_send_reply_wrap" ng-if="draftMessage.replyToMessage != null">
<a class="im_send_reply_cancel" ng-mousedown="draftMessage.replyClear()"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a>
@ -189,23 +198,24 @@ @@ -189,23 +198,24 @@
<div class="im_send_field_wrap" ng-class="historyState.replyKeyboard._ == 'replyKeyboardMarkup' ? 'im_send_field_wrap_2ndbtn' : ''">
<a class="composer_emoji_insert_btn"><i class="icon icon-emoji"></i></a>
<div class="composer_progress_icon" my-arc-progress width="22" stroke="2.5"></div>
<a class="composer_command_btn" ng-show="!historyState.replyKeyboard && commands.list.length > 0 && (!draftMessage.text.length || draftMessage.text[0] == '/')" ng-mousedown="toggleSlash($event)" ng-class="draftMessage.text[0] == '/' ? 'active' : ''"><i class="icon icon-slash"></i></a>
<a class="composer_keyboard_btn" ng-show="historyState.replyKeyboard._ == 'replyKeyboardMarkup'" ng-mousedown="replyKeyboardToggle($event)" ng-class="!historyState.replyKeyboard.pFlags.hidden ? 'active' : ''"><i class="icon icon-keyboard"></i></a>
<div class="im_send_dropbox_wrap" my-i18n="im_photos_drop_text"></div>
<textarea ng-model="draftMessage.text" class="form-control im_message_field no_outline" dir="auto"></textarea>
<textarea ng-model="draftMessage.text" class="form-control im_message_field no_outline" dir="auto" ng-trim="false"></textarea>
</div>
<div class="im_send_buttons_wrap clearfix">
<button type="submit" class="btn btn-md im_submit" my-i18n="im_submit_message"></button>
<div class="im_attach pull-left">
<input type="file" class="im_attach_input" size="28" multiple="true" title="{{'im_attach_file_title' | i18n}}" />
<input type="file" class="im_attach_input" size="28" multiple="multiple" title="{{'im_attach_file_title' | i18n}}" />
<i class="icon icon-paperclip"></i>
</div>
<div class="im_media_attach pull-left">
<input type="file" class="im_media_attach_input" size="28" multiple="true" accept="image/*, video/*, audio/*" title="{{'im_media_attach_title' | i18n}}"/>
<input type="file" class="im_media_attach_input" size="28" multiple="multiple" accept="image/*, video/*, audio/*" title="{{'im_media_attach_title' | i18n}}"/>
<i class="icon icon-camera"></i>
</div>

53
app/partials/desktop/inline_results.html

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
<ul class="inline_results_wrap composer_dropdown">
<li class="inline_result_wrap" ng-class="'inline_result_' + result.type" ng-repeat="result in botResults.results track by result.qID" ng-switch="result.type">
<a ng-switch-when="gif" class="img_gif_with_progress_wrap" data-inlineid="{{result.qID}}" ng-style="::{width: result.thumbW, height: result.thumbH}" ng-switch="result._">
<div ng-switch-when="botInlineMediaResultDocument" ng-switch="result.document.url !== undefined" class="inline_result_gif_mtproto">
<div ng-switch-when="true" ng-switch="result.document.mime_type == 'video/mp4'">
<video ng-switch-when="true" width="{{result.thumbW}}" height="{{result.thumbH}}" loop autoplay class="img_gif_video">
<source ng-src="{{result.document.url}}" type="video/mp4">
</video>
<img ng-switch-default class="img_gif_image" ng-src="{{result.document.url}}" width="{{result.thumbW}}" height="{{result.thumbH}}" />
</div>
<div ng-switch-default class="img_gif_image_wrap">
<img class="img_gif_thumb" my-load-thumb thumb="result.document.thumb" width="{{result.thumbW}}" height="{{result.thumbH}}" />
</div>
</div>
<div ng-switch-default ng-switch="result.contentUrl !== undefined" class="inline_result_gif_http">
<div ng-switch-when="true" ng-switch="result.content_type == 'video/mp4'">
<video ng-switch-when="true" width="{{result.thumbW}}" height="{{result.thumbH}}" loop autoplay class="img_gif_video">
<source ng-src="{{result.contentUrl}}" type="video/mp4">
</video>
<img ng-switch-default class="img_gif_image" ng-src="{{result.contentUrl}}" width="{{result.thumbW}}" height="{{result.thumbH}}" />
</div>
<img ng-switch-default ng-if="result.thumbUrl !== undefined" class="img_gif_thumb" width="{{result.thumbW}}" height="{{result.thumbH}}" ng-src="{{result.thumbUrl}}" />
</div>
</a>
<a ng-switch-when="photo" data-inlineid="{{result.qID}}" ng-style="::{width: result.thumbW, height: result.thumbH}" ng-switch="result._">
<div ng-switch-when="botInlineMediaResultPhoto" class="inline_result_photo_mtproto">
<img
class="inline_result_photo_image"
my-load-thumb
thumb="result.thumb"
/>
</div>
<div ng-switch-default ng-switch="result.contentUrl !== undefined" class="inline_result_photo_http">
<img ng-switch-default ng-if="result.thumbUrl !== undefined" class="inline_result_photo_image" width="{{result.thumbW}}" height="{{result.thumbH}}" ng-src="{{result.thumbUrl}}" />
</div>
</a>
<a ng-switch-default class="inline_result_article clearfix" data-inlineid="{{result.qID}}">
<div class="inline_article_thumb_wrap pull-left" ng-switch="result.thumbUrl !== undefined">
<img ng-switch-when="true" class="inline_article_thumb" ng-src="{{result.thumbUrl}}"/>
<div ng-switch-default class="inline_article_thumb_initials" ng-bind="result.initials"></div>
</div>
<div class="inline_article_content_wrap">
<div class="inline_article_title" ng-if="::result.title.length > 0" ng-bind-html="::result.rTitle"></div>
<div class="inline_article_description" ng-if="::result.description.length > 0" ng-bind-html="::result.rDescription"></div>
<div class="inline_article_url" ng-if="::result.url.length > 0" ng-bind="::result.url"></div>
</div>
</a>
</li>
</ul>

4
app/partials/desktop/message.html

@ -39,12 +39,12 @@ @@ -39,12 +39,12 @@
<i class="icon-message-views"></i><span class="im_message_views_cnt" my-message-views="historyMessage.mid"></span>
</div>
</div>
<span class="im_message_date" ng-bind="::historyMessage.date | time"></span>
<span class="im_message_date clickable" ng-bind="::historyMessage.date | time"></span>
</div>
<div class="im_message_body" ng-class="::{im_message_body_media: historyMessage._ == 'message' &amp;&amp; historyMessage.media ? true : false}">
<a class="im_message_author" my-peer-link="historyMessage.fromID" short="historyMessage.toID > 0" color="historyMessage.toID < 0" no-watch="true"></a><span ng-if="::historyMessage.viaBotID && !historyMessage.fwdFromID" class="im_message_author_via" my-i18n="message_via_bot"><my-i18n-param name="bot"><a class="im_message_fwd_author" my-peer-link="historyMessage.viaBotID" username="true" no-watch="true"></a></my-i18n-param></span>
<a class="im_message_author" my-peer-link="historyMessage.fromID" short="historyMessage.toID > 0" color="historyMessage.toID < 0" no-watch="true"></a><span ng-if="::historyMessage.viaBotID && !historyMessage.fwdFromID" class="im_message_author_via" my-i18n="message_via_bot"><my-i18n-param name="bot"><a class="im_message_fwd_author" my-peer-link="historyMessage.viaBotID" username="true" no-watch="true" ng-click="selectInlineBot(historyMessage.viaBotID, $event)"></a></my-i18n-param></span>
<a class="im_message_reply_wrap" my-reply-message="historyMessage.reply_to_msg" ng-if="::historyMessage.reply_to_mid"></a>

Loading…
Cancel
Save