Supported invite links

This commit is contained in:
Igor Zhukov 2015-05-01 16:01:48 +03:00
parent 31900bdcfd
commit 21f08cd29e
12 changed files with 160 additions and 73 deletions

View File

@ -28,7 +28,7 @@ angular.module('myApp', [
config(['$locationProvider', '$routeProvider', '$compileProvider', 'StorageProvider', function($locationProvider, $routeProvider, $compileProvider, StorageProvider) { config(['$locationProvider', '$routeProvider', '$compileProvider', 'StorageProvider', function($locationProvider, $routeProvider, $compileProvider, StorageProvider) {
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|file|blob|filesystem|chrome-extension|app):|data:image\//); $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|file|blob|filesystem|chrome-extension|app):|data:image\//);
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|file|mailto|blob|filesystem|chrome-extension|app):|data:/); $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|file|tg|mailto|blob|filesystem|chrome-extension|app):|data:/);
if (Config.Modes.test) { if (Config.Modes.test) {
StorageProvider.setPrefix('t_'); StorageProvider.setPrefix('t_');

View File

@ -3618,22 +3618,33 @@ angular.module('myApp.controllers', ['myApp.i18n'])
}; };
}) })
.controller('ChatInviteLinkModalController', function (_, $scope, $modalInstance, AppChatsManager) { .controller('ChatInviteLinkModalController', function (_, $scope, $timeout, $modalInstance, AppChatsManager, ErrorService) {
$scope.exportedInvite = {link: _('group_invite_link_loading_raw')}; $scope.exportedInvite = {link: _('group_invite_link_loading_raw')};
$scope.updateLink = function (force) { function updateLink (force) {
if (force) { if (force) {
$scope.exportedInvite.revoking = true; $scope.exportedInvite.revoking = true;
} }
AppChatsManager.getChatInviteLink($scope.chatID, force).then(function (link) { AppChatsManager.getChatInviteLink($scope.chatID, force).then(function (link) {
$scope.exportedInvite = {link: link}; $scope.exportedInvite = {link: link};
$timeout(function () {
$scope.$broadcast('ui_invite_select');
}, 100);
})['finally'](function () { })['finally'](function () {
delete $scope.exportedInvite.revoking; delete $scope.exportedInvite.revoking;
}); });
} }
$scope.updateLink(); $scope.revokeLink = function () {
ErrorService.confirm({
type: 'REVOKE_GROUP_INVITE_LINK'
}).then(function () {
updateLink(true);
})
}
updateLink();
}) })

View File

@ -2924,16 +2924,27 @@ angular.module('myApp.directives', ['myApp.filters'])
.directive('myCopyField', function () { .directive('myCopyField', function () {
return { return {
scope: {
selectEvent: '=myCopyField'
},
link: link link: link
}; };
function link($scope, element, attrs) { function link($scope, element, attrs) {
element.attr('readonly', 'true'); element.attr('readonly', 'true');
// element.on('keydown paste', cancelEvent); element[0].readonly = true;
element.on('click', function () { element.on('click', function () {
this.select(); this.select();
}); });
element[0].readonly = true;
if ($scope.selectEvent) {
$scope.$on($scope.selectEvent, function () {
setTimeout(function () {
element[0].focus();
element[0].select();
}, 100);
});
}
}; };
}) })

View File

