From a6753d73258983f52057497590d899173ad641f0 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Sat, 27 Feb 2021 23:01:25 +0400 Subject: [PATCH] Two-factor: Recovery email Fix enter shortcut on handhelds --- src/components/codeInputField.ts | 38 +++++++ src/components/misc.ts | 5 +- src/components/sidebarLeft/tabs/2fa/email.ts | 24 ++-- .../sidebarLeft/tabs/2fa/emailConfirmation.ts | 106 ++++++++++-------- .../sidebarLeft/tabs/2fa/enterPassword.ts | 23 ++-- src/components/sidebarLeft/tabs/2fa/hint.ts | 29 +++-- src/components/sidebarLeft/tabs/2fa/index.ts | 15 ++- .../sidebarLeft/tabs/2fa/passwordSet.ts | 3 +- .../sidebarLeft/tabs/2fa/reEnterPassword.ts | 12 +- .../sidebarLeft/tabs/privacyAndSecurity.ts | 9 +- src/helpers/dom.ts | 14 ++- src/lib/appManagers/appDialogsManager.ts | 2 +- src/lib/mtproto/passwordManager.ts | 12 ++ src/pages/pageAuthCode.ts | 44 ++------ src/scss/partials/_button.scss | 4 + src/scss/partials/_leftSidebar.scss | 16 +-- 16 files changed, 229 insertions(+), 127 deletions(-) create mode 100644 src/components/codeInputField.ts diff --git a/src/components/codeInputField.ts b/src/components/codeInputField.ts new file mode 100644 index 00000000..889c9753 --- /dev/null +++ b/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; + }); + } +} diff --git a/src/components/misc.ts b/src/components/misc.ts index 5c3cf6a7..0309ae9e 100644 --- a/src/components/misc.ts +++ b/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 = ` `; if(returnDiv) { - let div = document.createElement('div'); + const div = document.createElement('div'); div.classList.add('preloader'); div.innerHTML = html; @@ -74,6 +74,7 @@ export function putPreloader(elem: Element, returnDiv = false) { } elem.innerHTML += html; + return elem.lastElementChild as HTMLElement; } MOUNT_CLASS_TO && (MOUNT_CLASS_TO.putPreloader = putPreloader); diff --git a/src/components/sidebarLeft/tabs/2fa/email.ts b/src/components/sidebarLeft/tabs/2fa/email.ts index 4632b1e5..2d8f8e39 100644 --- a/src/components/sidebarLeft/tabs/2fa/email.ts +++ b/src/components/sidebarLeft/tabs/2fa/email.ts @@ -5,7 +5,7 @@ import Button from "../../../button"; import SidebarSlider, { SliderSuperTab } from "../../../slider"; import { wrapSticker } from "../../../wrappers"; import InputField from "../../../inputField"; -import { attachClickEvent, cancelEvent } from "../../../../helpers/dom"; +import { attachClickEvent, cancelEvent, canFocus } from "../../../../helpers/dom"; import PopupConfirmAction from "../../../popups/confirmAction"; import { putPreloader } from "../../../misc"; import passwordManager from "../../../../lib/mtproto/passwordManager"; @@ -19,6 +19,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { public plainPassword: string; public newPassword: string; public hint: string; + public isFirst = false; constructor(slider: SidebarSlider) { super(slider, true); @@ -29,7 +30,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { this.title.innerHTML = 'Recovery Email'; const section = new SettingSection({ - caption: ' ', + caption: '', noDelimiter: true }); @@ -69,7 +70,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { inputField.input.addEventListener('keypress', (e) => { if(e.key === 'Enter') { 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 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 = () => { new AppTwoStepVerificationSetTab(this.slider).open(); }; - attachClickEvent(btnContinue, (e) => { + const onContinueClick = () => { const email = inputField.value.trim(); const match = RichTextProcessor.matchEmail(email); if(!match || match[0].length !== email.length) { @@ -93,7 +94,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { } toggleButtons(true); - putPreloader(btnContinue); + const d = putPreloader(btnContinue); passwordManager.updateSettings({ hint: this.hint, @@ -108,9 +109,6 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { const tab = new AppTwoStepVerificationEmailConfirmationTab(this.slider); tab.state = this.state; - tab.newPassword = this.newPassword; - tab.plainPassword = this.plainPassword; - tab.hint = this.hint; tab.email = email; tab.length = symbols; tab.open(); @@ -119,8 +117,10 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { } toggleButtons(false); + d.remove(); }); - }); + }; + attachClickEvent(btnContinue, onContinueClick); const toggleButtons = (freeze: boolean) => { if(freeze) { @@ -145,7 +145,8 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { passwordManager.updateSettings({ hint: this.hint, currentPassword: this.plainPassword, - newPassword: this.newPassword + newPassword: this.newPassword, + email: '' }).then(() => { goNext(); }, (err) => { @@ -169,6 +170,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { } onOpenAfterTimeout() { + if(!canFocus(this.isFirst)) return; this.inputField.input.focus(); } } diff --git a/src/components/sidebarLeft/tabs/2fa/emailConfirmation.ts b/src/components/sidebarLeft/tabs/2fa/emailConfirmation.ts index e2163656..668ff5bd 100644 --- a/src/components/sidebarLeft/tabs/2fa/emailConfirmation.ts +++ b/src/components/sidebarLeft/tabs/2fa/emailConfirmation.ts @@ -4,21 +4,19 @@ import appStickersManager from "../../../../lib/appManagers/appStickersManager"; import Button from "../../../button"; import SidebarSlider, { SliderSuperTab } from "../../../slider"; import { wrapSticker } from "../../../wrappers"; -import InputField from "../../../inputField"; -import { attachClickEvent } from "../../../../helpers/dom"; -import PopupConfirmAction from "../../../popups/confirmAction"; -import { putPreloader } from "../../../misc"; +import { attachClickEvent, canFocus, toggleDisability } from "../../../../helpers/dom"; import passwordManager from "../../../../lib/mtproto/passwordManager"; import AppTwoStepVerificationSetTab from "./passwordSet"; +import CodeInputField from "../../../codeInputField"; +import AppTwoStepVerificationEmailTab from "./email"; +import { putPreloader } from "../../../misc"; export default class AppTwoStepVerificationEmailConfirmationTab extends SliderSuperTab { - public inputField: InputField; + public codeInputField: CodeInputField; public state: AccountPassword; - public plainPassword: string; - public newPassword: string; - public hint: string; public email: string; public length: number; + public isFirst = false; constructor(slider: SidebarSlider) { super(slider, true); @@ -29,10 +27,12 @@ export default class AppTwoStepVerificationEmailConfirmationTab extends SliderSu this.title.innerHTML = 'Recovery Email'; const section = new SettingSection({ - caption: ' ', + caption: 'Please enter code we\'ve just emailed at ', noDelimiter: true }); + (section.caption.lastElementChild as HTMLElement).innerText = this.email; + const emoji = '📬'; const doc = appStickersManager.getAnimatedEmojiSticker(emoji); const stickerContainer = document.createElement('div'); @@ -60,54 +60,69 @@ export default class AppTwoStepVerificationEmailConfirmationTab extends SliderSu const inputWrapper = document.createElement('div'); inputWrapper.classList.add('input-wrapper'); - const inputField = this.inputField = new InputField({ + const inputField = this.codeInputField = new CodeInputField({ 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 btnSkip = Button('btn-primary btn-primary-transparent primary', {text: 'SKIP'}); + const btnChange = Button('btn-primary btn-primary-transparent primary', {text: 'CHANGE EMAIL'}); + const btnResend = Button('btn-primary btn-secondary btn-primary-transparent primary', {text: 'RE-SEND CODE'}); const goNext = () => { new AppTwoStepVerificationSetTab(this.slider).open(); }; - attachClickEvent(btnContinue, (e) => { - - }); + const freeze = (disable: boolean) => { + toggleDisability([inputField.input, btnChange, btnResend], disable); + }; - attachClickEvent(btnSkip, (e) => { - const popup = new PopupConfirmAction('popup-skip-email', [{ - text: 'CANCEL', - isCancel: true - }, { - text: 'SKIP', - callback: () => { - //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.

If you forget your password, you will lose access to your Telegram account. There will be no way to restore it.' + attachClickEvent(btnChange, (e) => { + freeze(true); + passwordManager.cancelPasswordEmail().then(value => { + this.slider.sliceTabsUntilTab(AppTwoStepVerificationEmailTab, this); + this.close(); + }, () => { + freeze(false); }); + }); - 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); @@ -115,6 +130,7 @@ export default class AppTwoStepVerificationEmailConfirmationTab extends SliderSu } onOpenAfterTimeout() { - this.inputField.input.focus(); + if(!canFocus(this.isFirst)) return; + this.codeInputField.input.focus(); } } diff --git a/src/components/sidebarLeft/tabs/2fa/enterPassword.ts b/src/components/sidebarLeft/tabs/2fa/enterPassword.ts index e8dca6ea..0a23255e 100644 --- a/src/components/sidebarLeft/tabs/2fa/enterPassword.ts +++ b/src/components/sidebarLeft/tabs/2fa/enterPassword.ts @@ -1,6 +1,7 @@ import AppTwoStepVerificationTab 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 passwordManager from "../../../../lib/mtproto/passwordManager"; import RichTextProcessor from "../../../../lib/richtextprocessor"; @@ -16,6 +17,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT public state: AccountPassword; public passwordInputField: PasswordInputField; public plainPassword: string; + public isFirst = true; constructor(slider: SidebarSlider) { super(slider, true); @@ -56,7 +58,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT } if(e.key === 'Enter') { - return btnContinue.click(); + return onContinueClick(); } }); @@ -69,6 +71,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT return true; }; + let onContinueClick: (e?: Event) => void; if(!isNew) { let getStateInterval: number; @@ -123,12 +126,15 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT }); }; - attachClickEvent(btnContinue, submit); - + onContinueClick = submit; + getState(); } else { - attachClickEvent(btnContinue, (e) => { - cancelEvent(e); + onContinueClick = (e) => { + if(e) { + cancelEvent(e); + } + if(!verifyInput()) return; const tab = new AppTwoStepVerificationReEnterPasswordTab(this.slider); @@ -136,11 +142,14 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT tab.newPassword = passwordInputField.value; tab.plainPassword = this.plainPassword; tab.open(); - }); + }; } + + attachClickEvent(btnContinue, onContinueClick); } onOpenAfterTimeout() { + if(!canFocus(this.isFirst)) return; this.passwordInputField.input.focus(); } } diff --git a/src/components/sidebarLeft/tabs/2fa/hint.ts b/src/components/sidebarLeft/tabs/2fa/hint.ts index fd82bbe0..2f04a082 100644 --- a/src/components/sidebarLeft/tabs/2fa/hint.ts +++ b/src/components/sidebarLeft/tabs/2fa/hint.ts @@ -7,6 +7,7 @@ import { wrapSticker } from "../../../wrappers"; import InputField from "../../../inputField"; import AppTwoStepVerificationEmailTab from "./email"; import { attachClickEvent, cancelEvent } from "../../../../helpers/dom"; +import { toast } from "../../../toast"; export default class AppTwoStepVerificationHintTab extends SliderSuperTab { public inputField: InputField; @@ -23,7 +24,6 @@ export default class AppTwoStepVerificationHintTab extends SliderSuperTab { this.title.innerHTML = 'Password Hint'; const section = new SettingSection({ - caption: ' ', noDelimiter: true }); @@ -60,28 +60,37 @@ export default class AppTwoStepVerificationHintTab extends SliderSuperTab { inputField.input.addEventListener('keypress', (e) => { if(e.key === 'Enter') { cancelEvent(e); - return (inputField.value ? btnContinue : btnSkip).click(); + return inputField.value ? onContinueClick() : onSkipClick(); } }); - const goNext = (e: Event, saveHint: boolean) => { - cancelEvent(e); + const goNext = (e?: Event, saveHint?: boolean) => { + if(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); tab.state = this.state; tab.plainPassword = this.plainPassword; tab.newPassword = this.newPassword; - if(saveHint) { - tab.hint = inputField.value; - } + tab.hint = hint; tab.open(); }; 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)); - attachClickEvent(btnSkip, (e) => goNext(e, false)); + const onContinueClick = (e?: Event) => goNext(e, true); + const onSkipClick = (e?: Event) => goNext(e, false); + attachClickEvent(btnContinue, onContinueClick); + attachClickEvent(btnSkip, onSkipClick); inputWrapper.append(inputField.container, btnContinue, btnSkip); diff --git a/src/components/sidebarLeft/tabs/2fa/index.ts b/src/components/sidebarLeft/tabs/2fa/index.ts index e2671908..2ea1286c 100644 --- a/src/components/sidebarLeft/tabs/2fa/index.ts +++ b/src/components/sidebarLeft/tabs/2fa/index.ts @@ -8,6 +8,7 @@ import PopupConfirmAction from "../../../popups/confirmAction"; import SidebarSlider, { SliderSuperTab } from "../../../slider"; import { wrapSticker } from "../../../wrappers"; import AppSettingsTab from "../settings"; +import AppTwoStepVerificationEmailTab from "./email"; import AppTwoStepVerificationEnterPasswordTab from "./enterPassword"; export default class AppTwoStepVerificationTab extends SliderSuperTab { @@ -19,7 +20,7 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab { } 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'; 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 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, () => { const tab = new AppTwoStepVerificationEnterPasswordTab(this.slider); @@ -82,6 +83,16 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab { 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); } 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.'; diff --git a/src/components/sidebarLeft/tabs/2fa/passwordSet.ts b/src/components/sidebarLeft/tabs/2fa/passwordSet.ts index a2ae3bfa..ce4b0fd7 100644 --- a/src/components/sidebarLeft/tabs/2fa/passwordSet.ts +++ b/src/components/sidebarLeft/tabs/2fa/passwordSet.ts @@ -31,8 +31,7 @@ export default class AppTwoStepVerificationSetTab extends SliderSuperTab { loop: true, play: true, width: 160, - height: 160, - emoji + height: 160 }).then(() => { // this.animation = player; }); diff --git a/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts b/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts index 100802d9..89df6200 100644 --- a/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts +++ b/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts @@ -52,7 +52,7 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe } if(e.key === 'Enter') { - return btnContinue.click(); + return onContinueClick(); } }); @@ -65,8 +65,11 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe return true; }; - attachClickEvent(btnContinue, (e) => { - cancelEvent(e); + const onContinueClick = (e?: Event) => { + if(e) { + cancelEvent(e); + } + if(!verifyInput()) return; const tab = new AppTwoStepVerificationHintTab(this.slider); @@ -74,7 +77,8 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe tab.plainPassword = this.plainPassword; tab.newPassword = this.newPassword; tab.open(); - }); + }; + attachClickEvent(btnContinue, onContinueClick); } onOpenAfterTimeout() { diff --git a/src/components/sidebarLeft/tabs/privacyAndSecurity.ts b/src/components/sidebarLeft/tabs/privacyAndSecurity.ts index c88dc26a..2903707e 100644 --- a/src/components/sidebarLeft/tabs/privacyAndSecurity.ts +++ b/src/components/sidebarLeft/tabs/privacyAndSecurity.ts @@ -7,6 +7,7 @@ import AppPrivacyPhoneNumberTab from "./privacy/phoneNumber"; import AppTwoStepVerificationTab from "./2fa"; import passwordManager from "../../../lib/mtproto/passwordManager"; import AppTwoStepVerificationEnterPasswordTab from "./2fa/enterPassword"; +import AppTwoStepVerificationEmailConfirmationTab from "./2fa/emailConfirmation"; export default class AppPrivacyAndSecurityTab extends SliderSuperTab { constructor(slider: SidebarSlider) { @@ -35,9 +36,15 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab { title: 'Two-Step Verification', subtitle: 'Loading...', clickable: (e: Event) => { - let tab: AppTwoStepVerificationTab | AppTwoStepVerificationEnterPasswordTab; + let tab: AppTwoStepVerificationTab | AppTwoStepVerificationEnterPasswordTab | AppTwoStepVerificationEmailConfirmationTab; if(passwordState.pFlags.has_password) { 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 { tab = new AppTwoStepVerificationTab(this.slider); } diff --git a/src/helpers/dom.ts b/src/helpers/dom.ts index 4a805c11..64154227 100644 --- a/src/helpers/dom.ts +++ b/src/helpers/dom.ts @@ -2,7 +2,7 @@ import { MessageEntity } from "../layer"; import RichTextProcessor from "../lib/richtextprocessor"; import ListenerSetter from "./listenerSetter"; import { isTouchSupported } from "./touchSupport"; -import { isApple } from "./userAgent"; +import { isApple, isMobileSafari } from "./userAgent"; import rootScope from "../lib/rootScope"; import { MOUNT_CLASS_TO } from "../config/debug"; import { doubleRaf } from "./schedulers"; @@ -784,3 +784,15 @@ export function disableTransition(elements: HTMLElement[]) { 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; +} diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 6dfeb404..6c2606f9 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -181,7 +181,7 @@ export class AppDialogsManager { public doms: {[peerId: number]: DialogDom} = {}; public chatsContainer = document.getElementById('chatlist-container') as HTMLDivElement; - private chatsPreloader: HTMLDivElement; + private chatsPreloader: HTMLElement; public loadDialogsPromise: Promise; diff --git a/src/lib/mtproto/passwordManager.ts b/src/lib/mtproto/passwordManager.ts index b466cfd4..d149ced3 100644 --- a/src/lib/mtproto/passwordManager.ts +++ b/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 = {}) { return apiManager.invokeApi('auth.requestPasswordRecovery', {}, options); } diff --git a/src/pages/pageAuthCode.ts b/src/pages/pageAuthCode.ts index a712cea2..f7417fb8 100644 --- a/src/pages/pageAuthCode.ts +++ b/src/pages/pageAuthCode.ts @@ -1,5 +1,5 @@ import mediaSizes from '../helpers/mediaSizes'; -import { AuthSentCode, AuthSentCodeType } from '../layer'; +import { AuthSentCode, AuthSentCodeType, AuthSignIn } from '../layer'; import appStateManager from '../lib/appManagers/appStateManager'; import apiManager from '../lib/mtproto/mtprotoworker'; import Page from './page'; @@ -7,8 +7,8 @@ import pageIm from './pageIm'; import pagePassword from './pagePassword'; import pageSignIn from './pageSignIn'; import pageSignUp from './pageSignUp'; -import InputField from '../components/inputField'; import TrackingMonkey from '../components/monkeys/tracking'; +import CodeInputField from '../components/codeInputField'; let authCode: AuthSentCode.authSentCode = null; @@ -17,24 +17,21 @@ let sentTypeElement: HTMLParagraphElement = null; let codeInput: HTMLInputElement; let onFirstMount = (): Promise => { - let lastLength = 0; - const CODELENGTH = (authCode.type as AuthSentCodeType.authSentCodeTypeApp).length; - const codeInputField = new InputField({ + const codeInputField = new CodeInputField({ label: 'Code', name: 'code', - plainText: true + length: CODELENGTH, + onFill: (code) => { + submitCode('' + code); + } }); codeInput = codeInputField.input as HTMLInputElement; - codeInput.type = 'tel'; - codeInput.setAttribute('required', ''); - codeInput.autocomplete = 'off'; page.pageEl.querySelector('.input-wrapper').append(codeInputField.container); - const codeInputLabel = codeInput.nextElementSibling as HTMLLabelElement; const editButton = page.pageEl.querySelector('.phone-edit') as HTMLElement; editButton.addEventListener('click', function() { @@ -50,7 +47,7 @@ let onFirstMount = (): Promise => { const submitCode = (code: string) => { codeInput.setAttribute('disabled', 'true'); - const params = { + const params: AuthSignIn = { phone_number: authCode.phone_number, phone_code_hash: authCode.phone_code_hash, phone_code: code @@ -92,15 +89,15 @@ let onFirstMount = (): Promise => { break; case 'PHONE_CODE_EXPIRED': codeInput.classList.add('error'); - codeInputLabel.innerText = 'Code expired'; + codeInputField.label.innerText = 'Code expired'; break; case 'PHONE_CODE_EMPTY': case 'PHONE_CODE_INVALID': codeInput.classList.add('error'); - codeInputLabel.innerText = 'Invalid Code'; + codeInputField.label.innerText = 'Invalid Code'; break; default: - codeInputLabel.innerText = err.type; + codeInputField.label.innerText = err.type; break; } @@ -108,25 +105,6 @@ let onFirstMount = (): Promise => { }); }; - 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 size = mediaSizes.isMobile ? 100 : 166; const monkey = new TrackingMonkey(codeInputField, size); diff --git a/src/scss/partials/_button.scss b/src/scss/partials/_button.scss index f40a351d..48248d1e 100644 --- a/src/scss/partials/_button.scss +++ b/src/scss/partials/_button.scss @@ -264,6 +264,10 @@ @include hover-background-effect(); + &.danger { + @include hover-background-effect(red); + } + // * tgico &:before { color: #707579; diff --git a/src/scss/partials/_leftSidebar.scss b/src/scss/partials/_leftSidebar.scss index cdca1a3d..d3b14689 100644 --- a/src/scss/partials/_leftSidebar.scss +++ b/src/scss/partials/_leftSidebar.scss @@ -913,8 +913,14 @@ margin-bottom: 1.125rem; } - .btn-primary + .btn-primary { - margin-top: .125rem !important; + &-main { + .btn-primary + .btn-primary { + margin-top: .125rem !important; + } + } + + .btn-secondary { + margin-top: .5rem !important; } .media-sticker-wrapper { @@ -935,12 +941,6 @@ } } - &-hint, &-email { - .btn-primary + .btn-primary { - margin-top: .5rem !important; - } - } - &-hint { .media-sticker-wrapper { width: 160px;