diff --git a/src/components/MessagesList/MessagesGroup/Message/Message.vue b/src/components/MessagesList/MessagesGroup/Message/Message.vue
index 079a9ec4bcb..b7d49c175fc 100644
--- a/src/components/MessagesList/MessagesGroup/Message/Message.vue
+++ b/src/components/MessagesList/MessagesGroup/Message/Message.vue
@@ -232,6 +232,17 @@ export default {
type: Boolean,
default: undefined,
},
+ /**
+ * Specifies if the message is inside a collapsed group.
+ */
+ isCollapsedSystemMessage: {
+ type: Boolean,
+ default: false,
+ },
+ lastCollapsedMessageId: {
+ type: [String, Number],
+ default: 0,
+ },
/**
* The type of the message.
*/
@@ -329,8 +340,17 @@ export default {
return !this.nextMessageId || this.id === this.conversation?.lastMessage?.id
},
+ visualLastLastReadMessageId() {
+ return this.$store.getters.getVisualLastReadMessageId(this.token)
+ },
+
isLastReadMessage() {
- return !this.isLastMessage && this.id === this.$store.getters.getVisualLastReadMessageId(this.token)
+ if (this.isLastMessage) {
+ return false
+ }
+ return (!this.isCollapsedSystemMessage && this.id === this.visualLastLastReadMessageId)
+ || (this.isCollapsedSystemMessage && this.id === this.visualLastLastReadMessageId && this.id !== this.lastCollapsedMessageId)
+ || (this.isCombinedSystemMessage && this.lastCollapsedMessageId === this.visualLastLastReadMessageId)
},
isSystemMessage() {
diff --git a/src/components/MessagesList/MessagesGroup/MessagesSystemGroup.vue b/src/components/MessagesList/MessagesGroup/MessagesSystemGroup.vue
index 46d23ac6100..01dbd38b20a 100644
--- a/src/components/MessagesList/MessagesGroup/MessagesSystemGroup.vue
+++ b/src/components/MessagesList/MessagesGroup/MessagesSystemGroup.vue
@@ -32,6 +32,7 @@
:is-combined-system-message-collapsed="messagesCollapsed.collapsed"
:next-message-id="getNextMessageId(messagesCollapsed.messages.at(-1))"
:previous-message-id="getPrevMessageId(messagesCollapsed.messages.at(0))"
+ :last-collapsed-message-id="messagesCollapsed.lastId"
@toggle-combined-system-message="toggleCollapsed(messagesCollapsed)" />
@@ -153,6 +156,14 @@ export default {
return 'user_added'
}
+ // Group users removed by one actor
+ if (message1.systemMessage === 'user_removed'
+ && message1.systemMessage === message2.systemMessage
+ && message1.actorId === message2.actorId
+ && message1.actorType === message2.actorType) {
+ return 'user_removed'
+ }
+
// Group users reconnected in a minute
if (message1.systemMessage === 'call_joined'
&& message2.systemMessage === 'call_left'
@@ -194,30 +205,30 @@ export default {
let lastMessage = null
let forceNextGroup = false
for (const message of messages) {
- const isLastRead = message.id === this.lastReadMessageId
const groupingType = this.messagesShouldBeGrouped(message, lastMessage)
if (!groupingType || forceNextGroup) {
- groups.push({ id: message.id, messages: [message], type: '', collapsed: this.groupIsCollapsed[message.id] ?? !isLastRead })
+ groups.push({ id: message.id, lastId: message.id, messages: [message], type: '', collapsed: this.groupIsCollapsed[message.id] ?? true })
forceNextGroup = false
} else {
if (groupingType === 'call_reconnected') {
- groups.push({ id: message.id, messages: [groups.at(-1).messages.pop()], type: '', collapsed: this.groupIsCollapsed[message.id] ?? !isLastRead })
+ groups.push({ id: message.id, lastId: message.id, messages: [groups.at(-1).messages.pop()], type: '', collapsed: this.groupIsCollapsed[message.id] ?? true })
+ groups.at(-1).lastId = groups.at(-1).messages.at(-1).id
forceNextGroup = true
}
groups.at(-1).messages.push(message)
+ groups.at(-1).lastId = message.id
groups.at(-1).type = groupingType
- if (isLastRead) {
+
+ // Check if last read message is hidden inside the collapsed group, and open it, if so.
+ // Otherwise, combined system message will show a marker
+ const isLastReadInsideGroup = this.lastReadMessageId >= groups.at(-1).id && this.lastReadMessageId < groups.at(-1).lastId
+ if (isLastReadInsideGroup) {
groups.at(-1).collapsed = false
}
}
lastMessage = message
}
- groups.forEach(group => {
- if (this.groupIsCollapsed[group.id] === undefined) {
- this.groupIsCollapsed[group.id] = group.collapsed
- }
- })
return groups
},
diff --git a/src/components/MessagesList/MessagesList.vue b/src/components/MessagesList/MessagesList.vue
index 58c9778cd0f..e83128c4c75 100644
--- a/src/components/MessagesList/MessagesList.vue
+++ b/src/components/MessagesList/MessagesList.vue
@@ -431,10 +431,10 @@ export default {
},
softUpdateAuthorGroups(oldGroups, newGroups, dateTimestamp) {
- const oldKeys = Object.keys(oldGroups)
Object.entries(newGroups).forEach(([id, newGroup]) => {
if (!oldGroups[id]) {
- const oldId = oldKeys.find(key => id < key && oldGroups[key].nextMessageId <= newGroup.nextMessageId)
+ const oldId = Object.keys(oldGroups)
+ .find(key => id < key && oldGroups[key].nextMessageId <= newGroup.nextMessageId)
if (oldId) {
// newGroup includes oldGroup and more old messages, remove oldGroup
delete this.messagesGroupedByDateByAuthor[dateTimestamp][oldId]
diff --git a/src/composables/useCombinedSystemMessage.js b/src/composables/useCombinedSystemMessage.js
index a37640c86bc..c9810a86b7c 100644
--- a/src/composables/useCombinedSystemMessage.js
+++ b/src/composables/useCombinedSystemMessage.js
@@ -145,6 +145,57 @@ export function useCombinedSystemMessage() {
}
}
+ // Handle cases when actor removed users from conversation (when remove team/group, for example)
+ if (type === 'user_removed') {
+ messages.forEach(message => {
+ if (checkIfSelfIsOneOfUsers(message)) {
+ selfIsUser = true
+ } else {
+ combinedMessage.messageParameters[`user${referenceIndex}`] = message.messageParameters.user
+ referenceIndex++
+ }
+ usersCounter++
+ })
+
+ if (checkIfSelfIsActor(combinedMessage)) {
+ if (usersCounter === 2) {
+ combinedMessage.message = t('spreed', 'You removed {user0} and {user1}')
+ } else {
+ combinedMessage.message = n('spreed',
+ 'You removed {user0}, {user1} and %n more participant',
+ 'You removed {user0}, {user1} and %n more participants', usersCounter - 2)
+ }
+ } else if (selfIsUser) {
+ if (usersCounter === 2) {
+ combinedMessage.message = actorIsAdministrator
+ ? t('spreed', 'An administrator removed you and {user0}')
+ : t('spreed', '{actor} removed you and {user0}')
+ } else {
+ combinedMessage.message = actorIsAdministrator
+ ? n('spreed',
+ 'An administrator removed you, {user0} and %n more participant',
+ 'An administrator removed you, {user0} and %n more participants', usersCounter - 2)
+ : n('spreed',
+ '{actor} removed you, {user0} and %n more participant',
+ '{actor} removed you, {user0} and %n more participants', usersCounter - 2)
+ }
+ } else {
+ if (usersCounter === 2) {
+ combinedMessage.message = actorIsAdministrator
+ ? t('spreed', 'An administrator removed {user0} and {user1}')
+ : t('spreed', '{actor} removed {user0} and {user1}')
+ } else {
+ combinedMessage.message = actorIsAdministrator
+ ? n('spreed',
+ 'An administrator removed {user0}, {user1} and %n more participant',
+ 'An administrator removed {user0}, {user1} and %n more participants', usersCounter - 2)
+ : n('spreed',
+ '{actor} removed {user0}, {user1} and %n more participant',
+ '{actor} removed {user0}, {user1} and %n more participants', usersCounter - 2)
+ }
+ }
+ }
+
// Handle cases when users joined or left the call
if (type === 'call_joined' || type === 'call_left') {
const storedUniqueUsers = []