Browse Source

Game Platform

Basic support
Desktop only

Closes #1246
Closes #1241
master
Igor Zhukov 8 years ago
parent
commit
cd70b7e65b
  1. 47
      app/js/controllers.js
  2. 120
      app/js/directives.js
  3. 4
      app/js/lib/config.js
  4. 3
      app/js/lib/mtproto_wrapper.js
  5. 4
      app/js/lib/ng_utils.js
  6. 115
      app/js/lib/schema.tl.txt
  7. 4
      app/js/locales/en-us.json
  8. 88
      app/js/messages_manager.js
  9. 213
      app/js/services.js
  10. 5
      app/less/app.less
  11. 1
      app/partials/desktop/confirm_modal.html
  12. 35
      app/partials/desktop/game_modal.html
  13. 6
      app/partials/desktop/inline_results.html
  14. 17
      app/partials/desktop/message_attach_game.html
  15. 1
      app/partials/desktop/message_media.html
  16. 8
      app/partials/desktop/message_service.html
  17. 10
      app/partials/desktop/short_message.html

47
app/js/controllers.js

@ -171,7 +171,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -171,7 +171,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.sendCode = function () {
$timeout.cancel(nextTimeout)
var fullPhone = ($scope.credentials.phone_country || '') + ($scope.credentials.phone_number || '');
var fullPhone = ($scope.credentials.phone_country || '') + ($scope.credentials.phone_number || '')
var badPhone = !fullPhone.match(/^[\d\-+\s]+$/);
if (!badPhone) {
fullPhone = fullPhone.replace(/\D/g, '');
@ -3166,8 +3166,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -3166,8 +3166,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
ErrorService.confirm({type: 'PHOTO_DELETE'}).then(function () {
if (myUser && myUser.photo && myUser.photo.photo_id == photoID) {
MtpApiManager.invokeApi('photos.updateProfilePhoto', {
id: {_: 'inputPhotoEmpty'},
crop: {_: 'inputPhotoCropAuto'}
id: {_: 'inputPhotoEmpty'}
}).then(function (updateResult) {
ApiUpdatesManager.processUpdateMessage({
_: 'updateShort',
@ -3348,6 +3347,36 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -3348,6 +3347,36 @@ angular.module('myApp.controllers', ['myApp.i18n'])
}
})
.controller('GameModalController', function ($q, $scope, $rootScope, $modalInstance, AppPhotosManager, AppMessagesManager, AppPeersManager, AppGamesManager, PeersSelectService, ErrorService) {
$scope.game = AppGamesManager.wrapForFull($scope.gameID, $scope.messageID, $scope.embedUrl)
var messageID = $scope.messageID
var message = AppMessagesManager.getMessage(messageID)
$scope.botID = message.viaBotID || message.fromID
$scope.nav = {}
$scope.forward = function (withMyScore) {
PeersSelectService.selectPeer({canSend: true, confirm_type: 'INVITE_TO_GAME'}).then(function (peerString) {
var peerID = AppPeersManager.getPeerID(peerString)
AppMessagesManager.forwardMessages(peerID, [messageID], {
withMyScore: withMyScore
}).then(function () {
$rootScope.$broadcast('history_focus', {
peerString: peerString
})
})
})
}
$scope.$on('game_frame_event', function (e, eventData) {
if (eventData.eventType == 'share_score') {
$scope.forward(true)
}
})
})
.controller('UserModalController', function ($scope, $location, $rootScope, $modalInstance, AppProfileManager, $modal, AppUsersManager, MtpApiManager, NotificationsManager, AppPhotosManager, AppMessagesManager, AppPeersManager, PeersSelectService, ErrorService) {
var peerString = AppUsersManager.getUserString($scope.userID)
@ -3597,8 +3626,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -3597,8 +3626,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
chat_id: AppChatsManager.getChatInput($scope.chatID),
photo: {
_: 'inputChatUploadedPhoto',
file: inputFile,
crop: {_: 'inputPhotoCropAuto'}
file: inputFile
}
}).then(onChatUpdated)
})['finally'](function () {
@ -3753,8 +3781,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -3753,8 +3781,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
channel: AppChatsManager.getChannelInput($scope.chatID),
photo: {
_: 'inputChatUploadedPhoto',
file: inputFile,
crop: {_: 'inputPhotoCropAuto'}
file: inputFile
}
}).then(onChatUpdated)
})['finally'](function () {
@ -3876,8 +3903,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -3876,8 +3903,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
MtpApiManager.invokeApi('photos.uploadProfilePhoto', {
file: inputFile,
caption: '',
geo_point: {_: 'inputGeoPointEmpty'},
crop: {_: 'inputPhotoCropAuto'}
geo_point: {_: 'inputGeoPointEmpty'}
}).then(function (updateResult) {
AppUsersManager.saveApiUsers(updateResult.users)
MtpApiManager.getUserID().then(function (id) {
@ -3905,8 +3931,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -3905,8 +3931,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$scope.deletePhoto = function () {
$scope.photo.updating = true
MtpApiManager.invokeApi('photos.updateProfilePhoto', {
id: {_: 'inputPhotoEmpty'},
crop: {_: 'inputPhotoCropAuto'}
id: {_: 'inputPhotoEmpty'}
}).then(function (updateResult) {
MtpApiManager.getUserID().then(function (id) {
ApiUpdatesManager.processUpdateMessage({

120
app/js/directives.js

@ -187,31 +187,13 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -187,31 +187,13 @@ angular.module('myApp.directives', ['myApp.filters'])
}
function updateMessageText ($scope, element, message) {
if (typeof message.message !== 'string' ||
if (message.media && message.media.handleMessage ||
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)
var withBot = (fromBot ||
toPeerID < 0 && !(AppChatsManager.isChannel(-toPeerID) && !AppChatsManager.isMegagroup(-toPeerID)) ||
toPeerID > 0 && AppUsersManager.isBot(toPeerID))
var options = {
noCommands: !withBot,
fromBot: fromBot,
entities: message.totalEntities
}
if (message.pFlags.mentioned) {
var user = AppUsersManager.getSelf()
if (user) {
options.highlightUsername = user.username
}
}
var html = RichTextProcessor.wrapRichText(message.message, options)
var html = AppMessagesManager.wrapMessageText(message.id)
$('.im_message_text', element).html(html.valueOf())
}
@ -264,6 +246,9 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -264,6 +246,9 @@ angular.module('myApp.directives', ['myApp.filters'])
case 'keyboardButtonCallback':
AppInlineBotsManager.callbackButtonClick(message.mid, button)
break
case 'keyboardButtonGame':
AppInlineBotsManager.gameButtonClick(message.mid)
break
}
})
}
@ -458,6 +443,40 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -458,6 +443,40 @@ angular.module('myApp.directives', ['myApp.filters'])
}
}
})
.directive('myMessageGame', function (AppInlineBotsManager, AppMessagesManager) {
return {
scope: {
'media': '=myMessageGame',
'messageId': '=messageId'
},
templateUrl: templateUrl('message_attach_game'),
link: function ($scope, element) {
$scope.openGame = function () {
AppInlineBotsManager.gameButtonClick($scope.messageId)
}
function updateMessageText(argument) {
var message = AppMessagesManager.getMessage($scope.messageId)
if (message.message) {
var html = AppMessagesManager.wrapMessageText($scope.messageId)
$('.im_message_game_message', element).html(html.valueOf()).show()
$('.im_message_game_description', element).hide()
} else {
$('.im_message_game_message', element).html('').hide()
$('.im_message_game_description', element).show()
}
}
$scope.$on('message_edit', function (e, data) {
if (data.mid == $scope.messageId) {
updateMessageText()
}
})
updateMessageText()
}
}
})
.directive('myMessagePending', function () {
return {
scope: {
@ -2112,6 +2131,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -2112,6 +2131,7 @@ angular.module('myApp.directives', ['myApp.filters'])
access_hash: $scope.document.access_hash,
dc_id: $scope.document.dc_id,
file_name: $scope.document.file_name,
version: $scope.document.version,
sticker: true
}
@ -3415,12 +3435,70 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -3415,12 +3435,70 @@ angular.module('myApp.directives', ['myApp.filters'])
size: photoSize.size
}
}
if (result.type == 'game' && result.photo) {
var photoSize = AppPhotosManager.choosePhotoSize(result.photo, 100, 100)
// var dim = calcImageInBox(photoSize.w, photoSize.h, result.thumbW, result.thumbH)
result.thumb = {
// width: dim.w,
// height: dim.h,
location: photoSize.location,
size: photoSize.size
}
}
})
})
}
}
})
.directive('myGameCommunication', function ($window) {
function link ($scope, element, attrs) {
onContentLoaded(function () {
var iframe = $('iframe, webview', element)[0]
var contentWindow = iframe.contentWindow
var handler = function (event) {
event = event.originalEvent || event
if (event.source && event.source != contentWindow) {
return
}
var data = event.data
try {
var dataParsed = JSON.parse(data)
} catch (e) {
return
}
if (!dataParsed || !dataParsed.eventType) {
return
}
$scope.$emit('game_frame_event', dataParsed);
}
$($window).on('message', handler)
$scope.$on('$destroy', function () {
$($window).off('message', handler)
})
})
}
return {
link: link
}
})
.directive('myEmojiImage', function (RichTextProcessor) {
function link ($scope, element, attrs) {
var emoji = attrs.myEmojiImage
var html = RichTextProcessor.wrapRichText(emoji, {noLinks: true, noLinebreaks: true})
element.html(html.valueOf())
}
return {
link: link
}
})
.directive('myExternalEmbed', function () {
var twitterAttached = false
var facebookAttached = false

4
app/js/lib/config.js

File diff suppressed because one or more lines are too long

3
app/js/lib/mtproto_wrapper.js

@ -363,7 +363,8 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) @@ -363,7 +363,8 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto'])
if (location.sticker && !WebpManager.isWebpSupported()) {
ext += '.png'
}
return fileName[0] + '_' + location.id + '.' + ext
var versionPart = location.version ? ('v' + location.version) : ''
return fileName[0] + '_' + location.id + versionPart + '.' + ext
default:
if (!location.volume_id) {

4
app/js/lib/ng_utils.js

@ -1068,9 +1068,9 @@ angular.module('izhukov.utils', []) @@ -1068,9 +1068,9 @@ angular.module('izhukov.utils', [])
}, 10)
}
var debounceTimeout = $rootScope.idle.initial ? 0 : 1000;
var debounceTimeout = $rootScope.idle.initial ? 0 : 1000
if (e && !e.fake_initial) {
delete $rootScope.idle.initial;
delete $rootScope.idle.initial
}
$timeout.cancel(debouncePromise)

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

