Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85831373
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
6 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/composables/useConversation.js b/src/composables/useConversation.js
index 24d8bae530..e2aca729ff 100644
--- a/src/composables/useConversation.js
+++ b/src/composables/useConversation.js
@@ -1,176 +1,206 @@
import { storeToRefs } from 'pinia'
import { computed, provide, ref, watch, nextTick } 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,
fetchStatus as apiFetchStatus,
} from 'src/api/public.js'
import { WSConnectionStatus } from 'src/api/websocket.js'
export function useConversation(statusId, expanded) {
const loadError = ref(null)
const { status: currentStatus, mainStatus } = useMainStatus(statusId)
const mainStatusId = computed(() => mainStatus.value?.id)
// # Config
const { mergedConfig } = storeToRefs(useMergedConfigStore())
const { mastoUserSocketStatus } = storeToRefs(useStreamingStore())
const streamingEnabled = computed(
() =>
mergedConfig.value.useStreamingApi &&
mastoUserSocketStatus === WSConnectionStatus.JOINED,
)
watch(statusId, (neu, old) => {
if (neu !== old) {
fetchConversation()
}
})
const sortById = (a, b) => {
const idA = a.type === 'retweet' ? a.retweeted_status.id : a.id
const idB = b.type === 'retweet' ? b.retweeted_status.id : b.id
const seqA = Number(idA)
const seqB = Number(idB)
const isSeqA = !Number.isNaN(seqA)
const isSeqB = !Number.isNaN(seqB)
if (isSeqA && isSeqB) {
return seqA < seqB ? -1 : 1
} else if (isSeqA && !isSeqB) {
return -1
} else if (!isSeqA && isSeqB) {
return 1
} else {
return idA < idB ? -1 : 1
}
}
const fullyLoaded = ref(false)
const conversationId = computed(
() => mainStatus.value?.statusnet_conversation_id,
)
watch(conversationId, (neu, old) => {
if (neu !== old) fullyLoaded.value = false
})
const conversation = computed(() => {
if (!currentStatus.value) {
return []
}
if (!expanded.value || !fullyLoaded.value) {
return [currentStatus.value]
}
+ /* It took me a week or so to figure this out.
+ *
+ * Virtual scrolling can compensate for posts being prepended to content,
+ * and prepended posts changing height. The problem is that it has to happen
+ * in prepended (i.e. either above visible post and/or above screen boundary)
+ *
+ * Here's the problem: showing conversation from store can be broken. UI might
+ * already know that some posts belong to a conversation, because you were
+ * mentioned in it, but doesn't know the rest of it. It ends up displaying this
+ * "partial" conversation, and then the rest loads in.
+ *
+ * Problem is, this partial conversation can be very fragmented, with missing
+ * pieces appearing in-between posts. These pieces don't have proper heights
+ * assigned to them yet but their neigbours do and neither me nor virtual
+ * scrolling knows how to compensate for it, it ends up either not compensating
+ * or compensating wrong.
+ *
+ * Using "fullyLoaded" ref helps with this, to ensure that we have stable
+ * conversation expansion process of focused post -> entire convo
+ *
+ * With fullyLoaded:
+ * **id:3** -> id:1 id:2 id:3 id:4 id:5 id:6
+ *
+ * Without fullyLoaded:
+ * id:1 **id:3** id:5 -> id:1 id:2 **id:3** id:4 id:5 id:6
+ * (focused post is **id:3**)
+ *
+ * After initial load, fullyLoaded remains true, allowing newer updates to
+ * appear in conversation.
+ */
const fullConversation = useStatusesStore().conversations.get(
conversationId.value,
)
return [...fullConversation.keys()]
.map((k) => useStatusesStore().allStatuses.get(k))
.filter((status) => status.type != 'repeat') // Old backend behavior?
.toSorted(sortById)
})
const replies = computed(() =>
conversation.value.reduce(
(result, { id, in_reply_to_status_id: irid }, index) => {
if (irid) {
if (!result.has(irid)) {
result.set(irid, new Set())
}
result.get(irid).add({
name: `#${index}`,
id,
})
}
return result
},
new Map(),
),
)
const getReplies = (id) => replies.value.get(id) ?? new Set()
provide('conversation', conversation)
provide('replies', replies)
const fetchConversation = async () => {
if (currentStatus.value) {
const {
data: { ancestors, descendants },
timestamp,
} = await apiFetchConversation({
id: statusId.value,
credentials: useOAuthStore().token,
})
useStatusesStore().addNewStatuses({ statuses: ancestors, timestamp })
useStatusesStore().addNewStatuses({
statuses: descendants,
timestamp,
})
await nextTick()
fullyLoaded.value = true
} else {
try {
loadError.value = null
const { data: status } = await apiFetchStatus({
id: statusId.value,
credentials: useOAuthStore().token,
})
useStatusesStore().addNewStatuses({ statuses: [status] })
fetchConversation()
} catch (error) {
console.error(error)
loadError.value = error
}
}
}
// # Focus
const focused = ref(null)
const { mainStatus: focusedStatus } = useMainStatus(focused)
const setFocused = (id) => {
focused.value = id
}
watch(statusId, (val) => setFocused(val), { immediate: true })
const focusedId = computed(() => (expanded.value && fullyLoaded.value) ? focusedStatus.value?.id : null)
provide('focusedId', focusedId)
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 },
)
return {
focusedId,
conversationId,
setFocused,
currentStatus,
mainStatus,
conversation,
replies,
getReplies,
fetchConversation,
loadError,
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Nov 1, 2:11 PM (8 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1803094
Default Alt Text
(6 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline