diff options
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/common/arcade')
7 files changed, 1525 insertions, 0 deletions
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-common.js b/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-common.js new file mode 100644 index 0000000..440d350 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-common.js @@ -0,0 +1,441 @@ +/** + * Created by ls on 5/15/18. + */ +import { Color, isSome } from "@jet/environment"; +import * as models from "../../api/models/index"; +import * as serverData from "../../foundation/json-parsing/server-data"; +import { isDefinedNonNull } from "../../foundation/json-parsing/server-data"; +import * as mediaFetching from "../../foundation/media/data-fetching"; +import * as mediaNetwork from "../../foundation/media/network"; +import * as mediaRelationships from "../../foundation/media/relationships"; +import { Path, Protocol } from "../../foundation/network/url-constants"; +import * as urls from "../../foundation/network/urls"; +import * as artworkBuilder from "../content/artwork/artwork"; +import * as metricsHelpersClicks from "../metrics/helpers/clicks"; +import * as productPageVariants from "../product-page/product-page-variants"; +import { MetricsIdentifierType } from "../../foundation/metrics/metrics-identifiers-cache"; +import { named } from "../../foundation/util/color-util"; +import { makeRoutableArcadeSeeAllPageIntent } from "../../api/intents/routable-arcade-see-all-page-intent"; +import { getPlatform } from "../preview-platform"; +import { getLocale } from "../locale"; +import { makeArcadeSeeAllCanonicalUrl } from "./arcade-see-all-routing"; +import { shouldUsePrerenderedIconArtwork } from "../content/content"; +// endregion +// region Arcade Navigation Actions +/** + * Creates a flow action for going to the page to see all Arcade games. + * Defaults to sorting by release date (descending) + * @param {ArcadeSeeAllGamesPageSort} sort The order which the games in response will be sorted in. + * @param metricsPageInformation + * @param metricsLocationTracker + * @returns {FlowAction} Flow action to Arcade see all games page. + */ +export function seeAllArcadeGamesPageFlowAction(objectGraph, sort = "releaseDate", metricsPageInformation, metricsLocationTracker, title = undefined, id = undefined, idType = undefined, targetType = "button") { + const seeAllGamesUrl = urls.URL.fromComponents(Protocol.internal, null, `/${Path.arcadeSeeAllGames}`, { + sort: sort, + }); + const flowAction = new models.FlowAction("arcadeSeeAllGames", seeAllGamesUrl.build()); + flowAction.title = title !== null && title !== void 0 ? title : objectGraph.loc.string("Arcade.SeeAllGames.Button.Title"); + if (objectGraph.client.isWeb) { + const destination = makeRoutableArcadeSeeAllPageIntent({ + ...getLocale(objectGraph), + ...getPlatform(objectGraph), + }); + const pageUrl = makeArcadeSeeAllCanonicalUrl(objectGraph, destination); + flowAction.destination = destination; + flowAction.pageUrl = pageUrl; + } + const itemId = id !== null && id !== void 0 ? id : (objectGraph.client.isVision ? "SeeAllGames" : "arcade-see-all-games-button"); + const seeAllClickOptions = { + id: itemId, + idType: idType, + targetType: targetType, + actionType: "navigate", + actionContext: "Arcade", + pageInformation: metricsPageInformation, + locationTracker: metricsLocationTracker, + }; + metricsHelpersClicks.addClickEventToAction(objectGraph, flowAction, seeAllClickOptions); + return flowAction; +} +/** + * Create a flow action for opening the Arcade Subscribe page with optional parameters + * @param context The context in which the Arcade Subscribe page is being opened for, e.g. where the flow action was initiated from. + * @param contextualAppId Optional app ID to associate with flow, if any. This is used for flow into contextual upsell sheet. + * @returns {FlowAction} Flow action to `arcadeSubscribe` page. + */ +export function arcadeSubscribePageFlowAction(objectGraph, context, contextualAppId, purchaseSuccessAction, options) { + var _a, _b, _c, _d; + const upsellRequestInfo = new models.MarketingItemRequestInfo("arcade", context, objectGraph.bag.metricsTopic, contextualAppId); + upsellRequestInfo.purchaseSuccessAction = purchaseSuccessAction; + upsellRequestInfo.carrierLinkSuccessAction = purchaseSuccessAction; + const action = new models.FlowAction("upsellMarketingItem"); + if (isSome((_b = (_a = options === null || options === void 0 ? void 0 : options.pageInformation) === null || _a === void 0 ? void 0 : _a.searchTermContext) === null || _b === void 0 ? void 0 : _b.term)) { + upsellRequestInfo.metricsOverlay["searchTerm"] = (_c = options.pageInformation.searchTermContext) === null || _c === void 0 ? void 0 : _c.term; + } + const metricsIdentifierFields = (_d = objectGraph.metricsIdentifiersCache) === null || _d === void 0 ? void 0 : _d.getMetricsFieldsForTypes([ + MetricsIdentifierType.user, + MetricsIdentifierType.client, + ]); + if (isSome(metricsIdentifierFields)) { + upsellRequestInfo.metricsOverlay = { + ...upsellRequestInfo.metricsOverlay, + ...metricsIdentifierFields, + }; + } + action.pageData = upsellRequestInfo; + if (serverData.isDefinedNonNull(options)) { + metricsHelpersClicks.addClickEventToArcadeBuyInitiateAction(objectGraph, action, options); + } + return action; +} +/** + * Action to open the main Arcade page on each platform. + * Depending on the platform, this can be a tab change action or open action to separate Arcade app. + */ +export function openArcadeMainAction(objectGraph, metricsPageInformation, metricsLocationTracker, popToRoot) { + if (objectGraph.client.isTV) { + return openTVArcadeAppAction(objectGraph); + } + else { + const arcadeTabChangeAction = new models.TabChangeAction("arcade"); + if (serverData.isDefinedNonNull(popToRoot)) { + arcadeTabChangeAction.popToRoot = popToRoot; + } + /* + * Presidio / Yukon timeframe workaround for <rdar://problem/53600942> Allow deserialized TabChangeAction to have use `title` property from JS instead of always using `nil` + * We're wrapping a single `TabChangeAction` within `CompoundAction` since `TabChangeAction` deserialization drops the JS provided title. + * + * Tracking removing this workaround in: + * <rdar://problem/53601182> Arcade: Remove workaround for having a tab change action with a title + */ + return new models.CompoundAction([arcadeTabChangeAction]); + } +} +/** + * Creates an action to open Arcade app on tvOS. + */ +export function openTVArcadeAppAction(objectGraph) { + const url = "com.apple.Arcade://"; + return new models.ExternalUrlAction(url); +} +// endregion +/** + * Creates an action to open GamesUI. + */ +export function openGamesUIAction(objectGraph, target = { playNow: {} }) { + return new models.OpenGamesUIAction(target); +} +/** + * Creates Game Center header. + */ +export function makeGameCenterHeader(objectGraph, title = undefined, subtitle = undefined, useTitleArtwork = undefined) { + let eyebrowArtwork; + if (objectGraph.client.isTV) { + eyebrowArtwork = artworkBuilder.createArtworkForResource(objectGraph, "systemimage://gamecenter.fill", 16, 16); + } + else { + eyebrowArtwork = artworkBuilder.createArtworkForResource(objectGraph, "resource://GameCenterEyebrow", 16, 16); + } + const isShelfHeaderEnabled = objectGraph.featureFlags.isEnabled("shelf_header"); + const isGameCenterShelfHeaderEnabled = objectGraph.featureFlags.isEnabled("game_center_shelf_header"); + const isGSEUIEnabled = objectGraph.featureFlags.isGSEUIEnabled("de7bbd8e"); + if (isGSEUIEnabled) { + const configuration = { + eyebrowColor: named("secondaryText"), + includeSeparator: !isShelfHeaderEnabled, + prefersShelfHeader: isGameCenterShelfHeaderEnabled, + }; + const eyebrow = objectGraph.loc.string("GAME_CENTER"); + const shelfHeader = { + eyebrow: eyebrow, + eyebrowArtwork: eyebrowArtwork, + eyebrowArtworkType: models.ShelfHeaderArtworkType.Icon, + title: title, + subtitle: subtitle, + configuration: configuration, + }; + return shelfHeader; + } + else { + const configuration = { + eyebrowColor: isGSEUIEnabled ? named("systemBlue") : undefined, + includeSeparator: !isShelfHeaderEnabled, + prefersShelfHeader: isGameCenterShelfHeaderEnabled, + }; + if (isSome(useTitleArtwork) && useTitleArtwork) { + const shelfHeader = { + title: title, + titleArtwork: eyebrowArtwork, + titleArtworkType: models.ShelfHeaderArtworkType.Icon, + subtitle: subtitle, + configuration: configuration, + }; + return shelfHeader; + } + else { + const eyebrow = objectGraph.loc.uppercased(objectGraph.loc.string("GAME_CENTER")); + const shelfHeader = { + eyebrow: eyebrow, + eyebrowArtwork: eyebrowArtwork, + eyebrowArtworkType: models.ShelfHeaderArtworkType.Icon, + title: title, + subtitle: subtitle, + configuration: configuration, + }; + return shelfHeader; + } + } +} +// region Arcade Catalog MAPI Requests +/** + * Base request for all MAPI requests fetching a set of Arcade games from catalog. + * This request has a server-defined implicit limit value (e.g. 100). + * This request should be bare-bones. If additional attributes are needed, it should be chained to this request. + * + * @returns {mediaFetching.Request} Request object for fetching Arcade apps from MAPI catalog endpoint. + */ +export function arcadeAppsRequest(objectGraph, includeComingSoon = false) { + let request = new mediaFetching.Request(objectGraph).forType("arcade-apps").includingAgeRestrictions(); + if (includeComingSoon) { + request = request.addingQuery("with", "comingSoonApps"); + } + // For visionOS, we need icons for bincompat games. + if (objectGraph.client.isVision) { + request = request.includingAdditionalPlatforms(["iphone", "ipad"]); + request.attributeIncludes.add("compatibilityControllerRequirement"); + } + return request; +} +/** + * Request for fetching a set of arcade apps for displaying a set of Arcade Icons, e.g. for Arcade Grouping Footer, Contextual Upsell Icon Grid, and iOS Arcade Showcase. + * This request is a very special request with MAPI data containing only `artwork` attribute. Data is meant to be used *as-is* for showing a set of icons only. + * + * Requirements: + * - Icon Artwork for apps only. Additional metadata should be pruned if possible. + * + * @param limit Limit of apps. This should be configured to fit the view's needs. + */ +export function arcadeAppsRequestForIcons(objectGraph, limit) { + return arcadeAppsRequest(objectGraph) + .withSparseLimit(limit) + .asPartialResponseLimitedToFields(["artwork"]) + .usingCustomAttributes(productPageVariants.shouldFetchCustomAttributes(objectGraph)); +} +// endregion +// region Arcade Upsell Request +export function arcadeUpsellRequest(objectGraph, context, contextualAppId) { + return arcadeUpsellMarketingItemRequest(objectGraph, context, contextualAppId); +} +export function arcadeUpsellMarketingItemRequest(objectGraph, context, contextualAppId) { + // We always want `context` to be provided, but some callers will provide this value from an messy source, e.g. extracted from URL param. Fall back to `generic` just in case. + if (serverData.isNullOrEmpty(context)) { + context = models.marketingItemContextFromString("generic"); + } + const request = new mediaFetching.Request(objectGraph) + .forType("upsellMarketingItem") + .addingQuery("serviceType", "arcade") + .addingQuery("placement", context) + .includingMetaKeys("marketing-items", ["metrics"]) + .includingRelationships(["contents"]) + .includingAttributes(["marketingArtwork", "marketingVideo"]) + .includingAgeRestrictions(); + // Append app id that is promoted, if any. + if (serverData.isDefinedNonNull(contextualAppId)) { + request.addingQuery("seed", contextualAppId); + } + return request; +} +// endregion +/** + * Grab recently played games. + * @param objectGraph The object graph. + * @param {number} limit The number of recently played games to return. + * @param {boolean} shouldHydrateAppsData Whether the apps data should be fetched from media api. + * @param {number} timeout A timeout in seconds. + * @returns {DataContainer} The media api data container with the recently played games. + */ +export async function getRecentlyPlayedGames(objectGraph, limit = null, shouldHydrateAppsData = false, timeout = null) { + return await new Promise((resolve, reject) => { + const isRecentlyPlayedGamesSupported = objectGraph.host.isiOS || objectGraph.host.isMac || objectGraph.host.isTV; + // Check if the client supports recently played games. + if (!isRecentlyPlayedGamesSupported) { + resolve(null); + return; + } + const getRecentlyPlayGamesPromise = objectGraph.arcade.getRecentlyPlayedGamesWithTimeout(timeout); + // Get recently played games + getRecentlyPlayGamesPromise + .then((adamIds) => { + // Only perform request when there are recently played games. + if (serverData.isNull(adamIds) || adamIds.length === 0) { + resolve(null); + return; + } + // Enforce limit (if any). + if (serverData.isNumber(limit) && adamIds.length > limit) { + adamIds = adamIds.slice(0, limit); + } + if (shouldHydrateAppsData) { + // Fetch data for recently played games. + const attributes = [ + "editorialArtwork", + "editorialVideo", + "description", + "minimumOSVersion", + "minPlayers", + "maxPlayers", + "remoteControllerRequirement", + "requiresGameController", + "supportsSharePlay", + ]; + if (objectGraph.client.isVision) { + attributes.push("compatibilityControllerRequirement"); + } + if (objectGraph.client.isMac) { + attributes.push("hasMacIPAPackage"); + } + if (objectGraph.bag.enableUpdatedAgeRatings) { + attributes.push("ageRating"); + } + if (shouldUsePrerenderedIconArtwork(objectGraph)) { + attributes.push("iconArtwork"); + } + const mediaApiRequest = new mediaFetching.Request(objectGraph) + .withIdsOfType(adamIds, "apps") + .includingAttributes(attributes); + const fetchDataPromise = mediaNetwork.fetchData(objectGraph, mediaApiRequest); + fetchDataPromise.then((dataContainer) => resolve(dataContainer), (reason) => { + objectGraph.console.log(`getRecentlyPlayedGames() failed when calling mediaNetwork.fetchData() with reason: ${reason}`); + resolve(null); + }); + } + else { + // Create an incomplete data container to be fetched later. + const dataContainer = { + data: [], + }; + adamIds.forEach((adamId) => { + dataContainer.data.push({ + id: adamId, + type: "apps", + }); + }); + resolve(dataContainer); + } + }) + .catch((reason) => { + objectGraph.console.log(`getRecentlyPlayedGames() failed with: ${reason}`); + resolve(null); + }); + }); +} +/** + * Convenience function to build a `ArcadeUpsellData` representation from upsell relationship joined to some data. + * @seealso `upsellFromContentsOfUpsellResponse` + * @param objectGraph The object graph + * @param data Data containing `upsell` relationship to build `ArcadeUpsellData` with + */ +export function upsellFromRelationshipOf(objectGraph, data) { + // Data to extract. + let marketingItemData = null; + const upsellDataContainer = objectGraph.client.isVision || preprocessor.GAMES_TARGET + ? mediaRelationships.relationship(data, "contents") + : mediaRelationships.relationship(data, "upsell") || + mediaRelationships.relationship(data, "marketing-items"); + if (serverData.isNullOrEmpty(upsellDataContainer) || serverData.isNullOrEmpty(upsellDataContainer.data)) { + return null; + } + // Create marketing items array from data container. + const marketingItems = upsellDataContainer.data + .map((item) => { + if (item.type === "marketing-items") { + return item; + } + else { + return null; + } + }) + .filter((item) => isDefinedNonNull(item)); + // Return null if there are NOT any marketing items. + if (serverData.isNullOrEmpty(marketingItems)) { + return null; + } + const timeout = objectGraph.bag.marketingItemSelectionTimeout; + // If there is only one marketing item or timeout is zero, set this as the data. + // Otherwise get a single marketing item by calling AMS. + if (marketingItems.length === 1 || timeout === 0) { + marketingItemData = marketingItems[0]; + } + else { + try { + marketingItemData = objectGraph.arcade.getMarketingItemWithTimeout(marketingItems, timeout); + } + catch { + // Default to first item if the call was timed out. + marketingItemData = marketingItems[0]; + } + } + // Return null if marketing item is null. + if (serverData.isNull(marketingItemData)) { + return null; + } + return { + marketingItemData: marketingItemData, + }; +} +/** + * Convenience function to build a `ArcadeUpsellData` representation from contents of a engagement/upsell response. + * This doesn't really belong in + * @seealso `upsellFromRelationshipOf` + * @param arcadeUpsellResponse Response from the engagement/upsell endpoint. + */ +export function upsellFromContentsOfUpsellResponse(objectGraph, arcadeUpsellResponse) { + if (!arcadeUpsellResponse) { + return null; + } + let marketingItemData = null; + const responseDataArray = serverData.asArrayOrEmpty(arcadeUpsellResponse, "results.data"); + if (responseDataArray.length > 0) { + marketingItemData = responseDataArray[0]; + } + /** + * `arcadeUpsellResponse` is expected of form: + * { + * content: { mediaDataStructure.Data } + * } + * matching what is provided via the appropriate `action=<usage>` param from engagement/upsell endpoint. + */ + if (!serverData.isDefinedNonNull(marketingItemData)) { + return null; + } + return { + marketingItemData: marketingItemData, + }; +} +// endregion +// region Arcade Games For You Request +/** + * Request for fetching list of recommended Arcade apps from recommendations API. + * @param objectGraph The App Store object graph. + * @param limit Maximum number of apps to fetch. + * @returns {Request} Request object for fetching recommended Arcade apps from + * personalized recommendations endpoint. + */ +export function arcadeGamesForYouRequest(objectGraph, limit) { + let request = new mediaFetching.Request(objectGraph) + .forType("personal-recommendations") + .addingQuery("sparseLimit[contents]", `${limit}`) + .addingQuery("include[personal-recommendations]", "contents") + .addingQuery("filter[kind]", "arcadeGamesForYou") + .includingAgeRestrictions(); + // For visionOS, we need to include bincompat apps. + if (objectGraph.client.isVision) { + request = request.includingAdditionalPlatforms(["iphone", "ipad"]); + } + return request; +} +// endregion +// The color to use for Arcade content. +export const arcadeColor = Color.fromRGB(1, 90 / 255, 80 / 255); +//# sourceMappingURL=arcade-common.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-see-all-games-facets.js b/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-see-all-games-facets.js new file mode 100644 index 0000000..2418b5f --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-see-all-games-facets.js @@ -0,0 +1,245 @@ +/** + * Build methods for Arcade See All Games Facets. + */ +import * as models from "../../api/models"; +import { isDefinedNonNull, isDefinedNonNullNonEmpty, isNullOrEmpty } from "../../foundation/json-parsing/server-data"; +import { Parameters } from "../../foundation/network/url-constants"; +import * as metricsClickHelpers from "../metrics/helpers/clicks"; +// All Facet Options +export const sortReleaseDateFacetOptionValue = `-releaseDateByDay&sort=name`; +export const sortLastUpdatedFacetOptionValue = `-lastUpdatedByDay&sort=name`; +export const sortNameFacetOptionValue = `alphabet&sort=name`; +export const sortCategoryFacetOptionValue = `category&sort=-releaseDate`; +export const sortFacetOptionParameter = "groupBy"; +export const comingSoonFacetOptionParameter = "with"; +export const comingSoonAppsFacetOptionValue = "comingSoonApps"; +export const comingSoonGroupingFacetOptionValue = "comingSoonGrouping"; +/** + * The facet for filtering bincompat games in visionOS + * By default, not passing this parameter will includes both bincompat and native games. + * To filter native games only, passing `filter[platform]=reality` + */ +export const binCompatGamesFacetOptionParameter = "filter[platform]"; +// The facet option for toggling video preview in lockups. +// This facet is used for toggle `isCompactMode` locally, it don't match with any media api parameter. +export const gamePreviewsFacetOptionParameter = "gamePreviews"; +export const facetOptionsParameterMapping = {}; +let areFacetsInitialized = false; +let sortReleaseDateFacetOption = null; +let sortLastUpdatedFacetOption = null; +let sortNameFacetOption = null; +let sortCategoryFacetOption = null; +let ageRatingDefaultFacetOption = null; +let ageRating4PlusFacetOption = null; +let ageRating9PlusFacetOption = null; +let ageRating12PlusFacetOption = null; +let controllerSupportFacetOption = null; +let multiplayerSupportFacetOption = null; +let comingSoonFacetOption = null; +let includeBinCompatGamesFacetOption = null; +let onlyNativeGamesFacetOption = null; +let gamePreviewsFacetOption = null; +// Mapping of external facet parameters to media api paramter values +const facetParameterMediaApiMapping = {}; +facetParameterMediaApiMapping[Parameters.sort] = sortFacetOptionParameter; +facetParameterMediaApiMapping[Parameters.ageRating] = "filter[ageRating]"; +facetParameterMediaApiMapping[Parameters.controllerSupport] = "filter[supportsGameController]"; +facetParameterMediaApiMapping[Parameters.multiplayerSupport] = "filter[isMultiplayer]"; +facetParameterMediaApiMapping[Parameters.comingSoon] = comingSoonFacetOptionParameter; +facetParameterMediaApiMapping[Parameters.binCompatGames] = binCompatGamesFacetOptionParameter; +function initializeFacets(objectGraph) { + if (areFacetsInitialized) { + return; + } + areFacetsInitialized = true; + sortReleaseDateFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_SORTS_RELEASE_DATE"), sortReleaseDateFacetOptionValue, "releaseDate"); + sortLastUpdatedFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_SORTS_LAST_UPDATED"), sortLastUpdatedFacetOptionValue, "lastUpdated"); + sortNameFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_SORTS_NAME"), sortNameFacetOptionValue, "name"); + sortCategoryFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_SORTS_CATEGORY"), sortCategoryFacetOptionValue, "category"); + ageRatingDefaultFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_DEFAULT_ALL"), null); + ageRating4PlusFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_FILTERS_AGE_VALUE_4PLUS"), "4Plus"); + ageRating9PlusFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_FILTERS_AGE_VALUE_9PLUS"), "9Plus"); + ageRating12PlusFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_FILTERS_AGE_VALUE_12PLUS"), "12Plus"); + controllerSupportFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_FILTERS_CONTROLLER_SUPPORT"), "true", null, "gamecontroller"); + multiplayerSupportFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_FILTERS_MULTIPLAYER"), "true", null, "person.2"); + comingSoonFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_FILTERS_COMING_SOON"), "true"); + if (objectGraph.client.isVision) { + includeBinCompatGamesFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_FILTERS_BIN_COMPAT_GAMES"), null); + onlyNativeGamesFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_FILTERS_BIN_COMPAT_GAMES"), "realityDevice"); + gamePreviewsFacetOption = new models.PageFacetOption(objectGraph.loc.string("PAGE_FACETS_FILTERS_GAME_PREVIEWS"), null); + } + facetOptionsParameterMapping[Parameters.sort] = { + releaseDate: [sortReleaseDateFacetOption], + lastUpdated: [sortLastUpdatedFacetOption], + name: [sortNameFacetOption], + category: [sortCategoryFacetOption], + }; + facetOptionsParameterMapping[Parameters.ageRating] = { + "4plus": [ageRating4PlusFacetOption], + "9plus": [ageRating9PlusFacetOption], + "12plus": [ageRating12PlusFacetOption], + }; + facetOptionsParameterMapping[Parameters.controllerSupport] = { + true: [controllerSupportFacetOption], + false: [], + }; + facetOptionsParameterMapping[Parameters.multiplayerSupport] = { + true: [multiplayerSupportFacetOption], + false: [], + }; + facetOptionsParameterMapping[Parameters.comingSoon] = { + true: [comingSoonFacetOption], + false: [], + }; + if (objectGraph.client.isVision) { + facetOptionsParameterMapping[Parameters.binCompatGames] = { + true: [includeBinCompatGamesFacetOption], + false: [onlyNativeGamesFacetOption], + }; + facetOptionsParameterMapping[Parameters.gamePreviews] = { + true: [gamePreviewsFacetOption], + false: [], + }; + } +} +/** + * + * Create Page Facets. Theres platform specific variations here. + */ +export function createArcadeSeeAllGamesFacets(objectGraph) { + initializeFacets(objectGraph); + const isEnabledVisionSAGSegmentedNavFeature = objectGraph.featureFlags.isEnabled("see_all_games_segmented_nav_2024A"); + const ageRatingsFacetTitle = objectGraph.client.isMac + ? objectGraph.loc.string("PAGE_FACETS_AGE_RATINGS_TITLE") + : objectGraph.loc.string("PAGE_FACETS_FILTERS_AGE_RATINGS"); + const ageRatingsFacet = new models.PageFacetsFacet(Parameters.ageRating, "filter[ageRating]", ageRatingsFacetTitle, "singleSelection", [ageRatingDefaultFacetOption, ageRating4PlusFacetOption, ageRating9PlusFacetOption, ageRating12PlusFacetOption], [ageRatingDefaultFacetOption], "age", pageFacetChangeAction(objectGraph, "sort")); + // Sorts + const isEnabledArcadeSeeAllGamesUplift = objectGraph.featureFlags.isEnabled("arcade_see_all_games_menu_uplift"); + const isFeatureFlagEnabled = (objectGraph.client.isiOS && isEnabledArcadeSeeAllGamesUplift) || + (objectGraph.client.isVision && isEnabledVisionSAGSegmentedNavFeature); + const sortsFacet = new models.PageFacetsFacet(Parameters.sort, sortFacetOptionParameter, objectGraph.loc.string("PAGE_FACETS_SORT_BY_TITLE"), "singleSelection", [sortReleaseDateFacetOption, sortLastUpdatedFacetOption, sortNameFacetOption, sortCategoryFacetOption], isFeatureFlagEnabled ? [sortReleaseDateFacetOption] : null, "sort", pageFacetChangeAction(objectGraph, "sort")); + // Controllers + const controllerSupportFacetOptionFacetTitle = objectGraph.client.isMac + ? objectGraph.loc.string("PAGE_FACETS_FILTERS_CONTROLLERS") + : objectGraph.loc.string("PAGE_FACETS_FILTERS_CONTROLLER_SUPPORT"); + const controllerSupportFacetOptionFacet = new models.PageFacetsFacet(Parameters.controllerSupport, "filter[supportsGameController]", controllerSupportFacetOptionFacetTitle, "toggle", [controllerSupportFacetOption], null, "controllerSupport", pageFacetChangeAction(objectGraph, "sort")); + const multiplayerFacetTitle = objectGraph.client.isMac + ? objectGraph.loc.string("PAGE_FACETS_FILTERS_MULTIPLAYER") + : objectGraph.loc.string("PAGE_FACETS_FILTERS_MULTIPLAYER_GAMES"); + const multiplayerSupportFacetOptionFacet = new models.PageFacetsFacet(Parameters.multiplayerSupport, "filter[isMultiplayer]", multiplayerFacetTitle, "toggle", [multiplayerSupportFacetOption], null, "multiplayer", pageFacetChangeAction(objectGraph, "sort")); + const comingSoonFacet = new models.PageFacetsFacet(Parameters.comingSoon, comingSoonFacetOptionParameter, objectGraph.loc.string("PAGE_FACETS_FILTERS_COMING_SOON"), "toggle", [comingSoonFacetOption], [comingSoonFacetOption], "comingSoon", pageFacetChangeAction(objectGraph, "comingSoon")); + comingSoonFacet.isHiddenFromMenu = isFeatureFlagEnabled; + const pageFacets = new models.PageFacets([], false, null); + if (objectGraph.client.isVision) { + sortsFacet.displayOptionsInline = true; + pageFacets.facetGroups.push(new models.PageFacetsGroup([sortsFacet], objectGraph.loc.string("PAGE_FACETS_SORT_BY_TITLE_LOWERCASE"))); + ageRatingsFacet.showsSelectedOptions = true; + pageFacets.facetGroups.push(new models.PageFacetsGroup([controllerSupportFacetOptionFacet, multiplayerSupportFacetOptionFacet, ageRatingsFacet], objectGraph.loc.string("PAGE_FACETS_FILTER_BY_TITLE_LOWERCASE"))); + const binCompatGamesFacet = createBinCompatGamesFacet(objectGraph); + const gamePreviewsFacet = new models.PageFacetsFacet(Parameters.gamePreviews, gamePreviewsFacetOptionParameter, objectGraph.loc.string("PAGE_FACETS_FILTERS_GAME_PREVIEWS"), "toggle", [gamePreviewsFacetOption], [gamePreviewsFacetOption], "gamePreviews", pageFacetChangeAction(objectGraph, "gamePreviews")); + const includeGroupsTitle = objectGraph.client.isVision && isEnabledVisionSAGSegmentedNavFeature + ? objectGraph.loc.string("PAGE_FACETS_SHOW_TITLE_LOWERCASE") + : objectGraph.loc.string("PAGE_FACETS_INCLUDE_TITLE_LOWERCASE"); + pageFacets.facetGroups.push(new models.PageFacetsGroup([binCompatGamesFacet, gamePreviewsFacet, comingSoonFacet], includeGroupsTitle)); + } + else { + switch (objectGraph.client.deviceType) { + case "mac": + pageFacets.facetGroups.push(new models.PageFacetsGroup([ageRatingsFacet])); + if (objectGraph.bag.enableComingSoonToggle) { + pageFacets.facetGroups.push(new models.PageFacetsGroup([comingSoonFacet], objectGraph.loc.string("PAGE_FACETS_INCLUDE_TITLE"))); + } + pageFacets.facetGroups.push(new models.PageFacetsGroup([controllerSupportFacetOptionFacet, multiplayerSupportFacetOptionFacet], objectGraph.loc.string("PAGE_FACETS_SUPPORTS_TITLE"))); + pageFacets.facetGroups.push(new models.PageFacetsGroup([sortsFacet])); + break; + case "tv": + pageFacets.facetGroups.push(new models.PageFacetsGroup([ageRatingsFacet], objectGraph.loc.string("PAGE_FACETS_AGE_RATINGS_TITLE_LOWERCASE"))); + const filterGroupFacets = [ + controllerSupportFacetOptionFacet, + multiplayerSupportFacetOptionFacet, + ]; + if (objectGraph.bag.enableComingSoonToggle) { + filterGroupFacets.push(comingSoonFacet); + } + pageFacets.facetGroups.push(new models.PageFacetsGroup(filterGroupFacets, objectGraph.loc.string("PAGE_FACETS_FILTERS_TITLE"))); + pageFacets.facetGroups.push(new models.PageFacetsGroup([sortsFacet], objectGraph.loc.string("PAGE_FACETS_SORT_BY_TITLE_LOWERCASE"))); + break; + default: + sortsFacet.displayOptionsInline = true; + pageFacets.facetGroups.push(new models.PageFacetsGroup([sortsFacet], objectGraph.loc.string("PAGE_FACETS_SORT_BY_TITLE"))); + ageRatingsFacet.showsSelectedOptions = true; + pageFacets.facetGroups.push(new models.PageFacetsGroup([controllerSupportFacetOptionFacet, multiplayerSupportFacetOptionFacet, ageRatingsFacet], objectGraph.loc.string("PAGE_FACETS_FILTER_BY_TITLE"))); + if (objectGraph.bag.enableComingSoonToggle) { + pageFacets.facetGroups.push(new models.PageFacetsGroup([comingSoonFacet], objectGraph.loc.string("PAGE_FACETS_INCLUDE_TITLE"))); + } + break; + } + } + return pageFacets; +} +function createBinCompatGamesFacet(objectGraph) { + const binCompatGamesFacet = new models.PageFacetsFacet(Parameters.binCompatGames, binCompatGamesFacetOptionParameter, objectGraph.loc.string("PAGE_FACETS_FILTERS_BIN_COMPAT_GAMES"), "toggle", [includeBinCompatGamesFacetOption], [includeBinCompatGamesFacetOption], "binCompatGames", pageFacetChangeAction(objectGraph, "binCompatGames")); + // Hiding this facet from the Filters menu, because this facet will be control by the page segmented picker. + binCompatGamesFacet.isHiddenFromMenu = objectGraph.featureFlags.isEnabled("see_all_games_segmented_nav_2024A"); + return binCompatGamesFacet; +} +export function createDefaultSelectedFacetOptions(objectGraph, urlParameters = {}) { + initializeFacets(objectGraph); + const selectedFacetOptions = { + "filter[ageRating]": [ageRatingDefaultFacetOption], + }; + selectedFacetOptions[sortFacetOptionParameter] = [sortReleaseDateFacetOption]; + selectedFacetOptions[comingSoonFacetOptionParameter] = [comingSoonFacetOption]; + if (objectGraph.client.isVision) { + selectedFacetOptions[binCompatGamesFacetOptionParameter] = [includeBinCompatGamesFacetOption]; + selectedFacetOptions[gamePreviewsFacetOptionParameter] = [gamePreviewsFacetOption]; + } + const availableParameters = [ + Parameters.sort, + Parameters.ageRating, + Parameters.controllerSupport, + Parameters.multiplayerSupport, + Parameters.comingSoon, + ]; + if (objectGraph.client.isVision) { + availableParameters.push(Parameters.binCompatGames); + } + for (const parameter of availableParameters) { + const facetOption = urlParameters[parameter]; + if (isNullOrEmpty(facetOption)) { + continue; + } + const selectedFacetOption = facetOptionsParameterMapping[parameter][facetOption]; + const mediaApiFacetName = facetParameterMediaApiMapping[parameter]; + if (isDefinedNonNull(selectedFacetOption) && isDefinedNonNullNonEmpty(mediaApiFacetName)) { + selectedFacetOptions[`${mediaApiFacetName}`] = selectedFacetOption; + } + } + return selectedFacetOptions; +} +function pageFacetChangeAction(objectGraph, facetParameter) { + const action = new models.BlankAction(); + metricsClickHelpers.addClickEventToPageFacetsChangeAction(objectGraph, action, facetParameter); + return action; +} +export function createPageSegments(objectGraph) { + const isSegmentedNavEnabled = objectGraph.client.isVision && objectGraph.featureFlags.isEnabled("see_all_games_segmented_nav_2024A"); + if (!isSegmentedNavEnabled) { + return []; + } + else { + const binCompatGamesFacet = createBinCompatGamesFacet(objectGraph); + const allGamesPageSegment = { + id: "all_games", + title: objectGraph.loc.string("Arcade.SeeAllGames.PageSegment.AllGames.Title"), + segmentAction: new models.ArcadeSeeAllGamesPageSegmentChangeAction(binCompatGamesFacet, includeBinCompatGamesFacetOption), + }; + const visionGamesOnlyPageSegment = { + id: "vision", + title: objectGraph.loc.string("Arcade.SeeAllGames.PageSegment.AppleVisionGames.Title"), + segmentAction: new models.ArcadeSeeAllGamesPageSegmentChangeAction(binCompatGamesFacet, undefined), + }; + return [allGamesPageSegment, visionGamesOnlyPageSegment]; + } +} +//# sourceMappingURL=arcade-see-all-games-facets.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-see-all-request.js b/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-see-all-request.js new file mode 100644 index 0000000..6f20944 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-see-all-request.js @@ -0,0 +1,78 @@ +import { PageFacets } from "../../api/models"; +import { isDefinedNonNullNonEmpty, isNullOrEmpty } from "../../foundation/json-parsing/server-data"; +import { Parameters } from "../../foundation/network/url-constants"; +import { shouldUsePrerenderedIconArtwork } from "../content/content"; +import { binCompatGamesFacetOptionParameter, comingSoonAppsFacetOptionValue, comingSoonFacetOptionParameter, comingSoonGroupingFacetOptionValue, facetOptionsParameterMapping, sortCategoryFacetOptionValue, sortFacetOptionParameter, sortLastUpdatedFacetOptionValue, sortNameFacetOptionValue, sortReleaseDateFacetOptionValue, } from "./arcade-see-all-games-facets"; +export function defaultRequestAttributes(objectGraph) { + const attributes = [ + "editorialArtwork", + "editorialVideo", + "isAppleWatchSupported", + "requiredCapabilities", + "videoPreviewsByType", + "screenshotsByType", + ]; + if (objectGraph.appleSilicon.isSupportEnabled) { + attributes.push("macRequiredCapabilities"); + } + if (objectGraph.client.isMac) { + attributes.push("hasMacIPAPackage"); + } + if (objectGraph.client.isVision) { + attributes.push("compatibilityControllerRequirement"); + } + if (objectGraph.bag.enableUpdatedAgeRatings) { + attributes.push("ageRating"); + } + if (shouldUsePrerenderedIconArtwork(objectGraph)) { + attributes.push("iconArtwork"); + } + return attributes; +} +const defaultSparseCount = 4; +export function prepareRequestWithSelectedFacets(request, selectedFacetOptions) { + let includeSparseCount = false; + for (const key of Object.keys(selectedFacetOptions)) { + // We need to choose the correct value for the coming soon parameter if coming soon is enabled. + if (key === comingSoonFacetOptionParameter && isDefinedNonNullNonEmpty(selectedFacetOptions[key])) { + const selectedSortValue = selectedFacetOptions[sortFacetOptionParameter]; + if (isDefinedNonNullNonEmpty(selectedSortValue)) { + switch (selectedSortValue[0].value) { + case sortReleaseDateFacetOptionValue: + case sortLastUpdatedFacetOptionValue: + selectedFacetOptions[key][0].value = comingSoonGroupingFacetOptionValue; + break; + case sortNameFacetOptionValue: + selectedFacetOptions[key][0].value = comingSoonAppsFacetOptionValue; + break; + case sortCategoryFacetOptionValue: + selectedFacetOptions[key][0].value = comingSoonAppsFacetOptionValue; + includeSparseCount = true; + break; + default: + break; + } + } + } + // We need to choose the correct value for the bin compat games parameter if it is disabled. + if (key === binCompatGamesFacetOptionParameter && isNullOrEmpty(selectedFacetOptions[key])) { + selectedFacetOptions[key] = facetOptionsParameterMapping[Parameters.binCompatGames].false; + } + } + // We include the sparse count when dealing with the category sort. + if (includeSparseCount) { + request.withSparseCount(defaultSparseCount); + } + for (const key of Object.keys(selectedFacetOptions)) { + const requestValues = PageFacets.requestValuesForSelectedFacetOptions(selectedFacetOptions[key]); + if (isDefinedNonNullNonEmpty(requestValues)) { + if (isDefinedNonNullNonEmpty(requestValues.value)) { + request.addingQuery(key, requestValues.value); + } + for (const additionalKey of Object.keys(requestValues.additionalKeyValuePairs)) { + request.addingQuery(additionalKey, requestValues.additionalKeyValuePairs[additionalKey]); + } + } + } +} +//# sourceMappingURL=arcade-see-all-request.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-see-all-routing.js b/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-see-all-routing.js new file mode 100644 index 0000000..99b8df7 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-see-all-routing.js @@ -0,0 +1,5 @@ +import { makeRoutableArcadeSeeAllPageIntent } from "../../api/intents/routable-arcade-see-all-page-intent"; +import { generateRoutes } from "../util/generate-routes"; +const { routes, makeCanonicalUrl } = generateRoutes(makeRoutableArcadeSeeAllPageIntent, "/{platform}/arcade/see-all"); +export { routes as arcadeSeeAllRoutes, makeCanonicalUrl as makeArcadeSeeAllCanonicalUrl }; +//# sourceMappingURL=arcade-see-all-routing.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-upsell.js b/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-upsell.js new file mode 100644 index 0000000..5c5d30e --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-upsell.js @@ -0,0 +1,257 @@ +// +// arcade-upsell.ts +// AppStoreKit +// +// Created by Jonathan Ellenbogen on 11/19/19. +// Copyright (c) 2016 Apple Inc. All rights reserved. +// +import * as models from "../../api/models"; +import * as serverData from "../../foundation/json-parsing/server-data"; +import * as mediaAttributes from "../../foundation/media/attributes"; +import * as color from "../../foundation/util/color-util"; +import * as heroCommon from "../grouping/hero/hero-common"; +import * as metricsHelpersClicks from "../metrics/helpers/clicks"; +import * as metricsHelpersImpressions from "../metrics/helpers/impressions"; +import * as metricsHelpersLocation from "../metrics/helpers/location"; +import * as metricsHelpersUtil from "../metrics/helpers/util"; +import * as offers from "../offers/offers"; +import * as breakoutsCommon from "./breakouts-common"; +import { ArcadeSubscriptionStateAction } from "../../api/models"; +import { isFeatureEnabledForCurrentUser } from "../util/lottery"; +export function createUpsellBreakout(objectGraph, upsellData, metricsOptions, style = "white") { + if (serverData.isNullOrEmpty(upsellData) || + (serverData.isNullOrEmpty(upsellData.attributes) && !objectGraph.client.isVision)) { + return null; + } + const heroVideoData = heroCommon.heroVideoFromData(objectGraph, upsellData, true); + const heroArtworkData = heroCommon.heroArtworkFromData(objectGraph, upsellData); + const backgroundColor = heroVideoData.backgroundColor || heroArtworkData.backgroundColor; + const detailPosition = breakoutsCommon.detailPositionFromData(objectGraph, upsellData, true, true); + const supportsMaterial = objectGraph.client.isTV; + const wantsBlur = breakoutsCommon.wantsBlur(objectGraph, breakoutsCommon.detailBackgroundStyleFromData(objectGraph, upsellData, supportsMaterial, true, true), true); + const details = createBreakoutDetailsFromData(objectGraph, upsellData, null, supportsMaterial, wantsBlur, "wordmark"); + let buttonCallToAction = null; + if (objectGraph.client.deviceType !== "tv") { + buttonCallToAction = details.description; + details.description = null; + } + const offerTint = offerTintFromMarketingItem(objectGraph, upsellData); + const offerDisplayProperties = new models.OfferDisplayProperties("arcade", objectGraph.bag.arcadeAppAdamId, null, style, null, "arcade", offerTint, null, null, null, null, null, null, null, null, null, null, null, objectGraph.bag.arcadeProductFamilyId); + const displayProperties = { + backgroundColor: backgroundColor, + wantsMaterialDetailBackground: false, + wantsBlur: wantsBlur, + badgeColor: null, + titleColor: null, + descriptionColor: null, + callToActionColor: null, + textAlignment: null, + detailsPosition: detailPosition, + }; + const upsellBreakout = new models.UpsellBreakout(details, offerDisplayProperties, displayProperties, null, buttonCallToAction, heroArtworkData.artwork, heroVideoData.video); + const impressionOptions = metricsHelpersImpressions.impressionOptions(objectGraph, upsellData, upsellBreakout.details.title, { + ...metricsOptions, + targetType: "upsellBreakout", + }); + impressionOptions.displaysArcadeUpsell = true; + metricsHelpersImpressions.addImpressionFields(objectGraph, upsellBreakout, impressionOptions); + // Push the breakout here so that the click action has the breakout in its location + // but we do not want to add it to the overall location tracker, so pop it right after adding it to the button + // action + // <rdar://problem/60883269> Metrics: Arcade: Container values requested in Location field + metricsHelpersLocation.pushContentLocation(objectGraph, impressionOptions, upsellBreakout.details.title); + upsellBreakout.offerButtonAction = arcadeOfferButtonActionFromData(objectGraph, upsellData, models.marketingItemContextFromString("arcadeTabHeader"), metricsOptions); + metricsHelpersLocation.popLocation(metricsOptions.locationTracker); + return upsellBreakout; +} +export function pageTitleEffectFromData(objectGraph, upsellData) { + const heroVideoData = heroCommon.heroVideoFromData(objectGraph, upsellData, true); + const heroArtworkData = heroCommon.heroArtworkFromData(objectGraph, upsellData); + const titleEffectArtwork = heroVideoData.artworkData || heroArtworkData.artworkData; + return breakoutsCommon.titleEffectFromArtwork(objectGraph, titleEffectArtwork, true); +} +export function callToActionLabelFromData(objectGraph, data) { + return breakoutsCommon.callToActionLabelFromData(objectGraph, data); +} +export function artworkDictionaryFromData(objectGraph, data) { + return breakoutsCommon.artworkDictionaryFromData(objectGraph, data); +} +export function artworkDataFromData(objectGraph, data, attributePath) { + const dictionary = artworkDictionaryFromData(objectGraph, data); + return serverData.asDictionary(dictionary, attributePath); +} +export function titleFromData(objectGraph, data) { + return mediaAttributes.attributeAsString(data, "title"); +} +export function descriptionFromData(objectGraph, data) { + return mediaAttributes.attributeAsString(data, "subtitle"); +} +export function createBreakoutDetailsFromData(objectGraph, data, contextualAppDataOrNull, supportsMaterial, wantsBlur, badgeType) { + let badge = null; + switch (badgeType) { + case "wordmark": + badge = { + type: "wordmark", + }; + break; + case "text": + const badgeTitle = mediaAttributes.attributeAsString(data, "badge"); + if ((badgeTitle === null || badgeTitle === void 0 ? void 0 : badgeTitle.length) > 0) { + badge = { + type: "text", + title: badgeTitle, + }; + } + else { + badge = { + type: "none", + }; + } + break; + default: + badge = { + type: "none", + }; + break; + } + let backgroundStyle = breakoutsCommon.detailBackgroundStyleFromData(objectGraph, data, supportsMaterial, true, true); + const detailPosition = breakoutsCommon.detailPositionFromData(objectGraph, data, true, true); + if (wantsBlur) { + backgroundStyle = "dark"; + } + const details = new models.BreakoutDetails(titleFromData(objectGraph, data), descriptionFromData(objectGraph, data), badge, null, backgroundStyle, breakoutsCommon.detailTextAlignmentForDetailPosition(objectGraph, detailPosition, data, true)); + return details; +} +// region Offer Action +export function arcadeOfferButtonActionFromData(objectGraph, upsellData, context, metricsOptions) { + const arcadeUpsellData = { + marketingItemData: upsellData, + }; + // "arcadeTabHeader" context covers both hero and navbar button on the Arcade page + const showStarterPackOnboarding = context === models.marketingItemContextFromString("arcadeTabHeader") && + objectGraph.bag.arcadeDownloadPackPostSubscribeTrigger && + isFeatureEnabledForCurrentUser(objectGraph, objectGraph.bag.arcadeDownloadPackRolloutRate); + return arcadeActionFromUpsellData(objectGraph, arcadeUpsellData, callToActionLabelFromData(objectGraph, upsellData), metricsOptions, showStarterPackOnboarding); +} +export function arcadeActionFromUpsellData(objectGraph, data, title, baseMetricsOptions, showStarterPackOnboarding) { + const marketingItemData = data.marketingItemData; + if (!serverData.isDefinedNonNull(marketingItemData)) { + return null; + } + const offerData = offers.offerDataFromMarketingItem(objectGraph, marketingItemData); + const isLinkAction = serverData.asString(offerData, "kind") === "link"; + const linkUrlString = serverData.asString(offerData, "url"); + if (isLinkAction && linkUrlString) { + const linkAction = arcadeLinkActionFromMarketingItemLinkUrl(objectGraph, linkUrlString); + const linkClickOptions = { + id: objectGraph.bag.arcadeAppAdamId, + actionType: "buy", + actionContext: "Arcade", + contextualAdamId: objectGraph.bag.arcadeAppAdamId, + offerType: "subscribe", + targetType: "button", + mercuryMetricsData: metricsHelpersUtil.marketingItemTopLevelBaseFieldsFromData(objectGraph, marketingItemData), + ...baseMetricsOptions, + }; + linkAction.title = title; + metricsHelpersClicks.addClickEventToAction(objectGraph, linkAction, linkClickOptions); + return linkAction; + } + else { + const arcadeAction = arcadeBuyActionFromMarketingItemData(objectGraph, marketingItemData, title, baseMetricsOptions); + if (showStarterPackOnboarding) { + const downloadPackOnboarding = new models.FlowAction("arcadeDownloadPackCategories"); + const subscriptionStatus = "new"; + downloadPackOnboarding.pageData = subscriptionStatus; + downloadPackOnboarding.presentationContext = "presentModalFormSheet"; + // Passing `undefined` for other than subscribed actions instead of `BlankAction` to record an error. + const subscriptionCheckAction = new ArcadeSubscriptionStateAction(undefined, undefined, downloadPackOnboarding, undefined); + const action = new models.CompoundAction([arcadeAction, subscriptionCheckAction]); + action.title = title; + return action; + } + else { + return arcadeAction; + } + } +} +function arcadeLinkActionFromMarketingItemLinkUrl(objectGraph, urlString) { + /** + * Check against the bag routing regex and dispatch one of the AMS actions. + */ + // Dynamic UI + const dynamicUIRegexPatterns = objectGraph.bag.dynamicUIRegexStrings; + for (const pattern of dynamicUIRegexPatterns) { + const dynamicUIPattern = new RegExp(pattern); + if (dynamicUIPattern.test(urlString)) { + const action = new models.FlowAction("dynamicUI", urlString); + action.pageData = new models.DynamicUIRequestInfo(objectGraph.bag.metricsTopic); + return action; + } + } + // Finance UI regex check + const financeUIRegexPatterns = objectGraph.bag.financeUIRegexStrings; + for (const pattern of financeUIRegexPatterns) { + const financeUIPattern = new RegExp(pattern); + if (financeUIPattern.test(urlString)) { + return new models.FlowAction("finance", urlString); + } + } + // Web UI regex check + const webViewRegexPatterns = objectGraph.bag.webViewRegexStrings; + for (const pattern of webViewRegexPatterns) { + const webViewPattern = new RegExp(pattern); + if (webViewPattern.test(urlString)) { + return new models.FlowAction("webView", urlString); + } + } + return new models.ExternalUrlAction(urlString, false); +} +function arcadeBuyActionFromMarketingItemData(objectGraph, data, title, baseMetricsOptions) { + const offerData = offers.offerDataFromMarketingItem(objectGraph, data); + const offerName = serverData.asString(offerData, "offerName"); + const buyParams = serverData.asString(offerData, "buyParams"); + if (!serverData.isDefinedNonNull(offerName) || !serverData.isDefinedNonNull(buyParams)) { + return null; + } + const offerServiceTypes = serverData.asArrayOrEmpty(offerData, "serviceTypes"); + const isAristotleOffer = offerServiceTypes.length > 1 && objectGraph.bag.aristotleParentAppAdamId; + const parentAdamId = isAristotleOffer ? objectGraph.bag.aristotleParentAppAdamId : objectGraph.bag.arcadeAppAdamId; + const arcadeAction = new models.ArcadeAction(offerName, parentAdamId, { + buyParams: buyParams, + productIdentifier: offerName, + pageInformation: baseMetricsOptions.pageInformation, + }); + const metricsClickOptions = { + id: parentAdamId, + actionType: "buy", + targetType: "button", + subscriptionSKU: offerName, + actionContext: "Arcade", + contextualAdamId: parentAdamId, + actionDetails: { buyParams: buyParams }, + offerType: "subscribe", + mercuryMetricsData: metricsHelpersUtil.marketingItemTopLevelBaseFieldsFromData(objectGraph, data), + ...baseMetricsOptions, + }; + arcadeAction.title = title; + metricsHelpersClicks.addClickEventToAction(objectGraph, arcadeAction, metricsClickOptions); + return arcadeAction; +} +// endregion +// region Helpers +function offerTintFromMarketingItem(objectGraph, data) { + const templateParameters = breakoutsCommon.templateParametersFromData(objectGraph, data); + const fillColor = serverData.asString(templateParameters, "ctaButtonBackgroundColor"); + const textColor = serverData.asString(templateParameters, "ctaButtonTextColor"); + if (!serverData.isDefinedNonNull(fillColor) || !serverData.isDefinedNonNull(textColor)) { + return { type: "blue", fillColor: null, textColor: null }; + } + return { + type: "custom", + fillColor: color.fromHex(fillColor), + textColor: color.fromHex(textColor), + }; +} +// endregion +//# sourceMappingURL=arcade-upsell.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/common/arcade/breakouts-common.js b/node_modules/@jet-app/app-store/tmp/src/common/arcade/breakouts-common.js new file mode 100644 index 0000000..85d4bb4 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/common/arcade/breakouts-common.js @@ -0,0 +1,272 @@ +// +// breakouts-common.ts +// AppStoreKit +// +// Created by Jonathan Ellenbogen on 11/19/19. +// Copyright (c) 2016 Apple Inc. All rights reserved. +// +import { isNothing } from "@jet/environment"; +import * as models from "../../api/models"; +import * as serverData from "../../foundation/json-parsing/server-data"; +import * as mediaAttributes from "../../foundation/media/attributes"; +import * as platformAttributes from "../../foundation/media/platform-attributes"; +import * as mediaRelationship from "../../foundation/media/relationships"; +import * as color from "../../foundation/util/color-util"; +import { pageRouter } from "../builders/routing"; +import * as contentAttributes from "../content/attributes"; +import * as heroCommon from "../grouping/hero/hero-common"; +import * as offers from "../offers/offers"; +import * as productPageUtil from "../product-page/product-page-util"; +import { makeProductPageIntent } from "../../api/intents/product-page-intent"; +import { makeRoutableArticlePageIntent } from "../../api/intents/routable-article-page-intent"; +import { getLocale } from "../locale"; +import { makeRoutableArticlePageCanonicalUrl } from "../today/routable-article-page-url-utils"; +import { getPlatform } from "../preview-platform"; +export function detailBackgroundStyleFromData(objectGraph, breakoutData, supportsMaterial = true, isFirstShelf, isUpsell) { + const heroVideoData = heroCommon.heroVideoFromData(objectGraph, breakoutData); + const heroArtworkData = heroCommon.heroArtworkFromData(objectGraph, breakoutData); + const backgroundColor = heroVideoData.backgroundColor || heroArtworkData.backgroundColor; + const detailPosition = detailPositionFromData(objectGraph, breakoutData, isFirstShelf, isUpsell); + const displayBreakoutMaterial = breakoutData.type === "marketing-items" + ? serverData.asBooleanOrFalse(templateParametersFromData(objectGraph, breakoutData), "displayMaterial") + : mediaAttributes.attributeAsBooleanOrFalse(breakoutData, "displayBreakoutMaterial"); + const shouldUseMaterialBackground = displayBreakoutMaterial || (objectGraph.client.isTV && detailPosition === "center"); + if (shouldUseMaterialBackground && supportsMaterial) { + return "material"; + } + else { + return detailBackgroundStyleFromColor(objectGraph, backgroundColor); + } +} +export function detailBackgroundStyleFromColor(objectGraph, backgroundColor) { + if (!backgroundColor) { + return "dark"; + } + return color.isDarkColor(backgroundColor, 50) ? "dark" : "light"; +} +export function detailPositionFromData(objectGraph, data, isFirstShelf, isUpsell) { + // Upsells always position the details in the center. + if (objectGraph.client.isPhone || isUpsell) { + return "center"; + } + if (objectGraph.client.isTV) { + return isFirstShelf ? "center" : "leading"; + } + const breakoutDetailsString = data.type === "marketing-items" + ? serverData.asString(templateParametersFromData(objectGraph, data), "textPosition") + : mediaAttributes.attributeAsString(data, "breakoutTextAlignment"); + if (isNothing(breakoutDetailsString) || breakoutDetailsString.length === 0) { + return objectGraph.client.isMac ? "center" : "leading"; + } + switch (breakoutDetailsString.toLowerCase()) { + case "left": + return "leading"; + case "center": + return "center"; + case "right": + return "trailing"; + default: + return "leading"; + } +} +/** + * The text alignment to use for a particular detail position on large breakouts + * @param position The positioning of the details within the breakout. + */ +export function detailTextAlignmentForDetailPosition(objectGraph, position, breakoutData, isUpsell = false) { + const isTV = objectGraph.client.isTV; + switch (position) { + case "leading": + return isTV ? "center" : "leading"; + case "trailing": + return isTV ? "center" : "leading"; + case "center": + if (isUpsell && !isTV) { + return "center"; + } + else if (isTV) { + return "leading"; + } + else { + return breakoutData.type === "marketing-items" + ? "center" + : textAlignmentFromData(objectGraph, breakoutData); + } + default: + return "leading"; + } +} +function textAlignmentFromData(objectGraph, data) { + var _a; + const breakoutTextAlignmentString = (_a = mediaAttributes.attributeAsString(data, "breakoutTextAlignment")) !== null && _a !== void 0 ? _a : ""; + switch (breakoutTextAlignmentString.toLowerCase()) { + case "left": + return "leading"; + case "center": + return "center"; + case "right": + return "trailing"; + default: + return objectGraph.client.isMac ? "center" : "leading"; + } +} +export function templateParametersFromData(objectGraph, data) { + if (data.type !== "marketing-items") { + return null; + } + return mediaAttributes.attributeAsDictionary(data, "display.templateParameters"); +} +export function artworkDictionaryFromData(objectGraph, data) { + switch (data.type) { + case "editorial-items": + return mediaAttributes.attributeAsDictionary(data, "editorialArtwork"); + case "marketing-items": + return mediaAttributes.attributeAsDictionary(data, "marketingArtwork"); + default: + const attributePlatform = contentAttributes.bestAttributePlatformFromData(objectGraph, data); + return platformAttributes.platformAttributeAsDictionary(data, attributePlatform, "editorialArtwork"); + } +} +export function videoDictionaryFromData(objectGraph, data) { + switch (data.type) { + case "editorial-items": + return mediaAttributes.attributeAsDictionary(data, "editorialVideo"); + case "marketing-items": + return mediaAttributes.attributeAsDictionary(data, "marketingVideo"); + default: + if (preprocessor.GAMES_TARGET) { + return mediaAttributes.attributeAsDictionary(data, "editorialVideo"); + } + else { + const attributePlatform = contentAttributes.bestAttributePlatformFromData(objectGraph, data); + return platformAttributes.platformAttributeAsDictionary(data, attributePlatform, "marketingVideo"); + } + } +} +export function callToActionLabelFromData(objectGraph, data) { + switch (data.type) { + case "marketing-items": + const offerData = offers.offerDataFromMarketingItem(objectGraph, data); + return serverData.asString(offerData, "callToActionLabel"); + default: + return mediaAttributes.attributeAsString(data, "breakoutCallToActionLabel"); + } +} +export function requiresPrimaryContent(objectGraph, data) { + const linkData = mediaAttributes.attributeAsDictionary(data, "link"); + const isLinkAction = serverData.isDefinedNonNullNonEmpty(linkData); + const isStoryAction = mediaAttributes.attributeAsBooleanOrFalse(data, "isCanvasAvailable"); + return !isLinkAction && !isStoryAction; +} +export function wantsBlur(objectGraph, backgroundStyle, isInHeroPosition) { + return backgroundStyle !== "material" && isInHeroPosition; +} +/** + * The action to use when displaying a breakout's call to action button + * @param data The breakout node data from MAPI + */ +export function actionFromData(objectGraph, data) { + const linkData = mediaAttributes.attributeAsDictionary(data, "link"); + const isLinkAction = serverData.isDefinedNonNullNonEmpty(linkData); + const isProductAction = mediaAttributes.attributeAsString(data, "kind") === "App"; + const isStoryAction = mediaAttributes.attributeAsBooleanOrFalse(data, "isCanvasAvailable"); + const primaryContent = mediaRelationship.relationshipData(objectGraph, data, "primary-content"); + if (!isLinkAction && !isStoryAction && !mediaAttributes.hasAttributes(primaryContent)) { + return null; + } + let actionUrl = null; + if (isLinkAction) { + actionUrl = serverData.asString(linkData, "url"); + } + else if (isStoryAction) { + actionUrl = mediaAttributes.attributeAsString(data, "url"); + } + else { + actionUrl = mediaAttributes.attributeAsString(primaryContent, "url"); + } + if (serverData.isNull(actionUrl)) { + return null; + } + let action = null; + if (isLinkAction && serverData.asString(linkData, "target") === "external") { + const externalUrlAction = new models.ExternalUrlAction(actionUrl); + action = externalUrlAction; + } + else if (objectGraph.isAvailable(pageRouter)) { + const flowPage = objectGraph.required(pageRouter).fetchFlowPage(actionUrl); + const flowAction = new models.FlowAction(flowPage); + flowAction.pageUrl = actionUrl; + if (flowPage === "product") { + flowAction.pageData = productPageUtil.createProductPageSidePackFromResponse(objectGraph, primaryContent); + } + action = flowAction; + } + else if (objectGraph.client.isWeb && isStoryAction) { + const routableArticlePageIntent = makeRoutableArticlePageIntent({ + ...getLocale(objectGraph), + ...getPlatform(objectGraph), + id: data.id, + }); + const flowAction = new models.FlowAction("article"); + flowAction.title = mediaAttributes.attributeAsString(data, "breakoutCallToActionLabel"); + flowAction.pageUrl = makeRoutableArticlePageCanonicalUrl(objectGraph, routableArticlePageIntent); + flowAction.destination = routableArticlePageIntent; + action = flowAction; + } + else if (objectGraph.client.isWeb && isProductAction) { + const flowAction = new models.FlowAction("product"); + flowAction.title = mediaAttributes.attributeAsString(data, "breakoutCallToActionLabel"); + flowAction.pageUrl = actionUrl; + flowAction.destination = makeProductPageIntent({ + ...getLocale(objectGraph), + ...getPlatform(objectGraph), + id: primaryContent.id, + }); + action = flowAction; + } + if (action) { + action.title = callToActionLabelFromData(objectGraph, data); + } + return action; +} +export function titleEffectFromArtwork(objectGraph, editorialArtworkData, isUpsell = false) { + if (serverData.isNullOrEmpty(editorialArtworkData)) { + return null; + } + let titleEffect = null; + const white = color.named("white"); + const black = color.named("black"); + const backgroundColor = color.fromHex(serverData.asString(editorialArtworkData, "bgColor")); + const gradientColors = serverData + .asArrayOrEmpty(editorialArtworkData, "textGradient") + .map((hexColor) => { + return color.fromHex(hexColor); + }); + const backgroundStyle = detailBackgroundStyleFromColor(objectGraph, backgroundColor); + const hasGradient = gradientColors.length === 2; + if (hasGradient) { + titleEffect = new models.TitleEffect("horizontalGradient"); + const startColor = gradientColors[0]; + const endColor = gradientColors[1]; + titleEffect.gradientStartColor = color.dynamicWith(startColor, startColor); + titleEffect.gradientEndColor = color.dynamicWith(endColor, endColor); + titleEffect.filter = "plusLight"; + if (!isUpsell) { + titleEffect.accessoryColor = titleEffect.gradientEndColor; + } + } + else { + titleEffect = new models.TitleEffect("color"); + titleEffect.color = backgroundStyle === "dark" ? white : black; + if (!isUpsell) { + titleEffect.accessoryColor = titleEffect.color; + } + titleEffect.isFallbackStyle = true; + } + // The accessory on tvOS should always be white resulting in a dark accessory. + if (objectGraph.client.isTV) { + titleEffect.accessoryColor = white; + } + return titleEffect; +} +//# sourceMappingURL=breakouts-common.js.map
\ No newline at end of file diff --git a/node_modules/@jet-app/app-store/tmp/src/common/arcade/render-arcade-see-all-games-page.js b/node_modules/@jet-app/app-store/tmp/src/common/arcade/render-arcade-see-all-games-page.js new file mode 100644 index 0000000..f1d39e0 --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/common/arcade/render-arcade-see-all-games-page.js @@ -0,0 +1,227 @@ +import * as validation from "@jet/environment/json/validation"; +import { Shelf, Trailers, Video, } from "../../api/models"; +import { metricsFromMediaApiObject, } from "../../foundation/media/data-structure"; +import { hasAttributes } from "../../foundation/media/attributes"; +import { isDefinedNonNullNonEmpty, isNullOrEmpty } from "../../foundation/json-parsing/server-data"; +import { Parameters, Path, Protocol } from "../../foundation/network/url-constants"; +import { URL } from "../../foundation/network/urls"; +import * as lockups from "../../common/lockups/lockups"; +import * as metricsHelpersImpressions from "../../common/metrics/helpers/impressions"; +import * as videoDefaults from "../../common/constants/video-constants"; +import * as content from "../../common/content/content"; +import { newLocationTracker, nextPosition, popLocation, pushContentLocation, } from "../metrics/helpers/location"; +export function createArcadeSeeAllGamesPaginationToken(_objectGraph, requestDescriptor, pageInformation) { + const locationTracker = newLocationTracker(); + const paginationUrl = URL.fromComponents(Protocol.internal, null, `/${Path.arcadeSeeAllGames}/${Path.arcadeSeeAllGamesLoadMore}`).build(); + const paginationToken = { + url: paginationUrl, + metricsPageInformation: pageInformation, + metricsLocationTracker: locationTracker, + remainingGroups: [], + lastShelfIndex: 0, + isCompactMode: requestDescriptor.isCompactMode, + }; + return paginationToken; +} +export function createShelves(objectGraph, groups, paginationToken) { + return validation.context("createShelves", () => { + const shelves = []; + if (groups.length === 0) { + return shelves; + } + let shouldStartPagination = false; + for (const group of groups) { + // Start pagination if we hit a group who's first item has not been hydrated + shouldStartPagination = shouldStartPagination || !hasAttributes(group.data[0]); + if (shouldStartPagination) { + paginationToken.remainingGroups.push(group); + } + else { + const shelfToken = { + index: paginationToken.lastShelfIndex, + title: shelfTitleForGroup(objectGraph, group), + contentType: shelfContentTypeForMode(objectGraph, paginationToken.isCompactMode), + shouldFilter: false, + remainingContent: group.data, + groupKind: group.kind, + isCompactMode: paginationToken.isCompactMode, + hasExistingContent: false, + isFirstRender: true, + metricsPageInformation: paginationToken.metricsPageInformation, + metricsLocationTracker: paginationToken.metricsLocationTracker, + }; + const shelf = createShelf(objectGraph, shelfToken); + shelves.push(shelf); + paginationToken.lastShelfIndex++; + } + } + return shelves; + }); +} +const defaultRowsPerColumn = 3; +export function createShelf(objectGraph, shelfToken) { + const shelfItems = []; + const shelf = new Shelf(shelfToken.contentType); + shelf.title = shelfToken.title; + shelf.presentationHints = { showSupplementaryText: false }; + if (objectGraph.client.isVision) { + shelf.isHorizontal = + (shelfToken.groupKind === "comingSoonGrouping" && !shelfToken.isCompactMode) || + shelfToken.groupKind === "category"; + shelf.presentationHints = { + ...shelf.presentationHints, + isSAGComingSoon: true, + }; + } + else { + shelf.isHorizontal = shelfToken.groupKind === "comingSoonGrouping" || shelfToken.groupKind === "category"; + } + const shelfMetricsOptions = { + id: `${shelfToken.index}`, + kind: null, + softwareType: null, + targetType: "swoosh", + title: shelf.title, + pageInformation: shelfToken.metricsPageInformation, + locationTracker: shelfToken.metricsLocationTracker, + idType: "sequential", + }; + /// add impression fields + metricsHelpersImpressions.addImpressionFields(objectGraph, shelf, shelfMetricsOptions); + pushContentLocation(objectGraph, shelfMetricsOptions, shelf.title); + while (isDefinedNonNullNonEmpty(shelfToken.remainingContent) && hasAttributes(shelfToken.remainingContent[0])) { + const lockupData = shelfToken.remainingContent.shift(); + const lockupOptions = { + metricsOptions: { + pageInformation: shelfToken.metricsPageInformation, + locationTracker: shelfToken.metricsLocationTracker, + recoMetricsData: metricsFromMediaApiObject(lockupData), + }, + artworkUseCase: 1 /* content.ArtworkUseCase.LockupIconSmall */, + canDisplayArcadeOfferButton: content.shelfContentTypeCanDisplayArcadeOfferButtons(objectGraph, shelfToken.contentType), + shouldHideArcadeHeader: objectGraph.featureFlags.isEnabled("hide_arcade_header_on_arcade_tab"), + isContainedInPreorderExclusiveShelf: shelfToken.groupKind === "comingSoonGrouping", + }; + const cropCode = objectGraph.client.isVision ? "sr" : null; + const lockup = lockups.mixedMediaLockupFromData(objectGraph, lockupData, lockupOptions, videoDefaults.defaultVideoConfiguration(objectGraph), null, cropCode); + const lockupHasVideos = ensureLockupHasVideos(objectGraph, lockup, lockupData); + if (!lockupHasVideos) { + continue; + } + lockup.editorialTagline = null; + lockup.developerTagline = null; + shelfItems.push(lockup); + nextPosition(shelfToken.metricsLocationTracker); + } + shelf.mergeWhenFetched = shelfToken.hasExistingContent; + shelfToken.hasExistingContent = shelfToken.hasExistingContent || shelfItems.length > 0; + shelf.items = shelfItems; + if (isDefinedNonNullNonEmpty(shelfToken.remainingContent)) { + shelf.url = shelfUrlForToken(shelfToken); + } + else if (objectGraph.client.deviceType !== "tv" && + shelfToken.isCompactMode && + shelf.items.length < defaultRowsPerColumn) { + shelf.rowsPerColumn = shelf.items.length; + } + popLocation(shelfToken.metricsLocationTracker); + return shelf; +} +function shelfContentTypeForMode(objectGraph, isCompactMode) { + if (isCompactMode) { + return "smallLockup"; + } + else if (objectGraph.client.isTV) { + return "mixedMediaLockup"; + } + else { + return "appTrailerLockup"; + } +} +function shelfTitleForGroup(objectGraph, group) { + let shelfTitle; + let dateComponents = null; + let groupDateForTitle = null; + let sentence; + // rdar://104719340 (Arcade See All Games section has a missing name) + // Temporary(?) work around for MAPI sometimes not returning a group.name + if ((!group.name || group.name.length === 0) && group.kind !== "comingSoonGrouping") { + return ""; + } + switch (group.kind) { + case "releaseDateByDay": + dateComponents = group.name.split("-"); + groupDateForTitle = new Date(parseInt(dateComponents[0]), parseInt(dateComponents[1]) - 1, parseInt(dateComponents[2])); + sentence = objectGraph.loc.string("ALL_GAMES_SECTION_TITLE_RELEASE_DATE_SENTENCE"); + shelfTitle = objectGraph.loc.formatDateInSentence(sentence, "MMMM d, y", groupDateForTitle); + break; + case "lastUpdatedByDay": + dateComponents = group.name.split("-"); + groupDateForTitle = new Date(parseInt(dateComponents[0]), parseInt(dateComponents[1]) - 1, parseInt(dateComponents[2])); + sentence = objectGraph.loc.string("ALL_GAMES_SECTION_TITLE_LAST_UPDATED_SENTENCE"); + shelfTitle = objectGraph.loc.formatDateInSentence(sentence, "MMMM d, y", groupDateForTitle); + break; + case "comingSoonGrouping": + shelfTitle = objectGraph.loc.string("Arcade.SeeAllGames.ComingSoonShelf.Title"); + break; + default: + shelfTitle = group.name; + break; + } + return shelfTitle; +} +function shelfUrlForToken(shelfToken) { + if (isNullOrEmpty(shelfToken.remainingContent)) { + return null; + } + return `${Protocol.internal}:/${Path.arcadeSeeAllGames}/${Path.shelf}/?${Parameters.token}=${encodeURIComponent(JSON.stringify(shelfToken))}`; +} +// region Fallback Data +function ensureLockupHasVideos(objectGraph, lockup, lockupData) { + // In visionOS, the bin-compat apps usually have trailer from iPad which using 4:3 video + // But SAG lockup preferred 16:9 video, so even we already have lockup.trailers, we still want to override it + // with the editorial splash video `splashVideo16x9`. + // If there is no editorial splash video found, we fallback to the current `lockup.trailers` or create a dump video. + if (isNullOrEmpty(lockup.trailers) || objectGraph.client.isVision) { + const uberVideo = content.editorialSplashVideoFromData(objectGraph, lockupData); + if (isDefinedNonNullNonEmpty(uberVideo)) { + const uberTrailer = new Trailers(); + uberVideo.playbackControls = videoDefaults.standardControls(objectGraph); + uberVideo.autoPlayPlaybackControls = videoDefaults.autoPlayControls(objectGraph); + uberVideo.canPlayFullScreen = true; + uberTrailer.videos = [uberVideo]; + lockup.trailers = [uberTrailer]; + } + } + if (isNullOrEmpty(lockup.trailers)) { + /** + * <rdar://problem/55328972> See All Games: Bring back screenshots + * In the very rare case that an arcade game has absolutely no videos, + * that means no app trailer, no editorial video in any form. We're going to create a fake + * video that uses the first screenshot as a preview frame. This allows us to + * use the same AppTrailersLockup and get uniform sizing for lockups. + */ + addFakeVideoToMixedMediaLockup(lockup); + } + return isDefinedNonNullNonEmpty(lockup.trailers); +} +const fakeVideoURL = "x-as3-internal:/today/test"; +function addFakeVideoToMixedMediaLockup(lockup) { + const screenshots = lockup.screenshots[0]; + if (isNullOrEmpty(screenshots)) { + return; + } + const firstScreenshot = screenshots.artwork[0]; + const fakeVideo = new Video(fakeVideoURL, firstScreenshot, { + allowsAutoPlay: false, + looping: false, + canPlayFullScreen: false, + autoPlayPlaybackControls: {}, + playbackControls: {}, + }); + const fakeTrailers = new Trailers(); + fakeTrailers.videos = [fakeVideo]; + lockup.trailers = [fakeTrailers]; +} +// endregion +//# sourceMappingURL=render-arcade-see-all-games-page.js.map
\ No newline at end of file |
