@applicaster/zapp-react-native-ui-components
Version:
Applicaster Zapp React Native ui components for the Quick Brick App
205 lines (161 loc) • 6 kB
text/typescript
import { playerManager } from "@applicaster/zapp-react-native-utils/appUtils";
import { StorageSingleValueProvider } from "@applicaster/zapp-react-native-utils/storage/StorageSingleSelectProvider";
import { PushTopicManager } from "@applicaster/zapp-react-native-bridge/PushNotifications/PushTopicManager";
import { StorageMultiSelectProvider } from "@applicaster/zapp-react-native-utils/storage/StorageMultiSelectProvider";
import React, { useEffect } from "react";
import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
import { BehaviorSubject } from "rxjs";
import { masterCellLogger } from "../logger";
import get from "lodash/get";
import { ScreenMultiSelectProvider } from "@applicaster/zapp-react-native-utils/storage/ScreenStateMultiSelectProvider";
import { ScreenSingleValueProvider } from "@applicaster/zapp-react-native-utils/storage/ScreenSingleValueProvider";
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks";
import { useScreenStateStore } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useScreenStateStore";
const parseContextKey = (
key: string,
context: string = "ctx"
): string | null => {
if (!key?.startsWith(`@{${context}/`)) return null;
return key.substring(`@{${context}/`.length, key.length - 1);
};
const getDataSourceProvider = (
behavior: Behavior,
screenRoute: string,
screenStateStore: ReturnType<typeof useScreenStateStore>
): BehaviorSubject<string[] | string> | null => {
if (!behavior) return null;
const selection = String(behavior.current_selection);
const screenKey = parseContextKey(selection, "screen");
if (screenKey) {
if (behavior.select_mode === "multi") {
return ScreenMultiSelectProvider.getProvider(
screenKey,
screenRoute,
screenStateStore
).getObservable();
}
if (behavior.select_mode === "single") {
return ScreenSingleValueProvider.getProvider(
screenKey,
screenRoute,
screenStateStore
).getObservable();
}
}
const contextKey = parseContextKey(selection);
if (contextKey) {
// TODO: Add storage scope to behavior
if (behavior.select_mode === "multi") {
return StorageMultiSelectProvider.getProvider(contextKey).getObservable();
}
if (behavior.select_mode === "single") {
return StorageSingleValueProvider.getProvider(contextKey).getObservable();
}
}
if (behavior.selection_source === "@{push/topics}") {
return PushTopicManager.getInstance().getEntryObservable();
}
return null;
};
export const useBehaviorUpdate = (behavior: Behavior) => {
const [lastUpdate, setLastUpdate] = React.useState<number | null>(null);
const screenRoute = useRoute()?.pathname || "";
const screenStateStore = useScreenStateStore();
const player = usePlayer();
const triggerUpdate = () => setLastUpdate(Date.now());
useEffect(() => {
if (!behavior) return;
const dataSource = getDataSourceProvider(
behavior,
screenRoute,
screenStateStore
);
if (dataSource) {
const subscription = dataSource.subscribe(triggerUpdate);
return () => subscription.unsubscribe();
}
}, [behavior]);
useEffect(() => {
if (!behavior || !player || behavior.selection_source !== "now_playing") {
return;
}
const subscription = player.getEntryObservable().subscribe(triggerUpdate);
return () => subscription.unsubscribe();
}, [behavior, player]);
return lastUpdate;
};
// We cant use async in this function (its inside render),
// so we rely on useBehaviorUpdate to update current value and trigger re-render
export const isCellSelected = ({
item,
screenRoute,
screenStateStore,
behavior,
}: {
item: ZappEntry;
screenRoute: string;
screenStateStore: ReturnType<typeof useScreenStateStore>;
behavior?: Behavior;
}): boolean => {
if (!behavior) return false;
const id = behavior.selector ? get(item, behavior.selector) : item.id;
if (behavior.selection_source === "now_playing") {
const player = playerManager.getActivePlayer();
return player?.entry?.id === id;
}
if (behavior.selection_source === "@{push/topics}") {
if (behavior.select_mode === "single") {
masterCellLogger.warning(
"Unexpected single selection mode for push topics"
);
}
const tags = PushTopicManager.getInstance().getRegisteredTags();
return tags.includes(String(id));
}
const selection = String(behavior.current_selection);
const screenKey = parseContextKey(selection, "screen");
if (screenKey) {
if (behavior.select_mode === "single") {
const selectedItem = ScreenSingleValueProvider.getProvider(
screenKey,
screenRoute,
screenStateStore
).getValue();
return selectedItem === String(id);
}
if (behavior.select_mode === "multi") {
const selectedItems = ScreenMultiSelectProvider.getProvider(
screenKey,
screenRoute,
screenStateStore
).getSelectedItems();
return selectedItems?.includes(String(id));
}
}
const contextKey = parseContextKey(selection, "ctx");
if (contextKey) {
if (behavior.select_mode === "single") {
const selectedItem =
StorageSingleValueProvider.getProvider(contextKey)?.getValue();
return selectedItem === String(id);
}
if (behavior.select_mode === "multi") {
const selectedItems =
StorageMultiSelectProvider.getProvider(contextKey)?.getSelectedItems();
return selectedItems?.includes(String(id));
}
}
if (behavior.select_mode === "single") {
return behavior.current_selection === id;
}
if (
behavior.select_mode === "multi" &&
Array.isArray(behavior.current_selection)
) {
const currentSelection: string[] = behavior.current_selection.map(
(item): string => String(item)
);
return currentSelection.includes(String(id));
}
return false;
};