Merge branch 'website-attachment'
This commit is contained in:
commit
423380716a
@ -30,7 +30,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
LayoutSwitchService.start();
|
||||
})
|
||||
|
||||
.controller('AppLoginController', function ($scope, $rootScope, $location, $timeout, $modal, $modalStack, MtpApiManager, ErrorService, NotificationsManager, ChangelogNotifyService, IdleManager, LayoutSwitchService, TelegramMeWebService, _) {
|
||||
.controller('AppLoginController', function ($scope, $rootScope, $location, $timeout, $modal, $modalStack, MtpApiManager, ErrorService, NotificationsManager, PasswordManager, ChangelogNotifyService, IdleManager, LayoutSwitchService, TelegramMeWebService, _) {
|
||||
|
||||
$modalStack.dismissAll();
|
||||
IdleManager.start();
|
||||
@ -156,6 +156,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
|
||||
|
||||
var callTimeout;
|
||||
var updatePasswordTimeout = false;
|
||||
|
||||
function saveAuth (result) {
|
||||
MtpApiManager.setUserAuth(options.dcID, {
|
||||
@ -204,7 +205,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
phone_number: $scope.credentials.phone_full,
|
||||
// sms_type: 5,
|
||||
api_id: Config.App.id,
|
||||
api_hash: Config.App.hash
|
||||
api_hash: Config.App.hash,
|
||||
lang_code: navigator.language || 'en'
|
||||
}, options).then(function (sentCode) {
|
||||
$scope.progress.enabled = false;
|
||||
|
||||
@ -290,6 +292,16 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
} else if (error.code == 400 && error.type == 'PHONE_NUMBER_OCCUPIED') {
|
||||
error.handled = true;
|
||||
return $scope.logIn(false);
|
||||
} else if (error.code == 401 && error.type == 'SESSION_PASSWORD_NEEDED') {
|
||||
$scope.progress.enabled = true;
|
||||
updatePasswordState().then(function () {
|
||||
$scope.progress.enabled = false;
|
||||
$scope.credentials.phone_code_valid = true;
|
||||
$scope.credentials.password_needed = true;
|
||||
$scope.about = {};
|
||||
});
|
||||
error.handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -312,6 +324,82 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
|
||||
};
|
||||
|
||||
$scope.checkPassword = function () {
|
||||
return PasswordManager.check($scope.password, $scope.credentials.password, options).then(saveAuth, function (error) {
|
||||
switch (error.type) {
|
||||
case 'PASSWORD_HASH_INVALID':
|
||||
$scope.error = {field: 'password'};
|
||||
error.handled = true;
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.forgotPassword = function (event) {
|
||||
PasswordManager.requestRecovery($scope.password, options).then(function (emailRecovery) {
|
||||
|
||||
var scope = $rootScope.$new();
|
||||
scope.recovery = emailRecovery;
|
||||
scope.options = options;
|
||||
var modal = $modal.open({
|
||||
scope: scope,
|
||||
templateUrl: templateUrl('password_recovery_modal'),
|
||||
controller: 'PasswordRecoveryModalController',
|
||||
windowClass: 'md_simple_modal_window mobile_modal'
|
||||
});
|
||||
|
||||
modal.result.then(function (result) {
|
||||
if (result && result.user) {
|
||||
saveAuth(result);
|
||||
} else {
|
||||
$scope.canReset = true;
|
||||
}
|
||||
});
|
||||
|
||||
}, function (error) {
|
||||
switch (error.type) {
|
||||
case 'PASSWORD_EMPTY':
|
||||
$scope.logIn();
|
||||
break;
|
||||
case 'PASSWORD_RECOVERY_NA':
|
||||
$timeout(function () {
|
||||
$scope.canReset = true;
|
||||
}, 1000);
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
||||
return cancelEvent(event);
|
||||
};
|
||||
|
||||
$scope.resetAccount = function () {
|
||||
ErrorService.confirm({
|
||||
type: 'RESET_ACCOUNT'
|
||||
}).then(function () {
|
||||
$scope.progress.enabled = true;
|
||||
MtpApiManager.invokeApi('account.deleteAccount', {
|
||||
reason: 'Forgot password'
|
||||
}, options).then(function () {
|
||||
delete $scope.progress.enabled;
|
||||
delete $scope.credentials.password_needed;
|
||||
$scope.credentials.phone_unoccupied = true;
|
||||
}, function () {
|
||||
delete $scope.progress.enabled;
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
function updatePasswordState () {
|
||||
// $timeout.cancel(updatePasswordTimeout);
|
||||
// updatePasswordTimeout = false;
|
||||
return PasswordManager.getState(options).then(function (result) {
|
||||
return $scope.password = result;
|
||||
// if (result._ == 'account.noPassword' && result.email_unconfirmed_pattern) {
|
||||
// updatePasswordTimeout = $timeout(updatePasswordState, 5000);
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
ChangelogNotifyService.checkUpdate();
|
||||
LayoutSwitchService.start();
|
||||
})
|
||||
@ -364,6 +452,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
});
|
||||
};
|
||||
|
||||
// setTimeout($scope.openSettings, 1000);
|
||||
|
||||
$scope.openFaq = function () {
|
||||
var url = 'https://telegram.org/faq';
|
||||
switch (Config.I18n.locale) {
|
||||
@ -1524,7 +1614,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
$scope.$on('user_update', angular.noop);
|
||||
})
|
||||
|
||||
.controller('AppImSendController', function ($scope, $timeout, MtpApiManager, Storage, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, ApiUpdatesManager, MtpApiFileManager) {
|
||||
.controller('AppImSendController', function ($scope, $timeout, MtpApiManager, Storage, AppChatsManager, AppUsersManager, AppPeersManager, AppDocsManager, AppMessagesManager, MtpApiFileManager) {
|
||||
|
||||
$scope.$watch('curDialog.peer', resetDraft);
|
||||
$scope.$on('user_update', angular.noop);
|
||||
@ -1695,7 +1785,10 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
access_hash: doc.access_hash
|
||||
}
|
||||
}
|
||||
AppMessagesManager.sendOther($scope.curDialog.peerID, inputMedia);
|
||||
var options = {
|
||||
replyToMsgID: $scope.draftMessage.replyToMessage && $scope.draftMessage.replyToMessage.id
|
||||
};
|
||||
AppMessagesManager.sendOther($scope.curDialog.peerID, inputMedia, options);
|
||||
$scope.$broadcast('ui_message_send');
|
||||
}
|
||||
delete $scope.draftMessage.sticker;
|
||||
@ -2162,8 +2255,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
MtpApiManager.invokeApi('messages.editChatPhoto', {
|
||||
chat_id: $scope.chatID,
|
||||
photo: {_: 'inputChatPhotoEmpty'}
|
||||
}).then(function (updateResult) {
|
||||
AppMessagesManager.onStatedMessage(updateResult);
|
||||
}).then(function (updates) {
|
||||
ApiUpdatesManager.processUpdateMessage(updates);
|
||||
$modalInstance.dismiss();
|
||||
$rootScope.$broadcast('history_focus', {peerString: AppChatsManager.getChatString($scope.chatID)});
|
||||
})['finally'](function () {
|
||||
@ -2398,8 +2491,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
});
|
||||
|
||||
|
||||
function onStatedMessage (statedMessage) {
|
||||
AppMessagesManager.onStatedMessage(statedMessage);
|
||||
function onChatUpdated (updates) {
|
||||
ApiUpdatesManager.processUpdateMessage(updates);
|
||||
$rootScope.$broadcast('history_focus', {peerString: $scope.chatFull.peerString});
|
||||
}
|
||||
|
||||
@ -2408,14 +2501,14 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
MtpApiManager.invokeApi('messages.deleteChatUser', {
|
||||
chat_id: $scope.chatID,
|
||||
user_id: {_: 'inputUserSelf'}
|
||||
}).then(onStatedMessage);
|
||||
}).then(onChatUpdated);
|
||||
};
|
||||
|
||||
$scope.returnToGroup = function () {
|
||||
MtpApiManager.invokeApi('messages.addChatUser', {
|
||||
chat_id: $scope.chatID,
|
||||
user_id: {_: 'inputUserSelf'}
|
||||
}).then(onStatedMessage);
|
||||
}).then(onChatUpdated);
|
||||
};
|
||||
|
||||
|
||||
@ -2431,19 +2524,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
chat_id: $scope.chatID,
|
||||
user_id: AppUsersManager.getUserInput(userID),
|
||||
fwd_limit: 100
|
||||
}).then(function (addResult) {
|
||||
ApiUpdatesManager.processUpdateMessage({
|
||||
_: 'updates',
|
||||
users: addResult.users,
|
||||
chats: addResult.chats,
|
||||
seq: 0,
|
||||
updates: [{
|
||||
_: 'updateNewMessage',
|
||||
message: addResult.message,
|
||||
pts: addResult.pts,
|
||||
pts_count: addResult.pts_count
|
||||
}]
|
||||
});
|
||||
}).then(function (updates) {
|
||||
ApiUpdatesManager.processUpdateMessage(updates);
|
||||
});
|
||||
});
|
||||
|
||||
@ -2457,7 +2539,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
MtpApiManager.invokeApi('messages.deleteChatUser', {
|
||||
chat_id: $scope.chatID,
|
||||
user_id: {_: 'inputUserForeign', user_id: userID, access_hash: user.access_hash || '0'}
|
||||
}).then(onStatedMessage);
|
||||
}).then(onChatUpdated);
|
||||
};
|
||||
|
||||
|
||||
@ -2488,9 +2570,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
file: inputFile,
|
||||
crop: {_: 'inputPhotoCropAuto'}
|
||||
}
|
||||
}).then(function (updateResult) {
|
||||
onStatedMessage(updateResult);
|
||||
});
|
||||
}).then(onChatUpdated);
|
||||
})['finally'](function () {
|
||||
$scope.photo.updating = false;
|
||||
});
|
||||
@ -2501,9 +2581,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
MtpApiManager.invokeApi('messages.editChatPhoto', {
|
||||
chat_id: $scope.chatID,
|
||||
photo: {_: 'inputChatPhotoEmpty'}
|
||||
}).then(function (updateResult) {
|
||||
onStatedMessage(updateResult);
|
||||
})['finally'](function () {
|
||||
}).then(onChatUpdated)['finally'](function () {
|
||||
$scope.photo.updating = false;
|
||||
});
|
||||
};
|
||||
@ -2522,7 +2600,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
|
||||
})
|
||||
|
||||
.controller('SettingsModalController', function ($rootScope, $scope, $timeout, $modal, AppUsersManager, AppChatsManager, AppPhotosManager, MtpApiManager, Storage, NotificationsManager, MtpApiFileManager, ApiUpdatesManager, ChangelogNotifyService, LayoutSwitchService, AppRuntimeManager, ErrorService, _) {
|
||||
.controller('SettingsModalController', function ($rootScope, $scope, $timeout, $modal, AppUsersManager, AppChatsManager, AppPhotosManager, MtpApiManager, Storage, NotificationsManager, MtpApiFileManager, PasswordManager, ApiUpdatesManager, ChangelogNotifyService, LayoutSwitchService, AppRuntimeManager, ErrorService, _) {
|
||||
|
||||
$scope.profile = {};
|
||||
$scope.photo = {};
|
||||
@ -2545,6 +2623,42 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
|
||||
$scope.$watch('photo.file', onPhotoSelected);
|
||||
|
||||
$scope.password = {_: 'account.noPassword'};
|
||||
updatePasswordState();
|
||||
var updatePasswordTimeout = false;
|
||||
|
||||
$scope.changePassword = function (options) {
|
||||
options = options || {};
|
||||
if (options.action == 'cancel_email') {
|
||||
return ErrorService.confirm({type: 'PASSWORD_ABORT_SETUP'}).then(function () {
|
||||
PasswordManager.updateSettings($scope.password, {email: ''}).then(updatePasswordState);
|
||||
});
|
||||
}
|
||||
var scope = $rootScope.$new();
|
||||
scope.password = $scope.password;
|
||||
angular.extend(scope, options);
|
||||
var modal = $modal.open({
|
||||
scope: scope,
|
||||
templateUrl: templateUrl('password_update_modal'),
|
||||
controller: 'PasswordUpdateModalController',
|
||||
windowClass: 'md_simple_modal_window mobile_modal'
|
||||
});
|
||||
|
||||
modal.result['finally'](updatePasswordState);
|
||||
};
|
||||
|
||||
function updatePasswordState () {
|
||||
$timeout.cancel(updatePasswordTimeout);
|
||||
updatePasswordTimeout = false;
|
||||
PasswordManager.getState().then(function (result) {
|
||||
$scope.password = result;
|
||||
if (result._ == 'account.noPassword' && result.email_unconfirmed_pattern) {
|
||||
updatePasswordTimeout = $timeout(updatePasswordState, 5000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function onPhotoSelected (photo) {
|
||||
if (!photo || !photo.type || photo.type.indexOf('image') !== 0) {
|
||||
return;
|
||||
@ -2870,6 +2984,144 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
})
|
||||
})
|
||||
|
||||
.controller('PasswordUpdateModalController', function ($scope, $q, _, PasswordManager, MtpApiManager, ErrorService, $modalInstance) {
|
||||
|
||||
$scope.passwordSettings = {};
|
||||
|
||||
$scope.updatePassword = function () {
|
||||
delete $scope.passwordSettings.error_field;
|
||||
|
||||
var confirmPromise;
|
||||
if ($scope.action == 'disable') {
|
||||
confirmPromise = $q.when();
|
||||
}
|
||||
else {
|
||||
if (!$scope.passwordSettings.new_password) {
|
||||
$scope.passwordSettings.error_field = 'new_password';
|
||||
$scope.$broadcast('new_password_focus');
|
||||
return false;
|
||||
}
|
||||
if ($scope.passwordSettings.new_password != $scope.passwordSettings.confirm_password) {
|
||||
$scope.passwordSettings.error_field = 'confirm_password';
|
||||
$scope.$broadcast('confirm_password_focus');
|
||||
return false;
|
||||
}
|
||||
confirmPromise = $scope.passwordSettings.email
|
||||
? $q.when()
|
||||
: ErrorService.confirm({type: 'RECOVERY_EMAIL_EMPTY'});
|
||||
}
|
||||
|
||||
$scope.passwordSettings.loading = true;
|
||||
|
||||
confirmPromise.then(function () {
|
||||
PasswordManager.updateSettings($scope.password, {
|
||||
cur_password: $scope.passwordSettings.cur_password || '',
|
||||
new_password: $scope.passwordSettings.new_password,
|
||||
email: $scope.passwordSettings.email,
|
||||
hint: $scope.passwordSettings.hint
|
||||
}).then(function (result) {
|
||||
delete $scope.passwordSettings.loading;
|
||||
$modalInstance.close(true);
|
||||
if ($scope.action == 'disable') {
|
||||
ErrorService.alert(
|
||||
_('error_modal_password_disabled_title'),
|
||||
_('error_modal_password_disabled_descripion')
|
||||
);
|
||||
} else {
|
||||
ErrorService.alert(
|
||||
_('error_modal_password_success_title'),
|
||||
_('error_modal_password_success_descripion')
|
||||
);
|
||||
}
|
||||
}, function (error) {
|
||||
switch (error.type) {
|
||||
case 'PASSWORD_HASH_INVALID':
|
||||
case 'NEW_PASSWORD_BAD':
|
||||
$scope.passwordSettings.error_field = 'cur_password';
|
||||
error.handled = true;
|
||||
$scope.$broadcast('cur_password_focus');
|
||||
break;
|
||||
case 'NEW_PASSWORD_BAD':
|
||||
$scope.passwordSettings.error_field = 'new_password';
|
||||
error.handled = true;
|
||||
break;
|
||||
case 'EMAIL_INVALID':
|
||||
$scope.passwordSettings.error_field = 'email';
|
||||
error.handled = true;
|
||||
break;
|
||||
case 'EMAIL_UNCONFIRMED':
|
||||
ErrorService.alert(
|
||||
_('error_modal_email_unconfirmed_title'),
|
||||
_('error_modal_email_unconfirmed_descripion')
|
||||
);
|
||||
$modalInstance.close(true);
|
||||
error.handled = true;
|
||||
break;
|
||||
}
|
||||
delete $scope.passwordSettings.loading;
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
switch ($scope.action) {
|
||||
case 'disable':
|
||||
$scope.passwordSettings.new_password = '';
|
||||
break;
|
||||
case 'create':
|
||||
onContentLoaded(function () {
|
||||
$scope.$broadcast('new_password_focus');
|
||||
});
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
$scope.$watch('passwordSettings.new_password', function (newValue) {
|
||||
var len = newValue && newValue.length || 0;
|
||||
if (!len) {
|
||||
$scope.passwordSettings.hint = '';
|
||||
}
|
||||
else if (len <= 3) {
|
||||
$scope.passwordSettings.hint = '***';
|
||||
}
|
||||
else {
|
||||
$scope.passwordSettings.hint = newValue.charAt(0) + (new Array(len - 1)).join('*') + newValue.charAt(len - 1);
|
||||
}
|
||||
$scope.$broadcast('value_updated');
|
||||
})
|
||||
})
|
||||
|
||||
.controller('PasswordRecoveryModalController', function ($scope, $q, _, PasswordManager, MtpApiManager, ErrorService, $modalInstance) {
|
||||
|
||||
$scope.checkCode = function () {
|
||||
$scope.recovery.updating = true;
|
||||
|
||||
PasswordManager.recover($scope.recovery.code, $scope.options).then(function (result) {
|
||||
ErrorService.alert(
|
||||
_('error_modal_password_disabled_title'),
|
||||
_('error_modal_password_disabled_descripion')
|
||||
);
|
||||
$modalInstance.close(result);
|
||||
}, function (error) {
|
||||
delete $scope.recovery.updating;
|
||||
switch (error.type) {
|
||||
case 'CODE_EMPTY':
|
||||
case 'CODE_INVALID':
|
||||
$scope.recovery.error_field = 'code';
|
||||
error.handled = true;
|
||||
break;
|
||||
|
||||
case 'PASSWORD_EMPTY':
|
||||
case 'PASSWORD_RECOVERY_NA':
|
||||
case 'PASSWORD_RECOVERY_EXPIRED':
|
||||
$modalInstance.dismiss();
|
||||
error.handled = true;
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
})
|
||||
|
||||
.controller('ContactsModalController', function ($scope, $timeout, $modal, $modalInstance, MtpApiManager, AppUsersManager, ErrorService) {
|
||||
|
||||
$scope.contacts = [];
|
||||
@ -3115,19 +3367,8 @@ angular.module('myApp.controllers', ['myApp.i18n'])
|
||||
return MtpApiManager.invokeApi('messages.editChatTitle', {
|
||||
chat_id: $scope.chatID,
|
||||
title: $scope.group.name
|
||||
}).then(function (editResult) {
|
||||
ApiUpdatesManager.processUpdateMessage({
|
||||
_: 'updates',
|
||||
users: editResult.users,
|
||||
chats: editResult.chats,
|
||||
seq: 0,
|
||||
updates: [{
|
||||
_: 'updateNewMessage',
|
||||
message: editResult.message,
|
||||
pts: editResult.pts,
|
||||
pts_count: editResult.pts_count
|
||||
}]
|
||||
});
|
||||
}).then(function (updates) {
|
||||
ApiUpdatesManager.processUpdateMessage(updates);
|
||||
|
||||
var peerString = AppChatsManager.getChatString($scope.chatID);
|
||||
$rootScope.$broadcast('history_focus', {peerString: peerString});
|
||||
|
@ -457,6 +457,14 @@ angular.module('myApp.directives', ['myApp.filters'])
|
||||
templateUrl: templateUrl('message_attach_contact')
|
||||
};
|
||||
})
|
||||
.directive('myMessageWebpage', function() {
|
||||
return {
|
||||
scope: {
|
||||
'webpage': '=myMessageWebpage'
|
||||
},
|
||||
templateUrl: templateUrl('message_attach_webpage')
|
||||
};
|
||||
})
|
||||
.directive('myMessagePending', function() {
|
||||
return {
|
||||
templateUrl: templateUrl('message_attach_pending')
|
||||
@ -2834,7 +2842,7 @@ angular.module('myApp.directives', ['myApp.filters'])
|
||||
});
|
||||
}
|
||||
|
||||
$scope.$on('value_updated', function (event, args) {
|
||||
$scope.$on('value_updated', function () {
|
||||
setZeroTimeout(function () {
|
||||
updateHasValueClass();
|
||||
});
|
||||
@ -2852,6 +2860,7 @@ angular.module('myApp.directives', ['myApp.filters'])
|
||||
element.on('keydown', function (event) {
|
||||
if (event.keyCode == 13) {
|
||||
element.trigger('submit');
|
||||
return cancelEvent(event);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -317,6 +317,16 @@ function sha1BytesSync (bytes) {
|
||||
return bytesFromArrayBuffer(sha1HashSync(bytes));
|
||||
}
|
||||
|
||||
function sha256HashSync (bytes) {
|
||||
console.log(dT(), 'SHA-2 hash start', bytes.byteLength || bytes.length);
|
||||
var hashWords = CryptoJS.SHA256(bytes);
|
||||
console.log(dT(), 'SHA-2 hash finish');
|
||||
|
||||
var hashBytes = bytesFromWords(hashWords);
|
||||
|
||||
return hashBytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function rsaEncrypt (publicKey, bytes) {
|
||||
|
File diff suppressed because one or more lines are too long
@ -651,6 +651,7 @@ angular.module('izhukov.utils', [])
|
||||
awaiting = {},
|
||||
webCrypto = Config.Modes.webcrypto && window.crypto && (window.crypto.subtle || window.crypto.webkitSubtle)/* || window.msCrypto && window.msCrypto.subtle*/,
|
||||
useSha1Crypto = webCrypto && webCrypto.digest !== undefined,
|
||||
useSha256Crypto = webCrypto && webCrypto.digest !== undefined,
|
||||
finalizeTask = function (taskID, result) {
|
||||
var deferred = awaiting[taskID];
|
||||
if (deferred !== undefined) {
|
||||
@ -728,6 +729,26 @@ angular.module('izhukov.utils', [])
|
||||
return sha1HashSync(bytes);
|
||||
});
|
||||
},
|
||||
sha256Hash: function (bytes) {
|
||||
if (useSha256Crypto) {
|
||||
var deferred = $q.defer(),
|
||||
bytesTyped = Array.isArray(bytes) ? convertToUint8Array(bytes) : bytes;
|
||||
// console.log(dT(), 'Native sha1 start');
|
||||
webCrypto.digest({name: 'SHA-256'}, bytesTyped).then(function (digest) {
|
||||
// console.log(dT(), 'Native sha1 done');
|
||||
deferred.resolve(digest);
|
||||
}, function (e) {
|
||||
console.error('Crypto digest error', e);
|
||||
useSha256Crypto = false;
|
||||
deferred.resolve(sha256HashSync(bytes));
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
return $timeout(function () {
|
||||
return sha256HashSync(bytes);
|
||||
});
|
||||
},
|
||||
aesEncrypt: function (bytes, keyBytes, ivBytes) {
|
||||
if (naClEmbed) {
|
||||
return performTaskWorker('aes-encrypt', {
|
||||
|
@ -191,11 +191,7 @@ messages.messagesSlice#b446ae3 count:int messages:Vector<Message> chats:Vector<C
|
||||
|
||||
messages.messageEmpty#3f4e0648 = messages.Message;
|
||||
|
||||
messages.statedMessages#7d84b48 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> pts:int pts_count:int = messages.StatedMessages;
|
||||
|
||||
messages.statedMessage#96240c6a message:Message chats:Vector<Chat> users:Vector<User> pts:int pts_count:int = messages.StatedMessage;
|
||||
|
||||
messages.sentMessage#900eac40 id:int date:int pts:int pts_count:int = messages.SentMessage;
|
||||
messages.sentMessage#4c3d47f3 id:int date:int media:MessageMedia pts:int pts_count:int = messages.SentMessage;
|
||||
|
||||
messages.chats#64ff9fd5 chats:Vector<Chat> = messages.Chats;
|
||||
|
||||
@ -247,7 +243,7 @@ upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
|
||||
|
||||
dcOption#2ec2a43c id:int hostname:string ip_address:string port:int = DcOption;
|
||||
|
||||
config#3e6f732a date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int broadcast_size_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 disabled_features:Vector<DisabledFeature> = Config;
|
||||
config#68bac247 date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int broadcast_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 disabled_features:Vector<DisabledFeature> = Config;
|
||||
|
||||
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
|
||||
|
||||
@ -256,11 +252,7 @@ help.noAppUpdate#c45a6536 = help.AppUpdate;
|
||||
|
||||
help.inviteText#18cb9f78 message:string = help.InviteText;
|
||||
|
||||
messages.statedMessagesLinks#51be5d19 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> pts:int pts_count:int links:Vector<contacts.Link> seq:int = messages.StatedMessages;
|
||||
|
||||
messages.statedMessageLink#948a288 message:Message chats:Vector<Chat> users:Vector<User> pts:int pts_count:int links:Vector<contacts.Link> seq:int = messages.StatedMessage;
|
||||
|
||||
messages.sentMessageLink#e923400d id:int date:int pts:int pts_count:int links:Vector<contacts.Link> seq:int = messages.SentMessage;
|
||||
messages.sentMessageLink#35a1a663 id:int date:int media:MessageMedia pts:int pts_count:int links:Vector<contacts.Link> seq:int = messages.SentMessage;
|
||||
|
||||
inputGeoChat#74d456fa chat_id:int access_hash:long = InputGeoChat;
|
||||
|
||||
@ -410,9 +402,6 @@ account.sentChangePhoneCode#a4f58c4c phone_code_hash:string send_call_timeout:in
|
||||
|
||||
updateUserPhone#12b9417b user_id:int phone:string = Update;
|
||||
|
||||
account.noPassword#5770e7a9 new_salt:bytes = account.Password;
|
||||
account.password#739e5f72 current_salt:bytes new_salt:bytes hint:string = account.Password;
|
||||
|
||||
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
|
||||
documentAttributeAnimated#11b58939 = DocumentAttribute;
|
||||
documentAttributeSticker#994c9882 alt:string = DocumentAttribute;
|
||||
@ -440,6 +429,27 @@ contactLinkNone#feedd3ad = ContactLink;
|
||||
contactLinkHasPhone#268f3f59 = ContactLink;
|
||||
contactLinkContact#d502c2d0 = ContactLink;
|
||||
|
||||
updateWebPage#2cc36971 webpage:WebPage = Update;
|
||||
|
||||
webPageEmpty#eb1477e8 id:long = WebPage;
|
||||
webPagePending#c586da1c id:long date:int = WebPage;
|
||||
webPage#a31ea0b5 flags:# id:long url:string display_url:string type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string = WebPage;
|
||||
|
||||
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
|
||||
|
||||
authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization;
|
||||
|
||||
account.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations;
|
||||
|
||||
account.noPassword#96dabc18 new_salt:bytes email_unconfirmed_pattern:string = account.Password;
|
||||
account.password#7c18141c current_salt:bytes new_salt:bytes hint:string has_recovery:Bool email_unconfirmed_pattern:string = account.Password;
|
||||
|
||||
account.passwordSettings#b7b72ab3 email:string = account.PasswordSettings;
|
||||
|
||||
account.passwordInputSettings#bcfc532c flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string = account.PasswordInputSettings;
|
||||
|
||||
auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@ -491,16 +501,16 @@ messages.deleteHistory#f4f8fb61 peer:InputPeer offset:int = messages.AffectedHis
|
||||
messages.deleteMessages#a5f18925 id:Vector<int> = messages.AffectedMessages;
|
||||
messages.receivedMessages#28abcb68 max_id:int = Vector<int>;
|
||||
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
|
||||
messages.sendMessage#1ca852a1 peer:InputPeer reply_to_msg_id:int message:string random_id:long = messages.SentMessage;
|
||||
messages.sendMedia#fcee7fc0 peer:InputPeer reply_to_msg_id:int media:InputMedia random_id:long = messages.StatedMessage;
|
||||
messages.forwardMessages#ded42045 peer:InputPeer id:Vector<int> random_id:Vector<long> = messages.StatedMessages;
|
||||
messages.sendMessage#9add8f26 flags:# peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long = messages.SentMessage;
|
||||
messages.sendMedia#2d7923b1 flags:# peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia random_id:long = Updates;
|
||||
messages.forwardMessages#55e1728d peer:InputPeer id:Vector<int> random_id:Vector<long> = Updates;
|
||||
messages.getChats#3c6aa187 id:Vector<int> = messages.Chats;
|
||||
messages.getFullChat#3b831c66 chat_id:int = messages.ChatFull;
|
||||
messages.editChatTitle#b4bc68b5 chat_id:int title:string = messages.StatedMessage;
|
||||
messages.editChatPhoto#d881821d chat_id:int photo:InputChatPhoto = messages.StatedMessage;
|
||||
messages.addChatUser#2ee9ee9e chat_id:int user_id:InputUser fwd_limit:int = messages.StatedMessage;
|
||||
messages.deleteChatUser#c3c5cd23 chat_id:int user_id:InputUser = messages.StatedMessage;
|
||||
messages.createChat#419d9aee users:Vector<InputUser> title:string = messages.StatedMessage;
|
||||
messages.editChatTitle#dc452855 chat_id:int title:string = Updates;
|
||||
messages.editChatPhoto#ca4c79d8 chat_id:int photo:InputChatPhoto = Updates;
|
||||
messages.addChatUser#f9a0aa09 chat_id:int user_id:InputUser fwd_limit:int = Updates;
|
||||
messages.deleteChatUser#e0611f16 chat_id:int user_id:InputUser = Updates;
|
||||
messages.createChat#9cb126e users:Vector<InputUser> title:string = Updates;
|
||||
|
||||
updates.getState#edd4882a = updates.State;
|
||||
updates.getDifference#a041495 pts:int date:int qts:int = updates.Difference;
|
||||
@ -520,8 +530,8 @@ help.getInviteText#a4a95186 lang_code:string = help.InviteText;
|
||||
|
||||
photos.getUserPhotos#b7ee553c user_id:InputUser offset:int max_id:int limit:int = photos.Photos;
|
||||
|
||||
messages.forwardMessage#3f3f4f2 peer:InputPeer id:int random_id:long = messages.StatedMessage;
|
||||
messages.sendBroadcast#41bb0972 contacts:Vector<InputUser> message:string media:InputMedia = messages.StatedMessages;
|
||||
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;
|
||||
|
||||
geochats.getLocated#7f192d8f geo_point:InputGeoPoint radius:int limit:int = geochats.Located;
|
||||
geochats.getRecents#e1427e6f offset:int limit:int = geochats.Messages;
|
||||
@ -574,12 +584,20 @@ contacts.resolveUsername#bf0131c username:string = User;
|
||||
|
||||
account.sendChangePhoneCode#a407a8f4 phone_number:string = account.SentChangePhoneCode;
|
||||
account.changePhone#70c32edb phone_number:string phone_code_hash:string phone_code:string = User;
|
||||
account.getPassword#548a30f5 = account.Password;
|
||||
account.setPassword#dd2a4d8f current_password_hash:bytes new_salt:bytes new_password_hash:bytes hint:string = Bool;
|
||||
|
||||
auth.checkPassword#a63011e password_hash:bytes = auth.Authorization;
|
||||
|
||||
messages.getStickers#ae22e045 emoticon:string hash:string = messages.Stickers;
|
||||
messages.getAllStickers#aa3bc868 hash:string = messages.AllStickers;
|
||||
|
||||
account.updateDeviceLocked#38df3532 period:int = Bool;
|
||||
account.updateDeviceLocked#38df3532 period:int = Bool;
|
||||
|
||||
messages.getWebPagePreview#25223e24 message:string = MessageMedia;
|
||||
|
||||
account.getAuthorizations#e320c158 = account.Authorizations;
|
||||
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;
|
||||
|
||||
auth.checkPassword#a63011e password_hash:bytes = auth.Authorization;
|
||||
auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
|
||||
auth.recoverPassword#4ea56e92 code:string = auth.Authorization;
|
@ -122,6 +122,9 @@ TLSerialization.prototype.storeDouble = function (f) {
|
||||
TLSerialization.prototype.storeString = function (s, field) {
|
||||
this.debug && console.log('>>>', s, (field || '') + ':string');
|
||||
|
||||
if (s === undefined) {
|
||||
s = '';
|
||||
}
|
||||
var sUTF8 = unescape(encodeURIComponent(s));
|
||||
|
||||
this.checkLength(sUTF8.length + 8);
|
||||
@ -151,6 +154,9 @@ TLSerialization.prototype.storeBytes = function (bytes, field) {
|
||||
if (bytes instanceof ArrayBuffer) {
|
||||
bytes = new Uint8Array(bytes);
|
||||
}
|
||||
else if (bytes === undefined) {
|
||||
bytes = [];
|
||||
}
|
||||
this.debug && console.log('>>>', bytesToHex(bytes), (field || '') + ':bytes');
|
||||
|
||||
var len = bytes.byteLength || bytes.length;
|
||||
@ -222,7 +228,17 @@ TLSerialization.prototype.storeMethod = function (methodName, params) {
|
||||
|
||||
var self = this;
|
||||
angular.forEach(methodData.params, function (param) {
|
||||
self.storeObject(params[param.name], param.type, methodName + '[' + param.name + ']');
|
||||
var type = param.type;
|
||||
if (type.indexOf('?') !== -1) {
|
||||
var condType = type.split('?');
|
||||
var fieldBit = condType[0].split('.');
|
||||
if (!(params[fieldBit[0]] & (1 << fieldBit[1]))) {
|
||||
return;
|
||||
}
|
||||
type = condType[1];
|
||||
}
|
||||
|
||||
self.storeObject(params[param.name], type, methodName + '[' + param.name + ']');
|
||||
});
|
||||
|
||||
return methodData.type;
|
||||
@ -230,6 +246,7 @@ TLSerialization.prototype.storeMethod = function (methodName, params) {
|
||||
|
||||
TLSerialization.prototype.storeObject = function (obj, type, field) {
|
||||
switch (type) {
|
||||
case '#':
|
||||
case 'int': return this.storeInt(obj, field);
|
||||
case 'long': return this.storeLong(obj, field);
|
||||
case 'int128': return this.storeIntBytes(obj, 128, field);
|
||||
@ -293,7 +310,17 @@ TLSerialization.prototype.storeObject = function (obj, type, field) {
|
||||
|
||||
var self = this;
|
||||
angular.forEach(constructorData.params, function (param) {
|
||||
self.storeObject(obj[param.name], param.type, field + '[' + predicate + '][' + param.name + ']');
|
||||
var type = param.type;
|
||||
if (type.indexOf('?') !== -1) {
|
||||
var condType = type.split('?');
|
||||
var fieldBit = condType[0].split('.');
|
||||
if (!(obj[fieldBit[0]] & (1 << fieldBit[1]))) {
|
||||
return;
|
||||
}
|
||||
type = condType[1];
|
||||
}
|
||||
|
||||
self.storeObject(obj[param.name], type, field + '[' + predicate + '][' + param.name + ']');
|
||||
});
|
||||
|
||||
return constructorData.type;
|
||||
|
@ -54,6 +54,26 @@
|
||||
"settings_modal_follow_us_twitter": "Follow us on Twitter!",
|
||||
"settings_modal_recent_updates": "Recent updates (ver. {version})",
|
||||
|
||||
"settings_modal_set_password": "Set Additional Password",
|
||||
"settings_modal_change_password": "Change password",
|
||||
"settings_modal_disable_password": "Turn off",
|
||||
"settings_modal_password_email_pending": "Click the link in {email} to complete Two-Step Verification setup.",
|
||||
"settings_modal_password_email_pending_cancel": "Abort",
|
||||
|
||||
"password_delete_title": "Turn Password Off",
|
||||
"password_change_title": "Two-Step Verification",
|
||||
"password_current_placeholder": "Enter current password",
|
||||
"password_create_placeholder": "Enter a password",
|
||||
"password_new_placeholder": "Enter new password",
|
||||
"password_confirm_placeholder": "Re-enter new password",
|
||||
"password_hint_placeholder": "Enter password hint",
|
||||
"password_email_placeholder": "Enter recovery e-mail",
|
||||
"password_create_description": "This password will be required when you log in on a new device in addition to the pin code.",
|
||||
"password_create_active": "Saving...",
|
||||
"password_create_submit": "Save",
|
||||
"password_delete_active": "Deleting...",
|
||||
"password_delete_submit": "Delete password",
|
||||
|
||||
"page_title_pluralize_notifications": "{'0': 'No notifications', 'one': '1 notification', 'other': '{} notifications'}",
|
||||
|
||||
"profile_edit_modal_title": "Edit profile",
|
||||
@ -147,6 +167,9 @@
|
||||
"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?",
|
||||
"confirm_modal_resize_mobile_md": "Would you like to switch to mobile version?",
|
||||
"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_are_u_sure": "Are you sure?",
|
||||
|
||||
"confirm_modal_logout_submit": "Log Out",
|
||||
@ -161,6 +184,7 @@
|
||||
"confirm_modal_share_video_submit": "Forward video",
|
||||
"confirm_modal_share_contact_submit": "Send contact",
|
||||
"confirm_modal_share_file_submit": "Share file",
|
||||
"confirm_modal_reset_account_submit": "Reset my account",
|
||||
|
||||
"contacts_modal_edit_list": "Edit",
|
||||
"contacts_modal_edit_cancel": "Cancel",
|
||||
@ -232,7 +256,12 @@
|
||||
"error_modal_flood_title": "Too fast",
|
||||
"error_modal_internal_title": "Server error",
|
||||
"error_modal_alert": "Alert",
|
||||
"error_modal_email_unconfirmed_title": "Almost there!",
|
||||
"error_modal_email_unconfirmed_descripion": "Please check your e-mail (don't forget the spam folder) to complete Two-Step Verification setup.",
|
||||
"error_modal_password_success_title": "Success!",
|
||||
"error_modal_password_disabled_title": "Password deactivated",
|
||||
"error_modal_media_not_supported_title": "Unsupported media",
|
||||
"error_modal_recovery_na_title": "Sorry",
|
||||
|
||||
"error_modal_network_description": "Please check your internet connection.",
|
||||
"error_modal_firstname_invali_description": "The first name you entered is invalid.",
|
||||
@ -258,6 +287,9 @@
|
||||
"error_modal_internal_description": "Internal server error occured. Please try again later.",
|
||||
"error_modal_tech_details": "Technical details here",
|
||||
"error_modal_multiple_open_tabs": "Please close other Telegram app tabs.",
|
||||
"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.",
|
||||
|
||||
|
||||
"head_telegram": "Telegram",
|
||||
@ -367,6 +399,21 @@
|
||||
"login_about_desc3_md": "Our {source-link: source code} is open, so everyone can make a contribution.",
|
||||
"login_about_intro": "Welcome to the official Telegram web-client.",
|
||||
"login_about_learn": "Learn more",
|
||||
"login_password_title": "Password",
|
||||
"login_password_label": "You have enabled Two-Step Verification, so your account is protected with an additional password.",
|
||||
"login_password_forgot_link": "Forgot password?",
|
||||
"login_account_reset": "Reset account",
|
||||
"login_password": "Your Password",
|
||||
"login_incorrect_password": "Incorrect password",
|
||||
"login_checking_password": "Checking",
|
||||
"login_recovery_title": "Forgot password?",
|
||||
"login_code_placeholder": "Code",
|
||||
"login_code_incorrect": "Incorrect code",
|
||||
"login_recovery_description_md": "We have sent a recovery code to the e-mail you provided:\n\n{email}\n\nPlease check your e-mail and enter the 6-digit code we have sent here.",
|
||||
|
||||
"password_recover_active": "Checking...",
|
||||
"password_recover_submit": "Submit",
|
||||
|
||||
|
||||
"login_controller_unknown_country": "Unknown",
|
||||
|
||||
|
@ -802,6 +802,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
var pendingByRandomID = {};
|
||||
var pendingByMessageID = {};
|
||||
var pendingAfterMsgs = {};
|
||||
var pendingWebPages = {};
|
||||
var sendFilePromise = $q.when();
|
||||
var tempID = -1;
|
||||
|
||||
@ -1397,6 +1398,18 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
case 'messageMediaAudio':
|
||||
AppAudioManager.saveAudio(apiMessage.media.audio);
|
||||
break;
|
||||
case 'messageMediaWebPage':
|
||||
var webpage = apiMessage.media.webpage;
|
||||
if (webpage.photo && webpage.photo._ === 'photo') {
|
||||
AppPhotosManager.savePhoto(webpage.photo);
|
||||
} else {
|
||||
delete webpage.photo;
|
||||
}
|
||||
if (pendingWebPages[webpage.id] === undefined) {
|
||||
pendingWebPages[webpage.id] = {};
|
||||
}
|
||||
pendingWebPages[webpage.id][apiMessage.id] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (apiMessage.action && apiMessage.action._ == 'messageActionChatEditPhoto') {
|
||||
@ -1465,7 +1478,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
if (pendingAfterMsgs[peerID]) {
|
||||
sentRequestOptions.afterMessageID = pendingAfterMsgs[peerID].messageID;
|
||||
}
|
||||
var flags = 0;
|
||||
if (replyToMsgID) {
|
||||
flags |= 1;
|
||||
}
|
||||
MtpApiManager.invokeApi('messages.sendMessage', {
|
||||
flags: flags,
|
||||
peer: inputPeer,
|
||||
message: text,
|
||||
random_id: randomID,
|
||||
@ -1473,6 +1491,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
}, sentRequestOptions).then(function (sentMessage) {
|
||||
message.date = sentMessage.date;
|
||||
message.id = sentMessage.id;
|
||||
message.media = sentMessage.media;
|
||||
|
||||
ApiUpdatesManager.processUpdateMessage({
|
||||
_: 'updates',
|
||||
@ -1625,32 +1644,18 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
{_: 'documentAttributeFilename', file_name: file.name}
|
||||
]};
|
||||
}
|
||||
var flags = 0;
|
||||
if (replyToMsgID) {
|
||||
flags |= 1;
|
||||
}
|
||||
MtpApiManager.invokeApi('messages.sendMedia', {
|
||||
flags: flags,
|
||||
peer: inputPeer,
|
||||
media: inputMedia,
|
||||
random_id: randomID,
|
||||
reply_to_msg_id: replyToMsgID
|
||||
}).then(function (statedMessage) {
|
||||
message.date = statedMessage.message.date;
|
||||
message.id = statedMessage.message.id;
|
||||
message.media = statedMessage.message.media;
|
||||
|
||||
ApiUpdatesManager.processUpdateMessage({
|
||||
_: 'updates',
|
||||
users: statedMessage.users,
|
||||
chats: statedMessage.chats,
|
||||
seq: 0,
|
||||
updates: [{
|
||||
_: 'updateMessageID',
|
||||
random_id: randomIDS,
|
||||
id: statedMessage.message.id
|
||||
}, {
|
||||
_: 'updateNewMessage',
|
||||
message: message,
|
||||
pts: statedMessage.pts,
|
||||
pts_count: statedMessage.pts_count
|
||||
}]
|
||||
});
|
||||
}).then(function (updates) {
|
||||
ApiUpdatesManager.processUpdateMessage(updates);
|
||||
}, function (error) {
|
||||
if (attachType == 'photo' &&
|
||||
error.code == 400 &&
|
||||
@ -1697,12 +1702,15 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
pendingByRandomID[randomIDS] = [peerID, messageID];
|
||||
}
|
||||
|
||||
function sendOther(peerID, inputMedia) {
|
||||
function sendOther(peerID, inputMedia, options) {
|
||||
options = options || {};
|
||||
|
||||
var messageID = tempID--,
|
||||
randomID = [nextRandomInt(0xFFFFFFFF), nextRandomInt(0xFFFFFFFF)],
|
||||
randomIDS = bigint(randomID[0]).shiftLeft(32).add(bigint(randomID[1])).toString(),
|
||||
historyStorage = historiesStorage[peerID],
|
||||
inputPeer = AppPeersManager.getInputPeerByID(peerID);
|
||||
inputPeer = AppPeersManager.getInputPeerByID(peerID),
|
||||
replyToMsgID = options.replyToMsgID;
|
||||
|
||||
if (historyStorage === undefined) {
|
||||
historyStorage = historiesStorage[peerID] = {count: null, history: [], pending: []};
|
||||
@ -1760,32 +1768,18 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
}
|
||||
|
||||
message.send = function () {
|
||||
var flags = 0;
|
||||
if (replyToMsgID) {
|
||||
flags |= 1;
|
||||
}
|
||||
MtpApiManager.invokeApi('messages.sendMedia', {
|
||||
flags: flags,
|
||||
peer: inputPeer,
|
||||
media: inputMedia,
|
||||
random_id: randomID,
|
||||
reply_to_msg_id: 0
|
||||
}).then(function (statedMessage) {
|
||||
message.date = statedMessage.message.date;
|
||||
message.id = statedMessage.message.id;
|
||||
message.media = statedMessage.message.media;
|
||||
|
||||
ApiUpdatesManager.processUpdateMessage({
|
||||
_: 'updates',
|
||||
users: statedMessage.users,
|
||||
chats: statedMessage.chats,
|
||||
seq: 0,
|
||||
updates: [{
|
||||
_: 'updateMessageID',
|
||||
random_id: randomIDS,
|
||||
id: statedMessage.message.id
|
||||
}, {
|
||||
_: 'updateNewMessage',
|
||||
message: message,
|
||||
pts: statedMessage.pts,
|
||||
pts_count: statedMessage.pts_count
|
||||
}]
|
||||
});
|
||||
reply_to_msg_id: replyToMsgID
|
||||
}).then(function (updates) {
|
||||
ApiUpdatesManager.processUpdateMessage(updates);
|
||||
}, function (error) {
|
||||
toggleError(true);
|
||||
});
|
||||
@ -1815,24 +1809,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
peer: AppPeersManager.getInputPeerByID(peerID),
|
||||
id: msgIDs,
|
||||
random_id: randomIDs
|
||||
}).then(function (statedMessages) {
|
||||
var updates = [];
|
||||
angular.forEach(statedMessages.messages, function(apiMessage) {
|
||||
updates.push({
|
||||
_: 'updateNewMessage',
|
||||
message: apiMessage,
|
||||
pts: statedMessages.pts,
|
||||
pts_count: statedMessages.pts_count
|
||||
});
|
||||
});
|
||||
|
||||
ApiUpdatesManager.processUpdateMessage({
|
||||
_: 'updates',
|
||||
users: statedMessages.users,
|
||||
chats: statedMessages.chats,
|
||||
seq: 0,
|
||||
updates: updates
|
||||
});
|
||||
}).then(function (updates) {
|
||||
ApiUpdatesManager.processUpdateMessage(updates);
|
||||
});
|
||||
};
|
||||
|
||||
@ -1911,21 +1889,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
return false;
|
||||
}
|
||||
|
||||
function onStatedMessage (statedMessage) {
|
||||
ApiUpdatesManager.processUpdateMessage({
|
||||
_: 'updates',
|
||||
users: statedMessage.users,
|
||||
chats: statedMessage.chats,
|
||||
seq: 0,
|
||||
updates: [{
|
||||
_: 'updateNewMessage',
|
||||
message: statedMessage.message,
|
||||
pts: statedMessage.pts,
|
||||
pts_count: statedMessage.pts_count
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
function getMessagePeer (message) {
|
||||
var toID = message.to_id && AppPeersManager.getPeerID(message.to_id) || 0;
|
||||
|
||||
@ -2013,6 +1976,12 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
{noLinks: true, noLinebreaks: true}
|
||||
);
|
||||
break;
|
||||
|
||||
case 'messageMediaWebPage':
|
||||
if (message.media.webpage.photo) {
|
||||
message.media.webpage.photo = AppPhotosManager.wrapForHistory(message.media.webpage.photo.id, {website: true});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (message.action) {
|
||||
@ -2535,6 +2504,32 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'updateWebPage':
|
||||
console.log('webpage update', update);
|
||||
var webpage = update.webpage;
|
||||
if (pendingWebPages[webpage.id] !== undefined) {
|
||||
var message, historyMessage;
|
||||
angular.forEach(pendingWebPages[webpage.id], function (t, msgID) {
|
||||
if (message = messagesStorage[msgID]) {
|
||||
message.media = {
|
||||
_: 'messageMediaWebPage',
|
||||
webpage: webpage
|
||||
};
|
||||
}
|
||||
if (historyMessage = messagesForHistory[msgID]) {
|
||||
if (webpage.photo) {
|
||||
AppPhotosManager.savePhoto(webpage.photo);
|
||||
webpage.photo = AppPhotosManager.wrapForHistory(webpage.photo.id, {website: true});
|
||||
}
|
||||
historyMessage.media = {
|
||||
_: 'messageMediaWebPage',
|
||||
webpage: webpage
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
@ -2551,7 +2546,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
sendFile: sendFile,
|
||||
sendOther: sendOther,
|
||||
forwardMessages: forwardMessages,
|
||||
onStatedMessage: onStatedMessage,
|
||||
getMessagePeer: getMessagePeer,
|
||||
wrapForDialog: wrapForDialog,
|
||||
wrapForHistory: wrapForHistory,
|
||||
@ -2652,10 +2646,11 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
return photos[photoID] || {_: 'photoEmpty'};
|
||||
}
|
||||
|
||||
function wrapForHistory (photoID) {
|
||||
function wrapForHistory (photoID, options) {
|
||||
options = options || {};
|
||||
var photo = angular.copy(photos[photoID]) || {_: 'photoEmpty'},
|
||||
width = Math.min(windowW - 80, Config.Mobile ? 210 : 260),
|
||||
height = Math.min(windowH - 100, Config.Mobile ? 210 : 260),
|
||||
width = options.website ? 100 : Math.min(windowW - 80, Config.Mobile ? 210 : 260),
|
||||
height = options.website ? 100 : Math.min(windowH - 100, Config.Mobile ? 210 : 260),
|
||||
thumbPhotoSize = choosePhotoSize(photo, width, height),
|
||||
thumb = {
|
||||
placeholder: 'img/placeholders/PhotoThumbConversation.gif',
|
||||
@ -4543,6 +4538,106 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
|
||||
|
||||
})
|
||||
|
||||
.service('PasswordManager', function ($timeout, $q, $rootScope, MtpApiManager, CryptoWorker, MtpSecureRandom) {
|
||||
|
||||
return {
|
||||
check: check,
|
||||
getState: getState,
|
||||
requestRecovery: requestRecovery,
|
||||
recover: recover,
|
||||
updateSettings: updateSettings
|
||||
};
|
||||
|
||||
function getState (options) {
|
||||
return MtpApiManager.invokeApi('account.getPassword', {}, options).then(function (result) {
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
function updateSettings (state, settings) {
|
||||
var currentHashPromise;
|
||||
var newHashPromise;
|
||||
var params = {
|
||||
new_settings: {
|
||||
_: 'account.passwordInputSettings',
|
||||
flags: 0,
|
||||
hint: settings.hint || ''
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof settings.cur_password === 'string' &&
|
||||
settings.cur_password.length > 0) {
|
||||
currentHashPromise = makePasswordHash(state.current_salt, settings.cur_password);
|
||||
} else {
|
||||
currentHashPromise = $q.when([]);
|
||||
}
|
||||
|
||||
if (typeof settings.new_password === 'string' &&
|
||||
settings.new_password.length > 0) {
|
||||
var saltRandom = new Array(8);
|
||||
var newSalt = bufferConcat(state.new_salt, saltRandom);
|
||||
MtpSecureRandom.nextBytes(saltRandom);
|
||||
newHashPromise = makePasswordHash(newSalt, settings.new_password);
|
||||
params.new_settings.new_salt = newSalt;
|
||||
params.new_settings.flags |= 1;
|
||||
} else {
|
||||
if (typeof settings.new_password === 'string') {
|
||||
params.new_settings.flags |= 1;
|
||||
params.new_settings.new_salt = [];
|
||||
}
|
||||
newHashPromise = $q.when([]);
|
||||
}
|
||||
|
||||
if (typeof settings.email === 'string') {
|
||||
params.new_settings.flags |= 2;
|
||||
params.new_settings.email = settings.email || '';
|
||||
}
|
||||
|
||||
return $q.all([currentHashPromise, newHashPromise]).then(function (hashes) {
|
||||
params.current_password_hash = hashes[0];
|
||||
params.new_settings.new_password_hash = hashes[1];
|
||||
|
||||
return MtpApiManager.invokeApi('account.updatePasswordSettings', params);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function check (state, password, options) {
|
||||
return makePasswordHash(state.current_salt, password).then(function (passwordHash) {
|
||||
return MtpApiManager.invokeApi('auth.checkPassword', {
|
||||
password_hash: passwordHash
|
||||
}, options);
|
||||
});
|
||||
}
|
||||
|
||||
function requestRecovery (state, options) {
|
||||
return MtpApiManager.invokeApi('auth.requestPasswordRecovery', {}, options);
|
||||
}
|
||||
|
||||
function recover (code, options) {
|
||||
return MtpApiManager.invokeApi('auth.recoverPassword', {
|
||||
code: code
|
||||
}, options);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function makePasswordHash (salt, password) {
|
||||
var passwordUTF8 = unescape(encodeURIComponent(password));
|
||||
|
||||
var buffer = new ArrayBuffer(passwordUTF8.length);
|
||||
var byteView = new Uint8Array(buffer);
|
||||
for (var i = 0, len = passwordUTF8.length; i < len; i++) {
|
||||
byteView[i] = passwordUTF8.charCodeAt(i);
|
||||
}
|
||||
|
||||
buffer = bufferConcat(bufferConcat(salt, byteView), salt);
|
||||
|
||||
return CryptoWorker.sha256Hash(buffer);
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
.service('ErrorService', function ($rootScope, $modal, $window) {
|
||||
|
||||
|
@ -242,6 +242,9 @@ input[type="number"] {
|
||||
padding: 0;
|
||||
margin: 0 0 22px;
|
||||
}
|
||||
.md-input-grouped {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.md-input-label {
|
||||
font-weight: normal;
|
||||
@ -974,6 +977,12 @@ a.tg_radio_on:hover i.icon-radio {
|
||||
font-size: 13px;
|
||||
line-height: 160%;
|
||||
}
|
||||
.login_form_hint {
|
||||
color: #999;
|
||||
margin: 0 0 20px;
|
||||
font-size: 13px;
|
||||
line-height: 160%;
|
||||
}
|
||||
.login_form_messaging {
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
@ -1002,6 +1011,15 @@ a.tg_radio_on:hover i.icon-radio {
|
||||
}
|
||||
}
|
||||
|
||||
.login_forgot_button {
|
||||
text-align: center;
|
||||
margin: 30px 0 10px;
|
||||
}
|
||||
.login_reset_button {
|
||||
text-align: center;
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
|
||||
/* IM page start */
|
||||
|
||||
/* Dialogs list */
|
||||
@ -1614,7 +1632,8 @@ img.im_message_document_thumb {
|
||||
width: 265px;
|
||||
padding: 0 0 1px;
|
||||
}
|
||||
.im_message_document_actions {
|
||||
.im_message_document_actions,
|
||||
.im_message_website_description {
|
||||
width: 265px;
|
||||
}
|
||||
.im_message_document_name {
|
||||
@ -1760,7 +1779,8 @@ img.im_message_document_thumb {
|
||||
.im_message_document_name_wrap,
|
||||
.im_message_upload_progress_wrap,
|
||||
.im_message_download_progress_wrap,
|
||||
.im_message_document_actions {
|
||||
.im_message_document_actions,
|
||||
.im_message_website_description {
|
||||
width: 207px;
|
||||
}
|
||||
.im_message_document_name {
|
||||
@ -3361,6 +3381,15 @@ a.countries_modal_search_clear {
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
&.pull-right {
|
||||
color: #3a6d99;
|
||||
}
|
||||
}
|
||||
|
||||
&_text {
|
||||
display: block;
|
||||
padding: 4px 0;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
&_version {
|
||||
|
@ -43,6 +43,9 @@
|
||||
<div ng-switch-when="MIGRATE_TO_HTTPS" my-i18n="confirm_modal_migrate_to_https_md"></div>
|
||||
<div ng-switch-when="SWITCH_DESKTOP_VERSION" my-i18n="confirm_modal_resize_desktop_md"></div>
|
||||
<div ng-switch-when="SWITCH_MOBILE_VERSION" my-i18n="confirm_modal_resize_mobile_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="RESET_ACCOUNT" my-i18n="confirm_modal_reset_account_md"></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>
|
||||
@ -55,7 +58,7 @@
|
||||
<button class="btn btn-md" ng-click="$dismiss()">
|
||||
<span my-i18n="modal_cancel"></span>
|
||||
</button>
|
||||
<button class="btn btn-md btn-md-primary" ng-switch="type" ng-click="$close()" my-focused >
|
||||
<button class="btn btn-md btn-md-primary" ng-switch="type" ng-click="$close()" ng-class="{'btn-md-danger': type == 'RESET_ACCOUNT'}" my-focused >
|
||||
<span ng-switch-when="LOGOUT" my-i18n="confirm_modal_logout_submit"></span>
|
||||
<span ng-switch-when="HISTORY_FLUSH" my-i18n="confirm_modal_history_flush_submit"></span>
|
||||
<span ng-switch-when="FILES_CLIPBOARD_PASTE" my-i18n="confirm_modal_clipboard_files_send_submit"></span>
|
||||
@ -68,6 +71,7 @@
|
||||
<span ng-switch-when="VIDEO_SHARE_PEER" my-i18n="confirm_modal_share_video_submit"></span>
|
||||
<span ng-switch-when="SHARE_CONTACT_PEER" my-i18n="confirm_modal_share_contact_submit"></span>
|
||||
<span ng-switch-when="EXT_SHARE_PEER" my-i18n="confirm_modal_share_file_submit"></span>
|
||||
<span ng-switch-when="RESET_ACCOUNT" my-i18n="confirm_modal_reset_account_submit"></span>
|
||||
<span ng-switch-default my-i18n="modal_ok"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -5,6 +5,7 @@
|
||||
<h4 ng-if="error" class="md_simple_header" ng-switch="error.type">
|
||||
<span ng-switch-when="MEDIA_TYPE_NOT_SUPPORTED" my-i18n="error_modal_media_not_supported_title"></span>
|
||||
<span ng-switch-when="USERNAME_NOT_OCCUPIED" my-i18n="error_modal_not_found_title"></span>
|
||||
<span ng-switch-when="PASSWORD_RECOVERY_NA" my-i18n="error_modal_recovery_na_title"></span>
|
||||
<span ng-switch-default ng-switch="error.code">
|
||||
<span ng-switch-when="400" my-i18n="error_modal_bad_request_title"></span>
|
||||
<span ng-switch-when="401" my-i18n="error_modal_unauthorized_title"></span>
|
||||
@ -37,6 +38,7 @@
|
||||
<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>
|
||||
|
||||
|
||||
<div ng-switch-default ng-switch="error.code">
|
||||
@ -66,7 +68,7 @@ Stack: {{error.originalError.stack || error.stack}}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="md_simple_modal_footer">
|
||||
<button class="btn btn-md btn-md-primary" ng-click="$dismiss()">
|
||||
<button class="btn btn-md btn-md-primary" ng-click="$dismiss()" my-focused>
|
||||
<span my-i18n="modal_ok"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div class="login_page">
|
||||
<div class="login_head_wrap clearfix" ng-switch="progress.enabled">
|
||||
<div ng-switch-when="true" class="login_head_submit_progress">
|
||||
<my-i18n ng-if="!credentials.phone_code_hash" msgid="login_generating_key"></my-i18n><my-i18n ng-if="credentials.phone_code_hash && !credentials.phone_code_valid" msgid="login_checking_code"></my-i18n><my-i18n ng-if="credentials.phone_code_valid && credentials.phone_unoccupied" msgid="login_signing_up"></my-i18n><span my-loading-dots></span>
|
||||
<my-i18n ng-if="!credentials.phone_code_hash" msgid="login_generating_key"></my-i18n><my-i18n ng-if="credentials.phone_code_hash && !credentials.phone_code_valid" msgid="login_checking_code"></my-i18n><my-i18n ng-if="credentials.phone_code_valid && credentials.phone_unoccupied" msgid="login_signing_up"></my-i18n><my-i18n ng-if="credentials.phone_code_valid && credentials.password_needed" msgid="login_checking_password"></my-i18n><span my-loading-dots></span>
|
||||
</div>
|
||||
<div ng-switch-default class="login_head_submit_wrap">
|
||||
<a class="login_head_submit_btn" ng-if="!credentials.phone_code_hash" ng-click="sendCode()">
|
||||
@ -15,6 +15,9 @@
|
||||
<a class="login_head_submit_btn" ng-if="credentials.phone_code_valid && credentials.phone_unoccupied" ng-click="logIn(true)">
|
||||
<my-i18n msgid="modal_next"></my-i18n><i class="icon icon-next-submit"></i>
|
||||
</a>
|
||||
<a class="login_head_submit_btn" ng-if="credentials.phone_code_valid && credentials.password_needed" ng-click="checkPassword()">
|
||||
<my-i18n msgid="modal_next"></my-i18n><i class="icon icon-next-submit"></i>
|
||||
</a>
|
||||
</div>
|
||||
<a class="login_head_logo_link" href="https://telegram.org" target="_blank">
|
||||
<i class="icon icon-tg-logo"></i><i class="icon icon-tg-title"></i>
|
||||
@ -98,6 +101,28 @@
|
||||
|
||||
</form>
|
||||
|
||||
<form name="myPasswordForm" ng-if="credentials.phone_code_valid && credentials.password_needed" ng-submit="checkPassword()">
|
||||
<h3 class="login_form_head" my-i18n="login_password_title"></h3>
|
||||
<p class="login_form_lead" my-i18n="login_password_label"></p>
|
||||
|
||||
<div class="md-input-group" ng-class="{'md-input-error': error.field == 'password'}" my-labeled-input ng-switch="error.field == 'password'">
|
||||
<label ng-switch-when="true" class="md-input-label" my-i18n="login_incorrect_password"></label>
|
||||
<label ng-switch-default class="md-input-label" my-i18n="login_password"></label>
|
||||
<input autocomplete="off" class="md-input" my-focused name="password" type="password" ng-model="credentials.password" my-submit-on-enter required />
|
||||
</div>
|
||||
|
||||
<p ng-if="password.hint.length > 0" class="login_form_hint" ng-bind="password.hint"></p>
|
||||
|
||||
<div class="login_forgot_button">
|
||||
<button class="btn btn-md" ng-click="forgotPassword($event)" my-i18n="login_password_forgot_link"></button>
|
||||
</div>
|
||||
|
||||
<div ng-if="canReset" class="login_reset_button">
|
||||
<button class="btn btn-md btn-md-danger" ng-click="resetAccount($event)" my-i18n="login_account_reset"></button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div ng-switch="about.shown">
|
||||
|
@ -50,7 +50,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="::historyMessage.media ? true : false" class="im_message_media" ng-switch="historyMessage.media._">
|
||||
<div class="im_message_text" ng-if="::historyMessage.message.length || false" ng-bind-html="::historyMessage.richMessage" dir="auto"></div>
|
||||
<div class="im_message_external_embed_wrap" ng-if="::historyMessage.richUrlEmbed || false" my-external-embed="historyMessage.richUrlEmbed"></div>
|
||||
<div ng-if="::historyMessage.media || historyMessage.id < 0 ? true : false" class="im_message_media" ng-switch="historyMessage.media._">
|
||||
|
||||
<div ng-switch-when="messageMediaPhoto" my-message-photo></div>
|
||||
<div ng-switch-when="messageMediaVideo" my-message-video="historyMessage.media.video" message-id="historyMessage.id"></div>
|
||||
@ -58,6 +60,7 @@
|
||||
<div ng-switch-when="messageMediaAudio" class="im_message_audio" my-audio-player audio="historyMessage.media.audio"></div>
|
||||
<div ng-switch-when="messageMediaGeo" my-message-map></div>
|
||||
<div ng-switch-when="messageMediaContact" class="im_message_contact" my-message-contact></div>
|
||||
<!-- <div ng-switch-when="messageMediaWebPage" class="im_message_webpage" my-message-webpage="historyMessage.media.webpage"></div> -->
|
||||
<div ng-switch-when="messageMediaPending" my-message-pending></div>
|
||||
<div ng-switch-when="messageMediaUnsupported">
|
||||
<div class="im_message_text">
|
||||
@ -67,8 +70,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="im_message_text" ng-if="::historyMessage.message.length || false" ng-bind-html="::historyMessage.richMessage" dir="auto"></div>
|
||||
<div class="im_message_external_embed_wrap" ng-if="::historyMessage.richUrlEmbed || false" my-external-embed="historyMessage.richUrlEmbed"></div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
21
app/partials/desktop/message_attach_webpage.html
Normal file
21
app/partials/desktop/message_attach_webpage.html
Normal file
@ -0,0 +1,21 @@
|
||||
<div class="im_message_document clearfix" ng-class="{im_message_document_thumbed: !!webpage.photo}" ng-if="webpage._ == 'webPage'">
|
||||
|
||||
<a ng-if="::webpage.photo" ng-click="docOpen()">
|
||||
<div class="im_message_document_thumb_wrap">
|
||||
<img
|
||||
class="im_message_document_thumb"
|
||||
my-load-thumb
|
||||
thumb="webpage.photo.thumb"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="im_message_document_info">
|
||||
<div class="im_message_document_name_wrap">
|
||||
<a href="" ng-click="docOpen()" class="im_message_document_name" ng-bind="webpage.title || webpage.author"></a>
|
||||
<span class="im_message_document_size" ng-bind="webpage.display_url"></span>
|
||||
</div>
|
||||
<div class="im_message_website_description" ng-bind="webpage.description"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
32
app/partials/desktop/password_recovery_modal.html
Normal file
32
app/partials/desktop/password_recovery_modal.html
Normal file
@ -0,0 +1,32 @@
|
||||
<div class="md_simple_modal_wrap" my-modal-position>
|
||||
|
||||
<div class="md_simple_modal_body">
|
||||
|
||||
<form class="modal_simple_form" ng-submit="checkCode()">
|
||||
|
||||
<h4 my-i18n="login_recovery_title"></h4>
|
||||
|
||||
<div class="md_simple_form_description" my-i18n="login_recovery_description_md">
|
||||
<my-i18n-param name="email">
|
||||
<strong ng-bind="recovery.email_pattern"></strong>
|
||||
</my-i18n-param>
|
||||
</div>
|
||||
|
||||
<div class="md-input-group" ng-class="{'md-input-error': recovery.error_field == 'code'}" my-labeled-input ng-switch="recovery.error_field == 'code'">
|
||||
<label ng-switch-when="true" class="md-input-label" my-i18n="login_code_incorrect"></label>
|
||||
<label ng-switch-default class="md-input-label" my-i18n="login_code_placeholder"></label>
|
||||
<input class="md-input" my-focused type="text" ng-model="recovery.code" name="code" my-focused />
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<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: recovery.updating}" ng-click="checkCode()" ng-disabled="recovery.updating" ng-bind="recovery.updating ? 'password_recover_active' : 'password_recover_submit' | i18n"></button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
54
app/partials/desktop/password_update_modal.html
Normal file
54
app/partials/desktop/password_update_modal.html
Normal file
@ -0,0 +1,54 @@
|
||||
<div class="md_simple_modal_wrap" my-modal-position>
|
||||
|
||||
<div class="md_simple_modal_body">
|
||||
|
||||
<form class="modal_simple_form" ng-submit="updatePassword()">
|
||||
|
||||
<h4 ng-switch="action">
|
||||
<my-i18n ng-switch-when="disable" msgid="password_delete_title"></my-i18n>
|
||||
<my-i18n ng-switch-default msgid="password_change_title"></my-i18n>
|
||||
</h4>
|
||||
|
||||
<div ng-if="password._ != 'account.noPassword'" class="md-input-group" ng-class="{'md-input-error': passwordSettings.error_field == 'cur_password'}" my-labeled-input>
|
||||
<label class="md-input-label" my-i18n="password_current_placeholder"></label>
|
||||
<input class="md-input" my-focused type="password" ng-model="passwordSettings.cur_password" name="cur_password" my-focus-on="cur_password_focus" />
|
||||
</div>
|
||||
|
||||
<div ng-if="action != 'disable'" class="md-input-group md-input-grouped" ng-class="{'md-input-error': passwordSettings.error_field == 'new_password'}" my-labeled-input>
|
||||
<label class="md-input-label" my-i18n="password_new_placeholder"></label>
|
||||
<input class="md-input" type="password" ng-model="passwordSettings.new_password" name="new_password" my-focus-on="new_password_focus" />
|
||||
</div>
|
||||
|
||||
<div ng-if="action != 'disable'" class="md-input-group" ng-class="{'md-input-error': passwordSettings.confirm_password && passwordSettings.new_password && passwordSettings.confirm_password != passwordSettings.new_password}" my-labeled-input>
|
||||
<label class="md-input-label" my-i18n="password_confirm_placeholder"></label>
|
||||
<input class="md-input" type="password" ng-model="passwordSettings.confirm_password" name="confirm_password" my-focus-on="confirm_password_focus" />
|
||||
</div>
|
||||
|
||||
<div ng-if="action != 'disable'" class="md-input-group" my-labeled-input>
|
||||
<label class="md-input-label" my-i18n="password_hint_placeholder"></label>
|
||||
<input class="md-input" type="text" ng-model="passwordSettings.hint" name="hint" />
|
||||
</div>
|
||||
|
||||
<div ng-if="action != 'disable'" class="md_simple_form_description" my-i18n="password_create_description"></div>
|
||||
|
||||
<div ng-if="action != 'disable'" class="md-input-group" ng-class="{'md-input-error': passwordSettings.error_field == 'email'}" my-labeled-input>
|
||||
<label class="md-input-label" my-i18n="password_email_placeholder"></label>
|
||||
<input class="md-input" type="text" ng-model="passwordSettings.email" name="email" />
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<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: passwordSettings.updating}" ng-click="updatePassword()" ng-disabled="passwordSettings.updating" ng-switch="action">
|
||||
<span ng-switch-when="disable" ng-bind="passwordSettings.updating ? 'password_delete_active' : 'password_delete_submit' | i18n"></span>
|
||||
<span ng-switch-default ng-bind="passwordSettings.updating ? 'password_create_active' : 'password_create_submit' | i18n"></span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
@ -110,6 +110,20 @@
|
||||
<div class="md_modal_section_param_name" my-i18n="settings_modal_language"></div>
|
||||
</div>
|
||||
|
||||
<div class="md_modal_section_link_wrap">
|
||||
<a ng-if="password._ == 'account.noPassword' && password.email_unconfirmed_pattern.length" class="md_modal_section_link pull-right" ng-click="changePassword({action: 'cancel_email'})" my-i18n="settings_modal_password_email_pending_cancel">
|
||||
</a>
|
||||
<span class="md_modal_section_text" ng-if="password._ == 'account.noPassword' && password.email_unconfirmed_pattern.length" class="md_modal_section_link" my-i18n="settings_modal_password_email_pending">
|
||||
<my-i18n-param name="email">
|
||||
<span ng-bind="password.email_unconfirmed_pattern"></span>
|
||||
</my-i18n-param>
|
||||
</span>
|
||||
<a ng-if="password._ == 'account.noPassword' && !password.email_unconfirmed_pattern" class="md_modal_section_link" ng-click="changePassword({action: 'create'})" my-i18n="settings_modal_set_password"></a>
|
||||
|
||||
<a ng-if="password._ == 'account.password'" class="md_modal_section_link pull-right" ng-click="changePassword({action: 'disable'})" my-i18n="settings_modal_disable_password"></a>
|
||||
<a ng-if="password._ == 'account.password'" class="md_modal_section_link" ng-click="changePassword({action: 'change'})" my-i18n="settings_modal_change_password"></a>
|
||||
</div>
|
||||
|
||||
<div class="md_modal_section_link_wrap">
|
||||
<a class="md_modal_section_link" ng-click="terminateSessions()" my-i18n="settings_modal_terminate_sessions"></a>
|
||||
</div>
|
||||
|
304
app/vendor/cryptoJS/crypto.js
vendored
304
app/vendor/cryptoJS/crypto.js
vendored
@ -1912,136 +1912,188 @@ code.google.com/p/crypto-js/wiki/License
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Copyright (c) 2012 T. Michael Keesey
|
||||
* LICENSE: http://opensource.org/licenses/MIT
|
||||
*/
|
||||
var sha1;
|
||||
(function (sha1) {
|
||||
var POW_2_24 = Math.pow(2, 24);
|
||||
var POW_2_32 = Math.pow(2, 32);
|
||||
function hex(n) {
|
||||
var s = "", v;
|
||||
for(var i = 7; i >= 0; --i) {
|
||||
v = (n >>> (i << 2)) & 15;
|
||||
s += v.toString(16);
|
||||
}
|
||||
return s;
|
||||
};
|
||||
/*
|
||||
CryptoJS v3.1.2
|
||||
code.google.com/p/crypto-js
|
||||
(c) 2009-2013 by Jeff Mott. All rights reserved.
|
||||
code.google.com/p/crypto-js/wiki/License
|
||||
*/
|
||||
(function (Math) {
|
||||
// Shortcuts
|
||||
var C = CryptoJS;
|
||||
var C_lib = C.lib;
|
||||
var WordArray = C_lib.WordArray;
|
||||
var Hasher = C_lib.Hasher;
|
||||
var C_algo = C.algo;
|
||||
|
||||
function toBytes(n) {
|
||||
var b = [], v;
|
||||
for(var i = 3; i >= 0; --i) {
|
||||
v = (n >> (i * 8)) & 255;
|
||||
b.push(v);
|
||||
}
|
||||
return b;
|
||||
};
|
||||
// Initialization and round constants tables
|
||||
var H = [];
|
||||
var K = [];
|
||||
|
||||
function lrot(n, bits) {
|
||||
return ((n << bits) | (n >>> (32 - bits)));
|
||||
}
|
||||
var Uint32ArrayBigEndian = (function () {
|
||||
function Uint32ArrayBigEndian(length) {
|
||||
this.bytes = new Uint8Array(length << 2);
|
||||
}
|
||||
Uint32ArrayBigEndian.prototype.get = function (index) {
|
||||
index <<= 2;
|
||||
return (this.bytes[index] * POW_2_24) + ((this.bytes[index + 1] << 16) | (this.bytes[index + 2] << 8) | this.bytes[index + 3]);
|
||||
};
|
||||
Uint32ArrayBigEndian.prototype.set = function (index, value) {
|
||||
var high = Math.floor(value / POW_2_24), rest = value - (high * POW_2_24);
|
||||
index <<= 2;
|
||||
this.bytes[index] = high;
|
||||
this.bytes[index + 1] = rest >> 16;
|
||||
this.bytes[index + 2] = (rest >> 8) & 255;
|
||||
this.bytes[index + 3] = rest & 255;
|
||||
};
|
||||
return Uint32ArrayBigEndian;
|
||||
})();
|
||||
function string2ArrayBuffer(s) {
|
||||
s = s.replace(/[\u0080-\u07ff]/g, function (c) {
|
||||
var code = c.charCodeAt(0);
|
||||
return String.fromCharCode(192 | code >> 6, 128 | code & 63);
|
||||
});
|
||||
s = s.replace(/[\u0080-\uffff]/g, function (c) {
|
||||
var code = c.charCodeAt(0);
|
||||
return String.fromCharCode(224 | code >> 12, 128 | code >> 6 & 63, 128 | code & 63);
|
||||
});
|
||||
var n = s.length, array = new Uint8Array(n);
|
||||
for(var i = 0; i < n; ++i) {
|
||||
array[i] = s.charCodeAt(i);
|
||||
}
|
||||
return array.buffer;
|
||||
}
|
||||
function bytes2ArrayBuffer(b) {
|
||||
var n = b.length, array = new Uint8Array(n);
|
||||
for(var i = 0; i < n; ++i) {
|
||||
array[i] = b[i];
|
||||
}
|
||||
return array.buffer;
|
||||
}
|
||||
|
||||
function hash(bufferOrString, byteArray) {
|
||||
var source;
|
||||
if (bufferOrString instanceof ArrayBuffer) {
|
||||
source = bufferOrString;
|
||||
} else if (Object.prototype.toString.apply(bufferOrString) == '[object Array]') {
|
||||
source = bytes2ArrayBuffer(bufferOrString);
|
||||
} else {
|
||||
source = string2ArrayBuffer(String(bufferOrString));
|
||||
}
|
||||
var h0 = 1732584193, h1 = 4023233417, h2 = 2562383102, h3 = 271733878, h4 = 3285377520, i, sbytes = source.byteLength, sbits = sbytes << 3, minbits = sbits + 65, bits = Math.ceil(minbits / 512) << 9, bytes = bits >>> 3, slen = bytes >>> 2, s = new Uint32ArrayBigEndian(slen), s8 = s.bytes, j, w = new Uint32Array(80), sourceArray = new Uint8Array(source);
|
||||
for(i = 0; i < sbytes; ++i) {
|
||||
s8[i] = sourceArray[i];
|
||||
}
|
||||
s8[sbytes] = 128;
|
||||
s.set(slen - 2, Math.floor(sbits / POW_2_32));
|
||||
s.set(slen - 1, sbits & 4294967295);
|
||||
for(i = 0; i < slen; i += 16) {
|
||||
for(j = 0; j < 16; ++j) {
|
||||
w[j] = s.get(i + j);
|
||||
}
|
||||
for(; j < 80; ++j) {
|
||||
w[j] = lrot(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
|
||||
}
|
||||
var a = h0, b = h1, c = h2, d = h3, e = h4, f, k, temp;
|
||||
for(j = 0; j < 80; ++j) {
|
||||
if(j < 20) {
|
||||
f = (b & c) | ((~b) & d);
|
||||
k = 1518500249;
|
||||
} else {
|
||||
if(j < 40) {
|
||||
f = b ^ c ^ d;
|
||||
k = 1859775393;
|
||||
} else {
|
||||
if(j < 60) {
|
||||
f = (b & c) ^ (b & d) ^ (c & d);
|
||||
k = 2400959708;
|
||||
} else {
|
||||
f = b ^ c ^ d;
|
||||
k = 3395469782;
|
||||
}
|
||||
}
|
||||
// Compute constants
|
||||
(function () {
|
||||
function isPrime(n) {
|
||||
var sqrtN = Math.sqrt(n);
|
||||
for (var factor = 2; factor <= sqrtN; factor++) {
|
||||
if (!(n % factor)) {
|
||||
return false;
|
||||
}
|
||||
temp = (lrot(a, 5) + f + e + k + w[j]) & 4294967295;
|
||||
e = d;
|
||||
d = c;
|
||||
c = lrot(b, 30);
|
||||
b = a;
|
||||
a = temp;
|
||||
}
|
||||
h0 = (h0 + a) & 4294967295;
|
||||
h1 = (h1 + b) & 4294967295;
|
||||
h2 = (h2 + c) & 4294967295;
|
||||
h3 = (h3 + d) & 4294967295;
|
||||
h4 = (h4 + e) & 4294967295;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (byteArray) {
|
||||
return toBytes(h0).concat(toBytes(h1), toBytes(h2), toBytes(h3), toBytes(h4));
|
||||
function getFractionalBits(n) {
|
||||
return ((n - (n | 0)) * 0x100000000) | 0;
|
||||
}
|
||||
return hex(h0) + hex(h1) + hex(h2) + hex(h3) + hex(h4);
|
||||
}
|
||||
sha1.hash = hash;
|
||||
})(sha1 || (sha1 = {}));
|
||||
|
||||
var n = 2;
|
||||
var nPrime = 0;
|
||||
while (nPrime < 64) {
|
||||
if (isPrime(n)) {
|
||||
if (nPrime < 8) {
|
||||
H[nPrime] = getFractionalBits(Math.pow(n, 1 / 2));
|
||||
}
|
||||
K[nPrime] = getFractionalBits(Math.pow(n, 1 / 3));
|
||||
|
||||
nPrime++;
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
}());
|
||||
|
||||
// Reusable object
|
||||
var W = [];
|
||||
|
||||
/**
|
||||
* SHA-256 hash algorithm.
|
||||
*/
|
||||
var SHA256 = C_algo.SHA256 = Hasher.extend({
|
||||
_doReset: function () {
|
||||
this._hash = new WordArray.init(H.slice(0));
|
||||
},
|
||||
|
||||
_doProcessBlock: function (M, offset) {
|
||||
// Shortcut
|
||||
var H = this._hash.words;
|
||||
|
||||
// Working variables
|
||||
var a = H[0];
|
||||
var b = H[1];
|
||||
var c = H[2];
|
||||
var d = H[3];
|
||||
var e = H[4];
|
||||
var f = H[5];
|
||||
var g = H[6];
|
||||
var h = H[7];
|
||||
|
||||
// Computation
|
||||
for (var i = 0; i < 64; i++) {
|
||||
if (i < 16) {
|
||||
W[i] = M[offset + i] | 0;
|
||||
} else {
|
||||
var gamma0x = W[i - 15];
|
||||
var gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^
|
||||
((gamma0x << 14) | (gamma0x >>> 18)) ^
|
||||
(gamma0x >>> 3);
|
||||
|
||||
var gamma1x = W[i - 2];
|
||||
var gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^
|
||||
((gamma1x << 13) | (gamma1x >>> 19)) ^
|
||||
(gamma1x >>> 10);
|
||||
|
||||
W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16];
|
||||
}
|
||||
|
||||
var ch = (e & f) ^ (~e & g);
|
||||
var maj = (a & b) ^ (a & c) ^ (b & c);
|
||||
|
||||
var sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22));
|
||||
var sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25));
|
||||
|
||||
var t1 = h + sigma1 + ch + K[i] + W[i];
|
||||
var t2 = sigma0 + maj;
|
||||
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = (d + t1) | 0;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = (t1 + t2) | 0;
|
||||
}
|
||||
|
||||
// Intermediate hash value
|
||||
H[0] = (H[0] + a) | 0;
|
||||
H[1] = (H[1] + b) | 0;
|
||||
H[2] = (H[2] + c) | 0;
|
||||
H[3] = (H[3] + d) | 0;
|
||||
H[4] = (H[4] + e) | 0;
|
||||
H[5] = (H[5] + f) | 0;
|
||||
H[6] = (H[6] + g) | 0;
|
||||
H[7] = (H[7] + h) | 0;
|
||||
},
|
||||
|
||||
_doFinalize: function () {
|
||||
// Shortcuts
|
||||
var data = this._data;
|
||||
var dataWords = data.words;
|
||||
|
||||
var nBitsTotal = this._nDataBytes * 8;
|
||||
var nBitsLeft = data.sigBytes * 8;
|
||||
|
||||
// Add padding
|
||||
dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32);
|
||||
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000);
|
||||
dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal;
|
||||
data.sigBytes = dataWords.length * 4;
|
||||
|
||||
// Hash final blocks
|
||||
this._process();
|
||||
|
||||
// Return final computed hash
|
||||
return this._hash;
|
||||
},
|
||||
|
||||
clone: function () {
|
||||
var clone = Hasher.clone.call(this);
|
||||
clone._hash = this._hash.clone();
|
||||
|
||||
return clone;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Shortcut function to the hasher's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
*
|
||||
* @return {WordArray} The hash.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hash = CryptoJS.SHA256('message');
|
||||
* var hash = CryptoJS.SHA256(wordArray);
|
||||
*/
|
||||
C.SHA256 = Hasher._createHelper(SHA256);
|
||||
|
||||
/**
|
||||
* Shortcut function to the HMAC's object interface.
|
||||
*
|
||||
* @param {WordArray|string} message The message to hash.
|
||||
* @param {WordArray|string} key The secret key.
|
||||
*
|
||||
* @return {WordArray} The HMAC.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var hmac = CryptoJS.HmacSHA256(message, key);
|
||||
*/
|
||||
C.HmacSHA256 = Hasher._createHmacHelper(SHA256);
|
||||
}(Math));
|
||||
|
Loading…
Reference in New Issue
Block a user