Browse Source

Two-factor:

Recovery email
Fix enter shortcut on handhelds
master
Eduard Kuzmenko 4 years ago
parent
commit
a6753d7325
  1. 38
      src/components/codeInputField.ts
  2. 5
      src/components/misc.ts
  3. 24
      src/components/sidebarLeft/tabs/2fa/email.ts
  4. 104
      src/components/sidebarLeft/tabs/2fa/emailConfirmation.ts
  5. 19
      src/components/sidebarLeft/tabs/2fa/enterPassword.ts
  6. 27
      src/components/sidebarLeft/tabs/2fa/hint.ts
  7. 15
      src/components/sidebarLeft/tabs/2fa/index.ts
  8. 3
      src/components/sidebarLeft/tabs/2fa/passwordSet.ts
  9. 10
      src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts
  10. 9
      src/components/sidebarLeft/tabs/privacyAndSecurity.ts
  11. 14
      src/helpers/dom.ts
  12. 2
      src/lib/appManagers/appDialogsManager.ts
  13. 12
      src/lib/mtproto/passwordManager.ts
  14. 44
      src/pages/pageAuthCode.ts
  15. 4
      src/scss/partials/_button.scss
  16. 12
      src/scss/partials/_leftSidebar.scss

38
src/components/codeInputField.ts

@ -0,0 +1,38 @@
import InputField from "./inputField";
export default class CodeInputField extends InputField {
constructor(options: {
label?: string,
name?: string,
length: number,
onFill: (code: number) => void
}) {
super({
plainText: true,
...options
});
const input = this.input as HTMLInputElement;
input.type = 'tel';
input.setAttribute('required', '');
input.autocomplete = 'off';
let lastLength = 0;
this.input.addEventListener('input', (e) => {
this.input.classList.remove('error');
this.label.innerText = options.label;
const value = this.value.replace(/\D/g, '').slice(0, options.length);
this.setValueSilently(value);
const length = this.value.length;
if(length === options.length) { // submit code
options.onFill(+this.value);
} else if(length === lastLength) {
return;
}
lastLength = length;
});
}
}

5
src/components/misc.ts

@ -55,14 +55,14 @@ export function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGIma
} }
} }
export function putPreloader(elem: Element, returnDiv = false) { export function putPreloader(elem: Element, returnDiv = false): HTMLElement {
const html = ` const html = `
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50"> <svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50">
<circle class="preloader-path" cx="50" cy="50" r="20" fill="none" stroke-miterlimit="10"/> <circle class="preloader-path" cx="50" cy="50" r="20" fill="none" stroke-miterlimit="10"/>
</svg>`; </svg>`;
if(returnDiv) { if(returnDiv) {
let div = document.createElement('div'); const div = document.createElement('div');
div.classList.add('preloader'); div.classList.add('preloader');
div.innerHTML = html; div.innerHTML = html;
@ -74,6 +74,7 @@ export function putPreloader(elem: Element, returnDiv = false) {
} }
elem.innerHTML += html; elem.innerHTML += html;
return elem.lastElementChild as HTMLElement;
} }
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.putPreloader = putPreloader); MOUNT_CLASS_TO && (MOUNT_CLASS_TO.putPreloader = putPreloader);

24
src/components/sidebarLeft/tabs/2fa/email.ts

