Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85651595
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
28 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/components/chat/chat.js b/src/components/chat/chat.js
index 9c4e5b0554..803abf6988 100644
--- a/src/components/chat/chat.js
+++ b/src/components/chat/chat.js
@@ -1,333 +1,337 @@
import _ from 'lodash'
import { WSConnectionStatus } from '../../services/api/api.service.js'
import { mapGetters, mapState } from 'vuex'
import ChatMessage from '../chat_message/chat_message.vue'
import PostStatusForm from '../post_status_form/post_status_form.vue'
import ChatTitle from '../chat_title/chat_title.vue'
import chatService from '../../services/chat_service/chat_service.js'
import { getScrollPosition, getNewTopPosition, isBottomedOut, scrollableContainerHeight } from './chat_layout_utils.js'
const BOTTOMED_OUT_OFFSET = 10
const JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET = 150
const SAFE_RESIZE_TIME_OFFSET = 100
const Chat = {
components: {
ChatMessage,
ChatTitle,
PostStatusForm
},
data () {
return {
jumpToBottomButtonVisible: false,
hoveredMessageChainId: undefined,
lastScrollPosition: {},
scrollableContainerHeight: '100%',
errorLoadingChat: false
}
},
created () {
this.startFetching()
window.addEventListener('resize', this.handleLayoutChange)
},
mounted () {
window.addEventListener('scroll', this.handleScroll)
if (typeof document.hidden !== 'undefined') {
document.addEventListener('visibilitychange', this.handleVisibilityChange, false)
}
this.$nextTick(() => {
this.updateScrollableContainerHeight()
this.handleResize()
})
this.setChatLayout()
},
destroyed () {
window.removeEventListener('scroll', this.handleScroll)
window.removeEventListener('resize', this.handleLayoutChange)
this.unsetChatLayout()
if (typeof document.hidden !== 'undefined') document.removeEventListener('visibilitychange', this.handleVisibilityChange, false)
this.$store.dispatch('clearCurrentChat')
},
computed: {
recipient () {
return this.currentChat && this.currentChat.account
},
recipientId () {
return this.$route.params.recipient_id
},
formPlaceholder () {
if (this.recipient) {
return this.$t('chats.message_user', { nickname: this.recipient.screen_name })
} else {
return ''
}
},
chatViewItems () {
return chatService.getView(this.currentChatMessageService)
},
newMessageCount () {
return this.currentChatMessageService && this.currentChatMessageService.newMessageCount
},
streamingEnabled () {
return this.mergedConfig.useStreamingApi && this.mastoUserSocketStatus === WSConnectionStatus.JOINED
},
...mapGetters([
'currentChat',
'currentChatMessageService',
'findOpenedChatByRecipientId',
'mergedConfig'
]),
...mapState({
backendInteractor: state => state.api.backendInteractor,
mastoUserSocketStatus: state => state.api.mastoUserSocketStatus,
mobileLayout: state => state.interface.mobileLayout,
layoutHeight: state => state.interface.layoutHeight,
currentUser: state => state.users.currentUser
})
},
watch: {
chatViewItems () {
// We don't want to scroll to the bottom on a new message when the user is viewing older messages.
// Therefore we need to know whether the scroll position was at the bottom before the DOM update.
const bottomedOutBeforeUpdate = this.bottomedOut(BOTTOMED_OUT_OFFSET)
this.$nextTick(() => {
if (bottomedOutBeforeUpdate) {
this.scrollDown({ forceRead: !document.hidden })
}
})
},
'$route': function () {
this.startFetching()
},
layoutHeight () {
this.handleResize({ expand: true })
},
mastoUserSocketStatus (newValue) {
if (newValue === WSConnectionStatus.JOINED) {
this.fetchChat({ isFirstFetch: true })
}
}
},
methods: {
// Used to animate the avatar near the first message of the message chain when any message belonging to the chain is hovered
onMessageHover ({ isHovered, messageChainId }) {
this.hoveredMessageChainId = isHovered ? messageChainId : undefined
},
onFilesDropped () {
this.$nextTick(() => {
this.handleResize()
this.updateScrollableContainerHeight()
})
},
handleVisibilityChange () {
this.$nextTick(() => {
if (!document.hidden && this.bottomedOut(BOTTOMED_OUT_OFFSET)) {
this.scrollDown({ forceRead: true })
}
})
},
setChatLayout () {
// This is a hacky way to adjust the global layout to the mobile chat (without modifying the rest of the app).
// This layout prevents empty spaces from being visible at the bottom
// of the chat on iOS Safari (`safe-area-inset`) when
// - the on-screen keyboard appears and the user starts typing
// - the user selects the text inside the input area
// - the user selects and deletes the text that is multiple lines long
// TODO: unify the chat layout with the global layout.
let html = document.querySelector('html')
if (html) {
html.classList.add('chat-layout')
}
this.$nextTick(() => {
this.updateScrollableContainerHeight()
})
},
unsetChatLayout () {
let html = document.querySelector('html')
if (html) {
html.classList.remove('chat-layout')
}
},
handleLayoutChange () {
this.$nextTick(() => {
this.updateScrollableContainerHeight()
this.scrollDown()
})
},
// Ensures the proper position of the posting form in the mobile layout (the mobile browser panel does not overlap or hide it)
updateScrollableContainerHeight () {
const header = this.$refs.header
const footer = this.$refs.footer
const inner = this.mobileLayout ? window.document.body : this.$refs.inner
this.scrollableContainerHeight = scrollableContainerHeight(inner, header, footer) + 'px'
},
// Preserves the scroll position when OSK appears or the posting form changes its height.
handleResize (opts = {}) {
const { expand = false, delayed = false } = opts
if (delayed) {
setTimeout(() => {
this.handleResize({ ...opts, delayed: false })
}, SAFE_RESIZE_TIME_OFFSET)
return
}
this.$nextTick(() => {
this.updateScrollableContainerHeight()
const { offsetHeight = undefined } = this.lastScrollPosition
this.lastScrollPosition = getScrollPosition(this.$refs.scrollable)
const diff = this.lastScrollPosition.offsetHeight - offsetHeight
if (diff < 0 || (!this.bottomedOut() && expand)) {
this.$nextTick(() => {
this.updateScrollableContainerHeight()
this.$refs.scrollable.scrollTo({
top: this.$refs.scrollable.scrollTop - diff,
left: 0
})
})
}
})
},
scrollDown (options = {}) {
const { behavior = 'auto', forceRead = false } = options
const scrollable = this.$refs.scrollable
if (!scrollable) { return }
this.$nextTick(() => {
scrollable.scrollTo({ top: scrollable.scrollHeight, left: 0, behavior })
})
if (forceRead || this.newMessageCount > 0) {
this.readChat()
}
},
readChat () {
- if (!(this.currentChatMessageService && this.currentChatMessageService.lastMessage)) { return }
+ if (!(this.currentChatMessageService && this.currentChatMessageService.maxId)) { return }
if (document.hidden) { return }
- const lastReadId = this.currentChatMessageService.lastMessage.id
+ const lastReadId = this.currentChatMessageService.maxId
this.$store.dispatch('readChat', { id: this.currentChat.id, lastReadId })
},
bottomedOut (offset) {
return isBottomedOut(this.$refs.scrollable, offset)
},
reachedTop () {
const scrollable = this.$refs.scrollable
return scrollable && scrollable.scrollTop <= 0
},
handleScroll: _.throttle(function () {
if (!this.currentChat) { return }
if (this.reachedTop()) {
this.fetchChat({ maxId: this.currentChatMessageService.minId })
} else if (this.bottomedOut(JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET)) {
this.jumpToBottomButtonVisible = false
if (this.newMessageCount > 0) {
this.readChat()
}
} else {
this.jumpToBottomButtonVisible = true
}
}, 100),
handleScrollUp (positionBeforeLoading) {
const positionAfterLoading = getScrollPosition(this.$refs.scrollable)
this.$refs.scrollable.scrollTo({
top: getNewTopPosition(positionBeforeLoading, positionAfterLoading),
left: 0
})
},
fetchChat ({ isFirstFetch = false, fetchLatest = false, maxId }) {
const chatMessageService = this.currentChatMessageService
if (!chatMessageService) { return }
if (fetchLatest && this.streamingEnabled) { return }
const chatId = chatMessageService.chatId
const fetchOlderMessages = !!maxId
- const sinceId = fetchLatest && chatMessageService.lastMessage && chatMessageService.lastMessage.id
+ const sinceId = fetchLatest && chatMessageService.maxId
this.backendInteractor.chatMessages({ id: chatId, maxId, sinceId })
.then((messages) => {
// Clear the current chat in case we're recovering from a ws connection loss.
if (isFirstFetch) {
chatService.clear(chatMessageService)
}
const positionBeforeUpdate = getScrollPosition(this.$refs.scrollable)
this.$store.dispatch('addChatMessages', { chatId, messages }).then(() => {
this.$nextTick(() => {
if (fetchOlderMessages) {
this.handleScrollUp(positionBeforeUpdate)
}
if (isFirstFetch) {
this.updateScrollableContainerHeight()
}
})
})
})
},
async startFetching () {
let chat = this.findOpenedChatByRecipientId(this.recipientId)
if (!chat) {
try {
chat = await this.backendInteractor.getOrCreateChat({ accountId: this.recipientId })
} catch (e) {
console.error('Error creating or getting a chat', e)
this.errorLoadingChat = true
}
}
if (chat) {
this.$nextTick(() => {
this.scrollDown({ forceRead: true })
})
this.$store.dispatch('addOpenedChat', { chat })
this.doStartFetching()
}
},
doStartFetching () {
this.$store.dispatch('startFetchingCurrentChat', {
fetcher: () => setInterval(() => this.fetchChat({ fetchLatest: true }), 5000)
})
this.fetchChat({ isFirstFetch: true })
},
sendMessage ({ status, media }) {
const params = {
id: this.currentChat.id,
content: status
}
if (media[0]) {
params.mediaId = media[0].id
}
return this.backendInteractor.sendChatMessage(params)
.then(data => {
- this.$store.dispatch('addChatMessages', { chatId: this.currentChat.id, messages: [data] }).then(() => {
+ this.$store.dispatch('addChatMessages', {
+ chatId: this.currentChat.id,
+ messages: [data],
+ updateMaxId: false
+ }).then(() => {
this.$nextTick(() => {
this.handleResize()
// When the posting form size changes because of a media attachment, we need an extra resize
// to account for the potential delay in the DOM update.
setTimeout(() => {
this.updateScrollableContainerHeight()
}, SAFE_RESIZE_TIME_OFFSET)
this.scrollDown({ forceRead: true })
})
})
return data
})
.catch(error => {
console.error('Error sending message', error)
return {
error: this.$t('chats.error_sending_message')
}
})
},
goBack () {
this.$router.push({ name: 'chats', params: { username: this.currentUser.screen_name } })
}
}
}
export default Chat
diff --git a/src/modules/chats.js b/src/modules/chats.js
index c760901809..e752c6df8a 100644
--- a/src/modules/chats.js
+++ b/src/modules/chats.js
@@ -1,234 +1,220 @@
import Vue from 'vue'
import { find, omitBy, orderBy, sumBy } from 'lodash'
import chatService from '../services/chat_service/chat_service.js'
import { parseChat, parseChatMessage } from '../services/entity_normalizer/entity_normalizer.service.js'
import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js'
const emptyChatList = () => ({
data: [],
idStore: {}
})
const defaultState = {
chatList: emptyChatList(),
chatListFetcher: null,
openedChats: {},
openedChatMessageServices: {},
fetcher: undefined,
currentChatId: null
}
const getChatById = (state, id) => {
return find(state.chatList.data, { id })
}
const sortedChatList = (state) => {
return orderBy(state.chatList.data, ['updated_at'], ['desc'])
}
const unreadChatCount = (state) => {
return sumBy(state.chatList.data, 'unread')
}
const chats = {
state: { ...defaultState },
getters: {
currentChat: state => state.openedChats[state.currentChatId],
currentChatMessageService: state => state.openedChatMessageServices[state.currentChatId],
findOpenedChatByRecipientId: state => recipientId => find(state.openedChats, c => c.account.id === recipientId),
sortedChatList,
unreadChatCount
},
actions: {
// Chat list
startFetchingChats ({ dispatch, commit }) {
const fetcher = () => {
dispatch('fetchChats', { latest: true })
}
fetcher()
commit('setChatListFetcher', {
fetcher: () => setInterval(() => { fetcher() }, 5000)
})
},
stopFetchingChats ({ commit }) {
commit('setChatListFetcher', { fetcher: undefined })
},
fetchChats ({ dispatch, rootState, commit }, params = {}) {
return rootState.api.backendInteractor.chats()
.then(({ chats }) => {
dispatch('addNewChats', { chats })
return chats
})
},
addNewChats (store, { chats }) {
const { commit, dispatch, rootGetters } = store
const newChatMessageSideEffects = (chat) => {
maybeShowChatNotification(store, chat)
}
commit('addNewChats', { dispatch, chats, rootGetters, newChatMessageSideEffects })
},
updateChat ({ commit }, { chat }) {
commit('updateChat', { chat })
},
// Opened Chats
startFetchingCurrentChat ({ commit, dispatch }, { fetcher }) {
dispatch('setCurrentChatFetcher', { fetcher })
},
setCurrentChatFetcher ({ rootState, commit }, { fetcher }) {
commit('setCurrentChatFetcher', { fetcher })
},
addOpenedChat ({ rootState, commit, dispatch }, { chat }) {
commit('addOpenedChat', { dispatch, chat: parseChat(chat) })
dispatch('addNewUsers', [chat.account])
},
addChatMessages ({ commit }, value) {
commit('addChatMessages', { commit, ...value })
},
resetChatNewMessageCount ({ commit }, value) {
commit('resetChatNewMessageCount', value)
},
clearCurrentChat ({ rootState, commit, dispatch }, value) {
commit('setCurrentChatId', { chatId: undefined })
commit('setCurrentChatFetcher', { fetcher: undefined })
},
readChat ({ rootState, commit, dispatch }, { id, lastReadId }) {
dispatch('resetChatNewMessageCount')
commit('readChat', { id })
rootState.api.backendInteractor.readChat({ id, lastReadId })
},
deleteChatMessage ({ rootState, commit }, value) {
rootState.api.backendInteractor.deleteChatMessage(value)
commit('deleteChatMessage', { commit, ...value })
},
resetChats ({ commit, dispatch }) {
dispatch('clearCurrentChat')
commit('resetChats', { commit })
},
clearOpenedChats ({ rootState, commit, dispatch, rootGetters }) {
commit('clearOpenedChats', { commit })
}
},
mutations: {
setChatListFetcher (state, { commit, fetcher }) {
const prevFetcher = state.chatListFetcher
if (prevFetcher) {
clearInterval(prevFetcher)
}
state.chatListFetcher = fetcher && fetcher()
},
setCurrentChatFetcher (state, { fetcher }) {
const prevFetcher = state.fetcher
if (prevFetcher) {
clearInterval(prevFetcher)
}
state.fetcher = fetcher && fetcher()
},
addOpenedChat (state, { _dispatch, chat }) {
state.currentChatId = chat.id
Vue.set(state.openedChats, chat.id, chat)
if (!state.openedChatMessageServices[chat.id]) {
Vue.set(state.openedChatMessageServices, chat.id, chatService.empty(chat.id))
}
},
setCurrentChatId (state, { chatId }) {
state.currentChatId = chatId
},
addNewChats (state, { chats, newChatMessageSideEffects }) {
chats.forEach((updatedChat) => {
const chat = getChatById(state, updatedChat.id)
if (chat) {
const isNewMessage = (chat.lastMessage && chat.lastMessage.id) !== (updatedChat.lastMessage && updatedChat.lastMessage.id)
chat.lastMessage = updatedChat.lastMessage
chat.unread = updatedChat.unread
if (isNewMessage && chat.unread) {
newChatMessageSideEffects(updatedChat)
}
} else {
state.chatList.data.push(updatedChat)
Vue.set(state.chatList.idStore, updatedChat.id, updatedChat)
}
})
},
updateChat (state, { _dispatch, chat: updatedChat, _rootGetters }) {
const chat = getChatById(state, updatedChat.id)
if (chat) {
chat.lastMessage = updatedChat.lastMessage
chat.unread = updatedChat.unread
chat.updated_at = updatedChat.updated_at
}
if (!chat) { state.chatList.data.unshift(updatedChat) }
Vue.set(state.chatList.idStore, updatedChat.id, updatedChat)
},
deleteChat (state, { _dispatch, id, _rootGetters }) {
state.chats.data = state.chats.data.filter(conversation =>
conversation.last_status.id !== id
)
state.chats.idStore = omitBy(state.chats.idStore, conversation => conversation.last_status.id === id)
},
resetChats (state, { commit }) {
state.chatList = emptyChatList()
state.currentChatId = null
commit('setChatListFetcher', { fetcher: undefined })
for (const chatId in state.openedChats) {
chatService.clear(state.openedChatMessageServices[chatId])
Vue.delete(state.openedChats, chatId)
Vue.delete(state.openedChatMessageServices, chatId)
}
},
setChatsLoading (state, { value }) {
state.chats.loading = value
},
- addChatMessages (state, { commit, chatId, messages }) {
+ addChatMessages (state, { chatId, messages, updateMaxId }) {
const chatMessageService = state.openedChatMessageServices[chatId]
if (chatMessageService) {
- chatService.add(chatMessageService, { messages: messages.map(parseChatMessage) })
- commit('refreshLastMessage', { chatId })
+ chatService.add(chatMessageService, { messages: messages.map(parseChatMessage), updateMaxId })
}
},
- refreshLastMessage (state, { chatId }) {
- const chatMessageService = state.openedChatMessageServices[chatId]
- if (chatMessageService) {
- const chat = getChatById(state, chatId)
- if (chat) {
- chat.lastMessage = chatMessageService.lastMessage
- if (chatMessageService.lastMessage) {
- chat.updated_at = chatMessageService.lastMessage.created_at
- }
- }
- }
- },
- deleteChatMessage (state, { commit, chatId, messageId }) {
+ deleteChatMessage (state, { chatId, messageId }) {
const chatMessageService = state.openedChatMessageServices[chatId]
if (chatMessageService) {
chatService.deleteMessage(chatMessageService, messageId)
- commit('refreshLastMessage', { chatId })
}
},
resetChatNewMessageCount (state, _value) {
const chatMessageService = state.openedChatMessageServices[state.currentChatId]
chatService.resetNewMessageCount(chatMessageService)
},
// Used when a connection loss occurs
clearOpenedChats (state) {
const currentChatId = state.currentChatId
for (const chatId in state.openedChats) {
if (currentChatId !== chatId) {
chatService.clear(state.openedChatMessageServices[chatId])
Vue.delete(state.openedChats, chatId)
Vue.delete(state.openedChatMessageServices, chatId)
}
}
},
readChat (state, { id }) {
const chat = getChatById(state, id)
if (chat) {
chat.unread = 0
}
}
}
}
export default chats
diff --git a/src/services/chat_service/chat_service.js b/src/services/chat_service/chat_service.js
index b60a889bd0..95c6948235 100644
--- a/src/services/chat_service/chat_service.js
+++ b/src/services/chat_service/chat_service.js
@@ -1,151 +1,154 @@
import _ from 'lodash'
const empty = (chatId) => {
return {
idIndex: {},
messages: [],
newMessageCount: 0,
lastSeenTimestamp: 0,
chatId: chatId,
minId: undefined,
- lastMessage: undefined
+ maxId: undefined
}
}
const clear = (storage) => {
storage.idIndex = {}
storage.messages.splice(0, storage.messages.length)
storage.newMessageCount = 0
storage.lastSeenTimestamp = 0
storage.minId = undefined
- storage.lastMessage = undefined
+ storage.maxId = undefined
}
const deleteMessage = (storage, messageId) => {
if (!storage) { return }
storage.messages = storage.messages.filter(m => m.id !== messageId)
delete storage.idIndex[messageId]
- if (storage.lastMessage && (storage.lastMessage.id === messageId)) {
- storage.lastMessage = _.maxBy(storage.messages, 'id')
+ if (storage.maxId === messageId) {
+ const lastMessage = _.maxBy(storage.messages, 'id')
+ storage.maxId = lastMessage.id
}
if (storage.minId === messageId) {
const firstMessage = _.minBy(storage.messages, 'id')
storage.minId = firstMessage.id
}
}
-const add = (storage, { messages: newMessages }) => {
+const add = (storage, { messages: newMessages, updateMaxId = true }) => {
if (!storage) { return }
for (let i = 0; i < newMessages.length; i++) {
const message = newMessages[i]
// sanity check
if (message.chat_id !== storage.chatId) { return }
if (!storage.minId || message.id < storage.minId) {
storage.minId = message.id
}
- if (!storage.lastMessage || message.id > storage.lastMessage.id) {
- storage.lastMessage = message
+ if (!storage.maxId || message.id > storage.maxId) {
+ if (updateMaxId) {
+ storage.maxId = message.id
+ }
}
if (!storage.idIndex[message.id]) {
if (storage.lastSeenTimestamp < message.created_at) {
storage.newMessageCount++
}
storage.messages.push(message)
storage.idIndex[message.id] = message
}
}
}
const resetNewMessageCount = (storage) => {
if (!storage) { return }
storage.newMessageCount = 0
storage.lastSeenTimestamp = new Date()
}
// Inserts date separators and marks the head and tail if it's the chain of messages made by the same user
const getView = (storage) => {
if (!storage) { return [] }
const result = []
const messages = _.sortBy(storage.messages, ['id', 'desc'])
const firstMessage = messages[0]
let previousMessage = messages[messages.length - 1]
let currentMessageChainId
if (firstMessage) {
const date = new Date(firstMessage.created_at)
date.setHours(0, 0, 0, 0)
result.push({
type: 'date',
date,
id: date.getTime().toString()
})
}
let afterDate = false
for (let i = 0; i < messages.length; i++) {
const message = messages[i]
const nextMessage = messages[i + 1]
const date = new Date(message.created_at)
date.setHours(0, 0, 0, 0)
// insert date separator and start a new message chain
if (previousMessage && previousMessage.date < date) {
result.push({
type: 'date',
date,
id: date.getTime().toString()
})
previousMessage['isTail'] = true
currentMessageChainId = undefined
afterDate = true
}
const object = {
type: 'message',
data: message,
date,
id: message.id,
messageChainId: currentMessageChainId
}
// end a message chian
if ((nextMessage && nextMessage.account_id) !== message.account_id) {
object['isTail'] = true
currentMessageChainId = undefined
}
// start a new message chain
if ((previousMessage && previousMessage.data && previousMessage.data.account_id) !== message.account_id || afterDate) {
currentMessageChainId = _.uniqueId()
object['isHead'] = true
object['messageChainId'] = currentMessageChainId
}
result.push(object)
previousMessage = object
afterDate = false
}
return result
}
const ChatService = {
add,
empty,
getView,
deleteMessage,
resetNewMessageCount,
clear
}
export default ChatService
diff --git a/test/unit/specs/services/chat_service/chat_service.spec.js b/test/unit/specs/services/chat_service/chat_service.spec.js
index 3ee9839dfd..2eb89a2dff 100644
--- a/test/unit/specs/services/chat_service/chat_service.spec.js
+++ b/test/unit/specs/services/chat_service/chat_service.spec.js
@@ -1,88 +1,88 @@
import chatService from '../../../../../src/services/chat_service/chat_service.js'
const message1 = {
id: '9wLkdcmQXD21Oy8lEX',
created_at: (new Date('2020-06-22T18:45:53.000Z'))
}
const message2 = {
id: '9wLkdp6ihaOVdNj8Wu',
account_id: '9vmRb29zLQReckr5ay',
created_at: (new Date('2020-06-22T18:45:56.000Z'))
}
const message3 = {
id: '9wLke9zL4Dy4OZR2RM',
account_id: '9vmRb29zLQReckr5ay',
created_at: (new Date('2020-07-22T18:45:59.000Z'))
}
describe('chatService', () => {
describe('.add', () => {
it("Doesn't add duplicates", () => {
const chat = chatService.empty()
chatService.add(chat, { messages: [ message1 ] })
chatService.add(chat, { messages: [ message1 ] })
expect(chat.messages.length).to.eql(1)
chatService.add(chat, { messages: [ message2 ] })
expect(chat.messages.length).to.eql(2)
})
it('Updates minId and lastMessage and newMessageCount', () => {
const chat = chatService.empty()
chatService.add(chat, { messages: [ message1 ] })
- expect(chat.lastMessage.id).to.eql(message1.id)
+ expect(chat.maxId).to.eql(message1.id)
expect(chat.minId).to.eql(message1.id)
expect(chat.newMessageCount).to.eql(1)
chatService.add(chat, { messages: [ message2 ] })
- expect(chat.lastMessage.id).to.eql(message2.id)
+ expect(chat.maxId).to.eql(message2.id)
expect(chat.minId).to.eql(message1.id)
expect(chat.newMessageCount).to.eql(2)
chatService.resetNewMessageCount(chat)
expect(chat.newMessageCount).to.eql(0)
const createdAt = new Date()
createdAt.setSeconds(createdAt.getSeconds() + 10)
chatService.add(chat, { messages: [ { message3, created_at: createdAt } ] })
expect(chat.newMessageCount).to.eql(1)
})
})
describe('.delete', () => {
it('Updates minId and lastMessage', () => {
const chat = chatService.empty()
chatService.add(chat, { messages: [ message1 ] })
chatService.add(chat, { messages: [ message2 ] })
chatService.add(chat, { messages: [ message3 ] })
- expect(chat.lastMessage.id).to.eql(message3.id)
+ expect(chat.maxId).to.eql(message3.id)
expect(chat.minId).to.eql(message1.id)
chatService.deleteMessage(chat, message3.id)
- expect(chat.lastMessage.id).to.eql(message2.id)
+ expect(chat.maxId).to.eql(message2.id)
expect(chat.minId).to.eql(message1.id)
chatService.deleteMessage(chat, message1.id)
- expect(chat.lastMessage.id).to.eql(message2.id)
+ expect(chat.maxId).to.eql(message2.id)
expect(chat.minId).to.eql(message2.id)
})
})
describe('.getView', () => {
it('Inserts date separators', () => {
const chat = chatService.empty()
chatService.add(chat, { messages: [ message1 ] })
chatService.add(chat, { messages: [ message2 ] })
chatService.add(chat, { messages: [ message3 ] })
const view = chatService.getView(chat)
expect(view.map(i => i.type)).to.eql(['date', 'message', 'message', 'date', 'message'])
})
})
})
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Aug 31, 4:17 AM (7 h, 14 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1738262
Default Alt Text
(28 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline
Log In to Comment