@ -189,6 +189,8 @@
"confirm_modal_recovery_email_empty_md": "Warning! Are you sure you don't want to add a password recovery e-mail?\n\nIf you forget your password, you will lose access to your Telegram account", "confirm_modal_recovery_email_empty_md": "Warning! Are you sure you don't want to add a password recovery e-mail?\n\nIf you forget your password, you will lose access to your Telegram account",
"confirm_modal_abort_password_setup": "Abort two-step verification setup?", "confirm_modal_abort_password_setup": "Abort two-step verification setup?",
"confirm_modal_reset_account_md": "Are you sure?\nThis action can not be undone.\n\nYou will lose all your chats and messages, along with any media and files you shared, if you proceed with resetting your account.", "confirm_modal_reset_account_md": "Are you sure?\nThis action can not be undone.\n\nYou will lose all your chats and messages, along with any media and files you shared, if you proceed with resetting your account.",
"confirm_modal_join_group_link": "Do you want to join the group «{title}»?",
"confirm_modal_revoke_group_link": "Are you sure you want to revoke this link? Once you do, no one will be able to join the group using it.",
"confirm_modal_are_u_sure": "Are you sure?", "confirm_modal_are_u_sure": "Are you sure?",
"confirm_modal_logout_submit": "Log out", "confirm_modal_logout_submit": "Log out",
@ -251,6 +253,7 @@
"conversation_kicked_user": "removed {user}", "conversation_kicked_user": "removed {user}",
"conversation_invited_user_message": "invited user", "conversation_invited_user_message": "invited user",
"conversation_kicked_user_message": "removed user", "conversation_kicked_user_message": "removed user",
"conversation_joined_by_link": "joined group",
"conversation_message_sent": "sent you a message", "conversation_message_sent": "sent you a message",
"conversation_unknown_user": "Somebody", "conversation_unknown_user": "Somebody",
@ -264,6 +267,7 @@
"message_service_returned_to_group": "returned to group", "message_service_returned_to_group": "returned to group",
"message_service_kicked_user": "removed {user}", "message_service_kicked_user": "removed {user}",
"message_service_left_group": "left group", "message_service_left_group": "left group",
"message_service_joined_by_link": "joined group via invite link",
"message_service_unsupported_action": "Unsupported action {action}", "message_service_unsupported_action": "Unsupported action {action}",
"error_modal_warning_title": "Warning", "error_modal_warning_title": "Warning",
@ -286,7 +290,7 @@
"error_modal_firstname_invali_description": "The first name you entered is invalid.", "error_modal_firstname_invali_description": "The first name you entered is invalid.",
"error_modal_lastname_invalid_description": "The last name you entered is invalid.", "error_modal_lastname_invalid_description": "The last name you entered is invalid.",
"error_modal_phone_invalid_description": "The phone number you entered is invalid.", "error_modal_phone_invalid_description": "The phone number you entered is invalid.",
"error_modal_users_too_much_description": "You have selected too much users.", "error_modal_users_too_much_description": "Too many group members.",
"error_modal_photo_dimensions_invalid_description": "The photo dimensions are invalid, please select another file.", "error_modal_photo_dimensions_invalid_description": "The photo dimensions are invalid, please select another file.",
"error_modal_video_file_invalid_description": "The video file extension is invalid or unsupported, please select another file.", "error_modal_video_file_invalid_description": "The video file extension is invalid or unsupported, please select another file.",
"error_modal_photo_too_small_description": "The photo you provided is too small.", "error_modal_photo_too_small_description": "The photo you provided is too small.",
@ -309,6 +313,8 @@
"error_modal_recovery_na_description": "Since you haven't provided a recovery e-mail when setting up your password, your remaining options are either to remember your password or to reset your account.", "error_modal_recovery_na_description": "Since you haven't provided a recovery e-mail when setting up your password, your remaining options are either to remember your password or to reset your account.",
"error_modal_password_success_descripion": "Your password for Two-Step Verification is now active.", "error_modal_password_success_descripion": "Your password for Two-Step Verification is now active.",
"error_modal_password_disabled_descripion": "You have disabled Two-Step Verification.", "error_modal_password_disabled_descripion": "You have disabled Two-Step Verification.",
"error_modal_user_not_mutual_contact": "The user can be invited by his contact only",
"error_modal_invite_link_invalid": "The invite link is invalid",
"head_telegram": "Telegram", "head_telegram": "Telegram",

View File

