feat: global relationships batching (#24)
parent
5dc79df091
commit
ac156034d1
|
@ -1,25 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
import type { Account } from 'masto'
|
||||
|
||||
const { account, following } = defineProps<{
|
||||
const { account } = defineProps<{
|
||||
account: Account
|
||||
following?: boolean
|
||||
}>()
|
||||
|
||||
const masto = await useMasto()
|
||||
|
||||
let isFollowing = $ref<boolean | undefined>(following)
|
||||
watch($$(following), () => {
|
||||
isFollowing = following
|
||||
})
|
||||
const relationship = $(useRelationship(account))
|
||||
|
||||
function unfollow() {
|
||||
masto.accounts.unfollow(account.id)
|
||||
isFollowing = false
|
||||
relationship!.following = false
|
||||
}
|
||||
function follow() {
|
||||
masto.accounts.follow(account.id)
|
||||
isFollowing = true
|
||||
relationship!.following = true
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -27,8 +23,8 @@ function follow() {
|
|||
<div flex justify-between>
|
||||
<AccountInfo :account="account" p3 />
|
||||
<div h-full p5>
|
||||
<div v-if="isFollowing === true" color-purple hover:color-gray hover:cursor-pointer i-ri:user-unfollow-fill @click="unfollow" />
|
||||
<div v-else-if="isFollowing === false" color-gray hover:color-purple hover:cursor-pointer i-ri:user-follow-fill @click="follow" />
|
||||
<div v-if="relationship?.following === true" color-purple hover:color-gray hover:cursor-pointer i-ri:user-unfollow-fill @click="unfollow" />
|
||||
<div v-else-if="relationship?.following === false" color-gray hover:color-purple hover:cursor-pointer i-ri:user-follow-fill @click="follow" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -5,22 +5,14 @@ const { account } = defineProps<{
|
|||
account: Account
|
||||
}>()
|
||||
|
||||
let isFollowing = $ref<boolean | undefined>()
|
||||
let isFollowedBy = $ref<boolean | undefined>()
|
||||
const relationship = $(useRelationship(account))
|
||||
|
||||
let masto: MastoClient
|
||||
|
||||
onMounted(async () => {
|
||||
masto ??= await useMasto()
|
||||
const relationship = (await masto.accounts.fetchRelationships([account.id]))[0]
|
||||
isFollowing = relationship.following
|
||||
isFollowedBy = relationship.followedBy
|
||||
})
|
||||
|
||||
async function toggleFollow() {
|
||||
isFollowing = !isFollowing
|
||||
relationship!.following = !relationship!.following
|
||||
masto ??= await useMasto()
|
||||
await masto.accounts[isFollowing ? 'follow' : 'unfollow'](account.id)
|
||||
await masto.accounts[relationship!.following ? 'follow' : 'unfollow'](account.id)
|
||||
}
|
||||
|
||||
const createdAt = $computed(() => {
|
||||
|
@ -50,9 +42,9 @@ const createdAt = $computed(() => {
|
|||
</NuxtLink>
|
||||
</div>
|
||||
<div flex gap-2>
|
||||
<button flex gap-1 items-center w-full rounded op75 hover="op100 text-white b-purple" group @click="toggleFollow">
|
||||
<button v-if="relationship" flex gap-1 items-center w-full rounded op75 hover="op100 text-white b-purple" group @click="toggleFollow">
|
||||
<div rounded w-30 p2 group-hover="bg-rose/10">
|
||||
{{ isFollowing ? 'Unfollow' : isFollowedBy ? 'Follow back' : 'Follow' }}
|
||||
{{ relationship?.following ? 'Unfollow' : relationship?.followedBy ? 'Follow back' : 'Follow' }}
|
||||
</div>
|
||||
</button>
|
||||
<!-- <button flex gap-1 items-center w-full rounded op75 hover="op100 text-purple" group>
|
||||
|
|
|
@ -4,30 +4,16 @@ import type { Account, Paginator } from 'masto'
|
|||
const { paginator } = defineProps<{
|
||||
paginator: Paginator<any, Account[]>
|
||||
}>()
|
||||
|
||||
const masto = await useMasto()
|
||||
|
||||
const metadataMap = $ref<{ [key: string]: { following?: boolean } }>({})
|
||||
async function onNewItems(items: Account[]) {
|
||||
for (const item of items)
|
||||
metadataMap[item.id] = { following: undefined }
|
||||
|
||||
const relationships = await masto.accounts.fetchRelationships(items.map(item => item.id))
|
||||
for (const rel of relationships)
|
||||
metadataMap[rel.id].following = rel.following
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonPaginator
|
||||
:paginator="paginator"
|
||||
border="t border"
|
||||
@items="onNewItems"
|
||||
>
|
||||
<template #default="{ item }">
|
||||
<AccountCard
|
||||
:account="item"
|
||||
:following="metadataMap[item.id]?.following"
|
||||
border="b border" py-1
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import type { Paginator } from 'masto'
|
||||
|
||||
const { paginator, keyProp = 'id' } = defineProps<{
|
||||
|
@ -7,13 +6,7 @@ const { paginator, keyProp = 'id' } = defineProps<{
|
|||
keyProp?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['items'])
|
||||
|
||||
const { items, newItems, state, endAnchor, error } = usePaginator(paginator)
|
||||
|
||||
watch(newItems, () => {
|
||||
emit('items', newItems.value)
|
||||
})
|
||||
const { items, state, endAnchor, error } = usePaginator(paginator)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,5 +1,39 @@
|
|||
import type { Account } from 'masto'
|
||||
import type { Ref } from 'vue'
|
||||
import type { Account, MastoClient, Relationship } from 'masto'
|
||||
|
||||
export function getDisplayName(account: Account) {
|
||||
return account.displayName || account.username
|
||||
}
|
||||
|
||||
// Batch requests for relationships when used in the UI
|
||||
// We don't want to hold to old values, so every time a Relationship is needed it
|
||||
// is requested again from the server to show the latest state
|
||||
|
||||
const requestedRelationships = new Map<string, Ref<Relationship | undefined> >()
|
||||
let timeoutHandle: NodeJS.Timeout | undefined
|
||||
|
||||
export function useRelationship(account: Account): Ref<Relationship | undefined> {
|
||||
let relationship = requestedRelationships.get(account.id)
|
||||
if (relationship)
|
||||
return relationship
|
||||
relationship = ref<Relationship | undefined>()
|
||||
requestedRelationships.set(account.id, relationship)
|
||||
if (timeoutHandle)
|
||||
clearTimeout(timeoutHandle)
|
||||
timeoutHandle = setTimeout(() => {
|
||||
timeoutHandle = undefined
|
||||
fetchRelationships()
|
||||
}, 100)
|
||||
return relationship
|
||||
}
|
||||
|
||||
async function fetchRelationships() {
|
||||
const masto = await useMasto()
|
||||
|
||||
const requested = Array.from(requestedRelationships.entries())
|
||||
requestedRelationships.clear()
|
||||
|
||||
const relationships = await masto.accounts.fetchRelationships(requested.map(([id]) => id))
|
||||
for (let i = 0; i < requested.length; i++)
|
||||
requested[i][1].value = relationships[i]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue