cleanup and organization

This commit is contained in:
Henry Jameson 2026-09-08 17:17:24 +03:00
commit 06d3c41b63
2 changed files with 164 additions and 151 deletions

View file

@ -18,7 +18,7 @@ import { useStreamingStore } from 'src/stores/streaming.js'
import {
fetchConversation as apiFetchConversation,
fetchStatus,
fetchStatus as apiFetchStatus,
} from 'src/api/public.js'
import { WSConnectionStatus } from 'src/api/websocket.js'
@ -83,6 +83,27 @@ export default {
mastoUserSocketStatus === WSConnectionStatus.JOINED,
)
// # Misc
const loadStatusError = ref(null)
const { layoutType } = storeToRefs(useInterfaceStore())
const mobileLayout = computed(() => layoutType.value === 'mobile')
// # Focus
const focusedId = ref(statusId.value)
const focused = computed(() => (isExpanded.value ? focusedId.value : null))
const setFocused = (id) => {
if (!id) return
focusedId.value = id
if (!streamingEnabled.value) {
useStatusesStore().fetchStatus(id)
}
useStatusesStore().fetchFavsAndRepeats(id)
useStatusesStore().fetchEmojiReactions(id)
}
provide('focused', focused)
// # Main things
const getStatusObject = (id) => useStatusesStore().allStatuses.get(id)
const getConversationId = (statusId) => {
@ -93,13 +114,56 @@ export default {
get(status, 'statusnet_conversation_id'),
)
}
const status = computed(() => getStatusObject(focusedId.value))
const status = computed(() => getStatusObject(statusId.value))
const mainStatusId = computed(() => {
if (status.value.retweeted_status) {
return status.value.retweeted_status.id
const fetchConversation = async () => {
if (status.value) {
const {
data: { ancestors, descendants },
timestamp,
} = await apiFetchConversation({
id: statusId.value,
credentials: useOAuthStore().token,
})
useStatusesStore().addNewStatuses({ statuses: ancestors, timestamp })
useStatusesStore().addNewStatuses({
statuses: descendants,
timestamp,
})
} else {
return statusId.value
try {
loadStatusError.value = null
const { data: status } = await apiFetchStatus({
id: statusId.value,
credentials: useOAuthStore().token,
})
useStatusesStore().addNewStatuses({ statuses: [status] })
fetchConversation()
} catch (error) {
console.error(error)
loadStatusError.value = error
}
}
}
const resetDisplayState = () => {
setFocused(statusId.value)
threadDisplay.value = new Map()
}
watch(statusId, (newVal, oldVal) => {
const newConversationId = getConversationId(newVal)
const oldConversationId = getConversationId(oldVal)
if (
newConversationId &&
oldConversationId &&
newConversationId === oldConversationId
) {
setFocused(newVal)
} else {
resetDisplayState()
fetchConversation()
}
})
@ -157,74 +221,13 @@ export default {
),
)
const getReplies = (id) => replies.value.get(id) ?? new Set()
const statusReplies = computed(() => {
return getReplies(status.value.id)
})
provide('conversation', conversation)
provide('replies', replies)
const fetchConversation = async () => {
if (status.value) {
const {
data: { ancestors, descendants },
timestamp,
} = await apiFetchConversation({
id: statusId.value,
credentials: useOAuthStore().token,
})
useStatusesStore().addNewStatuses({ statuses: ancestors, timestamp })
useStatusesStore().addNewStatuses({
statuses: descendants,
timestamp,
})
setFocused(mainStatusId.value)
} else {
try {
loadStatusError.value = null
const { data: status } = await fetchStatus({
id: statusId.value,
credentials: useOAuthStore().token,
})
useStatusesStore().addNewStatuses({ statuses: [status] })
fetchConversation()
} catch (error) {
console.error(error)
loadStatusError.value = error
}
}
}
const resetDisplayState = () => {
setFocused(statusId.value)
threadDisplay.value = new Map()
}
// # Virtual scrolling stuff
const { virtualHidden } = toRefs(props)
const unsuspendibleIds = ref(new Set())
const suspendable = computed(() => unsuspendibleIds.value.size === 0)
const hide = computed(() => virtualHidden.value && suspendable.value)
const onStatusSuspendStateChange = ({ id, suspend }) => {
if (!suspend) {
unsuspendibleIds.value.add(id)
} else {
unsuspendibleIds.value.delete(id)
}
}
// # Misc UI things
const loadStatusError = ref(null)
const { layoutType } = storeToRefs(useInterfaceStore())
const mobileLayout = computed(() => layoutType.value === 'mobile')
const firstStatus = computed(() => conversation.value[0])
const lastStatus = computed(
() => conversation.value[conversation.value.legnth - 1],
)
const getStatusClasses = (status, active) => ({
'-first': status.id === firstStatus.value?.id,
'-last': status.id === lastStatus.value?.id,
})
// # Conversation Expansion
const expanded = ref(false)
const { isPage } = toRefs(props)
@ -242,65 +245,41 @@ export default {
provide('isExpanded', isExpanded)
provide('isPage', isPage)
// # Focus
const focusedId = ref(statusId.value)
const focused = computed(() => (isExpanded.value ? focusedId.value : null))
const setFocused = (id) => {
if (!id) return
focusedId.value = id
if (!streamingEnabled.value) {
useStatusesStore().fetchStatus(id)
}
useStatusesStore().fetchFavsAndRepeats(id)
useStatusesStore().fetchEmojiReactions(id)
}
watch(statusId, (newVal, oldVal) => {
const newConversationId = getConversationId(newVal)
const oldConversationId = getConversationId(oldVal)
if (
newConversationId &&
oldConversationId &&
newConversationId === oldConversationId
) {
setFocused(mainStatusId.value)
} else {
fetchConversation()
}
})
provide('focused', focused)
// Component created
if (isPage.value) {
fetchConversation()
}
// # Virtual scrolling stuff
const { virtualHidden } = toRefs(props)
const unsuspendibleIds = ref(new Set())
const suspendable = computed(() => unsuspendibleIds.value.size === 0)
const hide = computed(() => virtualHidden.value && suspendable.value)
const onStatusSuspendStateChange = ({ id, suspend }) => {
if (!suspend) {
unsuspendibleIds.value.add(id)
} else {
unsuspendibleIds.value.delete(id)
}
}
// # Misc UI things
const firstStatus = computed(() => conversation.value[0])
const lastStatus = computed(
() => conversation.value[conversation.value.legnth - 1],
)
const getStatusClasses = (status, active) => ({
'-first': status.id === firstStatus.value?.id,
'-last': status.id === lastStatus.value?.id,
})
// # Linear style stuff
const isLinearView = computed(() => displayStyle.value !== 'tree')
// # Tree style stuff
const isTreeView = computed(() => displayStyle.value === 'tree')
// ## Tree view settings
const treeViewIsSimple = computed(
() => !mergedConfig.value.conversationTreeAdvanced,
)
const shouldFadeAncestors = computed(
() => mergedConfig.value.conversationTreeFadeAncestors,
)
const showOtherRepliesButtonBelowStatus = computed(
() => mergedConfig.value.conversationOtherRepliesButton === 'below',
)
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)
})
// ## Tree style state
// ## Tree state
// ### Topology
const ancestors = computed(() => {
// First we fill map with empty sets and add given id's parent
@ -344,7 +323,6 @@ export default {
getAncestors(focusedId.value).reverse(),
)
const currentDepth = computed(() => currentAncestors.value.length)
const currentStatus = computed(() => getStatusObject(focusedId.value))
// ### Thread Display
const threadDisplay = ref(new Map()) // id => 'showing' | 'hidden'
@ -378,15 +356,31 @@ export default {
setThreadDisplayRecursively(id, 'showing')
}
// ## Derived values
// ## Derived values and config
const treeViewIsSimple = computed(
() => !mergedConfig.value.conversationTreeAdvanced,
)
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 shouldShowAllConversationButton = computed(
() => currentAncestors.value.length > 0 && topLevel.value.length > 1,
)
const shouldShowAncestors = computed(
() => isExpanded.value && ancestors.value.get(focusedId.value) != null,
)
const shouldFadeAncestors = computed(
() => mergedConfig.value.conversationTreeFadeAncestors,
)
const shouldShowOtherRepliesButton = computed(
() => mergedConfig.value.conversationOtherRepliesButton === 'below',
)
// # Scrolling / diving
// # Scrolling
const tryScrollTo = (id) => {
if (!id) {
return
@ -421,37 +415,56 @@ export default {
}
return {
hide,
isLinearView,
isTreeView,
isExpanded,
conversation,
mobileLayout,
toggleExpanded,
isPage,
status,
// # Misc
loadStatusError,
mobileLayout,
// # Focus
focused,
setFocused,
showOtherRepliesButtonBelowStatus,
// # Main things
status,
statusReplies,
conversation,
// # Conversation Expansion
isPage,
isExpanded,
toggleExpanded,
// # Virtual scrolling stuff
hide,
onStatusSuspendStateChange,
getStatusClasses,
getReplies,
currentAncestors,
statusId,
virtualHidden,
shouldShowAncestors,
shouldShowAllConversationButton,
// # Misc UI things
getStatusClasses,
// # Linear style stuff
isLinearView,
// # Tree style stuff
isTreeView,
// ## Tree state
// ### Topology
topLevel,
shouldFadeAncestors,
currentStatus,
treeViewIsSimple,
diveToTopLevel,
threadDisplay,
threadDisplayDefault,
replies,
diveIntoStatus,
currentAncestors,
// ### Thread Display
showThreadRecursively,
// ## Derived values and config
treeViewIsSimple,
shouldShowAllConversationButton,
shouldShowAncestors,
shouldFadeAncestors,
shouldShowOtherRepliesButton,
// # Scrolling
diveToTopLevel,
diveIntoStatus,
}
},
}

View file

@ -93,24 +93,24 @@
<article
v-for="status in currentAncestors"
class="thread-ancestor"
:class="{'thread-ancestor-has-other-replies': getReplies(status.id).length > 1, '-faded': shouldFadeAncestors}"
:class="{'thread-ancestor-has-other-replies': statusReplies.size > 1, '-faded': shouldFadeAncestors}"
>
<Status
class="conversation-status panel-body"
:class="getStatusClasses(status)"
:status-id="status.id"
:replies="getReplies(status.id)"
:replies="statusReplies"
:focused="focused === status.id"
can-dive
@goto="setFocused"
@dive="() => diveIntoStatus(status.id)"
@dive="diveIntoStatus(status.id)"
@suspendable-state-change="onStatusSuspendStateChange"
/>
<div
v-if="showOtherRepliesButtonBelowStatus && getReplies(status.id).size > 1"
v-if="shouldShowOtherRepliesButton && statusReplies.size > 1"
class="thread-ancestor-dive-box"
>
<div
@ -130,7 +130,7 @@
</template>
<template #text>
<span>
{{ $t('status.ancestor_follow', { numReplies: getReplies(status.id, getReplies(status.id).size - 1).size - 1 }) }}
{{ $t('status.ancestor_follow', { numReplies: statusReplies.size - 1 }) }}
</span>
</template>
</i18n-t>
@ -139,7 +139,7 @@
</article>
</div>
<ThreadTree
:status-id="currentStatus.id"
:status-id="status.id"
:depth="0"
@goto="setFocused"