Skip to content
This repository was archived by the owner on May 20, 2024. It is now read-only.

Commit 3a8a3da

Browse files
teemukaariatiltec
authored andcommitted
Group messages from same author (#1093)
* group close by messages together * add date as a title to grouped messages * add grouped messages story
1 parent d06220b commit 3a8a3da

File tree

6 files changed

+431
-36
lines changed

6 files changed

+431
-36
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ Please document your changes in this format:
2121
```
2222

2323
## [Unreleased]
24+
### Added
25+
- Group messages from same author @teemukaaria
2426

2527
## [8.1.2] - 2019-11-05
2628
### Fixed

src/__snapshots__/storyshots.spec.js.snap

+357-33
Large diffs are not rendered by default.

src/messages/components/ChatConversation.story.js

+16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { storiesOf } from '@storybook/vue'
33
import ChatConversation from './ChatConversation'
44
import * as factories from '>/enrichedFactories'
55
import { statusMocks, storybookDefaults as defaults } from '>/helpers'
6+
import { messagesMock } from '>/mockdata'
67

78
const conversation = factories.makeConversation()
89
const thread = factories.makeThread()
@@ -59,3 +60,18 @@ storiesOf('ChatConversation', module)
5960
}),
6061
}),
6162
}))
63+
.add('message groups', () => defaults({
64+
render: h => {
65+
const timeDiff = new Date() - messagesMock[messagesMock.length - 1].createdAt.getTime()
66+
return h(ChatConversation, {
67+
props: defaultProps({
68+
conversation: {
69+
...conversation,
70+
messages: messagesMock.map(
71+
message => ({ ...message, createdAt: new Date(message.createdAt.getTime() + timeDiff) }),
72+
),
73+
},
74+
}),
75+
})
76+
},
77+
}))

src/messages/components/ChatConversation.vue

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
class="bg-white"
1414
>
1515
<ConversationMessage
16-
v-for="message in conversation.messages"
16+
v-for="message in groupedMessages"
1717
:key="message.id"
1818
:message="message"
1919
slim
@@ -61,6 +61,7 @@
6161
import ConversationMessage from '@/messages/components/ConversationMessage'
6262
import ConversationCompose from '@/messages/components/ConversationCompose'
6363
import KSpinner from '@/utils/components/KSpinner'
64+
import groupedMessagesMixin from '@/utils/mixins/groupedMessagesMixin'
6465
import {
6566
scroll,
6667
dom,
@@ -93,6 +94,7 @@ export default {
9394
QScrollObserver,
9495
QInfiniteScroll,
9596
},
97+
mixins: [groupedMessagesMixin(['conversation', 'messages'])],
9698
props: {
9799
conversation: { type: Object, default: null },
98100
away: { type: Boolean, required: true },

src/messages/components/ConversationMessage.vue

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<QItem
33
v-if="!editMode"
4-
:class="{ isUnread: message.isUnread, slim }"
4+
:class="{ isUnread: message.isUnread, slim, continuation: !!message.continuation }"
55
class="conversation-message relative-position"
66
>
77
<QBtnGroup
@@ -45,6 +45,7 @@
4545
</QItemSection>
4646
<QItemSection>
4747
<QItemLabel
48+
v-if="!message.continuation"
4849
class="no-wrap k-message-meta"
4950
>
5051
<RouterLink :to="{ name: 'user', params: { userId: message.author.id } }">
@@ -62,7 +63,10 @@
6263
:title="$t('WALL.RECEIVED_VIA_EMAIL')"
6364
/>
6465
</QItemLabel>
65-
<div class="content">
66+
<div
67+
class="content"
68+
:title="slim && tooltipDate"
69+
>
6670
<Markdown :source="message.content" />
6771
</div>
6872
<QItemLabel
@@ -181,6 +185,9 @@ export default {
181185
showReplies () {
182186
return this.message.threadMeta && !this.slim
183187
},
188+
tooltipDate () {
189+
return this.$d(this.message.createdAt, 'long')
190+
},
184191
},
185192
methods: {
186193
toggleReaction (name) {
@@ -212,6 +219,10 @@ export default {
212219
.isUnread
213220
background linear-gradient(to right, $lightGreen, $lighterGreen)
214221
222+
.continuation
223+
padding-top 0
224+
min-height auto
225+
215226
body.mobile .conversation-message
216227
.k-message-meta
217228
font-size 80%
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import differenceInSeconds from 'date-fns/differenceInSeconds'
2+
import dateFnsHelper from '@/utils/dateFnsHelper'
3+
4+
// time difference in seconds of any two messages to be included into the same group
5+
const MESSAGE_GROUP_DISTANCE = 60
6+
// time difference in seconds of subsequent messages to be included into the same group
7+
const SUBSEQUENT_MESSAGE_DISTANCE = 30
8+
9+
export default function groupedMessagesMixin (path) {
10+
return ({
11+
computed: {
12+
groupedMessages () {
13+
const messages = path.reduce((acc, key) => acc[key], this)
14+
let groupHeading = { timestamp: '', createdAt: 0 }
15+
let prevMessage = { createdAt: 0, author: { id: -1 } }
16+
for (const message of messages) {
17+
const timestamp = dateFnsHelper.formatDistanceToNow(message.createdAt)
18+
// group messages together if their author is the same and:
19+
// 1. their "... ago" label is the same
20+
// 2. the difference to last group heading is small enough
21+
// (prevents messages from jumping from one group to another)
22+
// 3. two subsequent messages are close enough
23+
if (message.author.id === prevMessage.author.id &&
24+
(timestamp === groupHeading.timestamp ||
25+
differenceInSeconds(message.createdAt, groupHeading.createdAt) < MESSAGE_GROUP_DISTANCE ||
26+
differenceInSeconds(message.createdAt, prevMessage.createdAt) < SUBSEQUENT_MESSAGE_DISTANCE
27+
)) {
28+
message.continuation = true
29+
}
30+
else {
31+
message.continuation = false
32+
groupHeading = { timestamp, createdAt: message.createdAt }
33+
}
34+
prevMessage = message
35+
}
36+
return messages
37+
},
38+
},
39+
})
40+
}

0 commit comments

Comments
 (0)