aboutsummaryrefslogtreecommitdiff
path: root/src/components/status
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/status')
-rw-r--r--src/components/status/status.js55
-rw-r--r--src/components/status/status.vue241
2 files changed, 250 insertions, 46 deletions
diff --git a/src/components/status/status.js b/src/components/status/status.js
index 87fff879..4f5093e1 100644
--- a/src/components/status/status.js
+++ b/src/components/status/status.js
@@ -4,13 +4,17 @@ 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 { filter } from 'lodash'
const Status = {
props: [
'statusoid',
'expandable',
'inConversation',
- 'focused'
+ 'focused',
+ 'highlight',
+ 'compact',
+ 'replies'
],
data: () => ({
replying: false,
@@ -19,6 +23,9 @@ const Status = {
userExpanded: false
}),
computed: {
+ muteWords () {
+ return this.$store.state.config.muteWords
+ },
hideAttachments () {
return (this.$store.state.config.hideAttachments && !this.inConversation) ||
(this.$store.state.config.hideAttachmentsInConv && this.inConversation)
@@ -35,12 +42,30 @@ const Status = {
loggedIn () {
return !!this.$store.state.users.currentUser
},
- muted () { return !this.unmuted && this.status.user.muted },
+ 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) },
isReply () { return !!this.status.in_reply_to_status_id },
borderColor () {
return {
borderBottomColor: this.$store.state.config.colors['base02']
}
+ },
+ 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
}
},
components: {
@@ -63,6 +88,10 @@ const Status = {
toggleReplying () {
this.replying = !this.replying
},
+ gotoOriginal (id) {
+ // only handled by conversation, not status_or_conversation
+ this.$emit('goto', id)
+ },
toggleExpanded () {
this.$emit('toggleExpanded')
},
@@ -71,6 +100,28 @@ const Status = {
},
toggleUserExpanded () {
this.userExpanded = !this.userExpanded
+ },
+ replyEnter (id, event) {
+ if (this.$store.state.config.hoverPreview) {
+ let rect = event.target.getBoundingClientRect()
+ this.$emit('preview', Number(id), rect.left + 20, rect.top + 20 + window.pageYOffset)
+ }
+ },
+ replyLeave () {
+ this.$emit('preview', 0, 0, 0)
+ }
+ },
+ watch: {
+ 'highlight': function (id) {
+ id = Number(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)
+ }
+ }
}
}
}
diff --git a/src/components/status/status.vue b/src/components/status/status.vue
index 62a55505..e582a80d 100644
--- a/src/components/status/status.vue
+++ b/src/components/status/status.vue
@@ -1,9 +1,25 @@
<template>
- <div class="status-el base00-background base03-border" v-if="!status.deleted" v-bind:class="[{ 'base01-background': focused }, { 'status-conversation': inConversation }]" >
+ <div class="status-el base00-background" v-if="compact">
+ <div @click.prevent="linkClicked" class="status-content" v-html="status.statusnet_html"></div>
+ <div v-if="loggedIn">
+ <div class='status-actions'>
+ <div>
+ <a href="#" v-on:click.prevent="toggleReplying">
+ <i class="fa icon-reply" :class="{'icon-reply-active': replying}"></i>
+ </a>
+ </div>
+ <retweet-button :status=status></retweet-button>
+ <favorite-button :status=status></favorite-button>
+ </div>
+ </div>
+ <post-status-form class="reply-body" :reply-to="status.id" :attentions="status.attentions" :repliedUser="status.user" v-on:posted="toggleReplying" v-if="replying"/>
+ </div>
+ <div class="status-el base00-background base03-border" v-else-if="!status.deleted" v-bind:class="[{ 'base01-background': isFocused }, { 'status-conversation': inConversation }]" >
<template v-if="muted">
<div class="media status container muted">
<small><router-link :to="{ name: 'user-profile', params: { id: status.user.id } }">{{status.user.screen_name}}</router-link></small>
- <a href="#" class="unmute" @click.prevent="toggleMute"><i class="icon-eye-off"></i></a>
+ <small class="muteWords">{{muteWordHits.join(', ')}}</small>
+ <a href="#" class="unmute" @click.prevent="toggleMute"><i class="fa icon-eye-off"></i></a>
</div>
</template>
<template v-if="!muted">
@@ -12,13 +28,14 @@
<i class='fa icon-retweet retweeted'></i>
</div>
<div class="media-body">
- Retweeted by {{retweeter}}
+ Repeated by <a :href="statusoid.user.statusnet_profile_url" style="font-weight: bold;" :title="'@'+statusoid.user.screen_name">{{retweeter}}</a>
</div>
</div>
<div class="media status container">
<div class="media-left">
<a :href="status.user.statusnet_profile_url">
- <img @click.prevent="toggleUserExpanded" class='avatar' :src="status.user.profile_image_url_original">
+ <img @click.prevent="toggleUserExpanded" :class="{retweeted: retweet}" class='avatar' :src="status.user.profile_image_url_original">
+ <img v-if="retweet" class='avatar-retweeter' :src="statusoid.user.profile_image_url_original"></img>
</a>
</div>
<div class="media-body">
@@ -26,40 +43,45 @@
<user-card-content :user="status.user"></user-card-content>
</div>
<div class="user-content">
- <h4 class="media-heading">
- {{status.user.name}}
- <small><router-link :to="{ name: 'user-profile', params: { id: status.user.id } }">{{status.user.screen_name}}</router-link></small>
- <small v-if="status.in_reply_to_screen_name"> &gt;
- <router-link :to="{ name: 'user-profile', params: { id: status.in_reply_to_user_id } }">
- {{status.in_reply_to_screen_name}}
- </router-link>
- </small>
- <template v-if="isReply">
+ <div class="media-heading">
+ <div class="name-and-links">
+ <h4 class="user-name">{{status.user.name}}</h4>
+ <div class="links">
+ <h4>
+ <small><router-link :to="{ name: 'user-profile', params: { id: status.user.id } }">{{status.user.screen_name}}</router-link></small>
+ <small v-if="status.in_reply_to_screen_name"> &gt;
+ <router-link :to="{ name: 'user-profile', params: { id: status.in_reply_to_user_id } }">
+ {{status.in_reply_to_screen_name}}
+ </router-link>
+ </small>
+ <template v-if="isReply && !expandable">
+ <small>
+ <a href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)"><i class="icon-reply" @mouseenter="replyEnter(status.in_reply_to_status_id, $event)" @mouseout="replyLeave()"></i></a>
+ </small>
+ </template>
+ -
<small>
- <router-link :to="{ name: 'conversation', params: { id: status.in_reply_to_status_id } }">
- <i class="icon-reply"></i>
- </router-link>
+ <router-link :to="{ name: 'conversation', params: { id: status.id } }">
+ <timeago :since="status.created_at" :auto-update="60"></timeago>
+ </router-link>
+ </small>
+ </h4>
+ </div>
+ <h4 class="replies" v-if="inConversation">
+ <small v-if="replies.length">Replies:</small>
+ <small v-for="reply in replies">
+ <a href="#" @click.prevent="gotoOriginal(reply.id)" @mouseenter="replyEnter(reply.id, $event)" @mouseout="replyLeave()">{{reply.name}}&nbsp;</a>
</small>
- </template>
- -
- <small>
- <router-link :to="{ name: 'conversation', params: { id: status.id } }">
- <timeago :since="status.created_at" :auto-update="60"></timeago>
- </router-link>
- </small>
- <template v-if="expandable">
- -
- <small>
- <a href="#" @click.prevent="toggleExpanded" ><i class="icon-plus-squared"></i></a>
- </small>
- <small v-if="status.user.muted">
- <a href="#" @click.prevent="toggleMute" ><i class="icon-eye-off"></i></a>
- </small>
- </template>
- <small v-if="!status.is_local" class="source_url">
- <a :href="status.external_url" target="_blank" ><i class="icon-binoculars"></i></a>
- </small>
- </h4>
+ </h4>
+ </div>
+ <div class="heading-icons">
+ <a href="#" @click.prevent="toggleMute" v-if="unmuted"><i class="fa icon-eye-off"></i></a>
+ <a :href="status.external_url" target="_blank" v-if="!status.is_local" class="source_url"><i class="fa icon-binoculars"></i></a>
+ <template v-if="expandable">
+ <a href="#" @click.prevent="toggleExpanded" class="expand"><i class="fa icon-plus-squared"></i></a>
+ </template>
+ </div>
+ </div>
<div @click.prevent="linkClicked" class="status-content" v-html="status.statusnet_html"></div>
@@ -95,24 +117,65 @@
<style lang="scss">
@import '../../_variables.scss';
+
+ status-text-container {
+ display: block;
+}
+
.status-el {
hyphens: auto;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
border-left-width: 0px;
+ line-height: 18px;
+
+ .notify {
+ .avatar {
+ border-width: 3px;
+ border-style: solid;
+ }
+ }
+
+ .media-body {
+ flex: 1;
+ padding-left: 0.5em;
+ }
+
.user-content {
+
min-height: 52px;
padding-top: 1px;
}
+ .media-heading {
+ display: flex;
+ min-height: 1.4em;
+ margin-bottom: 0.3em;
+
+ small {
+ font-weight: lighter;
+ }
+ h4 {
+ margin-right: 0.4em;
+ }
+ .name-and-links {
+ flex: 1 0;
+ display: flex;
+ flex-wrap: wrap;
+ }
+ .replies {
+ flex-basis: 100%;
+ }
+ }
+
.source_url {
- float: right;
+
}
- .greentext {
- color: green;
+ .expand {
+ margin-right: -0.3em;
}
a {
@@ -129,6 +192,34 @@
margin-top: 0.2em;
margin-bottom: 0.5em;
}
+
+ .media-left {
+ img {
+ margin-top: 0.2em;
+ float: right;
+ margin-right: 0.3em;
+ border-radius: 5px;
+ }
+ }
+
+ .retweet-info {
+ padding: 0.7em 0 0 0.6em;
+
+ .media-left {
+ display: flex;
+
+ i {
+ align-self: center;
+ text-align: right;
+ flex: 1;
+ padding-right: 0.3em;
+ }
+ }
+ }
+ }
+
+ .greentext {
+ color: green;
}
.status-conversation {
@@ -136,7 +227,14 @@
}
.status-actions {
- padding-top: 5px;
+ padding-top: 0.15em;
+ width: 100%;
+ display: flex;
+
+ div, favorite-button {
+ max-width: 6em;
+ flex: 1;
+ }
}
.icon-reply:hover {
@@ -148,7 +246,23 @@
}
.status .avatar {
- width: 48px;
+ width: 48px;
+ height: 48px;
+
+ &.retweeted {
+ width: 40px;
+ height: 40px;
+ margin-right: 8px;
+ margin-bottom: 8px;
+ }
+ }
+
+ .status img.avatar-retweeter {
+ width: 24px;
+ height: 24px;
+ position: absolute;
+ margin-left: 24px;
+ margin-top: 24px;
}
.status.compact .avatar {
@@ -156,14 +270,22 @@
}
.status {
- padding: 0.65em 0.7em 0.8em 0.8em;
+ padding: 0.4em 0.7em 0.45em 0.7em;
border-bottom: 1px solid;
border-bottom-color: inherit;
border-left: 4px rgba(255, 48, 16, 0.65);
border-left-style: inherit;
}
- .muted button {
- margin-left: auto;
+
+ .muted {
+ padding: 0.1em 0.4em 0.1em 0.8em;
+ button {
+ margin-left: auto;
+ }
+
+ .muteWords {
+ margin-left: 10px;
+ }
}
a.unmute {
@@ -188,4 +310,35 @@
flex: 1;
}
+ @media all and (max-width: 960px) {
+ .status-el {
+ .name-and-links {
+ margin-left: -0.25em;
+ }
+ }
+ .status {
+ max-width: 100%;
+ }
+
+ .status .avatar {
+ width: 40px;
+ height: 40px;
+
+ &.retweeted {
+ width: 34px;
+ height: 34px;
+ margin-right: 8px;
+ margin-bottom: 8px;
+ }
+ }
+
+ .status img.avatar-retweeter {
+ width: 22px;
+ height: 22px;
+ position: absolute;
+ margin-left: 18px;
+ margin-top: 18px;
+ }
+ }
+
</style>