Browse Source

Two-Step Verification layout

master
Eduard Kuzmenko 4 years ago
parent
commit
23fd0376ba
  1. 2
      src/components/chat/topbar.ts
  2. 4
      src/components/horizontalMenu.ts
  3. 2
      src/components/popups/avatar.ts
  4. 2
      src/components/popups/index.ts
  5. 2
      src/components/popups/stickers.ts
  6. 68
      src/components/sidebarLeft/index.ts
  7. 28
      src/components/sidebarLeft/tabs/2fa/email.ts
  8. 8
      src/components/sidebarLeft/tabs/2fa/enterPassword.ts
  9. 12
      src/components/sidebarLeft/tabs/2fa/hint.ts
  10. 15
      src/components/sidebarLeft/tabs/2fa/index.ts
  11. 61
      src/components/sidebarLeft/tabs/2fa/passwordSet.ts
  12. 4
      src/components/sidebarLeft/tabs/2fa/reEnterPassword.ts
  13. 10
      src/components/sidebarLeft/tabs/addMembers.ts
  14. 33
      src/components/sidebarLeft/tabs/archivedTab.ts
  15. 41
      src/components/sidebarLeft/tabs/chatFolders.ts
  16. 51
      src/components/sidebarLeft/tabs/contacts.ts
  17. 20
      src/components/sidebarLeft/tabs/editFolder.ts
  18. 5
      src/components/sidebarLeft/tabs/editProfile.ts
  19. 2
      src/components/sidebarLeft/tabs/generalSettings.ts
  20. 18
      src/components/sidebarLeft/tabs/includedChats.ts
  21. 8
      src/components/sidebarLeft/tabs/newChannel.ts
  22. 4
      src/components/sidebarLeft/tabs/newGroup.ts
  23. 6
      src/components/sidebarLeft/tabs/privacyAndSecurity.ts
  24. 23
      src/components/sidebarLeft/tabs/settings.ts
  25. 14
      src/components/sidebarRight/index.ts
  26. 2
      src/components/sidebarRight/tabs/stickers.ts
  27. 121
      src/components/slider.ts
  28. 77
      src/components/sliderTab.ts
  29. 16
      src/components/transition.ts
  30. 76
      src/helpers/bytes.ts
  31. 31
      src/index.hbs
  32. 18
      src/lib/appManagers/appDialogsManager.ts
  33. 4
      src/lib/crypto/crypto_utils.ts
  34. 71
      src/lib/crypto/srp.ts
  35. 6
      src/pages/pagePassword.ts
  36. 2
      src/pages/pageSignIn.ts
  37. 2
      src/pages/pageSignUp.ts
  38. 12
      src/scss/partials/_button.scss
  39. 48
      src/scss/partials/_leftSidebar.scss
  40. 6
      src/scss/style.scss
  41. 2
      src/tests/srp.test.ts

2
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.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.btnJoin.append('SUBSCRIBE');
this.btnPinned = ButtonIcon('pinlist'); this.btnPinned = ButtonIcon('pinlist');

4
src/components/horizontalMenu.ts

