From 3e514c1caaae55af4124d1327f30bb1642a36769 Mon Sep 17 00:00:00 2001 From: morethanwords Date: Sat, 20 Feb 2021 13:07:52 +0400 Subject: [PATCH] Temp 2FA commit --- src/components/monkeys/tracking.ts | 144 ++++++++++++ src/components/sidebarLeft/tabs/2fa/email.ts | 100 ++++++++ .../sidebarLeft/tabs/2fa/enterPassword.ts | 19 +- src/components/sidebarLeft/tabs/2fa/hint.ts | 91 ++++++++ src/components/sidebarLeft/tabs/2fa/index.ts | 9 +- .../sidebarLeft/tabs/2fa/reEnterPassword.ts | 27 ++- src/lib/crypto/crypto_methods.ts | 4 +- src/lib/crypto/srp.ts | 11 +- src/lib/mtproto/passwordManager.ts | 4 +- src/pages/pageAuthCode.ts | 216 ++---------------- src/scss/partials/_button.scss | 11 + src/scss/partials/_leftSidebar.scss | 24 +- 12 files changed, 431 insertions(+), 229 deletions(-) create mode 100644 src/components/monkeys/tracking.ts create mode 100644 src/components/sidebarLeft/tabs/2fa/email.ts create mode 100644 src/components/sidebarLeft/tabs/2fa/hint.ts diff --git a/src/components/monkeys/tracking.ts b/src/components/monkeys/tracking.ts new file mode 100644 index 00000000..684ba825 --- /dev/null +++ b/src/components/monkeys/tracking.ts @@ -0,0 +1,144 @@ +import InputField from "../inputField"; +import lottieLoader, { RLottiePlayer } from "../../lib/lottieLoader"; + +export default class TrackingMonkey { + public container: HTMLElement; + + protected max = 45; + protected needFrame = 0; + + protected animation: RLottiePlayer; + protected idleAnimation: RLottiePlayer; + + protected loadPromise: Promise; + + constructor(protected inputField: InputField, protected size: number) { + this.container = document.createElement('div'); + this.container.classList.add('media-sticker-wrapper'); + + const input = inputField.input; + + input.addEventListener('blur', () => { + this.playAnimation(0); + }); + + input.addEventListener('input', (e) => { + this.playAnimation(inputField.value.length); + }); + + /* codeInput.addEventListener('focus', () => { + playAnimation(Math.max(codeInput.value.length, 1)); + }); */ + } + + // 1st symbol = frame 15 + // end symbol = frame 165 + public playAnimation(length: number) { + if(!this.animation) return; + + length = Math.min(length, 30); + let frame: number; + if(length) { + frame = Math.round(Math.min(this.max, length) * (165 / this.max) + 11.33); + + if(this.idleAnimation) { + this.idleAnimation.stop(true); + this.idleAnimation.canvas.style.display = 'none'; + } + + this.animation.canvas.style.display = ''; + } else { + /* const cb = (frameNo: number) => { + if(frameNo <= 1) { */ + /* idleAnimation.play(); + idleAnimation.canvas.style.display = ''; + animation.canvas.style.display = 'none'; */ + /* animation.removeListener('enterFrame', cb); + } + }; + animation.addListener('enterFrame', cb); */ + + frame = 0; + } + //animation.playSegments([1, 2]); + + const direction = this.needFrame > frame ? -1 : 1; + //console.log('keydown', length, frame, direction); + + this.animation.setDirection(direction); + if(this.needFrame !== 0 && frame === 0) { + this.animation.setSpeed(7); + } + /* let diff = Math.abs(needFrame - frame * direction); + if((diff / 20) > 1) animation.setSpeed(diff / 20 | 0); */ + this.needFrame = frame; + + this.animation.play(); + + /* animation.goToAndStop(15, true); */ + //animation.goToAndStop(length / max * ); + } + + public load() { + if(this.loadPromise) return this.loadPromise; + this.loadPromise = Promise.all([ + lottieLoader.loadAnimationFromURL({ + container: this.container, + loop: true, + autoplay: true, + width: this.size, + height: this.size + }, 'assets/img/TwoFactorSetupMonkeyIdle.tgs').then(animation => { + this.idleAnimation = animation; + + // ! animationIntersector will stop animation instantly + if(!this.inputField.value.length) { + animation.play(); + } + }), + + lottieLoader.loadAnimationFromURL({ + container: this.container, + loop: false, + autoplay: false, + width: this.size, + height: this.size + }, 'assets/img/TwoFactorSetupMonkeyTracking.tgs').then(_animation => { + this.animation = _animation; + + if(!this.inputField.value.length) { + this.animation.canvas.style.display = 'none'; + } + + this.animation.addListener('enterFrame', currentFrame => { + //console.log('enterFrame', currentFrame, needFrame); + //let currentFrame = Math.round(e.currentTime); + + if((this.animation.direction === 1 && currentFrame >= this.needFrame) || + (this.animation.direction === -1 && currentFrame <= this.needFrame)) { + this.animation.setSpeed(1); + this.animation.pause(); + } + + if(currentFrame === 0 && this.needFrame === 0) { + //animation.curFrame = 0; + + if(this.idleAnimation) { + this.idleAnimation.canvas.style.display = ''; + this.idleAnimation.play(); + this.animation.canvas.style.display = 'none'; + } + } + }); + //console.log(animation.getDuration(), animation.getDuration(true)); + }) + ]); + } + + public remove() { + if(this.animation) this.animation.remove(); + if(this.idleAnimation) this.idleAnimation.remove(); + } + + +} \ No newline at end of file diff --git a/src/components/sidebarLeft/tabs/2fa/email.ts b/src/components/sidebarLeft/tabs/2fa/email.ts new file mode 100644 index 00000000..e10348c9 --- /dev/null +++ b/src/components/sidebarLeft/tabs/2fa/email.ts @@ -0,0 +1,100 @@ +import { SettingSection } from "../.."; +import { AccountPassword } from "../../../../layer"; +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 passwordManager from "../../../../lib/mtproto/passwordManager"; + +export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { + public inputField: InputField; + public state: AccountPassword; + public plainPassword: string; + public newPassword: string; + public hint: string; + + constructor(slider: SidebarSlider) { + super(slider, true); + } + + protected init() { + this.container.classList.add('two-step-verification-email'); + this.title.innerHTML = 'Recovery Email'; + + const section = new SettingSection({ + caption: ' ', + noDelimiter: true + }); + + const emoji = '💌'; + const doc = appStickersManager.getAnimatedEmojiSticker(emoji); + const stickerContainer = document.createElement('div'); + + wrapSticker({ + doc, + div: stickerContainer, + loop: false, + play: true, + width: 168, + height: 168, + emoji + }).then(() => { + // this.animation = player; + }); + + section.content.append(stickerContainer); + + const inputContent = section.generateContentElement(); + + const inputWrapper = document.createElement('div'); + inputWrapper.classList.add('input-wrapper'); + + const inputField = this.inputField = new InputField({ + name: 'recovery-email', + label: 'Recovery Email' + }); + + const btnContinue = Button('btn-primary', {text: 'CONTINUE'}); + const btnSkip = Button('btn-primary btn-primary-transparent primary', {text: 'SKIP'}); + + attachClickEvent(btnSkip, (e) => { + const popup = new PopupConfirmAction('popup-skip-email', [{ + text: 'CANCEL', + isCancel: true + }, { + text: 'SKIP', + callback: () => { + inputContent.classList.add('sidebar-left-section-disabled'); + putPreloader(btnSkip); + passwordManager.updateSettings({ + hint: this.hint, + currentPassword: this.plainPassword, + newPassword: this.newPassword + }).then(() => { + + }); + }, + 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.' + }); + + popup.show(); + }); + + inputWrapper.append(inputField.container, btnContinue, btnSkip); + + inputContent.append(inputWrapper); + + this.scrollable.container.append(section.container); + } + + onOpenAfterTimeout() { + this.inputField.input.focus(); + } +} diff --git a/src/components/sidebarLeft/tabs/2fa/enterPassword.ts b/src/components/sidebarLeft/tabs/2fa/enterPassword.ts index 2968da77..c2b816c1 100644 --- a/src/components/sidebarLeft/tabs/2fa/enterPassword.ts +++ b/src/components/sidebarLeft/tabs/2fa/enterPassword.ts @@ -13,14 +13,17 @@ import AppTwoStepVerificationReEnterPasswordTab from "./reEnterPassword"; export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperTab { public state: AccountPassword; + public passwordInputField: PasswordInputField; + public plainPassword: string; constructor(slider: SidebarSlider) { super(slider, true); } protected init() { + const isNew = !this.state.pFlags.has_password || this.plainPassword; this.container.classList.add('two-step-verification-enter-password'); - this.title.innerHTML = this.state.pFlags.has_password ? 'Enter Your Password' : 'Enter a Password'; + this.title.innerHTML = isNew ? 'Enter a Password' : 'Enter your Password'; const section = new SettingSection({ noDelimiter: true @@ -29,9 +32,9 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT const inputWrapper = document.createElement('div'); inputWrapper.classList.add('input-wrapper'); - const passwordInputField = new PasswordInputField({ - name: 'first-password', - label: this.state.pFlags.has_password ? this.state.hint ?? 'Password' : 'Enter a Password' + const passwordInputField = this.passwordInputField = new PasswordInputField({ + name: 'enter-password', + label: isNew ? 'Enter a Password' : (this.state.hint ?? 'Password') }); const monkey = new PasswordMonkey(passwordInputField, 157); @@ -65,7 +68,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT return true; }; - if(this.state.pFlags.has_password) { + if(!isNew) { let getStateInterval: number; let getState = () => { @@ -128,8 +131,14 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT const tab = new AppTwoStepVerificationReEnterPasswordTab(this.slider); tab.state = this.state; + tab.newPassword = passwordInputField.value; + tab.plainPassword = this.plainPassword; tab.open(); }); } } + + onOpenAfterTimeout() { + this.passwordInputField.input.focus(); + } } diff --git a/src/components/sidebarLeft/tabs/2fa/hint.ts b/src/components/sidebarLeft/tabs/2fa/hint.ts new file mode 100644 index 00000000..44b3813b --- /dev/null +++ b/src/components/sidebarLeft/tabs/2fa/hint.ts @@ -0,0 +1,91 @@ +import { SettingSection } from "../.."; +import { AccountPassword } from "../../../../layer"; +import appStickersManager from "../../../../lib/appManagers/appStickersManager"; +import Button from "../../../button"; +import SidebarSlider, { SliderSuperTab } from "../../../slider"; +import { wrapSticker } from "../../../wrappers"; +import InputField from "../../../inputField"; +import AppTwoStepVerificationEmailTab from "./email"; +import { attachClickEvent, cancelEvent } from "../../../../helpers/dom"; + +export default class AppTwoStepVerificationHintTab extends SliderSuperTab { + public inputField: InputField; + public state: AccountPassword; + public plainPassword: string; + public newPassword: string; + + constructor(slider: SidebarSlider) { + super(slider, true); + } + + protected init() { + this.container.classList.add('two-step-verification-hint'); + this.title.innerHTML = 'Password Hint'; + + const section = new SettingSection({ + caption: ' ', + noDelimiter: true + }); + + const emoji = '💡'; + const doc = appStickersManager.getAnimatedEmojiSticker(emoji); + const stickerContainer = document.createElement('div'); + + wrapSticker({ + doc, + div: stickerContainer, + loop: false, + play: true, + width: 168, + height: 168, + emoji + }).then(() => { + // this.animation = player; + }); + + section.content.append(stickerContainer); + + const inputWrapper = document.createElement('div'); + inputWrapper.classList.add('input-wrapper'); + + const inputField = this.inputField = new InputField({ + name: 'hint', + label: 'Hint' + }); + + inputField.input.addEventListener('keypress', (e) => { + if(e.key === 'Enter') { + return (inputField.value ? btnContinue : btnSkip).click(); + } + }); + + const goNext = (e: Event, saveHint: boolean) => { + cancelEvent(e); + 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.open(); + }; + + const btnContinue = Button('btn-primary', {text: 'CONTINUE'}); + const btnSkip = Button('btn-primary btn-primary-transparent primary', {text: 'SKIP'}); + + attachClickEvent(btnContinue, (e) => goNext(e, true)); + attachClickEvent(btnSkip, (e) => goNext(e, false)); + + inputWrapper.append(inputField.container, btnContinue, btnSkip); + + section.content.append(inputWrapper); + + this.scrollable.container.append(section.container); + } + + onOpenAfterTimeout() { + this.inputField.input.focus(); + } +} diff --git a/src/components/sidebarLeft/tabs/2fa/index.ts b/src/components/sidebarLeft/tabs/2fa/index.ts index bd2fd14b..e68d179f 100644 --- a/src/components/sidebarLeft/tabs/2fa/index.ts +++ b/src/components/sidebarLeft/tabs/2fa/index.ts @@ -52,6 +52,13 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab { 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'}); + attachClickEvent(btnChangePassword, () => { + const tab = new AppTwoStepVerificationEnterPasswordTab(this.slider); + tab.state = this.state; + tab.plainPassword = this.plainPassword; + tab.open(); + }); + attachClickEvent(btnDisablePassword, () => { const popup = new PopupConfirmAction('popup-disable-password', [{ text: 'DISABLE', @@ -61,7 +68,7 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab { isDanger: true, }], { title: 'Warning', - text: 'Are you sure you want to disable your password?' + text: 'Are you sure you want to disable
your password?' }); popup.show(); diff --git a/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts b/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts index ef62dfae..decdff00 100644 --- a/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts +++ b/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts @@ -1,24 +1,25 @@ -import AppTwoStepVerificationTab from "."; import { SettingSection } from "../.."; import { attachClickEvent, cancelEvent } from "../../../../helpers/dom"; import { AccountPassword } from "../../../../layer"; -import passwordManager from "../../../../lib/mtproto/passwordManager"; import Button from "../../../button"; -import { putPreloader } from "../../../misc"; -import PasswordMonkey from "../../../monkeys/password"; import PasswordInputField from "../../../passwordInputField"; import { ripple } from "../../../ripple"; import SidebarSlider, { SliderSuperTab } from "../../../slider"; +import TrackingMonkey from "../../../monkeys/tracking"; +import AppTwoStepVerificationHintTab from "./hint"; export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSuperTab { public state: AccountPassword; + public passwordInputField: PasswordInputField; + public plainPassword: string; + public newPassword: string; constructor(slider: SidebarSlider) { super(slider, true); } protected init() { - this.container.classList.add('two-step-verification-enter-password'); + this.container.classList.add('two-step-verification-enter-password', 'two-step-verification-re-enter-password'); this.title.innerHTML = 'Re-Enter your Password'; const section = new SettingSection({ @@ -28,12 +29,12 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe const inputWrapper = document.createElement('div'); inputWrapper.classList.add('input-wrapper'); - const passwordInputField = new PasswordInputField({ + const passwordInputField = this.passwordInputField = new PasswordInputField({ name: 're-enter-password', label: 'Re-Enter your Password' }); - const monkey = new PasswordMonkey(passwordInputField, 157); + const monkey = new TrackingMonkey(passwordInputField, 157); monkey.load(); const btnContinue = Button('btn-primary', {text: 'CONTINUE'}); @@ -56,7 +57,7 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe }); const verifyInput = () => { - if(!passwordInputField.value.length) { + if(this.newPassword !== passwordInputField.value) { passwordInputField.input.classList.add('error'); return false; } @@ -68,7 +69,15 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe cancelEvent(e); if(!verifyInput()) return; - + const tab = new AppTwoStepVerificationHintTab(this.slider); + tab.state = this.state; + tab.plainPassword = this.plainPassword; + tab.newPassword = this.newPassword; + tab.open(); }); } + + onOpenAfterTimeout() { + this.passwordInputField.input.focus(); + } } diff --git a/src/lib/crypto/crypto_methods.ts b/src/lib/crypto/crypto_methods.ts index 8b94b045..4d4a0525 100644 --- a/src/lib/crypto/crypto_methods.ts +++ b/src/lib/crypto/crypto_methods.ts @@ -44,7 +44,7 @@ export default abstract class CryptoWorkerMethods { return this.performTaskWorker('gzipUncompress', bytes, toString); } - public computeSRP(password: string, state: any): Promise { - return this.performTaskWorker('computeSRP', password, state); + public computeSRP(password: string, state: any, isNew = false): Promise { + return this.performTaskWorker('computeSRP', password, state, isNew); } } \ No newline at end of file diff --git a/src/lib/crypto/srp.ts b/src/lib/crypto/srp.ts index 7b799eef..5c92c442 100644 --- a/src/lib/crypto/srp.ts +++ b/src/lib/crypto/srp.ts @@ -31,10 +31,10 @@ export async function makePasswordHash(password: string, client_salt: Uint8Array return buffer; } -export async function computeSRP(password: string, state: AccountPassword) { - //console.log('computeCheck:', password, state); +export async function computeSRP(password: string, state: AccountPassword, isNew: boolean) { + console.log('computeSRP:', password, state, isNew); - let algo = state.current_algo as PasswordKdfAlgo.passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow; + let algo = (state.current_algo || state.new_algo) as PasswordKdfAlgo.passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow; let p = str2bigInt(bytesToHex(algo.p), 16); let B = str2bigInt(bytesToHex(state.srp_B), 16); @@ -82,6 +82,11 @@ export async function computeSRP(password: string, state: AccountPassword) { let g_x = powMod(g, x, p); + // * https://core.telegram.org/api/srp#setting-a-new-2fa-password + if(isNew) { + return padArray(bytesFromHex(bigInt2str(g_x, 16)), 256); + } + //log('g_x', bigInt2str(g_x, 16)); let k: any = await CryptoWorker.sha256Hash(bufferConcat(pForHash, gForHash)); diff --git a/src/lib/mtproto/passwordManager.ts b/src/lib/mtproto/passwordManager.ts index 60cfd85a..b466cfd4 100644 --- a/src/lib/mtproto/passwordManager.ts +++ b/src/lib/mtproto/passwordManager.ts @@ -21,7 +21,7 @@ export class PasswordManager { //state = Object.assign({}, state); //state.new_algo = Object.assign({}, state.new_algo); - this.getState().then(state => { + return this.getState().then(state => { let currentHashPromise: ReturnType; let newHashPromise: Promise; const params: AccountUpdatePasswordSettings = { @@ -49,7 +49,7 @@ export class PasswordManager { newAlgo.salt1 = salt1; if(settings.newPassword) { - newHashPromise = Promise.resolve(new Uint8Array()); + newHashPromise = apiManager.computeSRP(settings.newPassword, state, true) as any; } else { newHashPromise = Promise.resolve(new Uint8Array()); } diff --git a/src/pages/pageAuthCode.ts b/src/pages/pageAuthCode.ts index 89a487d7..a712cea2 100644 --- a/src/pages/pageAuthCode.ts +++ b/src/pages/pageAuthCode.ts @@ -1,9 +1,6 @@ import mediaSizes from '../helpers/mediaSizes'; import { AuthSentCode, AuthSentCodeType } from '../layer'; import appStateManager from '../lib/appManagers/appStateManager'; -import LottieLoader, { RLottiePlayer } from '../lib/lottieLoader'; -//import CryptoWorker from '../lib/crypto/cryptoworker'; -//import apiManager from '../lib/mtproto/apiManager'; import apiManager from '../lib/mtproto/mtprotoworker'; import Page from './page'; import pageIm from './pageIm'; @@ -11,20 +8,16 @@ import pagePassword from './pagePassword'; import pageSignIn from './pageSignIn'; import pageSignUp from './pageSignUp'; import InputField from '../components/inputField'; +import TrackingMonkey from '../components/monkeys/tracking'; let authCode: AuthSentCode.authSentCode = null; -//const EDITONSAMEPAGE = false; - let headerElement: HTMLHeadElement = null; let sentTypeElement: HTMLParagraphElement = null; let codeInput: HTMLInputElement; let onFirstMount = (): Promise => { - let needFrame = 0, lastLength = 0; - - let animation: RLottiePlayer; - let idleAnimation: RLottiePlayer; + let lastLength = 0; const CODELENGTH = (authCode.type as AuthSentCodeType.authSentCodeTypeApp).length; @@ -44,92 +37,20 @@ let onFirstMount = (): Promise => { const codeInputLabel = codeInput.nextElementSibling as HTMLLabelElement; const editButton = page.pageEl.querySelector('.phone-edit') as HTMLElement; - /* if(EDITONSAMEPAGE) { - let editable = false; - let changePhonePromise: Promise; - - let changePhone = () => { - if(changePhonePromise) return; - - let phone_number = '+' + headerElement.innerText.replace(/\D/g, ''); - if(authCode.phone_number === phone_number) return; - - codeInput.setAttribute('disabled', 'true'); - - changePhonePromise = apiManager.invokeApi('auth.sendCode', { - phone_number: phone_number, - api_id: App.id, - api_hash: App.hash, - settings: { - _: 'codeSettings' // that's how we sending Type - } - //lang_code: navigator.language || 'en' - }).then((code: any) => { - console.log('got code 2', code); - - authCode = Object.assign(code, {phone_number}); - - changePhonePromise = undefined; - codeInput.removeAttribute('disabled'); - codeInput.focus(); - }).catch(err => { - switch(err.type) { - case 'PHONE_NUMBER_INVALID': - headerElement.classList.add('error'); - editable = true; - headerElement.setAttribute('contenteditable', '' + editable); - headerElement.focus(); - break; - default: - codeInputLabel.innerText = err.type; - break; - } - - changePhonePromise = undefined; - codeInput.removeAttribute('disabled'); - }); - }; - - headerElement.addEventListener('keypress', function(this, e) { - if(e.key === 'Enter') { - editable = false; - headerElement.setAttribute('contenteditable', '' + editable); - changePhone(); - } - - if(/\D/.test(e.key)) { - e.preventDefault(); - return false; - } - - this.classList.remove('error'); - }); - - editButton.addEventListener('click', function() { - if(changePhonePromise) return; - - editable = !editable; - headerElement.setAttribute('contenteditable', '' + editable); - - if(!editable) changePhone(); - }); - } else { */ - editButton.addEventListener('click', function() { - return pageSignIn.mount(); - }); - //} + editButton.addEventListener('click', function() { + return pageSignIn.mount(); + }); - let cleanup = () => { + const cleanup = () => { setTimeout(() => { - if(animation) animation.remove(); - if(idleAnimation) idleAnimation.remove(); + monkey.remove(); }, 300); }; - let submitCode = (code: string) => { + const submitCode = (code: string) => { codeInput.setAttribute('disabled', 'true'); - let params = { + const params = { phone_number: authCode.phone_number, phone_code_hash: authCode.phone_code_hash, phone_code: code @@ -187,9 +108,6 @@ let onFirstMount = (): Promise => { }); }; - const max = 45; - // 1st symbol = frame 15 - // end symbol = frame 165 codeInput.addEventListener('input', function(this: typeof codeInput, e) { this.classList.remove('error'); codeInputLabel.innerText = 'Code'; @@ -199,7 +117,7 @@ let onFirstMount = (): Promise => { this.value = this.value.slice(0, CODELENGTH); } - let length = this.value.length; + const length = this.value.length; if(length === CODELENGTH) { // submit code submitCode(this.value); } else if(length === lastLength) { @@ -207,117 +125,13 @@ let onFirstMount = (): Promise => { } lastLength = length; - - playAnimation(length); }); - const playAnimation = (length: number) => { - if(!animation) return; - - let frame: number; - if(length) { - frame = Math.round(Math.min(max, length) * (165 / max) + 11.33); - - if(idleAnimation) { - idleAnimation.stop(true); - idleAnimation.canvas.style.display = 'none'; - } - - animation.canvas.style.display = ''; - } else { - /* const cb = (frameNo: number) => { - if(frameNo <= 1) { */ - /* idleAnimation.play(); - idleAnimation.canvas.style.display = ''; - animation.canvas.style.display = 'none'; */ - /* animation.removeListener('enterFrame', cb); - } - }; - animation.addListener('enterFrame', cb); */ - - frame = 0; - } - //animation.playSegments([1, 2]); - - const direction = needFrame > frame ? -1 : 1; - //console.log('keydown', length, frame, direction); - - animation.setDirection(direction); - if(needFrame !== 0 && frame === 0) { - animation.setSpeed(7); - } - /* let diff = Math.abs(needFrame - frame * direction); - if((diff / 20) > 1) animation.setSpeed(diff / 20 | 0); */ - needFrame = frame; - - animation.play(); - - /* animation.goToAndStop(15, true); */ - //animation.goToAndStop(length / max * ); - }; - - /* codeInput.addEventListener('focus', () => { - playAnimation(Math.max(codeInput.value.length, 1)); - }); */ - - codeInput.addEventListener('blur', () => { - playAnimation(0); - }); - - let imageDiv = page.pageEl.querySelector('.auth-image') as HTMLDivElement; + const imageDiv = page.pageEl.querySelector('.auth-image') as HTMLDivElement; const size = mediaSizes.isMobile ? 100 : 166; - return Promise.all([ - LottieLoader.loadAnimationFromURL({ - container: imageDiv, - loop: true, - autoplay: true, - width: size, - height: size - }, 'assets/img/TwoFactorSetupMonkeyIdle.tgs').then(animation => { - idleAnimation = animation; - - // ! animationIntersector will stop animation instantly - if(!codeInput.value.length) { - animation.play(); - } - }), - - LottieLoader.loadAnimationFromURL({ - container: imageDiv, - loop: false, - autoplay: false, - width: size, - height: size - }, 'assets/img/TwoFactorSetupMonkeyTracking.tgs').then(_animation => { - animation = _animation; - - if(!codeInput.value.length) { - animation.canvas.style.display = 'none'; - } - - animation.addListener('enterFrame', currentFrame => { - //console.log('enterFrame', currentFrame, needFrame); - //let currentFrame = Math.round(e.currentTime); - - if((animation.direction === 1 && currentFrame >= needFrame) || - (animation.direction === -1 && currentFrame <= needFrame)) { - animation.setSpeed(1); - animation.pause(); - } - - if(currentFrame === 0 && needFrame === 0) { - //animation.curFrame = 0; - - if(idleAnimation) { - idleAnimation.canvas.style.display = ''; - idleAnimation.play(); - animation.canvas.style.display = 'none'; - } - } - }); - //console.log(animation.getDuration(), animation.getDuration(true)); - }) - ]); + const monkey = new TrackingMonkey(codeInputField, size); + imageDiv.append(monkey.container); + return monkey.load(); }; const page = new Page('page-authCode', true, onFirstMount, (_authCode: typeof authCode) => { @@ -334,8 +148,6 @@ const page = new Page('page-authCode', true, onFirstMount, (_authCode: typeof au codeInput.dispatchEvent(evt); } - //let LottieLoader = (await import('../lib/lottieLoader')).default; - headerElement.innerText = authCode.phone_number; switch(authCode.type._) { case 'auth.sentCodeTypeSms': diff --git a/src/scss/partials/_button.scss b/src/scss/partials/_button.scss index b47c8427..f70deec8 100644 --- a/src/scss/partials/_button.scss +++ b/src/scss/partials/_button.scss @@ -225,6 +225,17 @@ background: darken($color-blue, 8%); } + &-transparent { + background-color: transparent; + @include hover() { + background: hover-color($color-blue); + } + + .preloader-circular .preloader-path { + stroke: $color-blue; + } + } + body.animation-level-0 & { transition: none; } diff --git a/src/scss/partials/_leftSidebar.scss b/src/scss/partials/_leftSidebar.scss index 957dda02..a90e8311 100644 --- a/src/scss/partials/_leftSidebar.scss +++ b/src/scss/partials/_leftSidebar.scss @@ -907,12 +907,26 @@ width: 168px; height: 168px; } -} -.two-step-verification-enter-password { - .media-sticker-wrapper { - width: 157px; - height: 157px; + &-enter-password { + .media-sticker-wrapper { + width: 157px; + height: 157px; + } + } + + &-hint { + .media-sticker-wrapper { + width: 120px; + height: 120px; + } + } + + &-email { + .media-sticker-wrapper { + width: 120px; + height: 120px; + } } }