summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/common/today/today-controller-util.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/common/today/today-controller-util.js')
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/today/today-controller-util.js534
1 files changed, 534 insertions, 0 deletions
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