@ -32,7 +32,8 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
animate = false; animate = false;
} }
if(target.classList.contains('active') || id === selectTab.prevId) { const prevId = selectTab.prevId();
if(target.classList.contains('active') || id === prevId) {
return false; return false;
} }
@ -42,7 +43,6 @@ export function horizontalMenu(tabs: HTMLElement, content: HTMLElement, onClick?
prev && prev.classList.remove('active'); prev && prev.classList.remove('active');
}); });
const prevId = selectTab.prevId;
// stripe from ZINCHUK // stripe from ZINCHUK
if(useStripe && prevId !== -1 && animate) { if(useStripe && prevId !== -1 && animate) {
fastRaf(() => { fastRaf(() => {

2
src/components/popups/avatar.ts

@ -66,7 +66,7 @@ export default class PopupAvatar extends PopupElement {
}, false); }, false);
this.btnSubmit = document.createElement('button'); 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); ripple(this.btnSubmit);
this.btnSubmit.addEventListener('click', () => { this.btnSubmit.addEventListener('click', () => {
this.cropper.crop(); this.cropper.crop();

2
src/components/popups/index.ts

@ -64,7 +64,7 @@ export default class PopupElement {
if(options.withConfirm) { if(options.withConfirm) {
this.btnConfirm = document.createElement('button'); 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.btnConfirm.innerText = options.withConfirm;
this.header.append(this.btnConfirm); this.header.append(this.btnConfirm);
ripple(this.btnConfirm); ripple(this.btnConfirm);

2
src/components/popups/stickers.ts

@ -97,7 +97,7 @@ export default class PopupStickers extends PopupElement {
this.h6.innerHTML = RichTextProcessor.wrapEmojiText(set.set.title); this.h6.innerHTML = RichTextProcessor.wrapEmojiText(set.set.title);
!set.set.installed_date ? this.stickersFooter.classList.add('add') : this.stickersFooter.classList.remove('add'); !set.set.installed_date ? this.stickersFooter.classList.add('add') : this.stickersFooter.classList.remove('add');
this.stickersFooter.innerHTML = set.set.hasOwnProperty('installed_date') ? '<div style="cursor: pointer; margin: 0 auto; width: 150px;">Remove stickers</div>' : `<button class="btn-primary">ADD ${set.set.count} STICKERS</button>`; this.stickersFooter.innerHTML = set.set.hasOwnProperty('installed_date') ? '<div style="cursor: pointer; margin: 0 auto; width: 150px;">Remove stickers</div>' : `<button class="btn-primary btn-color-primary">ADD ${set.set.count} STICKERS</button>`;
this.stickersFooter.addEventListener('click', this.onFooterClick); this.stickersFooter.addEventListener('click', this.onFooterClick);

68
src/components/sidebarLeft/index.ts

@ -13,33 +13,18 @@ import Scrollable, { ScrollableX } from "../scrollable";
import InputSearch from "../inputSearch"; import InputSearch from "../inputSearch";
import SidebarSlider from "../slider"; import SidebarSlider from "../slider";
import { TransitionSlider } from "../transition"; 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 AppNewGroupTab from "./tabs/newGroup";
import AppSettingsTab from "./tabs/settings";
import appMessagesManager from "../../lib/appManagers/appMessagesManager"; import appMessagesManager from "../../lib/appManagers/appMessagesManager";
import apiManagerProxy from "../../lib/mtproto/mtprotoworker";
import AppSearchSuper from "../appSearchSuper."; import AppSearchSuper from "../appSearchSuper.";
import { DateData, fillTipDates } from "../../helpers/date"; import { DateData, fillTipDates } from "../../helpers/date";
import AppGeneralSettingsTab from "./tabs/generalSettings";
import AppPrivacyAndSecurityTab from "./tabs/privacyAndSecurity";
import { MOUNT_CLASS_TO } from "../../config/debug"; import { MOUNT_CLASS_TO } from "../../config/debug";
import AppSettingsTab from "./tabs/settings";
const contactsTab = new AppContactsTab(); import AppNewChannelTab from "./tabs/newChannel";
const archivedTab = new AppArchivedTab(); import AppContactsTab from "./tabs/contacts";
import AppArchivedTab from "./tabs/archivedTab";
import AppAddMembersTab from "./tabs/addMembers";
export class AppSidebarLeft extends SidebarSlider { export class AppSidebarLeft extends SidebarSlider {
public static SLIDERITEMSIDS = {
archived: 1,
contacts: 2
};
private toolsBtn: HTMLButtonElement; private toolsBtn: HTMLButtonElement;
private backBtn: HTMLButtonElement; private backBtn: HTMLButtonElement;
//private searchInput = document.getElementById('global-search') as HTMLInputElement; //private searchInput = document.getElementById('global-search') as HTMLInputElement;
@ -63,19 +48,6 @@ export class AppSidebarLeft extends SidebarSlider {
privateChat: HTMLButtonElement, privateChat: HTMLButtonElement,
} = {} as any; } = {} 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 log = logger('SL');
private searchGroups: {[k in 'contacts' | 'globalContacts' | 'messages' | 'people' | 'recent']: SearchGroup} = {} as any; private searchGroups: {[k in 'contacts' | 'globalContacts' | 'messages' | 'people' | 'recent']: SearchGroup} = {} as any;
@ -87,11 +59,6 @@ export class AppSidebarLeft extends SidebarSlider {
navigationType: 'left' navigationType: 'left'
}); });
Object.assign(this.tabs, {
[AppSidebarLeft.SLIDERITEMSIDS.archived]: archivedTab,
[AppSidebarLeft.SLIDERITEMSIDS.contacts]: contactsTab
});
//this._selectTab(0); // make first tab as default //this._selectTab(0); // make first tab as default
this.inputSearch = new InputSearch('Telegram Search'); 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.toolsBtn = this.sidebarEl.querySelector('.sidebar-tools-button') as HTMLButtonElement;
this.backBtn = this.sidebarEl.querySelector('.sidebar-back-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.menuEl = this.toolsBtn.querySelector('.btn-menu');
this.newBtnMenu = this.sidebarEl.querySelector('#new-menu'); this.newBtnMenu = this.sidebarEl.querySelector('#new-menu');
@ -132,29 +86,29 @@ export class AppSidebarLeft extends SidebarSlider {
}); });
attachClickEvent(this.buttons.archived, (e) => { attachClickEvent(this.buttons.archived, (e) => {
this.selectTab(AppSidebarLeft.SLIDERITEMSIDS.archived); new AppArchivedTab(this).open();
}); });
attachClickEvent(this.buttons.contacts, (e) => { attachClickEvent(this.buttons.contacts, (e) => {
this.contactsTab.openContacts(); new AppContactsTab(this).open();
}); });
attachClickEvent(this.buttons.settings, (e) => { attachClickEvent(this.buttons.settings, (e) => {
this.settingsTab.open(); new AppSettingsTab(this).open();
}); });
attachClickEvent(this.newButtons.channel, (e) => { attachClickEvent(this.newButtons.channel, (e) => {
this.newChannelTab.open(); new AppNewChannelTab(this).open();
}); });
[this.newButtons.group, this.buttons.newGroup].forEach(btn => { [this.newButtons.group, this.buttons.newGroup].forEach(btn => {
attachClickEvent(btn, (e) => { attachClickEvent(btn, (e) => {
this.addMembersTab.open({ new AppAddMembersTab(this).open({
peerId: 0, peerId: 0,
type: 'chat', type: 'chat',
skippable: false, skippable: false,
takeOut: (peerIds) => { takeOut: (peerIds) => {
this.newGroupTab.open(peerIds); new AppNewGroupTab(this).open(peerIds);
}, },
title: 'Add Members', title: 'Add Members',
placeholder: 'Add People...' placeholder: 'Add People...'

28
src/components/sidebarLeft/tabs/2fa/email.ts

@ -9,6 +9,7 @@ import { attachClickEvent } from "../../../../helpers/dom";
import PopupConfirmAction from "../../../popups/confirmAction"; import PopupConfirmAction from "../../../popups/confirmAction";
import { putPreloader } from "../../../misc"; import { putPreloader } from "../../../misc";
import passwordManager from "../../../../lib/mtproto/passwordManager"; import passwordManager from "../../../../lib/mtproto/passwordManager";
import AppTwoStepVerificationSetTab from "./passwordSet";
export default class AppTwoStepVerificationEmailTab extends SliderSuperTab { export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
public inputField: InputField; public inputField: InputField;
@ -22,7 +23,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
} }
protected init() { 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'; this.title.innerHTML = 'Recovery Email';
const section = new SettingSection({ const section = new SettingSection({
@ -34,17 +35,21 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
const doc = appStickersManager.getAnimatedEmojiSticker(emoji); const doc = appStickersManager.getAnimatedEmojiSticker(emoji);
const stickerContainer = document.createElement('div'); const stickerContainer = document.createElement('div');
if(doc) {
wrapSticker({ wrapSticker({
doc, doc,
div: stickerContainer, div: stickerContainer,
loop: false, loop: false,
play: true, play: true,
width: 168, width: 160,
height: 168, height: 160,
emoji emoji
}).then(() => { }).then(() => {
// this.animation = player; // this.animation = player;
}); });
} else {
stickerContainer.classList.add('media-sticker-wrapper');
}
section.content.append(stickerContainer); section.content.append(stickerContainer);
@ -58,9 +63,13 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
label: 'Recovery Email' 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 btnSkip = Button('btn-primary btn-primary-transparent primary', {text: 'SKIP'});
const goNext = () => {
new AppTwoStepVerificationSetTab(this.slider).open();
};
attachClickEvent(btnSkip, (e) => { attachClickEvent(btnSkip, (e) => {
const popup = new PopupConfirmAction('popup-skip-email', [{ const popup = new PopupConfirmAction('popup-skip-email', [{
text: 'CANCEL', text: 'CANCEL',
@ -68,20 +77,25 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
}, { }, {
text: 'SKIP', text: 'SKIP',
callback: () => { 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); putPreloader(btnSkip);
passwordManager.updateSettings({ passwordManager.updateSettings({
hint: this.hint, hint: this.hint,
currentPassword: this.plainPassword, currentPassword: this.plainPassword,
newPassword: this.newPassword newPassword: this.newPassword
}).then(() => { }).then(() => {
goNext();
}, (err) => {
btnContinue.removeAttribute('disabled');
btnSkip.removeAttribute('disabled');
}); });
}, },
isDanger: true, isDanger: true,
}], { }], {
title: 'Warning', title: 'Warning',
text: 'No, seriously.<br/><br/>If you forget your password, you will<br/>lose access to your Telegram account.<br/>There will be no way to restore it.' text: 'No, seriously.<br/><br/>If you forget your password, you will lose access to your Telegram account. There will be no way to restore it.'
}); });
popup.show(); popup.show();

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

@ -3,6 +3,7 @@ import { SettingSection } from "../..";
import { attachClickEvent, cancelEvent } from "../../../../helpers/dom"; import { attachClickEvent, cancelEvent } from "../../../../helpers/dom";
import { AccountPassword } from "../../../../layer"; import { AccountPassword } from "../../../../layer";
import passwordManager from "../../../../lib/mtproto/passwordManager"; import passwordManager from "../../../../lib/mtproto/passwordManager";
import RichTextProcessor from "../../../../lib/richtextprocessor";
import Button from "../../../button"; import Button from "../../../button";
import { putPreloader } from "../../../misc"; import { putPreloader } from "../../../misc";
import PasswordMonkey from "../../../monkeys/password"; import PasswordMonkey from "../../../monkeys/password";
@ -22,7 +23,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT
protected init() { protected init() {
const isNew = !this.state.pFlags.has_password || this.plainPassword; 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'; this.title.innerHTML = isNew ? 'Enter a Password' : 'Enter your Password';
const section = new SettingSection({ const section = new SettingSection({
@ -40,7 +41,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT
const monkey = new PasswordMonkey(passwordInputField, 157); const monkey = new PasswordMonkey(passwordInputField, 157);
monkey.load(); monkey.load();
const btnContinue = Button('btn-primary', {text: 'CONTINUE'}); const btnContinue = Button('btn-primary btn-color-primary', {text: 'CONTINUE'});
inputWrapper.append(passwordInputField.container, btnContinue); inputWrapper.append(passwordInputField.container, btnContinue);
section.content.append(monkey.container, inputWrapper); section.content.append(monkey.container, inputWrapper);
@ -80,7 +81,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT
return passwordManager.getState().then(_state => { return passwordManager.getState().then(_state => {
this.state = _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.state = this.state;
tab.plainPassword = plainPassword; tab.plainPassword = plainPassword;
tab.open(); tab.open();
this.slider.removeTabFromHistory(this);
} }
}, (err) => { }, (err) => {
btnContinue.removeAttribute('disabled'); btnContinue.removeAttribute('disabled');

12
src/components/sidebarLeft/tabs/2fa/hint.ts

@ -19,7 +19,7 @@ export default class AppTwoStepVerificationHintTab extends SliderSuperTab {
} }
protected init() { 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'; this.title.innerHTML = 'Password Hint';
const section = new SettingSection({ const section = new SettingSection({
@ -31,17 +31,21 @@ export default class AppTwoStepVerificationHintTab extends SliderSuperTab {
const doc = appStickersManager.getAnimatedEmojiSticker(emoji); const doc = appStickersManager.getAnimatedEmojiSticker(emoji);
const stickerContainer = document.createElement('div'); const stickerContainer = document.createElement('div');
if(doc) {
wrapSticker({ wrapSticker({
doc, doc,
div: stickerContainer, div: stickerContainer,
loop: false, loop: false,
play: true, play: true,
width: 168, width: 160,
height: 168, height: 160,
emoji emoji
}).then(() => { }).then(() => {
// this.animation = player; // this.animation = player;
}); });
} else {
stickerContainer.classList.add('media-sticker-wrapper');
}
section.content.append(stickerContainer); section.content.append(stickerContainer);
@ -72,7 +76,7 @@ export default class AppTwoStepVerificationHintTab extends SliderSuperTab {
tab.open(); 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'}); const btnSkip = Button('btn-primary btn-primary-transparent primary', {text: 'SKIP'});
attachClickEvent(btnContinue, (e) => goNext(e, true)); attachClickEvent(btnContinue, (e) => goNext(e, true));

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

@ -30,6 +30,7 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab {
const doc = appStickersManager.getAnimatedEmojiSticker(emoji); const doc = appStickersManager.getAnimatedEmojiSticker(emoji);
const stickerContainer = document.createElement('div'); const stickerContainer = document.createElement('div');
if(doc) {
wrapSticker({ wrapSticker({
doc, doc,
div: stickerContainer, div: stickerContainer,
@ -41,12 +42,15 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab {
}).then(() => { }).then(() => {
// this.animation = player; // this.animation = player;
}); });
} else {
stickerContainer.classList.add('media-sticker-wrapper');
}
section.content.append(stickerContainer); section.content.append(stickerContainer);
const c = section.generateContentElement(); const c = section.generateContentElement();
if(this.state.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'; 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 btnChangePassword = Button('btn-primary btn-transparent', {icon: 'edit', text: 'Change Password'});
const btnDisablePassword = Button('btn-primary btn-transparent', {icon: 'passwordoff', text: 'Turn Password Off'}); const btnDisablePassword = Button('btn-primary btn-transparent', {icon: 'passwordoff', text: 'Turn Password Off'});
@ -78,8 +82,13 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab {
} else { } 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.'; 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'}); const inputWrapper = document.createElement('div');
c.append(btnSetPassword); 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) => { attachClickEvent(btnSetPassword, (e) => {
const tab = new AppTwoStepVerificationEnterPasswordTab(this.slider); const tab = new AppTwoStepVerificationEnterPasswordTab(this.slider);

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

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

@ -19,7 +19,7 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe
} }
protected init() { 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'; this.title.innerHTML = 'Re-Enter your Password';
const section = new SettingSection({ const section = new SettingSection({
@ -37,7 +37,7 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe
const monkey = new TrackingMonkey(passwordInputField, 157); const monkey = new TrackingMonkey(passwordInputField, 157);
monkey.load(); monkey.load();
const btnContinue = Button('btn-primary', {text: 'CONTINUE'}); const btnContinue = Button('btn-primary btn-color-primary', {text: 'CONTINUE'});
inputWrapper.append(passwordInputField.container, btnContinue); inputWrapper.append(passwordInputField.container, btnContinue);
section.content.append(monkey.container, inputWrapper); section.content.append(monkey.container, inputWrapper);

10
src/components/sidebarLeft/tabs/addMembers.ts

@ -12,7 +12,7 @@ export default class AppAddMembersTab extends SliderSuperTab {
private skippable: boolean; private skippable: boolean;
constructor(slider: SidebarSlider) { constructor(slider: SidebarSlider) {
super(slider); super(slider, true);
} }
protected init() { 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: { public open(options: {
title: string, title: string,
placeholder: string, placeholder: string,
@ -67,7 +60,6 @@ export default class AppAddMembersTab extends SliderSuperTab {
this.takeOut = options.takeOut; this.takeOut = options.takeOut;
this.skippable = options.skippable; this.skippable = options.skippable;
this.onCloseAfterTimeout();
this.selector = new AppSelectPeers({ this.selector = new AppSelectPeers({
appendTo: this.content, appendTo: this.content,
onChange: this.skippable ? null : (length) => { onChange: this.skippable ? null : (length) => {

33
src/components/sidebarLeft/tabs/archivedTab.ts

@ -1,23 +1,27 @@
import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
import Scrollable from "../../scrollable"; import SidebarSlider, { SliderSuperTab } from "../../slider";
import { SliderTab } from "../../slider";
export default class AppArchivedTab implements SliderTab { export default class AppArchivedTab extends SliderSuperTab {
public container = document.getElementById('chats-archived-container') as HTMLDivElement;
public chatList = document.getElementById('dialogs-archived') as HTMLUListElement;
public scroll: Scrollable = null;
public loadedAll: boolean; public loadedAll: boolean;
public loadDialogsPromise: Promise<any>; public loadDialogsPromise: Promise<any>;
public wasFilterId: number; public wasFilterId: number;
constructor(slider: SidebarSlider) {
super(slider, true);
}
init() { init() {
this.scroll = new Scrollable(this.container, 'CLA', 500); this.container.id = 'chats-archived-container';
this.scroll.container.addEventListener('scroll', appDialogsManager.onChatsRegularScroll); this.title.innerHTML = 'Archived Chats';
this.scroll.setVirtualContainer(this.chatList);
this.scroll.onScrolledBottom = appDialogsManager.onChatsScroll; //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(); ///this.scroll.attachSentinels();
appDialogsManager.setListClickListener(this.chatList, null, true); appDialogsManager.setListClickListener(appDialogsManager.chatListArchived, null, true);
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
setTimeout(appDialogsManager.scroll.checkForTriggers, 0); setTimeout(appDialogsManager.scroll.checkForTriggers, 0);
@ -31,9 +35,11 @@ export default class AppArchivedTab implements SliderTab {
} }
this.wasFilterId = appDialogsManager.filterId; this.wasFilterId = appDialogsManager.filterId;
appDialogsManager.scroll = this.scroll; appDialogsManager.scroll = this.scrollable;
appDialogsManager.filterId = 1; appDialogsManager.filterId = 1;
appDialogsManager.onTabChange(); appDialogsManager.onTabChange();
return super.onOpen();
} }
// вообще, так делать нельзя, но нет времени чтобы переделать главный чатлист на слайд... // вообще, так делать нельзя, но нет времени чтобы переделать главный чатлист на слайд...
@ -48,6 +54,7 @@ export default class AppArchivedTab implements SliderTab {
} }
onCloseAfterTimeout() { onCloseAfterTimeout() {
this.chatList.innerHTML = ''; appDialogsManager.chatListArchived.innerHTML = '';
return super.onCloseAfterTimeout();
} }
} }

41
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 lottieLoader, { RLottiePlayer } from "../../../lib/lottieLoader";
import { RichTextProcessor } from "../../../lib/richtextprocessor"; import { RichTextProcessor } from "../../../lib/richtextprocessor";
import { cancelEvent, positionElementByIndex } from "../../../helpers/dom"; import { cancelEvent, positionElementByIndex } from "../../../helpers/dom";
import { ripple } from "../../ripple"; import { ripple } from "../../ripple";
import { toast } from "../../toast"; 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 { MyDialogFilter } from "../../../lib/storages/filters";
import type { AppPeersManager } from "../../../lib/appManagers/appPeersManager";
import type { AppSidebarLeft } from "..";
import type { DialogFilterSuggested, DialogFilter } from "../../../layer"; import type { DialogFilterSuggested, DialogFilter } from "../../../layer";
import type _rootScope from "../../../lib/rootScope"; import type _rootScope from "../../../lib/rootScope";
import Button from "../../button"; 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 { export default class AppChatFoldersTab extends SliderSuperTab {
public createFolderBtn: HTMLElement; public createFolderBtn: HTMLElement;
@ -22,8 +23,8 @@ export default class AppChatFoldersTab extends SliderSuperTab {
private filtersRendered: {[filterId: number]: HTMLElement} = {}; private filtersRendered: {[filterId: number]: HTMLElement} = {};
constructor(private appMessagesManager: AppMessagesManager, private appPeersManager: AppPeersManager, private appSidebarLeft: AppSidebarLeft, private apiManager: ApiManagerProxy, private rootScope: typeof _rootScope) { constructor(slider: SidebarSlider) {
super(appSidebarLeft); super(slider, true);
} }
private renderFolder(dialogFilter: DialogFilterSuggested | DialogFilter | MyDialogFilter, container?: HTMLElement, div: HTMLElement = document.createElement('div')) { 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; const filterId = filter.id;
if(!this.filtersRendered.hasOwnProperty(filter.id)) { if(!this.filtersRendered.hasOwnProperty(filter.id)) {
div.addEventListener('click', () => { 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'; else if(pFlags.exclude_archived) description += 'Unarchived';
d.push(description); d.push(description);
} else { } else {
const folder = this.appMessagesManager.dialogsStorage.getFolder(filter.id); const folder = appMessagesManager.dialogsStorage.getFolder(filter.id);
let chats = 0, channels = 0, groups = 0; let chats = 0, channels = 0, groups = 0;
for(const dialog of folder) { for(const dialog of folder) {
if(this.appPeersManager.isAnyGroup(dialog.peerId)) groups++; if(appPeersManager.isAnyGroup(dialog.peerId)) groups++;
else if(this.appPeersManager.isBroadcast(dialog.peerId)) channels++; else if(appPeersManager.isBroadcast(dialog.peerId)) channels++;
else chats++; else chats++;
} }
@ -109,7 +110,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
caption.classList.add('caption'); caption.classList.add('caption');
caption.innerHTML = `Create folders for different groups of chats<br>and quickly switch between them.`; caption.innerHTML = `Create folders for different groups of chats<br>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', text: 'Create Folder',
icon: 'add' icon: 'add'
}); });
@ -139,7 +140,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
if(Object.keys(this.filtersRendered).length >= 10) { if(Object.keys(this.filtersRendered).length >= 10) {
toast('Sorry, you can\'t create more folders.'); toast('Sorry, you can\'t create more folders.');
} else { } else {
this.appSidebarLeft.editFolderTab.open(); new AppEditFolderTab(this.slider).open();
} }
}); });
@ -153,13 +154,13 @@ export default class AppChatFoldersTab extends SliderSuperTab {
this.animation = player; this.animation = player;
}); });
this.appMessagesManager.filtersStorage.getDialogFilters().then(filters => { appMessagesManager.filtersStorage.getDialogFilters().then(filters => {
for(const filter of filters) { for(const filter of filters) {
this.renderFolder(filter, this.foldersContainer); this.renderFolder(filter, this.foldersContainer);
} }
}); });
this.rootScope.on('filter_update', (e) => { rootScope.on('filter_update', (e) => {
const filter = e; const filter = e;
if(this.filtersRendered.hasOwnProperty(filter.id)) { if(this.filtersRendered.hasOwnProperty(filter.id)) {
this.renderFolder(filter, null, this.filtersRendered[filter.id]); this.renderFolder(filter, null, this.filtersRendered[filter.id]);
@ -170,7 +171,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
this.getSuggestedFilters(); this.getSuggestedFilters();
}); });
this.rootScope.on('filter_delete', (e) => { rootScope.on('filter_delete', (e) => {
const filter = e; const filter = e;
if(this.filtersRendered.hasOwnProperty(filter.id)) { if(this.filtersRendered.hasOwnProperty(filter.id)) {
/* for(const suggested of this.suggestedFilters) { /* 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; const order = e;
order.forEach((filterId, idx) => { order.forEach((filterId, idx) => {
const div = this.filtersRendered[filterId]; const div = this.filtersRendered[filterId];
@ -197,14 +198,14 @@ export default class AppChatFoldersTab extends SliderSuperTab {
} }
private getSuggestedFilters() { private getSuggestedFilters() {
this.apiManager.invokeApi('messages.getSuggestedDialogFilters').then(suggestedFilters => { apiManager.invokeApi('messages.getSuggestedDialogFilters').then(suggestedFilters => {
this.suggestedContainer.style.display = suggestedFilters.length ? '' : 'none'; this.suggestedContainer.style.display = suggestedFilters.length ? '' : 'none';
Array.from(this.suggestedContainer.children).slice(1).forEach(el => el.remove()); Array.from(this.suggestedContainer.children).slice(1).forEach(el => el.remove());
suggestedFilters.forEach(filter => { suggestedFilters.forEach(filter => {
const div = this.renderFolder(filter); const div = this.renderFolder(filter);
const button = document.createElement('button'); const button = document.createElement('button');
button.classList.add('btn-primary'); button.classList.add('btn-primary', 'btn-color-primary');
button.innerText = 'Add'; button.innerText = 'Add';
div.append(button); div.append(button);
this.suggestedContainer.append(div); this.suggestedContainer.append(div);
@ -219,7 +220,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
button.setAttribute('disabled', 'true'); 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) { if(bool) {
div.remove(); div.remove();
} }

51
src/components/sidebarLeft/tabs/contacts.ts

@ -1,52 +1,52 @@
import { SliderTab } from "../../slider"; import SidebarSlider, { SliderSuperTab } from "../../slider";
import Scrollable from "../../scrollable";
import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
import appUsersManager from "../../../lib/appManagers/appUsersManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager";
import appPhotosManager from "../../../lib/appManagers/appPhotosManager"; import appPhotosManager from "../../../lib/appManagers/appPhotosManager";
import appSidebarLeft, { AppSidebarLeft } from "..";
import rootScope from "../../../lib/rootScope"; import rootScope from "../../../lib/rootScope";
import InputSearch from "../../inputSearch"; import InputSearch from "../../inputSearch";
// TODO: поиск по людям глобальный, если не нашло в контактах никого // TODO: поиск по людям глобальный, если не нашло в контактах никого
export default class AppContactsTab implements SliderTab { export default class AppContactsTab extends SliderSuperTab {
private container: HTMLElement;
private list: HTMLUListElement; private list: HTMLUListElement;
private scrollable: Scrollable;
private promise: Promise<void>; private promise: Promise<void>;
private inputSearch: InputSearch; private inputSearch: InputSearch;
private alive = true;
constructor(slider: SidebarSlider) {
super(slider, true);
}
init() { init() {
this.container = document.getElementById('contacts-container'); this.container.id = 'contacts-container';
this.list = this.container.querySelector('#contacts');
this.list = document.createElement('ul');
this.list.id = 'contacts';
this.list.classList.add('contacts-container');
appDialogsManager.setListClickListener(this.list, () => { appDialogsManager.setListClickListener(this.list, () => {
(this.container.querySelector('.sidebar-close-button') as HTMLElement).click(); (this.container.querySelector('.sidebar-close-button') as HTMLElement).click();
}, undefined, true); }, undefined, true);
this.scrollable = new Scrollable(this.list.parentElement);
this.inputSearch = new InputSearch('Search', (value) => { this.inputSearch = new InputSearch('Search', (value) => {
this.list.innerHTML = ''; this.list.innerHTML = '';
this.openContacts(value); this.openContacts(value);
}); });
this.container.firstElementChild.append(this.inputSearch.container); this.title.replaceWith(this.inputSearch.container);
this.scrollable.append(this.list);
// preload contacts // preload contacts
// appUsersManager.getContacts(); // appUsersManager.getContacts();
} }
// need to clear, and left 1 page for smooth slide onClose() {
public onClose() { this.alive = false;
/* // need to clear, and left 1 page for smooth slide
let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0; let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0;
(Array.from(this.list.children) as HTMLElement[]).slice(pageCount).forEach(el => el.remove()); (Array.from(this.list.children) as HTMLElement[]).slice(pageCount).forEach(el => el.remove()); */
}
public onCloseAfterTimeout() {
this.list.innerHTML = '';
this.inputSearch.value = '';
} }
public openContacts(query?: string) { public openContacts(query?: string) {
@ -55,18 +55,14 @@ export default class AppContactsTab implements SliderTab {
this.init = null; this.init = null;
} }
if(appSidebarLeft.historyTabIds.indexOf(AppSidebarLeft.SLIDERITEMSIDS.contacts) === -1) {
appSidebarLeft.selectTab(AppSidebarLeft.SLIDERITEMSIDS.contacts);
}
if(this.promise) return this.promise; if(this.promise) return this.promise;
this.scrollable.onScrolledBottom = null; this.scrollable.onScrolledBottom = null;
this.promise = appUsersManager.getContacts(query).then(_contacts => { this.promise = appUsersManager.getContacts(query).then(_contacts => {
this.promise = null; this.promise = null;
if(appSidebarLeft.historyTabIds[appSidebarLeft.historyTabIds.length - 1] !== AppSidebarLeft.SLIDERITEMSIDS.contacts) { if(!this.alive) {
console.warn('user closed contacts before it\'s loaded'); //console.warn('user closed contacts before it\'s loaded');
return; return;
} }
@ -118,4 +114,9 @@ export default class AppContactsTab implements SliderTab {
}; };
}); });
} }
public open() {
this.openContacts();
return super.open();
}
} }

20
src/components/sidebarLeft/tabs/editFolder.ts

@ -1,20 +1,18 @@
import appSidebarLeft, { AppSidebarLeft } from "..";
import { deepEqual, copy } from "../../../helpers/object"; import { deepEqual, copy } from "../../../helpers/object";
import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
import { MyDialogFilter as DialogFilter } from "../../../lib/storages/filters"; import { MyDialogFilter as DialogFilter } from "../../../lib/storages/filters";
import lottieLoader, { RLottiePlayer } from "../../../lib/lottieLoader"; import lottieLoader, { RLottiePlayer } from "../../../lib/lottieLoader";
import { parseMenuButtonsTo } from "../../misc";
import { ripple } from "../../ripple"; import { ripple } from "../../ripple";
import { SliderTab, SliderSuperTab } from "../../slider"; import SidebarSlider, { SliderSuperTab } from "../../slider";
import { toast } from "../../toast"; import { toast } from "../../toast";
import appMessagesManager from "../../../lib/appManagers/appMessagesManager"; import appMessagesManager from "../../../lib/appManagers/appMessagesManager";
import { attachClickEvent } from "../../../helpers/dom";
import InputField from "../../inputField"; import InputField from "../../inputField";
import RichTextProcessor from "../../../lib/richtextprocessor"; import RichTextProcessor from "../../../lib/richtextprocessor";
import ButtonIcon from "../../buttonIcon"; import ButtonIcon from "../../buttonIcon";
import ButtonMenuToggle from "../../buttonMenuToggle"; import ButtonMenuToggle from "../../buttonMenuToggle";
import { ButtonMenuItemOptions } from "../../buttonMenu"; import { ButtonMenuItemOptions } from "../../buttonMenu";
import Button from "../../button"; import Button from "../../button";
import AppIncludedChatsTab from "./includedChats";
const MAX_FOLDER_NAME_LENGTH = 12; const MAX_FOLDER_NAME_LENGTH = 12;
@ -36,8 +34,8 @@ export default class AppEditFolderTab extends SliderSuperTab {
private type: 'edit' | 'create'; private type: 'edit' | 'create';
constructor(appSidebarLeft: AppSidebarLeft) { constructor(slider: SidebarSlider) {
super(appSidebarLeft); super(slider, true);
} }
protected init() { protected init() {
@ -159,11 +157,11 @@ export default class AppEditFolderTab extends SliderSuperTab {
const excludedFlagsContainer = this.exclude_peers.querySelector('.folder-categories'); const excludedFlagsContainer = this.exclude_peers.querySelector('.folder-categories');
includedFlagsContainer.firstElementChild.addEventListener('click', () => { includedFlagsContainer.firstElementChild.addEventListener('click', () => {
appSidebarLeft.includedChatsTab.open(this.filter, 'included'); new AppIncludedChatsTab(this.slider).open(this.filter, 'included', this);
}); });
excludedFlagsContainer.firstElementChild.addEventListener('click', () => { excludedFlagsContainer.firstElementChild.addEventListener('click', () => {
appSidebarLeft.includedChatsTab.open(this.filter, 'excluded'); new AppIncludedChatsTab(this.slider).open(this.filter, 'excluded', this);
}); });
lottieLoader.loadAnimationFromURL({ lottieLoader.loadAnimationFromURL({
@ -228,10 +226,8 @@ export default class AppEditFolderTab extends SliderSuperTab {
if(this.animation) { if(this.animation) {
this.animation.restart(); this.animation.restart();
} }
}
onCloseAfterTimeout() { return super.onOpen();
Array.from(this.container.querySelectorAll('ul, .show-more')).forEach(el => el.remove());
} }
private onCreateOpen() { private onCreateOpen() {
@ -318,7 +314,7 @@ export default class AppEditFolderTab extends SliderSuperTab {
setFilter(filter: DialogFilter, firstTime: boolean) { setFilter(filter: DialogFilter, firstTime: boolean) {
// cleanup // cleanup
this.onCloseAfterTimeout(); Array.from(this.container.querySelectorAll('ul, .show-more')).forEach(el => el.remove());
if(firstTime) { if(firstTime) {
this.originalFilter = filter; this.originalFilter = filter;

5
src/components/sidebarLeft/tabs/editProfile.ts

@ -40,7 +40,7 @@ export default class AppEditProfileTab extends SliderSuperTab {
}; };
constructor(slider: SidebarSlider) { constructor(slider: SidebarSlider) {
super(slider); super(slider, true);
} }
protected init() { protected init() {
@ -282,7 +282,6 @@ export default class AppEditProfileTab extends SliderSuperTab {
}; };
onCloseAfterTimeout() { onCloseAfterTimeout() {
this.nextBtn.classList.remove('is-visible'); super.onCloseAfterTimeout();
this.firstNameInputField.value = this.lastNameInputField.value = this.bioInputField.value = '';
} }
} }

2
src/components/sidebarLeft/tabs/generalSettings.ts

@ -55,7 +55,7 @@ export class RangeSettingSelector {
export default class AppGeneralSettingsTab extends SliderSuperTab { export default class AppGeneralSettingsTab extends SliderSuperTab {
constructor(appSidebarLeft: AppSidebarLeft) { constructor(appSidebarLeft: AppSidebarLeft) {
super(appSidebarLeft); super(appSidebarLeft, true);
} }
init() { init() {

18
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 AppSelectPeers from "../../appSelectPeers";
import appSidebarLeft, { AppSidebarLeft } from "..";
import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
import appPeersManager from "../../../lib/appManagers/appPeersManager"; import appPeersManager from "../../../lib/appManagers/appPeersManager";
import appUsersManager from "../../../lib/appManagers/appUsersManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager";
@ -11,8 +10,10 @@ import ButtonIcon from "../../buttonIcon";
import { fastRaf } from "../../../helpers/schedulers"; import { fastRaf } from "../../../helpers/schedulers";
import CheckboxField from "../../checkbox"; import CheckboxField from "../../checkbox";
import Button from "../../button"; import Button from "../../button";
import AppEditFolderTab from "./editFolder";
export default class AppIncludedChatsTab extends SliderSuperTab { export default class AppIncludedChatsTab extends SliderSuperTab {
private editFolderTab: AppEditFolderTab;
private confirmBtn: HTMLElement; private confirmBtn: HTMLElement;
private selector: AppSelectPeers; private selector: AppSelectPeers;
@ -20,8 +21,8 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
private filter: DialogFilter; private filter: DialogFilter;
private originalFilter: DialogFilter; private originalFilter: DialogFilter;
constructor(appSidebarLeft: AppSidebarLeft) { constructor(slider: SidebarSlider) {
super(appSidebarLeft); super(slider, true);
} }
init() { init() {
@ -92,7 +93,7 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
this.filter[this.type === 'included' ? 'include_peers' : 'exclude_peers'] = peers; 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)); //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(); this.close();
}); });
} }
@ -233,6 +234,8 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
(categories.querySelector(`[data-peer-id="${flag}"]`) as HTMLElement).click(); (categories.querySelector(`[data-peer-id="${flag}"]`) as HTMLElement).click();
} }
} }
return super.onOpen();
} }
onSelectChange = (length: number) => { onSelectChange = (length: number) => {
@ -247,15 +250,18 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
this.selector.container.remove(); this.selector.container.remove();
this.selector = null; this.selector = null;
} }
return super.onCloseAfterTimeout();
} }
/** /**
* Do not ignore arguments! * 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.originalFilter = filter;
this.filter = copy(this.originalFilter); this.filter = copy(this.originalFilter);
this.type = type; this.type = type;
this.editFolderTab = editFolderTab;
return super.open(); return super.open();
} }

8
src/components/sidebarLeft/tabs/newChannel.ts

@ -5,6 +5,7 @@ import Button from "../../button";
import InputField from "../../inputField"; import InputField from "../../inputField";
import { SliderSuperTab } from "../../slider"; import { SliderSuperTab } from "../../slider";
import AvatarEdit from "../../avatarEdit"; import AvatarEdit from "../../avatarEdit";
import AppAddMembersTab from "./addMembers";
export default class AppNewChannelTab extends SliderSuperTab { export default class AppNewChannelTab extends SliderSuperTab {
private uploadAvatar: () => Promise<InputFile> = null; private uploadAvatar: () => Promise<InputFile> = null;
@ -15,7 +16,7 @@ export default class AppNewChannelTab extends SliderSuperTab {
private avatarEdit: AvatarEdit; private avatarEdit: AvatarEdit;
constructor(appSidebarLeft: AppSidebarLeft) { constructor(appSidebarLeft: AppSidebarLeft) {
super(appSidebarLeft); super(appSidebarLeft, true);
} }
protected init() { protected init() {
@ -68,8 +69,8 @@ export default class AppNewChannelTab extends SliderSuperTab {
}); });
} }
appSidebarLeft.removeTabFromHistory(this.id); appSidebarLeft.removeTabFromHistory(this);
appSidebarLeft.addMembersTab.open({ new AppAddMembersTab(this.slider).open({
peerId: channelId, peerId: channelId,
type: 'channel', type: 'channel',
skippable: true, skippable: true,
@ -92,5 +93,6 @@ export default class AppNewChannelTab extends SliderSuperTab {
this.channelNameInputField.value = ''; this.channelNameInputField.value = '';
this.channelDescriptionInputField.value = ''; this.channelDescriptionInputField.value = '';
this.nextBtn.disabled = false; this.nextBtn.disabled = false;
return super.onCloseAfterTimeout();
} }
} }

4
src/components/sidebarLeft/tabs/newGroup.ts

@ -18,7 +18,7 @@ export default class AppNewGroupTab extends SliderSuperTab {
private groupNameInputField: InputField; private groupNameInputField: InputField;
constructor(appSidebarLeft: AppSidebarLeft) { constructor(appSidebarLeft: AppSidebarLeft) {
super(appSidebarLeft); super(appSidebarLeft, true);
} }
protected init() { protected init() {
@ -57,7 +57,7 @@ export default class AppNewGroupTab extends SliderSuperTab {
}); });
} }
appSidebarLeft.removeTabFromHistory(this.id); appSidebarLeft.removeTabFromHistory(this);
appSidebarLeft.selectTab(0); appSidebarLeft.selectTab(0);
}); });
}); });

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

@ -10,7 +10,7 @@ import AppTwoStepVerificationEnterPasswordTab from "./2fa/enterPassword";
export default class AppPrivacyAndSecurityTab extends SliderSuperTab { export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
constructor(slider: SidebarSlider) { constructor(slider: SidebarSlider) {
super(slider); super(slider, true);
} }
protected init() { protected init() {
@ -79,7 +79,9 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTab {
const numberVisibilityRow = rowsByKeys['inputPrivacyKeyPhoneNumber'] = new Row({ const numberVisibilityRow = rowsByKeys['inputPrivacyKeyPhoneNumber'] = new Row({
title: 'Who can see my phone number?', title: 'Who can see my phone number?',
subtitle: 'My Contacts', subtitle: 'My Contacts',
navigationTab: new AppPrivacyPhoneNumberTab(this.slider) clickable: () => {
new AppPrivacyPhoneNumberTab(this.slider).open()
}
}); });
const lastSeenTimeRow = rowsByKeys['inputPrivacyKeyStatusTimestamp'] = new Row({ const lastSeenTimeRow = rowsByKeys['inputPrivacyKeyStatusTimestamp'] = new Row({

23
src/components/sidebarLeft/tabs/settings.ts

@ -1,10 +1,13 @@
import SidebarSlider, { SliderSuperTab } from "../../slider"; import SidebarSlider, { SliderSuperTab } from "../../slider";
import AvatarElement from "../../avatar"; import AvatarElement from "../../avatar";
import apiManager from "../../../lib/mtproto/mtprotoworker"; import apiManager from "../../../lib/mtproto/mtprotoworker";
import appSidebarLeft, { AppSidebarLeft } from "..";
import appUsersManager from "../../../lib/appManagers/appUsersManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager";
import ButtonMenuToggle from "../../buttonMenuToggle"; import ButtonMenuToggle from "../../buttonMenuToggle";
import Button from "../../button"; import Button from "../../button";
import AppPrivacyAndSecurityTab from "./privacyAndSecurity";
import AppGeneralSettingsTab from "./generalSettings";
import AppEditProfileTab from "./editProfile";
import AppChatFoldersTab from "./chatFolders";
//import AppMediaViewer from "../../appMediaViewerNew"; //import AppMediaViewer from "../../appMediaViewerNew";
export default class AppSettingsTab extends SliderSuperTab { export default class AppSettingsTab extends SliderSuperTab {
@ -22,7 +25,7 @@ export default class AppSettingsTab extends SliderSuperTab {
} = {} as any; } = {} as any;
constructor(slider: SidebarSlider) { constructor(slider: SidebarSlider) {
super(slider); super(slider, true);
} }
init() { init() {
@ -110,20 +113,21 @@ export default class AppSettingsTab extends SliderSuperTab {
}); */ }); */
this.buttons.edit.addEventListener('click', () => { this.buttons.edit.addEventListener('click', () => {
appSidebarLeft.editProfileTab.fillElements(); const tab = new AppEditProfileTab(this.slider);
appSidebarLeft.editProfileTab.open(); tab.fillElements();
tab.open();
}); });
this.buttons.folders.addEventListener('click', () => { this.buttons.folders.addEventListener('click', () => {
appSidebarLeft.chatFoldersTab.open(); new AppChatFoldersTab(this.slider).open();
}); });
this.buttons.general.addEventListener('click', () => { this.buttons.general.addEventListener('click', () => {
appSidebarLeft.generalSettingsTab.open(); new AppGeneralSettingsTab(this.slider as any).open();
}); });
this.buttons.privacy.addEventListener('click', () => { 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(); this.fillElements();
} return super.onOpen();
onClose() {
} }
} }

14
src/components/sidebarRight/index.ts

@ -34,13 +34,13 @@ export class AppSidebarRight extends SidebarSlider {
constructor() { constructor() {
super({ super({
sidebarEl: document.getElementById('column-right') as HTMLElement, sidebarEl: document.getElementById('column-right') as HTMLElement,
tabs: { tabs: new Map([
[AppSidebarRight.SLIDERITEMSIDS.sharedMedia]: sharedMediaTab, [AppSidebarRight.SLIDERITEMSIDS.sharedMedia, sharedMediaTab],
[AppSidebarRight.SLIDERITEMSIDS.search]: searchTab, [AppSidebarRight.SLIDERITEMSIDS.search, searchTab],
[AppSidebarRight.SLIDERITEMSIDS.stickers]: stickersTab, [AppSidebarRight.SLIDERITEMSIDS.stickers, stickersTab],
[AppSidebarRight.SLIDERITEMSIDS.pollResults]: pollResultsTab, [AppSidebarRight.SLIDERITEMSIDS.pollResults, pollResultsTab],
[AppSidebarRight.SLIDERITEMSIDS.gifs]: gifsTab [AppSidebarRight.SLIDERITEMSIDS.gifs, gifsTab]
}, ] as any[]),
canHideFirst: true, canHideFirst: true,
navigationType: 'right' navigationType: 'right'
}); });

2
src/components/sidebarRight/tabs/stickers.ts

@ -95,7 +95,7 @@ export default class AppStickersTab implements SliderTab {
`; `;
const button = document.createElement('button'); 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.innerText = set.installed_date ? 'Added' : 'Add';
// button.style.width = set.installed_date ? '68px' : '52px'; // button.style.width = set.installed_date ? '68px' : '52px';

121
src/components/slider.ts

@ -1,93 +1,20 @@
import { attachClickEvent } from "../helpers/dom"; import { attachClickEvent } from "../helpers/dom";
import { horizontalMenu } from "./horizontalMenu"; import { horizontalMenu } from "./horizontalMenu";
import ButtonIcon from "./buttonIcon";
import Scrollable from "./scrollable";
import { TransitionSlider } from "./transition"; import { TransitionSlider } from "./transition";
import appNavigationController, { NavigationItem } from "./appNavigationController"; import appNavigationController, { NavigationItem } from "./appNavigationController";
import { isSafari } from "../helpers/userAgent"; import SliderSuperTab, { SliderTab } from "./sliderTab";
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> | any {
}
// * fix incompability
public onOpen() {
}
public onCloseAfterTimeout() {
if(this.destroyable) { // ! WARNING, пока что это будет работать только с самой последней внутренней вкладкой !
delete this.slider.tabs[this.id];
this.container.remove();
}
}
}
const TRANSITION_TIME = 250; const TRANSITION_TIME = 250;
export type {SliderTab};
export {SliderSuperTab};
export default class SidebarSlider { export default class SidebarSlider {
protected _selectTab: ReturnType<typeof horizontalMenu>; protected _selectTab: ReturnType<typeof horizontalMenu>;
public historyTabIds: number[] = []; public historyTabIds: (number | SliderSuperTab)[] = []; // * key is any, since right sidebar is ugly nowz
public tabsContainer: HTMLElement; public tabsContainer: HTMLElement;
public sidebarEl: HTMLElement; public sidebarEl: HTMLElement;
public tabs: {[id: number]: SliderTab} = {}; public tabs: Map<any, SliderTab>; // * key is any, since right sidebar is ugly now
private canHideFirst = false; private canHideFirst = false;
private navigationType: NavigationItem['type'] private navigationType: NavigationItem['type']
@ -102,6 +29,10 @@ export default class SidebarSlider {
this[i] = options[i]; this[i] = options[i];
} }
if(!this.tabs) {
this.tabs = new Map();
}
this.tabsContainer = this.sidebarEl.querySelector('.sidebar-slider'); this.tabsContainer = this.sidebarEl.querySelector('.sidebar-slider');
this._selectTab = TransitionSlider(this.tabsContainer, 'navigation', TRANSITION_TIME); this._selectTab = TransitionSlider(this.tabsContainer, 'navigation', TRANSITION_TIME);
if(!this.canHideFirst) { if(!this.canHideFirst) {
@ -118,28 +49,30 @@ export default class SidebarSlider {
// this.closeTab(); // this.closeTab();
}; };
public closeTab = (tabId?: number, animate?: boolean) => { public closeTab = (id?: number | SliderSuperTab, animate?: boolean) => {
if(tabId !== undefined && this.historyTabIds[this.historyTabIds.length - 1] !== tabId) { if(id !== undefined && this.historyTabIds[this.historyTabIds.length - 1] !== id) {
return false; return false;
} }
//console.log('sidebar-close-button click:', this.historyTabIDs); //console.log('sidebar-close-button click:', this.historyTabIDs);
const closingId = this.historyTabIds.pop(); // pop current const closingId = this.historyTabIds.pop(); // pop current
this.onCloseTab(closingId, animate); 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; return true;
}; };
public selectTab(id: number | SliderSuperTab): boolean { public selectTab(id: number | SliderSuperTab): boolean {
if(id instanceof SliderSuperTab) { /* if(id instanceof SliderSuperTab) {
id = id.id; id = id.id;
} } */
if(this.historyTabIds[this.historyTabIds.length - 1] === id) { if(this.historyTabIds[this.historyTabIds.length - 1] === id) {
return false; return false;
} }
const tab = this.tabs[id]; const tab: SliderTab = id instanceof SliderSuperTab ? id : this.tabs.get(id);
if(tab) { if(tab) {
if(tab.onOpen) { if(tab.onOpen) {
tab.onOpen(); tab.onOpen();
@ -163,17 +96,17 @@ export default class SidebarSlider {
//} //}
this.historyTabIds.push(id); this.historyTabIds.push(id);
this._selectTab(id); this._selectTab(id instanceof SliderSuperTab ? id.container : id);
return true; return true;
} }
public removeTabFromHistory(id: number) { public removeTabFromHistory(id: number | SliderSuperTab) {
this.historyTabIds.findAndSplice(i => i === id); this.historyTabIds.findAndSplice(i => i === id);
this.onCloseTab(id, undefined); this.onCloseTab(id, undefined);
} }
public onCloseTab(id: number, animate: boolean) { public onCloseTab(id: number | SliderSuperTab, animate: boolean) {
let tab = this.tabs[id]; const tab: SliderTab = id instanceof SliderSuperTab ? id : this.tabs.get(id);
if(tab) { if(tab) {
if(tab.onClose) { if(tab.onClose) {
tab.onClose(); tab.onClose();
@ -188,20 +121,12 @@ export default class SidebarSlider {
} }
public addTab(tab: SliderSuperTab) { public addTab(tab: SliderSuperTab) {
let id: number; if(!tab.container.parentElement) {
if(tab.container.parentElement) {
id = Array.from(this.tabsContainer.children).findIndex(el => el === tab.container);
} else {
id = this.tabsContainer.childElementCount;
this.tabsContainer.append(tab.container); this.tabsContainer.append(tab.container);
if(tab.closeBtn) { if(tab.closeBtn) {
tab.closeBtn.addEventListener('click', this.onCloseBtnClick); tab.closeBtn.addEventListener('click', this.onCloseBtnClick);
} }
} }
this.tabs[id] = tab;
return id;
} }
} }

77
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> | any {
}
// * fix incompability
public onOpen() {
}
public onCloseAfterTimeout() {
if(this.destroyable) { // ! WARNING, пока что это будет работать только с самой последней внутренней вкладкой !
this.slider.tabs.delete(this);
this.container.remove();
}
}
}

16
src/components/transition.ts

@ -96,7 +96,7 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
} }
if(onTransitionEnd) { if(onTransitionEnd) {
onTransitionEnd(selectTab.prevId); onTransitionEnd(selectTab.prevId());
} }
content.classList.remove('animating', 'backwards', 'disable-hover'); content.classList.remove('animating', 'backwards', 'disable-hover');
@ -109,14 +109,15 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
id = whichChild(id); id = whichChild(id);
} }
if(id === self.prevId) return false; const prevId = self.prevId();
if(id === prevId) return false;
//console.log('selectTab id:', id); //console.log('selectTab id:', id);
const _from = from; const _from = from;
const to = content.children[id] as HTMLElement; const to = content.children[id] as HTMLElement;
if(!rootScope.settings.animationsEnabled || self.prevId === -1) { if(!rootScope.settings.animationsEnabled || prevId === -1) {
animate = false; animate = false;
} }
@ -129,10 +130,9 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
content.classList.remove('animating', 'backwards', 'disable-hover'); content.classList.remove('animating', 'backwards', 'disable-hover');
self.prevId = id;
from = to; from = to;
if(onTransitionEnd) onTransitionEnd(self.prevId); if(onTransitionEnd) onTransitionEnd(id);
return; return;
} }
@ -142,7 +142,7 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
} }
content.classList.add('animating', 'disable-hover'); content.classList.add('animating', 'disable-hover');
const toRight = self.prevId < id; const toRight = prevId < id;
content.classList.toggle('backwards', !toRight); content.classList.toggle('backwards', !toRight);
let onTransitionEndCallback: ReturnType<TransitionFunction>; let onTransitionEndCallback: ReturnType<TransitionFunction>;
@ -196,11 +196,11 @@ const Transition = (content: HTMLElement, animationFunction: TransitionFunction,
} }
} }
self.prevId = id;
from = to; from = to;
} }
selectTab.prevId = -1; //selectTab.prevId = -1;
selectTab.prevId = () => from ? whichChild(from) : -1;
return selectTab; return selectTab;
}; };

76
src/helpers/bytes.ts

@ -1,24 +1,23 @@
export function bytesToHex(bytes: ArrayLike<number>) { export function bytesToHex(bytes: ArrayLike<number>) {
bytes = bytes || []; bytes = bytes || [];
var arr = []; let arr: string[] = [];
for(var i = 0; i < bytes.length; i++) { for(let i = 0; i < bytes.length; ++i) {
arr.push((bytes[i] < 16 ? '0' : '') + (bytes[i] || 0).toString(16)); arr.push((bytes[i] < 16 ? '0' : '') + (bytes[i] || 0).toString(16));
} }
return arr.join(''); return arr.join('');
} }
export function bytesFromHex(hexString: string) { export function bytesFromHex(hexString: string) {
var len = hexString.length, const len = hexString.length;
i; let start = 0;
var start = 0; let bytes: number[] = [];
var bytes = [];
if(hexString.length % 2) { if(len % 2) { // read 0x581 as 0x0581
bytes.push(parseInt(hexString.charAt(0), 16)); 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)); bytes.push(parseInt(hexString.substr(i, 2), 16));
} }
@ -26,24 +25,24 @@ export function bytesFromHex(hexString: string) {
} }
export function bytesToBase64(bytes: number[] | Uint8Array) { export function bytesToBase64(bytes: number[] | Uint8Array) {
var mod3 let mod3: number;
var result = '' let result = '';
for (var nLen = bytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) { for(let nLen = bytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; ++nIdx) {
mod3 = nIdx % 3 mod3 = nIdx % 3;
nUint24 |= bytes[nIdx] << (16 >>> mod3 & 24) nUint24 |= bytes[nIdx] << (16 >>> mod3 & 24);
if(mod3 === 2 || nLen - nIdx === 1) { if(mod3 === 2 || nLen - nIdx === 1) {
result += String.fromCharCode( result += String.fromCharCode(
uint6ToBase64(nUint24 >>> 18 & 63), uint6ToBase64(nUint24 >>> 18 & 63),
uint6ToBase64(nUint24 >>> 12 & 63), uint6ToBase64(nUint24 >>> 12 & 63),
uint6ToBase64(nUint24 >>> 6 & 63), uint6ToBase64(nUint24 >>> 6 & 63),
uint6ToBase64(nUint24 & 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) { export function uint6ToBase64(nUint6: number) {
@ -61,12 +60,12 @@ export function uint6ToBase64(nUint6: number) {
} }
export function bytesCmp(bytes1: number[] | Uint8Array, bytes2: number[] | Uint8Array) { export function bytesCmp(bytes1: number[] | Uint8Array, bytes2: number[] | Uint8Array) {
var len = bytes1.length; const len = bytes1.length;
if(len !== bytes2.length) { if(len !== bytes2.length) {
return false; return false;
} }
for(var i = 0; i < len; i++) { for(let i = 0; i < len; ++i) {
if(bytes1[i] !== bytes2[i]) { if(bytes1[i] !== bytes2[i]) {
return false; return false;
} }
@ -76,10 +75,10 @@ export function bytesCmp(bytes1: number[] | Uint8Array, bytes2: number[] | Uint8
} }
export function bytesXor(bytes1: number[] | Uint8Array, bytes2: number[] | Uint8Array) { export function bytesXor(bytes1: number[] | Uint8Array, bytes2: number[] | Uint8Array) {
var len = bytes1.length; const len = bytes1.length;
var bytes = []; const bytes: number[] = [];
for (var i = 0; i < len; ++i) { for(let i = 0; i < len; ++i) {
bytes[i] = bytes1[i] ^ bytes2[i]; bytes[i] = bytes1[i] ^ bytes2[i];
} }
@ -111,11 +110,11 @@ export function convertToUint8Array(bytes: Uint8Array | number[]): Uint8Array {
} }
export function bytesFromArrayBuffer(buffer: ArrayBuffer) { export function bytesFromArrayBuffer(buffer: ArrayBuffer) {
var len = buffer.byteLength; const len = buffer.byteLength;
var byteView = new Uint8Array(buffer); const byteView = new Uint8Array(buffer);
var bytes = []; const bytes: number[] = [];
for(var i = 0; i < len; ++i) { for(let i = 0; i < len; ++i) {
bytes[i] = byteView[i]; bytes[i] = byteView[i];
} }
@ -123,9 +122,9 @@ export function bytesFromArrayBuffer(buffer: ArrayBuffer) {
} }
export function bufferConcat(buffer1: any, buffer2: any) { export function bufferConcat(buffer1: any, buffer2: any) {
var l1 = buffer1.byteLength || buffer1.length; const l1 = buffer1.byteLength || buffer1.length;
var l2 = buffer2.byteLength || buffer2.length; const l2 = buffer2.byteLength || buffer2.length;
var tmp = new Uint8Array(l1 + l2); const tmp = new Uint8Array(l1 + l2);
tmp.set(buffer1 instanceof ArrayBuffer ? new Uint8Array(buffer1) : buffer1, 0); tmp.set(buffer1 instanceof ArrayBuffer ? new Uint8Array(buffer1) : buffer1, 0);
tmp.set(buffer2 instanceof ArrayBuffer ? new Uint8Array(buffer2) : buffer2, l1); tmp.set(buffer2 instanceof ArrayBuffer ? new Uint8Array(buffer2) : buffer2, l1);
@ -136,7 +135,7 @@ export function bufferConcats(...args: any[]) {
let length = 0; let length = 0;
args.forEach(b => length += b.byteLength || b.length); args.forEach(b => length += b.byteLength || b.length);
var tmp = new Uint8Array(length); const tmp = new Uint8Array(length);
let lastLength = 0; let lastLength = 0;
args.forEach(b => { args.forEach(b => {
@ -148,8 +147,8 @@ export function bufferConcats(...args: any[]) {
} }
export function bytesFromWordss(input: Uint32Array) { export function bytesFromWordss(input: Uint32Array) {
var o = []; const o: number[] = [];
for(var i = 0; i < input.length * 4; i++) { for(let i = 0, length = input.length * 4; i < length; ++i) {
o.push((input[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff); 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); if(input instanceof ArrayBuffer) bytes = new Uint8Array(input);
else bytes = input; else bytes = input;
var len = bytes.length; const words: number[] = [];
var words: number[] = []; for(let i = 0, len = bytes.length; i < len; ++i) {
var i;
for(i = 0; i < len; i++) {
words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8); words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);
} }
return new Uint32Array(words); return new Uint32Array(words);
} }
// * 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]);
})(); */

31
src/index.hbs

@ -135,36 +135,6 @@
</button> </button>
</div> </div>
</div> </div>
<div class="sidebar-slider-item">
<div class="sidebar-header">
<button class="btn-icon tgico-arrow_back sidebar-close-button"></button>
<div class="sidebar-header__title">Archived Chats</div>
</div>
<div class="sidebar-content">
<div id="chats-archived-container">
<ul id="dialogs-archived"></ul>
</div>
</div>
</div>
<div class="sidebar-slider-item" id="contacts-container">
<div class="sidebar-header">
<button class="btn-icon tgico-arrow_back sidebar-close-button"></button>
</div>
<div class="sidebar-content">
<div>
<ul id="contacts" class="contacts-container"></ul>
</div>
</div>
</div>
<div class="sidebar-slider-item addmembers-container">
<div class="sidebar-header">
<button class="btn-icon tgico-arrow_back sidebar-close-button"></button>
<div class="sidebar-header__title">Add Members</div>
</div>
<div class="sidebar-content">
<button class="btn-circle rp btn-corner tgico-next"></button>
</div>
</div>
</div> </div>
</div> </div>
<div class="main-column" id="column-center"></div> <div class="main-column" id="column-center"></div>
@ -190,7 +160,6 @@
<avatar-element class="profile-avatar avatar-120" dialog="1" clickable></avatar-element> <avatar-element class="profile-avatar avatar-120" dialog="1" clickable></avatar-element>
<div class="profile-name"></div> <div class="profile-name"></div>
<div class="profile-subtitle"></div> <div class="profile-subtitle"></div>
<div class="profile-row profile-row-bio tgico-info"> <div class="profile-row profile-row-bio tgico-info">
<p></p> <p></p>
<p class="profile-row-label">Bio</p> <p class="profile-row-label">Bio</p>

18
src/lib/appManagers/appDialogsManager.ts

@ -176,6 +176,7 @@ class ConnectionStatusComponent {
export class AppDialogsManager { export class AppDialogsManager {
public _chatList = document.getElementById('dialogs') as HTMLUListElement; public _chatList = document.getElementById('dialogs') as HTMLUListElement;
public chatList = this._chatList; public chatList = this._chatList;
public chatListArchived: HTMLUListElement;
public doms: {[peerId: number]: DialogDom} = {}; public doms: {[peerId: number]: DialogDom} = {};
@ -191,10 +192,7 @@ export class AppDialogsManager {
public contextMenu = new DialogsContextMenu(); public contextMenu = new DialogsContextMenu();
public chatLists: {[filterId: number]: HTMLUListElement} = { public chatLists: {[filterId: number]: HTMLUListElement};
0: this.chatList,
1: appSidebarLeft.archivedTab.chatList
};
public filterId = 0; public filterId = 0;
private folders: {[k in 'menu' | 'container' | 'menuScrollContainer']: HTMLElement} = { private folders: {[k in 'menu' | 'container' | 'menuScrollContainer']: HTMLElement} = {
menu: document.getElementById('folders-tabs'), menu: document.getElementById('folders-tabs'),
@ -222,6 +220,14 @@ export class AppDialogsManager {
private lastActiveElements: Set<HTMLElement> = new Set(); private lastActiveElements: Set<HTMLElement> = new Set();
constructor() { 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.chatsPreloader = putPreloader(null, true);
this.allUnreadCount = this.folders.menu.querySelector('.badge'); this.allUnreadCount = this.folders.menu.querySelector('.badge');
@ -434,10 +440,10 @@ export class AppDialogsManager {
positionElementByIndex(renderedFilter.container, this.folders.container, filter.orderIndex); positionElementByIndex(renderedFilter.container, this.folders.container, filter.orderIndex);
}); });
if(this.filterId) { /* if(this.filterId) {
const tabIndex = order.indexOf(this.filterId) + 1; const tabIndex = order.indexOf(this.filterId) + 1;
selectTab.prevId = tabIndex; selectTab.prevId = tabIndex;
} } */
}); });
rootScope.on('peer_typings', (e) => { rootScope.on('peer_typings', (e) => {

4
src/lib/crypto/crypto_utils.ts

@ -183,13 +183,13 @@ export function pqPrimeLeemon(what: any) {
var x = new Array(minLen); var x = new Array(minLen);
var y = 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; q = (nextRandomInt(128) & 15) + 17;
copyInt_(x, nextRandomInt(1000000000) + 1); copyInt_(x, nextRandomInt(1000000000) + 1);
copy_(y, x); copy_(y, x);
lim = 1 << (i + 18); lim = 1 << (i + 18);
for (j = 1; j < lim; j++) { for (j = 1; j < lim; ++j) {
++it; ++it;
copy_(a, x); copy_(a, x);
copy_(b, x); copy_(b, x);

71
src/lib/crypto/srp.ts

@ -5,9 +5,12 @@ import {str2bigInt, isZero,
import {logger, LogLevels} from '../logger'; import {logger, LogLevels} from '../logger';
import { AccountPassword, PasswordKdfAlgo } from "../../layer"; import { AccountPassword, PasswordKdfAlgo } from "../../layer";
import { bufferConcats, bytesToHex, bytesFromHex, bufferConcat, bytesXor } from "../../helpers/bytes"; import { bufferConcats, bytesToHex, bytesFromHex, bufferConcat, bytesXor } from "../../helpers/bytes";
//import { MOUNT_CLASS_TO } from "../../config/debug";
const log = logger('SRP', LogLevels.error); 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<number[]> { export async function makePasswordHash(password: string, client_salt: Uint8Array, server_salt: Uint8Array): Promise<number[]> {
let clientSaltString = ''; let clientSaltString = '';
for(let i = 0; i < client_salt.length; i++) clientSaltString += String.fromCharCode(client_salt[i]); 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) { export async function computeSRP(password: string, state: AccountPassword, isNew: boolean) {
console.log('computeSRP:', password, state, isNew); const algo = (isNew ? state.new_algo : state.current_algo) as PasswordKdfAlgo.passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow;
//console.log('computeSRP:', password, state, isNew, algo);
let algo = (state.current_algo || state.new_algo) as PasswordKdfAlgo.passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow;
let p = str2bigInt(bytesToHex(algo.p), 16); const p = str2bigInt(bytesToHex(algo.p), 16);
let B = str2bigInt(bytesToHex(state.srp_B), 16); const B = str2bigInt(bytesToHex(state.srp_B), 16);
let g = int2bigInt(algo.g, 32, 256); const g = int2bigInt(algo.g, 32, 256);
//log('p', bigInt2str(p, 16)); //log('p', bigInt2str(p, 16));
//log('B', bigInt2str(B, 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); //check_prime_and_good(algo.p, g);
let pw_hash = await makePasswordHash(password, new Uint8Array(algo.salt1), new Uint8Array(algo.salt2)); const pw_hash = await makePasswordHash(password, new Uint8Array(algo.salt1), new Uint8Array(algo.salt2));
let x = str2bigInt(bytesToHex(new Uint8Array(pw_hash)), 16); const x = str2bigInt(bytesToHex(new Uint8Array(pw_hash)), 16);
//log('computed pw_hash:', pw_hash, x, bytesToHex(new Uint8Array(pw_hash))); //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); return Array(len).fill(fill).concat(arr).slice(-len);
}; };
let pForHash = padArray(bytesFromHex(bigInt2str(p, 16)), 256); const pForHash = padArray(bytesFromHex(bigInt2str(p, 16)), 256);
let gForHash = padArray(bytesFromHex(bigInt2str(g, 16)), 256); // like uint8array const gForHash = padArray(bytesFromHex(bigInt2str(g, 16)), 256); // like uint8array
let b_for_hash = padArray(bytesFromHex(bigInt2str(B, 16)), 256); const b_for_hash = padArray(bytesFromHex(bigInt2str(B, 16)), 256);
/* log(bytesToHex(pForHash)); /* log(bytesToHex(pForHash));
log(bytesToHex(gForHash)); log(bytesToHex(gForHash));
log(bytesToHex(b_for_hash)); */ 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 // * https://core.telegram.org/api/srp#setting-a-new-2fa-password
if(isNew) { 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)); //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)); //log('k', bigInt2str(k, 16));
// kg_x = (k * g_x) % p // kg_x = (k * g_x) % p
let kg_x = mod(mult(k, g_x), p); const k_v = mod(mult(k, v), p);
// good // good
//log('kg_x', bigInt2str(kg_x, 16)); //log('kg_x', bigInt2str(kg_x, 16));
let is_good_mod_exp_first = (modexp: any, prime: any) => { const is_good_mod_exp_first = (modexp: any, prime: any) => {
let diff = sub(prime, modexp); const diff = sub(prime, modexp);
let min_diff_bits_count = 2048 - 64; const min_diff_bits_count = 2048 - 64;
let max_mod_exp_size = 256; const max_mod_exp_size = 256;
if(negative(diff) || if(negative(diff) ||
bitSize(diff) < min_diff_bits_count || bitSize(diff) < min_diff_bits_count ||
bitSize(modexp) < 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; return true;
}; };
var flipper = (arr: Uint8Array | number[]) => { const generate_and_check_random = async() => {
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() => {
while(true) { while(true) {
const a = str2bigInt(bytesToHex(flipper(state.secure_random)), 16); const a = str2bigInt(bytesToHex(flipper(state.secure_random)), 16);
//const a = str2bigInt('9153faef8f2bb6da91f6e5bc96bc00860a530a572a0f45aac0842b4602d711f8bda8d59fb53705e4ae3e31a3c4f0681955425f224297b8e9efd898fec22046debb7ba8a0bcf2be1ada7b100424ea318fdcef6ccfe6d7ab7d978c0eb76a807d4ab200eb767a22de0d828bc53f42c5a35c2df6e6ceeef9a3487aae8e9ef2271f2f6742e83b8211161fb1a0e037491ab2c2c73ad63c8bd1d739de1b523fe8d461270cedcf240de8da75f31be4933576532955041dc5770c18d3e75d0b357df9da4a5c8726d4fced87d15752400883dc57fa1937ac17608c5446c4774dcd123676d683ce3a1ab9f7e020ca52faafc99969822717c8e07ea383d5fb1a007ba0d170cb', 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)); */ log('B - kg_x', bigInt2str(sub(B, kg_x), 16)); */
let g_b; let g_b;
if(!greater(B, kg_x)) { if(!greater(B, k_v)) {
//log('negative'); //log('negative');
g_b = add(B, p); g_b = add(B, p);
} else g_b = B; } 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); /* let g_b = sub(B, kg_x);
if(negative(g_b)) g_b = add(g_b, p); */ if(negative(g_b)) g_b = add(g_b, p); */

6
src/pages/pagePassword.ts

@ -2,7 +2,6 @@ import { putPreloader } from '../components/misc';
import mediaSizes from '../helpers/mediaSizes'; import mediaSizes from '../helpers/mediaSizes';
import { AccountPassword } from '../layer'; import { AccountPassword } from '../layer';
import appStateManager from '../lib/appManagers/appStateManager'; import appStateManager from '../lib/appManagers/appStateManager';
import apiManager from '../lib/mtproto/mtprotoworker';
import passwordManager from '../lib/mtproto/passwordManager'; import passwordManager from '../lib/mtproto/passwordManager';
import Page from './page'; import Page from './page';
import pageIm from './pageIm'; import pageIm from './pageIm';
@ -10,12 +9,13 @@ import Button from '../components/button';
import PasswordInputField from '../components/passwordInputField'; import PasswordInputField from '../components/passwordInputField';
import PasswordMonkey from '../components/monkeys/password'; import PasswordMonkey from '../components/monkeys/password';
import { ripple } from '../components/ripple'; import { ripple } from '../components/ripple';
import RichTextProcessor from '../lib/richtextprocessor';
const TEST = false; const TEST = false;
let passwordInput: HTMLInputElement; let passwordInput: HTMLInputElement;
let onFirstMount = (): Promise<any> => { let onFirstMount = (): Promise<any> => {
const btnNext = Button('btn-primary', {text: 'NEXT'}); const btnNext = Button('btn-primary btn-color-primary', {text: 'NEXT'});
const passwordInputField = new PasswordInputField({ const passwordInputField = new PasswordInputField({
label: 'Password', label: 'Password',
@ -37,7 +37,7 @@ let onFirstMount = (): Promise<any> => {
return !TEST && passwordManager.getState().then(_state => { return !TEST && passwordManager.getState().then(_state => {
state = _state; state = _state;
passwordInputField.label.innerText = state.hint ?? 'Password'; passwordInputField.label.innerHTML = state.hint ? RichTextProcessor.wrapEmojiText(state.hint) : 'Password';
}); });
}; };

2
src/pages/pageSignIn.ts

@ -297,7 +297,7 @@ let onFirstMount = () => {
}); });
signedCheckboxField.input.checked = true; signedCheckboxField.input.checked = true;
btnNext = Button('btn-primary', {text: 'NEXT'}); btnNext = Button('btn-primary btn-color-primary', {text: 'NEXT'});
btnNext.style.visibility = 'hidden'; btnNext.style.visibility = 'hidden';
btnNext.addEventListener('click', function(this: HTMLElement, e) { btnNext.addEventListener('click', function(this: HTMLElement, e) {

2
src/pages/pageSignUp.ts

@ -64,7 +64,7 @@ const onFirstMount = () => import('../lib/appManagers/appProfileManager').then(i
maxLength: 64 maxLength: 64
}); });
const btnSignUp = Button('btn-primary'); const btnSignUp = Button('btn-primary btn-color-primary');
btnSignUp.append('START MESSAGING'); btnSignUp.append('START MESSAGING');
inputWrapper.append(nameInputField.container, lastNameInputField.container, btnSignUp); inputWrapper.append(nameInputField.container, lastNameInputField.container, btnSignUp);

12
src/scss/partials/_button.scss

@ -206,8 +206,6 @@
} }
.btn-primary { .btn-primary {
background-color: $color-blue;
color: #fff;
border-radius: $border-radius-medium; border-radius: $border-radius-medium;
width: 100%; width: 100%;
text-align: center; text-align: center;
@ -227,6 +225,7 @@
&-transparent { &-transparent {
background-color: transparent; background-color: transparent;
@include hover() { @include hover() {
background: hover-color($color-blue); background: hover-color($color-blue);
} }
@ -345,3 +344,12 @@
@include btn-hoverable(); @include btn-hoverable();
} }
.btn-color-primary {
background: $color-blue;
color: #fff;
/* .c-ripple__circle {
background-color: var(--color-blue-hover);
} */
}

48
src/scss/partials/_leftSidebar.scss

@ -903,29 +903,69 @@
} }
.two-step-verification { .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 { .media-sticker-wrapper {
width: 168px; width: 168px;
height: 168px; height: 168px;
margin: .625rem auto 1.1875rem;
}
.input-wrapper .btn-primary:first-child:last-child {
margin-top: .25rem;
} }
&-enter-password { &-enter-password {
.media-sticker-wrapper { .media-sticker-wrapper {
margin: 1.125rem auto 1.8125rem;
width: 157px; width: 157px;
height: 157px; height: 157px;
} }
} }
&-hint, &-email {
.btn-primary + .btn-primary {
margin-top: .5rem !important;
}
}
&-hint { &-hint {
.media-sticker-wrapper { .media-sticker-wrapper {
width: 120px; width: 160px;
height: 120px; height: 160px;
margin: .5rem auto 2.25rem;
} }
} }
&-email { &-email {
.media-sticker-wrapper { .media-sticker-wrapper {
width: 120px; width: 160px;
height: 120px; 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;
}
} }
} }
} }

6
src/scss/style.scss

@ -754,6 +754,12 @@ img.emoji {
} }
} }
.popup-disable-password, .popup-skip-email {
.popup-description {
max-width: 284px;
}
}
.grid { .grid {
width: 100%; width: 100%;
display: grid; display: grid;

2
src/tests/srp.test.ts

@ -22,7 +22,7 @@ test('2FA whole (with negative)', async() => {
new_algo: null, new_algo: null,
new_secure_algo: null new_secure_algo: null
}).then(res => { }, false).then(res => {
expect(res.srp_id).toEqual(srp_id); expect(res.srp_id).toEqual(srp_id);
expect(res.A).toEqual(A); expect(res.A).toEqual(A);
expect(res.M1).toEqual(M1); expect(res.M1).toEqual(M1);

Loading…
Cancel
Save