From 24b7335903ed6f3db74d2ac59e76b3b63e05ea25 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 14 Sep 2026 21:14:23 +0300 Subject: [PATCH] move focused logic into useConversation (for real this time) --- src/components/conversation/conversation.js | 80 ++++---------------- src/components/conversation/conversation.vue | 4 +- src/components/thread_tree/thread_tree.js | 2 +- src/components/thread_tree/thread_tree.vue | 2 +- src/composables/useConversation.js | 77 +++++++++++++++---- 5 files changed, 82 insertions(+), 83 deletions(-) diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 1a2c61ab1..30fba1b6b 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -21,7 +21,6 @@ import ThreadTree from 'src/components/thread_tree/thread_tree.vue' import { useInterfaceStore } from 'src/stores/interface.js' import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useStatusesStore } from 'src/stores/statuses.js' -import { useStreamingStore } from 'src/stores/streaming.js' import { useConversation } from 'src/composables/useConversation.js' import { useInterfaceSizes } from 'src/composables/useInterfaceSizes.js' @@ -72,18 +71,9 @@ export default { }, emits: ['heightChange', 'suspendableStateChange'], setup(props, { emit }) { - // # Helpers - const getStatusObject = (id) => useStatusesStore().allStatuses.get(id) - const getConversationId = (statusId) => { - const status = getStatusObject(statusId) - return get( - status, - 'retweeted_status.statusnet_conversation_id', - get(status, 'statusnet_conversation_id'), - ) - } - + const router = useRouter() const scroller = useScrollPosition() + const tryScrollTo = async (id) => { if (!id) { return @@ -96,19 +86,9 @@ export default { return await scroller.scrollIntoView(target, { block: 'nearest' }) } - const { statusId } = toRefs(props) - - const router = useRouter() - // # Main Configuration / global state const { mergedConfig } = storeToRefs(useMergedConfigStore()) - const { mastoUserSocketStatus } = storeToRefs(useStreamingStore()) const displayStyle = computed(() => mergedConfig.value.conversationDisplay) - const streamingEnabled = computed( - () => - mergedConfig.value.useStreamingApi && - mastoUserSocketStatus === WSConnectionStatus.JOINED, - ) const { layoutType } = storeToRefs(useInterfaceStore()) const mobileLayout = computed(() => layoutType.value === 'mobile') @@ -123,24 +103,11 @@ export default { provide('isPage', isPage) provide('expandable', true) - // # 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 { statusId } = toRefs(props) const { + focusedId, + setFocused, currentStatus, mainStatus, conversation, @@ -148,40 +115,18 @@ export default { getReplies, fetchConversation, loadError, - } = useConversation(focusedId, isExpanded) + } = useConversation(statusId, isExpanded) watch( expanded, async (value) => { if (value) { await fetchConversation() - } else { - resetDisplayState() } - if (isPage.value) return }, { flush: 'post' }, ) - const resetDisplayState = () => { - setFocused(statusId.value) - resetThreadDisplay() - } - watch(statusId, (newVal, oldVal) => { - const newConversationId = getConversationId(newVal) - const oldConversationId = getConversationId(oldVal) - if ( - newConversationId && - oldConversationId && - newConversationId === oldConversationId - ) { - setFocused(newVal) - } else { - resetDisplayState() - fetchConversation() - } - }) - // Component created if (isPage.value) { fetchConversation() @@ -204,8 +149,6 @@ export default { return result } - const { fontSize } = useInterfaceSizes() - // External virtual scrolling const unsuspendableIds = ref(new Set()) const suspendable = computed( @@ -228,6 +171,7 @@ export default { onUnmounted(() => resizeObserver.value.disconnect()) // Placeholder heights. + const { fontSize } = useInterfaceSizes() const mutedStatusHeight = computed(() => fontSize.value * 1.5) const normalStatusHeight = computed(() => fontSize.value * 10) const getPlaceholderHeight = (id) => @@ -238,6 +182,7 @@ export default { const anchorIds = computed( () => new Set([mainStatus.value?.id, currentStatus.value?.id]), ) + // # Linear style stuff const isLinearView = computed(() => displayStyle.value !== 'tree') const linearElement = useTemplateRef('linear') @@ -274,6 +219,13 @@ export default { resetThreadDisplay, } = useTreeConversationTopology(conversation, replies, focusedId) provide('threadDisplay', threadDisplay) + watch( + isExpanded, + (value) => { + if (!value) resetThreadDisplay() + }, + { flush: 'post' }, + ) const ancestorsElement = useTemplateRef('ancestors') const treeScrollCompensation = computed(() => isTreeView.value) @@ -333,7 +285,7 @@ export default { toggleExpanded, // # Focus - focused, + focusedId, setFocused, // # Main things diff --git a/src/components/conversation/conversation.vue b/src/components/conversation/conversation.vue index 4e10bdcd0..3b5a96706 100644 --- a/src/components/conversation/conversation.vue +++ b/src/components/conversation/conversation.vue @@ -103,7 +103,7 @@ :status-id="element.item.id" :replies="getReplies(element.item.id)" - :focused="focused === element.item.id" + :focused="focusedId === element.item.id" conversation-rank="ancestor" :data-status-id="element.id" :data-vs-height="element.height" @@ -149,7 +149,7 @@ :status-id="element.item.id" :replies="getReplies(element.item.id)" - :focused="focused === element.id || focused === element.item.retweeted_status?.id" + :focused="focusedId === element.item.id || focusedId === element.item.retweeted_status?.id" :data-status-id="element.id" :data-vs-height="element.height" diff --git a/src/components/thread_tree/thread_tree.js b/src/components/thread_tree/thread_tree.js index 6dedf78ed..c6353bb84 100644 --- a/src/components/thread_tree/thread_tree.js +++ b/src/components/thread_tree/thread_tree.js @@ -37,7 +37,7 @@ const ThreadTree = { ], inject: [ 'conversation', - 'focused', + 'focusedId', 'replies', 'threadDisplay', 'isExpanded', diff --git a/src/components/thread_tree/thread_tree.vue b/src/components/thread_tree/thread_tree.vue index a13e3d692..2f9b07021 100644 --- a/src/components/thread_tree/thread_tree.vue +++ b/src/components/thread_tree/thread_tree.vue @@ -8,8 +8,8 @@ class="conversation-status conversation-status-treeview panel-body" :status-id="statusId" :replies="getReplies(statusId)" - :focused="focused === statusId" + :focused="focusedId === status.id" :data-status-id="statusId" :conversation-rank="depth === 0 ? 'current' : 'child'" :thread-display-state="threadDisplay.get(statusId)" diff --git a/src/composables/useConversation.js b/src/composables/useConversation.js index 9bec1e160..d449e0798 100644 --- a/src/composables/useConversation.js +++ b/src/composables/useConversation.js @@ -1,8 +1,13 @@ import { get } from 'lodash-es' -import { computed, provide, ref } from 'vue' +import { storeToRefs } from 'pinia' +import { computed, provide, ref, watch } from 'vue' +import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useOAuthStore } from 'src/stores/oauth.js' import { useStatusesStore } from 'src/stores/statuses.js' +import { useStreamingStore } from 'src/stores/streaming.js' + +import { useMainStatus } from 'src/composables/useMainStatus.js' import { fetchConversation as apiFetchConversation, @@ -10,20 +15,58 @@ import { } from 'src/api/public.js' export function useConversation(statusId, expanded) { - const getStatusObject = (id) => useStatusesStore().allStatuses.get(id) - const getConversationId = (statusId) => { - const status = getStatusObject(statusId) - return get( - status, - 'retweeted_status.statusnet_conversation_id', - get(status, 'statusnet_conversation_id'), - ) - } - const loadError = ref(null) - const currentStatus = computed(() => getStatusObject(statusId.value)) - const mainStatus = computed( - () => currentStatus.value?.retweeted_status ?? currentStatus.value, + const { status: currentStatus, mainStatus } = useMainStatus(statusId) + + // # Config + const { mergedConfig } = storeToRefs(useMergedConfigStore()) + const { mastoUserSocketStatus } = storeToRefs(useStreamingStore()) + const streamingEnabled = computed( + () => + mergedConfig.value.useStreamingApi && + mastoUserSocketStatus === WSConnectionStatus.JOINED, + ) + + // # Focus + const focusedId = ref(null) + const { mainStatus: focusedStatus } = useMainStatus(focusedId) + const setFocused = (id) => { + focusedId.value = id + console.log('SF', id) + } + provide('focusedId', focusedId) + + watch(mainStatus, (newStatus, oldStatus) => { + setFocused(newStatus.id) + const newConversationId = newStatus?.statusnet_conversation_id + const oldConversationId = oldStatus?.statusnet_conversation_id + if ( + newConversationId && + oldConversationId && + newConversationId === oldConversationId + ) { + } else { + // resetDisplayState() + // fetchConversation() + } + }) + watch(expanded, (value) => { + setFocused(value ? statusId.value : null) + }, { immediate: true }) + + watch( + focusedStatus, + (newVal, oldVal) => { + if (!newVal) return + if (newVal?.id === oldVal?.id) return // prevents infinite loop + if (!streamingEnabled.value) { + useStatusesStore().fetchStatus(newVal.id) + } + + useStatusesStore().fetchFavsAndRepeats(newVal.id) + useStatusesStore().fetchEmojiReactions(newVal.id) + }, + { immediate: true }, ) const sortById = (a, b) => { @@ -43,7 +86,9 @@ export function useConversation(statusId, expanded) { return idA < idB ? -1 : 1 } } - const conversationId = computed(() => getConversationId(statusId.value)) + const conversationId = computed( + () => mainStatus.value.statusnet_conversation_id, + ) const conversation = computed(() => { if (!currentStatus.value) { return [] @@ -117,6 +162,8 @@ export function useConversation(statusId, expanded) { } return { + focusedId, + setFocused, currentStatus, mainStatus, conversation,