forked from AkkomaGang/akkoma-fe
with some changes/merge conflicts resolution * switch-to-string-ids: fixx????? fix notifications? fix lint fix tests, removed one unused function, fix real problem that tests helped to surface added some more explicit to string conversion since BE seem to be sending numbers and it could cause an issue. Remove all explicit and implicit conversions of statusId to number, changed explicit ones so that they convert them to string
316 lines
10 KiB
JavaScript
316 lines
10 KiB
JavaScript
import Attachment from '../attachment/attachment.vue'
|
|
import FavoriteButton from '../favorite_button/favorite_button.vue'
|
|
import RetweetButton from '../retweet_button/retweet_button.vue'
|
|
import DeleteButton from '../delete_button/delete_button.vue'
|
|
import PostStatusForm from '../post_status_form/post_status_form.vue'
|
|
import UserCardContent from '../user_card_content/user_card_content.vue'
|
|
import StillImage from '../still-image/still-image.vue'
|
|
import { filter, find } from 'lodash'
|
|
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
|
|
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
|
|
|
|
const Status = {
|
|
name: 'Status',
|
|
props: [
|
|
'statusoid',
|
|
'expandable',
|
|
'inConversation',
|
|
'focused',
|
|
'highlight',
|
|
'compact',
|
|
'replies',
|
|
'noReplyLinks',
|
|
'noHeading',
|
|
'inlineExpanded'
|
|
],
|
|
data () {
|
|
return {
|
|
replying: false,
|
|
expanded: false,
|
|
unmuted: false,
|
|
userExpanded: false,
|
|
preview: null,
|
|
showPreview: false,
|
|
showingTall: false,
|
|
expandingSubject: typeof this.$store.state.config.collapseMessageWithSubject === 'undefined'
|
|
? !this.$store.state.instance.collapseMessageWithSubject
|
|
: !this.$store.state.config.collapseMessageWithSubject,
|
|
betterShadow: this.$store.state.interface.browserSupport.cssFilter
|
|
}
|
|
},
|
|
computed: {
|
|
localCollapseSubjectDefault () {
|
|
return typeof this.$store.state.config.collapseMessageWithSubject === 'undefined'
|
|
? this.$store.state.instance.collapseMessageWithSubject
|
|
: this.$store.state.config.collapseMessageWithSubject
|
|
},
|
|
muteWords () {
|
|
return this.$store.state.config.muteWords
|
|
},
|
|
repeaterClass () {
|
|
const user = this.statusoid.user
|
|
return highlightClass(user)
|
|
},
|
|
userClass () {
|
|
const user = this.retweet ? (this.statusoid.retweeted_status.user) : this.statusoid.user
|
|
return highlightClass(user)
|
|
},
|
|
deleted () {
|
|
return this.statusoid.deleted
|
|
},
|
|
repeaterStyle () {
|
|
const user = this.statusoid.user
|
|
const highlight = this.$store.state.config.highlight
|
|
return highlightStyle(highlight[user.screen_name])
|
|
},
|
|
userStyle () {
|
|
if (this.noHeading) return
|
|
const user = this.retweet ? (this.statusoid.retweeted_status.user) : this.statusoid.user
|
|
const highlight = this.$store.state.config.highlight
|
|
return highlightStyle(highlight[user.screen_name])
|
|
},
|
|
hideAttachments () {
|
|
return (this.$store.state.config.hideAttachments && !this.inConversation) ||
|
|
(this.$store.state.config.hideAttachmentsInConv && this.inConversation)
|
|
},
|
|
retweet () { return !!this.statusoid.retweeted_status },
|
|
retweeter () { return this.statusoid.user.name },
|
|
retweeterHtml () { return this.statusoid.user.name_html },
|
|
status () {
|
|
if (this.retweet) {
|
|
return this.statusoid.retweeted_status
|
|
} else {
|
|
return this.statusoid
|
|
}
|
|
},
|
|
loggedIn () {
|
|
return !!this.$store.state.users.currentUser
|
|
},
|
|
muteWordHits () {
|
|
const statusText = this.status.text.toLowerCase()
|
|
const hits = filter(this.muteWords, (muteWord) => {
|
|
return statusText.includes(muteWord.toLowerCase())
|
|
})
|
|
|
|
return hits
|
|
},
|
|
muted () { return !this.unmuted && (this.status.user.muted || this.muteWordHits.length > 0) },
|
|
isFocused () {
|
|
// retweet or root of an expanded conversation
|
|
if (this.focused) {
|
|
return true
|
|
} else if (!this.inConversation) {
|
|
return false
|
|
}
|
|
// use conversation highlight only when in conversation
|
|
return this.status.id === this.highlight
|
|
},
|
|
// 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.
|
|
tallStatus () {
|
|
const lengthScore = this.status.statusnet_html.split(/<p|<br/).length + this.status.text.length / 80
|
|
return lengthScore > 20
|
|
},
|
|
isReply () {
|
|
if (this.status.in_reply_to_status_id) {
|
|
return true
|
|
}
|
|
// For private replies where we can't see the OP, in_reply_to_status_id will be null.
|
|
// So instead, check that the post starts with a @mention.
|
|
if (this.status.visibility === 'private') {
|
|
var textBody = this.status.text
|
|
if (this.status.summary !== null) {
|
|
textBody = textBody.substring(this.status.summary.length, textBody.length)
|
|
}
|
|
return textBody.startsWith('@')
|
|
}
|
|
return false
|
|
},
|
|
hideReply () {
|
|
if (this.$store.state.config.replyVisibility === 'all') {
|
|
return false
|
|
}
|
|
if (this.inlineExpanded || this.expanded || this.inConversation || !this.isReply) {
|
|
return false
|
|
}
|
|
if (this.status.user.id === this.$store.state.users.currentUser.id) {
|
|
return false
|
|
}
|
|
if (this.status.activity_type === 'repeat') {
|
|
return false
|
|
}
|
|
var checkFollowing = this.$store.state.config.replyVisibility === 'following'
|
|
for (var i = 0; i < this.status.attentions.length; ++i) {
|
|
if (this.status.user.id === this.status.attentions[i].id) {
|
|
continue
|
|
}
|
|
if (checkFollowing && this.status.attentions[i].following) {
|
|
return false
|
|
}
|
|
if (this.status.attentions[i].id === this.$store.state.users.currentUser.id) {
|
|
return false
|
|
}
|
|
}
|
|
return this.status.attentions.length > 0
|
|
},
|
|
hideSubjectStatus () {
|
|
if (this.tallStatus && !this.localCollapseSubjectDefault) {
|
|
return false
|
|
}
|
|
return !this.expandingSubject && this.status.summary
|
|
},
|
|
hideTallStatus () {
|
|
if (this.status.summary && this.localCollapseSubjectDefault) {
|
|
return false
|
|
}
|
|
if (this.showingTall) {
|
|
return false
|
|
}
|
|
return this.tallStatus
|
|
},
|
|
showingMore () {
|
|
return this.showingTall || (this.status.summary && this.expandingSubject)
|
|
},
|
|
nsfwClickthrough () {
|
|
if (!this.status.nsfw) {
|
|
return false
|
|
}
|
|
if (this.status.summary && this.localCollapseSubjectDefault) {
|
|
return false
|
|
}
|
|
return true
|
|
},
|
|
replySubject () {
|
|
if (!this.status.summary) return ''
|
|
const behavior = typeof this.$store.state.config.subjectLineBehavior === 'undefined'
|
|
? this.$store.state.instance.subjectLineBehavior
|
|
: this.$store.state.config.subjectLineBehavior
|
|
const startsWithRe = this.status.summary.match(/^re[: ]/i)
|
|
if (behavior !== 'noop' && startsWithRe || behavior === 'masto') {
|
|
return this.status.summary
|
|
} else if (behavior === 'email') {
|
|
return 're: '.concat(this.status.summary)
|
|
} else if (behavior === 'noop') {
|
|
return ''
|
|
}
|
|
},
|
|
attachmentSize () {
|
|
if ((this.$store.state.config.hideAttachments && !this.inConversation) ||
|
|
(this.$store.state.config.hideAttachmentsInConv && this.inConversation)) {
|
|
return 'hide'
|
|
} else if (this.compact) {
|
|
return 'small'
|
|
}
|
|
return 'normal'
|
|
}
|
|
},
|
|
components: {
|
|
Attachment,
|
|
FavoriteButton,
|
|
RetweetButton,
|
|
DeleteButton,
|
|
PostStatusForm,
|
|
UserCardContent,
|
|
StillImage
|
|
},
|
|
methods: {
|
|
visibilityIcon (visibility) {
|
|
switch (visibility) {
|
|
case 'private':
|
|
return 'icon-lock'
|
|
case 'unlisted':
|
|
return 'icon-lock-open-alt'
|
|
case 'direct':
|
|
return 'icon-mail-alt'
|
|
default:
|
|
return 'icon-globe'
|
|
}
|
|
},
|
|
linkClicked ({target}) {
|
|
if (target.tagName === 'SPAN') {
|
|
target = target.parentNode
|
|
}
|
|
if (target.tagName === 'A') {
|
|
window.open(target.href, '_blank')
|
|
}
|
|
},
|
|
toggleReplying () {
|
|
this.replying = !this.replying
|
|
},
|
|
gotoOriginal (id) {
|
|
// only handled by conversation, not status_or_conversation
|
|
if (this.inConversation) {
|
|
this.$emit('goto', id)
|
|
}
|
|
},
|
|
toggleExpanded () {
|
|
this.$emit('toggleExpanded')
|
|
},
|
|
toggleMute () {
|
|
this.unmuted = !this.unmuted
|
|
},
|
|
toggleUserExpanded () {
|
|
this.userExpanded = !this.userExpanded
|
|
},
|
|
toggleShowMore () {
|
|
if (this.showingTall) {
|
|
this.showingTall = false
|
|
} else if (this.expandingSubject) {
|
|
this.expandingSubject = false
|
|
} else if (this.hideTallStatus) {
|
|
this.showingTall = true
|
|
} else if (this.hideSubjectStatus) {
|
|
this.expandingSubject = true
|
|
}
|
|
},
|
|
replyEnter (id, event) {
|
|
this.showPreview = true
|
|
const targetId = id
|
|
const statuses = this.$store.state.statuses.allStatuses
|
|
|
|
if (!this.preview) {
|
|
// if we have the status somewhere already
|
|
this.preview = find(statuses, { 'id': targetId })
|
|
// or if we have to fetch it
|
|
if (!this.preview) {
|
|
this.$store.state.api.backendInteractor.fetchStatus({id}).then((status) => {
|
|
this.preview = status
|
|
})
|
|
}
|
|
} else if (this.preview.id !== targetId) {
|
|
this.preview = find(statuses, { 'id': targetId })
|
|
}
|
|
},
|
|
replyLeave () {
|
|
this.showPreview = false
|
|
},
|
|
userProfileLink (id, name) {
|
|
return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)
|
|
}
|
|
},
|
|
watch: {
|
|
'highlight': function (id) {
|
|
if (this.status.id === id) {
|
|
let rect = this.$el.getBoundingClientRect()
|
|
if (rect.top < 100) {
|
|
window.scrollBy(0, rect.top - 200)
|
|
} else if (rect.bottom > window.innerHeight - 50) {
|
|
window.scrollBy(0, rect.bottom - window.innerHeight + 50)
|
|
}
|
|
}
|
|
}
|
|
},
|
|
filters: {
|
|
capitalize: function (str) {
|
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
|
}
|
|
}
|
|
}
|
|
|
|
export default Status
|