Browse Source

Two-Step Verification layout

master
Eduard Kuzmenko 3 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. 46
      src/components/sidebarLeft/tabs/2fa/email.ts
  8. 8
      src/components/sidebarLeft/tabs/2fa/enterPassword.ts
  9. 30
      src/components/sidebarLeft/tabs/2fa/hint.ts
  10. 37
      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. 10
      src/components/sidebarLeft/tabs/newChannel.ts
  22. 4
      src/components/sidebarLeft/tabs/newGroup.ts
  23. 8
      src/components/sidebarLeft/tabs/privacyAndSecurity.ts
  24. 25
      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. 80
      src/helpers/bytes.ts
  31. 2
      src/helpers/random.ts
  32. 31
      src/index.hbs
  33. 18
      src/lib/appManagers/appDialogsManager.ts
  34. 4
      src/lib/crypto/crypto_utils.ts
  35. 73
      src/lib/crypto/srp.ts
  36. 6
      src/pages/pagePassword.ts
  37. 2
      src/pages/pageSignIn.ts
  38. 2
      src/pages/pageSignUp.ts
  39. 12
      src/scss/partials/_button.scss
  40. 48
      src/scss/partials/_leftSidebar.scss
  41. 6
      src/scss/style.scss
  42. 2
      src/tests/srp.test.ts

2
src/components/chat/topbar.ts

