Merge branch 'from/develop/tusooa/tree-threading' into 'develop'
Add the option to display threads as trees See merge request pleroma/pleroma-fe!1407
This commit is contained in:
commit
e34d71fc1f
21 changed files with 1168 additions and 95 deletions
|
@ -35,7 +35,10 @@ import {
|
|||
faStar,
|
||||
faEyeSlash,
|
||||
faEye,
|
||||
faThumbtack
|
||||
faThumbtack,
|
||||
faChevronUp,
|
||||
faChevronDown,
|
||||
faAngleDoubleRight
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
|
@ -52,9 +55,47 @@ library.add(
|
|||
faEllipsisH,
|
||||
faEyeSlash,
|
||||
faEye,
|
||||
faThumbtack
|
||||
faThumbtack,
|
||||
faChevronUp,
|
||||
faChevronDown,
|
||||
faAngleDoubleRight
|
||||
)
|
||||
|
||||
const camelCase = name => name.charAt(0).toUpperCase() + name.slice(1)
|
||||
|
||||
const controlledOrUncontrolledGetters = list => list.reduce((res, name) => {
|
||||
const camelized = camelCase(name)
|
||||
const toggle = `controlledToggle${camelized}`
|
||||
const controlledName = `controlled${camelized}`
|
||||
const uncontrolledName = `uncontrolled${camelized}`
|
||||
res[name] = function () {
|
||||
return this[toggle] ? this[controlledName] : this[uncontrolledName]
|
||||
}
|
||||
return res
|
||||
}, {})
|
||||
|
||||
const controlledOrUncontrolledToggle = (obj, name) => {
|
||||
const camelized = camelCase(name)
|
||||
const toggle = `controlledToggle${camelized}`
|
||||
const uncontrolledName = `uncontrolled${camelized}`
|
||||
if (obj[toggle]) {
|
||||
obj[toggle]()
|
||||
} else {
|
||||
obj[uncontrolledName] = !obj[uncontrolledName]
|
||||
}
|
||||
}
|
||||
|
||||
const controlledOrUncontrolledSet = (obj, name, val) => {
|
||||
const camelized = camelCase(name)
|
||||
const set = `controlledSet${camelized}`
|
||||
const uncontrolledName = `uncontrolled${camelized}`
|
||||
if (obj[set]) {
|
||||
obj[set](val)
|
||||
} else {
|
||||
obj[uncontrolledName] = val
|
||||
}
|
||||
}
|
||||
|
||||
const Status = {
|
||||
name: 'Status',
|
||||
components: {
|
||||
|
@ -89,20 +130,38 @@ const Status = {
|
|||
'inlineExpanded',
|
||||
'showPinned',
|
||||
'inProfile',
|
||||
'profileUserId'
|
||||
'profileUserId',
|
||||
|
||||
'simpleTree',
|
||||
'controlledThreadDisplayStatus',
|
||||
'controlledToggleThreadDisplay',
|
||||
'showOtherRepliesAsButton',
|
||||
|
||||
'controlledShowingTall',
|
||||
'controlledToggleShowingTall',
|
||||
'controlledExpandingSubject',
|
||||
'controlledToggleExpandingSubject',
|
||||
'controlledShowingLongSubject',
|
||||
'controlledToggleShowingLongSubject',
|
||||
'controlledReplying',
|
||||
'controlledToggleReplying',
|
||||
'controlledMediaPlaying',
|
||||
'controlledSetMediaPlaying',
|
||||
'dive'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
replying: false,
|
||||
uncontrolledReplying: false,
|
||||
unmuted: false,
|
||||
userExpanded: false,
|
||||
mediaPlaying: [],
|
||||
uncontrolledMediaPlaying: [],
|
||||
suspendable: true,
|
||||
error: null,
|
||||
headTailLinks: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...controlledOrUncontrolledGetters(['replying', 'mediaPlaying']),
|
||||
muteWords () {
|
||||
return this.mergedConfig.muteWords
|
||||
},
|
||||
|
@ -318,6 +377,12 @@ const Status = {
|
|||
},
|
||||
isSuspendable () {
|
||||
return !this.replying && this.mediaPlaying.length === 0
|
||||
},
|
||||
inThreadForest () {
|
||||
return !!this.controlledThreadDisplayStatus
|
||||
},
|
||||
threadShowing () {
|
||||
return this.controlledThreadDisplayStatus === 'showing'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -340,7 +405,7 @@ const Status = {
|
|||
this.error = undefined
|
||||
},
|
||||
toggleReplying () {
|
||||
this.replying = !this.replying
|
||||
controlledOrUncontrolledToggle(this, 'replying')
|
||||
},
|
||||
gotoOriginal (id) {
|
||||
if (this.inConversation) {
|
||||
|
@ -360,17 +425,19 @@ const Status = {
|
|||
return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)
|
||||
},
|
||||
addMediaPlaying (id) {
|
||||
this.mediaPlaying.push(id)
|
||||
controlledOrUncontrolledSet(this, 'mediaPlaying', this.mediaPlaying.concat(id))
|
||||
},
|
||||
removeMediaPlaying (id) {
|
||||
this.mediaPlaying = this.mediaPlaying.filter(mediaId => mediaId !== id)
|
||||
controlledOrUncontrolledSet(this, 'mediaPlaying', this.mediaPlaying.filter(mediaId => mediaId !== id))
|
||||
},
|
||||
setHeadTailLinks (headTailLinks) {
|
||||
this.headTailLinks = headTailLinks
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'highlight': function (id) {
|
||||
},
|
||||
toggleThreadDisplay () {
|
||||
this.controlledToggleThreadDisplay()
|
||||
},
|
||||
scrollIfHighlighted (highlightId) {
|
||||
const id = highlightId
|
||||
if (this.status.id === id) {
|
||||
let rect = this.$el.getBoundingClientRect()
|
||||
if (rect.top < 100) {
|
||||
|
@ -384,6 +451,11 @@ const Status = {
|
|||
window.scrollBy(0, rect.bottom - window.innerHeight + 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'highlight': function (id) {
|
||||
this.scrollIfHighlighted(id)
|
||||
},
|
||||
'status.repeat_num': function (num) {
|
||||
// refetch repeats when repeat_num is changed in any way
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
@import '../../_variables.scss';
|
||||
|
||||
$status-margin: 0.75em;
|
||||
|
||||
.Status {
|
||||
min-width: 0;
|
||||
white-space: normal;
|
||||
|
@ -28,15 +26,8 @@ $status-margin: 0.75em;
|
|||
--icon: var(--selectedPostIcon, $fallback--icon);
|
||||
}
|
||||
|
||||
&.-conversation {
|
||||
border-left-width: 4px;
|
||||
border-left-style: solid;
|
||||
border-left-color: $fallback--cRed;
|
||||
border-left-color: var(--cRed, $fallback--cRed);
|
||||
}
|
||||
|
||||
.gravestone {
|
||||
padding: $status-margin;
|
||||
padding: var(--status-margin, $status-margin);
|
||||
color: $fallback--faint;
|
||||
color: var(--faint, $fallback--faint);
|
||||
display: flex;
|
||||
|
@ -49,7 +40,7 @@ $status-margin: 0.75em;
|
|||
|
||||
.status-container {
|
||||
display: flex;
|
||||
padding: $status-margin;
|
||||
padding: var(--status-margin, $status-margin);
|
||||
|
||||
&.-repeat {
|
||||
padding-top: 0;
|
||||
|
@ -57,7 +48,7 @@ $status-margin: 0.75em;
|
|||
}
|
||||
|
||||
.pin {
|
||||
padding: $status-margin $status-margin 0;
|
||||
padding: var(--status-margin, $status-margin) var(--status-margin, $status-margin) 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
@ -73,7 +64,7 @@ $status-margin: 0.75em;
|
|||
}
|
||||
|
||||
.left-side {
|
||||
margin-right: $status-margin;
|
||||
margin-right: var(--status-margin, $status-margin);
|
||||
}
|
||||
|
||||
.right-side {
|
||||
|
@ -82,7 +73,7 @@ $status-margin: 0.75em;
|
|||
}
|
||||
|
||||
.usercard {
|
||||
margin-bottom: $status-margin;
|
||||
margin-bottom: var(--status-margin, $status-margin);
|
||||
}
|
||||
|
||||
.status-username {
|
||||
|
@ -248,7 +239,7 @@ $status-margin: 0.75em;
|
|||
}
|
||||
|
||||
.repeat-info {
|
||||
padding: 0.4em $status-margin;
|
||||
padding: 0.4em var(--status-margin, $status-margin);
|
||||
|
||||
.repeat-icon {
|
||||
color: $fallback--cGreen;
|
||||
|
@ -294,7 +285,7 @@ $status-margin: 0.75em;
|
|||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
margin-top: $status-margin;
|
||||
margin-top: var(--status-margin, $status-margin);
|
||||
|
||||
> * {
|
||||
max-width: 4em;
|
||||
|
@ -362,7 +353,7 @@ $status-margin: 0.75em;
|
|||
}
|
||||
|
||||
.favs-repeated-users {
|
||||
margin-top: $status-margin;
|
||||
margin-top: var(--status-margin, $status-margin);
|
||||
}
|
||||
|
||||
.stats {
|
||||
|
@ -389,7 +380,7 @@ $status-margin: 0.75em;
|
|||
}
|
||||
|
||||
.stat-count {
|
||||
margin-right: $status-margin;
|
||||
margin-right: var(--status-margin, $status-margin);
|
||||
user-select: none;
|
||||
|
||||
.stat-title {
|
||||
|
|
|
@ -221,6 +221,31 @@
|
|||
class="fa-scale-110"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
v-if="inThreadForest && replies && replies.length && !simpleTree"
|
||||
class="button-unstyled"
|
||||
:title="threadShowing ? $t('status.thread_hide') : $t('status.thread_show')"
|
||||
:aria-expanded="threadShowing ? 'true' : 'false'"
|
||||
@click.prevent="toggleThreadDisplay"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
class="fa-scale-110"
|
||||
:icon="threadShowing ? 'chevron-up' : 'chevron-down'"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
v-if="dive && !simpleTree"
|
||||
class="button-unstyled"
|
||||
:title="$t('status.show_only_conversation_under_this')"
|
||||
@click.prevent="dive"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
class="fa-scale-110"
|
||||
:icon="'angle-double-right'"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
|
@ -308,6 +333,12 @@
|
|||
:no-heading="noHeading"
|
||||
:highlight="highlight"
|
||||
:focused="isFocused"
|
||||
:controlled-showing-tall="controlledShowingTall"
|
||||
:controlled-expanding-subject="controlledExpandingSubject"
|
||||
:controlled-showing-long-subject="controlledShowingLongSubject"
|
||||
:controlled-toggle-showing-tall="controlledToggleShowingTall"
|
||||
:controlled-toggle-expanding-subject="controlledToggleExpandingSubject"
|
||||
:controlled-toggle-showing-long-subject="controlledToggleShowingLongSubject"
|
||||
@mediaplay="addMediaPlaying($event)"
|
||||
@mediapause="removeMediaPlaying($event)"
|
||||
@parseReady="setHeadTailLinks"
|
||||
|
@ -317,7 +348,20 @@
|
|||
v-if="inConversation && !isPreview && replies && replies.length"
|
||||
class="replies"
|
||||
>
|
||||
<span class="faint">{{ $t('status.replies_list') }}</span>
|
||||
<button
|
||||
v-if="showOtherRepliesAsButton && replies.length > 1"
|
||||
class="button-unstyled -link faint"
|
||||
:title="$tc('status.ancestor_follow', replies.length - 1, { numReplies: replies.length - 1 })"
|
||||
@click.prevent="dive"
|
||||
>
|
||||
{{ $tc('status.replies_list_with_others', replies.length - 1, { numReplies: replies.length - 1 }) }}
|
||||
</button>
|
||||
<span
|
||||
v-else
|
||||
class="faint"
|
||||
>
|
||||
{{ $t('status.replies_list') }}
|
||||
</span>
|
||||
<StatusPopover
|
||||
v-for="reply in replies"
|
||||
:key="reply.id"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue