Browse Source

Date messages in chat

master
morethanwords 5 years ago
parent
commit
bd0a2bdf39
  1. 173
      src/lib/appManagers/appImManager.ts
  2. 23
      src/scss/partials/_chat.scss

173
src/lib/appManagers/appImManager.ts

@ -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,6 +666,9 @@ export class AppImManager {
} }
public onScroll() { public onScroll() {
if(this.onScrollRAF) window.cancelAnimationFrame(this.onScrollRAF);
this.onScrollRAF = window.requestAnimationFrame(() => {
let readed: number[] = []; let readed: number[] = [];
this.unreaded.forEachReverse((msgID, idx) => { this.unreaded.forEachReverse((msgID, idx) => {
@ -670,6 +698,17 @@ export class AppImManager {
}); });
} }
if(this.isScrollingTimeout) {
clearTimeout(this.isScrollingTimeout);
} else {
this.chatInner.classList.add('is-scrolling');
}
this.isScrollingTimeout = setTimeout(() => {
this.chatInner.classList.remove('is-scrolling');
this.isScrollingTimeout = 0;
}, 300);
if(this.scroll.scrollHeight - (this.scroll.scrollTop + this.scroll.offsetHeight) == 0/* <= 5 */) { if(this.scroll.scrollHeight - (this.scroll.scrollTop + this.scroll.offsetHeight) == 0/* <= 5 */) {
this.scroll.parentElement.classList.add('scrolled-down'); this.scroll.parentElement.classList.add('scrolled-down');
this.scrolledDown = true; this.scrolledDown = true;
@ -677,6 +716,9 @@ export class AppImManager {
this.scroll.parentElement.classList.remove('scrolled-down'); this.scroll.parentElement.classList.remove('scrolled-down');
this.scrolledDown = false; 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);

23
src/scss/partials/_chat.scss

@ -171,6 +171,10 @@ $chat-max-width: 696px;
margin-left: 45px; margin-left: 45px;
} }
} }
&.is-scrolling .is-sticky {
opacity: 1;
}
} }
#bubbles-go-down { #bubbles-go-down {
@ -226,6 +230,20 @@ $chat-max-width: 696px;
grid-template-columns: 1fr $chat-max-width 1fr; grid-template-columns: 1fr $chat-max-width 1fr;
grid-row-gap: 0px; grid-row-gap: 0px;
&.is-date {
position: -webkit-sticky;
position: sticky;
top: 5px;
z-index: 2;
pointer-events: none;
&.is-sticky {
-webkit-transition: opacity .3s ease;
transition: opacity .3s ease;
opacity: 0;
}
}
&:before, &:after { &:before, &:after {
content: " "; content: " ";
width: 100%; width: 100%;
@ -245,10 +263,11 @@ $chat-max-width: 696px;
} }
&.service { &.service {
display: block; //padding: 1rem 0;
padding: 1rem 0; padding: 5px 0;
.bubble__container { .bubble__container {
justify-self: center;
max-width: 100%; max-width: 100%;
} }
} }

Loading…
Cancel
Save