Eduard Kuzmenko
4 years ago
19 changed files with 580 additions and 213 deletions
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
import InputField from "../inputField"; |
||||
|
||||
export default interface Monkey { |
||||
attachToInputField: (inputField: InputField) => void; |
||||
} |
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
import lottieLoader, { RLottiePlayer } from "../../lib/lottieLoader"; |
||||
import PasswordInputField from "../passwordInputField"; |
||||
|
||||
export default class PasswordMonkey { |
||||
public container: HTMLElement; |
||||
public animation: RLottiePlayer; |
||||
public needFrame = 0; |
||||
protected loadPromise: Promise<any>; |
||||
|
||||
constructor(protected passwordInputField: PasswordInputField, protected size: number) { |
||||
this.container = document.createElement('div'); |
||||
this.container.classList.add('media-sticker-wrapper'); |
||||
} |
||||
|
||||
public load() { |
||||
if(this.loadPromise) return this.loadPromise; |
||||
return this.loadPromise = lottieLoader.loadAnimationFromURL({ |
||||
container: this.container, |
||||
loop: false, |
||||
autoplay: false, |
||||
width: this.size, |
||||
height: this.size, |
||||
noCache: true |
||||
//}, 'assets/img/TwoFactorSetupMonkeyClose.tgs').then(_animation => {
|
||||
}, 'assets/img/TwoFactorSetupMonkeyPeek.tgs').then(_animation => { |
||||
//return;
|
||||
this.animation = _animation; |
||||
this.animation.addListener('enterFrame', currentFrame => { |
||||
//console.log('enterFrame', currentFrame, this.needFrame);
|
||||
|
||||
if((this.animation.direction === 1 && currentFrame >= this.needFrame) || |
||||
(this.animation.direction === -1 && currentFrame <= this.needFrame)) { |
||||
this.animation.setSpeed(1); |
||||
this.animation.pause(); |
||||
} |
||||
}); |
||||
|
||||
this.passwordInputField.onVisibilityClickAdditional = () => { |
||||
if(this.passwordInputField.passwordVisible) { |
||||
this.animation.setDirection(1); |
||||
this.animation.curFrame = 0; |
||||
this.needFrame = 16; |
||||
this.animation.play(); |
||||
} else { |
||||
this.animation.setDirection(-1); |
||||
this.animation.curFrame = 16; |
||||
this.needFrame = 0; |
||||
this.animation.play(); |
||||
} |
||||
}; |
||||
}); |
||||
} |
||||
|
||||
public remove() { |
||||
if(this.animation) { |
||||
this.animation.remove(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,41 @@
@@ -0,0 +1,41 @@
|
||||
import { cancelEvent } from "../helpers/dom"; |
||||
import InputField from "./inputField"; |
||||
|
||||
export default class PasswordInputField extends InputField { |
||||
public passwordVisible = false; |
||||
public toggleVisible: HTMLElement; |
||||
public onVisibilityClickAdditional: () => void; |
||||
|
||||
constructor(options: { |
||||
label?: string, |
||||
name?: string |
||||
} = {}) { |
||||
super({ |
||||
plainText: true, |
||||
...options |
||||
}); |
||||
|
||||
const input = this.input as HTMLInputElement; |
||||
input.type = 'password'; |
||||
input.setAttribute('required', ''); |
||||
input.autocomplete = 'off'; |
||||
|
||||
const toggleVisible = this.toggleVisible = document.createElement('span'); |
||||
toggleVisible.classList.add('toggle-visible', 'tgico'); |
||||
|
||||
this.container.classList.add('input-field-password'); |
||||
this.container.append(toggleVisible); |
||||
|
||||
toggleVisible.addEventListener('click', this.onVisibilityClick); |
||||
toggleVisible.addEventListener('touchend', this.onVisibilityClick); |
||||
} |
||||
|
||||
public onVisibilityClick = (e: Event) => { |
||||
cancelEvent(e); |
||||
this.passwordVisible = !this.passwordVisible; |
||||
|
||||
this.toggleVisible.classList.toggle('eye-hidden', this.passwordVisible); |
||||
(this.input as HTMLInputElement).type = this.passwordVisible ? 'text' : 'password'; |
||||
this.onVisibilityClickAdditional && this.onVisibilityClickAdditional(); |
||||
}; |
||||
} |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
import PopupElement, { addCancelButton, PopupButton, PopupOptions } from "."; |
||||
|
||||
export default class PopupConfirmAction extends PopupElement { |
||||
constructor(className: string, buttons: PopupButton[], options: PopupOptions & Partial<{title: string, text: string}> = {}) { |
||||
super('popup-peer popup-confirm-action ' + className, addCancelButton(buttons), { |
||||
overlayClosable: true, |
||||
...options |
||||
}); |
||||
|
||||
this.title.innerHTML = options.title || 'Warning'; |
||||
|
||||
const p = document.createElement('p'); |
||||
p.classList.add('popup-description'); |
||||
p.innerHTML = options.text; |
||||
|
||||
this.container.insertBefore(p, this.header.nextElementSibling); |
||||
} |
||||
} |
@ -0,0 +1,135 @@
@@ -0,0 +1,135 @@
|
||||
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 AppTwoStepVerificationReEnterPasswordTab from "./reEnterPassword"; |
||||
|
||||
export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperTab { |
||||
public state: AccountPassword; |
||||
|
||||
constructor(slider: SidebarSlider) { |
||||
super(slider, true); |
||||
} |
||||
|
||||
protected init() { |
||||
this.container.classList.add('two-step-verification-enter-password'); |
||||
this.title.innerHTML = this.state.pFlags.has_password ? 'Enter Your Password' : 'Enter a Password'; |
||||
|
||||
const section = new SettingSection({ |
||||
noDelimiter: true |
||||
}); |
||||
|
||||
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 monkey = new PasswordMonkey(passwordInputField, 157); |
||||
monkey.load(); |
||||
|
||||
const btnContinue = Button('btn-primary', {text: 'CONTINUE'}); |
||||
|
||||
inputWrapper.append(passwordInputField.container, btnContinue); |
||||
section.content.append(monkey.container, inputWrapper); |
||||
|
||||
this.scrollable.container.append(section.container); |
||||
|
||||
passwordInputField.input.addEventListener('keypress', (e) => { |
||||
if(passwordInputField.input.classList.contains('error')) { |
||||
passwordInputField.input.classList.remove('error'); |
||||
btnContinue.innerText = 'CONTINUE'; |
||||
ripple(btnContinue); |
||||
} |
||||
|
||||
if(e.key === 'Enter') { |
||||
return btnContinue.click(); |
||||
} |
||||
}); |
||||
|
||||
const verifyInput = () => { |
||||
if(!passwordInputField.value.length) { |
||||
passwordInputField.input.classList.add('error'); |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
}; |
||||
|
||||
if(this.state.pFlags.has_password) { |
||||
let getStateInterval: number; |
||||
|
||||
let getState = () => { |
||||
// * just to check session relevance
|
||||
if(!getStateInterval) { |
||||
getStateInterval = window.setInterval(getState, 10e3); |
||||
} |
||||
|
||||
return passwordManager.getState().then(_state => { |
||||
this.state = _state; |
||||
|
||||
passwordInputField.label.innerText = this.state.hint ?? 'Password'; |
||||
}); |
||||
}; |
||||
|
||||
const submit = (e?: Event) => { |
||||
if(!verifyInput()) { |
||||
cancelEvent(e); |
||||
return; |
||||
} |
||||
|
||||
btnContinue.setAttribute('disabled', 'true'); |
||||
btnContinue.textContent = 'PLEASE WAIT...'; |
||||
putPreloader(btnContinue); |
||||
|
||||
const plainPassword = passwordInputField.value; |
||||
passwordManager.check(passwordInputField.value, this.state).then(auth => { |
||||
console.log(auth); |
||||
|
||||
if(auth._ === 'auth.authorization') { |
||||
clearInterval(getStateInterval); |
||||
if(monkey) monkey.remove(); |
||||
const tab = new AppTwoStepVerificationTab(this.slider); |
||||
tab.state = this.state; |
||||
tab.plainPassword = plainPassword; |
||||
tab.open(); |
||||
} |
||||
}, (err) => { |
||||
btnContinue.removeAttribute('disabled'); |
||||
passwordInputField.input.classList.add('error'); |
||||
|
||||
switch(err.type) { |
||||
default: |
||||
//btnContinue.innerText = err.type;
|
||||
btnContinue.innerText = 'INVALID PASSWORD'; |
||||
break; |
||||
} |
||||
|
||||
getState(); |
||||
}); |
||||
}; |
||||
|
||||
attachClickEvent(btnContinue, submit); |
||||
|
||||
getState(); |
||||
} else { |
||||
attachClickEvent(btnContinue, (e) => { |
||||
cancelEvent(e); |
||||
if(!verifyInput()) return; |
||||
|
||||
const tab = new AppTwoStepVerificationReEnterPasswordTab(this.slider); |
||||
tab.state = this.state; |
||||
tab.open(); |
||||
}); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
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"; |
||||
|
||||
export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSuperTab { |
||||
public state: AccountPassword; |
||||
|
||||
constructor(slider: SidebarSlider) { |
||||
super(slider, true); |
||||
} |
||||
|
||||
protected init() { |
||||
this.container.classList.add('two-step-verification-enter-password'); |
||||
this.title.innerHTML = 'Re-Enter your Password'; |
||||
|
||||
const section = new SettingSection({ |
||||
noDelimiter: true |
||||
}); |
||||
|
||||
const inputWrapper = document.createElement('div'); |
||||
inputWrapper.classList.add('input-wrapper'); |
||||
|
||||
const passwordInputField = new PasswordInputField({ |
||||
name: 're-enter-password', |
||||
label: 'Re-Enter your Password' |
||||
}); |
||||
|
||||
const monkey = new PasswordMonkey(passwordInputField, 157); |
||||
monkey.load(); |
||||
|
||||
const btnContinue = Button('btn-primary', {text: 'CONTINUE'}); |
||||
|
||||
inputWrapper.append(passwordInputField.container, btnContinue); |
||||
section.content.append(monkey.container, inputWrapper); |
||||
|
||||
this.scrollable.container.append(section.container); |
||||
|
||||
passwordInputField.input.addEventListener('keypress', (e) => { |
||||
if(passwordInputField.input.classList.contains('error')) { |
||||
passwordInputField.input.classList.remove('error'); |
||||
btnContinue.innerText = 'CONTINUE'; |
||||
ripple(btnContinue); |
||||
} |
||||
|
||||
if(e.key === 'Enter') { |
||||
return btnContinue.click(); |
||||
} |
||||
}); |
||||
|
||||
const verifyInput = () => { |
||||
if(!passwordInputField.value.length) { |
||||
passwordInputField.input.classList.add('error'); |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
}; |
||||
|
||||
attachClickEvent(btnContinue, (e) => { |
||||
cancelEvent(e); |
||||
if(!verifyInput()) return; |
||||
|
||||
|
||||
}); |
||||
} |
||||
} |
@ -1,49 +1,3 @@
@@ -1,49 +1,3 @@
|
||||
.page-password { |
||||
.input-field-input { |
||||
padding-right: 2.5rem; |
||||
max-height: 54px; |
||||
|
||||
&[type="password"] { |
||||
font-size: 2.25rem; |
||||
padding-left: 10px; |
||||
|
||||
@media (-webkit-min-device-pixel-ratio: 2) { |
||||
font-size: 1.75rem; |
||||
letter-spacing: .125rem; |
||||
} |
||||
|
||||
html.is-ios & { |
||||
// ! меньше 1rem будет зумить поле |
||||
font-size: 1rem; |
||||
} |
||||
} |
||||
} |
||||
/* .page-password { |
||||
|
||||
.toggle-visible { |
||||
position: absolute; |
||||
right: .25rem; |
||||
z-index: 2; |
||||
font-size: 1.25rem; |
||||
color: $placeholder-color; |
||||
cursor: pointer; |
||||
transition: color .2s; |
||||
padding: .5rem; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
top: 50%; |
||||
transform: translateY(-50%); |
||||
|
||||
&:before { |
||||
content: $tgico-eye1; |
||||
} |
||||
|
||||
&.eye-hidden:before { |
||||
content: $tgico-eye2; |
||||
} |
||||
|
||||
@include hover() { |
||||
color: #000; |
||||
} |
||||
} |
||||
} |
||||
} */ |
||||
|
Loading…
Reference in new issue