Refactor edit profile tab
This commit is contained in:
parent
5f3c48db65
commit
163185d021
25
src/components/avatarEdit.ts
Normal file
25
src/components/avatarEdit.ts
Normal file
@ -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<InputFile>) => 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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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');
|
||||
|
@ -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<InputFile> = 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<InputFile> = 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:<br>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.<br><br>You can use a-z, 0-9 and underscores. Minimum length is 5 characters.<br><br><div class="profile-url-container">This link opens a chat with you:
|
||||
<br><a class="profile-url" href="#" target="_blank"></a></div>`;
|
||||
|
||||
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<any>[] = [];
|
||||
|
||||
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;
|
||||
|
@ -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', () => {
|
||||
|
@ -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());
|
||||
|
@ -246,27 +246,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-slider-item edit-profile-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon tgico-back sidebar-close-button"></button>
|
||||
<div class="sidebar-header__title">Edit Profile</div>
|
||||
</div>
|
||||
<div class="sidebar-content">
|
||||
<div class="scroll-wrapper">
|
||||
<div class="avatar-edit">
|
||||
<canvas class="avatar-edit-canvas"></canvas>
|
||||
<span class="tgico tgico-cameraadd"></span>
|
||||
</div>
|
||||
<div class="caption">Any details such as age, occupation or city. Example:<br>23 y.o. designer from San Francisco.</div>
|
||||
<hr/>
|
||||
<div class="sidebar-left-h2">Username</div>
|
||||
<div class="caption">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.<br><br>You can use a-z, 0-9 and underscores. Minimum length is 5 characters.<br><br><div class="profile-url-container">This link opens a chat with you:
|
||||
<br><a class="profile-url" href="#" target="_blank"></a></div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn-circle rp btn-corner tgico-check"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-slider-item chat-folders-container">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon tgico-back sidebar-close-button"></button>
|
||||
|
@ -58,6 +58,15 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-padding {
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
html.is-safari &-padding {
|
||||
margin-right: -6px;
|
||||
}
|
||||
|
||||
/* &-sentinel {
|
||||
position: relative;
|
||||
left: 0;
|
||||
|
Loading…
Reference in New Issue
Block a user