aboutsummaryrefslogtreecommitdiff
path: root/src/components/status
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/status')
-rw-r--r--src/components/status/status.js42
-rw-r--r--src/components/status/status.vue79
2 files changed, 58 insertions, 63 deletions
diff --git a/src/components/status/status.js b/src/components/status/status.js
index c718fe9f..0273a5be 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -4,14 +4,14 @@ 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 UserAvatar from '../user_avatar/user_avatar.vue'
import Gallery from '../gallery/gallery.vue'
import LinkPreview from '../link-preview/link-preview.vue'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
import fileType from 'src/services/file_type/file_type.service'
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
-import { mentionMatchesUrl } from 'src/services/mention_matcher/mention_matcher.js'
-import { filter, find } from 'lodash'
+import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js'
+import { filter, find, unescape } from 'lodash'
const Status = {
name: 'Status',
@@ -36,6 +36,7 @@ const Status = {
preview: null,
showPreview: false,
showingTall: this.inConversation && this.focused,
+ showingLongSubject: false,
expandingSubject: typeof this.$store.state.config.collapseMessageWithSubject === 'undefined'
? !this.$store.state.instance.collapseMessageWithSubject
: !this.$store.state.config.collapseMessageWithSubject,
@@ -89,6 +90,7 @@ const Status = {
retweet () { return !!this.statusoid.retweeted_status },
retweeter () { return this.statusoid.user.name || this.statusoid.user.screen_name },
retweeterHtml () { return this.statusoid.user.name_html },
+ retweeterProfileLink () { return this.generateUserProfileLink(this.statusoid.user.id, this.statusoid.user.screen_name) },
status () {
if (this.retweet) {
return this.statusoid.retweeted_status
@@ -108,6 +110,14 @@ const Status = {
return hits
},
muted () { return !this.unmuted && (this.status.user.muted || this.muteWordHits.length > 0) },
+ hideFilteredStatuses () {
+ return typeof this.$store.state.config.hideFilteredStatuses === 'undefined'
+ ? this.$store.state.instance.hideFilteredStatuses
+ : this.$store.state.config.hideFilteredStatuses
+ },
+ hideStatus () {
+ return (this.hideReply || this.deleted) || (this.muted && this.hideFilteredStatuses)
+ },
isFocused () {
// retweet or root of an expanded conversation
if (this.focused) {
@@ -129,6 +139,9 @@ const Status = {
const lengthScore = this.status.statusnet_html.split(/<p|<br/).length + this.status.text.length / 80
return lengthScore > 20
},
+ longSubject () {
+ return this.status.summary.length > 900
+ },
isReply () {
return !!(this.status.in_reply_to_status_id && this.status.in_reply_to_user_id)
},
@@ -196,14 +209,15 @@ const Status = {
},
replySubject () {
if (!this.status.summary) return ''
+ const decodedSummary = unescape(this.status.summary)
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)
+ const startsWithRe = decodedSummary.match(/^re[: ]/i)
if (behavior !== 'noop' && startsWithRe || behavior === 'masto') {
- return this.status.summary
+ return decodedSummary
} else if (behavior === 'email') {
- return 're: '.concat(this.status.summary)
+ return 're: '.concat(decodedSummary)
} else if (behavior === 'noop') {
return ''
}
@@ -244,7 +258,7 @@ const Status = {
DeleteButton,
PostStatusForm,
UserCardContent,
- StillImage,
+ UserAvatar,
Gallery,
LinkPreview
},
@@ -268,7 +282,7 @@ const Status = {
}
if (target.tagName === 'A') {
if (target.className.match(/mention/)) {
- const href = target.getAttribute('href')
+ const href = target.href
const attn = this.status.attentions.find(attn => mentionMatchesUrl(attn, href))
if (attn) {
event.stopPropagation()
@@ -278,6 +292,15 @@ const Status = {
return
}
}
+ if (target.className.match(/hashtag/)) {
+ // Extract tag name from link url
+ const tag = extractTagFromUrl(target.href)
+ if (tag) {
+ const link = this.generateTagLink(tag)
+ this.$router.push(link)
+ return
+ }
+ }
window.open(target.href, '_blank')
}
},
@@ -334,6 +357,9 @@ const Status = {
generateUserProfileLink (id, name) {
return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)
},
+ generateTagLink (tag) {
+ return `/tag/${tag}`
+ },
setMedia () {
const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments
return () => this.$store.dispatch('setMedia', attachments)
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 3e3e82bf..aae365d1 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -1,5 +1,5 @@
<template>
- <div class="status-el" v-if="!hideReply && !deleted" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]">
+ <div class="status-el" v-if="!hideStatus" :class="[{ 'status-el_focused': isFocused }, { 'status-conversation': inlineExpanded }]">
<template v-if="muted && !noReplyLinks">
<div class="media status container muted">
<small>
@@ -13,10 +13,12 @@
</template>
<template v-else>
<div v-if="retweet && !noHeading" :class="[repeaterClass, { highlighted: repeaterStyle }]" :style="[repeaterStyle]" class="media container retweet-info">
- <StillImage v-if="retweet" class='avatar' :class='{ "better-shadow": betterShadow }' :src="statusoid.user.profile_image_url_original"/>
+ <UserAvatar v-if="retweet" :betterShadow="betterShadow" :src="statusoid.user.profile_image_url_original"/>
<div class="media-body faint">
- <a v-if="retweeterHtml" :href="statusoid.user.statusnet_profile_url" class="user-name" :title="'@'+statusoid.user.screen_name" v-html="retweeterHtml"></a>
- <a v-else :href="statusoid.user.statusnet_profile_url" class="user-name" :title="'@'+statusoid.user.screen_name">{{retweeter}}</a>
+ <span class="user-name">
+ <router-link v-if="retweeterHtml" :to="retweeterProfileLink" v-html="retweeterHtml"/>
+ <router-link v-else :to="retweeterProfileLink">{{retweeter}}</router-link>
+ </span>
<i class='fa icon-retweet retweeted' :title="$t('tool_tip.repeat')"></i>
{{$t('timeline.repeated')}}
</div>
@@ -25,7 +27,7 @@
<div :class="[userClass, { highlighted: userStyle, 'is-retweet': retweet }]" :style="[ userStyle ]" class="media status">
<div v-if="!noHeading" class="media-left">
<router-link :to="userProfileLink" @click.stop.prevent.capture.native="toggleUserExpanded">
- <StillImage class='avatar' :class="{'avatar-compact': compact, 'better-shadow': betterShadow}" :src="status.user.profile_image_url_original"/>
+ <UserAvatar :compact="compact" :betterShadow="betterShadow" :src="status.user.profile_image_url_original"/>
</router-link>
</div>
<div class="status-body">
@@ -54,7 +56,7 @@
</div>
<h4 class="replies" v-if="inConversation && !noReplyLinks">
<small v-if="replies.length">Replies:</small>
- <small class="reply-link" v-for="reply in replies">
+ <small class="reply-link" v-bind:key="reply.id" v-for="reply in replies">
<a href="#" @click.prevent="gotoOriginal(reply.id)" @mouseenter="replyEnter(reply.id, $event)" @mouseout="replyLeave()">{{reply.name}}&nbsp;</a>
</small>
</h4>
@@ -85,7 +87,12 @@
</div>
</div>
- <div :class="{'tall-status': hideTallStatus}" class="status-content-wrapper">
+ <div class="status-content-wrapper" :class="{ 'tall-status': !showingLongSubject }" v-if="longSubject">
+ <a class="tall-status-hider" :class="{ 'tall-status-hider_focused': isFocused }" v-if="!showingLongSubject" href="#" @click.prevent="showingLongSubject=true">Show more</a>
+ <div @click.prevent="linkClicked" class="status-content media-body" v-html="status.statusnet_html"></div>
+ <a v-if="showingLongSubject" href="#" class="status-unhider" @click.prevent="showingLongSubject=false">Show less</a>
+ </div>
+ <div :class="{'tall-status': hideTallStatus}" class="status-content-wrapper" v-else>
<a class="tall-status-hider" :class="{ 'tall-status-hider_focused': isFocused }" v-if="hideTallStatus" href="#" @click.prevent="toggleShowMore">Show more</a>
<div @click.prevent="linkClicked" class="status-content media-body" v-html="status.statusnet_html" v-if="!hideSubjectStatus"></div>
<div @click.prevent="linkClicked" class="status-content media-body" v-html="status.summary_html" v-else></div>
@@ -93,7 +100,7 @@
<a v-if="showingMore" href="#" class="status-unhider" @click.prevent="toggleShowMore">Show less</a>
</div>
- <div v-if="status.attachments && !hideSubjectStatus" class="attachments media-body">
+ <div v-if="status.attachments && (!hideSubjectStatus || showingLongSubject)" class="attachments media-body">
<attachment
class="non-gallery"
v-for="attachment in nonGalleryAttachments"
@@ -413,7 +420,7 @@
padding: 0.4em 0.6em 0 0.6em;
margin: 0;
- .avatar {
+ .avatar.still-image {
border-radius: $fallback--avatarAltRadius;
border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
margin-left: 28px;
@@ -431,6 +438,8 @@
.user-name {
font-weight: bold;
+ overflow: hidden;
+ text-overflow: ellipsis;
img {
width: 14px;
@@ -497,46 +506,6 @@
color: var(--cBlue, $fallback--cBlue);
}
-.status .avatar-compact {
- width: 32px;
- height: 32px;
- box-shadow: var(--avatarStatusShadow);
- border-radius: $fallback--avatarAltRadius;
- border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius);
-
- &.better-shadow {
- box-shadow: var(--avatarStatusShadowInset);
- filter: var(--avatarStatusShadowFilter)
- }
-}
-
-.avatar.still-image {
- width: 48px;
- height: 48px;
- box-shadow: var(--avatarStatusShadow);
- border-radius: $fallback--avatarRadius;
- border-radius: var(--avatarRadius, $fallback--avatarRadius);
- overflow: hidden;
- position: relative;
-
- &.better-shadow {
- box-shadow: var(--avatarStatusShadowInset);
- filter: var(--avatarStatusShadowFilter)
- }
-
- img {
- width: 100%;
- height: 100%;
- }
-
- &.animated::before {
- display: none;
- }
-
- &.retweeted {
- }
-}
-
.status:hover .animated.avatar {
canvas {
display: none;
@@ -594,7 +563,7 @@ a.unmute {
@media all and (max-width: 800px) {
.status-el {
.retweet-info {
- .avatar {
+ .avatar.still-image {
margin-left: 20px;
}
}
@@ -603,14 +572,14 @@ a.unmute {
max-width: 100%;
}
- .status .avatar {
+ .status .avatar.still-image {
width: 40px;
height: 40px;
- }
- .status .avatar-compact {
- width: 32px;
- height: 32px;
+ &.avatar-compact {
+ width: 32px;
+ height: 32px;
+ }
}
}