Browse Source

Supported 2-step verifications

Disabled webpage attachment
master
Igor Zhukov 9 years ago
parent
commit
8464acdfdc
  1. 246
      app/js/controllers.js
  2. 3
      app/js/directives.js
  3. 8
      app/js/lib/ng_utils.js
  4. 6
      app/js/lib/tl_utils.js
  5. 47
      app/js/locales/en-us.json
  6. 94
      app/js/services.js
  7. 27
      app/less/app.less
  8. 6
      app/partials/desktop/confirm_modal.html
  9. 4
      app/partials/desktop/error_modal.html
  10. 27
      app/partials/desktop/login.html
  11. 4
      app/partials/desktop/message.html
  12. 32
      app/partials/desktop/password_recovery_modal.html
  13. 35
      app/partials/desktop/password_update_modal.html
  14. 15
      app/partials/desktop/settings_modal.html

246
app/js/controllers.js

@ -30,7 +30,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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']) @@ -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) {
@ -2535,10 +2625,17 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2535,10 +2625,17 @@ angular.module('myApp.controllers', ['myApp.i18n'])
$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,
@ -2550,9 +2647,14 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2550,9 +2647,14 @@ angular.module('myApp.controllers', ['myApp.i18n'])
modal.result['finally'](updatePasswordState);
};
function updatePasswordState (argument) {
PasswordManager.getPasswordState().then(function (result) {
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);
}
});
}
@ -2882,7 +2984,141 @@ angular.module('myApp.controllers', ['myApp.i18n']) @@ -2882,7 +2984,141 @@ angular.module('myApp.controllers', ['myApp.i18n'])
})
})
.controller('PasswordUpdateModalController', function ($scope, PasswordManager, MtpApiManager) {
.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;
}
});
};
})

3
app/js/directives.js

