import * as validation from "@jet/environment/json/validation"; import * as models from "../../api/models"; import { isFeatureEnabledForCurrentUser } from "../../common/util/lottery"; import * as mediaAttributes from "../../foundation/media/attributes"; import * as color from "../../foundation/util/color-util"; import { createArtworkForResource } from "./artwork/artwork"; import * as contentAttributes from "./attributes"; import * as gameController from "./game-controller"; import { isSome } from "@jet/environment"; import { supportedGameCenterFeaturesFromData } from "./content"; import { makeRoutableArticlePageIntent } from "../../api/intents/routable-article-page-intent"; import { getLocale } from "../locale"; import { makeRoutableArticlePageCanonicalUrl } from "../today/routable-article-page-url-utils"; import { openGamesUIAction } from "../arcade/arcade-common"; import { getPlatform } from "../preview-platform"; /** * Creates a list of product capabilities for a given product. * * @param objectGraph The object graph * @param productData The data for the product * @param isFreeProduct Whether the offer is for a free product * @returns An array of product capabilities */ export function productCapabilitiesFromData(objectGraph, productData, isFreeProduct) { return validation.context("capabilitiesFromData", () => { return [ gameCenterCapabilityFromData(objectGraph, productData), siriCapabilityFromData(objectGraph, productData), sharePlayCapabilityFromData(objectGraph, productData), walletCapabilityFromData(objectGraph, productData), controllersCapabilityFromData(objectGraph, productData), familySharingCapabilityFromData(objectGraph, productData, isFreeProduct), spatialControlsCapabilityFromData(objectGraph, productData), safariExtensionCapabilityFromData(objectGraph, productData), ].filter((capability) => capability); }); } /** * Creates the Game Center capability. * @param objectGraph The object graph * @param productData The data for the product * @param gameInfoSummary The game info summary for the product * @returns A product capability, or null */ export function gameCenterCapabilityFromData(objectGraph, productData) { const isGameCenterEnabled = isSome(productData) && contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, productData, "isGameCenterEnabled"); if (!isGameCenterEnabled) { return null; } if (objectGraph.bag.gameCenterExtendSupportedFeatures) { const supportedGameCenterFeatures = supportedGameCenterFeaturesFromData(productData); const supportsLeaderboards = supportedGameCenterFeatures === null || supportedGameCenterFeatures === void 0 ? void 0 : supportedGameCenterFeatures.includes("leaderboards"); const supportsAchievements = supportedGameCenterFeatures === null || supportedGameCenterFeatures === void 0 ? void 0 : supportedGameCenterFeatures.includes("achievements"); let captionText; if (supportsLeaderboards && supportsAchievements) { captionText = objectGraph.loc.string("CAPABILITY_GAME_CENTER_EXPLANATION_ALL_FEATURES"); } else if (supportsLeaderboards) { captionText = objectGraph.loc.string("CAPABILITY_GAME_CENTER_EXPLANATION_ONLY_LEADERBOARDS"); } else if (supportsAchievements) { captionText = objectGraph.loc.string("CAPABILITY_GAME_CENTER_EXPLANATION_ONLY_ACHIEVEMENTS"); } else { captionText = objectGraph.loc.string("CAPABILITY_GAME_CENTER_EXPLANATION_NO_FEATURES"); } const title = objectGraph.loc.string("CAPABILITY_GAME_CENTER_TITLE"); const artwork = createArtworkForResource(objectGraph, "resource://ProductCapabilityGameCenter", 46, 45); if (objectGraph.featureFlags.isGSEUIEnabled("de7bbd8e")) { let linkAction; if (isSome(productData) && objectGraph.props.enabled("gameProductIdOpenGamesUIAction")) { linkAction = openGamesUIAction(objectGraph, { gamePage: { productID: productData.id }, }); } else { linkAction = openGamesUIAction(objectGraph); } const linkActionTitle = objectGraph.loc.string("ProductPage.Capability.GameCenter.ActionTitle"); captionText = `${captionText}\n${linkActionTitle}`; const styledText = new models.StyledText(captionText); const linkedSubstrings = {}; linkedSubstrings[linkActionTitle] = linkAction; const linkableCaption = new models.LinkableText(styledText, linkedSubstrings); const captionTrailingArtwork = createArtworkForResource(objectGraph, "systemimage://arrow.up.forward.square.fill", 16, 16); return new models.ProductCapability("gameCenter", title, linkableCaption, captionTrailingArtwork, undefined, artwork); } else { const styledText = new models.StyledText(captionText); const linkableCaption = new models.LinkableText(styledText); return new models.ProductCapability("gameCenter", title, linkableCaption, null, null, artwork); } } else { const title = objectGraph.loc.string("CAPABILITY_GAME_CENTER_TITLE"); const styledText = new models.StyledText(objectGraph.loc.string("CAPABILITY_GAME_CENTER_EXPLANATION")); const caption = new models.LinkableText(styledText); const artwork = createArtworkForResource(objectGraph, "resource://ProductCapabilityGameCenter", 46, 45); return new models.ProductCapability("gameCenter", title, caption, undefined, null, artwork); } } /** * Creates the Siri capability. * @param objectGraph The object graph * @param productData The data for the product * @returns A product capability, or null */ function siriCapabilityFromData(objectGraph, productData) { if (!contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, productData, "isSiriSupported")) { return null; } const title = objectGraph.loc.string("CAPABILITY_SIRI_TITLE"); const styledText = new models.StyledText(objectGraph.loc.string("CAPABILITY_SIRI_EXPLANATION")); const caption = new models.LinkableText(styledText); const artwork = createArtworkForResource(objectGraph, "resource://ProductCapabilitySiri", 46, 45); return new models.ProductCapability("siri", title, caption, undefined, null, artwork); } /** * Creates the Wallet capability. * @param objectGraph The object graph * @param productData The data for the product * @returns A product capability, or null */ function walletCapabilityFromData(objectGraph, productData) { if (!contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, productData, "supportsPassbook")) { return null; } const title = objectGraph.loc.string("CAPABILITY_WALLET_TITLE"); const styledText = new models.StyledText(objectGraph.loc.string("CAPABILITY_WALLET_EXPLANATION")); const caption = new models.LinkableText(styledText); const artwork = createArtworkForResource(objectGraph, "resource://ProductCapabilityWallet", 46, 45); return new models.ProductCapability("wallet", title, caption, undefined, null, artwork); } /** * Creates the Controllers capability. * @param objectGraph The object graph * @param productData The data for the product * @returns A product capability, or null */ function controllersCapabilityFromData(objectGraph, productData) { if (!gameController.isGameControllerSupported(objectGraph, productData)) { return null; } const title = objectGraph.loc.string("CAPABILITY_MFI_CONTROLLERS_TITLE"); // Game Controllers // Create the Learn More link let linkAction = null; const storyId = objectGraph.bag.gameControllerLearnMoreEditorialItemId; if (isFeatureEnabledForCurrentUser(objectGraph, objectGraph.bag.gameControllerRecommendedRolloutRate) && (objectGraph.client.isiOS || objectGraph.client.isVision || objectGraph.client.isWeb) && isSome(storyId) && (storyId === null || storyId === void 0 ? void 0 : storyId.length) > 0) { const routableArticlePageIntent = makeRoutableArticlePageIntent({ ...getLocale(objectGraph), ...getPlatform(objectGraph), id: storyId, }); linkAction = new models.FlowAction("article"); linkAction.title = objectGraph.loc.string("ProductPage.Capability.GameController.ActionTitle"); // Learn More linkAction.pageUrl = makeRoutableArticlePageCanonicalUrl(objectGraph, routableArticlePageIntent); if (objectGraph.client.isWeb) { linkAction.destination = routableArticlePageIntent; } } let captionText = linkAction ? objectGraph.loc.string("ProductPage.Capability.GameController.Explanation.v2.WithNewlineActionTemplate") : objectGraph.loc.string("ProductPage.Capability.GameController.Explanation.v2"); const linkedSubstrings = {}; if (linkAction === null || linkAction === void 0 ? void 0 : linkAction.title) { captionText = captionText.replace("{learnMoreLink}", linkAction.title); // Learn More linkedSubstrings[linkAction.title] = linkAction; } const styledText = new models.StyledText(captionText); const linkableCaption = new models.LinkableText(styledText, linkedSubstrings); const artwork = createArtworkForResource(objectGraph, "resource://ProductCapabilityController", 46, 45); return new models.ProductCapability("controllers", title, linkableCaption, undefined, null, artwork); } /** * Creates the Spatial Controls capability. * @param objectGraph The object graph * @param productData The data for the product * @returns A product capability, or null */ function spatialControlsCapabilityFromData(objectGraph, productData) { if (!objectGraph.client.isVision || !gameController.isSpatialControllerSupported(objectGraph, productData)) { return null; } const title = objectGraph.loc.string("ProductPage.Badge.SpatialController.Heading"); // Create the Learn More link let linkAction = null; const storyId = objectGraph.bag.spatialControlsLearnMoreEditorialItemId; if (isSome(storyId) && (storyId === null || storyId === void 0 ? void 0 : storyId.length) > 0) { const routableArticlePageIntent = makeRoutableArticlePageIntent({ ...getLocale(objectGraph), ...getPlatform(objectGraph), id: storyId, }); linkAction = new models.FlowAction("article"); linkAction.title = objectGraph.loc.string("Action.LearnMore"); linkAction.pageUrl = makeRoutableArticlePageCanonicalUrl(objectGraph, routableArticlePageIntent); if (objectGraph.client.isWeb) { linkAction.destination = routableArticlePageIntent; } } let captionText = linkAction !== null ? objectGraph.loc.string("ProductPage.Capability.SpatialController.Explanation.WithActionTemplate") : objectGraph.loc.string("ProductPage.Capability.SpatialController.Explanation"); const linkedSubstrings = {}; if (isSome(linkAction === null || linkAction === void 0 ? void 0 : linkAction.title)) { captionText = captionText.replace("{learnMoreLink}", linkAction.title); linkedSubstrings[linkAction.title] = linkAction; } const styledText = new models.StyledText(captionText); const linkableCaption = new models.LinkableText(styledText, linkedSubstrings); const artwork = createArtworkForResource(objectGraph, "resource://ProductCapabilitySpatialControllers"); return new models.ProductCapability("spatialControllers", title, linkableCaption, undefined, null, artwork); } /** * Creates a family sharing capability for the given product, if any. This is driven off whether family sharing * is enabled, whether there are any IAPs, and whther any of those IAPs are shareable. * * @param objectGraph The object graph * @param productData The data for the product * @param isFreeProduct Whether the offer is for a free product * @returns A product capability, or null */ function familySharingCapabilityFromData(objectGraph, productData, isFreeProduct) { // Check if family sharing is enabled, or if this is a SAD app const familyShareEnabledDateString = mediaAttributes.attributeAsString(productData, "familyShareEnabledDate"); if (!familyShareEnabledDateString || mediaAttributes.attributeAsBooleanOrFalse(productData, "isFirstPartyHideableApp")) { return null; } // Check family sharing was enabled in the past const familyShareEnabledDate = new Date(familyShareEnabledDateString); const now = new Date(); if (!familyShareEnabledDate || familyShareEnabledDate > now) { return null; } const hasInAppPurchases = contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, productData, "hasInAppPurchases"); const hasFamilyShareableInAppPurchases = hasInAppPurchases && contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, productData, "hasFamilyShareableInAppPurchases"); // Create the Learn More link let linkAction = null; const storyId = objectGraph.bag.familySubscriptionsLearnMoreEditorialItemId; const platformSupportsLink = objectGraph.client.isiOS || objectGraph.client.isMac || objectGraph.client.isVision || objectGraph.client.isWeb; if (isSome(storyId) && (storyId === null || storyId === void 0 ? void 0 : storyId.length) > 0 && platformSupportsLink && hasFamilyShareableInAppPurchases) { const routableArticlePageIntent = makeRoutableArticlePageIntent({ ...getLocale(objectGraph), ...getPlatform(objectGraph), id: storyId, }); linkAction = new models.FlowAction("article"); linkAction.title = objectGraph.loc.string("CAPABILITY_FAMILY_SHARING_ACTION_TITLE"); linkAction.pageUrl = makeRoutableArticlePageCanonicalUrl(objectGraph, routableArticlePageIntent); if (objectGraph.client.isWeb) { linkAction.destination = routableArticlePageIntent; } } // Generate the caption based on whether the product has IAPs, whether it has shareable IAPs, and whether it is free const title = objectGraph.loc.string("CAPABILITY_FAMILY_SHARING_TITLE"); let captionKey; if (hasFamilyShareableInAppPurchases) { captionKey = linkAction ? "CAPABILITY_FAMILY_SHARING_SOME_SHAREABLE_IAPS_EXPLANATION_WITH_ACTION_TEMPLATE" : "CAPABILITY_FAMILY_SHARING_SOME_SHAREABLE_IAPS_EXPLANATION"; } else if (!hasInAppPurchases && !isFreeProduct) { captionKey = linkAction ? "CAPABILITY_FAMILY_SHARING_PAID_APP_NO_IAPS_EXPLANATION_WITH_ACTION_TEMPLATE" : "CAPABILITY_FAMILY_SHARING_PAID_APP_NO_IAPS_EXPLANATION"; } if (!captionKey) { return null; } let captionText = objectGraph.loc.string(captionKey); const linkedSubstrings = {}; if (linkAction === null || linkAction === void 0 ? void 0 : linkAction.title) { captionText = captionText.replace("{learnMoreLink}", linkAction.title); linkedSubstrings[linkAction.title] = linkAction; } const styledText = new models.StyledText(captionText); const linkableCaption = new models.LinkableText(styledText, linkedSubstrings); const artwork = createArtworkForResource(objectGraph, "resource://ProductCapabilityFamilySharing", 46, 45); return new models.ProductCapability("familySharing", title, linkableCaption, undefined, linkAction, artwork); } /** * Creates the Safari Extension capability. * @param objectGraph The object graph * @param productData The data for the product * @returns A product capability, or null */ function safariExtensionCapabilityFromData(objectGraph, productData) { const productHasExtension = contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, productData, "hasSafariExtension"); const platformSupports = objectGraph.client.isMac || objectGraph.client.isiOS; if (!productHasExtension || !platformSupports) { return null; } const title = objectGraph.loc.string("CAPABILITY_SAFARI_EXTENSION_TITLE"); const styledText = new models.StyledText(objectGraph.loc.string("CAPABILITY_SAFARI_EXTENSION_EXPLANATION")); const caption = new models.LinkableText(styledText); const artwork = createArtworkForResource(objectGraph, "resource://ProductCapabilitySafariExtension", 129, 129); return new models.ProductCapability("safariExtensions", title, caption, undefined, null, artwork); } /** * Creates the SharePlay capability * @param objectGraph The object graph * @param productData The data for the product * @returns A product capability, or null */ function sharePlayCapabilityFromData(objectGraph, productData) { if (!contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, productData, "supportsSharePlay")) { return null; } const title = objectGraph.loc.string("CAPABILITY_SHAREPLAY_TITLE"); const styledText = new models.StyledText(objectGraph.loc.string("CAPABILITY_SHAREPLAY_EXPLANATION")); const caption = new models.LinkableText(styledText); const artwork = createArtworkForResource(objectGraph, "systemimage://shareplay"); const artworkTintColor = color.named("systemGreen"); return new models.ProductCapability("sharePlay", title, caption, undefined, null, artwork, artworkTintColor); } //# sourceMappingURL=product-capabilities.js.map