feat: global relationships batching (#24)
parent
5dc79df091
commit
ac156034d1
|
@ -1,25 +1,21 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Account } from 'masto'
|
import type { Account } from 'masto'
|
||||||
|
|
||||||
const { account, following } = defineProps<{
|
const { account } = defineProps<{
|
||||||
account: Account
|
account: Account
|
||||||
following?: boolean
|
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const masto = await useMasto()
|
const masto = await useMasto()
|
||||||
|
|
||||||
let isFollowing = $ref<boolean | undefined>(following)
|
const relationship = $(useRelationship(account))
|
||||||
watch($$(following), () => {
|
|
||||||
isFollowing = following
|
|
||||||
})
|
|
||||||
|
|
||||||
function unfollow() {
|
function unfollow() {
|
||||||
masto.accounts.unfollow(account.id)
|
masto.accounts.unfollow(account.id)
|
||||||
isFollowing = false
|
relationship!.following = false
|
||||||
}
|
}
|
||||||
function follow() {
|
function follow() {
|
||||||
masto.accounts.follow(account.id)
|
masto.accounts.follow(account.id)
|
||||||
isFollowing = true
|
relationship!.following = true
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -27,8 +23,8 @@ function follow() {
|
||||||
<div flex justify-between>
|
<div flex justify-between>
|
||||||
<AccountInfo :account="account" p3 />
|
<AccountInfo :account="account" p3 />
|
||||||
<div h-full p5>
|
<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-if="relationship?.following === 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-else-if="relationship?.following === false" color-gray hover:color-purple hover:cursor-pointer i-ri:user-follow-fill @click="follow" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -5,22 +5,14 @@ const { account } = defineProps<{
|
||||||
account: Account
|
account: Account
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
let isFollowing = $ref<boolean | undefined>()
|
const relationship = $(useRelationship(account))
|
||||||
let isFollowedBy = $ref<boolean | undefined>()
|
|
||||||
|
|
||||||
let masto: MastoClient
|
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() {
|
async function toggleFollow() {
|
||||||
isFollowing = !isFollowing
|
relationship!.following = !relationship!.following
|
||||||
masto ??= await useMasto()
|
masto ??= await useMasto()
|
||||||
await masto.accounts[isFollowing ? 'follow' : 'unfollow'](account.id)
|
await masto.accounts[relationship!.following ? 'follow' : 'unfollow'](account.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdAt = $computed(() => {
|
const createdAt = $computed(() => {
|
||||||
|
@ -50,9 +42,9 @@ const createdAt = $computed(() => {
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
<div flex gap-2>
|
<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">
|
<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>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<!-- <button flex gap-1 items-center w-full rounded op75 hover="op100 text-purple" group>
|
<!-- <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<{
|
const { paginator } = defineProps<{
|
||||||
paginator: Paginator<any, Account[]>
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<CommonPaginator
|
<CommonPaginator
|
||||||
:paginator="paginator"
|
:paginator="paginator"
|
||||||
border="t border"
|
border="t border"
|
||||||
@items="onNewItems"
|
|
||||||
>
|
>
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<AccountCard
|
<AccountCard
|
||||||
:account="item"
|
:account="item"
|
||||||
:following="metadataMap[item.id]?.following"
|
|
||||||
border="b border" py-1
|
border="b border" py-1
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Ref } from 'vue'
|
|
||||||
import type { Paginator } from 'masto'
|
import type { Paginator } from 'masto'
|
||||||
|
|
||||||
const { paginator, keyProp = 'id' } = defineProps<{
|
const { paginator, keyProp = 'id' } = defineProps<{
|
||||||
|
@ -7,13 +6,7 @@ const { paginator, keyProp = 'id' } = defineProps<{
|
||||||
keyProp?: string
|
keyProp?: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits(['items'])
|
const { items, state, endAnchor, error } = usePaginator(paginator)
|
||||||
|
|
||||||
const { items, newItems, state, endAnchor, error } = usePaginator(paginator)
|
|
||||||
|
|
||||||
watch(newItems, () => {
|
|
||||||
emit('items', newItems.value)
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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) {
|
export function getDisplayName(account: Account) {
|
||||||
return account.displayName || account.username
|
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