///
///
import React, { useEffect, useMemo } from "react";
import * as R from "ramda";
import { Platform } from "react-native";
import Url from "url";
import { favoritesListener } from "@applicaster/zapp-react-native-bridge/Favorites";
import {
getInflatedDataSourceUrl,
getSearchContext,
useFeedLoader,
useFeedRefresh,
useRoute,
} from "@applicaster/zapp-react-native-utils/reactHooks";
import { ComponentDataSourceContext, ZappPipesDataProps } from "../types";
import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
import { isVerticalListOrGrid } from "../utils";
import { useFilter } from "../utils/useFilter";
import { subscribeForKeyChanges } from "@applicaster/zapp-pipes-v2-client";
const FAVORITES_TYPE = "FAVOURITES";
function getDataSourceUrl({
component,
feedUrl,
screenData,
entryContext,
searchContext,
screenContext,
}): { dataSourceUrl: string | null; type: string | null } {
if (feedUrl) {
return { dataSourceUrl: feedUrl, type: null };
}
if (component.data) {
const {
data: { source, type, mapping },
} = component;
if (type === FAVORITES_TYPE) {
return { dataSourceUrl: source || FAVORITES_TYPE, type };
}
if (source) {
if (type === "APPLICASTER_COLLECTION" && !R.includes("://", source)) {
return {
dataSourceUrl: `applicaster://fetchData?type=${type}&collectionId=${source}`,
type,
};
}
if (type === "APPLICASTER_CATEGORY" && !R.includes("://", source)) {
return {
dataSourceUrl: `applicaster://fetchData?type=${type}&categoryId=${source}&platform=${Platform.OS}`,
type,
};
}
if (mapping) {
const contexts = {
entry: entryContext,
screen: screenContext || screenData,
search: getSearchContext(searchContext, mapping),
};
const dataSourceUrl = getInflatedDataSourceUrl({
source,
mapping,
contexts,
});
if (!dataSourceUrl) {
return { dataSourceUrl: null, type: null };
}
return { dataSourceUrl, type };
}
return { dataSourceUrl: source, type };
}
}
return { dataSourceUrl: null, type: null };
}
function getMethodAndParams(component) {
const method: "get" | "post" = R.pathOr("get", ["data", "method"], component);
const bodyParams: Record = R.pathOr(
{},
["data", "bodyParams"],
component
);
return {
method,
bodyParams,
};
}
function getListenerFromPlugin(
dataSourceUrl: string,
plugins: QuickBrickPlugin[]
): AddDataSourceListener | void {
const url = Url.parse(dataSourceUrl, false);
const addListener = R.compose(
R.path(["module", "addDataSourceListener"]),
R.find(R.propEq("identifier", url.host))
)(plugins);
return addListener;
}
function applyItemLimit(zappPipesData, component) {
if (!zappPipesData || !zappPipesData.data) {
return zappPipesData;
}
const { data } = zappPipesData;
const {
rules: { item_limit },
} = component;
if (item_limit && data.entry && data.entry.length) {
return {
...zappPipesData,
data: {
...data,
entry: R.slice(
0,
Math.min(Number(item_limit), R.length(data.entry)),
data.entry
),
},
};
}
return zappPipesData;
}
type UrlFeedResolverProps = ComponentDataSourceContext & {
children: (dataProps: ZappPipesDataProps) => React.ReactNode;
};
export function UrlFeedResolver({
children,
component,
feedUrl,
isLast,
entryContext,
screenContext,
searchContext,
screenData,
plugins,
}: UrlFeedResolverProps) {
const { dataSourceUrl, type } = useMemo(
() =>
getDataSourceUrl({
component,
feedUrl,
screenData,
entryContext,
searchContext,
screenContext,
}),
[
component?.id,
feedUrl,
entryContext,
searchContext,
screenContext,
screenData?.id,
]
);
const shouldClearCache = useMemo(
() => Boolean(component?.rules?.clear_cache_on_reload),
[component?.id]
);
const pipesOptions = useMemo(
() => ({
clearCache: type === FAVORITES_TYPE,
loadLocalFavorites: type === FAVORITES_TYPE,
silentRefresh: !shouldClearCache,
...getMethodAndParams(component),
}),
[type, shouldClearCache, component]
);
const { reloadData, loadNext, error, loading, url, data } = useFeedLoader({
feedUrl: dataSourceUrl ?? "",
pipesOptions,
});
const { pathname } = useRoute();
const screenStateStore = useScreenStateStore();
// Setup listeners for data source URL
useEffect(() => {
if (!reloadData) {
return;
}
if (dataSourceUrl?.includes("pipesv2://")) {
(
getListenerFromPlugin(dataSourceUrl, plugins) as AddDataSourceListener
)?.(reloadData);
} else {
return subscribeForKeyChanges({
url: dataSourceUrl,
pathname,
screenStateStore,
callback: reloadData,
});
}
}, [dataSourceUrl, reloadData, pathname, screenStateStore]);
// Setup favorites listener
useEffect(() => {
if (type === FAVORITES_TYPE && reloadData) {
favoritesListener.on("FAVORITES_CHANGED", reloadData);
return () => {
favoritesListener.removeHandler("FAVORITES_CHANGED", reloadData);
};
}
}, [type, reloadData]);
// Apply feed refresh hook
useFeedRefresh({
reloadData,
component,
});
const loadNextData = useMemo(
() => (!isLast && isVerticalListOrGrid(component) ? undefined : loadNext),
[isLast, component, loadNext]
);
const applyItemFilter = useFilter({ url, loading, data, error }, component);
const zappPipesDataProps = useMemo(() => {
const pipeData = { url, loading, data, error };
return {
zappPipesData: applyItemLimit(applyItemFilter(pipeData), component),
reloadData,
loadNextData,
};
}, [
url,
loading,
data,
error,
component,
reloadData,
loadNextData,
applyItemFilter,
]);
return <>{children(zappPipesDataProps)}>;
}