summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/common/today
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/common/today')
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/article-request.js86
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/article.js1572
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/category-detail-motion-16x9.js34
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/category-detail-static-16x9.js49
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/day-card.js51
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/event-card.js51
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/general-card.js51
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/index.js85
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/list-card-motion-21x9.js116
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/list-card-static-21x9.js92
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/media-card.js51
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/store-front-video-4x3.js22
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/store-front-video.js25
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-card-motion-16x9.js34
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-card-static-16x9.js49
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-centered-motion-16x9.js34
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-centered-static-16x9.js49
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/universal-a-motion-16x9.js146
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/universal-a-static-16x9.js107
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/artwork/today-artwork-util.js267
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-ad-card-builder.js232
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-app-event-card-builder.js112
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-base-card-builder.js244
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-branded-card-builder.js58
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-full-bleed-image-card-builder.js37
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-in-app-purchase-card-builder.js62
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-list-card-builder.js71
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-river-card-builder.js62
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-short-image-card-builder.js42
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-single-app-card-builder.js81
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-video-card-builder.js33
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/onboarding-cards.js23
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/routable-article-page-url-utils.js32
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/today-card-overlay-util.js111
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/today-card-util.js785
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/today-controller-util.js534
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/today-horizontal-card-util.js352
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/today-parse-util.js1047
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/today-types.js118
39 files changed, 7007 insertions, 0 deletions
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/article-request.js b/node_modules/@jet-app/app-store/tmp/src/common/today/article-request.js
new file mode 100644
index 0000000..4371605
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/article-request.js
@@ -0,0 +1,86 @@
+import { defaultAdditionalPlatformsForClient, Request } from "../../foundation/media/data-fetching";
+import { shouldFetchCustomAttributes } from "../product-page/product-page-variants";
+import { appEventsAreEnabled, appOfferItemsAreEnabled } from "../app-promotions/app-promotions-common";
+import { shouldUsePrerenderedIconArtwork } from "../content/content";
+import { AppEventsAttributes } from "../../gameservicesui/src/foundation/media-api/requests/recommendation-request-types";
+function buildAttributesForArticlePageRequest(objectGraph) {
+ const attributes = [
+ "screenshotsByType",
+ "videoPreviewsByType",
+ "requiredCapabilities",
+ "minimumOSVersion",
+ "editorialArtwork",
+ "editorialVideo",
+ "editorialClientParams",
+ "shortEditorialNotes",
+ "enrichedEditorialNotes",
+ ];
+ if (objectGraph.bag.enableUpdatedAgeRatings) {
+ attributes.push("ageRating");
+ }
+ if (objectGraph.appleSilicon.isSupportEnabled) {
+ attributes.push("macRequiredCapabilities");
+ }
+ if (objectGraph.client.isMac) {
+ attributes.push("hasMacIPAPackage");
+ }
+ if (objectGraph.client.isVision) {
+ attributes.push("compatibilityControllerRequirement");
+ }
+ if (shouldUsePrerenderedIconArtwork(objectGraph)) {
+ attributes.push("iconArtwork");
+ }
+ return attributes;
+}
+/**
+ * Create a Media API request for an `editorial-item`
+ *
+ * This corresponsd to an "Article" or "Story" page
+ */
+export function buildArticlePageRequest(objectGraph, intent, isIncomingURL) {
+ const request = new Request(objectGraph)
+ .withIdOfType(intent.id, "editorial-items")
+ .includingAdditionalPlatforms(defaultAdditionalPlatformsForClient(objectGraph))
+ .includingAttributes(buildAttributesForArticlePageRequest(objectGraph))
+ .includingRelationships(["canvas"])
+ .includingRelationshipsForUpsell(true)
+ .includingMacOSCompatibleIOSAppsWhenSupported(true)
+ .usingCustomAttributes(shouldFetchCustomAttributes(objectGraph));
+ if (!isIncomingURL) {
+ request.includingAgeRestrictions();
+ }
+ if (appEventsAreEnabled(objectGraph)) {
+ request.enablingFeature("appEvents");
+ request.includingScopedAttributes("app-events", AppEventsAttributes);
+ request.includingScopedRelationships("app-events", ["app"]);
+ request.includingScopedRelationships("editorial-item-shelves", ["app-events"]);
+ request.includingScopedAvailableIn("app-events", ["past"]);
+ }
+ if (appOfferItemsAreEnabled(objectGraph)) {
+ request.enablingFeature("offerItems");
+ request.includingScopedRelationships("offer-items", ["salables"]);
+ request.includingAssociateKeys("editorial-items", ["editorial-cards"]);
+ request.includingMetaKeys("offer-items:salables", ["discountOffer"]);
+ request.includingScopedAttributes("offer-items", [
+ "title",
+ "subtitle",
+ "additionalTerms",
+ "redemptionExpirationDate",
+ ]);
+ }
+ if (objectGraph.client.isVision) {
+ request.enablingFeature("supportsCustomTextColor");
+ request.includingScopedAttributes("editorial-items", ["enrichedEditorialNotes"]);
+ }
+ if (objectGraph.client.isWeb) {
+ request.includingAttributes([
+ // Publication date is used as part of SEO meta-data
+ "lastPublishedDate",
+ ]);
+ }
+ if (preprocessor.GAMES_TARGET) {
+ request.includingScopedAttributes("apps", ["isEligibleForGamesApp"]);
+ }
+ return request;
+}
+//# sourceMappingURL=article-request.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/article.js b/node_modules/@jet-app/app-store/tmp/src/common/today/article.js
new file mode 100644
index 0000000..a67fde4
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/article.js
@@ -0,0 +1,1572 @@
+/**
+ * Created by keithpk on 3/21/17.
+ */
+import { isNothing, isSome } from "@jet/environment";
+import * as validation from "@jet/environment/json/validation";
+import * as models from "../../api/models";
+import * as serverData from "../../foundation/json-parsing/server-data";
+import * as mediaAttributes from "../../foundation/media/attributes";
+import * as mediaAugment from "../../foundation/media/augment";
+import * as mediaDataStructure from "../../foundation/media/data-structure";
+import * as mediaNetwork from "../../foundation/media/network";
+import * as mediaRelationships from "../../foundation/media/relationships";
+import * as urls from "../../foundation/network/urls";
+import * as color from "../../foundation/util/color-util";
+import { PageID } from "../../gameservicesui/src/common/id-builder";
+import * as gamesComponentBuilder from "../../gameservicesui/src/editorial-page/editorial-component-builder";
+import * as appPromotionsShelf from "../app-promotions/app-promotions-shelf";
+import * as arcadeCommon from "../arcade/arcade-common";
+import * as arcadeUpsell from "../arcade/arcade-upsell";
+import * as breakoutsCommon from "../arcade/breakouts-common";
+import * as videoDefaults from "../constants/video-constants";
+import * as artworkBuilder from "../content/artwork/artwork";
+import * as content from "../content/content";
+import { EditorialMediaPlacement } from "../editorial-pages/editorial-media-util";
+import { buildSmallStoryCardShelf } from "../editorial-pages/editorial-page-shelf-builder/editorial-page-collection-shelf-builder/editorial-page-story-card-collection-shelf-builder";
+import { buildStoryCard } from "../editorial-pages/editorial-page-shelf-builder/editorial-page-collection-shelf-builder/editorial-page-story-card-utils";
+import { createBaseShelfToken } from "../editorial-pages/editorial-page-shelf-token";
+import { CollectionShelfDisplayStyle } from "../editorial-pages/editorial-page-types";
+import * as externalDeepLink from "../linking/external-deep-link";
+import * as links from "../linking/os-update-links";
+import * as lockups from "../lockups/lockups";
+import * as metricsHelpersClicks from "../metrics/helpers/clicks";
+import * as metricsHelpersImpressions from "../metrics/helpers/impressions";
+import * as metricsHelpersLocation from "../metrics/helpers/location";
+import * as metricsHelpersMedia from "../metrics/helpers/media";
+import * as metricsHelpersPage from "../metrics/helpers/page";
+import * as metricsHelpersUtil from "../metrics/helpers/util";
+import * as sharing from "../sharing";
+import { crossLinkSubtitleFromData, defaultTodayCardConfiguration, fallbackWatchTodayCardFromData, todayCardFromData, } from "./today-card-util";
+import * as todayHorizontalCardUtil from "./today-horizontal-card-util";
+import { todayCardPreviewUrlForTodayCard } from "./today-parse-util";
+import { HeroMediaDisplayContext, TodayCardDisplayStyle, TodayParseContext, } from "./today-types";
+export const iAPBackgroundColor = color.named("componentBackgroundStandout");
+const appShowcaseBackgroundColor = color.named("componentBackgroundStandout");
+const arcadeShowcaseShelfBackgroundColor = color.named("componentBackgroundStandout");
+/**
+ * Resolves the article module's app media platform to an `AppPlatform` to use for screenshots.
+ * @param {AppMediaPlatform} appMediaPlatform The server-dictated media platform to use for the module.
+ * @returns {AppPlatform} The app platform that is appropriate for this media platform, taking into account our device.
+ */
+function appPlatformFromAppMediaPlatform(objectGraph, appMediaPlatform) {
+ switch (appMediaPlatform) {
+ case "Watch":
+ return "watch";
+ case "iOS":
+ if (objectGraph.client.isPad) {
+ return "pad";
+ }
+ else {
+ return "phone";
+ }
+ case "tvOS":
+ return "tv";
+ case "Messages":
+ return "messages";
+ case "visionOS":
+ return "vision";
+ default:
+ return null;
+ }
+}
+export class ArticleParseContext {
+ constructor() {
+ // The index of the current module
+ this.index = 0;
+ // The reco metrics from the shelf on the today page
+ this.todayShelfRecoMetricsData = {};
+ /// Whether there are any focusable elements (for touch mode)
+ this.hasFocusableElements = false;
+ /// Whether there are any non-focusable elements (for touch mode)
+ this.hasNonFocusableElements = false;
+ /// Whether there is a resilient deep link.
+ this.isResilientDeepLink = false;
+ /// Whether or not to allow app event previews, used by editorial to preview app event stories before they are published
+ this.allowUnpublishedAppEventPreviews = false;
+ }
+}
+function todayCardConfigFromArticleContext(objectGraph, articleContext) {
+ if (!serverData.isDefinedNonNull(articleContext)) {
+ return null;
+ }
+ if (isSome(articleContext.todayCardConfig)) {
+ return articleContext.todayCardConfig;
+ }
+ const config = defaultTodayCardConfiguration(objectGraph);
+ config.enableListCardToMultiAppFallback = false;
+ config.clientIdentifierOverride = articleContext.clientIdentifierOverride;
+ config.useOTDTextStyle = false;
+ config.allowUnpublishedAppEventPreviews = articleContext.allowUnpublishedAppEventPreviews;
+ config.currentRowIndex = undefined;
+ switch (objectGraph.client.deviceType) {
+ case "mac":
+ config.prevailingCropCodes = { defaultCrop: "en" };
+ config.coercedCollectionTodayCardDisplayStyle = TodayCardDisplayStyle.Grid;
+ config.heroDisplayContext = HeroMediaDisplayContext.Article;
+ break;
+ case "tv":
+ config.prevailingCropCodes = {
+ "defaultCrop": "ek",
+ "editorialArtwork.storyCenteredStatic16x9": "SCS.ApDHXL01",
+ };
+ config.coercedCollectionTodayCardDisplayStyle = TodayCardDisplayStyle.Grid;
+ config.heroDisplayContext = HeroMediaDisplayContext.Article;
+ break;
+ case "web":
+ config.coercedCollectionTodayCardDisplayStyle = TodayCardDisplayStyle.River;
+ config.prevailingCropCodes = {
+ "defaultCrop": "sr",
+ "editorialArtwork.dayCard": "grav.west",
+ };
+ break;
+ default:
+ break;
+ }
+ return config;
+}
+export function articlePageFromResponse(objectGraph, articleResponse, context) {
+ return validation.context("articlePageWithResponse", () => {
+ var _a;
+ const articleData = mediaDataStructure.dataFromDataContainer(objectGraph, articleResponse);
+ context.metricsPageInformation = metricsHelpersPage.metricsPageInformationFromMediaApiResponse(objectGraph, "editorialItem", articleData.id, articleResponse);
+ context.metricsLocationTracker = metricsHelpersLocation.newLocationTracker();
+ context.pageId = articleData.id;
+ // Bridge over article contexts to today's metrics context and card config
+ const todayParseContext = new TodayParseContext(context.metricsPageInformation, context.metricsLocationTracker, context.refreshController);
+ const todayCardConfig = todayCardConfigFromArticleContext(objectGraph, context);
+ // Render the top card
+ let todayCard = todayCardFromData(objectGraph, articleData, todayCardConfig, todayParseContext);
+ let editorialStoryCard = null;
+ const todayCardMedia = todayCard === null || todayCard === void 0 ? void 0 : todayCard.media;
+ if (objectGraph.client.isVision || preprocessor.GAMES_TARGET) {
+ editorialStoryCard = buildStoryCard(objectGraph, articleData, EditorialMediaPlacement.StoryDetail, {
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ }, CollectionShelfDisplayStyle.StoryMedium, false);
+ todayCard = null;
+ }
+ if (isNothing(todayCard)) {
+ todayCard = fallbackWatchTodayCardFromData(objectGraph, articleData, todayCardConfig, todayParseContext);
+ }
+ // Get the title for metrics purposes.
+ const title = (_a = todayCard === null || todayCard === void 0 ? void 0 : todayCard.title) !== null && _a !== void 0 ? _a : editorialStoryCard === null || editorialStoryCard === void 0 ? void 0 : editorialStoryCard.title;
+ const editorialItemKind = mediaAttributes.attributeAsString(articleData, "kind");
+ // Configure subtitle for cross link
+ context.crossLinkSubtitle = crossLinkSubtitleFromData(objectGraph, articleData);
+ // Bridge today config back into articles, now that cards are created.
+ // Now we've created the card, reference the clientIdentifierOverride it used for the rest of the article.
+ context.clientIdentifierOverride = todayCardConfig.clientIdentifierOverride;
+ // Start a metrics location
+ metricsHelpersLocation.pushContentLocation(objectGraph, {
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ targetType: "article",
+ id: context.pageId,
+ idType: "its_id",
+ }, title);
+ // Render the article itself
+ const shelves = renderArticle(objectGraph, articleData, todayCardMedia, context);
+ const lastShelf = shelves[shelves.length - 1];
+ // Sharing
+ const shareAction = objectGraph.client.isTV ||
+ objectGraph.client.isWeb ||
+ context.isResilientDeepLink ||
+ preprocessor.GAMES_TARGET ||
+ editorialItemKind === "OfferItem"
+ ? null
+ : shareSheetActionFromData(objectGraph, articleData, todayCardConfig);
+ if (serverData.isDefinedNonNull(shareAction)) {
+ // Add click event
+ metricsHelpersClicks.addClickEventToAction(objectGraph, shareAction, {
+ targetType: "button",
+ id: context.pageId,
+ actionType: "share",
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ });
+ const isLastModuleFullWidth = isArticleShelfFullWidth(objectGraph, lastShelf, context.module);
+ const shareButtonShelf = createShareShelf(objectGraph, shareAction, context, isLastModuleFullWidth);
+ if (shareButtonShelf) {
+ shelves.push(shareButtonShelf);
+ }
+ }
+ const page = new models.ArticlePage(todayCard, shelves, shareAction);
+ page.editorialStoryCard = editorialStoryCard;
+ page.title = todayCard === null || todayCard === void 0 ? void 0 : todayCard.title;
+ page.subtitle = todayCard === null || todayCard === void 0 ? void 0 : todayCard.inlineDescription;
+ addFooterLockupForPageIfNeeded(objectGraph, page, articleData, context);
+ if (objectGraph.client.isTV) {
+ if (context.hasFocusableElements && !context.hasNonFocusableElements) {
+ page.touchMode = "focus";
+ }
+ else if (!context.hasFocusableElements && context.hasNonFocusableElements) {
+ page.touchMode = "pan";
+ }
+ else {
+ page.touchMode = "auto";
+ }
+ }
+ // Map whether the article should terminate on close.
+ page.shouldTerminateOnClose = context.isResilientDeepLink;
+ metricsHelpersPage.addMetricsEventsToPageWithInformation(objectGraph, page, context.metricsPageInformation, (fields) => {
+ let additionalValue = title;
+ if ((todayCard === null || todayCard === void 0 ? void 0 : todayCard.media) instanceof models.TodayCardMediaBrandedSingleApp &&
+ (todayCard === null || todayCard === void 0 ? void 0 : todayCard.overlay) instanceof models.TodayCardLockupOverlay) {
+ const lockupOverlay = todayCard === null || todayCard === void 0 ? void 0 : todayCard.overlay;
+ additionalValue = lockupOverlay.lockup.title;
+ }
+ if (!additionalValue) {
+ return;
+ }
+ let pageDetails = serverData.asString(serverData.asJSONValue(fields["pageDetails"]), "coercible");
+ pageDetails = pageDetails || serverData.asString(serverData.asJSONValue(fields["pageId"]));
+ if (pageDetails) {
+ fields["pageDetails"] = `${pageDetails}_${additionalValue}`;
+ }
+ else {
+ fields["pageDetails"] = `unknown_${additionalValue}`;
+ }
+ });
+ page.canonicalURL = mediaAttributes.attributeAsString(articleData, "url");
+ if (isSome(articleData)) {
+ const articleUrl = mediaAttributes.attributeAsString(articleData, "url");
+ if (isSome(articleUrl)) {
+ page.viewArticleAction = new models.ExternalUrlAction(articleUrl, true);
+ }
+ }
+ return page;
+ });
+}
+function renderArticle(objectGraph, articleData, cardMedia, context) {
+ return validation.context("renderArticle", () => {
+ var _a;
+ const shelves = [];
+ const canvas = (_a = mediaRelationships.relationshipCollection(articleData, "canvas")) !== null && _a !== void 0 ? _a : [];
+ for (const storyModule of canvas) {
+ context.module = mediaAttributes.attributeAsString(storyModule, "displayType");
+ context.subStyle = null;
+ const shelfIndex = shelves.length;
+ const shelvesToRender = renderModule(objectGraph, storyModule, articleData, context, shelfIndex);
+ if (shelvesToRender.length > 0) {
+ for (const shelf of shelvesToRender) {
+ shelf.title = context.titleForNextShelf;
+ if (objectGraph.client.isTV) {
+ // Skip unsupported tvOS shelves
+ if (shelf.contentType === "editorialLink") {
+ continue;
+ }
+ }
+ else if (objectGraph.client.isWatch) {
+ // Skip unsupported watchOS shelves
+ if (shelf.contentType === "editorialLink") {
+ continue;
+ }
+ }
+ shelves.push(shelf);
+ context.titleForNextShelf = null;
+ }
+ }
+ context.index++;
+ metricsHelpersLocation.nextPosition(context.metricsLocationTracker);
+ }
+ // If we we're showing the fallback list card type on the today page, we're going to show
+ // the lockups as a list shelf underneath so we can still display the list contents.
+ // If we're on watchOS, we also want to hit this codepath so that lockup lists do not
+ // show as empty pages.
+ if ((context.showingFallbackMediaInline ||
+ objectGraph.client.isWatch ||
+ objectGraph.client.isVision ||
+ objectGraph.client.isWeb ||
+ preprocessor.GAMES_TARGET) &&
+ shelves.length === 0) {
+ const fallbackShelf = createFallbackListShelf(objectGraph, cardMedia);
+ if (serverData.isDefinedNonNull(fallbackShelf)) {
+ shelves.push(fallbackShelf);
+ }
+ }
+ return shelves;
+ });
+}
+// region Data Augmenting
+/**
+ * Article specific entrypoint for page response augmenting. See `augment.ts`.
+ * @param response Response to augment.
+ */
+export async function fetchAdditionalDataForInitialResponse(objectGraph, response) {
+ return await mediaAugment.fetchAugmentedData(objectGraph, response, findAdditionalDataKeysForArticleResponse, fetchDataForArticleDataKey);
+}
+/**
+ * Determine the set of data, expressed as an set of `ArticleAdditionalDataKey`s, that need to be fetched for given article response to be displayed.
+ * This is equivalent to `AbstractMediaApiPageBuilder.additionalDataKeysNeededForData`, but for article builder which doesn't adopt the `builder` API.
+ *
+ * @param articleResponse Initial response to determine additional data requirements for.
+ * @returns {Set<ArticleAdditionalDataKey>} Additional data needed expressed as set of `ArticleAdditionalDataKey`
+ */
+function findAdditionalDataKeysForArticleResponse(objectGraph, articleResponse) {
+ /**
+ * Keys for requested requirements determined by:
+ * - Modules in canvas only now :)
+ */
+ const allAdditionalDataKeySet = new Set();
+ // Requirements based on canvas items:
+ const articleData = mediaDataStructure.dataFromDataContainer(objectGraph, articleResponse);
+ const canvasModules = mediaRelationships.relationshipCollection(articleData, "canvas");
+ for (const storyModule of canvasModules) {
+ // Determine additional requests and add to `allRequirementsSet`
+ const dataKeysForModule = additionalDataKeysForArticleModule(objectGraph, storyModule, articleData);
+ if (serverData.isDefinedNonNullNonEmpty(dataKeysForModule)) {
+ for (const requirement of dataKeysForModule) {
+ allAdditionalDataKeySet.add(requirement);
+ }
+ }
+ }
+ return allAdditionalDataKeySet;
+}
+/**
+ * Builds a promise that will fetch data fulfilling given requirement. Note that these promises will return `null` when they fail,
+ * and their failure should not cause the entire page to fail.
+ * This is equivalent to `AbstractMediaApiPageBuilder.fetchAdditionalDataForKey`, but for article builder which doesn't adopt the `builder` API.
+ *
+ * @param dataKey Corresponding data key to fetch data for.
+ */
+// eslint-disable-next-line @typescript-eslint/promise-function-async
+function fetchDataForArticleDataKey(objectGraph, dataKey) {
+ let request;
+ if (dataKey === "upsellForNonacquisitionCanvas") {
+ // Use `editorialItem` matching context of that would've otherwise been joined if this story was an acquisition story.
+ request = arcadeCommon.arcadeUpsellRequest(objectGraph, models.marketingItemContextFromString("editorialItemCanvas"));
+ }
+ if (dataKey === "arcadeIcons") {
+ // Require 10 for now.
+ request = arcadeCommon.arcadeAppsRequestForIcons(objectGraph, 10);
+ }
+ if (serverData.isNull(request)) {
+ return null;
+ }
+ // Failable data fetch, either resolving to valid response or `null`.
+ return mediaNetwork.fetchData(objectGraph, request).catch(() => null);
+}
+/**
+ * Determine the requirements for single article module as determined by it's type.
+ * @param storyModule The module to fetch additional requirements for.
+ * @param articleData The article that contains `storyModule` in its canvas.
+ * @returns {ArticleAdditionalDataKey[] | undefined} Set of data keys if any are needed for rendering given module.
+ */
+export function additionalDataKeysForArticleModule(objectGraph, storyModule, articleData) {
+ // Only `AppMarker` has additional requirements.
+ const moduleType = mediaAttributes.attributeAsString(storyModule, "displayType");
+ if (moduleType !== "AppMarker") {
+ return null;
+ }
+ const markerType = mediaAttributes.attributeAsString(storyModule, "appMarkerType");
+ // <rdar://problem/55919205> In story Arcade acquisition module dropping from stories
+ // Editorial wants to use the acquisition module in non-acquisition stories, but the `upsell` relationship is only joined for EIs marked with the acquisition flag.
+ // When an article is missing the upsell relationship, we'll fetch it separately if we have modules that need it...
+ const articleDataIsMissingUpsell = serverData.isNull(arcadeCommon.upsellFromRelationshipOf(objectGraph, articleData));
+ /**
+ * Acquisition AppMarker, i.e. `ArcadeShowcase` needs:
+ * 1. Upsell data for text data, e.g. editorial notes and breakoutCallToAction label, provided this data isn't already provided as part of original page.
+ * 2. Assortment of Arcade App Icons (iOS Only).
+ */
+ const additionalDataKeysForModule = [];
+ if (markerType === "Acquisition") {
+ // iOS needs icon dependency
+ if (objectGraph.host.isiOS || objectGraph.client.isVision) {
+ additionalDataKeysForModule.push("arcadeIcons");
+ }
+ // All platform needs upsell to render acquisition modules, add it if missing.
+ if (articleDataIsMissingUpsell) {
+ additionalDataKeysForModule.push("upsellForNonacquisitionCanvas");
+ }
+ }
+ return additionalDataKeysForModule;
+}
+// endregion
+/**
+ * Create a shelf model representing a single module within article pages.
+ * @param storyModule Module server data to build shelf and contents from.
+ * @param articleData The data for article that contains `storyModule` above.
+ * @param context Global parse context updated while entire sets of modules are being parsed.
+ * @returns an array of `Shelf` or `null` if building fails for given module.
+ */
+function renderModule(objectGraph, storyModule, articleData, context, shelfIndex) {
+ return validation.catchingContext(`module: ${context.module}`, () => {
+ var _a;
+ const shelves = [];
+ switch (context.module) {
+ case "Header": {
+ context.titleForNextShelf = mediaAttributes.attributeAsString(storyModule, "editorialCopy");
+ break;
+ }
+ case "TextBlock": {
+ const textBlockShelf = createParagraph(objectGraph, storyModule, context);
+ if (isSome(textBlockShelf)) {
+ shelves.push(textBlockShelf);
+ context.hasNonFocusableElements = true;
+ }
+ break;
+ }
+ case "CollectionLockup": {
+ const appListShelf = createAppList(objectGraph, storyModule, context);
+ if (isSome(appListShelf)) {
+ shelves.push(appListShelf);
+ context.hasFocusableElements = true;
+ }
+ break;
+ }
+ case "InlineImage": {
+ const inlineImageShelf = createImage(objectGraph, storyModule, context);
+ if (isSome(inlineImageShelf)) {
+ shelves.push(inlineImageShelf);
+ context.hasNonFocusableElements = true;
+ }
+ break;
+ }
+ case "AppLockup": {
+ const appLockupShelf = createAppLockup(objectGraph, storyModule, context);
+ if (isSome(appLockupShelf)) {
+ shelves.push(appLockupShelf);
+ context.hasFocusableElements = true;
+ }
+ break;
+ }
+ case "TipBlock": {
+ const tipShelf = createTip(objectGraph, storyModule, context);
+ if (isSome(tipShelf)) {
+ shelves.push(tipShelf);
+ context.hasNonFocusableElements = true;
+ }
+ break;
+ }
+ case "PullQuote": {
+ const pullQuoteShelf = createPullQuote(objectGraph, storyModule, context);
+ if (isSome(pullQuoteShelf)) {
+ shelves.push(pullQuoteShelf);
+ context.hasNonFocusableElements = true;
+ }
+ break;
+ }
+ case "HorizontalRule": {
+ const horizontalRuleShelf = createHorizontalRule(objectGraph, storyModule, context);
+ if (isSome(horizontalRuleShelf)) {
+ shelves.push(horizontalRuleShelf);
+ context.hasNonFocusableElements = true;
+ }
+ break;
+ }
+ case "InlineVideo": {
+ const inlineVideoShelf = createVideo(objectGraph, storyModule, context);
+ if (isSome(inlineVideoShelf)) {
+ shelves.push(inlineVideoShelf);
+ context.hasFocusableElements = true;
+ }
+ break;
+ }
+ case "AppMedia": {
+ const appMediaShelf = createAppMedia(objectGraph, storyModule, context);
+ if (isSome(appMediaShelf)) {
+ shelves.push(appMediaShelf);
+ context.hasFocusableElements = true;
+ }
+ break;
+ }
+ case "LinkBlock": {
+ const linkBlockShelf = createLink(objectGraph, storyModule, context);
+ if (isSome(linkBlockShelf)) {
+ shelves.push(linkBlockShelf);
+ context.hasFocusableElements = true;
+ }
+ break;
+ }
+ case "TextList": {
+ const textListShelf = createTextList(objectGraph, storyModule, context);
+ if (isSome(textListShelf)) {
+ shelves.push(textListShelf);
+ context.hasNonFocusableElements = true;
+ }
+ break;
+ }
+ case "IAPLockup": {
+ const iapLockupShelf = createIAPLockup(objectGraph, storyModule, context);
+ if (isSome(iapLockupShelf)) {
+ shelves.push(iapLockupShelf);
+ context.hasFocusableElements = true;
+ }
+ break;
+ }
+ case "AppMarker": {
+ const appMarkerShelf = createAppMarker(objectGraph, storyModule, articleData, context);
+ if (isSome(appMarkerShelf)) {
+ shelves.push(appMarkerShelf);
+ context.hasFocusableElements = true;
+ }
+ break;
+ }
+ case "StoryList": {
+ const storyListShelf = createStoryCards(objectGraph, storyModule, context, shelfIndex);
+ if (isSome(storyListShelf)) {
+ shelves.push(storyListShelf);
+ context.hasFocusableElements = true;
+ }
+ break;
+ }
+ case "AppEventLockup": {
+ const appEventShelf = createAppEventLockup(objectGraph, storyModule, context);
+ if (isSome(appEventShelf)) {
+ shelves.push(appEventShelf);
+ context.hasFocusableElements = true;
+ }
+ break;
+ }
+ case "OfferItemLockup": {
+ const offerItemShelves = createOfferItemLockup(objectGraph, storyModule, context);
+ if (isSome(offerItemShelves)) {
+ shelves.push(...offerItemShelves);
+ context.hasFocusableElements = true;
+ }
+ break;
+ }
+ default: {
+ objectGraph.console.log(`Unknown module: ${context.module}`);
+ }
+ }
+ for (const shelf of shelves) {
+ const existingShelfPresentationHints = (_a = shelf.presentationHints) !== null && _a !== void 0 ? _a : {};
+ shelf.presentationHints = {
+ ...existingShelfPresentationHints,
+ isArticleContext: true,
+ };
+ }
+ return shelves;
+ });
+}
+const FULL_WIDTH_MODULES = ["AppLockup", "InlineImage", "InlineVideo", "AppMarker"];
+/**
+ * Determines whether the provided parameters signifies a full-width article
+ * module.
+ * @param shelf The shelf in question.
+ * @param type The type of article module.
+ * @returns Whether or not the given shelf for the article type is full width.
+ */
+function isArticleShelfFullWidth(objectGraph, shelf, type) {
+ if (shelf && type) {
+ const itemCount = shelf.items.length;
+ if (itemCount > 0 && FULL_WIDTH_MODULES.indexOf(type) !== -1) {
+ const lastItem = shelf.items[itemCount - 1];
+ switch (shelf.contentType) {
+ case "framedArtwork": {
+ const framedArt = lastItem;
+ return framedArt && framedArt.isFullWidth;
+ }
+ case "framedVideo": {
+ const framedVideo = lastItem;
+ return framedVideo && framedVideo.isFullWidth;
+ }
+ default: {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+// region Footer Lockup
+/**
+ * Adds either a `footerLockup` or `arcadeFooterLockup` property on `ArticlePage` model, based on type of article.
+ * @param page Page to add footer to if needed.
+ * @param articleData Original data of article being rendered.
+ * @param context Parse context for article builder.
+ */
+function addFooterLockupForPageIfNeeded(objectGraph, page, articleData, context) {
+ // App Lockup for Articles about single specific app.
+ const footerProductData = productDataFromArticle(objectGraph, articleData);
+ if (footerProductData) {
+ const externalDeepLinkUrl = externalDeepLink.deepLinkUrlFromData(objectGraph, articleData);
+ page.footerLockup = productFooterLockupFromData(objectGraph, footerProductData, context, externalDeepLinkUrl);
+ return;
+ }
+ // Arcade Lockup for Acquisition Story for supported platforms
+ const isArcadeAcquisitionEI = mediaAttributes.attributeAsBooleanOrFalse(articleData, "isAcquisition");
+ const platformSupportsArcadeFooterLockup = objectGraph.host.isiOS || objectGraph.host.isMac;
+ const additionalDataIsAvailable = serverData.isDefinedNonNull(context.additionalData);
+ if (additionalDataIsAvailable && isArcadeAcquisitionEI && platformSupportsArcadeFooterLockup) {
+ const upsellData = arcadeCommon.upsellFromRelationshipOf(objectGraph, articleData);
+ page.arcadeFooterLockup = arcadeFooterLockupFromData(objectGraph, upsellData, context);
+ }
+}
+/**
+ * Find platform data from editorial item to enhance sharing and display in footer lockup
+ * At the moment, only single app editorials get footer lockups and have enhanced sharing.
+ *
+ * @param editorialItem Item to find footer content for
+ * @returns content to display in footer lockup, or null if no content should be displayed
+ */
+export function productDataFromArticle(objectGraph, editorialItem) {
+ const relatedContent = mediaRelationships.relationshipCollection(editorialItem, "card-contents");
+ if (relatedContent.length !== 1) {
+ return null;
+ }
+ const contentData = relatedContent[0];
+ if (!contentData) {
+ return null;
+ }
+ switch (contentData.type) {
+ case "apps":
+ case "app-bundles":
+ return contentData;
+ default:
+ return null;
+ }
+}
+/**
+ * Creates a footer lockup with a data for a specific app.
+ * Cover method over `lockupFromData` to override `offerStyle`.
+ *
+ * @param data MAPI data to build footer with.
+ * @param context Parse context
+ * @param externalDeepLinkUrl promotional deep link url to use on the lockup's offer.
+ * @returns A new `Lockup` object for footer lockups.
+ */
+function productFooterLockupFromData(objectGraph, data, context, externalDeepLinkUrl) {
+ const lockupOptions = {
+ offerStyle: footerLockupOfferStyle(objectGraph),
+ metricsOptions: {
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ },
+ clientIdentifierOverride: context.clientIdentifierOverride,
+ externalDeepLinkUrl: externalDeepLinkUrl,
+ crossLinkSubtitle: context.crossLinkSubtitle,
+ artworkUseCase: 0 /* content.ArtworkUseCase.Default */,
+ canDisplayArcadeOfferButton: content.shelfContentTypeCanDisplayArcadeOfferButtons(objectGraph, "smallLockup"),
+ };
+ return lockups.lockupFromData(objectGraph, data, lockupOptions);
+}
+/**
+ * Creates a footer lockup representing the Arcade subscription service.
+ * @param upsellData Contains both editorial and iAP data for Arcade
+ * @param context Parse context.
+ */
+function arcadeFooterLockupFromData(objectGraph, upsellData, context) {
+ const metricsOptions = {
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ };
+ return lockups.arcadeLockupFromData(objectGraph, upsellData, metricsOptions, models.marketingItemContextFromString("editorialItem"), "infer", null);
+}
+/**
+ * Determines the offer style to use for the footer lockup.
+ */
+function footerLockupOfferStyle(objectGraph) {
+ switch (objectGraph.client.deviceType) {
+ case "mac":
+ return "white";
+ default:
+ return "infer";
+ }
+}
+// endregion
+function createFallbackListShelf(objectGraph, cardMedia) {
+ if (cardMedia instanceof models.TodayCardMediaList || cardMedia instanceof models.TodayCardMediaRiver) {
+ const fallbackShelf = new models.Shelf("smallLockup");
+ fallbackShelf.items = cardMedia.lockups;
+ if (objectGraph.client.isWeb) {
+ fallbackShelf.presentationHints = {
+ ...fallbackShelf.presentationHints,
+ isArticleContext: true,
+ };
+ }
+ return fallbackShelf;
+ }
+ return null;
+}
+function shareSheetActionFromData(objectGraph, editorialItem, todayCardConfig) {
+ const productData = productDataFromArticle(objectGraph, editorialItem);
+ /*
+ * Determine title
+ */
+ let title = null;
+ const name = content.notesFromData(objectGraph, editorialItem, "name");
+ const short = content.notesFromData(objectGraph, editorialItem, "short");
+ // Prefer "name: short"
+ if (name && short) {
+ title = objectGraph.loc
+ .string("ShareSheet.TitleSubtitle.Format", "{title}: {subtitle}")
+ .replace("{title}", name)
+ .replace("{subtitle}", short);
+ }
+ // Followed by name
+ if (!title && name) {
+ title = name;
+ }
+ // Followed by short
+ if (!title && short) {
+ title = short;
+ }
+ // Followed by product name
+ if (!title && productData) {
+ const productTitle = mediaAttributes.attributeAsString(productData, "name");
+ const cardDisplayStyle = mediaAttributes.attributeAsString(editorialItem, "cardDisplayStyle");
+ switch (cardDisplayStyle) {
+ case TodayCardDisplayStyle.GameOfTheDay: {
+ title = objectGraph.loc.string("SHARE_SHEET_GAME_OF_DAY_TITLE_FORMAT").replace("{title}", productTitle);
+ break;
+ }
+ case TodayCardDisplayStyle.AppOfTheDay: {
+ title = objectGraph.loc.string("SHARE_SHEET_APP_OF_DAY_TITLE_FORMAT").replace("{title}", productTitle);
+ break;
+ }
+ default: {
+ objectGraph.console.log(`No title for article with unknown style: ${cardDisplayStyle}`);
+ break;
+ }
+ }
+ }
+ const url = mediaAttributes.attributeAsString(editorialItem, "url");
+ let articleArtwork;
+ const cardDisplayStyle = mediaAttributes.attributeAsString(editorialItem, "cardDisplayStyle");
+ switch (cardDisplayStyle) {
+ case TodayCardDisplayStyle.Grid:
+ case TodayCardDisplayStyle.List:
+ case TodayCardDisplayStyle.River:
+ articleArtwork = artworkBuilder.createArtworkForResource(objectGraph, "resource://ShareCollectionThumbnail", 40, 40);
+ break;
+ default:
+ articleArtwork = null;
+ break;
+ }
+ // Create share sheet model (bail out if unable to do so)
+ const shareData = sharing.shareSheetDataForArticle(objectGraph, title, url, null, articleArtwork, editorialItem);
+ if (!serverData.isDefinedNonNull(shareData)) {
+ return null;
+ }
+ const activities = sharing.shareSheetActivitiesForArticle(objectGraph, url, todayCardPreviewUrlForTodayCard(objectGraph, editorialItem.id, todayCardConfig), editorialItem.id);
+ return new models.ShareSheetAction(shareData, activities);
+}
+function createShareShelf(objectGraph, shareAction, context, isLastModuleFullWidth) {
+ if (!serverData.isDefinedNonNull(shareAction) ||
+ objectGraph.client.isVision ||
+ preprocessor.GAMES_TARGET ||
+ objectGraph.client.isCompanionVisionApp) {
+ return null;
+ }
+ // Create share button
+ const shareButton = new models.RoundedButton("share", objectGraph.loc.string("SHARE_STORY"), !isLastModuleFullWidth, shareAction);
+ // Add share shelf
+ const shareButtonShelf = new models.Shelf("roundedButton");
+ shareButtonShelf.items = [shareButton];
+ return shareButtonShelf;
+}
+function createParagraph(objectGraph, module, context) {
+ const text = mediaAttributes.attributeAsString(module, "editorialCopy");
+ if (!text) {
+ return null;
+ }
+ const paragraph = new models.Paragraph(text, "text/x-apple-as3-nqml", "article");
+ // Setup impressions
+ addImpressionsFieldsToModel(objectGraph, paragraph, context);
+ const shelf = new models.Shelf("paragraph");
+ shelf.items = [paragraph];
+ return shelf;
+}
+function createImage(objectGraph, module, context) {
+ const displayStyle = mediaAttributes.attributeAsString(module, "inlineImageDisplayType");
+ const artworkData = mediaAttributes.attributeAsDictionary(module, "artwork");
+ // If the displayStyle is FullWidth want to 'allowTransparency' so that images blend into the page in both
+ // light and dark mode. Previously editorial would bake white backgrounds into images they wanted to 'blend'
+ // with the page
+ const artwork = content.artworkFromApiArtwork(objectGraph, artworkData, {
+ useCase: 13 /* content.ArtworkUseCase.ArticleImage */,
+ allowingTransparency: displayStyle === "FullWidth" && !objectGraph.client.isVision,
+ withJoeColorPlaceholder: objectGraph.client.isVision,
+ });
+ if (!artwork) {
+ return null;
+ }
+ const frame = new models.FramedArtwork(artwork, false, "text/x-apple-as3-nqml");
+ // Get the optional caption
+ frame.caption = mediaAttributes.attributeAsString(module, "editorialCopy");
+ context.subStyle = displayStyle;
+ if (displayStyle) {
+ switch (displayStyle) {
+ case "BoundingBox": {
+ frame.isFullWidth = false;
+ frame.hasRoundedCorners = true;
+ break;
+ }
+ case "FullWidth":
+ default: {
+ frame.isFullWidth = true;
+ frame.hasRoundedCorners = false;
+ break;
+ }
+ }
+ }
+ // Setup impressions
+ addImpressionsFieldsToModel(objectGraph, frame, context);
+ const shelf = new models.Shelf("framedArtwork");
+ shelf.items = [frame];
+ return shelf;
+}
+function createTip(objectGraph, module, context) {
+ const artworkData = mediaAttributes.attributeAsDictionary(module, "artwork");
+ const artwork = content.artworkFromApiArtwork(objectGraph, artworkData, {
+ useCase: 13 /* content.ArtworkUseCase.ArticleImage */,
+ });
+ if (!artwork) {
+ return null;
+ }
+ const caption = mediaAttributes.attributeAsString(module, "editorialCopy");
+ const ordinal = mediaAttributes.attributeAsString(module, "tipNumber");
+ // Create the tip image
+ const frame = new models.FramedArtwork(artwork, false, "text/x-apple-as3-nqml");
+ frame.isFullWidth = false;
+ frame.hasRoundedCorners = true;
+ frame.caption = caption;
+ frame.ordinal = ordinal;
+ // Setup impressions
+ addImpressionsFieldsToModel(objectGraph, frame, context);
+ // Create the shelf
+ const shelf = new models.Shelf("framedArtwork");
+ shelf.items = [frame];
+ return shelf;
+}
+function createPullQuote(objectGraph, module, context) {
+ const text = mediaAttributes.attributeAsString(module, "quote");
+ const attribution = mediaAttributes.attributeAsString(module, "quoteAttribution");
+ // Get the optional artwork
+ const artworkData = mediaAttributes.attributeAsDictionary(module, "artwork");
+ const artwork = content.artworkFromApiArtwork(objectGraph, artworkData, {
+ useCase: 13 /* content.ArtworkUseCase.ArticleImage */,
+ });
+ const fullWidth = mediaAttributes.attributeAsString(module, "pullQuoteDisplayType") === "FullWidth";
+ // Create the quote
+ const quote = new models.Quote(text, attribution, artwork, fullWidth);
+ // Setup impressions
+ addImpressionsFieldsToModel(objectGraph, quote, context);
+ // Create the shelf
+ const shelf = new models.Shelf("quote");
+ shelf.items = [quote];
+ return shelf;
+}
+function createHorizontalRule(objectGraph, module, context) {
+ const lineStyle = mediaAttributes.attributeAsString(module, "lineStyle");
+ const fullWidth = mediaAttributes.attributeAsString(module, "displayStyle") === "FullWidth";
+ let ruleColor = color.named("defaultLine");
+ if (objectGraph.client.isVision && (lineStyle === "Dotted" || lineStyle === "Dashed")) {
+ ruleColor = color.white;
+ }
+ // Parse the customColor from Media API. This can only be a dynamic color.
+ const apiColor = mediaAttributes.attributeAsDictionary(module, "customColor");
+ const lightColor = color.fromHex(serverData.asString(apiColor, "lightMode"));
+ const darkColor = color.fromHex(serverData.asString(apiColor, "darkMode"));
+ if (!serverData.isNullOrEmpty(lightColor) && !serverData.isNullOrEmpty(darkColor)) {
+ ruleColor = color.dynamicWith(lightColor, darkColor);
+ }
+ const horizontalRule = new models.HorizontalRule(lineStyle, ruleColor, fullWidth);
+ // Create the Shelf
+ const shelf = new models.Shelf("horizontalRule");
+ shelf.items = [horizontalRule];
+ return shelf;
+}
+function createVideo(objectGraph, module, context) {
+ // Get the preview artwork
+ const artworkData = mediaAttributes.attributeAsDictionary(module, "video.previewFrame");
+ const artwork = content.artworkFromApiArtwork(objectGraph, artworkData, {
+ useCase: 13 /* content.ArtworkUseCase.ArticleImage */,
+ });
+ if (!artwork) {
+ return null;
+ }
+ // Get the video URL
+ const videoUrl = mediaAttributes.attributeAsString(module, "video.video");
+ if (!videoUrl) {
+ return null;
+ }
+ const videoDisplayType = mediaAttributes.attributeAsString(module, "inlineVideoDisplayType");
+ const isFullWidth = videoDisplayType === "FullWidth";
+ // Create the video
+ const video = new models.Video(videoUrl, artwork, videoDefaults.defaultVideoConfiguration(objectGraph));
+ metricsHelpersMedia.addMetricsEventsToVideo(objectGraph, video, {
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ id: context.pageId,
+ });
+ const videoModule = new models.FramedVideo(video, isFullWidth, "text/x-apple-as3-nqml");
+ // Get the optional caption
+ videoModule.caption = mediaAttributes.attributeAsString(module, "editorialCopy");
+ // Setup impressions
+ addImpressionsFieldsToModel(objectGraph, videoModule, context);
+ // Create the shelf
+ const shelf = new models.Shelf("framedVideo");
+ shelf.items = [videoModule];
+ return shelf;
+}
+function createAppLockup(objectGraph, module, context) {
+ const contentData = contentFromModule(objectGraph, module, context);
+ if (!contentData) {
+ return null;
+ }
+ // Shelf to generate. Either lockup, app showcase, or app event shelf
+ let shelf = null;
+ // If we have an app-events relationship, we want to use this as the priority. This sometimes exists on the
+ // AppLockup type, rather than as the AppEventLockup type, so that older clients can still render this
+ // item by falling back to the AppLockup type.
+ const appEventsDataItems = mediaRelationships.relationshipCollection(module, "app-events");
+ if (serverData.isDefinedNonNullNonEmpty(appEventsDataItems)) {
+ shelf = appPromotionsShelf.appEventsShelfForArticle(objectGraph, appEventsDataItems, context.metricsPageInformation, context.metricsLocationTracker, context);
+ if (serverData.isDefinedNonNull(shelf)) {
+ return shelf;
+ }
+ }
+ // Set the display style
+ const displayStyle = mediaAttributes.attributeAsString(module, "appLockupSize");
+ context.subStyle = displayStyle;
+ let shelfStyle;
+ let isLockup = false;
+ if (displayStyle) {
+ switch (displayStyle) {
+ case "Small": {
+ shelfStyle = "smallLockup";
+ isLockup = true;
+ break;
+ }
+ case "Medium": {
+ shelfStyle = "mediumLockup";
+ isLockup = true;
+ break;
+ }
+ case "Large":
+ default: {
+ if (objectGraph.client.isWatch ||
+ objectGraph.client.isTV ||
+ objectGraph.client.isVision ||
+ preprocessor.GAMES_TARGET) {
+ // Per design, on watchOS we always show a lockup for app showcases.
+ // Watch App Store treats all lockup sizes the same -- let's pick small.
+ shelfStyle = "smallLockup";
+ isLockup = true;
+ }
+ else {
+ shelfStyle = "appShowcase";
+ }
+ break;
+ }
+ }
+ }
+ // Determine the deep link URL, if there is one.
+ const externalDeepLinkUrl = externalDeepLink.deepLinkUrlFromData(objectGraph, module);
+ // Create the appropriate shelf item
+ if (isLockup) {
+ const lockupShelf = new models.Shelf(shelfStyle);
+ const metricsOptions = {
+ metricsOptions: {
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ },
+ clientIdentifierOverride: context.clientIdentifierOverride,
+ externalDeepLinkUrl: externalDeepLinkUrl,
+ artworkUseCase: content.artworkUseCaseFromShelfStyle(objectGraph, shelfStyle),
+ };
+ let lockup;
+ if (preprocessor.GAMES_TARGET) {
+ const shelfID = new PageID(context.pageId).shelfID(module.id);
+ lockup = gamesComponentBuilder.makeArticleGameLockup(objectGraph, contentData, shelfID);
+ }
+ else {
+ lockup = lockups.lockupFromData(objectGraph, contentData, metricsOptions);
+ }
+ if (isNothing(lockup)) {
+ return null;
+ }
+ lockupShelf.items = [lockup];
+ shelf = lockupShelf;
+ }
+ else {
+ // On all platforms, the AppLockup platform generates a AppShowcase when display style is large.
+ shelf = createAppShowcase(objectGraph, module, context);
+ }
+ return shelf;
+}
+function createAppShowcase(objectGraph, module, context) {
+ // Create the shelf
+ const shelf = new models.Shelf("appShowcase");
+ // Parameterize by platform:
+ // tvOS populates the `screenshots` field to display alongside video.
+ const showcaseHasScreenshots = objectGraph.client.isTV;
+ // Only non-tvOS has shelf background color
+ const shelfHasBackgroundColor = objectGraph.client.deviceType !== "tv";
+ const contentData = contentFromModule(objectGraph, module, context);
+ const externalDeepLinkUrl = externalDeepLink.deepLinkUrlFromData(objectGraph, module);
+ const lockup = lockups.lockupFromData(objectGraph, contentData, {
+ offerStyle: "colored",
+ metricsOptions: {
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ },
+ clientIdentifierOverride: context.clientIdentifierOverride,
+ externalDeepLinkUrl: externalDeepLinkUrl,
+ crossLinkSubtitle: context.crossLinkSubtitle,
+ artworkUseCase: 1 /* content.ArtworkUseCase.LockupIconSmall */,
+ });
+ const showcase = new models.AppShowcase("large", lockup);
+ showcase.description = lockups.subtitleFromData(objectGraph, contentData);
+ // Add Video
+ // Configure the video for the showcase, if the module demands it.
+ let showcaseVideo = null;
+ const videoType = mediaAttributes.attributeAsString(module, "appLockupVideo");
+ switch (videoType) {
+ case "AppTrailer": {
+ const allAppVideos = content.videoPreviewsFromData(objectGraph, contentData);
+ if (allAppVideos && allAppVideos.length > 0) {
+ showcaseVideo = allAppVideos[0];
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (showcaseVideo) {
+ metricsHelpersMedia.addMetricsEventsToVideo(objectGraph, showcaseVideo, {
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ id: context.pageId,
+ });
+ showcase.video = showcaseVideo;
+ }
+ // Add Screenshots for AppShowcase if necessary
+ if (showcaseHasScreenshots) {
+ showcase.screenshots = content.screenshotsFromData(objectGraph, contentData, 14 /* content.ArtworkUseCase.ArticleScreenshots */, [content.currentAppPlatform(objectGraph)]);
+ }
+ // Configure background if necessary.
+ if (shelfHasBackgroundColor) {
+ shelf.background = {
+ type: "color",
+ color: appShowcaseBackgroundColor,
+ };
+ }
+ shelf.items = [showcase];
+ return shelf;
+}
+function createIAPLockup(objectGraph, module, context) {
+ const contentData = contentFromModule(objectGraph, module, context);
+ if (!contentData) {
+ return null;
+ }
+ // Create the lockup
+ const lockup = lockups.inAppPurchaseLockupFromData(objectGraph, contentData, {
+ metricsOptions: {
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ },
+ clientIdentifierOverride: context.clientIdentifierOverride,
+ artworkUseCase: 1 /* content.ArtworkUseCase.LockupIconSmall */,
+ });
+ if (!lockup) {
+ return null;
+ }
+ const showcase = new models.InAppPurchaseShowcase(lockup);
+ // Create the shelf
+ const shelf = new models.Shelf("inAppPurchaseShowcase");
+ shelf.background = {
+ type: "color",
+ color: iAPBackgroundColor,
+ };
+ shelf.items = [showcase];
+ return shelf;
+}
+function createAppList(objectGraph, module, context) {
+ const showOrdinals = mediaAttributes.attributeAsBooleanOrFalse(module, "showOrdinals");
+ const ordinalDirection = mediaAttributes.attributeAsString(module, "collectionLockupDisplayType") === "OrdinalDesc"
+ ? "descending"
+ : "ascending";
+ // Set the display style
+ const displayStyle = mediaAttributes.attributeAsString(module, "collectionLockupSize");
+ context.subStyle = displayStyle;
+ let style;
+ if (displayStyle) {
+ switch (displayStyle) {
+ case "Large": {
+ style = "largeLockup";
+ break;
+ }
+ case "Medium": {
+ style = "mediumLockup";
+ break;
+ }
+ case "Small":
+ default: {
+ style = "smallLockup";
+ break;
+ }
+ }
+ }
+ // Construct the lockup options
+ const lockupOptions = {
+ metricsOptions: {
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ },
+ clientIdentifierOverride: context.clientIdentifierOverride,
+ artworkUseCase: content.artworkUseCaseFromShelfStyle(objectGraph, style),
+ canDisplayArcadeOfferButton: content.shelfContentTypeCanDisplayArcadeOfferButtons(objectGraph, style),
+ };
+ // Check if we have content
+ const contents = mediaRelationships.relationshipCollection(module, "contents");
+ if (isNothing(contents)) {
+ return null;
+ }
+ let childLockups = [];
+ if (preprocessor.GAMES_TARGET) {
+ const shelfID = new PageID(context.pageId).shelfID(module.id);
+ childLockups = gamesComponentBuilder.makeArticleGameLockups(objectGraph, contents, shelfID);
+ }
+ else {
+ childLockups = lockups.lockupsFromData(objectGraph, contents, {
+ includeOrdinals: showOrdinals,
+ ordinalDirection: ordinalDirection,
+ lockupOptions: lockupOptions,
+ });
+ }
+ if (!childLockups || childLockups.length === 0) {
+ return null;
+ }
+ // Create the shelf
+ const shelf = new models.Shelf(style);
+ shelf.items = childLockups;
+ return shelf;
+}
+function createAppMedia(objectGraph, module, context) {
+ const contentData = contentFromModule(objectGraph, module, context);
+ if (!contentData) {
+ return null;
+ }
+ // Set the display style
+ const mediaOption = mediaAttributes.attributeAsString(module, "appMediaOption");
+ const appMediaPlatform = mediaAttributes.attributeAsString(module, "appMediaPlatform");
+ context.subStyle = mediaOption;
+ switch (mediaOption) {
+ case "Screenshots": {
+ let shelf = null;
+ // I'm so sorry, but making this split makes the macOS client code infinitely better because we are able
+ // to reuse the same product media view and component contract that is used on product page screenshots/trailers.
+ // Really, iOS should be reworked such that its module & product page implementation has a single source,
+ // but this has serious design obstacles that need to be worked through.
+ if (objectGraph.client.isMac) {
+ shelf = new models.Shelf("productMedia");
+ const productMedia = content.productMediaFromData(objectGraph, contentData, 14 /* content.ArtworkUseCase.ArticleScreenshots */);
+ if (serverData.isDefinedNonNull(productMedia) && productMedia.length) {
+ shelf.items = productMedia;
+ }
+ }
+ else {
+ shelf = new models.Shelf("screenshots");
+ if (serverData.isNull(appMediaPlatform)) {
+ /**
+ * The server did not tell us which app platform to use, so we need to infer based on various keys in
+ * the response. These parameters are only fully baked into product-dv responses, so we we need to do
+ * the more expensive product-dv lookup in order to correctly infer the default screenshots to use for
+ * the shelf.
+ */
+ const screenshots = content.screenshotsFromData(objectGraph, contentData, 14 /* content.ArtworkUseCase.ArticleScreenshots */);
+ if (screenshots && screenshots.length > 0) {
+ shelf.items = [screenshots[0]];
+ }
+ }
+ else {
+ /**
+ * Server tells us which platform to use -- dictated by `appMediaPlatform`. Selectively do a lookup for
+ * just those screenshots.
+ */
+ const desiredAppPlatform = appPlatformFromAppMediaPlatform(objectGraph, appMediaPlatform);
+ if (desiredAppPlatform) {
+ const screenshots = content.screenshotsFromData(objectGraph, contentData, 14 /* content.ArtworkUseCase.ArticleScreenshots */, [desiredAppPlatform]);
+ if (screenshots && screenshots.length) {
+ shelf.items = [screenshots[0]];
+ }
+ }
+ }
+ }
+ if (serverData.isDefinedNonNull(shelf) && shelf.items.length === 0) {
+ return null;
+ }
+ return shelf;
+ }
+ case "AppTrailers":
+ const trailersShelf = new models.Shelf("framedVideo");
+ const videoPreviews = content.videoPreviewsFromData(objectGraph, contentData);
+ if (videoPreviews && videoPreviews.length > 0) {
+ const video = videoPreviews[0];
+ metricsHelpersMedia.addMetricsEventsToVideo(objectGraph, video, {
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ id: context.pageId,
+ });
+ const firstTrailer = new models.FramedVideo(video, false, "text/plain", null, null, true);
+ trailersShelf.items = [firstTrailer];
+ return trailersShelf;
+ }
+ else {
+ return null;
+ }
+ default: {
+ return null;
+ }
+ }
+}
+function createLink(objectGraph, module, context) {
+ if (objectGraph.client.isTV || objectGraph.client.isWatch) {
+ return null;
+ }
+ const urlString = mediaAttributes.attributeAsString(module, "url");
+ if (!urlString) {
+ return null;
+ }
+ const url = new urls.URL(urlString);
+ const linkTitle = mediaAttributes.attributeAsString(module, "urlTitle");
+ let text = mediaAttributes.attributeAsString(module, "editorialCopy");
+ if (!text) {
+ text = url.host;
+ }
+ const mediaHosts = [
+ "itunes.apple.com",
+ "apps.apple.com",
+ "music.apple.com",
+ "books.apple.com",
+ "podcasts.apple.com",
+ "watch-app.cdn-apple.com",
+ "tv.apple.com",
+ ];
+ let linkPresentationEnabled = false;
+ for (const mediaHost of mediaHosts) {
+ if (url.host.endsWith(mediaHost)) {
+ linkPresentationEnabled = true;
+ }
+ }
+ const action = new models.ExternalUrlAction(urlString);
+ metricsHelpersClicks.addClickEventToAction(objectGraph, action, {
+ targetType: "link",
+ pageInformation: context.metricsPageInformation,
+ id: `${context.index}`,
+ locationTracker: context.metricsLocationTracker,
+ });
+ const link = new models.EditorialLink(linkTitle, text, action, linkPresentationEnabled);
+ // Setup impressions
+ addImpressionsFieldsToModel(objectGraph, link, context);
+ const shelf = new models.Shelf("editorialLink");
+ shelf.items = [link];
+ return shelf;
+}
+function createTextList(objectGraph, module, context) {
+ const listEntries = mediaAttributes.attributeAsArrayOrEmpty(module, "editorialCopy");
+ if (!listEntries.length) {
+ return null;
+ }
+ const type = mediaAttributes.attributeAsString(module, "textListDisplayType");
+ context.subStyle = type;
+ let isBulleted = false;
+ switch (type) {
+ case "Bulleted": {
+ isBulleted = true;
+ break;
+ }
+ default: {
+ isBulleted = false;
+ break;
+ }
+ }
+ let text;
+ if (isBulleted) {
+ text = "<ul>";
+ }
+ else {
+ text = "<ol>";
+ }
+ for (const textEntry of listEntries) {
+ const listItemJSONString = JSON.stringify(textEntry);
+ // rdar://104446319 - We must use `parse` on our JSON string to convert back to
+ // a raw string object as this ensures leading/trailing quotation marks are *not* escaped
+ const listItem = JSON.parse(listItemJSONString);
+ text = `${text}<li>${listItem}</li>`;
+ }
+ if (isBulleted) {
+ text = `${text}</ul>`;
+ }
+ else {
+ text = `${text}</ol>`;
+ }
+ const paragraph = new models.Paragraph(text, "text/x-apple-as3-nqml", "article");
+ // Setup impressions
+ addImpressionsFieldsToModel(objectGraph, paragraph, context);
+ const shelf = new models.Shelf("paragraph");
+ shelf.items = [paragraph];
+ return shelf;
+}
+function createStoryCards(objectGraph, module, context, shelfIndex) {
+ if (objectGraph.client.isVision) {
+ const shelfToken = createBaseShelfToken(objectGraph, undefined, module, false, shelfIndex, context.metricsPageInformation, context.metricsLocationTracker);
+ const shelf = buildSmallStoryCardShelf(objectGraph, shelfToken);
+ shelf.isHorizontal = true;
+ return shelf;
+ }
+ const cards = mediaRelationships.relationshipCollection(module, "contents");
+ if (!cards) {
+ return null;
+ }
+ const title = mediaAttributes.attributeAsString(module, "name");
+ const subtitle = mediaAttributes.attributeAsString(module, "tagline");
+ let shelf = null;
+ if (objectGraph.client.isiOS && objectGraph.featureFlags.isEnabled("mini_today_cards_article")) {
+ const todayParseContext = new TodayParseContext(context.metricsPageInformation, context.metricsLocationTracker);
+ shelf = todayHorizontalCardUtil.shelfForMiniTodayCards(objectGraph, cards, title, subtitle, todayParseContext);
+ }
+ else {
+ const isSmallStoryCardsSupported = objectGraph.host.isiOS || objectGraph.host.isMac || objectGraph.host.isWeb;
+ const resolvedContentType = isSmallStoryCardsSupported ? "smallStoryCard" : "todayBrick";
+ shelf = todayHorizontalCardUtil.shelfForHorizontalCardItems(objectGraph, cards, resolvedContentType, title, subtitle, context, null);
+ if (isSmallStoryCardsSupported) {
+ // Only specific small story cards are supported and will crash otherwise, filter those here preemptively.
+ // rdar://91965501 (MAS Crashing - Earth Day Landing Page - 4/19)
+ if (Array.isArray(shelf.items)) {
+ shelf.items = shelf.items.filter((item) => {
+ if (!(item instanceof models.TodayCard)) {
+ return true;
+ }
+ return todayHorizontalCardUtil.isHorizontalCardSupportedForKind(objectGraph, item.media.kind, resolvedContentType);
+ });
+ }
+ }
+ }
+ return shelf;
+}
+function createAppEventLockup(objectGraph, module, context) {
+ const contentData = contentFromModule(objectGraph, module, context);
+ if (!contentData) {
+ return null;
+ }
+ return appPromotionsShelf.appEventsShelfForArticle(objectGraph, [contentData], context.metricsPageInformation, context.metricsLocationTracker, context);
+}
+function createOfferItemLockup(objectGraph, module, context) {
+ if (!objectGraph.client.isiOS) {
+ return [];
+ }
+ const offerItem = mediaRelationships.relationshipData(objectGraph, module, "contents");
+ if (serverData.isNullOrEmpty(offerItem)) {
+ return null;
+ }
+ // Offer detail Paragraph
+ const offerParagraph = mediaAttributes.attributeAsString(module, "editorialCopy");
+ const paragraph = new models.Paragraph(offerParagraph, "text/x-apple-as3-nqml", "article");
+ const paragraphShelf = new models.Shelf("paragraph");
+ paragraphShelf.items = [paragraph];
+ // Winback Offer Card
+ const offerItemShelf = appPromotionsShelf.appEventsShelfForArticle(objectGraph, [offerItem], context.metricsPageInformation, context.metricsLocationTracker, context);
+ return [paragraphShelf, offerItemShelf];
+}
+/**
+ * Ingests EI canvas modules of form:
+ * {
+ * id: <editorial-id>,
+ * type: "editorial-item-shelves",
+ * attributes: {
+ * displayType: "AppMarker",
+ * appMarkerType: <AppMarkerType>
+ * }
+ * }
+ * to generate an shelf for AppMarker model.
+ */
+function createAppMarker(objectGraph, appMarkerModule, articleData, context) {
+ const markerType = mediaAttributes.attributeAsString(appMarkerModule, "appMarkerType");
+ context.subStyle = markerType;
+ let shelf = null;
+ switch (markerType) {
+ case "OSUpgrade":
+ shelf = createOSUpgradeClientControlButton(objectGraph, appMarkerModule, context);
+ break;
+ case "Acquisition":
+ shelf = createArcadeShowcase(objectGraph, appMarkerModule, articleData, context);
+ break;
+ default:
+ break;
+ }
+ return shelf;
+}
+/**
+ * Ingests EI canvas modules of form:
+ * {
+ * id: <editorial-id>,
+ * type: "editorial-item-shelves",
+ * attributes: {
+ * displayType: "AppMarker",
+ * appMarkerType: "OSUpgrade"
+ * }
+ * }
+ * to generate an shelf with an button that links to preferences updates.
+ */
+function createOSUpgradeClientControlButton(objectGraph, osUpgradeModule, context) {
+ const deviceType = objectGraph.client.deviceType;
+ if (deviceType !== "mac") {
+ return null; // Early exit - Only MAS utilizes OS Upgrade Client Control Button currently.
+ }
+ const installUpdateUrl = links.osUpdateUrl(deviceType);
+ if (installUpdateUrl === null) {
+ return null;
+ }
+ // Action to Preferences
+ const openUpdatesAction = new models.ExternalUrlAction(installUpdateUrl);
+ // Action to open preferences is configured as `link`
+ metricsHelpersClicks.addClickEventToAction(objectGraph, openUpdatesAction, {
+ targetType: "link",
+ pageInformation: context.metricsPageInformation,
+ id: `${context.index}`,
+ locationTracker: context.metricsLocationTracker,
+ });
+ // Shelf model
+ const upgradeControlText = objectGraph.loc.string("CLIENT_CONTROL_OS_UPGRADE_TITLE", "CHECK FOR UPDATE");
+ const upgradeControl = new models.ClientControlButton(upgradeControlText, openUpdatesAction);
+ // Add impressions
+ addImpressionsFieldsToModel(objectGraph, upgradeControl, context);
+ const shelf = new models.Shelf("clientControlButton");
+ shelf.items = [upgradeControl];
+ return shelf;
+}
+/**
+ * Ingests EI canvas modules of form:
+ * {
+ * id: <editorial-id>,
+ * type: "editorial-item-shelves",
+ * }
+ * with additional data:
+ * - Upsell data on `context.additionalData`
+ * - Icon Artwork data (iOS only) on `context.additionalData`
+ *
+ * to generate an shelf that promotes Arcade service.
+ *
+ * @param arcadeShowcaseModule Arcade showcase module
+ * @param articleData The data backing the article containing the module. Used for top-level relationship.
+ * @param context Parse context for this page parsing. This context contains the additional requirements data.
+ */
+function createArcadeShowcase(objectGraph, arcadeShowcaseModule, articleData, context) {
+ const supportedOnPlatform = objectGraph.host.isiOS || objectGraph.host.isMac || objectGraph.client.isVision;
+ if (!supportedOnPlatform) {
+ return null;
+ }
+ // Default to upsell on relation, falling back to upsell that may have been fetched separately for orphaned acquisition modules.
+ let upsellData = arcadeCommon.upsellFromRelationshipOf(objectGraph, articleData);
+ if (!upsellData && context.additionalData) {
+ const upsellResponse = context.additionalData.get("upsellForNonacquisitionCanvas");
+ upsellData = arcadeCommon.upsellFromContentsOfUpsellResponse(objectGraph, upsellResponse);
+ }
+ if (!serverData.isDefinedNonNull(upsellData)) {
+ return null;
+ }
+ const baseMetricsOptions = {
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ };
+ // Flow to See All games if subscribed
+ const subscribedAction = arcadeCommon.openArcadeMainAction(objectGraph, context.metricsPageInformation, context.metricsLocationTracker, objectGraph.client.isVision);
+ if (preprocessor.GAMES_TARGET) {
+ subscribedAction.title = objectGraph.loc.string("OfferButton.Arcade.Title.Explore");
+ }
+ else {
+ subscribedAction.title = objectGraph.loc.string("ARCADE_ACTION_TITLE_EXPLORE", "EXPLORE");
+ }
+ // Flow to Arcade Subscribe page if unsubscribed.
+ let unsubscribedAction;
+ const unsubscribedActionTitle = breakoutsCommon.callToActionLabelFromData(objectGraph, upsellData.marketingItemData);
+ if (serverData.isDefinedNonNullNonEmpty(unsubscribedActionTitle)) {
+ // We support an inline offer here instead, when the pricing token is there.
+ unsubscribedAction = arcadeUpsell.arcadeOfferButtonActionFromData(objectGraph, upsellData.marketingItemData, models.marketingItemContextFromString("editorialItemCanvas"), baseMetricsOptions);
+ if (serverData.isDefinedNonNull(unsubscribedAction)) {
+ unsubscribedAction.title = unsubscribedActionTitle;
+ }
+ }
+ else {
+ // If Upsell EI is misconfigured and missing `breakoutCallToActionLabel`, default to opening Arcade app for unsubscribed state.
+ unsubscribedAction = arcadeCommon.openArcadeMainAction(objectGraph, context.metricsPageInformation, context.metricsLocationTracker, objectGraph.client.isVision);
+ if (preprocessor.GAMES_TARGET) {
+ unsubscribedAction.title = objectGraph.loc.string("OfferButton.Arcade.Title.Explore");
+ }
+ else {
+ unsubscribedAction.title = objectGraph.loc.string("ARCADE_ACTION_TITLE_EXPLORE", "EXPLORE");
+ }
+ }
+ const arcadeShowcase = new models.ArcadeShowcase(unsubscribedAction, subscribedAction);
+ const unsubscribedDescription = arcadeUpsell.descriptionFromData(objectGraph, upsellData.marketingItemData);
+ arcadeShowcase.unsubscribedDescription = unsubscribedDescription;
+ const offerDisplayProperties = new models.OfferDisplayProperties("arcade", objectGraph.bag.arcadeAppAdamId, null, "colored", null, "dark", null, null, null, null, null, null, null, null, null, null, null, null, objectGraph.bag.arcadeProductFamilyId);
+ if (preprocessor.GAMES_TARGET) {
+ offerDisplayProperties.titles["subscribed"] = objectGraph.loc.string("OfferButton.Arcade.Title.Explore");
+ }
+ else {
+ offerDisplayProperties.titles["subscribed"] = objectGraph.loc.string("ARCADE_ACTION_TITLE_EXPLORE", "EXPLORE");
+ }
+ arcadeShowcase.offerDisplayProperties = offerDisplayProperties;
+ const showcaseMetricsOptions = {
+ ...baseMetricsOptions,
+ targetType: "arcadeShowcase",
+ title: unsubscribedActionTitle,
+ id: arcadeShowcaseModule.id,
+ kind: "arcadeShowcase",
+ softwareType: null,
+ displaysArcadeUpsell: true,
+ };
+ metricsHelpersImpressions.addImpressionFields(objectGraph, arcadeShowcase, showcaseMetricsOptions);
+ // Build Artwork for iOS only
+ if (objectGraph.host.isiOS || objectGraph.client.isVision) {
+ // Context should have additional data to source icons.
+ if (serverData.isNull(context.additionalData)) {
+ return null;
+ }
+ const iconResponse = context.additionalData.get("arcadeIcons");
+ if (serverData.isDefinedNonNullNonEmpty(iconResponse)) {
+ const iconMetricsOptions = {
+ pageInformation: context.metricsPageInformation,
+ locationTracker: context.metricsLocationTracker,
+ };
+ const iconsDataCollection = mediaDataStructure.dataCollectionFromResultsListContainer(iconResponse);
+ arcadeShowcase.iconArtworks = content.impressionableAppIconsFromDataCollection(objectGraph, iconsDataCollection, iconMetricsOptions, {
+ useCase: 2 /* content.ArtworkUseCase.LockupIconMedium */,
+ });
+ }
+ }
+ const shelf = new models.Shelf("arcadeShowcase");
+ shelf.items = [arcadeShowcase];
+ const shelfHasBackgroundColor = objectGraph.host.isiOS || objectGraph.client.isVision;
+ if (shelfHasBackgroundColor) {
+ shelf.background = {
+ type: "color",
+ color: arcadeShowcaseShelfBackgroundColor,
+ };
+ }
+ return shelf;
+}
+// endregion
+function contentFromModule(objectGraph, module, context) {
+ const contents = mediaRelationships.relationshipData(objectGraph, module, "contents");
+ if (!contents) {
+ return null;
+ }
+ return contents;
+}
+function addImpressionsFieldsToModel(objectGraph, model, context, impressionData) {
+ if (!model) {
+ return;
+ }
+ let impressionType = context.module;
+ if (context.subStyle) {
+ impressionType = impressionType + "_" + context.subStyle;
+ }
+ if (serverData.isNull(impressionData)) {
+ impressionData = {
+ id: `${context.index}`,
+ impressionIndex: context.index,
+ idType: "sequential",
+ impressionType: impressionType,
+ kind: "iosModule",
+ };
+ }
+ model.impressionMetrics = new models.ImpressionMetrics(metricsHelpersUtil.sanitizedMetricsDictionary(impressionData));
+}
+//# sourceMappingURL=article.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/category-detail-motion-16x9.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/category-detail-motion-16x9.js
new file mode 100644
index 0000000..b209cba
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/category-detail-motion-16x9.js
@@ -0,0 +1,34 @@
+import { ArtworkContentMode, EdgeInsetsZero, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const categoryDetailMotion16x9Layout_mini = {
+ collapsedContentMode: ArtworkContentMode.bottom,
+ expandedContentMode: ArtworkContentMode.bottom,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const categoryDetailMotion16x9Layout_base = {
+ collapsedContentMode: ArtworkContentMode.top,
+ expandedContentMode: ArtworkContentMode.top,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const categoryDetailMotion16x9Metrics_mini = {
+ maxWidth: 250.0,
+ ltr: categoryDetailMotion16x9Layout_mini,
+ rtl: categoryDetailMotion16x9Layout_mini,
+ collapsedSize: { type: "absolute", width: 455, height: 256 },
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const categoryDetailMotion16x9Metrics_base = {
+ ltr: categoryDetailMotion16x9Layout_base,
+ rtl: categoryDetailMotion16x9Layout_base,
+ collapsedSize: { type: "fractionalHeight", height: 1.0 },
+};
+export const categoryDetailMotion16x9 = {
+ objectPath: "editorialVideo.categoryDetailMotion16x9",
+ cardArtLayoutMetrics: [categoryDetailMotion16x9Metrics_mini, categoryDetailMotion16x9Metrics_base],
+ sourceWidth: 800,
+ sourceHeight: 450,
+ type: "video",
+ crops: ["sr"],
+};
+//# sourceMappingURL=category-detail-motion-16x9.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/category-detail-static-16x9.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/category-detail-static-16x9.js
new file mode 100644
index 0000000..ab3c57f
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/category-detail-static-16x9.js
@@ -0,0 +1,49 @@
+import { ArtworkContentMode, EdgeInsetsZero, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const categoryDetailStatic16x9Layout_mini = {
+ collapsedContentMode: ArtworkContentMode.bottom,
+ expandedContentMode: ArtworkContentMode.bottom,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const categoryDetailStatic16x9Layout_base = {
+ collapsedContentMode: ArtworkContentMode.bottom,
+ expandedContentMode: ArtworkContentMode.bottom,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const categoryDetailStatic16x9Layout_700w = {
+ collapsedContentMode: ArtworkContentMode.scaleAspectFill,
+ expandedContentMode: ArtworkContentMode.bottom,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const categoryDetailStatic16x9Metrics_mini = {
+ maxWidth: 250.0,
+ ltr: categoryDetailStatic16x9Layout_mini,
+ rtl: categoryDetailStatic16x9Layout_mini,
+ collapsedSize: { type: "absolute", width: 455, height: 256 },
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const categoryDetailStatic16x9Metrics_base = {
+ maxWidth: 704,
+ ltr: categoryDetailStatic16x9Layout_base,
+ rtl: categoryDetailStatic16x9Layout_base,
+ collapsedSize: { type: "fractionalHeight", height: 1.08 },
+};
+const categoryDetailStatic16x9Metrics_700w = {
+ ltr: categoryDetailStatic16x9Layout_700w,
+ rtl: categoryDetailStatic16x9Layout_700w,
+};
+export const categoryDetailStatic16x9 = {
+ objectPath: "editorialArtwork.categoryDetailStatic16x9",
+ cardArtLayoutMetrics: [
+ categoryDetailStatic16x9Metrics_mini,
+ categoryDetailStatic16x9Metrics_base,
+ categoryDetailStatic16x9Metrics_700w,
+ ],
+ crops: ["sr"],
+ sourceWidth: 800,
+ sourceHeight: 450,
+ type: "image",
+};
+//# sourceMappingURL=category-detail-static-16x9.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/day-card.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/day-card.js
new file mode 100644
index 0000000..4688784
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/day-card.js
@@ -0,0 +1,51 @@
+import { ArtworkContentMode, EdgeInsetsZero, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const dayCardLayout_mini = {
+ collapsedContentMode: ArtworkContentMode.bottomLeft,
+ expandedContentMode: ArtworkContentMode.bottomLeft,
+ collapsedLayoutInsets: {
+ top: 0,
+ left: -8,
+ bottom: 0,
+ right: 0,
+ },
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const dayCardLayout_base = {
+ collapsedContentMode: ArtworkContentMode.bottomLeft,
+ expandedContentMode: ArtworkContentMode.bottomLeft,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const dayCardLayout_700w = {
+ collapsedContentMode: ArtworkContentMode.topLeft,
+ expandedContentMode: ArtworkContentMode.bottomLeft,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const dayCardMetrics_mini = {
+ maxWidth: 250,
+ ltr: dayCardLayout_mini,
+ rtl: dayCardLayout_mini,
+ collapsedSize: { type: "fractionalHeight", height: 1.0 },
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const dayCardMetrics_base = {
+ maxWidth: 704,
+ ltr: dayCardLayout_base,
+ rtl: dayCardLayout_base,
+ collapsedSize: { type: "fractionalHeight", height: 1.062 },
+};
+const dayCardMetrics_700w = {
+ ltr: dayCardLayout_700w,
+ rtl: dayCardLayout_700w,
+ collapsedSize: { type: "fractionalWidth", width: 1.0 },
+};
+export const dayCard = {
+ objectPath: "editorialArtwork.dayCard",
+ cardArtLayoutMetrics: [dayCardMetrics_mini, dayCardMetrics_base, dayCardMetrics_700w],
+ crops: ["sr"],
+ sourceWidth: 800,
+ sourceHeight: 490,
+ type: "image",
+};
+//# sourceMappingURL=day-card.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/event-card.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/event-card.js
new file mode 100644
index 0000000..3317d1b
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/event-card.js
@@ -0,0 +1,51 @@
+import { ArtworkContentMode, EdgeInsetsZero, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const eventCardLayout_mini = {
+ collapsedContentMode: ArtworkContentMode.bottomLeft,
+ collapsedLayoutInsets: {
+ top: 0,
+ left: -8,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.bottomLeft,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const eventCardLayout_base = {
+ collapsedContentMode: ArtworkContentMode.bottomLeft,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedContentMode: ArtworkContentMode.bottomLeft,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const eventCardLayout_700w = {
+ collapsedContentMode: ArtworkContentMode.scaleAspectFill,
+ expandedContentMode: ArtworkContentMode.bottomLeft,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const eventCardMetrics_mini = {
+ maxWidth: 250,
+ ltr: eventCardLayout_mini,
+ rtl: eventCardLayout_mini,
+ collapsedSize: { type: "fractionalHeight", height: 1.0 },
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const eventCardMetrics_base = {
+ maxWidth: 704,
+ ltr: eventCardLayout_base,
+ rtl: eventCardLayout_base,
+ collapsedSize: { type: "fractionalHeight", height: 1.062 },
+};
+const eventCardMetrics_700w = {
+ ltr: eventCardLayout_700w,
+ rtl: eventCardLayout_700w,
+ collapsedSize: { type: "fractionalWidth", width: 1.0 },
+};
+export const eventCard = {
+ objectPath: "editorialArtwork.eventCard",
+ cardArtLayoutMetrics: [eventCardMetrics_mini, eventCardMetrics_base, eventCardMetrics_700w],
+ crops: ["sr"],
+ sourceWidth: 800,
+ sourceHeight: 490,
+ type: "image",
+};
+//# sourceMappingURL=event-card.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/general-card.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/general-card.js
new file mode 100644
index 0000000..f50840f
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/general-card.js
@@ -0,0 +1,51 @@
+import { ArtworkContentMode, EdgeInsetsZero, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const generalCardLayout_mini = {
+ collapsedContentMode: ArtworkContentMode.bottomLeft,
+ expandedContentMode: ArtworkContentMode.bottomLeft,
+ collapsedLayoutInsets: {
+ top: 0,
+ left: -8,
+ bottom: 0,
+ right: 0,
+ },
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const generalCardLayout_base = {
+ collapsedContentMode: ArtworkContentMode.bottomLeft,
+ expandedContentMode: ArtworkContentMode.bottomLeft,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const generalCardLayout_700w = {
+ collapsedContentMode: ArtworkContentMode.scaleAspectFill,
+ expandedContentMode: ArtworkContentMode.bottomLeft,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const generalCardMetrics_mini = {
+ maxWidth: 250,
+ ltr: generalCardLayout_mini,
+ rtl: generalCardLayout_mini,
+ collapsedSize: { type: "fractionalHeight", height: 1.0 },
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const generalCardMetrics_base = {
+ maxWidth: 704,
+ ltr: generalCardLayout_base,
+ rtl: generalCardLayout_base,
+ collapsedSize: { type: "fractionalHeight", height: 1.062 },
+};
+const generalCardMetrics_700w = {
+ ltr: generalCardLayout_700w,
+ rtl: generalCardLayout_700w,
+ collapsedSize: { type: "fractionalWidth", width: 1.0 },
+};
+export const generalCard = {
+ objectPath: "editorialArtwork.generalCard",
+ cardArtLayoutMetrics: [generalCardMetrics_mini, generalCardMetrics_base, generalCardMetrics_700w],
+ crops: ["MC.ApSCFB01"],
+ sourceWidth: 800,
+ sourceHeight: 490,
+ type: "image",
+};
+//# sourceMappingURL=general-card.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/index.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/index.js
new file mode 100644
index 0000000..5efa2cb
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/index.js
@@ -0,0 +1,85 @@
+import { isSome } from "@jet/environment";
+import { dayCard } from "./day-card";
+import { eventCard } from "./event-card";
+import { generalCard } from "./general-card";
+import { listCardMotion21x9 } from "./list-card-motion-21x9";
+import { listCardStatic21x9 } from "./list-card-static-21x9";
+import { mediaCard } from "./media-card";
+import { storeFrontVideo } from "./store-front-video";
+import { storeFrontVideo4x3 } from "./store-front-video-4x3";
+import { storyCardMotion16x9 } from "./story-card-motion-16x9";
+import { storyCardStatic16x9 } from "./story-card-static-16x9";
+import { storyCenteredMotion16x9 } from "./story-centered-motion-16x9";
+import { storyCenteredStatic16x9 } from "./story-centered-static-16x9";
+import { storySearchStatic16x9 } from "./story-search-static-16x9";
+import { universalAMotion16x9 } from "./universal-a-motion-16x9";
+import { universalAStatic16x9 } from "./universal-a-static-16x9";
+import { categoryDetailMotion16x9 } from "./category-detail-motion-16x9";
+import { categoryDetailStatic16x9 } from "./category-detail-static-16x9";
+/**
+ * Hero position on iPad requires the new artwork flavor to be considered valid
+ */
+export function universalAConfigurations(objectGraph, isHeroPosition) {
+ return isHeroPosition
+ ? [universalAMotion16x9Configuration(objectGraph), universalAStatic16x9Configuration(objectGraph)]
+ : [];
+}
+export function dayCardConfiguration(objectGraph) {
+ return dayCard;
+}
+export function eventCardConfiguration(objectGraph) {
+ return eventCard;
+}
+export function generalCardConfiguration(objectGraph, cropCodeOverrides) {
+ return applyOverrides(generalCard, cropCodeOverrides);
+}
+export function listCardMotion21x9Configuration(objectGraph) {
+ return listCardMotion21x9;
+}
+export function listCardStatic21x9Configuration(objectGraph) {
+ return listCardStatic21x9;
+}
+export function mediaCardConfiguration(objectGraph, cropCodeOverrides) {
+ return applyOverrides(mediaCard, cropCodeOverrides);
+}
+export function storeFrontVideoConfiguration(objectGraph) {
+ return storeFrontVideo;
+}
+export function storeFrontVideo4x3Configuration(objectGraph) {
+ return storeFrontVideo4x3;
+}
+export function storyCardMotion16x9Configuration(objectGraph) {
+ return storyCardMotion16x9;
+}
+export function storyCardStatic16x9Configuration(objectGraph) {
+ return storyCardStatic16x9;
+}
+export function storyCenteredMotion16x9Configuration(objectGraph) {
+ return storyCenteredMotion16x9;
+}
+export function storyCenteredStatic16x9Configuration(objectGraph, cropCodeOverrides) {
+ return applyOverrides(storyCenteredStatic16x9, cropCodeOverrides);
+}
+export function categoryDetailMotion16x9Configuration(objectGraph) {
+ return categoryDetailMotion16x9;
+}
+export function categoryDetailStatic16x9Configuration(objectGraph, cropCodeOverrides) {
+ return applyOverrides(categoryDetailStatic16x9, cropCodeOverrides);
+}
+export function storySearchStatic16x9Configuration(objectGraph) {
+ return storySearchStatic16x9;
+}
+function universalAMotion16x9Configuration(objectGraph) {
+ return universalAMotion16x9;
+}
+function universalAStatic16x9Configuration(objectGraph) {
+ return universalAStatic16x9;
+}
+function applyOverrides(mediaConfiguration, cropCodeOverrides) {
+ const updatedMediaConfiguration = { ...mediaConfiguration };
+ if (isSome(cropCodeOverrides)) {
+ updatedMediaConfiguration.crops = cropCodeOverrides;
+ }
+ return updatedMediaConfiguration;
+}
+//# sourceMappingURL=index.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/list-card-motion-21x9.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/list-card-motion-21x9.js
new file mode 100644
index 0000000..8570df2
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/list-card-motion-21x9.js
@@ -0,0 +1,116 @@
+import { ArtworkContentMode, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const expandedContentMode = ArtworkContentMode.topRight;
+const expandedLayoutInsets = {
+ top: -108,
+ left: 495,
+ bottom: 0,
+ right: 0,
+};
+const expandedContentMode_rtl = ArtworkContentMode.topLeft;
+const expandedLayoutInsets_rtl = {
+ top: -108,
+ left: -495,
+ bottom: 0,
+ right: 0,
+};
+const expandedSize = { type: "absolute", width: 1124, height: 482 };
+const listCardMotion16x9Layout_mini = {
+ collapsedContentMode: ArtworkContentMode.top,
+ collapsedLayoutInsets: {
+ top: -117,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode,
+ expandedLayoutInsets,
+};
+const listCardMotion16x9Layout_mini_rtl = {
+ collapsedContentMode: ArtworkContentMode.top,
+ collapsedLayoutInsets: {
+ top: -117,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: expandedContentMode_rtl,
+ expandedLayoutInsets: expandedLayoutInsets_rtl,
+};
+const listCardMotion16x9Layout_base = {
+ collapsedContentMode: ArtworkContentMode.topRight,
+ collapsedLayoutInsets: {
+ top: -165,
+ left: 524,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode,
+ expandedLayoutInsets,
+};
+const listCardMotion16x9Layout_base_rtl = {
+ collapsedContentMode: ArtworkContentMode.topLeft,
+ collapsedLayoutInsets: {
+ top: -165,
+ left: -524,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: expandedContentMode_rtl,
+ expandedLayoutInsets: expandedLayoutInsets_rtl,
+};
+const listCardMotion16x9Layout_extraWide = {
+ collapsedContentMode: ArtworkContentMode.top,
+ collapsedLayoutInsets: {
+ top: -55,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode,
+ expandedLayoutInsets,
+};
+const listCardMotion16x9Layout_extraWide_rtl = {
+ collapsedContentMode: ArtworkContentMode.top,
+ collapsedLayoutInsets: {
+ top: -55,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: expandedContentMode_rtl,
+ expandedLayoutInsets: expandedLayoutInsets_rtl,
+};
+const listCardMotion16x9Metrics_mini = {
+ maxWidth: 250.0,
+ ltr: listCardMotion16x9Layout_mini,
+ rtl: listCardMotion16x9Layout_mini_rtl,
+ collapsedSize: { type: "absolute", width: 914, height: 392 },
+ expandedSize,
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const listCardMotion16x9Metrics_base = {
+ ltr: listCardMotion16x9Layout_base,
+ rtl: listCardMotion16x9Layout_base_rtl,
+ collapsedSize: { type: "absolute", width: 1188, height: 509 },
+ expandedSize,
+};
+const listCardMotion16x9Metrics_extraWide = {
+ ltr: listCardMotion16x9Layout_extraWide,
+ rtl: listCardMotion16x9Layout_extraWide_rtl,
+ collapsedSize: { type: "absolute", width: 1456, height: 624 },
+ expandedSize,
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.ExtraWide,
+};
+export const listCardMotion21x9 = {
+ objectPath: "editorialVideo.listCardMotion21x9",
+ cardArtLayoutMetrics: [
+ listCardMotion16x9Metrics_mini,
+ listCardMotion16x9Metrics_base,
+ listCardMotion16x9Metrics_extraWide,
+ ],
+ crops: [],
+ sourceWidth: 1208,
+ sourceHeight: 518,
+ type: "video",
+};
+//# sourceMappingURL=list-card-motion-21x9.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/list-card-static-21x9.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/list-card-static-21x9.js
new file mode 100644
index 0000000..8adecbe
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/list-card-static-21x9.js
@@ -0,0 +1,92 @@
+import { ArtworkContentMode, EdgeInsetsZero, Size, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const listCardStatic21x9Layout_mini = {
+ collapsedContentMode: ArtworkContentMode.bottomRight,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedContentMode: ArtworkContentMode.topRight,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const listCardStatic21x9Layout_mini_rtl = {
+ collapsedContentMode: ArtworkContentMode.bottomLeft,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedContentMode: ArtworkContentMode.topLeft,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const listCardStatic21x9Layout_base = {
+ collapsedContentMode: ArtworkContentMode.topRight,
+ collapsedLayoutInsets: {
+ top: -49,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.right,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const listCardStatic21x9Layout_base_rtl = {
+ collapsedContentMode: ArtworkContentMode.topLeft,
+ collapsedLayoutInsets: {
+ top: -49,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.left,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const listCardStatic16x9Layout_extraWide = {
+ collapsedContentMode: ArtworkContentMode.scaleAspectFill,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedContentMode: ArtworkContentMode.bottomRight,
+ expandedLayoutInsets: {
+ top: 50,
+ left: 495,
+ bottom: 0,
+ right: 0,
+ },
+};
+const listCardStatic16x9Layout_extraWide_rtl = {
+ collapsedContentMode: ArtworkContentMode.scaleAspectFill,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedContentMode: ArtworkContentMode.bottomRight,
+ expandedLayoutInsets: {
+ top: 50,
+ left: -495,
+ bottom: 0,
+ right: 0,
+ },
+};
+const listCardStatic21x9Metrics_mini = {
+ maxWidth: 250.0,
+ ltr: listCardStatic21x9Layout_mini,
+ rtl: listCardStatic21x9Layout_mini_rtl,
+ sourceCropOverrideLTR: "LCS.ApLCS01",
+ sourceCropOverrideRTL: "LCS.ApLCS02",
+ sourceSizeOverride: new Size(550, 264),
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const listCardStatic21x9Metrics_base = {
+ ltr: listCardStatic21x9Layout_base,
+ rtl: listCardStatic21x9Layout_base_rtl,
+ collapsedSize: { type: "absolute", width: 672, height: 279 },
+};
+const listCardMotion16x9Metrics_extraWide = {
+ ltr: listCardStatic16x9Layout_extraWide,
+ rtl: listCardStatic16x9Layout_extraWide_rtl,
+ expandedSize: { type: "absolute", width: 1124, height: 482 },
+ sourceCropOverrideLTR: "LCS.ApLCXW01",
+ sourceCropOverrideRTL: "LCS.ApLCXW01",
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.ExtraWide,
+};
+export const listCardStatic21x9 = {
+ objectPath: "editorialArtwork.listCardStatic21x9",
+ cardArtLayoutMetrics: [
+ listCardStatic21x9Metrics_mini,
+ listCardStatic21x9Metrics_base,
+ listCardMotion16x9Metrics_extraWide,
+ ],
+ crops: ["LCS.ApLCL01", "LCS.ApLCL02"],
+ sourceWidth: 688,
+ sourceHeight: 286,
+ type: "image",
+};
+//# sourceMappingURL=list-card-static-21x9.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/media-card.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/media-card.js
new file mode 100644
index 0000000..3b8ff21
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/media-card.js
@@ -0,0 +1,51 @@
+import { ArtworkContentMode, EdgeInsetsZero, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const mediaCardLayout_mini = {
+ collapsedContentMode: ArtworkContentMode.bottomLeft,
+ collapsedLayoutInsets: {
+ top: 0,
+ left: -8,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.bottomLeft,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const mediaCardLayout_base = {
+ collapsedContentMode: ArtworkContentMode.bottomLeft,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedContentMode: ArtworkContentMode.bottomLeft,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const mediaCardLayout_700w = {
+ collapsedContentMode: ArtworkContentMode.scaleAspectFill,
+ expandedContentMode: ArtworkContentMode.bottomLeft,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const mediaCardMetrics_mini = {
+ maxWidth: 250,
+ ltr: mediaCardLayout_mini,
+ rtl: mediaCardLayout_mini,
+ collapsedSize: { type: "fractionalHeight", height: 1.0 },
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const mediaCardMetrics_base = {
+ maxWidth: 704,
+ ltr: mediaCardLayout_base,
+ rtl: mediaCardLayout_base,
+ collapsedSize: { type: "fractionalHeight", height: 1.062 },
+};
+const mediaCardMetrics_700w = {
+ ltr: mediaCardLayout_700w,
+ rtl: mediaCardLayout_700w,
+ collapsedSize: { type: "fractionalWidth", width: 1.0 },
+};
+export const mediaCard = {
+ objectPath: "editorialArtwork.mediaCard",
+ cardArtLayoutMetrics: [mediaCardMetrics_mini, mediaCardMetrics_base, mediaCardMetrics_700w],
+ crops: ["MC.ApSCFB01"],
+ sourceWidth: 800,
+ sourceHeight: 490,
+ type: "image",
+};
+//# sourceMappingURL=media-card.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/store-front-video-4x3.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/store-front-video-4x3.js
new file mode 100644
index 0000000..653df0e
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/store-front-video-4x3.js
@@ -0,0 +1,22 @@
+import { ArtworkContentMode, EdgeInsetsZero } from "../../../../api/models";
+const storeFrontVideo4x3Layout = {
+ collapsedContentMode: ArtworkContentMode.center,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedContentMode: ArtworkContentMode.center,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const storeFrontVideo4x3Metrics = {
+ ltr: storeFrontVideo4x3Layout,
+ rtl: storeFrontVideo4x3Layout,
+ collapsedSize: { type: "fractionalHeight", height: 1.0 },
+ expandedSize: { type: "fractionalHeight", height: 1.0 },
+};
+export const storeFrontVideo4x3 = {
+ objectPath: "editorialVideo.storeFrontVideo4x3",
+ cardArtLayoutMetrics: [storeFrontVideo4x3Metrics],
+ crops: [],
+ sourceWidth: 656,
+ sourceHeight: 492,
+ type: "video",
+};
+//# sourceMappingURL=store-front-video-4x3.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/store-front-video.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/store-front-video.js
new file mode 100644
index 0000000..472e52c
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/store-front-video.js
@@ -0,0 +1,25 @@
+import { ArtworkContentMode, EdgeInsetsZero, } from "../../../../api/models";
+const storeFrontVideoLayout = {
+ collapsedContentMode: ArtworkContentMode.center,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedContentMode: ArtworkContentMode.center,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const storeFrontVideoMetrics = {
+ ltr: storeFrontVideoLayout,
+ rtl: storeFrontVideoLayout,
+ collapsedSize: { type: "fractionalHeight", height: 1.0 },
+ expandedSize: { type: "fractionalHeight", height: 1.0 },
+};
+/**
+ * This configuration is in 16x9 aspect ratio.
+ */
+export const storeFrontVideo = {
+ objectPath: "editorialVideo.storeFrontVideo",
+ cardArtLayoutMetrics: [storeFrontVideoMetrics],
+ crops: [],
+ sourceWidth: 875,
+ sourceHeight: 492,
+ type: "video",
+};
+//# sourceMappingURL=store-front-video.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-card-motion-16x9.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-card-motion-16x9.js
new file mode 100644
index 0000000..b0e4530
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-card-motion-16x9.js
@@ -0,0 +1,34 @@
+import { ArtworkContentMode, EdgeInsetsZero, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const storyCardMotion16x9Layout_mini = {
+ collapsedContentMode: ArtworkContentMode.bottom,
+ expandedContentMode: ArtworkContentMode.bottom,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const storyCardMotion16x9Layout_base = {
+ collapsedContentMode: ArtworkContentMode.top,
+ expandedContentMode: ArtworkContentMode.top,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const storyCardMotion16x9Metrics_mini = {
+ maxWidth: 250.0,
+ ltr: storyCardMotion16x9Layout_mini,
+ rtl: storyCardMotion16x9Layout_mini,
+ collapsedSize: { type: "absolute", width: 455, height: 256 },
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const storyCardMotion16x9Metrics_base = {
+ ltr: storyCardMotion16x9Layout_base,
+ rtl: storyCardMotion16x9Layout_base,
+ collapsedSize: { type: "fractionalHeight", height: 1.0 },
+};
+export const storyCardMotion16x9 = {
+ objectPath: "editorialVideo.storyCardMotion16x9",
+ cardArtLayoutMetrics: [storyCardMotion16x9Metrics_mini, storyCardMotion16x9Metrics_base],
+ sourceWidth: 800,
+ sourceHeight: 450,
+ type: "video",
+ crops: ["sr"],
+};
+//# sourceMappingURL=story-card-motion-16x9.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-card-static-16x9.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-card-static-16x9.js
new file mode 100644
index 0000000..c4e7220
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-card-static-16x9.js
@@ -0,0 +1,49 @@
+import { ArtworkContentMode, EdgeInsetsZero, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const storyCardStatic16x9Layout_mini = {
+ collapsedContentMode: ArtworkContentMode.bottom,
+ expandedContentMode: ArtworkContentMode.bottom,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const storyCardStatic16x9Layout_base = {
+ collapsedContentMode: ArtworkContentMode.bottom,
+ expandedContentMode: ArtworkContentMode.bottom,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const storyCardStatic16x9Layout_700w = {
+ collapsedContentMode: ArtworkContentMode.scaleAspectFill,
+ expandedContentMode: ArtworkContentMode.bottom,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const storyCardStatic16x9Metrics_mini = {
+ maxWidth: 250.0,
+ ltr: storyCardStatic16x9Layout_mini,
+ rtl: storyCardStatic16x9Layout_mini,
+ collapsedSize: { type: "absolute", width: 455, height: 256 },
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const storyCardStatic16x9Metrics_base = {
+ maxWidth: 704,
+ ltr: storyCardStatic16x9Layout_base,
+ rtl: storyCardStatic16x9Layout_base,
+ collapsedSize: { type: "fractionalHeight", height: 1.08 },
+};
+const storyCardStatic16x9Metrics_700w = {
+ ltr: storyCardStatic16x9Layout_700w,
+ rtl: storyCardStatic16x9Layout_700w,
+};
+export const storyCardStatic16x9 = {
+ objectPath: "editorialArtwork.storyCardStatic16x9",
+ cardArtLayoutMetrics: [
+ storyCardStatic16x9Metrics_mini,
+ storyCardStatic16x9Metrics_base,
+ storyCardStatic16x9Metrics_700w,
+ ],
+ crops: ["sr"],
+ sourceWidth: 800,
+ sourceHeight: 450,
+ type: "image",
+};
+//# sourceMappingURL=story-card-static-16x9.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-centered-motion-16x9.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-centered-motion-16x9.js
new file mode 100644
index 0000000..273503b
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-centered-motion-16x9.js
@@ -0,0 +1,34 @@
+import { ArtworkContentMode, EdgeInsetsZero, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const storyCenteredMotion16x9Layout_mini = {
+ collapsedContentMode: ArtworkContentMode.bottom,
+ expandedContentMode: ArtworkContentMode.bottom,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const storyCenteredMotion16x9Layout_base = {
+ collapsedContentMode: ArtworkContentMode.top,
+ expandedContentMode: ArtworkContentMode.top,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const storyCenteredMotion16x9Metrics_mini = {
+ maxWidth: 250.0,
+ ltr: storyCenteredMotion16x9Layout_mini,
+ rtl: storyCenteredMotion16x9Layout_mini,
+ collapsedSize: { type: "absolute", width: 455, height: 256 },
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const storyCenteredMotion16x9Metrics_base = {
+ ltr: storyCenteredMotion16x9Layout_base,
+ rtl: storyCenteredMotion16x9Layout_base,
+ collapsedSize: { type: "fractionalHeight", height: 1.0 },
+};
+export const storyCenteredMotion16x9 = {
+ objectPath: "editorialVideo.storyCenteredMotion16x9",
+ cardArtLayoutMetrics: [storyCenteredMotion16x9Metrics_mini, storyCenteredMotion16x9Metrics_base],
+ sourceWidth: 800,
+ sourceHeight: 450,
+ type: "video",
+ crops: ["sr"],
+};
+//# sourceMappingURL=story-centered-motion-16x9.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-centered-static-16x9.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-centered-static-16x9.js
new file mode 100644
index 0000000..404d58c
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/story-centered-static-16x9.js
@@ -0,0 +1,49 @@
+import { ArtworkContentMode, EdgeInsetsZero, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const storyCenteredStatic16x9Layout_mini = {
+ collapsedContentMode: ArtworkContentMode.bottom,
+ expandedContentMode: ArtworkContentMode.bottom,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const storyCenteredStatic16x9Layout_base = {
+ collapsedContentMode: ArtworkContentMode.bottom,
+ expandedContentMode: ArtworkContentMode.bottom,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const storyCenteredStatic16x9Layout_700w = {
+ collapsedContentMode: ArtworkContentMode.scaleAspectFill,
+ expandedContentMode: ArtworkContentMode.bottom,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const storyCenteredStatic16x9Metrics_mini = {
+ maxWidth: 250.0,
+ ltr: storyCenteredStatic16x9Layout_mini,
+ rtl: storyCenteredStatic16x9Layout_mini,
+ collapsedSize: { type: "absolute", width: 455, height: 256 },
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const storyCenteredStatic16x9Metrics_base = {
+ maxWidth: 704,
+ ltr: storyCenteredStatic16x9Layout_base,
+ rtl: storyCenteredStatic16x9Layout_base,
+ collapsedSize: { type: "fractionalHeight", height: 1.08 },
+};
+const storyCenteredStatic16x9Metrics_700w = {
+ ltr: storyCenteredStatic16x9Layout_700w,
+ rtl: storyCenteredStatic16x9Layout_700w,
+};
+export const storyCenteredStatic16x9 = {
+ objectPath: "editorialArtwork.storyCenteredStatic16x9",
+ cardArtLayoutMetrics: [
+ storyCenteredStatic16x9Metrics_mini,
+ storyCenteredStatic16x9Metrics_base,
+ storyCenteredStatic16x9Metrics_700w,
+ ],
+ crops: ["sr"],
+ sourceWidth: 800,
+ sourceHeight: 450,
+ type: "image",
+};
+//# sourceMappingURL=story-centered-static-16x9.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/universal-a-motion-16x9.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/universal-a-motion-16x9.js
new file mode 100644
index 0000000..f16d51f
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/universal-a-motion-16x9.js
@@ -0,0 +1,146 @@
+import { ArtworkContentMode, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const expandedSize = { type: "absolute", width: 1200, height: 675 };
+const expandedLayoutInsets = {
+ top: -130,
+ left: 0,
+ bottom: 0,
+ right: 0,
+};
+const universalAMotion16x9Layout_mini = {
+ collapsedContentMode: ArtworkContentMode.top,
+ collapsedLayoutInsets: {
+ top: -16,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.top,
+ expandedLayoutInsets,
+};
+const universalAMotion16x9Layout_base = {
+ collapsedContentMode: ArtworkContentMode.top,
+ collapsedLayoutInsets: {
+ top: -116,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.top,
+ expandedLayoutInsets,
+};
+const universalAMotion16x9Layout_700w = {
+ collapsedContentMode: ArtworkContentMode.topLeft,
+ collapsedLayoutInsets: {
+ top: -116,
+ left: -70,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.top,
+ expandedLayoutInsets,
+};
+const universalAMotion16x9Layout_700w_rtl = {
+ collapsedContentMode: ArtworkContentMode.topRight,
+ collapsedLayoutInsets: {
+ top: -116,
+ left: 70,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.top,
+ expandedLayoutInsets,
+};
+const universalAMotion16x9Layout_920w = {
+ collapsedContentMode: ArtworkContentMode.topLeft,
+ collapsedLayoutInsets: {
+ top: -148,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.top,
+ expandedLayoutInsets,
+};
+const universalAMotion16x9Layout_920w_rtl = {
+ collapsedContentMode: ArtworkContentMode.topRight,
+ collapsedLayoutInsets: {
+ top: -148,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.top,
+ expandedLayoutInsets,
+};
+const universalAMotion16x9Layout_max = {
+ collapsedContentMode: ArtworkContentMode.topLeft,
+ collapsedLayoutInsets: {
+ top: -170,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.top,
+ expandedLayoutInsets,
+};
+const universalAMotion16x9Layout_max_rtl = {
+ collapsedContentMode: ArtworkContentMode.topRight,
+ collapsedLayoutInsets: {
+ top: -170,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.top,
+ expandedLayoutInsets,
+};
+const universalAMotion16x9Metrics_mini = {
+ maxWidth: 250.0,
+ ltr: universalAMotion16x9Layout_mini,
+ rtl: universalAMotion16x9Layout_mini,
+ collapsedSize: { type: "absolute", width: 455, height: 256 },
+ expandedSize,
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const universalAMotion16x9Metrics_base = {
+ maxWidth: 699.0,
+ ltr: universalAMotion16x9Layout_base,
+ rtl: universalAMotion16x9Layout_base,
+ collapsedSize: { type: "absolute", width: 1094, height: 614 },
+ expandedSize,
+};
+const universalAMotion16x9Metrics_700w = {
+ maxWidth: 899.0,
+ ltr: universalAMotion16x9Layout_700w,
+ rtl: universalAMotion16x9Layout_700w_rtl,
+ collapsedSize: { type: "absolute", width: 1092, height: 614 },
+ expandedSize,
+};
+const universalAMotion16x9Metrics_920w = {
+ maxWidth: 1090.0,
+ ltr: universalAMotion16x9Layout_920w,
+ rtl: universalAMotion16x9Layout_920w_rtl,
+ collapsedSize: { type: "absolute", width: 1392, height: 783 },
+ expandedSize,
+};
+const universalAMotion16x9Metrics_max = {
+ ltr: universalAMotion16x9Layout_max,
+ rtl: universalAMotion16x9Layout_max_rtl,
+ collapsedSize: { type: "absolute", width: 1600, height: 900 },
+ expandedSize,
+};
+export const universalAMotion16x9 = {
+ objectPath: "editorialVideo.universalAMotion16x9",
+ cardArtLayoutMetrics: [
+ universalAMotion16x9Metrics_mini,
+ universalAMotion16x9Metrics_base,
+ universalAMotion16x9Metrics_700w,
+ universalAMotion16x9Metrics_920w,
+ universalAMotion16x9Metrics_max,
+ ],
+ crops: ["UAS.ApXWC01"],
+ sourceWidth: 1600,
+ sourceHeight: 900,
+ type: "video",
+};
+//# sourceMappingURL=universal-a-motion-16x9.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/universal-a-static-16x9.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/universal-a-static-16x9.js
new file mode 100644
index 0000000..f0d34ce
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/media-configurations/universal-a-static-16x9.js
@@ -0,0 +1,107 @@
+import { ArtworkContentMode, EdgeInsetsZero, TodayCardArtworkSizedLayoutMetricsPriority, } from "../../../../api/models";
+const universalAStatic16x9Layout_mini = {
+ collapsedContentMode: ArtworkContentMode.top,
+ collapsedLayoutInsets: {
+ top: 16,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.top,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const universalAStatic16x9Layout_base = {
+ collapsedContentMode: ArtworkContentMode.scaleAspectFill,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedContentMode: ArtworkContentMode.scaleAspectFill,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const universalAStatic16x9Layout_700w = {
+ collapsedContentMode: ArtworkContentMode.topLeft,
+ collapsedLayoutInsets: {
+ top: 0,
+ left: -70,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.center,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const universalAStatic16x9Layout_700w_rtl = {
+ collapsedContentMode: ArtworkContentMode.topRight,
+ collapsedLayoutInsets: {
+ top: 0,
+ left: 70,
+ bottom: 0,
+ right: 0,
+ },
+ expandedContentMode: ArtworkContentMode.center,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const universalAStatic16x9Layout_920w = {
+ collapsedContentMode: ArtworkContentMode.topLeft,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedContentMode: ArtworkContentMode.center,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const universalAStatic16x9Layout_920w_rtl = {
+ collapsedContentMode: ArtworkContentMode.topRight,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedContentMode: ArtworkContentMode.center,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const universalAStatic16x9Layout_max = {
+ collapsedContentMode: ArtworkContentMode.topLeft,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedContentMode: ArtworkContentMode.center,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const universalAStatic16x9Layout_max_rtl = {
+ collapsedContentMode: ArtworkContentMode.topRight,
+ collapsedLayoutInsets: EdgeInsetsZero,
+ expandedContentMode: ArtworkContentMode.center,
+ expandedLayoutInsets: EdgeInsetsZero,
+};
+const universalAStatic16x9Metrics_mini = {
+ maxWidth: 250.0,
+ ltr: universalAStatic16x9Layout_mini,
+ rtl: universalAStatic16x9Layout_mini,
+ collapsedSize: { type: "absolute", width: 455, height: 256 },
+ priority: TodayCardArtworkSizedLayoutMetricsPriority.Mini,
+};
+const universalAStatic16x9Metrics_base = {
+ maxWidth: 699.0,
+ ltr: universalAStatic16x9Layout_base,
+ rtl: universalAStatic16x9Layout_base,
+};
+const universalAStatic16x9Metrics_700w = {
+ maxWidth: 899.0,
+ ltr: universalAStatic16x9Layout_700w,
+ rtl: universalAStatic16x9Layout_700w_rtl,
+};
+const universalAStatic16x9Metrics_920w = {
+ maxWidth: 1090.0,
+ ltr: universalAStatic16x9Layout_920w,
+ rtl: universalAStatic16x9Layout_920w_rtl,
+ collapsedSize: { type: "absolute", width: 1400, height: 527 },
+};
+const universalAStatic16x9Metrics_max = {
+ ltr: universalAStatic16x9Layout_max,
+ rtl: universalAStatic16x9Layout_max_rtl,
+ collapsedSize: { type: "absolute", width: 1600, height: 604 },
+};
+export const universalAStatic16x9 = {
+ objectPath: "editorialArtwork.universalAStatic16x9",
+ cardArtLayoutMetrics: [
+ universalAStatic16x9Metrics_mini,
+ universalAStatic16x9Metrics_base,
+ universalAStatic16x9Metrics_700w,
+ universalAStatic16x9Metrics_920w,
+ universalAStatic16x9Metrics_max,
+ ],
+ crops: ["UAS.ApXWC01"],
+ sourceWidth: 1600,
+ sourceHeight: 604,
+ type: "image",
+};
+//# sourceMappingURL=universal-a-static-16x9.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/today-artwork-util.js b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/today-artwork-util.js
new file mode 100644
index 0000000..4705bef
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/artwork/today-artwork-util.js
@@ -0,0 +1,267 @@
+import { isNothing, isSome } from "@jet/environment";
+import { Video } from "../../../api/models";
+import { asDictionary, asNumber, asString } from "../../../foundation/json-parsing/server-data";
+import { editorialCardFromData } from "../../../foundation/media/associations";
+import * as mediaAttributes from "../../../foundation/media/attributes";
+import { autoPlayControls, standardControls } from "../../constants/video-constants";
+import { artworkFromApiArtwork, joeColorSetFromData } from "../../content/content";
+import { cardDisplayStyleFromData } from "../today-card-util";
+import { TodayCardDisplayStyle } from "../today-types";
+import { categoryDetailMotion16x9Configuration, categoryDetailStatic16x9Configuration, dayCardConfiguration, eventCardConfiguration, generalCardConfiguration, listCardMotion21x9Configuration, listCardStatic21x9Configuration, mediaCardConfiguration, storeFrontVideo4x3Configuration, storeFrontVideoConfiguration, storyCardMotion16x9Configuration, storyCardStatic16x9Configuration, storyCenteredMotion16x9Configuration, storyCenteredStatic16x9Configuration, storySearchStatic16x9Configuration, universalAConfigurations, } from "./media-configurations";
+/**
+ * Retrieves the artwork details for a today card. This function will determine the
+ * appropriate media configuration to use based on the card display style and the
+ * media data available in the card response.
+ *
+ * @param objectGraph - The object graph of the app store.
+ * @param data - The media data.
+ * @param cardConfig - The configuration for the today card.
+ * @returns The artwork details for the today card, or undefined if no artwork is available.
+ */
+export function todayCardArtworkDetails(objectGraph, data, cardConfig) {
+ var _a;
+ // Watch uses different artwork lookup logic
+ const isWebViewingWatch = objectGraph.client.isWeb && ((_a = objectGraph.activeIntent) === null || _a === void 0 ? void 0 : _a.previewPlatform) === "watch";
+ if (objectGraph.client.isWatch || isWebViewingWatch) {
+ return todayCardArtworkDetailsForWatch(objectGraph, data, cardConfig);
+ }
+ const cardDisplayStyle = cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle);
+ // Determine our preferred ranking of media flavors for the display style,
+ // then get the highest ranked media available on the card response.
+ const mediaSearchOrder = cardConfig.isHorizontalShelfContext
+ ? horizontalShelfMediaSearchOrderForCardDisplayStyle(objectGraph, cardConfig)
+ : mediaSearchOrderForCardDisplayStyle(objectGraph, cardDisplayStyle, cardConfig);
+ let mediaConfigurationForCard;
+ let isUsingEditorialCardOverride = false;
+ const editorialCardData = editorialCardFromData(data);
+ if (mediaAttributes.hasAttributes(editorialCardData)) {
+ mediaConfigurationForCard = mediaSearchOrder.find((conf) => {
+ return isSome(mediaAttributes.attributeAsDictionary(editorialCardData, conf.objectPath));
+ });
+ isUsingEditorialCardOverride = isSome(mediaConfigurationForCard);
+ }
+ if (isNothing(mediaConfigurationForCard)) {
+ mediaConfigurationForCard = mediaSearchOrder.find((conf) => {
+ return isSome(mediaAttributes.attributeAsDictionary(data, conf.objectPath));
+ });
+ }
+ if (!mediaConfigurationForCard) {
+ return undefined;
+ }
+ const mediaData = isUsingEditorialCardOverride
+ ? mediaAttributes.attributeAsDictionary(editorialCardData, mediaConfigurationForCard.objectPath)
+ : mediaAttributes.attributeAsDictionary(data, mediaConfigurationForCard.objectPath);
+ if (mediaConfigurationForCard.type === "image") {
+ // Return an Artwork and cardArtLayout
+ const artworks = mediaConfigurationForCard.crops.map((crop) => {
+ let cropCodeToUse = crop;
+ if (cardConfig.isSearchContext && isSome(cardConfig.prevailingCropCodes)) {
+ cropCodeToUse = cardConfig.prevailingCropCodes.defaultCrop;
+ }
+ else if (isSome(cardConfig.prevailingCropCodes) &&
+ isSome(cardConfig.prevailingCropCodes[mediaConfigurationForCard.objectPath])) {
+ cropCodeToUse = cardConfig.prevailingCropCodes[mediaConfigurationForCard.objectPath];
+ }
+ const artwork = artworkFromApiArtwork(objectGraph, mediaData, {
+ withJoeColorPlaceholder: true,
+ cropCode: cropCodeToUse,
+ useCase: 15 /* ArtworkUseCase.TodayCardMedia */,
+ overrideHeight: mediaConfigurationForCard.sourceHeight,
+ overrideWidth: mediaConfigurationForCard.sourceWidth,
+ });
+ return artwork;
+ });
+ const joeColors = joeColorSetFromData(mediaData);
+ return {
+ artworks: artworks,
+ videos: [],
+ artworkLayoutsWithMetrics: mediaConfigurationForCard.cardArtLayoutMetrics,
+ joeColors: joeColors,
+ };
+ }
+ else {
+ // Return a Video and cardArtLayout
+ const previewArtworkData = asDictionary(mediaData, "previewFrame");
+ // All TodayCardArtworkLayouts are specified in landscape aspect ratio,
+ // but videos can come back in either landscape or portrait. If we
+ // receive a portrait video, we need to flip the TodayCardArtworkLayout
+ const previewArtworkWidth = asNumber(previewArtworkData, "width");
+ const previewArtworkHeight = asNumber(previewArtworkData, "height");
+ const isPortrait = previewArtworkHeight >= previewArtworkWidth;
+ let overrideHeight = mediaConfigurationForCard.sourceHeight;
+ let overrideWidth = mediaConfigurationForCard.sourceWidth;
+ if (isPortrait) {
+ [overrideHeight, overrideWidth] = [overrideWidth, overrideHeight];
+ }
+ const previewArtwork = artworkFromApiArtwork(objectGraph, previewArtworkData, {
+ withJoeColorPlaceholder: true,
+ cropCode: mediaConfigurationForCard.crops[0],
+ useCase: 15 /* ArtworkUseCase.TodayCardMedia */,
+ overrideHeight: overrideHeight,
+ overrideWidth: overrideWidth,
+ });
+ if (isNothing(previewArtwork)) {
+ return undefined;
+ }
+ let playbackControls = standardControls(objectGraph);
+ if (isSome(cardConfig.videoPlaybackControls)) {
+ playbackControls = cardConfig.videoPlaybackControls;
+ }
+ if (objectGraph.client.isMac) {
+ playbackControls.scrubber = true;
+ }
+ let autoplayPlaybackControls = autoPlayControls(objectGraph);
+ if (isSome(cardConfig.videoPlaybackControls)) {
+ autoplayPlaybackControls = cardConfig.videoAutoplayPlaybackControls;
+ }
+ const videoUrl = asString(mediaData, "video");
+ if (isNothing(videoUrl)) {
+ return undefined;
+ }
+ const video = new Video(videoUrl, previewArtwork, {
+ playbackControls: playbackControls,
+ autoPlayPlaybackControls: autoplayPlaybackControls,
+ canPlayFullScreen: cardConfig.enableFullScreenVideo,
+ });
+ const joeColors = joeColorSetFromData(previewArtworkData);
+ return {
+ artworks: [],
+ videos: [video],
+ artworkLayoutsWithMetrics: mediaConfigurationForCard.cardArtLayoutMetrics,
+ joeColors: joeColors,
+ };
+ }
+}
+function mediaSearchOrderForCardDisplayStyle(objectGraph, cardDisplayStyle, cardConfig) {
+ // Hero position, non-list card display styles, on iPad requires the UniversalA flavor artwork to be considered valid,
+ // so we'll only check those 2 configurations in this scenario.
+ const listCardDisplayStyles = new Set([TodayCardDisplayStyle.List, TodayCardDisplayStyle.NumberedList]);
+ if (objectGraph.client.isPad && cardConfig.isHeroCard && !listCardDisplayStyles.has(cardDisplayStyle)) {
+ return universalAConfigurations(objectGraph, cardConfig.isHeroCard);
+ }
+ switch (cardDisplayStyle) {
+ case TodayCardDisplayStyle.AppEventCard:
+ return [
+ storyCardMotion16x9Configuration(objectGraph),
+ storyCardStatic16x9Configuration(objectGraph),
+ storyCenteredMotion16x9Configuration(objectGraph),
+ storyCenteredStatic16x9Configuration(objectGraph),
+ ...universalAConfigurations(objectGraph, cardConfig.isHeroCard),
+ eventCardConfiguration(objectGraph),
+ ];
+ case TodayCardDisplayStyle.AppOfTheDay:
+ case TodayCardDisplayStyle.GameOfTheDay:
+ return [
+ storyCardMotion16x9Configuration(objectGraph),
+ storyCardStatic16x9Configuration(objectGraph),
+ storyCenteredMotion16x9Configuration(objectGraph),
+ storyCenteredStatic16x9Configuration(objectGraph),
+ ...universalAConfigurations(objectGraph, cardConfig.isHeroCard),
+ dayCardConfiguration(objectGraph),
+ ];
+ case TodayCardDisplayStyle.List:
+ case TodayCardDisplayStyle.NumberedList:
+ return [listCardMotion21x9Configuration(objectGraph), listCardStatic21x9Configuration(objectGraph)];
+ case TodayCardDisplayStyle.ShortImage:
+ if (cardConfig.isSearchContext) {
+ return [
+ generalCardConfiguration(objectGraph),
+ storyCardMotion16x9Configuration(objectGraph),
+ storyCardStatic16x9Configuration(objectGraph),
+ storyCenteredMotion16x9Configuration(objectGraph),
+ storyCenteredStatic16x9Configuration(objectGraph),
+ ...universalAConfigurations(objectGraph, cardConfig.isHeroCard),
+ storeFrontVideoConfiguration(objectGraph),
+ storeFrontVideo4x3Configuration(objectGraph),
+ mediaCardConfiguration(objectGraph),
+ ];
+ }
+ else {
+ return [
+ storyCardMotion16x9Configuration(objectGraph),
+ storyCardStatic16x9Configuration(objectGraph),
+ storyCenteredMotion16x9Configuration(objectGraph),
+ storyCenteredStatic16x9Configuration(objectGraph),
+ ...universalAConfigurations(objectGraph, cardConfig.isHeroCard),
+ storeFrontVideoConfiguration(objectGraph),
+ storeFrontVideo4x3Configuration(objectGraph),
+ generalCardConfiguration(objectGraph),
+ mediaCardConfiguration(objectGraph),
+ ];
+ }
+ case TodayCardDisplayStyle.FullBleedImage:
+ const fullBleedConfigurations = [
+ storyCardMotion16x9Configuration(objectGraph),
+ storyCardStatic16x9Configuration(objectGraph),
+ categoryDetailMotion16x9Configuration(objectGraph),
+ categoryDetailStatic16x9Configuration(objectGraph),
+ storyCenteredMotion16x9Configuration(objectGraph),
+ storyCenteredStatic16x9Configuration(objectGraph),
+ ...universalAConfigurations(objectGraph, cardConfig.isHeroCard),
+ storeFrontVideoConfiguration(objectGraph),
+ storeFrontVideo4x3Configuration(objectGraph),
+ mediaCardConfiguration(objectGraph),
+ generalCardConfiguration(objectGraph),
+ ];
+ if (objectGraph.client.isVision && isSome(cardConfig.isSearchContext) && cardConfig.isSearchContext) {
+ fullBleedConfigurations.unshift(storySearchStatic16x9Configuration(objectGraph));
+ }
+ return fullBleedConfigurations;
+ case TodayCardDisplayStyle.SingleApp:
+ case TodayCardDisplayStyle.Video:
+ default:
+ const configurations = [
+ storyCardMotion16x9Configuration(objectGraph),
+ storyCardStatic16x9Configuration(objectGraph),
+ storyCenteredMotion16x9Configuration(objectGraph),
+ storyCenteredStatic16x9Configuration(objectGraph),
+ ...universalAConfigurations(objectGraph, cardConfig.isHeroCard),
+ storeFrontVideoConfiguration(objectGraph),
+ storeFrontVideo4x3Configuration(objectGraph),
+ mediaCardConfiguration(objectGraph),
+ generalCardConfiguration(objectGraph),
+ ];
+ if (objectGraph.client.isVision && isSome(cardConfig.isSearchContext) && cardConfig.isSearchContext) {
+ configurations.unshift(storySearchStatic16x9Configuration(objectGraph));
+ }
+ return configurations;
+ }
+}
+function horizontalShelfMediaSearchOrderForCardDisplayStyle(objectGraph, cardConfig) {
+ const searchOrder = [];
+ if (objectGraph.client.isiOS || objectGraph.client.isWeb) {
+ searchOrder.push(storyCenteredStatic16x9Configuration(objectGraph, ["SCS.ApDPCS01"]));
+ }
+ if (cardConfig.horizontalCardContentType === "largeStoryCard") {
+ searchOrder.push(storeFrontVideoConfiguration(objectGraph));
+ }
+ const shouldUseHorizontalCardCrop = objectGraph.client.isiOS || objectGraph.client.isMac;
+ searchOrder.push(mediaCardConfiguration(objectGraph, shouldUseHorizontalCardCrop ? ["sr"] : undefined), generalCardConfiguration(objectGraph, shouldUseHorizontalCardCrop ? ["sr"] : undefined));
+ return searchOrder;
+}
+/**
+ * Watch uses a more straightforward artwork lookup than other platforms.
+ * @param objectGraph The AppStoreObjectGraph
+ * @param data MAPI data for a Today Card
+ * @param cardConfig The TodayCardConfiguration
+ * @returns TodayCardArtworkDetails appropriate for display on Apple Watch
+ */
+function todayCardArtworkDetailsForWatch(objectGraph, data, cardConfig) {
+ var _a, _b;
+ const mediaData = mediaAttributes.attributeAsDictionary(data, "editorialArtwork.subscriptionHero");
+ const artwork = artworkFromApiArtwork(objectGraph, mediaData, {
+ withJoeColorPlaceholder: true,
+ cropCode: (_b = (_a = cardConfig.prevailingCropCodes) === null || _a === void 0 ? void 0 : _a.defaultCrop) !== null && _b !== void 0 ? _b : "SH.ApHXS01",
+ useCase: 15 /* ArtworkUseCase.TodayCardMedia */,
+ });
+ const joeColors = joeColorSetFromData(mediaData);
+ // Don't return an array containing `undefined`
+ const artworks = isSome(artwork) ? [artwork] : [];
+ return {
+ artworks: artworks,
+ videos: [],
+ artworkLayoutsWithMetrics: [],
+ joeColors: joeColors,
+ };
+}
+//# sourceMappingURL=today-artwork-util.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-ad-card-builder.js b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-ad-card-builder.js
new file mode 100644
index 0000000..dfe9836
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-ad-card-builder.js
@@ -0,0 +1,232 @@
+import { isSome } from "@jet/environment";
+import * as validation from "@jet/environment/json/validation";
+import { MediumAdLockupWithScreenshotsBackground, CondensedAdLockupWithIconBackground, TodayCardMediaSingleLockup, TodayCardMediaMediumLockupWithScreenshots, TodayCardMediaMediumLockupWithAlignedRegion, MediumAdLockupWithAlignedRegionBackground, } from "../../../api/models";
+import { asString, isDefinedNonNull, isNull } from "../../../foundation/json-parsing/server-data";
+import { isColorEqualToColor } from "../../../foundation/util/color-util";
+import { todayAdStyle } from "../../ads/ad-common";
+import { recordLockupFromDataFailed } from "../../ads/ad-incident-recorder";
+import { iconFromData } from "../../content/content";
+import { getTemplateTypeForMediumAdFromLockupWithCustomCreative, getTemplateTypeForMediumAdFromLockupWithScreenshots, isCppDeeplinkEnabledForAdvert, performAdOverridesForCard, } from "../../lockups/ad-lockups";
+import * as lockups from "../../lockups/lockups";
+import * as metricsHelpersImpressions from "../../metrics/helpers/impressions";
+import { productVariantDataForData } from "../../product-page/product-page-variants";
+import { popTodayCardLocation, pushTodayCardLocation } from "../today-card-util";
+import { createTodayBaseCard } from "./today-base-card-builder";
+import { getSelectedCustomCreativeId } from "../../search/custom-creative";
+/**
+ * Create TodayCard displaying an ad fetched from Ad platforms
+ *
+ * @param objectGraph The dependency graph for the App Store
+ * @param adData The media api data to build the ad card from
+ * @param adIncidentRecorder The incident recorder for the ad, for when an issues arises
+ * @param cardConfig The configuration for the card
+ * @param context The parse context for the over all today page
+ * @returns The newly created TodayCard, using the full bleed image media.
+ */
+export function createTodayAdCard(objectGraph, adData, adIncidentRecorder, cardConfig, context) {
+ return validation.context("createTodayAdCard", () => {
+ var _a, _b, _c, _d;
+ if (isNull(adData)) {
+ return null; // No task for the position
+ }
+ const adCard = createTodayBaseCard(objectGraph, adData, cardConfig, context);
+ pushTodayCardLocation(objectGraph, adData, cardConfig, context, asString(adData.attributes.name));
+ const metricsOptions = metricsHelpersImpressions.impressionOptions(objectGraph, adData, asString(adData.attributes.name), {
+ targetType: "todayCard",
+ pageInformation: context.pageInformation,
+ locationTracker: context.locationTracker,
+ isAdvert: true,
+ rowIndex: cardConfig.currentRowIndex,
+ displayStyle: cardConfig.metricsDisplayStyle,
+ });
+ const productVariantData = productVariantDataForData(objectGraph, adData);
+ metricsOptions.productVariantData = productVariantData;
+ metricsOptions.adSlotOverride = context.parsedCardCount;
+ metricsOptions.kind = "adItem";
+ const clickOptions = metricsOptions;
+ // Set up iAdInfo
+ metricsOptions.pageInformation.iAdInfo.apply(objectGraph, adData);
+ // Configure the action
+ let clientIdentifierOverride;
+ if (isDefinedNonNull(cardConfig)) {
+ clientIdentifierOverride = cardConfig.clientIdentifierOverride;
+ }
+ const customCreativeId = getSelectedCustomCreativeId(adData);
+ const isCustomCreative = isSome(customCreativeId);
+ adCard.style = "dark";
+ switch (todayAdStyle(objectGraph)) {
+ case "singleLockup":
+ // Hardcode the condensed ad template type. This is not dependent on media count.
+ (_a = metricsOptions.pageInformation.iAdInfo) === null || _a === void 0 ? void 0 : _a.setTemplateType("APPLOCKUP");
+ break;
+ case "mediumLockup":
+ if (isCustomCreative) {
+ (_b = metricsOptions.pageInformation.iAdInfo) === null || _b === void 0 ? void 0 : _b.setTemplateType(getTemplateTypeForMediumAdFromLockupWithCustomCreative());
+ }
+ break;
+ default:
+ break;
+ }
+ let lockup = createAdCardLockup(objectGraph, adData, cardConfig, context);
+ if (isNull(lockup)) {
+ recordLockupFromDataFailed(objectGraph, adIncidentRecorder, adData);
+ popTodayCardLocation(context);
+ // Configure impressions
+ metricsHelpersImpressions.addImpressionsFieldsToTodayCard(objectGraph, adCard, metricsOptions, null, null, false, false);
+ return null;
+ }
+ if (objectGraph.props.enabled("advertSlotReporting")) {
+ (_c = lockup.searchAdOpportunity) === null || _c === void 0 ? void 0 : _c.setTemplateType("APPLOCKUP");
+ }
+ else {
+ (_d = lockup.searchAd) === null || _d === void 0 ? void 0 : _d.setTemplateType("APPLOCKUP");
+ }
+ switch (todayAdStyle(objectGraph)) {
+ case "singleLockup":
+ const condensedAdLockupWithIconBackground = new CondensedAdLockupWithIconBackground(lockup, lockup.icon);
+ adCard.media = new TodayCardMediaSingleLockup(condensedAdLockupWithIconBackground);
+ adCard.media.impressionMetrics = lockup.impressionMetrics;
+ break;
+ case "mediumLockup":
+ if (preprocessor.CARRY_BUILD || preprocessor.DEBUG_BUILD) {
+ if (isCustomCreative && objectGraph.featureFlags.isEnabled("aligned_region_artwork_2025A")) {
+ adCard.media = createMediumLockupWithAlignedRegion(objectGraph, lockup, adData, metricsOptions.pageInformation);
+ // After the media is created, we need to recreate the lockup to ensure all instrumentation
+ // has the updated template type, which is derived from the final set of media in
+ // `createMediumLockupWithAlignedRegion`.
+ lockup = createAdCardLockup(objectGraph, adData, cardConfig, context);
+ // Preserve SearchAd metadata
+ lockup.searchAdOpportunity = adCard.media.mediumAdLockupWithAlignedRegionBackground.lockup.searchAdOpportunity;
+ lockup.searchAd = adCard.media.mediumAdLockupWithAlignedRegionBackground.lockup.searchAd;
+ // The new lockup with the correct metrics should be set on the media.
+ adCard.media.mediumAdLockupWithAlignedRegionBackground.lockup = lockup;
+ adCard.media.impressionMetrics = lockup.impressionMetrics;
+ break;
+ }
+ else {
+ adCard.media = createMediumScreenshotsCardMedia(adCard, objectGraph, lockup, adData, cardConfig, context, metricsOptions);
+ }
+ }
+ else {
+ adCard.media = createMediumScreenshotsCardMedia(adCard, objectGraph, lockup, adData, cardConfig, context, metricsOptions);
+ }
+ break;
+ default:
+ return null;
+ }
+ popTodayCardLocation(context);
+ // CPP deeplinks used for ads have a requirement to not be used when targeting criteria is too narrow.
+ const isCppDeeplinkingEnabled = isCppDeeplinkEnabledForAdvert(adData);
+ // It's important the click action is created after the template type is set, to ensure it's in the metrics.
+ adCard.clickAction = lockups.actionFromData(objectGraph, adData, clickOptions, clientIdentifierOverride, undefined, isCppDeeplinkingEnabled);
+ performAdOverridesForCard(objectGraph, adData, adCard, metricsOptions);
+ // Configure impressions
+ metricsHelpersImpressions.addImpressionsFieldsToTodayCard(objectGraph, adCard, metricsOptions, null, null, false, false);
+ return adCard;
+ });
+}
+function createMediumScreenshotsCardMedia(adCard, objectGraph, lockup, adData, cardConfig, context, metricsOptions) {
+ adCard.media = createMediumLockupWithScreenshots(objectGraph, lockup, objectGraph.bag.todayAdMediumLockupScreenshotAnimationEnabled, metricsOptions.pageInformation, adData);
+ // After the media is created, we need to recreate the lockup to ensure all instrumentation
+ // has the updated template type, which is derived from the final set of media in
+ // `createMediumLockupWithScreenshots`.
+ lockup = createAdCardLockup(objectGraph, adData, cardConfig, context);
+ if (isNull(lockup)) {
+ return null;
+ }
+ // Preserve SearchAd metadata
+ lockup.searchAdOpportunity = adCard.media.mediumAdLockupWithScreenshotsBackground.lockup.searchAdOpportunity;
+ lockup.searchAd = adCard.media.mediumAdLockupWithScreenshotsBackground.lockup.searchAd;
+ // The new lockup with the correct metrics should be set on the media.
+ adCard.media.mediumAdLockupWithScreenshotsBackground.lockup = lockup;
+ adCard.media.impressionMetrics = lockup.impressionMetrics;
+ return adCard.media;
+}
+function createAdCardLockup(objectGraph, data, cardConfig, context) {
+ const offerEnvironment = "ad";
+ const offerStyle = "transparent";
+ return lockups.mixedMediaAdLockupFromData(objectGraph, data, {
+ offerEnvironment: offerEnvironment,
+ offerStyle: offerStyle,
+ metricsOptions: {
+ pageInformation: context.pageInformation,
+ locationTracker: context.locationTracker,
+ isAdvert: true,
+ adSlotOverride: context.parsedCardCount,
+ disableFastImpressionsForAds: true,
+ },
+ clientIdentifierOverride: cardConfig.clientIdentifierOverride,
+ crossLinkSubtitle: cardConfig.crossLinkSubtitle,
+ artworkUseCase: 1 /* ArtworkUseCase.LockupIconSmall */,
+ canDisplayArcadeOfferButton: cardConfig.canDisplayArcadeOfferButton,
+ }, {
+ allowsAutoPlay: true,
+ looping: true,
+ canPlayFullScreen: false,
+ playbackControls: {},
+ }, null, false);
+}
+function createMediumLockupWithAlignedRegion(objectGraph, lockup, adData, pageInformation) {
+ var _a, _b, _c;
+ const alignedRegionArtwork = lockup.alignedRegionArtwork;
+ const templateString = getTemplateTypeForMediumAdFromLockupWithCustomCreative();
+ (_a = pageInformation === null || pageInformation === void 0 ? void 0 : pageInformation.iAdInfo) === null || _a === void 0 ? void 0 : _a.setTemplateType(templateString);
+ if (objectGraph.props.enabled("advertSlotReporting")) {
+ (_b = lockup.searchAdOpportunity) === null || _b === void 0 ? void 0 : _b.setTemplateType(templateString);
+ }
+ else {
+ (_c = lockup.searchAd) === null || _c === void 0 ? void 0 : _c.setTemplateType(templateString);
+ }
+ const mediumContainer = new MediumAdLockupWithAlignedRegionBackground(lockup, alignedRegionArtwork);
+ return new TodayCardMediaMediumLockupWithAlignedRegion(mediumContainer);
+}
+/**
+ * Creates the media for a medium Today ad, with the appropriate media.
+ * Also applies information about the created media template to a provided MetricsPageInformation.
+ * @param objectGraph The object graph.
+ * @param lockup The lockup for the app.
+ * @param isAnimated Whether the card background should be animated.
+ * @param pageInformation A MetricsPageInformation to apply the created template metrics information to.
+ * @param adData The data for the ad.
+ * @returns A `TodayCardMediumLockupWithScreenshots` with the correct media to be displayed.
+ */
+function createMediumLockupWithScreenshots(objectGraph, lockup, isAnimated, pageInformation, adData) {
+ var _a, _b, _c;
+ // Grab the first of the platform screenshots and trailers - the ones the ad will display.
+ const platformScreenshots = lockup.screenshots[0];
+ const templateString = getTemplateTypeForMediumAdFromLockupWithScreenshots(platformScreenshots);
+ (_a = pageInformation.iAdInfo) === null || _a === void 0 ? void 0 : _a.setTemplateType(templateString);
+ if (objectGraph.props.enabled("advertSlotReporting")) {
+ (_b = lockup.searchAdOpportunity) === null || _b === void 0 ? void 0 : _b.setTemplateType(templateString);
+ }
+ else {
+ (_c = lockup.searchAd) === null || _c === void 0 ? void 0 : _c.setTemplateType(templateString);
+ }
+ const iconData = iconFromData(objectGraph, adData, {
+ useCase: 0 /* ArtworkUseCase.Default */,
+ withJoeColorPlaceholder: true,
+ overrideTextColorKey: "textColor2",
+ });
+ let backgroundColor = iconData.backgroundColor;
+ let secondaryTextColor = iconData.textColor;
+ const rgbWhite = {
+ type: "rgb",
+ red: 1,
+ green: 1,
+ blue: 1,
+ };
+ // If `backgroundColor` is white, use `secondaryTextColor` as the "primary" color.
+ // Otherwise, if `secondaryTextColor` is white, remove it and use only `backgroundColor`.
+ // Native code has a fallback state where only `backgroundColor` is provided, so if
+ // either color is white, we prefer the single non-white color.
+ if (isColorEqualToColor(rgbWhite, backgroundColor)) {
+ backgroundColor = secondaryTextColor;
+ secondaryTextColor = undefined;
+ }
+ else if (isColorEqualToColor(rgbWhite, secondaryTextColor)) {
+ secondaryTextColor = undefined;
+ }
+ const mediumContainer = new MediumAdLockupWithScreenshotsBackground(lockup, [platformScreenshots], isAnimated, secondaryTextColor, backgroundColor, 8);
+ return new TodayCardMediaMediumLockupWithScreenshots(mediumContainer);
+}
+//# sourceMappingURL=today-ad-card-builder.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-app-event-card-builder.js b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-app-event-card-builder.js
new file mode 100644
index 0000000..c030a5a
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-app-event-card-builder.js
@@ -0,0 +1,112 @@
+import { isSome } from "@jet/environment";
+import * as validation from "@jet/environment/json/validation";
+import { isNothing } from "@jet/environment/types/optional";
+import { TodayCardLockupOverlay, TodayCardMediaAppEvent, } from "../../../api/models";
+import { asBooleanOrFalse } from "../../../foundation/json-parsing/server-data";
+import * as mediaAttributes from "../../../foundation/media/attributes";
+import * as mediaRelationship from "../../../foundation/media/relationships";
+import * as color from "../../../foundation/util/color-util";
+import { appEventOrPromotionStartDateFromData } from "../../app-promotions/app-event";
+import { appEventsAreEnabled } from "../../app-promotions/app-promotions-common";
+import { addNextPreferredContentRefreshDate } from "../../refresh/page-refresh-controller";
+import { todayCardArtworkDetails } from "../artwork/today-artwork-util";
+import { applyTodayCardOverridesForAcquisitionStoryIfNecessary } from "../today-card-overlay-util";
+import { cardDisplayStyleFromData, cardStyleFromJoeColorsWithoutFallback, offerEnvironmentForTodayCard, offerStyleForTodayCard, popTodayCardLocation, pushTodayCardLocation, } from "../today-card-util";
+import { createTodayBaseCard } from "./today-base-card-builder";
+/**
+ * Create TodayCard displaying an app event
+ *
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data to build the card from
+ * @param cardConfig The configuration for the card
+ * @param context The parse context for the over all today page
+ * @param augmentingData that stores some additional responses that may be used to enhance the contents of `data`
+ * @returns The newly created TodayCard, using a app event media.
+ */
+export function createTodayAppEventCard(objectGraph, data, cardConfig, context, augmentingData) {
+ return validation.context("createTodayAppEventCard", () => {
+ var _a, _b, _c, _d, _e;
+ if (!appEventsAreEnabled(objectGraph)) {
+ return null;
+ }
+ const appEventData = appEventDataFromData(objectGraph, data);
+ if (isNothing(appEventData)) {
+ return null;
+ }
+ const appEventCard = createTodayBaseCard(objectGraph, data, cardConfig, context, (clickOptions) => {
+ // Add app event ID to click options
+ clickOptions.inAppEventId = appEventData.id;
+ const parentAppData = mediaRelationship.relationshipData(objectGraph, appEventData, "app");
+ if (isSome(parentAppData)) {
+ clickOptions.relatedSubjectIds = [parentAppData.id];
+ }
+ });
+ if (isSome(appEventCard.editorialDisplayOptions)) {
+ appEventCard.editorialDisplayOptions.useMaterialBlur = true;
+ }
+ pushTodayCardLocation(objectGraph, data, cardConfig, context);
+ // Artwork
+ const mediaDetails = todayCardArtworkDetails(objectGraph, data, cardConfig);
+ if (isNothing(mediaDetails)) {
+ popTodayCardLocation(context);
+ return null;
+ }
+ // Tint and styling
+ const tintColor = (_b = (_a = mediaDetails.joeColors) === null || _a === void 0 ? void 0 : _a.textColor4) !== null && _b !== void 0 ? _b : color.black;
+ const artworkBackgroundColor = (_d = (_c = mediaDetails.joeColors) === null || _c === void 0 ? void 0 : _c.bgColor) !== null && _d !== void 0 ? _d : color.black;
+ const blurStyle = color.isDarkColor(artworkBackgroundColor) ? "dark" : "light";
+ // App event
+ const metricsOptions = {
+ pageInformation: context.pageInformation,
+ locationTracker: context.locationTracker,
+ targetType: "eventModule",
+ };
+ // Card style
+ appEventCard.style =
+ (_e = cardStyleFromJoeColorsWithoutFallback(mediaDetails.joeColors, "textColor4")) !== null && _e !== void 0 ? _e : (color.isDarkColor(tintColor) ? "dark" : "light");
+ // Offer style
+ const offerStyle = offerStyleForTodayCard(objectGraph, appEventCard.style);
+ const editorialKind = mediaAttributes.attributeAsString(data, "label");
+ const appEventOrDate = appEventOrPromotionStartDateFromData(objectGraph, appEventData, null, false, false, offerEnvironmentForTodayCard(appEventCard.style), offerStyle, true, metricsOptions, true, true, editorialKind, false, asBooleanOrFalse(cardConfig.allowUnpublishedAppEventPreviews));
+ // Return early if we received a Date, as this means the App Event shouldn't be shown yet.
+ if (isNothing(appEventOrDate) || appEventOrDate instanceof Date) {
+ addNextPreferredContentRefreshDate(appEventOrDate, context.refreshController);
+ popTodayCardLocation(context);
+ return null;
+ }
+ const appEvent = appEventOrDate;
+ // Card
+ appEventCard.media = new TodayCardMediaAppEvent(appEvent.formattedDates, appEvent.startDate, tintColor, mediaDetails.artworks, mediaDetails.videos, mediaDetails.artworkLayoutsWithMetrics, blurStyle);
+ if (isSome(appEvent.lockup)) {
+ appEventCard.overlay = new TodayCardLockupOverlay(appEvent.lockup);
+ }
+ // Special post-processing step for Acquisition story cards.
+ // This is needed to splice in data not included in initial response.
+ applyTodayCardOverridesForAcquisitionStoryIfNecessary(objectGraph, appEventCard, cardConfig, cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle), data, augmentingData, context);
+ popTodayCardLocation(context);
+ return appEventCard;
+ });
+}
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data to build the card from
+ * @returns The app event relationship data for this card
+ */
+function appEventDataFromData(objectGraph, data) {
+ let appEventData;
+ // Primary content
+ const primaryContent = mediaRelationship.relationshipCollection(data, "primary-content");
+ if (primaryContent.length > 0) {
+ appEventData = primaryContent[0];
+ }
+ else {
+ // Card contents
+ const cardContents = mediaRelationship.relationshipCollection(data, "card-contents");
+ if (cardContents.length === 0) {
+ return null;
+ }
+ appEventData = cardContents[0];
+ }
+ return appEventData;
+}
+//# sourceMappingURL=today-app-event-card-builder.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-base-card-builder.js b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-base-card-builder.js
new file mode 100644
index 0000000..77bd0de
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-base-card-builder.js
@@ -0,0 +1,244 @@
+import * as validation from "@jet/environment/json/validation";
+import { isNothing, isSome } from "@jet/environment/types/optional";
+import { ExternalUrlAction, TodayCard, TodayCardActionOverlay, TodayCardMediaArtwork, } from "../../../api/models";
+import { isNull } from "../../../foundation/json-parsing/server-data";
+import * as mediaAttributes from "../../../foundation/media/attributes";
+import { relationshipCollection } from "../../../foundation/media/relationships";
+import * as contentAttributes from "../../content/attributes";
+import { editorialNotesFromData, notesFromData } from "../../content/content";
+import { extractEditorialClientParams } from "../../editorial-pages/editorial-data-util";
+import { editorialItemActionFromData, subtitleFromData } from "../../lockups/lockups";
+import * as metricsHelpersImpressions from "../../metrics/helpers/impressions";
+import { addMetricsEventsToVideo } from "../../metrics/helpers/media";
+import { todayCardArtworkDetails } from "../artwork/today-artwork-util";
+import { isCardDataOnboardingCard } from "../onboarding-cards";
+import { brandedTitleArtworkForCard, cardDisplayStyleFromData, cardStyleFromJoeColors, isCardOTDIntention, relatedCardContentsContentsFromData, todayCardArtworkTitleBackingGradientForKey, todayCardEditorialDisplayOptionsFromData, todayCardMetricsOptions, } from "../today-card-util";
+import { recoMetricsFromTodayItem } from "../today-parse-util";
+import { TodayCardDisplayStyle } from "../today-types";
+/**
+ * If the title is not supplied for app of the day we use this
+ */
+const appOfTheDayFallbackTitle = "FEATURED APP";
+/**
+ * If the title is not supplied for game of the day we use this
+ */
+const gameOfTheDayFallbackTitle = "FEATURED GAME";
+/**
+ * Creates a TodayCard used as the base for all other TodayCards
+ *
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data to build the card from
+ * @param cardConfig The configuration for the card
+ * @param context The parse context for the over all today page
+ * @param clickOptionsModifier A function that can modify the click options for the card
+ * @returns The newly created TodayCard, which can then be modified by other TodayCard builders
+ */
+export function createTodayBaseCard(objectGraph, data, cardConfig, context, clickOptionsModifier) {
+ return validation.context("createTodayBaseCard", () => {
+ const baseCard = new TodayCard();
+ const cardDisplayStyle = cardDisplayStyleFromData(data, cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.coercedCollectionTodayCardDisplayStyle);
+ // Heading
+ const heading = cardHeadingFromData(objectGraph, data, cardDisplayStyle, cardConfig);
+ baseCard.heading = heading;
+ // Title, title artwork
+ const title = cardTitleFromData(objectGraph, data, cardConfig);
+ baseCard.title = title;
+ const shortTitle = contentAttributes.contentAttributeAsString(objectGraph, data, [
+ "shortEditorialNotes",
+ "name",
+ ]);
+ baseCard.shortTitle = shortTitle;
+ const cardTitleArtwork = brandedTitleArtworkForCard(objectGraph, data);
+ baseCard.titleArtwork = cardTitleArtwork;
+ // Description
+ const inlineDescription = cardDescriptionFromData(objectGraph, data);
+ baseCard.inlineDescription = inlineDescription;
+ const metricsOptions = todayCardMetricsOptions(objectGraph, data, cardConfig, context, title);
+ metricsOptions.adSlotOverride = context.parsedCardCount;
+ baseCard.clickAction = cardClickAction(objectGraph, data, cardDisplayStyle, cardConfig, context, metricsOptions, clickOptionsModifier);
+ // Configure impressions
+ metricsHelpersImpressions.addImpressionsFieldsToTodayCard(objectGraph, baseCard, metricsOptions, heading, cardDisplayStyle, isCardDataOnboardingCard(objectGraph, data));
+ baseCard.editorialDisplayOptions = todayCardEditorialDisplayOptionsFromData(objectGraph, data, cardConfig);
+ return baseCard;
+ });
+}
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param cardDisplayStyle The display style of the card
+ * @param data The data to get the branded single app overlay heading from
+ * @param cardConfig The configuration for the card
+ * @returns The heading to use on the branded single app overlay
+ */
+export function cardHeadingFromData(objectGraph, data, cardDisplayStyle, cardConfig) {
+ var _a, _b;
+ let heading = null;
+ if (cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.useOTDTextStyle) {
+ if (cardConfig.isHorizontalShelfContext) {
+ // We don't support branded images or titles for a cards in a horizontal shelf
+ // so want to show the "APP OF THE DAY" / "GAME OF THE DAY" text as the heading instead.
+ heading = mediaAttributes.attributeAsString(data, "label");
+ }
+ else {
+ heading = null;
+ }
+ }
+ else if (isCardOTDIntention(data, cardConfig)) {
+ heading =
+ (_a = mediaAttributes.attributeAsString(data, "alternateLabel")) !== null && _a !== void 0 ? _a : mediaAttributes.attributeAsString(data, "label");
+ if (isNull(heading) && cardDisplayStyle === TodayCardDisplayStyle.AppOfTheDay) {
+ heading = appOfTheDayFallbackTitle;
+ }
+ else if (isNull(heading) && cardDisplayStyle === TodayCardDisplayStyle.GameOfTheDay) {
+ heading = gameOfTheDayFallbackTitle;
+ }
+ }
+ else {
+ switch (cardDisplayStyle) {
+ case TodayCardDisplayStyle.AppEventCard:
+ const fallbackCardContents = fallbackCardContentFromData(objectGraph, data);
+ if (isSome(fallbackCardContents)) {
+ heading =
+ (_b = editorialNotesFromData(objectGraph, data, "badge", true)) !== null && _b !== void 0 ? _b : mediaAttributes.attributeAsString(fallbackCardContents, "kind");
+ }
+ break;
+ default:
+ heading = mediaAttributes.attributeAsString(data, "label");
+ break;
+ }
+ }
+ return heading;
+}
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data used to determine the title of this card
+ * @param cardConfig The configuration for the card
+ * @returns The title to use for this card
+ */
+function cardTitleFromData(objectGraph, data, cardConfig) {
+ var _a;
+ let title = null;
+ if (cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.useOTDTextStyle) {
+ title =
+ (_a = mediaAttributes.attributeAsString(data, "ofTheDayLabel")) !== null && _a !== void 0 ? _a : mediaAttributes.attributeAsString(data, "label");
+ // We need to replace \n characters with <br> tags to ensure the
+ // newlines in APP OF THE DAY titles are maintained. The pattern must be a RegExp since string patterns
+ // only replace the first occurrence.
+ if (isSome(title)) {
+ title = title.replace(/\n/g, "<br>");
+ }
+ }
+ if (isNothing(title)) {
+ title = notesFromData(objectGraph, data, "name", true);
+ }
+ if (isNothing(title)) {
+ // Lastly fallback to the first related content title
+ const fallbackCardContents = fallbackCardContentFromData(objectGraph, data);
+ title = isSome(fallbackCardContents) ? mediaAttributes.attributeAsString(fallbackCardContents, "name") : null;
+ }
+ return title;
+}
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data used to determine the description of this card
+ * @returns The title to use for this card
+ */
+function cardDescriptionFromData(objectGraph, data) {
+ var _a;
+ const editorialClientParams = extractEditorialClientParams(objectGraph, data);
+ const ignoreShortNotes = mediaAttributes.attributeAsBooleanOrFalse(data, "ignoreITunesShortNotes");
+ if (ignoreShortNotes || editorialClientParams.suppressNoteShort) {
+ return null;
+ }
+ let description = notesFromData(objectGraph, data, "short", true);
+ if (isNothing(description) && !editorialClientParams.suppressNoteTagline) {
+ // Lastly fallback to the first related content title
+ const fallbackCardContents = fallbackCardContentFromData(objectGraph, data);
+ if (isSome(fallbackCardContents)) {
+ description =
+ (_a = notesFromData(objectGraph, fallbackCardContents, "tagline")) !== null && _a !== void 0 ? _a : subtitleFromData(objectGraph, fallbackCardContents);
+ }
+ }
+ return description;
+}
+/**
+ *
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data used to build the card
+ * @returns
+ */
+function fallbackCardContentFromData(objectGraph, data) {
+ const primaryContent = relationshipCollection(data, "primary-content");
+ if ((primaryContent === null || primaryContent === void 0 ? void 0 : primaryContent.length) === 1) {
+ return primaryContent[0];
+ }
+ // Card contents
+ const cardContents = relatedCardContentsContentsFromData(objectGraph, data);
+ if (cardContents.length === 1) {
+ return cardContents[0];
+ }
+ return null;
+}
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data used to build the card this action is for
+ * @param cardDisplayStyle The display style of the card
+ * @param cardConfig The configuration for the card
+ * @param context The parse context for the over all today page
+ * @param baseMetricsOptions The base metrics options for the card
+ * @returns The click action used for tapping on the card
+ */
+function cardClickAction(objectGraph, data, cardDisplayStyle, cardConfig, context, baseMetricsOptions, clickOptionsModifier) {
+ var _a;
+ const clickOptions = baseMetricsOptions;
+ const franchise = mediaAttributes.attributeAsString(data, "label");
+ // <rdar://problem/33677354> Metrics: JS: Add franchise/label to todayCard click events
+ const actionDetails = {
+ cardType: cardDisplayStyle,
+ franchise: franchise,
+ };
+ const isOnboardingCard = isCardDataOnboardingCard(objectGraph, data);
+ if (isOnboardingCard) {
+ actionDetails["isOnboardingCard"] = isOnboardingCard;
+ }
+ clickOptions["actionDetails"] = actionDetails;
+ if (isSome(clickOptionsModifier)) {
+ clickOptionsModifier(clickOptions);
+ }
+ return editorialItemActionFromData(objectGraph, data, clickOptions, (_a = cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.clientIdentifierOverride) !== null && _a !== void 0 ? _a : objectGraph.host.clientIdentifier, recoMetricsFromTodayItem(context.currentTodayItem), cardConfig);
+}
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data used to build the base card
+ * @param baseCard The base card to add the artwork media to
+ * @param cropCode The crop code to use for the artwork
+ * @param context The context for the page were creating the card for
+ * @returns Whether or not we successfully added artwork media to the base card
+ */
+export function addArtworkMediaToBaseCard(objectGraph, data, baseCard, cardConfig, context) {
+ return validation.context("addArtworkMediaToBaseCard", () => {
+ const ignoreEditorialArt = mediaAttributes.attributeAsBooleanOrFalse(data, "ignoreEditorialArt");
+ if (ignoreEditorialArt) {
+ return false;
+ }
+ const mediaDetails = todayCardArtworkDetails(objectGraph, data, cardConfig);
+ if (isNothing(mediaDetails)) {
+ return false;
+ }
+ if (isSome(context)) {
+ addMetricsEventsToVideo(objectGraph, mediaDetails === null || mediaDetails === void 0 ? void 0 : mediaDetails.videos[0], {
+ pageInformation: context.pageInformation,
+ locationTracker: context.locationTracker,
+ id: data.id,
+ });
+ }
+ baseCard.media = new TodayCardMediaArtwork(mediaDetails.artworks, mediaDetails.videos, mediaDetails.artworkLayoutsWithMetrics, todayCardArtworkTitleBackingGradientForKey(objectGraph, data, cardConfig));
+ baseCard.style = cardStyleFromJoeColors(mediaDetails.joeColors, "bgColor");
+ // External URL
+ if (baseCard.clickAction instanceof ExternalUrlAction && objectGraph.client.isiOS) {
+ baseCard.overlay = new TodayCardActionOverlay(baseCard.clickAction);
+ baseCard.style = "white";
+ }
+ return true;
+ });
+}
+//# sourceMappingURL=today-base-card-builder.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-branded-card-builder.js b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-branded-card-builder.js
new file mode 100644
index 0000000..9088bfa
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-branded-card-builder.js
@@ -0,0 +1,58 @@
+import * as validation from "@jet/environment/json/validation";
+import { isSome } from "@jet/environment/types/optional";
+import { TodayCardMediaBrandedSingleApp } from "../../../api/models";
+import { isNullOrEmpty } from "../../../foundation/json-parsing/server-data";
+import { deepLinkUrlFromData } from "../../linking/external-deep-link";
+import { todayCardArtworkDetails } from "../artwork/today-artwork-util";
+import { applyTodayCardOverridesForAcquisitionStoryIfNecessary, relatedContentOverlayFromData, } from "../today-card-overlay-util";
+import { cardDisplayStyleFromData, cardStyleFromJoeColors, lockupsForRelatedContent, offerEnvironmentForTodayCard, offerStyleForTodayCard, popTodayCardLocation, pushTodayCardLocation, relatedCardContentsContentsFromData, todayCardArtworkTitleBackingGradientForKey, } from "../today-card-util";
+import { createTodayBaseCard } from "./today-base-card-builder";
+import { createTodaySingleAppCard } from "./today-single-app-card-builder";
+/**
+ * Create TodayCard displaying the App of the Day and Game of the Day editorial items
+ *
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data to build the card from
+ * @param cardConfig The configuration for the card
+ * @param context The parse context for the over all today page
+ * @param augmentingData that stores some additional responses that may be used to enhance the contents of `data`
+ * @returns The newly created TodayCard, using an artwork media to display the App of the Day and Game of the Day
+ */
+export function createTodayBrandedCard(objectGraph, data, cardConfig, context, augmentingData) {
+ return validation.context("createTodayBrandedCard", () => {
+ const brandedCard = createTodayBaseCard(objectGraph, data, cardConfig, context);
+ pushTodayCardLocation(objectGraph, data, cardConfig, context);
+ // Configure the card style
+ const mediaDetails = todayCardArtworkDetails(objectGraph, data, cardConfig);
+ if (isSome(mediaDetails) &&
+ isSome(mediaDetails.joeColors.bgColor) &&
+ (mediaDetails.artworks.length > 0 || mediaDetails.videos.length > 0)) {
+ brandedCard.style = cardStyleFromJoeColors(mediaDetails === null || mediaDetails === void 0 ? void 0 : mediaDetails.joeColors, "bgColor");
+ }
+ else {
+ // If we don't have artwork, try building a single app card instead.
+ popTodayCardLocation(context);
+ return createTodaySingleAppCard(objectGraph, data, cardConfig, context);
+ }
+ const offerStyle = offerStyleForTodayCard(objectGraph, brandedCard.style);
+ const relatedContent = relatedCardContentsContentsFromData(objectGraph, data);
+ const relatedContentLockups = lockupsForRelatedContent(objectGraph, relatedContent, cardConfig, context.pageInformation, context.locationTracker, offerEnvironmentForTodayCard(brandedCard.style), offerStyle, deepLinkUrlFromData(objectGraph, data), true);
+ if (isNullOrEmpty(relatedContentLockups) || relatedContentLockups.length !== 1) {
+ popTodayCardLocation(context);
+ return null;
+ }
+ if (!cardConfig.isHorizontalShelfContext) {
+ brandedCard.overlay = relatedContentOverlayFromData(objectGraph, brandedCard, cardConfig, data, relatedContentLockups);
+ }
+ // Configure the card media
+ const brandedAppLockup = relatedContentLockups[0];
+ brandedCard.media = new TodayCardMediaBrandedSingleApp(brandedAppLockup.icon, mediaDetails.artworks, mediaDetails.videos, mediaDetails.artworkLayoutsWithMetrics, todayCardArtworkTitleBackingGradientForKey(objectGraph, data, cardConfig));
+ brandedCard.media.impressionMetrics = brandedAppLockup.impressionMetrics;
+ // Special post-processing step for Acquisition story cards.
+ // This is needed to splice in data not included in initial response.
+ applyTodayCardOverridesForAcquisitionStoryIfNecessary(objectGraph, brandedCard, cardConfig, cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle), data, augmentingData, context);
+ popTodayCardLocation(context);
+ return brandedCard;
+ });
+}
+//# sourceMappingURL=today-branded-card-builder.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-full-bleed-image-card-builder.js b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-full-bleed-image-card-builder.js
new file mode 100644
index 0000000..86cacec
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-full-bleed-image-card-builder.js
@@ -0,0 +1,37 @@
+import * as validation from "@jet/environment/json/validation";
+import { deepLinkUrlFromData } from "../../linking/external-deep-link";
+import { applyTodayCardOverridesForAcquisitionStoryIfNecessary, relatedContentOverlayFromData, } from "../today-card-overlay-util";
+import { cardDisplayStyleFromData, lockupsForRelatedContent, offerEnvironmentForTodayCard, offerStyleForTodayCard, popTodayCardLocation, pushTodayCardLocation, relatedCardContentsContentsFromData, } from "../today-card-util";
+import { addArtworkMediaToBaseCard, createTodayBaseCard } from "./today-base-card-builder";
+/**
+ * Create TodayCard displaying the full bleed image
+ *
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data to build the card from
+ * @param cardConfig The configuration for the card
+ * @param context The parse context for the over all today page
+ * @param augmentingData that stores some additional responses that may be used to enhance the contents of `data`
+ * @returns The newly created TodayCard, using the full bleed image media.
+ */
+export function createTodayFullBleedImageCard(objectGraph, data, cardConfig, context, augmentingData) {
+ return validation.context("createTodayFullBleedImageCard", () => {
+ const fullBleedImageCard = createTodayBaseCard(objectGraph, data, cardConfig, context);
+ pushTodayCardLocation(objectGraph, data, cardConfig, context);
+ const didAddArtworkToCard = addArtworkMediaToBaseCard(objectGraph, data, fullBleedImageCard, cardConfig, context);
+ if (!didAddArtworkToCard) {
+ popTodayCardLocation(context);
+ return null;
+ }
+ const relatedContent = relatedCardContentsContentsFromData(objectGraph, data);
+ const offerStyle = offerStyleForTodayCard(objectGraph, fullBleedImageCard.style);
+ const relatedContentLockups = lockupsForRelatedContent(objectGraph, relatedContent, cardConfig, context.pageInformation, context.locationTracker, offerEnvironmentForTodayCard(fullBleedImageCard.style), offerStyle, deepLinkUrlFromData(objectGraph, data));
+ const overlay = relatedContentOverlayFromData(objectGraph, fullBleedImageCard, cardConfig, data, relatedContentLockups);
+ fullBleedImageCard.overlay = overlay;
+ // Special post-processing step for Acquisition story cards.
+ // This is needed to splice in data not included in initial response.
+ applyTodayCardOverridesForAcquisitionStoryIfNecessary(objectGraph, fullBleedImageCard, cardConfig, cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle), data, augmentingData, context);
+ popTodayCardLocation(context);
+ return fullBleedImageCard;
+ });
+}
+//# sourceMappingURL=today-full-bleed-image-card-builder.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-in-app-purchase-card-builder.js b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-in-app-purchase-card-builder.js
new file mode 100644
index 0000000..d326c5c
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-in-app-purchase-card-builder.js
@@ -0,0 +1,62 @@
+import * as validation from "@jet/environment/json/validation";
+import { TodayCardInAppPurchase } from "../../../api/models";
+import { isNullOrEmpty } from "../../../foundation/json-parsing/server-data";
+import { inAppPurchaseLockupFromData } from "../../lockups/lockups";
+import { applyTodayCardOverridesForAcquisitionStoryIfNecessary } from "../today-card-overlay-util";
+import { cardDisplayStyleFromData, popTodayCardLocation, pushTodayCardLocation, relatedCardContentsContentsFromData, } from "../today-card-util";
+import { createTodayBaseCard } from "./today-base-card-builder";
+/**
+ * Create TodayCard displaying an in app purchase
+ *
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data to build the card from
+ * @param cardConfig The configuration for the card
+ * @param context The parse context for the over all today page
+ * @param augmentingData that stores some additional responses that may be used to enhance the contents of `data`
+ * @returns The newly created TodayCard, using the in app purchase media.
+ */
+export function createTodayInAppPurchaseCard(objectGraph, data, cardConfig, context, augmentingData) {
+ return validation.context("createTodayInAppPurchaseCard", () => {
+ const inAppPurchaseCard = createTodayBaseCard(objectGraph, data, cardConfig, context);
+ pushTodayCardLocation(objectGraph, data, cardConfig, context);
+ const iAPData = inAppPurchaseDataFromRelatedContent(objectGraph, relatedCardContentsContentsFromData(objectGraph, data));
+ if (isNullOrEmpty(iAPData)) {
+ popTodayCardLocation(context);
+ return null;
+ }
+ const lockup = inAppPurchaseLockupFromData(objectGraph, iAPData, {
+ offerStyle: "colored",
+ metricsOptions: {
+ pageInformation: context.pageInformation,
+ locationTracker: context.locationTracker,
+ },
+ artworkUseCase: 1 /* ArtworkUseCase.LockupIconSmall */,
+ });
+ if (isNullOrEmpty(lockup)) {
+ popTodayCardLocation(context);
+ return null;
+ }
+ lockup.theme = "infer";
+ inAppPurchaseCard.media = new TodayCardInAppPurchase(lockup);
+ // we need to add impressions to the media here because
+ // there is no overlay on an IAP inAppPurchaseCard for some reason
+ inAppPurchaseCard.media.impressionMetrics = lockup.impressionMetrics;
+ inAppPurchaseCard.media.impressionMetrics.fields["parentId"] = inAppPurchaseCard.impressionMetrics.fields["id"];
+ inAppPurchaseCard.style = "white";
+ // Special post-processing step for Acquisition story cards.
+ // This is needed to splice in data not included in initial response.
+ applyTodayCardOverridesForAcquisitionStoryIfNecessary(objectGraph, inAppPurchaseCard, cardConfig, cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle), data, augmentingData, context);
+ popTodayCardLocation(context);
+ return inAppPurchaseCard;
+ });
+}
+function inAppPurchaseDataFromRelatedContent(objectGraph, relatedContent) {
+ if (relatedContent.length === 1) {
+ const contentData = relatedContent[0];
+ if (contentData.type === "in-apps") {
+ return contentData;
+ }
+ }
+ return null;
+}
+//# sourceMappingURL=today-in-app-purchase-card-builder.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-list-card-builder.js b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-list-card-builder.js
new file mode 100644
index 0000000..b803c9f
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-list-card-builder.js
@@ -0,0 +1,71 @@
+import * as validation from "@jet/environment/json/validation";
+import { TodayCardMediaList } from "../../../api/models";
+import * as videoDefaults from "../../constants/video-constants";
+import { todayCardArtworkDetails } from "../artwork/today-artwork-util";
+import { applyTodayCardOverridesForAcquisitionStoryIfNecessary } from "../today-card-overlay-util";
+import { applyMultiAppFallbackToCollectionCard, cardDisplayStyleFromData, cardStyleFromJoeColorsWithoutFallback, listFallbackLimit, lockupsForRelatedContent, popTodayCardLocation, pushTodayCardLocation, relatedCardContentsContentsFromData, } from "../today-card-util";
+import { TodayCardDisplayStyle, } from "../today-types";
+import { createTodayBaseCard } from "./today-base-card-builder";
+import { isEligibleForGamesApp } from "../../content/content";
+/**
+ * Create TodayCard displaying the list of lockups
+ *
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data to build the card from
+ * @param cardConfig The configuration for the card
+ * @param context The parse context for the over all today page
+ * @param augmentingData that stores some additional responses that may be used to enhance the contents of `data`
+ * @returns The newly created TodayCard, using a list or numbered list media.
+ */
+export function createTodayListCard(objectGraph, data, cardConfig, context, augmentingData) {
+ return validation.context("createTodayListCard", () => {
+ var _a, _b, _c;
+ const listCard = createTodayBaseCard(objectGraph, data, cardConfig, context);
+ pushTodayCardLocation(objectGraph, data, cardConfig, context);
+ cardConfig.useJoeColorIconPlaceholder = true;
+ let relatedContents = relatedCardContentsContentsFromData(objectGraph, data);
+ if (preprocessor.GAMES_TARGET) {
+ // Filtering out non-eligible games as they can be used as content for stories collection.
+ // see `createFallbackListShelf` in article.ts.
+ relatedContents = relatedContents.filter((item) => isEligibleForGamesApp(item));
+ }
+ const listLockups = lockupsForRelatedContent(objectGraph, relatedContents, cardConfig, context.pageInformation, context.locationTracker, undefined, undefined, undefined);
+ if (listLockups.length === 0) {
+ popTodayCardLocation(context);
+ return null;
+ }
+ cardConfig.canDisplayArcadeOfferButton = false;
+ // We require at least `listFallbackLimit` lockups for the list card
+ // If this is for use on top of an article its ok to have less than `listFallbackLimit`
+ if (listLockups.length < listFallbackLimit(objectGraph) && cardConfig.enableListCardToMultiAppFallback) {
+ applyMultiAppFallbackToCollectionCard(objectGraph, data, listLockups, listCard);
+ }
+ else {
+ const cardDisplayStyle = cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle);
+ if (cardDisplayStyle === TodayCardDisplayStyle.NumberedList) {
+ let index = 1;
+ for (const lockup of listLockups) {
+ lockup.ordinal = `${index}`;
+ index++;
+ }
+ }
+ const mediaDetails = todayCardArtworkDetails(objectGraph, data, cardConfig);
+ const cardStyle = cardStyleFromJoeColorsWithoutFallback(mediaDetails === null || mediaDetails === void 0 ? void 0 : mediaDetails.joeColors, "bgColor");
+ const isModernListCard = objectGraph.host.isiOS;
+ // Since list videos are very wide (21:9) and ~50% masked, we prevent them from going fullscreen
+ mediaDetails === null || mediaDetails === void 0 ? void 0 : mediaDetails.videos.forEach((video) => {
+ video.canPlayFullScreen = false;
+ video.playbackControls = videoDefaults.noControls(objectGraph);
+ video.autoPlayPlaybackControls = videoDefaults.noControls(objectGraph);
+ });
+ listCard.style = isModernListCard ? undefined : "white";
+ listCard.media = new TodayCardMediaList(listLockups, (_a = mediaDetails === null || mediaDetails === void 0 ? void 0 : mediaDetails.artworks) !== null && _a !== void 0 ? _a : [], (_b = mediaDetails === null || mediaDetails === void 0 ? void 0 : mediaDetails.videos) !== null && _b !== void 0 ? _b : [], (_c = mediaDetails === null || mediaDetails === void 0 ? void 0 : mediaDetails.artworkLayoutsWithMetrics) !== null && _c !== void 0 ? _c : [], undefined, isModernListCard ? cardStyle !== "white" : cardStyle === "dark");
+ }
+ // Special post-processing step for Acquisition story cards.
+ // This is needed to splice in data not included in initial response.
+ applyTodayCardOverridesForAcquisitionStoryIfNecessary(objectGraph, listCard, cardConfig, cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle), data, augmentingData, context);
+ popTodayCardLocation(context);
+ return listCard;
+ });
+}
+//# sourceMappingURL=today-list-card-builder.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-river-card-builder.js b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-river-card-builder.js
new file mode 100644
index 0000000..d93fa7e
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-river-card-builder.js
@@ -0,0 +1,62 @@
+import * as validation from "@jet/environment/json/validation";
+import { TodayCardMediaRiver } from "../../../api/models";
+import { isDarkColor, rgbWith } from "../../../foundation/util/color-util";
+import { applyTodayCardOverridesForAcquisitionStoryIfNecessary, relatedContentOverlayFromData, } from "../today-card-overlay-util";
+import { applyMultiAppFallbackToCollectionCard, cardDisplayStyleFromData, gridFallbackLimit, lockupsForCollectionCardFromData, popTodayCardLocation, pushTodayCardLocation, } from "../today-card-util";
+import { createTodayBaseCard } from "./today-base-card-builder";
+/**
+ * Create TodayCard displaying the river of lockups
+ *
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data to build the card from
+ * @param cardConfig The configuration for the card
+ * @param context The parse context for the over all today page
+ * @param augmentingData that stores some additional responses that may be used to enhance the contents of `data`
+ * @returns The newly created TodayCard, using a river media.
+ */
+export function createTodayRiverCard(objectGraph, data, cardConfig, context, augmentingData) {
+ return validation.context("createTodayRiverCard", () => {
+ const riverCard = createTodayBaseCard(objectGraph, data, cardConfig, context);
+ cardConfig.useJoeColorIconPlaceholder = true;
+ pushTodayCardLocation(objectGraph, data, cardConfig, context);
+ const shouldIncludeLockupClickActions = objectGraph.client.isWeb;
+ const riverLockups = lockupsForCollectionCardFromData(objectGraph, data, cardConfig, context, shouldIncludeLockupClickActions);
+ if (riverLockups.length === 0) {
+ popTodayCardLocation(context);
+ return null;
+ }
+ cardConfig.canDisplayArcadeOfferButton = false;
+ if (riverLockups.length < gridFallbackLimit(objectGraph)) {
+ // <rdar://problem/37261537> P2: Some Today cards not showing on Cinar
+ // less that `gridFallbackLimit` lockups gets a fallback card on Emet only
+ applyMultiAppFallbackToCollectionCard(objectGraph, data, riverLockups, riverCard);
+ }
+ else {
+ riverCard.style = "dark";
+ replaceHighLuminanceIconColorsInLockups(riverLockups);
+ riverCard.media = new TodayCardMediaRiver(riverLockups);
+ }
+ if (objectGraph.client.isWatch) {
+ riverCard.overlay = relatedContentOverlayFromData(objectGraph, riverCard, cardConfig, data, riverLockups);
+ }
+ // Special post-processing step for Acquisition story cards.
+ // This is needed to splice in data not included in initial response.
+ applyTodayCardOverridesForAcquisitionStoryIfNecessary(objectGraph, riverCard, cardConfig, cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle), data, augmentingData, context);
+ popTodayCardLocation(context);
+ return riverCard;
+ });
+}
+/**
+ * Replace any icon background colors with luminance > 0.9 with grey color.
+ * This ensure that river background colors don't compete with the overlayed white text.
+ *
+ * @param riverLockups The river lockups.
+ */
+function replaceHighLuminanceIconColorsInLockups(riverLockups) {
+ for (const lockup of riverLockups) {
+ if (!isDarkColor(lockup.icon.backgroundColor, 90)) {
+ lockup.icon.backgroundColor = rgbWith(0.8, 0.8, 0.8);
+ }
+ }
+}
+//# sourceMappingURL=today-river-card-builder.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-short-image-card-builder.js b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-short-image-card-builder.js
new file mode 100644
index 0000000..c50836a
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-short-image-card-builder.js
@@ -0,0 +1,42 @@
+import * as validation from "@jet/environment/json/validation";
+import { deepLinkUrlFromData } from "../../linking/external-deep-link";
+import { applyTodayCardOverridesForAcquisitionStoryIfNecessary, relatedContentOverlayFromData, } from "../today-card-overlay-util";
+import { cardDisplayStyleFromData, lockupsForRelatedContent, offerEnvironmentForTodayCard, offerStyleForTodayCard, popTodayCardLocation, pushTodayCardLocation, relatedCardContentsContentsFromData, } from "../today-card-util";
+import { addArtworkMediaToBaseCard, createTodayBaseCard } from "./today-base-card-builder";
+import { addSingleAppFallbackToCard } from "./today-single-app-card-builder";
+/**
+ * Create TodayCard displaying the full bleed image
+ *
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data to build the card from
+ * @param cardConfig The configuration for the card
+ * @param context The parse context for the over all today page
+ * @param augmentingData that stores some additional responses that may be used to enhance the contents of `data`
+ * @returns The newly created TodayCard, using the full bleed image media.
+ */
+export function createTodayShortImageCard(objectGraph, data, cardConfig, context, augmentingData) {
+ return validation.context("createTodayShortImageCard", () => {
+ const shortImageCard = createTodayBaseCard(objectGraph, data, cardConfig, context);
+ pushTodayCardLocation(objectGraph, data, cardConfig, context);
+ const didAddArtworkToCard = addArtworkMediaToBaseCard(objectGraph, data, shortImageCard, cardConfig, context);
+ let didAddSingleAppFallback = false;
+ const relatedContent = relatedCardContentsContentsFromData(objectGraph, data);
+ const offerStyle = offerStyleForTodayCard(objectGraph, shortImageCard.style);
+ const relatedContentLockups = lockupsForRelatedContent(objectGraph, relatedContent, cardConfig, context.pageInformation, context.locationTracker, offerEnvironmentForTodayCard(shortImageCard.style), offerStyle, deepLinkUrlFromData(objectGraph, data));
+ const overlay = relatedContentOverlayFromData(objectGraph, shortImageCard, cardConfig, data, relatedContentLockups);
+ shortImageCard.overlay = overlay;
+ if (!didAddArtworkToCard && relatedContent.length === 1) {
+ didAddSingleAppFallback = addSingleAppFallbackToCard(objectGraph, data, shortImageCard, relatedContent, cardConfig, context);
+ }
+ if (!didAddArtworkToCard && !didAddSingleAppFallback) {
+ popTodayCardLocation(context);
+ return null;
+ }
+ // Special post-processing step for Acquisition story cards.
+ // This is needed to splice in data not included in initial response.
+ applyTodayCardOverridesForAcquisitionStoryIfNecessary(objectGraph, shortImageCard, cardConfig, cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle), data, augmentingData, context);
+ popTodayCardLocation(context);
+ return shortImageCard;
+ });
+}
+//# sourceMappingURL=today-short-image-card-builder.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-single-app-card-builder.js b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-single-app-card-builder.js
new file mode 100644
index 0000000..18828c8
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-single-app-card-builder.js
@@ -0,0 +1,81 @@
+import * as validation from "@jet/environment/json/validation";
+import { TodayCardMediaAppIcon, TodayCardMediaList, TodayCardParagraphOverlay } from "../../../api/models";
+import { isDefinedNonNull } from "../../../foundation/json-parsing/server-data";
+import { deepLinkUrlFromData } from "../../linking/external-deep-link";
+import { applyTodayCardOverridesForAcquisitionStoryIfNecessary, relatedContentOverlayFromData, } from "../today-card-overlay-util";
+import { cardDisplayStyleFromData, lockupsForRelatedContent, offerEnvironmentForTodayCard, offerStyleForTodayCard, popTodayCardLocation, pushTodayCardLocation, relatedCardContentsContentsFromData, } from "../today-card-util";
+import { addArtworkMediaToBaseCard, createTodayBaseCard } from "./today-base-card-builder";
+/**
+ * Create TodayCard displaying the SingleApp card display style
+ *
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data to build the card from
+ * @param cardConfig The configuration for the card
+ * @param context The parse context for the over all today page
+ * @param augmentingData that stores some additional responses that may be used to enhance the contents of `data`
+ * @returns The newly created TodayCard, either artwork or the single app icon.
+ */
+export function createTodaySingleAppCard(objectGraph, data, cardConfig, context, augmentingData) {
+ return validation.context("createTodaySingleAppCard", () => {
+ const singleAppCard = createTodayBaseCard(objectGraph, data, cardConfig, context);
+ pushTodayCardLocation(objectGraph, data, cardConfig, context);
+ // Next prefer artwork card, and then fallback to substyles
+ const didAddArtworkToCard = addArtworkMediaToBaseCard(objectGraph, data, singleAppCard, cardConfig, context);
+ const relatedContent = relatedCardContentsContentsFromData(objectGraph, data);
+ const relatedContentLockups = lockupsForRelatedContent(objectGraph, relatedContent, cardConfig, context.pageInformation, context.locationTracker, "todayCard", null, deepLinkUrlFromData(objectGraph, data));
+ const overlay = relatedContentOverlayFromData(objectGraph, singleAppCard, cardConfig, data, relatedContentLockups);
+ if (isDefinedNonNull(overlay) && overlay instanceof TodayCardParagraphOverlay) {
+ overlay.style = "white";
+ }
+ singleAppCard.overlay = overlay;
+ if (!didAddArtworkToCard && relatedContent.length === 1) {
+ const didAddSingleAppFallback = addSingleAppFallbackToCard(objectGraph, data, singleAppCard, relatedContent, cardConfig, context);
+ if (!didAddSingleAppFallback) {
+ popTodayCardLocation(context);
+ return null;
+ }
+ }
+ // Special post-processing step for Acquisition story cards.
+ // This is needed to splice in data not included in initial response.
+ applyTodayCardOverridesForAcquisitionStoryIfNecessary(objectGraph, singleAppCard, cardConfig, cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle), data, augmentingData, context);
+ popTodayCardLocation(context);
+ return singleAppCard;
+ });
+}
+/**
+ * Adds a single app fallback to the card.
+ *
+ * @param objectGraph - The object graph of the app store.
+ * @param data - The media data.
+ * @param card - The today card.
+ * @param relatedContent - The related content data.
+ * @param cardConfig - The configuration for the today card.
+ * @param context - The parse context for today card.
+ * @returns Returns true or false based on whether we could create a single app fallback.
+ */
+export function addSingleAppFallbackToCard(objectGraph, data, card, relatedContent, cardConfig, context) {
+ if (relatedContent.length !== 1) {
+ return false;
+ }
+ cardConfig.useJoeColorIconPlaceholder = true;
+ card.style = "dark";
+ const offerStyle = offerStyleForTodayCard(objectGraph, card.style);
+ const relatedContentLockups = lockupsForRelatedContent(objectGraph, relatedContent, cardConfig, context.pageInformation, context.locationTracker, offerEnvironmentForTodayCard(card.style), offerStyle, deepLinkUrlFromData(objectGraph, data));
+ if (relatedContentLockups.length !== 1) {
+ return false;
+ }
+ const lockup = relatedContentLockups[0];
+ // tvOS doesn't have layout to properly render appIcon media.
+ // However, `media` can't be simply removed from TodayCard, as it is a required field in native code.
+ // This `TodayCardMediaList` works as an empty media placeholder,
+ // that allows to render text from TodayCard but not the image.
+ // From StarlightB AppIcon is disabled in native code and this check can be removed in the next major release.
+ if (objectGraph.client.isTV && !objectGraph.props.enabled("disableAppIconMediaTodayHeader")) {
+ card.media = new TodayCardMediaList([], [], [], [], undefined, undefined);
+ }
+ else {
+ card.media = new TodayCardMediaAppIcon(lockup.icon);
+ }
+ return true;
+}
+//# sourceMappingURL=today-single-app-card-builder.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-video-card-builder.js b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-video-card-builder.js
new file mode 100644
index 0000000..77516f6
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/cards/today-video-card-builder.js
@@ -0,0 +1,33 @@
+import * as validation from "@jet/environment/json/validation";
+import { deepLinkUrlFromData } from "../../linking/external-deep-link";
+import { applyTodayCardOverridesForAcquisitionStoryIfNecessary, relatedContentOverlayFromData, } from "../today-card-overlay-util";
+import { cardDisplayStyleFromData, lockupsForRelatedContent, offerStyleForTodayCard, popTodayCardLocation, pushTodayCardLocation, relatedCardContentsContentsFromData, } from "../today-card-util";
+import { addArtworkMediaToBaseCard, createTodayBaseCard } from "./today-base-card-builder";
+/**
+ * Create TodayCard displaying a video
+ *
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data to build the card from
+ * @param cardConfig The configuration for the card
+ * @param context The parse context for the over all today page
+ * @param augmentingData that stores some additional responses that may be used to enhance the contents of `data`
+ * @returns The newly created TodayCard, using the video media.
+ */
+export function createTodayVideoCard(objectGraph, data, cardConfig, context, augmentingData) {
+ return validation.context("createTodayVideoCard", () => {
+ const videoCard = createTodayBaseCard(objectGraph, data, cardConfig, context);
+ addArtworkMediaToBaseCard(objectGraph, data, videoCard, cardConfig, context);
+ pushTodayCardLocation(objectGraph, data, cardConfig, context);
+ const offerStyle = offerStyleForTodayCard(objectGraph, videoCard.style);
+ const relatedContent = relatedCardContentsContentsFromData(objectGraph, data);
+ const relatedContentLockups = lockupsForRelatedContent(objectGraph, relatedContent, cardConfig, context.pageInformation, context.locationTracker, "todayCard", offerStyle, deepLinkUrlFromData(objectGraph, data));
+ const overlay = relatedContentOverlayFromData(objectGraph, videoCard, cardConfig, data, relatedContentLockups);
+ videoCard.overlay = overlay;
+ // Special post-processing step for Acquisition story cards.
+ // This is needed to splice in data not included in initial response.
+ applyTodayCardOverridesForAcquisitionStoryIfNecessary(objectGraph, videoCard, cardConfig, cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle), data, augmentingData, context);
+ popTodayCardLocation(context);
+ return videoCard;
+ });
+}
+//# sourceMappingURL=today-video-card-builder.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/onboarding-cards.js b/node_modules/@jet-app/app-store/tmp/src/common/today/onboarding-cards.js
new file mode 100644
index 0000000..86af694
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/onboarding-cards.js
@@ -0,0 +1,23 @@
+/**
+ * Created by ls on 6/23/17.
+ */
+import * as serverData from "../../foundation/json-parsing/server-data";
+const isOnboardingCardKey = "isOnBoardingCard";
+/**
+ * Tag a data collection as onboarding cards for metrics.
+ * @param cardsData
+ */
+export function markDataCollectionAsOnboardingCards(objectGraph, cardsData) {
+ for (const card of cardsData) {
+ card[isOnboardingCardKey] = true;
+ }
+}
+/**
+ * Check whether given data was injected as onboarding card.
+ * @param cardData Media API data for single card.
+ * @returns A boolean returning `true` if `cardData` was from `makeOnBoardingCardToken`, `false` otherwise.
+ */
+export function isCardDataOnboardingCard(objectGraph, cardData) {
+ return serverData.asBooleanOrFalse(cardData, isOnboardingCardKey);
+}
+//# sourceMappingURL=onboarding-cards.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/routable-article-page-url-utils.js b/node_modules/@jet-app/app-store/tmp/src/common/today/routable-article-page-url-utils.js
new file mode 100644
index 0000000..5603a86
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/routable-article-page-url-utils.js
@@ -0,0 +1,32 @@
+import { makeRoutableArticlePageIntent, } from "../../api/intents/routable-article-page-intent";
+import { generateRoutes } from "../util/generate-routes";
+const { routes: routableArticlePageWithPlatformRoutes, makeCanonicalUrl: makeRoutableArticlePageWithPlatformUrl } = generateRoutes(makeRoutableArticlePageIntent, "/{platform}/story/{id}");
+const { routes: routableArticlePageWithoutPlatformRoutes, makeCanonicalUrl: makeRoutableArticlePageWithoutPlatformUrl, } = generateRoutes(makeRoutableArticlePageIntent, "/story/{id}");
+export { routableArticlePageWithPlatformRoutes, routableArticlePageWithoutPlatformRoutes };
+/**
+ * Generate the URL for an "Article Page" based on the {@linkcode intent}
+ *
+ * If the {@linkcode intent} has an explicitly-configured `platform` property,
+ * then the resulting URL will include the explicitly as the `{platform}` prefix
+ * segment. If the `platform` property is not provided, the resulting URL will
+ * not contain an explicit `{platform}` segment at all.
+ *
+ * @param objectGraph
+ * @param intent
+ * @returns the URL that, when parsed, produces the given `intent`
+ */
+export function makeRoutableArticlePageCanonicalUrl(objectGraph, intent) {
+ const intentWithExpectedID = {
+ ...intent,
+ // The `{id}` segment of the URL is expected to include the `id` prefix, which is removed
+ // when parsing the incoming URL into the `Intent`
+ id: `id${intent.id}`,
+ };
+ if (intent.platform) {
+ return makeRoutableArticlePageWithPlatformUrl(objectGraph, intentWithExpectedID);
+ }
+ else {
+ return makeRoutableArticlePageWithoutPlatformUrl(objectGraph, intentWithExpectedID);
+ }
+}
+//# sourceMappingURL=routable-article-page-url-utils.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/today-card-overlay-util.js b/node_modules/@jet-app/app-store/tmp/src/common/today/today-card-overlay-util.js
new file mode 100644
index 0000000..ab0829f
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/today-card-overlay-util.js
@@ -0,0 +1,111 @@
+import { isSome } from "@jet/environment";
+import { isNothing } from "@jet/environment/types/optional";
+import { marketingItemContextFromString, TodayCardArcadeLockupOverlay, TodayCardLockupListOverlay, TodayCardLockupOverlay, TodayCardThreeLineOverlay, } from "../../api/models";
+import * as mediaAttributes from "../../foundation/media/attributes";
+import { upsellFromContentsOfUpsellResponse, upsellFromRelationshipOf, } from "../arcade/arcade-common";
+import { extractEditorialClientParams } from "../editorial-pages/editorial-data-util";
+import { arcadeLockupFromData } from "../lockups/lockups";
+import { todayCardArtworkDetails } from "./artwork/today-artwork-util";
+import { cardStyleFromJoeColors, offerStyleForTodayCard } from "./today-card-util";
+// MARK: - Paragraph / Related Content Overlays
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param card The card being built that we'd like to add the overlay to
+ * @param cardConfig The configuration used to create the card
+ * @param data The media api data to build the card from
+ * @param relatedContentLockups The lockups created from the related content
+ * @returns A TodayCardLockupOverlay, TodayCardLockupListOverlay, TodayCardThreeLineOverlay, depending on whether
+ * the card should ignore the short notes, has a deep link, or has related content lockups.
+ */
+export function relatedContentOverlayFromData(objectGraph, card, cardConfig, data, relatedContentLockups) {
+ const editorialClientParams = extractEditorialClientParams(objectGraph, data);
+ if (isSome(editorialClientParams.suppressLockup) && editorialClientParams.suppressLockup) {
+ return null;
+ }
+ let overlay = null;
+ const hasSingleLockup = isSome(relatedContentLockups) && relatedContentLockups.length === 1;
+ const hasMultipleLockups = isSome(relatedContentLockups) && relatedContentLockups.length > 1;
+ const hasThreeLineCards = objectGraph.client.isMac || objectGraph.client.isWatch;
+ if (hasThreeLineCards) {
+ overlay = new TodayCardThreeLineOverlay(card.heading, card.title, card.inlineDescription);
+ }
+ else if (hasSingleLockup) {
+ overlay = new TodayCardLockupOverlay(relatedContentLockups[0]);
+ }
+ else if (hasMultipleLockups) {
+ overlay = new TodayCardLockupListOverlay(relatedContentLockups);
+ }
+ return overlay;
+}
+// MARK: - Arcade Acquisitions
+/**
+ * Applies a set of overrides to an existing `card` built by the standard pipelie using the contents of augmenting data fetched externally.
+ * This function provides support for parsing editorial-items fetched for:
+ * - Today
+ * - Articles (with data augmentation)
+ * - Groupings (with data augmentation)
+ *
+ * @param card Card to override behavior of.
+ * @param data Original data `card` was created with
+ * @param augmentingData Data used to augment `card`.
+ * @param metricsContext Metrics context to use for overrides.
+ * @returns Overridden Card, or `null` if overrides failed. Note that `card` is modified *IN PLACE*.
+ */
+export function applyTodayCardOverridesForAcquisitionStoryIfNecessary(objectGraph, card, cardConfig, cardDisplayStyle, data, augmentingData, context) {
+ const isAcquisitionStory = mediaAttributes.attributeAsBooleanOrFalse(data, "isAcquisition");
+ if (!isAcquisitionStory) {
+ return;
+ }
+ // Try to build upsell data from upsell relationship, if that exists.
+ let upsellData = upsellFromRelationshipOf(objectGraph, data);
+ if (isNothing(upsellData) && isSome(augmentingData)) {
+ // Fallback: Try to build off upsell data off `augmentingData`. This relies on the caller being proactive about fetching upsell separately for endpoints that don't return upsell relationship.
+ upsellData = upsellFromContentsOfUpsellResponse(objectGraph, augmentingData.arcadeUpsellEditorialResponse);
+ }
+ // Override: Arcade Overlay
+ const arcadeLockupOverlay = arcadeOverlayFromData(objectGraph, data, card, cardConfig, cardDisplayStyle, upsellData, context);
+ if (arcadeLockupOverlay) {
+ card.overlay = arcadeLockupOverlay;
+ card.impressionMetrics.fields["displaysArcadeUpsell"] = true;
+ // If we're overriding the card style used with the overlay we should also update the style for the card itself.
+ const style = cardStyleForArcadeOverlay(objectGraph, data, card, cardConfig);
+ if (card.style !== style) {
+ card.style = style;
+ }
+ }
+}
+/**
+ * Whether or not current platform supports arcade overlay.
+ */
+function currentPlatformSupportsArcadeOverlay(objectGraph) {
+ // Check if on platform that will actually display data.
+ const platform = objectGraph.host.platform;
+ const platformSupportsArcadeLockupOverlay = platform === "iOS" || platform === "macOS";
+ return platformSupportsArcadeLockupOverlay;
+}
+/**
+ * Creates an instance of `TodayCardArcadeLockupOverlay` to use for Acquisition Editorial Items.
+ * @param upsellData Upsell data containing editorial and iAP data.
+ * @param metricsContext Metrics context to use for generated lockup.
+ */
+function arcadeOverlayFromData(objectGraph, data, card, cardConfig, cardDisplayStyle, upsellData, context) {
+ if (!currentPlatformSupportsArcadeOverlay(objectGraph)) {
+ return null;
+ }
+ const style = cardStyleForArcadeOverlay(objectGraph, data, card, cardConfig);
+ const offerStyle = offerStyleForTodayCard(objectGraph, style);
+ const lockup = arcadeLockupFromData(objectGraph, upsellData, context, marketingItemContextFromString("editorialItem"), offerStyle, "todayCard");
+ return new TodayCardArcadeLockupOverlay(lockup);
+}
+/**
+ * Returns the `TodayCardStyle` that should be used for the arcade overlay.
+ * Some cards use different styles when fetched as part of an article vs. today feed. Using the media artwork to determine the
+ * cardStyle in these cases avoids issues with today cards changing styles when transitionin from to today feed to articles.
+ */
+function cardStyleForArcadeOverlay(objectGraph, data, card, cardConfig) {
+ const useCardStyle = !cardConfig.enableListCardToMultiAppFallback;
+ const mediaDetails = todayCardArtworkDetails(objectGraph, data, cardConfig);
+ const styleFromMedia = cardStyleFromJoeColors(mediaDetails === null || mediaDetails === void 0 ? void 0 : mediaDetails.joeColors);
+ return useCardStyle ? card.style : styleFromMedia;
+}
+//# sourceMappingURL=today-card-overlay-util.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/today-card-util.js b/node_modules/@jet-app/app-store/tmp/src/common/today/today-card-util.js
new file mode 100644
index 0000000..688f0fb
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/today-card-util.js
@@ -0,0 +1,785 @@
+import { isNothing, isSome } from "@jet/environment";
+import * as validation from "@jet/environment/json/validation";
+import { ArtworkContentMode, ExternalUrlAction, FlowAction, TodayCardActionOverlay, TodayCardMediaArtwork, TodayCardMediaHero, TodayCardMediaMultiApp, TodayCardMediaVideo, TodayCardMediaWithArtwork, } from "../../api/models";
+import { asBoolean, asDictionary, asString, isDefinedNonNull, isDefinedNonNullNonEmpty, isNull, isNullOrEmpty, objectPathToString, } from "../../foundation/json-parsing/server-data";
+import * as mediaAttributes from "../../foundation/media/attributes";
+import { relationshipCollection } from "../../foundation/media/relationships";
+import { Parameters } from "../../foundation/network/url-constants";
+import { URL } from "../../foundation/network/urls";
+import * as color from "../../foundation/util/color-util";
+import { artworkFromApiArtwork, editorialNotesFromData, hasMessagesExtensionFromData, isHiddenFromSpringboardFromData, joeColorSetFromData, notesFromData, screenSizeIPhone134, } from "../content/content";
+import { extractEditorialClientParams } from "../editorial-pages/editorial-data-util";
+import * as lockupsEditorialContext from "../lockups/editorial-context";
+import { lockupsFromData } from "../lockups/lockups";
+import * as metricsHelpersLocation from "../metrics/helpers/location";
+import * as metricsHelpersImpressions from "./../metrics/helpers/impressions";
+import { createTodayAppEventCard } from "./cards/today-app-event-card-builder";
+import { createTodayBrandedCard } from "./cards/today-branded-card-builder";
+import { createTodayFullBleedImageCard } from "./cards/today-full-bleed-image-card-builder";
+import { createTodayGridCard } from "./cards/today-grid-card-builder";
+import { createTodayInAppPurchaseCard } from "./cards/today-in-app-purchase-card-builder";
+import { createTodayListCard } from "./cards/today-list-card-builder";
+import { createTodayRiverCard } from "./cards/today-river-card-builder";
+import { createTodayShortImageCard } from "./cards/today-short-image-card-builder";
+import { createTodaySingleAppCard } from "./cards/today-single-app-card-builder";
+import { createTodayVideoCard } from "./cards/today-video-card-builder";
+import { HeroMediaDisplayContext, OfTheDayIntention, TodayCardDisplayStyle, } from "./today-types";
+import { createTodayBaseCard } from "./cards/today-base-card-builder";
+// MARK: - Today Card Creation
+/**
+ * Create a card with some appearance configuration within today page.
+ * @param {AppStoreObjectGraph} objectGraph The object graph for the app store.
+ * @param {Data} data to build card of.
+ * @param {TodayCardConfiguration} cardConfig flags on how to configure a given card.
+ * @param {TodayParseContext} context to store state updated throughout parsing.
+ * @param {TodayCardAugmentingData} augmentingData that stores some additional responses that may be used to enhance the contents of `data`
+ */
+export function todayCardFromData(objectGraph, data, cardConfig, context, augmentingData) {
+ return validation.catchingContext("todayCardFromData", () => {
+ const cardDisplayStyle = cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle);
+ const clientIdentifier = lockupsEditorialContext.clientIdentifierForEditorialContextInData(objectGraph, data);
+ // If clientIdentifier is empty, don't override the cardConfig as this may have been set
+ // at a higher level.
+ if (isSome(clientIdentifier)) {
+ if (shouldRespectClientIdentifierOverride(objectGraph, data, clientIdentifier, cardConfig)) {
+ cardConfig.clientIdentifierOverride = clientIdentifier;
+ }
+ else {
+ cardConfig.clientIdentifierOverride = null;
+ }
+ }
+ // Configure subtitle for cross link.
+ cardConfig.crossLinkSubtitle = crossLinkSubtitleFromData(objectGraph, data);
+ let card = null;
+ switch (cardDisplayStyle) {
+ case TodayCardDisplayStyle.AppOfTheDay:
+ case TodayCardDisplayStyle.GameOfTheDay:
+ card = createTodayBrandedCard(objectGraph, data, cardConfig, context, augmentingData);
+ break;
+ case TodayCardDisplayStyle.Video:
+ card = createTodayVideoCard(objectGraph, data, cardConfig, context, augmentingData);
+ break;
+ case TodayCardDisplayStyle.FullBleedImage:
+ card = createTodayFullBleedImageCard(objectGraph, data, cardConfig, context, augmentingData);
+ break;
+ case TodayCardDisplayStyle.InAppPurchase:
+ card = createTodayInAppPurchaseCard(objectGraph, data, cardConfig, context, augmentingData);
+ break;
+ case TodayCardDisplayStyle.AppEventCard:
+ card = createTodayAppEventCard(objectGraph, data, cardConfig, context, augmentingData);
+ break;
+ case TodayCardDisplayStyle.List:
+ case TodayCardDisplayStyle.NumberedList:
+ card = createTodayListCard(objectGraph, data, cardConfig, context, augmentingData);
+ break;
+ case TodayCardDisplayStyle.River:
+ case TodayCardDisplayStyle.Grid:
+ if (objectGraph.client.isMac) {
+ card = createTodayGridCard(objectGraph, data, cardConfig, context, augmentingData);
+ }
+ else {
+ card = createTodayRiverCard(objectGraph, data, cardConfig, context, augmentingData);
+ }
+ break;
+ case TodayCardDisplayStyle.SingleApp:
+ card = createTodaySingleAppCard(objectGraph, data, cardConfig, context, augmentingData);
+ break;
+ case TodayCardDisplayStyle.ShortImage:
+ card = createTodayShortImageCard(objectGraph, data, cardConfig, context, augmentingData);
+ break;
+ default:
+ card = null;
+ break;
+ }
+ if (isNull(card)) {
+ objectGraph.console.log(`Unknown style: ${cardDisplayStyle}`);
+ return card;
+ }
+ // For certain platforms we add hero media to the card.
+ addHeroMediaToTodayCardIfNecessary(objectGraph, card, cardConfig, data);
+ addExternalLinkOverlayToTodayCardIfNecessary(card);
+ addOTDStyleToCardIfNecessary(card, cardConfig);
+ enableFlipAndBlurIfNecessary(objectGraph, card);
+ if (isNothing(card.media)) {
+ objectGraph.console.log(`Missing required media: ${cardDisplayStyle}`);
+ card = null;
+ }
+ return card;
+ }, (error) => {
+ objectGraph.console.log(error);
+ return null;
+ });
+}
+/**
+ * On the watch we need the todayCard on the article to get the title and subtitle,
+ * so even if we dont successfully make a card for the article we create a basic one with an empty media so the title / subtitle can be used
+ * @param {AppStoreObjectGraph} objectGraph The object graph for the app store.
+ * @param {Data} data to build card of.
+ * @param {TodayCardConfiguration} cardConfig flags on how to configure a given card.
+ * @param {TodayParseContext} context to store state updated throughout parsing.
+ */
+export function fallbackWatchTodayCardFromData(objectGraph, data, cardConfig, context) {
+ if (!objectGraph.client.isWatch) {
+ return null;
+ }
+ const fallbackCard = createTodayBaseCard(objectGraph, data, cardConfig, context);
+ fallbackCard.media = new TodayCardMediaArtwork([], [], []);
+ return fallbackCard;
+}
+/**
+ * Create the {EditorialDisplayOptions for a card given the MAPI data
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The MAPI data for this today card
+ * @param cardConfig The configuration for the card
+ * @returns The editorialDisplayOptions for this card
+ */
+export function todayCardEditorialDisplayOptionsFromData(objectGraph, data, cardConfig) {
+ var _a;
+ // here is where we make the display options
+ const editorialClientParams = extractEditorialClientParams(objectGraph, data);
+ const isExtraWideCard = objectGraph.client.isPad && (cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.isHeroCard);
+ const editorialDisplayOptions = {
+ suppressTagline: mediaAttributes.attributeAsBoolean(data, "ignoreITunesShortNotes"),
+ suppressLockup: asBoolean(editorialClientParams["suppressLockup"]),
+ showBadgeInSmallCards: (_a = cardConfig.alwaysShowBadgeInSmallCards) !== null && _a !== void 0 ? _a : asBoolean(editorialClientParams["showBadgeInSmallCards"]),
+ useMaterialBlur: cardConfig.alwaysUseMaterialBlur || isExtraWideCard || asBoolean(editorialClientParams["useMaterialBlur"]),
+ };
+ return editorialDisplayOptions;
+}
+// MARK: - Today Configuration
+/**
+ * Create the "default" card configuration that callers can generally base their configuration from.
+ */
+export function defaultTodayCardConfiguration(objectGraph) {
+ return {
+ useOTDTextStyle: false,
+ enableFullScreenVideo: true,
+ enableListCardToMultiAppFallback: true,
+ canDisplayArcadeOfferButton: true,
+ isHeroCard: false,
+ };
+}
+// MARK: - Metrics / Location Tracking
+/**
+ * Allows for specialized card builders to share the same location tracking push / pop code.
+ *
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data to get location information from
+ * @param cardConfig The configuration for the card
+ * @param context The parse context for the over all today page
+ */
+export function pushTodayCardLocation(objectGraph, data, cardConfig, context, titleOverride) {
+ const title = isDefinedNonNullNonEmpty(titleOverride)
+ ? titleOverride
+ : todayCardTitleFromData(objectGraph, data, cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle));
+ metricsHelpersLocation.pushContentLocation(objectGraph, todayCardMetricsOptions(objectGraph, data, cardConfig, context, title), title !== null && title !== void 0 ? title : "");
+}
+export function todayCardMetricsOptions(objectGraph, data, cardConfig, context, titleOverride) {
+ var _a;
+ return metricsHelpersImpressions.impressionOptions(objectGraph, data, titleOverride !== null && titleOverride !== void 0 ? titleOverride : "", {
+ ...cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.baseMetricsOptions,
+ targetType: "todayCard",
+ pageInformation: context.pageInformation,
+ locationTracker: context.locationTracker,
+ isAdEligible: (_a = cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.isAdEligible) !== null && _a !== void 0 ? _a : false,
+ optimizationId: asString(data, "meta.personalizationData.optimizationId"),
+ optimizationEntityId: asString(data, "meta.personalizationData.optimizationEntityId"),
+ rowIndex: cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.currentRowIndex,
+ displayStyle: cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.metricsDisplayStyle,
+ });
+}
+/**
+ * Pop the today card location that was pushed by the pushTodayCardLocation function.
+ *
+ * @param context The parse context for the over all today page
+ */
+export function popTodayCardLocation(context) {
+ metricsHelpersLocation.popLocation(context.locationTracker);
+}
+// MARK: - Card Title
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media api data used to determine the title of the card
+ * @param cardDisplayStyle The display style of the card
+ * @returns The title of the card
+ */
+export function todayCardTitleFromData(objectGraph, data, cardDisplayStyle) {
+ let title = notesFromData(objectGraph, data, "name");
+ if (isNullOrEmpty(title)) {
+ switch (cardDisplayStyle) {
+ case TodayCardDisplayStyle.AppOfTheDay:
+ case TodayCardDisplayStyle.GameOfTheDay:
+ title = mediaAttributes.attributeAsString(data, "label");
+ break;
+ default:
+ break;
+ }
+ }
+ return title;
+}
+// MARK: - Card Display Styles
+/**
+ * These are today card types that can be coerced into an override type, there are times where we want to
+ * have all List cards for instance, render as grids, this set denotes the types that can be coerced.
+ */
+const coercibleTodayCardStyles = new Set([
+ TodayCardDisplayStyle.Grid,
+ TodayCardDisplayStyle.List,
+ TodayCardDisplayStyle.NumberedList,
+ TodayCardDisplayStyle.River,
+]);
+/**
+ * Find the today card style for the given data, allowing coercion if necessary.
+ * @param data The media api data for an editorial item
+ * @param styleOverride The override style to use if the data has a style that can be coerced
+ * @returns The resolved todayCardDisplayStyle
+ */
+export function cardDisplayStyleFromData(data, styleOverride) {
+ const cardDisplayStyle = mediaAttributes.attributeAsString(data, "cardDisplayStyle");
+ if (coercibleTodayCardStyles.has(cardDisplayStyle) && isSome(styleOverride)) {
+ return styleOverride;
+ }
+ return cardDisplayStyle;
+}
+// MARK: - Collection Cards
+/**
+ * @param objectGraph Dependency graph for the App Store
+ * @param data MAPI data for the card these lockups are included in
+ * @param cardConfig The configuration for the card these lockups are included in
+ * @param context The parse context for the over all today page
+ * @param includeLockupClickActions Whether we should include the click actions for the lockups, this is only needed
+ * when a card allows clicks on a lockup
+ * @returns The list of lockups for the collection displayed on a TodayCard
+ */
+export function lockupsForCollectionCardFromData(objectGraph, data, cardConfig, context, includeLockupClickActions) {
+ const relatedContent = relatedCardContentsContentsFromData(objectGraph, data);
+ const filteredRelatedContent = relatedContent.filter((cardData) => {
+ const isHiddenFromSpringboard = isHiddenFromSpringboardFromData(objectGraph, cardData);
+ const hasMessagesExtension = hasMessagesExtensionFromData(objectGraph, cardData);
+ return !hasMessagesExtension || !isHiddenFromSpringboard;
+ });
+ return lockupsForRelatedContent(objectGraph, filteredRelatedContent, cardConfig, context.pageInformation, context.locationTracker, undefined, undefined, undefined, includeLockupClickActions);
+}
+/**
+ * @param objectGraph Dependency graph for the App Store
+ * @param relatedContent The list of MAPI data objects to generate lockups for
+ * @param cardConfig The configuration for the card these lockups are included in
+ * @param pageInformation The pageInformation for the page these lockups are included in
+ * @param locationTracker The locationTracker used for impressions on these lockups
+ * @param offerEnvironment The offer environment to use for the lockups
+ * @param offerStyle The offerStyle to use for the lockups
+ * @param externalDeepLinkUrl The promotional deep link url to use on the lockup's offer.
+ * @param includeLockupClickActions Whether we should include the click actions for the lockups, this is only needed
+ * when a card allows clicks on a lockup
+ * @returns The list of lockups for the collection displayed on a TodayCard
+ */
+export function lockupsForRelatedContent(objectGraph, relatedContent, cardConfig, pageInformation, locationTracker, offerEnvironment, offerStyle, externalDeepLinkUrl, includeLockupClickActions = true) {
+ if (isNullOrEmpty(relatedContent)) {
+ return [];
+ }
+ // IAPs are only suitable for the TodayCardInAppPurchase card. That card follows a separate path, so we should
+ // filter them out here.
+ const filteredRelatedContent = relatedContent.filter((cardData) => {
+ if (isDefinedNonNullNonEmpty(cardData.attributes)) {
+ return cardData.type !== "in-apps";
+ }
+ return true;
+ });
+ const options = {
+ lockupOptions: {
+ metricsOptions: {
+ pageInformation: pageInformation,
+ locationTracker: locationTracker,
+ },
+ offerEnvironment: offerEnvironment,
+ offerStyle: offerStyle,
+ clientIdentifierOverride: cardConfig.clientIdentifierOverride,
+ externalDeepLinkUrl: externalDeepLinkUrl,
+ crossLinkSubtitle: cardConfig.crossLinkSubtitle,
+ artworkUseCase: 1 /* ArtworkUseCase.LockupIconSmall */,
+ canDisplayArcadeOfferButton: cardConfig.canDisplayArcadeOfferButton,
+ useJoeColorIconPlaceholder: cardConfig.useJoeColorIconPlaceholder,
+ skipDefaultClickAction: !includeLockupClickActions,
+ },
+ filter: 76670 /* Filter.TodayCard */,
+ };
+ return lockupsFromData(objectGraph, filteredRelatedContent, options);
+}
+// MARK: - Related Content
+/**
+ * @param objectGraph Dependency graph for the App Store
+ * @param data MAPI data to get the relationship from
+ * @returns The card-contents relationship from the MAPI data
+ */
+export function relatedCardContentsContentsFromData(objectGraph, data) {
+ return relationshipCollection(data, "card-contents");
+}
+// MARK: - Grid / List Collection Card Fallback
+/**
+ * @param objectGraph The object graph for the app store
+ * @returns The min icon count that a Grid card should use before falling back to fallback media
+ */
+export function gridFallbackLimit(objectGraph) {
+ switch (objectGraph.client.deviceType) {
+ case "tv":
+ return 2;
+ default:
+ return 4;
+ }
+}
+/**
+ * @param objectGraph The object graph for the app store
+ * @returns The min icon count that a List card should use before falling back to fallback media
+ */
+export function listFallbackLimit(objectGraph) {
+ // Limits before using fallback media card for list-types and grid-types (including river).
+ switch (objectGraph.client.deviceType) {
+ case "tv":
+ return 3;
+ default:
+ return 4;
+ }
+}
+/**
+ * When we do not have enough content to render a list or grid card, we will use a multi
+ * app fallback media in its place. This also updates the click action to include a param
+ * indicating we're using fallback media.
+ *
+ * @param objectGraph The object graph for the app store
+ * @param data The MAPI data for the card
+ * @param fallbackItems The list of lockups to use as fallback media
+ * @param card The base card to apply the fallback media to
+ */
+export function applyMultiAppFallbackToCollectionCard(objectGraph, data, fallbackItems, card) {
+ const extraText = notesFromData(objectGraph, data, "short");
+ card.media = new TodayCardMediaMultiApp(fallbackItems, extraText);
+ card.style = "dark";
+ if (card.clickAction instanceof FlowAction) {
+ const updatedPageUrl = URL.from(card.clickAction.pageUrl);
+ updatedPageUrl.param(Parameters.showingFallbackMedia, "true");
+ card.clickAction.pageUrl = updatedPageUrl.build();
+ }
+}
+// MARK: - Editorial Artwork
+/**
+ * @param objectGraph The object graph for the app store
+ * @param cardDisplayStyle The display style for the card we're getting the key path for
+ * @returns The keypath used to find the editorial artwork for the card
+ */
+export function editorialArtKeyPathForCardDisplayStyle(objectGraph, cardDisplayStyle) {
+ if (objectGraph.client.isWatch) {
+ return "editorialArtwork.subscriptionHero";
+ }
+ else if (objectGraph.client.isVision) {
+ return "editorialArtwork.storyCenteredStatic16x9";
+ }
+ else {
+ switch (cardDisplayStyle) {
+ case TodayCardDisplayStyle.AppOfTheDay:
+ case TodayCardDisplayStyle.GameOfTheDay:
+ return "editorialArtwork.dayCard";
+ case TodayCardDisplayStyle.AppEventCard:
+ return "editorialArtwork.eventCard";
+ case TodayCardDisplayStyle.Video:
+ case TodayCardDisplayStyle.FullBleedImage:
+ return "editorialArtwork.mediaCard";
+ default:
+ return "editorialArtwork.generalCard";
+ }
+ }
+}
+/**
+ * @param objectGraph The object graph for the app store
+ * @param artworkData The artwork data to create the artwork from, from the cards media api data
+ * @param cropCode The crop code to use for the artwork, otherwise we use the correct crop for the platform
+ * @returns The artwork model for the card
+ */
+export function todayCardArtworkFromArtworkData(objectGraph, artworkData, cropCode) {
+ if (isNullOrEmpty(artworkData)) {
+ return null;
+ }
+ const artwork = artworkFromApiArtwork(objectGraph, artworkData, {
+ withJoeColorPlaceholder: true,
+ useCase: 15 /* ArtworkUseCase.TodayCardMedia */,
+ });
+ if (cropCode) {
+ artwork.crop = cropCode;
+ }
+ else if (objectGraph.client.isMac || objectGraph.client.isTV) {
+ // <rdar://problem/39155084> Articles: Use new crossover crop code for macOS story card art
+ artwork.crop = "fn";
+ }
+ else {
+ artwork.crop = "sr";
+ }
+ return artwork;
+}
+/**
+ * Returns a branded title `Artwork` model object from the API `editorialArtwork.contentLogoTrimmed` response.
+ * This artwork can be used in place of a text title for a Today Card.
+ * @param objectGraph The dependency graph for the App Store
+ * @param data The media API data to fetch the Artwork from
+ * @returns The branded title `Artwork` object, or `undefined` if a branded title could not be found
+ */
+export function brandedTitleArtworkForCard(objectGraph, data) {
+ const brandedTitleData = mediaAttributes.attributeAsDictionary(data, "editorialArtwork.contentLogoTrimmed");
+ return artworkFromApiArtwork(objectGraph, brandedTitleData, {
+ contentMode: ArtworkContentMode.scaleAspectFit,
+ allowingTransparency: true,
+ useCase: 17 /* ArtworkUseCase.TodayCardBrandedTitle */,
+ });
+}
+/**
+ * @param objectGraph The object graph for the app store
+ * @param data The media api data for the card
+ * @param cardConfig The configuration for the card being built
+ * @returns The `bgGradientKind` field from the correct artwork dictionary
+ */
+export function todayCardArtworkTitleBackingGradientForKey(objectGraph, data, cardConfig) {
+ const cardDisplayStyle = cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle);
+ const artworkKey = editorialArtKeyPathForCardDisplayStyle(objectGraph, cardDisplayStyle);
+ const artworkData = mediaAttributes.attributeAsDictionary(data, artworkKey);
+ if (!isDefinedNonNull(artworkData)) {
+ return null;
+ }
+ return asString(artworkData, "bgGradientKind");
+}
+/**
+ * @param objectGraph The object graph for the app store
+ * @param artworkData The artwork data from the media api data for a card, that allows us to determine the style
+ * @returns The light or dark style for this card, if we can determine it.
+ */
+export function todayCardStyleFromArtwork(objectGraph, artworkData) {
+ var _a;
+ if (isNullOrEmpty(artworkData)) {
+ return undefined;
+ }
+ const joeColors = joeColorSetFromData(artworkData);
+ const backgroundColor = joeColors.bgColor;
+ const hasGradient = ((_a = joeColors.textGradient) === null || _a === void 0 ? void 0 : _a.length) === 2;
+ if (!backgroundColor && !hasGradient) {
+ return undefined;
+ }
+ if (objectGraph.client.isiOS || objectGraph.client.isWeb) {
+ return cardStyleFromJoeColors(joeColors, "bgColor");
+ }
+ else if (hasGradient) {
+ // Gradient colors are the text color, so if the gradient is a dark color, then
+ // the environment is a light environment
+ return color.isDarkColor(joeColors.textGradient[0]) ? "light" : "dark";
+ }
+ else {
+ return color.isDarkColor(backgroundColor) ? "dark" : "light";
+ }
+}
+export function cardStyleFromJoeColorsWithoutFallback(joeColors, preferredColorKeypath = "bgColor") {
+ if (isNothing(joeColors)) {
+ return undefined;
+ }
+ if (isSome(joeColors === null || joeColors === void 0 ? void 0 : joeColors.textGradient) && joeColors.textGradient.length === 2) {
+ // Rare, but if gradient colors are present, defer to them
+ return color.isDarkColor(joeColors.textGradient[0]) ? "white" : "dark";
+ }
+ const preferredColor = joeColors[preferredColorKeypath];
+ if (isNothing(preferredColor)) {
+ return undefined;
+ }
+ const luminance = color.luminanceFrom(preferredColor);
+ if (luminance <= 0.1) {
+ return "dark";
+ }
+ else if (luminance >= 0.84) {
+ return "white";
+ }
+ else {
+ return "light";
+ }
+}
+export function cardStyleFromJoeColors(joeColors, preferredColorKeypath = "bgColor") {
+ var _a;
+ return (_a = cardStyleFromJoeColorsWithoutFallback(joeColors, preferredColorKeypath)) !== null && _a !== void 0 ? _a : "light";
+}
+// MARK: - Editorial Text
+/**
+ * Determines the heading for the card, were it to appear inline.
+ * If the source card heading is null, we default to the existing
+ * inline heading, if any.
+ * @param objectGraph The object graph for the app store
+ * @param card The today card we're collapsing the heading for
+ * @returns string The title to use for the card.
+ */
+export function collapsedHeadingForTodayCard(objectGraph, card) {
+ if (isDefinedNonNull(card.heading)) {
+ return card.heading.replace(/\n/g, " ");
+ }
+ return card.collapsedHeading;
+}
+// MARK: - Offers
+/**
+ * Determines an appropriate OfferStyle for a TodayCard to ensure correct
+ * contrast with the card's contents.
+ *
+ * @param cardStyle The today style for the card
+ * @returns an appropriate offer style to use for the button
+ */
+export function offerStyleForTodayCard(objectGraph, cardStyle) {
+ return "transparent";
+}
+/**
+ * Determines an appropriate OfferEnvironment for a TodayCard to ensure correct
+ * contrast with the card's contents.
+ *
+ * @param cardStyle The today style for the card
+ * @returns an appropriate offer environment to use for the button
+ */
+export function offerEnvironmentForTodayCard(cardStyle) {
+ if (cardStyle === "white") {
+ return "light";
+ }
+ else {
+ return "todayCard";
+ }
+}
+// MARK: - Hero Media
+/**
+ * For mac and tv platforms, we add hero media to the today cards.
+ *
+ * @param objectGraph The dependency graph for the app store
+ * @param card The today card we're adding hero media to
+ * @param cardConfig The config used to create this card
+ * @param data The media api data used to create this card
+ */
+export function addHeroMediaToTodayCardIfNecessary(objectGraph, card, cardConfig, data) {
+ const cardDisplayStyle = cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle);
+ // MacOS and tvOS: Hero
+ const heroArt = todayCardHeroArtForData(objectGraph, data, cardConfig.heroDisplayContext, cardDisplayStyle, cardConfig.prevailingCropCodes);
+ let artworks = [];
+ let videos = [];
+ if (isDefinedNonNull(heroArt)) {
+ artworks = [heroArt];
+ }
+ if (isDefinedNonNull(card.media) &&
+ (card.media instanceof TodayCardMediaVideo || card.media instanceof TodayCardMediaArtwork)) {
+ videos = card.media.videos;
+ }
+ const hero = new TodayCardMediaHero(artworks, videos);
+ const supportsHeroMedia = objectGraph.client.isMac ||
+ objectGraph.client.isTV ||
+ objectGraph.client.isWeb ||
+ (objectGraph.client.isVision && !cardConfig.isSearchContext) ||
+ preprocessor.GAMES_TARGET;
+ if (supportsHeroMedia && hero.isValid()) {
+ card.heroMedia = hero;
+ // Ensure we update the card styling. We prefer hero art, and fallback to hero video.
+ const heroArtworkData = mediaAttributes.attributeAsDictionary(data, heroMediaArtworkKeyForContext(objectGraph, cardConfig.heroDisplayContext, cardDisplayStyle, Object.keys(mediaAttributes.attributeAsDictionary(data, "editorialArtwork"))));
+ let style = todayCardStyleFromArtwork(objectGraph, heroArtworkData);
+ if (!isDefinedNonNull(style)) {
+ const heroVideoPreviewData = asDictionary(todayCardHeroVideoFromData(objectGraph, data), "previewFrame");
+ style = todayCardStyleFromArtwork(objectGraph, heroVideoPreviewData);
+ }
+ card.style = style;
+ }
+}
+/**
+ * For external links we will display the standard link overlay in place of whatever overlay we had previously
+ *
+ * @param card The today card we're adding hero media to
+ */
+export function addExternalLinkOverlayToTodayCardIfNecessary(card) {
+ if (card.clickAction instanceof ExternalUrlAction) {
+ card.overlay = new TodayCardActionOverlay(card.clickAction);
+ card.style = "white";
+ }
+}
+/**
+ * @param objectGraph The dependency graph for the app store
+ * @param data The media api data to search for hero art in
+ * @param context The context in which this hero media will be displayed
+ * @param displayStyle The display style of the card
+ * @param prevailingCropCodes The prevailing crop code to use for inline hero media
+ * @returns The artwork to be used in the hero position for a today card
+ */
+export function todayCardHeroArtForData(objectGraph, data, context, displayStyle, prevailingCropCodes) {
+ const heroArtworkKey = heroMediaArtworkKeyForContext(objectGraph, context, displayStyle, Object.keys(mediaAttributes.attributeAsDictionary(data, "editorialArtwork")));
+ const heroArtworkData = mediaAttributes.attributeAsDictionary(data, heroArtworkKey);
+ return todayCardArtworkFromArtworkData(objectGraph, heroArtworkData, heroMediaArtworkCropForContext(objectGraph, context, objectPathToString(heroArtworkKey), prevailingCropCodes));
+}
+/**
+ * @param objectGraph The dependency graph for the app store
+ * @param context The context in which this hero media will be displayed
+ * @param displayStyle The display style of the card
+ * @returns The key to use to find the hero media artwork
+ */
+function heroMediaArtworkKeyForContext(objectGraph, context, displayStyle, availableArtworkKeys) {
+ if (context === "article" && (objectGraph.client.isMac || objectGraph.client.isiOS)) {
+ // Use "iOS" art
+ return editorialArtKeyPathForCardDisplayStyle(objectGraph, displayStyle);
+ }
+ else if (objectGraph.client.isVision) {
+ return "editorialArtwork.heroStatic16x9";
+ }
+ else {
+ if (objectGraph.client.isTV && availableArtworkKeys.includes("categoryDetailStatic16x9")) {
+ return "editorialArtwork.categoryDetailStatic16x9";
+ }
+ return "editorialArtwork.crossoverCard";
+ }
+}
+/**
+ * @param objectGraph The dependency graph for the app store
+ * @param context The context in which this hero media will be displayed
+ * @param prevailingCropCodes The crop code to use if we're not overriding it
+ * @returns The crop code to use for hero artwork in a given context
+ */
+function heroMediaArtworkCropForContext(objectGraph, context, heroArtworkKey, prevailingCropCodes) {
+ if (context === HeroMediaDisplayContext.Article && objectGraph.client.isMac) {
+ // Use "iOS" art crop code
+ return "fn";
+ }
+ else if (context === HeroMediaDisplayContext.Article &&
+ objectGraph.client.isTV &&
+ heroArtworkKey === "editorialArtwork.categoryDetailStatic16x9") {
+ return "sr";
+ }
+ else {
+ return prevailingCropCodes === null || prevailingCropCodes === void 0 ? void 0 : prevailingCropCodes.defaultCrop;
+ }
+}
+/**
+ * @param objectGraph The dependency graph for the app store
+ * @param data The media api data to search for hero video in
+ * @returns The video to be used in the hero position for a today card
+ */
+function todayCardHeroVideoFromData(objectGraph, data) {
+ let videoData;
+ const videoData4x3 = mediaAttributes.attributeAsDictionary(data, "editorialVideo.storeFrontVideo4x3");
+ const videoData16x9 = mediaAttributes.attributeAsDictionary(data, "editorialVideo.storeFrontVideo");
+ // <rdar://problem/34339194> Today: Video Card: Incorrect key for 4x3 videos
+ // On pad prefer 4x3 fallback to 16x9 and on phone vice versa
+ if (objectGraph.client.isPad || objectGraph.client.screenSize.isEqualTo(screenSizeIPhone134)) {
+ videoData = videoData4x3 || videoData16x9;
+ }
+ else {
+ videoData = videoData16x9 || videoData4x3;
+ }
+ return videoData;
+}
+// MARK: - Crosslinks
+/**
+ * Determines the cross link subtitle from card data.
+ */
+export function crossLinkSubtitleFromData(objectGraph, data) {
+ // Start with short notes
+ let subtitle = editorialNotesFromData(objectGraph, data, "short");
+ // Fallback to name
+ if (!isDefinedNonNullNonEmpty(subtitle)) {
+ subtitle = notesFromData(objectGraph, data, "name");
+ }
+ // Fallback to label
+ if (!isDefinedNonNullNonEmpty(subtitle)) {
+ const cardDisplayStyle = mediaAttributes.attributeAsString(data, "displayStyle");
+ if (cardDisplayStyle === TodayCardDisplayStyle.AppOfTheDay ||
+ cardDisplayStyle === TodayCardDisplayStyle.GameOfTheDay) {
+ subtitle = mediaAttributes.attributeAsString(data, "label");
+ }
+ }
+ return subtitle;
+}
+// MARK: - Client Identifier Overrids
+function shouldRespectClientIdentifierOverride(objectGraph, data, clientIdentifier, cardConfig) {
+ var _a;
+ if (clientIdentifier === "com.apple.visionproapp" /* ClientIdentifier.VisionCompanion */ || clientIdentifier === "VisionAppStore" /* ClientIdentifier.VisionAppStore */) {
+ // If using the companion app, it's expected that we always respect the given identifier.
+ return true;
+ }
+ const relatedContent = relatedCardContentsContentsFromData(objectGraph, data);
+ const cardDisplayStyle = cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle);
+ // Do not apply the override if this is not a Content type card.
+ const cardTypesRequiringClientIdentifierOverride = new Set([
+ TodayCardDisplayStyle.AppEventCard,
+ TodayCardDisplayStyle.Grid,
+ TodayCardDisplayStyle.InAppPurchase,
+ TodayCardDisplayStyle.List,
+ TodayCardDisplayStyle.NumberedList,
+ TodayCardDisplayStyle.River,
+ TodayCardDisplayStyle.ShortImage,
+ TodayCardDisplayStyle.SingleApp,
+ ]);
+ if (!cardTypesRequiringClientIdentifierOverride.has(cardDisplayStyle)) {
+ return false;
+ }
+ // Grids with >= 6 items are unsupported
+ if (relatedContent.length >= 6 && cardDisplayStyle === TodayCardDisplayStyle.Grid) {
+ return false;
+ }
+ // Content cards with Artwork are unsupported
+ const artworkKey = editorialArtKeyPathForCardDisplayStyle(objectGraph, cardDisplayStyle);
+ if (!mediaAttributes.attributeAsBooleanOrFalse(data, "ignoreEditorialArt") &&
+ todayCardArtworkFromArtworkData(objectGraph, mediaAttributes.attributeAsDictionary(data, artworkKey), (_a = cardConfig.prevailingCropCodes) === null || _a === void 0 ? void 0 : _a.defaultCrop)) {
+ return false;
+ }
+ // Content cards with iAP are unsupported
+ if (inAppPurchaseDataFromRelatedContent(objectGraph, relatedContent)) {
+ return false;
+ }
+ return true;
+}
+function inAppPurchaseDataFromRelatedContent(objectGraph, relatedContent) {
+ if (relatedContent.length === 1) {
+ const contentData = relatedContent[0];
+ if (contentData.type === "in-apps") {
+ return contentData;
+ }
+ }
+ return null;
+}
+/**
+ * If a card should be using the OTD style we need to make sure we set that on the card
+ *
+ * @param card The today card we're adding the style to
+ * @param cardConfig The config used to create this card
+ */
+function addOTDStyleToCardIfNecessary(card, cardConfig) {
+ if (isNothing(card.media)) {
+ return;
+ }
+ card.media.otdTextStyle = cardConfig.useOTDTextStyle;
+}
+/**
+ * Check if this card should have the flip and blur enabled, this is the case when a card has videos.
+ * @param objectGraph The dependency graph for the app store
+ * @param card The today card we're modifying
+ */
+function enableFlipAndBlurIfNecessary(objectGraph, card) {
+ const cardMedia = card.media;
+ const cardMediaHasArtwork = cardMedia instanceof TodayCardMediaWithArtwork;
+ if (!cardMediaHasArtwork) {
+ return;
+ }
+ const cardMdiaWithArtwork = cardMedia;
+ const hasVideos = isDefinedNonNullNonEmpty(cardMdiaWithArtwork.videos);
+ card.supportsMediaMirroring = hasVideos;
+}
+/**
+ * @param data The media API data used to determine the intention of the card
+ * @param cardConfig The configuration for the card
+ * @returns Whether this app is an App of the Day or Game of the Day
+ */
+export function isCardOTDIntention(data, cardConfig) {
+ let otdIntention = mediaAttributes.attributeAsString(data, "ofTheDayIntent");
+ if (isNothing(otdIntention)) {
+ const cardDisplayStyle = cardDisplayStyleFromData(data, cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.coercedCollectionTodayCardDisplayStyle);
+ switch (cardDisplayStyle) {
+ case TodayCardDisplayStyle.AppOfTheDay:
+ otdIntention = OfTheDayIntention.AppOfTheDay;
+ break;
+ case TodayCardDisplayStyle.GameOfTheDay:
+ otdIntention = OfTheDayIntention.GameOfTheDay;
+ break;
+ default:
+ break;
+ }
+ }
+ return otdIntention === OfTheDayIntention.AppOfTheDay || otdIntention === OfTheDayIntention.GameOfTheDay;
+}
+//# sourceMappingURL=today-card-util.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/today-controller-util.js b/node_modules/@jet-app/app-store/tmp/src/common/today/today-controller-util.js
new file mode 100644
index 0000000..98f9fbc
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/today-controller-util.js
@@ -0,0 +1,534 @@
+import { isNothing, isSome } from "@jet/environment";
+import * as validation from "@jet/environment/json/validation";
+import { FetchTimingMetricsBuilder } from "@jet/environment/metrics";
+import * as models from "../../api/models";
+import * as impressionDemotion from "../../common/personalization/on-device-impression-demotion";
+import { asArrayOrEmpty, asBoolean, asString, isDefinedNonNull, isDefinedNonNullNonEmpty, isNullOrEmpty, } from "../../foundation/json-parsing/server-data";
+import { hasAttributes } from "../../foundation/media/attributes";
+import * as mediaDataFetching from "../../foundation/media/data-fetching";
+import * as mediaDataStructure from "../../foundation/media/data-structure";
+import { fetchData } from "../../foundation/media/network";
+import { Parameters, Path } from "../../foundation/network/url-constants";
+import { URL } from "../../foundation/network/urls";
+import { optional, required } from "../../foundation/util/promise-util";
+import { AppEventsAttributes } from "../../gameservicesui/src/foundation/media-api/requests/recommendation-request-types";
+import { applySearchAdMissedOpportunityToShelvesIfNeeded, eligibleSlotPositionsForAdPlacement, iadInfoFromOnDeviceAdResponse, isAdPlacementEnabled, } from "../ads/ad-common";
+import * as adIncidents from "../ads/ad-incident-recorder";
+import { fetchAds, parallelOrganicRequestDidFinish } from "../ads/on-device-ad-fetch";
+import * as appPromotionsCommon from "../app-promotions/app-promotions-common";
+import { fetchPageWithAdditionalPageRequirements } from "../builders/additional-page-requirement-util";
+import { pageRouter } from "../builders/routing";
+import { shouldUsePrerenderedIconArtwork } from "../content/content";
+import { shelfForFooterButtons, shelfForTermsAndConditions, shelfForUnifiedMessage } from "../grouping/grouping-common";
+import { newLocationTracker } from "../metrics/helpers/location";
+import { iAdInformationFromMediaApiResponse } from "../metrics/helpers/models";
+import { addMetricsEventsToPageWithInformation, metricsPageInformationFromMediaApiResponse, } from "../metrics/helpers/page";
+import { combinedRecoMetricsDataFromMetricsData } from "../metrics/helpers/util";
+import * as onDevicePersonalization from "../personalization/on-device-personalization";
+import { fetchTodayRecommendationsWithTimeout, isTodayTabArcadePersonalizationAvailable, } from "../personalization/on-device-recommendations-today";
+import { setPreviewPlatform } from "../preview-platform";
+import * as productPageVariants from "../product-page/product-page-variants";
+import { newPageRefreshControllerFromResponse } from "../refresh/page-refresh-controller";
+import { storeCohortIdForUserFromResponse } from "../search/landing/search-landing-cohort";
+import { FlattenedTodayItemType, createAdShelfForTodayPageContextIfNecessary, feedPreviewUrlFromFlattenedTodayItems, flattenTodayModules, nextFlattenedItemIsHydrated, todayShelfForEditorialItem, todayShelfForEditorialItemGroup, } from "./today-parse-util";
+import { TodayPageContext } from "./today-types";
+/**
+ * Keys used to manage additional data needed to render the today page.
+ *
+ * - onboardingCards: key used to fetch the onboarding cards for the feed.
+ *
+ * - ads: Key used for fetching the today placement ads
+ */
+export var TodayControllerAdditionalDataKey;
+(function (TodayControllerAdditionalDataKey) {
+ TodayControllerAdditionalDataKey["OnboardingCards"] = "onboardingCards";
+ TodayControllerAdditionalDataKey["Ads"] = "ads";
+ TodayControllerAdditionalDataKey["ODP"] = "ODP";
+ TodayControllerAdditionalDataKey["AMDData"] = "amdData";
+})(TodayControllerAdditionalDataKey || (TodayControllerAdditionalDataKey = {}));
+export function defaultPlatforms(objectGraph) {
+ return mediaDataFetching.defaultAdditionalPlatformsForClient(objectGraph);
+}
+/// Resource attributes to include for `editorial-items` fetched for displaying in today page.
+export function defaultAttributes(objectGraph) {
+ const attributes = [
+ "editorialArtwork",
+ "editorialVideo",
+ "enrichedEditorialNotes",
+ "minimumOSVersion",
+ "headerName",
+ "headerBadge",
+ "headerTagline",
+ "editorialClientParams",
+ "requiredCapabilities",
+ "shortEditorialNotes",
+ ];
+ if (objectGraph.bag.enableUpdatedAgeRatings) {
+ attributes.push("ageRating");
+ }
+ if (shouldUsePrerenderedIconArtwork(objectGraph)) {
+ attributes.push("iconArtwork");
+ }
+ return attributes;
+}
+export function prepareRequest(objectGraph, request, isPageRequest) {
+ var _a, _b;
+ request
+ .includingAgeRestrictions()
+ .includingAdditionalPlatforms(defaultPlatforms(objectGraph))
+ .includingAttributes(defaultAttributes(objectGraph))
+ .includingScopedAttributes("editorial-item-groups", ["editorialClientParams"])
+ .includingScopedRelationships("editorial-items", ["primary-content", "header-contents"])
+ .includingScopedRelationships("editorial-item-groups", ["header-contents"])
+ .enablingFeature("editorialItemGroups")
+ .usingCustomAttributes(productPageVariants.shouldFetchCustomAttributes(objectGraph))
+ .includingRelationshipsForUpsell(true);
+ if (isPageRequest) {
+ request.includingAssociateKeys("editorial-item-groups", ["recommendations", "editorial-cards"]);
+ request.includingAssociateKeys("editorial-items", ["editorial-cards"]);
+ }
+ if (appPromotionsCommon.appEventsAreEnabled(objectGraph)) {
+ request.enablingFeature("appEvents");
+ request.addingQuery("meta", "personalizationData");
+ request.includingScopedRelationships("app-events", ["app"]);
+ request.includingScopedAttributes("app-events", AppEventsAttributes);
+ }
+ if (appPromotionsCommon.appOfferItemsAreEnabled(objectGraph)) {
+ request.enablingFeature("offerItems");
+ if (isPageRequest) {
+ request.includingKindsKeys("offer-items", ["winback"]);
+ }
+ }
+ if (impressionDemotion.isImpressionDemotionAvailable(objectGraph)) {
+ request.enablingFeature("eiGroupEISelectionOnDevice");
+ }
+ if (isAdPlacementEnabled(objectGraph, "today")) {
+ request.enablingFeature("adSupport");
+ }
+ request.enablingFeature("heroStyles");
+ const displayDeviceDrivenContent = (_b = (_a = objectGraph.userDefaults) === null || _a === void 0 ? void 0 : _a.bool("displayDeviceDrivenContent")) !== null && _b !== void 0 ? _b : false;
+ if (objectGraph.bag.enableDeviceDrivenDiscoveryContent &&
+ displayDeviceDrivenContent &&
+ request.resourceType === "today") {
+ request.addingQuery("pairedDevices", "visionPro");
+ }
+}
+// MARK: - Page Fetching
+export function generateTodayPageRequest(objectGraph) {
+ // Note: The sparse limit is now 2 because story groups will include all of their content which means
+ // if we continue to use the original 6 count we'll end up with way too many EI's
+ // rdar://108635958 ([Network Launch | 283ms | 19%] App Store - Dawn - D33: 21A236 vs 20E245 network-warm-launch:
+ // extendedLaunchTime is 1731.0ms vs 1448.0ms)
+ const sparseCount = objectGraph.client.isWeb ? 40 : 1;
+ const sparseLimit = objectGraph.client.isWeb ? 40 : 2;
+ const mediaApiRequest = new mediaDataFetching.Request(objectGraph)
+ .forType("today")
+ .withSparseCount(sparseCount)
+ .withSparseLimit(sparseLimit);
+ return setPreviewPlatform(objectGraph, mediaApiRequest);
+}
+/**
+ * @param objectGraph The object graph for the AppStore.
+ * @param onboardingCardIds The onboardingCard ids to fetch.
+ * @returns The map of additional page requirements.
+ */
+function generateAdditionalPageRequirements(objectGraph, onboardingCardIds) {
+ const additionalRequirements = {};
+ /**
+ * Currently, to avoid double filtering, the fix for:
+ * <rdar://problem/45031644> If an onboarding card is empty don't add it to the request
+ * depends on the `Request` object filtering IDs from under caller. The caller is expected to perform some validation to
+ * guarantee that this `Request` object is not malformed after it was processed here, and it can still build a valid URL.
+ *
+ * This mechanic is undesired, and once this filtering is removed from `Request`, the caller should filter empty onboarding
+ * IDs via an explicit method instead before instantiating an malformed `Request`.
+ */
+ if (isDefinedNonNullNonEmpty(onboardingCardIds)) {
+ const onboardingCardsRequest = new mediaDataFetching.Request(objectGraph)
+ .withIdsOfType(onboardingCardIds, "editorial-items")
+ .includingAdditionalPlatforms(defaultPlatforms(objectGraph))
+ .includingAttributes(defaultAttributes(objectGraph));
+ additionalRequirements[TodayControllerAdditionalDataKey.OnboardingCards] = optional(fetchData(objectGraph, onboardingCardsRequest, {}));
+ }
+ if (isAdPlacementEnabled(objectGraph, "today")) {
+ additionalRequirements[TodayControllerAdditionalDataKey.Ads] = optional(fetchAds(objectGraph, "today"));
+ }
+ if (isTodayTabArcadePersonalizationAvailable(objectGraph)) {
+ additionalRequirements[TodayControllerAdditionalDataKey.ODP] = optional(fetchTodayRecommendationsWithTimeout(objectGraph, undefined));
+ }
+ const amsEngagement = objectGraph.amsEngagement;
+ if (amsEngagement && impressionDemotion.isImpressionDemotionAvailable(objectGraph)) {
+ const request = {
+ timeout: 1000,
+ eventType: impressionDemotion.AMSEngagementAppStoreEventKey,
+ tab: "today",
+ };
+ additionalRequirements[TodayControllerAdditionalDataKey.AMDData] = optional(amsEngagement.performRequest(request));
+ }
+ return additionalRequirements;
+}
+export async function fetchTodayPage(objectGraph, options) {
+ const router = objectGraph.required(pageRouter);
+ const isTodayTestPage = options.url === "x-as3-internal:/today/test";
+ const isTodayCardPreviewPage = options.url.indexOf(Path.todayCardPreview) !== -1;
+ if (isTodayTestPage) {
+ return await router.fetchPage(objectGraph, options.url, models.TodayPage);
+ }
+ else if (isTodayCardPreviewPage) {
+ const dataFetchUrl = URL.from(options.url);
+ dataFetchUrl.param(Parameters.fetchData, "true");
+ return await router.fetchPage(objectGraph, dataFetchUrl.build(), models.TodayPage);
+ }
+ else {
+ const request = generateTodayPageRequest(objectGraph);
+ prepareRequest(objectGraph, request, true);
+ const requestHeaders = isSome(options.experimentIdHeader)
+ ? { "X-Apple-User-Experiment-Ids": options.experimentIdHeader }
+ : undefined;
+ return await buildTodayPageFromRequest(objectGraph, request, requestHeaders, options);
+ }
+}
+export async function buildTodayPageFromRequest(objectGraph, request, requestHeaders, options) {
+ var _a;
+ const fetchTimingMetricsBuilder = new FetchTimingMetricsBuilder();
+ const modifiedObjectGraph = objectGraph.addingFetchTimingMetricsBuilder(fetchTimingMetricsBuilder);
+ const primaryDataFetchPromise = fetchData(modifiedObjectGraph, request, {
+ headers: requestHeaders !== null && requestHeaders !== void 0 ? requestHeaders : undefined,
+ });
+ // Should this be needed on web, this should have a .catch() added to avoid
+ // an unhandled rejection.
+ if (!objectGraph.client.isWeb) {
+ void primaryDataFetchPromise.then((response) => {
+ if (isAdPlacementEnabled(objectGraph, "today")) {
+ parallelOrganicRequestDidFinish(objectGraph, "today");
+ }
+ storeCohortIdForUserFromResponse(objectGraph, objectGraph.user.dsid, response);
+ });
+ }
+ const primaryDataFetchInput = required(primaryDataFetchPromise);
+ const additionalRequirements = generateAdditionalPageRequirements(objectGraph, (_a = options === null || options === void 0 ? void 0 : options.onboardingCardIds) !== null && _a !== void 0 ? _a : []);
+ const pageDataRequirements = await fetchPageWithAdditionalPageRequirements(objectGraph, primaryDataFetchInput, additionalRequirements);
+ return fetchTimingMetricsBuilder.measureModelConstruction(() => {
+ var _a, _b;
+ const onboardingCardsResponse = pageDataRequirements.additionalData[TodayControllerAdditionalDataKey.OnboardingCards];
+ const onboardingCards = mediaDataStructure.dataCollectionFromDataContainer(onboardingCardsResponse);
+ const adsResponse = pageDataRequirements.additionalData[TodayControllerAdditionalDataKey.Ads];
+ const todayRecommendations = pageDataRequirements.additionalData[TodayControllerAdditionalDataKey.ODP];
+ const amdData = pageDataRequirements.additionalData[TodayControllerAdditionalDataKey.AMDData];
+ const flattenedTodayItems = flattenTodayModules(objectGraph, (_b = (_a = pageDataRequirements.primaryPageData.results) === null || _a === void 0 ? void 0 : _a.data) !== null && _b !== void 0 ? _b : [], todayRecommendations, onboardingCards, adsResponse);
+ return buildTodayPage(modifiedObjectGraph, flattenedTodayItems, pageDataRequirements.primaryPageData, adsResponse, amdData);
+ });
+}
+// MARK: - Today Page Creation
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param flattenedTodayItems The flattened Today items used to render the page
+ * @param primaryPageData The primary page data for the Today page
+ * @param adsResponse The response from ad platforms
+ * @returns A TodayPage model
+ */
+export function buildTodayPage(objectGraph, flattenedTodayItems, primaryPageData, adsResponse, amdResponse) {
+ return validation.context("renderPage", () => {
+ if (isNullOrEmpty(flattenedTodayItems)) {
+ return null;
+ }
+ const pageContext = createTodayPageContext(objectGraph, flattenedTodayItems, primaryPageData, adsResponse, amdResponse);
+ const todayPage = todayPageFromPageContext(objectGraph, pageContext);
+ todayPage.shelves.splice(0, 0, shelfForUnifiedMessage(objectGraph, "todayPageHeader"));
+ return todayPage;
+ });
+}
+/**
+ * From the provided today page context, create a new today page with the hydrated content
+ * @param objectGraph The dependency graph for the App Store
+ * @param pageContext The page context for the Today page
+ */
+export function todayPageFromPageContext(objectGraph, pageContext) {
+ var _a;
+ const shelves = [];
+ const feedPreviewUrl = feedPreviewUrlFromFlattenedTodayItems(objectGraph, pageContext.remainingContent);
+ let hasMoreHydratedContent = nextFlattenedItemIsHydrated(pageContext.remainingContent);
+ const isPageLoadNotPaginated = (_a = pageContext.remainingContent[0]) === null || _a === void 0 ? void 0 : _a.isFirstItemInModule;
+ while (hasMoreHydratedContent) {
+ const todayItem = pageContext.remainingContent.shift();
+ if (isNothing(todayItem)) {
+ hasMoreHydratedContent = false;
+ break;
+ }
+ const preItemAdShelf = createAdShelfForTodayPageContextIfNecessary(objectGraph, pageContext);
+ if (isSome(preItemAdShelf)) {
+ shelves.push(preItemAdShelf);
+ }
+ pageContext.currentTodayItem = todayItem;
+ let todayItemShelf = null;
+ switch (todayItem.type) {
+ case FlattenedTodayItemType.EditorialItem:
+ todayItemShelf = todayShelfForEditorialItem(objectGraph, todayItem, pageContext);
+ break;
+ case FlattenedTodayItemType.EditorialItemGroup:
+ todayItemShelf = todayShelfForEditorialItemGroup(objectGraph, todayItem, pageContext);
+ break;
+ default:
+ break;
+ }
+ pageContext.currentTodayItem = undefined;
+ if (isSome(todayItemShelf)) {
+ shelves.push(todayItemShelf);
+ }
+ const postItemAdShelf = createAdShelfForTodayPageContextIfNecessary(objectGraph, pageContext);
+ if (isSome(postItemAdShelf)) {
+ shelves.push(postItemAdShelf);
+ }
+ hasMoreHydratedContent = nextFlattenedItemIsHydrated(pageContext.remainingContent);
+ }
+ if (isPageLoadNotPaginated) {
+ applySearchAdMissedOpportunityToShelvesIfNeeded(objectGraph, shelves, "today", "today", pageContext.pageInformation);
+ }
+ const todayPage = new models.TodayPage(shelves);
+ const todayTab = objectGraph.bag.tabsStandard.find((tab) => tab.id === "today");
+ const todayTabTitle = asString(todayTab, "title");
+ const tabImageIdentifier = asString(todayTab, "image-identifier");
+ const includeTitleDetail = isNothing(tabImageIdentifier) || tabImageIdentifier === "doc.text.image";
+ const todayTitle = todayTabTitle !== null && todayTabTitle !== void 0 ? todayTabTitle : objectGraph.loc.string("PAGE_TITLE_TODAY");
+ todayPage.title = todayTitle;
+ todayPage.tabTitle = todayTitle;
+ todayPage.titleDetail = includeTitleDetail ? todayPageTitleDetail(objectGraph) : undefined;
+ todayPage.shortTitleDetail = includeTitleDetail ? todayPageShortTitleDetail(objectGraph) : undefined;
+ todayPage.longTitle = includeTitleDetail ? todayPageLongTitle(objectGraph) : undefined;
+ todayPage.feedPreviewUrl = feedPreviewUrl;
+ if (isDefinedNonNullNonEmpty(pageContext.remainingContent)) {
+ pageContext.pageHasDisplayedContent =
+ pageContext.pageHasDisplayedContent ||
+ shelves.some((shelf) => {
+ isDefinedNonNullNonEmpty(shelf.items);
+ });
+ todayPage.nextPage = pageContext;
+ }
+ else if (!objectGraph.client.isWeb) {
+ footerShelvesForTodayPage(objectGraph, pageContext.pageInformation, pageContext.locationTracker).forEach((shelf) => {
+ todayPage.shelves.push(shelf);
+ });
+ }
+ if (isAdPlacementEnabled(objectGraph, "today")) {
+ // Ad Incidents
+ todayPage.adIncidents = adIncidents.recordedIncidents(objectGraph, pageContext.adIncidentRecorder);
+ }
+ addMetricsEventsToPageWithInformation(objectGraph, todayPage, pageContext.pageInformation);
+ return todayPage;
+}
+/**
+ * Fetch the next batch of content for the Today page, and hydrate the page context
+ * @param objectGraph The dependency graph for the App Store
+ * @param pageContext The page context for the Today page
+ * @returns
+ */
+export async function hydrateNextBatchOfContent(objectGraph, pageContext) {
+ const batchLoadSize = pageContext.pageHasDisplayedContent ? 6 : 12;
+ let continueAddingContent = true;
+ let unhydratedItemIndex = 0;
+ let unhydratedContents = [];
+ while (continueAddingContent) {
+ const nextContent = pageContext.remainingContent[unhydratedItemIndex];
+ switch (nextContent.type) {
+ case FlattenedTodayItemType.EditorialItem:
+ unhydratedContents.push(nextContent.data);
+ break;
+ case FlattenedTodayItemType.EditorialItemGroup:
+ unhydratedContents.push(nextContent.data);
+ const groupItems = asArrayOrEmpty(nextContent.data, "meta.associations.recommendations.data");
+ unhydratedContents = [...unhydratedContents, ...groupItems];
+ break;
+ default:
+ break;
+ }
+ unhydratedItemIndex++;
+ continueAddingContent =
+ unhydratedContents.length < batchLoadSize && unhydratedItemIndex < pageContext.remainingContent.length;
+ }
+ // MAPI request for module slice's contents
+ const requestedContentIds = new Set(unhydratedContents.map((content) => content.id));
+ const contentsFetchRequest = new mediaDataFetching.Request(objectGraph, unhydratedContents, true, [
+ "recommendations",
+ "editorial-cards",
+ ]);
+ prepareRequest(objectGraph, contentsFetchRequest, false);
+ const fetchedData = await fetchData(objectGraph, contentsFetchRequest, { allowEmptyDataResponse: true });
+ const fetchedDataMap = {};
+ for (const data of fetchedData.data) {
+ fetchedDataMap[data.id] = data;
+ }
+ // Hydrate the page context with the fetched data
+ for (const content of pageContext.remainingContent) {
+ hydrateFlattenedItemWithFetchedDataMap(content, fetchedDataMap);
+ }
+ /// Now take a pass and remove any items that are still unhydrated after performing the fetch
+ pageContext.remainingContent = pageContext.remainingContent.filter((content) => {
+ return content.isDataHydrated || !requestedContentIds.has(content.data.id);
+ });
+ return pageContext;
+}
+/**
+ * For a given today item, take the fetched MAPI data and apply that to the item.
+ *
+ * @param flattenedItem The today item we're going to be hydrating
+ * @param fetchedDataMap The mapping from id to fetched media api data.
+ */
+function hydrateFlattenedItemWithFetchedDataMap(flattenedItem, fetchedDataMap) {
+ var _a, _b, _c;
+ switch (flattenedItem.type) {
+ case FlattenedTodayItemType.EditorialItem:
+ if (isDefinedNonNullNonEmpty(fetchedDataMap[flattenedItem.data.id])) {
+ flattenedItem.data = {
+ ...flattenedItem.data,
+ ...fetchedDataMap[flattenedItem.data.id],
+ };
+ }
+ flattenedItem.isDataHydrated = hasAttributes(flattenedItem.data);
+ break;
+ case FlattenedTodayItemType.EditorialItemGroup:
+ if (isDefinedNonNullNonEmpty(fetchedDataMap[flattenedItem.data.id])) {
+ flattenedItem.data = {
+ ...flattenedItem.data,
+ attributes: (_a = fetchedDataMap[flattenedItem.data.id]) === null || _a === void 0 ? void 0 : _a.attributes,
+ relationships: (_b = fetchedDataMap[flattenedItem.data.id]) === null || _b === void 0 ? void 0 : _b.relationships,
+ meta: {
+ ...flattenedItem.data.meta,
+ ...(_c = fetchedDataMap[flattenedItem.data.id]) === null || _c === void 0 ? void 0 : _c.meta,
+ },
+ };
+ }
+ flattenedItem.isDataHydrated = hasAttributes(flattenedItem.data);
+ break;
+ default:
+ break;
+ }
+}
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param todayItems The list of all the items displayed on the today page.
+ * @param todayResponse The response from the today endpoint
+ * @param adResponse The response from the ads service
+ * @returns The context to use when parsing today cards
+ */
+function createTodayPageContext(objectGraph, todayItems, todayResponse, adsResponse, amdResponse) {
+ var _a, _b, _c;
+ const pageInformation = metricsPageInformationFromMediaApiResponse(objectGraph, "Today", "today", todayResponse, null, (_a = iadInfoFromOnDeviceAdResponse(objectGraph, "today", adsResponse, todayItems)) !== null && _a !== void 0 ? _a : iAdInformationFromMediaApiResponse(objectGraph, "today", todayResponse, (_b = adsResponse === null || adsResponse === void 0 ? void 0 : adsResponse.onDeviceAd) === null || _b === void 0 ? void 0 : _b.positionInfo, todayItems));
+ const onDevicePersonalizationMetricsData = onDevicePersonalization.metricsData(objectGraph);
+ pageInformation.recoMetricsData = combinedRecoMetricsDataFromMetricsData(pageInformation.recoMetricsData, null, onDevicePersonalizationMetricsData);
+ const context = new TodayPageContext(todayItems, pageInformation, newLocationTracker(), newPageRefreshControllerFromResponse(todayResponse), impressionDemotion.impressionEventsFromData(objectGraph, amdResponse));
+ if (isAdPlacementEnabled(objectGraph, "today")) {
+ const eligibleAdPositions = eligibleSlotPositionsForAdPlacement(objectGraph, "today");
+ if (isSome(eligibleAdPositions)) {
+ // The slot as provided by ad platforms is one-based - adjust it so we're working with zero-based numbers.
+ context.eligibleAdLocations = eligibleAdPositions.map((position) => position.slot);
+ }
+ const adIncidentRecorder = adIncidents.newRecorder(objectGraph, pageInformation.iAdInfo);
+ adIncidents.recordAdResponseEventsIfNeeded(objectGraph, adIncidentRecorder, adsResponse);
+ context.adIncidentRecorder = adIncidentRecorder;
+ context.adPlacementBehavior = getAdPlacementBehaviorForPage(todayItems);
+ if (isNothing(adsResponse === null || adsResponse === void 0 ? void 0 : adsResponse.failureReason)) {
+ context.adData = mediaDataStructure.dataFromDataContainer(objectGraph, adsResponse === null || adsResponse === void 0 ? void 0 : adsResponse.mediaResponse);
+ const rawPositionInfo = (_c = adsResponse === null || adsResponse === void 0 ? void 0 : adsResponse.onDeviceAd) === null || _c === void 0 ? void 0 : _c.positionInfo;
+ if (isDefinedNonNullNonEmpty(rawPositionInfo)) {
+ // The slot as provided by ad platforms is one-based - adjust it so we're working with zero-based numbers.
+ const adjustedAdLocation = rawPositionInfo.slot - 1;
+ context.adLocation = adjustedAdLocation;
+ }
+ }
+ }
+ return context;
+}
+function getAdPlacementBehaviorForPage(todayItems) {
+ let adPlacementBehavior = models.AdPlacementBehavior.insertIntoShelf;
+ for (const todayItem of todayItems) {
+ let todayItemAdPlacementBehavior = adPlacementBehavior;
+ switch (todayItem.type) {
+ case FlattenedTodayItemType.EditorialItem:
+ todayItemAdPlacementBehavior = getAdPlacementBehaviorForData(todayItem.data);
+ break;
+ case FlattenedTodayItemType.EditorialItemGroup:
+ const groupItems = asArrayOrEmpty(todayItem.data, "meta.associations.recommendations.data");
+ if (isDefinedNonNullNonEmpty(groupItems)) {
+ for (const groupItem of groupItems) {
+ todayItemAdPlacementBehavior = getAdPlacementBehaviorForData(groupItem);
+ if (todayItemAdPlacementBehavior !== models.AdPlacementBehavior.insertIntoShelf) {
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ if (todayItemAdPlacementBehavior !== models.AdPlacementBehavior.insertIntoShelf) {
+ adPlacementBehavior = todayItemAdPlacementBehavior;
+ break;
+ }
+ }
+ return adPlacementBehavior;
+}
+function getAdPlacementBehaviorForData(data) {
+ const replaceIfAdPresent = asBoolean(data, "meta.personalizationData.replaceIfAdPresent");
+ if (isDefinedNonNull(replaceIfAdPresent) && replaceIfAdPresent) {
+ return models.AdPlacementBehavior.replaceOrganic;
+ }
+ else if (isDefinedNonNull(replaceIfAdPresent) && !replaceIfAdPresent) {
+ return models.AdPlacementBehavior.dropAd;
+ }
+ else {
+ return models.AdPlacementBehavior.insertIntoShelf;
+ }
+}
+/**
+ * Return the first shelf title for the today page. This is used to generate the tab title.
+ * @param objectGraph The dependency graph of the app store
+ */
+function todayPageTitleDetail(objectGraph) {
+ const now = new Date();
+ const dateFormat = objectGraph.loc.string("Today.ShelfTitle.DateFormat");
+ return objectGraph.loc.formatDate(dateFormat, now);
+}
+/**
+ * Return the first shelf title for the today page on iPad. This is used to generate the tab title.
+ * @param objectGraph The dependency graph of the app store
+ */
+function todayPageLongTitle(objectGraph) {
+ if (!objectGraph.client.isPad) {
+ return undefined;
+ }
+ const now = new Date();
+ const dateFormat = objectGraph.loc.string("Today.ShelfTitle.DateFormat.IPad");
+ return objectGraph.loc.formatDate(dateFormat, now);
+}
+/**
+ * Return a short version of the first shelf title for the today page. This is used to generate the tab title,
+ * and is used where less horizontal space is available.
+ * @param objectGraph The dependency graph of the app store
+ */
+export function todayPageShortTitleDetail(objectGraph) {
+ const now = new Date();
+ return objectGraph.loc.formatDate("MMM d", now);
+}
+/**
+ * Generate the last shelves of the today page
+ * @param objectGraph The dependency graph for the app
+ * @param pageInformation The page information for the today page
+ * @param locationTracker The Location tracker for the current today page
+ * @returns The shelves that appear tha the bottom of the today page
+ */
+function footerShelvesForTodayPage(objectGraph, pageInformation, locationTracker) {
+ const shelves = [];
+ // Add Footer Buttons
+ const footerButtonShelf = shelfForFooterButtons(objectGraph, pageInformation, locationTracker);
+ if (isDefinedNonNull(footerButtonShelf)) {
+ shelves.push(footerButtonShelf);
+ }
+ // Add T&C
+ const url = objectGraph.bag.termsAndConditionsURL;
+ if (isDefinedNonNull(url)) {
+ const termsAndConditionsShelf = shelfForTermsAndConditions(objectGraph, url);
+ shelves.push(termsAndConditionsShelf);
+ }
+ return shelves;
+}
+//# sourceMappingURL=today-controller-util.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/today-horizontal-card-util.js b/node_modules/@jet-app/app-store/tmp/src/common/today/today-horizontal-card-util.js
new file mode 100644
index 0000000..0a2c0cb
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/today-horizontal-card-util.js
@@ -0,0 +1,352 @@
+import * as validation from "@jet/environment/json/validation";
+import * as models from "../../api/models";
+import * as serverData from "../../foundation/json-parsing/server-data";
+import { attributeAsBooleanOrFalse } from "../../foundation/media/attributes";
+import { openTVArcadeAppAction } from "../arcade/arcade-common";
+import { editorialNotesFromData } from "../content/content";
+import * as metricsHelpersLocation from "../metrics/helpers/location";
+import { cardDisplayStyleFromData, collapsedHeadingForTodayCard, defaultTodayCardConfiguration, todayCardFromData, } from "./today-card-util";
+import { TodayCardDisplayStyle, TodayCardMetricsDisplayStyle, TodayParseContext, } from "./today-types";
+const supportedSmallHorizontalCardKinds = new Set([
+ "artwork",
+ "appIcon",
+ "grid",
+ "multiApp",
+ "video",
+]);
+const supportedMediumHorizontalCardKinds = new Set([
+ "brandedSingleApp",
+ "artwork",
+ "appIcon",
+ "grid",
+ "multiApp",
+ "video",
+]);
+const supportedLargeHorizontalCardKinds = new Set([
+ "brandedSingleApp",
+ "artwork",
+ "appIcon",
+ "grid",
+ "multiApp",
+ "video",
+]);
+const iOSSupportedSmallHorizontalCardKinds = new Set([
+ "brandedSingleApp",
+ "artwork",
+ "grid",
+ "video",
+]);
+const webSupportedSmallHorizontalCardKinds = new Set([
+ "brandedSingleApp",
+ "artwork",
+ "river",
+ "appIcon",
+ "video",
+]);
+// MARK: - Horizontal Cards
+/**
+ * The crop code to use for a card using the provided horizontal card shelf content type.
+ * @param {ShelfContentType} contentType The content type of the shelf's items.
+ * @returns {CropCode} The crop code to use for the artwork.
+ */
+function horizontalCardCropCodeForContentType(objectGraph, contentType, cardDisplayStyle) {
+ switch (contentType) {
+ case "smallStoryCard":
+ if (objectGraph.host.isTV) {
+ return cardDisplayStyle === TodayCardDisplayStyle.Video ? null : { defaultCrop: "fo" };
+ }
+ else if (objectGraph.host.isiOS) {
+ return null;
+ }
+ else if (objectGraph.client.isWeb) {
+ return { defaultCrop: "sr" };
+ }
+ else {
+ return { defaultCrop: "em" };
+ }
+ case "mediumStoryCard":
+ return { defaultCrop: "el" };
+ case "largeStoryCard":
+ return cardDisplayStyle === TodayCardDisplayStyle.Video ? { defaultCrop: "sr" } : { defaultCrop: "ek" };
+ default:
+ return null;
+ }
+}
+export function isHorizontalCardSupportedForKind(objectGraph, kind, contentType) {
+ switch (contentType) {
+ case "smallStoryCard":
+ if (objectGraph.host.isiOS) {
+ return iOSSupportedSmallHorizontalCardKinds.has(kind);
+ }
+ else if (objectGraph.client.isWeb) {
+ return webSupportedSmallHorizontalCardKinds.has(kind);
+ }
+ else {
+ return supportedSmallHorizontalCardKinds.has(kind);
+ }
+ case "mediumStoryCard":
+ return supportedMediumHorizontalCardKinds.has(kind);
+ case "largeStoryCard":
+ return supportedLargeHorizontalCardKinds.has(kind);
+ case "todayCard":
+ return objectGraph.client.isWatch;
+ default:
+ return false;
+ }
+}
+export function horizontalCardItemsFromCards(objectGraph, cards, contentType, context, contentUnavailable, horizontalShelfCardConfig) {
+ const items = [];
+ for (const cardData of cards) {
+ const todayCard = horizontalCardItemFromCard(objectGraph, cardData, contentType, context, contentUnavailable, horizontalShelfCardConfig);
+ if (serverData.isNullOrEmpty(todayCard)) {
+ continue;
+ }
+ items.push(todayCard);
+ metricsHelpersLocation.nextPosition(context.metricsLocationTracker);
+ }
+ return items;
+}
+export function horizontalCardItemFromCard(objectGraph, cardData, contentType, context, contentUnavailable, horizontalShelfCardConfig) {
+ const parseContext = new TodayParseContext(context.metricsPageInformation, context.metricsLocationTracker);
+ if (horizontalShelfCardConfig === null || horizontalShelfCardConfig === undefined) {
+ horizontalShelfCardConfig = defaultTodayCardConfiguration(objectGraph);
+ horizontalShelfCardConfig.isHorizontalShelfContext = true;
+ }
+ if (objectGraph.client.deviceType !== "watch") {
+ // All today cards are deserialized through this code path on watchOS.
+ // We support all substyles, so we need to not specify this override.
+ horizontalShelfCardConfig.coercedCollectionTodayCardDisplayStyle = TodayCardDisplayStyle.Grid;
+ }
+ const cardDisplayStyle = cardDisplayStyleFromData(cardData, horizontalShelfCardConfig.coercedCollectionTodayCardDisplayStyle);
+ horizontalShelfCardConfig.prevailingCropCodes = horizontalCardCropCodeForContentType(objectGraph, contentType, cardDisplayStyle);
+ horizontalShelfCardConfig.horizontalCardContentType = contentType;
+ if (!serverData.isDefinedNonNull(cardData.attributes)) {
+ if (contentUnavailable) {
+ contentUnavailable(cardData);
+ }
+ return null;
+ }
+ const todayCard = todayCardFromData(objectGraph, cardData, horizontalShelfCardConfig, parseContext, context.augmentingData);
+ // Ignore the Date here, as we don't care about using it as a refresh signal when embedded in a page other than Today for now.
+ if (serverData.isNull(todayCard)) {
+ return null;
+ }
+ if (objectGraph.client.isiOS) {
+ todayCard.collapsedHeading = collapsedHeadingForTodayCard(objectGraph, todayCard);
+ todayCard.inlineDescription = featuredInDescriptionForCard(objectGraph, todayCard, cardData);
+ // In certain fallback cases, todayCardFromData() will make title and heading the same.
+ // If our generated heading and description are the same, we just show the heading.
+ if (todayCard.inlineDescription === todayCard.collapsedHeading) {
+ todayCard.inlineDescription = null;
+ }
+ // We want to avoid displaying the branded title over AotD/GotD cards, but only when they are not using
+ // the fallback art.
+ const brandedMedia = todayCard.media;
+ if (brandedMedia && brandedMedia.kind === "brandedSingleApp") {
+ // <rdar://problem/37249620> LOC: GLOBAL: App Store on iOS: Word wrapping in App/Game of the...
+ // Branded text on inline today cards is kind of a mess visually, so rely on the inline
+ // description.
+ todayCard.title = null;
+ }
+ }
+ if (contentType === "largeStoryCard") {
+ const heroMedia = todayCard.heroMedia;
+ if (!serverData.isDefinedNonNull(heroMedia) ||
+ (heroMedia.artworks.length === 0 && heroMedia.videos.length === 0)) {
+ return null;
+ }
+ }
+ if (isHorizontalCardSupportedForKind(objectGraph, todayCard.media.kind, contentType)) {
+ if (serverData.isDefinedNonNull(context.filterOutMediaCardKinds) &&
+ context.filterOutMediaCardKinds.has(todayCard.media.kind)) {
+ return null;
+ }
+ }
+ // Override `todayCard.clickAction` for some special cards.
+ overrideClickActionForTVAcquisitionCardIfNeeded(objectGraph, todayCard, cardData);
+ return todayCard;
+}
+export function shelfForHorizontalCardItems(objectGraph, cards, contentType, title, subtitle, context, contentUnavailable) {
+ if (!serverData.isDefinedNonNull(contentType)) {
+ return null;
+ }
+ const shelf = new models.Shelf(contentType);
+ if (title) {
+ shelf.title = title;
+ }
+ shelf.subtitle = subtitle;
+ shelf.isHorizontal = true;
+ switch (contentType) {
+ case "todayBrick": // As iOS doesn't support horizontalCardItemsFromCards, we map the small/medium/large story cards to todayBricks instead.
+ shelf.items = [
+ featuredInTodayCardsFromData(objectGraph, cards, context.metricsPageInformation, context.metricsLocationTracker, () => true, contentUnavailable),
+ ];
+ break;
+ default:
+ shelf.items = horizontalCardItemsFromCards(objectGraph, cards, contentType, context, contentUnavailable);
+ break;
+ }
+ return shelf;
+}
+/**
+ * Creates a shelf for mini today cards.
+ *
+ * @param objectGraph - The application store object graph.
+ * @param cards - An array of media data structures.
+ * @param contentType - The type of content for the shelf.
+ * @param title - The title of the shelf.
+ * @param subtitle - The subtitle of the shelf.
+ * @param context - The context for parsing today data.
+ * @param contentUnavailable - Optional handler for unavailable content.
+ * @returns A shelf containing today cards.
+ */
+export function shelfForMiniTodayCards(objectGraph, cards, title, subtitle, context, contentUnavailable) {
+ const shelf = new models.Shelf("miniTodayCard");
+ if (title) {
+ shelf.title = title;
+ }
+ shelf.subtitle = subtitle;
+ shelf.isHorizontal = true;
+ const items = [];
+ const cardConfig = defaultTodayCardConfiguration(objectGraph);
+ cardConfig.metricsDisplayStyle = TodayCardMetricsDisplayStyle.SmallCard;
+ cardConfig.alwaysShowBadgeInSmallCards = true;
+ cardConfig.alwaysUseMaterialBlur = true;
+ for (const cardData of cards) {
+ if (!serverData.isDefinedNonNull(cardData.attributes)) {
+ if (contentUnavailable) {
+ contentUnavailable(cardData);
+ }
+ continue;
+ }
+ const todayCard = todayCardFromData(objectGraph, cardData, cardConfig, context);
+ if (serverData.isNullOrEmpty(todayCard)) {
+ continue;
+ }
+ items.push(todayCard);
+ metricsHelpersLocation.nextPosition(context.locationTracker);
+ }
+ shelf.items = items;
+ return shelf;
+}
+// MARK: - Featured In
+/**
+ * Determines the description for the card, were it to appear inline.
+ * @param {TodayCard} card The card in question (for legacy support)
+ * @param {Data} data the card data to get the description from
+ * @returns {string} The description text to use for the card.
+ */
+function featuredInDescriptionForCard(objectGraph, card, data) {
+ const isSmallStoryCardsSupported = objectGraph.host.isiOS || objectGraph.host.isMac || objectGraph.host.isWeb;
+ if (isSmallStoryCardsSupported) {
+ let editorialName = editorialNotesFromData(objectGraph, data, "name");
+ if (serverData.isDefinedNonNull(editorialName)) {
+ editorialName = editorialName.replace(/\n/g, " ");
+ }
+ switch (card.media.kind) {
+ case "brandedSingleApp":
+ if (card.overlay instanceof models.TodayCardMarketingLockupOverlay &&
+ serverData.isDefinedNonNull(card.overlay.lockup)) {
+ return card.overlay.lockup.title;
+ }
+ else {
+ return editorialName;
+ }
+ default:
+ return editorialName;
+ }
+ }
+ switch (card.media.kind) {
+ case "brandedSingleApp":
+ if (card.overlay instanceof models.TodayCardThreeLineOverlay) {
+ return card.overlay.heading;
+ }
+ else if (serverData.isDefinedNonNull(card.title)) {
+ return card.title.replace(/\n/g, " ");
+ }
+ return null;
+ default:
+ const unformattedDescription = objectGraph.loc.string("TODAY_BRICK_STANDARD_DESCRIPTION");
+ let heading;
+ let title;
+ if (card.overlay instanceof models.TodayCardThreeLineOverlay) {
+ heading = card.overlay.heading;
+ title = card.overlay.title;
+ }
+ else {
+ heading = card.heading;
+ title = card.title;
+ }
+ if (!unformattedDescription || !heading || !title) {
+ return null;
+ }
+ return unformattedDescription.replace("{heading}", heading).replace("{title}", title);
+ }
+}
+export function featuredInTodayCardsFromData(objectGraph, dataArray, metricsPageInformation, metricsLocationTracker, isIncluded, contentUnavailable) {
+ return validation.context("todayCardsFromPlatformData", () => {
+ if (!dataArray) {
+ return null;
+ }
+ const featuredInCardConfig = defaultTodayCardConfiguration(objectGraph);
+ featuredInCardConfig.enableFullScreenVideo = false;
+ featuredInCardConfig.coercedCollectionTodayCardDisplayStyle = TodayCardDisplayStyle.Grid;
+ featuredInCardConfig.prevailingCropCodes = { defaultCrop: "fo" };
+ featuredInCardConfig.isHorizontalShelfContext = true;
+ const context = new TodayParseContext(metricsPageInformation, metricsLocationTracker);
+ const todayCards = [];
+ for (const data of dataArray) {
+ if (!serverData.isDefinedNonNull(data.attributes)) {
+ if (contentUnavailable) {
+ contentUnavailable(data);
+ }
+ continue;
+ }
+ const todayCard = todayCardFromData(objectGraph, data, featuredInCardConfig, context);
+ // Ignore the Date here, as we don't care about using it as a refresh signal when embedded in a page other than Today for now.
+ if (serverData.isNull(todayCard)) {
+ continue;
+ }
+ if (isIncluded(todayCard)) {
+ todayCard.collapsedHeading = collapsedHeadingForTodayCard(objectGraph, todayCard);
+ todayCard.inlineDescription = featuredInDescriptionForCard(objectGraph, todayCard, data);
+ // In certain fallback cases, todayCardFromData() will make title and heading the same.
+ // If our generated heading and description are the same, we just show the heading.
+ const isSmallStoryCardsSupported = objectGraph.host.isiOS || objectGraph.host.isMac || objectGraph.host.isWeb;
+ if (isSmallStoryCardsSupported && todayCard.inlineDescription === todayCard.collapsedHeading) {
+ todayCard.inlineDescription = null;
+ }
+ // We want to avoid displaying the branded title over AotD/GotD cards, but only when they are not using
+ // the fallback art.
+ const brandedMedia = todayCard.media;
+ if (brandedMedia && brandedMedia.kind === "brandedSingleApp") {
+ // <rdar://problem/37249620> LOC: GLOBAL: App Store on iOS: Word wrapping in App/Game of the...
+ // Branded text on inline today cards is kind of a mess visually, so rely on the inline
+ // description.
+ todayCard.title = null;
+ }
+ todayCards.push(todayCard);
+ }
+ }
+ if (!todayCards.length) {
+ return null;
+ }
+ return new models.InlineTodayCards(todayCards);
+ });
+}
+// MARK: - tvOS Acquisition EI Card
+/**
+ * On tvOS, some special editorial items are supposed to link directly into the Apple Arcade app.
+ * This is to override the click action if needed.
+ * @param card Today card to potentially modify.
+ * @param data Data that card was built from.
+ */
+function overrideClickActionForTVAcquisitionCardIfNeeded(objectGraph, card, data) {
+ if (objectGraph.client.deviceType !== "tv" || !attributeAsBooleanOrFalse(data, "isAcquisition")) {
+ return; // Only override on acquisition stories on tvOS.
+ }
+ const openArcadeAppAction = openTVArcadeAppAction(objectGraph);
+ card.clickAction = openArcadeAppAction;
+}
+// endregion
+//# sourceMappingURL=today-horizontal-card-util.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/today-parse-util.js b/node_modules/@jet-app/app-store/tmp/src/common/today/today-parse-util.js
new file mode 100644
index 0000000..a1e27b0
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/today-parse-util.js
@@ -0,0 +1,1047 @@
+import { isNothing, isSome } from "@jet/environment";
+import * as validation from "@jet/environment/json/validation";
+import * as models from "../../api/models";
+import { asArrayOrEmpty, asBoolean, asDictionary, asInterface, asString, isDefinedNonNull, isDefinedNonNullNonEmpty, isNull, isNullOrEmpty, } from "../../foundation/json-parsing/server-data";
+import { editorialCardFromData } from "../../foundation/media/associations";
+import * as mediaAttributes from "../../foundation/media/attributes";
+import { attributeAsString, hasAttributes } from "../../foundation/media/attributes";
+import * as mediaDataStructure from "../../foundation/media/data-structure";
+import { relationshipData } from "../../foundation/media/relationships";
+import { Parameters, Path, Protocol } from "../../foundation/network/url-constants";
+import { URL } from "../../foundation/network/urls";
+import { isAdPlacementEnabled } from "../ads/ad-common";
+import { categoryArtworkData } from "../categories";
+import { artworkFromApiArtwork, iconFromData } from "../content/content";
+import { extractEditorialClientParams } from "../editorial-pages/editorial-data-util";
+import { addImpressionFields } from "../metrics/helpers/impressions";
+import { nextPosition, popLocation, pushContentLocation } from "../metrics/helpers/location";
+import { combinedRecoMetricsDataFromMetricsData } from "../metrics/helpers/util";
+import * as onDevicePersonalization from "../personalization/on-device-personalization";
+import * as color from "./../../foundation/util/color-util";
+import { asNumber } from "@apple-media-services/media-api";
+import { createTodayAdCard } from "./cards/today-ad-card-builder";
+import { defaultTodayCardConfiguration, todayCardFromData } from "./today-card-util";
+import * as impressionDemotion from "../../common/personalization/on-device-impression-demotion";
+import { EditorialBackgroundType, TodayCardMetricsDisplayStyle, TodayHeaderArtworkBehavior, TodaySectionHeaderArtworkPlacement, } from "./today-types";
+/**
+ * We try to flatten the today response so we can treat it as a single list
+ * of items. These are the different item types in the flattened list.
+ */
+export var FlattenedTodayItemType;
+(function (FlattenedTodayItemType) {
+ FlattenedTodayItemType["EditorialItem"] = "editorialItem";
+ FlattenedTodayItemType["EditorialItemGroup"] = "editorialItemGroup";
+})(FlattenedTodayItemType || (FlattenedTodayItemType = {}));
+/**
+ * Flatten the entire modules array from the TodayPage response into a single list of items.
+ * @param objectGraph The dependency graph for the App Store
+ * @param todayModules The today modules to flatten from the page response
+ * @param onboardingCardsData The media api data for the onboarding cards, inserted into the flattened list
+ * @param todayRecommendations The today recommendations result, inserted into the flattened list
+ * @param adsResponse The response from ad platforms
+ * @returns The flattened list of today items
+ */
+export function flattenTodayModules(objectGraph, todayModules, todayRecommendations, onboardingCardsData, adsResponse) {
+ const flattenedItems = [];
+ let insertedOnboardingCards = isNullOrEmpty(onboardingCardsData);
+ // Retrieve our personalization data
+ const personalizationDataContainer = personalizationDataContainerForTodayModules(objectGraph, todayModules);
+ let absoluteFeedIndex = 0;
+ for (const todayModule of todayModules) {
+ if (isNullOrEmpty(todayModule.contents)) {
+ continue;
+ }
+ const personalizedModuleDataResult = onDevicePersonalization.personalizeDataItems(objectGraph, "today", todayModule.contents, true, personalizationDataContainer);
+ todayModule.contents = personalizedModuleDataResult.personalizedData;
+ todayModule.onDevicePersonalizationProcessingType = personalizedModuleDataResult.processingType;
+ let isFirstItemInModule = true;
+ const moduleMetadata = {
+ label: todayModule.label,
+ title: todayModule.title,
+ meta: todayModule.meta,
+ date: todayModule.date,
+ onDevicePersonalizationProcessingType: todayModule.onDevicePersonalizationProcessingType,
+ isTodayForAppsModule: mediaDataStructure.isModuleTodayForApps(todayModule),
+ };
+ if (!insertedOnboardingCards) {
+ for (const onboardingCardData of onboardingCardsData) {
+ flattenedItems.push({
+ type: FlattenedTodayItemType.EditorialItem,
+ data: onboardingCardData,
+ isDataHydrated: hasAttributes(onboardingCardData),
+ isFirstItemInModule,
+ moduleMetadata: { ...moduleMetadata },
+ containedAdSlots: [absoluteFeedIndex],
+ });
+ }
+ isFirstItemInModule = false;
+ insertedOnboardingCards = true;
+ absoluteFeedIndex += 1;
+ }
+ for (let moduleItem of todayModule.contents) {
+ const onDeviceUseCase = asString(moduleItem, "meta.personalizationData.onDeviceUseCase");
+ switch (moduleItem.type) {
+ case "editorial-items":
+ if (isSome(onDeviceUseCase)) {
+ // ODP personalization for Today Arcade stories.
+ const storyData = todayRecommendations === null || todayRecommendations === void 0 ? void 0 : todayRecommendations.storyData(onDeviceUseCase);
+ if (isSome(storyData)) {
+ moduleItem = storyData;
+ }
+ }
+ flattenedItems.push({
+ type: FlattenedTodayItemType.EditorialItem,
+ data: moduleItem,
+ isDataHydrated: hasAttributes(moduleItem),
+ isFirstItemInModule,
+ moduleMetadata: { ...moduleMetadata },
+ containedAdSlots: [absoluteFeedIndex],
+ });
+ isFirstItemInModule = false;
+ absoluteFeedIndex += 1;
+ break;
+ case "editorial-item-groups":
+ const groupContents = asArrayOrEmpty(moduleItem, "meta.associations.recommendations.data");
+ if (isNullOrEmpty(groupContents)) {
+ continue;
+ }
+ let storyGroupData;
+ if (isSome(onDeviceUseCase)) {
+ // ODP personalization for Today Arcade story groups.
+ storyGroupData = todayRecommendations === null || todayRecommendations === void 0 ? void 0 : todayRecommendations.storyGroupData(onDeviceUseCase);
+ }
+ if (isSome(storyGroupData)) {
+ moduleItem = storyGroupData;
+ }
+ else {
+ // ODP personalization for in-app event story groups.
+ const personalizedStoryGroupDataResult = onDevicePersonalization.personalizeDataItems(objectGraph, "today", groupContents, true, personalizationDataContainer);
+ moduleItem["meta"]["associations"]["recommendations"]["data"] =
+ personalizedStoryGroupDataResult.personalizedData;
+ todayModule.onDevicePersonalizationProcessingType =
+ personalizedStoryGroupDataResult.processingType;
+ }
+ flattenedItems.push({
+ type: FlattenedTodayItemType.EditorialItemGroup,
+ data: moduleItem,
+ isDataHydrated: hasAttributes(moduleItem),
+ isFirstItemInModule,
+ moduleMetadata: { ...moduleMetadata },
+ containedAdSlots: Array.from({ length: groupContents.length }, (key, value) => value + absoluteFeedIndex),
+ });
+ isFirstItemInModule = false;
+ absoluteFeedIndex += groupContents.length;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return flattenedItems;
+}
+/**
+ * Whether the next content item in the flattened list is hydrated.
+ * @param flattenedItems The flattened list of today items
+ * @returns True if the next content item is hydrated, false otherwise
+ */
+export function nextFlattenedItemIsHydrated(flattenedItems) {
+ for (const flattenedItem of flattenedItems) {
+ switch (flattenedItem.type) {
+ case FlattenedTodayItemType.EditorialItem:
+ case FlattenedTodayItemType.EditorialItemGroup:
+ return hasAttributes(flattenedItem.data);
+ default:
+ break;
+ }
+ }
+ return false;
+}
+/**
+ * Iterates through all the today modules, and creates a set of personalization
+ * data that is targetted only to the contents of these modules.
+ *
+ * @param dataArray The input array of today modules.
+ * @returns Any relevant OnDevicePersonalizaionData
+ */
+export function personalizationDataContainerForTodayModules(objectGraph, todayModules) {
+ if (!onDevicePersonalization.isPersonalizationAvailable(objectGraph)) {
+ return null;
+ }
+ // Here we iterate through all the modules, and look inside each content items meta for personalizationData.
+ const appIds = new Set();
+ for (const todayModule of todayModules) {
+ if (isNull(todayModule.contents)) {
+ continue;
+ }
+ const appIdFromContent = (contentData) => {
+ return asString(contentData, "meta.personalizationData.appId");
+ };
+ for (const contentData of todayModule.contents) {
+ switch (contentData.type) {
+ case "editorial-item-groups":
+ const groupItems = asArrayOrEmpty(contentData.meta, "associations.recommendations.data");
+ for (const groupItem of groupItems) {
+ const appId = appIdFromContent(groupItem);
+ if (isDefinedNonNullNonEmpty(appId)) {
+ appIds.add(appId.toString());
+ }
+ }
+ break;
+ default:
+ const appId = appIdFromContent(contentData);
+ if (isDefinedNonNullNonEmpty(appId)) {
+ appIds.add(appId.toString());
+ }
+ break;
+ }
+ }
+ }
+ return onDevicePersonalization.personalizationDataContainerForAppIds(objectGraph, appIds);
+}
+// MARK: - Shelf Creation
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param todayItem The today item that this card is contained in, this could be a single item or a story group item
+ * @param todayCardData The MAPI data that will be used to create this today card
+ * @param isAdEligible Whether the current card is being placed in a slot that was eligible for an ad
+ * @param currentRowIndex The current row index this card is going to be in
+ * @param metricsDisplayStyle: The display style for impressions and location
+ * @param isHeroCard: Whether this is the first card in a hero story group
+ * @returns The configuration to use when parsing a today card
+ */
+function createTodayCardConfiguration(objectGraph, todayCardData, isAdEligible, currentRowIndex, metricsDisplayStyle, isHeroCard) {
+ var _a;
+ const cardConfig = defaultTodayCardConfiguration(objectGraph);
+ cardConfig.useOTDTextStyle = (_a = asBoolean(todayCardData, "meta.personalizationData.isOfTheDay")) !== null && _a !== void 0 ? _a : false;
+ cardConfig.replaceIfAdPresent = asBoolean(todayCardData, "meta.personalizationData.replaceIfAdPresent");
+ cardConfig.isAdEligible = isAdEligible;
+ cardConfig.currentRowIndex = currentRowIndex;
+ cardConfig.metricsDisplayStyle = metricsDisplayStyle;
+ cardConfig.isHeroCard = isHeroCard;
+ if (objectGraph.client.isWeb) {
+ cardConfig.prevailingCropCodes = {
+ "defaultCrop": "sr",
+ "editorialArtwork.dayCard": "grav.west",
+ };
+ }
+ return cardConfig;
+}
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param pageContext The page context for the today page
+ * @returns The currentRowIndex from the pageContext, this is only used on iPhone, otherwise return
+ * null since we cant reliably know the row index
+ */
+function rowCountFromContext(objectGraph, pageContext) {
+ if (!objectGraph.client.isPhone) {
+ return undefined;
+ }
+ return pageContext.currentRowIndex;
+}
+/**
+ * Update the page context with the current row count, this uses the groupDisplayStyle to determine
+ * the display style for the cards in the current row
+ * @param pageContext The page context for the today page we're parsing
+ * @param currentGroupDisplayStyle The current groupDisplayItem from the TodayItem we're parsing
+ * @param currentItemIndex The index of the current item in the shelf, that we just created a card
+ * for, this method is called after creating a card
+ */
+function incrementRowIndexIfNecessary(pageContext, currentGroupDisplayStyle, currentItemIndex) {
+ switch (currentGroupDisplayStyle) {
+ case models.GroupDisplayStyle.Grid:
+ if (currentItemIndex % 2 === 1) {
+ pageContext.currentRowIndex++;
+ }
+ break;
+ case models.GroupDisplayStyle.Hero:
+ if (currentItemIndex === 0 || (currentItemIndex - 1) % 2 === 1) {
+ pageContext.currentRowIndex++;
+ }
+ break;
+ case models.GroupDisplayStyle.Standard:
+ pageContext.currentRowIndex++;
+ break;
+ default:
+ break;
+ }
+}
+/**
+ * Update the page context with the current metrics display style, this is based off the groupDisplayStyle
+ * @param objectGraph The dependency graph for the App Store
+ * @param pageContext The page context for the today page we're parsing
+ * @param currentGroupDisplayStyle The current groupDisplayItem from the TodayItem we're parsing
+ * @param currentItemIndex The index of the current item in the shelf, that we just created a card
+ * for, this method is called after creating a card
+ */
+function updateCurrentRowMetricsDisplayStyle(objectGraph, pageContext, currentGroupDisplayStyle, currentItemIndex) {
+ if (objectGraph.client.isPad) {
+ pageContext.currentRowMetricsDisplayStyle = TodayCardMetricsDisplayStyle.MediumCard;
+ return;
+ }
+ switch (currentGroupDisplayStyle) {
+ case models.GroupDisplayStyle.Grid:
+ pageContext.currentRowMetricsDisplayStyle = TodayCardMetricsDisplayStyle.SmallCard;
+ break;
+ case models.GroupDisplayStyle.Hero:
+ if (currentItemIndex === 0) {
+ pageContext.currentRowMetricsDisplayStyle = TodayCardMetricsDisplayStyle.MediumCard;
+ }
+ else {
+ pageContext.currentRowMetricsDisplayStyle = TodayCardMetricsDisplayStyle.SmallCard;
+ }
+ break;
+ case models.GroupDisplayStyle.Standard:
+ pageContext.currentRowMetricsDisplayStyle = TodayCardMetricsDisplayStyle.MediumCard;
+ break;
+ default:
+ break;
+ }
+}
+/**
+ * @param objectGraph The app store object graph
+ * @param data The data from MAPI for a single item in the today feed, group or single EI
+ * @returns The group display style for the given data
+ */
+function groupDisplayStyleFromData(objectGraph, data) {
+ var _a;
+ if (data.type === "editorial-items") {
+ return models.GroupDisplayStyle.Standard;
+ }
+ let groupDisplayStyle;
+ const editorialCard = editorialCardFromData(data);
+ if (hasAttributes(editorialCard)) {
+ groupDisplayStyle = mediaAttributes.attributeAsString(editorialCard, "editorialItemGroupDisplayStyle");
+ }
+ if (isNothing(groupDisplayStyle)) {
+ groupDisplayStyle =
+ (_a = mediaAttributes.attributeAsString(data, "displayStyle")) !== null && _a !== void 0 ? _a : models.GroupDisplayStyle.Standard;
+ }
+ return isGroupDisplayStyleSupported(objectGraph, groupDisplayStyle)
+ ? groupDisplayStyle
+ : models.GroupDisplayStyle.Standard;
+}
+/**
+ * @param objectGraph The app dependency graph
+ * @param displayStyle The display style to check support for
+ * @returns Whether group display style is supported for this platform
+ */
+function isGroupDisplayStyleSupported(objectGraph, displayStyle) {
+ if (isNothing(displayStyle)) {
+ return false;
+ }
+ switch (displayStyle) {
+ case models.GroupDisplayStyle.Grid:
+ return objectGraph.client.isPhone;
+ default:
+ return true;
+ }
+}
+/**
+ * Determine if the current card is eligible for an ad, based on the parsed card count, and the
+ * bag defined values for slots that can have ads
+ * @param pageContext The page context for the today page
+ * @returns Whether the current card is eligible for an ad
+ */
+function isTodayCardConfigurationAdEligible(pageContext) {
+ if (pageContext.adLocation === pageContext.parsedCardCount) {
+ return true;
+ }
+ if (isNothing(pageContext.eligibleAdLocations)) {
+ return false;
+ }
+ return pageContext.eligibleAdLocations.includes(pageContext.parsedCardCount);
+}
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param todayItem The flattened today item to create a shelf for
+ * @param pageContext The page context for the today page
+ * @returns The shelf for the given today item, or null if we cant create one
+ */
+export function todayShelfForEditorialItem(objectGraph, todayItem, pageContext) {
+ var _a;
+ const shelf = createTodayShelfWithItems(objectGraph, todayItem, pageContext, () => {
+ var _a, _b, _c;
+ const shelfItems = [];
+ (_a = pageContext.pageInformation.iAdInfo) === null || _a === void 0 ? void 0 : _a.updateContainerId((_b = pageContext.pageInformation.iAdInfo) === null || _b === void 0 ? void 0 : _b.containerIdForSlotIndex((_c = pageContext.parsedCardCount) !== null && _c !== void 0 ? _c : 0));
+ const groupDisplayStyle = groupDisplayStyleFromData(objectGraph, todayItem.data);
+ updateCurrentRowMetricsDisplayStyle(objectGraph, pageContext, groupDisplayStyle, 0);
+ const cardConfig = createTodayCardConfiguration(objectGraph, todayItem.data, isTodayCardConfigurationAdEligible(pageContext), rowCountFromContext(objectGraph, pageContext), pageContext.currentRowMetricsDisplayStyle, false);
+ cardConfig.baseMetricsOptions = {
+ recoMetricsData: recoMetricsFromTodayItem(todayItem),
+ };
+ const editorialItemTodayCard = createTodayCardForPageContext(objectGraph, pageContext, cardConfig, todayItem.data);
+ if (isNothing(editorialItemTodayCard)) {
+ return shelfItems;
+ }
+ shelfItems.push(editorialItemTodayCard);
+ nextPosition(pageContext.locationTracker);
+ pageContext.parsedCardCount++;
+ incrementRowIndexIfNecessary(pageContext, groupDisplayStyle, 0);
+ return shelfItems;
+ });
+ shelf.contentsMetadata = {
+ type: "todaySection",
+ debugSectionTypeIndicatorColor: todayItem.type === FlattenedTodayItemType.EditorialItemGroup
+ ? color.named("systemGreen")
+ : color.named("systemBlue"),
+ groupDisplayStyle: models.GroupDisplayStyle.Standard,
+ };
+ // If this is not the first item in the module, attempt to set the background
+ if (!todayItem.isFirstItemInModule) {
+ const editorialShelfBackgroundInfo = todaySectionEditorialBackground(objectGraph, todayItem);
+ if (isSome(editorialShelfBackgroundInfo)) {
+ shelf.background = editorialShelfBackgroundInfo.shelfBackground;
+ if (isSome((_a = shelf.header) === null || _a === void 0 ? void 0 : _a.configuration)) {
+ shelf.header.configuration.eyebrowColor = editorialShelfBackgroundInfo.eyebrowColor;
+ shelf.header.configuration.titleColor = editorialShelfBackgroundInfo.titleColor;
+ shelf.header.configuration.subtitleColor = editorialShelfBackgroundInfo.subtitleColor;
+ }
+ }
+ }
+ return shelf;
+}
+/**
+
+ * @param objectGraph The dependency graph for the App Store
+ * @param todayItem The flattened today item, of type EditorialItemGroup to create a shelf for
+ * @param pageContext The page context for the today page
+ * @returns The shelf for the given today item, or null if we cant create one
+ */
+export function todayShelfForEditorialItemGroup(objectGraph, todayItem, pageContext) {
+ var _a;
+ let isValidHeroStoryGroup = true;
+ const shelf = createTodayShelfWithItems(objectGraph, todayItem, pageContext, () => {
+ var _a, _b, _c, _d, _e, _f, _g;
+ const shelfItems = [];
+ const displayLimit = impressionDemotion.isImpressionDemotionAvailable(objectGraph)
+ ? (_a = asNumber(todayItem.data, "meta.personalizationData.displayEICount")) !== null && _a !== void 0 ? _a : 100
+ : 100;
+ let groupItems = asArrayOrEmpty(todayItem.data.meta, "associations.recommendations.data");
+ if (isSome(pageContext.recoImpressionData)) {
+ groupItems = impressionDemotion.personalizeDataItems(groupItems, pageContext.recoImpressionData, (_b = pageContext.pageInformation.recoMetricsData) !== null && _b !== void 0 ? _b : {});
+ }
+ const groupDisplayStyle = groupDisplayStyleFromData(objectGraph, todayItem.data);
+ let parsedGroupItemCount = 0;
+ for (const [index, groupItem] of groupItems.entries()) {
+ (_c = pageContext.pageInformation.iAdInfo) === null || _c === void 0 ? void 0 : _c.updateContainerId((_d = pageContext.pageInformation.iAdInfo) === null || _d === void 0 ? void 0 : _d.containerIdForSlotIndex((_e = pageContext.parsedCardCount) !== null && _e !== void 0 ? _e : 0));
+ updateCurrentRowMetricsDisplayStyle(objectGraph, pageContext, groupDisplayStyle, parsedGroupItemCount);
+ const cardConfig = createTodayCardConfiguration(objectGraph, groupItem, isTodayCardConfigurationAdEligible(pageContext), rowCountFromContext(objectGraph, pageContext), pageContext.currentRowMetricsDisplayStyle, groupDisplayStyle === models.GroupDisplayStyle.Hero && index === 0);
+ const shelfItem = createTodayCardForPageContext(objectGraph, pageContext, cardConfig, groupItem);
+ if (isSome(shelfItem)) {
+ shelfItems.push(shelfItem);
+ nextPosition(pageContext.locationTracker);
+ pageContext.parsedCardCount++;
+ incrementRowIndexIfNecessary(pageContext, groupDisplayStyle, parsedGroupItemCount);
+ parsedGroupItemCount++;
+ }
+ if (cardConfig.isHeroCard && isNothing(shelfItem)) {
+ if (["debug", "internal"].includes(objectGraph.client.buildType)) {
+ validation.unexpectedType("defaultValue", `Hero story group ${(_f = todayItem.data) === null || _f === void 0 ? void 0 : _f.id} must contain a valid hero card at index ${index}. Unable to parse card ${groupItem.id}.`, null);
+ }
+ isValidHeroStoryGroup = false;
+ }
+ if (index < groupItems.length - 1 &&
+ pageContext.adPlacementBehavior === models.AdPlacementBehavior.insertIntoShelf) {
+ // Attempt to insert the ad card after creating the next item in the group, but only
+ // if we're still **within** the story group, ads that fall at the beginning or end of
+ // the group are handled at the top level of the page parsing.
+ const adCard = createAdCardForTodayPageContextIfNecessary(objectGraph, pageContext, createTodayCardConfiguration(objectGraph, pageContext.adData, isTodayCardConfigurationAdEligible(pageContext), rowCountFromContext(objectGraph, pageContext), undefined, false));
+ if (isSome(adCard)) {
+ pageContext.parsedCardCount++;
+ incrementRowIndexIfNecessary(pageContext, models.GroupDisplayStyle.Standard, 0);
+ nextPosition(pageContext.locationTracker);
+ shelfItems.push(adCard);
+ }
+ }
+ // if the count is equal to the story limit. stop here and break.
+ if (shelfItems.length === displayLimit) {
+ break;
+ }
+ }
+ if (isValidHeroStoryGroup) {
+ // The number of items required to be a valid hero story group, this includes the hero
+ // card, and two additional cards to form a row
+ const heroStoryGroupRequiredItemCount = 3;
+ if (shelfItems.length !== heroStoryGroupRequiredItemCount) {
+ if (["debug", "internal"].includes(objectGraph.client.buildType)) {
+ validation.unexpectedType("defaultValue", `Hero story group ${(_g = todayItem.data) === null || _g === void 0 ? void 0 : _g.id} must contain exactly ${heroStoryGroupRequiredItemCount} items but only found ${shelfItems.length} items.`, null);
+ }
+ isValidHeroStoryGroup = false;
+ }
+ }
+ return shelfItems;
+ });
+ // Fallback to standard group display style if we had an issue creating a valid hero group
+ let groupDisplayStyle = groupDisplayStyleFromData(objectGraph, todayItem.data);
+ if (groupDisplayStyle === models.GroupDisplayStyle.Hero && !isValidHeroStoryGroup) {
+ groupDisplayStyle = models.GroupDisplayStyle.Standard;
+ }
+ shelf.contentsMetadata = {
+ type: "todaySection",
+ debugSectionTypeIndicatorColor: color.named("systemGreen"),
+ groupDisplayStyle: groupDisplayStyle,
+ };
+ // If this is not the first item in the module, attempt to set the background
+ if (!todayItem.isFirstItemInModule) {
+ const editorialShelfBackgroundInfo = todaySectionEditorialBackground(objectGraph, todayItem);
+ if (isSome(editorialShelfBackgroundInfo)) {
+ shelf.background = editorialShelfBackgroundInfo.shelfBackground;
+ if (isSome((_a = shelf.header) === null || _a === void 0 ? void 0 : _a.configuration)) {
+ shelf.header.configuration.eyebrowColor = editorialShelfBackgroundInfo.eyebrowColor;
+ shelf.header.configuration.titleColor = editorialShelfBackgroundInfo.titleColor;
+ shelf.header.configuration.subtitleColor = editorialShelfBackgroundInfo.subtitleColor;
+ }
+ }
+ else if (groupDisplayStyle === models.GroupDisplayStyle.Hero && Array.isArray(shelf.items)) {
+ shelf.background = todaySectionBackgroundForHeroDisplayStyle(shelf.items);
+ }
+ }
+ return shelf;
+}
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param todayItem The flattened today item, used to create the items in this shelf
+ * @param pageContext The page context for the today page
+ * @param itemProvider The function that will provide the items for this shelf
+ * @returns The shelf for the given today item
+ */
+function createTodayShelfWithItems(objectGraph, todayItem, pageContext, itemProvider) {
+ const shouldRecordShelfMetrics = todayItem.type === FlattenedTodayItemType.EditorialItemGroup;
+ const shelf = new models.Shelf("todayCard");
+ shelf.id = todayItem.data.id;
+ shelf.isHorizontal = false;
+ shelf.header = createTodayShelfHeaderForTodayItem(objectGraph, todayItem, pageContext);
+ if (shouldRecordShelfMetrics) {
+ const shelfMetricsOptions = {
+ id: shelf.id,
+ kind: "editorialItemGroup",
+ softwareType: null,
+ targetType: "swoosh",
+ title: sectionTitleFromTodayItem(objectGraph, todayItem, true),
+ pageInformation: pageContext.pageInformation,
+ locationTracker: pageContext.locationTracker,
+ idType: "its_id",
+ recoMetricsData: recoMetricsFromTodayItem(todayItem),
+ };
+ if (todayItem.type === FlattenedTodayItemType.EditorialItemGroup) {
+ shelfMetricsOptions["optimizationId"] = asString(todayItem.data, "meta.personalizationData.optimizationId");
+ shelfMetricsOptions["optimizationEntityId"] = asString(todayItem.data, "meta.personalizationData.optimizationEntityId");
+ }
+ addImpressionFields(objectGraph, shelf, shelfMetricsOptions);
+ pushContentLocation(objectGraph, shelfMetricsOptions, shelfMetricsOptions.title);
+ }
+ shelf.items = itemProvider();
+ shelf.isHidden = isNullOrEmpty(shelf.items);
+ if (shouldRecordShelfMetrics) {
+ popLocation(pageContext.locationTracker);
+ nextPosition(pageContext.locationTracker);
+ }
+ return shelf;
+}
+function createTodayShelfHeaderForTodayItem(objectGraph, todayItem, pageContext) {
+ var _a;
+ const shouldSuppressHeader = (_a = asBoolean(todayItem.data, "meta.personalizationData.suppressHeader")) !== null && _a !== void 0 ? _a : false;
+ if (shouldSuppressHeader) {
+ return null;
+ }
+ const shelfHeaderConfiguration = {
+ eyebrowImageColor: null,
+ titleImageColor: null,
+ includeSeparator: false,
+ };
+ const shelfHeader = {
+ eyebrow: sectionEyebrowFromTodayItem(objectGraph, todayItem),
+ eyebrowArtwork: sectionHeaderArtworkFromTodayItemForPlacement(objectGraph, todayItem, TodaySectionHeaderArtworkPlacement.Eyebrow),
+ eyebrowArtworkType: sectionHeaderArtworkTypeFromTodayItemForPlacement(objectGraph, todayItem, TodaySectionHeaderArtworkPlacement.Eyebrow),
+ title: sectionTitleFromTodayItem(objectGraph, todayItem),
+ titleArtwork: sectionHeaderArtworkFromTodayItemForPlacement(objectGraph, todayItem, TodaySectionHeaderArtworkPlacement.Title),
+ titleArtworkType: sectionHeaderArtworkTypeFromTodayItemForPlacement(objectGraph, todayItem, TodaySectionHeaderArtworkPlacement.Title),
+ subtitle: sectionSubtitleFromTodayItem(objectGraph, todayItem),
+ configuration: shelfHeaderConfiguration,
+ };
+ if (isSome(shelfHeader.eyebrow) || isSome(shelfHeader.title) || isSome(shelfHeader.subtitle)) {
+ return shelfHeader;
+ }
+ else {
+ return null;
+ }
+}
+/**
+ * Create a today card for a given today item in a page context. This handles requirements for ad replacement if needed
+ * @param objectGraph The dependency graph of the app store
+ * @param pageContext The context of the page so we can tell whether its time to insert an ad
+ * @param cardConfig The configuration for the today item
+ * @param todayItem The flattened today item that should be placed in the shelf or replaced with an ad card
+ * @returns The today card to insert in a shelf
+ */
+function createTodayCardForPageContext(objectGraph, pageContext, cardConfig, todayItem) {
+ var _a, _b;
+ let editorialItemTodayCard;
+ if (pageContext.adPlacementBehavior === models.AdPlacementBehavior.replaceOrganic &&
+ isDefinedNonNull(cardConfig.replaceIfAdPresent) &&
+ asBoolean(cardConfig.replaceIfAdPresent)) {
+ // the ad card should replace the organic
+ const adCard = createAdCardForTodayPageContextIfNecessary(objectGraph, pageContext, cardConfig);
+ if (isDefinedNonNullNonEmpty(adCard)) {
+ editorialItemTodayCard = adCard;
+ }
+ else {
+ editorialItemTodayCard = todayCardFromData(objectGraph, todayItem, cardConfig, pageContext);
+ }
+ }
+ else if (pageContext.adPlacementBehavior === models.AdPlacementBehavior.dropAd &&
+ isDefinedNonNull(cardConfig.replaceIfAdPresent) &&
+ !asBoolean(cardConfig.replaceIfAdPresent)) {
+ // the organic is not replaceable
+ editorialItemTodayCard = todayCardFromData(objectGraph, todayItem, cardConfig, pageContext);
+ if (isDefinedNonNullNonEmpty(pageContext.adData)) {
+ const recorder = pageContext.adIncidentRecorder;
+ (_a = recorder === null || recorder === void 0 ? void 0 : recorder.iAdInfo) === null || _a === void 0 ? void 0 : _a.setMissedOpportunity(objectGraph, "EDITORIALTAKEOVER", (_b = recorder === null || recorder === void 0 ? void 0 : recorder.iAdInfo) === null || _b === void 0 ? void 0 : _b.placementType);
+ }
+ }
+ else {
+ // we're using existing insertion logic that will insert an ad elsewhere. create a regular card for the flattened item
+ editorialItemTodayCard = todayCardFromData(objectGraph, todayItem, cardConfig, pageContext);
+ }
+ return editorialItemTodayCard;
+}
+// MARK: - Ads
+/**
+ * Try create a shelf to display an ad if we're currently at the correct spot in the feed. This is only used
+ * for single editorialItem shelves, and the end of an editorial item group, since these locations it does not
+ * make sense to include the ad within the shelf for that TodayItem.
+ * @param objectGraph The dependency graph of the app store
+ * @param pageContext The context of the page so we can tell whether its time to insert an ad
+ * @returns The card to insert at the ad slot if necessary
+ */
+export function createAdShelfForTodayPageContextIfNecessary(objectGraph, pageContext) {
+ let adShelf = null;
+ if (!isAdPlacementEnabled(objectGraph, "today") ||
+ isNothing(pageContext.adData) ||
+ pageContext.adPlacementBehavior !== models.AdPlacementBehavior.insertIntoShelf) {
+ return adShelf;
+ }
+ if (pageContext.adLocation !== pageContext.parsedCardCount) {
+ return adShelf;
+ }
+ adShelf = new models.Shelf("todayCard");
+ adShelf.id = pageContext.adData.id;
+ adShelf.isHorizontal = false;
+ adShelf.contentsMetadata = {
+ type: "todaySection",
+ debugSectionTypeIndicatorColor: color.named("systemBlue"),
+ groupDisplayStyle: models.GroupDisplayStyle.Standard,
+ };
+ const shelfItems = [];
+ /// Attempt to create an ad card before creating the next item in the group
+ const adCard = createAdCardForTodayPageContextIfNecessary(objectGraph, pageContext, createTodayCardConfiguration(objectGraph, pageContext.adData, isTodayCardConfigurationAdEligible(pageContext), rowCountFromContext(objectGraph, pageContext), undefined, false));
+ if (isSome(adCard)) {
+ pageContext.parsedCardCount++;
+ incrementRowIndexIfNecessary(pageContext, models.GroupDisplayStyle.Standard, 0);
+ nextPosition(pageContext.locationTracker);
+ shelfItems.push(adCard);
+ }
+ adShelf.items = shelfItems;
+ return isDefinedNonNullNonEmpty(adShelf.items) ? adShelf : null;
+}
+/**
+ * Try to create an ad card for the page context if the requirements are met
+ * @param objectGraph The dependency graph of the app store
+ * @param pageContext The context of the page so we can tell whether its time to insert an ad
+ * @param cardConfig The configuration of the card
+ * @returns The card to insert at the ad slot if necessary
+ */
+export function createAdCardForTodayPageContextIfNecessary(objectGraph, pageContext, cardConfig) {
+ var _a, _b, _c;
+ if (!isAdPlacementEnabled(objectGraph, "today")) {
+ return null;
+ }
+ // The ad should respect its slot, unless we're using reco logic for organic replacement. If we need to replace
+ // the organic, we will arbitrarily respect the reco flag to avoid edge cases with mismatching data
+ if (pageContext.adLocation !== pageContext.parsedCardCount &&
+ pageContext.adPlacementBehavior !== models.AdPlacementBehavior.replaceOrganic) {
+ return null;
+ }
+ (_a = pageContext.pageInformation.iAdInfo) === null || _a === void 0 ? void 0 : _a.updateContainerId((_b = pageContext.pageInformation.iAdInfo) === null || _b === void 0 ? void 0 : _b.containerIdForSlotIndex((_c = pageContext.parsedCardCount) !== null && _c !== void 0 ? _c : 0));
+ const adCard = createTodayAdCard(objectGraph, pageContext.adData, pageContext.adIncidentRecorder, cardConfig, pageContext);
+ if (isSome(adCard)) {
+ return adCard;
+ }
+ else {
+ return null;
+ }
+}
+// MARK: - Shelf Header Content
+/**
+ * @param objectGraph The dependency graph of the app store
+ * @param item The today item to look for the eyebrow in
+ * @returns The text displayed above the title on a section header
+ */
+export function sectionEyebrowFromTodayItem(objectGraph, item) {
+ const editorialClientParams = extractEditorialClientParams(objectGraph, item.data);
+ if (item.isFirstItemInModule || editorialClientParams.suppressHeaderBadge) {
+ return null;
+ }
+ let sectionBadge;
+ const editorialCard = editorialCardFromData(item.data);
+ if (hasAttributes(editorialCard)) {
+ sectionBadge = mediaAttributes.attributeAsString(editorialCard, "headerBadge");
+ }
+ if (isSome(sectionBadge)) {
+ return sectionBadge;
+ }
+ switch (item.data.type) {
+ case "editorial-items":
+ sectionBadge = attributeAsString(item.data, "headerBadge");
+ break;
+ case "editorial-item-groups":
+ sectionBadge = attributeAsString(item.data, ["editorialNotes", "badge"]);
+ break;
+ default:
+ break;
+ }
+ return sectionBadge;
+}
+/**
+ * @param objectGraph The dependency graph of the app store
+ * @param item The today item to look for the title in
+ * @param alwaysReturnTitle If true, the title will be returned even if it is the first item in a module
+ * @returns The title displayed above a section
+ */
+export function sectionTitleFromTodayItem(objectGraph, item, alwaysReturnTitle = false) {
+ const editorialClientParams = extractEditorialClientParams(objectGraph, item.data);
+ if ((item.isFirstItemInModule || editorialClientParams.suppressHeaderName) && !alwaysReturnTitle) {
+ return null;
+ }
+ let sectionTitle;
+ const editorialCard = editorialCardFromData(item.data);
+ if (hasAttributes(editorialCard)) {
+ sectionTitle = mediaAttributes.attributeAsString(editorialCard, "headerName");
+ }
+ if (isSome(sectionTitle)) {
+ return sectionTitle;
+ }
+ switch (item.data.type) {
+ case "editorial-items":
+ sectionTitle = attributeAsString(item.data, "headerName");
+ break;
+ case "editorial-item-groups":
+ sectionTitle = attributeAsString(item.data, ["editorialNotes", "name"]);
+ break;
+ default:
+ break;
+ }
+ return sectionTitle;
+}
+/**
+ * @param objectGraph The dependency graph of the app store
+ * @param item The today item to look for the subtitle in
+ * @returns The subtitle displayed below the title on a section header
+ */
+function sectionSubtitleFromTodayItem(objectGraph, item) {
+ const editorialClientParams = extractEditorialClientParams(objectGraph, item.data);
+ if (item.isFirstItemInModule || editorialClientParams.suppressHeaderTagline) {
+ return null;
+ }
+ let sectionSubtitle;
+ const editorialCard = editorialCardFromData(item.data);
+ if (hasAttributes(editorialCard)) {
+ sectionSubtitle = mediaAttributes.attributeAsString(editorialCard, "headerTagline");
+ }
+ if (isSome(sectionSubtitle)) {
+ return sectionSubtitle;
+ }
+ switch (item.data.type) {
+ case "editorial-items":
+ sectionSubtitle = attributeAsString(item.data, "headerTagline");
+ break;
+ case "editorial-item-groups":
+ sectionSubtitle = attributeAsString(item.data, ["editorialNotes", "tagline"]);
+ break;
+ default:
+ break;
+ }
+ return sectionSubtitle;
+}
+/**
+ * @param objectGraph The dependency graph of the app store
+ * @param item The today item to look for the subtitle in
+ * @returns The artwork displayed in the eyebrow of a today section.
+ */
+function sectionHeaderArtworkFromTodayItemForPlacement(objectGraph, item, placement) {
+ var _a;
+ const editorialClientParams = extractEditorialClientParams(objectGraph, item.data);
+ const headerContents = relationshipData(objectGraph, item.data, "header-contents");
+ const artworkBehavior = (_a = editorialClientParams.headerArtworkBehavior) !== null && _a !== void 0 ? _a : TodayHeaderArtworkBehavior.NoArtwork;
+ switch (placement) {
+ case TodaySectionHeaderArtworkPlacement.Eyebrow:
+ switch (artworkBehavior) {
+ case TodayHeaderArtworkBehavior.CategoryArtworkWithBadge:
+ return categoryArtworkFromData(objectGraph, headerContents);
+ default:
+ return null;
+ }
+ case TodaySectionHeaderArtworkPlacement.Title:
+ switch (artworkBehavior) {
+ case TodayHeaderArtworkBehavior.CategoryArtworkWithTitle:
+ return categoryArtworkFromData(objectGraph, headerContents);
+ case TodayHeaderArtworkBehavior.ContentArtworkWithTitle:
+ return iconFromData(objectGraph, headerContents, {
+ useCase: 1 /* ArtworkUseCase.LockupIconSmall */,
+ });
+ default:
+ return null;
+ }
+ default:
+ return null;
+ }
+}
+/**
+ * @param objectGraph The dependency graph of the app store
+ * @param item The today item to look for the subtitle in
+ * @returns The artwork type displayed in the eyebrow of a today section.
+ */
+function sectionHeaderArtworkTypeFromTodayItemForPlacement(objectGraph, item, placement) {
+ const headerContents = relationshipData(objectGraph, item.data, "header-contents");
+ const artworkBehavior = attributeAsString(item.data, [
+ "editorialClientParams",
+ "headerArtworkBehavior",
+ ]);
+ switch (placement) {
+ case TodaySectionHeaderArtworkPlacement.Eyebrow:
+ switch (artworkBehavior) {
+ case TodayHeaderArtworkBehavior.CategoryArtworkWithBadge:
+ const hasCategoryArtwork = isSome(categoryArtworkFromData(objectGraph, headerContents));
+ return hasCategoryArtwork ? models.ShelfHeaderArtworkType.Category : null;
+ default:
+ return null;
+ }
+ case TodaySectionHeaderArtworkPlacement.Title:
+ switch (artworkBehavior) {
+ case TodayHeaderArtworkBehavior.CategoryArtworkWithTitle:
+ const hasCategoryArtwork = isSome(categoryArtworkFromData(objectGraph, headerContents));
+ return hasCategoryArtwork ? models.ShelfHeaderArtworkType.Category : null;
+ case TodayHeaderArtworkBehavior.ContentArtworkWithTitle:
+ const hasIconArtwork = isSome(categoryArtworkFromData(objectGraph, headerContents));
+ return hasIconArtwork ? models.ShelfHeaderArtworkType.Icon : null;
+ default:
+ return null;
+ }
+ default:
+ return null;
+ }
+}
+/**
+ *
+ * @param objectGraph The dependency graph of the app store
+ * @param data The MAPI data for the related content for a today item
+ * @returns The artwork to use for the related content
+ */
+function categoryArtworkFromData(objectGraph, data) {
+ const artworkData = categoryArtworkData(objectGraph, data, false, false, false);
+ if (isNothing(artworkData)) {
+ return null;
+ }
+ const artwork = artworkFromApiArtwork(objectGraph, artworkData, {
+ useCase: 20 /* ArtworkUseCase.CategoryIcon */,
+ allowingTransparency: true,
+ cropCode: "sr",
+ });
+ return artwork;
+}
+/**
+ * @param objectGraph The dependency graph for the App Store
+ * @param todayItem The today item that contains the data for a editorial-item or editorial-item-group, this is used to look for
+ * editorialBackground, which can then be used to generate the section gradient background
+ * @returns The background to use for the section
+ */
+function todaySectionEditorialBackground(objectGraph, todayItem) {
+ const editorialBackground = mediaAttributes.attributeAsDictionary(todayItem.data, "editorialBackground", null);
+ const editorialBackgroundType = editorialBackground === null || editorialBackground === void 0 ? void 0 : editorialBackground["type"];
+ if (isNothing(editorialBackgroundType)) {
+ return null;
+ }
+ let backgroundInfo = null;
+ switch (editorialBackgroundType) {
+ case EditorialBackgroundType.LinearGradient:
+ const linearGradientData = asInterface(editorialBackground);
+ const colors = linearGradientData.stops.map((stop) => color.fromHex(stop.color));
+ const shelfBackground = {
+ type: "gradient",
+ colors: colors,
+ start: models.ShelfBackgroundGradientLocation.Top,
+ end: models.ShelfBackgroundGradientLocation.Bottom,
+ };
+ const isDark = color.isDarkColor(colors[0]);
+ const secondaryLabelLightColor = {
+ type: "rgb",
+ red: 60 / 255,
+ green: 60 / 255,
+ blue: 67 / 255,
+ alpha: 0.6,
+ };
+ const secondaryLabelDarkColor = {
+ type: "rgb",
+ red: 235.0 / 255,
+ green: 235.0 / 255,
+ blue: 245.0 / 255,
+ alpha: 0.6,
+ };
+ backgroundInfo = {
+ shelfBackground: shelfBackground,
+ eyebrowColor: isDark ? secondaryLabelDarkColor : secondaryLabelLightColor,
+ titleColor: isDark ? color.named("white") : color.named("black"),
+ subtitleColor: isDark ? secondaryLabelDarkColor : secondaryLabelLightColor,
+ };
+ break;
+ default:
+ backgroundInfo = null;
+ break;
+ }
+ return backgroundInfo;
+}
+/**
+ * @param todayCards The today cards that are contained in the section
+ * @returns The background to use for the section
+ */
+function todaySectionBackgroundForHeroDisplayStyle(todayCards) {
+ const backgroundColors = todayCards
+ .map((todayCard) => {
+ return todayCard.media.bestBackgroundColor();
+ })
+ .filter((backgroundColor) => isSome(backgroundColor));
+ let shelfBackground = null;
+ if (backgroundColors.length > 0 && backgroundColors.length <= 4 && backgroundColors.length === todayCards.length) {
+ switch (backgroundColors.length) {
+ case 1:
+ shelfBackground = {
+ type: "materialGradient",
+ colors: {
+ colorCount: "oneColor",
+ color: backgroundColors[0],
+ },
+ };
+ break;
+ case 2:
+ shelfBackground = {
+ type: "materialGradient",
+ colors: {
+ colorCount: "twoColor",
+ top: backgroundColors[0],
+ bottom: backgroundColors[1],
+ },
+ };
+ break;
+ case 3:
+ shelfBackground = {
+ type: "materialGradient",
+ colors: {
+ colorCount: "threeColor",
+ top: backgroundColors[0],
+ bottomLeading: backgroundColors[1],
+ bottomTrailing: backgroundColors[2],
+ },
+ };
+ break;
+ case 4:
+ shelfBackground = {
+ type: "materialGradient",
+ colors: {
+ colorCount: "fourColor",
+ topLeading: backgroundColors[0],
+ topTrailing: backgroundColors[1],
+ bottomLeading: backgroundColors[2],
+ bottomTrailing: backgroundColors[3],
+ },
+ };
+ break;
+ default:
+ break;
+ }
+ }
+ else {
+ shelfBackground = {
+ type: "color",
+ color: color.named("secondarySystemBackground"),
+ };
+ }
+ return shelfBackground;
+}
+// MARK: - Metrics
+/**
+ * Retrieves the recommendation metrics data from a FlattenedTodayItem object.
+ * @param todayItem - The FlattenedTodayItem object containing the module metadata.
+ * @returns The recommendation metrics data as a JSONData object.
+ */
+export function recoMetricsFromTodayItem(todayItem) {
+ var _a, _b;
+ if (isNothing(todayItem)) {
+ return {};
+ }
+ const recoMetricsData = (_a = asDictionary(todayItem.moduleMetadata, "meta.metrics")) !== null && _a !== void 0 ? _a : {};
+ const combinedRecoMetricsData = (_b = combinedRecoMetricsDataFromMetricsData(recoMetricsData, todayItem.moduleMetadata.onDevicePersonalizationProcessingType, null)) !== null && _b !== void 0 ? _b : {};
+ return combinedRecoMetricsData;
+}
+// MARK: - Debug Util
+/**
+ * Generate a todayCardPreview url that contains the entire original today feed.
+ * @param objectGraph The dependency graph of the app store
+ * @param todayItems The flattened today feed items
+ * @returns The url to add to the today page for debug purposes
+ */
+export function feedPreviewUrlFromFlattenedTodayItems(objectGraph, todayItems) {
+ switch (objectGraph.client.buildType) {
+ case "debug":
+ case "internal":
+ const feedPreviewUrl = new URL();
+ feedPreviewUrl.protocol = Protocol.https;
+ feedPreviewUrl.host = "apps.apple.com";
+ feedPreviewUrl.pathname = `/${Path.todayCardPreview}`;
+ const idsParamValues = [];
+ for (const item of todayItems) {
+ switch (item.type) {
+ case FlattenedTodayItemType.EditorialItem:
+ idsParamValues.push(item.data.id);
+ break;
+ case FlattenedTodayItemType.EditorialItemGroup:
+ const groupItems = asArrayOrEmpty(item.data.meta, "associations.recommendations.data");
+ idsParamValues.push(`${item.data.id}:[${groupItems.map((groupItem) => groupItem.id).join(",")}]`);
+ break;
+ default:
+ break;
+ }
+ }
+ feedPreviewUrl.param(Parameters.ids, idsParamValues.join(","));
+ feedPreviewUrl.param(Parameters.isTodayFeedPreview, "true");
+ return decodeURIComponent(feedPreviewUrl.build());
+ default:
+ return null;
+ }
+}
+/**
+ * Generate a todayCardPreview url that can display a single today card
+ * @param objectGraph The dependency graph of the app store
+ * @param todayCardId The id of the created today card
+ * @param cardConfig The card config for the created today card
+ * @returns The url to add to the today page for debug purposes
+ */
+export function todayCardPreviewUrlForTodayCard(objectGraph, todayCardId, cardConfig) {
+ if (isNothing(todayCardId) || !objectGraph.client.isiOS) {
+ return null;
+ }
+ switch (objectGraph.client.buildType) {
+ case "debug":
+ case "internal":
+ const feedPreviewUrl = new URL();
+ feedPreviewUrl.protocol = Protocol.https;
+ feedPreviewUrl.host = "apps.apple.com";
+ feedPreviewUrl.pathname = `/${Path.todayCardPreview}`;
+ feedPreviewUrl.param(Parameters.ids, `${todayCardId}`);
+ feedPreviewUrl.param(Parameters.isTodayFeedPreview, "true");
+ feedPreviewUrl.param(Parameters.isTodaySection, cardConfig.useOTDTextStyle ? "true" : "false");
+ return decodeURIComponent(feedPreviewUrl.build());
+ default:
+ return null;
+ }
+}
+//# sourceMappingURL=today-parse-util.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/today/today-types.js b/node_modules/@jet-app/app-store/tmp/src/common/today/today-types.js
new file mode 100644
index 0000000..3c02e97
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/today/today-types.js
@@ -0,0 +1,118 @@
+import * as models from "../../api/models";
+import * as metricsHelpersLocation from "../metrics/helpers/location";
+/**
+ * Stateful container passed along pipeline from initial page building, mutating while components are being built.
+ * Properties on this *can* be snapshotted to re-initialize a context for shelf-reloading and pagination.
+ */
+export class TodayParseContext {
+ constructor(pageInformation, locationTracker, refreshController) {
+ // The location tracker for building metrics location information
+ this.locationTracker = metricsHelpersLocation.newLocationTracker();
+ /// The number of cards that we've successfully parsed, This excludes failed cards
+ this.parsedCardCount = 0;
+ /// The number of rows of cards that we've successfully added to the page. This is only applicable on
+ /// iPhone where we know there is only one layout per page. This differs from parsedCardCount in that
+ /// rows can contain multiple cards, such is the case with hero / grid shelves.
+ this.currentRowIndex = 0;
+ this.pageInformation = pageInformation;
+ this.locationTracker = locationTracker !== null && locationTracker !== void 0 ? locationTracker : metricsHelpersLocation.newLocationTracker();
+ this.refreshController = refreshController;
+ }
+}
+/**
+ * The `TodayPageContext` is used for building the initial page. It is used to maintain a record
+ * of the remaining items left in the today page, so it is also used for pagination
+ */
+export class TodayPageContext extends TodayParseContext {
+ constructor(remainingContent, pageInformation, locationTracker, refreshController, recoImpressionData) {
+ super(pageInformation, locationTracker, refreshController);
+ // Flag indicating whether we've ever successfully displayed any content on the page, if we're
+ // doing a secondary fetch and this is false, we try to fetch a bit more than normal pagination
+ // to have a higher chance of getting **some** content back.
+ this.pageHasDisplayedContent = false;
+ this.remainingContent = remainingContent;
+ this.adPlacementBehavior = models.AdPlacementBehavior.insertIntoShelf;
+ this.recoImpressionData = recoImpressionData;
+ }
+}
+/**
+ * A `TodayShelfToken` is used to maintain a record of each shelf's contents when subsequent content fetch is needed. It is encoded in the `url` property of `Shelf`.
+ */
+export class TodayShelfToken {
+ // endregion
+ constructor(todayModule, contentOffsetWithinModule, metricsPageInformation, metricsLocationTracker) {
+ this.todayModule = todayModule;
+ this.metricsPageInformation = metricsPageInformation;
+ this.contentOffsetWithinModule = contentOffsetWithinModule;
+ this.metricsLocationTracker = metricsLocationTracker;
+ }
+}
+/**
+ * These are the different ways we can display a today card
+ */
+export var TodayCardDisplayStyle;
+(function (TodayCardDisplayStyle) {
+ TodayCardDisplayStyle["AppEventCard"] = "AppEventCard";
+ TodayCardDisplayStyle["AppOfTheDay"] = "AppOfTheDay";
+ TodayCardDisplayStyle["FullBleedImage"] = "FullBleedImage";
+ TodayCardDisplayStyle["GameOfTheDay"] = "GameOfTheDay";
+ TodayCardDisplayStyle["Grid"] = "Grid";
+ TodayCardDisplayStyle["InAppPurchase"] = "InAppPurchase";
+ TodayCardDisplayStyle["List"] = "List";
+ TodayCardDisplayStyle["NumberedList"] = "NumberedList";
+ TodayCardDisplayStyle["River"] = "River";
+ TodayCardDisplayStyle["ShortImage"] = "ShortImage";
+ TodayCardDisplayStyle["SingleApp"] = "SingleApp";
+ TodayCardDisplayStyle["Video"] = "Video";
+})(TodayCardDisplayStyle || (TodayCardDisplayStyle = {}));
+/**
+ * These are the different sizes of today card we can have,
+ * the reason this uses the term `DisplayStyle` is because the the metrics spec
+ * already uses the term `DisplayStyle` to describe the size of different components
+ * so we're reusing that term here. This is not to be confused with `TodayCardDisplayStyle`
+ */
+export var TodayCardMetricsDisplayStyle;
+(function (TodayCardMetricsDisplayStyle) {
+ TodayCardMetricsDisplayStyle["SmallCard"] = "smallCard";
+ TodayCardMetricsDisplayStyle["MediumCard"] = "mediumCard";
+})(TodayCardMetricsDisplayStyle || (TodayCardMetricsDisplayStyle = {}));
+/**
+ * These are the different ways we can display artwork within a today section header
+ */
+export var TodayHeaderArtworkBehavior;
+(function (TodayHeaderArtworkBehavior) {
+ TodayHeaderArtworkBehavior["NoArtwork"] = "no-artwork";
+ TodayHeaderArtworkBehavior["ContentArtworkWithTitle"] = "content-artwork-with-title";
+ TodayHeaderArtworkBehavior["CategoryArtworkWithTitle"] = "category-artwork-with-title";
+ TodayHeaderArtworkBehavior["CategoryArtworkWithBadge"] = "category-artwork-with-badge";
+})(TodayHeaderArtworkBehavior || (TodayHeaderArtworkBehavior = {}));
+/**
+ * The different types of today card OTD intentions
+ */
+export var OfTheDayIntention;
+(function (OfTheDayIntention) {
+ OfTheDayIntention["AppOfTheDay"] = "app-of-the-day";
+ OfTheDayIntention["GameOfTheDay"] = "game-of-the-day";
+})(OfTheDayIntention || (OfTheDayIntention = {}));
+/**
+ * Indicates the different places we can have artwork in the section header
+ */
+export var TodaySectionHeaderArtworkPlacement;
+(function (TodaySectionHeaderArtworkPlacement) {
+ TodaySectionHeaderArtworkPlacement["Eyebrow"] = "eyebrow";
+ TodaySectionHeaderArtworkPlacement["Title"] = "title";
+})(TodaySectionHeaderArtworkPlacement || (TodaySectionHeaderArtworkPlacement = {}));
+export var EditorialBackgroundType;
+(function (EditorialBackgroundType) {
+ EditorialBackgroundType["LinearGradient"] = "linear-gradient";
+})(EditorialBackgroundType || (EditorialBackgroundType = {}));
+/**
+ * Describes the context in which hero media appears. Used to
+ * configure cards differently on macOS depending on where it appears.
+ */
+export var HeroMediaDisplayContext;
+(function (HeroMediaDisplayContext) {
+ HeroMediaDisplayContext["Inline"] = "inline";
+ HeroMediaDisplayContext["Article"] = "article";
+})(HeroMediaDisplayContext || (HeroMediaDisplayContext = {}));
+//# sourceMappingURL=today-types.js.map \ No newline at end of file