+
+
@@ -40,13 +39,13 @@
ref="message"
v-bind="message"
:is-first-message="index === 0"
+ :is-temporary="message.timestamp === 0"
:next-message-id="(messages[index + 1] && messages[index + 1].id) || nextMessageId"
:previous-message-id="(index > 0 && messages[index - 1].id) || previousMessageId"
:actor-type="actorType"
:actor-id="actorId"
:actor-display-name="actorDisplayName"
- :show-author="!isSystemMessage"
- :is-temporary="message.timestamp === 0" />
+ show-author />
@@ -138,14 +137,6 @@ export default {
return displayName
},
- /**
- * Whether the given message is a system message
- *
- * @return {boolean}
- */
- isSystemMessage() {
- return this.messages[0].systemMessage.length !== 0
- },
},
methods: {
@@ -188,9 +179,6 @@ export default {
display: flex;
margin: auto;
padding: 0;
- &--system {
- padding-left: $clickable-area + 8px;
- }
&:focus {
background-color: rgba(47, 47, 47, 0.068);
}
diff --git a/src/components/MessagesList/MessagesGroup/MessagesSystemGroup.vue b/src/components/MessagesList/MessagesGroup/MessagesSystemGroup.vue
new file mode 100644
index 00000000000..f30bb8730dd
--- /dev/null
+++ b/src/components/MessagesList/MessagesGroup/MessagesSystemGroup.vue
@@ -0,0 +1,337 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/MessagesList/MessagesList.spec.js b/src/components/MessagesList/MessagesList.spec.js
index d3515a36349..95c68065c55 100644
--- a/src/components/MessagesList/MessagesList.spec.js
+++ b/src/components/MessagesList/MessagesList.spec.js
@@ -270,7 +270,7 @@ describe('MessagesList.vue', () => {
},
})
- const groups = wrapper.findAllComponents({ name: 'MessagesGroup' })
+ const groups = wrapper.findAllComponents({ ref: 'messagesGroup' })
expect(groups.exists()).toBe(true)
@@ -298,7 +298,7 @@ describe('MessagesList.vue', () => {
},
})
- const groups = wrapper.findAllComponents({ name: 'MessagesGroup' })
+ const groups = wrapper.findAllComponents({ ref: 'messagesGroup' })
expect(groups.exists()).toBe(true)
diff --git a/src/components/MessagesList/MessagesList.vue b/src/components/MessagesList/MessagesList.vue
index bd4721de6fd..a44aac58019 100644
--- a/src/components/MessagesList/MessagesList.vue
+++ b/src/components/MessagesList/MessagesList.vue
@@ -33,7 +33,8 @@ get the messagesList array and loop through the list to generate the messages.
:class="{'scroller--chatScrolledToBottom': isChatScrolledToBottom}"
@scroll="debounceHandleScroll">
-
diff --git a/src/composables/useCombinedSystemMessage.js b/src/composables/useCombinedSystemMessage.js
new file mode 100644
index 00000000000..4baafc8def5
--- /dev/null
+++ b/src/composables/useCombinedSystemMessage.js
@@ -0,0 +1,319 @@
+/*
+ * @copyright Copyright (c) 2023 Maksim Sukharev
+ *
+ * @author Maksim Sukharev
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import cloneDeep from 'lodash/cloneDeep.js'
+
+import { useStore } from './useStore.js'
+
+/**
+ * Create combined system message from the passed object
+ *
+ */
+export function useCombinedSystemMessage() {
+ const store = useStore()
+
+ /**
+ *
+ * @param {object} message message to check for
+ * @return {boolean}
+ */
+ function checkIfSelfIsActor(message) {
+ return message.actorId === store.getters.getActorId()
+ && message.actorType === store.getters.getActorType()
+ }
+
+ /**
+ *
+ * @param {object} message message to check for
+ * @return {boolean}
+ */
+ function checkIfSelfIsOneOfActors(message) {
+ return message.messageParameters.actor.id === store.getters.getActorId()
+ && message.messageParameters.actor.type + 's' === store.getters.getActorType()
+ }
+
+ /**
+ *
+ * @param {object} message message to check for
+ * @return {boolean}
+ */
+ function checkIfSelfIsOneOfUsers(message) {
+ return message.messageParameters.user.id === store.getters.getActorId()
+ && message.messageParameters.user.type + 's' === store.getters.getActorType()
+ }
+
+ /**
+ *
+ * @param {object} group object representing the group of system messages
+ * @param {number} group.id id of the group
+ * @param {Array} group.messages array of grouped messages
+ * @param {string} group.type combination type
+ * @param {boolean} group.collapsed collapsed state
+ * @return {object}
+ */
+ function createCombinedSystemMessage({ id, messages, type, collapsed }) {
+ const combinedMessage = cloneDeep(messages[0])
+
+ // Handle cases when users reconnected to the call
+ if (type === 'call_reconnected') {
+ if (checkIfSelfIsOneOfActors(combinedMessage)) {
+ combinedMessage.message = t('spreed', 'You reconnected to the call')
+ } else {
+ combinedMessage.message = t('spreed', '{actor} reconnected to the call')
+ }
+
+ return combinedMessage
+ }
+
+ // clear messageParameters to be filled later
+ const actor = messages[0].messageParameters.actor
+ combinedMessage.messageParameters = { actor }
+ const actorIsAdministrator = actor.id === 'guest/cli' && actor.type === 'guest'
+
+ // usersCounter should be equal at least 2, as we're using method only for groups
+ let usersCounter = 0
+ let selfIsUser = false
+ let referenceIndex = 0
+
+ // Handle cases when actor added users to conversation (when populate on creation, for example)
+ if (type === 'user_added') {
+ 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 added {user0} and {user1}')
+ } else {
+ combinedMessage.message = n('spreed',
+ 'You added {user0}, {user1} and %n more participant',
+ 'You added {user0}, {user1} and %n more participants', usersCounter - 2)
+ }
+ } else if (selfIsUser) {
+ if (usersCounter === 2) {
+ combinedMessage.message = actorIsAdministrator
+ ? t('spreed', 'An administrator added you and {user0}')
+ : t('spreed', '{actor} added you and {user0}')
+ } else {
+ combinedMessage.message = actorIsAdministrator
+ ? n('spreed',
+ 'An administrator added you, {user0} and %n more participant',
+ 'An administrator added you, {user0} and %n more participants', usersCounter - 2)
+ : n('spreed',
+ '{actor} added you, {user0} and %n more participant',
+ '{actor} added you, {user0} and %n more participants', usersCounter - 2)
+ }
+ } else {
+ if (usersCounter === 2) {
+ combinedMessage.message = actorIsAdministrator
+ ? t('spreed', 'An administrator added {user0} and {user1}')
+ : t('spreed', '{actor} added {user0} and {user1}')
+ } else {
+ combinedMessage.message = actorIsAdministrator
+ ? n('spreed',
+ 'An administrator added {user0}, {user1} and %n more participant',
+ 'An administrator added {user0}, {user1} and %n more participants', usersCounter - 2)
+ : n('spreed',
+ '{actor} added {user0}, {user1} and %n more participant',
+ '{actor} added {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 = []
+
+ messages.forEach(message => {
+ const actorReference = `${message.messageParameters.actor.id}_${message.messageParameters.actor.type}`
+ if (storedUniqueUsers.includes(actorReference)) {
+ return
+ }
+ if (checkIfSelfIsOneOfActors(message)) {
+ selfIsUser = true
+ } else {
+ combinedMessage.messageParameters[`user${referenceIndex}`] = message.messageParameters.actor
+ storedUniqueUsers.push(actorReference)
+ referenceIndex++
+ }
+ usersCounter++
+ })
+
+ if (usersCounter === 1) {
+ combinedMessage.message = messages[0].message
+ return combinedMessage
+ }
+
+ if (type === 'call_joined') {
+ if (selfIsUser) {
+ if (usersCounter === 2) {
+ combinedMessage.message = t('spreed', 'You and {user0} joined the call')
+ } else {
+ combinedMessage.message = n('spreed',
+ 'You, {user0} and %n more participant joined the call',
+ 'You, {user0} and %n more participants joined the call', usersCounter - 2)
+ }
+ } else {
+ if (usersCounter === 2) {
+ combinedMessage.message = t('spreed', '{user0} and {user1} joined the call')
+ } else {
+ combinedMessage.message = n('spreed',
+ '{user0}, {user1} and %n more participant joined the call',
+ '{user0}, {user1} and %n more participants joined the call', usersCounter - 2)
+ }
+ }
+ } else if (type === 'call_left') {
+ if (selfIsUser) {
+ if (usersCounter === 2) {
+ combinedMessage.message = t('spreed', 'You and {user0} left the call')
+ } else {
+ combinedMessage.message = n('spreed',
+ 'You, {user0} and %n more participant left the call',
+ 'You, {user0} and %n more participants left the call', usersCounter - 2)
+ }
+ } else {
+ if (usersCounter === 2) {
+ combinedMessage.message = t('spreed', '{user0} and {user1} left the call')
+ } else {
+ combinedMessage.message = n('spreed',
+ '{user0}, {user1} and %n more participant left the call',
+ '{user0}, {user1} and %n more participants left the call', usersCounter - 2)
+ }
+ }
+ }
+
+ }
+
+ // Handle cases when actor promoted several users to moderators
+ if (type === 'moderator_promoted') {
+ 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 promoted {user0} and {user1} to moderators')
+ } else {
+ combinedMessage.message = n('spreed',
+ 'You promoted {user0}, {user1} and %n more participant to moderators',
+ 'You promoted {user0}, {user1} and %n more participants to moderators', usersCounter - 2)
+ }
+ } else if (selfIsUser) {
+ if (usersCounter === 2) {
+ combinedMessage.message = actorIsAdministrator
+ ? t('spreed', 'An administrator promoted you and {user0} to moderators')
+ : t('spreed', '{actor} promoted you and {user0} to moderators')
+ } else {
+ combinedMessage.message = actorIsAdministrator
+ ? n('spreed',
+ 'An administrator promoted you, {user0} and %n more participant to moderators',
+ 'An administrator promoted you, {user0} and %n more participants to moderators', usersCounter - 2)
+ : n('spreed',
+ '{actor} promoted you, {user0} and %n more participant to moderators',
+ '{actor} promoted you, {user0} and %n more participants to moderators', usersCounter - 2)
+ }
+ } else {
+ if (usersCounter === 2) {
+ combinedMessage.message = actorIsAdministrator
+ ? t('spreed', 'An administrator promoted {user0} and {user1} to moderators')
+ : t('spreed', '{actor} promoted {user0} and {user1} to moderators')
+ } else {
+ combinedMessage.message = actorIsAdministrator
+ ? n('spreed',
+ 'An administrator promoted {user0}, {user1} and %n more participant to moderators',
+ 'An administrator promoted {user0}, {user1} and %n more participants to moderators', usersCounter - 2)
+ : n('spreed',
+ '{actor} promoted {user0}, {user1} and %n more participant to moderators',
+ '{actor} promoted {user0}, {user1} and %n more participants to moderators', usersCounter - 2)
+ }
+ }
+ }
+
+ // Handle cases when actor demoted several users from moderators
+ if (type === 'moderator_demoted') {
+ 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 demoted {user0} and {user1} from moderators')
+ } else {
+ combinedMessage.message = n('spreed',
+ 'You demoted {user0}, {user1} and %n more participant from moderators',
+ 'You demoted {user0}, {user1} and %n more participants from moderators', usersCounter - 2)
+ }
+ } else if (selfIsUser) {
+ if (usersCounter === 2) {
+ combinedMessage.message = actorIsAdministrator
+ ? t('spreed', 'An administrator demoted you and {user0} from moderators')
+ : t('spreed', '{actor} demoted you and {user0} from moderators')
+ } else {
+ combinedMessage.message = actorIsAdministrator
+ ? n('spreed',
+ 'An administrator demoted you, {user0} and %n more participant from moderators',
+ 'An administrator demoted you, {user0} and %n more participants from moderators', usersCounter - 2)
+ : n('spreed',
+ '{actor} demoted you, {user0} and %n more participant from moderators',
+ '{actor} demoted you, {user0} and %n more participants from moderators', usersCounter - 2)
+ }
+ } else {
+ if (usersCounter === 2) {
+ combinedMessage.message = actorIsAdministrator
+ ? t('spreed', 'An administrator demoted {user0} and {user1} from moderators')
+ : t('spreed', '{actor} demoted {user0} and {user1} from moderators')
+ } else {
+ combinedMessage.message = actorIsAdministrator
+ ? n('spreed',
+ 'An administrator demoted {user0}, {user1} and %n more participant from moderators',
+ 'An administrator demoted {user0}, {user1} and %n more participants from moderators', usersCounter - 2)
+ : n('spreed',
+ '{actor} demoted {user0}, {user1} and %n more participant from moderators',
+ '{actor} demoted {user0}, {user1} and %n more participants from moderators', usersCounter - 2)
+ }
+ }
+ }
+
+ return combinedMessage
+ }
+
+ return {
+ createCombinedSystemMessage,
+ }
+}