mirror of
https://github.com/nextcloud/spreed.git
synced 2025-07-22 06:48:21 +00:00
Merge pull request #15519 from nextcloud/fix/noid/thread-poll-message
fix(threads): add instant update for threads list
This commit is contained in:
@ -117,6 +117,9 @@ function isExistingMessage(message: ChatMessage | DeletedParentMessage): message
|
||||
return 'messageType' in message
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to thread if it exists
|
||||
*/
|
||||
function goToThread() {
|
||||
if (isExistingMessage(message) && message.threadId) {
|
||||
threadId.value = message.threadId
|
||||
|
@ -17,6 +17,7 @@ import NcDateTime from '@nextcloud/vue/components/NcDateTime'
|
||||
import NcListItem from '@nextcloud/vue/components/NcListItem'
|
||||
import IconArrowLeftTop from 'vue-material-design-icons/ArrowLeftTop.vue'
|
||||
import IconBellOutline from 'vue-material-design-icons/BellOutline.vue'
|
||||
import IconCommentAlertOutline from 'vue-material-design-icons/CommentAlertOutline.vue'
|
||||
import AvatarWrapper from '../../AvatarWrapper/AvatarWrapper.vue'
|
||||
import { getDisplayNameWithFallback } from '../../../utils/getDisplayName.ts'
|
||||
import { parseToSimpleMessage } from '../../../utils/textParse.ts'
|
||||
@ -26,9 +27,19 @@ const { thread } = defineProps<{ thread: ThreadInfo }>()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const threadAuthor = computed(() => getDisplayNameWithFallback(thread.first.actorDisplayName, thread.first.actorType, true))
|
||||
const threadAuthor = computed(() => {
|
||||
if (!thread.first) {
|
||||
return
|
||||
}
|
||||
return getDisplayNameWithFallback(thread.first.actorDisplayName, thread.first.actorType, true)
|
||||
})
|
||||
const lastActivity = computed(() => thread.thread.lastActivity * 1000)
|
||||
const name = computed(() => parseToSimpleMessage(thread.first.message, thread.first.messageParameters))
|
||||
const name = computed(() => {
|
||||
if (!thread.first) {
|
||||
return t('spreed', 'Thread origin message expired')
|
||||
}
|
||||
return parseToSimpleMessage(thread.first.message, thread.first.messageParameters)
|
||||
})
|
||||
const subname = computed(() => {
|
||||
if (!thread.last) {
|
||||
return t('spreed', 'No messages')
|
||||
@ -72,14 +83,22 @@ const timeFormat = computed<Intl.DateTimeFormatOptions>(() => {
|
||||
force-menu>
|
||||
<template #icon>
|
||||
<AvatarWrapper
|
||||
v-if="thread.first"
|
||||
:id="thread.first.actorId"
|
||||
:name="thread.first.actorDisplayName"
|
||||
:source="thread.first.actorType"
|
||||
disable-menu
|
||||
:token="thread.thread.roomToken" />
|
||||
<IconCommentAlertOutline
|
||||
v-else
|
||||
:size="20" />
|
||||
</template>
|
||||
<template #name>
|
||||
<span class="thread__author">{{ threadAuthor }}</span>
|
||||
<span
|
||||
v-if="threadAuthor"
|
||||
class="thread__author">
|
||||
{{ threadAuthor }}
|
||||
</span>
|
||||
<span>{{ name }}</span>
|
||||
</template>
|
||||
<template #subname>
|
||||
|
@ -26,6 +26,7 @@ import {
|
||||
} from '../services/messagesService.ts'
|
||||
import { useActorStore } from '../stores/actor.ts'
|
||||
import { useCallViewStore } from '../stores/callView.ts'
|
||||
import { useChatExtrasStore } from '../stores/chatExtras.ts'
|
||||
import { useGuestNameStore } from '../stores/guestName.js'
|
||||
import { usePollsStore } from '../stores/polls.ts'
|
||||
import { useReactionsStore } from '../stores/reactions.js'
|
||||
@ -298,6 +299,10 @@ const mutations = {
|
||||
const preparedMessage = !message.parent && storedMessage?.parent
|
||||
? { ...message, parent: storedMessage.parent }
|
||||
: message
|
||||
|
||||
if (preparedMessage.parent) {
|
||||
preparedMessage.parent.isThread = preparedMessage.isThread
|
||||
}
|
||||
state.messages[token][message.id] = preparedMessage
|
||||
},
|
||||
/**
|
||||
@ -475,21 +480,6 @@ const mutations = {
|
||||
}
|
||||
},
|
||||
|
||||
removeExpiredMessages(state, { token }) {
|
||||
if (!state.messages[token]) {
|
||||
return
|
||||
}
|
||||
|
||||
const timestamp = convertToUnix(Date.now())
|
||||
const messageIds = Object.keys(state.messages[token])
|
||||
messageIds.forEach((messageId) => {
|
||||
if (state.messages[token][messageId].expirationTimestamp
|
||||
&& timestamp > state.messages[token][messageId].expirationTimestamp) {
|
||||
delete state.messages[token][messageId]
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
easeMessageList(state, { token, lastReadMessage }) {
|
||||
if (!state.messages[token]) {
|
||||
return
|
||||
@ -544,6 +534,7 @@ const actions = {
|
||||
processMessage(context, { token, message }) {
|
||||
const sharedItemsStore = useSharedItemsStore()
|
||||
const actorStore = useActorStore()
|
||||
const chatExtrasStore = useChatExtrasStore()
|
||||
|
||||
if (message.systemMessage === 'message_deleted'
|
||||
|| message.systemMessage === 'reaction'
|
||||
@ -574,6 +565,21 @@ const actions = {
|
||||
if (message.parent.id === context.getters.conversation(token).lastMessage?.id) {
|
||||
context.dispatch('updateConversationLastMessage', { token, lastMessage: message.parent })
|
||||
}
|
||||
|
||||
const thread = chatExtrasStore.getThread(token, message.parent.threadId)
|
||||
// update threads, if it is the first or the last message in the thread
|
||||
if (thread && (thread.last?.id === message.parent.id || thread.first?.id === message.parent.id)) {
|
||||
const updatedData = {
|
||||
thread: {
|
||||
...thread.thread,
|
||||
lastActivity: message.parent.timestamp,
|
||||
},
|
||||
first: (thread.first?.id === message.parent.id) ? message.parent : undefined,
|
||||
last: (thread.last?.id === message.parent.id) ? message.parent : undefined,
|
||||
}
|
||||
chatExtrasStore.updateThread(token, message.parent.threadId, updatedData)
|
||||
}
|
||||
|
||||
// Check existing messages for having a deleted / edited message as parent, and update them
|
||||
context.getters.messagesList(token)
|
||||
.filter((storedMessage) => storedMessage.parent?.id === message.parent.id && JSON.stringify(storedMessage.parent) !== JSON.stringify(message.parent))
|
||||
@ -589,6 +595,10 @@ const actions = {
|
||||
.forEach((storedMessage) => {
|
||||
context.commit('addMessage', { token, message: Object.assign({}, storedMessage, { isThread: true }) })
|
||||
})
|
||||
// Fetch thread data in case it doesn't exist in the store yet
|
||||
if (!chatExtrasStore.getThread(token, message.threadId) && chatExtrasStore.threads[token] !== undefined) {
|
||||
chatExtrasStore.fetchSingleThread(token, message.threadId)
|
||||
}
|
||||
}
|
||||
|
||||
// Quit processing
|
||||
@ -639,6 +649,7 @@ const actions = {
|
||||
|
||||
if (message.systemMessage === 'history_cleared') {
|
||||
sharedItemsStore.purgeSharedItemsStore(token, message.id)
|
||||
chatExtrasStore.clearThreads(token, message.id)
|
||||
context.commit('clearMessagesHistory', {
|
||||
token,
|
||||
id: message.id,
|
||||
@ -647,6 +658,24 @@ const actions = {
|
||||
|
||||
context.commit('addMessage', { token, message })
|
||||
|
||||
// Update threads
|
||||
if (message.isThread) {
|
||||
const thread = chatExtrasStore.getThread(token, message.threadId)
|
||||
if (thread) {
|
||||
const updateNumReplies = thread.thread.lastMessageId < message.id
|
||||
chatExtrasStore.updateThread(message.token, message.threadId, {
|
||||
thread: {
|
||||
id: message.threadId,
|
||||
roomToken: message.token,
|
||||
lastMessageId: message.id,
|
||||
lastActivity: message.timestamp,
|
||||
numReplies: thread.thread.numReplies + (updateNumReplies ? 1 : 0),
|
||||
},
|
||||
last: message,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (message.messageParameters && [MESSAGE.TYPE.COMMENT, MESSAGE.TYPE.VOICE_MESSAGE, MESSAGE.TYPE.RECORD_AUDIO, MESSAGE.TYPE.RECORD_VIDEO].includes(message.messageType)) {
|
||||
if (message.messageParameters?.object || message.messageParameters?.file) {
|
||||
// Handle voice messages, shares with single file, polls, deck cards, e.t.c
|
||||
@ -1416,7 +1445,20 @@ const actions = {
|
||||
},
|
||||
|
||||
async removeExpiredMessages(context, { token }) {
|
||||
context.commit('removeExpiredMessages', { token })
|
||||
if (!context.state.messages[token]) {
|
||||
return
|
||||
}
|
||||
const chatExtrasStore = useChatExtrasStore()
|
||||
const timestamp = convertToUnix(Date.now())
|
||||
|
||||
context.getters.messagesList(token).forEach((message) => {
|
||||
if (message.expirationTimestamp && timestamp > message.expirationTimestamp) {
|
||||
if (message.isThread) {
|
||||
chatExtrasStore.removeMessageFromThread(token, message.threadId, message.id)
|
||||
}
|
||||
context.commit('deleteMessage', { token, id: message.id })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
async easeMessageList(context, { token }) {
|
||||
|
@ -177,6 +177,52 @@ export const useChatExtrasStore = defineStore('chatExtras', {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a thread from the store
|
||||
*
|
||||
* @param token - conversation token
|
||||
* @param messageId - message id to remove all preceding threads (remove all, if omitted)
|
||||
*/
|
||||
clearThreads(token: string, messageId?: number) {
|
||||
if (messageId) {
|
||||
// Clear threads that are older than the given messageId
|
||||
for (const threadId of Object.keys(Object(this.threads[token]))) {
|
||||
if (+threadId < messageId) {
|
||||
delete this.threads[token][+threadId]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Clear all threads for the conversation
|
||||
delete this.threads[token]
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a message from a thread object
|
||||
*
|
||||
* @param token - conversation token
|
||||
* @param threadId - thread id to remove message from
|
||||
* @param messageId - message id to remove
|
||||
*/
|
||||
removeMessageFromThread(token: string, threadId: number, messageId: number) {
|
||||
if (!this.threads[token]?.[threadId]) {
|
||||
return
|
||||
}
|
||||
|
||||
const thread = this.threads[token][threadId]
|
||||
if (thread.first.id === messageId) {
|
||||
// @ts-expect-error - missing null type in ThreadInfo
|
||||
thread.first = null
|
||||
} else {
|
||||
this.threads[token][threadId].thread.numReplies -= 1
|
||||
if (thread.last.id === messageId) {
|
||||
// Last message was removed but there might be older messages in the thread
|
||||
// that don't have expiration timestamp
|
||||
this.fetchSingleThread(token, threadId)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get chat input for current conversation (from store or BrowserStorage)
|
||||
*
|
||||
@ -318,6 +364,7 @@ export const useChatExtrasStore = defineStore('chatExtras', {
|
||||
purgeChatExtras(token: string) {
|
||||
this.removeParentIdToReply(token)
|
||||
this.removeChatInput(token)
|
||||
this.clearThreads(token)
|
||||
},
|
||||
|
||||
setTasksCounters({ tasksCount, tasksDoneCount }: { tasksCount: number, tasksDoneCount: number }) {
|
||||
|
Reference in New Issue
Block a user