|
|
@ -45,10 +45,12 @@ class ListLoader<T> { |
|
|
|
public current: T; |
|
|
|
public current: T; |
|
|
|
public previous: T[] = []; |
|
|
|
public previous: T[] = []; |
|
|
|
public next: T[] = []; |
|
|
|
public next: T[] = []; |
|
|
|
|
|
|
|
public count: number; |
|
|
|
|
|
|
|
|
|
|
|
public tempId = 0; |
|
|
|
public tempId = 0; |
|
|
|
public loadMore: (anchor: T, older: boolean) => Promise<ListLoaderResult<T>>; |
|
|
|
public loadMore: (anchor: T, older: boolean) => Promise<ListLoaderResult<T>>; |
|
|
|
public processItem: (item: any) => false | T; |
|
|
|
public processItem: (item: any) => false | T; |
|
|
|
|
|
|
|
public onJump: (item: T, older: boolean) => void; |
|
|
|
public loadCount = 50; |
|
|
|
public loadCount = 50; |
|
|
|
public reverse = false; // reverse means next = higher msgid
|
|
|
|
public reverse = false; // reverse means next = higher msgid
|
|
|
|
|
|
|
|
|
|
|
@ -61,23 +63,39 @@ class ListLoader<T> { |
|
|
|
loadMore: ListLoader<T>['loadMore'], |
|
|
|
loadMore: ListLoader<T>['loadMore'], |
|
|
|
loadCount: ListLoader<T>['loadCount'], |
|
|
|
loadCount: ListLoader<T>['loadCount'], |
|
|
|
processItem?: ListLoader<T>['processItem'], |
|
|
|
processItem?: ListLoader<T>['processItem'], |
|
|
|
|
|
|
|
onJump: ListLoader<T>['onJump'], |
|
|
|
}) { |
|
|
|
}) { |
|
|
|
safeAssign(this, options); |
|
|
|
safeAssign(this, options); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
get index() { |
|
|
|
|
|
|
|
return this.count !== undefined ? this.previous.length : -1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public go(length: number) { |
|
|
|
public go(length: number) { |
|
|
|
let items: T[], item: T; |
|
|
|
let items: T[], item: T; |
|
|
|
if(length > 0) { |
|
|
|
if(length > 0) { |
|
|
|
items = this.next.splice(0, length); |
|
|
|
items = this.next.splice(0, length); |
|
|
|
item = items.pop(); |
|
|
|
item = items.pop(); |
|
|
|
this.previous.push(...items); |
|
|
|
if(!item) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.previous.push(this.current, ...items); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
items = this.previous.splice(this.previous.length - length, length); |
|
|
|
items = this.previous.splice(this.previous.length + length, -length); |
|
|
|
item = items.shift(); |
|
|
|
item = items.shift(); |
|
|
|
this.next.unshift(...items); |
|
|
|
if(!item) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.next.unshift(...items, this.current); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.current = item; |
|
|
|
|
|
|
|
this.onJump(item, length > 0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public load(older: boolean) { |
|
|
|
public load(older: boolean) { |
|
|
@ -103,6 +121,10 @@ class ListLoader<T> { |
|
|
|
else this.loadedAllUp = true; |
|
|
|
else this.loadedAllUp = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(this.count === undefined) { |
|
|
|
|
|
|
|
this.count = result.count || result.items.length; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const method = older ? result.items.forEach.bind(result.items) : forEachReverse.bind(null, result.items); |
|
|
|
const method = older ? result.items.forEach.bind(result.items) : forEachReverse.bind(null, result.items); |
|
|
|
method((item: any) => { |
|
|
|
method((item: any) => { |
|
|
|
const processed = this.processItem ? this.processItem(item) : item; |
|
|
|
const processed = this.processItem ? this.processItem(item) : item; |
|
|
@ -134,6 +156,7 @@ class PeerProfileAvatars { |
|
|
|
public container: HTMLElement; |
|
|
|
public container: HTMLElement; |
|
|
|
public avatars: HTMLElement; |
|
|
|
public avatars: HTMLElement; |
|
|
|
public info: HTMLElement; |
|
|
|
public info: HTMLElement; |
|
|
|
|
|
|
|
public tabs: HTMLDivElement; |
|
|
|
public listLoader: ListLoader<string>; |
|
|
|
public listLoader: ListLoader<string>; |
|
|
|
public peerId: number; |
|
|
|
public peerId: number; |
|
|
|
|
|
|
|
|
|
|
@ -147,7 +170,10 @@ class PeerProfileAvatars { |
|
|
|
this.info = document.createElement('div'); |
|
|
|
this.info = document.createElement('div'); |
|
|
|
this.info.classList.add(PeerProfileAvatars.BASE_CLASS + '-info'); |
|
|
|
this.info.classList.add(PeerProfileAvatars.BASE_CLASS + '-info'); |
|
|
|
|
|
|
|
|
|
|
|
this.container.append(this.avatars, this.info); |
|
|
|
this.tabs = document.createElement('div'); |
|
|
|
|
|
|
|
this.tabs.classList.add(PeerProfileAvatars.BASE_CLASS + '-tabs'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.container.append(this.avatars, this.info, this.tabs); |
|
|
|
|
|
|
|
|
|
|
|
attachClickEvent(this.container, (_e) => { |
|
|
|
attachClickEvent(this.container, (_e) => { |
|
|
|
const rect = this.container.getBoundingClientRect(); |
|
|
|
const rect = this.container.getBoundingClientRect(); |
|
|
@ -158,9 +184,7 @@ class PeerProfileAvatars { |
|
|
|
const centerX = rect.right - (rect.width / 2); |
|
|
|
const centerX = rect.right - (rect.width / 2); |
|
|
|
const toRight = x > centerX; |
|
|
|
const toRight = x > centerX; |
|
|
|
|
|
|
|
|
|
|
|
const id = this.listLoader.previous.length; |
|
|
|
this.listLoader.go(toRight ? 1 : -1); |
|
|
|
const nextId = Math.max(0, id + (toRight ? 1 : -1)); |
|
|
|
|
|
|
|
this.avatars.style.transform = `translateX(-${100 * nextId}%)`; |
|
|
|
|
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -183,7 +207,18 @@ class PeerProfileAvatars { |
|
|
|
}; |
|
|
|
}; |
|
|
|
}); |
|
|
|
}); |
|
|
|
}, |
|
|
|
}, |
|
|
|
processItem: this.processItem |
|
|
|
processItem: this.processItem, |
|
|
|
|
|
|
|
onJump: (item, older) => { |
|
|
|
|
|
|
|
const id = this.listLoader.index; |
|
|
|
|
|
|
|
//const nextId = Math.max(0, id);
|
|
|
|
|
|
|
|
this.avatars.style.transform = `translateX(-${100 * id}%)`; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const activeTab = this.tabs.querySelector('.active'); |
|
|
|
|
|
|
|
if(activeTab) activeTab.classList.remove('active'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const tab = this.tabs.children[id] as HTMLElement; |
|
|
|
|
|
|
|
tab.classList.add('active'); |
|
|
|
|
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
listLoader.current = (photo as UserProfilePhoto.userProfilePhoto).photo_id; |
|
|
|
listLoader.current = (photo as UserProfilePhoto.userProfilePhoto).photo_id; |
|
|
@ -192,6 +227,16 @@ class PeerProfileAvatars { |
|
|
|
listLoader.load(true); |
|
|
|
listLoader.load(true); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public addTab() { |
|
|
|
|
|
|
|
const tab = document.createElement('div'); |
|
|
|
|
|
|
|
tab.classList.add(PeerProfileAvatars.BASE_CLASS + '-tab'); |
|
|
|
|
|
|
|
this.tabs.append(tab); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(this.tabs.childElementCount === 1) { |
|
|
|
|
|
|
|
tab.classList.add('active'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public processItem = (photoId: string) => { |
|
|
|
public processItem = (photoId: string) => { |
|
|
|
const avatar = document.createElement('div'); |
|
|
|
const avatar = document.createElement('div'); |
|
|
|
avatar.classList.add(PeerProfileAvatars.BASE_CLASS + '-avatar'); |
|
|
|
avatar.classList.add(PeerProfileAvatars.BASE_CLASS + '-avatar'); |
|
|
@ -211,13 +256,15 @@ class PeerProfileAvatars { |
|
|
|
|
|
|
|
|
|
|
|
this.avatars.append(avatar); |
|
|
|
this.avatars.append(avatar); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.addTab(); |
|
|
|
|
|
|
|
|
|
|
|
return photoId; |
|
|
|
return photoId; |
|
|
|
}; |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
class PeerProfile { |
|
|
|
class PeerProfile { |
|
|
|
public element: HTMLElement; |
|
|
|
public element: HTMLElement; |
|
|
|
private avatars: PeerProfileAvatars; |
|
|
|
public avatars: PeerProfileAvatars; |
|
|
|
private avatar: AvatarElement; |
|
|
|
private avatar: AvatarElement; |
|
|
|
private section: SettingSection; |
|
|
|
private section: SettingSection; |
|
|
|
private name: HTMLDivElement; |
|
|
|
private name: HTMLDivElement; |
|
|
@ -244,8 +291,6 @@ class PeerProfile { |
|
|
|
noDelimiter: true |
|
|
|
noDelimiter: true |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
this.avatars = new PeerProfileAvatars(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.avatar = new AvatarElement(); |
|
|
|
this.avatar = new AvatarElement(); |
|
|
|
this.avatar.classList.add('profile-avatar', 'avatar-120'); |
|
|
|
this.avatar.classList.add('profile-avatar', 'avatar-120'); |
|
|
|
this.avatar.setAttribute('dialog', '1'); |
|
|
|
this.avatar.setAttribute('dialog', '1'); |
|
|
@ -301,9 +346,8 @@ class PeerProfile { |
|
|
|
checkboxField: new CheckboxField({text: 'Notifications'}) |
|
|
|
checkboxField: new CheckboxField({text: 'Notifications'}) |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
this.section.content.append(/* this.name, this.subtitle, */ |
|
|
|
this.section.content.append(this.phone.container, this.username.container, this.bio.container, this.notifications.container); |
|
|
|
this.phone.container, this.username.container, this.bio.container, this.notifications.container); |
|
|
|
this.element.append(this.section.container); |
|
|
|
this.element.append(this.avatars.container, this.section.container); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.notifications.checkboxField.input.addEventListener('change', (e) => { |
|
|
|
this.notifications.checkboxField.input.addEventListener('change', (e) => { |
|
|
|
if(!e.isTrusted) { |
|
|
|
if(!e.isTrusted) { |
|
|
@ -376,6 +420,31 @@ class PeerProfile { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public setAvatar() { |
|
|
|
|
|
|
|
const photo = appPeersManager.getPeerPhoto(this.peerId); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(photo) { |
|
|
|
|
|
|
|
const oldAvatars = this.avatars; |
|
|
|
|
|
|
|
this.avatars = new PeerProfileAvatars(); |
|
|
|
|
|
|
|
this.avatars.setPeer(this.peerId); |
|
|
|
|
|
|
|
this.avatars.info.append(this.name, this.subtitle); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.avatar.remove(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(oldAvatars) oldAvatars.container.replaceWith(this.avatars.container); |
|
|
|
|
|
|
|
else this.element.prepend(this.avatars.container); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if(this.avatars) { |
|
|
|
|
|
|
|
this.avatars.container.remove(); |
|
|
|
|
|
|
|
this.avatars = undefined; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.avatar.setAttribute('peer', '' + this.peerId); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.section.content.prepend(this.avatar, this.name, this.subtitle); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public fillProfileElements() { |
|
|
|
public fillProfileElements() { |
|
|
|
if(!this.cleaned) return; |
|
|
|
if(!this.cleaned) return; |
|
|
|
this.cleaned = false; |
|
|
|
this.cleaned = false; |
|
|
@ -384,10 +453,7 @@ class PeerProfile { |
|
|
|
|
|
|
|
|
|
|
|
this.cleanupHTML(); |
|
|
|
this.cleanupHTML(); |
|
|
|
|
|
|
|
|
|
|
|
this.avatar.setAttribute('peer', '' + peerId); |
|
|
|
this.setAvatar(); |
|
|
|
|
|
|
|
|
|
|
|
this.avatars.setPeer(peerId); |
|
|
|
|
|
|
|
this.avatars.info.append(this.name, this.subtitle); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// username
|
|
|
|
// username
|
|
|
|
if(peerId !== rootScope.myId) { |
|
|
|
if(peerId !== rootScope.myId) { |
|
|
@ -574,6 +640,15 @@ export default class AppSharedMediaTab extends SliderSuperTab { |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.scrollable.container.addEventListener('scroll', () => { |
|
|
|
|
|
|
|
if(this.profile.avatars) { |
|
|
|
|
|
|
|
const scrollTop = this.scrollable.scrollTop; |
|
|
|
|
|
|
|
const y = scrollTop / 2; |
|
|
|
|
|
|
|
this.profile.avatars.avatars.style.transform = `translateY(${y}px)`; |
|
|
|
|
|
|
|
//this.profile.avatars.tabs.style.transform = `translateY(${scrollTop}px)`;
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
const transition = TransitionSlider(transitionContainer, 'slide-fade', 400, null, false); |
|
|
|
const transition = TransitionSlider(transitionContainer, 'slide-fade', 400, null, false); |
|
|
|
|
|
|
|
|
|
|
|
transition(0); |
|
|
|
transition(0); |
|
|
|