diff --git a/app/soapbox/entity-store/hooks/useEntity.ts b/app/soapbox/entity-store/hooks/useEntity.ts index 37027f73f..63447ae67 100644 --- a/app/soapbox/entity-store/hooks/useEntity.ts +++ b/app/soapbox/entity-store/hooks/useEntity.ts @@ -59,4 +59,5 @@ function useEntity( export { useEntity, + type UseEntityOpts, }; \ No newline at end of file diff --git a/app/soapbox/entity-store/hooks/useEntityLookup.ts b/app/soapbox/entity-store/hooks/useEntityLookup.ts new file mode 100644 index 000000000..ab0ea0b41 --- /dev/null +++ b/app/soapbox/entity-store/hooks/useEntityLookup.ts @@ -0,0 +1,66 @@ +import { useEffect } from 'react'; +import { z } from 'zod'; + +import { useAppDispatch, useAppSelector, useLoading } from 'soapbox/hooks'; +import { type RootState } from 'soapbox/store'; + +import { importEntities } from '../actions'; +import { Entity } from '../types'; + +import { EntityFn } from './types'; +import { type UseEntityOpts } from './useEntity'; + +/** Entities will be filtered through this function until it returns true. */ +type LookupFn = (entity: TEntity) => boolean + +function useEntityLookup( + entityType: string, + lookupFn: LookupFn, + entityFn: EntityFn, + opts: UseEntityOpts = {}, +) { + const { schema = z.custom() } = opts; + + const dispatch = useAppDispatch(); + const [isFetching, setPromise] = useLoading(true); + + const entity = useAppSelector(state => findEntity(state, entityType, lookupFn)); + const isLoading = isFetching && !entity; + + const fetchEntity = async () => { + try { + const response = await setPromise(entityFn()); + const entity = schema.parse(response.data); + dispatch(importEntities([entity], entityType)); + } catch (e) { + // do nothing + } + }; + + useEffect(() => { + if (!entity || opts.refetch) { + fetchEntity(); + } + }, []); + + return { + entity, + fetchEntity, + isFetching, + isLoading, + }; +} + +function findEntity( + state: RootState, + entityType: string, + lookupFn: LookupFn, +) { + const cache = state.entities[entityType]; + + if (cache) { + return (Object.values(cache.store) as TEntity[]).find(lookupFn); + } +} + +export default useEntityLookup; \ No newline at end of file