@applicaster/zapp-react-native-ui-components
Version:
Applicaster Zapp React Native ui components for the Quick Brick App
199 lines (160 loc) • 5.51 kB
text/typescript
import { all, equals, path, prop, isEmpty, pluck, values } from "ramda";
import { useEffect, useMemo } from "react";
import { useDispatch } from "react-redux";
import { useLayoutPresets } from "@applicaster/zapp-react-native-redux/hooks/useLayoutPresets";
import { useZappPipesFeeds } from "@applicaster/zapp-react-native-redux/hooks/useZappPipesFeeds";
import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
import { Categories } from "./logger";
import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
import {
ZappPipesEntryContext,
ZappPipesScreenContext,
ZappPipesSearchContext,
} from "@applicaster/zapp-react-native-ui-components/Contexts";
import {
getInflatedDataSourceUrl,
getSearchContext,
} from "@applicaster/zapp-react-native-utils/reactHooks/feed/useInflatedUrl";
import { produce } from "immer";
// types reference
declare type CurationEntry = { preset_name: string; feed_url: string };
type Feeds = Record<string, ZappPipesData>;
type LayoutPresets = PresetsMapping["presets_mappings"];
const SMART_COMPONENT_TYPE = "quick-brick-smart-component";
const SOURCE_PATH = ["data", "source"];
const MAPPING_PATH = ["data", "mapping"];
const isSmartComponent = (component) =>
component.component_type === SMART_COMPONENT_TYPE;
const logger = createLogger({ category: Categories.CURATION_API });
export const getTransformedPreset = (
preset: CurationEntry,
componentId: ZappUIComponent["id"],
index: number,
layoutPresets: LayoutPresets
): ZappUIComponent | undefined => {
const presetComponent = layoutPresets?.[preset?.preset_name];
if (!presetComponent) {
logger.log_error("Preset missing or wrong data format", { entry: preset });
return;
}
const updatedPresetComponent = produce(presetComponent, (draft) => {
draft.id = `${componentId}-${preset?.feed_url}-${index}`;
draft.data.source = preset?.feed_url;
if (draft.ui_components) {
draft.ui_components.forEach((comp, uiIndex) => {
comp.id = `${draft.id}-${uiIndex}`;
});
}
});
return updatedPresetComponent;
};
export const enrichComponent = (
comp: ZappUIComponent,
index: number,
feeds: Feeds,
layoutPresets: LayoutPresets,
urlsMap: { [key: string]: string }
): ZappUIComponent[] | null => {
if (!isSmartComponent(comp)) {
return [comp];
}
const presets = prop<string[]>(urlsMap[comp.id], feeds);
if (presets && presets.data && presets.data.entry) {
return presets.data.entry
.map((preset: CurationEntry, _index) =>
getTransformedPreset(preset, comp.id, index + _index, layoutPresets)
)
.filter(Boolean);
}
return null;
};
export const getFinalComponents = (
enrichedComponents: Array<ZappUIComponent>,
smartComponents: ZappUIComponent[],
urls: string[],
feeds: Feeds,
components: ZappUIComponent[]
): ZappUIComponent[] => {
if (isEmpty(smartComponents)) {
return components;
}
if (all(isEmptyOrNil)(urls)) {
return enrichedComponents;
}
if (isEmptyOrNil(feeds)) {
return [];
}
if (all(equals(false))(values(pluck("loading", feeds)))) {
return enrichedComponents;
} else {
return [];
}
};
export const useCurationAPI = (
components: Array<ZappUIComponent>
): ZappUIComponent[] => {
const dispatch = useDispatch();
const smartComponents = useMemo(
() => components?.filter?.(isSmartComponent) ?? [],
[components]
);
const { pathname } = useRoute();
const [entryContext] = ZappPipesEntryContext.useZappPipesContext(pathname);
const [searchContext] = ZappPipesSearchContext.useZappPipesContext();
const [screenContext] = ZappPipesScreenContext.useZappPipesContext();
const urlsMap = useMemo<{ [key: string]: string }>(() => {
const map = {};
smartComponents?.forEach?.((component) => {
const url = path(SOURCE_PATH, component);
const mapping = path(MAPPING_PATH, component);
map[component.id] = mapping
? getInflatedDataSourceUrl({
source: url,
contexts: {
entry: entryContext,
screen: screenContext,
search: getSearchContext(searchContext, mapping),
},
mapping,
})
: url;
});
return map;
}, [smartComponents, entryContext, screenContext, searchContext]);
const urls = useMemo<string[]>(() => Object.values(urlsMap), [urlsMap]);
useEffect(() => {
urls.forEach((url, index) => {
if (url) {
dispatch(loadPipesData(url, { clearCache: false }));
} else {
logger.log_error("Curation url is empty", {
componentId: smartComponents?.[index]?.id,
});
}
});
}, [urls]);
const feeds = useZappPipesFeeds(urls);
const layoutPresets = useLayoutPresets();
const enrichedComponents = useMemo(() => {
if (!components) return [];
return components.reduce((acc, comp, index) => {
const enrichedComp = enrichComponent(
comp,
index,
feeds,
layoutPresets,
urlsMap
);
return enrichedComp ? [...acc, ...enrichedComp] : acc;
}, [] as Array<ZappUIComponent>);
}, [components, feeds, layoutPresets, urlsMap]);
return getFinalComponents(
enrichedComponents,
smartComponents,
urls,
feeds,
components
);
};