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

200 lines
6.1 KiB
JavaScript
Raw Normal View History

2026-02-23 20:14:39 +02:00
import { mapState } from 'pinia'
2026-01-08 17:26:52 +02:00
import RichContent from 'src/components/rich_content/rich_content.jsx'
2026-02-23 20:14:39 +02:00
2026-07-23 16:08:17 +03:00
import { useMergedConfigStore } from 'src/stores/merged_config.js'
2026-08-10 15:48:27 +03:00
import { useUsersStore } from 'src/stores/users.js'
2026-07-23 16:08:17 +03:00
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faFile,
faImage,
faLink,
2026-01-06 16:23:17 +02:00
faMusic,
2026-01-06 16:22:52 +02:00
faPollH,
} from '@fortawesome/free-solid-svg-icons'
2026-01-06 16:22:52 +02:00
library.add(faFile, faMusic, faImage, faLink, faPollH)
2025-08-06 22:24:58 +03:00
const StatusBody = {
name: 'StatusBody',
components: {
RichContent,
},
props: {
status: {
// Main thing
type: Object,
required: true,
},
compact: {
// Resizes emoji and minimizes vertical space used
// Primarily used for showing status in react notifications
type: Boolean,
default: false,
},
collapse: {
// replaces newlines with spaces
type: Boolean,
default: false,
},
singleLine: {
// Show entire thing (subject and content) in a single line
// Primarily used in chats
type: Boolean,
default: false,
},
inConversation: {
// Is status rendered within open conversation?
// Used to automatically expand subjects (if collapsed)
type: Boolean,
default: false,
2026-06-30 23:52:11 +03:00
},
2026-07-23 13:35:07 +03:00
ignoreSubject: {
// Pretend subject line doesn't exist. Useful for chat messages
// to indicate what post reply belongs to
type: Boolean,
default: false,
2026-07-23 16:08:17 +03:00
},
},
2026-01-06 16:22:52 +02:00
data() {
return {
2021-08-15 02:55:45 +03:00
postLength: this.status.text.length,
2026-01-06 16:22:52 +02:00
parseReadyDone: false,
showingTall: false,
showingLongSubject: false,
expandingSubject: null,
}
},
2025-08-19 17:28:19 +03:00
emits: ['parseReady'],
computed: {
2026-05-10 17:21:49 +03:00
allowNonSquareEmoji() {
return this.mergedConfig.nonSquareEmoji
},
2026-07-07 11:40:37 +03:00
pauseMfm() {
return this.mergedConfig.pauseMfm
},
scaleMfm() {
return this.mergedConfig.scaleMfm
},
// This is a bit hacky, but we want to approximate post height before rendering
// so we count newlines (masto uses <p> for paragraphs, GS uses <br> between them)
// as well as approximate line count by counting characters and approximating ~80
// per line.
//
// Using max-height + overflow: auto for status components resulted in false positives
// very often with japanese characters, and it was very annoying.
hasLongSubject() {
return this.status.summary.length > 240
},
hasSubject() {
2026-07-23 13:35:07 +03:00
return !!this.status.summary && !this.ignoreSubject
},
// When a status has a subject and is also tall, we should only have one show more/less
// button. If the default is to collapse statuses with subjects, we just treat it like
// a status with a subject; otherwise, we just treat it like a tall status.
2026-01-06 16:22:52 +02:00
mightHideBecauseSubject() {
2026-06-30 23:52:11 +03:00
return (
!this.inConversation &&
this.hasSubject &&
this.mergedConfig.collapseMessageWithSubject
)
},
2026-01-06 16:22:52 +02:00
mightHideBecauseTall() {
if (this.singleLine || this.compact) return false
const lengthScore =
this.status.raw_html.split(/<p|<br/).length + this.postLength / 80
return lengthScore > 20
},
2026-01-06 16:22:52 +02:00
hideSubjectStatus() {
return this.mightHideBecauseSubject && !this.expandingSubject
},
2026-01-06 16:22:52 +02:00
hideTallStatus() {
return this.mightHideBecauseTall && !this.showingTall
},
shouldShowExpandToggle() {
2025-06-25 00:50:42 -04:00
return this.mightHideBecauseSubject || this.mightHideBecauseTall
},
2026-01-06 16:22:52 +02:00
toggleButtonClasses() {
2025-06-25 00:50:42 -04:00
return {
'cw-status-hider': !this.showingMore && this.mightHideBecauseSubject,
'tall-status-hider': !this.showingMore && this.mightHideBecauseTall,
'status-unhider': this.showingMore,
}
},
2026-01-06 16:22:52 +02:00
toggleText() {
2025-06-25 00:50:42 -04:00
if (this.showingMore) {
2026-01-06 16:22:52 +02:00
return this.mightHideBecauseSubject
? this.$t('status.hide_content')
: this.$t('general.show_less')
2025-06-25 00:50:42 -04:00
} else {
2026-01-06 16:22:52 +02:00
return this.mightHideBecauseSubject
? this.$t('status.show_content')
: this.$t('general.show_more')
2025-06-25 00:50:42 -04:00
}
},
shouldHide() {
return (
!this.showingMore && this.mightHideBecauseSubject && this.hasSubject
)
},
2026-01-06 16:22:52 +02:00
showingMore() {
return (
(this.mightHideBecauseTall && this.showingTall) ||
(this.mightHideBecauseSubject && this.expandingSubject)
)
},
2026-01-06 16:22:52 +02:00
attachmentTypes() {
2026-03-06 15:25:30 +02:00
return this.status.attachments.map((file) => file.type)
},
2026-01-06 16:22:52 +02:00
collapsedStatus() {
2026-08-04 17:12:46 +03:00
return this.status.raw_html.replaceAll('(\n|<br\s?\/?>)', ' ')
},
...mapState(useMergedConfigStore, ['mergedConfig']),
},
2026-01-06 16:22:52 +02:00
mounted() {
2026-08-04 18:04:20 +03:00
this.status.attentions?.forEach((attn) => {
const { id } = attn
2026-08-10 15:48:27 +03:00
useUsersStore().fetchUserIfMissing(id)
2026-08-04 18:04:20 +03:00
})
},
methods: {
2026-01-06 16:22:52 +02:00
onParseReady(event) {
2021-08-15 02:55:45 +03:00
if (this.parseReadyDone) return
this.parseReadyDone = true
this.$emit('parseReady', event)
const { writtenMentions, invisibleMentions } = event
writtenMentions
2026-01-06 16:22:52 +02:00
.filter((mention) => !mention.notifying)
.forEach((mention) => {
const { content, url } = mention
2026-08-04 17:12:46 +03:00
const cleanedString = content.replaceAll(/<[^>]+?>/gi, '') // remove all tags
if (!cleanedString.startsWith('@')) return
const handle = cleanedString.slice(1)
const host = url.replace(/^https?:\/\//, '').replace(/\/.+?$/, '')
2026-08-10 15:48:27 +03:00
useUsersStore().fetchUserIfMissing(`${handle}@${host}`)
})
/* This is a bit of a hack to make current tall status detector work
* with rich mentions. Invisible mentions are detected at RichContent level
* and also we generate plaintext version of mentions by stripping tags
* so here we subtract from post length by each mention that became invisible
* via MentionsLine
*/
this.postLength = invisibleMentions.reduce((acc, mention) => {
return acc - mention.textContent.length - 1
}, this.postLength)
},
2026-01-06 16:22:52 +02:00
toggleShowMore() {
if (this.mightHideBecauseTall) {
this.showingTall = !this.showingTall
} else if (this.mightHideBecauseSubject) {
this.expandingSubject = !this.expandingSubject
}
},
2026-01-06 16:22:52 +02:00
generateTagLink(tag) {
return `/tag/${tag}`
2026-01-06 16:22:52 +02:00
},
},
}
2025-08-06 22:24:58 +03:00
export default StatusBody