Merge branch 'feature/reply-previews' into 'develop'
Reply previews in conversations See merge request !89
This commit is contained in:
commit
fc341aa40d
8 changed files with 80 additions and 11 deletions
|
@ -1,4 +1,4 @@
|
||||||
import { filter, sortBy } from 'lodash'
|
import { find, filter, sortBy } from 'lodash'
|
||||||
import { statusType } from '../../modules/statuses.js'
|
import { statusType } from '../../modules/statuses.js'
|
||||||
import Status from '../status/status.vue'
|
import Status from '../status/status.vue'
|
||||||
|
|
||||||
|
@ -10,7 +10,12 @@ const sortAndFilterConversation = (conversation) => {
|
||||||
const conversation = {
|
const conversation = {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
highlight: null
|
highlight: null,
|
||||||
|
preview: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
status: null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: [
|
props: [
|
||||||
|
@ -76,6 +81,15 @@ const conversation = {
|
||||||
},
|
},
|
||||||
setHighlight (id) {
|
setHighlight (id) {
|
||||||
this.highlight = Number(id)
|
this.highlight = Number(id)
|
||||||
|
},
|
||||||
|
setPreview (id, x, y) {
|
||||||
|
if (id) {
|
||||||
|
this.preview.x = x
|
||||||
|
this.preview.y = y
|
||||||
|
this.preview.status = find(this.conversation, { id: id })
|
||||||
|
} else {
|
||||||
|
this.preview.status = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,17 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="timeline">
|
<div class="timeline">
|
||||||
<status v-for="status in conversation" @goto="setHighlight" :key="status.id" :statusoid="status" :expandable='false' :focused="focused(status.id)" :inConversation='true' :highlight="highlight" :replies="getReplies(status.id)"></status>
|
<status v-for="status in conversation" @goto="setHighlight" :key="status.id" @preview="setPreview" :statusoid="status" :expandable='false' :focused="focused(status.id)" :inConversation='true' :highlight="highlight" :replies="getReplies(status.id)"></status>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="status-preview base00-background base03-border" :style="{ left: preview.x + 'px', top: preview.y + 'px'}" v-if="preview.status">
|
||||||
|
<img class="avatar" :src="preview.status.user.profile_image_url_original">
|
||||||
|
<div class="text">
|
||||||
|
<h4>
|
||||||
|
{{ preview.status.user.name }}
|
||||||
|
<small><a>{{ preview.status.user.screen_name}}</a></small>
|
||||||
|
</h4>
|
||||||
|
<div @click.prevent="linkClicked" class="status-content" v-html="preview.status.statusnet_html"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,4 +31,30 @@
|
||||||
border-bottom-style: solid;
|
border-bottom-style: solid;
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-preview {
|
||||||
|
position: absolute;
|
||||||
|
max-width: 35em;
|
||||||
|
padding: 0.5em;
|
||||||
|
display: flex;
|
||||||
|
border-color: inherit;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
|
||||||
|
.avatar {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
h4 {
|
||||||
|
margin-bottom: 0.4em;
|
||||||
|
small {
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
padding: 0 0.5em 0.5em 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -8,6 +8,7 @@ const settings = {
|
||||||
hideAttachmentsInConvLocal: this.$store.state.config.hideAttachmentsInConv,
|
hideAttachmentsInConvLocal: this.$store.state.config.hideAttachmentsInConv,
|
||||||
hideNsfwLocal: this.$store.state.config.hideNsfw,
|
hideNsfwLocal: this.$store.state.config.hideNsfw,
|
||||||
autoLoadLocal: this.$store.state.config.autoLoad,
|
autoLoadLocal: this.$store.state.config.autoLoad,
|
||||||
|
hoverPreviewLocal: this.$store.state.config.hoverPreview,
|
||||||
muteWordsString: this.$store.state.config.muteWords.join('\n')
|
muteWordsString: this.$store.state.config.muteWords.join('\n')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -27,6 +28,9 @@ const settings = {
|
||||||
autoLoadLocal (value) {
|
autoLoadLocal (value) {
|
||||||
this.$store.dispatch('setOption', { name: 'autoLoad', value })
|
this.$store.dispatch('setOption', { name: 'autoLoad', value })
|
||||||
},
|
},
|
||||||
|
hoverPreviewLocal (value) {
|
||||||
|
this.$store.dispatch('setOption', { name: 'hoverPreview', value })
|
||||||
|
},
|
||||||
muteWordsString (value) {
|
muteWordsString (value) {
|
||||||
value = filter(value.split('\n'), (word) => trim(word).length > 0)
|
value = filter(value.split('\n'), (word) => trim(word).length > 0)
|
||||||
this.$store.dispatch('setOption', { name: 'muteWords', value })
|
this.$store.dispatch('setOption', { name: 'muteWords', value })
|
||||||
|
|
|
@ -32,6 +32,10 @@
|
||||||
<input type="checkbox" id="autoLoad" v-model="autoLoadLocal">
|
<input type="checkbox" id="autoLoad" v-model="autoLoadLocal">
|
||||||
<label for="autoLoad">Enable automatic loading when scrolled to the bottom</label>
|
<label for="autoLoad">Enable automatic loading when scrolled to the bottom</label>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<input type="checkbox" id="hoverPreview" v-model="hoverPreviewLocal">
|
||||||
|
<label for="hoverPreview">Enable reply-link preview on mouse hover</label>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -100,6 +100,15 @@ const Status = {
|
||||||
},
|
},
|
||||||
toggleUserExpanded () {
|
toggleUserExpanded () {
|
||||||
this.userExpanded = !this.userExpanded
|
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: {
|
watch: {
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<div class="media status container 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>
|
<small><router-link :to="{ name: 'user-profile', params: { id: status.user.id } }">{{status.user.screen_name}}</router-link></small>
|
||||||
<small class="muteWords">{{muteWordHits.join(', ')}}</small>
|
<small class="muteWords">{{muteWordHits.join(', ')}}</small>
|
||||||
<a href="#" class="unmute" @click.prevent="toggleMute"><i class="icon-eye-off"></i></a>
|
<a href="#" class="unmute" @click.prevent="toggleMute"><i class="fa icon-eye-off"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="!muted">
|
<template v-if="!muted">
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
</small>
|
</small>
|
||||||
<template v-if="isReply && !expandable">
|
<template v-if="isReply && !expandable">
|
||||||
<small>
|
<small>
|
||||||
<a href="#" @click.prevent="gotoOriginal(status.in_reply_to_status_id)" ><i class="icon-reply"></i></a>
|
<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>
|
</small>
|
||||||
</template>
|
</template>
|
||||||
-
|
-
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
<h4 class="replies" v-if="inConversation">
|
<h4 class="replies" v-if="inConversation">
|
||||||
<small v-if="replies.length">Replies:</small>
|
<small v-if="replies.length">Replies:</small>
|
||||||
<small v-for="reply in replies">
|
<small v-for="reply in replies">
|
||||||
<a href="#" @click.prevent="gotoOriginal(reply.id)">{{reply.name}} </a>
|
<a href="#" @click.prevent="gotoOriginal(reply.id)" @mouseenter="replyEnter(reply.id, $event)" @mouseout="replyLeave()">{{reply.name}} </a>
|
||||||
</small>
|
</small>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
|
@ -178,10 +178,6 @@
|
||||||
margin-right: -0.3em;
|
margin-right: -0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.greentext {
|
|
||||||
color: green;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
@ -222,6 +218,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.greentext {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
.status-conversation {
|
.status-conversation {
|
||||||
border-left-style: solid;
|
border-left-style: solid;
|
||||||
}
|
}
|
||||||
|
@ -278,7 +278,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.muted {
|
.muted {
|
||||||
padding: 0.1em 0.7em 0.1em 0.8em;
|
padding: 0.1em 0.4em 0.1em 0.8em;
|
||||||
button {
|
button {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ const persistedStateOptions = {
|
||||||
'config.hideAttachmentsInConv',
|
'config.hideAttachmentsInConv',
|
||||||
'config.hideNsfw',
|
'config.hideNsfw',
|
||||||
'config.autoLoad',
|
'config.autoLoad',
|
||||||
|
'config.hoverPreview',
|
||||||
'config.muteWords',
|
'config.muteWords',
|
||||||
'statuses.notifications',
|
'statuses.notifications',
|
||||||
'users.users'
|
'users.users'
|
||||||
|
|
|
@ -8,6 +8,7 @@ const defaultState = {
|
||||||
hideAttachmentsInConv: false,
|
hideAttachmentsInConv: false,
|
||||||
hideNsfw: true,
|
hideNsfw: true,
|
||||||
autoLoad: true,
|
autoLoad: true,
|
||||||
|
hoverPreview: true,
|
||||||
muteWords: []
|
muteWords: []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue