Browse Source

Supported invite links

master
Igor Zhukov 10 years ago
parent
commit
21f08cd29e
  1. 2
      app/js/app.js
  2. 17
      app/js/controllers.js
  3. 15
      app/js/directives.js
  4. 8
      app/js/locales/en-us.json
  5. 159
      app/js/services.js
  6. 4
      app/partials/desktop/chat_invite_link_modal.html
  7. 6
      app/partials/desktop/confirm_modal.html
  8. 3
      app/partials/desktop/dialog.html
  9. 6
      app/partials/desktop/error_modal.html
  10. 5
      app/partials/desktop/message_service.html
  11. 2
      app/partials/mobile/dialog.html
  12. 6
      app/partials/mobile/message_service.html

2
app/js/app.js

@ -28,7 +28,7 @@ angular.module('myApp', [ @@ -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_');

17
app/js/controllers.js

@ -3618,22 +3618,33 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -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();
})

15
app/js/directives.js

@ -2924,16 +2924,27 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -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);
});
}
};
})

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

@ -189,6 +189,8 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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",

159
app/js/services.js

@ -617,46 +617,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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 {

4
app/partials/desktop/chat_invite_link_modal.html

@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
<div class="md-input-group md-textarea-group" my-labeled-input>
<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>
@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
<div class="md_simple_modal_footer">
<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>

6
app/partials/desktop/confirm_modal.html

@ -47,6 +47,12 @@ @@ -47,6 +47,12 @@
<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="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-when="true" ng-bind="message"></span>
<span ng-switch-default my-i18n="confirm_modal_are_u_sure"></span>

3
app/partials/desktop/dialog.html

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

6
app/partials/desktop/error_modal.html

@ -38,7 +38,11 @@ @@ -38,7 +38,11 @@
<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="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">

5
app/partials/desktop/message_service.html

@ -18,6 +18,9 @@ @@ -18,6 +18,9 @@
</span>
<span ng-switch-default my-i18n="message_service_left_group"></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>

2
app/partials/mobile/dialog.html

@ -102,6 +102,8 @@ @@ -102,6 +102,8 @@
</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>
</div>
</div>

6
app/partials/mobile/message_service.html

@ -19,5 +19,9 @@ @@ -19,5 +19,9 @@
<span ng-switch-default my-i18n="message_service_left_group"></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>

Loading…
Cancel
Save