UNPKG

@applicaster/zapp-react-native-utils

Version:

Applicaster Zapp React Native utilities package

164 lines (130 loc) 4.97 kB
import { isNil, complement, compose, map, min, prop, take, uniq } from "ramda"; import { useDispatch } from "react-redux"; import * as React from "react"; import { useZappPipesFeeds } from "@applicaster/zapp-react-native-redux/hooks/useZappPipesFeeds"; import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes"; import { isNilOrEmpty } from "../../reactUtils/helpers"; import { ZappPipesSearchContext } from "@applicaster/zapp-react-native-ui-components/Contexts"; import { getInflatedDataSourceUrl, getSearchContext, } from "@applicaster/zapp-react-native-utils/reactHooks"; import { useScreenContext } from "../screen/useScreenContext"; type Options = { initialBatchSize?: number; riverId?: string; isNestedScreen?: boolean; }; const DEFAULT_BATCH_SIZE = 5; const dataOf: (feed: ZappPipesData) => ZappPipesData["data"] = prop("data"); const feedIsLoading: (feed: ZappPipesData) => ZappPipesData["loading"] = prop("loading"); const getErrorProp: (feed: ZappPipesData) => ZappPipesData["error"] = prop("error"); const feedHasData: (feed: ZappPipesData) => boolean = compose( complement(isNil), dataOf ); type ZappDataSourceSource = ZappDataSource["source"]; type ZappPipesCollection = Record<string, ZappPipesData>; const checkFeedIsReady = (feed: ZappPipesData): boolean => feed && !feedIsLoading(feed) && (feedHasData(feed) || !!getErrorProp(feed)); const filterEmptyData = (data) => { if (isNilOrEmpty(data) || isNilOrEmpty(data?.source)) return false; return true; }; const getData = (rawData) => rawData.component_type === "gallery-qb" ? rawData.ui_components[0].data : rawData.data; const extractData = compose(uniq, map(getData)); export const allFeedsIsReady = ( feeds: ZappPipesCollection, urls: (ZappDataSourceSource | null)[] ): boolean => urls.every((url) => isNil(url) ? true : url && feeds[url] ? checkFeedIsReady(feeds[url]) : false ); export const useBatchLoading = ( componentsToRender: { data?: ZappDataSource; component_type: string }[], options: Options ) => { const dispatch = useDispatch(); const { screen: screenContext, entry: entryContext } = useScreenContext(); const [searchContext] = ZappPipesSearchContext.useZappPipesContext(); const [hasEverBeenReady, setHasEverBeenReady] = React.useState(false); // if first component is gallery-qb, take only one component for initial load const takeSize = componentsToRender?.[0]?.component_type === "gallery-qb" ? 1 : min( options.initialBatchSize ?? DEFAULT_BATCH_SIZE, componentsToRender.length ); const takeBatchSize = React.useCallback(take(takeSize), [takeSize]); // Prepare inflated url for datasource const getUrl = React.useCallback( (data) => { const mappedFeed = data.mapping ? getInflatedDataSourceUrl({ source: data.source, contexts: { entry: entryContext, screen: screenContext, search: getSearchContext(searchContext, data.mapping), }, mapping: data.mapping, }) : data.source; return mappedFeed; }, [entryContext, screenContext, searchContext] ); // components for initial load const batchComponents = takeBatchSize(componentsToRender); // contains datasources from batch components loading const feedData: ZappDataSource[] = React.useMemo( () => extractData(batchComponents).filter(filterEmptyData), [batchComponents] ); // contains inflated urls from batch components loading const feedUrls: ZappDataSourceSource[] = React.useMemo( () => feedData.map((data) => { const mappedFeedUrl = getUrl(data); return mappedFeedUrl; }), [] ); const feeds = useZappPipesFeeds(feedUrls); // dispatch loadPipesData for each feed that is not loaded const runBatchLoading = React.useCallback(() => { batchComponents.forEach((rawData: any) => { // 1. get data (dataOf) const data = getData(rawData); // 2. filter out empty data filterEmptyData if (!filterEmptyData(data)) return false; // 3. filter out data that is already loaded (feeds[data.source]) & !feedHasData(feed) && !feedIsLoading(feed); const feed = feeds[data?.source]; if (!feed || (!feedHasData(feed) && !feedIsLoading(feed))) { const mappedFeedUrl = getUrl(data); if (mappedFeedUrl) { // 4. load data return dispatch( loadPipesData(mappedFeedUrl, { riverId: options.riverId }) ); } } }); }, [feedUrls]); React.useEffect(() => { runBatchLoading(); }, []); React.useEffect(() => { // check if all feeds are ready and set hasEverBeenReady to true if (allFeedsIsReady(feeds, feedUrls) && !hasEverBeenReady) { setHasEverBeenReady(true); } }, [feeds, feedUrls, hasEverBeenReady]); return hasEverBeenReady; };