pleroma-fe/src/components/chat_message/chat_message.js

219 lines
6.4 KiB
JavaScript
Raw Normal View History

2023-04-05 21:06:37 -06:00
import { mapState as mapPiniaState } from 'pinia'
2026-06-25 13:46:27 +03:00
import { mapState } from 'vuex'
2026-01-08 17:26:52 +02:00
2026-06-02 18:09:12 +03:00
import Attachment from 'src/components/attachment/attachment.vue'
2026-07-22 16:03:48 +03:00
import MentionLink from 'src/components/mention_link/mention_link.vue'
2026-06-02 18:09:12 +03:00
import ChatMessageDate from 'src/components/chat_message_date/chat_message_date.vue'
2026-06-04 21:59:09 +03:00
import Gallery from 'src/components/gallery/gallery.vue'
2026-06-02 18:09:12 +03:00
import LinkPreview from 'src/components/link-preview/link-preview.vue'
import Popover from 'src/components/popover/popover.vue'
2026-07-10 17:09:17 +03:00
import StatusActionButtons from 'src/components/status_action_buttons/status_action_buttons.vue'
2026-07-21 18:25:56 +03:00
import StatusBody from 'src/components/status_body/status_body.vue'
import StatusContent from 'src/components/status_content/status_content.vue'
2026-07-22 16:03:48 +03:00
import StatusPopover from 'src/components/status_popover/status_popover.vue'
2026-06-02 18:09:12 +03:00
import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
import UserPopover from 'src/components/user_popover/user_popover.vue'
2026-07-22 21:42:59 +03:00
import Timeago from 'src/components/timeago/timeago.vue'
2026-07-23 14:08:40 +03:00
import EmojiReactions from 'src/components/emoji_reactions/emoji_reactions.vue'
2020-10-20 21:18:23 +03:00
2026-01-29 20:40:00 +02:00
import { useInstanceStore } from 'src/stores/instance.js'
import { useInterfaceStore } from 'src/stores/interface'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
2026-01-29 20:40:00 +02:00
2026-01-08 17:26:52 +02:00
import { library } from '@fortawesome/fontawesome-svg-core'
2026-07-08 20:29:59 +03:00
import {
faCircleNotch,
faEllipsisH,
faTimes,
2026-07-22 16:03:48 +03:00
faReply,
2026-07-23 14:08:40 +03:00
faRetweet,
faStar,
2026-07-08 20:29:59 +03:00
} from '@fortawesome/free-solid-svg-icons'
2026-01-08 17:26:52 +02:00
2026-07-23 14:08:40 +03:00
library.add(faTimes, faEllipsisH, faCircleNotch, faReply, faStar, faRetweet)
2020-05-07 16:10:53 +03:00
const ChatMessage = {
name: 'ChatMessage',
2026-07-21 18:25:56 +03:00
props: [
'edited',
'noHeading',
'previousItem',
'chatItem',
'previousItem',
'hoveredMessageChain',
2026-07-22 16:03:48 +03:00
'focused',
'repliedTo',
2026-07-21 18:25:56 +03:00
],
2026-07-10 17:09:17 +03:00
emits: ['hover', 'replyRequested'],
2020-05-07 16:10:53 +03:00
components: {
Popover,
Attachment,
StatusContent,
2026-07-10 17:09:17 +03:00
StatusBody,
StatusActionButtons,
2020-05-07 16:10:53 +03:00
UserAvatar,
Gallery,
2020-05-07 16:10:53 +03:00
LinkPreview,
2022-07-20 12:54:51 +03:00
ChatMessageDate,
2026-07-23 14:08:40 +03:00
EmojiReactions,
2026-07-21 18:25:56 +03:00
UserPopover,
2026-07-22 16:03:48 +03:00
StatusPopover,
MentionLink,
2026-07-22 21:42:59 +03:00
Timeago,
2026-07-21 18:25:56 +03:00
},
2020-05-07 16:10:53 +03:00
computed: {
2026-07-08 19:34:09 +03:00
isStatus() {
// ChatMessage only has account_id while Status has full user data
return !!this.message.user
},
author() {
const accountId = this.message.account_id || this.message.user.id
return this.$store.getters.findUser(accountId)
},
2026-01-06 16:22:52 +02:00
isCurrentUser() {
return this.author.id === this.currentUser.id
2020-05-07 16:10:53 +03:00
},
2026-01-06 16:22:52 +02:00
message() {
return this.isMessage ? this.chatItem.data : null
2020-05-07 16:10:53 +03:00
},
2026-01-06 16:22:52 +02:00
isMessage() {
return this.chatItem.type === 'message'
2020-05-07 16:10:53 +03:00
},
2026-07-10 17:09:17 +03:00
isCustomReply() {
if (!this.previousItem) return false
2026-07-21 18:56:20 +03:00
if (!this.chatItem.data.in_reply_to_status_id) return false
2026-07-21 18:25:56 +03:00
return (
this.previousItem.data.id !== this.chatItem.data.in_reply_to_status_id
)
2026-07-10 17:09:17 +03:00
},
2026-07-22 21:38:49 +03:00
isBrokenReply() {
if (!this.previousItem) return false
return !this.chatItem.data.in_reply_to_status_id
},
2026-07-10 17:09:17 +03:00
customReplyTo() {
2026-07-21 18:56:20 +03:00
return this.$store.state.statuses.allStatusesObject[
this.chatItem.data.in_reply_to_status_id
]
2026-07-10 17:09:17 +03:00
},
2026-07-22 16:03:48 +03:00
replyToName() {
if (this.message.in_reply_to_screen_name) {
return this.message.in_reply_to_screen_name
} else {
const user = this.$store.getters.findUser(
this.status.in_reply_to_user_id,
)
return user && user.screen_name_ui
}
},
replyProfileLink() {
if (this.isCustomReply) {
const user = this.$store.getters.findUser(
this.message.in_reply_to_user_id,
)
// FIXME Why user not found sometimes???
return user ? user.statusnet_profile_url : 'NOT_FOUND'
}
},
2026-01-06 16:22:52 +02:00
messageForStatusContent() {
2020-05-07 16:10:53 +03:00
return {
summary: '',
emojis: this.message.emojis,
raw_html: this.message.content || this.message.raw_html || '',
2021-06-13 21:42:25 +03:00
text: this.message.content || '',
2026-01-06 16:22:52 +02:00
attachments: this.message.attachments,
2020-05-07 16:10:53 +03:00
}
},
2026-01-06 16:22:52 +02:00
hasAttachment() {
2020-05-07 16:10:53 +03:00
return this.message.attachments.length > 0
},
2026-07-22 16:03:48 +03:00
classnames() {
return {
'-outgoing': this.isCurrentUser,
'-incoming': !this.isCurrentUser,
'-pending': this.message.pending,
'-focused': this.focused,
}
},
2023-04-05 21:06:37 -06:00
...mapPiniaState(useInterfaceStore, {
2026-01-06 16:22:52 +02:00
betterShadow: (store) => store.browserSupport.cssFilter,
2023-04-05 21:06:37 -06:00
}),
2020-05-07 16:10:53 +03:00
...mapState({
2026-01-06 16:22:52 +02:00
currentUser: (state) => state.users.currentUser,
restrictedNicknames: (state) => useInstanceStore().restrictedNicknames,
2020-05-07 16:10:53 +03:00
}),
2026-01-06 16:22:52 +02:00
popoverMarginStyle() {
2020-05-07 16:10:53 +03:00
if (this.isCurrentUser) {
return {}
} else {
return { left: 50 }
}
},
...mapPiniaState(useMergedConfigStore, ['mergedConfig', 'findUser']),
2020-05-07 16:10:53 +03:00
},
2026-01-06 16:22:52 +02:00
data() {
2020-05-07 16:10:53 +03:00
return {
hovered: false,
2026-01-06 16:22:52 +02:00
menuOpened: false,
2020-05-07 16:10:53 +03:00
}
},
2026-07-23 13:51:41 +03:00
watch: {
focused: function (value) {
this.scrollIfFocused(value)
},
},
2020-05-07 16:10:53 +03:00
methods: {
2026-01-06 16:22:52 +02:00
onHover(bool) {
this.$emit('hover', {
isHovered: bool,
messageChainId: this.chatItem.messageChainId,
2026-01-06 16:22:52 +02:00
})
2020-05-07 16:10:53 +03:00
},
2026-07-09 01:07:59 +03:00
visibilityIcon(visibility) {
switch (visibility) {
case 'private':
return 'lock'
case 'unlisted':
return 'lock-open'
case 'direct':
return 'envelope'
case 'local':
return 'igloo'
default:
return 'globe'
}
},
2026-07-21 23:22:34 +03:00
visibilityLocalized() {
return this.$i18n.t('general.scope_in_timeline.' + this.status.visibility)
},
2026-01-06 16:22:52 +02:00
async deleteMessage() {
2020-05-07 16:10:53 +03:00
const confirmed = window.confirm(this.$t('chats.delete_confirm'))
if (confirmed) {
2026-07-08 19:34:09 +03:00
await this.$emit('delete', {
messageId: this.chatItem.data.id,
chatId: this.chatItem.data.chat_id,
2020-05-07 16:10:53 +03:00
})
}
this.hovered = false
this.menuOpened = false
2026-01-06 16:22:52 +02:00
},
2026-07-23 13:51:41 +03:00
scrollIfFocused(focused) {
if (this.$el.getBoundingClientRect == null) return
if (focused) {
const rect = this.$el.getBoundingClientRect()
if (rect.top < 100) {
// Post is above screen, match its top to screen top
window.scrollBy(0, rect.top - 100)
} else if (rect.height >= window.innerHeight - 50) {
// Post we want to see is taller than screen so match its top to screen top
window.scrollBy(0, rect.top - 100)
} else if (rect.bottom > window.innerHeight - 50) {
// Post is below screen, match its bottom to screen bottom
window.scrollBy(0, rect.bottom - window.innerHeight + 50)
}
}
},
2026-01-06 16:22:52 +02:00
},
2020-05-07 16:10:53 +03:00
}
export default ChatMessage