pleroma-fe/src/composables/useTreeConversationTopology.js
Henry Jameson b7943cac84 scroll.
2026-09-10 19:10:01 +03:00

111 lines
3.5 KiB
JavaScript

import { storeToRefs } from 'pinia'
import { computed, ref } from 'vue'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useStatusesStore } from 'src/stores/statuses.js'
export function useTreeConversationTopology(conversation, replies, current) {
const getStatusObject = (id) => useStatusesStore().allStatuses.get(id)
const getReplies = (id) => replies.value.get(id) ?? new Set()
const { mergedConfig } = storeToRefs(useMergedConfigStore())
const maxDepthToShowByDefault = computed(() => {
// maxDepthInThread = max number of depths that is *visible*
// since our depth starts with 0 and "showing" means "showing children"
// there is a -2 here
const maxDepth = mergedConfig.value.maxDepthInThread - 2
return Math.min(1, maxDepth)
})
const ancestors = computed(() => {
// First we fill map with empty sets and add given id's parent
// as set's only element (if any)
const parentMap = conversation.value.reduce(
(result, { id, in_reply_to_status_id: irid }) => {
if (!result.has(id)) {
result.set(id, new Set())
}
if (irid && conversation.value.length !== 1) {
// Setting parent for current item
result.get(id).add(irid)
}
return result
},
new Map(),
)
// Next we iterate over each entry and fill the whole ancestry chain
parentMap.entries().forEach(([originId, originSet]) => {
let current = originSet.values().next().value
while (current) {
originSet.add(current)
const parent = parentMap.get(current) ?? new Set()
current = parent.values().next().value
}
})
return parentMap
})
const topLevel = computed(() =>
[...ancestors.value.entries()]
.filter(([id, ancestors]) => ancestors.size === 0)
.map(([id]) => getStatusObject(id)),
)
const getAncestorIds = (id) => ancestors.value.get(id) ?? new Set()
const getAncestors = (id) =>
[...getAncestorIds(id)].map(getStatusObject).filter(Boolean)
const currentAncestors = computed(() => getAncestors(current.value).reverse())
const currentDepth = computed(() => currentAncestors.value.length)
// Thread Display, for collapsing/expanding tree branches
// Map of id => 'showing' | 'hidden'
const threadDisplayOverride = ref(new Map())
const threadDisplayDefault = computed(() => {
return conversation.value.reduce((map, status) => {
const { id } = status
const depth = ancestors.value.get(id).size
const state = (() => {
if (depth - currentDepth.value <= maxDepthToShowByDefault.value) {
return 'showing'
} else {
return 'hidden'
}
})()
map.set(id, state)
return map
}, new Map())
})
const threadDisplay = computed(() => {
return new Map(
[...threadDisplayDefault.value.entries()].map(([k, v]) => [
k,
threadDisplayOverride.value.get(k) ?? threadDisplayDefault.value.get(k),
]),
)
})
const setThreadDisplayRecursively = (id, value) => {
threadDisplayOverride.value.set(id, value)
;[...getReplies(id)]
.map((k) => k.id)
.map((id) => setThreadDisplayRecursively(id, value))
}
const showThreadRecursively = (id) => {
setThreadDisplayRecursively(id, 'showing')
}
const resetThreadDisplay = () => {
threadDisplayOverride.value = new Map()
}
return {
topLevel,
currentAncestors,
threadDisplay,
showThreadRecursively,
resetThreadDisplay,
}
}