@ -617,46 +617,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}); });
} }
function openInviteLink (hash) {
return MtpApiManager.invokeApi('messages.checkChatInvite', {
hash: hash
}).then(function (chatInvite) {
var chatTitle;
if (chatInvite._ == 'chatInviteAlready') {
saveApiChat(chatInvite.chat);
if (!chatInvite.chat.left) {
return $rootScope.$broadcast('history_focus', {
peerString: getChatString(chatInvite.chat.id)
});
}
chatTitle = chatInvite.chat.title;
} else {
chatTitle = chatInvite.title;
}
ErrorService.confirm({
type: 'JOIN_GROUP_BY_LINK',
title: chatTitle
}).then(function () {
return MtpApiManager.invokeApi('messages.importChatInvite', {
hash: hash
}).then(function (updates) {
ApiUpdatesManager.processUpdateMessage(updates);
if (updates.updates && updates.updates.length) {
for (var i = 0, len = updates.updates.length, update; i < len; i++) {
update = updates.updates[i];
if (update._ == 'updateNewMessage') {
$rootScope.$broadcast('history_focus', {peerString: AppChatsManager.getChatString(update.message.to_id.chat_id)
});
break;
}
}
}
});
});
});
}
function hasChat (id) { function hasChat (id) {
return angular.isObject(chats[id]); return angular.isObject(chats[id]);
} }
@ -777,7 +737,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
getChatPhoto: getChatPhoto, getChatPhoto: getChatPhoto,
getChatString: getChatString, getChatString: getChatString,
getChatInviteLink: getChatInviteLink, getChatInviteLink: getChatInviteLink,
openInviteLink: openInviteLink,
hasChat: hasChat, hasChat: hasChat,
wrapForFull: wrapForFull, wrapForFull: wrapForFull,
openChat: openChat openChat: openChat
@ -853,7 +812,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
} }
}) })
.service('AppMessagesManager', function ($q, $rootScope, $location, $filter, $timeout, $sce, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, AppAudioManager, AppWebPagesManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, PeersSelectService, Storage, FileManager, TelegramMeWebService, StatusManager, _) { .service('AppMessagesManager', function ($q, $rootScope, $location, $filter, $timeout, $sce, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, AppAudioManager, AppWebPagesManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, PeersSelectService, Storage, FileManager, TelegramMeWebService, ErrorService, StatusManager, _) {
var messagesStorage = {}; var messagesStorage = {};
var messagesForHistory = {}; var messagesForHistory = {};
@ -1963,6 +1922,46 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
return false; return false;
} }
function openChatInviteLink (hash) {
return MtpApiManager.invokeApi('messages.checkChatInvite', {
hash: hash
}).then(function (chatInvite) {
var chatTitle;
if (chatInvite._ == 'chatInviteAlready') {
AppChatsManager.saveApiChat(chatInvite.chat);
if (!chatInvite.chat.left) {
return $rootScope.$broadcast('history_focus', {
peerString: AppChatsManager.getChatString(chatInvite.chat.id)
});
}
chatTitle = chatInvite.chat.title;
} else {
chatTitle = chatInvite.title;
}
ErrorService.confirm({
type: 'JOIN_GROUP_BY_LINK',
title: chatTitle
}).then(function () {
return MtpApiManager.invokeApi('messages.importChatInvite', {
hash: hash
}).then(function (updates) {
ApiUpdatesManager.processUpdateMessage(updates);
if (updates.updates && updates.updates.length) {
for (var i = 0, len = updates.updates.length, update; i < len; i++) {
update = updates.updates[i];
if (update._ == 'updateNewMessage') {
$rootScope.$broadcast('history_focus', {peerString: AppChatsManager.getChatString(update.message.to_id.chat_id)
});
break;
}
}
}
});
});
});
}
function getMessagePeer (message) { function getMessagePeer (message) {
var toID = message.to_id && AppPeersManager.getPeerID(message.to_id) || 0; var toID = message.to_id && AppPeersManager.getPeerID(message.to_id) || 0;
@ -2682,6 +2681,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
sendFile: sendFile, sendFile: sendFile,
sendOther: sendOther, sendOther: sendOther,
forwardMessages: forwardMessages, forwardMessages: forwardMessages,
openChatInviteLink: openChatInviteLink,
getMessagePeer: getMessagePeer, getMessagePeer: getMessagePeer,
wrapForDialog: wrapForDialog, wrapForDialog: wrapForDialog,
wrapForHistory: wrapForHistory, wrapForHistory: wrapForHistory,
@ -4095,7 +4095,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
// resource path // resource path
"(?:/(?:\\S{0,255}[^\\s.;,(\\[\\]{}<>\"'])?)?"; "(?:/(?:\\S{0,255}[^\\s.;,(\\[\\]{}<>\"'])?)?";
var regExp = new RegExp('(^|\\s)((?:https?://)?telegram\\.me/|@)([a-zA-Z\\d_]{5,32})|(' + urlRegex + ')|(\\n)|(' + emojiRegex + ')|(^|\\s)(#[' + regexAlphaNumericChars + ']{2,64})', 'i'); var regExp = new RegExp('(^|\\s)(@)([a-zA-Z\\d_]{5,32})|(' + urlRegex + ')|(\\n)|(' + emojiRegex + ')|(^|\\s)(#[' + regexAlphaNumericChars + ']{2,64})', 'i');
var emailRegex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; var emailRegex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
var youtubeRegex = /^(?:https?:\/\/)?(?:www\.)?youtu(?:|\.be|be\.com|\.b)(?:\/v\/|\/watch\\?v=|e\/|(?:\/\??#)?\/watch(?:.+)v=)(.{11})(?:\&[^\s]*)?/; var youtubeRegex = /^(?:https?:\/\/)?(?:www\.)?youtu(?:|\.be|be\.com|\.b)(?:\/v\/|\/watch\\?v=|e\/|(?:\/\??#)?\/watch(?:.+)v=)(.{11})(?:\&[^\s]*)?/;
@ -4166,14 +4166,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
if (match[3]) { // telegram.me links if (match[3]) { // telegram.me links
var contextUrl = !options.noLinks && siteMentions[contextSite]; var contextUrl = !options.noLinks && siteMentions[contextSite];
if (match[2] != '@' && contextExternal) {
contextUrl = false;
}
if (contextUrl) { if (contextUrl) {
var attr = ''; var attr = '';
if (options.highlightUsername && if (options.highlightUsername &&
options.highlightUsername.toLowerCase() == match[3].toLowerCase() && options.highlightUsername.toLowerCase() == match[3].toLowerCase()) {
match[2] == '@') {
attr = 'class="im_message_mymention"'; attr = 'class="im_message_mymention"';
} }
html.push( html.push(
@ -4225,6 +4221,19 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
url = (match[5] ? '' : protocol) + match[4]; url = (match[5] ? '' : protocol) + match[4];
} }
var tgMeMatch;
if (tld == 'me' &&
(tgMeMatch = url.match(/^https?:\/\/telegram\.me\/(.+)/))) {
var path = tgMeMatch[1].split('/');
switch (path[0]) {
case 'joinchat':
url = 'tg://join?invite=' + path[1];
break;
default:
url = 'tg://resolve?domain=' + path[0];
}
}
} else { // IP address } else { // IP address
url = (match[5] ? '' : 'http://') + match[4]; url = (match[5] ? '' : 'http://') + match[4];
} }
@ -5269,26 +5278,37 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
}) })
.service('LocationParamsService', function ($rootScope, $routeParams, AppUsersManager, AppChatsManager) { .service('LocationParamsService', function ($rootScope, $routeParams, AppUsersManager, AppMessagesManager) {
function checkTgAddr () { var tgAddrRegEx = /^(web\+)?tg:(\/\/)?(.+)/;
if (!$routeParams.tgaddr) {
return; function checkLocationTgAddr () {
if ($routeParams.tgaddr) {
var matches = $routeParams.tgaddr.match(tgAddrRegEx);
if (matches) {
handleTgProtoAddr(matches[3]);
}
} }
var matches = $routeParams.tgaddr.match(/^(web\+)?tg:(\/\/)?resolve\?domain=(.+)$/); }
if (matches && matches[3]) {
AppUsersManager.resolveUsername(matches[3]).then(function (userID) { function handleTgProtoAddr (url) {
var matches;
if (matches = url.match(/^resolve\?domain=(.+)$/)) {
AppUsersManager.resolveUsername(matches[1]).then(function (userID) {
$rootScope.$broadcast('history_focus', { $rootScope.$broadcast('history_focus', {
peerString: AppUsersManager.getUserString(userID) peerString: AppUsersManager.getUserString(userID)
}); });
}); });
return; return true;
} }
var matches = $routeParams.tgaddr.match(/^(web\+)?tg:(\/\/)?join\?invite=(.+)$/); if (matches = url.match(/^join\?invite=(.+)$/)) {
if (matches && matches[3]) { AppMessagesManager.openChatInviteLink(matches[1]);
AppChatsManager.openInviteLink(matches[3]); return true;
} }
return false;
} }
var started = !('registerProtocolHandler' in navigator); var started = !('registerProtocolHandler' in navigator);
@ -5304,8 +5324,25 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
navigator.registerProtocolHandler('web+tg', '#im?tgaddr=%s', 'Telegram Web'); navigator.registerProtocolHandler('web+tg', '#im?tgaddr=%s', 'Telegram Web');
} catch (e) {} } catch (e) {}
$rootScope.$on('$routeUpdate', checkTgAddr);
checkTgAddr(); $(document).on('click', function (event) {
var target = event.target;
if (target &&
target.tagName == 'A' &&
!target.onclick &&
!target.onmousedown) {
var href = $(target).attr('href') || target.href || '';
var match = href.match(tgAddrRegEx);
if (match) {
if (handleTgProtoAddr(match[3])) {
return cancelEvent(event);
}
}
}
});
$rootScope.$on('$routeUpdate', checkLocationTgAddr);
checkLocationTgAddr();
}; };
return { return {

View File

@ -8,7 +8,7 @@
<div class="md-input-group md-textarea-group" my-labeled-input> <div class="md-input-group md-textarea-group" my-labeled-input>
<label class="md-input-label" my-i18n="group_invite_link_link_label"></label> <label class="md-input-label" my-i18n="group_invite_link_link_label"></label>
<textarea class="md-input" ng-model="exportedInvite.link" rows="2" my-copy-field></textarea> <textarea class="md-input" ng-model="exportedInvite.link" rows="2" my-copy-field="'ui_invite_select'"></textarea>
</div> </div>
</div> </div>
@ -17,7 +17,7 @@
<div class="md_simple_modal_footer"> <div class="md_simple_modal_footer">
<button class="btn btn-md" ng-click="$dismiss()" my-i18n="modal_cancel"></button> <button class="btn btn-md" ng-click="$dismiss()" my-i18n="modal_cancel"></button>
<button class="btn btn-md btn-md-primary" ng-class="{disabled: exportedInvite.revoking}" ng-click="updateLink(true)" ng-bind="exportedInvite.revoking ? 'group_invite_revoke_active' : 'group_invite_revoke' | i18n" ng-disabled="exportedInvite.revoking"></button> <button class="btn btn-md btn-md-primary" ng-class="{disabled: exportedInvite.revoking}" ng-click="revokeLink()" ng-bind="exportedInvite.revoking ? 'group_invite_revoke_active' : 'group_invite_revoke' | i18n" ng-disabled="exportedInvite.revoking"></button>
</div> </div>
</div> </div>

View File

@ -47,6 +47,12 @@
<div ng-switch-when="RECOVERY_EMAIL_EMPTY" my-i18n="confirm_modal_recovery_email_empty_md"></div> <div ng-switch-when="RECOVERY_EMAIL_EMPTY" my-i18n="confirm_modal_recovery_email_empty_md"></div>
<div ng-switch-when="PASSWORD_ABORT_SETUP" my-i18n="confirm_modal_abort_password_setup"></div> <div ng-switch-when="PASSWORD_ABORT_SETUP" my-i18n="confirm_modal_abort_password_setup"></div>
<div ng-switch-when="RESET_ACCOUNT" my-i18n="confirm_modal_reset_account_md"></div> <div ng-switch-when="RESET_ACCOUNT" my-i18n="confirm_modal_reset_account_md"></div>
<div ng-switch-when="JOIN_GROUP_BY_LINK" my-i18n="confirm_modal_join_group_link">
<my-i18n-param name="title"><strong ng-bind="title"></strong></my-i18n-param>
</div>
<div ng-switch-when="REVOKE_GROUP_INVITE_LINK" my-i18n="confirm_modal_revoke_group_link"></div>
<span ng-switch-default ng-switch="message.length > 0"> <span ng-switch-default ng-switch="message.length > 0">
<span ng-switch-when="true" ng-bind="message"></span> <span ng-switch-when="true" ng-bind="message"></span>
<span ng-switch-default my-i18n="confirm_modal_are_u_sure"></span> <span ng-switch-default my-i18n="confirm_modal_are_u_sure"></span>

View File

@ -93,6 +93,9 @@
<my-i18n-param name="user"><span my-user-link="dialogMessage.action.user_id"></span></my-i18n-param> <my-i18n-param name="user"><span my-user-link="dialogMessage.action.user_id"></span></my-i18n-param>
</span> </span>
</span> </span>
<span ng-switch-when="messageActionChatJoinedByLink" my-i18n="conversation_joined_by_link"></span>
</span> </span>
<span class="im_dialog_message_text" ng-if="dialogMessage.message.length" ng-bind-html="dialogMessage.richMessage"></span> <span class="im_dialog_message_text" ng-if="dialogMessage.message.length" ng-bind-html="dialogMessage.richMessage"></span>

View File

@ -38,7 +38,11 @@
<span ng-switch-when="USERNAME_OCCUPIED" my-i18n="error_modal_username_occupied_description"></span> <span ng-switch-when="USERNAME_OCCUPIED" my-i18n="error_modal_username_occupied_description"></span>
<span ng-switch-when="MEDIA_TYPE_NOT_SUPPORTED" my-i18n="error_modal_media_not_supported_description"></span> <span ng-switch-when="MEDIA_TYPE_NOT_SUPPORTED" my-i18n="error_modal_media_not_supported_description"></span>
<span ng-switch-when="USERNAME_NOT_OCCUPIED" my-i18n="error_modal_username_not_found_description"></span> <span ng-switch-when="USERNAME_NOT_OCCUPIED" my-i18n="error_modal_username_not_found_description"></span>
<span ng-switch-when="PASSWORD_RECOVERY_NA" my-i18n="error_modal_recovery_na_description"></span> <span ng-switch-when="USER_NOT_MUTUAL_CONTACT" my-i18n="error_modal_user_not_mutual_contact"></span>
<span ng-switch-when="INVITE_HASH_INVALID" my-i18n="error_modal_invite_link_invalid"></span>
<span ng-switch-when="INVITE_HASH_EXPIRED" my-i18n="error_modal_invite_link_invalid"></span>
<span ng-switch-when="INVITE_HASH_EMPTY" my-i18n="error_modal_invite_link_invalid"></span>
<div ng-switch-default ng-switch="error.code"> <div ng-switch-default ng-switch="error.code">

View File

@ -18,6 +18,9 @@
</span> </span>
<span ng-switch-default my-i18n="message_service_left_group"></span> <span ng-switch-default my-i18n="message_service_left_group"></span>
</span> </span>
<span ng-switch-when="messageActionChatJoinedByLink" my-i18n="message_service_joined_by_link"></span>
<span ng-switch-default ng-bind="'message_service_unsupported_action' | i18n:historyMessage.action._"></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>
</span> </span>

View File

@ -102,6 +102,8 @@
</span> </span>
</span> </span>
<span ng-switch-when="messageActionChatJoinedByLink" my-i18n="conversation_joined_by_link"></span>
<span class="im_dialog_message_text" ng-if="dialogMessage.message.length" ng-bind-html="dialogMessage.richMessage"></span> <span class="im_dialog_message_text" ng-if="dialogMessage.message.length" ng-bind-html="dialogMessage.richMessage"></span>
</div> </div>
</div> </div>

View File

@ -19,5 +19,9 @@
<span ng-switch-default my-i18n="message_service_left_group"></span> <span ng-switch-default my-i18n="message_service_left_group"></span>
</span> </span>
<span ng-switch-default ng-bind="'message_service_unsupported_action' | i18n:historyMessage.action._"></span> <span ng-switch-when="messageActionChatJoinedByLink" my-i18n="message_service_joined_by_link"></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>
</span> </span>