From 23fd0376bad061f4e576ecc501c5096007f191e0 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Sat, 20 Feb 2021 21:10:26 +0400 Subject: [PATCH] Two-Step Verification layout --- src/components/chat/topbar.ts | 2 +- src/components/horizontalMenu.ts | 4 +- src/components/popups/avatar.ts | 2 +- src/components/popups/index.ts | 2 +- src/components/popups/stickers.ts | 2 +- src/components/sidebarLeft/index.ts | 68 ++-------- src/components/sidebarLeft/tabs/2fa/email.ts | 46 ++++--- .../sidebarLeft/tabs/2fa/enterPassword.ts | 8 +- src/components/sidebarLeft/tabs/2fa/hint.ts | 30 +++-- src/components/sidebarLeft/tabs/2fa/index.ts | 37 ++++-- .../sidebarLeft/tabs/2fa/passwordSet.ts | 61 +++++++++ .../sidebarLeft/tabs/2fa/reEnterPassword.ts | 4 +- src/components/sidebarLeft/tabs/addMembers.ts | 10 +- .../sidebarLeft/tabs/archivedTab.ts | 33 +++-- .../sidebarLeft/tabs/chatFolders.ts | 41 +++--- src/components/sidebarLeft/tabs/contacts.ts | 51 ++++---- src/components/sidebarLeft/tabs/editFolder.ts | 20 ++- .../sidebarLeft/tabs/editProfile.ts | 5 +- .../sidebarLeft/tabs/generalSettings.ts | 2 +- .../sidebarLeft/tabs/includedChats.ts | 18 ++- src/components/sidebarLeft/tabs/newChannel.ts | 10 +- src/components/sidebarLeft/tabs/newGroup.ts | 4 +- .../sidebarLeft/tabs/privacyAndSecurity.ts | 8 +- src/components/sidebarLeft/tabs/settings.ts | 25 ++-- src/components/sidebarRight/index.ts | 14 +- src/components/sidebarRight/tabs/stickers.ts | 2 +- src/components/slider.ts | 121 ++++-------------- src/components/sliderTab.ts | 77 +++++++++++ src/components/transition.ts | 16 +-- src/helpers/bytes.ts | 80 ++++++------ src/helpers/random.ts | 2 +- src/index.hbs | 31 ----- src/lib/appManagers/appDialogsManager.ts | 18 ++- src/lib/crypto/crypto_utils.ts | 4 +- src/lib/crypto/srp.ts | 73 ++++++----- src/pages/pagePassword.ts | 6 +- src/pages/pageSignIn.ts | 2 +- src/pages/pageSignUp.ts | 2 +- src/scss/partials/_button.scss | 12 +- src/scss/partials/_leftSidebar.scss | 48 ++++++- src/scss/style.scss | 6 + src/tests/srp.test.ts | 2 +- 42 files changed, 549 insertions(+), 460 deletions(-) create mode 100644 src/components/sidebarLeft/tabs/2fa/passwordSet.ts create mode 100644 src/components/sliderTab.ts diff --git a/src/components/chat/topbar.ts b/src/components/chat/topbar.ts index 60a2a0c6..2623c7c2 100644 --- a/src/components/chat/topbar.ts +++ b/src/components/chat/topbar.ts @@ -218,7 +218,7 @@ export default class ChatTopbar { this.pinnedMessage = new ChatPinnedMessage(this, this.chat, this.appMessagesManager, this.appPeersManager); - this.btnJoin = Button('btn-primary chat-join hide'); + this.btnJoin = Button('btn-primary btn-color-primary chat-join hide'); this.btnJoin.append('SUBSCRIBE'); this.btnPinned = ButtonIcon('pinlist'); diff --git a/src/components/horizontalMenu.ts b/src/components/horizontalMenu.ts index 77334720..766e32b8 100644 --- a/src/components/horizontalMenu.ts +++ b/src/components/horizontalMenu.ts @@ -32,7 +32,8 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick? animate = false; } - if(target.classList.contains('active') || id === selectTab.prevId) { + const prevId = selectTab.prevId(); + if(target.classList.contains('active') || id === prevId) { return false; } @@ -42,7 +43,6 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick? prev && prev.classList.remove('active'); }); - const prevId = selectTab.prevId; // stripe from ZINCHUK if(useStripe && prevId !== -1 && animate) { fastRaf(() => { diff --git a/src/components/popups/avatar.ts b/src/components/popups/avatar.ts index fd66c827..227b1ea7 100644 --- a/src/components/popups/avatar.ts +++ b/src/components/popups/avatar.ts @@ -66,7 +66,7 @@ export default class PopupAvatar extends PopupElement { }, false); this.btnSubmit = document.createElement('button'); - this.btnSubmit.className = 'btn-primary btn-circle btn-crop btn-icon tgico-check z-depth-1'; + this.btnSubmit.className = 'btn-primary btn-color-primary btn-circle btn-crop btn-icon tgico-check z-depth-1'; ripple(this.btnSubmit); this.btnSubmit.addEventListener('click', () => { this.cropper.crop(); diff --git a/src/components/popups/index.ts b/src/components/popups/index.ts index d34d3952..b13331b8 100644 --- a/src/components/popups/index.ts +++ b/src/components/popups/index.ts @@ -64,7 +64,7 @@ export default class PopupElement { if(options.withConfirm) { this.btnConfirm = document.createElement('button'); - this.btnConfirm.classList.add('btn-primary'); + this.btnConfirm.classList.add('btn-primary', 'btn-color-primary'); this.btnConfirm.innerText = options.withConfirm; this.header.append(this.btnConfirm); ripple(this.btnConfirm); diff --git a/src/components/popups/stickers.ts b/src/components/popups/stickers.ts index 46ac7211..3b32bb2d 100644 --- a/src/components/popups/stickers.ts +++ b/src/components/popups/stickers.ts @@ -97,7 +97,7 @@ export default class PopupStickers extends PopupElement { this.h6.innerHTML = RichTextProcessor.wrapEmojiText(set.set.title); !set.set.installed_date ? this.stickersFooter.classList.add('add') : this.stickersFooter.classList.remove('add'); - this.stickersFooter.innerHTML = set.set.hasOwnProperty('installed_date') ? '
Remove stickers
' : ``; + this.stickersFooter.innerHTML = set.set.hasOwnProperty('installed_date') ? '
Remove stickers
' : ``; this.stickersFooter.addEventListener('click', this.onFooterClick); diff --git a/src/components/sidebarLeft/index.ts b/src/components/sidebarLeft/index.ts index 10ec260b..ef4d9fe7 100644 --- a/src/components/sidebarLeft/index.ts +++ b/src/components/sidebarLeft/index.ts @@ -13,33 +13,18 @@ import Scrollable, { ScrollableX } from "../scrollable"; import InputSearch from "../inputSearch"; import SidebarSlider from "../slider"; import { TransitionSlider } from "../transition"; -import AppAddMembersTab from "./tabs/addMembers"; -import AppArchivedTab from "./tabs/archivedTab"; -import AppChatFoldersTab from "./tabs/chatFolders"; -import AppContactsTab from "./tabs/contacts"; -import AppEditFolderTab from "./tabs/editFolder"; -import AppEditProfileTab from "./tabs/editProfile"; -import AppIncludedChatsTab from "./tabs/includedChats"; -import AppNewChannelTab from "./tabs/newChannel"; import AppNewGroupTab from "./tabs/newGroup"; -import AppSettingsTab from "./tabs/settings"; import appMessagesManager from "../../lib/appManagers/appMessagesManager"; -import apiManagerProxy from "../../lib/mtproto/mtprotoworker"; import AppSearchSuper from "../appSearchSuper."; import { DateData, fillTipDates } from "../../helpers/date"; -import AppGeneralSettingsTab from "./tabs/generalSettings"; -import AppPrivacyAndSecurityTab from "./tabs/privacyAndSecurity"; import { MOUNT_CLASS_TO } from "../../config/debug"; - -const contactsTab = new AppContactsTab(); -const archivedTab = new AppArchivedTab(); +import AppSettingsTab from "./tabs/settings"; +import AppNewChannelTab from "./tabs/newChannel"; +import AppContactsTab from "./tabs/contacts"; +import AppArchivedTab from "./tabs/archivedTab"; +import AppAddMembersTab from "./tabs/addMembers"; export class AppSidebarLeft extends SidebarSlider { - public static SLIDERITEMSIDS = { - archived: 1, - contacts: 2 - }; - private toolsBtn: HTMLButtonElement; private backBtn: HTMLButtonElement; //private searchInput = document.getElementById('global-search') as HTMLInputElement; @@ -63,19 +48,6 @@ export class AppSidebarLeft extends SidebarSlider { privateChat: HTMLButtonElement, } = {} as any; - public archivedTab: AppArchivedTab; - public newChannelTab: AppNewChannelTab; - public addMembersTab: AppAddMembersTab; - public contactsTab: AppContactsTab; - public newGroupTab: AppNewGroupTab; - public settingsTab: AppSettingsTab; - public editProfileTab: AppEditProfileTab; - public chatFoldersTab: AppChatFoldersTab; - public editFolderTab: AppEditFolderTab; - public includedChatsTab: AppIncludedChatsTab; - public generalSettingsTab: AppGeneralSettingsTab; - public privacyAndSecurityTab: AppPrivacyAndSecurityTab; - //private log = logger('SL'); private searchGroups: {[k in 'contacts' | 'globalContacts' | 'messages' | 'people' | 'recent']: SearchGroup} = {} as any; @@ -87,11 +59,6 @@ export class AppSidebarLeft extends SidebarSlider { navigationType: 'left' }); - Object.assign(this.tabs, { - [AppSidebarLeft.SLIDERITEMSIDS.archived]: archivedTab, - [AppSidebarLeft.SLIDERITEMSIDS.contacts]: contactsTab - }); - //this._selectTab(0); // make first tab as default this.inputSearch = new InputSearch('Telegram Search'); @@ -101,19 +68,6 @@ export class AppSidebarLeft extends SidebarSlider { this.toolsBtn = this.sidebarEl.querySelector('.sidebar-tools-button') as HTMLButtonElement; this.backBtn = this.sidebarEl.querySelector('.sidebar-back-button') as HTMLButtonElement; - this.archivedTab = archivedTab; - this.newChannelTab = new AppNewChannelTab(this); - this.contactsTab = contactsTab; - this.newGroupTab = new AppNewGroupTab(this); - this.settingsTab = new AppSettingsTab(this); - this.chatFoldersTab = new AppChatFoldersTab(appMessagesManager, appPeersManager, this, apiManagerProxy, rootScope); - this.editFolderTab = new AppEditFolderTab(this); - this.includedChatsTab = new AppIncludedChatsTab(this); - this.editProfileTab = new AppEditProfileTab(this); - this.generalSettingsTab = new AppGeneralSettingsTab(this); - this.privacyAndSecurityTab = new AppPrivacyAndSecurityTab(this); - this.addMembersTab = new AppAddMembersTab(this); - this.menuEl = this.toolsBtn.querySelector('.btn-menu'); this.newBtnMenu = this.sidebarEl.querySelector('#new-menu'); @@ -132,29 +86,29 @@ export class AppSidebarLeft extends SidebarSlider { }); attachClickEvent(this.buttons.archived, (e) => { - this.selectTab(AppSidebarLeft.SLIDERITEMSIDS.archived); + new AppArchivedTab(this).open(); }); attachClickEvent(this.buttons.contacts, (e) => { - this.contactsTab.openContacts(); + new AppContactsTab(this).open(); }); attachClickEvent(this.buttons.settings, (e) => { - this.settingsTab.open(); + new AppSettingsTab(this).open(); }); attachClickEvent(this.newButtons.channel, (e) => { - this.newChannelTab.open(); + new AppNewChannelTab(this).open(); }); [this.newButtons.group, this.buttons.newGroup].forEach(btn => { attachClickEvent(btn, (e) => { - this.addMembersTab.open({ + new AppAddMembersTab(this).open({ peerId: 0, type: 'chat', skippable: false, takeOut: (peerIds) => { - this.newGroupTab.open(peerIds); + new AppNewGroupTab(this).open(peerIds); }, title: 'Add Members', placeholder: 'Add People...' diff --git a/src/components/sidebarLeft/tabs/2fa/email.ts b/src/components/sidebarLeft/tabs/2fa/email.ts index e10348c9..5a6d3137 100644 --- a/src/components/sidebarLeft/tabs/2fa/email.ts +++ b/src/components/sidebarLeft/tabs/2fa/email.ts @@ -9,6 +9,7 @@ import { attachClickEvent } from "../../../../helpers/dom"; import PopupConfirmAction from "../../../popups/confirmAction"; import { putPreloader } from "../../../misc"; import passwordManager from "../../../../lib/mtproto/passwordManager"; +import AppTwoStepVerificationSetTab from "./passwordSet"; export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { public inputField: InputField; @@ -22,7 +23,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { } protected init() { - this.container.classList.add('two-step-verification-email'); + this.container.classList.add('two-step-verification', 'two-step-verification-email'); this.title.innerHTML = 'Recovery Email'; const section = new SettingSection({ @@ -34,17 +35,21 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { 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; - }); + if(doc) { + wrapSticker({ + doc, + div: stickerContainer, + loop: false, + play: true, + width: 160, + height: 160, + emoji + }).then(() => { + // this.animation = player; + }); + } else { + stickerContainer.classList.add('media-sticker-wrapper'); + } section.content.append(stickerContainer); @@ -58,9 +63,13 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { label: 'Recovery Email' }); - const btnContinue = Button('btn-primary', {text: 'CONTINUE'}); + const btnContinue = Button('btn-primary btn-color-primary', {text: 'CONTINUE'}); const btnSkip = Button('btn-primary btn-primary-transparent primary', {text: 'SKIP'}); + const goNext = () => { + new AppTwoStepVerificationSetTab(this.slider).open(); + }; + attachClickEvent(btnSkip, (e) => { const popup = new PopupConfirmAction('popup-skip-email', [{ text: 'CANCEL', @@ -68,20 +77,25 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { }, { text: 'SKIP', callback: () => { - inputContent.classList.add('sidebar-left-section-disabled'); + //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.' + 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(); diff --git a/src/components/sidebarLeft/tabs/2fa/enterPassword.ts b/src/components/sidebarLeft/tabs/2fa/enterPassword.ts index c2b816c1..e8dca6ea 100644 --- a/src/components/sidebarLeft/tabs/2fa/enterPassword.ts +++ b/src/components/sidebarLeft/tabs/2fa/enterPassword.ts @@ -3,6 +3,7 @@ import { SettingSection } from "../.."; import { attachClickEvent, cancelEvent } from "../../../../helpers/dom"; import { AccountPassword } from "../../../../layer"; import passwordManager from "../../../../lib/mtproto/passwordManager"; +import RichTextProcessor from "../../../../lib/richtextprocessor"; import Button from "../../../button"; import { putPreloader } from "../../../misc"; import PasswordMonkey from "../../../monkeys/password"; @@ -22,7 +23,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT protected init() { const isNew = !this.state.pFlags.has_password || this.plainPassword; - this.container.classList.add('two-step-verification-enter-password'); + this.container.classList.add('two-step-verification', 'two-step-verification-enter-password'); this.title.innerHTML = isNew ? 'Enter a Password' : 'Enter your Password'; const section = new SettingSection({ @@ -40,7 +41,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT const monkey = new PasswordMonkey(passwordInputField, 157); monkey.load(); - const btnContinue = Button('btn-primary', {text: 'CONTINUE'}); + const btnContinue = Button('btn-primary btn-color-primary', {text: 'CONTINUE'}); inputWrapper.append(passwordInputField.container, btnContinue); section.content.append(monkey.container, inputWrapper); @@ -80,7 +81,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT return passwordManager.getState().then(_state => { this.state = _state; - passwordInputField.label.innerText = this.state.hint ?? 'Password'; + passwordInputField.label.innerHTML = this.state.hint ? RichTextProcessor.wrapEmojiText(this.state.hint) : 'Password'; }); }; @@ -105,6 +106,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT tab.state = this.state; tab.plainPassword = plainPassword; tab.open(); + this.slider.removeTabFromHistory(this); } }, (err) => { btnContinue.removeAttribute('disabled'); diff --git a/src/components/sidebarLeft/tabs/2fa/hint.ts b/src/components/sidebarLeft/tabs/2fa/hint.ts index 44b3813b..5a9e9bba 100644 --- a/src/components/sidebarLeft/tabs/2fa/hint.ts +++ b/src/components/sidebarLeft/tabs/2fa/hint.ts @@ -19,7 +19,7 @@ export default class AppTwoStepVerificationHintTab extends SliderSuperTab { } protected init() { - this.container.classList.add('two-step-verification-hint'); + this.container.classList.add('two-step-verification', 'two-step-verification-hint'); this.title.innerHTML = 'Password Hint'; const section = new SettingSection({ @@ -31,17 +31,21 @@ export default class AppTwoStepVerificationHintTab extends SliderSuperTab { 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; - }); + if(doc) { + wrapSticker({ + doc, + div: stickerContainer, + loop: false, + play: true, + width: 160, + height: 160, + emoji + }).then(() => { + // this.animation = player; + }); + } else { + stickerContainer.classList.add('media-sticker-wrapper'); + } section.content.append(stickerContainer); @@ -72,7 +76,7 @@ export default class AppTwoStepVerificationHintTab extends SliderSuperTab { tab.open(); }; - const btnContinue = Button('btn-primary', {text: 'CONTINUE'}); + const btnContinue = Button('btn-primary btn-color-primary', {text: 'CONTINUE'}); const btnSkip = Button('btn-primary btn-primary-transparent primary', {text: 'SKIP'}); attachClickEvent(btnContinue, (e) => goNext(e, true)); diff --git a/src/components/sidebarLeft/tabs/2fa/index.ts b/src/components/sidebarLeft/tabs/2fa/index.ts index e68d179f..32890e0c 100644 --- a/src/components/sidebarLeft/tabs/2fa/index.ts +++ b/src/components/sidebarLeft/tabs/2fa/index.ts @@ -30,23 +30,27 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab { 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; - }); + if(doc) { + wrapSticker({ + doc, + div: stickerContainer, + loop: false, + play: true, + width: 168, + height: 168, + emoji + }).then(() => { + // this.animation = player; + }); + } else { + stickerContainer.classList.add('media-sticker-wrapper'); + } section.content.append(stickerContainer); const c = section.generateContentElement(); if(this.state.pFlags.has_password) { - section.caption.innerHTML = 'You have enabled Two-Step verification.
You\'ll need the password you set up here to log in to your Telegram account'; + section.caption.innerHTML = 'You have enabled Two-Step verification.
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 btnDisablePassword = Button('btn-primary btn-transparent', {icon: 'passwordoff', text: 'Turn Password Off'}); @@ -78,8 +82,13 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab { } 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); + const inputWrapper = document.createElement('div'); + inputWrapper.classList.add('input-wrapper'); + + const btnSetPassword = Button('btn-primary btn-color-primary', {text: 'SET PASSWORD'}); + + inputWrapper.append(btnSetPassword); + c.append(inputWrapper); attachClickEvent(btnSetPassword, (e) => { const tab = new AppTwoStepVerificationEnterPasswordTab(this.slider); diff --git a/src/components/sidebarLeft/tabs/2fa/passwordSet.ts b/src/components/sidebarLeft/tabs/2fa/passwordSet.ts new file mode 100644 index 00000000..5f9357bf --- /dev/null +++ b/src/components/sidebarLeft/tabs/2fa/passwordSet.ts @@ -0,0 +1,61 @@ +import { SettingSection } from "../.."; +import { attachClickEvent } from "../../../../helpers/dom"; +import appStickersManager from "../../../../lib/appManagers/appStickersManager"; +import Button from "../../../button"; +import SidebarSlider, { SliderSuperTab } from "../../../slider"; +import { wrapSticker } from "../../../wrappers"; + +export default class AppTwoStepVerificationSetTab extends SliderSuperTab { + constructor(slider: SidebarSlider) { + super(slider, true); + } + + protected init() { + this.container.classList.add('two-step-verification', 'two-step-verification-set'); + this.title.innerHTML = 'Password Set!'; + + const section = new SettingSection({ + caption: 'This password will be required when you log in on a new device in addition to the code you get via SMS.', + noDelimiter: true + }); + + const emoji = '🥳'; + const doc = appStickersManager.getAnimatedEmojiSticker(emoji); + const stickerContainer = document.createElement('div'); + + if(doc) { + wrapSticker({ + doc, + div: stickerContainer, + loop: false, + play: true, + width: 160, + height: 160, + emoji + }).then(() => { + // this.animation = player; + }); + } else { + stickerContainer.classList.add('media-sticker-wrapper'); + } + + section.content.append(stickerContainer); + + const inputContent = section.generateContentElement(); + + const inputWrapper = document.createElement('div'); + inputWrapper.classList.add('input-wrapper'); + + const btnReturn = Button('btn-primary btn-color-primary', {text: 'RETURN TO SETTINGS'}); + + attachClickEvent(btnReturn, (e) => { + + }); + + inputWrapper.append(btnReturn); + + inputContent.append(inputWrapper); + + this.scrollable.container.append(section.container); + } +} diff --git a/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts b/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts index decdff00..100802d9 100644 --- a/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts +++ b/src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts @@ -19,7 +19,7 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe } protected init() { - this.container.classList.add('two-step-verification-enter-password', 'two-step-verification-re-enter-password'); + this.container.classList.add('two-step-verification', 'two-step-verification-enter-password', 'two-step-verification-re-enter-password'); this.title.innerHTML = 'Re-Enter your Password'; const section = new SettingSection({ @@ -37,7 +37,7 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe const monkey = new TrackingMonkey(passwordInputField, 157); monkey.load(); - const btnContinue = Button('btn-primary', {text: 'CONTINUE'}); + const btnContinue = Button('btn-primary btn-color-primary', {text: 'CONTINUE'}); inputWrapper.append(passwordInputField.container, btnContinue); section.content.append(monkey.container, inputWrapper); diff --git a/src/components/sidebarLeft/tabs/addMembers.ts b/src/components/sidebarLeft/tabs/addMembers.ts index 61e9b0d2..f86d7f89 100644 --- a/src/components/sidebarLeft/tabs/addMembers.ts +++ b/src/components/sidebarLeft/tabs/addMembers.ts @@ -12,7 +12,7 @@ export default class AppAddMembersTab extends SliderSuperTab { private skippable: boolean; constructor(slider: SidebarSlider) { - super(slider); + super(slider, true); } protected init() { @@ -44,13 +44,6 @@ export default class AppAddMembersTab extends SliderSuperTab { }); } - public onCloseAfterTimeout() { - if(this.selector) { - this.selector.container.remove(); - this.selector = null; - } - } - public open(options: { title: string, placeholder: string, @@ -67,7 +60,6 @@ export default class AppAddMembersTab extends SliderSuperTab { this.takeOut = options.takeOut; this.skippable = options.skippable; - this.onCloseAfterTimeout(); this.selector = new AppSelectPeers({ appendTo: this.content, onChange: this.skippable ? null : (length) => { diff --git a/src/components/sidebarLeft/tabs/archivedTab.ts b/src/components/sidebarLeft/tabs/archivedTab.ts index fd025317..b201f44d 100644 --- a/src/components/sidebarLeft/tabs/archivedTab.ts +++ b/src/components/sidebarLeft/tabs/archivedTab.ts @@ -1,23 +1,27 @@ import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; -import Scrollable from "../../scrollable"; -import { SliderTab } from "../../slider"; +import SidebarSlider, { SliderSuperTab } from "../../slider"; -export default class AppArchivedTab implements SliderTab { - public container = document.getElementById('chats-archived-container') as HTMLDivElement; - public chatList = document.getElementById('dialogs-archived') as HTMLUListElement; - public scroll: Scrollable = null; +export default class AppArchivedTab extends SliderSuperTab { public loadedAll: boolean; public loadDialogsPromise: Promise; public wasFilterId: number; + constructor(slider: SidebarSlider) { + super(slider, true); + } + init() { - this.scroll = new Scrollable(this.container, 'CLA', 500); - this.scroll.container.addEventListener('scroll', appDialogsManager.onChatsRegularScroll); - this.scroll.setVirtualContainer(this.chatList); - this.scroll.onScrolledBottom = appDialogsManager.onChatsScroll; + this.container.id = 'chats-archived-container'; + this.title.innerHTML = 'Archived Chats'; + + //this.scrollable = new Scrollable(this.container, 'CLA', 500); + this.scrollable.append(appDialogsManager.chatListArchived); + this.scrollable.container.addEventListener('scroll', appDialogsManager.onChatsRegularScroll); + this.scrollable.setVirtualContainer(appDialogsManager.chatListArchived); + this.scrollable.onScrolledBottom = appDialogsManager.onChatsScroll; ///this.scroll.attachSentinels(); - appDialogsManager.setListClickListener(this.chatList, null, true); + appDialogsManager.setListClickListener(appDialogsManager.chatListArchived, null, true); window.addEventListener('resize', () => { setTimeout(appDialogsManager.scroll.checkForTriggers, 0); @@ -31,9 +35,11 @@ export default class AppArchivedTab implements SliderTab { } this.wasFilterId = appDialogsManager.filterId; - appDialogsManager.scroll = this.scroll; + appDialogsManager.scroll = this.scrollable; appDialogsManager.filterId = 1; appDialogsManager.onTabChange(); + + return super.onOpen(); } // вообще, так делать нельзя, но нет времени чтобы переделать главный чатлист на слайд... @@ -48,6 +54,7 @@ export default class AppArchivedTab implements SliderTab { } onCloseAfterTimeout() { - this.chatList.innerHTML = ''; + appDialogsManager.chatListArchived.innerHTML = ''; + return super.onCloseAfterTimeout(); } } diff --git a/src/components/sidebarLeft/tabs/chatFolders.ts b/src/components/sidebarLeft/tabs/chatFolders.ts index 2dfebca4..5c61fbb4 100644 --- a/src/components/sidebarLeft/tabs/chatFolders.ts +++ b/src/components/sidebarLeft/tabs/chatFolders.ts @@ -1,17 +1,18 @@ -import { SliderTab, SliderSuperTab } from "../../slider"; +import SidebarSlider, { SliderSuperTab } from "../../slider"; import lottieLoader, { RLottiePlayer } from "../../../lib/lottieLoader"; import { RichTextProcessor } from "../../../lib/richtextprocessor"; import { cancelEvent, positionElementByIndex } from "../../../helpers/dom"; import { ripple } from "../../ripple"; import { toast } from "../../toast"; -import type { ApiManagerProxy } from "../../../lib/mtproto/mtprotoworker"; -import type { AppMessagesManager } from "../../../lib/appManagers/appMessagesManager"; import type { MyDialogFilter } from "../../../lib/storages/filters"; -import type { AppPeersManager } from "../../../lib/appManagers/appPeersManager"; -import type { AppSidebarLeft } from ".."; import type { DialogFilterSuggested, DialogFilter } from "../../../layer"; import type _rootScope from "../../../lib/rootScope"; import Button from "../../button"; +import appMessagesManager from "../../../lib/appManagers/appMessagesManager"; +import appPeersManager from "../../../lib/appManagers/appPeersManager"; +import apiManager from "../../../lib/mtproto/mtprotoworker"; +import rootScope from "../../../lib/rootScope"; +import AppEditFolderTab from "./editFolder"; export default class AppChatFoldersTab extends SliderSuperTab { public createFolderBtn: HTMLElement; @@ -22,8 +23,8 @@ export default class AppChatFoldersTab extends SliderSuperTab { private filtersRendered: {[filterId: number]: HTMLElement} = {}; - constructor(private appMessagesManager: AppMessagesManager, private appPeersManager: AppPeersManager, private appSidebarLeft: AppSidebarLeft, private apiManager: ApiManagerProxy, private rootScope: typeof _rootScope) { - super(appSidebarLeft); + constructor(slider: SidebarSlider) { + super(slider, true); } private renderFolder(dialogFilter: DialogFilterSuggested | DialogFilter | MyDialogFilter, container?: HTMLElement, div: HTMLElement = document.createElement('div')) { @@ -40,7 +41,7 @@ export default class AppChatFoldersTab extends SliderSuperTab { const filterId = filter.id; if(!this.filtersRendered.hasOwnProperty(filter.id)) { div.addEventListener('click', () => { - this.appSidebarLeft.editFolderTab.open(this.appMessagesManager.filtersStorage.filters[filterId]); + new AppEditFolderTab(this.slider).open(appMessagesManager.filtersStorage.filters[filterId]); }); } @@ -65,11 +66,11 @@ export default class AppChatFoldersTab extends SliderSuperTab { else if(pFlags.exclude_archived) description += 'Unarchived'; d.push(description); } else { - const folder = this.appMessagesManager.dialogsStorage.getFolder(filter.id); + const folder = appMessagesManager.dialogsStorage.getFolder(filter.id); let chats = 0, channels = 0, groups = 0; for(const dialog of folder) { - if(this.appPeersManager.isAnyGroup(dialog.peerId)) groups++; - else if(this.appPeersManager.isBroadcast(dialog.peerId)) channels++; + if(appPeersManager.isAnyGroup(dialog.peerId)) groups++; + else if(appPeersManager.isBroadcast(dialog.peerId)) channels++; else chats++; } @@ -109,7 +110,7 @@ export default class AppChatFoldersTab extends SliderSuperTab { caption.classList.add('caption'); caption.innerHTML = `Create folders for different groups of chats
and quickly switch between them.`; - this.createFolderBtn = Button('btn-primary btn-create-folder', { + this.createFolderBtn = Button('btn-primary btn-color-primary btn-create-folder', { text: 'Create Folder', icon: 'add' }); @@ -139,7 +140,7 @@ export default class AppChatFoldersTab extends SliderSuperTab { if(Object.keys(this.filtersRendered).length >= 10) { toast('Sorry, you can\'t create more folders.'); } else { - this.appSidebarLeft.editFolderTab.open(); + new AppEditFolderTab(this.slider).open(); } }); @@ -153,13 +154,13 @@ export default class AppChatFoldersTab extends SliderSuperTab { this.animation = player; }); - this.appMessagesManager.filtersStorage.getDialogFilters().then(filters => { + appMessagesManager.filtersStorage.getDialogFilters().then(filters => { for(const filter of filters) { this.renderFolder(filter, this.foldersContainer); } }); - this.rootScope.on('filter_update', (e) => { + rootScope.on('filter_update', (e) => { const filter = e; if(this.filtersRendered.hasOwnProperty(filter.id)) { this.renderFolder(filter, null, this.filtersRendered[filter.id]); @@ -170,7 +171,7 @@ export default class AppChatFoldersTab extends SliderSuperTab { this.getSuggestedFilters(); }); - this.rootScope.on('filter_delete', (e) => { + rootScope.on('filter_delete', (e) => { const filter = e; if(this.filtersRendered.hasOwnProperty(filter.id)) { /* for(const suggested of this.suggestedFilters) { @@ -185,7 +186,7 @@ export default class AppChatFoldersTab extends SliderSuperTab { } }); - this.rootScope.on('filter_order', (e) => { + rootScope.on('filter_order', (e) => { const order = e; order.forEach((filterId, idx) => { const div = this.filtersRendered[filterId]; @@ -197,14 +198,14 @@ export default class AppChatFoldersTab extends SliderSuperTab { } private getSuggestedFilters() { - this.apiManager.invokeApi('messages.getSuggestedDialogFilters').then(suggestedFilters => { + apiManager.invokeApi('messages.getSuggestedDialogFilters').then(suggestedFilters => { this.suggestedContainer.style.display = suggestedFilters.length ? '' : 'none'; Array.from(this.suggestedContainer.children).slice(1).forEach(el => el.remove()); suggestedFilters.forEach(filter => { const div = this.renderFolder(filter); const button = document.createElement('button'); - button.classList.add('btn-primary'); + button.classList.add('btn-primary', 'btn-color-primary'); button.innerText = 'Add'; div.append(button); this.suggestedContainer.append(div); @@ -219,7 +220,7 @@ export default class AppChatFoldersTab extends SliderSuperTab { button.setAttribute('disabled', 'true'); - this.appMessagesManager.filtersStorage.createDialogFilter(filter.filter as any).then(bool => { + appMessagesManager.filtersStorage.createDialogFilter(filter.filter as any).then(bool => { if(bool) { div.remove(); } diff --git a/src/components/sidebarLeft/tabs/contacts.ts b/src/components/sidebarLeft/tabs/contacts.ts index e347b0ba..fce289e9 100644 --- a/src/components/sidebarLeft/tabs/contacts.ts +++ b/src/components/sidebarLeft/tabs/contacts.ts @@ -1,52 +1,52 @@ -import { SliderTab } from "../../slider"; -import Scrollable from "../../scrollable"; +import SidebarSlider, { SliderSuperTab } from "../../slider"; import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager"; import appPhotosManager from "../../../lib/appManagers/appPhotosManager"; -import appSidebarLeft, { AppSidebarLeft } from ".."; import rootScope from "../../../lib/rootScope"; import InputSearch from "../../inputSearch"; // TODO: поиск по людям глобальный, если не нашло в контактах никого -export default class AppContactsTab implements SliderTab { - private container: HTMLElement; +export default class AppContactsTab extends SliderSuperTab { private list: HTMLUListElement; - private scrollable: Scrollable; private promise: Promise; private inputSearch: InputSearch; + private alive = true; + + constructor(slider: SidebarSlider) { + super(slider, true); + } init() { - this.container = document.getElementById('contacts-container'); - this.list = this.container.querySelector('#contacts'); + this.container.id = 'contacts-container'; + + this.list = document.createElement('ul'); + this.list.id = 'contacts'; + this.list.classList.add('contacts-container'); appDialogsManager.setListClickListener(this.list, () => { (this.container.querySelector('.sidebar-close-button') as HTMLElement).click(); }, undefined, true); - this.scrollable = new Scrollable(this.list.parentElement); - this.inputSearch = new InputSearch('Search', (value) => { this.list.innerHTML = ''; this.openContacts(value); }); - this.container.firstElementChild.append(this.inputSearch.container); + this.title.replaceWith(this.inputSearch.container); + + this.scrollable.append(this.list); // preload contacts // appUsersManager.getContacts(); } - // need to clear, and left 1 page for smooth slide - public onClose() { + onClose() { + this.alive = false; + /* // need to clear, and left 1 page for smooth slide let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0; - (Array.from(this.list.children) as HTMLElement[]).slice(pageCount).forEach(el => el.remove()); - } - - public onCloseAfterTimeout() { - this.list.innerHTML = ''; - this.inputSearch.value = ''; + (Array.from(this.list.children) as HTMLElement[]).slice(pageCount).forEach(el => el.remove()); */ } public openContacts(query?: string) { @@ -55,18 +55,14 @@ export default class AppContactsTab implements SliderTab { this.init = null; } - if(appSidebarLeft.historyTabIds.indexOf(AppSidebarLeft.SLIDERITEMSIDS.contacts) === -1) { - appSidebarLeft.selectTab(AppSidebarLeft.SLIDERITEMSIDS.contacts); - } - if(this.promise) return this.promise; this.scrollable.onScrolledBottom = null; this.promise = appUsersManager.getContacts(query).then(_contacts => { this.promise = null; - if(appSidebarLeft.historyTabIds[appSidebarLeft.historyTabIds.length - 1] !== AppSidebarLeft.SLIDERITEMSIDS.contacts) { - console.warn('user closed contacts before it\'s loaded'); + if(!this.alive) { + //console.warn('user closed contacts before it\'s loaded'); return; } @@ -118,4 +114,9 @@ export default class AppContactsTab implements SliderTab { }; }); } + + public open() { + this.openContacts(); + return super.open(); + } } \ No newline at end of file diff --git a/src/components/sidebarLeft/tabs/editFolder.ts b/src/components/sidebarLeft/tabs/editFolder.ts index aee48fbd..e693ea46 100644 --- a/src/components/sidebarLeft/tabs/editFolder.ts +++ b/src/components/sidebarLeft/tabs/editFolder.ts @@ -1,20 +1,18 @@ -import appSidebarLeft, { AppSidebarLeft } from ".."; import { deepEqual, copy } from "../../../helpers/object"; import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; import { MyDialogFilter as DialogFilter } from "../../../lib/storages/filters"; import lottieLoader, { RLottiePlayer } from "../../../lib/lottieLoader"; -import { parseMenuButtonsTo } from "../../misc"; import { ripple } from "../../ripple"; -import { SliderTab, SliderSuperTab } from "../../slider"; +import SidebarSlider, { SliderSuperTab } from "../../slider"; import { toast } from "../../toast"; import appMessagesManager from "../../../lib/appManagers/appMessagesManager"; -import { attachClickEvent } from "../../../helpers/dom"; import InputField from "../../inputField"; import RichTextProcessor from "../../../lib/richtextprocessor"; import ButtonIcon from "../../buttonIcon"; import ButtonMenuToggle from "../../buttonMenuToggle"; import { ButtonMenuItemOptions } from "../../buttonMenu"; import Button from "../../button"; +import AppIncludedChatsTab from "./includedChats"; const MAX_FOLDER_NAME_LENGTH = 12; @@ -36,8 +34,8 @@ export default class AppEditFolderTab extends SliderSuperTab { private type: 'edit' | 'create'; - constructor(appSidebarLeft: AppSidebarLeft) { - super(appSidebarLeft); + constructor(slider: SidebarSlider) { + super(slider, true); } protected init() { @@ -159,11 +157,11 @@ export default class AppEditFolderTab extends SliderSuperTab { const excludedFlagsContainer = this.exclude_peers.querySelector('.folder-categories'); includedFlagsContainer.firstElementChild.addEventListener('click', () => { - appSidebarLeft.includedChatsTab.open(this.filter, 'included'); + new AppIncludedChatsTab(this.slider).open(this.filter, 'included', this); }); excludedFlagsContainer.firstElementChild.addEventListener('click', () => { - appSidebarLeft.includedChatsTab.open(this.filter, 'excluded'); + new AppIncludedChatsTab(this.slider).open(this.filter, 'excluded', this); }); lottieLoader.loadAnimationFromURL({ @@ -228,10 +226,8 @@ export default class AppEditFolderTab extends SliderSuperTab { if(this.animation) { this.animation.restart(); } - } - onCloseAfterTimeout() { - Array.from(this.container.querySelectorAll('ul, .show-more')).forEach(el => el.remove()); + return super.onOpen(); } private onCreateOpen() { @@ -318,7 +314,7 @@ export default class AppEditFolderTab extends SliderSuperTab { setFilter(filter: DialogFilter, firstTime: boolean) { // cleanup - this.onCloseAfterTimeout(); + Array.from(this.container.querySelectorAll('ul, .show-more')).forEach(el => el.remove()); if(firstTime) { this.originalFilter = filter; diff --git a/src/components/sidebarLeft/tabs/editProfile.ts b/src/components/sidebarLeft/tabs/editProfile.ts index 8f222150..524982cf 100644 --- a/src/components/sidebarLeft/tabs/editProfile.ts +++ b/src/components/sidebarLeft/tabs/editProfile.ts @@ -40,7 +40,7 @@ export default class AppEditProfileTab extends SliderSuperTab { }; constructor(slider: SidebarSlider) { - super(slider); + super(slider, true); } protected init() { @@ -282,7 +282,6 @@ export default class AppEditProfileTab extends SliderSuperTab { }; onCloseAfterTimeout() { - this.nextBtn.classList.remove('is-visible'); - this.firstNameInputField.value = this.lastNameInputField.value = this.bioInputField.value = ''; + super.onCloseAfterTimeout(); } } \ No newline at end of file diff --git a/src/components/sidebarLeft/tabs/generalSettings.ts b/src/components/sidebarLeft/tabs/generalSettings.ts index fb4d1a67..ac865c0b 100644 --- a/src/components/sidebarLeft/tabs/generalSettings.ts +++ b/src/components/sidebarLeft/tabs/generalSettings.ts @@ -55,7 +55,7 @@ export class RangeSettingSelector { export default class AppGeneralSettingsTab extends SliderSuperTab { constructor(appSidebarLeft: AppSidebarLeft) { - super(appSidebarLeft); + super(appSidebarLeft, true); } init() { diff --git a/src/components/sidebarLeft/tabs/includedChats.ts b/src/components/sidebarLeft/tabs/includedChats.ts index bc2509a8..de85c3a4 100644 --- a/src/components/sidebarLeft/tabs/includedChats.ts +++ b/src/components/sidebarLeft/tabs/includedChats.ts @@ -1,6 +1,5 @@ -import { SliderTab, SliderSuperTab } from "../../slider"; +import SidebarSlider, { SliderSuperTab } from "../../slider"; import AppSelectPeers from "../../appSelectPeers"; -import appSidebarLeft, { AppSidebarLeft } from ".."; import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; import appPeersManager from "../../../lib/appManagers/appPeersManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager"; @@ -11,8 +10,10 @@ import ButtonIcon from "../../buttonIcon"; import { fastRaf } from "../../../helpers/schedulers"; import CheckboxField from "../../checkbox"; import Button from "../../button"; +import AppEditFolderTab from "./editFolder"; export default class AppIncludedChatsTab extends SliderSuperTab { + private editFolderTab: AppEditFolderTab; private confirmBtn: HTMLElement; private selector: AppSelectPeers; @@ -20,8 +21,8 @@ export default class AppIncludedChatsTab extends SliderSuperTab { private filter: DialogFilter; private originalFilter: DialogFilter; - constructor(appSidebarLeft: AppSidebarLeft) { - super(appSidebarLeft); + constructor(slider: SidebarSlider) { + super(slider, true); } init() { @@ -92,7 +93,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab { this.filter[this.type === 'included' ? 'include_peers' : 'exclude_peers'] = peers; //this.filter.pinned_peers = this.filter.pinned_peers.filter(peerId => this.filter.include_peers.includes(peerId)); - appSidebarLeft.editFolderTab.setFilter(this.filter, false); + this.editFolderTab.setFilter(this.filter, false); this.close(); }); } @@ -233,6 +234,8 @@ export default class AppIncludedChatsTab extends SliderSuperTab { (categories.querySelector(`[data-peer-id="${flag}"]`) as HTMLElement).click(); } } + + return super.onOpen(); } onSelectChange = (length: number) => { @@ -247,15 +250,18 @@ export default class AppIncludedChatsTab extends SliderSuperTab { this.selector.container.remove(); this.selector = null; } + + return super.onCloseAfterTimeout(); } /** * Do not ignore arguments! */ - public open(filter?: DialogFilter, type?: 'included' | 'excluded') { + public open(filter?: DialogFilter, type?: 'included' | 'excluded', editFolderTab?: AppIncludedChatsTab['editFolderTab']) { this.originalFilter = filter; this.filter = copy(this.originalFilter); this.type = type; + this.editFolderTab = editFolderTab; return super.open(); } diff --git a/src/components/sidebarLeft/tabs/newChannel.ts b/src/components/sidebarLeft/tabs/newChannel.ts index 125483bc..9295c342 100644 --- a/src/components/sidebarLeft/tabs/newChannel.ts +++ b/src/components/sidebarLeft/tabs/newChannel.ts @@ -5,6 +5,7 @@ import Button from "../../button"; import InputField from "../../inputField"; import { SliderSuperTab } from "../../slider"; import AvatarEdit from "../../avatarEdit"; +import AppAddMembersTab from "./addMembers"; export default class AppNewChannelTab extends SliderSuperTab { private uploadAvatar: () => Promise = null; @@ -15,7 +16,7 @@ export default class AppNewChannelTab extends SliderSuperTab { private avatarEdit: AvatarEdit; constructor(appSidebarLeft: AppSidebarLeft) { - super(appSidebarLeft); + super(appSidebarLeft, true); } protected init() { @@ -68,8 +69,8 @@ export default class AppNewChannelTab extends SliderSuperTab { }); } - appSidebarLeft.removeTabFromHistory(this.id); - appSidebarLeft.addMembersTab.open({ + appSidebarLeft.removeTabFromHistory(this); + new AppAddMembersTab(this.slider).open({ peerId: channelId, type: 'channel', skippable: true, @@ -92,5 +93,6 @@ export default class AppNewChannelTab extends SliderSuperTab { this.channelNameInputField.value = ''; this.channelDescriptionInputField.value = ''; this.nextBtn.disabled = false; + return super.onCloseAfterTimeout(); } -} \ No newline at end of file +} diff --git a/src/components/sidebarLeft/tabs/newGroup.ts b/src/components/sidebarLeft/tabs/newGroup.ts index bd2d9fe9..762f2745 100644 --- a/src/components/sidebarLeft/tabs/newGroup.ts +++ b/src/components/sidebarLeft/tabs/newGroup.ts @@ -18,7 +18,7 @@ export default class AppNewGroupTab extends SliderSuperTab { private groupNameInputField: InputField; constructor(appSidebarLeft: AppSidebarLeft) { - super(appSidebarLeft); + super(appSidebarLeft, true); } protected init() { @@ -57,7 +57,7 @@ export default class AppNewGroupTab extends SliderSuperTab { }); } - appSidebarLeft.removeTabFromHistory(this.id); + appSidebarLeft.removeTabFromHistory(this); appSidebarLeft.selectTab(0); }); }); diff --git a/src/components/sidebarLeft/tabs/privacyAndSecurity.ts b/src/components/sidebarLeft/tabs/privacyAndSecurity.ts index 68dffe83..c88dc26a 100644 --- a/src/components/sidebarLeft/tabs/privacyAndSecurity.ts +++ b/src/components/sidebarLeft/tabs/privacyAndSecurity.ts @@ -10,7 +10,7 @@ import AppTwoStepVerificationEnterPasswordTab from "./2fa/enterPassword"; export default class AppPrivacyAndSecurityTab extends SliderSuperTab { constructor(slider: SidebarSlider) { - super(slider); + super(slider, true); } protected init() { @@ -79,7 +79,9 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab { const numberVisibilityRow = rowsByKeys['inputPrivacyKeyPhoneNumber'] = new Row({ title: 'Who can see my phone number?', subtitle: 'My Contacts', - navigationTab: new AppPrivacyPhoneNumberTab(this.slider) + clickable: () => { + new AppPrivacyPhoneNumberTab(this.slider).open() + } }); const lastSeenTimeRow = rowsByKeys['inputPrivacyKeyStatusTimestamp'] = new Row({ @@ -121,4 +123,4 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab { container.append(numberVisibilityRow.container, lastSeenTimeRow.container, photoVisibilityRow.container, linkAccountRow.container, groupChatsAddRow.container); } } -} \ No newline at end of file +} diff --git a/src/components/sidebarLeft/tabs/settings.ts b/src/components/sidebarLeft/tabs/settings.ts index 6d5cadcb..4c403e23 100644 --- a/src/components/sidebarLeft/tabs/settings.ts +++ b/src/components/sidebarLeft/tabs/settings.ts @@ -1,10 +1,13 @@ import SidebarSlider, { SliderSuperTab } from "../../slider"; import AvatarElement from "../../avatar"; import apiManager from "../../../lib/mtproto/mtprotoworker"; -import appSidebarLeft, { AppSidebarLeft } from ".."; import appUsersManager from "../../../lib/appManagers/appUsersManager"; import ButtonMenuToggle from "../../buttonMenuToggle"; import Button from "../../button"; +import AppPrivacyAndSecurityTab from "./privacyAndSecurity"; +import AppGeneralSettingsTab from "./generalSettings"; +import AppEditProfileTab from "./editProfile"; +import AppChatFoldersTab from "./chatFolders"; //import AppMediaViewer from "../../appMediaViewerNew"; export default class AppSettingsTab extends SliderSuperTab { @@ -22,7 +25,7 @@ export default class AppSettingsTab extends SliderSuperTab { } = {} as any; constructor(slider: SidebarSlider) { - super(slider); + super(slider, true); } init() { @@ -110,20 +113,21 @@ export default class AppSettingsTab extends SliderSuperTab { }); */ this.buttons.edit.addEventListener('click', () => { - appSidebarLeft.editProfileTab.fillElements(); - appSidebarLeft.editProfileTab.open(); + const tab = new AppEditProfileTab(this.slider); + tab.fillElements(); + tab.open(); }); this.buttons.folders.addEventListener('click', () => { - appSidebarLeft.chatFoldersTab.open(); + new AppChatFoldersTab(this.slider).open(); }); this.buttons.general.addEventListener('click', () => { - appSidebarLeft.generalSettingsTab.open(); + new AppGeneralSettingsTab(this.slider as any).open(); }); this.buttons.privacy.addEventListener('click', () => { - appSidebarLeft.privacyAndSecurityTab.open(); + new AppPrivacyAndSecurityTab(this.slider).open(); }); } @@ -142,9 +146,6 @@ export default class AppSettingsTab extends SliderSuperTab { } this.fillElements(); + return super.onOpen(); } - - onClose() { - - } -} \ No newline at end of file +} diff --git a/src/components/sidebarRight/index.ts b/src/components/sidebarRight/index.ts index 58cc223e..eee758dc 100644 --- a/src/components/sidebarRight/index.ts +++ b/src/components/sidebarRight/index.ts @@ -34,13 +34,13 @@ export class AppSidebarRight extends SidebarSlider { constructor() { super({ sidebarEl: document.getElementById('column-right') as HTMLElement, - tabs: { - [AppSidebarRight.SLIDERITEMSIDS.sharedMedia]: sharedMediaTab, - [AppSidebarRight.SLIDERITEMSIDS.search]: searchTab, - [AppSidebarRight.SLIDERITEMSIDS.stickers]: stickersTab, - [AppSidebarRight.SLIDERITEMSIDS.pollResults]: pollResultsTab, - [AppSidebarRight.SLIDERITEMSIDS.gifs]: gifsTab - }, + tabs: new Map([ + [AppSidebarRight.SLIDERITEMSIDS.sharedMedia, sharedMediaTab], + [AppSidebarRight.SLIDERITEMSIDS.search, searchTab], + [AppSidebarRight.SLIDERITEMSIDS.stickers, stickersTab], + [AppSidebarRight.SLIDERITEMSIDS.pollResults, pollResultsTab], + [AppSidebarRight.SLIDERITEMSIDS.gifs, gifsTab] + ] as any[]), canHideFirst: true, navigationType: 'right' }); diff --git a/src/components/sidebarRight/tabs/stickers.ts b/src/components/sidebarRight/tabs/stickers.ts index c4d9eec1..e39449b2 100644 --- a/src/components/sidebarRight/tabs/stickers.ts +++ b/src/components/sidebarRight/tabs/stickers.ts @@ -95,7 +95,7 @@ export default class AppStickersTab implements SliderTab { `; const button = document.createElement('button'); - button.classList.add('btn-primary', 'sticker-set-button'); + button.classList.add('btn-primary', 'btn-color-primary', 'sticker-set-button'); button.innerText = set.installed_date ? 'Added' : 'Add'; // button.style.width = set.installed_date ? '68px' : '52px'; diff --git a/src/components/slider.ts b/src/components/slider.ts index 03cfa23b..d0030e95 100644 --- a/src/components/slider.ts +++ b/src/components/slider.ts @@ -1,93 +1,20 @@ import { attachClickEvent } from "../helpers/dom"; import { horizontalMenu } from "./horizontalMenu"; -import ButtonIcon from "./buttonIcon"; -import Scrollable from "./scrollable"; import { TransitionSlider } from "./transition"; import appNavigationController, { NavigationItem } from "./appNavigationController"; -import { isSafari } from "../helpers/userAgent"; - -export interface SliderTab { - onOpen?: () => void, - onOpenAfterTimeout?: () => void, - onClose?: () => void, - onCloseAfterTimeout?: () => void -} - -export class SliderSuperTab implements SliderTab { - public container: HTMLElement; - - public header: HTMLElement; - public closeBtn: HTMLElement; - public title: HTMLElement; - - public content: HTMLElement; - public scrollable: Scrollable; - - public id: number; - - constructor(protected slider: SidebarSlider, protected destroyable = false) { - this.container = document.createElement('div'); - this.container.classList.add('sidebar-slider-item'); - - // * Header - this.header = document.createElement('div'); - this.header.classList.add('sidebar-header'); - - this.closeBtn = ButtonIcon('arrow_back sidebar-close-button', {noRipple: true}); - this.title = document.createElement('div'); - this.title.classList.add('sidebar-header__title'); - this.header.append(this.closeBtn, this.title); - - // * Content - this.content = document.createElement('div'); - this.content.classList.add('sidebar-content'); - - this.scrollable = new Scrollable(this.content, undefined, undefined, true); - - this.container.append(this.header, this.content); - - this.id = this.slider.addTab(this); - } - - public close() { - return this.slider.closeTab(this.id); - } - - public async open(...args: any[]) { - if(this.init) { - const result = this.init(); - this.init = null; - await (result instanceof Promise ? result : Promise.resolve()); - } - - return this.slider.selectTab(this); - } - - protected init(): Promise | any { - - } - - // * fix incompability - public onOpen() { - - } - - public onCloseAfterTimeout() { - if(this.destroyable) { // ! WARNING, пока что это будет работать только с самой последней внутренней вкладкой ! - delete this.slider.tabs[this.id]; - this.container.remove(); - } - } -} +import SliderSuperTab, { SliderTab } from "./sliderTab"; const TRANSITION_TIME = 250; +export type {SliderTab}; +export {SliderSuperTab}; + export default class SidebarSlider { protected _selectTab: ReturnType; - public historyTabIds: number[] = []; + public historyTabIds: (number | SliderSuperTab)[] = []; // * key is any, since right sidebar is ugly nowz public tabsContainer: HTMLElement; public sidebarEl: HTMLElement; - public tabs: {[id: number]: SliderTab} = {}; + public tabs: Map; // * key is any, since right sidebar is ugly now private canHideFirst = false; private navigationType: NavigationItem['type'] @@ -102,6 +29,10 @@ export default class SidebarSlider { this[i] = options[i]; } + if(!this.tabs) { + this.tabs = new Map(); + } + this.tabsContainer = this.sidebarEl.querySelector('.sidebar-slider'); this._selectTab = TransitionSlider(this.tabsContainer, 'navigation', TRANSITION_TIME); if(!this.canHideFirst) { @@ -118,28 +49,30 @@ export default class SidebarSlider { // this.closeTab(); }; - public closeTab = (tabId?: number, animate?: boolean) => { - if(tabId !== undefined && this.historyTabIds[this.historyTabIds.length - 1] !== tabId) { + public closeTab = (id?: number | SliderSuperTab, animate?: boolean) => { + if(id !== undefined && this.historyTabIds[this.historyTabIds.length - 1] !== id) { return false; } //console.log('sidebar-close-button click:', this.historyTabIDs); const closingId = this.historyTabIds.pop(); // pop current this.onCloseTab(closingId, animate); - this._selectTab(this.historyTabIds[this.historyTabIds.length - 1] ?? (this.canHideFirst ? -1 : 0), animate); + + const tab = this.historyTabIds[this.historyTabIds.length - 1]; + this._selectTab(tab !== undefined ? (tab instanceof SliderSuperTab ? tab.container : tab) : (this.canHideFirst ? -1 : 0), animate); return true; }; public selectTab(id: number | SliderSuperTab): boolean { - if(id instanceof SliderSuperTab) { + /* if(id instanceof SliderSuperTab) { id = id.id; - } + } */ if(this.historyTabIds[this.historyTabIds.length - 1] === id) { return false; } - const tab = this.tabs[id]; + const tab: SliderTab = id instanceof SliderSuperTab ? id : this.tabs.get(id); if(tab) { if(tab.onOpen) { tab.onOpen(); @@ -163,17 +96,17 @@ export default class SidebarSlider { //} this.historyTabIds.push(id); - this._selectTab(id); + this._selectTab(id instanceof SliderSuperTab ? id.container : id); return true; } - public removeTabFromHistory(id: number) { + public removeTabFromHistory(id: number | SliderSuperTab) { this.historyTabIds.findAndSplice(i => i === id); this.onCloseTab(id, undefined); } - public onCloseTab(id: number, animate: boolean) { - let tab = this.tabs[id]; + public onCloseTab(id: number | SliderSuperTab, animate: boolean) { + const tab: SliderTab = id instanceof SliderSuperTab ? id : this.tabs.get(id); if(tab) { if(tab.onClose) { tab.onClose(); @@ -188,20 +121,12 @@ export default class SidebarSlider { } public addTab(tab: SliderSuperTab) { - let id: number; - if(tab.container.parentElement) { - id = Array.from(this.tabsContainer.children).findIndex(el => el === tab.container); - } else { - id = this.tabsContainer.childElementCount; + if(!tab.container.parentElement) { this.tabsContainer.append(tab.container); if(tab.closeBtn) { tab.closeBtn.addEventListener('click', this.onCloseBtnClick); } } - - this.tabs[id] = tab; - - return id; } } diff --git a/src/components/sliderTab.ts b/src/components/sliderTab.ts new file mode 100644 index 00000000..c50bcedf --- /dev/null +++ b/src/components/sliderTab.ts @@ -0,0 +1,77 @@ +import ButtonIcon from "./buttonIcon"; +import Scrollable from "./scrollable"; +import SidebarSlider from "./slider"; + +export interface SliderTab { + onOpen?: () => void, + onOpenAfterTimeout?: () => void, + onClose?: () => void, + onCloseAfterTimeout?: () => void +} + +export default class SliderSuperTab implements SliderTab { + public container: HTMLElement; + + public header: HTMLElement; + public closeBtn: HTMLElement; + public title: HTMLElement; + + public content: HTMLElement; + public scrollable: Scrollable; + + constructor(protected slider: SidebarSlider, protected destroyable = false) { + this.container = document.createElement('div'); + this.container.classList.add('sidebar-slider-item'); + + // * Header + this.header = document.createElement('div'); + this.header.classList.add('sidebar-header'); + + this.closeBtn = ButtonIcon('arrow_back sidebar-close-button', {noRipple: true}); + this.title = document.createElement('div'); + this.title.classList.add('sidebar-header__title'); + this.header.append(this.closeBtn, this.title); + + // * Content + this.content = document.createElement('div'); + this.content.classList.add('sidebar-content'); + + this.scrollable = new Scrollable(this.content, undefined, undefined, true); + + this.container.append(this.header, this.content); + + this.slider.addTab(this); + } + + public close() { + return this.slider.closeTab(this); + } + + public async open(...args: any[]) { + if(this.init) { + const result = this.init(); + this.init = null; + if(result instanceof Promise) { + await result; + } + } + + return this.slider.selectTab(this); + } + + protected init(): Promise | any { + + } + + // * fix incompability + public onOpen() { + + } + + public onCloseAfterTimeout() { + if(this.destroyable) { // ! WARNING, пока что это будет работать только с самой последней внутренней вкладкой ! + this.slider.tabs.delete(this); + this.container.remove(); + } + } +} diff --git a/src/components/transition.ts b/src/components/transition.ts index d23cb62a..32892d9c 100644 --- a/src/components/transition.ts +++ b/src/components/transition.ts @@ -96,7 +96,7 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction, } if(onTransitionEnd) { - onTransitionEnd(selectTab.prevId); + onTransitionEnd(selectTab.prevId()); } content.classList.remove('animating', 'backwards', 'disable-hover'); @@ -109,14 +109,15 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction, id = whichChild(id); } - if(id === self.prevId) return false; + const prevId = self.prevId(); + if(id === prevId) return false; //console.log('selectTab id:', id); const _from = from; const to = content.children[id] as HTMLElement; - if(!rootScope.settings.animationsEnabled || self.prevId === -1) { + if(!rootScope.settings.animationsEnabled || prevId === -1) { animate = false; } @@ -129,10 +130,9 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction, content.classList.remove('animating', 'backwards', 'disable-hover'); - self.prevId = id; from = to; - if(onTransitionEnd) onTransitionEnd(self.prevId); + if(onTransitionEnd) onTransitionEnd(id); return; } @@ -142,7 +142,7 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction, } content.classList.add('animating', 'disable-hover'); - const toRight = self.prevId < id; + const toRight = prevId < id; content.classList.toggle('backwards', !toRight); let onTransitionEndCallback: ReturnType; @@ -196,11 +196,11 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction, } } - self.prevId = id; from = to; } - selectTab.prevId = -1; + //selectTab.prevId = -1; + selectTab.prevId = () => from ? whichChild(from) : -1; return selectTab; }; diff --git a/src/helpers/bytes.ts b/src/helpers/bytes.ts index 21926158..d0c636eb 100644 --- a/src/helpers/bytes.ts +++ b/src/helpers/bytes.ts @@ -1,24 +1,23 @@ export function bytesToHex(bytes: ArrayLike) { bytes = bytes || []; - var arr = []; - for(var i = 0; i < bytes.length; i++) { + let arr: string[] = []; + for(let i = 0; i < bytes.length; ++i) { arr.push((bytes[i] < 16 ? '0' : '') + (bytes[i] || 0).toString(16)); } return arr.join(''); } export function bytesFromHex(hexString: string) { - var len = hexString.length, - i; - var start = 0; - var bytes = []; + const len = hexString.length; + let start = 0; + let bytes: number[] = []; - if(hexString.length % 2) { + if(len % 2) { // read 0x581 as 0x0581 bytes.push(parseInt(hexString.charAt(0), 16)); - start++; + ++start; } - for(i = start; i < len; i += 2) { + for(let i = start; i < len; i += 2) { bytes.push(parseInt(hexString.substr(i, 2), 16)); } @@ -26,24 +25,24 @@ export function bytesFromHex(hexString: string) { } export function bytesToBase64(bytes: number[] | Uint8Array) { - var mod3 - var result = '' + let mod3: number; + let result = ''; - for (var nLen = bytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) { - mod3 = nIdx % 3 - nUint24 |= bytes[nIdx] << (16 >>> mod3 & 24) - if (mod3 === 2 || nLen - nIdx === 1) { + for(let nLen = bytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; ++nIdx) { + mod3 = nIdx % 3; + nUint24 |= bytes[nIdx] << (16 >>> mod3 & 24); + if(mod3 === 2 || nLen - nIdx === 1) { result += String.fromCharCode( uint6ToBase64(nUint24 >>> 18 & 63), uint6ToBase64(nUint24 >>> 12 & 63), uint6ToBase64(nUint24 >>> 6 & 63), uint6ToBase64(nUint24 & 63) - ) - nUint24 = 0 + ); + nUint24 = 0; } } - return result.replace(/A(?=A$|$)/g, '=') + return result.replace(/A(?=A$|$)/g, '='); } export function uint6ToBase64(nUint6: number) { @@ -61,12 +60,12 @@ export function uint6ToBase64(nUint6: number) { } export function bytesCmp(bytes1: number[] | Uint8Array, bytes2: number[] | Uint8Array) { - var len = bytes1.length; + const len = bytes1.length; if(len !== bytes2.length) { return false; } - for(var i = 0; i < len; i++) { + for(let i = 0; i < len; ++i) { if(bytes1[i] !== bytes2[i]) { return false; } @@ -76,10 +75,10 @@ export function bytesCmp(bytes1: number[] | Uint8Array, bytes2: number[] | Uint8 } export function bytesXor(bytes1: number[] | Uint8Array, bytes2: number[] | Uint8Array) { - var len = bytes1.length; - var bytes = []; + const len = bytes1.length; + const bytes: number[] = []; - for (var i = 0; i < len; ++i) { + for(let i = 0; i < len; ++i) { bytes[i] = bytes1[i] ^ bytes2[i]; } @@ -111,11 +110,11 @@ export function convertToUint8Array(bytes: Uint8Array | number[]): Uint8Array { } export function bytesFromArrayBuffer(buffer: ArrayBuffer) { - var len = buffer.byteLength; - var byteView = new Uint8Array(buffer); - var bytes = []; + const len = buffer.byteLength; + const byteView = new Uint8Array(buffer); + const bytes: number[] = []; - for(var i = 0; i < len; ++i) { + for(let i = 0; i < len; ++i) { bytes[i] = byteView[i]; } @@ -123,9 +122,9 @@ export function bytesFromArrayBuffer(buffer: ArrayBuffer) { } export function bufferConcat(buffer1: any, buffer2: any) { - var l1 = buffer1.byteLength || buffer1.length; - var l2 = buffer2.byteLength || buffer2.length; - var tmp = new Uint8Array(l1 + l2); + const l1 = buffer1.byteLength || buffer1.length; + const l2 = buffer2.byteLength || buffer2.length; + const tmp = new Uint8Array(l1 + l2); tmp.set(buffer1 instanceof ArrayBuffer ? new Uint8Array(buffer1) : buffer1, 0); tmp.set(buffer2 instanceof ArrayBuffer ? new Uint8Array(buffer2) : buffer2, l1); @@ -136,7 +135,7 @@ export function bufferConcats(...args: any[]) { let length = 0; args.forEach(b => length += b.byteLength || b.length); - var tmp = new Uint8Array(length); + const tmp = new Uint8Array(length); let lastLength = 0; args.forEach(b => { @@ -148,8 +147,8 @@ export function bufferConcats(...args: any[]) { } export function bytesFromWordss(input: Uint32Array) { - var o = []; - for(var i = 0; i < input.length * 4; i++) { + const o: number[] = []; + for(let i = 0, length = input.length * 4; i < length; ++i) { o.push((input[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff); } @@ -161,12 +160,17 @@ export function bytesToWordss(input: ArrayBuffer | Uint8Array) { if(input instanceof ArrayBuffer) bytes = new Uint8Array(input); else bytes = input; - var len = bytes.length; - var words: number[] = []; - var i; - for(i = 0; i < len; i++) { + const words: number[] = []; + for(let i = 0, len = bytes.length; i < len; ++i) { words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8); } return new Uint32Array(words); -} \ No newline at end of file +} + +// * https://stackoverflow.com/a/52827031 +/* export const isBigEndian = (() => { + const array = new Uint8Array(4); + const view = new Uint32Array(array.buffer); + return !((view[0] = 1) & array[0]); +})(); */ diff --git a/src/helpers/random.ts b/src/helpers/random.ts index a52cdd00..51b601ee 100644 --- a/src/helpers/random.ts +++ b/src/helpers/random.ts @@ -5,4 +5,4 @@ export function nextRandomInt(maxValue: number) { export function randomLong() { return '' + nextRandomInt(0xFFFFFFFF) + nextRandomInt(0xFFFFFF); //return '' + parseInt(nextRandomInt(0xFFFFFFFF).toString(16) + nextRandomInt(0xFFFFFFFF).toString(16), 16); -} \ No newline at end of file +} diff --git a/src/index.hbs b/src/index.hbs index e4c655e1..98a84300 100644 --- a/src/index.hbs +++ b/src/index.hbs @@ -135,36 +135,6 @@ - - -
@@ -190,7 +160,6 @@
-

Bio

diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 5f6e51d4..9a7a162c 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -176,6 +176,7 @@ class ConnectionStatusComponent { export class AppDialogsManager { public _chatList = document.getElementById('dialogs') as HTMLUListElement; public chatList = this._chatList; + public chatListArchived: HTMLUListElement; public doms: {[peerId: number]: DialogDom} = {}; @@ -191,10 +192,7 @@ export class AppDialogsManager { public contextMenu = new DialogsContextMenu(); - public chatLists: {[filterId: number]: HTMLUListElement} = { - 0: this.chatList, - 1: appSidebarLeft.archivedTab.chatList - }; + public chatLists: {[filterId: number]: HTMLUListElement}; public filterId = 0; private folders: {[k in 'menu' | 'container' | 'menuScrollContainer']: HTMLElement} = { menu: document.getElementById('folders-tabs'), @@ -222,6 +220,14 @@ export class AppDialogsManager { private lastActiveElements: Set = new Set(); constructor() { + this.chatListArchived = document.createElement('ul'); + this.chatListArchived.id = 'dialogs-archived'; + + this.chatLists = { + 0: this.chatList, + 1: this.chatListArchived + }; + this.chatsPreloader = putPreloader(null, true); this.allUnreadCount = this.folders.menu.querySelector('.badge'); @@ -434,10 +440,10 @@ export class AppDialogsManager { positionElementByIndex(renderedFilter.container, this.folders.container, filter.orderIndex); }); - if(this.filterId) { + /* if(this.filterId) { const tabIndex = order.indexOf(this.filterId) + 1; selectTab.prevId = tabIndex; - } + } */ }); rootScope.on('peer_typings', (e) => { diff --git a/src/lib/crypto/crypto_utils.ts b/src/lib/crypto/crypto_utils.ts index 0195cf3f..8759a488 100644 --- a/src/lib/crypto/crypto_utils.ts +++ b/src/lib/crypto/crypto_utils.ts @@ -183,13 +183,13 @@ export function pqPrimeLeemon(what: any) { var x = new Array(minLen); var y = new Array(minLen); - for(i = 0; i < 3; i++) { + for(i = 0; i < 3; ++i) { q = (nextRandomInt(128) & 15) + 17; copyInt_(x, nextRandomInt(1000000000) + 1); copy_(y, x); lim = 1 << (i + 18); - for (j = 1; j < lim; j++) { + for (j = 1; j < lim; ++j) { ++it; copy_(a, x); copy_(b, x); diff --git a/src/lib/crypto/srp.ts b/src/lib/crypto/srp.ts index 5c92c442..27ea3dba 100644 --- a/src/lib/crypto/srp.ts +++ b/src/lib/crypto/srp.ts @@ -5,9 +5,12 @@ import {str2bigInt, isZero, import {logger, LogLevels} from '../logger'; import { AccountPassword, PasswordKdfAlgo } from "../../layer"; import { bufferConcats, bytesToHex, bytesFromHex, bufferConcat, bytesXor } from "../../helpers/bytes"; +//import { MOUNT_CLASS_TO } from "../../config/debug"; const log = logger('SRP', LogLevels.error); +//MOUNT_CLASS_TO && Object.assign(MOUNT_CLASS_TO, {str2bigInt, bigInt2str, int2bigInt}); + export async function makePasswordHash(password: string, client_salt: Uint8Array, server_salt: Uint8Array): Promise { let clientSaltString = ''; for(let i = 0; i < client_salt.length; i++) clientSaltString += String.fromCharCode(client_salt[i]); @@ -32,13 +35,12 @@ export async function makePasswordHash(password: string, client_salt: Uint8Array } export async function computeSRP(password: string, state: AccountPassword, isNew: boolean) { - console.log('computeSRP:', password, state, isNew); - - let algo = (state.current_algo || state.new_algo) as PasswordKdfAlgo.passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow; + const algo = (isNew ? state.new_algo : state.current_algo) as PasswordKdfAlgo.passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow; + //console.log('computeSRP:', password, state, isNew, algo); - let p = str2bigInt(bytesToHex(algo.p), 16); - let B = str2bigInt(bytesToHex(state.srp_B), 16); - let g = int2bigInt(algo.g, 32, 256); + const p = str2bigInt(bytesToHex(algo.p), 16); + const B = str2bigInt(bytesToHex(state.srp_B), 16); + const g = int2bigInt(algo.g, 32, 256); //log('p', bigInt2str(p, 16)); //log('B', bigInt2str(B, 16)); @@ -63,28 +65,41 @@ export async function computeSRP(password: string, state: AccountPassword, isNew //check_prime_and_good(algo.p, g); - let pw_hash = await makePasswordHash(password, new Uint8Array(algo.salt1), new Uint8Array(algo.salt2)); - let x = str2bigInt(bytesToHex(new Uint8Array(pw_hash)), 16); + const pw_hash = await makePasswordHash(password, new Uint8Array(algo.salt1), new Uint8Array(algo.salt2)); + const x = str2bigInt(bytesToHex(new Uint8Array(pw_hash)), 16); //log('computed pw_hash:', pw_hash, x, bytesToHex(new Uint8Array(pw_hash))); - var padArray = function(arr: any[], len: number, fill = 0) { + const padArray = function(arr: any[], len: number, fill = 0) { return Array(len).fill(fill).concat(arr).slice(-len); }; - let pForHash = padArray(bytesFromHex(bigInt2str(p, 16)), 256); - let gForHash = padArray(bytesFromHex(bigInt2str(g, 16)), 256); // like uint8array - let b_for_hash = padArray(bytesFromHex(bigInt2str(B, 16)), 256); + const pForHash = padArray(bytesFromHex(bigInt2str(p, 16)), 256); + const gForHash = padArray(bytesFromHex(bigInt2str(g, 16)), 256); // like uint8array + const b_for_hash = padArray(bytesFromHex(bigInt2str(B, 16)), 256); /* log(bytesToHex(pForHash)); log(bytesToHex(gForHash)); log(bytesToHex(b_for_hash)); */ - let g_x = powMod(g, x, p); + const v = powMod(g, x, p); + + const flipper = (arr: Uint8Array | number[]) => { + const out = new Uint8Array(arr.length); + for(let i = 0; i < arr.length; i += 4) { + out[i] = arr[i + 3]; + out[i + 1] = arr[i + 2]; + out[i + 2] = arr[i + 1]; + out[i + 3] = arr[i]; + } + + return out; + }; // * https://core.telegram.org/api/srp#setting-a-new-2fa-password if(isNew) { - return padArray(bytesFromHex(bigInt2str(g_x, 16)), 256); + const bytes = bytesFromHex(bigInt2str(v, 16)); + return padArray(/* (isBigEndian ? bytes.reverse() : bytes) */bytes, 256); } //log('g_x', bigInt2str(g_x, 16)); @@ -95,16 +110,16 @@ export async function computeSRP(password: string, state: AccountPassword, isNew //log('k', bigInt2str(k, 16)); // kg_x = (k * g_x) % p - let kg_x = mod(mult(k, g_x), p); + const k_v = mod(mult(k, v), p); // good //log('kg_x', bigInt2str(kg_x, 16)); - let is_good_mod_exp_first = (modexp: any, prime: any) => { - let diff = sub(prime, modexp); - let min_diff_bits_count = 2048 - 64; - let max_mod_exp_size = 256; + const is_good_mod_exp_first = (modexp: any, prime: any) => { + const diff = sub(prime, modexp); + const min_diff_bits_count = 2048 - 64; + const max_mod_exp_size = 256; if(negative(diff) || bitSize(diff) < min_diff_bits_count || bitSize(modexp) < min_diff_bits_count || @@ -113,19 +128,7 @@ export async function computeSRP(password: string, state: AccountPassword, isNew return true; }; - var flipper = (arr: Uint8Array | number[]) => { - let out = new Uint8Array(arr.length); - for(let i = 0; i < arr.length; i += 4) { - out[i] = arr[i + 3]; - out[i + 1] = arr[i + 2]; - out[i + 2] = arr[i + 1]; - out[i + 3] = arr[i]; - } - - return out; - }; - - let generate_and_check_random = async() => { + const generate_and_check_random = async() => { while(true) { const a = str2bigInt(bytesToHex(flipper(state.secure_random)), 16); //const a = str2bigInt('9153faef8f2bb6da91f6e5bc96bc00860a530a572a0f45aac0842b4602d711f8bda8d59fb53705e4ae3e31a3c4f0681955425f224297b8e9efd898fec22046debb7ba8a0bcf2be1ada7b100424ea318fdcef6ccfe6d7ab7d978c0eb76a807d4ab200eb767a22de0d828bc53f42c5a35c2df6e6ceeef9a3487aae8e9ef2271f2f6742e83b8211161fb1a0e037491ab2c2c73ad63c8bd1d739de1b523fe8d461270cedcf240de8da75f31be4933576532955041dc5770c18d3e75d0b357df9da4a5c8726d4fced87d15752400883dc57fa1937ac17608c5446c4774dcd123676d683ce3a1ab9f7e020ca52faafc99969822717c8e07ea383d5fb1a007ba0d170cb', 16); @@ -161,11 +164,11 @@ export async function computeSRP(password: string, state: AccountPassword, isNew log('B - kg_x', bigInt2str(sub(B, kg_x), 16)); */ let g_b; - if(!greater(B, kg_x)) { + if(!greater(B, k_v)) { //log('negative'); g_b = add(B, p); } else g_b = B; - g_b = mod(sub(g_b, kg_x), p); + g_b = mod(sub(g_b, k_v), p); /* let g_b = sub(B, kg_x); if(negative(g_b)) g_b = add(g_b, p); */ @@ -209,4 +212,4 @@ export async function computeSRP(password: string, state: AccountPassword, isNew return out; /* console.log(gForHash, pForHash, bForHash); */ -} \ No newline at end of file +} diff --git a/src/pages/pagePassword.ts b/src/pages/pagePassword.ts index 5a2749de..1a01fe48 100644 --- a/src/pages/pagePassword.ts +++ b/src/pages/pagePassword.ts @@ -2,7 +2,6 @@ import { putPreloader } from '../components/misc'; import mediaSizes from '../helpers/mediaSizes'; import { AccountPassword } from '../layer'; import appStateManager from '../lib/appManagers/appStateManager'; -import apiManager from '../lib/mtproto/mtprotoworker'; import passwordManager from '../lib/mtproto/passwordManager'; import Page from './page'; import pageIm from './pageIm'; @@ -10,12 +9,13 @@ import Button from '../components/button'; import PasswordInputField from '../components/passwordInputField'; import PasswordMonkey from '../components/monkeys/password'; import { ripple } from '../components/ripple'; +import RichTextProcessor from '../lib/richtextprocessor'; const TEST = false; let passwordInput: HTMLInputElement; let onFirstMount = (): Promise => { - const btnNext = Button('btn-primary', {text: 'NEXT'}); + const btnNext = Button('btn-primary btn-color-primary', {text: 'NEXT'}); const passwordInputField = new PasswordInputField({ label: 'Password', @@ -37,7 +37,7 @@ let onFirstMount = (): Promise => { return !TEST && passwordManager.getState().then(_state => { state = _state; - passwordInputField.label.innerText = state.hint ?? 'Password'; + passwordInputField.label.innerHTML = state.hint ? RichTextProcessor.wrapEmojiText(state.hint) : 'Password'; }); }; diff --git a/src/pages/pageSignIn.ts b/src/pages/pageSignIn.ts index 72aaaa96..cb36b456 100644 --- a/src/pages/pageSignIn.ts +++ b/src/pages/pageSignIn.ts @@ -297,7 +297,7 @@ let onFirstMount = () => { }); signedCheckboxField.input.checked = true; - btnNext = Button('btn-primary', {text: 'NEXT'}); + btnNext = Button('btn-primary btn-color-primary', {text: 'NEXT'}); btnNext.style.visibility = 'hidden'; btnNext.addEventListener('click', function(this: HTMLElement, e) { diff --git a/src/pages/pageSignUp.ts b/src/pages/pageSignUp.ts index 11c117eb..e3823529 100644 --- a/src/pages/pageSignUp.ts +++ b/src/pages/pageSignUp.ts @@ -64,7 +64,7 @@ const onFirstMount = () => import('../lib/appManagers/appProfileManager').then(i maxLength: 64 }); - const btnSignUp = Button('btn-primary'); + const btnSignUp = Button('btn-primary btn-color-primary'); btnSignUp.append('START MESSAGING'); inputWrapper.append(nameInputField.container, lastNameInputField.container, btnSignUp); diff --git a/src/scss/partials/_button.scss b/src/scss/partials/_button.scss index f70deec8..f40a351d 100644 --- a/src/scss/partials/_button.scss +++ b/src/scss/partials/_button.scss @@ -206,8 +206,6 @@ } .btn-primary { - background-color: $color-blue; - color: #fff; border-radius: $border-radius-medium; width: 100%; text-align: center; @@ -227,6 +225,7 @@ &-transparent { background-color: transparent; + @include hover() { background: hover-color($color-blue); } @@ -345,3 +344,12 @@ @include btn-hoverable(); } + +.btn-color-primary { + background: $color-blue; + color: #fff; + + /* .c-ripple__circle { + background-color: var(--color-blue-hover); + } */ +} diff --git a/src/scss/partials/_leftSidebar.scss b/src/scss/partials/_leftSidebar.scss index a90e8311..cdca1a3d 100644 --- a/src/scss/partials/_leftSidebar.scss +++ b/src/scss/partials/_leftSidebar.scss @@ -903,29 +903,69 @@ } .two-step-verification { + .sidebar-left-section-caption { // * main tab verified with mockup + text-align: center; + max-width: 342px; + margin-left: auto; + margin-right: auto; + font-size: 1rem; + line-height: 1.3125; + margin-bottom: 1.125rem; + } + + .btn-primary + .btn-primary { + margin-top: .125rem !important; + } + .media-sticker-wrapper { width: 168px; height: 168px; + margin: .625rem auto 1.1875rem; + } + + .input-wrapper .btn-primary:first-child:last-child { + margin-top: .25rem; } &-enter-password { .media-sticker-wrapper { + margin: 1.125rem auto 1.8125rem; width: 157px; height: 157px; } } + &-hint, &-email { + .btn-primary + .btn-primary { + margin-top: .5rem !important; + } + } + &-hint { .media-sticker-wrapper { - width: 120px; - height: 120px; + width: 160px; + height: 160px; + margin: .5rem auto 2.25rem; } } &-email { .media-sticker-wrapper { - width: 120px; - height: 120px; + width: 160px; + height: 160px; + margin: .5625rem auto 2.1875rem; + } + } + + &-set { + .media-sticker-wrapper { + width: 160px; + height: 160px; + margin: 1rem auto 1.3125rem; + + .rlottie, .rlottie-vector { + left: .625rem; + } } } } diff --git a/src/scss/style.scss b/src/scss/style.scss index 2de3bccc..c575615f 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -754,6 +754,12 @@ img.emoji { } } +.popup-disable-password, .popup-skip-email { + .popup-description { + max-width: 284px; + } +} + .grid { width: 100%; display: grid; diff --git a/src/tests/srp.test.ts b/src/tests/srp.test.ts index 8c4870cf..c5021211 100644 --- a/src/tests/srp.test.ts +++ b/src/tests/srp.test.ts @@ -22,7 +22,7 @@ test('2FA whole (with negative)', async() => { new_algo: null, new_secure_algo: null - }).then(res => { + }, false).then(res => { expect(res.srp_id).toEqual(srp_id); expect(res.A).toEqual(A); expect(res.M1).toEqual(M1);