Browse Source

added unread message label

resolve-20231003
PhilReact 1 year ago
parent
commit
8dfab316a7
  1. 190
      plugins/plugins/core/components/ChatPage.js
  2. 11
      plugins/plugins/core/components/ChatScroller-css.js
  3. 88
      plugins/plugins/core/components/ChatScroller.js

190
plugins/plugins/core/components/ChatPage.js

@ -55,8 +55,8 @@ const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
export const queue = new RequestQueue(); export const queue = new RequestQueue();
export const chatLimit = 10 export const chatLimit = 40
export const totalMsgCount = 20 export const totalMsgCount = 120
class ChatPage extends LitElement { class ChatPage extends LitElement {
static get properties() { static get properties() {
return { return {
@ -1367,6 +1367,8 @@ class ChatPage extends LitElement {
this.addToUpdateMessageHashmap = this.addToUpdateMessageHashmap.bind(this) this.addToUpdateMessageHashmap = this.addToUpdateMessageHashmap.bind(this)
this.getAfterMessages = this.getAfterMessages.bind(this) this.getAfterMessages = this.getAfterMessages.bind(this)
this.oldMessages = [] this.oldMessages = []
this.lastReadMessageTimestamp = 0
this.initUpdate = this.initUpdate.bind(this)
} }
setOpenGifModal(value) { setOpenGifModal(value) {
@ -2006,42 +2008,42 @@ class ChatPage extends LitElement {
document.addEventListener('keydown', this.initialChat) document.addEventListener('keydown', this.initialChat)
document.addEventListener('paste', this.pasteImage) document.addEventListener('paste', this.pasteImage)
if (this.chatId) { // if (this.chatId) {
window.parent.reduxStore.dispatch(window.parent.reduxAction.addChatLastSeen({ // window.parent.reduxStore.dispatch(window.parent.reduxAction.addChatLastSeen({
key: this.chatId, // key: this.chatId,
timestamp: Date.now() // timestamp: Date.now()
})) // }))
} // }
let callback = (entries, observer) => { // let callback = (entries, observer) => {
entries.forEach(entry => { // entries.forEach(entry => {
if (entry.isIntersecting) { // if (entry.isIntersecting) {
this.isPageVisible = true // this.isPageVisible = true
if (this.chatId) { // if (this.chatId) {
window.parent.reduxStore.dispatch(window.parent.reduxAction.addChatLastSeen({ // window.parent.reduxStore.dispatch(window.parent.reduxAction.addChatLastSeen({
key: this.chatId, // key: this.chatId,
timestamp: Date.now() // timestamp: Date.now()
})) // }))
} // }
} else { // } else {
this.isPageVisible = false // this.isPageVisible = false
} // }
}) // })
} // }
let options = { // let options = {
root: null, // root: null,
rootMargin: '0px', // rootMargin: '0px',
threshold: 0.5 // threshold: 0.5
} // }
// Create the observer with the callback function and options // // Create the observer with the callback function and options
this.observer = new IntersectionObserver(callback, options) // this.observer = new IntersectionObserver(callback, options)
const mainContainer = this.shadowRoot.querySelector('.main-container') // const mainContainer = this.shadowRoot.querySelector('.main-container')
this.observer.observe(mainContainer) // this.observer.observe(mainContainer)
} }
disconnectedCallback() { disconnectedCallback() {
@ -2416,6 +2418,17 @@ class ChatPage extends LitElement {
// this.selectedAddress = selectedAddress // this.selectedAddress = selectedAddress
// }) // })
// })
this.lastReadMessageTimestamp = await chatLastSeen.getItem(this.chatId) || 0
// parentEpml.subscribe('chat_last_seen', async chatList => {
// const parsedChatList = JSON.parse(chatList)
// console.log({parsedChatList}, this.chatId)
// const findChatSeen = parsedChatList.find(chat=> chat.key === this.chatId)
// console.log({findChatSeen})
// if(findChatSeen && this.lastReadMessageTimestamp !== findChatSeen.timestamp){
// this.lastReadMessageTimestamp = findChatSeen.timestamp
// }
// }) // })
parentEpml.imReady() parentEpml.imReady()
@ -2994,9 +3007,28 @@ class ChatPage extends LitElement {
this.requestUpdate() this.requestUpdate()
} }
findContent(identifier, data) {
const [type, id] = identifier.split('/');
if (type === 'group') {
for (let group of data.groups) {
if (group.groupId === parseInt(id, 10)) {
return group;
}
}
} else if (type === 'direct') {
for (let direct of data.direct) {
if (direct.address === id) {
return direct;
}
}
}
return null;
}
async processMessages(messages, isInitial) { async processMessages(messages, isInitial, isUnread) {
const isReceipient = this.chatId.includes('direct') const isReceipient = this.chatId.includes('direct')
let decodedMessages = [] let decodedMessages = []
if(!this.webWorkerDecodeMessages){ if(!this.webWorkerDecodeMessages){
@ -3058,10 +3090,27 @@ class ChatPage extends LitElement {
// TODO: Determine number of initial messages by screen height... // TODO: Determine number of initial messages by screen height...
// this.messagesRendered = this._messages // this.messagesRendered = this._messages
const lastReadMessageTimestamp = this.lastReadMessageTimestamp
if(isUnread){
this.messagesRendered = {
messages: this._messages,
type: 'initialLastSeen',
lastReadMessageTimestamp
}
window.parent.reduxStore.dispatch(window.parent.reduxAction.addChatLastSeen({
key: this.chatId,
timestamp: Date.now()
}))
} else {
this.messagesRendered = { this.messagesRendered = {
messages: this._messages, messages: this._messages,
type: 'initial' type: 'initial'
} }
}
this.isLoadingMessages = false this.isLoadingMessages = false
setTimeout(() => this.downElementObserver(), 500) setTimeout(() => this.downElementObserver(), 500)
@ -3272,17 +3321,37 @@ class ChatPage extends LitElement {
directSocketTimeout = setTimeout(pingDirectSocket, 45000) directSocketTimeout = setTimeout(pingDirectSocket, 45000)
return return
} }
if (initial === 0) { if (initial === 0) {
this.lastReadMessageTimestamp = await chatLastSeen.getItem(this.chatId) || 0
if (noInitial) return if (noInitial) return
const cachedData = null
let getInitialMessages = [] let getInitialMessages = []
if (cachedData && cachedData.length !== 0) { let isUnread = false
const lastMessage = cachedData[cachedData.length - 1]
const newMessages = await parentEpml.request('apiCall', { const chatId = this.chatId
console.log('this.chatHeads', this.chatHeads)
const findContent = this.chatHeads.find((item)=> item.url === chatId)
const chatInfoTimestamp = findContent.timestamp || 0
const lastReadMessageTimestamp = this.lastReadMessageTimestamp
console.log({lastReadMessageTimestamp, chatInfoTimestamp})
if(lastReadMessageTimestamp && chatInfoTimestamp){
if(lastReadMessageTimestamp < chatInfoTimestamp){
isUnread = true
}
}
console.log({isUnread})
if(isUnread){
const getInitialMessagesBefore = await parentEpml.request('apiCall', {
type: 'api',
url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=${20}&reverse=true&before=${lastReadMessageTimestamp}&haschatreference=false&encoding=BASE64`
})
const getInitialMessagesAfter = await parentEpml.request('apiCall', {
type: 'api', type: 'api',
url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=${chatLimit}&reverse=true&after=${lastMessage.timestamp}&haschatreference=false&encoding=BASE64` url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=${20}&reverse=false&after=${lastReadMessageTimestamp - 1000}&haschatreference=false&encoding=BASE64`
}) })
getInitialMessages = [...newMessages] getInitialMessages = [...getInitialMessagesBefore, ...getInitialMessagesAfter]
} else { } else {
getInitialMessages = await parentEpml.request('apiCall', { getInitialMessages = await parentEpml.request('apiCall', {
type: 'api', type: 'api',
@ -3290,7 +3359,9 @@ class ChatPage extends LitElement {
}) })
} }
this.processMessages(getInitialMessages, true)
this.processMessages(getInitialMessages, true, isUnread)
initial = initial + 1 initial = initial + 1
@ -3371,27 +3442,48 @@ class ChatPage extends LitElement {
return return
} }
if (initial === 0) { if (initial === 0) {
this.lastReadMessageTimestamp = await chatLastSeen.getItem(this.chatId) || 0
if (noInitial) return if (noInitial) return
const cachedData = null
let getInitialMessages = [] let getInitialMessages = []
if (cachedData && cachedData.length !== 0) { const lastReadMessageTimestamp = this.lastReadMessageTimestamp
let isUnread = false
const chatId = this.chatId
console.log('this.chatHeads', this.chatHeads)
const findContent = this.chatHeads.find((item)=> item.url === chatId)
const chatInfoTimestamp = findContent.timestamp || 0
console.log({lastReadMessageTimestamp, chatInfoTimestamp})
if(lastReadMessageTimestamp && chatInfoTimestamp){
if(lastReadMessageTimestamp < chatInfoTimestamp){
isUnread = true
}
}
console.log({isUnread}, '2')
if(isUnread){
const lastMessage = cachedData[cachedData.length - 1]
const newMessages = await parentEpml.request('apiCall', { const getInitialMessagesBefore = await parentEpml.request('apiCall', {
type: 'api', type: 'api',
url: `/chat/messages?txGroupId=${groupId}&limit=${chatLimit}&reverse=true&after=${lastMessage.timestamp}&haschatreference=false&encoding=BASE64` url: `/chat/messages?txGroupId=${groupId}&limit=${20}&reverse=true&before=${lastReadMessageTimestamp}&haschatreference=false&encoding=BASE64`
}) })
getInitialMessages = [...newMessages] const getInitialMessagesAfter = await parentEpml.request('apiCall', {
}else { type: 'api',
url: `/chat/messages?txGroupId=${groupId}&limit=${20}&reverse=false&after=${lastReadMessageTimestamp - 1000}&haschatreference=false&encoding=BASE64`
})
getInitialMessages = [...getInitialMessagesBefore, ...getInitialMessagesAfter]
} else {
getInitialMessages = await parentEpml.request('apiCall', { getInitialMessages = await parentEpml.request('apiCall', {
type: 'api', type: 'api',
url: `/chat/messages?txGroupId=${groupId}&limit=${chatLimit}&reverse=true&haschatreference=false&encoding=BASE64` url: `/chat/messages?txGroupId=${groupId}&limit=${chatLimit}&reverse=true&haschatreference=false&encoding=BASE64`
}) })
} }
this.processMessages(getInitialMessages, true)
this.processMessages(getInitialMessages, true, isUnread)
initial = initial + 1 initial = initial + 1
} else { } else {

11
plugins/plugins/core/components/ChatScroller-css.js

@ -753,6 +753,17 @@ export const chatStyles = css`
visibility: visible; visibility: visible;
} }
.unread-divider {
width: 100%;
background: #9B111E;
padding: 5px;
color: #FAEBD7;
display: flex;
justify-content: center;
border-radius: 2px;
margin-top: 5px;
}
.blink-bg{ .blink-bg{
border-radius: 8px; border-radius: 8px;
animation: blinkingBackground 3s; animation: blinkingBackground 3s;

88
plugins/plugins/core/components/ChatScroller.js

@ -10,6 +10,7 @@ import { roundToNearestDecimal } from '../../utils/roundToNearestDecimal.js'
import { EmojiPicker } from 'emoji-picker-js' import { EmojiPicker } from 'emoji-picker-js'
import { generateHTML } from '@tiptap/core' import { generateHTML } from '@tiptap/core'
import isElectron from 'is-electron' import isElectron from 'is-electron'
import localForage from 'localforage'
import axios from 'axios' import axios from 'axios'
import Highlight from '@tiptap/extension-highlight' import Highlight from '@tiptap/extension-highlight'
@ -32,7 +33,9 @@ import '@vaadin/tooltip'
import { chatLimit, totalMsgCount } from './ChatPage.js' import { chatLimit, totalMsgCount } from './ChatPage.js'
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
const chatLastSeen = localForage.createInstance({
name: "chat-last-seen",
})
let toggledMessage = {} let toggledMessage = {}
const uid = new ShortUniqueId() const uid = new ShortUniqueId()
@ -259,7 +262,9 @@ class ChatScroller extends LitElement {
this.oldMessages = [] this.oldMessages = []
this._upObserverhandler = this._upObserverhandler.bind(this) this._upObserverhandler = this._upObserverhandler.bind(this)
this.newListMessages = this.newListMessages.bind(this) this.newListMessages = this.newListMessages.bind(this)
this.newListMessagesUnreadMessages = this.newListMessagesUnreadMessages.bind(this)
this._downObserverHandler = this._downObserverHandler.bind(this) this._downObserverHandler = this._downObserverHandler.bind(this)
this.isLastMessageBeforeUnread = this.isLastMessageBeforeUnread.bind(this)
this.replaceMessagesWithUpdate = this.replaceMessagesWithUpdate.bind(this) this.replaceMessagesWithUpdate = this.replaceMessagesWithUpdate.bind(this)
this.__bottomObserverForFetchingMessagesHandler = this.__bottomObserverForFetchingMessagesHandler.bind(this) this.__bottomObserverForFetchingMessagesHandler = this.__bottomObserverForFetchingMessagesHandler.bind(this)
this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address
@ -273,6 +278,7 @@ class ChatScroller extends LitElement {
this.isLoadingBefore = false this.isLoadingBefore = false
this.isLoadingAfter = false this.isLoadingAfter = false
this.disableAddingNewMessages = false this.disableAddingNewMessages = false
this.lastReadMessageTimestamp = null
} }
addSeenMessage(val) { addSeenMessage(val) {
@ -346,6 +352,57 @@ class ChatScroller extends LitElement {
} }
async newListMessagesUnreadMessages(newMessages, message, lastReadMessageTimestamp) {
const viewElement = this.shadowRoot.querySelector("#viewElement");
console.log('sup', lastReadMessageTimestamp);
let data = [];
const copy = [...newMessages];
let dividerPlaced = false; // To ensure the divider is added only once
// Start from the end of the list (newest messages)
for (let i = copy.length - 1; i >= 0; i--) {
let newMessage = copy[i];
// Initialize a property for the divider
newMessage.isDivider = false;
// Check if this is the message before which the divider should be placed
if (!dividerPlaced && newMessage.timestamp <= lastReadMessageTimestamp) {
console.log('true true')
newMessage.isDivider = true;
dividerPlaced = true; // Ensure the divider is only added once
break; // Exit once the divider is placed
}
}
copy.forEach((newMessage, groupIndex) => {
const lastGroupedMessage = data[data.length - 1];
if (this.shouldGroupWithLastMessage(newMessage, lastGroupedMessage)) {
lastGroupedMessage.messages.push(newMessage);
} else {
data.push({
messages: [newMessage],
...newMessage
});
}
});
console.log({ data });
this.messagesToRender = data;
this.clearLoaders();
this.requestUpdate();
await this.updateComplete;
const findElement = this.shadowRoot.getElementById('unread-divider-id')
if (findElement) {
findElement.scrollIntoView({ behavior: 'auto', block: 'center' })
}
}
async addNewMessages(newMessages, type) { async addNewMessages(newMessages, type) {
@ -402,7 +459,7 @@ class ChatScroller extends LitElement {
if (type === 'initial') { if (type === 'initial') {
this.viewElement.scrollTop = this.viewElement.scrollHeight viewElement.scrollTop = viewElement.scrollHeight
@ -547,13 +604,17 @@ class ChatScroller extends LitElement {
async updated(changedProperties) { async updated(changedProperties) {
if (changedProperties && changedProperties.has('messages')) { if (changedProperties && changedProperties.has('messages')) {
console.log({changedProperties}, this.messages)
if (this.messages.type === 'initial') { if (this.messages.type === 'initial') {
this.addNewMessages(this.messages.messages, 'initial') this.addNewMessages(this.messages.messages, 'initial')
} else if (this.messages.type === 'new') this.addNewMessages(this.messages.messages) } else if (this.messages.type === 'initialLastSeen') {
this.newListMessagesUnreadMessages(this.messages.messages, 'initialLastSeen', this.messages.lastReadMessageTimestamp)
}
else if (this.messages.type === 'new') this.addNewMessages(this.messages.messages)
else if(this.messages.type === 'newComingInAuto') this.addNewMessages(this.messages.messages, 'newComingInAuto') else if(this.messages.type === 'newComingInAuto') this.addNewMessages(this.messages.messages, 'newComingInAuto')
else if (this.messages.type === 'old') this.prependOldMessages(this.messages.messages) else if (this.messages.type === 'old') this.prependOldMessages(this.messages.messages)
else if (this.messages.type === 'inBetween') this.newListMessages(this.messages.messages, this.messages.signature) else if (this.messages.type === 'inBetween') this.newListMessages(this.messages.messages, this.messages.signature)
@ -567,6 +628,15 @@ class ChatScroller extends LitElement {
} }
isLastMessageBeforeUnread(message, formattedMessages) {
// if the message is the last one in the older messages list and its timestamp is before the user's last seen timestamp
if (message.timestamp < this.lastReadMessageTimestamp && formattedMessages.indexOf(message) === (formattedMessages.length - 21)) {
return true;
}
return false;
}
render() { render() {
// let formattedMessages = this.messages.reduce((messageArray, message) => { // let formattedMessages = this.messages.reduce((messageArray, message) => {
// const currentMessage = this.updateMessageHash[message.signature] || message; // const currentMessage = this.updateMessageHash[message.signature] || message;
@ -617,10 +687,12 @@ class ChatScroller extends LitElement {
formattedMessages, formattedMessages,
(formattedMessage) => formattedMessage.id, // Use .id as the unique key for formattedMessage. (formattedMessage) => formattedMessage.id, // Use .id as the unique key for formattedMessage.
(formattedMessage) => html` (formattedMessage) => html`
${repeat( ${repeat(
formattedMessage.messages, formattedMessage.messages,
(message) => message.signature, (message) => message.signature,
(message, indexMessage) => html` (message, indexMessage) => html`
<message-template <message-template
.emojiPicker=${this.emojiPicker} .emojiPicker=${this.emojiPicker}
.escapeHTML=${this.escapeHTML} .escapeHTML=${this.escapeHTML}
@ -644,8 +716,13 @@ class ChatScroller extends LitElement {
.addSeenMessage=${(val) => this.addSeenMessage(val)} .addSeenMessage=${(val) => this.addSeenMessage(val)}
.listSeenMessages=${this.listSeenMessages} .listSeenMessages=${this.listSeenMessages}
chatId=${this.chatId} chatId=${this.chatId}
></message-template>` ></message-template>
${message.isDivider ? html`<div class="unread-divider" id="unread-divider-id">Unread Messages Below</div>` : null}
`
)} )}
` `
)} )}
<div style=${"height: 1px; margin-top: -100px"} id='bottomObserverForFetchingMessages'></div> <div style=${"height: 1px; margin-top: -100px"} id='bottomObserverForFetchingMessages'></div>
@ -708,7 +785,6 @@ class ChatScroller extends LitElement {
async firstUpdated() { async firstUpdated() {
this.changeTheme() this.changeTheme()
window.addEventListener('storage', () => { window.addEventListener('storage', () => {
const checkTheme = localStorage.getItem('qortalTheme') const checkTheme = localStorage.getItem('qortalTheme')

Loading…
Cancel
Save