2026-01-08 17:26:52 +02:00
import { mapGetters } from 'vuex'
import RichContent from 'src/components/rich_content/rich_content.jsx'
import fileType from 'src/services/file_type/file_type.service'
2021-06-07 18:41:55 +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 ,
2021-06-07 18:41:55 +03:00
} from '@fortawesome/free-solid-svg-icons'
2026-01-06 16:22:52 +02:00
library . add ( faFile , faMusic , faImage , faLink , faPollH )
2021-06-07 18:41:55 +03:00
2025-08-06 22:24:58 +03:00
const StatusBody = {
name : 'StatusBody' ,
2021-06-07 18:41:55 +03:00
props : [
2021-06-14 02:52:41 +03:00
'compact' ,
2025-08-14 12:31:12 +03:00
'collapse' , // replaces newlines with spaces
2021-06-07 18:41:55 +03:00
'status' ,
'focused' ,
'noHeading' ,
'fullContent' ,
2021-09-09 00:03:10 -04:00
'singleLine' ,
'showingTall' ,
'expandingSubject' ,
'showingLongSubject' ,
'toggleShowingTall' ,
'toggleExpandingSubject' ,
2026-01-06 16:22:52 +02:00
'toggleShowingLongSubject' ,
2021-06-07 18:41:55 +03:00
] ,
2026-01-06 16:22:52 +02:00
data ( ) {
2021-06-07 18:41:55 +03:00
return {
2021-08-15 02:55:45 +03:00
postLength : this . status . text . length ,
2026-01-06 16:22:52 +02:00
parseReadyDone : false ,
2021-06-07 18:41:55 +03:00
}
} ,
2025-08-19 17:28:19 +03:00
emits : [ 'parseReady' ] ,
2021-06-07 18:41:55 +03:00
computed : {
2026-01-06 16:22:52 +02:00
localCollapseSubjectDefault ( ) {
2021-06-07 18:41:55 +03:00
return this . mergedConfig . collapseMessageWithSubject
} ,
// 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.
2026-01-06 16:22:52 +02:00
tallStatus ( ) {
2021-08-15 18:21:25 +03:00
if ( this . singleLine || this . compact ) return false
2026-01-06 16:22:52 +02:00
const lengthScore =
this . status . raw _html . split ( /<p|<br/ ) . length + this . postLength / 80
2021-06-07 18:41:55 +03:00
return lengthScore > 20
} ,
2026-01-06 16:22:52 +02:00
longSubject ( ) {
2021-06-07 18:41:55 +03:00
return this . status . summary . length > 240
} ,
// 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 ( ) {
2021-06-07 18:41:55 +03:00
return ! ! this . status . summary && this . localCollapseSubjectDefault
} ,
2026-01-06 16:22:52 +02:00
mightHideBecauseTall ( ) {
return (
this . tallStatus &&
! ( this . status . summary && this . localCollapseSubjectDefault )
)
2021-06-07 18:41:55 +03:00
} ,
2026-01-06 16:22:52 +02:00
hideSubjectStatus ( ) {
2021-06-07 18:41:55 +03:00
return this . mightHideBecauseSubject && ! this . expandingSubject
} ,
2026-01-06 16:22:52 +02:00
hideTallStatus ( ) {
2021-06-07 18:41:55 +03:00
return this . mightHideBecauseTall && ! this . showingTall
} ,
2026-01-06 16:22:52 +02:00
shouldShowToggle ( ) {
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
}
} ,
2026-01-06 16:22:52 +02:00
showingMore ( ) {
return (
( this . mightHideBecauseTall && this . showingTall ) ||
( this . mightHideBecauseSubject && this . expandingSubject )
)
2021-06-07 18:41:55 +03:00
} ,
2026-01-06 16:22:52 +02:00
attachmentTypes ( ) {
return this . status . attachments . map ( ( file ) =>
fileType . fileType ( file . mimetype ) ,
)
2021-06-07 18:41:55 +03:00
} ,
2026-01-06 16:22:52 +02:00
collapsedStatus ( ) {
2025-08-14 12:31:12 +03:00
return this . status . raw _html . replace ( /(\n|<br\s?\/?>)/g , ' ' )
} ,
2026-01-06 16:22:52 +02:00
... mapGetters ( [ 'mergedConfig' ] ) ,
2021-06-07 18:41:55 +03:00
} ,
components : {
2026-01-06 16:22:52 +02:00
RichContent ,
2021-06-07 18:41:55 +03:00
} ,
2026-01-06 16:22:52 +02:00
mounted ( ) {
this . status . attentions &&
this . status . attentions . forEach ( ( attn ) => {
const { id } = attn
this . $store . dispatch ( 'fetchUserIfMissing' , id )
} )
2021-06-07 18:41:55 +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
2021-06-22 20:16:26 +03:00
this . $emit ( 'parseReady' , event )
2021-08-15 02:41:53 +03:00
const { writtenMentions , invisibleMentions } = event
2021-06-22 20:16:26 +03:00
writtenMentions
2026-01-06 16:22:52 +02:00
. filter ( ( mention ) => ! mention . notifying )
. forEach ( ( mention ) => {
2021-06-22 20:16:26 +03:00
const { content , url } = mention
const cleanedString = content . replace ( /<[^>]+?>/gi , '' ) // remove all tags
if ( ! cleanedString . startsWith ( '@' ) ) return
const handle = cleanedString . slice ( 1 )
const host = url . replace ( /^https?:\/\// , '' ) . replace ( /\/.+?$/ , '' )
this . $store . dispatch ( 'fetchUserIfMissing' , ` ${ handle } @ ${ host } ` )
} )
2021-08-15 02:41:53 +03:00
/ * T h i s i s a b i t o f a h a c k t o m a k e c u r r e n t t a l l s t a t u s d e t e c t o r w o r k
* 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 )
2021-06-22 20:16:26 +03:00
} ,
2026-01-06 16:22:52 +02:00
toggleShowMore ( ) {
2021-06-07 18:41:55 +03:00
if ( this . mightHideBecauseTall ) {
2021-09-09 00:03:10 -04:00
this . toggleShowingTall ( )
2021-06-07 18:41:55 +03:00
} else if ( this . mightHideBecauseSubject ) {
2021-09-09 00:03:10 -04:00
this . toggleExpandingSubject ( )
2021-06-07 18:41:55 +03:00
}
} ,
2026-01-06 16:22:52 +02:00
generateTagLink ( tag ) {
2021-06-07 18:41:55 +03:00
return ` /tag/ ${ tag } `
2026-01-06 16:22:52 +02:00
} ,
} ,
2021-06-07 18:41:55 +03:00
}
2025-08-06 22:24:58 +03:00
export default StatusBody