Browse Source

Temporary commit

master
Eduard Kuzmenko 3 years ago
parent
commit
67e76a3f03
  1. 11
      src/components/inputField.ts
  2. 5
      src/components/monkeys/index.ts
  3. 59
      src/components/monkeys/password.ts
  4. 41
      src/components/passwordInputField.ts
  5. 18
      src/components/popups/confirmAction.ts
  6. 33
      src/components/popups/index.ts
  7. 15
      src/components/row.ts
  8. 135
      src/components/sidebarLeft/tabs/2fa/enterPassword.ts
  9. 38
      src/components/sidebarLeft/tabs/2fa/index.ts
  10. 74
      src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts
  11. 29
      src/components/sidebarLeft/tabs/privacyAndSecurity.ts
  12. 2
      src/lib/appManagers/appMessagesManager.ts
  13. 101
      src/lib/mtproto/passwordManager.ts
  14. 94
      src/pages/pagePassword.ts
  15. 53
      src/scss/partials/_input.scss
  16. 13
      src/scss/partials/_leftSidebar.scss
  17. 3
      src/scss/partials/pages/_chats.scss
  18. 50
      src/scss/partials/pages/_password.scss
  19. 19
      src/scss/partials/popups/_peer.scss

11
src/components/inputField.ts

@ -55,12 +55,13 @@ class InputField { @@ -55,12 +55,13 @@ class InputField {
public container: HTMLElement;
public input: HTMLElement;
public inputFake: HTMLElement;
public label: HTMLLabelElement;
//public onLengthChange: (length: number, isOverflow: boolean) => void;
private wasInputFakeClientHeight: number;
private showScrollDebounced: () => void;
protected wasInputFakeClientHeight: number;
protected showScrollDebounced: () => void;
constructor(private options: {
constructor(protected options: {
placeholder?: string,
label?: string,
name?: string,
@ -131,6 +132,10 @@ class InputField { @@ -131,6 +132,10 @@ class InputField {
input.addEventListener('input', () => checkAndSetRTL(input));
}
if(label) {
this.label = this.container.lastElementChild as HTMLLabelElement;
}
let processInput: () => void;
if(maxLength) {
const labelEl = this.container.lastElementChild as HTMLLabelElement;

5
src/components/monkeys/index.ts

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
import InputField from "../inputField";
export default interface Monkey {
attachToInputField: (inputField: InputField) => void;
}

59
src/components/monkeys/password.ts

@ -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();
}
}
}

41
src/components/passwordInputField.ts

@ -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();
};
}

18
src/components/popups/confirmAction.ts

@ -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);
}
}

33
src/components/popups/index.ts

@ -1,11 +1,23 @@ @@ -1,11 +1,23 @@
import rootScope from "../../lib/rootScope";
import { blurActiveElement, cancelEvent, findUpClassName } from "../../helpers/dom";
import { blurActiveElement, findUpClassName } from "../../helpers/dom";
import { ripple } from "../ripple";
import animationIntersector from "../animationIntersector";
import appNavigationController, { NavigationItem } from "../appNavigationController";
import { isMobileSafari, isSafari } from "../../helpers/userAgent";
export type PopupOptions = Partial<{closable: true, overlayClosable: true, withConfirm: string, body: true}>;
export type PopupButton = {
text: string,
callback?: () => void,
isDanger?: true,
isCancel?: true
};
export type PopupOptions = Partial<{
closable: true,
overlayClosable: true,
withConfirm: string,
body: true
}>;
export default class PopupElement {
protected element = document.createElement('div');
protected container = document.createElement('div');
@ -140,9 +152,14 @@ export default class PopupElement { @@ -140,9 +152,14 @@ export default class PopupElement {
};
}
export type PopupButton = {
text: string,
callback?: () => void,
isDanger?: true,
isCancel?: true
export const addCancelButton = (buttons: PopupButton[]) => {
const button = buttons.find(b => b.isCancel);
if(!button) {
buttons.push({
text: 'CANCEL',
isCancel: true
});
}
return buttons;
};

15
src/components/row.ts

@ -20,7 +20,7 @@ export default class Row { @@ -20,7 +20,7 @@ export default class Row {
radioField: Row['radioField'],
checkboxField: Row['checkboxField'],
title: string,
clickable: boolean,
clickable: boolean | ((e: Event) => void),
navigationTab: SliderSuperTab
}> = {}) {
this.container = document.createElement('div');
@ -63,14 +63,17 @@ export default class Row { @@ -63,14 +63,17 @@ export default class Row {
}
if(options.navigationTab) {
this.container.addEventListener('click', () => {
if(this.freezed) return;
options.navigationTab.open();
});
options.clickable = true;
options.clickable = () => options.navigationTab.open();
}
if(options.clickable) {
if(typeof(options.clickable) === 'function') {
this.container.addEventListener('click', (e) => {
if(this.freezed) return;
(options.clickable as any)(e);
});
}
this.container.classList.add('row-clickable', 'hover-effect');
ripple(this.container);
}

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

@ -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();
});
}
}
}

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

