This commit is contained in:
Henry Jameson 2026-07-21 18:25:56 +03:00
commit 2871326c35
9 changed files with 79 additions and 46 deletions

View file

@ -17,7 +17,6 @@ export const paramsString = (params = {}) => {
} }
})() })()
const arrays = [] const arrays = []
const nonArrays = [] const nonArrays = []

View file

@ -7,9 +7,9 @@ import ChatMessageDate from 'src/components/chat_message_date/chat_message_date.
import Gallery from 'src/components/gallery/gallery.vue' import Gallery from 'src/components/gallery/gallery.vue'
import LinkPreview from 'src/components/link-preview/link-preview.vue' import LinkPreview from 'src/components/link-preview/link-preview.vue'
import Popover from 'src/components/popover/popover.vue' import Popover from 'src/components/popover/popover.vue'
import StatusContent from 'src/components/status_content/status_content.vue'
import StatusBody from 'src/components/status_body/status_body.vue'
import StatusActionButtons from 'src/components/status_action_buttons/status_action_buttons.vue' import StatusActionButtons from 'src/components/status_action_buttons/status_action_buttons.vue'
import StatusBody from 'src/components/status_body/status_body.vue'
import StatusContent from 'src/components/status_content/status_content.vue'
import UserAvatar from 'src/components/user_avatar/user_avatar.vue' import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
import UserPopover from 'src/components/user_popover/user_popover.vue' import UserPopover from 'src/components/user_popover/user_popover.vue'
@ -28,7 +28,14 @@ library.add(faTimes, faEllipsisH, faCircleNotch)
const ChatMessage = { const ChatMessage = {
name: 'ChatMessage', name: 'ChatMessage',
props: ['edited', 'noHeading', 'previousItem', 'chatItem', 'previousItem', 'hoveredMessageChain'], props: [
'edited',
'noHeading',
'previousItem',
'chatItem',
'previousItem',
'hoveredMessageChain',
],
emits: ['hover', 'replyRequested'], emits: ['hover', 'replyRequested'],
components: { components: {
Popover, Popover,
@ -40,7 +47,8 @@ const ChatMessage = {
Gallery, Gallery,
LinkPreview, LinkPreview,
ChatMessageDate, ChatMessageDate,
UserPopover,}, UserPopover,
},
computed: { computed: {
// Returns HH:MM (hours and minutes) in local time. // Returns HH:MM (hours and minutes) in local time.
createdAt() { createdAt() {
@ -74,10 +82,14 @@ const ChatMessage = {
console.log('==') console.log('==')
console.log('PREV', toValue(this.previousItem.data.raw_html)) console.log('PREV', toValue(this.previousItem.data.raw_html))
console.log('CURR', toValue(this.chatItem.data.raw_html)) console.log('CURR', toValue(this.chatItem.data.raw_html))
return this.previousItem.data.id !== this.chatItem.data.in_reply_to_status_id return (
this.previousItem.data.id !== this.chatItem.data.in_reply_to_status_id
)
}, },
customReplyTo() { customReplyTo() {
return find(this.$store.state.statuses.allStatuses, { id: this.chatItem.data.in_reply_to_status_id }) return find(this.$store.state.statuses.allStatuses, {
id: this.chatItem.data.in_reply_to_status_id,
})
}, },
messageForStatusContent() { messageForStatusContent() {
return { return {

View file

@ -51,6 +51,9 @@ const Chat = {
ChatTitle, ChatTitle,
PostStatusForm, PostStatusForm,
}, },
props: {
testMode: Boolean,
},
data() { data() {
return { return {
// Main info // Main info
@ -76,6 +79,7 @@ const Chat = {
} }
}, },
created() { created() {
if (this.testMode) return
this.startFetching() this.startFetching()
window.addEventListener('resize', this.handleResize) window.addEventListener('resize', this.handleResize)
}, },
@ -210,13 +214,16 @@ const Chat = {
if (!isNewMessage) return if (!isNewMessage) return
await readChat({ if (!this.testMode)
id: this.chat.id, await readChat({
lastReadId, id: this.chat.id,
credentials: useOAuthStore().token, lastReadId,
}) credentials: useOAuthStore().token,
})
useChatsStore().readChat(this.chat.id) useChatsStore().readChat(this.chat.id)
this.lastReadMessageId = this.maxId
this.newMessageCount = 0
}, },
bottomedOut(offset) { bottomedOut(offset) {
return isBottomedOut(offset) return isBottomedOut(offset)
@ -224,24 +231,27 @@ const Chat = {
reachedTop() { reachedTop() {
return window.scrollY <= 0 return window.scrollY <= 0
}, },
cullOlder() {
const maxIndex = this.messages.length
const minIndex = maxIndex - 50
if (maxIndex <= 50) return
this.messages = sortBy(this.messages, ['id'])
this.minId = this.messages[minIndex].id
for (const message of this.messages) {
if (message.id < this.minId) {
delete this.messagesIndex[message.id]
delete this.idempotencyKeyIndex[message.idempotency_key]
}
}
this.messages = this.messages.slice(minIndex, maxIndex)
},
cullOlderCheck() { cullOlderCheck() {
window.setTimeout(() => { window.setTimeout(() => {
if (this.bottomedOut(JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET)) { if (this.bottomedOut(JUMP_TO_BOTTOM_BUTTON_VISIBILITY_OFFSET)) {
const maxIndex = this.messages.length this.cullOlder()
const minIndex = maxIndex - 50
if (maxIndex <= 50) return
this.messages = sortBy(this.messages, ['id'])
this.minId = this.messages[minIndex].id
for (const message of this.messages) {
if (message.id < this.minId) {
delete this.messagesIndex[message.id]
delete this.idempotencyKeyIndex[message.idempotency_key]
}
}
this.messages = this.messages.slice(minIndex, maxIndex)
} }
}, 5000) }, 5000)
}, },
@ -354,11 +364,12 @@ const Chat = {
this.fetchChat({ isFirstFetch: true }) this.fetchChat({ isFirstFetch: true })
}, },
async deleteChatMessage({ chatId, messageId }) { async deleteChatMessage({ chatId, messageId }) {
await deleteChatMessage({ if (!this.testMode)
chatId, await deleteChatMessage({
messageId, chatId,
credentials: useOAuthStore().token, messageId,
}) credentials: useOAuthStore().token,
})
this.messages = this.messages.filter((m) => m.id !== messageId) this.messages = this.messages.filter((m) => m.id !== messageId)
delete this.messagesIndex[messageId] delete this.messagesIndex[messageId]
@ -406,7 +417,7 @@ const Chat = {
} }
if (!this.messagesIndex[message.id] && !isConfirmation(this, message)) { if (!this.messagesIndex[message.id] && !isConfirmation(this, message)) {
if (this.lastSeenMessageId < message.id) { if (this.lastReadMessageId < message.id) {
this.newMessageCount++ this.newMessageCount++
} }
this.messagesIndex[message.id] = message this.messagesIndex[message.id] = message

View file

@ -2,11 +2,11 @@ import { clone, filter, findIndex, get, reduce } from 'lodash'
import { mapState as mapPiniaState } from 'pinia' import { mapState as mapPiniaState } from 'pinia'
import { mapState } from 'vuex' import { mapState } from 'vuex'
import StatusContent from 'src/components/status_content/status_content.vue'
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue' import ChatMessageList from 'src/components/chat_message_list/chat_message_list.vue'
import PostStatusForm from 'src/components/post_status_form/post_status_form.vue'
import QuickFilterSettings from 'src/components/quick_filter_settings/quick_filter_settings.vue' import QuickFilterSettings from 'src/components/quick_filter_settings/quick_filter_settings.vue'
import QuickViewSettings from 'src/components/quick_view_settings/quick_view_settings.vue' import QuickViewSettings from 'src/components/quick_view_settings/quick_view_settings.vue'
import StatusContent from 'src/components/status_content/status_content.vue'
import ThreadTree from 'src/components/thread_tree/thread_tree.vue' import ThreadTree from 'src/components/thread_tree/thread_tree.vue'
import { useInterfaceStore } from 'src/stores/interface' import { useInterfaceStore } from 'src/stores/interface'
@ -25,7 +25,13 @@ import {
faTimes, faTimes,
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
library.add(faAngleDoubleDown, faAngleDoubleLeft, faChevronLeft, faReply, faTimes) library.add(
faAngleDoubleDown,
faAngleDoubleLeft,
faChevronLeft,
faReply,
faTimes,
)
const sortById = (a, b) => { const sortById = (a, b) => {
const idA = a.type === 'retweet' ? a.retweeted_status.id : a.id const idA = a.type === 'retweet' ? a.retweeted_status.id : a.id

View file

@ -7,12 +7,12 @@ import GestureService from '../../services/gesture_service/gesture_service'
import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils' import { unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils'
import { useAnnouncementsStore } from 'src/stores/announcements' import { useAnnouncementsStore } from 'src/stores/announcements'
import { useChatsStore } from 'src/stores/chats.js'
import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js' import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
import { useInterfaceStore } from 'src/stores/interface' import { useInterfaceStore } from 'src/stores/interface'
import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useShoutStore } from 'src/stores/shout' import { useShoutStore } from 'src/stores/shout'
import { useChatsStore } from 'src/stores/chats.js'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { import {

View file

@ -105,7 +105,9 @@ export default {
return useMergedConfigStore().mergedConfig.hidePostStats return useMergedConfigStore().mergedConfig.hidePostStats
}, },
buttonInnerClass() { buttonInnerClass() {
const buttonStyleClass = this.defaultButtonStyle ? 'button-default' : 'button-unstyled' const buttonStyleClass = this.defaultButtonStyle
? 'button-default'
: 'button-unstyled'
return [ return [
this.button.name + '-button', this.button.name + '-button',
{ {

View file

@ -42,7 +42,7 @@ export default {
), ),
), ),
}, },
props: ['button', 'status', 'defaultButton','hideLabel'], props: ['button', 'status', 'defaultButton', 'hideLabel'],
emits: ['emojiPickerShown'], emits: ['emojiPickerShown'],
mounted() { mounted() {
if (this.button.name === 'mute') { if (this.button.name === 'mute') {

View file

@ -38,7 +38,7 @@ const StatusActionButtons = {
hideLabels: { hideLabels: {
type: Boolean, type: Boolean,
default: false, default: false,
} },
}, },
emits: ['toggleReplying', 'onSuccess', 'onError'], emits: ['toggleReplying', 'onSuccess', 'onError'],
data() { data() {
@ -64,7 +64,8 @@ const StatusActionButtons = {
}, },
computed: { computed: {
...mapState(useSyncConfigStore, { ...mapState(useSyncConfigStore, {
userPinnedItems: (store) => new Set(store.prefsStorage.collections.pinnedStatusActions), userPinnedItems: (store) =>
new Set(store.prefsStorage.collections.pinnedStatusActions),
}), }),
pinnedItems() { pinnedItems() {
if (this.fixedPinned) { if (this.fixedPinned) {

View file

@ -1,7 +1,7 @@
import { createTestingPinia } from '@pinia/testing'
import { shallowMount } from '@vue/test-utils' import { shallowMount } from '@vue/test-utils'
import { setActivePinia } from 'pinia' import { setActivePinia } from 'pinia'
import { createTestingPinia } from '@pinia/testing'
import { HttpResponse, http } from 'msw'
import ChatView from './chat_view.vue' import ChatView from './chat_view.vue'
const message1 = { const message1 = {
@ -32,7 +32,7 @@ const global = {
$store: { $store: {
state: { state: {
api: {}, api: {},
users: {} users: {},
}, },
}, },
$route: { $route: {
@ -41,8 +41,10 @@ const global = {
}, },
}, },
$router: { $router: {
push: () => {} push: () => {
} /* noop */
},
},
}, },
stubs: { stubs: {
FAIcon: true, FAIcon: true,