@ -2840,7 +2840,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -2840,7 +2840,7 @@ angular.module('myApp.directives', ['myApp.filters'])
});
}
$scope.$on('value_updated', function (event, args) {
$scope.$on('value_updated', function () {
setZeroTimeout(function () {
updateHasValueClass();
});
@ -2858,6 +2858,7 @@ angular.module('myApp.directives', ['myApp.filters']) @@ -2858,6 +2858,7 @@ angular.module('myApp.directives', ['myApp.filters'])
element.on('keydown', function (event) {
if (event.keyCode == 13) {
element.trigger('submit');
return cancelEvent(event);
}
});
};

8
app/js/lib/ng_utils.js

@ -651,7 +651,7 @@ angular.module('izhukov.utils', []) @@ -651,7 +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,
useSha2Crypto = webCrypto && webCrypto.digest !== undefined,
useSha256Crypto = webCrypto && webCrypto.digest !== undefined,
finalizeTask = function (taskID, result) {
var deferred = awaiting[taskID];
if (deferred !== undefined) {
@ -729,8 +729,8 @@ angular.module('izhukov.utils', []) @@ -729,8 +729,8 @@ angular.module('izhukov.utils', [])
return sha1HashSync(bytes);
});
},
sha2Hash: function (bytes) {
if (useSha2Crypto) {
sha256Hash: function (bytes) {
if (useSha256Crypto) {
var deferred = $q.defer(),
bytesTyped = Array.isArray(bytes) ? convertToUint8Array(bytes) : bytes;
// console.log(dT(), 'Native sha1 start');
@ -739,7 +739,7 @@ angular.module('izhukov.utils', []) @@ -739,7 +739,7 @@ angular.module('izhukov.utils', [])
deferred.resolve(digest);
}, function (e) {
console.error('Crypto digest error', e);
useSha2Crypto = false;
useSha256Crypto = false;
deferred.resolve(sha256HashSync(bytes));
});

6
app/js/lib/tl_utils.js

@ -122,6 +122,9 @@ TLSerialization.prototype.storeDouble = function (f) { @@ -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) { @@ -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;

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

@ -54,6 +54,26 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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",

94
app/js/services.js

@ -4537,18 +4537,104 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) @@ -4537,18 +4537,104 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
})
.service('PasswordManager', function ($timeout, $rootScope, MtpApiManager) {
.service('PasswordManager', function ($timeout, $q, $rootScope, MtpApiManager, CryptoWorker, MtpSecureRandom) {
return {
getPasswordState: getPasswordState
check: check,
getState: getState,
requestRecovery: requestRecovery,
recover: recover,
updateSettings: updateSettings
};
function getPasswordState () {
return MtpApiManager.invokeApi('account.getPassword').then(function (result) {
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);
}
})

27
app/less/app.less

@ -242,6 +242,9 @@ input[type="number"] { @@ -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 { @@ -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 { @@ -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 */
@ -3363,6 +3381,15 @@ a.countries_modal_search_clear { @@ -3363,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 {

6
app/partials/desktop/confirm_modal.html

@ -43,6 +43,9 @@ @@ -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 @@ @@ -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 @@ @@ -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>

4
app/partials/desktop/error_modal.html

@ -5,6 +5,7 @@ @@ -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 @@ @@ -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> @@ -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>

27
app/partials/desktop/login.html

@ -3,7 +3,7 @@ @@ -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 &amp;&amp; !credentials.phone_code_valid" msgid="login_checking_code"></my-i18n><my-i18n ng-if="credentials.phone_code_valid &amp;&amp; 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 &amp;&amp; !credentials.phone_code_valid" msgid="login_checking_code"></my-i18n><my-i18n ng-if="credentials.phone_code_valid &amp;&amp; credentials.phone_unoccupied" msgid="login_signing_up"></my-i18n><my-i18n ng-if="credentials.phone_code_valid &amp;&amp; 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 @@ @@ -15,6 +15,9 @@
<a class="login_head_submit_btn" ng-if="credentials.phone_code_valid &amp;&amp; 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 &amp;&amp; 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 @@ @@ -98,6 +101,28 @@
</form>
<form name="myPasswordForm" ng-if="credentials.phone_code_valid &amp;&amp; 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">

4
app/partials/desktop/message.html

@ -51,7 +51,7 @@ @@ -51,7 +51,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 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>
@ -60,7 +60,7 @@ @@ -60,7 +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="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">

32
app/partials/desktop/password_recovery_modal.html

@ -0,0 +1,32 @@ @@ -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>

35
app/partials/desktop/password_update_modal.html

@ -4,34 +4,38 @@ @@ -4,34 +4,38 @@
<form class="modal_simple_form" ng-submit="updatePassword()">
<h4 my-i18n="username_edit_modal_title"></h4>
<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': checked.error}" my-labeled-input>
<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="text" ng-model="passwordSettings.current_password" name="current_password" />
<input class="md-input" my-focused type="password" ng-model="passwordSettings.cur_password" name="cur_password" my-focus-on="cur_password_focus" />
</div>
<div class="md-input-group" ng-class="{'md-input-error': checked.error}" my-labeled-input>
<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="text" ng-model="passwordSettings.new_password" name="new_password" />
<input class="md-input" type="password" ng-model="passwordSettings.new_password" name="new_password" my-focus-on="new_password_focus" />
</div>
<div class="md-input-group" ng-class="{'md-input-error': checked.error}" my-labeled-input>
<label class="md-input-label" my-i18n="password_new_placeholder"></label>
<input class="md-input" type="text" ng-model="passwordSettings.confirm_password" name="confirm_password" />
<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 class="md-input-group" ng-class="{'md-input-error': checked.error}" my-labeled-input>
<label class="md-input-label" my-i18n="password_new_placeholder"></label>
<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 class="md-input-group" ng-class="{'md-input-error': checked.error}" my-labeled-input>
<label class="md-input-label" my-i18n="password_new_placeholder"></label>
<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>
<div class="md_simple_form_description" my-i18n="username_edit_description_md"></div>
</form>
@ -40,7 +44,10 @@ @@ -40,7 +44,10 @@
<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-bind="passwordSettings.updating ? 'username_edit_submit_active' : 'username_edit_submit' | i18n" ng-disabled="passwordSettings.updating"></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>

15
app/partials/desktop/settings_modal.html

@ -111,10 +111,17 @@ @@ -111,10 +111,17 @@
</div>
<div class="md_modal_section_link_wrap">
<a ng-if="password._ == 'account.noPassword' && !password.email_unconfirmed_pattern" class="md_modal_section_link" ng-click="changePassword()">Set password</a>
<a ng-if="password._ == 'account.noPassword' && password.email_unconfirmed_pattern.length" class="md_modal_section_link" ng-click="changePassword({cancelEmail: true})">Cancel pending {{password.email_unconfirmed_pattern}}</a>
<a ng-if="password._ == 'account.password'" class="md_modal_section_link" ng-click="updatePassword({disable: true})">Turn off</a>
<a ng-if="password._ == 'account.password'" class="md_modal_section_link" ng-click="changePassword()">Change password</a>
<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">

Loading…
Cancel
Save