fixes for reprööts/favorites display

This commit is contained in:
Henry Jameson 2026-08-28 15:38:05 +03:00
commit 5854b222bd
6 changed files with 61 additions and 57 deletions

View file

@ -1,14 +1,19 @@
import UserAvatar from 'src/components/user_avatar/user_avatar.vue' import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceStore } from 'src/stores/instance.js'
import { useUsersStore } from 'src/stores/users.js'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
const AvatarList = { const AvatarList = {
props: ['users'], props: {
userIds: Set,
},
computed: { computed: {
slicedUsers() { slicedUsers() {
return this.users ? this.users.slice(0, 15) : [] return [...(this.userIds ?? [])]
.slice(0, 15)
.map((id) => useUsersStore().findUser(id))
}, },
}, },
components: { components: {

View file

@ -1,4 +1,3 @@
import { uniqBy } from 'lodash'
import { defineAsyncComponent } from 'vue' import { defineAsyncComponent } from 'vue'
import AvatarList from 'src/components/avatar_list/avatar_list.vue' import AvatarList from 'src/components/avatar_list/avatar_list.vue'
@ -179,6 +178,12 @@ const Status = {
useUserHighlightStore().get(this.repeater.screen_name), useUserHighlightStore().get(this.repeater.screen_name),
) )
}, },
favoritedBy() {
return useStatusesStore().favs.get(this.mainStatus.id) ?? new Set()
},
repeatedBy() {
return useStatusesStore().repeats.get(this.mainStatus.id) ?? new Set()
},
userStyle() { userStyle() {
if (this.noHeading) return if (this.noHeading) return
return highlightStyle(useUserHighlightStore().get(this.user.screen_name)) return highlightStyle(useUserHighlightStore().get(this.user.screen_name))
@ -383,12 +388,10 @@ const Status = {
} }
}, },
combinedFavsAndRepeatsUsers() { combinedFavsAndRepeatsUsers() {
// Use the status from the global status repository since favs and repeats are saved in it return new Set([
const combinedUsers = [].concat( ...this.favoritedBy,
this.mainStatus.favoritedBy, ...this.repeatedBy,
this.mainStatus.rebloggedBy, ])
)
return uniqBy(combinedUsers, 'id')
}, },
tags() { tags() {
return [...this.status.tags] return [...this.status.tags]
@ -403,7 +406,7 @@ const Status = {
return ( return (
!this.hidePostStats && !this.hidePostStats &&
this.focused && this.focused &&
(this.combinedFavsAndRepeatsUsers.length > 0 || (this.combinedFavsAndRepeatsUsers.size > 0 ||
this.mainStatus.quotes_count) this.mainStatus.quotes_count)
) )
}, },
@ -588,20 +591,18 @@ const Status = {
// refetch repeats when repeat_num is changed in any way // refetch repeats when repeat_num is changed in any way
if ( if (
this.focused && this.focused &&
this.mainStatus.rebloggedBy && this.repeatedBy.size !== num
this.mainStatus.rebloggedBy.length !== num
) { ) {
useStatusesStore().fetchRepeats(this.status.id) useStatusesStore().fetchRepeats(this.mainStatus.id)
} }
}, },
'mainStatus.fave_num': function (num) { 'mainStatus.fave_num': function (num) {
// refetch favs when fave_num is changed in any way // refetch favs when fave_num is changed in any way
if ( if (
this.focused && this.focused &&
this.mainStatus.favoritedBy && this.favoritedBy.size !== num
this.mainStatus.favoritedBy.length !== num
) { ) {
useStatusesStore().fetchFavs(this.status.id) useStatusesStore().fetchFavs(this.mainStatus.id)
} }
}, },
isSuspendable: function (suspend) { isSuspendable: function (suspend) {

View file

@ -464,26 +464,26 @@
> >
<div class="stats"> <div class="stats">
<UserListPopover <UserListPopover
v-if="mainStatus.rebloggedBy && mainStatus.rebloggedBy.length > 0" v-if="repeatedBy.size > 0"
:users="mainStatus.rebloggedBy" :user-ids="repeatedBy"
> >
<div class="stat-count"> <div class="stat-count">
<a class="stat-title">{{ $t('status.repeats') }}</a> <a class="stat-title">{{ $t('status.repeats') }}</a>
<div class="stat-number"> <div class="stat-number">
{{ mainStatus.rebloggedBy.length }} {{ repeatedBy.size }}
</div> </div>
</div> </div>
</UserListPopover> </UserListPopover>
<UserListPopover <UserListPopover
v-if="mainStatus.favoritedBy && mainStatus.favoritedBy.length > 0" v-if="favoritedBy.size > 0"
:users="mainStatus.favoritedBy" :user-ids="favoritedBy"
> >
<div <div
class="stat-count" class="stat-count"
> >
<a class="stat-title">{{ $t('status.favorites') }}</a> <a class="stat-title">{{ $t('status.favorites') }}</a>
<div class="stat-number"> <div class="stat-number">
{{ mainStatus.favoritedBy.length }} {{ favoritedBy.size }}
</div> </div>
</div> </div>
</UserListPopover> </UserListPopover>
@ -501,7 +501,7 @@
</div> </div>
</router-link> </router-link>
<div class="avatar-row"> <div class="avatar-row">
<AvatarList :users="combinedFavsAndRepeatsUsers" /> <AvatarList :user-ids="combinedFavsAndRepeatsUsers" />
</div> </div>
</div> </div>
</div> </div>

View file

@ -4,6 +4,7 @@ import UserAvatar from 'src/components/user_avatar/user_avatar.vue'
import { useInstanceStore } from 'src/stores/instance.js' import { useInstanceStore } from 'src/stores/instance.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js' import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useUsersStore } from 'src/stores/users.js'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
@ -14,7 +15,9 @@ library.add(faCircleNotch)
const UserListPopover = { const UserListPopover = {
name: 'UserListPopover', name: 'UserListPopover',
props: ['users'], props: {
userIds: Set
},
components: { components: {
UnicodeDomainIndicator, UnicodeDomainIndicator,
Popover, Popover,
@ -22,7 +25,7 @@ const UserListPopover = {
}, },
computed: { computed: {
usersCapped() { usersCapped() {
return this.users.slice(0, 16) return [...this.userIds].slice(0, 16).map((id) => useUsersStore().findUser(id))
}, },
allowNonSquareEmoji() { allowNonSquareEmoji() {
return useMergedConfigStore().mergedConfig.nonSquareEmoji return useMergedConfigStore().mergedConfig.nonSquareEmoji

View file

@ -9,7 +9,7 @@
</template> </template>
<template #content> <template #content>
<div class="user-list-popover"> <div class="user-list-popover">
<template v-if="users.length"> <template v-if="userIds.size > 0">
<router-link <router-link
v-for="(user) in usersCapped" v-for="(user) in usersCapped"
:key="user.id" :key="user.id"

View file

@ -36,6 +36,8 @@ export const defaultState = () => ({
conversations: new Map(), conversations: new Map(),
favorites: new Set(), favorites: new Set(),
socket: null, socket: null,
favs: new Map(),
repeats: new Map(),
}) })
export const useStatusesStore = defineStore('statuses', { export const useStatusesStore = defineStore('statuses', {
@ -189,60 +191,53 @@ export const useStatusesStore = defineStore('statuses', {
id, id,
credentials: useOAuthStore().token, credentials: useOAuthStore().token,
}).then(({ data: emojiReactions }) => { }).then(({ data: emojiReactions }) => {
this.addEmojiReactionsBy({ this.addEmojiReactionsBy(id, emojiReactions)
id,
emojiReactions,
})
}) })
}, },
fetchFavs(id) { fetchFavs(id) {
return fetchFavoritedByUsers({ return fetchFavoritedByUsers({
id, id,
credentials: useOAuthStore().token, credentials: useOAuthStore().token,
}).then(({ data: favoritedByUsers }) => }).then((result) => {
this.addFavs({ const users = useUsersStore().addNewUsers(result)
id, return this.addFavs(id, new Set(users.map(({ id }) => id)))
favoritedByUsers, })
}),
)
}, },
fetchRepeats(id) { fetchRepeats(id) {
return fetchRebloggedByUsers({ return fetchRebloggedByUsers({
id, id,
credentials: useOAuthStore().token, credentials: useOAuthStore().token,
}).then(({ data: rebloggedByUsers }) => }).then((result) => {
this.addRepeats({ const users = useUsersStore().addNewUsers(result)
id, return this.addRepeats(id, new Set(users.map(({ id }) => id)))
rebloggedByUsers, })
}),
)
}, },
fetchFavsAndRepeats(id) { fetchFavsAndRepeats(id) {
return Promise.all([this.fetchFavs(id), this.fetchRepeats(id)]) return Promise.all([this.fetchFavs(id), this.fetchRepeats(id)])
}, },
// Updates // Updates
addRepeats({ id, rebloggedByUsers }) { addRepeats(id, users) {
const currentUser = useUsersStore().currentUser const currentUser = useUsersStore().currentUser
const newStatus = this.allStatuses.get(id) const newStatus = this.allStatuses.get(id)
newStatus.rebloggedBy = rebloggedByUsers.filter(Boolean) this.repeats.set(id, users)
// repeats stats can be incorrect based on polling condition, let's update them using the most recent data
newStatus.repeat_num = newStatus.rebloggedBy.length // repeats stats can be incorrect based on polling
newStatus.repeated = !!newStatus.rebloggedBy.find( // condition, let's update them using the most recent data
({ id }) => currentUser?.id === id, newStatus.repeat_num = users.size
) newStatus.repeated = users.has(currentUser?.id)
}, },
addFavs({ id, favoritedByUsers }) { addFavs(id, users) {
const currentUser = useUsersStore().currentUser const currentUser = useUsersStore().currentUser
const newStatus = this.allStatuses.get(id) const newStatus = this.allStatuses.get(id)
newStatus.favoritedBy = favoritedByUsers.filter(Boolean) this.favs.set(id, users)
// favorites stats can be incorrect based on polling condition, let's update them using the most recent data
newStatus.fave_num = newStatus.favoritedBy.length // favorites stats can be incorrect based on polling
newStatus.favorited = !!newStatus.favoritedBy.find( // condition, let's update them using the most recent data
({ id }) => currentUser?.id === id, newStatus.fave_num = users.size
) newStatus.favorited = users.has(currentUser?.id)
}, },
addEmojiReactionsBy({ id, emojiReactions }) { addEmojiReactionsBy(id, emojiReactions) {
const status = this.allStatuses.get(id) const status = this.allStatuses.get(id)
status.emoji_reactions = emojiReactions status.emoji_reactions = emojiReactions
}, },