Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ describe('Message.vue', () => {

test('renders author if first message', async () => {
messageProps.isFirstMessage = true
messageProps.showAuthor = true
const wrapper = shallowMount(Message, {
localVue,
store,
Expand Down
79 changes: 70 additions & 9 deletions src/components/MessagesList/MessagesGroup/Message/Message.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ the main body of the message as well as a quote.
@animationend="isHighlighted = false"
@mouseover="handleMouseover"
@mouseleave="handleMouseleave">
<div :class="{'normal-message-body': !isSystemMessage && !isDeletedMessage, 'system' : isSystemMessage}"
<div :class="{'normal-message-body': !isSystemMessage && !isDeletedMessage,
'system' : isSystemMessage,
'combined-system': isCombinedSystemMessage}"
class="message-body">
<div v-if="isFirstMessage && showAuthor"
class="message-body__author"
Expand Down Expand Up @@ -184,6 +186,7 @@ the main body of the message as well as a quote.
<div class="message-body__scroll">
<MessageButtonsBar v-if="showMessageButtonsBar"
ref="messageButtonsBar"
class="message-buttons-bar"
:is-translation-available="isTranslationAvailable"
:is-action-menu-open.sync="isActionMenuOpen"
:is-emoji-picker-open.sync="isEmojiPickerOpen"
Expand All @@ -201,6 +204,18 @@ the main body of the message as well as a quote.
:sent-icon-tooltip="sentIconTooltip"
@show-translate-dialog="isTranslateDialogOpen = true"
@delete="handleDelete" />
<div v-else-if="showCombinedSystemMessageToggle"
class="message-buttons-bar">
<NcButton type="tertiary"
:aria-label="t('spreed', 'Show or collapse system messages')"
:title="t('spreed', 'Show or collapse system messages')"
@click="toggleCombinedSystemMessage">
<template #icon>
<UnfoldMore v-if="isCombinedSystemMessageCollapsed" />
<UnfoldLess v-else />
</template>
</NcButton>
</div>
</div>

<MessageTranslateDialog v-if="isTranslateDialogOpen"
Expand All @@ -225,6 +240,8 @@ import Check from 'vue-material-design-icons/Check.vue'
import CheckAll from 'vue-material-design-icons/CheckAll.vue'
import EmoticonOutline from 'vue-material-design-icons/EmoticonOutline.vue'
import Reload from 'vue-material-design-icons/Reload.vue'
import UnfoldLess from 'vue-material-design-icons/UnfoldLessHorizontal.vue'
import UnfoldMore from 'vue-material-design-icons/UnfoldMoreHorizontal.vue'

import { getCapabilities } from '@nextcloud/capabilities'
import { showError, showSuccess, showWarning, TOAST_DEFAULT_TIMEOUT } from '@nextcloud/dialogs'
Expand Down Expand Up @@ -276,6 +293,8 @@ export default {
CheckAll,
EmoticonOutline,
Reload,
UnfoldLess,
UnfoldMore,
},

mixins: [
Expand Down Expand Up @@ -341,22 +360,22 @@ export default {
*/
showAuthor: {
type: Boolean,
default: true,
default: false,
},
/**
* Specifies if the message is temporary in order to display the spinner instead
* of the message time.
*/
isTemporary: {
type: Boolean,
required: true,
default: false,
},
/**
* Specifies if the message is the first of a group of same-author messages.
*/
isFirstMessage: {
type: Boolean,
required: true,
default: false,
},
/**
* Specifies if the message can be replied to.
Expand All @@ -379,6 +398,20 @@ export default {
type: String,
required: true,
},
/**
* Specifies if the message is a combined system message.
*/
isCombinedSystemMessage: {
type: Boolean,
default: false,
},
/**
* Specifies whether the combined system message is collapsed.
*/
isCombinedSystemMessageCollapsed: {
type: Boolean,
default: undefined,
},
/**
* The type of the message.
*/
Expand Down Expand Up @@ -426,6 +459,8 @@ export default {
},
},

emits: ['toggle-combined-system-message'],

setup() {
const isInCall = useIsInCall()
return { isInCall, isTranslationAvailable }
Expand Down Expand Up @@ -613,6 +648,11 @@ export default {
|| this.isReactionsMenuOpen || this.isForwarderOpen || this.isTranslateDialogOpen)
},

showCombinedSystemMessageToggle() {
return this.isSystemMessage && !this.isDeletedMessage && !this.isTemporary
&& this.isCombinedSystemMessage && (this.isHovered || !this.isCombinedSystemMessageCollapsed)
},

isTemporaryUpload() {
return this.isTemporary && this.messageParameters.file
},
Expand Down Expand Up @@ -851,24 +891,29 @@ export default {

return displayName
},

toggleCombinedSystemMessage() {
this.$emit('toggle-combined-system-message')
},
},
}
</script>

<style lang="scss" scoped>
@import '../../../../assets/variables';

.message:hover .normal-message-body {
border-radius: 8px;
background-color: var(--color-background-hover);
}

.message {
position: relative;

&__last {
margin-bottom: 12px;
}

&:hover .normal-message-body,
&:hover .combined-system {
border-radius: 8px;
background-color: var(--color-background-hover);
}
}

.message-body {
Expand Down Expand Up @@ -1025,6 +1070,22 @@ export default {
padding: 8px;
}

.message-buttons-bar {
display: flex;
right: 14px;
top: 8px;
position: sticky;
background-color: var(--color-main-background);
border-radius: calc(var(--default-clickable-area) / 2);
box-shadow: 0 0 4px 0 var(--color-box-shadow);
height: 44px;
z-index: 1;

& h6 {
margin-left: auto;
}
}

.message-body__main__text--markdown {
:deep(.rich-text--wrapper) {
h1 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@

<template>
<!-- Message Actions -->
<div v-click-outside="handleClickOutside"
class="message-buttons-bar">
<div v-click-outside="handleClickOutside">
<template v-if="!isReactionsMenuOpen">
<NcButton v-if="canReact"
type="tertiary"
Expand Down Expand Up @@ -578,24 +577,3 @@ export default {
},
}
</script>

<style lang="scss" scoped>
@import '../../../../../assets/variables';

.message-buttons-bar {
display: flex;
right: 14px;
top: 8px;
position: sticky;
background-color: var(--color-main-background);
border-radius: calc(var(--default-clickable-area) / 2);
box-shadow: 0 0 4px 0 var(--color-box-shadow);
height: 44px;
z-index: 1;

& h6 {
margin-left: auto;
}
}

</style>
87 changes: 45 additions & 42 deletions src/components/MessagesList/MessagesGroup/MessagesGroup.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { cloneDeep } from 'lodash'
import Vuex from 'vuex'

import MessagesGroup from './MessagesGroup.vue'
import MessagesSystemGroup from './MessagesSystemGroup.vue'

import { ATTENDEE } from '../../../constants.js'
import storeConfig from '../../../store/storeConfig.js'
Expand Down Expand Up @@ -123,7 +124,33 @@ describe('MessagesGroup.vue', () => {
})

test('renders grouped system messages', () => {
const wrapper = shallowMount(MessagesGroup, {
const MESSAGES = [{
id: 100,
token: TOKEN,
actorId: 'actor-1',
actorDisplayName: 'actor one',
actorType: ATTENDEE.ACTOR_TYPE.USERS,
message: 'Actor entered the scene',
messageType: 'comment',
messageParameters: {},
systemMessage: 'call_started',
timestamp: 100,
isReplyable: false,
}, {
id: 110,
token: TOKEN,
actorId: 'actor-1',
actorDisplayName: 'actor one',
actorType: ATTENDEE.ACTOR_TYPE.USERS,
message: 'Actor left the scene',
messageType: 'comment',
messageParameters: {},
systemMessage: 'call_stopped',
timestamp: 200,
isReplyable: false,
}]

const wrapper = shallowMount(MessagesSystemGroup, {
localVue,
store,
propsData: {
Expand All @@ -132,31 +159,7 @@ describe('MessagesGroup.vue', () => {
dateSeparator: '<date separator>',
previousMessageId: 90,
nextMessageId: 200,
messages: [{
id: 100,
token: TOKEN,
actorId: 'actor-1',
actorDisplayName: 'actor one',
actorType: ATTENDEE.ACTOR_TYPE.USERS,
message: 'Actor entered the scene',
messageType: 'comment',
messageParameters: {},
systemMessage: 'call_started',
timestamp: 100,
isReplyable: false,
}, {
id: 110,
token: TOKEN,
actorId: 'actor-1',
actorDisplayName: 'actor one',
actorType: ATTENDEE.ACTOR_TYPE.USERS,
message: 'Actor left the scene',
messageType: 'comment',
messageParameters: {},
systemMessage: 'call_stopped',
timestamp: 200,
isReplyable: false,
}],
messages: MESSAGES,
},
})

Expand All @@ -169,24 +172,24 @@ describe('MessagesGroup.vue', () => {
const messagesEl = wrapper.findAllComponents({ name: 'Message' })
// TODO: date separator
let message = messagesEl.at(0)
expect(message.attributes('id')).toBe('100')
expect(message.attributes('message')).toBe('Actor entered the scene')
expect(message.attributes('actorid')).toBe('actor-1')
expect(message.attributes('actordisplayname')).toBe('actor one')
expect(message.attributes('previousmessageid')).toBe('90')
expect(message.attributes('nextmessageid')).toBe('110')
expect(message.attributes('isfirstmessage')).toBe('true')
expect(message.attributes('showauthor')).not.toBeDefined()
expect(message.props('id')).toBe(MESSAGES[0].id)
expect(message.props('message')).toBe(MESSAGES[0].message)
expect(message.props('actorid')).toBe(MESSAGES[0].actorid)
expect(message.props('actordisplayname')).toBe(MESSAGES[0].actordisplayname)
expect(message.props('previousmessageid')).toBe(MESSAGES[0].previousmessageid)
expect(message.props('nextmessageid')).toBe(MESSAGES[0].nextmessageid)
expect(message.props('isfirstmessage')).toBe(MESSAGES[0].isfirstmessage)
expect(message.props('showauthor')).not.toBeDefined()

message = messagesEl.at(1)
expect(message.attributes('id')).toBe('110')
expect(message.attributes('message')).toBe('Actor left the scene')
expect(message.attributes('actorid')).toBe('actor-1')
expect(message.attributes('actordisplayname')).toBe('actor one')
expect(message.attributes('previousmessageid')).toBe('100')
expect(message.attributes('nextmessageid')).toBe('200')
expect(message.attributes('isfirstmessage')).not.toBeDefined()
expect(message.attributes('showauthor')).not.toBeDefined()
expect(message.props('id')).toBe(MESSAGES[1].id)
expect(message.props('message')).toBe(MESSAGES[1].message)
expect(message.props('actorid')).toBe(MESSAGES[1].actorid)
expect(message.props('actordisplayname')).toBe(MESSAGES[1].actordisplayname)
expect(message.props('previousmessageid')).toBe(MESSAGES[1].previousmessageid)
expect(message.props('nextmessageid')).toBe(MESSAGES[1].nextmessageid)
expect(message.props('isfirstmessage')).not.toBeDefined()
expect(message.props('showauthor')).not.toBeDefined()
})

test('renders guest display name', () => {
Expand Down
20 changes: 4 additions & 16 deletions src/components/MessagesList/MessagesGroup/MessagesGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@
role="heading"
aria-level="3">{{ dateSeparator }}</span>
</div>
<div class="wrapper"
:class="{'wrapper--system': isSystemMessage}">
<div v-if="!isSystemMessage" class="messages__avatar">
<div class="wrapper">
<div class="messages__avatar">
<AuthorAvatar :author-type="actorType"
:author-id="actorId"
:display-name="actorDisplayName" />
Expand All @@ -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 />
</ul>
</div>
</div>
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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);
}
Expand Down
Loading