digitalfabrik/integreat-app

View on GitHub
native/src/hooks/useLoadExtraCityContent.ts

Summary

Maintainability
A
25 mins
Test Coverage
F
42%
import { useCallback } from 'react'

import { loadFromEndpoint, ReturnType, useLoadAsync, Endpoint, ErrorCode } from 'shared/api'

import { determineApiUrl } from '../utils/helpers'
import useLoadCityContent, { CityContentData } from './useLoadCityContent'

type Params = {
  cityCode: string
  languageCode: string
}

type Load<T extends object> =
  | {
      createEndpoint: (baseUrl: string) => Endpoint<{ city: string; language: string }, T>
      load?: null
    }
  | {
      load: () => Promise<T>
      createEndpoint?: null
    }

type UseLoadExtraCityContentParams<T extends object> = Params & Load<T>

type UseLoadExtraCityContentData<T> = CityContentData & { extra: T }
export type UseLoadExtraCityContentReturn<T> = Omit<
  Omit<ReturnType<UseLoadExtraCityContentData<T>>, 'error'>,
  'setData'
> & {
  error: ErrorCode | Error | null
}

/**
 * Hook to load city content and some other data at the same time.
 * Either a function creating an endpoint or a regular async function to load the data has to be passed.
 */
const useLoadExtraCityContent = <T extends object>({
  cityCode,
  languageCode,
  createEndpoint,
  load,
}: UseLoadExtraCityContentParams<T>): UseLoadExtraCityContentReturn<T> => {
  const { refresh: refreshCityContent, ...cityContentReturn } = useLoadCityContent({ cityCode, languageCode })
  const loadAsync = useCallback(
    async () =>
      load
        ? load()
        : loadFromEndpoint(createEndpoint, await determineApiUrl(), { city: cityCode, language: languageCode }),
    [createEndpoint, load, cityCode, languageCode],
  )
  const { refresh: refreshExtra, ...extraReturn } = useLoadAsync(loadAsync)

  const refresh = useCallback(() => {
    refreshCityContent()
    refreshExtra()
  }, [refreshCityContent, refreshExtra])

  return {
    loading: cityContentReturn.loading || extraReturn.loading,
    error: cityContentReturn.error ?? extraReturn.error,
    refresh,
    data: cityContentReturn.data && extraReturn.data ? { ...cityContentReturn.data, extra: extraReturn.data } : null,
  }
}

export default useLoadExtraCityContent