From 163185d02118c42d93e296a1945300c29d58a143 Mon Sep 17 00:00:00 2001 From: morethanwords Date: Fri, 1 Jan 2021 18:24:49 +0400 Subject: [PATCH] Refactor edit profile tab --- src/components/avatarEdit.ts | 25 ++++++ src/components/scrollable.ts | 18 ++-- src/components/sidebarLeft/index.ts | 11 +-- .../sidebarLeft/tabs/editProfile.ts | 84 +++++++++++-------- src/components/sidebarLeft/tabs/settings.ts | 2 +- src/components/slider.ts | 54 +++++++++--- src/index.hbs | 21 ----- src/scss/partials/_scrollable.scss | 9 ++ 8 files changed, 145 insertions(+), 79 deletions(-) create mode 100644 src/components/avatarEdit.ts diff --git a/src/components/avatarEdit.ts b/src/components/avatarEdit.ts new file mode 100644 index 00000000..6082b8dc --- /dev/null +++ b/src/components/avatarEdit.ts @@ -0,0 +1,25 @@ +import type { CancellablePromise } from "../helpers/cancellablePromise"; +import type { InputFile } from "../layer"; +import PopupAvatar from "./popups/avatar"; + +export default class AvatarEdit { + public container: HTMLElement; + private canvas: HTMLCanvasElement; + private icon: HTMLSpanElement; + + constructor(onChange: (uploadAvatar: () => CancellablePromise) => void) { + this.container = document.createElement('div'); + this.container.classList.add('avatar-edit'); + + this.canvas = document.createElement('canvas'); + + this.icon = document.createElement('span'); + this.icon.classList.add('tgico', 'tgico-cameraadd'); + + this.container.append(this.canvas, this.icon); + + this.container.addEventListener('click', () => { + new PopupAvatar().open(this.canvas, onChange); + }); + } +} \ No newline at end of file diff --git a/src/components/scrollable.ts b/src/components/scrollable.ts index 8fec1d33..418d5078 100644 --- a/src/components/scrollable.ts +++ b/src/components/scrollable.ts @@ -123,6 +123,7 @@ export type SliceSidesContainer = {[k in SliceSides]: boolean}; export default class Scrollable extends ScrollableBase { public splitUp: HTMLElement; + public padding: HTMLElement; public onAdditionalScroll: () => void = null; public onScrolledTop: () => void = null; @@ -135,9 +136,16 @@ export default class Scrollable extends ScrollableBase { public loadedAll: SliceSidesContainer = {top: true, bottom: false}; - constructor(el: HTMLElement, logPrefix = '', public onScrollOffset = 300) { + constructor(el: HTMLElement, logPrefix = '', public onScrollOffset = 300, withPaddingContainer?: boolean) { super(el, logPrefix); + if(withPaddingContainer) { + this.padding = document.createElement('div'); + this.padding.classList.add('scrollable-padding'); + Array.from(this.container.children).forEach(c => this.padding.append(c)); + this.container.append(this.padding); + } + this.container.classList.add('scrollable-y'); this.setListeners(); } @@ -197,12 +205,12 @@ export default class Scrollable extends ScrollableBase { } }; - public prepend(element: HTMLElement) { - (this.splitUp || this.container).prepend(element); + public prepend(...elements: HTMLElement[]) { + (this.splitUp || this.padding || this.container).prepend(...elements); } - public append(element: HTMLElement) { - (this.splitUp || this.container).append(element); + public append(...elements: HTMLElement[]) { + (this.splitUp || this.padding || this.container).append(...elements); } public scrollIntoView(element: HTMLElement, smooth = true) { diff --git a/src/components/sidebarLeft/index.ts b/src/components/sidebarLeft/index.ts index 30c0a11b..c5efc2ac 100644 --- a/src/components/sidebarLeft/index.ts +++ b/src/components/sidebarLeft/index.ts @@ -34,7 +34,6 @@ const addMembersTab = new AppAddMembersTab(); const contactsTab = new AppContactsTab(); const newGroupTab = new AppNewGroupTab(); const settingsTab = new AppSettingsTab(); -const editProfileTab = new AppEditProfileTab(); const editFolderTab = new AppEditFolderTab(); const includedChatsTab = new AppIncludedChatsTab(); const archivedTab = new AppArchivedTab(); @@ -47,10 +46,9 @@ export class AppSidebarLeft extends SidebarSlider { addMembers: 4, newGroup: 5, settings: 6, - editProfile: 7, - chatFolders: 8, - editFolder: 9, - includedChats: 10, + chatFolders: 7, + editFolder: 8, + includedChats: 9, }; private toolsBtn: HTMLButtonElement; @@ -102,7 +100,6 @@ export class AppSidebarLeft extends SidebarSlider { [AppSidebarLeft.SLIDERITEMSIDS.addMembers]: addMembersTab, [AppSidebarLeft.SLIDERITEMSIDS.newGroup]: newGroupTab, [AppSidebarLeft.SLIDERITEMSIDS.settings]: settingsTab, - [AppSidebarLeft.SLIDERITEMSIDS.editProfile]: editProfileTab, [AppSidebarLeft.SLIDERITEMSIDS.chatFolders]: this.chatFoldersTab = new AppChatFoldersTab(appMessagesManager, appPeersManager, this, apiManagerProxy, rootScope), [AppSidebarLeft.SLIDERITEMSIDS.editFolder]: editFolderTab, [AppSidebarLeft.SLIDERITEMSIDS.includedChats]: includedChatsTab, @@ -123,9 +120,9 @@ export class AppSidebarLeft extends SidebarSlider { this.contactsTab = contactsTab; this.newGroupTab = newGroupTab; this.settingsTab = settingsTab; - this.editProfileTab = editProfileTab; this.editFolderTab = editFolderTab; this.includedChatsTab = includedChatsTab; + this.editProfileTab = new AppEditProfileTab(this); this.menuEl = this.toolsBtn.querySelector('.btn-menu'); this.newBtnMenu = this.sidebarEl.querySelector('#new-menu'); diff --git a/src/components/sidebarLeft/tabs/editProfile.ts b/src/components/sidebarLeft/tabs/editProfile.ts index a32d9cc1..95e5ce3f 100644 --- a/src/components/sidebarLeft/tabs/editProfile.ts +++ b/src/components/sidebarLeft/tabs/editProfile.ts @@ -1,5 +1,4 @@ -import appSidebarLeft from ".."; -import { InputFile } from "../../../layer"; +import type { InputFile } from "../../../layer"; import appProfileManager from "../../../lib/appManagers/appProfileManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager"; import apiManager from "../../../lib/mtproto/mtprotoworker"; @@ -7,19 +6,17 @@ import RichTextProcessor from "../../../lib/richtextprocessor"; import rootScope from "../../../lib/rootScope"; import AvatarElement from "../../avatar"; import InputField from "../../inputField"; -import PopupAvatar from "../../popups/avatar"; import Scrollable from "../../scrollable"; -import { SliderTab } from "../../slider"; +import SidebarSlider, { SliderSuperTab } from "../../slider"; +import AvatarEdit from "../../avatarEdit"; +import ButtonIcon from "../../buttonIcon"; // TODO: аватарка не поменяется в этой вкладке после изменения почему-то (если поставить в другом клиенте, и потом тут проверить, для этого ещё вышел в чатлист) -export default class AppEditProfileTab implements SliderTab { - private container: HTMLElement; - private scrollWrapper: HTMLElement; +export default class AppEditProfileTab extends SliderSuperTab { + //private scrollWrapper: HTMLElement; private nextBtn: HTMLButtonElement; - private canvas: HTMLCanvasElement; - private uploadAvatar: () => Promise = null; - + private firstNameInputField: InputField; private lastNameInputField: InputField; private bioInputField: InputField; @@ -28,7 +25,9 @@ export default class AppEditProfileTab implements SliderTab { private lastNameInput: HTMLElement; private bioInput: HTMLElement; private userNameInput: HTMLElement; - + + private uploadAvatar: () => Promise = null; + private avatarEdit: AvatarEdit; private avatarElem: AvatarElement; private profileUrlContainer: HTMLDivElement; @@ -41,27 +40,28 @@ export default class AppEditProfileTab implements SliderTab { bio: '' }; + constructor(slider: SidebarSlider) { + super(slider); + } + public init() { - this.container = document.querySelector('.edit-profile-container'); - this.scrollWrapper = this.container.querySelector('.scroll-wrapper'); - this.nextBtn = this.container.querySelector('.btn-corner'); - this.canvas = this.container.querySelector('.avatar-edit-canvas'); + this.container.classList.add('edit-profile-container'); + this.title.innerText = 'Edit Profile'; + //this.scrollWrapper = this.container.querySelector('.scroll-wrapper'); + this.nextBtn = ButtonIcon('check btn-circle btn-corner'); + this.content.append(this.nextBtn); this.avatarElem = document.createElement('avatar-element') as AvatarElement; this.avatarElem.classList.add('avatar-placeholder'); - - this.profileUrlContainer = this.container.querySelector('.profile-url-container'); - this.profileUrlAnchor = this.profileUrlContainer.lastElementChild as HTMLAnchorElement; - - const avatarEdit = this.container.querySelector('.avatar-edit'); - avatarEdit.addEventListener('click', () => { - new PopupAvatar().open(this.canvas, (_upload) => { - this.uploadAvatar = _upload; - this.handleChange(); - this.avatarElem.remove(); - }); + + this.avatarEdit = new AvatarEdit((_upload) => { + this.uploadAvatar = _upload; + this.handleChange(); + this.avatarElem.remove(); }); + this.scrollable.append(this.avatarEdit.container); + { const inputWrapper = document.createElement('div'); inputWrapper.classList.add('input-wrapper'); @@ -87,10 +87,21 @@ export default class AppEditProfileTab implements SliderTab { this.bioInput = this.bioInputField.input; inputWrapper.append(this.firstNameInputField.container, this.lastNameInputField.container, this.bioInputField.container); - avatarEdit.parentElement.insertBefore(inputWrapper, avatarEdit.nextElementSibling); + + const caption = document.createElement('div'); + caption.classList.add('caption'); + caption.innerHTML = 'Any details such as age, occupation or city. Example:
23 y.o. designer from San Francisco.'; + + this.scrollable.append(inputWrapper, caption); } + this.scrollable.append(document.createElement('hr')); + { + const h2 = document.createElement('div'); + h2.classList.add('sidebar-left-h2'); + h2.innerText = 'Username'; + const inputWrapper = document.createElement('div'); inputWrapper.classList.add('input-wrapper'); @@ -102,9 +113,16 @@ export default class AppEditProfileTab implements SliderTab { this.userNameInput = this.userNameInputField.input; inputWrapper.append(this.userNameInputField.container); - - const caption = this.profileUrlContainer.parentElement; - caption.parentElement.insertBefore(inputWrapper, caption); + + const caption = document.createElement('div'); + caption.classList.add('caption'); + caption.innerHTML = `You can choose a username on Telegram. If you do, other people will be able to find you by this username and contact you without knowing your phone number.

You can use a-z, 0-9 and underscores. Minimum length is 5 characters.

This link opens a chat with you: +
`; + + this.profileUrlContainer = caption.querySelector('.profile-url-container'); + this.profileUrlAnchor = this.profileUrlContainer.lastElementChild as HTMLAnchorElement; + + this.scrollable.append(h2, inputWrapper, caption); } let userNameLabel = this.userNameInput.nextElementSibling as HTMLLabelElement; @@ -173,7 +191,7 @@ export default class AppEditProfileTab implements SliderTab { let promises: Promise[] = []; promises.push(appProfileManager.updateProfile(this.firstNameInputField.value, this.lastNameInputField.value, this.bioInputField.value).then(() => { - appSidebarLeft.selectTab(0); + this.slider.selectTab(0); }, (err) => { console.error('updateProfile error:', err); })); @@ -192,8 +210,6 @@ export default class AppEditProfileTab implements SliderTab { this.nextBtn.removeAttribute('disabled'); }); }); - - let scrollable = new Scrollable(this.scrollWrapper as HTMLElement); } public fillElements() { @@ -230,7 +246,7 @@ export default class AppEditProfileTab implements SliderTab { this.avatarElem.setAttribute('peer', '' + rootScope.myId); if(!this.avatarElem.parentElement) { - this.canvas.parentElement.append(this.avatarElem); + this.avatarEdit.container.append(this.avatarElem); } this.uploadAvatar = null; diff --git a/src/components/sidebarLeft/tabs/settings.ts b/src/components/sidebarLeft/tabs/settings.ts index b2f519e0..948d56f7 100644 --- a/src/components/sidebarLeft/tabs/settings.ts +++ b/src/components/sidebarLeft/tabs/settings.ts @@ -37,7 +37,7 @@ export default class AppSettingsTab implements SliderTab { this.buttons.edit.addEventListener('click', () => { appSidebarLeft.editProfileTab.fillElements(); - appSidebarLeft.selectTab(AppSidebarLeft.SLIDERITEMSIDS.editProfile); + appSidebarLeft.editProfileTab.open(); }); this.buttons.folders.addEventListener('click', () => { diff --git a/src/components/slider.ts b/src/components/slider.ts index ae8b3a68..20737da0 100644 --- a/src/components/slider.ts +++ b/src/components/slider.ts @@ -1,5 +1,8 @@ import { attachClickEvent } from "../helpers/dom"; import { horizontalMenu } from "./horizontalMenu"; +import ButtonIcon from "./buttonIcon"; +import Scrollable from "./scrollable"; +import { p } from "../mock/srp"; export interface SliderTab { onOpen?: () => void, @@ -8,15 +11,42 @@ export interface SliderTab { onCloseAfterTimeout?: () => void } -export class SuperSliderTab implements SliderTab { - public id: number; +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; // fix incompability public onOpen: SliderTab['onOpen']; - constructor(protected slider: SidebarSlider, public container: HTMLElement) { - this.closeBtn = this.container.querySelector('.sidebar-close-button'); + constructor(protected slider: SidebarSlider) { + 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('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); } @@ -34,9 +64,11 @@ const TRANSITION_TIME = 250; export default class SidebarSlider { protected _selectTab: (id: number) => void; public historyTabIds: number[] = []; + public tabsContainer: HTMLElement; constructor(public sidebarEl: HTMLElement, public tabs: {[id: number]: SliderTab} = {}, private canHideFirst = false) { - this._selectTab = horizontalMenu(null, this.sidebarEl.querySelector('.sidebar-slider') as HTMLDivElement, null, null, TRANSITION_TIME); + this.tabsContainer = this.sidebarEl.querySelector('.sidebar-slider'); + this._selectTab = horizontalMenu(null, this.tabsContainer as HTMLDivElement, null, null, TRANSITION_TIME); if(!canHideFirst) { this._selectTab(0); } @@ -58,8 +90,8 @@ export default class SidebarSlider { return true; }; - public selectTab(id: number | SuperSliderTab): boolean { - if(id instanceof SuperSliderTab) { + public selectTab(id: number | SliderSuperTab): boolean { + if(id instanceof SliderSuperTab) { id = id.id; } @@ -105,13 +137,13 @@ export default class SidebarSlider { } } - public addTab(tab: SuperSliderTab) { + public addTab(tab: SliderSuperTab) { let id: number; if(tab.container.parentElement) { - id = Array.from(this.sidebarEl.children).findIndex(el => el === tab.container); + id = Array.from(this.tabsContainer.children).findIndex(el => el === tab.container); } else { - id = this.sidebarEl.childElementCount; - this.sidebarEl.append(tab.container); + id = this.tabsContainer.childElementCount; + this.tabsContainer.append(tab.container); if(tab.closeBtn) { tab.closeBtn.addEventListener('click', () => this.closeTab()); diff --git a/src/index.hbs b/src/index.hbs index 0921f39f..31bcb5aa 100644 --- a/src/index.hbs +++ b/src/index.hbs @@ -246,27 +246,6 @@ -