summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/common/arcade
diff options
context:
space:
mode:
authorrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
committerrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
commitbce557cc2dc767628bed6aac87301a1be7c5431b (patch)
treeb51a051228d01fe3306cd7626d4a96768aadb944 /node_modules/@jet-app/app-store/tmp/src/common/arcade
init commit
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/common/arcade')
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-common.js441
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-see-all-games-facets.js245
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-see-all-request.js78
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-see-all-routing.js5
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/arcade/arcade-upsell.js257
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/arcade/breakouts-common.js272
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/arcade/render-arcade-see-all-games-page.js227
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