|
|
@ -52,7 +52,7 @@ export class AppImManager { |
|
|
|
public dateMessages: {[timestamp: number]: { |
|
|
|
public dateMessages: {[timestamp: number]: { |
|
|
|
div: HTMLDivElement, |
|
|
|
div: HTMLDivElement, |
|
|
|
firstTimestamp: number, |
|
|
|
firstTimestamp: number, |
|
|
|
bubble: HTMLDivElement, |
|
|
|
container: HTMLDivElement, |
|
|
|
timeout?: number |
|
|
|
timeout?: number |
|
|
|
}} = {}; |
|
|
|
}} = {}; |
|
|
|
public unreaded: number[] = []; |
|
|
|
public unreaded: number[] = []; |
|
|
@ -102,6 +102,11 @@ export class AppImManager { |
|
|
|
public bubbleGroups = new BubbleGroups(); |
|
|
|
public bubbleGroups = new BubbleGroups(); |
|
|
|
|
|
|
|
|
|
|
|
private scrolledDown = true; |
|
|
|
private scrolledDown = true; |
|
|
|
|
|
|
|
private onScrollRAF = 0; |
|
|
|
|
|
|
|
private isScrollingTimeout = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private datesIntersectionObserver: IntersectionObserver = null; |
|
|
|
|
|
|
|
private lastDateMessageDiv: HTMLDivElement = null; |
|
|
|
|
|
|
|
|
|
|
|
constructor() { |
|
|
|
constructor() { |
|
|
|
/* if(!lottieLoader.loaded) { |
|
|
|
/* if(!lottieLoader.loaded) { |
|
|
@ -559,6 +564,26 @@ export class AppImManager { |
|
|
|
|
|
|
|
|
|
|
|
this.setScroll(); |
|
|
|
this.setScroll(); |
|
|
|
apiUpdatesManager.attach(); |
|
|
|
apiUpdatesManager.attach(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.datesIntersectionObserver = new IntersectionObserver((entries) => { |
|
|
|
|
|
|
|
this.log('intersection', entries); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let entry = entries.filter(entry => entry.boundingClientRect.top < 0).sort((a, b) => b.boundingClientRect.top - a.boundingClientRect.top)[0]; |
|
|
|
|
|
|
|
if(!entry) return; |
|
|
|
|
|
|
|
let container = entry.isIntersecting ? entry.target : entry.target.nextElementSibling; |
|
|
|
|
|
|
|
for(let timestamp in this.dateMessages) { |
|
|
|
|
|
|
|
let dateMessage = this.dateMessages[timestamp]; |
|
|
|
|
|
|
|
if(dateMessage.container == container) { |
|
|
|
|
|
|
|
if(this.lastDateMessageDiv) { |
|
|
|
|
|
|
|
this.lastDateMessageDiv.classList.remove('is-sticky'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dateMessage.div.classList.add('is-sticky'); |
|
|
|
|
|
|
|
this.lastDateMessageDiv = dateMessage.div; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}/* , {root: this.chatInner} */); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public deleteMessages(revoke = false) { |
|
|
|
public deleteMessages(revoke = false) { |
|
|
@ -641,42 +666,59 @@ export class AppImManager { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public onScroll() { |
|
|
|
public onScroll() { |
|
|
|
let readed: number[] = []; |
|
|
|
if(this.onScrollRAF) window.cancelAnimationFrame(this.onScrollRAF); |
|
|
|
|
|
|
|
|
|
|
|
this.unreaded.forEachReverse((msgID, idx) => { |
|
|
|
this.onScrollRAF = window.requestAnimationFrame(() => { |
|
|
|
let bubble = this.bubbles[msgID]; |
|
|
|
let readed: number[] = []; |
|
|
|
|
|
|
|
|
|
|
|
if(isElementInViewport(bubble)) { |
|
|
|
this.unreaded.forEachReverse((msgID, idx) => { |
|
|
|
readed.push(msgID); |
|
|
|
let bubble = this.bubbles[msgID]; |
|
|
|
this.unreaded.splice(idx, 1); |
|
|
|
|
|
|
|
} |
|
|
|
if(isElementInViewport(bubble)) { |
|
|
|
}); |
|
|
|
readed.push(msgID); |
|
|
|
|
|
|
|
this.unreaded.splice(idx, 1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lottieLoader.checkAnimations(false, 'chat'); |
|
|
|
|
|
|
|
|
|
|
|
lottieLoader.checkAnimations(false, 'chat'); |
|
|
|
if(readed.length) { |
|
|
|
|
|
|
|
let max = Math.max(...readed); |
|
|
|
|
|
|
|
let min = Math.min(...readed); |
|
|
|
|
|
|
|
|
|
|
|
if(readed.length) { |
|
|
|
if(this.peerID < 0) { |
|
|
|
let max = Math.max(...readed); |
|
|
|
max = appMessagesIDsManager.getMessageIDInfo(max)[0]; |
|
|
|
let min = Math.min(...readed); |
|
|
|
min = appMessagesIDsManager.getMessageIDInfo(min)[0]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if(this.peerID < 0) { |
|
|
|
//appMessagesManager.readMessages(readed);
|
|
|
|
max = appMessagesIDsManager.getMessageIDInfo(max)[0]; |
|
|
|
appMessagesManager.readHistory(this.peerID, max, min).catch((err: any) => { |
|
|
|
min = appMessagesIDsManager.getMessageIDInfo(min)[0]; |
|
|
|
this.log.error('readHistory err:', err); |
|
|
|
|
|
|
|
appMessagesManager.readHistory(this.peerID, max, min); |
|
|
|
|
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//appMessagesManager.readMessages(readed);
|
|
|
|
if(this.isScrollingTimeout) { |
|
|
|
appMessagesManager.readHistory(this.peerID, max, min).catch((err: any) => { |
|
|
|
clearTimeout(this.isScrollingTimeout); |
|
|
|
this.log.error('readHistory err:', err); |
|
|
|
} else { |
|
|
|
appMessagesManager.readHistory(this.peerID, max, min); |
|
|
|
this.chatInner.classList.add('is-scrolling'); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(this.scroll.scrollHeight - (this.scroll.scrollTop + this.scroll.offsetHeight) == 0/* <= 5 */) { |
|
|
|
this.isScrollingTimeout = setTimeout(() => { |
|
|
|
this.scroll.parentElement.classList.add('scrolled-down'); |
|
|
|
this.chatInner.classList.remove('is-scrolling'); |
|
|
|
this.scrolledDown = true; |
|
|
|
this.isScrollingTimeout = 0; |
|
|
|
} else if(this.scroll.parentElement.classList.contains('scrolled-down')) { |
|
|
|
}, 300); |
|
|
|
this.scroll.parentElement.classList.remove('scrolled-down'); |
|
|
|
|
|
|
|
this.scrolledDown = false; |
|
|
|
if(this.scroll.scrollHeight - (this.scroll.scrollTop + this.scroll.offsetHeight) == 0/* <= 5 */) { |
|
|
|
} |
|
|
|
this.scroll.parentElement.classList.add('scrolled-down'); |
|
|
|
|
|
|
|
this.scrolledDown = true; |
|
|
|
|
|
|
|
} else if(this.scroll.parentElement.classList.contains('scrolled-down')) { |
|
|
|
|
|
|
|
this.scroll.parentElement.classList.remove('scrolled-down'); |
|
|
|
|
|
|
|
this.scrolledDown = false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.onScrollRAF = 0; |
|
|
|
|
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public setScroll() { |
|
|
|
public setScroll() { |
|
|
@ -827,6 +869,9 @@ export class AppImManager { |
|
|
|
//this.scrollable.setVirtualContainer(this.chatInner);
|
|
|
|
//this.scrollable.setVirtualContainer(this.chatInner);
|
|
|
|
this.scrollable.setVirtualContainer(null); |
|
|
|
this.scrollable.setVirtualContainer(null); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.datesIntersectionObserver.disconnect(); |
|
|
|
|
|
|
|
this.lastDateMessageDiv = null; |
|
|
|
|
|
|
|
|
|
|
|
////console.timeEnd('appImManager cleanup');
|
|
|
|
////console.timeEnd('appImManager cleanup');
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1056,6 +1101,60 @@ export class AppImManager { |
|
|
|
if(scrolledDown) this.scrollable.scrollTop = this.scrollable.scrollHeight; |
|
|
|
if(scrolledDown) this.scrollable.scrollTop = this.scrollable.scrollHeight; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public getDateContainerByMessage(message: any, reverse: boolean) { |
|
|
|
|
|
|
|
let date = new Date(message.date * 1000); |
|
|
|
|
|
|
|
let justDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()); |
|
|
|
|
|
|
|
let dateTimestamp = justDate.getTime(); |
|
|
|
|
|
|
|
if(!(dateTimestamp in this.dateMessages)) { |
|
|
|
|
|
|
|
let str = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let today = new Date(); |
|
|
|
|
|
|
|
today.setHours(0); |
|
|
|
|
|
|
|
today.setMinutes(0); |
|
|
|
|
|
|
|
today.setSeconds(0); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(today < date) { |
|
|
|
|
|
|
|
str = 'Today'; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; |
|
|
|
|
|
|
|
str = justDate.getFullYear() == new Date().getFullYear() ? |
|
|
|
|
|
|
|
months[justDate.getMonth()] + ' ' + justDate.getDate() : |
|
|
|
|
|
|
|
justDate.toISOString().split('T')[0].split('-').reverse().join('.'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let div = document.createElement('div'); |
|
|
|
|
|
|
|
div.className = 'bubble service is-date'; |
|
|
|
|
|
|
|
div.innerHTML = `<div class="bubble__container"><div class="service-msg">${str}</div></div>`; |
|
|
|
|
|
|
|
////////this.log('need to render date message', dateTimestamp, str);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let container = document.createElement('div'); |
|
|
|
|
|
|
|
container.className = 'bubbles-date-group'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.dateMessages[dateTimestamp] = { |
|
|
|
|
|
|
|
div, |
|
|
|
|
|
|
|
container, |
|
|
|
|
|
|
|
firstTimestamp: date.getTime() |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
container.append(div); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(reverse) { |
|
|
|
|
|
|
|
let scrollTopPrevious = this.scrollable.scrollTop; |
|
|
|
|
|
|
|
this.scrollable.prepend(container); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(!scrollTopPrevious) { |
|
|
|
|
|
|
|
this.scrollable.scrollTop += container.scrollHeight; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
this.scrollable.append(container); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.datesIntersectionObserver.observe(container); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this.dateMessages[dateTimestamp]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// reverse means top
|
|
|
|
// reverse means top
|
|
|
|
public renderMessage(message: any, reverse = false, multipleRender = false, bubble: HTMLDivElement = null, updatePosition = true) { |
|
|
|
public renderMessage(message: any, reverse = false, multipleRender = false, bubble: HTMLDivElement = null, updatePosition = true) { |
|
|
|
/////this.log('message to render:', message);
|
|
|
|
/////this.log('message to render:', message);
|
|
|
@ -1118,6 +1217,15 @@ export class AppImManager { |
|
|
|
this.scrollable.append(bubble); |
|
|
|
this.scrollable.append(bubble); |
|
|
|
} */ |
|
|
|
} */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let dateContainer = this.getDateContainerByMessage(message, reverse); |
|
|
|
|
|
|
|
if(!updatePosition) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else if(reverse) { |
|
|
|
|
|
|
|
dateContainer.container.insertBefore(bubble, dateContainer.div.nextSibling); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
dateContainer.container.append(bubble); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return bubble; |
|
|
|
return bubble; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1538,62 +1646,21 @@ export class AppImManager { |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
this.scrollable.appendByBatch(bubble); |
|
|
|
this.scrollable.appendByBatch(bubble); |
|
|
|
} */ |
|
|
|
} */ |
|
|
|
if(!multipleRender) { |
|
|
|
// раскомментировать ////// если рендер должен быть в другой функции (если хочешь сделать через requestAnimationFrame)
|
|
|
|
if(reverse) { |
|
|
|
//////if(!multipleRender) {
|
|
|
|
|
|
|
|
/* if(reverse) { |
|
|
|
this.scrollable.prepend(bubble); |
|
|
|
this.scrollable.prepend(bubble); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
this.scrollable.append(bubble); |
|
|
|
this.scrollable.append(bubble); |
|
|
|
} |
|
|
|
} */ |
|
|
|
} |
|
|
|
//////}
|
|
|
|
//});
|
|
|
|
//});
|
|
|
|
|
|
|
|
|
|
|
|
let justDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()); |
|
|
|
let dateMessage = this.getDateContainerByMessage(message, reverse); |
|
|
|
let dateTimestamp = justDate.getTime(); |
|
|
|
if(reverse) { |
|
|
|
let needUpdatePos = false; |
|
|
|
dateMessage.container.insertBefore(bubble, dateMessage.div.nextSibling); |
|
|
|
if(!(dateTimestamp in this.dateMessages)) { |
|
|
|
|
|
|
|
let str = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let today = new Date(); |
|
|
|
|
|
|
|
today.setHours(0); |
|
|
|
|
|
|
|
today.setMinutes(0); |
|
|
|
|
|
|
|
today.setSeconds(0); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(today < date) { |
|
|
|
|
|
|
|
str = 'Today'; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; |
|
|
|
|
|
|
|
str = justDate.getFullYear() == new Date().getFullYear() ? |
|
|
|
|
|
|
|
months[justDate.getMonth()] + ' ' + justDate.getDate() : |
|
|
|
|
|
|
|
justDate.toISOString().split('T')[0].split('-').reverse().join('.'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let div = document.createElement('div'); |
|
|
|
|
|
|
|
div.className = 'bubble service'; |
|
|
|
|
|
|
|
div.innerHTML = `<div class="service-msg">${str}</div>`; |
|
|
|
|
|
|
|
////////this.log('need to render date message', dateTimestamp, str);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.dateMessages[dateTimestamp] = { |
|
|
|
|
|
|
|
div, |
|
|
|
|
|
|
|
bubble, |
|
|
|
|
|
|
|
firstTimestamp: date.getTime() |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
needUpdatePos = true; |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
let dateMessage = this.dateMessages[dateTimestamp]; |
|
|
|
dateMessage.container.append(bubble); |
|
|
|
if(dateMessage.firstTimestamp > date.getTime()) { |
|
|
|
|
|
|
|
dateMessage.bubble = bubble; |
|
|
|
|
|
|
|
needUpdatePos = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let dateMessage = this.dateMessages[dateTimestamp]; |
|
|
|
|
|
|
|
if(needUpdatePos && !dateMessage.timeout) { |
|
|
|
|
|
|
|
//dateMessage.timeout = setTimeout(() => {
|
|
|
|
|
|
|
|
delete dateMessage.timeout; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//this.scrollable.insertBefore(dateMessage.div, dateMessage.bubble);
|
|
|
|
|
|
|
|
//}, 0);
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
this.bubbleGroups.updateGroupByMessageID(message.mid); |
|
|
|
this.bubbleGroups.updateGroupByMessageID(message.mid); |
|
|
@ -1684,7 +1751,7 @@ export class AppImManager { |
|
|
|
let bubble = this.renderMessage(message, reverse, true); |
|
|
|
let bubble = this.renderMessage(message, reverse, true); |
|
|
|
if(bubble) { |
|
|
|
if(bubble) { |
|
|
|
if(reverse) { |
|
|
|
if(reverse) { |
|
|
|
this.scrollable.prepend(bubble); |
|
|
|
////////this.scrollable.prepend(bubble);
|
|
|
|
|
|
|
|
|
|
|
|
//this.log('performHistoryResult scrollTop', this.scrollable.scrollTop, bubble.scrollHeight);
|
|
|
|
//this.log('performHistoryResult scrollTop', this.scrollable.scrollTop, bubble.scrollHeight);
|
|
|
|
|
|
|
|
|
|
|
@ -1712,7 +1779,7 @@ export class AppImManager { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
this.scrollable.append(bubble); |
|
|
|
////////this.scrollable.append(bubble);
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
//} while(cached && !this.scrollable.scrollTop);
|
|
|
|
//} while(cached && !this.scrollable.scrollTop);
|
|
|
|