@ -25,19 +25,22 @@ inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile @@ -25,19 +25,22 @@ inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile
inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile;
inputMediaEmpty#9664f57f = InputMedia;
inputMediaUploadedPhoto#f7aff1c0 file:InputFile caption:string = InputMedia;
inputMediaUploadedPhoto#630c9af1 flags:# file:InputFile caption:string stickers:flags.0?Vector<InputDocument> = 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;
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;
inputMediaUploadedDocument#d070f1e9 flags:# file:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string stickers:flags.0?Vector<InputDocument> = InputMedia;
inputMediaUploadedThumbDocument#50d88cae flags:# file:InputFile thumb:InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string stickers:flags.0?Vector<InputDocument> = InputMedia;
inputMediaDocument#1a77f29c id:InputDocument caption:string = InputMedia;
inputMediaVenue#2827a81a geo_point:InputGeoPoint title:string address:string provider:string venue_id:string = InputMedia;
inputMediaGifExternal#4843b0fd url:string q:string = InputMedia;
inputMediaPhotoExternal#b55f4f18 url:string caption:string = InputMedia;
inputMediaDocumentExternal#e5e9607c url:string caption:string = InputMedia;
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
inputChatUploadedPhoto#94254732 file:InputFile crop:InputPhotoCrop = InputChatPhoto;
inputChatPhoto#b2e1bf08 id:InputPhoto crop:InputPhotoCrop = InputChatPhoto;
inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
inputChatPhoto#8953ad37 id:InputPhoto = InputChatPhoto;
inputGeoPointEmpty#e4c123d6 = InputGeoPoint;
inputGeoPoint#f3b7acc9 lat:double long:double = InputGeoPoint;
@ -47,10 +50,7 @@ inputPhoto#fb95c6c4 id:long access_hash:long = InputPhoto; @@ -47,10 +50,7 @@ inputPhoto#fb95c6c4 id:long access_hash:long = InputPhoto;
inputFileLocation#14637196 volume_id:long local_id:int secret:long = InputFileLocation;
inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation;
inputDocumentFileLocation#4e45abe9 id:long access_hash:long = InputFileLocation;
inputPhotoCropAuto#ade6b004 = InputPhotoCrop;
inputPhotoCrop#d9915325 crop_left:double crop_top:double crop_width:double = InputPhotoCrop;
inputDocumentFileLocation#430f0724 id:long access_hash:long version:int = InputFileLocation;
inputAppEvent#770656a8 time:double type:string peer:long data:string = InputAppEvent;
@ -116,6 +116,7 @@ messageMediaUnsupported#9f84f49e = MessageMedia; @@ -116,6 +116,7 @@ messageMediaUnsupported#9f84f49e = MessageMedia;
messageMediaDocument#f3e02ea8 document:Document caption:string = MessageMedia;
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
messageMediaVenue#7912b71f geo:GeoPoint title:string address:string provider:string venue_id:string = MessageMedia;
messageMediaGame#fdb19008 game:Game = MessageMedia;
messageActionEmpty#b6aef7b0 = MessageAction;
messageActionChatCreate#a6638b9a title:string users:Vector<int> = MessageAction;
@ -130,11 +131,12 @@ messageActionChatMigrateTo#51bdb021 channel_id:int = MessageAction; @@ -130,11 +131,12 @@ messageActionChatMigrateTo#51bdb021 channel_id:int = MessageAction;
messageActionChannelMigrateFrom#b055eaee title:string chat_id:int = MessageAction;
messageActionPinMessage#94bd38ed = MessageAction;
messageActionHistoryClear#9fbab604 = MessageAction;
messageActionGameScore#92a72876 game_id:long score:int = MessageAction;
dialog#66ffba14 flags:# peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
photoEmpty#2331b22d id:long = Photo;
photo#cded42fe id:long access_hash:long date:int sizes:Vector<PhotoSize> = Photo;
photo#9288dd29 flags:# has_stickers:flags.0?true id:long access_hash:long date:int sizes:Vector<PhotoSize> = Photo;
photoSizeEmpty#e17e23c type:string = PhotoSize;
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
@ -147,7 +149,7 @@ auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone; @@ -147,7 +149,7 @@ auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone;
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;
auth.authorization#cd050916 flags:# tmp_sessions:flags.0?int user:User = auth.Authorization;
auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorization;
@ -259,18 +261,22 @@ updateChannelMessageViews#98a12b4b channel_id:int id:int views:int = Update; @@ -259,18 +261,22 @@ updateChannelMessageViews#98a12b4b channel_id:int id:int views:int = Update;
updateChatAdmins#6e947941 chat_id:int enabled:Bool version:int = Update;
updateChatParticipantAdmin#b6901959 chat_id:int user_id:int is_admin:Bool version:int = Update;
updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update;
updateStickerSetsOrder#f0dfb451 order:Vector<long> = Update;
updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true order:Vector<long> = Update;
updateStickerSets#43ae3dec = Update;
updateSavedGifs#9375341e = 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;
updateBotCallbackQuery#e73547e1 flags:# query_id:long user_id:int peer:Peer msg_id:int chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = 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;
updateInlineBotCallbackQuery#f9d27a5a flags:# query_id:long user_id:int msg_id:InputBotInlineMessageID chat_instance:long data:flags.0?bytes game_short_name:flags.1?string = Update;
updateReadChannelOutbox#25d6c9c7 channel_id:int max_id:int = Update;
updateDraftMessage#ee2bb969 peer:Peer draft:DraftMessage = Update;
updateReadFeaturedStickers#571d2742 = Update;
updateRecentStickers#9a422c20 = Update;
updateConfig#a229dd06 = Update;
updatePtsChanged#3354678f = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -295,7 +301,7 @@ upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File; @@ -295,7 +301,7 @@ upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
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#c9411388 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 rating_e_decay:int disabled_features:Vector<DisabledFeature> = Config;
config#9a6b2e2a flags:# 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 rating_e_decay:int stickers_recent_limit:int tmp_sessions:flags.0?int disabled_features:Vector<DisabledFeature> = Config;
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
@ -333,7 +339,7 @@ inputDocumentEmpty#72f0eaae = InputDocument; @@ -333,7 +339,7 @@ inputDocumentEmpty#72f0eaae = InputDocument;
inputDocument#18798952 id:long access_hash:long = InputDocument;
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;
document#87232bc7 id:long access_hash:long date:int mime_type:string size:int thumb:PhotoSize dc_id:int version:int attributes:Vector<DocumentAttribute> = Document;
help.support#17c6b5f6 phone_number:string user:User = help.Support;
@ -352,6 +358,7 @@ sendMessageUploadPhotoAction#d1d34a26 progress:int = SendMessageAction; @@ -352,6 +358,7 @@ sendMessageUploadPhotoAction#d1d34a26 progress:int = SendMessageAction;
sendMessageUploadDocumentAction#aa0cd9e4 progress:int = SendMessageAction;
sendMessageGeoLocationAction#176f8ba1 = SendMessageAction;
sendMessageChooseContactAction#628cbc6f = SendMessageAction;
sendMessageGamePlayAction#dd6a8f48 = SendMessageAction;
contacts.found#1aa1f784 results:Vector<Peer> chats:Vector<Chat> users:Vector<User> = contacts.Found;
@ -381,10 +388,11 @@ accountDaysTTL#b8d0afdf days:int = AccountDaysTTL; @@ -381,10 +388,11 @@ accountDaysTTL#b8d0afdf days:int = AccountDaysTTL;
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
documentAttributeAnimated#11b58939 = DocumentAttribute;
documentAttributeSticker#3a556302 alt:string stickerset:InputStickerSet = DocumentAttribute;
documentAttributeSticker#6319d612 flags:# mask:flags.1?true alt:string stickerset:InputStickerSet mask_coords:flags.0?MaskCoords = DocumentAttribute;
documentAttributeVideo#5910cccb duration:int w:int h:int = 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;
documentAttributeHasStickers#9801d2f7 = DocumentAttribute;
messages.stickersNotModified#f1749a22 = messages.Stickers;
messages.stickers#8a8ecd32 hash:string stickers:Vector<Document> = messages.Stickers;
@ -426,13 +434,13 @@ chatInviteEmpty#69df3769 = ExportedChatInvite; @@ -426,13 +434,13 @@ chatInviteEmpty#69df3769 = ExportedChatInvite;
chatInviteExported#fc2e05bc link:string = ExportedChatInvite;
chatInviteAlready#5a686d7c chat:Chat = ChatInvite;
chatInvite#93e99b60 flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string = ChatInvite;
chatInvite#db74f558 flags:# channel:flags.0?true broadcast:flags.1?true public:flags.2?true megagroup:flags.3?true title:string photo:ChatPhoto participants_count:int participants:flags.4?Vector<User> = ChatInvite;
inputStickerSetEmpty#ffb62b95 = InputStickerSet;
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
stickerSet#cd303b41 flags:# installed:flags.0?true disabled:flags.1?true official:flags.2?true id:long access_hash:long title:string short_name:string count:int hash:int = StickerSet;
stickerSet#cd303b41 flags:# installed:flags.0?true archived:flags.1?true official:flags.2?true masks:flags.3?true id:long access_hash:long title:string short_name:string count:int hash:int = StickerSet;
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
@ -445,7 +453,8 @@ keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton; @@ -445,7 +453,8 @@ 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;
keyboardButtonSwitchInline#568a748 flags:# same_peer:flags.0?true text:string query:string = KeyboardButton;
keyboardButtonGame#50f41ccf text:string = KeyboardButton;
keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
@ -520,10 +529,12 @@ inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true message:strin @@ -520,10 +529,12 @@ inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true message:strin
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;
inputBotInlineMessageGame#4b425864 flags:# 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;
inputBotInlineResultGame#4fa417f2 id:string short_name:string send_message:InputBotInlineMessage = InputBotInlineResult;
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;
@ -549,7 +560,7 @@ auth.sentCodeTypeSms#c000bba2 length:int = auth.SentCodeType; @@ -549,7 +560,7 @@ 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.botCallbackAnswer#b10df1fb flags:# alert:flags.1?true has_url:flags.3?true message:flags.0?string url:flags.2?string = messages.BotCallbackAnswer;
messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData;
@ -575,6 +586,34 @@ contacts.topPeers#70b772a8 categories:Vector<TopPeerCategoryPeers> chats:Vector< @@ -575,6 +586,34 @@ contacts.topPeers#70b772a8 categories:Vector<TopPeerCategoryPeers> chats:Vector<
draftMessageEmpty#ba4baec5 = DraftMessage;
draftMessage#fd8e711f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int message:string entities:flags.3?Vector<MessageEntity> date:int = DraftMessage;
messages.featuredStickersNotModified#4ede3cf = messages.FeaturedStickers;
messages.featuredStickers#f89d88e5 hash:int sets:Vector<StickerSetCovered> unread:Vector<long> = messages.FeaturedStickers;
messages.recentStickersNotModified#b17f890 = messages.RecentStickers;
messages.recentStickers#5ce20970 hash:int stickers:Vector<Document> = messages.RecentStickers;
messages.archivedStickers#4fcba9c8 count:int sets:Vector<StickerSetCovered> = messages.ArchivedStickers;
messages.stickerSetInstallResultSuccess#38641628 = messages.StickerSetInstallResult;
messages.stickerSetInstallResultArchive#35e410a8 sets:Vector<StickerSetCovered> = messages.StickerSetInstallResult;
stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered;
stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector<Document> = StickerSetCovered;
maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;
inputStickeredMediaPhoto#4a992157 id:InputPhoto = InputStickeredMedia;
inputStickeredMediaDocument#438865b id:InputDocument = InputStickeredMedia;
game#bdf9653b flags:# id:long access_hash:long short_name:string title:string description:string photo:Photo document:flags.0?Document = Game;
inputGameID#32c3e77 id:long access_hash:long = InputGame;
inputGameShortName#c331e80a bot_id:InputUser short_name:string = InputGame;
highScore#58fffcd0 pos:int user_id:int score:int = HighScore;
messages.highScores#9a3bfd99 scores:Vector<HighScore> users:Vector<User> = messages.HighScores;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -599,6 +638,7 @@ auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery; @@ -599,6 +638,7 @@ 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;
auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector<long> = Bool;
account.registerDevice#637ea878 token_type:int token:string = Bool;
account.unregisterDevice#65c55b40 token_type:int token:string = Bool;
@ -624,6 +664,8 @@ account.resetAuthorization#df77f3bc hash:long = Bool; @@ -624,6 +664,8 @@ account.resetAuthorization#df77f3bc hash:long = Bool;
account.getPassword#548a30f5 = account.Password;
account.getPasswordSettings#bc8d11bb current_password_hash:bytes = account.PasswordSettings;
account.updatePasswordSettings#fa7c4b86 current_password_hash:bytes new_settings:account.PasswordInputSettings = Bool;
account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode;
account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
@ -654,7 +696,7 @@ messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>; @@ -654,7 +696,7 @@ 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 silent:flags.5?true background:flags.6?true clear_draft:flags.7?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:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?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:# silent:flags.5?true background:flags.6?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer = Updates;
messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?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;
@ -666,7 +708,6 @@ messages.addChatUser#f9a0aa09 chat_id:int user_id:InputUser fwd_limit:int = Upda @@ -666,7 +708,6 @@ messages.addChatUser#f9a0aa09 chat_id:int user_id:InputUser fwd_limit:int = Upda
messages.deleteChatUser#e0611f16 chat_id:int user_id:InputUser = Updates;
messages.createChat#9cb126e users:Vector<InputUser> title:string = Updates;
messages.forwardMessage#33963bf9 peer:InputPeer id:int random_id:long = Updates;
messages.sendBroadcast#bf73f4da contacts:Vector<InputUser> random_id:Vector<long> message:string media:InputMedia = Updates;
messages.getDhConfig#26cf8950 version:int random_length:int = messages.DhConfig;
messages.requestEncryption#f64daf43 user_id:InputUser random_id:int g_a:bytes = EncryptedChat;
messages.acceptEncryption#3dbc0415 peer:InputEncryptedChat g_b:bytes key_fingerprint:long = EncryptedChat;
@ -678,14 +719,13 @@ messages.sendEncryptedFile#9a901b66 peer:InputEncryptedChat random_id:long data: @@ -678,14 +719,13 @@ messages.sendEncryptedFile#9a901b66 peer:InputEncryptedChat random_id:long data:
messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long data:bytes = messages.SentEncryptedMessage;
messages.receivedQueue#55a5bb66 max_qts:int = Vector<long>;
messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages;
messages.getStickers#ae22e045 emoticon:string hash:string = messages.Stickers;
messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers;
messages.getWebPagePreview#25223e24 message:string = MessageMedia;
messages.exportChatInvite#7d885289 chat_id:int = ExportedChatInvite;
messages.checkChatInvite#3eadb1bb hash:string = ChatInvite;
messages.importChatInvite#6c50051c hash:string = Updates;
messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet;
messages.installStickerSet#7b30c3a6 stickerset:InputStickerSet disabled:Bool = Bool;
messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult;
messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool;
messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates;
messages.getMessagesViews#c4c8a55d peer:InputPeer id:Vector<int> increment:Bool = Vector<int>;
@ -693,7 +733,7 @@ messages.toggleChatAdmins#ec8bd9e1 chat_id:int enabled:Bool = Updates; @@ -693,7 +733,7 @@ messages.toggleChatAdmins#ec8bd9e1 chat_id:int enabled:Bool = Updates;
messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool;
messages.migrateChat#15a3b8e3 chat_id:int = Updates;
messages.searchGlobal#9e3cacb0 q:string offset_date:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
messages.reorderStickerSets#9fcfbc30 order:Vector<long> = Bool;
messages.reorderStickerSets#78337739 flags:# masks:flags.0?true order:Vector<long> = Bool;
messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document;
messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs;
messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
@ -704,18 +744,30 @@ messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:fla @@ -704,18 +744,30 @@ messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:fla
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;
messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer;
messages.setBotCallbackAnswer#c927d44b flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string = Bool;
messages.getPeerDialogs#2d9776b9 peers:Vector<InputPeer> = messages.PeerDialogs;
messages.saveDraft#bc39e14b flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int peer:InputPeer message:string entities:flags.3?Vector<MessageEntity> = Bool;
messages.getAllDrafts#6a3f8d65 = Updates;
messages.getFeaturedStickers#2dacca4f hash:int = messages.FeaturedStickers;
messages.readFeaturedStickers#5b118126 id:Vector<long> = Bool;
messages.getRecentStickers#5ea192c9 flags:# attached:flags.0?true hash:int = messages.RecentStickers;
messages.saveRecentSticker#392718f8 flags:# attached:flags.0?true id:InputDocument unsave:Bool = Bool;
messages.clearRecentStickers#8999602d flags:# attached:flags.0?true = Bool;
messages.getArchivedStickers#57f17692 flags:# masks:flags.0?true offset_id:long limit:int = messages.ArchivedStickers;
messages.getMaskStickers#65b8c79f hash:int = messages.AllStickers;
messages.getAttachedStickers#cc5b67cc media:InputStickeredMedia = Vector<StickerSetCovered>;
messages.setGameScore#8ef8ecc0 flags:# edit_message:flags.0?true peer:InputPeer id:int user_id:InputUser score:int = Updates;
messages.setInlineGameScore#15ad9f64 flags:# edit_message:flags.0?true id:InputBotInlineMessageID user_id:InputUser score:int = Bool;
messages.getGameHighScores#e822649d peer:InputPeer id:int user_id:InputUser = messages.HighScores;
messages.getInlineGameHighScores#f635e1b id:InputBotInlineMessageID user_id:InputUser = messages.HighScores;
updates.getState#edd4882a = updates.State;
updates.getDifference#a041495 pts:int date:int qts:int = updates.Difference;
updates.getChannelDifference#bb32d7c0 channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
photos.updateProfilePhoto#eef579a0 id:InputPhoto crop:InputPhotoCrop = UserProfilePhoto;
photos.uploadProfilePhoto#d50f9c88 file:InputFile caption:string geo_point:InputGeoPoint crop:InputPhotoCrop = photos.Photo;
photos.updateProfilePhoto#f0bb5152 id:InputPhoto = UserProfilePhoto;
photos.uploadProfilePhoto#4f32c098 file:InputFile = photos.Photo;
photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
@ -757,4 +809,5 @@ channels.deleteChannel#c0111fe3 channel:InputChannel = Updates; @@ -757,4 +809,5 @@ 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;
channels.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = Updates;
channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats;

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