@ -5,7 +5,7 @@ import Button from "../../../button";
import SidebarSlider, { SliderSuperTab } from "../../../slider"; import SidebarSlider, { SliderSuperTab } from "../../../slider";
import { wrapSticker } from "../../../wrappers"; import { wrapSticker } from "../../../wrappers";
import InputField from "../../../inputField"; import InputField from "../../../inputField";
import { attachClickEvent, cancelEvent } from "../../../../helpers/dom"; import { attachClickEvent, cancelEvent, canFocus } from "../../../../helpers/dom";
import PopupConfirmAction from "../../../popups/confirmAction"; import PopupConfirmAction from "../../../popups/confirmAction";
import { putPreloader } from "../../../misc"; import { putPreloader } from "../../../misc";
import passwordManager from "../../../../lib/mtproto/passwordManager"; import passwordManager from "../../../../lib/mtproto/passwordManager";
@ -19,6 +19,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
public plainPassword: string; public plainPassword: string;
public newPassword: string; public newPassword: string;
public hint: string; public hint: string;
public isFirst = false;
constructor(slider: SidebarSlider) { constructor(slider: SidebarSlider) {
super(slider, true); super(slider, true);
@ -29,7 +30,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
this.title.innerHTML = 'Recovery Email'; this.title.innerHTML = 'Recovery Email';
const section = new SettingSection({ const section = new SettingSection({
caption: ' ', caption: '',
noDelimiter: true noDelimiter: true
}); });
@ -69,7 +70,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
inputField.input.addEventListener('keypress', (e) => { inputField.input.addEventListener('keypress', (e) => {
if(e.key === 'Enter') { if(e.key === 'Enter') {
cancelEvent(e); cancelEvent(e);
return btnContinue.click(); return onContinueClick();
} }
}); });
@ -78,13 +79,13 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
}); });
const btnContinue = Button('btn-primary btn-color-primary', {text: 'CONTINUE'}); const btnContinue = Button('btn-primary btn-color-primary', {text: 'CONTINUE'});
const btnSkip = Button('btn-primary btn-primary-transparent primary', {text: 'SKIP'}); const btnSkip = Button('btn-primary btn-secondary btn-primary-transparent primary', {text: 'SKIP'});
const goNext = () => { const goNext = () => {
new AppTwoStepVerificationSetTab(this.slider).open(); new AppTwoStepVerificationSetTab(this.slider).open();
}; };
attachClickEvent(btnContinue, (e) => { const onContinueClick = () => {
const email = inputField.value.trim(); const email = inputField.value.trim();
const match = RichTextProcessor.matchEmail(email); const match = RichTextProcessor.matchEmail(email);
if(!match || match[0].length !== email.length) { if(!match || match[0].length !== email.length) {
@ -93,7 +94,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
} }
toggleButtons(true); toggleButtons(true);
putPreloader(btnContinue); const d = putPreloader(btnContinue);
passwordManager.updateSettings({ passwordManager.updateSettings({
hint: this.hint, hint: this.hint,
@ -108,9 +109,6 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
const tab = new AppTwoStepVerificationEmailConfirmationTab(this.slider); const tab = new AppTwoStepVerificationEmailConfirmationTab(this.slider);
tab.state = this.state; tab.state = this.state;
tab.newPassword = this.newPassword;
tab.plainPassword = this.plainPassword;
tab.hint = this.hint;
tab.email = email; tab.email = email;
tab.length = symbols; tab.length = symbols;
tab.open(); tab.open();
@ -119,8 +117,10 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
} }
toggleButtons(false); toggleButtons(false);
d.remove();
}); });
}); };
attachClickEvent(btnContinue, onContinueClick);
const toggleButtons = (freeze: boolean) => { const toggleButtons = (freeze: boolean) => {
if(freeze) { if(freeze) {
@ -145,7 +145,8 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
passwordManager.updateSettings({ passwordManager.updateSettings({
hint: this.hint, hint: this.hint,
currentPassword: this.plainPassword, currentPassword: this.plainPassword,
newPassword: this.newPassword newPassword: this.newPassword,
email: ''
}).then(() => { }).then(() => {
goNext(); goNext();
}, (err) => { }, (err) => {
@ -169,6 +170,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
} }
onOpenAfterTimeout() { onOpenAfterTimeout() {
if(!canFocus(this.isFirst)) return;
this.inputField.input.focus(); this.inputField.input.focus();
} }
} }

104
src/components/sidebarLeft/tabs/2fa/emailConfirmation.ts

@ -4,21 +4,19 @@ import appStickersManager from "../../../../lib/appManagers/appStickersManager";
import Button from "../../../button"; import Button from "../../../button";
import SidebarSlider, { SliderSuperTab } from "../../../slider"; import SidebarSlider, { SliderSuperTab } from "../../../slider";
import { wrapSticker } from "../../../wrappers"; import { wrapSticker } from "../../../wrappers";
import InputField from "../../../inputField"; import { attachClickEvent, canFocus, toggleDisability } from "../../../../helpers/dom";
import { attachClickEvent } from "../../../../helpers/dom";
import PopupConfirmAction from "../../../popups/confirmAction";
import { putPreloader } from "../../../misc";
import passwordManager from "../../../../lib/mtproto/passwordManager"; import passwordManager from "../../../../lib/mtproto/passwordManager";
import AppTwoStepVerificationSetTab from "./passwordSet"; import AppTwoStepVerificationSetTab from "./passwordSet";
import CodeInputField from "../../../codeInputField";
import AppTwoStepVerificationEmailTab from "./email";
import { putPreloader } from "../../../misc";
export default class AppTwoStepVerificationEmailConfirmationTab extends SliderSuperTab { export default class AppTwoStepVerificationEmailConfirmationTab extends SliderSuperTab {
public inputField: InputField; public codeInputField: CodeInputField;
public state: AccountPassword; public state: AccountPassword;
public plainPassword: string;
public newPassword: string;
public hint: string;
public email: string; public email: string;
public length: number; public length: number;
public isFirst = false;
constructor(slider: SidebarSlider) { constructor(slider: SidebarSlider) {
super(slider, true); super(slider, true);
@ -29,10 +27,12 @@ export default class AppTwoStepVerificationEmailConfirmationTab extends SliderSu
this.title.innerHTML = 'Recovery Email'; this.title.innerHTML = 'Recovery Email';
const section = new SettingSection({ const section = new SettingSection({
caption: ' ', caption: 'Please enter code we\'ve just emailed at <b></b>',
noDelimiter: true noDelimiter: true
}); });
(section.caption.lastElementChild as HTMLElement).innerText = this.email;
const emoji = '📬'; const emoji = '📬';
const doc = appStickersManager.getAnimatedEmojiSticker(emoji); const doc = appStickersManager.getAnimatedEmojiSticker(emoji);
const stickerContainer = document.createElement('div'); const stickerContainer = document.createElement('div');
@ -60,54 +60,69 @@ export default class AppTwoStepVerificationEmailConfirmationTab extends SliderSu
const inputWrapper = document.createElement('div'); const inputWrapper = document.createElement('div');
inputWrapper.classList.add('input-wrapper'); inputWrapper.classList.add('input-wrapper');
const inputField = this.inputField = new InputField({ const inputField = this.codeInputField = new CodeInputField({
name: 'recovery-email-code', name: 'recovery-email-code',
label: 'Code' label: 'Code',
length: this.length,
onFill: (code) => {
freeze(true);
passwordManager.confirmPasswordEmail('' + code)
.then(value => {
if(!value) {
}
goNext();
})
.catch(err => {
switch(err.type) {
case 'CODE_INVALID':
inputField.input.classList.add('error');
inputField.label.innerText = 'Invalid Code';
break;
default:
console.error('confirm error', err);
break;
}
freeze(false);
});
}
}); });
const btnContinue = Button('btn-primary btn-color-primary', {text: 'CONTINUE'}); const btnChange = Button('btn-primary btn-primary-transparent primary', {text: 'CHANGE EMAIL'});
const btnSkip = Button('btn-primary btn-primary-transparent primary', {text: 'SKIP'}); const btnResend = Button('btn-primary btn-secondary btn-primary-transparent primary', {text: 'RE-SEND CODE'});
const goNext = () => { const goNext = () => {
new AppTwoStepVerificationSetTab(this.slider).open(); new AppTwoStepVerificationSetTab(this.slider).open();
}; };
attachClickEvent(btnContinue, (e) => { const freeze = (disable: boolean) => {
toggleDisability([inputField.input, btnChange, btnResend], disable);
}); };
attachClickEvent(btnSkip, (e) => { attachClickEvent(btnChange, (e) => {
const popup = new PopupConfirmAction('popup-skip-email', [{ freeze(true);
text: 'CANCEL', passwordManager.cancelPasswordEmail().then(value => {
isCancel: true this.slider.sliceTabsUntilTab(AppTwoStepVerificationEmailTab, this);
}, { this.close();
text: 'SKIP', }, () => {
callback: () => { freeze(false);
//inputContent.classList.add('sidebar-left-section-disabled');
btnContinue.setAttribute('disabled', 'true');
btnSkip.setAttribute('disabled', 'true');
putPreloader(btnSkip);
passwordManager.updateSettings({
hint: this.hint,
currentPassword: this.plainPassword,
newPassword: this.newPassword
}).then(() => {
goNext();
}, (err) => {
btnContinue.removeAttribute('disabled');
btnSkip.removeAttribute('disabled');
}); });
},
isDanger: true,
}], {
title: 'Warning',
text: 'No, seriously.<br/><br/>If you forget your password, you will lose access to your Telegram account. There will be no way to restore it.'
}); });
popup.show(); attachClickEvent(btnResend, (e) => {
freeze(true);
const d = putPreloader(btnResend);
passwordManager.resendPasswordEmail().then(value => {
d.remove();
freeze(false);
});
}); });
inputWrapper.append(inputField.container, btnContinue, btnSkip); inputWrapper.append(inputField.container, btnChange, btnResend);
inputContent.append(inputWrapper); inputContent.append(inputWrapper);
@ -115,6 +130,7 @@ export default class AppTwoStepVerificationEmailConfirmationTab extends SliderSu
} }
onOpenAfterTimeout() { onOpenAfterTimeout() {
this.inputField.input.focus(); if(!canFocus(this.isFirst)) return;
this.codeInputField.input.focus();
} }
} }

19
src/components/sidebarLeft/tabs/2fa/enterPassword.ts

@ -1,6 +1,7 @@
import AppTwoStepVerificationTab from "."; import AppTwoStepVerificationTab from ".";
import { SettingSection } from "../.."; import { SettingSection } from "../..";
import { attachClickEvent, cancelEvent } from "../../../../helpers/dom"; import { attachClickEvent, cancelEvent, canFocus } from "../../../../helpers/dom";
import { isMobileSafari } from "../../../../helpers/userAgent";
import { AccountPassword } from "../../../../layer"; import { AccountPassword } from "../../../../layer";
import passwordManager from "../../../../lib/mtproto/passwordManager"; import passwordManager from "../../../../lib/mtproto/passwordManager";
import RichTextProcessor from "../../../../lib/richtextprocessor"; import RichTextProcessor from "../../../../lib/richtextprocessor";
@ -16,6 +17,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT
public state: AccountPassword; public state: AccountPassword;
public passwordInputField: PasswordInputField; public passwordInputField: PasswordInputField;
public plainPassword: string; public plainPassword: string;
public isFirst = true;
constructor(slider: SidebarSlider) { constructor(slider: SidebarSlider) {
super(slider, true); super(slider, true);
@ -56,7 +58,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT
} }
if(e.key === 'Enter') { if(e.key === 'Enter') {
return btnContinue.click(); return onContinueClick();
} }
}); });
@ -69,6 +71,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT
return true; return true;
}; };
let onContinueClick: (e?: Event) => void;
if(!isNew) { if(!isNew) {
let getStateInterval: number; let getStateInterval: number;
@ -123,12 +126,15 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT
}); });
}; };
attachClickEvent(btnContinue, submit); onContinueClick = submit;
getState(); getState();
} else { } else {
attachClickEvent(btnContinue, (e) => { onContinueClick = (e) => {
if(e) {
cancelEvent(e); cancelEvent(e);
}
if(!verifyInput()) return; if(!verifyInput()) return;
const tab = new AppTwoStepVerificationReEnterPasswordTab(this.slider); const tab = new AppTwoStepVerificationReEnterPasswordTab(this.slider);
@ -136,11 +142,14 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT
tab.newPassword = passwordInputField.value; tab.newPassword = passwordInputField.value;
tab.plainPassword = this.plainPassword; tab.plainPassword = this.plainPassword;
tab.open(); tab.open();
}); };
} }
attachClickEvent(btnContinue, onContinueClick);
} }
onOpenAfterTimeout() { onOpenAfterTimeout() {
if(!canFocus(this.isFirst)) return;
this.passwordInputField.input.focus(); this.passwordInputField.input.focus();
} }
} }

