@applicaster/quick-brick-core
Version:
Core package for Applicaster's Quick Brick App
189 lines (157 loc) • 4.96 kB
text/typescript
import { Linking } from "react-native";
import * as R from "ramda";
import {
getRiverFromRoute,
getTargetRoute,
} from "@applicaster/zapp-react-native-utils/navigationUtils";
import { findPluginByType } from "@applicaster/zapp-react-native-utils/pluginUtils";
import { coreAppLogger } from "../logger";
import { isString } from "@applicaster/zapp-react-native-utils/stringUtils";
import { SCREEN_TYPES } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
const EXTERNAL_URL_TYPE = "external_url";
const EXTERNAL_URL_FLAG = "open_external_url";
const WEBVIEW_SCREEN_IDENTIFIER = "webview_screen_qb";
const logger = coreAppLogger.addSubsystem("Navigator");
export function targetShouldOpenExternally(
target: ZappEntry,
targetScreen: ZappRiver | null,
rivers: Record<string, ZappRiver>
) {
const href = target?.link?.href;
const hasWebviewScreen = R.compose(
R.not,
isNilOrEmpty,
R.find(R.propEq("type", WEBVIEW_SCREEN_IDENTIFIER)),
R.values
)(rivers);
const isExternalLink =
isString(href) &&
(target?.type?.value === EXTERNAL_URL_TYPE ||
href.includes(`${EXTERNAL_URL_TYPE}=true`) ||
target?.extensions?.[EXTERNAL_URL_FLAG] === true ||
target?.extensions?.[EXTERNAL_URL_FLAG] === "true" ||
href.startsWith("mailto:"));
return (
isExternalLink ||
(targetScreen?.screenType === SCREEN_TYPES.LINK && !hasWebviewScreen)
);
}
export function openExternalUrl(url: string) {
Linking.openURL(url);
}
export function legacyScreenData(
screenState: NavigationScreenData,
plugins?: QuickBrickPlugin[]
): QuickBrickNavigationData {
let targetScreen = screenState?.screen;
const entry = screenState?.entry;
// @ts-ignore - this happens only on launch
if (!targetScreen) return {};
/**
* HACK: We mutate targetScreen by merging screenOrientation for the player.
* screen_orientation value is used inside the Transitioner to determine if app should animate the transition.
* (Transitions with the orientation change shouldn't be animated).
*
* I tried adding screen_orientation directly to the default-player manifest but unfortunately,
* Zapp is not passing new configuration values to the layout.json manifest.
*
* TODO: add general.screen_orientation to player configuration and find a way to merge defaults into data before we
* inject data to redux.
*/
if (!targetScreen?.general?.screen_orientation) {
const isPlayer =
targetScreen.plugin_type === "player" ||
targetScreen.screenType === "playable";
if (isPlayer) {
const player = findPluginByType("player", plugins, {
returnFullObject: true,
});
const orientation =
player?.configuration?.screen_orientation || "landscapeSensor";
targetScreen = R.mergeDeepRight(targetScreen, {
general: { screen_orientation: orientation },
});
}
}
if (entry) {
return {
...entry,
targetScreen,
};
}
return targetScreen;
}
function itemIsEntry(
item: ZappEntry | ZappRiver,
layoutVersion: ZappLayoutVersions
): Nullable<ZappEntry> {
if (
typeof (item as ZappEntry)?.type?.value !== "undefined" ||
layoutVersion === "v1"
) {
return item as ZappEntry;
}
return null;
}
export function getNavigationTarget(
item: ZappEntry | ZappRiver,
pathname: string,
contentTypes: ZappContentTypes,
layoutVersion: ZappLayoutVersions,
rivers: Record<string, ZappRiver>
): {
entry?: ZappEntry;
screen?: ZappRiver;
targetRoute?: string;
externalUrl?: string;
} {
if (!item) {
return {};
}
const targetRoute = getTargetRoute(item, pathname, {
layoutVersion,
contentTypes,
rivers,
});
const targetScreen = getRiverFromRoute({ route: targetRoute, rivers });
if (targetShouldOpenExternally(item as ZappEntry, targetScreen, rivers)) {
return { externalUrl: (item as ZappEntry)?.link?.href };
}
if (
"screen_type" in item &&
!item.screen_type &&
!contentTypes?.[(item as ZappEntry)?.type?.value]
) {
logger.warning({
message: `Couldn't find a navigation target\nCheck your ${
layoutVersion === "v1"
? "connected screen or feed"
: "types mapping or feed"
}`,
});
return {};
}
const data: { entry?: ZappEntry; screen: ZappRiver; targetRoute: string } = {
screen: targetScreen,
targetRoute,
};
const targetEntry = itemIsEntry(item, layoutVersion);
if (targetEntry) {
data.entry = targetEntry;
}
return data;
}
export function getTargetScreen({
entry,
rivers,
contentTypes,
}: {
entry: ZappEntry;
rivers: Record<string, ZappRiver>;
contentTypes: ZappContentTypes;
}): ZappRiver | undefined {
const type = entry?.type?.value;
const contentType = contentTypes?.[type];
return rivers[contentType?.screen_id];
}