Browse Source

Merge branch 'layer-51'

master
Igor Zhukov 8 years ago
parent
commit
18ce3a790b
  1. 2
      app/index.html
  2. 5
      app/js/app.js
  3. 200
      app/js/controllers.js
  4. 809
      app/js/directives.js
  5. 4
      app/js/lib/config.js
  6. 13
      app/js/lib/mtproto_wrapper.js
  7. 107
      app/js/lib/ng_utils.js
  8. 164
      app/js/lib/schema.tl.txt
  9. 2
      app/js/lib/tl_utils.js
  10. 11
      app/js/lib/utils.js
  11. 8
      app/js/locales/en-us.json
  12. 20
      app/js/locales/it-it.json
  13. 42
      app/js/locales/nl-nl.json
  14. 4
      app/js/locales/pt-br.json
  15. 14
      app/js/locales/ru-ru.json
  16. 14
      app/js/message_composer.js
  17. 201
      app/js/messages_manager.js
  18. 913
      app/js/services.js
  19. 100
      app/less/app.less
  20. 3
      app/partials/desktop/confirm_modal.html
  21. 24
      app/partials/desktop/dialog.html
  22. 21
      app/partials/desktop/forwarded_messages.html
  23. 4
      app/partials/desktop/im.html
  24. 24
      app/partials/desktop/inline_results.html
  25. 42
      app/partials/desktop/login.html
  26. 25
      app/partials/desktop/message.html
  27. 12
      app/partials/desktop/message_attach_contact.html
  28. 41
      app/partials/desktop/message_attach_document.html
  29. 10
      app/partials/desktop/message_attach_pending.html
  30. 10
      app/partials/desktop/message_attach_venue.html
  31. 35
      app/partials/desktop/message_attach_video.html
  32. 38
      app/partials/desktop/message_attach_webpage.html
  33. 14
      app/partials/desktop/message_media.html
  34. 5
      app/partials/desktop/message_service.html
  35. 1
      app/partials/desktop/pinned_message.html
  36. 5
      app/partials/desktop/reply_markup.html
  37. 23
      app/partials/desktop/reply_message.html
  38. 22
      app/partials/desktop/short_message.html
  39. 16
      app/partials/desktop/user_modal.html
  40. 22
      app/partials/mobile/dialog.html
  41. 4
      app/partials/mobile/im.html
  42. 40
      app/partials/mobile/login.html
  43. 44
      app/partials/mobile/message.html
  44. 12
      app/partials/mobile/message_attach_contact.html
  45. 18
      app/partials/mobile/message_attach_document.html
  46. 10
      app/partials/mobile/message_attach_pending.html
  47. 9
      app/partials/mobile/message_attach_venue.html
  48. 12
      app/partials/mobile/message_attach_video.html
  49. 4
      app/partials/mobile/message_service.html
  50. 4
      app/partials/mobile/user_modal.html
  51. 254
      app/vendor/angularjs-toaster/toaster.css
  52. 507
      app/vendor/angularjs-toaster/toaster.js
  53. 12
      app/vendor/angularjs-toaster/toaster.min.css
  54. 13
      app/vendor/angularjs-toaster/toaster.min.js
  55. 2
      app/vendor/libwebpjs/libwebp-0.2.0.js
  56. 2
      package.json

2
app/index.html

@ -11,6 +11,7 @@ @@ -11,6 +11,7 @@
<!-- build:css css/app.css -->
<link rel="stylesheet" href="vendor/angular/angular-csp.css"/>
<link rel="stylesheet" href="vendor/bootstrap/css/bootstrap.css"/>
<link rel="stylesheet" href="vendor/angularjs-toaster/toaster.css"/>
<link rel="stylesheet" href="css/app.css"/>
<!-- endbuild -->
@ -65,6 +66,7 @@ @@ -65,6 +66,7 @@
<script type="text/javascript" src="vendor/closure/long.js"></script>
<script type="text/javascript" src="vendor/leemon_bigint/bigint.js"></script>
<script type="text/javascript" src="vendor/libwebpjs/libwebp-0.2.0.js"></script>
<script type="text/javascript" src="vendor/angularjs-toaster/toaster.js"></script>
<script type="text/javascript" src="js/lib/utils.js"></script>

5
app/js/app.js

