better lazy loading for chat
This commit is contained in:
parent
63f0512ced
commit
6a4cc802a4
@ -1650,10 +1650,12 @@ sm-button[variant=primary] {
|
|||||||
|
|
||||||
.sent {
|
.sent {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
color: #efefef;
|
|
||||||
background: var(--accent-color);
|
background: var(--accent-color);
|
||||||
border-radius: 0.8rem 0 0.8rem 0.8rem;
|
border-radius: 0.8rem 0 0.8rem 0.8rem;
|
||||||
}
|
}
|
||||||
|
.sent > * {
|
||||||
|
color: #efefef;
|
||||||
|
}
|
||||||
.sent::after {
|
.sent::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -1864,7 +1866,7 @@ sm-button[variant=primary] {
|
|||||||
font-size: 2.6rem !important;
|
font-size: 2.6rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat_middle {
|
#messages_container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
}
|
}
|
||||||
@ -1924,7 +1926,7 @@ sm-button[variant=primary] {
|
|||||||
stroke: rgba(var(--text-color), 0.4);
|
stroke: rgba(var(--text-color), 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
#chat_middle,
|
#messages_container,
|
||||||
#chats_list,
|
#chats_list,
|
||||||
#inbox_mail_container,
|
#inbox_mail_container,
|
||||||
#sent_mail_container,
|
#sent_mail_container,
|
||||||
|
|||||||
2
css/main.min.css
vendored
2
css/main.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1483,9 +1483,11 @@ sm-button[variant="primary"] {
|
|||||||
}
|
}
|
||||||
.sent {
|
.sent {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
color: #efefef;
|
|
||||||
background: var(--accent-color);
|
background: var(--accent-color);
|
||||||
border-radius: 0.8rem 0 0.8rem 0.8rem;
|
border-radius: 0.8rem 0 0.8rem 0.8rem;
|
||||||
|
& > * {
|
||||||
|
color: #efefef;
|
||||||
|
}
|
||||||
&::after {
|
&::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -1677,7 +1679,7 @@ sm-button[variant="primary"] {
|
|||||||
font-size: 2.6rem !important;
|
font-size: 2.6rem !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#chat_middle {
|
#messages_container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
}
|
}
|
||||||
@ -1726,7 +1728,7 @@ sm-button[variant="primary"] {
|
|||||||
stroke-width: 16;
|
stroke-width: 16;
|
||||||
stroke: rgba(var(--text-color), 0.4);
|
stroke: rgba(var(--text-color), 0.4);
|
||||||
}
|
}
|
||||||
#chat_middle,
|
#messages_container,
|
||||||
#chats_list,
|
#chats_list,
|
||||||
#inbox_mail_container,
|
#inbox_mail_container,
|
||||||
#sent_mail_container,
|
#sent_mail_container,
|
||||||
|
|||||||
436
index.html
436
index.html
@ -30,11 +30,6 @@
|
|||||||
|
|
||||||
floDapps.setCustomPrivKeyInput(getSignedIn)
|
floDapps.setCustomPrivKeyInput(getSignedIn)
|
||||||
getRef('emoji_picker').shadowRoot.append(style);
|
getRef('emoji_picker').shadowRoot.append(style);
|
||||||
|
|
||||||
chatMutationObserver.observe(getRef('messages_container'), {
|
|
||||||
childList: true,
|
|
||||||
subtree: true
|
|
||||||
})
|
|
||||||
//invoke the startup functions
|
//invoke the startup functions
|
||||||
floDapps.launchStartUp().then(result => {
|
floDapps.launchStartUp().then(result => {
|
||||||
console.log(result)
|
console.log(result)
|
||||||
@ -297,21 +292,14 @@
|
|||||||
<div id="receiver_initial" class="initial flex align-center"></div>
|
<div id="receiver_initial" class="initial flex align-center"></div>
|
||||||
<h4 id="receiver_name"></h4>
|
<h4 id="receiver_name"></h4>
|
||||||
</div>
|
</div>
|
||||||
<sm-button id="video_call_button" class="hide" onclick="createOffer()">Call
|
|
||||||
</sm-button>
|
|
||||||
</header>
|
</header>
|
||||||
<section id="chat_middle" class="flex flex-direction-column">
|
<section id="messages_container" class="flex flex-direction-column"></section>
|
||||||
<div id="chat_first_child"></div>
|
<div id="scroll_to_bottom" onclick="scrollToBottom()">
|
||||||
<h5 id="warn_no_encryption">Messages are not encrypted until receiver replies</h5>
|
<svg class="icon" viewBox="0 0 64 64">
|
||||||
<section id="messages_container" class="flex flex-direction-column">
|
<title></title>
|
||||||
</section>
|
<polyline points="63.65 15.99 32 47.66 0.35 15.99" />
|
||||||
<div id="scroll_to_bottom" onclick="scrollToBottom()">
|
</svg>
|
||||||
<svg class="icon" viewBox="0 0 64 64">
|
</div>
|
||||||
<title></title>
|
|
||||||
<polyline points="63.65 15.99 32 47.66 0.35 15.99" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<footer id="chat_footer" class="grid">
|
<footer id="chat_footer" class="grid">
|
||||||
<emoji-picker id="emoji_picker" class="hide"></emoji-picker>
|
<emoji-picker id="emoji_picker" class="hide"></emoji-picker>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
@ -331,11 +319,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="chat_details_panel" class="hide">
|
<div id="chat_details_panel" class="hide">
|
||||||
<header class="flex align-center">
|
<header class="flex align-center">
|
||||||
<svg class="icon" onclick="showChatDetails({show: false})" viewBox="0 0 64 64">
|
<button class="icon-only" onclick="showChatDetails({show: false})">
|
||||||
<title>close</title>
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24"
|
||||||
<line x1="64" y1="0" x2="0" y2="64" />
|
width="24px" fill="#000000">
|
||||||
<line x1="64" y1="64" x2="0" y2="0" />
|
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||||
</svg>
|
<path
|
||||||
|
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</header>
|
</header>
|
||||||
<div id="chat_profile" class="card">
|
<div id="chat_profile" class="card">
|
||||||
<div id="chat_dp" class="initial flex align-center"></div>
|
<div id="chat_dp" class="initial flex align-center"></div>
|
||||||
@ -1019,7 +1010,7 @@
|
|||||||
})
|
})
|
||||||
isRemovingMember = false
|
isRemovingMember = false
|
||||||
break
|
break
|
||||||
case 'new_chat_popup':
|
case 'new_message_popup':
|
||||||
renderContactList(floGlobals.contacts)
|
renderContactList(floGlobals.contacts)
|
||||||
break
|
break
|
||||||
case 'group_creation_popup':
|
case 'group_creation_popup':
|
||||||
@ -1270,6 +1261,131 @@
|
|||||||
circle.remove();
|
circle.remove();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// class based lazy loading
|
||||||
|
class LazyLoader {
|
||||||
|
constructor(container, elementsToRender, renderFn, options = {}) {
|
||||||
|
const { batchSize = 10, freshRender, bottomFirst = false, hasUhtml = false } = options
|
||||||
|
|
||||||
|
this.elementsToRender = elementsToRender
|
||||||
|
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
|
||||||
|
this.renderFn = renderFn
|
||||||
|
this.intersectionObserver
|
||||||
|
|
||||||
|
this.batchSize = batchSize
|
||||||
|
this.freshRender = freshRender
|
||||||
|
this.bottomFirst = bottomFirst
|
||||||
|
this.hasUhtml = hasUhtml
|
||||||
|
|
||||||
|
this.lastScrollTop = 0
|
||||||
|
this.lastScrollHeight = 0
|
||||||
|
|
||||||
|
this.lazyContainer = document.querySelector(container)
|
||||||
|
|
||||||
|
this.update = this.update.bind(this)
|
||||||
|
this.render = this.render.bind(this)
|
||||||
|
this.init = this.init.bind(this)
|
||||||
|
this.clear = this.clear.bind(this)
|
||||||
|
}
|
||||||
|
get elements() {
|
||||||
|
return this.arrayOfElements
|
||||||
|
}
|
||||||
|
init() {
|
||||||
|
this.intersectionObserver = new IntersectionObserver((entries, observer) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
observer.disconnect()
|
||||||
|
this.render({ lazyLoad: true })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, {
|
||||||
|
threshold: 0.3
|
||||||
|
})
|
||||||
|
this.mutationObserver = new MutationObserver(mutationList => {
|
||||||
|
mutationList.forEach(mutation => {
|
||||||
|
if (mutation.type === 'childList') {
|
||||||
|
if (mutation.addedNodes.length) {
|
||||||
|
if (this.bottomFirst) {
|
||||||
|
if (this.lazyContainer.firstElementChild)
|
||||||
|
this.intersectionObserver.observe(this.lazyContainer.firstElementChild)
|
||||||
|
} else {
|
||||||
|
if (this.lazyContainer.lastElementChild)
|
||||||
|
this.intersectionObserver.observe(this.lazyContainer.lastElementChild)
|
||||||
|
}
|
||||||
|
// scroll anchoring for reverse scrolling
|
||||||
|
this.lastScrollTop = this.lazyContainer.scrollHeight - this.lastScrollHeight + this.lazyContainer.scrollTop
|
||||||
|
this.lazyContainer.scrollTo({ top: this.lastScrollTop })
|
||||||
|
this.lastScrollHeight = this.lazyContainer.scrollHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.mutationObserver.observe(this.lazyContainer, {
|
||||||
|
childList: true,
|
||||||
|
})
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
update(elementsToRender) {
|
||||||
|
this.arrayOfElements = (typeof elementsToRender === 'function') ? this.elementsToRender() : elementsToRender || []
|
||||||
|
}
|
||||||
|
render(options = {}) {
|
||||||
|
let { lazyLoad = false } = options
|
||||||
|
const frag = document.createDocumentFragment();
|
||||||
|
if (lazyLoad) {
|
||||||
|
this.updateStartIndex = this.updateEndIndex
|
||||||
|
this.updateEndIndex = this.updateEndIndex + this.batchSize
|
||||||
|
} else {
|
||||||
|
this.intersectionObserver.disconnect()
|
||||||
|
if (this.hasUhtml)
|
||||||
|
renderElem(this.lazyContainer, html``)
|
||||||
|
else
|
||||||
|
this.lazyContainer.innerHTML = ``;
|
||||||
|
this.updateStartIndex = 0
|
||||||
|
this.updateEndIndex = this.batchSize
|
||||||
|
}
|
||||||
|
this.lastScrollHeight = this.lazyContainer.scrollHeight
|
||||||
|
this.lastScrollTop = this.lazyContainer.scrollTop
|
||||||
|
if (this.bottomFirst) {
|
||||||
|
if (this.hasUhtml) {
|
||||||
|
console.log('hasUhtml')
|
||||||
|
renderElem(this.lazyContainer, html`${this.arrayOfElements.slice(-this.updateEndIndex).reverse().map(this.renderFn)}`)
|
||||||
|
} else {
|
||||||
|
this.arrayOfElements.slice(this.updateStartIndex, this.updateEndIndex).forEach(element => {
|
||||||
|
frag.prepend(this.renderFn(element))
|
||||||
|
})
|
||||||
|
this.lazyContainer.prepend(frag)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.hasUhtml) {
|
||||||
|
renderElem(this.lazyContainer, html`${this.arrayOfElements.slice(0, this.updateEndIndex).map(this.renderFn)}`)
|
||||||
|
} else {
|
||||||
|
this.arrayOfElements.slice(this.updateStartIndex, this.updateEndIndex).forEach(element => {
|
||||||
|
frag.appendChild(this.renderFn(element))
|
||||||
|
})
|
||||||
|
this.lazyContainer.append(frag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!lazyLoad && this.bottomFirst)
|
||||||
|
this.lazyContainer.scrollTo({
|
||||||
|
top: this.lazyContainer.scrollHeight,
|
||||||
|
})
|
||||||
|
// Callback to be called if elements are updated or rendered for first time
|
||||||
|
if (!lazyLoad && this.freshRender)
|
||||||
|
this.freshRender()
|
||||||
|
}
|
||||||
|
clear() {
|
||||||
|
this.intersectionObserver.disconnect()
|
||||||
|
this.mutationObserver.disconnect()
|
||||||
|
this.lazyContainer.innerHTML = ``;
|
||||||
|
}
|
||||||
|
reset() {
|
||||||
|
this.arrayOfElements = (typeof this.elementsToRender === 'function') ? this.elementsToRender() : this.elementsToRender || []
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let isMobileView = false
|
let isMobileView = false
|
||||||
const mobileQuery = window.matchMedia('(max-width: 40rem)')
|
const mobileQuery = window.matchMedia('(max-width: 40rem)')
|
||||||
function handleMobileChange(e) {
|
function handleMobileChange(e) {
|
||||||
@ -1414,7 +1530,7 @@
|
|||||||
if (type === 'group' && lastMessage.time === 0)
|
if (type === 'group' && lastMessage.time === 0)
|
||||||
lastMessage.time = messenger.groups[floID].created
|
lastMessage.time = messenger.groups[floID].created
|
||||||
const amISender = type === 'chat' && lastMessage.category === 'sent' || type === 'group' && lastMessage.sender === myFloID
|
const amISender = type === 'chat' && lastMessage.category === 'sent' || type === 'group' && lastMessage.sender === myFloID
|
||||||
const lastText = html`<p class="last-message">${amISender ? 'You' : ''} ${lastMessage.message}</p>`
|
const lastText = html`<p class="last-message">${amISender ? 'You: ' : ''} ${lastMessage.message}</p>`
|
||||||
const timeAndOptions = html`
|
const timeAndOptions = html`
|
||||||
<h5 class="time">${relativeTime.from(lastMessage.time)}</h5>
|
<h5 class="time">${relativeTime.from(lastMessage.time)}</h5>
|
||||||
<svg class="icon menu" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"> <circle cx="5.59" cy="32" r="5.59"/> <circle cx="58.41" cy="32" r="5.59"/> <circle cx="31.89" cy="32" r="5.59"/> </svg>
|
<svg class="icon menu" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"> <circle cx="5.59" cy="32" r="5.59"/> <circle cx="58.41" cy="32" r="5.59"/> <circle cx="31.89" cy="32" r="5.59"/> </svg>
|
||||||
@ -1601,7 +1717,7 @@
|
|||||||
|
|
||||||
|
|
||||||
function renderDirectUI(data) {
|
function renderDirectUI(data) {
|
||||||
renderMessages(data.messages, { updateChatCard: true });
|
renderMessages({ updateChatCard: true });
|
||||||
renderMailList(data.mails)
|
renderMailList(data.mails)
|
||||||
//let order = Object.keys(data.messages).map(a => a.split('_')).sort((a, b) => a[0] - b[0]).map(a => a[1])
|
//let order = Object.keys(data.messages).map(a => a.split('_')).sort((a, b) => a[0] - b[0]).map(a => a[1])
|
||||||
if (Object.keys(data.messages).length) {
|
if (Object.keys(data.messages).length) {
|
||||||
@ -1613,7 +1729,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderGroupUI(data) {
|
function renderGroupUI(data) {
|
||||||
renderMessages(data.messages, { updateChatCard: true });
|
renderMessages({ updateChatCard: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('focus', e => {
|
window.addEventListener('focus', e => {
|
||||||
@ -1682,7 +1798,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
const chatScrollInfo = {};
|
const chatScrollInfo = {};
|
||||||
getRef('chat_middle').addEventListener('scroll', debounce(() => {
|
getRef('messages_container').addEventListener('scroll', debounce(() => {
|
||||||
chatScrollInfo['scrollTop'] = this.scrollTop
|
chatScrollInfo['scrollTop'] = this.scrollTop
|
||||||
chatScrollInfo['scrollheight'] = this.scrollHeight
|
chatScrollInfo['scrollheight'] = this.scrollHeight
|
||||||
if ((this.scrollHeight > this.clientHeight) && (this.scrollHeight - this.clientHeight - this.scrollTop >= 100)) {
|
if ((this.scrollHeight > this.clientHeight) && (this.scrollHeight - this.clientHeight - this.scrollTop >= 100)) {
|
||||||
@ -1818,17 +1934,11 @@
|
|||||||
isGroup: messenger.groups.hasOwnProperty(clickedContact['floID'])
|
isGroup: messenger.groups.hasOwnProperty(clickedContact['floID'])
|
||||||
}
|
}
|
||||||
if (clickedContact['floID'] === myFloID) return
|
if (clickedContact['floID'] === myFloID) return
|
||||||
|
if (e.target.closest(".initial") || e.target.closest(".menu")) {
|
||||||
if (e.target.closest(".selectable")) {
|
|
||||||
if (isRemovingMember)
|
|
||||||
selectMemberToRemove(e.target.closest(".selectable"))
|
|
||||||
}
|
|
||||||
else if (e.target.closest(".initial") || e.target.closest(".menu")) {
|
|
||||||
openPopup('contact_details_popup')
|
openPopup('contact_details_popup')
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
contact.classList.remove('unread')
|
contact.classList.remove('unread')
|
||||||
if (activeChat['chatCard'] === contact && window.innerWidth > 640) return
|
if (activeChat['chatCard'] === contact && !isMobileView) return
|
||||||
showChatDetails({ show: false, animate: false })
|
showChatDetails({ show: false, animate: false })
|
||||||
document.title = `FLO Messenger`
|
document.title = `FLO Messenger`
|
||||||
getRef('chat').classList.remove('hide')
|
getRef('chat').classList.remove('hide')
|
||||||
@ -1848,6 +1958,37 @@
|
|||||||
removeSelectedContact(e.target.closest('.contact-preview').getAttribute('flo-id'))
|
removeSelectedContact(e.target.closest('.contact-preview').getAttribute('flo-id'))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
getRef('contacts_container').addEventListener('click', e => {
|
||||||
|
//detect click on chat cards
|
||||||
|
if (e.target.closest(".contact")) {
|
||||||
|
let contact = e.target.closest(".contact")
|
||||||
|
clickedContact = {
|
||||||
|
...clickedContact,
|
||||||
|
chatCard: contact,
|
||||||
|
floID: contact.getAttribute("flo-id"),
|
||||||
|
name: contact.getAttribute("name"),
|
||||||
|
isGroup: messenger.groups.hasOwnProperty(clickedContact['floID'])
|
||||||
|
}
|
||||||
|
if (clickedContact['floID'] === myFloID) return
|
||||||
|
contact.classList.remove('unread')
|
||||||
|
if (activeChat['chatCard'] === contact && !isMobileView) return
|
||||||
|
showChatDetails({ show: false, animate: false })
|
||||||
|
document.title = `FLO Messenger`
|
||||||
|
getRef('chat').classList.remove('hide')
|
||||||
|
viewConversation(contact)
|
||||||
|
if (activeChat['chatCard'])
|
||||||
|
activeChat['chatCard'].classList.remove('active')
|
||||||
|
contact.classList.add('active')
|
||||||
|
activeChat['chatCard'] = contact
|
||||||
|
if (activeChatPage.id === 'contacts') {
|
||||||
|
getRef('chat').classList.remove('hide-on-mobile')
|
||||||
|
getRef('contacts').classList.add('hide-on-mobile')
|
||||||
|
activeChatPage = getRef('chat')
|
||||||
|
getRef('main_navbar').classList.add('hide-on-mobile')
|
||||||
|
}
|
||||||
|
closePopup()
|
||||||
|
}
|
||||||
|
})
|
||||||
getRef('contacts_popup').addEventListener('click', e => {
|
getRef('contacts_popup').addEventListener('click', e => {
|
||||||
//detect click on contacts
|
//detect click on contacts
|
||||||
if (e.target.closest(".selectable")) {
|
if (e.target.closest(".selectable")) {
|
||||||
@ -1972,7 +2113,7 @@
|
|||||||
getRef('emoji_picker').addEventListener('emoji-click', e => {
|
getRef('emoji_picker').addEventListener('emoji-click', e => {
|
||||||
const clickedEmoji = e.detail.unicode
|
const clickedEmoji = e.detail.unicode
|
||||||
getRef('type_message').value += clickedEmoji
|
getRef('type_message').value += clickedEmoji
|
||||||
if (window.innerWidth > 640) {
|
if (!isMobileView) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
getRef('type_message').focusIn()
|
getRef('type_message').focusIn()
|
||||||
}, 0);
|
}, 0);
|
||||||
@ -2024,7 +2165,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
function updateHeight() {
|
function updateHeight() {
|
||||||
if (window.innerWidth < 640) {
|
if (isMobileView) {
|
||||||
getRef('chat').style.height = window.innerHeight + 'px'
|
getRef('chat').style.height = window.innerHeight + 'px'
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -2154,7 +2295,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
function sendMessage() {
|
function sendMessage() {
|
||||||
if (window.innerWidth > 640)
|
if (!isMobileView)
|
||||||
getRef('type_message').focusIn()
|
getRef('type_message').focusIn()
|
||||||
let receiver = activeChat['floID']
|
let receiver = activeChat['floID']
|
||||||
let container;
|
let container;
|
||||||
@ -2244,6 +2385,7 @@
|
|||||||
let isSelected = selectedGroupMembers.has(floID)
|
let isSelected = selectedGroupMembers.has(floID)
|
||||||
contacts.push(render.contactCard(floID, { type: 'contact', isSelected }))
|
contacts.push(render.contactCard(floID, { type: 'contact', isSelected }))
|
||||||
}
|
}
|
||||||
|
console.log(contacts, contactList)
|
||||||
renderElem(getRef('contacts_container'), html`${contacts}`)
|
renderElem(getRef('contacts_container'), html`${contacts}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2275,150 +2417,89 @@
|
|||||||
}
|
}
|
||||||
getRef('scroll_to_bottom').classList.remove('new-message')
|
getRef('scroll_to_bottom').classList.remove('new-message')
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
getRef('chat_middle').scrollTo({ top: getRef('chat_middle').scrollHeight, behavior: smooth ? 'smooth' : undefined })
|
getRef('messages_container').scrollTo({ top: getRef('messages_container').scrollHeight, behavior: smooth ? 'smooth' : undefined })
|
||||||
}, smooth ? 300 : 0);
|
}, smooth ? 300 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let startIndex = 0,
|
let chatLazyLoader
|
||||||
endIndex = 0
|
async function renderMessages(options) {
|
||||||
function renderMessages(data, options) {
|
let { markUnread = true, updateChatCard = false } = options
|
||||||
let { markUnread = true, updateChatCard = false, reRender = false, lazyLoad = false } = options
|
try {
|
||||||
let messages
|
if (activeChat.floID) {
|
||||||
if (reRender) {
|
let messages = Object.values(await messenger.getChat(activeChat['floID'])).reverse()
|
||||||
activeChat['allMessages'] = Object.values(data)
|
if (chatLazyLoader) {
|
||||||
startIndex = activeChat['allMessages'].length > 20 ? activeChat['allMessages'].length - 20 : 0
|
chatLazyLoader.update(messages)
|
||||||
endIndex = activeChat['allMessages'].length
|
} else {
|
||||||
messages = activeChat['allMessages']
|
chatLazyLoader = new LazyLoader('#messages_container', messages, render.messageBubble, {
|
||||||
renderedDates.clear()
|
bottomFirst: true, hasUhtml: true, freshRender: () => {
|
||||||
}
|
currentDate = null
|
||||||
else if (lazyLoad) {
|
lastSender = null
|
||||||
messages = activeChat['allMessages']
|
}
|
||||||
endIndex = startIndex
|
});
|
||||||
startIndex = endIndex > 20 ? endIndex - 20 : 0
|
|
||||||
markUnread = false
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
messages = Object.values(data)
|
|
||||||
if (messages.length) {
|
|
||||||
startIndex = 0
|
|
||||||
endIndex = messages.length
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messages && messages.length) {
|
|
||||||
for (let i = startIndex; i < endIndex; i++) {
|
|
||||||
let { floID, groupID, sender, message, time, category } = messages[i]
|
|
||||||
//Stops message from rendering in wrong chat window
|
|
||||||
if (activeChat['floID'] && (activeChat['floID'] === floID || activeChat['floID'] === groupID)) {
|
|
||||||
// Stops message rendering if message is sent from original user causing duplication
|
|
||||||
if (updateChatCard && activeChat.isGroup && message && sender === myFloID) {
|
|
||||||
messenger.removeMark(groupID, 'unread')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
frag.append(render.messageBubble({ ...messages[i], updateChatCard }))
|
|
||||||
}
|
}
|
||||||
const contact = getRef('chats_list').querySelector(`.contact[flo-id='${floID || groupID}']`)
|
chatLazyLoader.init()
|
||||||
if (markUnread && contact) {
|
messages.forEach(messageDetails => {
|
||||||
contact.classList.add("unread");
|
let { floID, groupID, sender, message, time, category } = messageDetails
|
||||||
if (contact !== getRef('chats_list').children[0]) {
|
const contact = getRef('chats_list').querySelector(`.contact[flo-id='${floID || groupID}']`)
|
||||||
const cloneContact = contact.cloneNode(true)
|
if (markUnread && contact) {
|
||||||
contact.remove()
|
contact.classList.add("unread");
|
||||||
getRef('chats_list').prepend(cloneContact)
|
if (contact !== getRef('chats_list').children[0]) {
|
||||||
animateTo(getRef('chats_list').children[0], [
|
const cloneContact = contact.cloneNode(true)
|
||||||
{ transform: 'translateY(1rem)' },
|
contact.remove()
|
||||||
{ transform: 'none' },
|
getRef('chats_list').prepend(cloneContact)
|
||||||
],
|
animateTo(getRef('chats_list').children[0], [
|
||||||
{
|
{ transform: 'translateY(1rem)' },
|
||||||
easing: 'ease',
|
{ transform: 'none' },
|
||||||
duration: 300
|
],
|
||||||
}
|
{
|
||||||
)
|
easing: 'ease',
|
||||||
|
duration: 300
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (updateChatCard) {
|
||||||
if (updateChatCard) {
|
let chatCard
|
||||||
let chatCard
|
if (!contact) {
|
||||||
if (!contact) {
|
getRef('chats_list').prepend(render.contactCard(floID, { type: 'chat', markUnread: true }))
|
||||||
getRef('chats_list').prepend(render.contactCard(floID, { type: 'chat', markUnread: true }))
|
chatCard = getRef('chats_list').firstElementChild
|
||||||
chatCard = getRef('chats_list').firstElementChild
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
chatCard = contact
|
|
||||||
let finalMessage
|
|
||||||
if (floGlobals.contacts[sender])
|
|
||||||
finalMessage = `${floGlobals.contacts[sender]}: ${message}`
|
|
||||||
else if (sender === myFloID)
|
|
||||||
finalMessage = `You: ${message}`
|
|
||||||
else
|
|
||||||
finalMessage = message
|
|
||||||
if (chatCard.querySelector('.last-message'))
|
|
||||||
chatCard.querySelector('.last-message').textContent = finalMessage
|
|
||||||
chatCard.querySelector('.time').textContent = relativeTime.from(time)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeChat.floID === (floID || groupID)) {
|
|
||||||
if (chatScrollInfo.isScrolledUp)
|
|
||||||
getRef('scroll_to_bottom').classList.add('new-message')
|
|
||||||
else {
|
else {
|
||||||
if (document.hasFocus()) {
|
chatCard = contact
|
||||||
messenger.removeMark((floID || groupID), 'unread')
|
let finalMessage
|
||||||
setTimeout(() => {
|
if (floGlobals.contacts[sender])
|
||||||
document.title = 'FLO Messenger'
|
finalMessage = `${floGlobals.contacts[sender]}: ${message}`
|
||||||
activeChat.chatCard.classList.remove('unread')
|
else if (sender === myFloID)
|
||||||
}, 1000);
|
finalMessage = `You: ${message}`
|
||||||
|
else
|
||||||
|
finalMessage = message
|
||||||
|
if (chatCard.querySelector('.last-message'))
|
||||||
|
chatCard.querySelector('.last-message').textContent = finalMessage
|
||||||
|
chatCard.querySelector('.time').textContent = relativeTime.from(time)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeChat.floID === (floID || groupID)) {
|
||||||
|
if (chatScrollInfo.isScrolledUp)
|
||||||
|
getRef('scroll_to_bottom').classList.add('new-message')
|
||||||
|
else {
|
||||||
|
if (document.hasFocus()) {
|
||||||
|
messenger.removeMark((floID || groupID), 'unread')
|
||||||
|
setTimeout(() => {
|
||||||
|
document.title = 'FLO Messenger'
|
||||||
|
activeChat.chatCard.classList.remove('unread')
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
if (!lazyLoad && !reRender) {
|
|
||||||
endIndex = messages.length
|
|
||||||
getRef('messages_container').append(frag)
|
|
||||||
if (!chatScrollInfo['isScrolledUp']) {
|
|
||||||
setTimeout(() => {
|
|
||||||
scrollToBottom(true)
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (reRender || lazyLoad) {
|
|
||||||
currentDate = null
|
|
||||||
lastSender = null
|
|
||||||
chatScrollInfo['scrollTop'] = getRef('chat_middle').scrollTop
|
|
||||||
chatScrollInfo['scrollHeight'] = getRef('chat_middle').scrollHeight
|
|
||||||
getRef('messages_container').prepend(frag)
|
|
||||||
}
|
|
||||||
if (reRender) {
|
|
||||||
scrollToBottom()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//checks for added elements in chat
|
|
||||||
const chatMutationObserver = new MutationObserver(
|
|
||||||
(mutations, observer) => {
|
|
||||||
for (const mutation of mutations) {
|
|
||||||
if (mutation.type === 'childList' && mutation.addedNodes.length && getRef('messages_container').firstElementChild) {
|
|
||||||
chatMessageObserver.observe(getRef('messages_container').firstElementChild)
|
|
||||||
chatScrollInfo['scrollTop'] += (getRef('chat_middle').scrollHeight - chatScrollInfo['scrollHeight'])
|
|
||||||
chatScrollInfo['scrollHeight'] = getRef('chat_middle').scrollHeight
|
|
||||||
getRef('chat_middle').scrollTo({ top: chatScrollInfo['scrollTop'] })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
//Lazy loading for chat messages
|
|
||||||
const chatMessageObserver = new IntersectionObserver(
|
|
||||||
(entries, observer) => {
|
|
||||||
if (entries[0].isIntersecting) {
|
|
||||||
renderMessages('', { lazyLoad: true })
|
|
||||||
observer.disconnect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
async function viewConversation(contact) {
|
async function viewConversation(contact) {
|
||||||
getRef('messages_container').innerHTML = ''
|
|
||||||
let floID = clickedContact['floID'],
|
let floID = clickedContact['floID'],
|
||||||
name = contact.getAttribute('name'),
|
name = contact.getAttribute('name'),
|
||||||
textColor = contact.getAttribute('text-color'),
|
textColor = contact.getAttribute('text-color'),
|
||||||
@ -2432,18 +2513,15 @@
|
|||||||
getRef("receiver_initial").innerHTML = `
|
getRef("receiver_initial").innerHTML = `
|
||||||
<svg class="icon group-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M13.61,28.09c-1.63,0-4.72-2.35-5.33-3.58a21.65,21.65,0,0,1-1.35-7.32s-.26-6.07,6.68-6.07a6.38,6.38,0,0,1,6.69,6.07A21.65,21.65,0,0,1,19,24.51c-.62,1.23-3.7,3.58-5.34,3.58"/><path d="M50.39,28.09c-1.64,0-4.72-2.35-5.34-3.58a21.9,21.9,0,0,1-1.35-7.32s-.26-6.07,6.69-6.07a6.37,6.37,0,0,1,6.68,6.07,21.65,21.65,0,0,1-1.35,7.32c-.61,1.23-3.7,3.58-5.33,3.58"/><path d="M32,31.74c-2.21,0-6.37-3.17-7.2-4.83A29.3,29.3,0,0,1,23,17s-.35-8.21,9-8.21c8.68,0,9,8.21,9,8.21a29.3,29.3,0,0,1-1.83,9.88c-.82,1.66-5,4.83-7.2,4.83"/><path d="M48.29,38.58c-4.16-1.83-8.57-3.08-10.34-6.4a12,12,0,0,1-6,3.73,12,12,0,0,1-5.95-3.73c-1.77,3.32-6.18,4.57-10.34,6.4-1.7.71-3.11,9.88-1.13,9.88A33.06,33.06,0,0,0,31.23,53h1.54a33.06,33.06,0,0,0,16.65-4.53C51.4,48.46,50,39.29,48.29,38.58Z"/><path d="M14.82,36.57c.76-.33,1.54-.65,2.3-1,2.49-1,4.85-2,6.22-3.44C21.07,31.23,19,30.25,18,28.41a8.83,8.83,0,0,1-4.41,2.76,8.83,8.83,0,0,1-4.4-2.76c-1.31,2.46-4.58,3.38-7.66,4.74-1.26.52-2.3,7.31-.84,7.31a24.55,24.55,0,0,0,10.86,3.31C11.89,40.81,12.86,37.39,14.82,36.57Z"/><path d="M62.45,33.15c-3.08-1.36-6.35-2.28-7.66-4.74a8.83,8.83,0,0,1-4.4,2.76A8.83,8.83,0,0,1,46,28.41c-1,1.84-3,2.82-5.32,3.76,1.37,1.43,3.73,2.41,6.22,3.44.76.31,1.54.63,2.26,1,2,.83,3,4.25,3.29,7.21a24.55,24.55,0,0,0,10.86-3.31C64.75,40.46,63.71,33.67,62.45,33.15Z"/></svg>
|
<svg class="icon group-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path d="M13.61,28.09c-1.63,0-4.72-2.35-5.33-3.58a21.65,21.65,0,0,1-1.35-7.32s-.26-6.07,6.68-6.07a6.38,6.38,0,0,1,6.69,6.07A21.65,21.65,0,0,1,19,24.51c-.62,1.23-3.7,3.58-5.34,3.58"/><path d="M50.39,28.09c-1.64,0-4.72-2.35-5.34-3.58a21.9,21.9,0,0,1-1.35-7.32s-.26-6.07,6.69-6.07a6.37,6.37,0,0,1,6.68,6.07,21.65,21.65,0,0,1-1.35,7.32c-.61,1.23-3.7,3.58-5.33,3.58"/><path d="M32,31.74c-2.21,0-6.37-3.17-7.2-4.83A29.3,29.3,0,0,1,23,17s-.35-8.21,9-8.21c8.68,0,9,8.21,9,8.21a29.3,29.3,0,0,1-1.83,9.88c-.82,1.66-5,4.83-7.2,4.83"/><path d="M48.29,38.58c-4.16-1.83-8.57-3.08-10.34-6.4a12,12,0,0,1-6,3.73,12,12,0,0,1-5.95-3.73c-1.77,3.32-6.18,4.57-10.34,6.4-1.7.71-3.11,9.88-1.13,9.88A33.06,33.06,0,0,0,31.23,53h1.54a33.06,33.06,0,0,0,16.65-4.53C51.4,48.46,50,39.29,48.29,38.58Z"/><path d="M14.82,36.57c.76-.33,1.54-.65,2.3-1,2.49-1,4.85-2,6.22-3.44C21.07,31.23,19,30.25,18,28.41a8.83,8.83,0,0,1-4.41,2.76,8.83,8.83,0,0,1-4.4-2.76c-1.31,2.46-4.58,3.38-7.66,4.74-1.26.52-2.3,7.31-.84,7.31a24.55,24.55,0,0,0,10.86,3.31C11.89,40.81,12.86,37.39,14.82,36.57Z"/><path d="M62.45,33.15c-3.08-1.36-6.35-2.28-7.66-4.74a8.83,8.83,0,0,1-4.4,2.76A8.83,8.83,0,0,1,46,28.41c-1,1.84-3,2.82-5.32,3.76,1.37,1.43,3.73,2.41,6.22,3.44.76.31,1.54.63,2.26,1,2,.83,3,4.25,3.29,7.21a24.55,24.55,0,0,0,10.86-3.31C64.75,40.46,63.71,33.67,62.45,33.15Z"/></svg>
|
||||||
`
|
`
|
||||||
getRef('video_call_button').classList.add('hide')
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
getRef("receiver_initial").textContent = getContactName(floID).charAt(0);
|
getRef("receiver_initial").textContent = getContactName(floID).charAt(0);
|
||||||
// getRef('video_call_button').classList.remove('hide')
|
|
||||||
}
|
}
|
||||||
getRef("receiver_initial").setAttribute('style', `color: ${textColor}; background-color: ${backgroundColor};`)
|
getRef("receiver_initial").setAttribute('style', `color: ${textColor}; background-color: ${backgroundColor};`)
|
||||||
if (floGlobals.pubKeys[floID] || activeChat.isGroup)
|
renderMessages({ markUnread: false }).then(() => {
|
||||||
getRef("warn_no_encryption").classList.add("hide");
|
if (!floGlobals.pubKeys[floID] && !activeChat.isGroup)
|
||||||
else
|
getRef('messages_container').prepend(html.node`<strong id="warn_no_encryption">Converstion is not encrypted until receiver replies</strong>`)
|
||||||
getRef("warn_no_encryption").classList.remove("hide");
|
})
|
||||||
renderMessages(await messenger.getChat(floID), { markUnread: false, reRender: true })
|
|
||||||
messenger.removeMark(floID, "unread");
|
messenger.removeMark(floID, "unread");
|
||||||
if (this.scrollHeight <= this.clientHeight) {
|
if (this.scrollHeight <= this.clientHeight) {
|
||||||
chatScrollInfo['isScrolledUp'] = false
|
chatScrollInfo['isScrolledUp'] = false
|
||||||
@ -2814,7 +2892,7 @@
|
|||||||
async function clearChat() {
|
async function clearChat() {
|
||||||
if (await confirmation('Clear chat?', `Are you sure to clear this chat?`, 'No', "Yes")) {
|
if (await confirmation('Clear chat?', `Are you sure to clear this chat?`, 'No', "Yes")) {
|
||||||
messenger.clearChat(clickedContact.floID).then(result => {
|
messenger.clearChat(clickedContact.floID).then(result => {
|
||||||
getRef('messages_container').innerHTML = ''
|
renderElem(getRef('messages_container'), html``)
|
||||||
closePopup()
|
closePopup()
|
||||||
notify('Chat cleared', 'success')
|
notify('Chat cleared', 'success')
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user