From fdf67210e99652e47cbb97bf1c0111a0ead0de97 Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Sat, 2 Mar 2024 00:09:51 +0100 Subject: WIP Signed-off-by: Maksim Sukharev --- .../Message/MessagePart/MessageBody.vue | 3 +- src/components/MessagesList/MessagesList.vue | 117 ++++++++++++++++++--- src/constants.js | 2 + src/store/messagesStore.js | 24 +++++ 4 files changed, 129 insertions(+), 17 deletions(-) diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue index 30dc957a8..d51d87902 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue +++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/MessageBody.vue @@ -269,7 +269,8 @@ export default { // Add a new line after file to split content into different paragraphs return '{file}' + '\n\n' + this.message } else { - return this.message + // TODO remove + return this.id + ' -- ' + this.message } }, diff --git a/src/components/MessagesList/MessagesList.vue b/src/components/MessagesList/MessagesList.vue index 29a3c4998..661f59ca5 100644 --- a/src/components/MessagesList/MessagesList.vue +++ b/src/components/MessagesList/MessagesList.vue @@ -68,6 +68,18 @@ + +
+

{{ t('spreed', 'This conversation is in history mode') }}

+
+ + {{ t('spreed', 'Load recent messages') }} + + + {{ t('spreed', 'Load current history') }} + +
+
@@ -82,6 +94,7 @@ import { getCapabilities } from '@nextcloud/capabilities' import { subscribe, unsubscribe } from '@nextcloud/event-bus' import moment from '@nextcloud/moment' +import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js' import MessagesGroup from './MessagesGroup/MessagesGroup.vue' @@ -98,6 +111,7 @@ export default { components: { LoadingPlaceholder, Message, + NcButton, NcEmptyContent, TransitionWrapper }, @@ -161,6 +175,8 @@ export default { loadingOldMessages: false, + loadingNewMessages: false, + isInitialisingMessages: false, isFocusingMessage: false, @@ -235,6 +251,10 @@ export default { return this.$store.getters.hasMoreMessagesToLoad(this.token) }, + isConversationInHistoryMode() { + return this.$store.getters.isConversationInHistoryMode(this.token) + }, + /** * Returns whether the current participant is a participant of the * current conversation or not. @@ -712,8 +732,13 @@ export default { this.isInitialisingMessages = false - // get new messages - await this.lookForNewMessages() + if (!this.isConversationInHistoryMode) { + // get new messages + await this.lookForNewMessages() + } else { + // stop polling + this.$store.dispatch('cancelLookForNewMessages', { requestId: this.chatIdentifier }) + } if (this.loadChatInLegacyMode || focusMessageId === null) { // don't scroll if lookForNewMessages was polling as we don't want @@ -799,6 +824,40 @@ export default { } }, + /** + * Get message history (with new messages relative to context). + * + * @param {boolean} [includeLastKnown] Include or exclude the last known message in the response + */ + async getNewMessages(includeLastKnown = false) { + // Make the request + this.loadingNewMessages = true + try { + await this.$store.dispatch('fetchMessages', { + token: this.token, + lastKnownMessageId: this.$store.getters.getLastKnownMessageId(this.token), + includeLastKnown, + lookIntoFuture: 1, + minimumVisible: CHAT.MINIMUM_VISIBLE, + }) + } catch (exception) { + if (Axios.isCancel(exception)) { + console.debug('The request has been canceled', exception) + } + // if (exception?.response?.status === 304) { + // 304 - Not modified + // } + console.error(exception) + } + this.loadingNewMessages = false + + console.log('InHistoryMode', this.isConversationInHistoryMode) + if (!this.isConversationInHistoryMode) { + // Start polling new messages, if this is the end of the chat + this.getNewMessagesPolling() + } + }, + /** * Creates a long polling request for a new message. * @@ -922,8 +981,9 @@ export default { const tolerance = 10 // For chats, scrolled to bottom or / and fitted in one screen - if (scrollOffset < clientHeight + tolerance && scrollOffset > clientHeight - tolerance && !this.hasMoreMessagesToLoad) { - this.setChatScrolledToBottom(true) + if (scrollOffset < clientHeight + tolerance && scrollOffset > clientHeight - tolerance + && (!this.hasMoreMessagesToLoad || this.isConversationInHistoryMode)) { + this.setChatScrolledToBottom(!this.hasMoreMessagesToLoad) this.displayMessagesLoader = false this.previousScrollTopValue = scrollTop this.debounceUpdateReadMarkerPosition() @@ -1243,29 +1303,34 @@ export default { && from.token === to.token && from.hash !== to.hash) { + // the hash is cleared, need to purge messages list and load new messages + if (this.isConversationInHistoryMode && !to.hash) { + await this.$store.dispatch('purgeMessagesStore', this.token) + this.$nextTick(async () => { + // TODO just fetch last pack or start with preconditions? + await this.handleStartGettingMessagesPreconditions() + }) + } + // the hash changed, need to focus/highlight another message if (to.hash && to.hash.startsWith('#message_')) { const focusedId = this.getMessageIdFromHash(to.hash) if (this.messagesList.find(m => m.id === focusedId)) { // need some delay (next tick is too short) to be able to run - // after the browser's native "scroll to anchor" from - // the hash + // after the browser's native "scroll to anchor" from the hash window.setTimeout(() => { // scroll to message in URL anchor this.focusMessage(focusedId, true) }, 2) } else { - // Update environment around context to fill the gaps - this.$store.dispatch('setFirstKnownMessageId', { - token: this.token, - id: focusedId, - }) - this.$store.dispatch('setLastKnownMessageId', { - token: this.token, - id: focusedId, + // the message is far from current list, need to purge messages list and load old messages + // TODO delete only for 'history' mode - need to know conditions (context timestamp) + console.log('InHistoryMode', this.isConversationInHistoryMode) + this.$store.dispatch('purgeMessagesStore', this.token) + this.$nextTick(async () => { + await this.handleStartGettingMessagesPreconditions() + this.focusMessage(focusedId, true) }) - await this.getMessageContext(focusedId) - this.focusMessage(focusedId, true) } } } @@ -1287,6 +1352,11 @@ export default { messagesGroupComponent(group) { return group.isSystemMessagesGroup ? MessagesSystemGroup : MessagesGroup }, + + clearRouterHash() { + this.$router.push({ name: 'conversation', params: { token: this.token } }) + .catch(err => console.debug(`Error while pushing the new conversation's route: ${err}`)) + }, }, } @@ -1311,6 +1381,21 @@ export default { height: 40px; transform: translatex(-64px); } + + &__wrapper { + max-width: 800px; + padding: 8px 140px 8px 52px; + margin: 0 auto; + + &-content { + display: flex; + justify-content: center; + align-items: center; + gap: 8px; + margin-top: 4px; + color: var(--color-text-maxcontrast); + } + } } .messages-list { diff --git a/src/constants.js b/src/constants.js index 9a40b0c2f..5b471dbc2 100644 --- a/src/constants.js +++ b/src/constants.js @@ -35,6 +35,8 @@ export const SESSION = { export const CHAT = { FETCH_LIMIT: 100, MINIMUM_VISIBLE: 5, + HISTORY_LIMIT_TIME: 10 * 60, // 24 hours in seconds + HISTORY_LIMIT_AMOUNT: 20, // 300 known unread messages } export const CALL = { diff --git a/src/store/messagesStore.js b/src/store/messagesStore.js index 932766db2..7da5cf6c9 100644 --- a/src/store/messagesStore.js +++ b/src/store/messagesStore.js @@ -157,6 +157,27 @@ const getters = { return getters.getLastKnownMessageId(token) < conversation.lastMessage.id }, + /** + * Returns whether the conversation is in 'history' mode, which means that the current + * message list contain message context which is older than 24 hours + * or amount of unread messages is larger than 300 messages. + * If true, the call to "lookForNewMessages" will be blocked. + * + * @param {object} state the state object. + * @param {object} getters the getters object. + * @return {boolean} true if context is old enough, false otherwise + */ + isConversationInHistoryMode: (state, getters) => (token) => { + const conversation = getters.conversation(token) + const lastKnownMessage = getters.message(token, getters.getLastKnownMessageId(token)) + if (!conversation || !lastKnownMessage) { + return false + } + + return conversation.lastMessage.timestamp - lastKnownMessage.timestamp > CHAT.HISTORY_LIMIT_TIME + || conversation.unreadMessages > CHAT.HISTORY_LIMIT_AMOUNT + }, + isMessageListPopulated: (state) => (token) => { return !!state.loadedMessages[token] }, @@ -414,6 +435,9 @@ const mutations = { if (state.messages[token]) { Vue.delete(state.messages, token) } + if (state.loadedMessages[token]) { + Vue.delete(state.loadedMessages, token) + } }, /** -- cgit v1.2.3