@ -232,6 +232,7 @@ @@ -232,6 +232,7 @@
"confirm_modal_send_to_peer": "Send to {peer}?",
"confirm_modal_share_file_peer": "Share with {peer}?",
"confirm_modal_invite_peer": "Invite to {peer}?",
"confirm_modal_share_game": "Share the game to {peer}?",
"confirm_modal_apply_lang_with_reload_md": "Reload the App to apply language?",
"confirm_modal_migrate_to_https_md": "Telegram Web now supports additional SSL encryption. Would you like to switch to HTTPS?\nThe HTTP version will be disabled soon.",
"confirm_modal_resize_desktop_md": "Would you like to switch to desktop version?",
@ -327,6 +328,7 @@ @@ -327,6 +328,7 @@
"conversation_changed_channel_photo": "Channel photo updated",
"conversation_removed_channel_photo": "Channel photo removed",
"conversation_pinned_message": "pinned message",
"conversation_scored_X": "{'one': 'scored {}', 'other': 'scored {}'}",
"conversation_message_sent": "sent you a message",
"conversation_forwarded_X_messages": "{'one': 'forwarded {} message', 'other': 'forwarded {} messages'}",
@ -346,6 +348,7 @@ @@ -346,6 +348,7 @@
"message_service_joined_by_link": "joined group via invite link",
"message_service_joined": "joined the group",
"message_service_pinned_message": "pinned «{message}»",
"message_service_scored_game": "{scored} in {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",
@ -353,6 +356,7 @@ @@ -353,6 +356,7 @@
"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_scored_X": "{'one': 'scored {}', 'other': 'scored {}'}",
"message_action_reply": "Reply",
"message_action_delete": "Delete",

88
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, AppDocsManager, AppStickersManager, AppMessagesIDsManager, DraftsManager, AppWebPagesManager, MtpApiManager, MtpApiFileManager, ServerTimeManager, RichTextProcessor, NotificationsManager, Storage, AppProfileManager, TelegramMeWebService, ErrorService, StatusManager, _) {
.service('AppMessagesManager', function ($q, $rootScope, $location, $filter, $timeout, $sce, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppDocsManager, AppStickersManager, AppMessagesIDsManager, DraftsManager, AppWebPagesManager, AppGamesManager, MtpApiManager, MtpApiFileManager, ServerTimeManager, RichTextProcessor, NotificationsManager, Storage, AppProfileManager, TelegramMeWebService, ErrorService, StatusManager, _) {
var messagesStorage = {}
var messagesForHistory = {}
var messagesForDialogs = {}
@ -43,6 +43,7 @@ angular.module('myApp.services') @@ -43,6 +43,7 @@ angular.module('myApp.services')
var dateOrTimeFilter = $filter('dateOrTime')
var fwdMessagesPluralize = _.pluralize('conversation_forwarded_X_messages')
var gameScorePluralize = _.pluralize('conversation_scored_X')
NotificationsManager.start()
@ -1183,6 +1184,10 @@ angular.module('myApp.services') @@ -1183,6 +1184,10 @@ angular.module('myApp.services')
case 'messageMediaWebPage':
AppWebPagesManager.saveWebPage(apiMessage.media.webpage, apiMessage.mid, mediaContext)
break
case 'messageMediaGame':
AppGamesManager.saveGame(apiMessage.media.game, apiMessage.mid, mediaContext)
apiMessage.media.handleMessage = true
break
}
}
if (apiMessage.action) {
@ -1571,7 +1576,7 @@ angular.module('myApp.services') @@ -1571,7 +1576,7 @@ angular.module('myApp.services')
var inputMedia
switch (attachType) {
case 'photo':
inputMedia = {_: 'inputMediaUploadedPhoto', file: inputFile}
inputMedia = {_: 'inputMediaUploadedPhoto', flags: 0, file: inputFile}
break
case 'document':
@ -1832,8 +1837,9 @@ angular.module('myApp.services') @@ -1832,8 +1837,9 @@ angular.module('myApp.services')
pendingByRandomID[randomIDS] = [peerID, messageID]
}
function forwardMessages (peerID, mids) {
function forwardMessages (peerID, mids, options) {
mids = mids.sort()
options = options || {}
var flags = 0
var isChannel = AppPeersManager.isChannel(peerID)
@ -1843,6 +1849,9 @@ angular.module('myApp.services') @@ -1843,6 +1849,9 @@ angular.module('myApp.services')
if (asChannel) {
flags |= 16
}
if (options.withMyScore) {
flags |= 256
}
var splitted = AppMessagesIDsManager.splitMessageIDsByChannels(mids)
var promises = []
@ -1926,6 +1935,21 @@ angular.module('myApp.services') @@ -1926,6 +1935,21 @@ angular.module('myApp.services')
return sendText(peerID, '/start')
}
function shareGame (botID, peerID, inputGame) {
var randomID = bigint(nextRandomInt(0xFFFFFFFF)).shiftLeft(32).add(bigint(nextRandomInt(0xFFFFFFFF))).toString()
return MtpApiManager.invokeApi('messages.sendMedia', {
flags: 0,
peer: AppPeersManager.getInputPeerByID(peerID),
media: {
_: 'inputMediaGame',
id: inputGame
},
random_id: randomID
}).then(function (updates) {
ApiUpdatesManager.processUpdateMessage(updates)
})
}
function cancelPendingMessage (randomID) {
var pendingData = pendingByRandomID[randomID]
@ -2148,6 +2172,10 @@ angular.module('myApp.services') @@ -2148,6 +2172,10 @@ angular.module('myApp.services')
}
message.media.webpage = AppWebPagesManager.wrapForHistory(message.media.webpage.id)
break
case 'messageMediaGame':
message.media.game = AppGamesManager.wrapForHistory(message.media.game.id)
break
}
}
else if (message.action) {
@ -2204,6 +2232,29 @@ angular.module('myApp.services') @@ -2204,6 +2232,29 @@ angular.module('myApp.services')
return replyMarkup
}
function wrapMessageText(msgID) {
var message = getMessage(msgID)
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)
var withBot = (fromBot ||
toPeerID < 0 && !(AppChatsManager.isChannel(-toPeerID) && !AppChatsManager.isMegagroup(-toPeerID)) ||
toPeerID > 0 && AppUsersManager.isBot(toPeerID))
var options = {
noCommands: !withBot,
fromBot: fromBot,
entities: message.totalEntities
}
if (message.pFlags.mentioned) {
var user = AppUsersManager.getSelf()
if (user) {
options.highlightUsername = user.username
}
}
return RichTextProcessor.wrapRichText(message.message, options)
}
function fetchSingleMessages () {
if (fetchSingleMessagesTimeout !== false) {
clearTimeout(fetchSingleMessagesTimeout)
@ -2452,31 +2503,41 @@ angular.module('myApp.services') @@ -2452,31 +2503,41 @@ angular.module('myApp.services')
notificationMessage = RichTextProcessor.wrapPlainText(message.message)
}
} else if (message.media) {
var captionEmoji = false;
switch (message.media._) {
case 'messageMediaPhoto':
notificationMessage = _('conversation_media_photo_raw')
captionEmoji = '🖼'
break
case 'messageMediaDocument':
switch (message.media.document.type) {
case 'gif':
notificationMessage = _('conversation_media_gif_raw')
captionEmoji = '🎬'
break
case 'sticker':
notificationMessage = _('conversation_media_sticker')
var stickerEmoji = message.media.document.stickerEmojiRaw
if (stickerEmoji !== undefined) {
notificationMessage = RichTextProcessor.wrapPlainText(stickerEmoji) + ' ' + notificationMessage
} else {
notificationMessage = _('conversation_media_sticker')
}
break
case 'video':
notificationMessage = _('conversation_media_video_raw')
captionEmoji = '📹'
break
case 'voice':
case 'audio':
notificationMessage = _('conversation_media_audio_raw')
break
default:
notificationMessage = message.media.document.file_name || _('conversation_media_document_raw')
if (message.media.document.file_name) {
notificationMessage = RichTextProcessor.wrapPlainText('📎 ' + message.media.document.file_name)
} else {
notificationMessage = _('conversation_media_document_raw')
captionEmoji = '📎'
}
break
}
break
@ -2484,14 +2545,23 @@ angular.module('myApp.services') @@ -2484,14 +2545,23 @@ angular.module('myApp.services')
case 'messageMediaGeo':
case 'messageMediaVenue':
notificationMessage = _('conversation_media_location_raw')
captionEmoji = '📍'
break
case 'messageMediaContact':
notificationMessage = _('conversation_media_contact_raw')
break
case 'messageMediaGame':
notificationMessage = RichTextProcessor.wrapPlainText('🎮 ' + message.media.game.title)
break
default:
notificationMessage = _('conversation_media_attachment_raw')
break
}
if (captionEmoji !== false &&
message.media.caption) {
notificationMessage = RichTextProcessor.wrapPlainText(captionEmoji + ' ' + message.media.caption)
}
} else if (message._ == 'messageService') {
switch (message.action._) {
case 'messageActionChatCreate':
@ -2537,6 +2607,12 @@ angular.module('myApp.services') @@ -2537,6 +2607,12 @@ angular.module('myApp.services')
case 'messageActionChannelDeletePhoto':
notificationMessage = _('conversation_removed_channel_photo_raw')
break
case 'messageActionPinMessage':
notificationMessage = _('conversation_pinned_message_raw')
break
case 'messageActionGameScore':
notificationMessage = gameScorePluralize(message.action.score)
break
}
}
@ -3169,6 +3245,7 @@ angular.module('myApp.services') @@ -3169,6 +3245,7 @@ angular.module('myApp.services')
sendOther: sendOther,
forwardMessages: forwardMessages,
startBot: startBot,
shareGame: shareGame,
convertMigratedPeer: convertMigratedPeer,
getMessagePeer: getMessagePeer,
getMessageThumb: getMessageThumb,
@ -3178,6 +3255,7 @@ angular.module('myApp.services') @@ -3178,6 +3255,7 @@ angular.module('myApp.services')
wrapForHistory: wrapForHistory,
wrapReplyMarkup: wrapReplyMarkup,
wrapSingleMessage: wrapSingleMessage,
wrapMessageText: wrapMessageText,
regroupWrappedHistory: regroupWrappedHistory
}
})

