From 21f08cd29e3117c545772fa4f929ca18a8063e02 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Fri, 1 May 2015 16:01:48 +0300 Subject: [PATCH] Supported invite links --- app/js/app.js | 2 +- app/js/controllers.js | 17 +- app/js/directives.js | 15 +- app/js/locales/en-us.json | 8 +- app/js/services.js | 159 +++++++++++------- .../desktop/chat_invite_link_modal.html | 4 +- app/partials/desktop/confirm_modal.html | 6 + app/partials/desktop/dialog.html | 3 + app/partials/desktop/error_modal.html | 6 +- app/partials/desktop/message_service.html | 5 +- app/partials/mobile/dialog.html | 2 + app/partials/mobile/message_service.html | 6 +- 12 files changed, 160 insertions(+), 73 deletions(-) diff --git a/app/js/app.js b/app/js/app.js index d1576816..b29b135c 100644 --- a/app/js/app.js +++ b/app/js/app.js @@ -28,7 +28,7 @@ angular.module('myApp', [ config(['$locationProvider', '$routeProvider', '$compileProvider', 'StorageProvider', function($locationProvider, $routeProvider, $compileProvider, StorageProvider) { $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) { StorageProvider.setPrefix('t_'); diff --git a/app/js/controllers.js b/app/js/controllers.js index da7618e7..83e0bd3b 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -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.updateLink = function (force) { + function updateLink (force) { if (force) { $scope.exportedInvite.revoking = true; } AppChatsManager.getChatInviteLink($scope.chatID, force).then(function (link) { $scope.exportedInvite = {link: link}; + $timeout(function () { + $scope.$broadcast('ui_invite_select'); + }, 100); })['finally'](function () { delete $scope.exportedInvite.revoking; }); } - $scope.updateLink(); + $scope.revokeLink = function () { + ErrorService.confirm({ + type: 'REVOKE_GROUP_INVITE_LINK' + }).then(function () { + updateLink(true); + }) + } + + updateLink(); }) diff --git a/app/js/directives.js b/app/js/directives.js index cc65f932..925a2e3f 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -2924,16 +2924,27 @@ angular.module('myApp.directives', ['myApp.filters']) .directive('myCopyField', function () { return { + scope: { + selectEvent: '=myCopyField' + }, link: link }; function link($scope, element, attrs) { element.attr('readonly', 'true'); - // element.on('keydown paste', cancelEvent); + element[0].readonly = true; element.on('click', function () { this.select(); }); - element[0].readonly = true; + + if ($scope.selectEvent) { + $scope.$on($scope.selectEvent, function () { + setTimeout(function () { + element[0].focus(); + element[0].select(); + }, 100); + }); + } }; }) diff --git a/app/js/locales/en-us.json b/app/js/locales/en-us.json index 47d30d26..073b7511 100644 --- a/app/js/locales/en-us.json +++ b/app/js/locales/en-us.json @@ -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_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_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_logout_submit": "Log out", @@ -251,6 +253,7 @@ "conversation_kicked_user": "removed {user}", "conversation_invited_user_message": "invited user", "conversation_kicked_user_message": "removed user", + "conversation_joined_by_link": "joined group", "conversation_message_sent": "sent you a message", "conversation_unknown_user": "Somebody", @@ -264,6 +267,7 @@ "message_service_returned_to_group": "returned to group", "message_service_kicked_user": "removed {user}", "message_service_left_group": "left group", + "message_service_joined_by_link": "joined group via invite link", "message_service_unsupported_action": "Unsupported action {action}", "error_modal_warning_title": "Warning", @@ -286,7 +290,7 @@ "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_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_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.", @@ -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_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_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", diff --git a/app/js/services.js b/app/js/services.js index bed9675a..b3b59f02 100755 --- a/app/js/services.js +++ b/app/js/services.js @@ -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) { return angular.isObject(chats[id]); } @@ -777,7 +737,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) getChatPhoto: getChatPhoto, getChatString: getChatString, getChatInviteLink: getChatInviteLink, - openInviteLink: openInviteLink, hasChat: hasChat, wrapForFull: wrapForFull, 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 messagesForHistory = {}; @@ -1963,6 +1922,46 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) 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) { 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, sendOther: sendOther, forwardMessages: forwardMessages, + openChatInviteLink: openChatInviteLink, getMessagePeer: getMessagePeer, wrapForDialog: wrapForDialog, wrapForHistory: wrapForHistory, @@ -4095,7 +4095,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) // resource path "(?:/(?:\\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 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 var contextUrl = !options.noLinks && siteMentions[contextSite]; - if (match[2] != '@' && contextExternal) { - contextUrl = false; - } if (contextUrl) { var attr = ''; if (options.highlightUsername && - options.highlightUsername.toLowerCase() == match[3].toLowerCase() && - match[2] == '@') { + options.highlightUsername.toLowerCase() == match[3].toLowerCase()) { attr = 'class="im_message_mymention"'; } html.push( @@ -4225,6 +4221,19 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) 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 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 () { - if (!$routeParams.tgaddr) { - return; + var tgAddrRegEx = /^(web\+)?tg:(\/\/)?(.+)/; + + 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', { peerString: AppUsersManager.getUserString(userID) }); }); - return; + return true; } - var matches = $routeParams.tgaddr.match(/^(web\+)?tg:(\/\/)?join\?invite=(.+)$/); - if (matches && matches[3]) { - AppChatsManager.openInviteLink(matches[3]); + if (matches = url.match(/^join\?invite=(.+)$/)) { + AppMessagesManager.openChatInviteLink(matches[1]); + return true; } + + return false; } 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'); } 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 { diff --git a/app/partials/desktop/chat_invite_link_modal.html b/app/partials/desktop/chat_invite_link_modal.html index b0282245..5d45f8ca 100644 --- a/app/partials/desktop/chat_invite_link_modal.html +++ b/app/partials/desktop/chat_invite_link_modal.html @@ -8,7 +8,7 @@
- +
@@ -17,7 +17,7 @@ \ No newline at end of file diff --git a/app/partials/desktop/confirm_modal.html b/app/partials/desktop/confirm_modal.html index 5e5dd2ed..d1c583cd 100644 --- a/app/partials/desktop/confirm_modal.html +++ b/app/partials/desktop/confirm_modal.html @@ -47,6 +47,12 @@
+
+ +
+
+ + diff --git a/app/partials/desktop/dialog.html b/app/partials/desktop/dialog.html index fc466f8c..b1a8cc61 100644 --- a/app/partials/desktop/dialog.html +++ b/app/partials/desktop/dialog.html @@ -93,6 +93,9 @@ + + + diff --git a/app/partials/desktop/error_modal.html b/app/partials/desktop/error_modal.html index 84256160..90c518f0 100644 --- a/app/partials/desktop/error_modal.html +++ b/app/partials/desktop/error_modal.html @@ -38,7 +38,11 @@ - + + + + +
diff --git a/app/partials/desktop/message_service.html b/app/partials/desktop/message_service.html index d23f9f1b..39e6cac0 100644 --- a/app/partials/desktop/message_service.html +++ b/app/partials/desktop/message_service.html @@ -18,6 +18,9 @@ + - + + + diff --git a/app/partials/mobile/dialog.html b/app/partials/mobile/dialog.html index 6cd6fa56..9c283976 100644 --- a/app/partials/mobile/dialog.html +++ b/app/partials/mobile/dialog.html @@ -102,6 +102,8 @@ + +
diff --git a/app/partials/mobile/message_service.html b/app/partials/mobile/message_service.html index d23f9f1b..e91b7465 100644 --- a/app/partials/mobile/message_service.html +++ b/app/partials/mobile/message_service.html @@ -19,5 +19,9 @@ - + + + + +