@ -1,15 +1,20 @@ @@ -1,15 +1,20 @@
import { generateSection, SettingSection } from "../..";
import { SettingSection } from "../..";
import { attachClickEvent } from "../../../../helpers/dom";
import { AccountPassword } from "../../../../layer";
import appStickersManager from "../../../../lib/appManagers/appStickersManager";
import passwordManager from "../../../../lib/mtproto/passwordManager";
import Button from "../../../button";
import PopupConfirmAction from "../../../popups/confirmAction";
import SidebarSlider, { SliderSuperTab } from "../../../slider";
import { wrapSticker } from "../../../wrappers";
import AppTwoStepVerificationEnterPasswordTab from "./enterPassword";
export default class AppTwoStepVerificationTab extends SliderSuperTab {
public passwordState: AccountPassword;
public state: AccountPassword;
public plainPassword: string;
constructor(slider: SidebarSlider) {
super(slider);
super(slider, true);
}
protected init() {
@ -40,19 +45,40 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab { @@ -40,19 +45,40 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab {
section.content.append(stickerContainer);
const c = section.generateContentElement();
if(this.passwordState.pFlags.has_password) {
if(this.state.pFlags.has_password) {
section.caption.innerHTML = 'You have enabled Two-Step verification.<br/>You\'ll need the password you set up here to log in to your Telegram account';
const btnChangePassword = Button('btn-primary btn-transparent', {icon: 'edit', text: 'Change Password'});
const btnRemovePassword = 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'});
c.append(btnChangePassword, btnRemovePassword, btnSetRecoveryEmail);
attachClickEvent(btnDisablePassword, () => {
const popup = new PopupConfirmAction('popup-disable-password', [{
text: 'DISABLE',
callback: () => {
passwordManager.updateSettings({currentPassword: this.plainPassword});
},
isDanger: true,
}], {
title: 'Warning',
text: 'Are you sure you want to disable your password?'
});
popup.show();
});
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.';
const btnSetPassword = Button('btn-primary', {text: 'SET PASSWORD'});
c.append(btnSetPassword);
attachClickEvent(btnSetPassword, (e) => {
const tab = new AppTwoStepVerificationEnterPasswordTab(this.slider);
tab.state = this.state;
tab.open();
});
}
this.scrollable.container.append(section.container);

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

@ -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;
});
}
}

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

@ -1,11 +1,12 @@ @@ -1,11 +1,12 @@
import SidebarSlider, { SliderSuperTab } from "../../slider";
import { generateSection, SettingSection } from "..";
import Row from "../../row";
import { InputPrivacyKey, PrivacyRule } from "../../../layer";
import { AccountPassword, InputPrivacyKey, PrivacyRule } from "../../../layer";
import appPrivacyManager from "../../../lib/appManagers/appPrivacyManager";
import AppPrivacyPhoneNumberTab from "./privacy/phoneNumber";
import AppTwoStepVerificationTab from "./2fa";
import passwordManager from "../../../lib/mtproto/passwordManager";
import AppTwoStepVerificationEnterPasswordTab from "./2fa/enterPassword";
export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
constructor(slider: SidebarSlider) {
@ -28,14 +29,25 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab { @@ -28,14 +29,25 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
clickable: true
});
const tab = new AppTwoStepVerificationTab(this.slider);
const twoFactorRow = new Row({
let passwordState: AccountPassword;
const twoFactorRowOptions = {
icon: 'lock',
title: 'Two-Step Verification',
subtitle: 'Loading...',
navigationTab: tab
});
clickable: (e: Event) => {
let tab: AppTwoStepVerificationTab | AppTwoStepVerificationEnterPasswordTab;
if(passwordState.pFlags.has_password) {
tab = new AppTwoStepVerificationEnterPasswordTab(this.slider);
} else {
tab = new AppTwoStepVerificationTab(this.slider);
}
tab.state = passwordState;
tab.open();
}
};
const twoFactorRow = new Row(twoFactorRowOptions);
twoFactorRow.freezed = true;
const activeSessionRow = new Row({
@ -49,10 +61,11 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab { @@ -49,10 +61,11 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
this.scrollable.append(section.container);
passwordManager.getState().then(state => {
passwordState = state;
twoFactorRow.subtitle.innerText = state.pFlags.has_password ? 'On' : 'Off';
twoFactorRow.freezed = false;
tab.passwordState = state;
//console.log('password state', state);
console.log('password state', state);
});
}

2
src/lib/appManagers/appMessagesManager.ts

@ -2465,7 +2465,7 @@ export class AppMessagesManager { @@ -2465,7 +2465,7 @@ export class AppMessagesManager {
if(document.type === 'video') {
messageText = '<i>Video' + (message.message ? ', ' : '') + '</i>';
} else if(document.type === 'voice') {
messageText = '<i>Voice message</i>';
messageText = '<i>Voice message' + (message.message ? ', ' : '') + '</i>';
} else if(document.type === 'gif') {
messageText = '<i>GIF' + (message.message ? ', ' : '') + '</i>';
} else if(document.type === 'round') {

101
src/lib/mtproto/passwordManager.ts

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
import type { AccountPassword, AccountPasswordInputSettings, AccountUpdatePasswordSettings, InputCheckPasswordSRP, PasswordKdfAlgo } from '../../layer';
import type CryptoWorkerMethods from '../crypto/crypto_methods';
import { MOUNT_CLASS_TO } from '../../config/debug';
import { AccountPassword } from '../../layer';
import appUsersManager from '../appManagers/appUsersManager';
import apiManager from './mtprotoworker';
//import { computeCheck } from "../crypto/srp";
@ -10,54 +12,71 @@ export class PasswordManager { @@ -10,54 +12,71 @@ export class PasswordManager {
});
}
/* public updateSettings(state: any, settings: any) {
var currentHashPromise;
var newHashPromise;
var params: any = {
new_settings: {
_: 'account.passwordInputSettings',
hint: settings.hint || ''
}
};
if(typeof settings.cur_password === 'string' &&
settings.cur_password.length > 0) {
currentHashPromise = this.makePasswordHash(state.current_salt, settings.cur_password);
} else {
currentHashPromise = Promise.resolve([]);
}
public updateSettings(settings: {
hint?: string,
email?: string,
newPassword?: string,
currentPassword?: string
} = {}) {
//state = Object.assign({}, state);
//state.new_algo = Object.assign({}, state.new_algo);
if (typeof settings.new_password === 'string' &&
settings.new_password.length > 0) {
var saltRandom = new Array(8);
var newSalt = bufferConcat(state.new_salt, saltRandom);
secureRandom.nextBytes(saltRandom);
newHashPromise = this.makePasswordHash(newSalt, settings.new_password);
params.new_settings.new_salt = newSalt;
} else {
if(typeof settings.new_password === 'string') {
params.new_settings.new_salt = [];
this.getState().then(state => {
let currentHashPromise: ReturnType<CryptoWorkerMethods['computeSRP']>;
let newHashPromise: Promise<Uint8Array>;
const params: AccountUpdatePasswordSettings = {
password: null,
new_settings: {
_: 'account.passwordInputSettings',
hint: settings.hint,
email: settings.email
}
};
if(settings.currentPassword) {
currentHashPromise = apiManager.computeSRP(settings.currentPassword, state);
} else {
currentHashPromise = Promise.resolve({
_: 'inputCheckPasswordEmpty'
});
}
newHashPromise = Promise.resolve([]);
}
if(typeof settings.email === 'string') {
params.new_settings.email = settings.email || '';
}
return Promise.all([currentHashPromise, newHashPromise]).then((hashes) => {
params.current_password_hash = hashes[0];
params.new_settings.new_password_hash = hashes[1];
return apiManager.invokeApi('account.updatePasswordSettings', params);
// * https://core.telegram.org/api/srp#setting-a-new-2fa-password, but still there is a mistake, TDesktop passes 'new_algo' everytime
const newAlgo = state.new_algo as PasswordKdfAlgo.passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow;
const salt1 = new Uint8Array(newAlgo.salt1.length + 32);
salt1.randomize();
salt1.set(newAlgo.salt1, 0);
newAlgo.salt1 = salt1;
if(settings.newPassword) {
newHashPromise = Promise.resolve(new Uint8Array());
} else {
newHashPromise = Promise.resolve(new Uint8Array());
}
return Promise.all([currentHashPromise, newHashPromise]).then((hashes) => {
params.password = hashes[0];
params.new_settings.new_algo = newAlgo;
params.new_settings.new_password_hash = hashes[1];
return apiManager.invokeApi('account.updatePasswordSettings', params);
});
});
} */
}
public check(password: string, state: AccountPassword, options: any = {}) {
return apiManager.computeSRP(password, state).then((inputCheckPassword) => {
//console.log('SRP', inputCheckPassword);
return apiManager.invokeApi('auth.checkPassword', {
password: inputCheckPassword
}, options);
}, options).then(auth => {
if(auth._ === 'auth.authorization') {
appUsersManager.saveApiUser(auth.user);
apiManager.setUserAuth(auth.user.id);
}
return auth;
});
});
}

94
src/pages/pagePassword.ts

@ -1,48 +1,28 @@ @@ -1,48 +1,28 @@
//import CryptoWorker from '../lib/crypto/cryptoworker';
//import apiManager from '../lib/mtproto/apiManager';
import { putPreloader } from '../components/misc';
import mediaSizes from '../helpers/mediaSizes';
import { isAppleMobile, isSafari } from '../helpers/userAgent';
import { AccountPassword } from '../layer';
import appStateManager from '../lib/appManagers/appStateManager';
import LottieLoader, { RLottiePlayer } from '../lib/lottieLoader';
//import passwordManager from '../lib/mtproto/passwordManager';
import apiManager from '../lib/mtproto/mtprotoworker';
import passwordManager from '../lib/mtproto/passwordManager';
import { cancelEvent } from '../helpers/dom';
import Page from './page';
import pageIm from './pageIm';
import InputField from '../components/inputField';
import Button from '../components/button';
import PasswordInputField from '../components/passwordInputField';
import PasswordMonkey from '../components/monkeys/password';
import { ripple } from '../components/ripple';
const TEST = false;
let passwordInput: HTMLInputElement;
let onFirstMount = (): Promise<any> => {
let needFrame = 0;
let animation: RLottiePlayer;
let passwordVisible = false;
const btnNext = Button('btn-primary', {text: 'NEXT'});
const passwordInputField = new InputField({
const passwordInputField = new PasswordInputField({
label: 'Password',
name: 'password',
plainText: true
name: 'password'
});
passwordInput = passwordInputField.input as HTMLInputElement;
passwordInput.type = 'password';
passwordInput.setAttribute('required', '');
passwordInput.autocomplete = 'off';
const passwordInputLabel = passwordInput.nextElementSibling as HTMLLabelElement;
const toggleVisible = document.createElement('span');
toggleVisible.classList.add('toggle-visible', 'tgico');
passwordInputField.container.append(toggleVisible);
page.pageEl.querySelector('.input-wrapper').append(passwordInputField.container, btnNext);
@ -57,13 +37,13 @@ let onFirstMount = (): Promise<any> => { @@ -57,13 +37,13 @@ let onFirstMount = (): Promise<any> => {
return !TEST && passwordManager.getState().then(_state => {
state = _state;
passwordInputLabel.innerText = state.hint ?? 'Password';
passwordInputField.label.innerText = state.hint ?? 'Password';
});
};
let handleError = (err: any) => {
btnNext.removeAttribute('disabled');
passwordInput.classList.add('error');
passwordInputField.input.classList.add('error');
switch(err.type) {
default:
@ -75,29 +55,6 @@ let onFirstMount = (): Promise<any> => { @@ -75,29 +55,6 @@ let onFirstMount = (): Promise<any> => {
getState();
};
const onVisibilityClick = function(this: typeof toggleVisible, e: Event) {
cancelEvent(e);
passwordVisible = !passwordVisible;
this.classList.toggle('eye-hidden', passwordVisible);
if(passwordVisible) {
passwordInput.setAttribute('type', 'text');
animation.setDirection(1);
animation.curFrame = 0;
needFrame = 16;
animation.play();
} else {
passwordInput.setAttribute('type', 'password');
animation.setDirection(-1);
animation.curFrame = 16;
needFrame = 0;
animation.play();
}
};
toggleVisible.addEventListener('click', onVisibilityClick);
toggleVisible.addEventListener('touchend', onVisibilityClick);
let state: AccountPassword;
btnNext.addEventListener('click', function(this, e) {
@ -117,15 +74,14 @@ let onFirstMount = (): Promise<any> => { @@ -117,15 +74,14 @@ let onFirstMount = (): Promise<any> => {
switch(response._) {
case 'auth.authorization':
apiManager.setUserAuth(response.user.id);
clearInterval(getStateInterval);
pageIm.mount();
if(animation) animation.remove();
if(monkey) monkey.remove();
break;
default:
btnNext.removeAttribute('disabled');
btnNext.innerText = response._;
ripple(btnNext);
break;
}
}).catch(handleError);
@ -134,42 +90,18 @@ let onFirstMount = (): Promise<any> => { @@ -134,42 +90,18 @@ let onFirstMount = (): Promise<any> => {
passwordInput.addEventListener('keypress', function(this, e) {
this.classList.remove('error');
btnNext.innerText = 'NEXT';
ripple(btnNext);
if(e.key === 'Enter') {
return btnNext.click();
}
});
/* passwordInput.addEventListener('input', function(this, e) {
}); */
const size = mediaSizes.isMobile ? 100 : 166;
const monkey = new PasswordMonkey(passwordInputField, size);
page.pageEl.querySelector('.auth-image').append(monkey.container);
return Promise.all([
LottieLoader.loadAnimationFromURL({
container: page.pageEl.querySelector('.auth-image'),
loop: false,
autoplay: false,
width: size,
height: size,
noCache: true
//}, 'assets/img/TwoFactorSetupMonkeyClose.tgs').then(_animation => {
}, 'assets/img/TwoFactorSetupMonkeyPeek.tgs').then(_animation => {
//return;
animation = _animation;
animation.addListener('enterFrame', currentFrame => {
//console.log('enterFrame', e, needFrame);
if((animation.direction === 1 && currentFrame >= needFrame) ||
(animation.direction === -1 && currentFrame <= needFrame)) {
animation.setSpeed(1);
animation.pause();
}
});
needFrame = 49;
//animation.play();
}),
monkey.load(),
getState()
]);
};

53
src/scss/partials/_input.scss

@ -51,6 +51,7 @@ @@ -51,6 +51,7 @@
transform-origin: left center;
pointer-events: none;
margin-top: calc((var(--height) - 1.5rem) / 2); // * Center of first line
user-select: none;
body.animation-level-0 & {
transition: none;
@ -301,4 +302,54 @@ input:focus, button:focus { @@ -301,4 +302,54 @@ input:focus, button:focus {
margin-right: -1px;
}
}
}
}
.input-field-password {
.input-field-input {
padding-right: 2.5rem;
max-height: 54px;
&[type="password"] {
font-size: 2.25rem;
padding-left: calc(.875rem - var(--border-width));
@media (-webkit-min-device-pixel-ratio: 2) {
font-size: 1.75rem;
letter-spacing: .125rem;
}
html.is-ios & {
// ! меньше 1rem будет зумить поле
font-size: 1rem;
}
}
}
.toggle-visible {
position: absolute;
right: .375rem;
z-index: 2;
font-size: 1.5rem;
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;
}
}
}

13
src/scss/partials/_leftSidebar.scss

@ -843,6 +843,10 @@ @@ -843,6 +843,10 @@
@include respond-to(not-handhelds) {
margin: 0 .625rem;
}
> .btn-primary {
margin: 0;
}
}
&-name {
@ -857,7 +861,7 @@ @@ -857,7 +861,7 @@
padding: 0 .875rem;
}
.btn-primary, .checkbox-field, .radio-field {
.checkbox-field, .radio-field {
margin: 0;
}
@ -905,6 +909,13 @@ @@ -905,6 +909,13 @@
}
}
.two-step-verification-enter-password {
.media-sticker-wrapper {
width: 157px;
height: 157px;
}
}
.range-setting-selector {
padding: 1rem .875rem;

3
src/scss/partials/pages/_chats.scss

@ -91,7 +91,8 @@ @@ -91,7 +91,8 @@
left: 50%;
transform: translate3d(-50%, -50%, 0);
&-path {
// ! do not change it to &-path
.preloader-path {
stroke: $button-primary-background;
}
}

50
src/scss/partials/pages/_password.scss

@ -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;
}
}
}
} */

19
src/scss/partials/popups/_peer.scss

@ -4,25 +4,28 @@ @@ -4,25 +4,28 @@
#{$parent} {
&-header {
display: flex;
margin-bottom: 0.4375rem;
margin-bottom: .4375rem;
align-items: center;
padding: 0.125rem 0.25rem;
padding: .125rem .25rem;
}
&-container {
padding: 1rem 1.5rem 0.75rem 1rem;
padding: 1rem 1.5rem .75rem 1rem;
}
&-title {
padding-left: 0.75rem;
font-size: 1.25rem;
font-weight: 500;
margin-bottom: 0.125rem;
margin-bottom: .125rem;
text-transform: capitalize;
&:not(:first-child) {
padding-left: .75rem;
}
}
&-description {
padding: 0 0.25rem;
padding: 0 .25rem;
margin-top: 0;
margin-bottom: 1.625rem;
min-width: 15rem;
@ -33,13 +36,13 @@ @@ -33,13 +36,13 @@
}
&-buttons {
margin-right: -0.75rem;
margin-right: -.75rem;
.btn {
font-weight: 500;
& + .btn {
margin-top: 0.625rem;
margin-top: .625rem;
}
}
}

Loading…
Cancel
Save