Add simple tree style navigation

This commit is contained in:
Tusooa Zhu 2021-08-07 18:53:23 -04:00
parent 8c0deb905e
commit d9a9f97751
No known key found for this signature in database
GPG key ID: 7B467EDE43A08224
8 changed files with 113 additions and 20 deletions

View file

@ -80,7 +80,10 @@ const conversation = {
return this.$store.state.config.conversationDisplay return this.$store.state.config.conversationDisplay
}, },
isTreeView () { isTreeView () {
return this.displayStyle === 'tree' return this.displayStyle === 'tree' || this.displayStyle === 'simple_tree'
},
treeViewIsSimple () {
return this.displayStyle === 'simple_tree'
}, },
isLinearView () { isLinearView () {
return this.displayStyle === 'linear' return this.displayStyle === 'linear'
@ -297,6 +300,14 @@ const conversation = {
}, },
canDive () { canDive () {
return this.isTreeView && this.isExpanded return this.isTreeView && this.isExpanded
},
focused () {
return (id) => {
return (this.isExpanded) && id === this.highlight
}
},
maybeHighlight () {
return this.isExpanded ? this.highlight : null
} }
}, },
components: { components: {
@ -316,6 +327,9 @@ const conversation = {
expanded (value) { expanded (value) {
if (value) { if (value) {
this.fetchConversation() this.fetchConversation()
} else {
// if we collapse it, we should reset the dive
this._diven = false
} }
}, },
virtualHidden (value) { virtualHidden (value) {
@ -323,6 +337,9 @@ const conversation = {
'setVirtualHeight', 'setVirtualHeight',
{ statusId: this.statusId, height: `${this.$el.clientHeight}px` } { statusId: this.statusId, height: `${this.$el.clientHeight}px` }
) )
},
highlight (value, old) {
console.log('highlight:', old, ' => ', value)
} }
}, },
methods: { methods: {
@ -341,7 +358,8 @@ const conversation = {
'this.threadDisplayStatus ', this.threadDisplayStatus, 'this.threadDisplayStatus ', this.threadDisplayStatus,
'this.statusId', this.statusId) 'this.statusId', this.statusId)
if (this.threadDisplayStatus[this.statusId] === 'hidden') { if (this.threadDisplayStatus[this.statusId] === 'hidden') {
this.diveIntoStatus(parentOrSelf) this.diveIntoStatus(parentOrSelf, /* preventScroll */ true)
this.tryScrollTo(this.statusId)
} }
} }
}, },
@ -365,18 +383,16 @@ const conversation = {
getReplies (id) { getReplies (id) {
return this.replies[id] || [] return this.replies[id] || []
}, },
focused (id) { getHighlight () {
return (this.isExpanded) && id === this.statusId return this.isExpanded ? this.highlight : null
}, },
setHighlight (id) { setHighlight (id) {
console.log('setHighlight', id)
if (!id) return if (!id) return
this.highlight = id this.highlight = id
this.$store.dispatch('fetchFavsAndRepeats', id) this.$store.dispatch('fetchFavsAndRepeats', id)
this.$store.dispatch('fetchEmojiReactionsBy', id) this.$store.dispatch('fetchEmojiReactionsBy', id)
}, },
getHighlight () {
return this.isExpanded ? this.highlight : null
},
toggleExpanded () { toggleExpanded () {
this.expanded = !this.expanded this.expanded = !this.expanded
}, },
@ -420,14 +436,52 @@ const conversation = {
toggleStatusContentProperty (id, name) { toggleStatusContentProperty (id, name) {
this.setStatusContentProperty(id, name, !this.statusContentProperties[id][name]) this.setStatusContentProperty(id, name, !this.statusContentProperties[id][name])
}, },
diveIntoStatus (id) { leastShowingAncestor (id) {
let cur = id
let parent = this.parentOf(cur)
while (cur) {
// if the parent is showing it means cur is visible
if (this.threadDisplayStatus[parent] === 'showing') {
return cur
}
parent = this.parentOf(parent)
cur = this.parentOf(cur)
}
// nothing found, fall back to toplevel
return topLevel[0].id
},
diveIntoStatus (id, preventScroll) {
this.diveHistory = [...this.diveHistory, id] this.diveHistory = [...this.diveHistory, id]
if (!preventScroll) {
this.goToCurrent()
}
}, },
diveBack () { diveBack () {
const oldHighlight = this.highlight
this.diveHistory = [...this.diveHistory.slice(0, this.diveHistory.length - 1)] this.diveHistory = [...this.diveHistory.slice(0, this.diveHistory.length - 1)]
if (oldHighlight) {
this.tryScrollTo(this.leastShowingAncestor(oldHighlight))
}
}, },
undive () { undive () {
const oldHighlight = this.highlight
this.diveHistory = [] this.diveHistory = []
if (oldHighlight) {
this.tryScrollTo(this.leastShowingAncestor(oldHighlight))
} else {
this.goToCurrent()
}
},
tryScrollTo (id) {
if (this.isPage) {
// set statusId
this.$router.push({ name: 'conversation', params: { id } })
}
this.setHighlight(id)
},
goToCurrent () {
this.tryScrollTo(this.diveRoot || this.topLevel[0].id)
}, },
statusById (id) { statusById (id) {
return this.statusMap[id] return this.statusMap[id]

View file

@ -61,10 +61,11 @@
:focused="focused" :focused="focused"
:get-replies="getReplies" :get-replies="getReplies"
:get-highlight="getHighlight" :highlight="maybeHighlight"
:set-highlight="setHighlight" :set-highlight="setHighlight"
:toggle-expanded="toggleExpanded" :toggle-expanded="toggleExpanded"
:simple="treeViewIsSimple"
:toggle-thread-display="toggleThreadDisplay" :toggle-thread-display="toggleThreadDisplay"
:thread-display-status="threadDisplayStatus" :thread-display-status="threadDisplayStatus"
:show-thread-recursively="showThreadRecursively" :show-thread-recursively="showThreadRecursively"

View file

@ -20,7 +20,7 @@ const GeneralTab = {
value: mode, value: mode,
label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`) label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`)
})), })),
conversationDisplayOptions: ['tree', 'linear'].map(mode => ({ conversationDisplayOptions: ['tree', 'simple_tree', 'linear'].map(mode => ({
key: mode, key: mode,
value: mode, value: mode,
label: this.$t(`settings.conversation_display_${mode}`) label: this.$t(`settings.conversation_display_${mode}`)

View file

@ -97,6 +97,7 @@ const Status = {
'inProfile', 'inProfile',
'profileUserId', 'profileUserId',
'simpleTree',
'controlledThreadDisplayStatus', 'controlledThreadDisplayStatus',
'controlledToggleThreadDisplay', 'controlledToggleThreadDisplay',
@ -379,10 +380,9 @@ const Status = {
}, },
toggleThreadDisplay () { toggleThreadDisplay () {
this.controlledToggleThreadDisplay() this.controlledToggleThreadDisplay()
}
}, },
watch: { scrollIfHighlighted (highlightId) {
'highlight': function (id) { const id = highlightId
if (this.status.id === id) { if (this.status.id === id) {
let rect = this.$el.getBoundingClientRect() let rect = this.$el.getBoundingClientRect()
if (rect.top < 100) { if (rect.top < 100) {
@ -396,6 +396,14 @@ const Status = {
window.scrollBy(0, rect.bottom - window.innerHeight + 50) window.scrollBy(0, rect.bottom - window.innerHeight + 50)
} }
} }
}
},
mounted () {
this.scrollIfHighlighted(this.highlight)
},
watch: {
'highlight': function (id) {
this.scrollIfHighlighted(id)
}, },
'status.repeat_num': function (num) { 'status.repeat_num': function (num) {
// refetch repeats when repeat_num is changed in any way // refetch repeats when repeat_num is changed in any way

View file

@ -220,7 +220,7 @@
/> />
</button> </button>
<button <button
v-if="inThreadForest && replies && replies.length && toggleThreadDisplay" v-if="inThreadForest && replies && replies.length && !simpleTree"
class="button-unstyled" class="button-unstyled"
:title="threadShowing ? $t('status.thread_hide') : $t('status.thread_show')" :title="threadShowing ? $t('status.thread_hide') : $t('status.thread_show')"
:aria-expanded="threadShowing ? 'true' : 'false'" :aria-expanded="threadShowing ? 'true' : 'false'"
@ -233,7 +233,7 @@
/> />
</button> </button>
<button <button
v-if="dive" v-if="dive && !simpleTree"
class="button-unstyled" class="button-unstyled"
:title="$t('status.show_only_conversation_under_this')" :title="$t('status.show_only_conversation_under_this')"
@click.prevent="dive" @click.prevent="dive"

View file

@ -1,5 +1,16 @@
import Status from '../status/status.vue' import Status from '../status/status.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
faAngleDoubleDown,
faAngleDoubleRight
} from '@fortawesome/free-solid-svg-icons'
library.add(
faAngleDoubleDown,
faAngleDoubleRight
)
// const debug = console.log // const debug = console.log
const debug = () => {} const debug = () => {}
@ -19,11 +30,12 @@ const ThreadTree = {
profileUserId: String, profileUserId: String,
focused: Function, focused: Function,
getHighlight: Function, highlight: String,
getReplies: Function, getReplies: Function,
setHighlight: Function, setHighlight: Function,
toggleExpanded: Function, toggleExpanded: Function,
simple: Boolean,
// to control display of the whole thread forest // to control display of the whole thread forest
toggleThreadDisplay: Function, toggleThreadDisplay: Function,
threadDisplayStatus: Object, threadDisplayStatus: Object,

View file

@ -9,12 +9,13 @@
:show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]" :show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
:focused="focused(status.id)" :focused="focused(status.id)"
:in-conversation="isExpanded" :in-conversation="isExpanded"
:highlight="getHighlight()" :highlight="highlight"
:replies="getReplies(status.id)" :replies="getReplies(status.id)"
:in-profile="inProfile" :in-profile="inProfile"
:profile-user-id="profileUserId" :profile-user-id="profileUserId"
class="conversation-status conversation-status-treeview status-fadein panel-body" class="conversation-status conversation-status-treeview status-fadein panel-body"
:simple-tree="simple"
:controlled-thread-display-status="threadDisplayStatus[status.id]" :controlled-thread-display-status="threadDisplayStatus[status.id]"
:controlled-toggle-thread-display="() => toggleThreadDisplay(status.id)" :controlled-toggle-thread-display="() => toggleThreadDisplay(status.id)"
@ -49,10 +50,11 @@
:focused="focused" :focused="focused"
:get-replies="getReplies" :get-replies="getReplies"
:get-highlight="getHighlight" :highlight="highlight"
:set-highlight="setHighlight" :set-highlight="setHighlight"
:toggle-expanded="toggleExpanded" :toggle-expanded="toggleExpanded"
:simple="simple"
:toggle-thread-display="toggleThreadDisplay" :toggle-thread-display="toggleThreadDisplay"
:thread-display-status="threadDisplayStatus" :thread-display-status="threadDisplayStatus"
:show-thread-recursively="showThreadRecursively" :show-thread-recursively="showThreadRecursively"
@ -69,6 +71,22 @@
class="thread-tree-replies thread-tree-replies-hidden" class="thread-tree-replies thread-tree-replies-hidden"
> >
<i18n <i18n
v-if="simple"
tag="button"
path="status.thread_follow_with_icon"
class="button-unstyled -link thread-tree-show-replies-button"
@click.prevent="dive(status.id)"
>
<FAIcon
place="icon"
icon="angle-double-right"
/>
<span place="text">
{{ $tc('status.thread_follow', totalReplyCount[status.id], { numStatus: totalReplyCount[status.id] }) }}
</span>
</i18n>
<i18n
v-else
tag="button" tag="button"
path="status.thread_show_full_with_icon" path="status.thread_show_full_with_icon"
class="button-unstyled -link thread-tree-show-replies-button" class="button-unstyled -link thread-tree-show-replies-button"

View file

@ -53,7 +53,7 @@ const defaultState = {
theme: 'pleroma-dark', theme: 'pleroma-dark',
virtualScrolling: true, virtualScrolling: true,
sensitiveByDefault: false, sensitiveByDefault: false,
conversationDisplay: 'tree', conversationDisplay: 'simple_tree',
// Nasty stuff // Nasty stuff
customEmoji: [], customEmoji: [],