Page MenuHomePhorge

No OneTemporary

Size
13 KB
Referenced Files
None
Subscribers
None
diff --git a/src/components/chat_message_list/chat_message_list.js b/src/components/chat_message_list/chat_message_list.js
index 96dfb2c1cc..fe984020ea 100644
--- a/src/components/chat_message_list/chat_message_list.js
+++ b/src/components/chat_message_list/chat_message_list.js
@@ -1,151 +1,154 @@
import { computed, inject, ref, toRefs, useTemplateRef } from 'vue'
import ChatMessage from 'src/components/chat_message/chat_message.vue'
import { useInterfaceSizes } from 'src/composables/useInterfaceSizes.js'
import { useVirtualScrolling } from 'src/composables/useVirtualScrolling.js'
const ChatMessageList = {
components: {
ChatMessage,
},
props: {
pending: Boolean,
messages: Array,
headerDate: Boolean,
focusedId: String,
repliedId: String,
},
emits: ['replyRequested'],
setup(props, { emit }) {
const { messages, headerDate } = toRefs(props)
const hoveredMessageChainId = ref(null)
const chatItems = computed(() => {
return messages.value
.reduceRight((acc, message, index) => {
const date = new Date(message.created_at)
const olderMessage = messages.value[index - 1]
const newerItem = acc.at(-1)
const diff = olderMessage
? message.created_at - olderMessage.created_at
: null
const MAX_DIFF = 1000 * 60 * 5 // 5 minutes
const dateDiffs = (() => {
if (olderMessage) {
const newerDate = new Date(message.created_at)
const olderDate = new Date(olderMessage.created_at)
newerDate.setHours(0, 0, 0, 0)
olderDate.setHours(0, 0, 0, 0)
return newerDate.toISOString() !== olderDate.toISOString()
} else {
return true
}
})()
const chatItem = {
type: 'message',
data: message,
date,
id: message.id,
isTail: true,
isHead: true,
olderMessage,
}
if (newerItem == null) {
chatItem.messageChainId = message.id
} else if (newerItem.type === 'date') {
chatItem.messageChainId = message.id
} else if (newerItem.type === 'message') {
const newerUser =
newerItem.data.account_id || newerItem.data.user.id
const olderUser = message.account_id || message.user.id
if (newerUser !== olderUser) {
chatItem.messageChainId = message.id
} else {
chatItem.messageChainId = newerItem.messageChainId
chatItem.isTail = false
newerItem.isHead = false
}
}
if (diff > MAX_DIFF || (!olderMessage && headerDate.value)) {
return [
...acc,
chatItem,
{
type: 'date',
date,
isDate: dateDiffs,
isTime: diff > MAX_DIFF && !dateDiffs,
id: 'fake-date-' + date.getTime().toString(),
},
]
} else {
return [...acc, chatItem]
}
}, [])
.reverse()
})
const chatItemsIndex = computed(() =>
chatItems.value.reduce((map, value) => {
map.set(value.id, value)
return map
}, new Map()),
)
const getCurrentItem = (id) => chatItemsIndex.value.get(id)
const scroller = inject('bodyScrollPosition')
const body = useTemplateRef('body')
const { fontSize } = useInterfaceSizes()
const normalHeight = computed(() => fontSize.value * 3)
const dateHeight = computed(() => fontSize.value * 1.5)
const getPlaceholderHeight = (id) =>
id?.startsWith('fake-date-') ? dateHeight : normalHeight
const { pending } = toRefs(props)
const { heightChart, changeSuspendState, updateVirtualHeight } =
useVirtualScrolling({
name: pending.value
? 'ChatMessageList<Pending>'
: 'ChatMessageList<Real>',
enabled: ref(true),
list: chatItems,
body,
scrollPositionInstance: scroller,
scrollCompensation: true,
getPlaceholderHeight,
invertDirection: true,
})
const { focusedId, repliedId } = toRefs(props)
const onMessageHover = ({ isHovered, messageChainId }) => {
hoveredMessageChainId.value = isHovered ? messageChainId : undefined
}
const onReplyRequested = (message) => emit('replyRequested', message)
return {
heightChart,
updateVirtualHeight,
getCurrentItem,
hoveredMessageChainId,
changeSuspendState,
focusedId,
repliedId,
onReplyRequested,
onMessageHover,
+ _test: {
+ chatItems,
+ },
}
},
}
export default ChatMessageList
diff --git a/test/unit/specs/components/chat_message_list.spec.js b/test/unit/specs/components/chat_message_list.spec.js
index 126332b958..6e381ea4a5 100644
--- a/test/unit/specs/components/chat_message_list.spec.js
+++ b/test/unit/specs/components/chat_message_list.spec.js
@@ -1,244 +1,205 @@
+import { createTestingPinia } from '@pinia/testing'
+import { setActivePinia } from 'pinia'
import { shallowMount } from '@vue/test-utils'
+import { ref } from 'vue'
import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue'
+const scrollPositionInstance = {
+ x: ref(0),
+ y: ref(0),
+ vHeight: ref(768),
+ vWidth: ref(1024),
+ vHeight: ref(768),
+ vWidth: ref(1024),
+ scrollBy: vi.fn(),
+ hasReachedTop: ref(false),
+ hasReachedBottom: ref(false),
+}
+const global = {
+ provide: {
+ bodyScrollPosition: scrollPositionInstance
+ }
+}
describe('ChatMessageList', () => {
- describe('computed.chatItems', () => {
+ beforeEach(() => {
+ setActivePinia(createTestingPinia({ stubActions: false }))
+ })
+
+ describe('computed.chatItems.value', () => {
it('Inserts date separators', () => {
const component = shallowMount(ChatMessageList, {
+ global,
props: {
messages: [
{
id: '0',
account_id: 'Alice',
created_at: new Date('2020-06-22T20:00:00.000Z'),
},
{
id: '1',
account_id: 'Alice',
created_at: new Date('2020-06-22T20:01:00.000Z'),
},
{
id: '2',
account_id: 'Alice',
created_at: new Date('2020-06-23T20:00:00.000Z'),
},
],
},
})
- expect(component.vm.chatItems.map((i) => i.type)).to.eql([
+ expect(component.vm._test.chatItems.value.map((i) => i.type)).to.eql([
'message',
'message',
'date',
'message',
])
})
it('Inserts date header if needed', () => {
const component = shallowMount(ChatMessageList, {
+ global,
props: {
headerDate: true,
messages: [
{
id: '0',
account_id: 'Alice',
created_at: new Date('2020-06-23T20:00:00.000Z'),
},
],
},
})
- expect(component.vm.chatItems.map((i) => i.type)).to.eql([
+ expect(component.vm._test.chatItems.value.map((i) => i.type)).to.eql([
'date',
'message',
])
})
it('Inserts time separators if messages were sent with considerable delay (5 minutes)', () => {
const component = shallowMount(ChatMessageList, {
+ global,
props: {
messages: [
{
id: '0',
account_id: 'Alice',
created_at: new Date('2020-06-22T20:00:00.000Z'),
},
{
id: '1',
account_id: 'Alice',
created_at: new Date('2020-06-22T20:06:00.000Z'),
},
{
id: '2',
account_id: 'Alice',
created_at: new Date('2020-06-23T20:00:00.000Z'),
},
],
},
})
- expect(component.vm.chatItems.map((i) => i.type)).to.eql([
+ expect(component.vm._test.chatItems.value.map((i) => i.type)).to.eql([
'message',
'date',
'message',
'date',
'message',
])
- expect(component.vm.chatItems.map((i) => i.isTime)).to.eql([
+ expect(component.vm._test.chatItems.value.map((i) => i.isTime)).to.eql([
undefined,
true,
undefined,
false,
undefined,
])
})
it('Groups message chains by time and author', () => {
const component = shallowMount(ChatMessageList, {
+ global,
props: {
messages: [
{
id: '0',
account_id: 'Alice',
created_at: new Date('2020-06-22T20:00:00.000Z'),
},
{
id: '1',
account_id: 'Alice',
created_at: new Date('2020-06-22T20:06:00.000Z'),
},
{
id: '2',
account_id: 'Alice',
created_at: new Date('2020-06-23T20:00:00.000Z'),
},
{
id: '3',
account_id: 'Bob',
created_at: new Date('2020-06-23T20:01:00.000Z'),
},
{
id: '4',
account_id: 'Bob',
created_at: new Date('2020-06-23T20:02:00.000Z'),
},
{
id: '5',
account_id: 'Bob',
created_at: new Date('2020-06-23T20:03:00.000Z'),
},
{
id: '6',
account_id: 'Eve',
created_at: new Date('2020-06-23T20:04:00.000Z'),
},
],
},
})
// Type check
- expect(component.vm.chatItems.map((i) => i.type)).to.eql([
+ expect(component.vm._test.chatItems.value.map((i) => i.type)).to.eql([
'message',
'date',
'message',
'date',
'message',
'message',
'message',
'message',
'message',
])
// Chain head/Tail checks
- expect(component.vm.chatItems.map((i) => [i.isHead, i.isTail])).to.eql([
+ expect(component.vm._test.chatItems.value.map((i) => [i.isHead, i.isTail])).to.eql([
[true, true],
[undefined, undefined],
[true, true],
[undefined, undefined],
[true, true],
[true, false],
[false, false],
[false, true],
[true, true],
])
// Unique ID is randomly generated so we have to compare data against itself
// Two messages from Bob next to each other
- expect(component.vm.chatItems[5].messageChainId).to.eql(
- component.vm.chatItems[6].messageChainId,
+ expect(component.vm._test.chatItems.value[5].messageChainId).to.eql(
+ component.vm._test.chatItems.value[6].messageChainId,
)
// Message from Even right after Bob
- expect(component.vm.chatItems[7].messageChainId).to.not.eql(
- component.vm.chatItems[8].messageChainId,
+ expect(component.vm._test.chatItems.value[7].messageChainId).to.not.eql(
+ component.vm._test.chatItems.value[8].messageChainId,
)
})
})
- describe('methods.getPreviousItem', () => {
- describe('Finds correct previous meaningful (non-separator) message in the chatlist', () => {
- let component
-
- beforeEach(() => {
- component = shallowMount(ChatMessageList, {
- props: {
- messages: [
- {
- id: '0',
- account_id: 'Alice',
- created_at: new Date('2020-06-22T20:00:00.000Z'),
- },
- {
- // Separator
- id: '1', // 2
- account_id: 'Alice',
- created_at: new Date('2020-06-22T20:06:00.000Z'),
- },
- {
- // Separator
- id: '2', // 4
- account_id: 'Alice',
- created_at: new Date('2020-06-23T20:00:00.000Z'),
- },
- {
- id: '3', // 5
- account_id: 'Bob',
- created_at: new Date('2020-06-23T20:01:00.000Z'),
- },
- {
- id: '4', // 6
- account_id: 'Bob',
- created_at: new Date('2020-06-23T20:02:00.000Z'),
- },
- {
- id: '5', // 7
- account_id: 'Bob',
- created_at: new Date('2020-06-23T20:03:00.000Z'),
- },
- {
- id: '6', // 8
- account_id: 'Eve',
- created_at: new Date('2020-06-23T20:04:00.000Z'),
- },
- ],
- },
- })
- })
-
- it('Directly next to each other', () => {
- const correct = component.vm.chatItems[6]
- expect(component.vm.getPreviousItem(7)).to.eql(correct)
- })
-
- it('Across separator', () => {
- const correct = component.vm.chatItems[2]
- expect(component.vm.getPreviousItem(4)).to.eql(correct)
- })
-
- it('Returns null if no previous item exist', () => {
- const correct = null
- expect(component.vm.getPreviousItem(0)).to.eql(correct)
- })
- })
- })
})

File Metadata

Mime Type
text/x-diff
Expires
Sat, Oct 10, 11:45 PM (1 d, 10 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1785509
Default Alt Text
(13 KB)

Event Timeline