@ -218,7 +218,7 @@ export default class ChatTopbar { @@ -218,7 +218,7 @@ export default class ChatTopbar {
this.pinnedMessage = new ChatPinnedMessage(this, this.chat, this.appMessagesManager, this.appPeersManager);
this.btnJoin = Button('btn-primary chat-join hide');
this.btnJoin = Button('btn-primary btn-color-primary chat-join hide');
this.btnJoin.append('SUBSCRIBE');
this.btnPinned = ButtonIcon('pinlist');

4
src/components/horizontalMenu.ts

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

2
src/components/popups/avatar.ts

@ -66,7 +66,7 @@ export default class PopupAvatar extends PopupElement { @@ -66,7 +66,7 @@ export default class PopupAvatar extends PopupElement {
}, false);
this.btnSubmit = document.createElement('button');
this.btnSubmit.className = 'btn-primary btn-circle btn-crop btn-icon tgico-check z-depth-1';
this.btnSubmit.className = 'btn-primary btn-color-primary btn-circle btn-crop btn-icon tgico-check z-depth-1';
ripple(this.btnSubmit);
this.btnSubmit.addEventListener('click', () => {
this.cropper.crop();

2
src/components/popups/index.ts

@ -64,7 +64,7 @@ export default class PopupElement { @@ -64,7 +64,7 @@ export default class PopupElement {
if(options.withConfirm) {
this.btnConfirm = document.createElement('button');
this.btnConfirm.classList.add('btn-primary');
this.btnConfirm.classList.add('btn-primary', 'btn-color-primary');
this.btnConfirm.innerText = options.withConfirm;
this.header.append(this.btnConfirm);
ripple(this.btnConfirm);

2
src/components/popups/stickers.ts

@ -97,7 +97,7 @@ export default class PopupStickers extends PopupElement { @@ -97,7 +97,7 @@ export default class PopupStickers extends PopupElement {
this.h6.innerHTML = RichTextProcessor.wrapEmojiText(set.set.title);
!set.set.installed_date ? this.stickersFooter.classList.add('add') : this.stickersFooter.classList.remove('add');
this.stickersFooter.innerHTML = set.set.hasOwnProperty('installed_date') ? '<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);

68
src/components/sidebarLeft/index.ts

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

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

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

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

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

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

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

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

@ -30,23 +30,27 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab { @@ -30,23 +30,27 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab {
const doc = appStickersManager.getAnimatedEmojiSticker(emoji);
const stickerContainer = document.createElement('div');
wrapSticker({
doc,
div: stickerContainer,
loop: false,
play: true,
width: 168,
height: 168,
emoji
}).then(() => {
// this.animation = player;
});
if(doc) {
wrapSticker({
doc,
div: stickerContainer,
loop: false,
play: true,
width: 168,
height: 168,
emoji
}).then(() => {
// this.animation = player;
});
} else {
stickerContainer.classList.add('media-sticker-wrapper');
}
section.content.append(stickerContainer);
const c = section.generateContentElement();
if(this.state.pFlags.has_password) {
section.caption.innerHTML = 'You have enabled Two-Step verification.<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 btnDisablePassword = Button('btn-primary btn-transparent', {icon: 'passwordoff', text: 'Turn Password Off'});
@ -78,8 +82,13 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab { @@ -78,8 +82,13 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab {
} else {
section.caption.innerHTML = 'You can set a password that will be required when you log in on a new device in addition to the code you get in the SMS.';
const btnSetPassword = Button('btn-primary', {text: 'SET PASSWORD'});
c.append(btnSetPassword);
const inputWrapper = document.createElement('div');
inputWrapper.classList.add('input-wrapper');
const btnSetPassword = Button('btn-primary btn-color-primary', {text: 'SET PASSWORD'});
inputWrapper.append(btnSetPassword);
c.append(inputWrapper);
attachClickEvent(btnSetPassword, (e) => {
const tab = new AppTwoStepVerificationEnterPasswordTab(this.slider);

61
src/components/sidebarLeft/tabs/2fa/passwordSet.ts

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

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

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

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

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

41
src/components/sidebarLeft/tabs/chatFolders.ts

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

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

@ -1,52 +1,52 @@ @@ -1,52 +1,52 @@
import { SliderTab } from "../../slider";
import Scrollable from "../../scrollable";
import SidebarSlider, { SliderSuperTab } from "../../slider";
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
import appUsersManager from "../../../lib/appManagers/appUsersManager";
import appPhotosManager from "../../../lib/appManagers/appPhotosManager";
import appSidebarLeft, { AppSidebarLeft } from "..";
import rootScope from "../../../lib/rootScope";
import InputSearch from "../../inputSearch";
// TODO: поиск по людям глобальный, если не нашло в контактах никого
export default class AppContactsTab implements SliderTab {
private container: HTMLElement;
export default class AppContactsTab extends SliderSuperTab {
private list: HTMLUListElement;
private scrollable: Scrollable;
private promise: Promise<void>;
private inputSearch: InputSearch;
private alive = true;
constructor(slider: SidebarSlider) {
super(slider, true);
}
init() {
this.container = document.getElementById('contacts-container');
this.list = this.container.querySelector('#contacts');
this.container.id = 'contacts-container';
this.list = document.createElement('ul');
this.list.id = 'contacts';
this.list.classList.add('contacts-container');
appDialogsManager.setListClickListener(this.list, () => {
(this.container.querySelector('.sidebar-close-button') as HTMLElement).click();
}, undefined, true);
this.scrollable = new Scrollable(this.list.parentElement);
this.inputSearch = new InputSearch('Search', (value) => {
this.list.innerHTML = '';
this.openContacts(value);
});
this.container.firstElementChild.append(this.inputSearch.container);
this.title.replaceWith(this.inputSearch.container);
this.scrollable.append(this.list);
// preload contacts
// appUsersManager.getContacts();
}
// need to clear, and left 1 page for smooth slide
public onClose() {
onClose() {
this.alive = false;
/* // need to clear, and left 1 page for smooth slide
let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0;
(Array.from(this.list.children) as HTMLElement[]).slice(pageCount).forEach(el => el.remove());
}
public onCloseAfterTimeout() {
this.list.innerHTML = '';
this.inputSearch.value = '';
(Array.from(this.list.children) as HTMLElement[]).slice(pageCount).forEach(el => el.remove()); */
}
public openContacts(query?: string) {
@ -55,18 +55,14 @@ export default class AppContactsTab implements SliderTab { @@ -55,18 +55,14 @@ export default class AppContactsTab implements SliderTab {
this.init = null;
}
if(appSidebarLeft.historyTabIds.indexOf(AppSidebarLeft.SLIDERITEMSIDS.contacts) === -1) {
appSidebarLeft.selectTab(AppSidebarLeft.SLIDERITEMSIDS.contacts);
}
if(this.promise) return this.promise;
this.scrollable.onScrolledBottom = null;
this.promise = appUsersManager.getContacts(query).then(_contacts => {
this.promise = null;
if(appSidebarLeft.historyTabIds[appSidebarLeft.historyTabIds.length - 1] !== AppSidebarLeft.SLIDERITEMSIDS.contacts) {
console.warn('user closed contacts before it\'s loaded');
if(!this.alive) {
//console.warn('user closed contacts before it\'s loaded');
return;
}
@ -118,4 +114,9 @@ export default class AppContactsTab implements SliderTab { @@ -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 @@ @@ -1,20 +1,18 @@
import appSidebarLeft, { AppSidebarLeft } from "..";
import { deepEqual, copy } from "../../../helpers/object";
import appDialogsManager from "../../../lib/appManagers/appDialogsManager";
import { MyDialogFilter as DialogFilter } from "../../../lib/storages/filters";
import lottieLoader, { RLottiePlayer } from "../../../lib/lottieLoader";
import { parseMenuButtonsTo } from "../../misc";
import { ripple } from "../../ripple";
import { SliderTab, SliderSuperTab } from "../../slider";
import SidebarSlider, { SliderSuperTab } from "../../slider";
import { toast } from "../../toast";
import appMessagesManager from "../../../lib/appManagers/appMessagesManager";
import { attachClickEvent } from "../../../helpers/dom";
import InputField from "../../inputField";
import RichTextProcessor from "../../../lib/richtextprocessor";
import ButtonIcon from "../../buttonIcon";
import ButtonMenuToggle from "../../buttonMenuToggle";
import { ButtonMenuItemOptions } from "../../buttonMenu";
import Button from "../../button";
import AppIncludedChatsTab from "./includedChats";
const MAX_FOLDER_NAME_LENGTH = 12;
@ -36,8 +34,8 @@ export default class AppEditFolderTab extends SliderSuperTab { @@ -36,8 +34,8 @@ export default class AppEditFolderTab extends SliderSuperTab {
private type: 'edit' | 'create';
constructor(appSidebarLeft: AppSidebarLeft) {
super(appSidebarLeft);
constructor(slider: SidebarSlider) {
super(slider, true);
}
protected init() {
@ -159,11 +157,11 @@ export default class AppEditFolderTab extends SliderSuperTab { @@ -159,11 +157,11 @@ export default class AppEditFolderTab extends SliderSuperTab {
const excludedFlagsContainer = this.exclude_peers.querySelector('.folder-categories');
includedFlagsContainer.firstElementChild.addEventListener('click', () => {
appSidebarLeft.includedChatsTab.open(this.filter, 'included');
new AppIncludedChatsTab(this.slider).open(this.filter, 'included', this);
});
excludedFlagsContainer.firstElementChild.addEventListener('click', () => {
appSidebarLeft.includedChatsTab.open(this.filter, 'excluded');
new AppIncludedChatsTab(this.slider).open(this.filter, 'excluded', this);
});
lottieLoader.loadAnimationFromURL({
@ -228,10 +226,8 @@ export default class AppEditFolderTab extends SliderSuperTab { @@ -228,10 +226,8 @@ export default class AppEditFolderTab extends SliderSuperTab {
if(this.animation) {
this.animation.restart();
}
}
onCloseAfterTimeout() {
Array.from(this.container.querySelectorAll('ul, .show-more')).forEach(el => el.remove());
return super.onOpen();
}
private onCreateOpen() {
@ -318,7 +314,7 @@ export default class AppEditFolderTab extends SliderSuperTab { @@ -318,7 +314,7 @@ export default class AppEditFolderTab extends SliderSuperTab {
setFilter(filter: DialogFilter, firstTime: boolean) {
// cleanup
this.onCloseAfterTimeout();
Array.from(this.container.querySelectorAll('ul, .show-more')).forEach(el => el.remove());
if(firstTime) {
this.originalFilter = filter;

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

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

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

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

18
src/components/sidebarLeft/tabs/includedChats.ts

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

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

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

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

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

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

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

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

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

14
src/components/sidebarRight/index.ts

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

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

@ -95,7 +95,7 @@ export default class AppStickersTab implements SliderTab { @@ -95,7 +95,7 @@ export default class AppStickersTab implements SliderTab {
`;
const button = document.createElement('button');
button.classList.add('btn-primary', 'sticker-set-button');
button.classList.add('btn-primary', 'btn-color-primary', 'sticker-set-button');
button.innerText = set.installed_date ? 'Added' : 'Add';
// button.style.width = set.installed_date ? '68px' : '52px';

121
src/components/slider.ts

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

77
src/components/sliderTab.ts

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

80
src/helpers/bytes.ts

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

2
src/helpers/random.ts

@ -5,4 +5,4 @@ export function nextRandomInt(maxValue: number) { @@ -5,4 +5,4 @@ export function nextRandomInt(maxValue: number) {
export function randomLong() {
return '' + nextRandomInt(0xFFFFFFFF) + nextRandomInt(0xFFFFFF);
//return '' + parseInt(nextRandomInt(0xFFFFFFFF).toString(16) + nextRandomInt(0xFFFFFFFF).toString(16), 16);
}
}

31
src/index.hbs

@ -135,36 +135,6 @@ @@ -135,36 +135,6 @@
</button>
</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 class="main-column" id="column-center"></div>
@ -190,7 +160,6 @@ @@ -190,7 +160,6 @@
<avatar-element class="profile-avatar avatar-120" dialog="1" clickable></avatar-element>
<div class="profile-name"></div>
<div class="profile-subtitle"></div>
<div class="profile-row profile-row-bio tgico-info">
<p></p>
<p class="profile-row-label">Bio</p>

18
src/lib/appManagers/appDialogsManager.ts

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

4
src/lib/crypto/crypto_utils.ts

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

73
src/lib/crypto/srp.ts

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

6
src/pages/pagePassword.ts

@ -2,7 +2,6 @@ import { putPreloader } from '../components/misc'; @@ -2,7 +2,6 @@ import { putPreloader } from '../components/misc';
import mediaSizes from '../helpers/mediaSizes';
import { AccountPassword } from '../layer';
import appStateManager from '../lib/appManagers/appStateManager';
import apiManager from '../lib/mtproto/mtprotoworker';
import passwordManager from '../lib/mtproto/passwordManager';
import Page from './page';
import pageIm from './pageIm';
@ -10,12 +9,13 @@ import Button from '../components/button'; @@ -10,12 +9,13 @@ import Button from '../components/button';
import PasswordInputField from '../components/passwordInputField';
import PasswordMonkey from '../components/monkeys/password';
import { ripple } from '../components/ripple';
import RichTextProcessor from '../lib/richtextprocessor';
const TEST = false;
let passwordInput: HTMLInputElement;
let onFirstMount = (): Promise<any> => {
const btnNext = Button('btn-primary', {text: 'NEXT'});
const btnNext = Button('btn-primary btn-color-primary', {text: 'NEXT'});
const passwordInputField = new PasswordInputField({
label: 'Password',
@ -37,7 +37,7 @@ let onFirstMount = (): Promise<any> => { @@ -37,7 +37,7 @@ let onFirstMount = (): Promise<any> => {
return !TEST && passwordManager.getState().then(_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 = () => { @@ -297,7 +297,7 @@ let onFirstMount = () => {
});
signedCheckboxField.input.checked = true;
btnNext = Button('btn-primary', {text: 'NEXT'});
btnNext = Button('btn-primary btn-color-primary', {text: 'NEXT'});
btnNext.style.visibility = 'hidden';
btnNext.addEventListener('click', function(this: HTMLElement, e) {

2
src/pages/pageSignUp.ts

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

12
src/scss/partials/_button.scss

@ -206,8 +206,6 @@ @@ -206,8 +206,6 @@
}
.btn-primary {
background-color: $color-blue;
color: #fff;
border-radius: $border-radius-medium;
width: 100%;
text-align: center;
@ -227,6 +225,7 @@ @@ -227,6 +225,7 @@
&-transparent {
background-color: transparent;
@include hover() {
background: hover-color($color-blue);
}
@ -345,3 +344,12 @@ @@ -345,3 +344,12 @@
@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 @@ @@ -903,29 +903,69 @@
}
.two-step-verification {
.sidebar-left-section-caption { // * main tab verified with mockup
text-align: center;
max-width: 342px;
margin-left: auto;
margin-right: auto;
font-size: 1rem;
line-height: 1.3125;
margin-bottom: 1.125rem;
}
.btn-primary + .btn-primary {
margin-top: .125rem !important;
}
.media-sticker-wrapper {
width: 168px;
height: 168px;
margin: .625rem auto 1.1875rem;
}
.input-wrapper .btn-primary:first-child:last-child {
margin-top: .25rem;
}
&-enter-password {
.media-sticker-wrapper {
margin: 1.125rem auto 1.8125rem;
width: 157px;
height: 157px;
}
}
&-hint, &-email {
.btn-primary + .btn-primary {
margin-top: .5rem !important;
}
}
&-hint {
.media-sticker-wrapper {
width: 120px;
height: 120px;
width: 160px;
height: 160px;
margin: .5rem auto 2.25rem;
}
}
&-email {
.media-sticker-wrapper {
width: 120px;
height: 120px;
width: 160px;
height: 160px;
margin: .5625rem auto 2.1875rem;
}
}
&-set {
.media-sticker-wrapper {
width: 160px;
height: 160px;
margin: 1rem auto 1.3125rem;
.rlottie, .rlottie-vector {
left: .625rem;
}
}
}
}

6
src/scss/style.scss

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

2
src/tests/srp.test.ts

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

Loading…
Cancel
Save