27
src/components/sidebarLeft/tabs/2fa/hint.ts

@ -7,6 +7,7 @@ import { wrapSticker } from "../../../wrappers";
import InputField from "../../../inputField"; import InputField from "../../../inputField";
import AppTwoStepVerificationEmailTab from "./email"; import AppTwoStepVerificationEmailTab from "./email";
import { attachClickEvent, cancelEvent } from "../../../../helpers/dom"; import { attachClickEvent, cancelEvent } from "../../../../helpers/dom";
import { toast } from "../../../toast";
export default class AppTwoStepVerificationHintTab extends SliderSuperTab { export default class AppTwoStepVerificationHintTab extends SliderSuperTab {
public inputField: InputField; public inputField: InputField;
@ -23,7 +24,6 @@ export default class AppTwoStepVerificationHintTab extends SliderSuperTab {
this.title.innerHTML = 'Password Hint'; this.title.innerHTML = 'Password Hint';
const section = new SettingSection({ const section = new SettingSection({
caption: ' ',
noDelimiter: true noDelimiter: true
}); });
@ -60,28 +60,37 @@ export default class AppTwoStepVerificationHintTab extends SliderSuperTab {
inputField.input.addEventListener('keypress', (e) => { inputField.input.addEventListener('keypress', (e) => {
if(e.key === 'Enter') { if(e.key === 'Enter') {
cancelEvent(e); cancelEvent(e);
return (inputField.value ? btnContinue : btnSkip).click(); return inputField.value ? onContinueClick() : onSkipClick();
} }
}); });
const goNext = (e: Event, saveHint: boolean) => { const goNext = (e?: Event, saveHint?: boolean) => {
if(e) {
cancelEvent(e); cancelEvent(e);
}
const hint = saveHint ? inputField.value : undefined;
if(hint && this.newPassword === hint) {
toast('Hint must be different from your password');
return;
}
const tab = new AppTwoStepVerificationEmailTab(this.slider); const tab = new AppTwoStepVerificationEmailTab(this.slider);
tab.state = this.state; tab.state = this.state;
tab.plainPassword = this.plainPassword; tab.plainPassword = this.plainPassword;
tab.newPassword = this.newPassword; tab.newPassword = this.newPassword;
if(saveHint) { tab.hint = hint;
tab.hint = inputField.value;
}
tab.open(); tab.open();
}; };
const btnContinue = Button('btn-primary btn-color-primary', {text: 'CONTINUE'}); const btnContinue = Button('btn-primary btn-color-primary', {text: 'CONTINUE'});
const btnSkip = Button('btn-primary btn-primary-transparent primary', {text: 'SKIP'}); const btnSkip = Button('btn-primary btn-secondary btn-primary-transparent primary', {text: 'SKIP'});
attachClickEvent(btnContinue, (e) => goNext(e, true)); const onContinueClick = (e?: Event) => goNext(e, true);
attachClickEvent(btnSkip, (e) => goNext(e, false)); const onSkipClick = (e?: Event) => goNext(e, false);
attachClickEvent(btnContinue, onContinueClick);
attachClickEvent(btnSkip, onSkipClick);
inputWrapper.append(inputField.container, btnContinue, btnSkip); inputWrapper.append(inputField.container, btnContinue, btnSkip);

15
src/components/sidebarLeft/tabs/2fa/index.ts

@ -8,6 +8,7 @@ import PopupConfirmAction from "../../../popups/confirmAction";
import SidebarSlider, { SliderSuperTab } from "../../../slider"; import SidebarSlider, { SliderSuperTab } from "../../../slider";
import { wrapSticker } from "../../../wrappers"; import { wrapSticker } from "../../../wrappers";
import AppSettingsTab from "../settings"; import AppSettingsTab from "../settings";
import AppTwoStepVerificationEmailTab from "./email";
import AppTwoStepVerificationEnterPasswordTab from "./enterPassword"; import AppTwoStepVerificationEnterPasswordTab from "./enterPassword";
export default class AppTwoStepVerificationTab extends SliderSuperTab { export default class AppTwoStepVerificationTab extends SliderSuperTab {
@ -19,7 +20,7 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab {
} }
protected init() { protected init() {
this.container.classList.add('two-step-verification'); this.container.classList.add('two-step-verification', 'two-step-verification-main');
this.title.innerHTML = 'Two-Step Verification'; this.title.innerHTML = 'Two-Step Verification';
const section = new SettingSection({ const section = new SettingSection({
@ -55,7 +56,7 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab {
const btnChangePassword = Button('btn-primary btn-transparent', {icon: 'edit', text: 'Change Password'}); const btnChangePassword = Button('btn-primary btn-transparent', {icon: 'edit', text: 'Change Password'});
const btnDisablePassword = Button('btn-primary btn-transparent', {icon: 'passwordoff', text: 'Turn Password Off'}); const btnDisablePassword = Button('btn-primary btn-transparent', {icon: 'passwordoff', text: 'Turn Password Off'});
const btnSetRecoveryEmail = Button('btn-primary btn-transparent', {icon: 'email', text: 'Set Recovery Email'}); const btnSetRecoveryEmail = Button('btn-primary btn-transparent', {icon: 'email', text: this.state.pFlags.has_recovery ? 'Change Recovery Email' : 'Set Recovery Email'});
attachClickEvent(btnChangePassword, () => { attachClickEvent(btnChangePassword, () => {
const tab = new AppTwoStepVerificationEnterPasswordTab(this.slider); const tab = new AppTwoStepVerificationEnterPasswordTab(this.slider);
@ -82,6 +83,16 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab {
popup.show(); popup.show();
}); });
attachClickEvent(btnSetRecoveryEmail, () => {
const tab = new AppTwoStepVerificationEmailTab(this.slider);
tab.state = this.state;
tab.hint = this.state.hint;
tab.plainPassword = this.plainPassword;
tab.newPassword = this.plainPassword;
tab.isFirst = true;
tab.open();
});
c.append(btnChangePassword, btnDisablePassword, btnSetRecoveryEmail); c.append(btnChangePassword, btnDisablePassword, btnSetRecoveryEmail);
} else { } else {
section.caption.innerHTML = 'You can set a password that will be required when you log in on a new device in addition to the code you get in the SMS.'; section.caption.innerHTML = 'You can set a password that will be required when you log in on a new device in addition to the code you get in the SMS.';

3
src/components/sidebarLeft/tabs/2fa/passwordSet.ts

@ -31,8 +31,7 @@ export default class AppTwoStepVerificationSetTab extends SliderSuperTab {
loop: true, loop: true,
play: true, play: true,
width: 160, width: 160,
height: 160, height: 160
emoji
}).then(() => { }).then(() => {
// this.animation = player; // this.animation = player;
}); });

10
src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts

@ -52,7 +52,7 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe
} }
if(e.key === 'Enter') { if(e.key === 'Enter') {
return btnContinue.click(); return onContinueClick();
} }
}); });
@ -65,8 +65,11 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe
return true; return true;
}; };
attachClickEvent(btnContinue, (e) => { const onContinueClick = (e?: Event) => {
if(e) {
cancelEvent(e); cancelEvent(e);
}
if(!verifyInput()) return; if(!verifyInput()) return;
const tab = new AppTwoStepVerificationHintTab(this.slider); const tab = new AppTwoStepVerificationHintTab(this.slider);
@ -74,7 +77,8 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe
tab.plainPassword = this.plainPassword; tab.plainPassword = this.plainPassword;
tab.newPassword = this.newPassword; tab.newPassword = this.newPassword;
tab.open(); tab.open();
}); };
attachClickEvent(btnContinue, onContinueClick);
} }
onOpenAfterTimeout() { onOpenAfterTimeout() {

9
src/components/sidebarLeft/tabs/privacyAndSecurity.ts

@ -7,6 +7,7 @@ import AppPrivacyPhoneNumberTab from "./privacy/phoneNumber";
import AppTwoStepVerificationTab from "./2fa"; import AppTwoStepVerificationTab from "./2fa";
import passwordManager from "../../../lib/mtproto/passwordManager"; import passwordManager from "../../../lib/mtproto/passwordManager";
import AppTwoStepVerificationEnterPasswordTab from "./2fa/enterPassword"; import AppTwoStepVerificationEnterPasswordTab from "./2fa/enterPassword";
import AppTwoStepVerificationEmailConfirmationTab from "./2fa/emailConfirmation";
export default class AppPrivacyAndSecurityTab extends SliderSuperTab { export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
constructor(slider: SidebarSlider) { constructor(slider: SidebarSlider) {
@ -35,9 +36,15 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
title: 'Two-Step Verification', title: 'Two-Step Verification',
subtitle: 'Loading...', subtitle: 'Loading...',
clickable: (e: Event) => { clickable: (e: Event) => {
let tab: AppTwoStepVerificationTab | AppTwoStepVerificationEnterPasswordTab; let tab: AppTwoStepVerificationTab | AppTwoStepVerificationEnterPasswordTab | AppTwoStepVerificationEmailConfirmationTab;
if(passwordState.pFlags.has_password) { if(passwordState.pFlags.has_password) {
tab = new AppTwoStepVerificationEnterPasswordTab(this.slider); tab = new AppTwoStepVerificationEnterPasswordTab(this.slider);
} else if(passwordState.email_unconfirmed_pattern) {
tab = new AppTwoStepVerificationEmailConfirmationTab(this.slider);
tab.email = passwordState.email_unconfirmed_pattern;
tab.length = 6;
tab.isFirst = true;
passwordManager.resendPasswordEmail();
} else { } else {
tab = new AppTwoStepVerificationTab(this.slider); tab = new AppTwoStepVerificationTab(this.slider);
} }

14
src/helpers/dom.ts

@ -2,7 +2,7 @@ import { MessageEntity } from "../layer";
import RichTextProcessor from "../lib/richtextprocessor"; import RichTextProcessor from "../lib/richtextprocessor";
import ListenerSetter from "./listenerSetter"; import ListenerSetter from "./listenerSetter";
import { isTouchSupported } from "./touchSupport"; import { isTouchSupported } from "./touchSupport";
import { isApple } from "./userAgent"; import { isApple, isMobileSafari } from "./userAgent";
import rootScope from "../lib/rootScope"; import rootScope from "../lib/rootScope";
import { MOUNT_CLASS_TO } from "../config/debug"; import { MOUNT_CLASS_TO } from "../config/debug";
import { doubleRaf } from "./schedulers"; import { doubleRaf } from "./schedulers";
@ -784,3 +784,15 @@ export function disableTransition(elements: HTMLElement[]) {
elements.forEach(el => el.classList.remove('no-transition')); elements.forEach(el => el.classList.remove('no-transition'));
}); });
} }
export function toggleDisability(elements: HTMLElement[], disable: boolean) {
if(disable) {
elements.forEach(el => el.setAttribute('disabled', 'true'));
} else {
elements.forEach(el => el.removeAttribute('disabled'));
}
}
export function canFocus(isFirstInput: boolean) {
return !isMobileSafari || !isFirstInput;
}

2
src/lib/appManagers/appDialogsManager.ts

@ -181,7 +181,7 @@ export class AppDialogsManager {
public doms: {[peerId: number]: DialogDom} = {}; public doms: {[peerId: number]: DialogDom} = {};
public chatsContainer = document.getElementById('chatlist-container') as HTMLDivElement; public chatsContainer = document.getElementById('chatlist-container') as HTMLDivElement;
private chatsPreloader: HTMLDivElement; private chatsPreloader: HTMLElement;
public loadDialogsPromise: Promise<any>; public loadDialogsPromise: Promise<any>;

12
src/lib/mtproto/passwordManager.ts

@ -80,6 +80,18 @@ export class PasswordManager {
}); });
} }
public confirmPasswordEmail(code: string) {
return apiManager.invokeApi('account.confirmPasswordEmail', {code});
}
public resendPasswordEmail() {
return apiManager.invokeApi('account.resendPasswordEmail');
}
public cancelPasswordEmail() {
return apiManager.invokeApi('account.cancelPasswordEmail');
}
/* public requestRecovery(options: any = {}) { /* public requestRecovery(options: any = {}) {
return apiManager.invokeApi('auth.requestPasswordRecovery', {}, options); return apiManager.invokeApi('auth.requestPasswordRecovery', {}, options);
} }

44
src/pages/pageAuthCode.ts

@ -1,5 +1,5 @@
import mediaSizes from '../helpers/mediaSizes'; import mediaSizes from '../helpers/mediaSizes';
import { AuthSentCode, AuthSentCodeType } from '../layer'; import { AuthSentCode, AuthSentCodeType, AuthSignIn } from '../layer';
import appStateManager from '../lib/appManagers/appStateManager'; import appStateManager from '../lib/appManagers/appStateManager';
import apiManager from '../lib/mtproto/mtprotoworker'; import apiManager from '../lib/mtproto/mtprotoworker';
import Page from './page'; import Page from './page';
@ -7,8 +7,8 @@ import pageIm from './pageIm';
import pagePassword from './pagePassword'; import pagePassword from './pagePassword';
import pageSignIn from './pageSignIn'; import pageSignIn from './pageSignIn';
import pageSignUp from './pageSignUp'; import pageSignUp from './pageSignUp';
import InputField from '../components/inputField';
import TrackingMonkey from '../components/monkeys/tracking'; import TrackingMonkey from '../components/monkeys/tracking';
import CodeInputField from '../components/codeInputField';
let authCode: AuthSentCode.authSentCode = null; let authCode: AuthSentCode.authSentCode = null;
@ -17,24 +17,21 @@ let sentTypeElement: HTMLParagraphElement = null;
let codeInput: HTMLInputElement; let codeInput: HTMLInputElement;
let onFirstMount = (): Promise<any> => { let onFirstMount = (): Promise<any> => {
let lastLength = 0;
const CODELENGTH = (authCode.type as AuthSentCodeType.authSentCodeTypeApp).length; const CODELENGTH = (authCode.type as AuthSentCodeType.authSentCodeTypeApp).length;
const codeInputField = new InputField({ const codeInputField = new CodeInputField({
label: 'Code', label: 'Code',
name: 'code', name: 'code',
plainText: true length: CODELENGTH,
onFill: (code) => {
submitCode('' + code);
}
}); });
codeInput = codeInputField.input as HTMLInputElement; codeInput = codeInputField.input as HTMLInputElement;
codeInput.type = 'tel';
codeInput.setAttribute('required', '');
codeInput.autocomplete = 'off';
page.pageEl.querySelector('.input-wrapper').append(codeInputField.container); page.pageEl.querySelector('.input-wrapper').append(codeInputField.container);
const codeInputLabel = codeInput.nextElementSibling as HTMLLabelElement;
const editButton = page.pageEl.querySelector('.phone-edit') as HTMLElement; const editButton = page.pageEl.querySelector('.phone-edit') as HTMLElement;
editButton.addEventListener('click', function() { editButton.addEventListener('click', function() {
@ -50,7 +47,7 @@ let onFirstMount = (): Promise<any> => {
const submitCode = (code: string) => { const submitCode = (code: string) => {
codeInput.setAttribute('disabled', 'true'); codeInput.setAttribute('disabled', 'true');
const params = { const params: AuthSignIn = {
phone_number: authCode.phone_number, phone_number: authCode.phone_number,
phone_code_hash: authCode.phone_code_hash, phone_code_hash: authCode.phone_code_hash,
phone_code: code phone_code: code
@ -92,15 +89,15 @@ let onFirstMount = (): Promise<any> => {
break; break;
case 'PHONE_CODE_EXPIRED': case 'PHONE_CODE_EXPIRED':
codeInput.classList.add('error'); codeInput.classList.add('error');
codeInputLabel.innerText = 'Code expired'; codeInputField.label.innerText = 'Code expired';
break; break;
case 'PHONE_CODE_EMPTY': case 'PHONE_CODE_EMPTY':
case 'PHONE_CODE_INVALID': case 'PHONE_CODE_INVALID':
codeInput.classList.add('error'); codeInput.classList.add('error');
codeInputLabel.innerText = 'Invalid Code'; codeInputField.label.innerText = 'Invalid Code';
break; break;
default: default:
codeInputLabel.innerText = err.type; codeInputField.label.innerText = err.type;
break; break;
} }
@ -108,25 +105,6 @@ let onFirstMount = (): Promise<any> => {
}); });
}; };
codeInput.addEventListener('input', function(this: typeof codeInput, e) {
this.classList.remove('error');
codeInputLabel.innerText = 'Code';
this.value = this.value.replace(/\D/g, '');
if(this.value.length > CODELENGTH) {
this.value = this.value.slice(0, CODELENGTH);
}
const length = this.value.length;
if(length === CODELENGTH) { // submit code
submitCode(this.value);
} else if(length === lastLength) {
return;
}
lastLength = length;
});
const imageDiv = page.pageEl.querySelector('.auth-image') as HTMLDivElement; const imageDiv = page.pageEl.querySelector('.auth-image') as HTMLDivElement;
const size = mediaSizes.isMobile ? 100 : 166; const size = mediaSizes.isMobile ? 100 : 166;
const monkey = new TrackingMonkey(codeInputField, size); const monkey = new TrackingMonkey(codeInputField, size);

4
src/scss/partials/_button.scss

@ -264,6 +264,10 @@
@include hover-background-effect(); @include hover-background-effect();
&.danger {
@include hover-background-effect(red);
}
// * tgico // * tgico
&:before { &:before {
color: #707579; color: #707579;

12
src/scss/partials/_leftSidebar.scss

@ -913,9 +913,15 @@
margin-bottom: 1.125rem; margin-bottom: 1.125rem;
} }
&-main {
.btn-primary + .btn-primary { .btn-primary + .btn-primary {
margin-top: .125rem !important; margin-top: .125rem !important;
} }
}
.btn-secondary {
margin-top: .5rem !important;
}
.media-sticker-wrapper { .media-sticker-wrapper {
width: 168px; width: 168px;
@ -935,12 +941,6 @@
} }
} }
&-hint, &-email {
.btn-primary + .btn-primary {
margin-top: .5rem !important;
}
}
&-hint { &-hint {
.media-sticker-wrapper { .media-sticker-wrapper {
width: 160px; width: 160px;

Loading…
Cancel
Save