commit: e560fbc9352f9f8754451f38c5e3ecef6da96686
parent 4adffb483579108c0bfe7593157e9bed3571903f
Author: Tusooa Zhu <tusooa@kazv.moe>
Date: Tue, 10 Aug 2021 23:58:27 -0400
Implement Misskey-style tree view
Now the tree will be always rooted at the highlighted status, and
all its ancestors shown linearly on the top.
Enhancement: If an ancestor has more
than one reply (i.e. it has a child that is not on current status's
ancestor chain), we are given a link to root the thread at that status.
Diffstat:
2 files changed, 124 insertions(+), 55 deletions(-)
diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js
@@ -55,7 +55,7 @@ const conversation = {
expanded: false,
threadDisplayStatusObject: {}, // id => 'showing' | 'hidden'
statusContentPropertiesObject: {},
- diveHistory: []
+ inlineDivePosition: null
}
},
props: [
@@ -231,7 +231,10 @@ const conversation = {
return this.topLevel
},
diveRoot () {
- return this.diveHistory[this.diveHistory.length - 1]
+ (() => {})(this.conversation)
+ const statusId = this.inlineDivePosition || this.statusId
+ const isTopLevel = !this.parentOf(statusId)
+ return isTopLevel ? null : statusId
},
diveDepth () {
return this.canDive && this.diveRoot ? this.depths[this.diveRoot] : 0
@@ -332,7 +335,6 @@ const conversation = {
this.fetchConversation()
} else {
// if we collapse it, we should reset the dive
- this._diven = false
this.undive()
}
},
@@ -348,19 +350,6 @@ const conversation = {
if (!this.isExpanded) {
return
}
-
- if (!this._diven) {
- if (!this.threadDisplayStatus[this.statusId]) {
- return
- }
- this._diven = true
- const parentOrSelf = this.parentOrSelf(this.originalStatusId)
- // If current status is not visible
- if (this.threadDisplayStatus[parentOrSelf] === 'hidden') {
- this.diveIntoStatus(parentOrSelf, /* preventScroll */ true)
- this.tryScrollTo(this.statusId)
- }
- }
},
fetchConversation () {
if (this.status) {
@@ -449,26 +438,15 @@ const conversation = {
return this.topLevel[0] ? this.topLevel[0].id : undefined
},
diveIntoStatus (id, preventScroll) {
- this.diveHistory = [...this.diveHistory, id]
- if (!preventScroll) {
- this.goToCurrent()
- }
+ this.tryScrollTo(id)
},
- diveBack () {
- const oldHighlight = this.highlight
- this.diveHistory = [...this.diveHistory.slice(0, this.diveHistory.length - 1)]
- if (oldHighlight) {
- this.tryScrollTo(this.leastVisibleAncestor(oldHighlight))
- }
+ diveToTopLevel () {
+ this.tryScrollTo(this.topLevel[0].id)
},
+ // only used when we are not on a page
undive () {
- const oldHighlight = this.highlight
- this.diveHistory = []
- if (oldHighlight) {
- this.tryScrollTo(this.leastVisibleAncestor(oldHighlight))
- } else {
- this.goToCurrent()
- }
+ this.inlineDivePosition = null
+ this.setHighlight(this.statusId)
},
tryScrollTo (id) {
if (!id) {
@@ -477,8 +455,9 @@ const conversation = {
if (this.isPage) {
// set statusId
this.$router.push({ name: 'conversation', params: { id } })
+ } else {
+ this.inlineDivePosition = id
}
-
this.setHighlight(id)
},
goToCurrent () {
@@ -493,10 +472,24 @@ const conversation = {
return undefined
}
const { in_reply_to_status_id: parentId } = status
+ if (!this.statusMap[parentId]) {
+ return undefined
+ }
return parentId
},
parentOrSelf (id) {
return this.parentOf(id) || id
+ },
+ // Ancestors of some status, from top to bottom
+ ancestorsOf (id) {
+ const ancestors = []
+ let cur = this.parentOf(id)
+ while (cur) {
+ ancestors.unshift(this.statusMap[cur])
+ cur = this.parentOf(cur)
+ }
+ // console.log('ancestors = ', ancestors, 'conversation = ', this.conversation.map(k => k.id), 'statusContentProperties=', this.statusContentProperties)
+ return ancestors
}
}
}
diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue
@@ -21,34 +21,88 @@
<div class="conversation-body panel-body">
<div
v-if="diveMode"
- class="conversation-undive-box"
+ class="conversation-dive-to-top-level-box"
>
<i18n
path="status.show_all_conversation"
tag="button"
class="button-unstyled -link"
- @click.prevent="undive"
+ @click.prevent="diveToTopLevel"
>
<FAIcon icon="angle-double-left" />
</i18n>
</div>
<div
- v-if="diveMode"
- class="conversation-undive-box"
- >
- <i18n
- path="status.return_to_last_showing"
- tag="button"
- class="button-unstyled -link"
- @click.prevent="diveBack"
- >
- <FAIcon icon="chevron-left" />
- </i18n>
- </div>
- <div
v-if="isTreeView"
class="thread-body"
>
+ <div
+ v-if="ancestorsOf(diveRoot).length"
+ class="thread-ancestors"
+ >
+ <div
+ v-for="status in ancestorsOf(diveRoot)"
+ :key="status.id"
+ class="thread-ancestor"
+ :class="{'thread-ancestor-has-other-replies': getReplies(status.id).length > 1}"
+ >
+ <status
+ ref="statusComponent"
+ :inline-expanded="collapsable && isExpanded"
+ :statusoid="status"
+ :expandable="!isExpanded"
+ :show-pinned="pinnedStatusIdsObject && pinnedStatusIdsObject[status.id]"
+ :focused="focused(status.id)"
+ :in-conversation="isExpanded"
+ :highlight="getHighlight()"
+ :replies="getReplies(status.id)"
+ :in-profile="inProfile"
+ :profile-user-id="profileUserId"
+ class="conversation-status status-fadein panel-body"
+
+ :simple="treeViewIsSimple"
+ :toggle-thread-display="toggleThreadDisplay"
+ :thread-display-status="threadDisplayStatus"
+ :show-thread-recursively="showThreadRecursively"
+ :total-reply-count="totalReplyCount"
+ :total-reply-depth="totalReplyDepth"
+ :dive="(!treeViewIsSimple) ? () => diveIntoStatus(status.id) : null"
+
+ :controlled-showing-tall="statusContentProperties[status.id].showingTall"
+ :controlled-expanding-subject="statusContentProperties[status.id].expandingSubject"
+ :controlled-showing-long-subject="statusContentProperties[status.id].showingLongSubject"
+ :controlled-toggle-showing-tall="() => toggleStatusContentProperty(status.id, 'showingTall')"
+ :controlled-toggle-expanding-subject="() => toggleStatusContentProperty(status.id, 'expandingSubject')"
+ :controlled-toggle-showing-long-subject="() => toggleStatusContentProperty(status.id, 'showingLongSubject')"
+
+ @goto="setHighlight"
+ @toggleExpanded="toggleExpanded"
+ />
+ <div
+ v-if="getReplies(status.id).length > 1"
+ class="thread-ancestor-dive-box"
+ >
+ <div
+ class="thread-ancestor-dive-box-inner"
+ >
+ <i18n
+ tag="button"
+ path="status.ancestor_follow_with_icon"
+ class="button-unstyled -link thread-tree-show-replies-button"
+ @click.prevent="diveIntoStatus(status.id)"
+ >
+ <FAIcon
+ place="icon"
+ icon="angle-double-right"
+ />
+ <span place="text">
+ {{ $tc('status.ancestor_follow', getReplies(status.id).length - 1, { numReplies: getReplies(status.id).length - 1 }) }}
+ </span>
+ </i18n>
+ </div>
+ </div>
+ </div>
+ </div>
<thread-tree
v-for="status in showingTopLevel"
:key="status.id"
@@ -128,7 +182,7 @@
@import '../../_variables.scss';
.Conversation {
- .conversation-undive-box {
+ .conversation-dive-to-top-level-box {
padding: $status-margin;
border-bottom-width: 1px;
border-bottom-style: solid;
@@ -140,6 +194,27 @@
flex-direction: column;
}
+ .thread-ancestor {
+ --link: var(--faintLink);
+ --text: var(--faint);
+ color: var(--text);
+ }
+ .thread-ancestor-dive-box {
+ padding-left: $status-margin;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+ border-bottom-color: var(--border, $fallback--border);
+ border-radius: 0;
+ /* Make the button stretch along the whole row */
+ display: flex;
+ align-items: stretch;
+ flex-direction: column;
+ }
+ .thread-ancestor-dive-box-inner {
+ padding: $status-margin;
+ //border-left: 2px solid var(--border, $fallback--border);
+ }
+
/* HACK: we want the border width to scale with the status *below it* */
.conversation-status {
border-bottom-width: 1px;
@@ -148,6 +223,7 @@
border-radius: 0;
}
+ .thread-ancestor-has-other-replies .conversation-status,
&.-expanded .thread-tree .conversation-status {
border-bottom: none;
}
@@ -162,10 +238,10 @@
border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
border-bottom: 1px solid var(--border, $fallback--border);
}
- &.-expanded {
- .conversation-status:last-child {
- border-bottom: none;
- }
- }
+ /* &.-expanded { */
+ /* .conversation-status:last-child { */
+ /* border-bottom: none; */
+ /* } */
+ /* } */
}
</style>