conversation composable + virtual scrolling for trees (somewhat)
This commit is contained in:
parent
218c2ca561
commit
a24d2a1bba
6 changed files with 259 additions and 168 deletions
124
src/composables/useConversation.js
Normal file
124
src/composables/useConversation.js
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
import { get } from 'lodash-es'
|
||||
import { computed, provide, ref } from 'vue'
|
||||
|
||||
import { useOAuthStore } from 'src/stores/oauth.js'
|
||||
import { useStatusesStore } from 'src/stores/statuses.js'
|
||||
|
||||
import {
|
||||
fetchConversation as apiFetchConversation,
|
||||
fetchStatus as apiFetchStatus,
|
||||
} 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 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 conversationId = computed(() => getConversationId(statusId.value))
|
||||
const conversation = computed(() => {
|
||||
if (!currentStatus.value) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (!expanded.value) {
|
||||
return [currentStatus.value]
|
||||
}
|
||||
|
||||
const conversation = useStatusesStore().conversations.get(
|
||||
conversationId.value,
|
||||
)
|
||||
|
||||
return [...conversation.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,
|
||||
})
|
||||
} 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
currentStatus,
|
||||
conversation,
|
||||
replies,
|
||||
getReplies,
|
||||
fetchConversation,
|
||||
loadError,
|
||||
}
|
||||
}
|
||||
|
|
@ -63,11 +63,11 @@ export function useVirtualScrolling(conversation, body) {
|
|||
}
|
||||
|
||||
// Scrolling
|
||||
const { y: topScrollBoundary } = useScrollPosition()
|
||||
const { y: scrollY } = useScrollPosition()
|
||||
const { height: windowHeight } = useWindowSize()
|
||||
|
||||
const realTopScrollBoundary = ref(0)
|
||||
const realBottomScrollBoundary = ref(0)
|
||||
const topScrollBoundary = ref(0)
|
||||
const bottomScrollBoundary = ref(0)
|
||||
const updateBoundaries = () => {
|
||||
if (!body.value) return // Not mounted yet
|
||||
|
||||
|
|
@ -76,11 +76,11 @@ export function useVirtualScrolling(conversation, body) {
|
|||
const distanceItemTopToWindowTop = 0 - top
|
||||
const distanceItemTopToWindowBottom = windowHeight.value - top
|
||||
|
||||
realTopScrollBoundary.value = distanceItemTopToWindowTop
|
||||
realBottomScrollBoundary.value = distanceItemTopToWindowBottom
|
||||
topScrollBoundary.value = distanceItemTopToWindowTop
|
||||
bottomScrollBoundary.value = distanceItemTopToWindowBottom
|
||||
}
|
||||
watch(windowHeight, updateBoundaries)
|
||||
watch(topScrollBoundary, updateBoundaries)
|
||||
watch(scrollY, updateBoundaries)
|
||||
watch(totalHeight, updateBoundaries)
|
||||
onMounted(updateBoundaries)
|
||||
|
||||
|
|
@ -113,17 +113,17 @@ export function useVirtualScrolling(conversation, body) {
|
|||
const itemBottomBoundary = heightChartItem.top + heightChartItem.height
|
||||
const itemTopBoundary = heightChartItem.top
|
||||
|
||||
const finalTopScrollBoundary = realTopScrollBoundary.value - buffer.value
|
||||
const finalTopScrollBoundary = topScrollBoundary.value - buffer.value
|
||||
const finalBottomScrollBoundary =
|
||||
realBottomScrollBoundary.value + buffer.value
|
||||
bottomScrollBoundary.value + buffer.value
|
||||
|
||||
// To be visible, item's bottom boundary shoud be below top scroll boundary)
|
||||
const belowTopBoundary = itemBottomBoundary > finalTopScrollBoundary
|
||||
const isBelowTopBoundary = itemBottomBoundary > finalTopScrollBoundary
|
||||
// To be visible, item's top boundary shoud be above bottom scroll boundary)
|
||||
const aboveBottomBoundary = itemTopBoundary < finalBottomScrollBoundary
|
||||
const isAboveBottomBoundary = itemTopBoundary < finalBottomScrollBoundary
|
||||
// This accounts for the case where item's boundaries exceed scroll boundary
|
||||
|
||||
heightChartItem.visible = belowTopBoundary && aboveBottomBoundary
|
||||
heightChartItem.visible = isBelowTopBoundary && isAboveBottomBoundary
|
||||
})
|
||||
|
||||
// Group invisible statuses into spacers
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue