From 0582f19e7c2c6f916b427d5ecfbbb571178ce841 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 6 Aug 2021 20:18:27 -0400 Subject: Add tree-style thread display --- src/components/thread_tree/thread_tree.js | 52 +++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/components/thread_tree/thread_tree.js (limited to 'src/components/thread_tree/thread_tree.js') diff --git a/src/components/thread_tree/thread_tree.js b/src/components/thread_tree/thread_tree.js new file mode 100644 index 00000000..4f95122a --- /dev/null +++ b/src/components/thread_tree/thread_tree.js @@ -0,0 +1,52 @@ +import Status from '../status/status.vue' + +const debug = console.log + +const ThreadTree = { + components: { + Status + }, + name: 'ThreadTree', + props: { + depth: Number, + status: Object, + inProfile: Boolean, + conversation: Array, + collapsable: Boolean, + isExpanded: Boolean, + pinnedStatusIdsObject: Object, + profileUserId: String, + + focused: Function, + getHighlight: Function, + getReplies: Function, + setHighlight: Function, + toggleExpanded: Function + }, + computed: { + reverseLookupTable () { + return this.conversation.reduce((table, status, index) => { + table[status.id] = index + return table + }, {}) + }, + currentReplies () { + debug('status:', this.status) + debug('getReplies:', this.getReplies(this.status.id)) + return this.getReplies(this.status.id).map(({ id }) => this.statusById(id)) + }, + }, + methods: { + statusById (id) { + return this.conversation[this.reverseLookupTable[id]] + }, + collapseThread () { + }, + showThread () { + }, + showAllSubthreads () { + } + } +} + +export default ThreadTree -- cgit v1.2.3-70-g09d2 From 0f2fd8a3523e9e2cd1ca6fe287eb7304895f2cba Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 7 Aug 2021 00:33:06 -0400 Subject: Implement thread folding/expanding --- src/components/conversation/conversation.js | 105 +++++++++++++++++++++++++-- src/components/conversation/conversation.vue | 7 ++ src/components/status/status.js | 22 +++++- src/components/status/status.vue | 13 ++++ src/components/thread_tree/thread_tree.js | 12 ++- src/components/thread_tree/thread_tree.vue | 35 ++++++++- 6 files changed, 180 insertions(+), 14 deletions(-) (limited to 'src/components/thread_tree/thread_tree.js') diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index e663ba15..0abb7abd 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -38,7 +38,8 @@ const conversation = { data () { return { highlight: null, - expanded: false + expanded: false, + threadDisplayStatusObject: {} // id => 'showing' | 'hidden' } }, props: [ @@ -56,6 +57,9 @@ const conversation = { } }, computed: { + maxDepthToShowByDefault () { + return 4 + }, displayStyle () { return this.$store.state.config.conversationDisplay }, @@ -112,15 +116,14 @@ const conversation = { const id = cur.id a.forest[id] = this.getReplies(id) .map(s => s.id) - .sort((a, b) => reverseLookupTable[a] - reverseLookupTable[b]) - a.topLevel = a.topLevel.filter(k => a.forest[id].contains(k)) return a }, { forest: {}, - topLevel: this.conversation.map(s => s.id) }) + debug('threads = ', threads) + const walk = (forest, topLevel, depth = 0, processed = {}) => topLevel.map(id => { if (processed[id]) { return [] @@ -131,18 +134,63 @@ const conversation = { status: this.conversation[reverseLookupTable[id]], id, depth - }, walk(forest, forest[child], depth + 1, processed)].reduce((a, b) => a.concat(b), []) + }, walk(forest, forest[id], depth + 1, processed)].reduce((a, b) => a.concat(b), []) }).reduce((a, b) => a.concat(b), []) - const linearized = walk(threads.forest, threads.topLevel) + const linearized = walk(threads.forest, this.topLevel.map(k => k.id)) return linearized }, + replyIds () { + return this.conversation.map(k => k.id) + .reduce((res, id) => { + res[id] = (this.replies[id] || []).map(k => k.id) + return res + }, {}) + }, + totalReplyCount () { + debug('replyIds=', this.replyIds) + const sizes = {} + const subTreeSizeFor = (id) => { + if (sizes[id]) { + return sizes[id] + } + sizes[id] = 1 + this.replyIds[id].map(cid => subTreeSizeFor(cid)).reduce((a, b) => a + b, 0) + return sizes[id] + } + this.conversation.map(k => k.id).map(subTreeSizeFor) + debug('totalReplyCount=', sizes) + return Object.keys(sizes).reduce((res, id) => { + res[id] = sizes[id] - 1 // exclude itself + return res + }, {}) + }, + totalReplyDepth () { + const depths = {} + const subTreeDepthFor = (id) => { + if (depths[id]) { + return depths[id] + } + depths[id] = 1 + this.replyIds[id].map(cid => subTreeDepthFor(cid)).reduce((a, b) => a > b ? a : b, 0) + return depths[id] + } + this.conversation.map(k => k.id).map(subTreeDepthFor) + return Object.keys(depths).reduce((res, id) => { + res[id] = depths[id] - 1 // exclude itself + return res + }, {}) + }, + depths () { + debug('threadTree', this.threadTree) + return this.threadTree.reduce((a, k) => { + a[k.id] = k.depth + return a + }, {}) + }, topLevel () { const topLevel = this.conversation.reduce((tl, cur) => tl.filter(k => this.getReplies(cur.id).map(v => v.id).indexOf(k.id) === -1), this.conversation) debug("toplevel =", topLevel) - debug("toplevel =", topLevel) return topLevel }, replies () { @@ -169,6 +217,25 @@ const conversation = { hiddenStyle () { const height = (this.status && this.status.virtualHeight) || '120px' return this.virtualHidden ? { height } : {} + }, + threadDisplayStatus () { + return this.conversation.reduce((a, k) => { + const id = k.id + const depth = this.depths[id] + const status = (() => { + if (this.threadDisplayStatusObject[id]) { + return this.threadDisplayStatusObject[id] + } + if (depth <= this.maxDepthToShowByDefault) { + return 'showing' + } else { + return 'hidden' + } + })() + + a[id] = status + return a + }, {}) } }, components: { @@ -235,6 +302,30 @@ const conversation = { getConversationId (statusId) { const status = this.$store.state.statuses.allStatusesObject[statusId] return get(status, 'retweeted_status.statusnet_conversation_id', get(status, 'statusnet_conversation_id')) + }, + setThreadDisplay (id, nextStatus) { + this.threadDisplayStatusObject = { + ...this.threadDisplayStatusObject, + [id]: nextStatus + } + }, + toggleThreadDisplay (id) { + const depth = this.depths[id] + debug('depth = ', depth) + debug( + 'threadDisplayStatus = ', this.threadDisplayStatus, + 'threadDisplayStatusObject = ', this.threadDisplayStatusObject) + const curStatus = this.threadDisplayStatus[id] + const nextStatus = curStatus === 'showing' ? 'hidden' : 'showing' + debug('toggling', id, 'to', nextStatus) + this.setThreadDisplay(id, nextStatus) + }, + setThreadDisplayRecursively (id, nextStatus) { + this.setThreadDisplay(id, nextStatus) + this.getReplies(id).map(k => k.id).map(id => this.setThreadDisplayRecursively(id, nextStatus)) + }, + showThreadRecursively (id) { + this.setThreadDisplayRecursively(id, 'showing') } } } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index cea5f88f..783cc894 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -23,6 +23,7 @@ v-for="status in topLevel" :key="status.id" ref="statusComponent" + :depth="0" :status="status" :in-profile="inProfile" @@ -37,6 +38,12 @@ :get-highlight="getHighlight" :set-highlight="setHighlight" :toggle-expanded="toggleExpanded" + + :toggle-thread-display="toggleThreadDisplay" + :thread-display-status="threadDisplayStatus" + :show-thread-recursively="showThreadRecursively" + :total-reply-count="totalReplyCount" + :total-reply-depth="totalReplyDepth" />
diff --git a/src/components/status/status.js b/src/components/status/status.js index d8f94926..9d423631 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -35,7 +35,9 @@ import { faStar, faEyeSlash, faEye, - faThumbtack + faThumbtack, + faAngleDoubleUp, + faAngleDoubleDown } from '@fortawesome/free-solid-svg-icons' library.add( @@ -52,7 +54,9 @@ library.add( faEllipsisH, faEyeSlash, faEye, - faThumbtack + faThumbtack, + faAngleDoubleUp, + faAngleDoubleDown ) const Status = { @@ -89,7 +93,10 @@ const Status = { 'inlineExpanded', 'showPinned', 'inProfile', - 'profileUserId' + 'profileUserId', + + 'controlledThreadDisplayStatus', + 'controlledToggleThreadDisplay' ], data () { return { @@ -304,6 +311,12 @@ const Status = { }, isSuspendable () { return !this.replying && this.mediaPlaying.length === 0 + }, + inThreadForest () { + return !!this.controlledThreadDisplayStatus + }, + threadShowing () { + return this.controlledThreadDisplayStatus === 'showing' } }, methods: { @@ -353,6 +366,9 @@ const Status = { }, setHeadTailLinks (headTailLinks) { this.headTailLinks = headTailLinks + }, + toggleThreadDisplay () { + this.controlledToggleThreadDisplay() } }, watch: { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 3bb29db6..2ebf5638 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -219,6 +219,19 @@ class="fa-scale-110" /> +
this.statusById(id)) }, + threadShowing () { + return this.threadDisplayStatus[this.status.id] === 'showing' + } }, methods: { statusById (id) { diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue index 8256eee6..9f591585 100644 --- a/src/components/thread_tree/thread_tree.vue +++ b/src/components/thread_tree/thread_tree.vue @@ -13,18 +13,23 @@ :replies="getReplies(status.id)" :in-profile="inProfile" :profile-user-id="profileUserId" - class="conversation-status status-fadein panel-body" + class="conversation-status conversation-status-treeview status-fadein panel-body" + + :controlled-thread-display-status="threadDisplayStatus[status.id]" + :controlled-toggle-thread-display="() => toggleThreadDisplay(status.id)" + @goto="setHighlight" @toggleExpanded="toggleExpanded" />
+
+ +
-- cgit v1.2.3-70-g09d2 From ace1f5067c90be2fa0b8da22d39b0e2c88f590fb Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 7 Aug 2021 10:28:45 -0400 Subject: Make status display controlled --- src/components/conversation/conversation.js | 37 +++++++++++++++- src/components/conversation/conversation.vue | 3 ++ src/components/status/status.js | 9 +++- src/components/status/status.vue | 6 +++ src/components/status_content/status_content.js | 59 ++++++++++++++++++++++++- src/components/thread_tree/thread_tree.js | 11 ++++- src/components/thread_tree/thread_tree.vue | 10 +++++ 7 files changed, 130 insertions(+), 5 deletions(-) (limited to 'src/components/thread_tree/thread_tree.js') diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 0abb7abd..6fc86b2c 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -2,7 +2,8 @@ import { reduce, filter, findIndex, clone, get } from 'lodash' import Status from '../status/status.vue' import ThreadTree from '../thread_tree/thread_tree.vue' -const debug = console.log +// const debug = console.log +const debug = () => {} const sortById = (a, b) => { const idA = a.type === 'retweet' ? a.retweeted_status.id : a.id @@ -39,7 +40,8 @@ const conversation = { return { highlight: null, expanded: false, - threadDisplayStatusObject: {} // id => 'showing' | 'hidden' + threadDisplayStatusObject: {}, // id => 'showing' | 'hidden' + statusContentPropertiesObject: {} } }, props: [ @@ -236,6 +238,25 @@ const conversation = { a[id] = status return a }, {}) + }, + statusContentProperties () { + return this.conversation.reduce((a, k) => { + const id = k.id + const depth = this.depths[id] + const props = (() => { + if (this.statusContentPropertiesObject[id]) { + return this.statusContentPropertiesObject[id] + } + return { + showingTall: false, + expandingSubject: false, + showingLongSubject: false, + } + })() + + a[id] = props + return a + }, {}) } }, components: { @@ -326,6 +347,18 @@ const conversation = { }, showThreadRecursively (id) { this.setThreadDisplayRecursively(id, 'showing') + }, + setStatusContentProperty (id, name, value) { + this.statusContentPropertiesObject = { + ...this.statusContentPropertiesObject, + [id]: { + ...this.statusContentPropertiesObject[id], + [name]: value + } + } + }, + toggleStatusContentProperty (id, name) { + this.setStatusContentProperty(id, name, !this.statusContentProperties[id][name]) } } } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 783cc894..08cb72d0 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -44,6 +44,9 @@ :show-thread-recursively="showThreadRecursively" :total-reply-count="totalReplyCount" :total-reply-depth="totalReplyDepth" + :status-content-properties="statusContentProperties" + :set-status-content-property="setStatusContentProperty" + :toggle-status-content-property="toggleStatusContentProperty" />
diff --git a/src/components/status/status.js b/src/components/status/status.js index 9d423631..d5ee7d4e 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -96,7 +96,14 @@ const Status = { 'profileUserId', 'controlledThreadDisplayStatus', - 'controlledToggleThreadDisplay' + 'controlledToggleThreadDisplay', + + 'controlledShowingTall', + 'controlledToggleShowingTall', + 'controlledExpandingSubject', + 'controlledToggleExpandingSubject', + 'controlledShowingLongSubject', + 'controlledToggleShowingLongSubject' ], data () { return { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 2ebf5638..8fdebe44 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -319,6 +319,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" diff --git a/src/components/status_content/status_content.js b/src/components/status_content/status_content.js index dec8914a..65ec85c4 100644 --- a/src/components/status_content/status_content.js +++ b/src/components/status_content/status_content.js @@ -23,6 +23,31 @@ library.add( faPollH ) +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 controlledName = `controlled${camelized}` + const uncontrolledName = `uncontrolled${camelized}` + if (obj[toggle]) { + obj[toggle]() + } else { + obj[uncontrolledName] = !obj[uncontrolledName] + } +} + const StatusContent = { name: 'StatusContent', props: [ @@ -31,9 +56,22 @@ const StatusContent = { 'focused', 'noHeading', 'fullContent', - 'singleLine' + 'singleLine', + 'controlledShowingTall', + 'controlledExpandingSubject', + 'controlledToggleShowingTall', + 'controlledToggleExpandingSubject' ], + data () { + return { + uncontrolledShowingTall: this.fullContent || (this.inConversation && this.focused), + uncontrolledShowingLongSubject: false, + // not as computed because it sets the initial state which will be changed later + uncontrolledExpandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject + } + }, computed: { + ...controlledOrUncontrolledGetters(['showingTall', 'expandingSubject', 'showingLongSubject']), hideAttachments () { return (this.mergedConfig.hideAttachments && !this.inConversation) || (this.mergedConfig.hideAttachmentsInConv && this.inConversation) @@ -71,6 +109,25 @@ const StatusContent = { Gallery, LinkPreview, StatusBody + }, + methods: { + toggleShowingTall () { + controlledOrUncontrolledToggle(this, 'showingTall') + }, + toggleExpandingSubject () { + controlledOrUncontrolledToggle(this, 'expandingSubject') + }, + toggleShowMore () { + if (this.mightHideBecauseTall) { + this.toggleShowingTall() + } else if (this.mightHideBecauseSubject) { + this.toggleExpandingSubject() + } + }, + setMedia () { + const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments + return () => this.$store.dispatch('setMedia', attachments) + } } } diff --git a/src/components/thread_tree/thread_tree.js b/src/components/thread_tree/thread_tree.js index 88b60109..46245bdf 100644 --- a/src/components/thread_tree/thread_tree.js +++ b/src/components/thread_tree/thread_tree.js @@ -28,7 +28,10 @@ const ThreadTree = { threadDisplayStatus: Object, showThreadRecursively: Function, totalReplyCount: Object, - totalReplyDepth: Object + totalReplyDepth: Object, + statusContentProperties: Object, + setStatusContentProperty: Function, + toggleStatusContentProperty: Function }, computed: { reverseLookupTable () { @@ -44,6 +47,9 @@ const ThreadTree = { }, threadShowing () { return this.threadDisplayStatus[this.status.id] === 'showing' + }, + currentProp () { + return this.statusContentProperties[this.status.id] } }, methods: { @@ -55,6 +61,9 @@ const ThreadTree = { showThread () { }, showAllSubthreads () { + }, + toggleCurrentProp (name) { + this.toggleStatusContentProperty(this.status.id, name) } } } diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue index bbdda6fb..d7077bfd 100644 --- a/src/components/thread_tree/thread_tree.vue +++ b/src/components/thread_tree/thread_tree.vue @@ -18,6 +18,13 @@ :controlled-thread-display-status="threadDisplayStatus[status.id]" :controlled-toggle-thread-display="() => toggleThreadDisplay(status.id)" + :controlled-showing-tall="currentProp.showingTall" + :controlled-expanding-subject="currentProp.expandingSubject" + :controlled-showing-long-subject="currentProp.showingLongSubject" + :controlled-toggle-showing-tall="() => toggleCurrentProp('ShowingTall')" + :controlled-toggle-expanding-subject="() => toggleCurrentProp('expandingSubject')" + :controlled-toggle-showing-long-subject="() => toggleCurrentProp('showingLongSubject')" + @goto="setHighlight" @toggleExpanded="toggleExpanded" /> @@ -50,6 +57,9 @@ :show-thread-recursively="showThreadRecursively" :total-reply-count="totalReplyCount" :total-reply-depth="totalReplyDepth" + :status-content-properties="statusContentProperties" + :set-status-content-property="setStatusContentProperty" + :toggle-status-content-property="toggleStatusContentProperty" />
Date: Sat, 7 Aug 2021 11:59:10 -0400 Subject: Support diving into one status in a conversation --- src/components/conversation/conversation.js | 32 +++++++++++++++++++++++++++- src/components/conversation/conversation.vue | 30 +++++++++++++++++++++++++- src/components/status/status.js | 13 ++++++----- src/components/status/status.vue | 14 +++++++++++- src/components/thread_tree/thread_tree.js | 3 ++- src/components/thread_tree/thread_tree.vue | 15 +++++++++---- 6 files changed, 94 insertions(+), 13 deletions(-) (limited to 'src/components/thread_tree/thread_tree.js') diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 6fc86b2c..584f310c 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -2,6 +2,17 @@ import { reduce, filter, findIndex, clone, get } from 'lodash' import Status from '../status/status.vue' import ThreadTree from '../thread_tree/thread_tree.vue' +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faAngleDoubleDown, + faAngleDoubleLeft +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faAngleDoubleDown, + faAngleDoubleLeft +) + // const debug = console.log const debug = () => {} @@ -41,7 +52,8 @@ const conversation = { highlight: null, expanded: false, threadDisplayStatusObject: {}, // id => 'showing' | 'hidden' - statusContentPropertiesObject: {} + statusContentPropertiesObject: {}, + diveRoot: null } }, props: [ @@ -195,6 +207,18 @@ const conversation = { debug("toplevel =", topLevel) return topLevel }, + showingTopLevel () { + if (this.diveRoot) { + return [this.conversation.filter(k => this.diveRoot === k.id)[0]] + } + return this.topLevel + }, + diveDepth () { + return this.diveRoot ? this.depths[this.diveRoot] : 0 + }, + diveMode () { + return !!this.diveRoot + }, replies () { let i = 1 // eslint-disable-next-line camelcase @@ -359,6 +383,12 @@ const conversation = { }, toggleStatusContentProperty (id, name) { this.setStatusContentProperty(id, name, !this.statusContentProperties[id][name]) + }, + diveIntoStatus (id) { + this.diveRoot = id + }, + undive () { + this.diveRoot = null } } } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 08cb72d0..84e8d8b2 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -18,9 +18,22 @@ {{ $t('timeline.collapse') }}
+
+ + + +
@@ -65,6 +79,16 @@ :in-profile="inProfile" :profile-user-id="profileUserId" class="conversation-status status-fadein panel-body" + + :toggle-thread-display="toggleThreadDisplay" + :thread-display-status="threadDisplayStatus" + :show-thread-recursively="showThreadRecursively" + :total-reply-count="totalReplyCount" + :total-reply-depth="totalReplyDepth" + :status-content-properties="statusContentProperties" + :set-status-content-property="setStatusContentProperty" + :toggle-status-content-property="toggleStatusContentProperty" + @goto="setHighlight" @toggleExpanded="toggleExpanded" /> @@ -82,6 +106,10 @@ @import '../../_variables.scss'; .Conversation { + .conversation-undive-box { + padding: 1em; + } + .conversation-undive-box, .conversation-status { border-bottom-width: 1px; border-bottom-style: solid; diff --git a/src/components/status/status.js b/src/components/status/status.js index d5ee7d4e..f119f42e 100644 --- a/src/components/status/status.js +++ b/src/components/status/status.js @@ -36,8 +36,9 @@ import { faEyeSlash, faEye, faThumbtack, - faAngleDoubleUp, - faAngleDoubleDown + faChevronUp, + faChevronDown, + faAngleDoubleRight } from '@fortawesome/free-solid-svg-icons' library.add( @@ -55,8 +56,9 @@ library.add( faEyeSlash, faEye, faThumbtack, - faAngleDoubleUp, - faAngleDoubleDown + faChevronUp, + faChevronDown, + faAngleDoubleRight ) const Status = { @@ -103,7 +105,8 @@ const Status = { 'controlledExpandingSubject', 'controlledToggleExpandingSubject', 'controlledShowingLongSubject', - 'controlledToggleShowingLongSubject' + 'controlledToggleShowingLongSubject', + 'dive' ], data () { return { diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 8fdebe44..c2df6021 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -229,7 +229,19 @@ + + diff --git a/src/components/thread_tree/thread_tree.js b/src/components/thread_tree/thread_tree.js index 46245bdf..3e8eedb1 100644 --- a/src/components/thread_tree/thread_tree.js +++ b/src/components/thread_tree/thread_tree.js @@ -31,7 +31,8 @@ const ThreadTree = { totalReplyDepth: Object, statusContentProperties: Object, setStatusContentProperty: Function, - toggleStatusContentProperty: Function + toggleStatusContentProperty: Function, + dive: Function }, computed: { reverseLookupTable () { diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue index d7077bfd..adf7bcdf 100644 --- a/src/components/thread_tree/thread_tree.vue +++ b/src/components/thread_tree/thread_tree.vue @@ -24,6 +24,7 @@ :controlled-toggle-showing-tall="() => toggleCurrentProp('ShowingTall')" :controlled-toggle-expanding-subject="() => toggleCurrentProp('expandingSubject')" :controlled-toggle-showing-long-subject="() => toggleCurrentProp('showingLongSubject')" + :dive="dive ? () => dive(status.id) : undefined" @goto="setHighlight" @toggleExpanded="toggleExpanded" @@ -60,18 +61,24 @@ :status-content-properties="statusContentProperties" :set-status-content-property="setStatusContentProperty" :toggle-status-content-property="toggleStatusContentProperty" + :dive="dive" />
- + + + {{ $tc('status.thread_show_full', totalReplyCount[status.id], { numStatus: totalReplyCount[status.id], depth: totalReplyDepth[status.id] }) }} + +
-- cgit v1.2.3-70-g09d2 From d15d24c11c57ecfc49705af648b1e8f73caec51e Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Sat, 7 Aug 2021 14:11:34 -0400 Subject: Add dive functionality --- src/components/conversation/conversation.js | 69 ++++++++++++++++++++++++---- src/components/conversation/conversation.vue | 15 +++++- src/components/status/status.vue | 2 +- src/components/thread_tree/thread_tree.js | 3 +- src/components/thread_tree/thread_tree.vue | 5 +- 5 files changed, 80 insertions(+), 14 deletions(-) (limited to 'src/components/thread_tree/thread_tree.js') diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 584f310c..b2af1d6c 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -5,12 +5,14 @@ import ThreadTree from '../thread_tree/thread_tree.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faAngleDoubleDown, - faAngleDoubleLeft + faAngleDoubleLeft, + faChevronLeft } from '@fortawesome/free-solid-svg-icons' library.add( faAngleDoubleDown, - faAngleDoubleLeft + faAngleDoubleLeft, + faChevronLeft ) // const debug = console.log @@ -53,7 +55,7 @@ const conversation = { expanded: false, threadDisplayStatusObject: {}, // id => 'showing' | 'hidden' statusContentPropertiesObject: {}, - diveRoot: null + diveHistory: [] } }, props: [ @@ -120,6 +122,14 @@ const conversation = { return sortAndFilterConversation(conversation, this.status) }, + conversationDive () { + }, + statusMap () { + return this.conversation.reduce((res, s) => { + res[s.id] = s + return res + }, {}) + }, threadTree () { const reverseLookupTable = this.conversation.reduce((table, status, index) => { table[status.id] = index @@ -208,16 +218,19 @@ const conversation = { return topLevel }, showingTopLevel () { - if (this.diveRoot) { - return [this.conversation.filter(k => this.diveRoot === k.id)[0]] + if (this.canDive && this.diveRoot) { + return [this.statusMap[this.diveRoot]] } return this.topLevel }, + diveRoot () { + return this.diveHistory[this.diveHistory.length - 1] + }, diveDepth () { - return this.diveRoot ? this.depths[this.diveRoot] : 0 + return this.canDive && this.diveRoot ? this.depths[this.diveRoot] : 0 }, diveMode () { - return !!this.diveRoot + return this.canDive && !!this.diveRoot }, replies () { let i = 1 @@ -252,7 +265,7 @@ const conversation = { if (this.threadDisplayStatusObject[id]) { return this.threadDisplayStatusObject[id] } - if (depth <= this.maxDepthToShowByDefault) { + if ((depth - this.diveDepth) <= this.maxDepthToShowByDefault) { return 'showing' } else { return 'hidden' @@ -281,6 +294,9 @@ const conversation = { a[id] = props return a }, {}) + }, + canDive () { + return this.isTreeView && this.isExpanded } }, components: { @@ -310,6 +326,25 @@ const conversation = { } }, methods: { + conversationFetched () { + if (!this.isExpanded) { + return + } + + if (!this._diven) { + if (!this.threadDisplayStatus[this.statusId]) { + return + } + this._diven = true + const parentOrSelf = this.parentOrSelf(this.originalStatusId) + console.log( + 'this.threadDisplayStatus ', this.threadDisplayStatus, + 'this.statusId', this.statusId) + if (this.threadDisplayStatus[this.statusId] === 'hidden') { + this.diveIntoStatus(parentOrSelf) + } + } + }, fetchConversation () { if (this.status) { this.$store.state.api.backendInteractor.fetchConversation({ id: this.statusId }) @@ -318,6 +353,7 @@ const conversation = { this.$store.dispatch('addNewStatuses', { statuses: descendants }) this.setHighlight(this.originalStatusId) }) + .then(this.conversationFetched) } else { this.$store.state.api.backendInteractor.fetchStatus({ id: this.statusId }) .then((status) => { @@ -385,10 +421,23 @@ const conversation = { this.setStatusContentProperty(id, name, !this.statusContentProperties[id][name]) }, diveIntoStatus (id) { - this.diveRoot = id + this.diveHistory = [...this.diveHistory, id] + }, + diveBack () { + this.diveHistory = [...this.diveHistory.slice(0, this.diveHistory.length - 1)] }, undive () { - this.diveRoot = null + this.diveHistory = [] + }, + statusById (id) { + return this.statusMap[id] + }, + parentOf (id) { + const { in_reply_to_status_id: parentId } = this.statusById(id) + return parentId + }, + parentOrSelf (id) { + return this.parentOf(id) || id } } } diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 84e8d8b2..99bc7bcc 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -31,6 +31,19 @@ +
+ + + +
diff --git a/src/components/status/status.vue b/src/components/status/status.vue index c2df6021..47d35de8 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -220,7 +220,7 @@ />