213
app/js/services.js

@ -1748,6 +1748,98 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1748,6 +1748,98 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}
})
.service('AppGamesManager', function ($modal, $sce, $window, $rootScope, MtpApiManager, AppPhotosManager, AppDocsManager, RichTextProcessor) {
var games = {}
function saveGame (apiGame, messageID, mediaContext) {
if (apiGame.photo && apiGame.photo._ === 'photo') {
AppPhotosManager.savePhoto(apiGame.photo, mediaContext)
} else {
delete apiGame.photo
}
if (apiGame.document && apiGame.document._ === 'document') {
AppDocsManager.saveDoc(apiGame.document, mediaContext)
} else {
delete apiGame.document
}
apiGame.rTitle = RichTextProcessor.wrapRichText(apiGame.title, {noLinks: true, noLinebreaks: true})
apiGame.rDescription = RichTextProcessor.wrapRichText(
apiGame.description || '', {}
)
if (games[apiGame.id] === undefined) {
games[apiGame.id] = apiGame
} else {
safeReplaceObject(games[apiGame.id], apiGame)
}
}
function openGame (gameID, messageID, embedUrl) {
var scope = $rootScope.$new(true)
scope.gameID = gameID
scope.messageID = messageID
scope.embedUrl = embedUrl
$modal.open({
templateUrl: templateUrl('game_modal'),
windowTemplateUrl: templateUrl('media_modal_layout'),
controller: 'GameModalController',
scope: scope,
windowClass: 'photo_modal_window'
})
}
function wrapForHistory (gameID) {
var game = angular.copy(games[gameID]) || {_: 'gameEmpty'}
if (game.photo && game.photo.id) {
game.photo = AppPhotosManager.wrapForHistory(game.photo.id)
}
if (game.document && game.document.id) {
game.document = AppDocsManager.wrapForHistory(game.document.id)
}
return game
}
function wrapForFull (gameID, msgID, embedUrl) {
var game = wrapForHistory(gameID)
var fullWidth = $(window).width() - (Config.Mobile ? 0 : 10)
var fullHeight = $($window).height() - (Config.Mobile ? 92 : 150)
if (!Config.Mobile && fullWidth > 800) {
fullWidth -= 208
}
var full = {
width: fullWidth,
height: fullHeight
}
var embedTag = Config.Modes.chrome_packed ? 'webview' : 'iframe'
var embedType = 'text/html'
var embedHtml = '<' + embedTag + ' src="' + encodeEntities(embedUrl) + '" type="' + encodeEntities(embedType) + '" frameborder="0" border="0" webkitallowfullscreen mozallowfullscreen allowfullscreen width="' + full.width + '" height="' + full.height + '" style="width: ' + full.width + 'px; height: ' + full.height + 'px;"></' + embedTag + '>'
full.html = $sce.trustAs('html', embedHtml)
game.full = full
return game
}
return {
saveGame: saveGame,
openGame: openGame,
wrapForFull: wrapForFull,
wrapForHistory: wrapForHistory
}
})
.service('AppDocsManager', function ($sce, $rootScope, $modal, $window, $q, $timeout, RichTextProcessor, MtpApiFileManager, FileManager, qSync) {
var docs = {}
var docsForHistory = {}
@ -1940,6 +2032,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1940,6 +2032,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
_: 'inputDocumentFileLocation',
id: docID,
access_hash: doc.access_hash,
version: doc.version,
file_name: getFileName(doc)
}
@ -1959,6 +2052,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -1959,6 +2052,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
_: 'inputDocumentFileLocation',
id: docID,
access_hash: doc.access_hash,
version: doc.version,
file_name: getFileName(doc)
}
@ -2118,6 +2212,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2118,6 +2212,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
case 'updateNewStickerSet':
var fullSet = update.stickerset
var set = fullSet.set
if (set.pFlags.masks) {
return false
}
var pos = false
for (var i = 0, len = stickers.sets.length; i < len; i++) {
if (stickers.sets[i].id == set.id) {
@ -2147,6 +2244,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2147,6 +2244,9 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
break
case 'updateStickerSetsOrder':
if (update.pFlags.masks) {
return
}
var order = update.order
stickers.sets.sort(function (a, b) {
return order.indexOf(a.id) - order.indexOf(b.id)
@ -2397,7 +2497,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2397,7 +2497,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}
})
.service('AppInlineBotsManager', function (qSync, $q, $rootScope, toaster, Storage, ErrorService, MtpApiManager, AppMessagesManager, AppMessagesIDsManager, AppDocsManager, AppPhotosManager, RichTextProcessor, AppUsersManager, AppPeersManager, PeersSelectService, GeoLocationManager) {
.service('AppInlineBotsManager', function (qSync, $q, $rootScope, toaster, Storage, ErrorService, MtpApiManager, AppMessagesManager, AppMessagesIDsManager, AppDocsManager, AppPhotosManager, AppGamesManager, RichTextProcessor, AppUsersManager, AppPeersManager, PeersSelectService, GeoLocationManager) {
var inlineResults = {}
return {
@ -2409,7 +2509,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2409,7 +2509,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
switchToPM: switchToPM,
checkSwitchReturn: checkSwitchReturn,
switchInlineButtonClick: switchInlineButtonClick,
callbackButtonClick: callbackButtonClick
callbackButtonClick: callbackButtonClick,
gameButtonClick: gameButtonClick
}
function getPopularBots () {
@ -2650,6 +2751,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2650,6 +2751,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
function switchInlineButtonClick (id, button) {
var message = AppMessagesManager.getMessage(id)
var botID = message.viaBotID || message.fromID
if (button.pFlags && button.pFlags.same_peer) {
var peerID = AppMessagesManager.getMessagePeer(message)
var toPeerString = AppPeersManager.getPeerString(peerID)
switchInlineQuery(botID, toPeerString, button.query)
return
}
return checkSwitchReturn(botID).then(function (retPeerString) {
if (retPeerString) {
return switchInlineQuery(botID, retPeerString, button.query)
@ -2668,31 +2775,62 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -2668,31 +2775,62 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
var peerID = AppMessagesManager.getMessagePeer(message)
return MtpApiManager.invokeApi('messages.getBotCallbackAnswer', {
flags: 1,
peer: AppPeersManager.getInputPeerByID(peerID),
msg_id: AppMessagesIDsManager.getMessageLocalID(id),
data: button.data
}, {timeout: 1, stopTime: -1, noErrorBox: true}).then(function (callbackAnswer) {
if (typeof callbackAnswer.message != 'string' ||
!callbackAnswer.message.length) {
return
if (typeof callbackAnswer.message === 'string' &&
callbackAnswer.message.length) {
showCallbackMessage(callbackAnswer.message, callbackAnswer.pFlags.alert)
}
var html = RichTextProcessor.wrapRichText(callbackAnswer.message, {noLinks: true, noLinebreaks: true})
if (callbackAnswer.pFlags.alert) {
ErrorService.show({
title_html: html,
alert: true
})
} else {
toaster.pop({
type: 'info',
body: html.valueOf(),
bodyOutputType: 'trustedHtml',
showCloseButton: false
})
else if (typeof callbackAnswer.url === 'string') {
LocationParamsService.openUrl(callbackAnswer.url)
}
})
}
function gameButtonClick (id) {
console.trace()
var message = AppMessagesManager.getMessage(id)
var peerID = AppMessagesManager.getMessagePeer(message)
return MtpApiManager.invokeApi('messages.getBotCallbackAnswer', {
flags: 2,
peer: AppPeersManager.getInputPeerByID(peerID),
msg_id: AppMessagesIDsManager.getMessageLocalID(id)
}, {timeout: 1, stopTime: -1, noErrorBox: true}).then(function (callbackAnswer) {
if (typeof callbackAnswer.message === 'string' &&
callbackAnswer.message.length) {
showCallbackMessage(callbackAnswer.message, callbackAnswer.pFlags.alert)
}
else if (typeof callbackAnswer.url === 'string') {
AppGamesManager.openGame(message.media.game.id, id, callbackAnswer.url)
}
})
}
function showCallbackMessage(message, isAlert) {
if (typeof message != 'string' ||
!message.length) {
return
}
var html = RichTextProcessor.wrapRichText(message, {noLinks: true, noLinebreaks: true})
if (isAlert) {
ErrorService.show({
title_html: html,
alert: true
})
} else {
toaster.pop({
type: 'info',
body: html.valueOf(),
bodyOutputType: 'trustedHtml',
showCloseButton: false
})
}
}
function sendInlineResult (peerID, qID, options) {
var inlineResult = inlineResults[qID]
if (inlineResult === undefined) {
@ -4216,16 +4354,29 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -4216,16 +4354,29 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
function handleTgProtoAddr (url, inner) {
var matches
if (matches = url.match(/^resolve\?domain=(.+?)(?:&(start|startgroup|post)=(.+))?$/)) {
if (matches = url.match(/^resolve\?domain=(.+?)(?:&(start|startgroup|post|game)=(.+))?$/)) {
AppPeersManager.resolveUsername(matches[1]).then(function (peerID) {
if (peerID > 0 && AppUsersManager.isBot(peerID) && matches[2] == 'startgroup') {
if (peerID > 0 && AppUsersManager.isBot(peerID) &&
(matches[2] == 'startgroup' || matches[2] == 'game')) {
var isStartGroup = matches[2] == 'startgroup'
PeersSelectService.selectPeer({
confirm_type: 'INVITE_TO_GROUP',
noUsers: true
confirm_type: isStartGroup ? 'INVITE_TO_GROUP' : 'INVITE_TO_GAME',
noUsers: isStartGroup
}).then(function (toPeerString) {
var toPeerID = AppPeersManager.getPeerID(toPeerString)
var toChatID = toPeerID < 0 ? -toPeerID : 0
AppMessagesManager.startBot(peerID, toChatID, matches[3]).then(function () {
var sendPromise
if (isStartGroup) {
var toChatID = toPeerID < 0 ? -toPeerID : 0
sendPromise = AppMessagesManager.startBot(peerID, toChatID, matches[3])
} else {
inputGame = {
_: 'inputGameShortName',
bot_id: AppUsersManager.getUserInput(peerID),
short_name: matches[3]
}
sendPromise = AppMessagesManager.shareGame(peerID, toPeerID, inputGame)
}
sendPromise.then(function () {
$rootScope.$broadcast('history_focus', {peerString: toPeerString})
})
})
@ -4403,6 +4554,17 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -4403,6 +4554,17 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
checkLocationTgAddr()
}
function openUrl(url) {
var match = url.match(tgAddrRegExp)
if (match) {
if (handleTgProtoAddr(match[3], true)) {
return true
}
}
var wnd = window.open(url, '_blank')
return wnd ? true : false
}
function shareUrl (url, text, shareLink) {
var options = {}
if (shareLink) {
@ -4468,7 +4630,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -4468,7 +4630,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
return {
start: start,
shareUrl: shareUrl
shareUrl: shareUrl,
openUrl: openUrl
}
})

5
app/less/app.less

@ -3415,6 +3415,11 @@ li.inline_result_sticker.composer_autocomplete_option_active a { @@ -3415,6 +3415,11 @@ li.inline_result_sticker.composer_autocomplete_option_active a {
.inline_result_sticker img {
object-fit: contain;
}
.inline_result_game img {
object-fit: cover;
width: 50px;
height: 50px;
}
.inline_result_gif_mtproto,
.inline_result_gif_http,
.inline_result_photo_mtproto,

1
app/partials/desktop/confirm_modal.html

@ -40,6 +40,7 @@ @@ -40,6 +40,7 @@
<span ng-switch-when="SHARE_URL" my-i18n-format="confirm_modal_send_to_peer"></span>
<span ng-switch-when="EXT_SHARE_PEER" my-i18n-format="confirm_modal_share_file_peer"></span>
<span ng-switch-when="INVITE_TO_GROUP" my-i18n-format="confirm_modal_invite_peer"></span>
<span ng-switch-when="INVITE_TO_GAME" my-i18n-format="confirm_modal_share_game"></span>
<my-i18n-param name="peer">
<strong my-peer-link="peer_id"></strong>
</my-i18n-param>

35
app/partials/desktop/game_modal.html

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
<div class="modal_close_wrap" ng-click="$close()">
<div class="modal_close"></div>
</div>
<div class="media_modal_bottom_panel_wrap">
<div class="media_modal_bottom_panel">
<div class="media_modal_bottom_actions pull-right">
<a class="media_modal_action_btn" ng-click="forward()" title="{{'media_modal_forward' | i18n}}">
<i class="media_modal_action_btn_forward"></i>
</a>
</div>
<div class="media_modal_info_wrap pull-left">
<a class="media_modal_author_photo pull-left" my-peer-photolink="botID" img-class="media_modal_author_photo"></a>
<div class="media_modal_author_name">
<a class="media_modal_author" my-peer-link="botID"></a>
</div>
<div class="media_modal_date">
<a class="media_modal_date" my-peer-link="botID" username="true"></a>
</div>
</div>
<div class="media_modal_title_wrap" ng-bind-html="game.rTitle"></div>
</div>
</div>
<div class="modal-dialog">
<div class="modal-content">
<div my-modal-width="{{game.full.width - 32}}" class="media_modal_wrap embed_modal_wrap" my-modal-position animation="no">
<div class="modal-body" ng-bind-html="game.full.html" my-game-communication></div>
</div>
</div>
</div>

6
app/partials/desktop/inline_results.html

@ -48,8 +48,10 @@ @@ -48,8 +48,10 @@
</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 ? 'thumb' : (result.send_message.geo ? 'geo' : false)">
<img ng-switch-when="thumb" class="inline_article_thumb" ng-src="{{result.thumbUrl}}"/>
<div class="inline_article_thumb_wrap pull-left" ng-switch="result.thumb !== undefined ? 'thumb' : (result.thumbUrl !== undefined ? 'thumbHttp' : (result.send_message.geo ? 'geo' : false))">
<img ng-switch-when="thumb" class="inline_article_thumb" my-load-thumb
thumb="result.thumb"/>
<img ng-switch-when="thumbHttp" class="inline_article_thumb" ng-src="{{result.thumbUrl}}"/>
<img
ng-switch-when="geo"
class="inline_article_thumb"

17
app/partials/desktop/message_attach_game.html

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
<div class="im_message_webpage_wrap clearfix">
<div class="im_message_webpage_photo">
<div class="im_message_webpage_title">
<a href="" ng-click="openGame()" target="_blank" rel="noopener noreferrer" ng-bind-html="media.game.rTitle"></a>
</div>
<div class="im_message_webpage_description im_message_game_description" ng-bind-html="::media.game.rDescription"></div>
<div class="im_message_webpage_description im_message_game_message"></div>
<a class="im_message_photo_thumb" ng-click="openGame()" ng-style="::{width: media.game.photo.thumb.width + 'px'}">
<img
class="im_message_photo_thumb"
my-load-thumb
thumb="media.game.photo.thumb"
alt="[{{::media.game.title}}]"
/>
</a>
</div>
</div>

1
app/partials/desktop/message_media.html

@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
<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="messageMediaGame" my-message-game="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">

8
app/partials/desktop/message_service.html

@ -30,6 +30,14 @@ @@ -30,6 +30,14 @@
<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="messageActionGameScore" my-i18n="message_service_scored_game">
<my-i18n-param name="scored">
<ng-pluralize count="historyMessage.action.score"
when="message_service_scored_X"></ng-pluralize>
</my-i18n-param>
<my-i18n-param name="message"><a class="im_service_message_pinned" my-pinned-message="::historyMessage.reply_to_mid"></a></my-i18n-param>
</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_mid"></a></my-i18n-param>
</span>

10
app/partials/desktop/short_message.html

@ -14,6 +14,10 @@ @@ -14,6 +14,10 @@
<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 ng-switch-when="messageMediaGame">
<span my-emoji-image="🎮"></span>
<span ng-bind-html="message.media.game.rTitle"></span>
</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>
@ -40,4 +44,10 @@ @@ -40,4 +44,10 @@
<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="messageActionPinMessage" my-i18n="conversation_pinned_message"></span>
<span ng-switch-when="messageActionGameScore">
<ng-pluralize count="message.action.score"
when="conversation_scored_X"></ng-pluralize>
</my-i18n-param>
</span>
</span><span class="im_short_message_text" ng-if="message.message.length" ng-bind-html="message.richMessage"></span>
Loading…
Cancel
Save