virtual chatlist scroll (care)
This commit is contained in:
parent
fad3455a2e
commit
b9a86781ca
|
@ -242,7 +242,16 @@
|
|||
</div>
|
||||
<div class="chats-container sidebar sidebar-left">
|
||||
<div class="sidebar-header">
|
||||
<button class="btn-icon tgico-menu rp sidebar-tools-button"></button>
|
||||
<div class="btn-icon tgico-menu btn-menu-toggle rp sidebar-tools-button">
|
||||
<div class="btn-menu bottom-right">
|
||||
<div class="btn-menu-item menu-new-group tgico-newgroup rp">New Group</div>
|
||||
<div class="btn-menu-item menu-contacts tgico-user rp">Contacts</div>
|
||||
<div class="btn-menu-item menu-archive tgico-archive rp">Archived</div>
|
||||
<div class="btn-menu-item menu-saved tgico-savedmessages rp">Saved</div>
|
||||
<div class="btn-menu-item menu-settings tgico-settings rp">Settings</div>
|
||||
<div class="btn-menu-item menu-help tgico-help rp">Help</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-search">
|
||||
<input type="text" placeholder="Search" id="global-search" />
|
||||
<span class="tgico tgico-search"></span>
|
||||
|
@ -254,8 +263,7 @@
|
|||
<ul id="dialogs-pinned"></ul>
|
||||
<ul id="dialogs"></ul>
|
||||
</div>
|
||||
<div id="search-container">
|
||||
|
||||
<div id="search-container" class="active">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -278,15 +286,7 @@
|
|||
<div class="pinned-message-border"></div>
|
||||
<div class="pinned-message-content">
|
||||
<div class="pinned-message-title">Pinned Message</div>
|
||||
<div class="pinned-message-subtitle">At the end of this stage, all apps should be able to:
|
||||
+ Update the chat list and receive new messages.
|
||||
+ Send new messages.
|
||||
- Display and send media, files and links. (2 дня)
|
||||
+ Display a 'Shared media' section for chats that includes photos and files. (2 дня максимум)
|
||||
+ Display stickers, send emoji and stickers. (3 дня)
|
||||
+ Use the default camomile background in chats. (1 день)
|
||||
- Search for messages in chats. (2 дня (+фикс скролла))
|
||||
</div>
|
||||
<div class="pinned-message-subtitle"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -294,7 +294,12 @@
|
|||
<div class="pinned-msg"></div>
|
||||
<div class="btn-icon rp tool-mute chat-mute-button" style="display: none;"></div>
|
||||
<div class="btn-icon rp tgico-search chat-search-button"></div>
|
||||
<div class="btn-icon rp tgico-more chat-more-button"></div>
|
||||
<div class="btn-icon btn-menu-toggle rp tgico-more chat-more-button">
|
||||
<div class="btn-menu bottom-left">
|
||||
<div class="btn-menu-item menu-mute rp">Mute</div>
|
||||
<div class="btn-menu-item menu-delete tgico-delete danger rp">Delete and Leave</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="bubbles">
|
||||
<div id="bubbles-inner"></div>
|
||||
|
@ -364,10 +369,10 @@
|
|||
|
||||
<button class="btn-icon rp tgico-edit sidebar-edit-button"></button>
|
||||
|
||||
<div class="btn-icon tgico-more rp sidebar-menu-button">
|
||||
<div class="sidebar-menu is-bottom-left">
|
||||
<button class="btn-icon rp tgico-edit sidebar-menu-edit"></button>
|
||||
<button class="sidebar-menu-block a-menu-item is-danger">Block</button>
|
||||
<div class="btn-icon tgico-more rp btn-menu-toggle">
|
||||
<div class="btn-menu bottom-left">
|
||||
<div class="btn-menu-item menu-mute rp">Mute</div>
|
||||
<div class="btn-menu-item menu-delete tgico-delete danger rp">Delete and Leave</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Binary file not shown.
|
@ -260,11 +260,9 @@
|
|||
<!-- <div class="tabs-container"> -->
|
||||
<div class="sidebar-content">
|
||||
<div id="chats-container">
|
||||
<ul id="dialogs-pinned"></ul>
|
||||
<ul id="dialogs"></ul>
|
||||
<ul id="dialogs"></ul>
|
||||
</div>
|
||||
<div id="search-container">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -376,62 +376,151 @@ export function scrollable(el: HTMLDivElement, x = false, y = true) {
|
|||
|
||||
let resize = () => {
|
||||
// @ts-ignore
|
||||
scrollHeight = container[scrollType];
|
||||
scrollSize = container[scrollType];
|
||||
|
||||
let rect = container.getBoundingClientRect();
|
||||
|
||||
// @ts-ignore
|
||||
height = rect[type];
|
||||
size = rect[type];
|
||||
|
||||
if(!height || height == scrollHeight) {
|
||||
thumbHeight = 0;
|
||||
if(!size || size == scrollSize) {
|
||||
thumbSize = 0;
|
||||
|
||||
// @ts-ignore
|
||||
thumb.style[type] = thumbHeight + 'px';
|
||||
thumb.style[type] = thumbSize + 'px';
|
||||
return;
|
||||
}
|
||||
//if(!height) return;
|
||||
|
||||
let divider = scrollHeight / height / 0.5;
|
||||
thumbHeight = height / divider;
|
||||
let divider = scrollSize / size / 0.5;
|
||||
thumbSize = size / divider;
|
||||
|
||||
if(thumbHeight < 20) thumbHeight = 20;
|
||||
if(thumbSize < 20) thumbSize = 20;
|
||||
|
||||
// @ts-ignore
|
||||
thumb.style[type] = thumbHeight + 'px';
|
||||
thumb.style[type] = thumbSize + 'px';
|
||||
|
||||
// @ts-ignore
|
||||
//console.log('onresize', thumb.style[type], thumbHeight, height);
|
||||
};
|
||||
|
||||
let scrollHeight = -1;
|
||||
let height = 0;
|
||||
let thumbHeight = 0;
|
||||
let scrollSize = -1;
|
||||
let size = 0;
|
||||
let thumbSize = 0;
|
||||
window.addEventListener('resize', resize);
|
||||
//container.addEventListener('DOMNodeInserted', resize);
|
||||
|
||||
container.addEventListener('scroll', (e) => {
|
||||
|
||||
let hiddenElements: {
|
||||
up: Element[],
|
||||
down: Element[]
|
||||
} = {
|
||||
up: [],
|
||||
down: []
|
||||
};
|
||||
|
||||
let paddings = {up: 0, down: 0};
|
||||
|
||||
let paddingTopDiv = document.createElement('div');
|
||||
paddingTopDiv.classList.add('scroll-padding');
|
||||
let paddingBottomDiv = document.createElement('div');
|
||||
paddingBottomDiv.classList.add('scroll-padding');
|
||||
|
||||
let onScroll = (e: Event) => {
|
||||
// @ts-ignore
|
||||
if(container[scrollType] != scrollHeight || thumbHeight == 0) {
|
||||
//let st = container[scrollSide];
|
||||
|
||||
// @ts-ignore
|
||||
if(container[scrollType] != scrollSize || thumbSize == 0) {
|
||||
resize();
|
||||
}
|
||||
|
||||
let splitUp = container.querySelector('ul');
|
||||
let children = Array.from(splitUp.children) as HTMLElement[];
|
||||
let firstVisible = -1, lastVisible = -1;
|
||||
let length = children.length;
|
||||
for(let i = 0; i < length; ++i) {
|
||||
let child = children[i];
|
||||
if(isElementInViewport(child)) {
|
||||
if(firstVisible < 0) firstVisible = i;
|
||||
lastVisible = i;
|
||||
}
|
||||
}
|
||||
|
||||
if(firstVisible > 0) {
|
||||
let sliced = children.slice(0, firstVisible);
|
||||
|
||||
for(let child of sliced) {
|
||||
paddings.up += child.scrollHeight;
|
||||
hiddenElements.up.push(child);
|
||||
child.parentElement.removeChild(child);
|
||||
}
|
||||
|
||||
//console.log('sliced up', sliced.length);
|
||||
|
||||
//sliced.forEach(child => child.style.display = 'none');
|
||||
paddingTopDiv.style.height = paddings.up + 'px';
|
||||
//console.log('onscroll need to add padding: ', paddings.up);
|
||||
} else if(hiddenElements.up.length) {
|
||||
while(isElementInViewport(paddingTopDiv) && paddings.up) {
|
||||
let child = hiddenElements.up.pop();
|
||||
|
||||
splitUp.prepend(child);
|
||||
|
||||
paddings.up -= child.scrollHeight;
|
||||
paddingTopDiv.style.height = paddings.up + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
if(lastVisible < (length - 1)) {
|
||||
let sliced = children.slice(lastVisible + 1).reverse();
|
||||
|
||||
for(let child of sliced) {
|
||||
paddings.down += child.scrollHeight;
|
||||
hiddenElements.down.unshift(child);
|
||||
child.parentElement.removeChild(child);
|
||||
}
|
||||
|
||||
//console.log('onscroll sliced down', sliced.length);
|
||||
|
||||
//sliced.forEach(child => child.style.display = 'none');
|
||||
paddingBottomDiv.style.height = paddings.down + 'px';
|
||||
//console.log('onscroll need to add padding: ', paddings.up);
|
||||
} else if(hiddenElements.down.length) {
|
||||
while(isElementInViewport(paddingBottomDiv) && paddings.down) {
|
||||
let child = hiddenElements.down.shift();
|
||||
|
||||
splitUp.append(child);
|
||||
|
||||
paddings.down -= child.scrollHeight;
|
||||
paddingBottomDiv.style.height = paddings.down + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
//console.log('onscroll', container, firstVisible, lastVisible, hiddenElements);
|
||||
|
||||
// @ts-ignore
|
||||
let value = container[scrollSide] / (scrollHeight - height) * 100;
|
||||
let maxValue = 100 - (thumbHeight / height * 100);
|
||||
let value = container[scrollSide] / (scrollSize - size) * 100;
|
||||
let maxValue = 100 - (thumbSize / size * 100);
|
||||
|
||||
//console.log('onscroll', container.scrollHeight, thumbHeight, height, value, maxValue);
|
||||
|
||||
// @ts-ignore
|
||||
thumb.style[side] = (value >= maxValue ? maxValue : value) + '%';
|
||||
});
|
||||
|
||||
//lastScrollPos = st;
|
||||
};
|
||||
|
||||
let lastScrollPos = 0;
|
||||
container.addEventListener('scroll', onScroll);
|
||||
|
||||
container.append(paddingTopDiv);
|
||||
Array.from(el.children).forEach(c => container.append(c));
|
||||
container.append(paddingBottomDiv);
|
||||
|
||||
el.append(container);//container.append(el);
|
||||
container.parentElement.append(thumb);
|
||||
resize();
|
||||
return container;
|
||||
return {container, hiddenElements, onScroll};
|
||||
}
|
||||
|
||||
export function wrapPhoto(this: AppImManager, photo: any, message: any, container: HTMLDivElement) {
|
||||
|
|
|
@ -13,6 +13,7 @@ import appStickersManager, { MTStickerSet } from "../lib/appManagers/appStickers
|
|||
import { AppImManager } from "../lib/appManagers/appImManager";
|
||||
import { AppMessagesManager } from "../lib/appManagers/appMessagesManager";
|
||||
import appSidebarRight from "../lib/appManagers/appSidebarRight";
|
||||
import appSidebarLeft from "../lib/appManagers/appSidebarLeft";
|
||||
|
||||
const EMOTICONSSTICKERGROUP = 'emoticons-dropdown';
|
||||
|
||||
|
@ -175,7 +176,7 @@ let initEmoticonsDropdown = (pageEl: HTMLDivElement,
|
|||
|
||||
let prevCategoryIndex = 1;
|
||||
let menu = contentEmojiDiv.nextElementSibling as HTMLUListElement;
|
||||
let emojiScroll = scrollable(contentEmojiDiv);
|
||||
let emojiScroll = scrollable(contentEmojiDiv).container;
|
||||
emojiScroll.addEventListener('scroll', (e) => {
|
||||
prevCategoryIndex = emoticonsContentOnScroll(menu, heights, prevCategoryIndex, emojiScroll);
|
||||
});
|
||||
|
@ -341,7 +342,7 @@ let initEmoticonsDropdown = (pageEl: HTMLDivElement,
|
|||
});
|
||||
|
||||
let prevCategoryIndex = 0;
|
||||
let stickersScroll = scrollable(contentStickersDiv);
|
||||
let stickersScroll = scrollable(contentStickersDiv).container;
|
||||
stickersScroll.addEventListener('scroll', (e) => {
|
||||
lazyLoadQueue.check();
|
||||
lottieLoader.checkAnimations();
|
||||
|
@ -362,72 +363,15 @@ export default () => import('../lib/services').then(services => {
|
|||
|
||||
let {appImManager, appMessagesManager, appDialogsManager, apiUpdatesManager, appUsersManager} = services;
|
||||
//export default () => {
|
||||
let chatsContainer = document.getElementById('chats-container') as HTMLDivElement;
|
||||
let d = document.createElement('div');
|
||||
d.classList.add('preloader');
|
||||
putPreloader(d);
|
||||
chatsContainer.append(d);
|
||||
|
||||
let pageEl = document.body.getElementsByClassName('page-chats')[0] as HTMLDivElement;
|
||||
pageEl.style.display = '';
|
||||
|
||||
const loadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
|
||||
|
||||
let chatsScroll = scrollable(chatsContainer as HTMLDivElement);
|
||||
let sidebarScroll = scrollable(document.body.querySelector('.profile-container'));
|
||||
let chatScroll = scrollable(document.getElementById('bubbles') as HTMLDivElement);
|
||||
let sidebarScroll = scrollable(document.body.querySelector('.profile-container')).container;
|
||||
let chatScroll = scrollable(document.getElementById('bubbles') as HTMLDivElement).container;
|
||||
|
||||
apiUpdatesManager.attach();
|
||||
|
||||
let offsetIndex = 0;
|
||||
let loadDialogsPromise: Promise<any>;
|
||||
let loadDialogs = async() => {
|
||||
if(loadDialogsPromise) return loadDialogsPromise;
|
||||
|
||||
chatsContainer.append(d);
|
||||
|
||||
//let offset = appMessagesManager.generateDialogIndex();/* appMessagesManager.dialogsNum */;
|
||||
|
||||
try {
|
||||
loadDialogsPromise = appMessagesManager.getConversations('', offsetIndex, loadCount);
|
||||
|
||||
let result = await loadDialogsPromise;
|
||||
|
||||
console.log('loaded ' + loadCount + ' dialogs by offset:', offsetIndex, result);
|
||||
|
||||
if(result && result.dialogs && result.dialogs.length) {
|
||||
offsetIndex = result.dialogs[result.dialogs.length - 1].index;
|
||||
result.dialogs.forEach((dialog: any) => {
|
||||
appDialogsManager.addDialog(dialog);
|
||||
});
|
||||
}
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
d.remove();
|
||||
loadDialogsPromise = undefined;
|
||||
};
|
||||
|
||||
let onScroll = () => {
|
||||
if(!loadDialogsPromise) {
|
||||
let d = Array.from(appDialogsManager.chatList.childNodes).slice(-5);
|
||||
for(let node of d) {
|
||||
if(isElementInViewport(node)) {
|
||||
loadDialogs();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//console.log('last 5 dialogs:', d);
|
||||
}
|
||||
};
|
||||
|
||||
chatsScroll.addEventListener('scroll', onScroll);
|
||||
window.addEventListener('resize', () => {
|
||||
setTimeout(onScroll, 0);
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
document.addEventListener('user_update', (e: CustomEvent) => {
|
||||
let userID = e.detail;
|
||||
|
@ -797,9 +741,8 @@ export default () => import('../lib/services').then(services => {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
loadDialogs().then(result => {
|
||||
onScroll();
|
||||
appSidebarLeft.loadDialogs().then(result => {
|
||||
appSidebarLeft.onChatsScroll();
|
||||
appImManager.setScroll(chatScroll);
|
||||
appSidebarRight.setScroll(sidebarScroll);
|
||||
});
|
||||
|
|
|
@ -51,7 +51,7 @@ export default () => {
|
|||
wrapper.appendChild(list);
|
||||
|
||||
//let wrapperScroll = OverlayScrollbars(wrapper, (window as any).scrollbarOptions);
|
||||
let wrapperScroll = scrollable(wrapper);
|
||||
scrollable(wrapper);
|
||||
|
||||
let initedSelect = false;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import apiManager from "../mtproto/apiManager";
|
||||
import apiFileManager from '../mtproto/apiFileManager';
|
||||
import { $rootScope, findUpTag } from "../utils";
|
||||
import { $rootScope, findUpTag, isElementInViewport } from "../utils";
|
||||
import appImManager from "./appImManager";
|
||||
import appPeersManager from './appPeersManager';
|
||||
import appMessagesManager from "./appMessagesManager";
|
||||
|
@ -20,11 +20,9 @@ type DialogDom = {
|
|||
};
|
||||
|
||||
export class AppDialogsManager {
|
||||
public pinnedChatList = document.getElementById('dialogs-pinned') as HTMLUListElement;
|
||||
public chatList = document.getElementById('dialogs') as HTMLUListElement;
|
||||
public chatsHidden: any;
|
||||
|
||||
|
||||
|
||||
public myID = 0;
|
||||
public doms: {[x: number]: any} = {};
|
||||
|
||||
|
@ -40,7 +38,6 @@ export class AppDialogsManager {
|
|||
|
||||
//let chatClosedDiv = document.getElementById('chat-closed');
|
||||
|
||||
this.setListClickListener(this.pinnedChatList);
|
||||
this.setListClickListener(this.chatList);
|
||||
}
|
||||
|
||||
|
@ -131,20 +128,54 @@ export class AppDialogsManager {
|
|||
}
|
||||
|
||||
public sortDom() {
|
||||
/* let sorted = */appMessagesManager.dialogsStorage.dialogs
|
||||
console.log('sortDom');
|
||||
//return;
|
||||
|
||||
let dialogs = appMessagesManager.dialogsStorage.dialogs;
|
||||
|
||||
let inUpper: HTMLLIElement[] = [];
|
||||
let inBottom: HTMLLIElement[] = [];
|
||||
|
||||
let lastPinnedIndex = -1;
|
||||
for(let i = 0; i < dialogs.length; ++i) {
|
||||
let dialog = dialogs[i];
|
||||
if(!dialog.pFlags.pinned) break;
|
||||
lastPinnedIndex = i;
|
||||
}
|
||||
|
||||
let sorted = dialogs
|
||||
.filter((d: any) => !d.pFlags.pinned)
|
||||
.sort((a: any, b: any) => {
|
||||
let timeA = appMessagesManager.getMessage(a.top_message).date;
|
||||
let timeB = appMessagesManager.getMessage(b.top_message).date;
|
||||
|
||||
return timeB - timeA;
|
||||
})
|
||||
.forEach((d: any) => {
|
||||
});
|
||||
|
||||
if(lastPinnedIndex != -1) {
|
||||
sorted = dialogs.slice(0, lastPinnedIndex + 1).concat(sorted);
|
||||
}
|
||||
|
||||
let inViewportIndex = -1;
|
||||
sorted.forEach((d: any) => {
|
||||
let dom = this.getDialogDom(d.peerID);
|
||||
if(!dom) return;
|
||||
|
||||
this.chatList.append(dom.listEl);
|
||||
if(this.chatsHidden.up.find((d: HTMLLIElement) => d === dom.listEl)) {
|
||||
inUpper.push(dom.listEl);
|
||||
} else if(isElementInViewport(dom.listEl)) {
|
||||
this.chatList.insertBefore(dom.listEl, this.chatList.children[++inViewportIndex]);
|
||||
} else if(this.chatsHidden.down.find((d: HTMLLIElement) => d === dom.listEl)) {
|
||||
inBottom.push(dom.listEl);
|
||||
} else {
|
||||
console.warn('found no dom!', dom);
|
||||
}
|
||||
|
||||
//this.chatList.append(dom.listEl);
|
||||
});
|
||||
|
||||
this.chatsHidden.up = inUpper;
|
||||
this.chatsHidden.down = inBottom;
|
||||
}
|
||||
|
||||
public setLastMessage(dialog: any, lastMessage?: any, dom?: DialogDom) {
|
||||
|
@ -405,7 +436,13 @@ export class AppDialogsManager {
|
|||
};
|
||||
|
||||
if(!container) {
|
||||
(dialog.pFlags.pinned ? this.pinnedChatList : this.chatList).append(li);
|
||||
this.chatList.append(li);
|
||||
//this.appendTo.push(li);
|
||||
|
||||
if(dialog.pFlags.pinned) {
|
||||
li.classList.add('dialog-pinned');
|
||||
}
|
||||
|
||||
this.doms[dialog.peerID] = dom;
|
||||
this.setLastMessage(dialog);
|
||||
} else {
|
||||
|
|
|
@ -132,6 +132,7 @@ export class AppImManager {
|
|||
|
||||
private topbar: HTMLDivElement = null;
|
||||
private chatInput: HTMLDivElement = null;
|
||||
scrolledAll: boolean;
|
||||
|
||||
constructor() {
|
||||
this.log = logger('IM');
|
||||
|
@ -380,30 +381,39 @@ export class AppImManager {
|
|||
|
||||
// load more history
|
||||
if(!this.getHistoryPromise && !this.getHistoryTimeout /* && false */) {
|
||||
let history = Object.keys(this.bubbles).map(id => +id).sort();
|
||||
/* let history = appMessagesManager.historiesStorage[this.peerID].history;
|
||||
let length = history.length; */
|
||||
this.getHistoryTimeout = setTimeout(() => { // must be
|
||||
let history = Object.keys(this.bubbles).map(id => +id).sort();
|
||||
|
||||
/* let history = appMessagesManager.historiesStorage[this.peerID].history;
|
||||
let length = history.length; */
|
||||
|
||||
// filter negative ids
|
||||
for(let i = 0; i < history.length; ++i) {
|
||||
if(history[i] <= 0) history.splice(i, 1);
|
||||
else break;
|
||||
}
|
||||
|
||||
this.getHistoryTimeout = 0;
|
||||
|
||||
let willLoad = false;
|
||||
for(let i = 0; i < 10; ++i) {
|
||||
let msgID = history[i];
|
||||
if(!(msgID in this.bubbles) || msgID <= 0) continue;
|
||||
if(!this.scrolledAll) {
|
||||
for(let i = 0; i < 10; ++i) {
|
||||
let msgID = history[i];
|
||||
|
||||
let bubble = this.bubbles[msgID];
|
||||
|
||||
if(isElementInViewport(bubble)) {
|
||||
willLoad = true;
|
||||
|
||||
let bubble = this.bubbles[msgID];
|
||||
|
||||
if(isElementInViewport(bubble)) {
|
||||
willLoad = true;
|
||||
|
||||
this.log('Will load more (up) history by id:', history[0], 'maxID:', history[history.length - 1], history, bubble);
|
||||
/* false && */!testScroll && this.getHistory(history[0], true).then(() => { // uncomment
|
||||
this.onScroll();
|
||||
}).catch(err => {
|
||||
this.log.warn('Could not load more history, err:', err);
|
||||
});
|
||||
|
||||
break;
|
||||
this.log('Will load more (up) history by id:', history[0], 'maxID:', history[history.length - 1], history, bubble);
|
||||
/* false && */!testScroll && this.getHistory(history[0], true).then(() => { // uncomment
|
||||
this.onScroll();
|
||||
}).catch(err => {
|
||||
this.log.warn('Could not load more history, err:', err);
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -413,8 +423,6 @@ export class AppImManager {
|
|||
if(!willLoad && history.indexOf(/* this.lastDialog */dialog.top_message) === -1) {
|
||||
let lastMsgIDs = history.slice(-10);
|
||||
for(let msgID of lastMsgIDs) {
|
||||
if(!(msgID in this.bubbles) || msgID <= 0) continue;
|
||||
|
||||
let bubble = this.bubbles[msgID];
|
||||
|
||||
if(isElementInViewport(bubble)) {
|
||||
|
@ -520,6 +528,7 @@ export class AppImManager {
|
|||
|
||||
public cleanup() {
|
||||
this.peerID = $rootScope.selectedPeerID = 0;
|
||||
this.scrolledAll = false;
|
||||
|
||||
if(this.lastContainerDiv) this.lastContainerDiv.remove();
|
||||
if(this.firstContainerDiv) this.firstContainerDiv.remove();
|
||||
|
@ -645,7 +654,7 @@ export class AppImManager {
|
|||
|
||||
appSidebarRight.fillProfileElements()
|
||||
]).catch(err => {
|
||||
this.log.error(err);
|
||||
this.log.error('setPeer promises error:', err);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1189,7 +1198,12 @@ export class AppImManager {
|
|||
if(!result || !result.history) {
|
||||
console.timeEnd('render history total');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// commented bot getProfile in getHistory!
|
||||
if(!result.history/* .filter((id: number) => id > 0) */.length && !isBackLimit) {
|
||||
this.scrolledAll = true;
|
||||
}
|
||||
|
||||
//this.chatInner.innerHTML = '';
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ export class AppMessagesIDsManager {
|
|||
}
|
||||
|
||||
public getMessageLocalID(fullMsgID: number) {
|
||||
if (!fullMsgID) {
|
||||
if(!fullMsgID) {
|
||||
return 0;
|
||||
}
|
||||
return fullMsgID % this.fullMsgIDModulus;
|
||||
|
|
|
@ -2418,7 +2418,7 @@ export class AppMessagesManager {
|
|||
|
||||
// need be commented for read out messages
|
||||
//if(newUnreadCount != 0 || !isOut) { // fix 16.11.2019 (maybe not)
|
||||
console.warn(dT(), 'cnt', peerID, newUnreadCount);
|
||||
console.warn(dT(), 'cnt', peerID, newUnreadCount, isOut, foundDialog, update, foundAffected);
|
||||
$rootScope.$broadcast('dialog_unread', {peerID: peerID, count: newUnreadCount});
|
||||
//}
|
||||
|
||||
|
@ -3068,7 +3068,7 @@ export class AppMessagesManager {
|
|||
public requestHistory(peerID: number, maxID: number, limit: number, offset = 0) {
|
||||
var isChannel = AppPeersManager.isChannel(peerID);
|
||||
|
||||
console.trace('requestHistory', peerID, maxID, limit, offset);
|
||||
//console.trace('requestHistory', peerID, maxID, limit, offset);
|
||||
|
||||
return MTProto.apiManager.invokeApi('messages.getHistory', {
|
||||
peer: AppPeersManager.getInputPeerByID(peerID),
|
||||
|
@ -3079,16 +3079,7 @@ export class AppMessagesManager {
|
|||
max_id: 0,
|
||||
min_id: 0,
|
||||
hash: 0
|
||||
}/* {
|
||||
peer: AppPeersManager.getInputPeerByID(peerID),
|
||||
offset_id: offset,
|
||||
offset_date: 0,
|
||||
add_offset: offset || 0,
|
||||
limit: limit || 0,
|
||||
max_id: maxID,
|
||||
min_id: 0,
|
||||
hash: 0
|
||||
} */, {
|
||||
}, {
|
||||
timeout: 300,
|
||||
noErrorBox: true
|
||||
}).then((historyResult: any) => {
|
||||
|
@ -3101,22 +3092,19 @@ export class AppMessagesManager {
|
|||
}
|
||||
|
||||
var length = historyResult.messages.length;
|
||||
if(length &&
|
||||
historyResult.messages[length - 1].deleted) {
|
||||
if(length && historyResult.messages[length - 1].deleted) {
|
||||
historyResult.messages.splice(length - 1, 1);
|
||||
length--;
|
||||
historyResult.count--;
|
||||
}
|
||||
|
||||
if(
|
||||
peerID < 0 ||
|
||||
!appUsersManager.isBot(peerID) ||
|
||||
(length == limit && limit < historyResult.count)
|
||||
) {
|
||||
// don't need the intro now
|
||||
/* if(peerID < 0 || !appUsersManager.isBot(peerID) || (length == limit && limit < historyResult.count)) {
|
||||
return historyResult;
|
||||
}
|
||||
} */
|
||||
return historyResult;
|
||||
|
||||
return appProfileManager.getProfile(peerID).then((userFull: any) => {
|
||||
/* return appProfileManager.getProfile(peerID).then((userFull: any) => {
|
||||
var description = userFull.bot_info && userFull.bot_info.description;
|
||||
if(description) {
|
||||
var messageID = this.tempID--;
|
||||
|
@ -3142,7 +3130,7 @@ export class AppMessagesManager {
|
|||
}
|
||||
|
||||
return historyResult;
|
||||
});
|
||||
}); */
|
||||
}, (error) => {
|
||||
switch (error.type) {
|
||||
case 'CHANNEL_PRIVATE':
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { logger } from "../polyfill";
|
||||
import { scrollable } from "../../components/misc";
|
||||
import { scrollable, putPreloader } from "../../components/misc";
|
||||
import appMessagesManager from "./appMessagesManager";
|
||||
import appDialogsManager from "./appDialogsManager";
|
||||
import { isElementInViewport, $rootScope } from "../utils";
|
||||
import { isElementInViewport } from "../utils";
|
||||
import appMessagesIDsManager from "./appMessagesIDsManager";
|
||||
import apiManager from '../mtproto/apiManager';
|
||||
import appImManager from "./appImManager";
|
||||
|
||||
class AppSidebarLeft {
|
||||
|
@ -12,13 +11,22 @@ class AppSidebarLeft {
|
|||
private searchInput = document.getElementById('global-search') as HTMLInputElement;
|
||||
private toolsBtn = this.sidebarEl.querySelector('.sidebar-tools-button') as HTMLButtonElement;
|
||||
private searchContainer = this.sidebarEl.querySelector('#search-container') as HTMLDivElement;
|
||||
|
||||
|
||||
private menuEl = this.toolsBtn.querySelector('.btn-menu');
|
||||
private savedBtn = this.menuEl.querySelector('.menu-saved');
|
||||
|
||||
private listsContainer: HTMLDivElement = null;
|
||||
private searchMessagesList: HTMLUListElement = null;
|
||||
|
||||
private chatsContainer = document.getElementById('chats-container') as HTMLDivElement;
|
||||
private chatsOffsetIndex = 0;
|
||||
private chatsScroll: HTMLDivElement;
|
||||
private chatsHidden: any;
|
||||
private chatsPreloader: HTMLDivElement;
|
||||
private chatsLoadCount = 0;
|
||||
private loadDialogsPromise: Promise<any>;
|
||||
private hiddenScroll: any;
|
||||
|
||||
private log = logger('SL');
|
||||
|
||||
private peerID = 0;
|
||||
|
@ -29,31 +37,43 @@ class AppSidebarLeft {
|
|||
|
||||
private searchPromise: Promise<void> = null;
|
||||
private searchTimeout: number = 0;
|
||||
|
||||
|
||||
private query = '';
|
||||
|
||||
public myID = 0;
|
||||
|
||||
constructor() {
|
||||
this.listsContainer = scrollable(this.searchContainer);
|
||||
this.chatsPreloader = document.createElement('div');
|
||||
this.chatsPreloader.classList.add('preloader');
|
||||
putPreloader(this.chatsPreloader);
|
||||
this.chatsContainer.append(this.chatsPreloader);
|
||||
|
||||
this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
|
||||
|
||||
let {container: chatsScroll, hiddenElements: chatsHidden, onScroll: hiddenScroll} = scrollable(this.chatsContainer as HTMLDivElement);
|
||||
this.chatsScroll = chatsScroll;
|
||||
this.chatsHidden = chatsHidden;
|
||||
this.hiddenScroll = hiddenScroll;
|
||||
|
||||
appDialogsManager.chatsHidden = this.chatsHidden;
|
||||
|
||||
chatsScroll.addEventListener('scroll', this.onChatsScroll.bind(this));
|
||||
|
||||
this.listsContainer = scrollable(this.searchContainer).container;
|
||||
this.searchMessagesList = document.createElement('ul');
|
||||
|
||||
apiManager.getUserID().then((id) => {
|
||||
this.myID = id;
|
||||
});
|
||||
|
||||
$rootScope.$on('user_auth', (e: CustomEvent) => {
|
||||
let userAuth = e.detail;
|
||||
this.myID = userAuth ? userAuth.id : 0;
|
||||
});
|
||||
|
||||
|
||||
this.savedBtn.addEventListener('click', () => {
|
||||
appImManager.setPeer(this.myID);
|
||||
appImManager.setPeer(appImManager.myID);
|
||||
});
|
||||
|
||||
/* this.listsContainer.insertBefore(this.searchMessagesList, this.listsContainer.lastElementChild);
|
||||
for(let i = 0; i < 25; ++i) {
|
||||
let li = document.createElement('li');
|
||||
li.innerHTML = `<div class="user-avatar is-online" style="font-size: 0px;"><img src="assets/img/camomile.jpg"></div><div class="user-caption"><p><span class="user-title">Влад</span><span><span class="message-status"></span><span class="message-time">14:41</span></span></p><p><span class="user-last-message">это важно</span><span class="tgico-pinnedchat"></span></p></div><div class="c-ripple"><span class="c-ripple__circle" style="top: 65px; left: 338.5px;"></span></div>`;
|
||||
this.searchMessagesList.append(li);
|
||||
} */
|
||||
|
||||
this.listsContainer.addEventListener('scroll', this.onSidebarScroll.bind(this));
|
||||
|
||||
this.searchContainer.append(this.listsContainer);
|
||||
//this.searchContainer.append(this.listsContainer);
|
||||
|
||||
appDialogsManager.setListClickListener(this.searchMessagesList);
|
||||
|
||||
|
@ -85,13 +105,13 @@ class AppSidebarLeft {
|
|||
this.log('input', value);
|
||||
|
||||
if(this.listsContainer.contains(this.searchMessagesList)) {
|
||||
this.listsContainer.removeChild(this.searchMessagesList)
|
||||
this.listsContainer.removeChild(this.searchMessagesList);
|
||||
}
|
||||
|
||||
if(!value.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.query = value;
|
||||
this.minMsgID = 0;
|
||||
this.loadedCount = 0;
|
||||
|
@ -113,18 +133,70 @@ class AppSidebarLeft {
|
|||
this.peerID = 0;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
setTimeout(() => this.onSidebarScroll(), 0);
|
||||
this.chatsLoadCount = Math.round(document.body.scrollHeight / 70 * 1.5);
|
||||
|
||||
setTimeout(() => {
|
||||
this.onSidebarScroll();
|
||||
this.onChatsScroll();
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
public async loadDialogs() {
|
||||
if(this.loadDialogsPromise/* || 1 == 1 */) return this.loadDialogsPromise;
|
||||
|
||||
this.chatsContainer.append(this.chatsPreloader);
|
||||
|
||||
//let offset = appMessagesManager.generateDialogIndex();/* appMessagesManager.dialogsNum */;
|
||||
|
||||
try {
|
||||
this.loadDialogsPromise = appMessagesManager.getConversations('', this.chatsOffsetIndex, this.chatsLoadCount);
|
||||
|
||||
let result = await this.loadDialogsPromise;
|
||||
|
||||
if(result && result.dialogs && result.dialogs.length) {
|
||||
this.chatsOffsetIndex = result.dialogs[result.dialogs.length - 1].index;
|
||||
result.dialogs.forEach((dialog: any) => {
|
||||
appDialogsManager.addDialog(dialog);
|
||||
});
|
||||
}
|
||||
|
||||
this.log('loaded ' + this.chatsLoadCount + ' dialogs by offset:', this.chatsOffsetIndex, result, this.chatsHidden);
|
||||
this.hiddenScroll();
|
||||
} catch(err) {
|
||||
this.log.error(err);
|
||||
}
|
||||
|
||||
this.chatsPreloader.remove();
|
||||
this.loadDialogsPromise = undefined;
|
||||
}
|
||||
|
||||
public onChatsScroll() {
|
||||
if(this.chatsHidden.down.length > 0/* || 1 == 1 */) return;
|
||||
|
||||
if(!this.loadDialogsPromise) {
|
||||
let d = Array.from(appDialogsManager.chatList.childNodes).slice(-5);
|
||||
for(let node of d) {
|
||||
if(isElementInViewport(node)) {
|
||||
this.loadDialogs();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//console.log('last 5 dialogs:', d);
|
||||
}
|
||||
}
|
||||
|
||||
public onSidebarScroll() {
|
||||
if(!this.query.trim()) return;
|
||||
|
||||
let elements = Array.from(this.searchMessagesList.childNodes).slice(-5);
|
||||
for(let li of elements) {
|
||||
if(isElementInViewport(li)) {
|
||||
this.log('Will load more search');
|
||||
|
||||
|
||||
if(!this.searchTimeout) {
|
||||
this.searchTimeout = setTimeout(() => {
|
||||
this.searchMore();
|
||||
|
@ -144,21 +216,23 @@ class AppSidebarLeft {
|
|||
|
||||
this.searchInput.focus();
|
||||
}
|
||||
|
||||
|
||||
private searchMore() {
|
||||
if(this.searchPromise) return this.searchPromise;
|
||||
|
||||
|
||||
let query = this.query;
|
||||
|
||||
|
||||
if(!query.trim()) return;
|
||||
|
||||
if(this.loadedCount != 0 && this.loadedCount >= this.foundCount) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let maxID = appMessagesIDsManager.getMessageIDInfo(this.minMsgID)[0];
|
||||
|
||||
|
||||
return this.searchPromise = appMessagesManager.getSearch(this.peerID, query, null, maxID, 20, this.offsetRate).then(res => {
|
||||
this.searchPromise = null;
|
||||
|
||||
|
||||
if(this.searchInput.value != query) {
|
||||
return;
|
||||
}
|
||||
|
@ -166,7 +240,7 @@ class AppSidebarLeft {
|
|||
this.log('input search result:', this.peerID, query, null, maxID, 20, res);
|
||||
|
||||
let {count, history, next_rate} = res;
|
||||
|
||||
|
||||
if(history[0] == this.minMsgID) {
|
||||
history.shift();
|
||||
}
|
||||
|
@ -177,7 +251,7 @@ class AppSidebarLeft {
|
|||
|
||||
if(!originalDialog) {
|
||||
this.log('no original dialog by message:', message);
|
||||
|
||||
|
||||
originalDialog = {
|
||||
peerID: message.peerID,
|
||||
pFlags: {},
|
||||
|
@ -188,11 +262,11 @@ class AppSidebarLeft {
|
|||
let {dialog, dom} = appDialogsManager.addDialog(originalDialog, this.searchMessagesList, false);
|
||||
appDialogsManager.setLastMessage(dialog, message, dom);
|
||||
});
|
||||
|
||||
|
||||
this.minMsgID = history[history.length - 1];
|
||||
this.offsetRate = next_rate;
|
||||
this.loadedCount += history.length;
|
||||
|
||||
|
||||
if(!this.foundCount) {
|
||||
this.foundCount = count;
|
||||
}
|
||||
|
|
|
@ -65,6 +65,8 @@ class AppSidebarRight {
|
|||
|
||||
private log = logger('SR');
|
||||
|
||||
private peerID = 0;
|
||||
|
||||
constructor() {
|
||||
let container = this.profileContentEl.querySelector('.profile-tabs-content') as HTMLDivElement;
|
||||
let tabs = this.profileContentEl.querySelector('.profile-tabs') as HTMLUListElement;
|
||||
|
@ -143,7 +145,7 @@ class AppSidebarRight {
|
|||
}
|
||||
|
||||
public loadSidebarMedia(single = false) {
|
||||
let peerID = $rootScope.selectedPeerID;
|
||||
let peerID = this.peerID;
|
||||
|
||||
let typesToLoad = single ? [this.sharedMediaType] : this.sharedMediaTypes;
|
||||
|
||||
|
@ -164,14 +166,14 @@ class AppSidebarRight {
|
|||
? appMessagesManager.historiesStorage[peerID].history.slice() : [];
|
||||
|
||||
maxID = !maxID && ids.length ? ids[ids.length - 1] : maxID;
|
||||
this.log('search house of glass pre', type, ids, maxID);
|
||||
//this.log('search house of glass pre', type, ids, maxID);
|
||||
|
||||
return this.loadSidebarMediaPromises[type] = appMessagesManager.getSearch(peerID, '', {_: type}, maxID, 50)
|
||||
.then(value => {
|
||||
ids = ids.concat(value.history);
|
||||
history.push(...ids);
|
||||
|
||||
this.log('search house of glass', type, value, ids, this.cleared);
|
||||
//this.log('search house of glass', type, value, ids, this.cleared);
|
||||
|
||||
if($rootScope.selectedPeerID != peerID) {
|
||||
this.log.warn('peer changed');
|
||||
|
@ -188,11 +190,11 @@ class AppSidebarRight {
|
|||
let message = appMessagesManager.getMessage(mid);
|
||||
if(!message.media) return;
|
||||
|
||||
/* 'inputMessagesFilterContacts',
|
||||
'inputMessagesFilterPhotoVideo',
|
||||
'inputMessagesFilterDocument',
|
||||
'inputMessagesFilterUrl',
|
||||
'inputMessagesFilterVoice'*/
|
||||
/*'inputMessagesFilterContacts',
|
||||
'inputMessagesFilterPhotoVideo',
|
||||
'inputMessagesFilterDocument',
|
||||
'inputMessagesFilterUrl',
|
||||
'inputMessagesFilterVoice'*/
|
||||
switch(type) {
|
||||
case 'inputMessagesFilterPhotoVideo': {
|
||||
/* if(!(message.media.photo || message.media.document || message.media.webpage.document)) {
|
||||
|
@ -281,7 +283,8 @@ class AppSidebarRight {
|
|||
}
|
||||
|
||||
public fillProfileElements() {
|
||||
let peerID = $rootScope.selectedPeerID;
|
||||
let peerID = this.peerID = $rootScope.selectedPeerID;
|
||||
this.loadSidebarMediaPromises = {};
|
||||
|
||||
this.profileContentEl.parentElement.scrollTop = 0;
|
||||
this.profileElements.bio.style.display = 'none';
|
||||
|
@ -311,19 +314,19 @@ class AppSidebarRight {
|
|||
};
|
||||
|
||||
// username
|
||||
let username = appPeersManager.getPeerUsername($rootScope.selectedPeerID);
|
||||
let username = appPeersManager.getPeerUsername(peerID);
|
||||
if(username) {
|
||||
setText(appPeersManager.getPeerUsername($rootScope.selectedPeerID), this.profileElements.username);
|
||||
setText(appPeersManager.getPeerUsername(peerID), this.profileElements.username);
|
||||
}
|
||||
|
||||
if($rootScope.selectedPeerID > 0) {
|
||||
let user = appUsersManager.getUser($rootScope.selectedPeerID);
|
||||
if(peerID > 0) {
|
||||
let user = appUsersManager.getUser(peerID);
|
||||
if(user.phone) {
|
||||
setText('+' + formatPhoneNumber(user.phone).formatted, this.profileElements.phone);
|
||||
}
|
||||
|
||||
appProfileManager.getProfile($rootScope.selectedPeerID, true).then(userFull => {
|
||||
if($rootScope.selectedPeerID != peerID) {
|
||||
appProfileManager.getProfile(peerID, true).then(userFull => {
|
||||
if(this.peerID != peerID) {
|
||||
this.log.warn('peer changed');
|
||||
return;
|
||||
}
|
||||
|
@ -340,10 +343,10 @@ class AppSidebarRight {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
let chat = appPeersManager.getPeer($rootScope.selectedPeerID);
|
||||
let chat = appPeersManager.getPeer(peerID);
|
||||
|
||||
appProfileManager.getChatFull(chat.id).then((chatFull: any) => {
|
||||
if($rootScope.selectedPeerID != peerID) {
|
||||
if(this.peerID != peerID) {
|
||||
this.log.warn('peer changed');
|
||||
return;
|
||||
}
|
||||
|
@ -356,7 +359,7 @@ class AppSidebarRight {
|
|||
});
|
||||
}
|
||||
|
||||
let dialog: any = appMessagesManager.getDialogByPeerID($rootScope.selectedPeerID);
|
||||
let dialog: any = appMessagesManager.getDialogByPeerID(peerID);
|
||||
if(dialog.length) {
|
||||
dialog = dialog[0];
|
||||
let muted = false;
|
||||
|
@ -368,8 +371,8 @@ class AppSidebarRight {
|
|||
}
|
||||
}
|
||||
|
||||
if($rootScope.selectedPeerID < 0) { // not human
|
||||
let isChannel = appPeersManager.isChannel($rootScope.selectedPeerID) && !appPeersManager.isMegagroup($rootScope.selectedPeerID);
|
||||
if(peerID < 0) { // not human
|
||||
let isChannel = appPeersManager.isChannel(peerID) && !appPeersManager.isMegagroup(peerID);
|
||||
if(isChannel) {
|
||||
appImManager.btnMute.classList.remove('tgico-mute', 'tgico-unmute');
|
||||
appImManager.btnMute.classList.add(muted ? 'tgico-unmute' : 'tgico-mute');
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
@charset "UTF-8";
|
||||
|
||||
// Color
|
||||
@import "components/color-variables";
|
||||
@import "components/color-classes";
|
||||
|
||||
// Variables;
|
||||
@import "components/variables";
|
||||
|
||||
// Reset
|
||||
@import "components/normalize";
|
||||
|
||||
// components
|
||||
@import "components/global";
|
||||
@import "components/badges";
|
||||
@import "components/icons-material-design";
|
||||
@import "components/grid";
|
||||
@import "components/navbar";
|
||||
@import "components/typography";
|
||||
@import "components/transitions";
|
||||
@import "components/cards";
|
||||
@import "components/toast";
|
||||
@import "components/tabs";
|
||||
@import "components/tooltip";
|
||||
@import "components/buttons";
|
||||
@import "components/dropdown";
|
||||
@import "components/waves";
|
||||
@import "components/modal";
|
||||
@import "components/collapsible";
|
||||
@import "components/chips";
|
||||
@import "components/materialbox";
|
||||
@import "components/forms/forms";
|
||||
@import "components/table_of_contents";
|
||||
@import "components/sidenav";
|
||||
@import "components/preloader";
|
||||
@import "components/slider";
|
||||
@import "components/carousel";
|
||||
@import "components/tapTarget";
|
||||
@import "components/pulse";
|
||||
@import "components/datepicker";
|
||||
@import "components/timepicker";
|
|
@ -5,9 +5,14 @@
|
|||
.sidebar-content {
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex; /* idk why but need */
|
||||
position: relative;
|
||||
|
||||
> div {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
#chats-container {
|
||||
|
@ -29,7 +34,7 @@
|
|||
background: #fff;
|
||||
|
||||
&.active {
|
||||
display: block;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue