Cache vote creators

This commit is contained in:
MartinBraquet
2025-10-26 13:53:46 +01:00
parent 4c4f2e720d
commit 7cf83f65c3
3 changed files with 60 additions and 62 deletions

View File

@@ -4,10 +4,9 @@ import {Row as rowFor} from 'common/supabase/utils'
import {Content} from 'web/components/widgets/editor'
import {JSONContent} from '@tiptap/core'
import {VoteButtons} from 'web/components/votes/vote-buttons'
import {getVoteCreator} from "web/lib/supabase/votes";
import {useEffect, useState} from "react";
import Link from "next/link";
import {STATUS_CHOICES} from "common/votes/constants";
import {useUserInStore} from "web/hooks/use-user-supabase";
export type Vote = rowFor<'votes'> & {
votes_for: number
@@ -17,15 +16,22 @@ export type Vote = rowFor<'votes'> & {
status?: string
}
function Username(props: {
creatorId: string
}) {
const {creatorId} = props
const creator = useUserInStore(creatorId)
return <>
{creator?.username &&
<Link href={`/${creator.username}`} className="custom-link">{creator.username}</Link>}
</>;
}
export function VoteItem(props: {
vote: Vote
onVoted?: () => void | Promise<void>
}) {
const {vote, onVoted} = props
const [creator, setCreator] = useState<any>(null)
useEffect(() => {
getVoteCreator(vote.creator_id).then(setCreator)
}, [vote.creator_id])
// console.debug('creator', creator, vote)
return (
<Col className={'mb-4 rounded-lg border border-canvas-200 p-4'}>
@@ -37,8 +43,7 @@ export function VoteItem(props: {
</Col>
<Row className={'gap-2 mt-2 items-center justify-between w-full custom-link flex-wrap'}>
{!!vote.priority ? <div>Priority: {vote.priority.toFixed(0)}%</div> : <p></p>}
{!vote.is_anonymous && creator?.username &&
<Link href={`/${creator.username}`} className="custom-link">{creator.username}</Link>}
{!vote.is_anonymous && <Username creatorId={vote.creator_id}/>}
</Row>
</Col>
</Row>

View File

@@ -1,15 +1,11 @@
import { useEffect, useRef, useState } from 'react'
import { useEffectCheckEquality } from './use-effect-check-equality'
import { uniqBy, uniq } from 'lodash'
import { usePersistentLocalState } from 'web/hooks/use-persistent-local-state'
import { filterDefined } from 'common/util/array'
import { usePersistentInMemoryState } from './use-persistent-in-memory-state'
import {
DisplayUser,
getDisplayUsers,
getFullUserById,
} from 'web/lib/supabase/users'
import { FullUser } from 'common/api/user-types'
import {useEffect, useState} from 'react'
import {useEffectCheckEquality} from './use-effect-check-equality'
import {uniq, uniqBy} from 'lodash'
import {usePersistentLocalState} from 'web/hooks/use-persistent-local-state'
import {filterDefined} from 'common/util/array'
import {usePersistentInMemoryState} from './use-persistent-in-memory-state'
import {DisplayUser, getDisplayUsers, getFullUserById,} from 'web/lib/supabase/users'
import {FullUser} from 'common/api/user-types'
export function useUserById(userId: string | undefined) {
const [user, setUser] = usePersistentInMemoryState<
@@ -25,8 +21,8 @@ export function useUserById(userId: string | undefined) {
return user
}
const cache = new Map<string, DisplayUser | null>()
// const cache = new Map<string, DisplayUser | null>()
//
// export function useDisplayUserById(userId: string | undefined) {
// const [user, setUser] = usePersistentInMemoryState<
// DisplayUser | null | undefined
@@ -51,45 +47,48 @@ const cache = new Map<string, DisplayUser | null>()
// return user
// }
export function useUsers(userIds: string[]) {
const [users, setUsers] = useState<(DisplayUser | null)[] | undefined>(
undefined
)
const requestIdRef = useRef(0)
useEffectCheckEquality(() => {
const requestId = ++requestIdRef.current
const missing = userIds.filter((id) => !cache.has(id))
getDisplayUsers(missing).then((users) => {
users.forEach((user) => {
cache.set(user.id, user)
})
if (requestId !== requestIdRef.current) return
setUsers(userIds.map((id) => cache.get(id) ?? null))
})
}, [userIds])
return users
}
// export function useUsers(userIds: string[]) {
// const [users, setUsers] = useState<(DisplayUser | null)[] | undefined>(
// undefined
// )
//
// const requestIdRef = useRef(0)
// useEffectCheckEquality(() => {
// const requestId = ++requestIdRef.current
//
// const missing = userIds.filter((id) => !cache.has(id))
//
// getDisplayUsers(missing).then((users) => {
// users.forEach((user) => {
// cache.set(user.id, user)
// })
// if (requestId !== requestIdRef.current) return
// setUsers(userIds.map((id) => cache.get(id) ?? null))
// })
// }, [userIds])
//
// return users
// }
// TODO: decide whether in-memory or in-localstorage is better and stick to it
export function useUsersInStore(
userIds: string[],
userIds: (string | null)[],
key: string,
limit?: number
) {
const validUserIds = Array.from(new Set(userIds.filter((id): id is string => !!id)))
const [users, setUsers] = usePersistentLocalState<DisplayUser[] | undefined>(
undefined,
'use-users-in-local-storage' + key
'use-users-in-local-storage-' + key
)
// Fetch all users at least once on load.
const [userIdsFetched, setUserIdsFetched] = useState<string[]>([])
const fetchedSet = new Set(userIdsFetched)
const userIdsNotFetched = userIds.filter((id) => !fetchedSet.has(id))
const userIdsNotFetched = validUserIds.filter((id) => !fetchedSet.has(id))
const userIdsToFetch = limit
? userIdsNotFetched.slice(0, limit)
: userIdsNotFetched
@@ -104,5 +103,12 @@ export function useUsersInStore(
})
}, [userIdsToFetch])
return users?.filter((user) => userIds.includes(user?.id))
return users?.filter((user) => validUserIds.includes(user?.id))
}
export function useUserInStore(userId: string | null) {
if (!userId) return null // early return avoids invalid key + hook calls are safe
const users = useUsersInStore(userId ? [userId] : [], userId ?? 'empty')
return users?.[0] ?? null
}

View File

@@ -1,10 +1,9 @@
import {run} from 'common/supabase/utils'
import {db} from 'web/lib/supabase/db'
import {OrderBy} from "common/votes/constants";
export const getVotes = async (params: { orderBy: OrderBy }) => {
const { orderBy } = params
const {orderBy} = params
const {data, error} = await db.rpc('get_votes_with_results' as any, {
order_by: orderBy,
});
@@ -12,15 +11,3 @@ export const getVotes = async (params: { orderBy: OrderBy }) => {
return data
}
export const getVoteCreator = async (creatorId: string) => {
const {data} = await run(
db
.from('users')
.select(`id, name, username`)
.eq('id', creatorId)
.limit(1)
)
return data[0]
}