Skip to content

Commit 8879269

Browse files
committed
feat(conversation): extract reused properties to composable, expose messages in selector optionally
Signed-off-by: Maksim Sukharev <[email protected]>
1 parent bbbb190 commit 8879269

File tree

5 files changed

+172
-207
lines changed

5 files changed

+172
-207
lines changed

src/components/LeftSidebar/ConversationsList/Conversation.spec.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ describe('Conversation.vue', () => {
4545
testStoreConfig = cloneDeep(storeConfig)
4646
messagesMock = jest.fn().mockReturnValue({})
4747
testStoreConfig.modules.messagesStore.getters.messages = () => messagesMock
48-
testStoreConfig.modules.actorStore.getters.getUserId = () => jest.fn().mockReturnValue('user-id-self')
4948
store = new Vuex.Store(testStoreConfig)
5049

5150
// common defaults
5251
item = {
5352
token: TOKEN,
5453
actorId: 'actor-id-1',
54+
actorType: ATTENDEE.ACTOR_TYPE.USERS,
5555
participants: [
5656
],
5757
participantType: PARTICIPANT.TYPE.USER,
@@ -155,7 +155,7 @@ describe('Conversation.vue', () => {
155155
})
156156

157157
test('displays own last chat message with "You" as author', () => {
158-
item.lastMessage.actorId = 'user-id-self'
158+
item.lastMessage.actorId = 'actor-id-1'
159159

160160
testConversationLabel(item, 'You: hello')
161161
})
@@ -174,7 +174,7 @@ describe('Conversation.vue', () => {
174174

175175
test('displays own last message with "You" author in one to one conversations', () => {
176176
item.type = CONVERSATION.TYPE.ONE_TO_ONE
177-
item.lastMessage.actorId = 'user-id-self'
177+
item.lastMessage.actorId = 'actor-id-1'
178178

179179
testConversationLabel(item, 'You: hello')
180180
})

src/components/LeftSidebar/ConversationsList/Conversation.vue

+13-105
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@
138138

139139
<script>
140140

141+
import { toRefs } from 'vue'
141142
import { Fragment } from 'vue-frag'
142143
import { isNavigationFailure, NavigationFailureType } from 'vue-router'
143144

@@ -162,7 +163,8 @@ import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js'
162163

163164
import ConversationIcon from './../../ConversationIcon.vue'
164165

165-
import { CONVERSATION, PARTICIPANT, ATTENDEE } from '../../../constants.js'
166+
import { useConversationInfo } from '../../../composables/useConversationInfo.js'
167+
import { CONVERSATION, PARTICIPANT } from '../../../constants.js'
166168
import { copyConversationLinkToClipboard } from '../../../services/urlService.js'
167169

168170
export default {
@@ -215,6 +217,16 @@ export default {
215217

216218
emits: ['click'],
217219

220+
setup(props) {
221+
const { item, isSearchResult } = toRefs(props)
222+
const { counterType, conversationInformation } = useConversationInfo({ item, isSearchResult })
223+
224+
return {
225+
counterType,
226+
conversationInformation,
227+
}
228+
},
229+
218230
data() {
219231
return {
220232
isDialogOpen: false,
@@ -226,18 +238,6 @@ export default {
226238
return this.$store.getters.getMainContainerSelector()
227239
},
228240

229-
counterType() {
230-
if (this.item.unreadMentionDirect || (this.item.unreadMessages !== 0 && (
231-
this.item.type === CONVERSATION.TYPE.ONE_TO_ONE || this.item.type === CONVERSATION.TYPE.ONE_TO_ONE_FORMER
232-
))) {
233-
return 'highlighted'
234-
} else if (this.item.unreadMention) {
235-
return 'outlined'
236-
} else {
237-
return ''
238-
}
239-
},
240-
241241
canFavorite() {
242242
return this.item.participantType !== PARTICIPANT.TYPE.USER_SELF_JOINED
243243
},
@@ -266,105 +266,13 @@ export default {
266266
return this.item.canLeaveConversation
267267
},
268268

269-
conversationInformation() {
270-
// temporary item while joining
271-
if (!this.isSearchResult && !this.item.actorId) {
272-
return t('spreed', 'Joining conversation …')
273-
}
274-
275-
if (!Object.keys(this.lastChatMessage).length) {
276-
return ''
277-
}
278-
279-
if (this.shortLastChatMessageAuthor === '') {
280-
return this.simpleLastChatMessage
281-
}
282-
283-
if (this.lastChatMessage.actorId === this.$store.getters.getUserId()) {
284-
return t('spreed', 'You: {lastMessage}', {
285-
lastMessage: this.simpleLastChatMessage,
286-
}, undefined, {
287-
escape: false,
288-
sanitize: false,
289-
})
290-
}
291-
292-
if (this.item.type === CONVERSATION.TYPE.ONE_TO_ONE
293-
|| this.item.type === CONVERSATION.TYPE.ONE_TO_ONE_FORMER
294-
|| this.item.type === CONVERSATION.TYPE.CHANGELOG) {
295-
return this.simpleLastChatMessage
296-
}
297-
298-
return t('spreed', '{actor}: {lastMessage}', {
299-
actor: this.shortLastChatMessageAuthor,
300-
lastMessage: this.simpleLastChatMessage,
301-
}, undefined, {
302-
escape: false,
303-
sanitize: false,
304-
})
305-
},
306-
307-
// Get the last message for this conversation from the message store instead
308-
// of the conversations store. The message store is updated immediately,
309-
// while the conversations store is refreshed every 30 seconds. This allows
310-
// to display message previews in this component as soon as new messages are
311-
// received by the server.
312-
lastChatMessage() {
313-
return this.item.lastMessage
314-
},
315-
316269
dialogMessage() {
317270
return t('spreed', 'Do you really want to delete "{displayName}"?', this.item, undefined, {
318271
escape: false,
319272
sanitize: false,
320273
})
321274
},
322275

323-
/**
324-
* This is a simplified version of the last chat message.
325-
* Parameters are parsed without markup (just replaced with the name),
326-
* e.g. no avatars on mentions.
327-
*
328-
* @return {string} A simple message to show below the conversation name
329-
*/
330-
simpleLastChatMessage() {
331-
if (!Object.keys(this.lastChatMessage).length) {
332-
return ''
333-
}
334-
335-
const params = this.lastChatMessage.messageParameters
336-
let subtitle = this.lastChatMessage.message.trim()
337-
338-
// We don't really use rich objects in the subtitle, instead we fall back to the name of the item
339-
Object.keys(params).forEach((parameterKey) => {
340-
subtitle = subtitle.replace('{' + parameterKey + '}', params[parameterKey].name)
341-
})
342-
343-
return subtitle
344-
},
345-
346-
/**
347-
* @return {string} Part of the name until the first space
348-
*/
349-
shortLastChatMessageAuthor() {
350-
if (!Object.keys(this.lastChatMessage).length
351-
|| this.lastChatMessage.systemMessage.length) {
352-
return ''
353-
}
354-
355-
let author = this.lastChatMessage.actorDisplayName.trim()
356-
const spacePosition = author.indexOf(' ')
357-
if (spacePosition !== -1) {
358-
author = author.substring(0, spacePosition)
359-
}
360-
361-
if (author.length === 0 && this.lastChatMessage.actorType === ATTENDEE.ACTOR_TYPE.GUESTS) {
362-
return t('spreed', 'Guest')
363-
}
364-
365-
return author
366-
},
367-
368276
to() {
369277
return this.item?.token
370278
? {

src/components/LeftSidebar/ConversationsList/ConversationSearchResult.vue

+13-97
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
:name="item.displayName"
2626
:title="item.displayName"
2727
:active="item.token === selectedRoom"
28-
:bold="!!item.unreadMessages"
29-
:counter-number="item.unreadMessages"
28+
:bold="exposeMessages && !!item.unreadMessages"
29+
:counter-number="exposeMessages ? item.unreadMessages : 0"
3030
:counter-type="counterType"
3131
@click="onClick">
3232
<template #icon>
@@ -39,13 +39,13 @@
3939
</template>
4040

4141
<script>
42-
import { inject } from 'vue'
42+
import { inject, toRefs } from 'vue'
4343

4444
import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js'
4545

4646
import ConversationIcon from './../../ConversationIcon.vue'
4747

48-
import { CONVERSATION, ATTENDEE } from '../../../constants.js'
48+
import { useConversationInfo } from '../../../composables/useConversationInfo.js'
4949

5050
export default {
5151
name: 'ConversationSearchResult',
@@ -56,6 +56,10 @@ export default {
5656
},
5757

5858
props: {
59+
exposeMessages: {
60+
type: Boolean,
61+
default: false,
62+
},
5963
item: {
6064
type: Object,
6165
default() {
@@ -78,106 +82,18 @@ export default {
7882

7983
emits: ['click'],
8084

81-
setup() {
85+
setup(props) {
86+
const { item, exposeMessages } = toRefs(props)
8287
const selectedRoom = inject('selectedRoom', null)
88+
const { counterType, conversationInformation } = useConversationInfo({ item, exposeMessages })
8389

8490
return {
8591
selectedRoom,
92+
counterType,
93+
conversationInformation,
8694
}
8795
},
8896

89-
computed: {
90-
counterType() {
91-
if (this.item.unreadMentionDirect || (this.item.unreadMessages !== 0 && (
92-
this.item.type === CONVERSATION.TYPE.ONE_TO_ONE || this.item.type === CONVERSATION.TYPE.ONE_TO_ONE_FORMER
93-
))) {
94-
return 'highlighted'
95-
} else if (this.item.unreadMention) {
96-
return 'outlined'
97-
} else {
98-
return ''
99-
}
100-
},
101-
102-
conversationInformation() {
103-
if (!Object.keys(Object(this.item?.lastMessage)).length) {
104-
return ''
105-
}
106-
107-
if (this.shortLastChatMessageAuthor === '') {
108-
return this.simpleLastChatMessage
109-
}
110-
111-
if (this.item.lastMessage.actorId === this.item.actorId) {
112-
return t('spreed', 'You: {lastMessage}', {
113-
lastMessage: this.simpleLastChatMessage,
114-
}, undefined, {
115-
escape: false,
116-
sanitize: false,
117-
})
118-
}
119-
120-
if (this.item.type === CONVERSATION.TYPE.ONE_TO_ONE
121-
|| this.item.type === CONVERSATION.TYPE.ONE_TO_ONE_FORMER
122-
|| this.item.type === CONVERSATION.TYPE.CHANGELOG) {
123-
return this.simpleLastChatMessage
124-
}
125-
126-
return t('spreed', '{actor}: {lastMessage}', {
127-
actor: this.shortLastChatMessageAuthor,
128-
lastMessage: this.simpleLastChatMessage,
129-
}, undefined, {
130-
escape: false,
131-
sanitize: false,
132-
})
133-
},
134-
135-
/**
136-
* This is a simplified version of the last chat message.
137-
* Parameters are parsed without markup (just replaced with the name),
138-
* e.g. no avatars on mentions.
139-
*
140-
* @return {string} A simple message to show below the conversation name
141-
*/
142-
simpleLastChatMessage() {
143-
if (!Object.keys(this.item.lastMessage).length) {
144-
return ''
145-
}
146-
147-
const params = this.item.lastMessage.messageParameters
148-
let subtitle = this.item.lastMessage.message.trim()
149-
150-
// We don't really use rich objects in the subtitle, instead we fall back to the name of the item
151-
Object.keys(params).forEach((parameterKey) => {
152-
subtitle = subtitle.replace('{' + parameterKey + '}', params[parameterKey].name)
153-
})
154-
155-
return subtitle
156-
},
157-
158-
/**
159-
* @return {string} Part of the name until the first space
160-
*/
161-
shortLastChatMessageAuthor() {
162-
if (!Object.keys(this.item.lastMessage).length
163-
|| this.item.lastMessage.systemMessage.length) {
164-
return ''
165-
}
166-
167-
let author = this.item.lastMessage.actorDisplayName.trim()
168-
const spacePosition = author.indexOf(' ')
169-
if (spacePosition !== -1) {
170-
author = author.substring(0, spacePosition)
171-
}
172-
173-
if (author.length === 0 && this.item.lastMessage.actorType === ATTENDEE.ACTOR_TYPE.GUESTS) {
174-
return t('spreed', 'Guest')
175-
}
176-
177-
return author
178-
},
179-
},
180-
18197
methods: {
18298
onClick() {
18399
this.$emit('click', this.item)

src/components/LeftSidebar/ConversationsList/ConversationsSearchListVirtual.vue

+5-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
:item-size="CONVERSATION_ITEM_SIZE"
2828
key-field="token">
2929
<template #default="{ item }">
30-
<ConversationSearchResult :item="item" @click="onClick" />
30+
<ConversationSearchResult :item="item" :expose-messages="exposeMessages" @click="onClick" />
3131
</template>
3232
<template #after>
3333
<LoadingPlaceholder v-if="loading" type="conversations" />
@@ -59,7 +59,10 @@ export default {
5959
type: Array,
6060
required: true,
6161
},
62-
62+
exposeMessages: {
63+
type: Boolean,
64+
default: false,
65+
},
6366
loading: {
6467
type: Boolean,
6568
default: false,

0 commit comments

Comments
 (0)