@ -20,6 +20,7 @@ angular.module('myApp', [ @@ -20,6 +20,7 @@ angular.module('myApp', [
'ngTouch',
'ui.bootstrap',
'mediaPlayer',
'toaster',
'izhukov.utils',
'izhukov.mtproto',
'izhukov.mtproto.wrapper',
@ -36,6 +37,10 @@ config(['$locationProvider', '$routeProvider', '$compileProvider', 'StorageProvi @@ -36,6 +37,10 @@ config(['$locationProvider', '$routeProvider', '$compileProvider', 'StorageProvi
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|file|blob|filesystem|chrome-extension|app):|data:image\//);
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|file|tg|mailto|blob|filesystem|chrome-extension|app):|data:/);
/*PRODUCTION_ONLY_BEGIN
$compileProvider.debugInfoEnabled(false);
PRODUCTION_ONLY_END*/
if (Config.Modes.test) {
StorageProvider.setPrefix('t_');
}

200
app/js/controllers.js

@ -55,7 +55,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -55,7 +55,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.credentials = {phone_country: '', phone_country_name: '', phone_number: '', phone_full: ''};
$scope.progress = {};
$scope.callPending = {};
$scope.nextPending = {};
$scope.about = {};
$scope.chooseCountry = function () {
@ -158,38 +158,20 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -158,38 +158,20 @@ angular.module('myApp.controllers', ['myApp.i18n'])
initPhoneCountry();
var callTimeout;
var nextTimeout;
var updatePasswordTimeout = false;
function saveAuth (result) {
MtpApiManager.setUserAuth(options.dcID, {
id: result.user.id
});
$timeout.cancel(callTimeout);
$timeout.cancel(nextTimeout);
$location.url('/im');
};
function callCheck () {
$timeout.cancel(callTimeout);
if ($scope.credentials.viaApp) {
return;
}
if (!(--$scope.callPending.remaining)) {
$scope.callPending.success = false;
MtpApiManager.invokeApi('auth.sendCall', {
phone_number: $scope.credentials.phone_full,
phone_code_hash: $scope.credentials.phone_code_hash
}, options).then(function () {
$scope.callPending.success = true;
});
} else {
callTimeout = $timeout(callCheck, 1000);
}
}
$scope.sendCode = function () {
$timeout.cancel(callTimeout);
$timeout.cancel(nextTimeout);
ErrorService.confirm({
type: 'LOGIN_PHONE_CORRECT',
@ -204,26 +186,18 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -204,26 +186,18 @@ angular.module('myApp.controllers', ['myApp.i18n'])
var authKeyStarted = tsNow();
MtpApiManager.invokeApi('auth.sendCode', {
flags: 0,
phone_number: $scope.credentials.phone_full,
sms_type: 5,
api_id: Config.App.id,
api_hash: Config.App.hash,
lang_code: navigator.language || 'en'
}, options).then(function (sentCode) {
$scope.progress.enabled = false;
$scope.credentials.phone_code_hash = sentCode.phone_code_hash;
$scope.credentials.phone_occupied = sentCode.phone_registered;
$scope.credentials.viaApp = sentCode._ == 'auth.sentAppCode';
$scope.callPending.remaining = sentCode.send_call_timeout || 60;
$scope.error = {};
$scope.about = {};
callCheck();
onContentLoaded(function () {
$scope.$broadcast('ui_height');
});
$scope.credentials.phone_code_hash = sentCode.phone_code_hash;
applySentCode(sentCode);
}, function (error) {
$scope.progress.enabled = false;
@ -246,28 +220,71 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -246,28 +220,71 @@ angular.module('myApp.controllers', ['myApp.i18n'])
});
}
$scope.sendSms = function () {
if (!$scope.credentials.viaApp) {
function applySentCode(sentCode) {
$scope.credentials.type = sentCode.type;
$scope.nextPending.type = sentCode.next_type || false;
$scope.nextPending.remaining = sentCode.timeout || false;
nextTimeoutCheck();
onContentLoaded(function () {
$scope.$broadcast('ui_height');
});
}
$scope.sendNext = function () {
if (!$scope.nextPending.type ||
$scope.nextPending.remaining > 0) {
return;
}
delete $scope.credentials.viaApp;
MtpApiManager.invokeApi('auth.sendSms', {
MtpApiManager.invokeApi('auth.resendCode', {
phone_number: $scope.credentials.phone_full,
phone_code_hash: $scope.credentials.phone_code_hash
}, options).then(callCheck);
}, options).then(applySentCode);
}
function nextTimeoutCheck () {
$timeout.cancel(nextTimeout);
if (!$scope.nextPending.type ||
$scope.nextPending.remaining === false) {
return;
}
if (!(--$scope.nextPending.remaining)) {
$scope.nextPending.success = false;
$scope.sendNext();
} else {
nextTimeout = $timeout(nextTimeoutCheck, 1000);
}
}
$scope.editPhone = function () {
$timeout.cancel(callTimeout);
$timeout.cancel(nextTimeout);
if ($scope.credentials.phone_full &&
$scope.credentials.phone_code_hash) {
MtpApiManager.invokeApi('auth.cancelCode', {
phone_number: $scope.credentials.phone_full,
phone_code_hash: $scope.credentials.phone_code_hash
}, options);
}
delete $scope.credentials.phone_code_hash;
delete $scope.credentials.phone_unoccupied;
delete $scope.credentials.phone_code_valid;
delete $scope.credentials.viaApp;
delete $scope.callPending.remaining;
delete $scope.callPending.success;
delete $scope.nextPending.remaining;
delete $scope.nextPending.success;
}
$scope.$watch('credentials.phone_code', function (newVal) {
if (newVal &&
newVal.match(/^\d+$/) &&
$scope.credentials.type &&
$scope.credentials.type.length &&
newVal.length == $scope.credentials.type.length) {
$scope.logIn();
}
});
$scope.logIn = function (forceSignUp) {
var method = 'auth.signIn', params = {
phone_number: $scope.credentials.phone_full,
@ -1030,7 +1047,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1030,7 +1047,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
})
.controller('AppImHistoryController', function ($scope, $location, $timeout, $modal, $rootScope, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager, PeersSelectService, IdleManager, StatusManager, NotificationsManager, ErrorService) {
.controller('AppImHistoryController', function ($scope, $location, $timeout, $modal, $rootScope, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager, PeersSelectService, IdleManager, StatusManager, NotificationsManager, ErrorService, GeoLocationManager) {
$scope.$watchCollection('curDialog', applyDialogSelect);
@ -1089,7 +1106,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1089,7 +1106,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
photos: 'inputMessagesFilterPhotos',
video: 'inputMessagesFilterVideo',
documents: 'inputMessagesFilterDocument',
audio: 'inputMessagesFilterAudio'
audio: 'inputMessagesFilterVoice'
},
unfocusMessagePromise,
jump = 0,
@ -1787,9 +1804,45 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -1787,9 +1804,45 @@ angular.module('myApp.controllers', ['myApp.i18n'])
if (!replyKeyboard) {
return;
}
AppMessagesManager.sendText(peerID, button.text, {
var sendOptions = {
replyToMsgID: peerID < 0 && replyKeyboard.mid
});
};
switch (button._) {
case 'keyboardButtonRequestPhone':
ErrorService.confirm({type: 'BOT_ACCESS_PHONE'}).then(function () {
var user = AppUsersManager.getSelf();
AppMessagesManager.sendOther(peerID, {
_: 'inputMediaContact',
phone_number: user.phone,
first_name: user.first_name,
last_name: user.last_name
}, sendOptions);
});
break;
case 'keyboardButtonRequestGeoLocation':
ErrorService.confirm({type: 'BOT_ACCESS_GEO'}).then(function () {
return GeoLocationManager.getPosition().then(function (coords) {
AppMessagesManager.sendOther(peerID, {
_: 'inputMediaGeoPoint',
geo_point: {
_: 'inputGeoPoint',
'lat': coords['lat'],
'long': coords['long']
}
}, sendOptions);
}, function (error) {
ErrorService.alert(
_('error_modal_bad_request_title_raw'),
_('error_modal_gelocation_na_raw')
);
});
});
break;
default:
AppMessagesManager.sendText(peerID, button.text, sendOptions);
}
});
$scope.$on('history_reload', function (e, updPeerID) {
@ -2134,13 +2187,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2134,13 +2187,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
var text = $scope.draftMessage.text;
if (angular.isString(text) && text.length > 0) {
text = text.replace(/:([a-z0-9\-\+\*_]+?):/gi, function (all, shortcut) {
var emojiCode = EmojiHelper.shortcuts[shortcut];
if (emojiCode !== undefined) {
return EmojiHelper.emojis[emojiCode][0];
}
return all;
});
text = RichTextProcessor.parseEmojis(text);
var timeout = 0;
var options = {
@ -2337,6 +2384,22 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2337,6 +2384,22 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.$broadcast('ui_peer_draft');
});
}
else if (attachment._ == 'inline_query') {
var mention = attachment.mention;
var query = attachment.query;
forceDraft = $scope.curDialog.peer;
$timeout(function () {
$scope.draftMessage.text = mention + ' ' + query;
$scope.$broadcast('ui_peer_draft', {
customSelection: [
mention + " " + query,
'',
''
]
});
}, 1000);
}
}
function replySelect(messageID) {
@ -2452,6 +2515,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2452,6 +2515,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
var inlineUsernameRegex = /^@([a-zA-Z\d_]{1,32})( | )([\s\S]*)$/;
var getInlineResultsTO = false;
var lastInlineBot = false;
var jump = 0;
function checkInlinePattern (message) {
@ -2471,11 +2535,18 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2471,11 +2535,18 @@ angular.module('myApp.controllers', ['myApp.i18n'])
return;
}
var username = matches[1];
var inlineBotPromise;
$scope.draftMessage.inlineProgress = true;
AppPeersManager.resolveInlineMention(username).then(function (inlineBot) {
if (lastInlineBot && lastInlineBot.username == username) {
inlineBotPromise = $q.when(lastInlineBot);
} else {
inlineBotPromise = AppInlineBotsManager.resolveInlineMention(username);
}
inlineBotPromise.then(function (inlineBot) {
if (curJump != jump) {
return;
}
lastInlineBot = inlineBot;
$scope.$broadcast('inline_placeholder', {
prefix: '@' + username + matches[2],
placeholder: inlineBot.placeholder
@ -2484,7 +2555,8 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2484,7 +2555,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$timeout.cancel(getInlineResultsTO);
}
getInlineResultsTO = $timeout(function () {
AppInlineBotsManager.getInlineResults(inlineBot.id, matches[3], '').then(function (botResults) {
var query = RichTextProcessor.parseEmojis(matches[3]);
AppInlineBotsManager.getInlineResults($scope.curDialog.peerID, inlineBot.id, query, inlineBot.geo, '').then(function (botResults) {
getInlineResultsTO = false;
if (curJump != jump) {
return;
@ -2497,7 +2569,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2497,7 +2569,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
delete $scope.draftMessage.inlineProgress;
});
}, 500);
}, function () {
}, function (error) {
$scope.$broadcast('inline_results', false);
delete $scope.draftMessage.inlineProgress;
});
@ -2582,6 +2654,13 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2582,6 +2654,13 @@ angular.module('myApp.controllers', ['myApp.i18n'])
if (!qID) {
return;
}
if (qID.substr(0, 11) == '_switch_pm_') {
var botID = lastInlineBot.id;
var startParam = qID.substr(11);
return AppInlineBotsManager.switchToPM($scope.curDialog.peerID, botID, startParam);
}
var options = {
replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.mid
};
@ -3080,9 +3159,9 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -3080,9 +3159,9 @@ angular.module('myApp.controllers', ['myApp.i18n'])
})
.controller('VideoModalController', function ($scope, $rootScope, $modalInstance, PeersSelectService, AppMessagesManager, AppVideoManager, AppPeersManager, ErrorService) {
.controller('VideoModalController', function ($scope, $rootScope, $modalInstance, PeersSelectService, AppMessagesManager, AppDocsManager, AppPeersManager, ErrorService) {
$scope.video = AppVideoManager.wrapForFull($scope.videoID);
$scope.video = AppDocsManager.wrapVideoForFull($scope.docID);
$scope.progress = {enabled: false};
$scope.player = {};
@ -3109,7 +3188,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -3109,7 +3188,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
};
$scope.download = function () {
AppVideoManager.saveVideoFile($scope.videoID);
AppDocsManager.saveDocFile($scope.docID);
};
$scope.$on('history_delete', function (e, historyUpdate) {
@ -3192,8 +3271,9 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -3192,8 +3271,9 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.settings = {notifications: true};
AppProfileManager.getProfile($scope.userID, $scope.override).then(function (userFull) {
$scope.blocked = userFull.blocked;
$scope.blocked = userFull.pFlags.blocked;
$scope.bot_info = userFull.bot_info;
$scope.rAbout = userFull.rAbout;
NotificationsManager.getPeerMuted($scope.userID).then(function (muted) {
$scope.settings.notifications = !muted;

809
app/js/directives.js

@ -175,292 +175,25 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -175,292 +175,25 @@ angular.module('myApp.directives', ['myApp.filters'])
}
}
})
.directive('myExternalEmbed', function () {
var twitterAttached = false;
var facebookAttached = false;
var gplusAttached = false;
var twitterPendingWidgets = [];
var facebookPendingWidgets = [];
var embedTag = Config.Modes.chrome_packed ? 'webview' : 'iframe';
function link ($scope, element, attrs) {
var embedData = $scope.$eval(attrs.myExternalEmbed);
if (!embedData) {
return;
}
var html = '';
var callback = false;
var needTwitter = false;
switch (embedData[0]) {
case 'youtube':
var videoID = embedData[1];
html = '<div class="im_message_media_embed im_message_video_embed"><' + embedTag + ' type="text/html" frameborder="0" ' +
'src="https://www.youtube.com/embed/' + videoID +
'?autoplay=0&amp;controls=2" webkitallowfullscreen mozallowfullscreen allowfullscreen></' + embedTag + '></div>';
break;
case 'vimeo':
var videoID = embedData[1];
html = '<div class="im_message_media_embed im_message_video_embed"><' + embedTag + ' type="text/html" frameborder="0" ' +
'src="https://player.vimeo.com/video/' + videoID +
'?title=0&amp;byline=0&amp;portrait=0" webkitallowfullscreen mozallowfullscreen allowfullscreen></' + embedTag + '></div>';
break;
case 'instagram':
var instaID = embedData[1];
html = '<div class="im_message_media_embed im_message_insta_embed"><' + embedTag + ' type="text/html" frameborder="0" ' +
'src="https://instagram.com/p/' + instaID +
'/embed/"></' + embedTag + '></div>';
break;
case 'vine':
var vineID = embedData[1];
html = '<div class="im_message_media_embed im_message_vine_embed"><' + embedTag + ' type="text/html" frameborder="0" ' +
'src="https://vine.co/v/' + vineID + '/embed/simple"></' + embedTag + '></div>';
break;
case 'soundcloud':
var soundcloudUrl = embedData[1];
html = '<div class="im_message_media_embed im_message_soundcloud_embed"><' + embedTag + ' type="text/html" frameborder="0" ' +
'src="https://w.soundcloud.com/player/?url=' + encodeEntities(encodeURIComponent(soundcloudUrl)) +
'&amp;auto_play=false&amp;hide_related=true&amp;show_comments=false&amp;show_user=true&amp;show_reposts=false&amp;visual=true"></' + embedTag + '></div>';
break;
case 'spotify':
var spotifyUrl = embedData[1];
html = '<div class="im_message_media_embed im_message_spotify_embed"><' + embedTag + ' type="text/html" frameborder="0" allowtransparency="true" ' +
'src="https://embed.spotify.com/?uri=spotify:' + encodeEntities(encodeURIComponent(spotifyUrl)) +
'"></' + embedTag + '></div>';
break;
case 'twitter':
html = '<div class="im_message_twitter_embed"><blockquote class="twitter-tweet" lang="en"><a href="' + embedData[1] + '"></a></blockquote></div>';
callback = function () {
if (!twitterAttached) {
twitterAttached = true;
$('<script>')
.appendTo('body')
.on('load', function () {
twttr.events.bind('loaded', function (event) {
for (var i = 0; i < twitterPendingWidgets.length; i++) {
twitterPendingWidgets[i].$emit('ui_height');
}
twitterPendingWidgets = [];
});
})
.attr('src', 'https://platform.twitter.com/widgets.js');
}
else if (window.twttr) {
twttr.widgets.load(element[0]);
}
twitterPendingWidgets.push($scope);
};
break;
case 'facebook':
html = '<div class="im_message_facebook_embed"><div class="fb-post" data-href="' + embedData[1] + '" data-width="300"></div></div>';
callback = function () {
if (!facebookAttached) {
facebookAttached = true;
$('<script>')
.appendTo('body')
.on('load', function () {
FB.Event.subscribe('xfbml.render', function (event) {
for (var i = 0; i < facebookPendingWidgets.length; i++) {
facebookPendingWidgets[i].$emit('ui_height');
}
facebookPendingWidgets = [];
});
})
.attr('src', 'https://connect.facebook.net/en_US/sdk.js#xfbml=1&appId=254098051407226&version=v2.0');
}
else if (window.FB) {
FB.XFBML.parse(element[0]);
}
facebookPendingWidgets.push($scope);
};
break;
case 'gplus':
html = '<div class="im_message_gplus_embed"><div class="g-post" data-href="' + embedData[1] + '"></div></div>';
callback = function () {
if (!gplusAttached) {
gplusAttached = true;
window.___gcfg = {"parsetags": "explicit"};
$('<script>')
.appendTo('body')
.on('load', function () {
gapi.post.go();
})
.attr('src', 'https://apis.google.com/js/plusone.js');
}
else if (window.gapi) {
gapi.post.go(element[0]);
}
element.one('load', function () {
$scope.$emit('ui_height');
});
};
break;
}
if (html) {
element[0].innerHTML = html;
if (callback) {
callback();
}
}
}
return {
link: link
};
})
.directive('myServiceMessage', function() {
return {
templateUrl: templateUrl('message_service')
};
})
.directive('myServiceShortMessage', function() {
return {
scope: {
message: '=myServiceShortMessage'
},
templateUrl: templateUrl('dialog_service')
};
})
.directive('myReplyMessage', function(AppPhotosManager, AppMessagesManager, AppPeersManager, $rootScope) {
return {
templateUrl: templateUrl('reply_message'),
scope: {
'replyMessage': '=myReplyMessage'
},
link: link
};
function link ($scope, element, attrs) {
if (attrs.watch) {
$scope.$watch('replyMessage', function () {
checkMessage($scope, element);
});
} else {
checkMessage($scope, element);
}
}
function checkMessage ($scope, element) {
var message = $scope.replyMessage;
if (!message.loading) {
updateMessage($scope, element);
} else {
var mid = message.mid;
var stopWaiting = $scope.$on('messages_downloaded', function (e, mids) {
if (mids.indexOf(mid) != -1) {
$scope.replyMessage = AppMessagesManager.wrapForDialog(mid);
updateMessage($scope, element);
stopWaiting();
}
});
}
}
function updateMessage($scope, element) {
var message = $scope.replyMessage;
if (!message || message.deleted || !message.to_id) {
$(element).remove();
return;
}
$scope.thumb = AppMessagesManager.getMessageThumb(message, 42, 42);
if (element[0].tagName == 'A') {
element.on('click', function () {
var peerID = AppMessagesManager.getMessagePeer(message);
var peerString = AppPeersManager.getPeerString(peerID);
$rootScope.$broadcast('history_focus', {peerString: peerString, messageID: message.mid});
})
}
onContentLoaded(function () {
$scope.$emit('ui_height');
})
}
.directive('myMessageBody', function($compile, AppPeersManager, AppChatsManager, AppUsersManager, AppMessagesManager, AppInlineBotsManager, RichTextProcessor) {
})
.directive('myForwardedMessages', function(AppPhotosManager, AppMessagesManager, AppPeersManager, $rootScope) {
var messageMediaCompiled = $compile('<div class="im_message_media" my-message-media="media" message-id="messageId"></div>');
var messageKeyboardCompiled = $compile('<div class="im_message_keyboard" my-inline-reply-markup="markup"></div>');
return {
templateUrl: templateUrl('forwarded_messages'),
scope: {
'forwardMessages': '=myForwardedMessages'
},
link: link
};
function link ($scope, element, attrs) {
if (attrs.watch) {
$scope.$watch('forwardMessages', function () {
updateMessages($scope, element);
});
} else {
updateMessages($scope, element);
}
}
function updateMessages ($scope, element) {
var mids = $scope.forwardMessages;
var length = mids.length;
var fromID = false;
var single = length == 1;
$scope.thumb = false;
$scope.singleMessage = false;
angular.forEach(mids, function (mid) {
var message = AppMessagesManager.getMessage(mid);
if (fromID === false) {
fromID = message.fromID;
} else {
if (fromID !== message.fromID) {
fromID = AppMessagesManager.getMessagePeer(message);
}
}
if (single) {
$scope.thumb = AppMessagesManager.getMessageThumb(message, 42, 42);
$scope.singleMessage = AppMessagesManager.wrapForDialog(mid);
}
});
$scope.fromID = fromID;
$scope.count = length;
onContentLoaded(function () {
$scope.$emit('ui_height');
})
}
})
.directive('myMessageText', function(AppPeersManager, AppMessagesManager, AppChatsManager, AppUsersManager, RichTextProcessor) {
return {
link: link,
scope: {
message: '=myMessageText'
message: '=myMessageBody'
}
};
function updateHtml (message, element) {
var entities = message.totalEntities;
function updateMessageText ($scope, element, message) {
if (typeof message.message !== 'string' ||
!message.message.length) {
$('.im_message_text', element).hide();
return;
}
var fromUser = message.from_id && AppUsersManager.getUser(message.from_id);
var fromBot = fromUser && fromUser.pFlags.bot && fromUser.username || false;
var toPeerID = AppPeersManager.getPeerID(message.to_id);
@ -471,36 +204,87 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -471,36 +204,87 @@ angular.module('myApp.directives', ['myApp.filters'])
var options = {
noCommands: !withBot,
fromBot: fromBot,
entities: entities
entities: message.totalEntities
};
if (message.flags & 16) {
if (message.pFlags.mentioned) {
var user = AppUsersManager.getSelf();
if (user) {
options.highlightUsername = user.username;
}
}
var html = RichTextProcessor.wrapRichText(message.message, options);
// console.log('dd', entities, html);
element.html(html.valueOf());
$('.im_message_text', element).html(html.valueOf());
}
function updateMessageMedia($scope, element, message) {
if (!message.media) {
$('.im_message_media', element).hide();
return;
}
var scope = $scope.$new(true);
scope.media = message.media;
scope.messageId = message.mid;
messageMediaCompiled(scope, function (clonedElement) {
$('.im_message_media', element).replaceWith(clonedElement);
});
}
function updateMessageKeyboard($scope, element, message) {
if (!message.reply_markup ||
message.reply_markup._ != 'replyInlineMarkup') {
$('.im_message_keyboard', element).hide();
return;
}
var scope = $scope.$new(true);
scope.markup = AppMessagesManager.wrapReplyMarkup(message.reply_markup);
scope.messageId = message.mid;
messageKeyboardCompiled(scope, function (clonedElement) {
$('.im_message_keyboard', element).replaceWith(clonedElement);
});
scope.$on('reply_inline_button_press', function (e, button) {
switch (button._) {
case 'keyboardButtonSwitchInline':
AppInlineBotsManager.switchInlineButtonClick(message.mid, button);
break;
case 'keyboardButtonCallback':
AppInlineBotsManager.callbackButtonClick(message.mid, button);
break;
}
});
}
function link ($scope, element, attrs) {
var message = $scope.message;
message.dir = true;
var msgID = message.mid;
// var msgID = $scope.$eval(attrs.myMessageText);
// var message = AppMessagesManager.getMessage(msgID);
updateHtml(message, element);
updateMessageText($scope, element, message);
updateMessageMedia($scope, element, message);
updateMessageKeyboard($scope, element, message);
if (message.pending) {
var unlink = $scope.$on('messages_pending', function () {
if (message.mid != msgID) {
updateHtml(message, element);
updateMessageText($scope, element, message);
unlink();
}
})
});
}
$scope.$on('message_edit', function (e, data) {
if (data.mid != message.mid) {
return;
}
// console.log(dT(), 'Directive' edit', message);
updateMessageText($scope, element, message);
updateMessageMedia($scope, element, message);
updateMessageKeyboard($scope, element, message);
$scope.$emit('ui_height');
});
}
})
@ -547,7 +331,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -547,7 +331,7 @@ angular.module('myApp.directives', ['myApp.filters'])
classPrefix: 'reply_markup',
maxHeight: 170
});
$scope.buttonSend = function (button) {
$scope.buttonClick = function (button) {
$scope.$emit('reply_button_press', button);
}
@ -566,34 +350,25 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -566,34 +350,25 @@ angular.module('myApp.directives', ['myApp.filters'])
})
.directive('myMessagePhoto', function(AppPhotosManager) {
.directive('myMessageMedia', function() {
return {
scope: {
'media': '=myMessagePhoto',
'media': '=myMessageMedia',
'messageId': '=messageId'
},
templateUrl: templateUrl('message_attach_photo'),
link: function ($scope, element, attrs) {
$scope.openPhoto = AppPhotosManager.openPhoto;
$scope.preloadPhoto = AppPhotosManager.preloadPhoto;
}
templateUrl: templateUrl('message_media')
};
})
.directive('myMessageVideo', function(AppVideoManager) {
.directive('myMessagePhoto', function(AppPhotosManager) {
return {
scope: {
'media': '=myMessageVideo',
'media': '=myMessagePhoto',
'messageId': '=messageId'
},
templateUrl: templateUrl('message_attach_video'),
templateUrl: templateUrl('message_attach_photo'),
link: function ($scope, element, attrs) {
AppVideoManager.updateVideoDownloaded($scope.media.video.id);
$scope.videoSave = function () {
AppVideoManager.saveVideoFile($scope.media.video.id);
};
$scope.videoOpen = function () {
AppVideoManager.openVideo($scope.media.video.id, $scope.messageId);
};
$scope.openPhoto = AppPhotosManager.openPhoto;
$scope.preloadPhoto = AppPhotosManager.preloadPhoto;
}
};
})
@ -615,6 +390,9 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -615,6 +390,9 @@ angular.module('myApp.directives', ['myApp.filters'])
}
AppDocsManager.openDoc($scope.media.document.id, $scope.messageId);
};
$scope.videoOpen = function () {
AppDocsManager.openVideo($scope.media.document.id, $scope.messageId);
};
}
};
})
@ -629,44 +407,246 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -629,44 +407,246 @@ angular.module('myApp.directives', ['myApp.filters'])
.directive('myMessageVenue', function() {
return {
scope: {
'venue': '=myMessageVenue'
'media': '=myMessageVenue'
},
templateUrl: templateUrl('message_attach_venue')
};
})
.directive('myMessageContact', function() {
return {
scope: {
'media': '=myMessageContact'
},
templateUrl: templateUrl('message_attach_contact')
};
})
.directive('myMessageWebpage', function(AppWebPagesManager, AppPhotosManager) {
return {
scope: {
'webpage': '=myMessageWebpage',
'messageId': '=messageId'
'media': '=myMessageWebpage',
'messageId': '=messageId'
},
templateUrl: templateUrl('message_attach_webpage'),
link: function ($scope) {
$scope.openPhoto = AppPhotosManager.openPhoto;
$scope.openEmbed = function ($event) {
if ($scope.media.webpage &&
$scope.media.webpage.embed_url) {
AppWebPagesManager.openEmbed($scope.media.webpage.id, $scope.messageId);
return cancelEvent($event);
}
};
$scope.$on('webpage_updated', function (e, eventData) {
if ($scope.media.webpage &&
$scope.media.webpage.id == eventData.id) {
$scope.$emit('ui_height');
}
});
}
};
})
.directive('myMessagePending', function() {
return {
scope: {
'media': '=myMessagePending'
},
templateUrl: templateUrl('message_attach_pending')
};
})
.directive('myInlineReplyMarkup', function() {
return {
templateUrl: templateUrl('reply_markup'),
scope: {
'replyMarkup': '=myInlineReplyMarkup'
},
link: link
};
function link ($scope, element, attrs) {
$scope.buttonClick = function (button) {
$scope.$emit('reply_inline_button_press', button);
}
}
})
.directive('myServiceMessage', function() {
return {
templateUrl: templateUrl('message_service')
};
})
.directive('myShortMessage', function() {
return {
scope: {
message: '=myShortMessage'
},
templateUrl: templateUrl('short_message')
};
})
.directive('myReplyMessage', function(AppMessagesManager, AppPeersManager, $rootScope) {
return {
templateUrl: templateUrl('reply_message'),
scope: {
'replyMessage': '=myReplyMessage'
},
link: link
};
function link ($scope, element, attrs) {
if (attrs.watch) {
$scope.$watch('replyMessage', function () {
checkMessage($scope, element);
});
} else {
checkMessage($scope, element);
}
}
function checkMessage ($scope, element) {
var message = $scope.replyMessage;
if (!message.loading) {
updateMessage($scope, element);
} else {
var mid = message.mid;
var stopWaiting = $scope.$on('messages_downloaded', function (e, mids) {
if (mids.indexOf(mid) != -1) {
$scope.replyMessage = AppMessagesManager.wrapForDialog(mid);
updateMessage($scope, element);
stopWaiting();
}
});
}
}
function updateMessage($scope, element) {
var message = $scope.replyMessage;
if (!message || message.deleted || !message.to_id) {
$(element).remove();
return;
}
$scope.thumb = AppMessagesManager.getMessageThumb(message, 42, 42);
if (element[0].tagName == 'A') {
element.on('click', function () {
var peerID = AppMessagesManager.getMessagePeer(message);
var peerString = AppPeersManager.getPeerString(peerID);
$rootScope.$broadcast('history_focus', {peerString: peerString, messageID: message.mid});
})
}
onContentLoaded(function () {
$scope.$emit('ui_height');
})
}
})
.directive('myPinnedMessage', function(AppMessagesManager, AppPeersManager, $rootScope) {
return {
templateUrl: templateUrl('pinned_message'),
scope: {
'pinnedMessage': '=myPinnedMessage'
},
templateUrl: templateUrl('message_attach_webpage'),
link: function ($scope) {
$scope.openPhoto = AppPhotosManager.openPhoto;
$scope.openEmbed = function ($event) {
if ($scope.webpage && $scope.webpage.embed_url) {
AppWebPagesManager.openEmbed($scope.webpage.id, $scope.messageId);
return cancelEvent($event);
}
};
link: link
};
$scope.$on('webpage_updated', function (e, eventData) {
if ($scope.webpage && $scope.webpage.id == eventData.id) {
$scope.$emit('ui_height');
function link ($scope, element, attrs) {
var message = $scope.pinnedMessage;
if (!message.loading) {
updateMessage($scope, element);
} else {
var mid = message.mid;
var stopWaiting = $scope.$on('messages_downloaded', function (e, mids) {
if (mids.indexOf(mid) != -1) {
$scope.pinnedMessage = AppMessagesManager.wrapForDialog(mid);
updateMessage($scope, element);
stopWaiting();
}
});
}
};
}
function updateMessage($scope, element) {
var message = $scope.pinnedMessage;
if (!message || message.deleted || !message.to_id) {
$(element).remove();
return;
}
if (element[0].tagName == 'A') {
element.on('click', function () {
var peerID = AppMessagesManager.getMessagePeer(message);
var peerString = AppPeersManager.getPeerString(peerID);
$rootScope.$broadcast('history_focus', {peerString: peerString, messageID: message.mid});
})
}
onContentLoaded(function () {
$scope.$emit('ui_height');
})
}
})
.directive('myMessagePending', function() {
.directive('myForwardedMessages', function(AppPhotosManager, AppMessagesManager, AppPeersManager, $rootScope) {
return {
templateUrl: templateUrl('message_attach_pending')
templateUrl: templateUrl('forwarded_messages'),
scope: {
'forwardMessages': '=myForwardedMessages'
},
link: link
};
function link ($scope, element, attrs) {
if (attrs.watch) {
$scope.$watch('forwardMessages', function () {
updateMessages($scope, element);
});
} else {
updateMessages($scope, element);
}
}
function updateMessages ($scope, element) {
var mids = $scope.forwardMessages;
var length = mids.length;
var fromID = false;
var single = length == 1;
$scope.thumb = false;
$scope.singleMessage = false;
angular.forEach(mids, function (mid) {
var message = AppMessagesManager.getMessage(mid);
if (fromID === false) {
fromID = message.fromID;
} else {
if (fromID !== message.fromID) {
fromID = AppMessagesManager.getMessagePeer(message);
}
}
if (single) {
$scope.thumb = AppMessagesManager.getMessageThumb(message, 42, 42);
$scope.singleMessage = AppMessagesManager.wrapForDialog(mid);
}
});
$scope.fromID = fromID;
$scope.count = length;
onContentLoaded(function () {
$scope.$emit('ui_height');
})
}
})
.directive('myDialogs', function ($modalStack, $transition, $window, $timeout) {
@ -1944,7 +1924,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -1944,7 +1924,7 @@ angular.module('myApp.directives', ['myApp.filters'])
})
.directive('myLoadVideo', function($sce, AppVideoManager, ErrorService, _) {
.directive('myLoadVideo', function($sce, AppDocsManager, ErrorService, _) {
return {
link: link,
@ -1957,7 +1937,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -1957,7 +1937,7 @@ angular.module('myApp.directives', ['myApp.filters'])
function link ($scope, element, attrs) {
var downloadPromise = AppVideoManager.downloadVideo($scope.video.id);
var downloadPromise = AppDocsManager.downloadDoc($scope.video.id);
downloadPromise.then(function () {
$scope.$emit('ui_height');
@ -2298,12 +2278,12 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -2298,12 +2278,12 @@ angular.module('myApp.directives', ['myApp.filters'])
function link ($scope, element, attrs) {
var width = element.attr('width') || 200;
var height = element.attr('height') || 200;
var apiKey = Config.ExtCredentials.gmaps.api_key;
var zoom = width > 200 ? 15 : 13;
element.attr('src', 'img/blank.gif');
var apiKey = Config.ExtCredentials.gmaps.api_key;
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;
var src = 'https://maps.googleapis.com/maps/api/staticmap?sensor=false&center=' + $scope.point['lat'] + ',' + $scope.point['long'] + '&zoom=' + zoom + '&size='+width+'x'+height+'&scale=2&key=' + apiKey;
ExternalResourcesManager.downloadByURL(src).then(function (url) {
element.attr('src', url.valueOf());
@ -2950,7 +2930,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -2950,7 +2930,7 @@ angular.module('myApp.directives', ['myApp.filters'])
}
})
.directive('myAudioPlayer', function ($timeout, $q, Storage, AppAudioManager, AppDocsManager, AppMessagesManager, ErrorService) {
.directive('myAudioPlayer', function ($timeout, $q, Storage, AppDocsManager, AppMessagesManager, ErrorService) {
var currentPlayer = false;
var audioVolume = 0.5;
@ -2995,21 +2975,13 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -2995,21 +2975,13 @@ angular.module('myApp.directives', ['myApp.filters'])
}
function link($scope, element, attrs) {
if ($scope.audio._ == 'audio') {
AppAudioManager.updateAudioDownloaded($scope.audio.id);
} else {
AppDocsManager.updateDocDownloaded($scope.audio.id);
}
AppDocsManager.updateDocDownloaded($scope.audio.id);
$scope.volume = audioVolume;
$scope.mediaPlayer = {};
$scope.download = function () {
if ($scope.audio._ == 'audio') {
AppAudioManager.saveAudioFile($scope.audio.id);
} else {
AppDocsManager.saveDocFile($scope.audio.id);
}
AppDocsManager.saveDocFile($scope.audio.id);
};
$scope.togglePlay = function () {
@ -3021,14 +2993,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -3021,14 +2993,7 @@ angular.module('myApp.directives', ['myApp.filters'])
return;
}
else {
var downloadPromise;
if ($scope.audio._ == 'audio') {
downloadPromise = AppAudioManager.downloadAudio($scope.audio.id);
} else {
downloadPromise = AppDocsManager.downloadDoc($scope.audio.id);
}
downloadPromise.then(function () {
AppDocsManager.downloadDoc($scope.audio.id).then(function () {
onContentLoaded(function () {
var errorListenerEl = $('audio', element)[0] || element[0];
if (errorListenerEl) {
@ -3450,7 +3415,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -3450,7 +3415,7 @@ angular.module('myApp.directives', ['myApp.filters'])
result.contentUrl = url;
});
}
if (result.type == 'gif' && result.document) {
if ((result.type == 'gif' || result.type == 'sticker') && result.document) {
AppDocsManager.downloadDoc(result.document.id);
}
if (result.type == 'photo' && result.photo) {
@ -3469,3 +3434,153 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -3469,3 +3434,153 @@ angular.module('myApp.directives', ['myApp.filters'])
}
})
.directive('myExternalEmbed', function () {
var twitterAttached = false;
var facebookAttached = false;
var gplusAttached = false;
var twitterPendingWidgets = [];
var facebookPendingWidgets = [];
var embedTag = Config.Modes.chrome_packed ? 'webview' : 'iframe';
function link ($scope, element, attrs) {
var embedData = $scope.$eval(attrs.myExternalEmbed);
if (!embedData) {
return;
}
var html = '';
var callback = false;
var needTwitter = false;
switch (embedData[0]) {
case 'youtube':
var videoID = embedData[1];
html = '<div class="im_message_media_embed im_message_video_embed"><' + embedTag + ' type="text/html" frameborder="0" ' +
'src="https://www.youtube.com/embed/' + videoID +
'?autoplay=0&amp;controls=2" webkitallowfullscreen mozallowfullscreen allowfullscreen></' + embedTag + '></div>';
break;
case 'vimeo':
var videoID = embedData[1];
html = '<div class="im_message_media_embed im_message_video_embed"><' + embedTag + ' type="text/html" frameborder="0" ' +
'src="https://player.vimeo.com/video/' + videoID +
'?title=0&amp;byline=0&amp;portrait=0" webkitallowfullscreen mozallowfullscreen allowfullscreen></' + embedTag + '></div>';
break;
case 'instagram':
var instaID = embedData[1];
html = '<div class="im_message_media_embed im_message_insta_embed"><' + embedTag + ' type="text/html" frameborder="0" ' +
'src="https://instagram.com/p/' + instaID +
'/embed/"></' + embedTag + '></div>';
break;
case 'vine':
var vineID = embedData[1];
html = '<div class="im_message_media_embed im_message_vine_embed"><' + embedTag + ' type="text/html" frameborder="0" ' +
'src="https://vine.co/v/' + vineID + '/embed/simple"></' + embedTag + '></div>';
break;
case 'soundcloud':
var soundcloudUrl = embedData[1];
html = '<div class="im_message_media_embed im_message_soundcloud_embed"><' + embedTag + ' type="text/html" frameborder="0" ' +
'src="https://w.soundcloud.com/player/?url=' + encodeEntities(encodeURIComponent(soundcloudUrl)) +
'&amp;auto_play=false&amp;hide_related=true&amp;show_comments=false&amp;show_user=true&amp;show_reposts=false&amp;visual=true"></' + embedTag + '></div>';
break;
case 'spotify':
var spotifyUrl = embedData[1];
html = '<div class="im_message_media_embed im_message_spotify_embed"><' + embedTag + ' type="text/html" frameborder="0" allowtransparency="true" ' +
'src="https://embed.spotify.com/?uri=spotify:' + encodeEntities(encodeURIComponent(spotifyUrl)) +
'"></' + embedTag + '></div>';
break;
case 'twitter':
html = '<div class="im_message_twitter_embed"><blockquote class="twitter-tweet" lang="en"><a href="' + embedData[1] + '"></a></blockquote></div>';
callback = function () {
if (!twitterAttached) {
twitterAttached = true;
$('<script>')
.appendTo('body')
.on('load', function () {
twttr.events.bind('loaded', function (event) {
for (var i = 0; i < twitterPendingWidgets.length; i++) {
twitterPendingWidgets[i].$emit('ui_height');
}
twitterPendingWidgets = [];
});
})
.attr('src', 'https://platform.twitter.com/widgets.js');
}
else if (window.twttr) {
twttr.widgets.load(element[0]);
}
twitterPendingWidgets.push($scope);
};
break;
case 'facebook':
html = '<div class="im_message_facebook_embed"><div class="fb-post" data-href="' + embedData[1] + '" data-width="300"></div></div>';
callback = function () {
if (!facebookAttached) {
facebookAttached = true;
$('<script>')
.appendTo('body')
.on('load', function () {
FB.Event.subscribe('xfbml.render', function (event) {
for (var i = 0; i < facebookPendingWidgets.length; i++) {
facebookPendingWidgets[i].$emit('ui_height');
}
facebookPendingWidgets = [];
});
})
.attr('src', 'https://connect.facebook.net/en_US/sdk.js#xfbml=1&appId=254098051407226&version=v2.0');
}
else if (window.FB) {
FB.XFBML.parse(element[0]);
}
facebookPendingWidgets.push($scope);
};
break;
case 'gplus':
html = '<div class="im_message_gplus_embed"><div class="g-post" data-href="' + embedData[1] + '"></div></div>';
callback = function () {
if (!gplusAttached) {
gplusAttached = true;
window.___gcfg = {"parsetags": "explicit"};
$('<script>')
.appendTo('body')
.on('load', function () {
gapi.post.go();
})
.attr('src', 'https://apis.google.com/js/plusone.js');
}
else if (window.gapi) {
gapi.post.go(element[0]);
}
element.one('load', function () {
$scope.$emit('ui_height');
});
};
break;
}
if (html) {
element[0].innerHTML = html;
if (callback) {
callback();
}
}
}
return {
link: link
};
})

4
app/js/lib/config.js

File diff suppressed because one or more lines are too long

13
app/js/lib/mtproto_wrapper.js

@ -133,7 +133,7 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) @@ -133,7 +133,7 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
if (!options.noErrorBox) {
error.input = method;
error.stack = error.originalError && error.originalError.stack || error.stack || (new Error()).stack;
error.stack = stack || (error.originalError && error.originalError.stack) || error.stack || (new Error()).stack;
setTimeout(function () {
if (!error.handled) {
if (error.code == 401) {
@ -339,22 +339,13 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) @@ -339,22 +339,13 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
function getFileName(location) {
switch (location._) {
case 'inputVideoFileLocation':
return 'video' + location.id + '.mp4';
case 'inputDocumentFileLocation':
var fileName = (location.file_name || '').split('.', 2);
var ext = fileName[1] || '';
if (location.sticker && !WebpManager.isWebpSupported()) {
ext += '.png';
}
if (fileName.length) {
return fileName[0] + '_' + location.id + '.' + ext;
}
return 'doc' + location.id;
case 'inputAudioFileLocation':
return 'audio' + location.id;
return fileName[0] + '_' + location.id + '.' + ext;
default:
if (!location.volume_id) {

107
app/js/lib/ng_utils.js

@ -1084,6 +1084,42 @@ angular.module('izhukov.utils', []) @@ -1084,6 +1084,42 @@ angular.module('izhukov.utils', [])
}
})
.service('GeoLocationManager', function ($q) {
var lastCoords = false;
function isAvailable() {
return navigator.geolocation !== undefined;
}
function getPosition(force) {
if (!force && lastCoords) {
return $q.when(lastCoords);
}
if (!isAvailable()) {
return $q.reject();
}
var deferred = $q.defer();
navigator.geolocation.getCurrentPosition(function (position) {
lastCoords = {
lat: position.coords.latitude,
long: position.coords.longitude
};
deferred.resolve(lastCoords);
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
}
return {
getPosition: getPosition,
isAvailable: isAvailable
}
})
.service('AppRuntimeManager', function ($window) {
return {
@ -1217,8 +1253,10 @@ angular.module('izhukov.utils', []) @@ -1217,8 +1253,10 @@ angular.module('izhukov.utils', [])
return {
wrapRichText: wrapRichText,
wrapPlainText: wrapPlainText,
wrapUrl: wrapUrl,
parseEntities: parseEntities,
parseMarkdown: parseMarkdown,
parseEmojis: parseEmojis,
mergeEntities: mergeEntities
};
@ -1347,6 +1385,16 @@ angular.module('izhukov.utils', []) @@ -1347,6 +1385,16 @@ angular.module('izhukov.utils', [])
return entities;
}
function parseEmojis(text) {
return text.replace(/:([a-z0-9\-\+\*_]+?):/gi, function (all, shortcut) {
var emojiCode = EmojiHelper.shortcuts[shortcut];
if (emojiCode !== undefined) {
return EmojiHelper.emojis[emojiCode][0];
}
return all;
});
}
function parseMarkdown (text, entities) {
if (text.indexOf('`') == -1) {
return text;
@ -1573,32 +1621,7 @@ angular.module('izhukov.utils', []) @@ -1573,32 +1621,7 @@ angular.module('izhukov.utils', [])
break;
}
var url = entity.url || entityText;
if (!url.match(/^https?:\/\//i)) {
url = 'http://' + url;
}
var tgMeMatch;
if (entity._ == 'messageEntityTextUrl') {
url = 'tg://unsafe_url?url=' + encodeURIComponent(url);
}
else if ((tgMeMatch = url.match(/^https?:\/\/telegram\.me\/(.+)/))) {
var path = tgMeMatch[1].split('/');
switch (path[0]) {
case 'joinchat':
url = 'tg://join?invite=' + path[1];
break;
case 'addstickers':
url = 'tg://addstickers?set=' + path[1];
break;
default:
if (path[1] && path[1].match(/^\d+$/)) {
url = 'tg://resolve?domain=' + path[0] + '&post=' + path[1];
}
else if (!path[1]) {
var domainQuery = path[0].split('?');
url = 'tg://resolve?domain=' + domainQuery[0] + (domainQuery[1] ? '&' + domainQuery[1] : '');
}
}
}
url = wrapUrl(url, entity._ == 'messageEntityTextUrl');
html.push(
'<a href="',
encodeEntities(url),
@ -1754,7 +1777,37 @@ angular.module('izhukov.utils', []) @@ -1754,7 +1777,37 @@ angular.module('izhukov.utils', [])
return text.join('');
}
})
function wrapUrl(url, unsafe) {
if (!url.match(/^https?:\/\//i)) {
url = 'http://' + url;
}
var tgMeMatch;
if (unsafe) {
url = 'tg://unsafe_url?url=' + encodeURIComponent(url);
}
else if ((tgMeMatch = url.match(/^https?:\/\/telegram\.me\/(.+)/))) {
var path = tgMeMatch[1].split('/');
switch (path[0]) {
case 'joinchat':
url = 'tg://join?invite=' + path[1];
break;
case 'addstickers':
url = 'tg://addstickers?set=' + path[1];
break;
default:
if (path[1] && path[1].match(/^\d+$/)) {
url = 'tg://resolve?domain=' + path[0] + '&post=' + path[1];
}
else if (!path[1]) {
var domainQuery = path[0].split('?');
url = 'tg://resolve?domain=' + domainQuery[0] + (domainQuery[1] ? '&' + domainQuery[1] : '');
}
}
}
return url;
}
});

164
app/js/lib/schema.tl.txt

@ -29,11 +29,6 @@ inputMediaUploadedPhoto#f7aff1c0 file:InputFile caption:string = InputMedia; @@ -29,11 +29,6 @@ inputMediaUploadedPhoto#f7aff1c0 file:InputFile caption:string = InputMedia;
inputMediaPhoto#e9bfb4f3 id:InputPhoto caption:string = InputMedia;
inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
inputMediaContact#a6e45987 phone_number:string first_name:string last_name:string = InputMedia;
inputMediaUploadedVideo#82713fdf file:InputFile duration:int w:int h:int mime_type:string caption:string = InputMedia;
inputMediaUploadedThumbVideo#7780ddf9 file:InputFile thumb:InputFile duration:int w:int h:int mime_type:string caption:string = InputMedia;
inputMediaVideo#936a4ebd id:InputVideo caption:string = InputMedia;
inputMediaUploadedAudio#4e498cab file:InputFile duration:int mime_type:string = InputMedia;
inputMediaAudio#89938781 id:InputAudio = InputMedia;
inputMediaUploadedDocument#1d89306d file:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string = InputMedia;
inputMediaUploadedThumbDocument#ad613491 file:InputFile thumb:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string = InputMedia;
inputMediaDocument#1a77f29c id:InputDocument caption:string = InputMedia;
@ -50,13 +45,8 @@ inputGeoPoint#f3b7acc9 lat:double long:double = InputGeoPoint; @@ -50,13 +45,8 @@ inputGeoPoint#f3b7acc9 lat:double long:double = InputGeoPoint;
inputPhotoEmpty#1cd7bf0d = InputPhoto;
inputPhoto#fb95c6c4 id:long access_hash:long = InputPhoto;
inputVideoEmpty#5508ec75 = InputVideo;
inputVideo#ee579652 id:long access_hash:long = InputVideo;
inputFileLocation#14637196 volume_id:long local_id:int secret:long = InputFileLocation;
inputVideoFileLocation#3d0364ec id:long access_hash:long = InputFileLocation;
inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation;
inputAudioFileLocation#74dc404d id:long access_hash:long = InputFileLocation;
inputDocumentFileLocation#4e45abe9 id:long access_hash:long = InputFileLocation;
inputPhotoCropAuto#ade6b004 = InputPhotoCrop;
@ -83,7 +73,7 @@ fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileL @@ -83,7 +73,7 @@ fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileL
fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation;
userEmpty#200250ba id:int = User;
user#d10d979a flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string = User;
user#d10d979a flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto;
@ -98,11 +88,11 @@ userStatusLastMonth#77ebc742 = UserStatus; @@ -98,11 +88,11 @@ userStatusLastMonth#77ebc742 = UserStatus;
chatEmpty#9ba2d800 id:int = Chat;
chat#d91cdd54 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true admins_enabled:flags.3?true admin:flags.4?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel = Chat;
chatForbidden#7328bdb id:int title:string = Chat;
channel#4b1b7506 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true editor:flags.3?true moderator:flags.4?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true id:int access_hash:long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string = Chat;
channel#a14dca52 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true editor:flags.3?true moderator:flags.4?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string = Chat;
channelForbidden#2d85832c id:int access_hash:long title:string = Chat;
chatFull#2e02a614 id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> = ChatFull;
channelFull#9e341ddf flags:# can_view_participants:flags.3?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int read_inbox_max_id:int unread_count:int unread_important_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int = ChatFull;
channelFull#97bee562 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int read_inbox_max_id:int unread_count:int unread_important_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int = ChatFull;
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
@ -115,17 +105,15 @@ chatPhotoEmpty#37c1011c = ChatPhoto; @@ -115,17 +105,15 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto;
messageEmpty#83e5de54 id:int = Message;
message#c992e15c flags:# unread:flags.0?true out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true id:int from_id:flags.8?int to_id:Peer fwd_from_id:flags.2?Peer fwd_date:flags.2?int via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int = Message;
messageService#c06b9607 flags:# unread:flags.0?true out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true id:int from_id:flags.8?int to_id:Peer date:int action:MessageAction = Message;
message#c09be45f flags:# unread:flags.0?true out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int = Message;
messageService#9e19a1f6 flags:# unread:flags.0?true out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
messageMediaEmpty#3ded6320 = MessageMedia;
messageMediaPhoto#3d8ce53d photo:Photo caption:string = MessageMedia;
messageMediaVideo#5bcf1675 video:Video caption:string = MessageMedia;
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
messageMediaContact#5e7d2f39 phone_number:string first_name:string last_name:string user_id:int = MessageMedia;
messageMediaUnsupported#9f84f49e = MessageMedia;
messageMediaDocument#f3e02ea8 document:Document caption:string = MessageMedia;
messageMediaAudio#c6b68300 audio:Audio = MessageMedia;
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
messageMediaVenue#7912b71f geo:GeoPoint title:string address:string provider:string venue_id:string = MessageMedia;
@ -140,6 +128,7 @@ messageActionChatJoinedByLink#f89cf5e8 inviter_id:int = MessageAction; @@ -140,6 +128,7 @@ messageActionChatJoinedByLink#f89cf5e8 inviter_id:int = MessageAction;
messageActionChannelCreate#95d2ac92 title:string = MessageAction;
messageActionChatMigrateTo#51bdb021 channel_id:int = MessageAction;
messageActionChannelMigrateFrom#b055eaee title:string chat_id:int = MessageAction;
messageActionPinMessage#94bd38ed = MessageAction;
dialog#c1dd804a peer:Peer top_message:int read_inbox_max_id:int unread_count:int notify_settings:PeerNotifySettings = Dialog;
dialogChannel#5b8496b2 peer:Peer top_message:int top_important_message:int read_inbox_max_id:int unread_count:int unread_important_count:int notify_settings:PeerNotifySettings pts:int = Dialog;
@ -151,16 +140,12 @@ photoSizeEmpty#e17e23c type:string = PhotoSize; @@ -151,16 +140,12 @@ photoSizeEmpty#e17e23c type:string = PhotoSize;
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
videoEmpty#c10658a8 id:long = Video;
video#f72887d3 id:long access_hash:long date:int duration:int mime_type:string size:int thumb:PhotoSize dc_id:int w:int h:int = Video;
geoPointEmpty#1117dd5f = GeoPoint;
geoPoint#2049d70c long:double lat:double = GeoPoint;
auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone;
auth.sentCode#efed51d9 phone_registered:Bool phone_code_hash:string send_call_timeout:int is_password:Bool = auth.SentCode;
auth.sentAppCode#e325edcf phone_registered:Bool phone_code_hash:string send_call_timeout:int is_password:Bool = auth.SentCode;
auth.sentCode#5e002502 flags:# phone_registered:flags.0?true type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode;
auth.authorization#ff036af1 user:User = auth.Authorization;
@ -174,13 +159,15 @@ inputNotifyAll#a429b886 = InputNotifyPeer; @@ -174,13 +159,15 @@ inputNotifyAll#a429b886 = InputNotifyPeer;
inputPeerNotifyEventsEmpty#f03064d8 = InputPeerNotifyEvents;
inputPeerNotifyEventsAll#e86a2c74 = InputPeerNotifyEvents;
inputPeerNotifySettings#46a2ce98 mute_until:int sound:string show_previews:Bool events_mask:int = InputPeerNotifySettings;
inputPeerNotifySettings#38935eb2 flags:# show_previews:flags.0?true silent:flags.1?true mute_until:int sound:string = InputPeerNotifySettings;
peerNotifyEventsEmpty#add53cb3 = PeerNotifyEvents;
peerNotifyEventsAll#6d1ded88 = PeerNotifyEvents;
peerNotifySettingsEmpty#70a68512 = PeerNotifySettings;
peerNotifySettings#8d5e11ee mute_until:int sound:string show_previews:Bool events_mask:int = PeerNotifySettings;
peerNotifySettings#9acda4c0 flags:# show_previews:flags.0?true silent:flags.1?true mute_until:int sound:string = PeerNotifySettings;
peerSettings#818426cd flags:# report_spam:flags.0?true = PeerSettings;
wallPaper#ccb03657 id:int title:string sizes:Vector<PhotoSize> color:int = WallPaper;
wallPaperSolid#63117f24 id:int title:string bg_color:int color:int = WallPaper;
@ -190,7 +177,7 @@ inputReportReasonViolence#1e22c78d = ReportReason; @@ -190,7 +177,7 @@ inputReportReasonViolence#1e22c78d = ReportReason;
inputReportReasonPornography#2e59d922 = ReportReason;
inputReportReasonOther#e1746d0a text:string = ReportReason;
userFull#5a89ac5b user:User link:contacts.Link profile_photo:Photo notify_settings:PeerNotifySettings blocked:Bool bot_info:BotInfo = UserFull;
userFull#5932fc03 flags:# blocked:flags.0?true user:User about:flags.1?string link:contacts.Link profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo = UserFull;
contact#f911c994 user_id:int mutual:Bool = Contact;
@ -229,10 +216,10 @@ inputMessagesFilterVideo#9fc00e65 = MessagesFilter; @@ -229,10 +216,10 @@ inputMessagesFilterVideo#9fc00e65 = MessagesFilter;
inputMessagesFilterPhotoVideo#56e9f0e4 = MessagesFilter;
inputMessagesFilterPhotoVideoDocuments#d95e73bb = MessagesFilter;
inputMessagesFilterDocument#9eddf188 = MessagesFilter;
inputMessagesFilterAudio#cfc87522 = MessagesFilter;
inputMessagesFilterAudioDocuments#5afbf764 = MessagesFilter;
inputMessagesFilterUrl#7ef0dd87 = MessagesFilter;
inputMessagesFilterGif#ffc86587 = MessagesFilter;
inputMessagesFilterVoice#50f5c392 = MessagesFilter;
inputMessagesFilterMusic#3751b49e = MessagesFilter;
updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;
updateMessageID#4e90bfd6 id:int random_id:long = Update;
@ -262,7 +249,7 @@ updateReadHistoryInbox#9961fd5c peer:Peer max_id:int pts:int pts_count:int = Upd @@ -262,7 +249,7 @@ updateReadHistoryInbox#9961fd5c peer:Peer max_id:int pts:int pts_count:int = Upd
updateReadHistoryOutbox#2f2f21bf peer:Peer max_id:int pts:int pts_count:int = Update;
updateWebPage#7f891213 webpage:WebPage pts:int pts_count:int = Update;
updateReadMessagesContents#68c13933 messages:Vector<int> pts:int pts_count:int = Update;
updateChannelTooLong#60946422 channel_id:int = Update;
updateChannelTooLong#eb0467fb flags:# channel_id:int pts:flags.0?int = Update;
updateChannel#b6d45656 channel_id:int = Update;
updateChannelGroup#c36c1e3c channel_id:int group:MessageGroup = Update;
updateNewChannelMessage#62ba04d9 message:Message pts:int pts_count:int = Update;
@ -275,8 +262,13 @@ updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update; @@ -275,8 +262,13 @@ updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update;
updateStickerSetsOrder#f0dfb451 order:Vector<long> = Update;
updateStickerSets#43ae3dec = Update;
updateSavedGifs#9375341e = Update;
updateBotInlineQuery#c01eea08 query_id:long user_id:int query:string offset:string = Update;
updateBotInlineSend#f69e113 user_id:int query:string id:string = Update;
updateBotInlineQuery#54826690 flags:# query_id:long user_id:int query:string geo:flags.0?GeoPoint offset:string = Update;
updateBotInlineSend#e48f964 flags:# user_id:int query:string geo:flags.0?GeoPoint id:string msg_id:flags.1?InputBotInlineMessageID = Update;
updateEditChannelMessage#1b3f4df7 message:Message pts:int pts_count:int = Update;
updateChannelPinnedMessage#98592475 channel_id:int id:int = Update;
updateBotCallbackQuery#a68c688c query_id:long user_id:int peer:Peer msg_id:int data:bytes = Update;
updateEditMessage#e40370a3 message:Message pts:int pts_count:int = Update;
updateInlineBotCallbackQuery#2cbd95af query_id:long user_id:int msg_id:InputBotInlineMessageID data:bytes = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -285,8 +277,8 @@ updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Ve @@ -285,8 +277,8 @@ updates.difference#f49ca0 new_messages:Vector<Message> new_encrypted_messages:Ve
updates.differenceSlice#a8fb1981 new_messages:Vector<Message> new_encrypted_messages:Vector<EncryptedMessage> other_updates:Vector<Update> chats:Vector<Chat> users:Vector<User> intermediate_state:updates.State = updates.Difference;
updatesTooLong#e317af7e = Updates;
updateShortMessage#13e4deaa flags:# unread:flags.0?true out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true id:int user_id:int message:string pts:int pts_count:int date:int fwd_from_id:flags.2?Peer fwd_date:flags.2?int via_bot_id:flags.11?int reply_to_msg_id:flags.3?int entities:flags.7?Vector<MessageEntity> = Updates;
updateShortChatMessage#248afa62 flags:# unread:flags.0?true out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from_id:flags.2?Peer fwd_date:flags.2?int via_bot_id:flags.11?int reply_to_msg_id:flags.3?int entities:flags.7?Vector<MessageEntity> = Updates;
updateShortMessage#914fbf11 flags:# unread:flags.0?true out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int user_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int entities:flags.7?Vector<MessageEntity> = Updates;
updateShortChatMessage#16812688 flags:# unread:flags.0?true out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true id:int from_id:int chat_id:int message:string pts:int pts_count:int date:int fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int entities:flags.7?Vector<MessageEntity> = Updates;
updateShort#78d4dec1 update:Update date:int = Updates;
updatesCombined#725b04c3 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq_start:int seq:int = Updates;
updates#74ae4240 updates:Vector<Update> users:Vector<User> chats:Vector<Chat> date:int seq:int = Updates;
@ -299,9 +291,9 @@ photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo; @@ -299,9 +291,9 @@ photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo;
upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true id:int ip_address:string port:int = DcOption;
dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true id:int ip_address:string port:int = DcOption;
config#6bbc5f8 date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int disabled_features:Vector<DisabledFeature> = Config;
config#317ceef4 date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int disabled_features:Vector<DisabledFeature> = Config;
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
@ -335,15 +327,9 @@ messages.dhConfig#2c221edd g:int p:bytes version:int random:bytes = messages.DhC @@ -335,15 +327,9 @@ messages.dhConfig#2c221edd g:int p:bytes version:int random:bytes = messages.DhC
messages.sentEncryptedMessage#560f8935 date:int = messages.SentEncryptedMessage;
messages.sentEncryptedFile#9493ff32 date:int file:EncryptedFile = messages.SentEncryptedMessage;
inputAudioEmpty#d95adc84 = InputAudio;
inputAudio#77d440ff id:long access_hash:long = InputAudio;
inputDocumentEmpty#72f0eaae = InputDocument;
inputDocument#18798952 id:long access_hash:long = InputDocument;
audioEmpty#586988d8 id:long = Audio;
audio#f9e35055 id:long access_hash:long date:int duration:int mime_type:string size:int dc_id:int = Audio;
documentEmpty#36f8c871 id:long = Document;
document#f9a39f4f id:long access_hash:long date:int mime_type:string size:int thumb:PhotoSize dc_id:int attributes:Vector<DocumentAttribute> = Document;
@ -368,8 +354,10 @@ sendMessageChooseContactAction#628cbc6f = SendMessageAction; @@ -368,8 +354,10 @@ sendMessageChooseContactAction#628cbc6f = SendMessageAction;
contacts.found#1aa1f784 results:Vector<Peer> chats:Vector<Chat> users:Vector<User> = contacts.Found;
inputPrivacyKeyStatusTimestamp#4f96cb18 = InputPrivacyKey;
inputPrivacyKeyChatInvite#bdfb0426 = InputPrivacyKey;
privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
privacyKeyChatInvite#500e6dfa = PrivacyKey;
inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
@ -389,13 +377,11 @@ account.privacyRules#554abb6f rules:Vector<PrivacyRule> users:Vector<User> = acc @@ -389,13 +377,11 @@ account.privacyRules#554abb6f rules:Vector<PrivacyRule> users:Vector<User> = acc
accountDaysTTL#b8d0afdf days:int = AccountDaysTTL;
account.sentChangePhoneCode#a4f58c4c phone_code_hash:string send_call_timeout:int = account.SentChangePhoneCode;
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
documentAttributeAnimated#11b58939 = DocumentAttribute;
documentAttributeSticker#3a556302 alt:string stickerset:InputStickerSet = DocumentAttribute;
documentAttributeVideo#5910cccb duration:int w:int h:int = DocumentAttribute;
documentAttributeAudio#ded218e0 duration:int title:string performer:string = DocumentAttribute;
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
messages.stickersNotModified#f1749a22 = messages.Stickers;
@ -428,7 +414,7 @@ account.password#7c18141c current_salt:bytes new_salt:bytes hint:string has_reco @@ -428,7 +414,7 @@ account.password#7c18141c current_salt:bytes new_salt:bytes hint:string has_reco
account.passwordSettings#b7b72ab3 email:string = account.PasswordSettings;
account.passwordInputSettings#bcfc532c flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string = account.PasswordInputSettings;
account.passwordInputSettings#86916deb flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string = account.PasswordInputSettings;
auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
@ -450,16 +436,21 @@ messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents: @@ -450,16 +436,21 @@ messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:
botCommand#c27ac8c7 command:string description:string = BotCommand;
botInfoEmpty#bb2e37ce = BotInfo;
botInfo#9cf585d user_id:int version:int share_text:string description:string commands:Vector<BotCommand> = BotInfo;
botInfo#98e81d3a user_id:int description:string commands:Vector<BotCommand> = BotInfo;
keyboardButton#a2fa4880 text:string = KeyboardButton;
keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton;
keyboardButtonCallback#683a5e46 text:string data:bytes = KeyboardButton;
keyboardButtonRequestPhone#b16a6c29 text:string = KeyboardButton;
keyboardButtonRequestGeoLocation#fc796b3f text:string = KeyboardButton;
keyboardButtonSwitchInline#ea1b7a14 text:string query:string = KeyboardButton;
keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
replyKeyboardHide#a03e5b85 flags:# selective:flags.2?true = ReplyMarkup;
replyKeyboardForceReply#f4108aa0 flags:# single_use:flags.1?true selective:flags.2?true = ReplyMarkup;
replyKeyboardMarkup#3502758c flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true rows:Vector<KeyboardButtonRow> = ReplyMarkup;
replyInlineMarkup#48a30254 rows:Vector<KeyboardButtonRow> = ReplyMarkup;
help.appChangelogEmpty#af7e0394 = help.AppChangelog;
help.appChangelog#4668e6bd text:string = help.AppChangelog;
@ -523,19 +514,47 @@ messages.foundGifs#450a1c0a next_offset:int results:Vector<FoundGif> = messages. @@ -523,19 +514,47 @@ messages.foundGifs#450a1c0a next_offset:int results:Vector<FoundGif> = messages.
messages.savedGifsNotModified#e8025ca2 = messages.SavedGifs;
messages.savedGifs#2e0709a5 hash:int gifs:Vector<Document> = messages.SavedGifs;
inputBotInlineMessageMediaAuto#2e43e587 caption:string = InputBotInlineMessage;
inputBotInlineMessageText#adf0df71 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> = InputBotInlineMessage;
inputBotInlineMessageMediaAuto#292fed13 flags:# caption:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaGeo#f4a59de1 flags:# geo_point:InputGeoPoint reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaVenue#aaafadc8 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaContact#2daf01a7 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineResult#2cbbe15a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb_url:flags.4?string content_url:flags.5?string content_type:flags.5?string w:flags.6?int h:flags.6?int duration:flags.7?int send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultDocument#fff8fdc4 flags:# id:string type:string title:flags.1?string description:flags.2?string document:InputDocument send_message:InputBotInlineMessage = InputBotInlineResult;
botInlineMessageMediaAuto#fc56e87d caption:string = BotInlineMessage;
botInlineMessageText#a56197a9 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> = BotInlineMessage;
botInlineMessageMediaAuto#a74b15b flags:# caption:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaGeo#3a8fd8b8 flags:# geo:GeoPoint reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaVenue#4366232e flags:# geo:GeoPoint title:string address:string provider:string venue_id:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaContact#35edb4d4 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMediaResultDocument#f897d33e id:string type:string document:Document send_message:BotInlineMessage = BotInlineResult;
botInlineMediaResultPhoto#c5528587 id:string type:string photo:Photo send_message:BotInlineMessage = BotInlineResult;
botInlineResult#9bebaeb9 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb_url:flags.4?string content_url:flags.5?string content_type:flags.5?string w:flags.6?int h:flags.6?int duration:flags.7?int send_message:BotInlineMessage = BotInlineResult;
botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult;
messages.botResults#256709a6 flags:# gallery:flags.0?true query_id:long next_offset:flags.1?string switch_pm:flags.2?InlineBotSwitchPM results:Vector<BotInlineResult> = messages.BotResults;
exportedMessageLink#1f486803 link:string = ExportedMessageLink;
messageFwdHeader#c786ddcb flags:# from_id:flags.0?int date:int channel_id:flags.1?int channel_post:flags.2?int = MessageFwdHeader;
auth.codeTypeSms#72a3158c = auth.CodeType;
auth.codeTypeCall#741cd3e3 = auth.CodeType;
auth.codeTypeFlashCall#226ccefb = auth.CodeType;
auth.sentCodeTypeApp#3dbb5986 length:int = auth.SentCodeType;
auth.sentCodeTypeSms#c000bba2 length:int = auth.SentCodeType;
auth.sentCodeTypeCall#5353e5a7 length:int = auth.SentCodeType;
auth.sentCodeTypeFlashCall#ab03c6d9 pattern:string = auth.SentCodeType;
messages.botCallbackAnswer#1264f1c6 flags:# alert:flags.1?true message:flags.0?string = messages.BotCallbackAnswer;
messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData;
inputBotInlineMessageID#890c3d89 dc_id:int id:long access_hash:long = InputBotInlineMessageID;
messages.botResults#1170b0a3 flags:# gallery:flags.0?true query_id:long next_offset:flags.1?string results:Vector<BotInlineResult> = messages.BotResults;
inlineBotSwitchPM#3c20629f text:string start_param:string = InlineBotSwitchPM;
---functions---
@ -546,8 +565,7 @@ invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X; @@ -546,8 +565,7 @@ invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
auth.checkPhone#6fe51dfb phone_number:string = auth.CheckedPhone;
auth.sendCode#768d5f4d phone_number:string sms_type:int api_id:int api_hash:string lang_code:string = auth.SentCode;
auth.sendCall#3c51564 phone_number:string phone_code_hash:string = Bool;
auth.sendCode#ccfd70cf flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool api_id:int api_hash:string lang_code:string = auth.SentCode;
auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization;
auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
auth.logOut#5717da40 = Bool;
@ -556,18 +574,19 @@ auth.sendInvites#771c1d97 phone_numbers:Vector<string> message:string = Bool; @@ -556,18 +574,19 @@ auth.sendInvites#771c1d97 phone_numbers:Vector<string> message:string = Bool;
auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization;
auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
auth.sendSms#da9f3e8 phone_number:string phone_code_hash:string = Bool;
auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization;
auth.checkPassword#a63011e password_hash:bytes = auth.Authorization;
auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
auth.recoverPassword#4ea56e92 code:string = auth.Authorization;
auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode;
auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool;
account.registerDevice#446c712c token_type:int token:string device_model:string system_version:string app_version:string app_sandbox:Bool lang_code:string = Bool;
account.unregisterDevice#65c55b40 token_type:int token:string = Bool;
account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool;
account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings;
account.resetNotifySettings#db7e1747 = Bool;
account.updateProfile#f0888d68 first_name:string last_name:string = User;
account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User;
account.updateStatus#6628562c offline:Bool = Bool;
account.getWallPapers#c04cfac2 = Vector<WallPaper>;
account.reportPeer#ae189d5f peer:InputPeer reason:ReportReason = Bool;
@ -578,7 +597,7 @@ account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector<InputPrivacyRule> = @@ -578,7 +597,7 @@ account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector<InputPrivacyRule> =
account.deleteAccount#418d4e0b reason:string = Bool;
account.getAccountTTL#8fc711d = AccountDaysTTL;
account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool;
account.sendChangePhoneCode#a407a8f4 phone_number:string = account.SentChangePhoneCode;
account.sendChangePhoneCode#8e57deb flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode;
account.changePhone#70c32edb phone_number:string phone_code_hash:string phone_code:string = User;
account.updateDeviceLocked#38df3532 period:int = Bool;
account.getAuthorizations#e320c158 = account.Authorizations;
@ -605,17 +624,19 @@ contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer; @@ -605,17 +624,19 @@ contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
messages.getMessages#4222fa74 id:Vector<int> = messages.Messages;
messages.getDialogs#6b47f94d offset_date:int offset_id:int offset_peer:InputPeer limit:int = messages.Dialogs;
messages.getHistory#8a8ec2da peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.getHistory#afa92846 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.search#d4569248 flags:# important_only:flags.0?true peer:InputPeer q:string filter:MessagesFilter min_date:int max_date:int offset:int max_id:int limit:int = messages.Messages;
messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages;
messages.deleteHistory#b7c13bd9 peer:InputPeer max_id:int = messages.AffectedHistory;
messages.deleteMessages#a5f18925 id:Vector<int> = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
messages.sendMessage#fa88427a flags:# no_webpage:flags.1?true broadcast:flags.4?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.sendMedia#c8f16791 flags:# broadcast:flags.4?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia random_id:long reply_markup:flags.2?ReplyMarkup = Updates;
messages.forwardMessages#708e0195 flags:# broadcast:flags.4?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer = Updates;
messages.sendMessage#fa88427a flags:# no_webpage:flags.1?true broadcast:flags.4?true silent:flags.5?true background:flags.6?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.sendMedia#c8f16791 flags:# broadcast:flags.4?true silent:flags.5?true background:flags.6?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia random_id:long reply_markup:flags.2?ReplyMarkup = Updates;
messages.forwardMessages#708e0195 flags:# broadcast:flags.4?true silent:flags.5?true background:flags.6?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.hideReportSpam#a8f1709b peer:InputPeer = Bool;
messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings;
messages.getChats#3c6aa187 id:Vector<int> = messages.Chats;
messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull;
messages.editChatTitle#dc452855 chat_id:int title:string = Updates;
@ -656,9 +677,14 @@ messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Doc @@ -656,9 +677,14 @@ messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Doc
messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs;
messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
messages.getInlineBotResults#9324600d bot:InputUser query:string offset:string = messages.BotResults;
messages.setInlineBotResults#3f23ec12 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string = Bool;
messages.sendInlineBotResult#b16e06fe flags:# broadcast:flags.4?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates;
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
messages.sendInlineBotResult#b16e06fe flags:# broadcast:flags.4?true silent:flags.5?true background:flags.6?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates;
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
messages.editMessage#ce91e4ca flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.editInlineBotMessage#130c2c85 flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
messages.getBotCallbackAnswer#a6e94f04 peer:InputPeer msg_id:int data:bytes = messages.BotCallbackAnswer;
messages.setBotCallbackAnswer#481c591a flags:# alert:flags.1?true query_id:long message:flags.0?string = Bool;
updates.getState#edd4882a = updates.State;
updates.getDifference#a041495 pts:int date:int qts:int = updates.Difference;
@ -683,7 +709,7 @@ help.getAppChangelog#5bab7fb2 device_model:string system_version:string app_vers @@ -683,7 +709,7 @@ help.getAppChangelog#5bab7fb2 device_model:string system_version:string app_vers
help.getTermsOfService#37d78f83 lang_code:string = help.TermsOfService;
channels.getDialogs#a9d3d249 offset:int limit:int = messages.Dialogs;
channels.getImportantHistory#ddb929cb channel:InputChannel offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
channels.getImportantHistory#8f494bb2 channel:InputChannel offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
channels.deleteUserHistory#d10dd71b channel:InputChannel user_id:InputUser = messages.AffectedHistory;
@ -706,4 +732,8 @@ channels.leaveChannel#f836aa95 channel:InputChannel = Updates; @@ -706,4 +732,8 @@ channels.leaveChannel#f836aa95 channel:InputChannel = Updates;
channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> = Updates;
channels.kickFromChannel#a672de14 channel:InputChannel user_id:InputUser kicked:Bool = Updates;
channels.exportInvite#c7560885 channel:InputChannel = ExportedChatInvite;
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
channels.toggleInvites#49609307 channel:InputChannel enabled:Bool = Updates;
channels.exportMessageLink#c846d22d channel:InputChannel id:int = ExportedMessageLink;
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = Updates;

2
app/js/lib/tl_utils.js

@ -111,7 +111,7 @@ TLSerialization.prototype.storeLong = function (sLong, field) { @@ -111,7 +111,7 @@ TLSerialization.prototype.storeLong = function (sLong, field) {
this.writeInt(intToUint(divRem[0].intValue()), (field || '') + ':long[high]');
};
TLSerialization.prototype.storeDouble = function (f) {
TLSerialization.prototype.storeDouble = function (f, field) {
var buffer = new ArrayBuffer(8);
var intView = new Int32Array(buffer);
var doubleView = new Float64Array(buffer);

11
app/js/lib/utils.js

@ -164,7 +164,10 @@ function getRichValue(field) { @@ -164,7 +164,10 @@ function getRichValue(field) {
lines.push(line.join(''));
}
return lines.join('\n');
var value = lines.join('\n');
value = value.replace(/\u00A0/g, ' ');
return value;
}
function getRichValueWithCaret(field) {
@ -197,6 +200,7 @@ function getRichValueWithCaret(field) { @@ -197,6 +200,7 @@ function getRichValueWithCaret(field) {
if (caretPos != -1) {
value = value.substr(0, caretPos) + value.substr(caretPos + 1);
}
value = value.replace(/\u00A0/g, ' ');
return [value, caretPos];
}
@ -376,10 +380,13 @@ function templateUrl (tplName) { @@ -376,10 +380,13 @@ function templateUrl (tplName) {
media_modal_layout: 'desktop',
slider: 'desktop',
reply_message: 'desktop',
message_body: 'desktop',
message_media: 'desktop',
forwarded_messages: 'desktop',
chat_invite_link_modal: 'desktop',
reply_markup: 'desktop',
dialog_service: 'desktop',
short_message: 'desktop',
pinned_message: 'desktop',
channel_edit_modal: 'desktop',
megagroup_edit_modal: 'desktop',
inline_results: 'desktop',

8
app/js/locales/en-us.json

@ -235,6 +235,9 @@ @@ -235,6 +235,9 @@
"confirm_modal_delete_group_md": "Are you sure you want to delete this group?\n\nAll members will be removed and all messages will be lost.",
"confirm_modal_jump_ext_url_md": "Open this link?\n\n{url}",
"confirm_modal_migrate_supergroup_md": "Please note that group members will need to update their Telegram apps to the latest version to see your supergroup.\n\nAre you sure you want to upgrade this group?",
"confirm_modal_bot_access_phone": "The bot will know your phone number. This can be useful for integration with other services.",
"confirm_modal_bot_access_geo": "This will send your current location to the bot.",
"confirm_modal_bot_access_geo_inline": "This bot would like to know your location each time you send it a request. This can be used to provide location-specific results.",
"confirm_modal_are_u_sure": "Are you sure?",
@ -308,6 +311,7 @@ @@ -308,6 +311,7 @@
"conversation_changed_channel_name": "Channel renamed",
"conversation_changed_channel_photo": "Channel photo updated",
"conversation_removed_channel_photo": "Channel photo removed",
"conversation_pinned_message": "pinned message",
"conversation_message_sent": "sent you a message",
"conversation_forwarded_X_messages": "{'one': 'forwarded {} message', 'other': 'forwarded {} messages'}",
@ -326,6 +330,7 @@ @@ -326,6 +330,7 @@
"message_service_left_group": "left group",
"message_service_joined_by_link": "joined group via invite link",
"message_service_joined": "joined the group",
"message_service_pinned_message": "pinned «{message}»",
"message_service_unsupported_action": "unsupported action {action}",
"message_service_bot_intro_header": "What can this bot do?",
"message_service_converted_to_supergroup": "upgraded the group to a supergroup",
@ -387,6 +392,7 @@ @@ -387,6 +392,7 @@
"error_modal_invite_link_invalid": "The invite link is invalid",
"error_modal_channel_not_accessible": "Sorry, this channel is not accessible.",
"error_modal_not_contact_flood": "Sorry, you can only send messages to mutual contacts at the moment. {more-info-link: More info »}",
"error_modal_gelocation_na": "App was unable to determine your current location",
"head_telegram": "Telegram",
@ -540,6 +546,8 @@ @@ -540,6 +546,8 @@
"message_attach_video_save": "Save file",
"message_attach_video_play": "Play video",
"message_attach_unsupported": "The message is not supported on your version of Telegram Web. Update the app to view: {link}.",
"conversation_select_modal_title": "Select conversation",
"conversation_select_modal_contacts": "Contacts",
"conversation_one_selected": "{name1}",

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

@ -62,7 +62,7 @@ @@ -62,7 +62,7 @@
"settings_modal_sound": "Suono",
"settings_modal_enter_send_description_md": "**Invio** - invia messaggio, **Shift + Invio** - a capo",
"settings_modal_ctrl_enter_send_description_md": "**Ctrl + Invio** - invia messaggio, **Invio** - a capo",
"settings_modal_send_on_enter": "Spedisci con Invio",
"settings_modal_send_on_enter": "Invia con tasto invio",
"settings_switch_back_to_desktop": "Torna alla modalità desktop",
"settings_modal_about": "Info",
"settings_modal_source_code_github": "Codice sorgente su GitHub",
@ -77,7 +77,7 @@ @@ -77,7 +77,7 @@
"settings_modal_password_email_pending_cancel_mobile": "Annulla password",
"password_delete_title": "Disattiva password",
"password_change_title": "Verifica in due passaggi",
"password_current_placeholder": "Inserisci la password attuale",
"password_current_placeholder": "Inserisci la password corrente",
"password_create_placeholder": "Inserisci una password",
"password_new_placeholder": "Inserisci la nuova password",
"password_confirm_placeholder": "Reinserisci la nuova password",
@ -90,7 +90,7 @@ @@ -90,7 +90,7 @@
"password_delete_submit": "Elimina password",
"sessions_modal_title": "Sessioni attive",
"sessions_modal_loading": "Caricamento{dots}",
"sessions_modal_current_session": "Sessione attuale",
"sessions_modal_current_session": "Sessione corrente",
"sessions_modal_session_online": "in linea",
"sessions_modal_terminate_one": "Termina",
"sessions_modal_terminate_all": "Termina tutte le altre sessioni",
@ -345,13 +345,13 @@ @@ -345,13 +345,13 @@
"error_modal_user_not_mutual_contact": "L'utente può essere invitato solo da un suo contatto",
"error_modal_invite_link_invalid": "Il link di invito non è valido",
"error_modal_channel_not_accessible": "Spiacenti, questo canale non è accessibile.",
"error_modal_not_contact_flood": "Spiacenti, ma al momento puoi scrivere solo ai contatti in comune. {more-info-link: Maggiori informazioni »}",
"error_modal_not_contact_flood": "Spiacenti, ma al momento puoi scrivere solo ai contatti reciproci. {more-info-link: Maggiori informazioni »}",
"head_telegram": "Telegram",
"head_new_group": "Nuovo gruppo",
"head_new_contact": "Nuovo contatto",
"head_contacts": "Contatti",
"head_contacts_title": "Contatti",
"head_telegram_faq": "FAQ di Telegram",
"head_telegram_faq": "Domande frequenti",
"head_settings": "Impostazioni",
"head_log_out": "Disconnetti",
"head_peer_more": "Altro",
@ -359,7 +359,7 @@ @@ -359,7 +359,7 @@
"head_media_photos": "Foto",
"head_media_video": "Video",
"head_media_documents": "File",
"head_media_audio": "Note vocali",
"head_media_audio": "Messaggi vocali",
"head_about": "Info",
"head_clear_all": "Pulisci tutto",
"head_edit": "Modifica",
@ -387,7 +387,7 @@ @@ -387,7 +387,7 @@
"im_get_started": "Inizia",
"im_welcome_text": "Benvenuto su Telegram Web. Puoi sempre impostare una foto profilo e cambiare il tuo nome nelle Impostazioni.",
"im_open_settings": "Apri impostazioni",
"im_select_a_chat": "Seleziona una chat per iniziare a messaggiare",
"im_select_a_chat": "Seleziona una chat per iniziare a chattare",
"im_loading_history": "Caricamento messaggi precedenti",
"im_info": "Info",
"im_edit": "Modifica",
@ -395,7 +395,7 @@ @@ -395,7 +395,7 @@
"im_media_photos": "Foto",
"im_media_video": "Video",
"im_media_documents": "File",
"im_media_audio": "Note vocali",
"im_media_audio": "Messaggi vocali",
"im_pluralize_participants": "{'0': 'Nessun membro', 'one': '1 membro', 'other': '{} membri'}",
"im_show_recent_messages": "Mostra messaggi recenti",
"im_show_all_messages": "Mostra tutti i messaggi",
@ -454,7 +454,7 @@ @@ -454,7 +454,7 @@
"login_sign_up": "Registrati",
"login_about_title": "Info",
"login_about_desc1_md": "Il client web di Telegram è un modo gratuito, veloce e sicuro per goderti le funzioni di **Telegram** sul tuo **browser web**.",
"login_about_desc2_md": "È sempre sincronizzato con **Telegram** sul tuo dispositivo mobile, quindi è lo strumento perfetto per messaggiare e condividere file.",
"login_about_desc2_md": "È sempre sincronizzato con **Telegram** sul tuo dispositivo mobile, quindi è lo strumento perfetto per chattare e condividere file.",
"login_about_desc3_md": "Il nostro {source-link: codice sorgente} è aperto, così ognuno può contribuire.",
"login_about_intro": "Benvenuto nel client web ufficiale di Telegram.",
"login_about_learn": "Scopri di più",
@ -476,7 +476,7 @@ @@ -476,7 +476,7 @@
"message_via_bot": "via {bot}",
"message_forwarded_message_mobile": "Inoltrato da {from}",
"message_forwarded_via_message_mobile": "Inoltrato da {from} via {bot}",
"message_attach_audio_message": "Nota vocale",
"message_attach_audio_message": "Messaggio vocale",
"message_attach_audio_play": "Riproduci",
"message_attach_document_open": "Apri",
"message_attach_document_download": "Scarica",

42
app/js/locales/nl-nl.json

@ -8,10 +8,10 @@ @@ -8,10 +8,10 @@
"modal_ok": "OK",
"modal_done": "Gereed",
"group_modal_info": "Groepsinformatie",
"group_modal_pluralize_participants": "{'0': 'Geen deelnemers', 'one': '1 deelnemer', 'other': '{} deelnemers'}",
"group_modal_pluralize_participants": "{'0': 'Geen leden', 'one': '1 lid', 'other': '{} leden'}",
"group_modal_pluralize_online_participants": "{'one': '1 online', 'other': '{} online'}",
"group_modal_participants": "{total}, {online}",
"group_modal_add_member": "Deelnemer toevoegen",
"group_modal_add_member": "Lid toevoegen",
"group_modal_return": "Terugkeren naar groep",
"group_modal_update_photo": "Foto wijzigen",
"group_modal_update_active": "Bijwerken",
@ -24,9 +24,9 @@ @@ -24,9 +24,9 @@
"group_modal_notifications": "Meldingen",
"group_modal_menu_share_link": "Uitnodigingslink sturen",
"group_modal_migrate_to_supergroup": "Opwaarderen naar supergroep",
"group_modal_members": "Deelnemers",
"group_modal_members": "Leden",
"group_modal_members_kick": "Verwijder",
"group_modal_migrate_header": "Deelnemerslimiet bereikt.",
"group_modal_migrate_header": "Ledenlimiet bereikt.",
"group_modal_migrate_desc": "Wil je extra functies en een hogere limiet? Waardeer op naar een supergroep:",
"group_modal_migrate_item1": "Supergroepen hebben tot 1000 leden",
"group_modal_migrate_item2": "Nieuwe leden zien de hele geschiedenis",
@ -37,8 +37,8 @@ @@ -37,8 +37,8 @@
"channel_modal_description": "Beschrijving",
"channel_modal_share_link": "Link delen",
"channel_modal_share_loading": "Laden{dots}",
"channel_modal_join": "Deelnemen aan kanaal",
"channel_modal_add_member": "Deelnemers toevoegen",
"channel_modal_join": "Lid worden van kanaal",
"channel_modal_add_member": "Leden toevoegen",
"channel_modal_leave_channel": "Kanaal verlaten",
"channel_modal_delete_channel": "Kanaal verwijderen",
"country_select_modal_title": "Land",
@ -183,7 +183,7 @@ @@ -183,7 +183,7 @@
"group_invite_revoke": "Intrekken",
"confirm_modal_logout": "Weet je zeker dat je wilt uitloggen?",
"confirm_modal_update_reload": "Een nieuwe versie van Telegram Web is gedownload. Deze starten?",
"confirm_modal_history_flush": "Weet je het zeker? Dit kan niet ongedaan worden gemaakt!",
"confirm_modal_history_flush": "Weet je het zeker? Je kunt dit niet ongedaan maken.",
"confirm_modal_terminate_sessions": "Weet je zeker dat je wilt uitloggen van alle apparaten behalve het huidige apparaat?",
"confirm_modal_terminate_session": "Weet je zeker dat je wilt uitloggen?",
"confirm_modal_clipboard_file_send": "Bestand(en) echt vanaf klembord versturen?",
@ -203,15 +203,15 @@ @@ -203,15 +203,15 @@
"confirm_modal_resize_mobile_md": "Wil je omschakelen naar de mobiele versie?",
"confirm_modal_recovery_email_empty_md": "Let op: Weet je zeker dat je geen herstel-e-mailadres wilt instellen?\n\nAls je je wachtwoord vergeet verlies je toegang tot je Telegram account.",
"confirm_modal_abort_password_setup": "Twee-staps-verificatie annuleren?",
"confirm_modal_reset_account_md": "Weet je het zeker?\nDeze actie kan niet worden hersteld.\n\nAl je chats, berichten en alle andere data gaan verloren als je verder gaat met de account-reset.",
"confirm_modal_join_group_link": "Wil je deelnemen aan de groep «{title}»?",
"confirm_modal_join_channel_link": "Deelnemen aan kanaal «{title}»?",
"confirm_modal_reset_account_md": "Weet je het zeker?\nJe kunt dit niet ongedaan maken.\n\nAl je chats, berichten en alle andere data gaan verloren als je verder gaat met de account-reset.",
"confirm_modal_join_group_link": "Wil je lid worden van de groep «{title}»?",
"confirm_modal_join_channel_link": "Lid worden van kanaal «{title}»?",
"confirm_modal_revoke_group_link": "Deze link echt intrekken? Na intrekken kan niemand meer lid worden met de oude link.",
"confirm_modal_revoke_channel_link": "Deze link echt intrekken? Na intrekken kan niemand meer lid worden met de oude link.",
"confirm_modal_delete_channel_md": "Kanaal echt verwijderen?\n\nBerichten worden gewist en alle deelnemers verwijderd.",
"confirm_modal_delete_group_md": "Groep echt verwijderen?\n\nBerichten worden gewist en alle deelnemers verwijderd.",
"confirm_modal_delete_channel_md": "Kanaal echt verwijderen?\n\nBerichten worden gewist en alle leden verwijderd.",
"confirm_modal_delete_group_md": "Groep echt verwijderen?\n\nBerichten worden gewist en alle leden verwijderd.",
"confirm_modal_jump_ext_url_md": "Link openen?\n\n{url}",
"confirm_modal_migrate_supergroup_md": "Groepsdeelnemers moeten updaten naar de meest recente Telegram om je supergroep te kunnen zien.\n\nGroep echt opwaarderen?",
"confirm_modal_migrate_supergroup_md": "Groepsleden moeten updaten naar de meest recente Telegram om je supergroep te kunnen zien.\n\nGroep echt opwaarderen?",
"confirm_modal_are_u_sure": "Weet je het zeker?",
"confirm_modal_logout_submit": "Uitloggen",
"confirm_modal_history_flush_submit": "Chat verwijderen",
@ -229,7 +229,7 @@ @@ -229,7 +229,7 @@
"contacts_modal_edit_list": "Wijzig",
"contacts_modal_edit_cancel": "Annuleren",
"contacts_modal_edit_delete": "Verwijder",
"contacts_modal_pluralize_new_group_members": "{'one': '1 deelnemer', 'other': '{} deelnemers'}",
"contacts_modal_pluralize_new_group_members": "{'one': '1 lid', 'other': '{} leden'}",
"contacts_modal_title": "Contacten",
"contacts_modal_new_contact": "Nieuw contact",
"contacts_modal_empty_list": "Je lijst met contacten is leeg. Je kunt een {import-link: contact toevoegen} aan de hand van het telefoonnummer.",
@ -263,13 +263,13 @@ @@ -263,13 +263,13 @@
"conversation_group_photo_updated": "heeft de groepsafbeelding gewijzigd",
"conversation_group_photo_removed": "heeft de groepsafbeelding verwijderd",
"conversation_returned_to_group": "Teruggekeerd naar de groep",
"conversation_invited_user": "{user} uitgenodigd",
"conversation_invited_user": "heeft {user} uitgenodigd",
"conversation_invited_users": "{'one': '{} gebruiker uitgenodigd', 'other': '{} gebruikers uitgenodigd'}",
"conversation_left_group": "heeft de groep verlaten",
"conversation_kicked_user": "{user} verwijderd",
"conversation_invited_user_message": "gebruiker uitgenodigd",
"conversation_kicked_user_message": "gebruiker verwijderd",
"conversation_joined_by_link": "Neemt deel aan de groep",
"conversation_joined_by_link": "is nu lid van de groep",
"conversation_converted_to_supergroup": "opgewaardeerd naar een supergroep",
"conversation_created_channel": "Kanaal gemaakt",
"conversation_changed_channel_name": "Kanaalnaam gewijzigd",
@ -283,12 +283,12 @@ @@ -283,12 +283,12 @@
"message_service_changed_group_name": "de groepsnaam is gewijzigd naar {group-name}",
"message_service_changed_group_photo": "heeft de groepsafbeelding gewijzigd",
"message_service_removed_group_photo": "heeft de groepsafbeelding verwijderd",
"message_service_invited_user": "{user} uitgenodigd",
"message_service_invited_user": "heeft {user} uitgenodigd",
"message_service_invited_users": "{user} en {num-more} andere uitgenodigd",
"message_service_returned_to_group": "Teruggekeerd naar de groep",
"message_service_kicked_user": "{user} verwijderd",
"message_service_left_group": "heeft de groep verlaten",
"message_service_joined_by_link": "Neemt deel aan de groep via uitnodigingslink",
"message_service_joined_by_link": "is nu lid van de groep via uitnodigingslink",
"message_service_unsupported_action": "Ongeldige actie {action}",
"message_service_bot_intro_header": "Wat kan deze bot? ",
"message_service_converted_to_supergroup": "heeft de groep opgewaardeerd naar een supergroep",
@ -364,7 +364,7 @@ @@ -364,7 +364,7 @@
"head_clear_all": "Alles wissen",
"head_edit": "Wijzig",
"head_typing": "aan het typen",
"head_pluralize_participants": "{'0': 'Geen deelnemers', 'one': '1 deelnemer', 'other': '{} deelnemers'}",
"head_pluralize_participants": "{'0': 'Geen leden', 'one': '1 lid', 'other': '{} leden'}",
"head_one_typing": "{name1} is aan het typen{dots}",
"head_two_typing": "{name1}, {name2}{dots}",
"head_many_typing": "{name1}+{names}{dots}",
@ -396,7 +396,7 @@ @@ -396,7 +396,7 @@
"im_media_video": "Video's",
"im_media_documents": "Bestanden",
"im_media_audio": "Spraakberichten",
"im_pluralize_participants": "{'0': 'Geen deelnemers', 'one': '1 deelnemer', 'other': '{} deelnemers'}",
"im_pluralize_participants": "{'0': 'Geen leden', 'one': '1 lid', 'other': '{} leden'}",
"im_show_recent_messages": "Recente berichten laten zien",
"im_show_all_messages": "Laat alle berichten zien",
"im_no_messages": "Nog geen berichten",
@ -411,7 +411,7 @@ @@ -411,7 +411,7 @@
"im_forward": "Doorsturen {count}",
"im_reply": "Antwoord",
"im_start": "Begin",
"im_channel_join": "Deelnemen",
"im_channel_join": "Lid worden",
"im_channel_mute": "Geluid uit",
"im_channel_unmute": "Stil uitschakelen",
"im_reply_loading": "Laden{dots}",

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

@ -266,7 +266,7 @@ @@ -266,7 +266,7 @@
"conversation_invited_user": "convidou {user}",
"conversation_invited_users": "{'one': 'Convidou {} usuário', 'other': 'Convidou {} usuários'}",
"conversation_left_group": "saiu do grupo",
"conversation_kicked_user": "{user} removido",
"conversation_kicked_user": "removeu {user}",
"conversation_invited_user_message": "usuário convidado",
"conversation_kicked_user_message": "usuário removido",
"conversation_joined_by_link": "entrou no grupo",
@ -286,7 +286,7 @@ @@ -286,7 +286,7 @@
"message_service_invited_user": "convidou {user}",
"message_service_invited_users": "Convidou {user} e mais {num-more}",
"message_service_returned_to_group": "retornou ao grupo",
"message_service_kicked_user": "{user} removido",
"message_service_kicked_user": "removeu {user}",
"message_service_left_group": "saiu do grupo",
"message_service_joined_by_link": "entrou para o grupo via link de convite",
"message_service_unsupported_action": "ação sem suporte {action}",

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

@ -271,10 +271,10 @@ @@ -271,10 +271,10 @@
"conversation_kicked_user_message": "исключённый пользователь",
"conversation_joined_by_link": "присоединился к группе",
"conversation_converted_to_supergroup": "upgraded to a supergroup",
"conversation_created_channel": "Channel created",
"conversation_changed_channel_name": "Channel renamed",
"conversation_changed_channel_photo": "Channel photo updated",
"conversation_removed_channel_photo": "Channel photo removed",
"conversation_created_channel": "Канал создан",
"conversation_changed_channel_name": "Канал переименован",
"conversation_changed_channel_photo": "Фото канала изменено",
"conversation_removed_channel_photo": "Фото канала удалено",
"conversation_message_sent": "прислал(а) вам сообщение",
"conversation_forwarded_X_messages": "{'одно': 'отправленное {} сообщение', 'другие': 'отправленные {} сообщения'}",
"conversation_unknown_user": "Кто-то",
@ -292,10 +292,10 @@ @@ -292,10 +292,10 @@
"message_service_unsupported_action": "неподдерживаемое действие {action}",
"message_service_bot_intro_header": "Что этот бот может делать?",
"message_service_converted_to_supergroup": "upgraded the group to a supergroup",
"message_service_created_channel": "Channel created",
"message_service_created_channel": "Канал создан",
"message_service_changed_channel_name": "Channel renamed to {channel-name}",
"message_service_changed_channel_photo": "Channel photo updated",
"message_service_removed_channel_photo": "Channel photo removed",
"message_service_changed_channel_photo": "Фото канала изменено",
"message_service_removed_channel_photo": "Фото канала удалено",
"message_action_reply": "Ответить",
"message_action_delete": "Удалить",
"message_action_forward": "Переслать",

14
app/js/message_composer.js

@ -585,7 +585,7 @@ EmojiTooltip.prototype.activateStickerCategory = function () { @@ -585,7 +585,7 @@ EmojiTooltip.prototype.activateStickerCategory = function () {
var viewportWidth = categoriesEl.clientWidth;
// console.log('current cat el', categoryEl, left, width, viewportWidth);
$(categoriesEl).animate({scrollLeft: left - (viewportWidth - width) / 2}, 200);
$(categoriesEl).stop(true).animate({scrollLeft: left - (viewportWidth - width) / 2}, 200);
}
@ -978,7 +978,6 @@ MessageComposer.prototype.checkAutocomplete = function (forceFull) { @@ -978,7 +978,6 @@ MessageComposer.prototype.checkAutocomplete = function (forceFull) {
if (value &&
this.curInlineResults &&
this.curInlineResults.text == value) {
console.trace(dT(), value, this.curInlineResults);
this.showInlineSuggestions(this.curInlineResults);
return;
};
@ -1335,7 +1334,6 @@ MessageComposer.prototype.onChange = function (e) { @@ -1335,7 +1334,6 @@ MessageComposer.prototype.onChange = function (e) {
if (this.richTextareaEl) {
delete this.keyupStarted;
var richValue = getRichValue(this.richTextareaEl[0]);
richValue = richValue.replace(/\u00A0/g, ' ');
this.textareaEl.val(richValue).trigger('change');
}
this.updateInlinePlaceholder();
@ -1390,13 +1388,18 @@ MessageComposer.prototype.setFocusedValue = function (parts) { @@ -1390,13 +1388,18 @@ MessageComposer.prototype.setFocusedValue = function (parts) {
MessageComposer.prototype.getRichHtml = function (text) {
return $('<div>').text(text).html().replace(/\n/g, '<br/>').replace(/:([A-Za-z0-9\-\+\*_]+?):/gi, (function (all, shortcut) {
var html = $('<div>').text(text).html();
html = html.replace(/\n/g, '<br/>');
html = html.replace(/:([A-Za-z0-9\-\+\*_]+?):/gi, (function (all, shortcut) {
var code = EmojiHelper.shortcuts[shortcut];
if (code !== undefined) {
return this.getEmojiHtml(code);
}
return all;
}).bind(this));
html = html.replace(/ /g, " \u00A0").replace(/^ | $/g, "\u00A0");
return html;
}
@ -1471,7 +1474,8 @@ MessageComposer.prototype.showInlineSuggestions = function (botResults) { @@ -1471,7 +1474,8 @@ MessageComposer.prototype.showInlineSuggestions = function (botResults) {
}
var self = this;
if (self.autoCompleteScope.type == 'inline' &&
self.autoCompleteScope.botResults == botResults) {
self.autoCompleteScope.botResults == botResults &&
self.autocompleteShown) {
return;
}
setZeroTimeout(function () {

201
app/js/messages_manager.js

@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
angular.module('myApp.services')
.service('AppMessagesManager', function ($q, $rootScope, $location, $filter, $timeout, $sce, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, AppStickersManager, AppAudioManager, AppWebPagesManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, Storage, AppProfileManager, TelegramMeWebService, ErrorService, StatusManager, _) {
.service('AppMessagesManager', function ($q, $rootScope, $location, $filter, $timeout, $sce, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppDocsManager, AppStickersManager, AppWebPagesManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, Storage, AppProfileManager, TelegramMeWebService, ErrorService, StatusManager, _) {
var messagesStorage = {};
var messagesForHistory = {};
@ -260,7 +260,9 @@ angular.module('myApp.services') @@ -260,7 +260,9 @@ angular.module('myApp.services')
dialog.top_message > maxSeenID
) {
var notifyPeer = message.flags & 16 ? message.from_id : peerID;
if (message.pFlags.unread && !message.pFlags.out) {
if (message.pFlags.unread &&
!message.pFlags.out &&
!message.pFlags.silent) {
NotificationsManager.getPeerMuted(notifyPeer).then(function (muted) {
if (!muted) {
notifyAboutMessage(message);
@ -719,6 +721,10 @@ angular.module('myApp.services') @@ -719,6 +721,10 @@ angular.module('myApp.services')
!message.action) {
return false;
}
if (message.reply_markup &&
message.reply_markup._ == 'replyInlineMarkup') {
return false;
}
var messageReplyMarkup = message.reply_markup;
var lastReplyMarkup = historyStorage.reply_markup;
if (messageReplyMarkup) {
@ -797,6 +803,7 @@ angular.module('myApp.services') @@ -797,6 +803,7 @@ angular.module('myApp.services')
if (historyStorage !== undefined && historyStorage.history.length) {
var neededContents = {},
neededDocType,
neededLimit = limit || 20,
i, message;
@ -805,26 +812,35 @@ angular.module('myApp.services') @@ -805,26 +812,35 @@ angular.module('myApp.services')
neededContents['messageMediaPhoto'] = true;
break;
case 'inputMessagesFilterVideo':
neededContents['messageMediaVideo'] = true;
break;
case 'inputMessagesFilterPhotoVideo':
neededContents['messageMediaPhoto'] = true;
neededContents['messageMediaVideo'] = true;
neededContents['messageMediaDocument'] = true;
neededDocType = 'video';
break;
case 'inputMessagesFilterVideo':
neededContents['messageMediaDocument'] = true;
neededDocType = 'video';
break;
case 'inputMessagesFilterDocument':
neededContents['messageMediaDocument'] = true;
neededDocType = false;
break;
case 'inputMessagesFilterAudio':
neededContents['messageMediaAudio'] = true;
case 'inputMessagesFilterVoice':
neededContents['messageMediaDocument'] = true;
neededDocType = 'voice';
break;
}
for (i = 0; i < historyStorage.history.length; i++) {
message = messagesStorage[historyStorage.history[i]];
if (message.media && neededContents[message.media._]) {
if (neededDocType !== undefined &&
message.media._ == 'messageMediaDocument' &&
message.media.document.type != neededDocType) {
continue;
}
foundMsgs.push(message.mid);
if (foundMsgs.length >= neededLimit) {
break;
@ -1138,7 +1154,7 @@ angular.module('myApp.services') @@ -1138,7 +1154,7 @@ angular.module('myApp.services')
});
}
function saveMessages (apiMessages) {
function saveMessages (apiMessages, edited) {
angular.forEach(apiMessages, function (apiMessage) {
if (apiMessage.pFlags === undefined) {
apiMessage.pFlags = {};
@ -1160,13 +1176,10 @@ angular.module('myApp.services') @@ -1160,13 +1176,10 @@ angular.module('myApp.services')
var mid = getFullMessageID(apiMessage.id, channelID);
apiMessage.mid = mid;
messagesStorage[mid] = apiMessage;
if (channelID && !apiMessage.pFlags.out) {
var dialog = getDialogByPeerID(toPeerID)[0];
apiMessage.pFlags.unread = dialog ? mid > dialog.read_inbox_max_id : true;
} else {
apiMessage.pFlags.unread = apiMessage.flags & 1 ? true : false;
}
if (apiMessage.reply_to_msg_id) {
@ -1174,14 +1187,16 @@ angular.module('myApp.services') @@ -1174,14 +1187,16 @@ angular.module('myApp.services')
}
apiMessage.date -= serverTimeOffset;
if (apiMessage.fwd_date) {
apiMessage.fwd_date -= serverTimeOffset;
var fwdHeader = apiMessage.fwd_from;
if (fwdHeader) {
apiMessage.fwdFromID = fwdHeader.from_id ? fwdHeader.from_id : -fwdHeader.channel_id;
fwdHeader.date -= serverTimeOffset;
}
apiMessage.toID = toPeerID;
apiMessage.fromID = apiMessage.from_id || toPeerID;
if (apiMessage.fwd_from_id) {
apiMessage.fwdFromID = AppPeersManager.getPeerID(apiMessage.fwd_from_id);
}
if (apiMessage.via_bot_id > 0) {
apiMessage.viaBotID = apiMessage.via_bot_id;
}
@ -1199,15 +1214,9 @@ angular.module('myApp.services') @@ -1199,15 +1214,9 @@ angular.module('myApp.services')
case 'messageMediaPhoto':
AppPhotosManager.savePhoto(apiMessage.media.photo, mediaContext);
break;
case 'messageMediaVideo':
AppVideoManager.saveVideo(apiMessage.media.video, mediaContext);
break;
case 'messageMediaDocument':
AppDocsManager.saveDoc(apiMessage.media.document, mediaContext);
break;
case 'messageMediaAudio':
AppAudioManager.saveAudio(apiMessage.media.audio);
break;
case 'messageMediaWebPage':
AppWebPagesManager.saveWebPage(apiMessage.media.webpage, apiMessage.mid, mediaContext);
break;
@ -1280,6 +1289,10 @@ angular.module('myApp.services') @@ -1280,6 +1289,10 @@ angular.module('myApp.services')
var apiEntities = apiMessage.entities || [];
apiMessage.totalEntities = RichTextProcessor.mergeEntities(myEntities, apiEntities, !apiMessage.pending);
}
if (!edited) {
messagesStorage[mid] = apiMessage;
}
});
}
@ -1338,6 +1351,7 @@ angular.module('myApp.services') @@ -1338,6 +1351,7 @@ angular.module('myApp.services')
random_id: randomIDS,
reply_to_msg_id: replyToMsgID,
via_bot_id: options.viaBotID,
reply_markup: options.reply_markup,
entities: entities,
views: asChannel && 1,
pending: true
@ -1559,14 +1573,6 @@ angular.module('myApp.services') @@ -1559,14 +1573,6 @@ angular.module('myApp.services')
inputMedia = {_: 'inputMediaUploadedPhoto', file: inputFile};
break;
case 'video':
inputMedia = {_: 'inputMediaUploadedVideo', file: inputFile, duration: 0, w: 0, h: 0, mime_type: file.type};
break;
case 'audio':
inputMedia = {_: 'inputMediaUploadedAudio', file: inputFile, duration: 0, mime_type: file.type};
break;
case 'document':
default:
inputMedia = {_: 'inputMediaUploadedDocument', file: inputFile, mime_type: file.type, caption: '', attributes: [
@ -1653,10 +1659,6 @@ angular.module('myApp.services') @@ -1653,10 +1659,6 @@ angular.module('myApp.services')
var fromID = AppUsersManager.getSelf().id;
var media;
switch (inputMedia._) {
case 'inputMediaContact':
media = angular.extend({}, inputMedia, {_: 'messageMediaContact'});
break;
case 'inputMediaPhoto':
media = {
_: 'messageMediaPhoto',
@ -1677,6 +1679,42 @@ angular.module('myApp.services') @@ -1677,6 +1679,42 @@ angular.module('myApp.services')
};
break;
case 'inputMediaContact':
media = {
_: 'messageMediaContact',
phone_number: inputMedia.phone_number,
first_name: inputMedia.first_name,
last_name: inputMedia.last_name,
user_id: 0
};
break;
case 'inputMediaGeoPoint':
media = {
_: 'messageMediaGeo',
geo: {
_: 'geoPoint',
'lat': inputMedia.geo_point['lat'],
'long': inputMedia.geo_point['long']
}
};
break;
case 'inputMediaVenue':
media = {
_: 'messageMediaVenue',
geo: {
_: 'geoPoint',
'lat': inputMedia.geo_point['lat'],
'long': inputMedia.geo_point['long']
},
title: inputMedia.title,
address: inputMedia.address,
provider: inputMedia.provider,
venue_id: inputMedia.venue_id
};
break;
case 'messageMediaPending':
media = inputMedia;
break;
@ -1714,6 +1752,7 @@ angular.module('myApp.services') @@ -1714,6 +1752,7 @@ angular.module('myApp.services')
random_id: randomIDS,
reply_to_msg_id: replyToMsgID,
via_bot_id: options.viaBotID,
reply_markup: options.reply_markup,
views: asChannel && 1,
pending: true
};
@ -2093,18 +2132,10 @@ angular.module('myApp.services') @@ -2093,18 +2132,10 @@ angular.module('myApp.services')
message.media.photo = AppPhotosManager.wrapForHistory(message.media.photo.id);
break;
case 'messageMediaVideo':
message.media.video = AppVideoManager.wrapForHistory(message.media.video.id);
break;
case 'messageMediaDocument':
message.media.document = AppDocsManager.wrapForHistory(message.media.document.id);
break;
case 'messageMediaAudio':
message.media.audio = AppAudioManager.wrapForHistory(message.media.audio.id);
break;
case 'messageMediaGeo':
var mapUrl = 'https://maps.google.com/?q=' + message.media.geo['lat'] + ',' + message.media.geo['long'];
message.media.mapUrl = $sce.trustAsResourceUrl(mapUrl);
@ -2187,20 +2218,23 @@ angular.module('myApp.services') @@ -2187,20 +2218,23 @@ angular.module('myApp.services')
if (replyMarkup.wrapped) {
return replyMarkup;
}
var isInline = replyMarkup._ == 'replyInlineMarkup';
var count = replyMarkup.rows && replyMarkup.rows.length || 0;
if (count > 0 && count <= 4 && !replyMarkup.pFlags.resize) {
if (!isInline &&
count > 0 &&
count <= 4 &&
!(replyMarkup.pFlags && replyMarkup.pFlags.resize)) {
replyMarkup.splitCount = count;
}
replyMarkup.wrapped = true;
angular.forEach(replyMarkup.rows, function (markupRow) {
angular.forEach(markupRow.buttons, function (markupButton) {
markupButton.rText = RichTextProcessor.wrapRichText(markupButton.text, {noLinks: true, noLinebreaks: true});
if (markupButton._ == 'keyboardButtonUrl') {
markupButton.pUrl = RichTextProcessor.wrapUrl(markupButton.url, true);
}
})
})
if (nextRandomInt(1)) {
replyMarkup.rows = replyMarkup.rows.slice(0, 2);
}
});
return replyMarkup;
}
@ -2395,10 +2429,6 @@ angular.module('myApp.services') @@ -2395,10 +2429,6 @@ angular.module('myApp.services')
sticker = true;
}
break;
case 'messageMediaVideo':
thumbPhotoSize = message.media.video.thumb;
break;
}
}
@ -2453,11 +2483,8 @@ angular.module('myApp.services') @@ -2453,11 +2483,8 @@ angular.module('myApp.services')
case 'messageMediaPhoto':
notificationMessage = _('conversation_media_photo_raw');
break;
case 'messageMediaVideo':
notificationMessage = _('conversation_media_video_raw');
break;
case 'messageMediaDocument':
switch (message.media.document.isSpecial) {
switch (message.media.document.type) {
case 'gif':
notificationMessage = _('conversation_media_gif_raw');
break;
@ -2468,27 +2495,19 @@ angular.module('myApp.services') @@ -2468,27 +2495,19 @@ angular.module('myApp.services')
notificationMessage = RichTextProcessor.wrapPlainText(stickerEmoji) + ' ' + notificationMessage;
}
break;
case 'video':
notificationMessage = _('conversation_media_video_raw');
break;
case 'voice':
case 'audio':
notificationMessage = _('conversation_media_audio_raw');
break;
default:
notificationMessage = message.media.document.file_name || _('conversation_media_attachment_raw');
notificationMessage = message.media.document.file_name || _('conversation_media_document_raw');
break;
}
if (message.media.document.sticker) {
notificationMessage = _('conversation_media_sticker');
var stickerEmoji = message.media.document.stickerEmojiRaw;
if (stickerEmoji !== undefined) {
notificationMessage = RichTextProcessor.wrapPlainText(stickerEmoji) + ' (' + notificationMessage + ')';
}
} else {
notificationMessage = message.media.document.file_name || _('conversation_media_document_raw');
}
break;
case 'messageMediaAudio':
notificationMessage = _('conversation_media_audio_raw');
break;
case 'messageMediaGeo':
case 'messageMediaVenue':
notificationMessage = _('conversation_media_location_raw');
@ -2672,12 +2691,11 @@ angular.module('myApp.services') @@ -2672,12 +2691,11 @@ angular.module('myApp.services')
case 'updateNewChannelMessage':
var message = update.message,
peerID = getMessagePeer(message),
historyStorage = historiesStorage[peerID],
messageForMe = true;
historyStorage = historiesStorage[peerID];
if (update._ == 'updateNewChannelMessage') {
if (!AppChatsManager.isMegagroup(-peerID) &&
!(message.flags & 16 || message.flags & 2 || (message.flags & 256) == 0)) {
!(message.pFlags.out || message.pFlags.mention || message.pFlags.post)) {
// we don't support not important messages in channels yet
break;
}
@ -2765,7 +2783,9 @@ angular.module('myApp.services') @@ -2765,7 +2783,9 @@ angular.module('myApp.services')
newDialogsHandlePromise = $timeout(handleNewDialogs, 0);
}
if (inboxUnread && ($rootScope.selectedPeerID != peerID || $rootScope.idle.isIDLE)) {
if (inboxUnread &&
($rootScope.selectedPeerID != peerID || $rootScope.idle.isIDLE) &&
!message.pFlags.silent) {
var notifyPeer = message.flags & 16 ? message.from_id : peerID;
var notifyPeerToHandle = notificationsToHandle[notifyPeer];
@ -2795,6 +2815,34 @@ angular.module('myApp.services') @@ -2795,6 +2815,34 @@ angular.module('myApp.services')
incrementMaxSeenID(message.id);
break;
case 'updateEditMessage':
case 'updateEditChannelMessage':
var message = update.message;
var peerID = getMessagePeer(message);
var channelID = message.to_id._ == 'peerChannel' ? -peerID : 0;
var mid = getFullMessageID(message.id, channelID);
if (messagesStorage[mid] === undefined) {
break;
}
// console.trace(dT(), 'edit message', message);
saveMessages([message], true);
safeReplaceObject(messagesStorage[mid], message);
var wasForHistory = messagesForHistory[mid];
if (wasForHistory !== undefined) {
delete messagesForHistory[mid];
var newForHistory = wrapForHistory(mid);
safeReplaceObject(wasForHistory, newForHistory);
messagesForHistory[mid] = wasForHistory;
}
$rootScope.$broadcast('message_edit', {
peerID: peerID,
id: message.id,
mid: mid
});
break;
case 'updateReadHistoryInbox':
case 'updateReadHistoryOutbox':
case 'updateReadChannelInbox':
@ -3063,6 +3111,7 @@ angular.module('myApp.services') @@ -3063,6 +3111,7 @@ angular.module('myApp.services')
getHistory: getHistory,
getSearch: getSearch,
getMessage: getMessage,
getMessageLocalID: getMessageLocalID,
getReplyKeyboard: getReplyKeyboard,
readHistory: readHistory,
readMessages: readMessages,

913
app/js/services.js

File diff suppressed because it is too large Load Diff

100
app/less/app.less

@ -1292,18 +1292,22 @@ i.icon-verified { @@ -1292,18 +1292,22 @@ i.icon-verified {
overflow: hidden;
word-wrap: break-word;
}
.im_dialog_chat_from_wrap,
.im_dialog_message_media,
.im_dialog_message_service {
color: #3a6d99;
}
.im_dialog_message_text {
color: #808080;
a.im_dialog {
.im_dialog_chat_from_wrap,
.im_short_message_media,
.im_short_message_service {
color: #3a6d99;
}
.im_short_message_text {
color: #808080;
}
}
a.im_dialog:hover,
a.im_dialog_selected {
.im_dialog_message_text {
.im_short_message_text {
color: #698192;
}
}
@ -1311,9 +1315,9 @@ a.im_dialog_selected { @@ -1311,9 +1315,9 @@ a.im_dialog_selected {
.active {
a.im_dialog {
.im_dialog_chat_from_wrap,
.im_dialog_message_media,
.im_dialog_message_service,
.im_dialog_message_text,
.im_short_message_media,
.im_short_message_service,
.im_short_message_text,
.im_dialog_message {
color: #fff;
}
@ -1388,10 +1392,6 @@ a.im_dialog_selected { @@ -1388,10 +1392,6 @@ a.im_dialog_selected {
}
}
.im_dialog_service {
font-style: italic;
color: #999;
}
.im_dialog_message,
.im_dialog_peer {
overflow: hidden;
@ -1778,6 +1778,7 @@ div.im_message_video_thumb { @@ -1778,6 +1778,7 @@ div.im_message_video_thumb {
.im_history_select_active {
a,
button,
.clickable {
pointer-events: none;
}
@ -2060,6 +2061,16 @@ img.im_message_document_thumb { @@ -2060,6 +2061,16 @@ img.im_message_document_thumb {
display: inline-block;
margin: 0 auto;
}
.im_service_message .im_service_message_pinned {
color: inherit;
font-weight: bold;
display: inline-block;
max-width: 150px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
}
.im_message_date,
.im_message_fwd_date {
@ -2186,8 +2197,8 @@ div.im_message_body { @@ -2186,8 +2197,8 @@ div.im_message_body {
.im_message_reply_loading {
padding: 7px 0 8px;
}
.im_reply_message_service,
.im_reply_message_media {
.im_message_reply_body .im_short_message_service,
.im_message_reply_body .im_short_message_media {
color: #999;
}
.im_message_reply_body {
@ -2245,6 +2256,10 @@ a.im_message_fwd_photo { @@ -2245,6 +2256,10 @@ a.im_message_fwd_photo {
.reply_markup_wrap {
margin: 15px -2px 0;
.im_message_keyboard & {
margin-top: 7px;
}
}
.reply_markup_row {
padding: 4px 0;
@ -2286,6 +2301,7 @@ a.im_message_fwd_photo { @@ -2286,6 +2301,7 @@ a.im_message_fwd_photo {
height: 36px;
}
}
.reply_markup_button:focus,
.reply_markup_button:hover {
color: #3a6d99;
background: #dfe8f0;
@ -2348,7 +2364,7 @@ a.im_message_fwd_photo { @@ -2348,7 +2364,7 @@ a.im_message_fwd_photo {
}
}
.im_history_loading {
width: 60px;
width: 35px;
margin: 0 auto;
visibility: hidden;
@ -2427,11 +2443,16 @@ textarea.im_message_field { @@ -2427,11 +2443,16 @@ textarea.im_message_field {
resize: none;
}
.im_inline_placeholder_wrap {
color: #9aa2ab;
position: absolute;
margin-top: 2px;
white-space: nowrap;
pointer-events: none;
text-overflow: ellipsis;
display: none;
width: 100%;
width: ~"calc(100% - 30px)";
overflow: hidden;
}
.im_inline_placeholder_wrap.active {
display: block;
@ -2439,9 +2460,6 @@ textarea.im_message_field { @@ -2439,9 +2460,6 @@ textarea.im_message_field {
.im_inline_placeholder_prefix {
visibility: hidden;
}
.im_inline_placeholder {
color: #9aa2ab;
}
.icon-online {
background: #6ec26d;
@ -3215,6 +3233,21 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before { @@ -3215,6 +3233,21 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before {
}
}
.inline_switch_pm {
text-decoration: none !important;
display: block;
font-size: 13px;
font-weight: bold;
line-height: 15px;
padding: 10px 10px;
text-align: center;
color: #52719a;
&:hover {
background: #f2f6fa;
color: #52719a;
}
}
.inline_results_wrap {
line-height: 0;
}
@ -3222,7 +3255,8 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before { @@ -3222,7 +3255,8 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before {
display: block;
}
.inline_result_gif,
.inline_result_photo {
.inline_result_photo,
.inline_result_sticker {
display: inline-block;
padding: 1px;
}
@ -3241,6 +3275,14 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before { @@ -3241,6 +3275,14 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before {
max-height: 50px;
line-height: 0;
}
.inline_result_video .inline_article_thumb_wrap {
width: 90px;
height: 50px;
}
.inline_result_video .inline_article_thumb {
max-width: 90px;
max-height: 50px;
}
.inline_article_thumb_initials {
color: #999;
background: #EEE;
@ -3268,25 +3310,32 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before { @@ -3268,25 +3310,32 @@ _:-ms-lang(x), .composer_rich_textarea:empty:focus:before {
.inline_article_title {
color: #222;
font-weight: bold;
font-size: 12px;
}
.inline_article_description {
color: #808080;
padding-top: 4px;
font-size: 12px;
li a:hover &,
li.composer_autocomplete_option_active a & {
color: #698192;
}
}
.inline_article_url {
padding-top: 4px;
}
.composer_dropdown > li.inline_result_gif > a,
.composer_dropdown > li.inline_result_photo > a {
.composer_dropdown > li.inline_result_photo > a,
.composer_dropdown > li.inline_result_sticker > a {
padding: 0;
line-height: 0;
display: block;
overflow: hidden;
}
li.inline_result_gif.composer_autocomplete_option_active a,
li.inline_result_photo.composer_autocomplete_option_active a {
li.inline_result_photo.composer_autocomplete_option_active a,
li.inline_result_sticker.composer_autocomplete_option_active a {
position: relative;
.inline_result_ind {
@ -3301,6 +3350,9 @@ li.inline_result_photo.composer_autocomplete_option_active a { @@ -3301,6 +3350,9 @@ li.inline_result_photo.composer_autocomplete_option_active a {
.inline_result_photo .inline_result_photo_image {
object-fit: cover;
}
.inline_result_sticker img {
object-fit: contain;
}
.inline_result_gif_mtproto,
.inline_result_gif_http,
.inline_result_photo_mtproto,

3
app/partials/desktop/confirm_modal.html

@ -63,6 +63,9 @@ @@ -63,6 +63,9 @@
<my-i18n-param name="url"><strong ng-bind="url"></strong></my-i18n-param>
</div>
<div ng-switch-when="SUPERGROUP_MIGRATE" my-i18n="confirm_modal_migrate_supergroup_md"></div>
<div ng-switch-when="BOT_ACCESS_PHONE" my-i18n="confirm_modal_bot_access_phone"></div>
<div ng-switch-when="BOT_ACCESS_GEO" my-i18n="confirm_modal_bot_access_geo"></div>
<div ng-switch-when="BOT_ACCESS_GEO_INLINE" my-i18n="confirm_modal_bot_access_geo_inline"></div>

24
app/partials/desktop/dialog.html

@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
</div>
<div ng-if="dialogMessage.typing > 0" class="im_dialog_message">
<span class="im_dialog_message_service" my-i18n="im_conversation_group_typing">
<span class="im_short_message_service" my-i18n="im_conversation_group_typing">
<my-i18n-param name="name"><span my-peer-link="dialogMessage.typing" short="true" class="im_dialog_chat_from_wrap"></span></my-i18n-param><my-i18n-param name="dots"><span my-loading-dots></span></my-i18n-param>
</span>
</div>
@ -57,27 +57,7 @@ @@ -57,27 +57,7 @@
</span>
</span>
<span class="im_dialog_message_media" ng-if="dialogMessage.media" ng-switch="dialogMessage.media._">
<span ng-switch-when="messageMediaPhoto" my-i18n="conversation_media_photo"></span>
<span ng-switch-when="messageMediaVideo" my-i18n="conversation_media_video"></span>
<span ng-switch-when="messageMediaDocument" ng-switch="dialogMessage.media.document.isSpecial || false">
<span ng-switch-when="sticker">
<span ng-bind-html="dialogMessage.media.document.stickerEmoji"></span>
<my-i18n msgid="conversation_media_sticker"></my-i18n>
</span>
<span ng-switch-when="gif" my-i18n="conversation_media_gif"></span>
<span ng-switch-when="audio" my-i18n="conversation_media_audio"></span>
<span ng-switch-default ng-bind="dialogMessage.media.document.file_name"></span>
</span>
<span ng-switch-when="messageMediaAudio" my-i18n="conversation_media_audio"></span>
<span ng-switch-when="messageMediaGeo" my-i18n="conversation_media_location"></span>
<span ng-switch-when="messageMediaVenue" my-i18n="conversation_media_location"></span>
<span ng-switch-when="messageMediaContact" my-i18n="conversation_media_contact"></span>
</span>
<span class="im_dialog_message_service" ng-if="dialogMessage._ == 'messageService'" my-service-short-message="dialogMessage"></span>
<span class="im_dialog_message_text" ng-if="dialogMessage.message.length" ng-bind-html="dialogMessage.richMessage"></span>
<span my-short-message="dialogMessage"></span>
</div>
</div>

21
app/partials/desktop/forwarded_messages.html

@ -13,26 +13,7 @@ @@ -13,26 +13,7 @@
</div>
<div class="im_message_reply_body" ng-switch="singleMessage !== false">
<div ng-switch-when="true">
<span class="im_reply_message_media" ng-if="singleMessage.media" ng-switch="singleMessage.media._">
<span ng-switch-when="messageMediaPhoto" my-i18n="conversation_media_photo"></span>
<span ng-switch-when="messageMediaVideo" my-i18n="conversation_media_video"></span>
<span ng-switch-when="messageMediaDocument" ng-switch="::singleMessage.media.document.sticker || false">
<span ng-switch-when="1" my-i18n="conversation_media_sticker"></span>
<span ng-switch-when="2">
<span ng-bind-html="singleMessage.media.document.stickerEmoji"></span>
(<my-i18n msgid="conversation_media_sticker"></my-i18n>)
</span>
<span ng-switch-default ng-bind="singleMessage.media.document.file_name"></span>
</span>
<span ng-switch-when="messageMediaAudio" my-i18n="conversation_media_audio"></span>
<span ng-switch-when="messageMediaGeo" my-i18n="conversation_media_location"></span>
<span ng-switch-when="messageMediaVenue" my-i18n="conversation_media_location"></span>
<span ng-switch-when="messageMediaContact" my-i18n="conversation_media_contact"></span>
</span>
<span class="im_reply_message_service" ng-if="singleMessage._ == 'messageService'" my-service-short-message="singleMessage"></span>
<span class="im_reply_message_text" ng-if="singleMessage.message.length" ng-bind-html="singleMessage.richMessage"></span>
<span my-short-message="singleMessage"></span>
</div>
<div ng-switch-default>
<span class="im_reply_message_service">

4
app/partials/desktop/im.html

@ -106,7 +106,7 @@ @@ -106,7 +106,7 @@
<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.3" padding="true">
<div my-arc-progress stroke="5" width="50"></div>
<div my-arc-progress stroke="4" width="32"></div>
</div>
<div ng-switch-default class="im_history_empty" my-vertical-position="0.25" padding="true" my-i18n="im_no_messages"></div>
</div>
@ -247,3 +247,5 @@ @@ -247,3 +247,5 @@
</div>
<div class="footer_wrap footer_empty"></div>
<toaster-container toaster-options="{'position-class': 'toast-bottom-center'}"></toaster-container>

24
app/partials/desktop/inline_results.html

@ -1,9 +1,11 @@ @@ -1,9 +1,11 @@
<a ng-if="botResults.switch_pm !== undefined" class="inline_switch_pm" ng-bind-html="botResults.switch_pm.rText" data-inlineid="_switch_pm_{{botResults.switch_pm.start_param}}"></a>
<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 class="inline_result_ind"></div>
<div ng-switch-when="botInlineMediaResultDocument" ng-switch="result.document.url !== undefined" class="inline_result_gif_mtproto">
<div ng-switch-when="botInlineMediaResult" 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">
@ -25,9 +27,14 @@ @@ -25,9 +27,14 @@
</div>
</a>
<a ng-switch-when="sticker" data-inlineid="{{result.qID}}" ng-style="::{width: result.thumbW, height: result.thumbH}">
<div class="inline_result_ind"></div>
<div class="inline_result_sticker_image" my-load-sticker document="result.document" dim="{width: result.thumbW, height: result.thumbH}"></div>
</a>
<a ng-switch-when="photo" data-inlineid="{{result.qID}}" ng-style="::{width: result.thumbW, height: result.thumbH}" ng-switch="result._">
<div class="inline_result_ind"></div>
<div ng-switch-when="botInlineMediaResultPhoto" class="inline_result_photo_mtproto">
<div ng-switch-when="botInlineMediaResult" class="inline_result_photo_mtproto">
<img
class="inline_result_photo_image"
my-load-thumb
@ -35,14 +42,21 @@ @@ -35,14 +42,21 @@
ng-style="::{width: result.thumbW, height: result.thumbH}"
/>
</div>
<div ng-switch-default ng-switch="result.contentUrl !== undefined" class="inline_result_photo_http">
<div ng-switch-default 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 class="inline_article_thumb_wrap pull-left" ng-switch="result.thumbUrl !== undefined ? 'thumb' : (result.send_message.geo ? 'geo' : false)">
<img ng-switch-when="thumb" class="inline_article_thumb" ng-src="{{result.thumbUrl}}"/>
<img
ng-switch-when="geo"
class="inline_article_thumb"
my-geo-point-map="result.send_message.geo"
width="50"
height="50"
/>
<div ng-switch-default class="inline_article_thumb_initials" ng-bind="result.initials"></div>
</div>
<div class="inline_article_content_wrap">

42
app/partials/desktop/login.html

@ -56,26 +56,32 @@ @@ -56,26 +56,32 @@
<form name="myLoginForm" ng-if="credentials.phone_code_hash &amp;&amp; !credentials.phone_code_valid" ng-submit="logIn()">
<h3 class="login_phone_head"><span ng-bind="credentials.phone_country"></span> <span ng-bind="credentials.phone_number"></span></h3>
<div class="login_edit_phone"><a ng-click="editPhone()" my-i18n="login_edit_number"></a></div>
<div ng-switch="credentials.viaApp">
<div ng-switch-when="true">
<p class="login_smscode_lead" my-i18n="login_enter_code_label_md"></p>
<p class="login_smscode_lead">
<a ng-click="sendSms()" my-i18n="login_code_not_received"></a>
</p>
</div>
<div ng-switch-default>
<p class="login_smscode_lead" my-i18n="login_enter_sms_code_label_md"></p>
<p class="login_smscode_lead">
<span ng-show="callPending.remaining > 0" my-i18n="login_call_remaining">
<my-i18n-param name="remaining">{{callPending.remaining | duration}}</my-i18n-param>
</span>
<span ng-show="!callPending.remaining &amp;&amp; !callPending.success" my-i18n="login_calling"></span>
<span ng-show="!callPending.remaining &amp;&amp; callPending.success" my-i18n="login_number_dialed"></span>
</p>
</div>
<div ng-switch="credentials.type._">
<p ng-switch-when="auth.sentCodeTypeApp" class="login_smscode_lead" my-i18n="login_enter_code_label_md"></p>
<p ng-switch-default class="login_smscode_lead" my-i18n="login_enter_sms_code_label_md"></p>
</div>
<div ng-if="nextPending.type" ng-switch="nextPending.remaining === false">
<p ng-switch-when="true" class="login_smscode_lead">
<a ng-click="sendNext()" my-i18n="login_code_not_received"></a>
</p>
<p ng-switch-default class="login_smscode_lead">
<span ng-show="nextPending.remaining > 0" my-i18n="login_call_remaining">
<my-i18n-param name="remaining" ng-bind="nextPending.remaining | duration"></my-i18n-param>
</span>
<span ng-show="!nextPending.remaining" my-i18n="login_calling"></span>
</p>
</div>
<div ng-if="credentials.type._ == 'auth.sentCodeTypeCall'">
<p class="login_smscode_lead">
<span my-i18n="login_number_dialed"></span>
</p>
</div>
<div class="md-input-group md-input-group-centered" ng-class="{'md-input-error': error.field == 'phone_code'}" my-labeled-input ng-switch="error.field == 'phone_code'">
<label ng-switch-when="true" class="md-input-label" my-i18n="login_incorrect_sms_code"></label>
<label ng-switch-default class="md-input-label" my-i18n="login_number_input_placeholder"></label>

25
app/partials/desktop/message.html

@ -55,32 +55,17 @@ @@ -55,32 +55,17 @@
<span class="copyonly"><span my-i18n="message_forwarded_message"></span>:&nbsp;</span>
<a class="im_message_fwd_photo pull-left" my-peer-photolink="::historyMessage.fwdFromID" img-class="im_message_fwd_photo"></a>
<div class="im_message_fwd_author_wrap">
<a class="im_message_fwd_author" my-peer-link="historyMessage.fwdFromID"></a><a ng-if="::historyMessage.viaBotID" class="im_message_fwd_via" ng-click="selectInlineBot(historyMessage.viaBotID, $event)"><span class="copyonly">&nbsp;</span><span my-i18n="message_via_bot"><my-i18n-param name="bot"><span class="im_message_fwd_author" my-peer-link="historyMessage.viaBotID" username="true" no-watch="true"></span></my-i18n-param></span></a><span class="copyonly">&nbsp;[</span><span class="im_message_fwd_date" ng-bind="::historyMessage.fwd_date | dateOrTime"></span><span class="copyonly">]&nbsp;</span>
<a class="im_message_fwd_author" my-peer-link="historyMessage.fwdFromID"></a><a ng-if="::historyMessage.viaBotID" class="im_message_fwd_via" ng-click="selectInlineBot(historyMessage.viaBotID, $event)"><span class="copyonly">&nbsp;</span><span my-i18n="message_via_bot"><my-i18n-param name="bot"><span class="im_message_fwd_author" my-peer-link="historyMessage.viaBotID" username="true" no-watch="true"></span></my-i18n-param></span></a><span class="copyonly">&nbsp;[</span><span class="im_message_fwd_date" ng-bind="::historyMessage.fwd_from.date | dateOrTime"></span><span class="copyonly">]&nbsp;</span>
<span class="im_message_views_inline" ng-if="::historyMessage.views > 0">
<i class="icon-message-views"></i><span class="im_message_views_cnt" my-message-views="historyMessage.mid"></span>
</span>
</div>
</div>
<div class="im_message_text" ng-if="::historyMessage.message.length || false" my-message-text="::historyMessage" dir="auto"></div>
<div ng-if="::historyMessage.media || historyMessage.mid < 0 ? true : false" class="im_message_media" ng-switch="historyMessage.media._">
<div ng-switch-when="messageMediaPhoto" my-message-photo="historyMessage.media" message-id="historyMessage.mid"></div>
<div ng-switch-when="messageMediaVideo" my-message-video="historyMessage.media" message-id="historyMessage.mid"></div>
<div ng-switch-when="messageMediaDocument" my-message-document="historyMessage.media" message-id="historyMessage.mid"></div>
<div ng-switch-when="messageMediaAudio" class="im_message_audio" my-audio-player audio="historyMessage.media.audio" message="historyMessage"></div>
<div ng-switch-when="messageMediaGeo" my-message-geo="historyMessage.media"></div>
<div ng-switch-when="messageMediaVenue" my-message-venue="historyMessage.media"></div>
<div ng-switch-when="messageMediaContact" class="im_message_contact" my-message-contact></div>
<div ng-switch-when="messageMediaWebPage" class="im_message_webpage" my-message-webpage="historyMessage.media.webpage" message-id="historyMessage.mid"></div>
<div ng-switch-when="messageMediaPending" my-message-pending></div>
<div ng-switch-when="messageMediaUnsupported">
<div class="im_message_text">
The message is not supported on your version of Telegram Web. Update the app to view: <a href="https://web.telegram.org">web.telegram.org</a>.
</div>
</div>
<div my-message-body="historyMessage">
<div class="im_message_text" dir="auto"></div>
<div class="im_message_media"></div>
<div class="im_message_keyboard"></div>
</div>

12
app/partials/desktop/message_attach_contact.html

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
<div>
<a ng-if="historyMessage.media.user_id > 0" class="im_message_contact_photo pull-left" my-peer-photolink="historyMessage.media.user_id" img-class="im_message_contact_photo" user-override="historyMessage.media"></a>
<div class="im_message_contact_name" ng-switch="historyMessage.media.user_id > 0">
<a ng-switch-when="true" my-peer-link="historyMessage.media.user_id" user-override="historyMessage.media"></a>
<span ng-switch-default ng-bind-html="::historyMessage.media.rFullName"></span>
<div class="im_message_contact">
<a ng-if="::media.user_id > 0" class="im_message_contact_photo pull-left" my-peer-photolink="media.user_id" img-class="im_message_contact_photo" user-override="media"></a>
<div class="im_message_contact_name" ng-switch="media.user_id > 0">
<a ng-switch-when="true" my-peer-link="media.user_id" user-override="media"></a>
<span ng-switch-default ng-bind-html="::media.rFullName"></span>
</div>
<div class="im_message_contact_phone" ng-bind="::historyMessage.media.phone_number | phoneNumber"></div>
<div class="im_message_contact_phone" ng-bind="::media.phone_number | phoneNumber"></div>
</div>

41
app/partials/desktop/message_attach_document.html

@ -1,13 +1,52 @@ @@ -1,13 +1,52 @@
<div ng-switch="::media.document.isSpecial">
<div ng-switch="::media.document.type">
<div ng-switch-when="gif" my-load-gif document="media.document"></div>
<div ng-switch-when="sticker" my-load-sticker document="media.document" open="true"></div>
<div ng-switch-when="voice" class="im_message_audio">
<div my-audio-player audio="media.document"></div>
</div>
<div ng-switch-when="audio" class="im_message_audio">
<div my-audio-player audio="media.document"></div>
</div>
<div ng-switch-when="video" class="im_message_video im_message_document_thumbed">
<a class="im_message_video_thumb" ng-click="videoOpen()" ng-style="::{width: media.document.thumb.width + 'px'}">
<span class="im_message_video_duration nocopy" data-content="{{::media.document.duration | duration}}"></span>
<i class="icon icon-videoplay"></i>
<img
class="im_message_video_thumb im_message_video_thumb_blurred"
my-load-thumb
thumb="media.document.thumb"
/>
</a>
<div class="im_message_document_info">
<div class="im_message_document_name_wrap">
<span class="copyonly">[</span><span class="im_message_document_name" my-i18n="message_attach_video_video"></span><span class="copyonly">&nbsp;<span ng-bind="::media.document.duration | duration"></span>]</span>
<span class="im_message_document_size" ng-if="!media.document.progress.enabled" ng-bind="::media.document.size | formatSize"></span>
<span class="im_message_document_size" ng-if="media.document.progress.enabled" ng-bind="media.document.progress | formatSizeProgress"></span>
</div>
<div class="im_message_document_actions noselect" ng-if="!media.document.progress.enabled">
<a href="" ng-click="docSave()" ng-switch="media.document.downloaded">
<span class="nocopy" ng-switch-when="true" my-i18n="message_attach_video_save"></span>
<span class="nocopy" ng-switch-default my-i18n="message_attach_video_download"></span>
</a>
<a class="nocopy" href="" ng-click="videoOpen()" my-i18n="message_attach_video_play"></a>
</div>
<div class="clearfix im_message_cancelable_progress_wrap" ng-if="media.document.progress.enabled">
<a class="im_message_media_progress_cancel pull-right nocopy" ng-click="media.document.progress.cancel()" my-i18n="modal_cancel"></a>
<div class="im_message_download_progress_wrap">
<div class="progress tg_down_progress">
<div class="progress-bar progress-bar-success" ng-style="{width: media.document.progress.percent + '%'}"></div>
</div>
</div>
</div>
</div>
</div>
<div ng-switch-default class="im_message_document clearfix" ng-class="{im_message_document_thumbed: !!media.document.thumb, im_message_document_progress: media.document.progress.enabled}">
<a ng-if="::!media.document.thumb" class="im_message_file_button" ng-click="docOpen()" ng-class="{im_message_file_button_dl_doc: media.document.downloaded}">

10
app/partials/desktop/message_attach_pending.html

@ -1,17 +1,17 @@ @@ -1,17 +1,17 @@
<div class="im_message_document im_message_upload_file" ng-class="::'im_message_upload_' + historyMessage.media.type">
<div class="im_message_document im_message_upload_file" ng-class="::'im_message_upload_' + media.type">
<div class="im_message_file_button im_message_file_button_upload">
<i class="im_message_file_button_icon"></i>
</div>
<div class="im_message_document_info">
<div class="im_message_document_name_wrap">
<span class="im_message_document_name" ng-bind="::historyMessage.media.file_name"></span>
<span class="im_message_document_size" ng-if="historyMessage.media.progress" ng-bind="historyMessage.media.progress | formatSizeProgress"></span>
<span class="im_message_document_name" ng-bind="::media.file_name"></span>
<span class="im_message_document_size" ng-if="media.progress" ng-bind="media.progress | formatSizeProgress"></span>
</div>
<div class="clearfix im_message_cancelable_progress_wrap">
<a class="im_message_media_progress_cancel pull-right" ng-click="historyMessage.media.progress.cancel()" my-i18n="modal_cancel"></a>
<a class="im_message_media_progress_cancel pull-right" ng-click="media.progress.cancel()" my-i18n="modal_cancel"></a>
<div class="im_message_download_progress_wrap">
<div class="progress tg_up_progress">
<div class="progress-bar progress-bar-success" role="progressbar" ng-style="{width: historyMessage.media.progress.percent + '%'}"></div>
<div class="progress-bar progress-bar-success" role="progressbar" ng-style="{width: media.progress.percent + '%'}"></div>
</div>
</div>
</div>

10
app/partials/desktop/message_attach_venue.html

@ -1,21 +1,21 @@ @@ -1,21 +1,21 @@
<div class="im_message_venue clearfix">
<a ng-href="{{::venue.mapUrl}}" target="_blank" class="im_message_venue_geopoint_wrap">
<a ng-href="{{::media.mapUrl}}" target="_blank" class="im_message_venue_geopoint_wrap">
<i class="icon icon-geo-point"></i>
<img
class="im_message_venue_geopoint_image"
my-geo-point-map="venue.geo"
my-geo-point-map="media.geo"
width="100"
height="100"
alt="[{{::'conversation_media_location' | i18n}} {{::venue.mapUrl}}]"
alt="[{{::'conversation_media_location' | i18n}} {{::media.mapUrl}}]"
/>
</a>
<div class="im_message_venue_info">
<div class="im_message_venue_title_wrap">
<a ng-href="{{::venue.mapUrl}}" target="_blank" class="im_message_document_name" ng-bind="::venue.title"></a>
<a ng-href="{{::media.mapUrl}}" target="_blank" class="im_message_document_name" ng-bind="::media.title"></a>
</div>
<div class="im_message_venue_address" ng-bind="::venue.address"></div>
<div class="im_message_venue_address" ng-bind="::media.address"></div>
</div>
</div>

35
app/partials/desktop/message_attach_video.html

@ -1,35 +0,0 @@ @@ -1,35 +0,0 @@
<div class="im_message_video im_message_document_thumbed">
<a class="im_message_video_thumb" ng-click="videoOpen()" ng-style="::{width: media.video.thumb.width + 'px'}">
<span class="im_message_video_duration nocopy" data-content="{{::media.video.duration | duration}}"></span>
<i class="icon icon-videoplay"></i>
<img
class="im_message_video_thumb im_message_video_thumb_blurred"
my-load-thumb
thumb="media.video.thumb"
/>
</a>
<div class="im_message_document_info">
<div class="im_message_document_name_wrap">
<span class="copyonly">[</span><span class="im_message_document_name" my-i18n="message_attach_video_video"></span><span class="copyonly">&nbsp;<span ng-bind="::media.video.duration | duration"></span>]</span>
<span class="im_message_document_size" ng-if="!media.video.progress.enabled" ng-bind="::media.video.size | formatSize"></span>
<span class="im_message_document_size" ng-if="media.video.progress.enabled" ng-bind="media.video.progress | formatSizeProgress"></span>
</div>
<div class="im_message_document_actions noselect" ng-if="!media.video.progress.enabled">
<a href="" ng-click="videoSave()" ng-switch="media.video.downloaded">
<span class="nocopy" ng-switch-when="true" my-i18n="message_attach_video_save"></span>
<span class="nocopy" ng-switch-default my-i18n="message_attach_video_download"></span>
</a>
<a class="nocopy" href="" ng-click="videoOpen()" my-i18n="message_attach_video_play"></a>
</div>
<div class="clearfix im_message_cancelable_progress_wrap" ng-if="media.video.progress.enabled">
<a class="im_message_media_progress_cancel pull-right nocopy" ng-click="media.video.progress.cancel()" my-i18n="modal_cancel"></a>
<div class="im_message_download_progress_wrap">
<div class="progress tg_down_progress">
<div class="progress-bar progress-bar-success" ng-style="{width: media.video.progress.percent + '%'}"></div>
</div>
</div>
</div>
</div>
</div>
<div ng-if="::media.rCaption" class="im_message_video_caption" ng-bind-html="::media.rCaption"></div>

38
app/partials/desktop/message_attach_webpage.html

@ -1,59 +1,59 @@ @@ -1,59 +1,59 @@
<div ng-show="webpage._ == 'webPage'" class="im_message_webpage_wrap clearfix" ng-switch="webpage.type">
<div ng-show="media.webpage._ == 'webPage'" class="im_message_webpage_wrap clearfix" ng-switch="media.webpage.type">
<div ng-switch-when="photo" class="im_message_webpage_photo">
<div class="im_message_webpage_title">
<a href="{{webpage.url}}" target="_blank" ng-bind-html="webpage.rTitle"></a>
<a href="{{media.webpage.url}}" target="_blank" ng-bind-html="media.webpage.rTitle"></a>
</div>
<div ng-if="webpage.description.length" class="im_message_webpage_description" ng-bind-html="webpage.rDescription"></div>
<a class="im_message_photo_thumb" ng-click="openPhoto(webpage.photo.id, {w: webpage.id, m: messageId})" ng-style="::{width: webpage.photo.thumb.width + 'px'}" ng-mouseover="preloadPhoto(webpage.photo.id)">
<div ng-if="media.webpage.description.length" class="im_message_webpage_description" ng-bind-html="media.webpage.rDescription"></div>
<a class="im_message_photo_thumb" ng-click="openPhoto(media.webpage.photo.id, {w: media.webpage.id, m: messageId})" ng-style="::{width: media.webpage.photo.thumb.width + 'px'}" ng-mouseover="preloadPhoto(media.webpage.photo.id)">
<img
class="im_message_photo_thumb"
my-load-thumb
thumb="webpage.photo.thumb"
thumb="media.webpage.photo.thumb"
alt="[{{::'conversation_media_photo' | i18n}}]"
/>
</a>
</div>
<div ng-switch-when="video" class="im_message_webpage_video">
<div class="im_message_webpage_site" ng-bind="webpage.site_name || webpage.display_url"></div>
<div class="im_message_webpage_site" ng-bind="media.webpage.site_name || media.webpage.display_url"></div>
<div class="im_message_webpage_title">
<a ng-click="openEmbed($event)" href="{{webpage.url}}" target="_blank" ng-bind-html="webpage.rTitle"></a>
<a ng-click="openEmbed($event)" href="{{media.webpage.url}}" target="_blank" ng-bind-html="media.webpage.rTitle"></a>
</div>
<div ng-if="webpage.description.length" class="im_message_webpage_description" ng-bind-html="webpage.rDescription"></div>
<a class="im_message_video_thumb" ng-click="openEmbed($event)" ng-href="{{webpage.url}}" target="_blank" ng-style="::{width: video.thumb.width + 'px'}">
<span ng-if="webpage.duration > 0" class="im_message_video_duration nocopy" data-content="{{::webpage.duration | duration}}"></span>
<div ng-if="media.webpage.description.length" class="im_message_webpage_description" ng-bind-html="media.webpage.rDescription"></div>
<a class="im_message_video_thumb" ng-click="openEmbed($event)" ng-href="{{media.webpage.url}}" target="_blank" ng-style="::{width: video.thumb.width + 'px'}">
<span ng-if="media.webpage.duration > 0" class="im_message_video_duration nocopy" data-content="{{::media.webpage.duration | duration}}"></span>
<i class="icon icon-videoplay"></i>
<img
class="im_message_video_thumb"
my-load-thumb
thumb="webpage.photo.thumb"
thumb="media.webpage.photo.thumb"
alt="[{{::'conversation_media_video' | i18n}}]"
/>
</a>
</div>
<div ng-switch-when="document" class="im_message_webpage_document">
<div my-message-document="webpage" message-id="messageId"></div>
<div my-message-document="media.webpage" message-id="messageId"></div>
</div>
<div ng-switch-when="gif" class="im_message_webpage_gif">
<div class="im_message_webpage_title">
<a href="{{webpage.url}}" target="_blank" ng-bind-html="webpage.rTitle"></a>
<a href="{{media.webpage.url}}" target="_blank" ng-bind-html="media.webpage.rTitle"></a>
</div>
<div my-message-document="webpage" message-id="messageId"></div>
<div my-message-document="media.webpage" message-id="messageId"></div>
</div>
<div ng-switch-default class="im_message_webpage_article">
<a ng-if="webpage.photo" href="{{webpage.url}}" target="_blank" class="im_message_webpage_article_photo pull-right">
<a ng-if="media.webpage.photo" href="{{media.webpage.url}}" target="_blank" class="im_message_webpage_article_photo pull-right">
<img
class="im_message_article_thumb"
my-load-thumb
thumb="webpage.photo.thumb"
thumb="media.webpage.photo.thumb"
/>
</a>
<div class="im_message_webpage_site" ng-bind="webpage.site_name"></div>
<div ng-if="media.webpage.site_name" class="im_message_webpage_site" ng-bind="media.webpage.site_name"></div>
<div class="im_message_webpage_title">
<a ng-click="openEmbed($event)" href="{{webpage.url}}" target="_blank" ng-bind-html="webpage.rTitle"></a>
<a ng-click="openEmbed($event)" href="{{media.webpage.url}}" target="_blank" ng-bind-html="media.webpage.rTitle"></a>
</div>
<div ng-if="webpage.description.length" class="im_message_webpage_description" ng-bind-html="webpage.rDescription"></div>
<div ng-if="media.webpage.description.length" class="im_message_webpage_description" ng-bind-html="media.webpage.rDescription"></div>
</div>
</div>

14
app/partials/desktop/message_media.html

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
<div ng-switch="::media._">
<div ng-switch-when="messageMediaPhoto" my-message-photo="media" message-id="messageId"></div>
<div ng-switch-when="messageMediaDocument" my-message-document="media" message-id="messageId"></div>
<div ng-switch-when="messageMediaGeo" my-message-geo="media"></div>
<div ng-switch-when="messageMediaVenue" my-message-venue="media"></div>
<div ng-switch-when="messageMediaContact" my-message-contact="media"></div>
<div ng-switch-when="messageMediaWebPage" my-message-webpage="media" message-id="messageId"></div>
<div ng-switch-when="messageMediaPending" my-message-pending="media"></div>
<div ng-switch-when="messageMediaUnsupported">
<div class="im_message_text" my-i18n="message_attach_unsupported">
<my-i18n-param name="link"><a href="https://web.telegram.org" target="_blank">web.telegram.org</a></my-i18n-param>
</div>
</div>
</div>

5
app/partials/desktop/message_service.html

@ -30,6 +30,11 @@ @@ -30,6 +30,11 @@
<span ng-switch-when="messageActionChannelEditPhoto" my-i18n="message_service_changed_channel_photo"></span>
<span ng-switch-when="messageActionChannelDeletePhoto" my-i18n="message_service_removed_channel_photo"></span>
<span ng-switch-when="messageActionPinMessage" my-i18n="message_service_pinned_message">
<my-i18n-param name="message"><a class="im_service_message_pinned" my-pinned-message="historyMessage.reply_to_msg"></a></my-i18n-param>
</span>
<span ng-switch-default my-i18n="message_service_unsupported_action">
<my-i18n-param name="action"><span ng-bind="historyMessage.action._"></span></my-i18n-param>
</span>

1
app/partials/desktop/pinned_message.html

@ -0,0 +1 @@ @@ -0,0 +1 @@
<span ng-switch="pinnedMessage.loading"><span ng-switch-when="true" my-i18n="im_reply_loading"><my-i18n-param name="dots"><span my-loading-dots></span></my-i18n-param></span><span ng-switch-default my-short-message="pinnedMessage"></span></span>

5
app/partials/desktop/reply_markup.html

@ -1,8 +1,9 @@ @@ -1,8 +1,9 @@
<div class="reply_markup_wrap">
<div class="reply_markup" ng-class="replyMarkup.splitCount ? 'reply_markup_h' + replyMarkup.splitCount : ''">
<div class="reply_markup_row" ng-repeat="row in replyMarkup.rows">
<div class="reply_markup_button_wrap" ng-class="'reply_markup_button_w' + row.buttons.length" ng-repeat="button in row.buttons">
<button class="btn reply_markup_button" ng-bind-html="::button.rText" ng-click="buttonSend(button)"></button>
<div class="reply_markup_button_wrap" ng-class="'reply_markup_button_w' + row.buttons.length" ng-repeat="button in row.buttons" ng-switch="button._">
<a ng-switch-when="keyboardButtonUrl" class="btn reply_markup_button" href="{{button.pUrl}}" ng-bind-html="::button.rText"></a>
<button ng-switch-default class="btn reply_markup_button" ng-bind-html="::button.rText" ng-click="buttonClick(button)"></button>
</div>
</div>
</div>

23
app/partials/desktop/reply_message.html

@ -17,27 +17,6 @@ @@ -17,27 +17,6 @@
</div>
<div class="im_message_reply_body" ng-switch-default>
<span class="copyonly">&gt;&nbsp;</span>
<span class="im_reply_message_media" ng-if="replyMessage.media" ng-switch="replyMessage.media._">
<span ng-switch-when="messageMediaPhoto" my-i18n="conversation_media_photo"></span>
<span ng-switch-when="messageMediaVideo" my-i18n="conversation_media_video"></span>
<span ng-switch-when="messageMediaDocument" ng-switch="dialogMessage.media.document.isSpecial || false">
<span ng-switch-when="sticker">
<span ng-bind-html="replyMessage.media.document.stickerEmoji"></span>
<my-i18n msgid="conversation_media_sticker"></my-i18n>
</span>
<span ng-switch-when="gif" my-i18n="conversation_media_gif"></span>
<span ng-switch-when="audio" my-i18n="conversation_media_audio"></span>
<span ng-switch-default ng-bind="replyMessage.media.document.file_name"></span>
</span>
<span ng-switch-when="messageMediaAudio" my-i18n="conversation_media_audio"></span>
<span ng-switch-when="messageMediaGeo" my-i18n="conversation_media_location"></span>
<span ng-switch-when="messageMediaVenue" my-i18n="conversation_media_location"></span>
<span ng-switch-when="messageMediaContact" my-i18n="conversation_media_contact"></span>
</span>
<span class="im_reply_message_service" ng-if="replyMessage._ == 'messageService'" my-service-short-message="replyMessage"></span>
<span class="im_reply_message_text" ng-if="replyMessage.message.length" ng-bind-html="replyMessage.richMessage"></span>
<span my-short-message="replyMessage"></span>
</div>
</div>

22
app/partials/desktop/dialog_service.html → app/partials/desktop/short_message.html

@ -1,4 +1,20 @@ @@ -1,4 +1,20 @@
<span ng-switch="message.action._">
<span class="im_short_message_media" ng-if="message.media" ng-switch="message.media._">
<span ng-switch-when="messageMediaPhoto" my-i18n="conversation_media_photo"></span>
<span ng-switch-when="messageMediaDocument" ng-switch="message.media.document.type || false">
<span ng-switch-when="sticker">
<span ng-bind-html="message.media.document.stickerEmoji"></span>
<my-i18n msgid="conversation_media_sticker"></my-i18n>
</span>
<span ng-switch-when="gif" my-i18n="conversation_media_gif"></span>
<span ng-switch-when="audio" my-i18n="conversation_media_audio"></span>
<span ng-switch-when="voice" my-i18n="conversation_media_audio"></span>
<span ng-switch-when="video" my-i18n="conversation_media_video"></span>
<span ng-switch-default ng-bind="message.media.document.file_name"></span>
</span>
<span ng-switch-when="messageMediaGeo" my-i18n="conversation_media_location"></span>
<span ng-switch-when="messageMediaVenue" my-i18n="conversation_media_location"></span>
<span ng-switch-when="messageMediaContact" my-i18n="conversation_media_contact"></span>
</span><span class="im_short_message_service" ng-if="message._ == 'messageService'" ng-switch="message.action._">
<span ng-switch-when="messageActionChatCreate" my-i18n="conversation_group_created"></span>
<span ng-switch-when="messageActionChatEditTitle" my-i18n="conversation_group_renamed"></span>
<span ng-switch-when="messageActionChatEditPhoto" my-i18n="conversation_group_photo_updated"></span>
@ -23,5 +39,5 @@ @@ -23,5 +39,5 @@
<span ng-switch-when="messageActionChannelCreate" my-i18n="conversation_created_channel"></span>
<span ng-switch-when="messageActionChannelEditTitle" my-i18n="conversation_changed_channel_name"></span>
<span ng-switch-when="messageActionChannelEditPhoto" my-i18n="conversation_changed_channel_photo"></span>
<span ng-switch-when="messageActionChannelDeletePhoto" my-i18n="conversation_removed_channel_photo"></span>
</span>
<span ng-switch-when="messageActionPinMessage" my-i18n="conversation_pinned_message"></span>
</span><span class="im_short_message_text" ng-if="message.message.length" ng-bind-html="message.richMessage"></span>

16
app/partials/desktop/user_modal.html

@ -33,16 +33,9 @@ @@ -33,16 +33,9 @@
<div class="md_modal_sections clearfix">
<div class="md_modal_iconed_section_wrap md_modal_iconed_section_number" ng-if="user.phone || user.username">
<div class="md_modal_iconed_section_wrap md_modal_iconed_section_number" ng-if="user.phone || user.username || rAbout">
<i class="md_modal_section_icon md_modal_section_icon_phone"></i>
<div class="md_modal_section_param_wrap" ng-if="user.pFlags.bot &amp;&amp; bot_info.rAbout">
<div class="md_modal_section_param_value">
<span ng-bind-html="bot_info.rAbout"></span>
</div>
<div class="md_modal_section_param_name" my-i18n="user_modal_about"></div>
</div>
<div class="md_modal_section_param_wrap" ng-if="user.phone">
<div class="md_modal_section_param_value" ng-bind="user.phone | phoneNumber"></div>
<div class="md_modal_section_param_name" my-i18n="user_modal_phone"></div>
@ -54,6 +47,13 @@ @@ -54,6 +47,13 @@
</div>
<div class="md_modal_section_param_name" my-i18n="user_modal_username"></div>
</div>
<div class="md_modal_section_param_wrap" ng-if="rAbout">
<div class="md_modal_section_param_value">
<span ng-bind-html="rAbout"></span>
</div>
<div class="md_modal_section_param_name" my-i18n="user_modal_about"></div>
</div>
</div>
<div class="md_modal_iconed_section_wrap md_modal_iconed_section_toggle">

22
app/partials/mobile/dialog.html

@ -57,27 +57,7 @@ @@ -57,27 +57,7 @@
</span>
</span>
<span class="im_dialog_message_media" ng-if="dialogMessage.media" ng-switch="dialogMessage.media._">
<span ng-switch-when="messageMediaPhoto" my-i18n="conversation_media_photo"></span>
<span ng-switch-when="messageMediaVideo" my-i18n="conversation_media_video"></span>
<span ng-switch-when="messageMediaDocument" ng-switch="dialogMessage.media.document.isSpecial || false">
<span ng-switch-when="sticker">
<span ng-bind-html="dialogMessage.media.document.stickerEmoji"></span>
<my-i18n msgid="conversation_media_sticker"></my-i18n>
</span>
<span ng-switch-when="gif" my-i18n="conversation_media_gif"></span>
<span ng-switch-when="audio" my-i18n="conversation_media_audio"></span>
<span ng-switch-default ng-bind="dialogMessage.media.document.file_name"></span>
</span>
<span ng-switch-when="messageMediaAudio" my-i18n="conversation_media_audio"></span>
<span ng-switch-when="messageMediaGeo" my-i18n="conversation_media_location"></span>
<span ng-switch-when="messageMediaVenue" my-i18n="conversation_media_location"></span>
<span ng-switch-when="messageMediaContact" my-i18n="conversation_media_contact"></span>
</span>
<span class="im_dialog_message_service" ng-if="dialogMessage._ == 'messageService'" my-service-short-message="dialogMessage"></span>
<span class="im_dialog_message_text" ng-if="dialogMessage.message.length" ng-bind-html="dialogMessage.richMessage"></span>
<span my-short-message="dialogMessage"></span>
</div>
</div>

4
app/partials/mobile/im.html

@ -192,4 +192,6 @@ @@ -192,4 +192,6 @@
</div>
</div>
</div>
<toaster-container toaster-options="{'position-class': 'toast-bottom-center'}"></toaster-container>

40
app/partials/mobile/login.html

@ -66,24 +66,28 @@ @@ -66,24 +66,28 @@
<form name="myLoginForm" ng-if="credentials.phone_code_hash &amp;&amp; !credentials.phone_code_valid" ng-submit="logIn()">
<h3 class="login_phone_head"><span ng-bind="credentials.phone_country"></span> <span ng-bind="credentials.phone_number"></span></h3>
<div class="login_edit_phone"><a ng-click="editPhone()" my-i18n="login_edit_number"></a></div>
<div ng-switch="credentials.viaApp">
<div ng-switch-when="true">
<p class="login_smscode_lead" my-i18n="login_enter_code_label_md"></p>
<p class="login_smscode_lead">
<a ng-click="sendSms()" my-i18n="login_code_not_received"></a>
</p>
</div>
<div ng-switch-default>
<p class="login_smscode_lead" my-i18n="login_enter_sms_code_label_md"></p>
<p class="login_smscode_lead">
<span ng-show="callPending.remaining > 0" my-i18n="login_call_remaining">
<my-i18n-param name="remaining">{{callPending.remaining | duration}}</my-i18n-param>
</span>
<span ng-show="!callPending.remaining &amp;&amp; !callPending.success" my-i18n="login_calling"></span>
<span ng-show="!callPending.remaining &amp;&amp; callPending.success" my-i18n="login_number_dialed"></span>
</p>
</div>
<div ng-switch="credentials.type._">
<p ng-switch-when="auth.sentCodeTypeApp" class="login_smscode_lead" my-i18n="login_enter_code_label_md"></p>
<p ng-switch-default class="login_smscode_lead" my-i18n="login_enter_sms_code_label_md"></p>
</div>
<div ng-if="nextPending.type" ng-switch="nextPending.remaining === false">
<p ng-switch-when="true" class="login_smscode_lead">
<a ng-click="sendNext()" my-i18n="login_code_not_received"></a>
</p>
<p ng-switch-default class="login_smscode_lead">
<span ng-show="nextPending.remaining > 0" my-i18n="login_call_remaining">
<my-i18n-param name="remaining" ng-bind="nextPending.remaining | duration"></my-i18n-param>
</span>
<span ng-show="!nextPending.remaining" my-i18n="login_calling"></span>
</p>
</div>
<div ng-if="credentials.type._ == 'auth.sentCodeTypeCall'">
<p class="login_smscode_lead">
<span my-i18n="login_number_dialed"></span>
</p>
</div>
<div class="md-input-group md-input-group-centered" ng-class="{'md-input-error': error.field == 'phone_code'}" my-labeled-input ng-switch="error.field == 'phone_code'">

44
app/partials/mobile/message.html

@ -40,40 +40,28 @@ @@ -40,40 +40,28 @@
<span class="im_message_date" ng-bind="::historyMessage.date | time"></span>
</div>
<div class="im_message_body" ng-class="::{im_message_body_media: historyMessage._ == 'message' &amp;&amp; historyMessage.media && historyMessage.media._ != 'messageMediaWebPage' && !historyMessage.media.rCaption && !historyMessage.viaBotID ? true : false}">
<div my-message-body="historyMessage">
<div class="im_message_body" ng-class="::{im_message_body_media: historyMessage._ == 'message' &amp;&amp; historyMessage.media && historyMessage.media._ != 'messageMediaWebPage' && !historyMessage.media.rCaption && !historyMessage.viaBotID ? true : false}">
<a class="im_message_author" my-peer-link="historyMessage.fromID" short="historyMessage.toID > 0" color="historyMessage.toID < 0" no-watch="true"></a>
<a ng-if="::historyMessage.viaBotID && !historyMessage.fwdFromID" class="im_message_author_via" my-i18n="message_via_bot" ng-click="selectInlineBot(historyMessage.viaBotID, $event)"><my-i18n-param name="bot"><span class="im_message_fwd_author" my-peer-link="historyMessage.viaBotID" username="true" no-watch="true"></span></my-i18n-param></a>
<a class="im_message_author" my-peer-link="historyMessage.fromID" short="historyMessage.toID > 0" color="historyMessage.toID < 0" no-watch="true"></a>
<a ng-if="::historyMessage.viaBotID && !historyMessage.fwdFromID" class="im_message_author_via" my-i18n="message_via_bot" ng-click="selectInlineBot(historyMessage.viaBotID, $event)"><my-i18n-param name="bot"><span class="im_message_fwd_author" my-peer-link="historyMessage.viaBotID" username="true" no-watch="true"></span></my-i18n-param></a>
<a class="im_message_reply_wrap" my-reply-message="historyMessage.reply_to_msg" ng-if="::historyMessage.reply_to_mid"></a>
<a class="im_message_reply_wrap" my-reply-message="historyMessage.reply_to_msg" ng-if="::historyMessage.reply_to_mid"></a>
<div ng-if="::!!historyMessage.fwdFromID &amp;&amp; !historyMessage.media" class="im_message_fwd_header" ng-switch="!!historyMessage.viaBotID" my-i18n>
<span ng-switch-when="true" my-i18n-format="message_forwarded_via_message_mobile"></span>
<span ng-switch-default my-i18n-format="message_forwarded_message_mobile"></span>
<my-i18n-param name="from"><a class="im_message_fwd_author" my-peer-link="historyMessage.fwdFromID" no-watch="true"></a></my-i18n-param>
<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>
</div>
<div class="im_message_text" ng-if="::historyMessage.message.length || false" my-message-text="::historyMessage" dir="auto"></div>
<div ng-if="::historyMessage.media || historyMessage.mid < 0 ? true : false" class="im_message_media" ng-switch="historyMessage.media._">
<div ng-switch-when="messageMediaPhoto" my-message-photo="historyMessage.media" message-id="historyMessage.mid"></div>
<div ng-switch-when="messageMediaVideo" my-message-video="historyMessage.media" message-id="historyMessage.mid"></div>
<div ng-switch-when="messageMediaDocument" my-message-document="historyMessage.media" message-id="historyMessage.mid"></div>
<div ng-switch-when="messageMediaAudio" class="im_message_audio" my-audio-player audio="historyMessage.media.audio" message="historyMessage"></div>
<div ng-switch-when="messageMediaGeo" my-message-geo="historyMessage.media"></div>
<div ng-switch-when="messageMediaVenue" my-message-venue="historyMessage.media"></div>
<div ng-switch-when="messageMediaContact" class="im_message_contact" my-message-contact></div>
<div ng-switch-when="messageMediaWebPage" class="im_message_webpage" my-message-webpage="historyMessage.media.webpage" message-id="historyMessage.mid"></div>
<div ng-switch-when="messageMediaPending" my-message-pending></div>
<div ng-switch-when="messageMediaUnsupported">
<div class="im_message_text">
The message is not supported on your version of Telegram Web. Update the app to view: <a href="https://web.telegram.org">web.telegram.org</a>.
</div>
<div ng-if="::!!historyMessage.fwdFromID &amp;&amp; !historyMessage.media" class="im_message_fwd_header" ng-switch="!!historyMessage.viaBotID" my-i18n>
<span ng-switch-when="true" my-i18n-format="message_forwarded_via_message_mobile"></span>
<span ng-switch-default my-i18n-format="message_forwarded_message_mobile"></span>
<my-i18n-param name="from"><a class="im_message_fwd_author" my-peer-link="historyMessage.fwdFromID" no-watch="true"></a></my-i18n-param>
<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>
</div>
<div class="im_message_text" dir="auto"></div>
<div class="im_message_media"></div>
</div>
<div class="im_message_keyboard"></div>
</div>
</div>

12
app/partials/mobile/message_attach_contact.html

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
<div>
<a ng-if="historyMessage.media.user_id > 0" class="im_message_contact_photo pull-left" my-peer-photolink="historyMessage.media.user_id" img-class="im_message_contact_photo" user-override="historyMessage.media"></a>
<div class="im_message_contact_name" ng-switch="historyMessage.media.user_id > 0">
<a ng-switch-when="true" my-peer-link="historyMessage.media.user_id" user-override="historyMessage.media"></a>
<span ng-switch-default ng-bind-html="::historyMessage.media.rFullName"></span>
<div class="im_message_contact">
<a ng-if="::media.user_id > 0" class="im_message_contact_photo pull-left" my-peer-photolink="media.user_id" img-class="im_message_contact_photo" user-override="media"></a>
<div class="im_message_contact_name" ng-switch="media.user_id > 0">
<a ng-switch-when="true" my-peer-link="media.user_id" user-override="media"></a>
<span ng-switch-default ng-bind-html="::media.rFullName"></span>
</div>
<div class="im_message_contact_phone" ng-bind="::historyMessage.media.phone_number | phoneNumber"></div>
<div class="im_message_contact_phone" ng-bind="::media.phone_number | phoneNumber"></div>
</div>

18
app/partials/mobile/message_attach_document.html

@ -1,13 +1,29 @@ @@ -1,13 +1,29 @@
<div ng-switch="::media.document.isSpecial">
<div ng-switch="::media.document.type">
<div ng-switch-when="gif" my-load-gif document="media.document"></div>
<div ng-switch-when="sticker" my-load-sticker document="media.document" open="true"></div>
<div ng-switch-when="voice" class="im_message_audio">
<div my-audio-player audio="media.document"></div>
</div>
<div ng-switch-when="audio" class="im_message_audio">
<div my-audio-player audio="media.document"></div>
</div>
<div ng-switch-when="video" class="im_message_video im_message_document_thumbed">
<a class="im_message_video_thumb" href="" ng-click="videoOpen()" ng-style="::{width: media.document.thumb.width + 'px'}">
<span class="im_message_video_duration" ng-bind="::media.document.duration | duration"></span>
<i class="icon icon-videoplay"></i>
<img
class="im_message_video_thumb im_message_video_thumb_blurred"
my-load-thumb
thumb="media.document.thumb"
/>
</a>
</div>
<div ng-switch-default class="im_message_document clearfix" ng-class="{im_message_document_thumbed: !!media.document.thumb, im_message_document_progress: media.document.progress.enabled}">
<a ng-if="::!media.document.thumb" class="im_message_file_button" ng-click="docOpen()" ng-class="{im_message_file_button_dl_doc: media.document.downloaded}">

10
app/partials/mobile/message_attach_pending.html

@ -1,17 +1,17 @@ @@ -1,17 +1,17 @@
<div class="im_message_document im_message_upload_file" ng-class="::'im_message_upload_' + historyMessage.media.type">
<div class="im_message_document im_message_upload_file" ng-class="::'im_message_upload_' + media.type">
<div class="im_message_file_button im_message_file_button_upload">
<i class="im_message_file_button_icon"></i>
</div>
<div class="im_message_document_info">
<div class="im_message_document_name_wrap">
<span class="im_message_document_name" ng-bind="::historyMessage.media.file_name"></span>
<span class="im_message_document_size" ng-if="historyMessage.media.progress" ng-bind="historyMessage.media.progress | formatSizeProgress"></span>
<span class="im_message_document_name" ng-bind="::media.file_name"></span>
<span class="im_message_document_size" ng-if="media.progress" ng-bind="media.progress | formatSizeProgress"></span>
</div>
<div class="clearfix im_message_cancelable_progress_wrap">
<a class="im_message_media_progress_cancel pull-right" ng-click="historyMessage.media.progress.cancel()" my-i18n="modal_cancel"></a>
<a class="im_message_media_progress_cancel pull-right" ng-click="media.progress.cancel()" my-i18n="modal_cancel"></a>
<div class="im_message_download_progress_wrap">
<div class="progress tg_down_progress">
<div class="progress-bar progress-bar-success" role="progressbar" ng-style="{width: historyMessage.media.progress.percent + '%'}"></div>
<div class="progress-bar progress-bar-success" role="progressbar" ng-style="{width: media.progress.percent + '%'}"></div>
</div>
</div>
</div>

9
app/partials/mobile/message_attach_venue.html

@ -1,20 +1,21 @@ @@ -1,20 +1,21 @@
<div class="im_message_venue clearfix">
<a ng-href="{{::venue.mapUrl}}" target="_blank" class="im_message_venue_geopoint_wrap">
<a ng-href="{{::media.mapUrl}}" target="_blank" class="im_message_venue_geopoint_wrap">
<i class="icon icon-geo-point"></i>
<img
class="im_message_venue_geopoint_image"
my-geo-point-map="venue.geo"
my-geo-point-map="media.geo"
width="100"
height="100"
alt="[{{::'conversation_media_location' | i18n}} {{::media.mapUrl}}]"
/>
</a>
<div class="im_message_venue_info">
<div class="im_message_venue_title_wrap">
<a ng-href="{{::venue.mapUrl}}" target="_blank" class="im_message_document_name" ng-bind="::venue.title"></a>
<a ng-href="{{::media.mapUrl}}" target="_blank" class="im_message_document_name" ng-bind="::media.title"></a>
</div>
<div class="im_message_venue_address" ng-bind="::venue.address"></div>
<div class="im_message_venue_address" ng-bind="::media.address"></div>
</div>
</div>

12
app/partials/mobile/message_attach_video.html

@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
<div class="im_message_video im_message_document_thumbed">
<a class="im_message_video_thumb" href="" ng-click="videoOpen()" ng-style="::{width: media.video.thumb.width + 'px'}">
<span class="im_message_video_duration" ng-bind="::media.video.duration | duration"></span>
<i class="icon icon-videoplay"></i>
<img
class="im_message_video_thumb im_message_video_thumb_blurred"
my-load-thumb
thumb="media.video.thumb"
/>
</a>
</div>
<div ng-if="::media.rCaption" class="im_message_video_caption" ng-bind-html="::media.rCaption"></div>

4
app/partials/mobile/message_service.html

@ -30,6 +30,10 @@ @@ -30,6 +30,10 @@
<span ng-switch-when="messageActionChannelEditPhoto" my-i18n="message_service_changed_channel_photo"></span>
<span ng-switch-when="messageActionChannelDeletePhoto" my-i18n="message_service_removed_channel_photo"></span>
<span ng-switch-when="messageActionPinMessage" my-i18n="message_service_pinned_message">
<my-i18n-param name="message"><a my-pinned-message="historyMessage.reply_to_msg"></a></my-i18n-param>
</span>
<span ng-switch-default my-i18n="message_service_unsupported_action">
<my-i18n-param name="action"><span ng-bind="historyMessage.action._"></span></my-i18n-param>
</span>

4
app/partials/mobile/user_modal.html

@ -58,9 +58,9 @@ @@ -58,9 +58,9 @@
</div>
<div class="mobile_modal_section" ng-if="user.pFlags.bot &amp;&amp; bot_info.rAbout">
<div class="mobile_modal_section" ng-if="rAbout">
<h4 class="mobile_modal_section_header" my-i18n="user_modal_about"></h4>
<div class="mobile_modal_section_value" ng-bind-html="bot_info.rAbout"></div>
<div class="mobile_modal_section_value" ng-bind-html="rAbout"></div>
</div>
<div class="mobile_modal_action_wrap">

254
app/vendor/angularjs-toaster/toaster.css vendored

@ -0,0 +1,254 @@ @@ -0,0 +1,254 @@
/*
* Toastr
* Version 2.0.1
* Copyright 2012 John Papa and Hans Fjallemark.
* All Rights Reserved.
* Use, reproduction, distribution, and modification of this code is subject to the terms and
* conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php
*
* Author: John Papa and Hans Fjallemark
* Project: https://github.com/CodeSeven/toastr
*/
.toast-title {
font-weight: bold;
}
.toast-message {
-ms-word-wrap: break-word;
word-wrap: break-word;
}
.toast-message a,
.toast-message label {
color: #ffffff;
}
.toast-message a:hover {
color: #cccccc;
text-decoration: none;
}
.toast-close-button {
position: relative;
right: -0.3em;
top: -0.3em;
float: right;
font-size: 20px;
font-weight: bold;
color: #ffffff;
-webkit-text-shadow: 0 1px 0 #ffffff;
text-shadow: 0 1px 0 #ffffff;
opacity: 0.8;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
filter: alpha(opacity=80);
}
.toast-close-button:hover,
.toast-close-button:focus {
color: #000000;
text-decoration: none;
cursor: pointer;
opacity: 0.4;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40);
filter: alpha(opacity=40);
}
/*Additional properties for button version
iOS requires the button element instead of an anchor tag.
If you want the anchor version, it requires `href="#"`.*/
button.toast-close-button {
padding: 0;
cursor: pointer;
background: transparent;
border: 0;
-webkit-appearance: none;
}
.toast-top-full-width {
top: 0;
right: 0;
width: 100%;
}
.toast-bottom-full-width {
bottom: 0;
right: 0;
width: 100%;
}
.toast-top-left {
top: 12px;
left: 12px;
}
.toast-top-center {
top: 12px;
}
.toast-top-right {
top: 12px;
right: 12px;
}
.toast-bottom-right {
right: 12px;
bottom: 12px;
}
.toast-bottom-center {
bottom: 12px;
}
.toast-bottom-left {
bottom: 12px;
left: 12px;
}
.toast-center {
top: 45%;
}
#toast-container {
position: fixed;
z-index: 999999;
pointer-events: auto;
/*overrides*/
}
#toast-container.toast-center,
#toast-container.toast-top-center,
#toast-container.toast-bottom-center{
width: 100%;
pointer-events: none;
}
#toast-container.toast-center > div,
#toast-container.toast-top-center > div,
#toast-container.toast-bottom-center > div{
margin: auto;
pointer-events: auto;
}
#toast-container.toast-center > button,
#toast-container.toast-top-center > button,
#toast-container.toast-bottom-center > button{
pointer-events: auto;
}
#toast-container * {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
#toast-container > div {
margin: 0 0 6px;
padding: 15px 15px 15px 50px;
width: 300px;
-moz-border-radius: 3px 3px 3px 3px;
-webkit-border-radius: 3px 3px 3px 3px;
border-radius: 3px 3px 3px 3px;
background-position: 15px center;
background-repeat: no-repeat;
-moz-box-shadow: 0 0 12px #999999;
-webkit-box-shadow: 0 0 12px #999999;
box-shadow: 0 0 12px #999999;
color: #ffffff;
opacity: 0.8;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
filter: alpha(opacity=80);
}
#toast-container > :hover {
-moz-box-shadow: 0 0 12px #000000;
-webkit-box-shadow: 0 0 12px #000000;
box-shadow: 0 0 12px #000000;
opacity: 1;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
filter: alpha(opacity=100);
cursor: pointer;
}
/*#toast-container > .toast-info {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=") !important;
}*/
#toast-container > .toast-wait {
background-image: url("data:image/gif;base64,R0lGODlhIAAgAIQAAAQCBISGhMzKzERCROTm5CQiJKyurHx+fPz+/ExOTOzu7Dw+PIyOjCwqLFRWVAwKDIyKjMzOzOzq7CQmJLy6vFRSVPTy9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQJCQAXACwAAAAAIAAgAAAF3eAljmRpnmh6VRSVqLDpIDTixOdUlFSNUDhSQUAT7ES9GnD0SFQAKWItMqr4bqKHVPDI+WiTkaOFFVlrFe83rDrT0qeIjwrT0iLdU0GOiBxhAA4VeSk6QYeIOAsQEAuJKgw+EI8nA18IA48JBAQvFxCXDI8SNAQikV+iiaQIpheWX5mJmxKeF6g0qpQmA4yOu8C7EwYWCgZswRcTFj4KyMAGlwYxDwcHhCXMXxYxBzQHKNo+3DDeCOAn0V/TddbYJA0K48gAEAFQicMWFsfwNA3JSgAIAAFfwIMIL4QAACH5BAkJABoALAAAAAAgACAAhAQCBIyKjERCRMzOzCQiJPTy9DQyNGRmZMTCxOTm5CwqLHx+fBQWFJyenNTW1Pz6/Dw6PGxubAwKDIyOjNTS1CQmJCwuLPz+/Dw+PHRydAAAAAAAAAAAAAAAAAAAAAAAAAXboCaOZGmeaKoxWcSosMkk15W8cZ7VdZaXkcEgQtrxfD9RhHchima1GwlCGUBSFCaFxMrgRtnLFhWujWHhs2nJc8KoVlWGQnEn7/i8XgOwWAB7JwoONQ4KgSQAZRcOgHgSCwsSIhZMNRZ5CzULIgaWF5h4mhecfIQ8jXmQkiODhYeIiRYGjrG2PxgBARi3IhNMAbcCnwI5BAQpAZ8TIwK6vCQVDwUVKL+WzAANTA210g/VJ8OWxQefByQE4dZMzBoInwh4zrtgn2p725YNthUFTNRuGYB3AYGBHCEAACH5BAkJAB0ALAAAAAAgACAAhAQCBISChFRWVMzKzCQiJOTm5GxqbCwuLJSWlPz6/NTW1AwODJSSlGRmZCwqLOzu7HR2dDQ2NAQGBISGhFxaXNTS1CQmJOzq7GxubDQyNKSmpPz+/Nza3AAAAAAAAAAAAAXfYCeOZGmeaKqurHBdAiuP17Zdc0lMAVHWt9yI8LA9fCPB4xEjARoNSWpis01kBpshFahurqzsZosiGpErScMAUO0maKF8Tq/bTQCIQgFp30cQXhB1BHEcXhx0FgkJFiOHVYlzi42AgoRxeRx8fn+en3UABwedKgsBAwMBCygOCjYKDisLFV4VrCUAtVUKpSZdXl8mB8EbByQWcQPFAyYZxccdB7sV0cvBzbmvvG0LBV4FrFTBYCWuNhyyHRTFFB20trh4BxmdYl4YIqepq0IRxRE+IfDCAFQHARo0NGERAgAh+QQJCQAgACwAAAAAIAAgAIUEAgSEgoRMTkzMyswcHhzk5uR0cnQUFhRcXlwsKiz09vQMCgyMiozU1tQkJiR8fnxkZmT8/vwEBgSEhoRcWlzU0tQkIiT08vR0dnQcGhxkYmQ0MjT8+vwMDgyMjozc2twAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG+UCQcEgsGo/IpHLJXDweC6Z0+IhEHlOjRGIMWLHZoUZx0RQlAajxkFFKFFYFl5m5KNpIySU+X2bIBEoQZBBZGQdMElFhjI2Oj5AgHQEDAw8dQxYeDBaNHRVWVhWYCXsRFwmMXqFWEyAerB6MA6xWA6+xs7URt6VWqIwTu64gDh4eDp6goaORQ5OVAZjO1EgEGhB4RwAYDQ0YAEwIcBEKFEgYrBhLBORxgUYfrB9LELuF8fNDAAaVBuEg7NXCVyRdqHVCGLBiIIQAB1Yc4BXh9uEbwAXuyi2iQI7DuSwHdiFqCEGDtizLRFUDsaGAlQIbVoJYIEDAIiZBAAAh+QQJCQAbACwAAAAAIAAgAIQEAgSMioxcWlz08vQcHhysqqwMDgx8enwsKiykoqRkZmT8+vzEwsQMCgyUlpQkJiS0srQEBgSMjoxcXlz09vQkIiSsrqwUEhQ0MjRsamz8/vwAAAAAAAAAAAAAAAAAAAAF7+AmjmRpnmiqruz2PG0sIssCj4CQJAIgj4/abRNJaI6agu9kCAQaphdJgEQKUIFjgGWsahJYLdf7RTWfLKr3+jsBClVlG5Xb9eb4fImgUBBKDVB4ExRHFGwbGRQLGXMEhUgUfw2QC4IyCmSNDQtHlm2ZXgoiGQsUjW0EnUgLfyKBeYSeiHojfH61uS0GBisVEgEVLRcWRxAXKAgDRwMILMVIECgSVRIrBmS9JtRI1iMVBweuGxerSNolyszOIhjLGs0jEFXSKA8SEkMbcEgWIxfzNBxrw6AKgxIGkM05UOWALhERHJhysOThBgAVWYQAACH5BAkJABkALAAAAAAgACAAhAQGBIyKjERCRMzOzCwuLGRiZPz6/OTm5AwODLSytFRSVNTW1Dw6PHx6fAwKDJSSlERGRNTS1DQyNGxqbPz+/BQSFLy6vFRWVNza3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAXqYCaO5FgFwxBUZeu61ULNFMa+eBvQdJD/owFvFhkBBAwHsBQZUooZyWF2YOQkBNJu6ANMaQeli0AxSEwymi0DcUJeEgPlbEJFAghRe/h+Eeg/Dl9UYks5DF9VhksOAgKFi5GSSwh5kzgVCXIJNxknD5aSCTwJIw8zD5MITpanFKmSCHI8NxUPoJejNKWXLZkznL0vCJ3CxsckDpA/ChYJFzkTBgYTSxc80C4OswbLLhY8Fi/bMwYAJVgl4DTiL9LUJADrFuci1zTZLwD1IwU8BSQuWLCQb1EDHg2QiSDALYvCDAISJLDy8FIIACH5BAkJAB4ALAAAAAAgACAAhAQGBISGhFRSVNTW1CQiJKyqrGRmZOzu7CwuLIyOjGxubPz6/BQSFGRiZOTi5CwqLLy6vDQ2NIyKjFRWVCQmJKyurGxqbPT29DQyNJSSlHRydPz+/BQWFOzq7AAAAAAAAAXhoCeOJElYClGubOs117YtjWuvxCLLi3qbhc6h4FPsdorfiNI5dige43GT9AAkHUcCwCpMNxVP7tgTJY4J1uF7EBl0M8Ooueuo2SOCIkVa11kVX2E2EmgsFH4yBz4uAAkdHVstBAUHQ4xKmZqbnJ2bAhAQAiURGJ4eE0cTIxgzpp0QRxCsrp6xO7MjpaepO6unKxOhv8DFxsfIJBwaChw2DAkZDEocDjIOzi0ZMhlKUjIaLtsb3T8aR+EtDBkJ0yQUBQVQI9XX2ZsDMgMlyxr3mzE2XEgmotCGAARFIHiQ0FMIACH5BAkJABgALAAAAAAgACAAhAQCBISGhDw+POTi5CwuLLS2tPTy9BQSFJyenGRiZDQ2NIyOjLy+vPz6/BweHIyKjFRSVOzq7DQyNLy6vBQWFHRydDw6PPz+/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXXICaOZHkcZaquIjVd10SxtFrAcFGrVhBYIwoON9uNAsOA6DCEFTEKBEKxEjQvAtELNxkpGrAGNfW4Plpb2QgxRKjKzfPoVGLj3CnLNUv7hscpSDhKOxJSgDwPP0ZGAACMjAQFDQYFBJA0BAZDBpeYGBQVFUU3TV2YFAMwAzNgTQ2PkBVDFRiuQ7CYszi1pUOnkKmrM5qcnqiiTwQTDQ2Wn9DR0tPUfRKQEBEREDQSFw3XRhEwEd3f4TvjF+XWKgJ8JNnb0QkwCdUlCzAL+CQODAwc9BtIMAQAOw==") !important;
}
#toast-container > .toast-error {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important;
}
#toast-container > .toast-success {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==") !important;
}
#toast-container > .toast-warning {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=") !important;
}
#toast-container.toast-top-full-width > div,
#toast-container.toast-bottom-full-width > div {
width: 96%;
margin: auto;
}
.toast {
background-color: #030303;
}
.toast-success {
background-color: #51a351;
}
.toast-error {
background-color: #bd362f;
}
.toast-info {
background-color: #2f96b4;
}
.toast-wait {
background-color: #2f96b4;
}
.toast-warning {
background-color: #f89406;
}
#toast-container > .toast-info {
background-color: #404040;
border-radius: 3px;
box-shadow: 0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24);
color: #fff;
line-height: 20px;
padding: 16px;
font-size: 13px;
text-align: center;
}
/*Responsive Design*/
@media all and (max-width: 240px) {
#toast-container > div {
padding: 8px 8px 8px 50px;
width: 11em;
}
#toast-container .toast-close-button {
right: -0.2em;
top: -0.2em;
}
}
@media all and (min-width: 241px) and (max-width: 480px) {
#toast-container > div {
padding: 8px 8px 8px 50px;
width: 18em;
}
#toast-container .toast-close-button {
right: -0.2em;
top: -0.2em;
}
}
@media all and (min-width: 481px) and (max-width: 768px) {
#toast-container > div {
padding: 15px 15px 15px 50px;
width: 25em;
}
}
/*
* AngularJS-Toaster
* Version 0.3
*/
:not(.no-enter)#toast-container > div.ng-enter,
:not(.no-leave)#toast-container > div.ng-leave
{
-webkit-transition: 1000ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
-moz-transition: 1000ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
-ms-transition: 1000ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
-o-transition: 1000ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
transition: 1000ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
}
:not(.no-enter)#toast-container > div.ng-enter.ng-enter-active,
:not(.no-leave)#toast-container > div.ng-leave {
opacity: 0.8;
}
:not(.no-leave)#toast-container > div.ng-leave.ng-leave-active,
:not(.no-enter)#toast-container > div.ng-enter {
opacity: 0;
}

507
app/vendor/angularjs-toaster/toaster.js vendored

@ -0,0 +1,507 @@ @@ -0,0 +1,507 @@
/* global angular */
(function(window, document) {
'use strict';
/*
* AngularJS Toaster
* Version: 2.0.0
*
* Copyright 2013-2016 Jiri Kavulak.
* All Rights Reserved.
* Use, reproduction, distribution, and modification of this code is subject to the terms and
* conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php
*
* Author: Jiri Kavulak
* Related to project of John Papa, Hans Fjällemark and Nguyễn Thiện Hùng (thienhung1989)
*/
angular.module('toaster', []).constant(
'toasterConfig', {
'limit': 0, // limits max number of toasts
'tap-to-dismiss': true,
'close-button': false,
'close-html': '<button class="toast-close-button" type="button">&times;</button>',
'newest-on-top': true,
'time-out': 5000,
'icon-classes': {
error: 'toast-error',
info: 'toast-info',
wait: 'toast-wait',
success: 'toast-success',
warning: 'toast-warning'
},
'body-output-type': '', // Options: '', 'trustedHtml', 'template', 'templateWithData', 'directive'
'body-template': 'toasterBodyTmpl.html',
'icon-class': 'toast-info',
'position-class': 'toast-top-right', // Options (see CSS):
// 'toast-top-full-width', 'toast-bottom-full-width', 'toast-center',
// 'toast-top-left', 'toast-top-center', 'toast-top-right',
// 'toast-bottom-left', 'toast-bottom-center', 'toast-bottom-right',
'title-class': 'toast-title',
'message-class': 'toast-message',
'prevent-duplicates': false,
'mouseover-timer-stop': true // stop timeout on mouseover and restart timer on mouseout
}
).service(
'toaster', [
'$rootScope', 'toasterConfig', function($rootScope, toasterConfig) {
// http://stackoverflow.com/questions/26501688/a-typescript-guid-class
var Guid = (function() {
var Guid = {};
Guid.newGuid = function() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
};
return Guid;
}());
this.pop = function(type, title, body, timeout, bodyOutputType, clickHandler, toasterId, showCloseButton, toastId, onHideCallback) {
if (angular.isObject(type)) {
var params = type; // Enable named parameters as pop argument
this.toast = {
type: params.type,
title: params.title,
body: params.body,
timeout: params.timeout,
bodyOutputType: params.bodyOutputType,
clickHandler: params.clickHandler,
showCloseButton: params.showCloseButton,
closeHtml: params.closeHtml,
toastId: params.toastId,
onShowCallback: params.onShowCallback,
onHideCallback: params.onHideCallback,
directiveData: params.directiveData
};
toasterId = params.toasterId;
} else {
this.toast = {
type: type,
title: title,
body: body,
timeout: timeout,
bodyOutputType: bodyOutputType,
clickHandler: clickHandler,
showCloseButton: showCloseButton,
toastId: toastId,
onHideCallback: onHideCallback
};
}
if (!this.toast.toastId || !this.toast.toastId.length) {
this.toast.toastId = Guid.newGuid();
}
$rootScope.$emit('toaster-newToast', toasterId, this.toast.toastId);
return {
toasterId: toasterId,
toastId: this.toast.toastId
};
};
this.clear = function(toasterId, toastId) {
if (angular.isObject(toasterId)) {
$rootScope.$emit('toaster-clearToasts', toasterId.toasterId, toasterId.toastId);
} else {
$rootScope.$emit('toaster-clearToasts', toasterId, toastId);
}
};
// Create one method per icon class, to allow to call toaster.info() and similar
for (var type in toasterConfig['icon-classes']) {
this[type] = createTypeMethod(type);
}
function createTypeMethod(toasterType) {
return function(title, body, timeout, bodyOutputType, clickHandler, toasterId, showCloseButton, toastId, onHideCallback) {
if (angular.isString(title)) {
return this.pop(
toasterType,
title,
body,
timeout,
bodyOutputType,
clickHandler,
toasterId,
showCloseButton,
toastId,
onHideCallback);
} else { // 'title' is actually an object with options
return this.pop(angular.extend(title, { type: toasterType }));
}
};
}
}]
).factory(
'toasterEventRegistry', [
'$rootScope', function($rootScope) {
var deregisterNewToast = null, deregisterClearToasts = null, newToastEventSubscribers = [], clearToastsEventSubscribers = [], toasterFactory;
toasterFactory = {
setup: function() {
if (!deregisterNewToast) {
deregisterNewToast = $rootScope.$on(
'toaster-newToast', function(event, toasterId, toastId) {
for (var i = 0, len = newToastEventSubscribers.length; i < len; i++) {
newToastEventSubscribers[i](event, toasterId, toastId);
}
});
}
if (!deregisterClearToasts) {
deregisterClearToasts = $rootScope.$on(
'toaster-clearToasts', function(event, toasterId, toastId) {
for (var i = 0, len = clearToastsEventSubscribers.length; i < len; i++) {
clearToastsEventSubscribers[i](event, toasterId, toastId);
}
});
}
},
subscribeToNewToastEvent: function(onNewToast) {
newToastEventSubscribers.push(onNewToast);
},
subscribeToClearToastsEvent: function(onClearToasts) {
clearToastsEventSubscribers.push(onClearToasts);
},
unsubscribeToNewToastEvent: function(onNewToast) {
var index = newToastEventSubscribers.indexOf(onNewToast);
if (index >= 0) {
newToastEventSubscribers.splice(index, 1);
}
if (newToastEventSubscribers.length === 0) {
deregisterNewToast();
deregisterNewToast = null;
}
},
unsubscribeToClearToastsEvent: function(onClearToasts) {
var index = clearToastsEventSubscribers.indexOf(onClearToasts);
if (index >= 0) {
clearToastsEventSubscribers.splice(index, 1);
}
if (clearToastsEventSubscribers.length === 0) {
deregisterClearToasts();
deregisterClearToasts = null;
}
}
};
return {
setup: toasterFactory.setup,
subscribeToNewToastEvent: toasterFactory.subscribeToNewToastEvent,
subscribeToClearToastsEvent: toasterFactory.subscribeToClearToastsEvent,
unsubscribeToNewToastEvent: toasterFactory.unsubscribeToNewToastEvent,
unsubscribeToClearToastsEvent: toasterFactory.unsubscribeToClearToastsEvent
};
}]
)
.directive('directiveTemplate', ['$compile', '$injector', function($compile, $injector) {
return {
restrict: 'A',
scope: {
directiveName: '@directiveName',
directiveData: '@directiveData'
},
replace: true,
link: function(scope, elm, attrs) {
scope.$watch('directiveName', function(directiveName) {
if (angular.isUndefined(directiveName) || directiveName.length <= 0)
throw new Error('A valid directive name must be provided via the toast body argument when using bodyOutputType: directive');
var directive;
try {
directive = $injector.get(attrs.$normalize(directiveName) + 'Directive');
} catch (e) {
throw new Error(directiveName + ' could not be found. ' +
'The name should appear as it exists in the markup, not camelCased as it would appear in the directive declaration,' +
' e.g. directive-name not directiveName.');
}
var directiveDetails = directive[0];
if (directiveDetails.scope !== true && directiveDetails.scope) {
throw new Error('Cannot use a directive with an isolated scope. ' +
'The scope must be either true or falsy (e.g. false/null/undefined). ' +
'Occurred for directive ' + directiveName + '.');
}
if (directiveDetails.restrict.indexOf('A') < 0) {
throw new Error('Directives must be usable as attributes. ' +
'Add "A" to the restrict option (or remove the option entirely). Occurred for directive ' +
directiveName + '.');
}
if (scope.directiveData)
scope.directiveData = angular.fromJson(scope.directiveData);
var template = $compile('<div ' + directiveName + '></div>')(scope);
elm.append(template);
});
}
};
}])
.directive(
'toasterContainer', [
'$parse', '$rootScope', '$interval', '$sce', 'toasterConfig', 'toaster', 'toasterEventRegistry',
function($parse, $rootScope, $interval, $sce, toasterConfig, toaster, toasterEventRegistry) {
return {
replace: true,
restrict: 'EA',
scope: true, // creates an internal scope for this directive (one per directive instance)
link: function(scope, elm, attrs) {
var mergedConfig;
// Merges configuration set in directive with default one
mergedConfig = angular.extend({}, toasterConfig, scope.$eval(attrs.toasterOptions));
scope.config = {
toasterId: mergedConfig['toaster-id'],
position: mergedConfig['position-class'],
title: mergedConfig['title-class'],
message: mergedConfig['message-class'],
tap: mergedConfig['tap-to-dismiss'],
closeButton: mergedConfig['close-button'],
closeHtml: mergedConfig['close-html'],
animation: mergedConfig['animation-class'],
mouseoverTimer: mergedConfig['mouseover-timer-stop']
};
scope.$on(
"$destroy", function() {
toasterEventRegistry.unsubscribeToNewToastEvent(scope._onNewToast);
toasterEventRegistry.unsubscribeToClearToastsEvent(scope._onClearToasts);
}
);
function setTimeout(toast, time) {
toast.timeoutPromise = $interval(
function() {
scope.removeToast(toast.toastId);
}, time, 1
);
}
scope.configureTimer = function(toast) {
var timeout = angular.isNumber(toast.timeout) ? toast.timeout : mergedConfig['time-out'];
if (typeof timeout === "object") timeout = timeout[toast.type];
if (timeout > 0) {
setTimeout(toast, timeout);
}
};
function addToast(toast, toastId) {
toast.type = mergedConfig['icon-classes'][toast.type];
if (!toast.type) {
toast.type = mergedConfig['icon-class'];
}
if (mergedConfig['prevent-duplicates'] === true && scope.toasters.length) {
if (scope.toasters[scope.toasters.length - 1].body === toast.body) {
return;
} else {
var i, len, dupFound = false;
for (i = 0, len = scope.toasters.length; i < len; i++) {
if (scope.toasters[i].toastId === toastId) {
dupFound = true;
break;
}
}
if (dupFound) return;
}
}
// set the showCloseButton property on the toast so that
// each template can bind directly to the property to show/hide
// the close button
var closeButton = mergedConfig['close-button'];
// if toast.showCloseButton is a boolean value,
// it was specifically overriden in the pop arguments
if (typeof toast.showCloseButton === "boolean") {
} else if (typeof closeButton === "boolean") {
toast.showCloseButton = closeButton;
} else if (typeof closeButton === "object") {
var closeButtonForType = closeButton[toast.type];
if (typeof closeButtonForType !== "undefined" && closeButtonForType !== null) {
toast.showCloseButton = closeButtonForType;
}
} else {
// if an option was not set, default to false.
toast.showCloseButton = false;
}
if (toast.showCloseButton) {
toast.closeHtml = $sce.trustAsHtml(toast.closeHtml || scope.config.closeHtml);
}
// Set the toast.bodyOutputType to the default if it isn't set
toast.bodyOutputType = toast.bodyOutputType || mergedConfig['body-output-type'];
switch (toast.bodyOutputType) {
case 'trustedHtml':
toast.html = $sce.trustAsHtml(toast.body);
break;
case 'template':
toast.bodyTemplate = toast.body || mergedConfig['body-template'];
break;
case 'templateWithData':
var fcGet = $parse(toast.body || mergedConfig['body-template']);
var templateWithData = fcGet(scope);
toast.bodyTemplate = templateWithData.template;
toast.data = templateWithData.data;
break;
case 'directive':
toast.html = toast.body;
break;
}
scope.configureTimer(toast);
if (mergedConfig['newest-on-top'] === true) {
scope.toasters.unshift(toast);
if (mergedConfig['limit'] > 0 && scope.toasters.length > mergedConfig['limit']) {
scope.toasters.pop();
}
} else {
scope.toasters.push(toast);
if (mergedConfig['limit'] > 0 && scope.toasters.length > mergedConfig['limit']) {
scope.toasters.shift();
}
}
if (angular.isFunction(toast.onShowCallback)) {
toast.onShowCallback();
}
}
scope.removeToast = function(toastId) {
var i, len;
for (i = 0, len = scope.toasters.length; i < len; i++) {
if (scope.toasters[i].toastId === toastId) {
removeToast(i);
break;
}
}
};
function removeToast(toastIndex) {
var toast = scope.toasters[toastIndex];
// toast is always defined since the index always has a match
if (toast.timeoutPromise) {
$interval.cancel(toast.timeoutPromise);
}
scope.toasters.splice(toastIndex, 1);
if (angular.isFunction(toast.onHideCallback)) {
toast.onHideCallback();
}
}
function removeAllToasts(toastId) {
for (var i = scope.toasters.length - 1; i >= 0; i--) {
if (isUndefinedOrNull(toastId)) {
removeToast(i);
} else {
if (scope.toasters[i].toastId == toastId) {
removeToast(i);
}
}
}
}
scope.toasters = [];
function isUndefinedOrNull(val) {
return angular.isUndefined(val) || val === null;
}
scope._onNewToast = function(event, toasterId, toastId) {
// Compatibility: if toaster has no toasterId defined, and if call to display
// hasn't either, then the request is for us
if ((isUndefinedOrNull(scope.config.toasterId) && isUndefinedOrNull(toasterId)) || (!isUndefinedOrNull(scope.config.toasterId) && !isUndefinedOrNull(toasterId) && scope.config.toasterId == toasterId)) {
addToast(toaster.toast, toastId);
}
};
scope._onClearToasts = function(event, toasterId, toastId) {
// Compatibility: if toaster has no toasterId defined, and if call to display
// hasn't either, then the request is for us
if (toasterId == '*' || (isUndefinedOrNull(scope.config.toasterId) && isUndefinedOrNull(toasterId)) || (!isUndefinedOrNull(scope.config.toasterId) && !isUndefinedOrNull(toasterId) && scope.config.toasterId == toasterId)) {
removeAllToasts(toastId);
}
};
toasterEventRegistry.setup();
toasterEventRegistry.subscribeToNewToastEvent(scope._onNewToast);
toasterEventRegistry.subscribeToClearToastsEvent(scope._onClearToasts);
},
controller: [
'$scope', '$element', '$attrs', function($scope, $element, $attrs) {
// Called on mouseover
$scope.stopTimer = function(toast) {
if ($scope.config.mouseoverTimer === true) {
if (toast.timeoutPromise) {
$interval.cancel(toast.timeoutPromise);
toast.timeoutPromise = null;
}
}
};
// Called on mouseout
$scope.restartTimer = function(toast) {
if ($scope.config.mouseoverTimer === true) {
if (!toast.timeoutPromise) {
$scope.configureTimer(toast);
}
} else if (toast.timeoutPromise === null) {
$scope.removeToast(toast.toastId);
}
};
$scope.click = function(toast, isCloseButton) {
if ($scope.config.tap === true || (toast.showCloseButton === true && isCloseButton === true)) {
var removeToast = true;
if (toast.clickHandler) {
if (angular.isFunction(toast.clickHandler)) {
removeToast = toast.clickHandler(toast, isCloseButton);
} else if (angular.isFunction($scope.$parent.$eval(toast.clickHandler))) {
removeToast = $scope.$parent.$eval(toast.clickHandler)(toast, isCloseButton);
} else {
console.log("TOAST-NOTE: Your click handler is not inside a parent scope of toaster-container.");
}
}
if (removeToast) {
$scope.removeToast(toast.toastId);
}
}
};
}],
template:
'<div id="toast-container" ng-class="[config.position, config.animation]">' +
'<div ng-repeat="toaster in toasters" class="toast" ng-class="toaster.type" ng-click="click(toaster)" ng-mouseover="stopTimer(toaster)" ng-mouseout="restartTimer(toaster)">' +
'<div ng-if="toaster.showCloseButton" ng-click="click(toaster, true)" ng-bind-html="toaster.closeHtml"></div>' +
'<div ng-class="config.title">{{toaster.title}}</div>' +
'<div ng-class="config.message" ng-switch on="toaster.bodyOutputType">' +
'<div ng-switch-when="trustedHtml" ng-bind-html="toaster.html"></div>' +
'<div ng-switch-when="template"><div ng-include="toaster.bodyTemplate"></div></div>' +
'<div ng-switch-when="templateWithData"><div ng-include="toaster.bodyTemplate"></div></div>' +
'<div ng-switch-when="directive"><div directive-template directive-name="{{toaster.html}}" directive-data="{{toaster.directiveData}}"></div></div>' +
'<div ng-switch-default >{{toaster.body}}</div>' +
'</div>' +
'</div>' +
'</div>'
};
}]
);
})(window, document);

12
app/vendor/angularjs-toaster/toaster.min.css vendored

File diff suppressed because one or more lines are too long

13
app/vendor/angularjs-toaster/toaster.min.js vendored

File diff suppressed because one or more lines are too long

2
app/vendor/libwebpjs/libwebp-0.2.0.js vendored

@ -4076,4 +4076,4 @@ @@ -4076,4 +4076,4 @@
}
};
})()
})();

2
package.json

@ -48,7 +48,7 @@ @@ -48,7 +48,7 @@
"gulp-concat": "^2.1.7",
"gulp-grep-stream": "0.0.2",
"gulp-imagemin": "^2.3.0",
"gulp-less": "^3.0.2",
"gulp-less": "^3.0.5",
"gulp-livereload": "^3.0.2",
"gulp-load-plugins": "^0.4.0",
"gulp-manifest": "0.0.3",

Loading…
Cancel
Save