import * as models from "../../../api/models"; import * as serverData from "../../../foundation/json-parsing/server-data"; import * as mediaAttributes from "../../../foundation/media/attributes"; import * as mediaDataStructure from "../../../foundation/media/data-structure"; import * as mediaRelationship from "../../../foundation/media/relationships"; import * as adIncidents from "../../ads/ad-incident-recorder"; import * as adStitch from "../../ads/ad-stitcher"; import { searchLandingPagePositionInfo } from "../../ads/on-device-ad-stitch"; import * as mediaUrlMapping from "../../builders/url-mapping"; import * as videoDefaults from "../../constants/video-constants"; import * as content from "../../content/content"; import { CollectionShelfDisplayStyle } from "../../editorial-pages/editorial-page-types"; import * as filtering from "../../filtering"; import * as adLockups from "../../lockups/ad-lockups"; import * as lockupsEditorialContext from "../../lockups/editorial-context"; import * as lockups from "../../lockups/lockups"; import * as metricsHelpersClicks from "../../metrics/helpers/clicks"; import * as metricsHelpersLocation from "../../metrics/helpers/location"; import * as placeholders from "../../placeholders/placeholders"; import * as room from "../../room/room-common"; import * as topChartsCommon from "../../top-charts-common"; import * as pageCommon from "../../util/page-common"; import * as groupingTypes from "../grouping-types"; import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common"; import * as color from "../../../foundation/util/color-util"; import { pageRouter } from "../../builders/routing"; import { isNothing, isSome } from "@jet/environment/types/optional"; import * as modelsBase from "../../../api/models/base"; import * as metricsLocation from "../../../common/metrics/helpers/location"; import { makeGameCenterHeader, openGamesUIAction } from "../../arcade/arcade-common"; import { makeRoomPageIntent } from "../../../api/intents/room-page-intent"; import { makeChartsPageIntent } from "../../../api/intents/charts-page-intent"; import { makeChartsPageURL } from "../../../common/charts/charts-page-url"; import { getLocale } from "../../locale"; import { getPlatform } from "../../preview-platform"; import { genreIdFromChartURL } from "../../../common/grouping/render-grouping-page"; import { injectCrossfireFlowForGameCenter } from "./grouping-game-center-popular-with-your-friends-shelf-controller"; /** * Determine the shelfContentType to use for a shelf given the shelf token * @param objectGraph * @param mediaApiData * @param shelfToken * @param groupingParseContext */ export function shelfContentType(objectGraph, mediaApiData, shelfToken, groupingParseContext) { const featuredContentId = shelfToken.featuredContentId; if (featuredContentId === 557 /* groupingTypes.FeaturedContentID.AppStore_CategoryBreakoutMarker */ || featuredContentId === 418 /* groupingTypes.FeaturedContentID.AppStore_Shelf */ || featuredContentId === 495 /* groupingTypes.FeaturedContentID.AppStore_PopularWithYourFriendsMarker */ || groupingTypes.isRecommendationsLockupShelf(featuredContentId)) { let displayStyle = mediaAttributes.attributeAsString(mediaApiData, "displayStyle"); if (!displayStyle) { if (featuredContentId === 311 /* groupingTypes.FeaturedContentID.Sundance_RecommendedAppsShelf */ || featuredContentId === 312 /* groupingTypes.FeaturedContentID.Sundance_RecommendedGamesShelf */) { displayStyle = "large"; } else if (featuredContentId === 495 /* groupingTypes.FeaturedContentID.AppStore_PopularWithYourFriendsMarker */ || featuredContentId === 557 /* groupingTypes.FeaturedContentID.AppStore_CategoryBreakoutMarker */) { displayStyle = "medium"; } else { displayStyle = "small"; } } return `${displayStyle}Lockup`; } else if (featuredContentId === 431 /* groupingTypes.FeaturedContentID.AppStore_iAPShelf */) { return "inAppPurchaseTiledLockup"; } else if (featuredContentId === 429 /* groupingTypes.FeaturedContentID.AppStore_ScreenShotShelf */) { return "screenshotsLockup"; } else if (featuredContentId === 304 /* groupingTypes.FeaturedContentID.Sundance_iPhoneAppTrailerShelf */ || featuredContentId === 305 /* groupingTypes.FeaturedContentID.Sundance_iPadAppTrailerShelf */ || featuredContentId === 430 /* groupingTypes.FeaturedContentID.AppStore_VideoShelf */ || featuredContentId === 420 /* groupingTypes.FeaturedContentID.AppStore_TrailerShelf */) { return "appTrailerLockup"; } else if (groupingTypes.isTopChart(featuredContentId)) { return "smallLockup"; } else if (featuredContentId === 497 /* groupingTypes.FeaturedContentID.AppStore_ComingSoon */) { return "posterLockup"; } else { switch (groupingParseContext.shelves.length % 3) { case 0: { return "smallLockup"; } case 1: { return "mediumLockup"; } default: { return "largeLockup"; } } } } // region Shelf Token export function lockupShelfTokenFromBaseTokenAndMediaApiData(objectGraph, mediaApiData, baseShelfToken, groupingParseContext) { const lockupShelfToken = { ...baseShelfToken, shouldFilter: !mediaAttributes.attributeAsBooleanOrFalse(mediaApiData, "doNotFilter"), chartUrl: mediaAttributes.attributeAsString(mediaApiData, "chartHref"), chartIdentifier: mediaAttributes.attributeAsString(mediaApiData, "chart"), roomRelationshipData: mediaRelationship.relationshipData(objectGraph, mediaApiData, "room"), }; if (groupingTypes.isTopChart(lockupShelfToken.featuredContentId)) { lockupShelfToken.seeAllUrl = topChartsCommon.makeSeeAllUrlFromMediaApiData(objectGraph, mediaApiData, // chart shelf data includes selected chart groupingParseContext.chartSet); lockupShelfToken.showOrdinals = true; lockupShelfToken.shouldFilter = false; } else if (serverData.isDefinedNonNullNonEmpty(lockupShelfToken.roomRelationshipData)) { lockupShelfToken.seeAllUrl = mediaUrlMapping.hrefToRoutableUrl(objectGraph, lockupShelfToken.roomRelationshipData.href); } lockupShelfToken.shelfStyle = shelfContentType(objectGraph, mediaApiData, lockupShelfToken, groupingParseContext); lockupShelfToken.clientIdentifierOverride = lockupsEditorialContext.clientIdentifierForEditorialContextInData(objectGraph, mediaApiData); return lockupShelfToken; } // endregion // region Shelf Creation /** * * @param objectGraph The App Store dependency graph * @param shelfToken The shelf shelfToken for this current shelf creation request * @param shelfData The media api shelfContents array for this shelf * @param groupingParseContext The parse context used to generate the grouping page on the initial page load, * this will be missing when this controller renders a secondary or incomplete shelf fetch. */ export function createLockupShelf(objectGraph, shelfToken, shelfData, groupingParseContext) { const filterType = groupingTypes.isMacTopChart(shelfToken.featuredContentId) ? 84862 /* filtering.Filter.ChartsMac */ : 80894 /* filtering.Filter.All */; let items = []; // Stitch First Position Ad (only if lockup array is nonempty) if (serverData.isDefinedNonNullNonEmpty(shelfData.shelfContents)) { const adLockup = lockupFromAdStitcher(objectGraph, groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.adStitcher, groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.adIncidentRecorder, searchLandingPagePositionInfo, shelfToken); if (adLockup && adLockup instanceof models.Lockup) { items.push(adLockup); metricsHelpersLocation.nextPosition(shelfToken.metricsLocationTracker); shelfToken.ordinalIndex++; shelfData.shelfContents = shelfData.shelfContents.filter((data) => data.id !== adLockup.adamId); // Filter dupe } } // Data to use for `seeAllContents` in Category Breakout shelf let seeAllContents; // Build Lockups for (const lockupData of shelfData.shelfContents) { // If we encounter a type of app-events, this means they have been incorrectly programmed, // and we should throw the shelf away. if (lockupData.type === "app-events") { return null; } // If we encounter a type of `editorial-items` in // Category Breakout shelf, it is to use for `seeAllContents`. if (shelfToken.featuredContentId === 557 /* groupingTypes.FeaturedContentID.AppStore_CategoryBreakoutMarker */ && lockupData.type === "editorial-items") { seeAllContents = lockupData; continue; } if (serverData.isNull(lockupData.attributes) || groupingShelfControllerCommon.shouldDefer(shelfToken)) { shelfToken.isDeferring = true; shelfToken.remainingItems.push(lockupData); continue; } // Filter out unwanted content if (filtering.shouldFilter(objectGraph, lockupData, filterType)) { continue; } const lockup = lockupFromData(objectGraph, lockupData, shelfToken); if (lockup) { items.push(lockup); metricsHelpersLocation.nextPosition(shelfToken.metricsLocationTracker); shelfToken.ordinalIndex++; } } // Truncate non-Arcade top charts to multiples of 3 if (groupingTypes.isTopChart(shelfToken.featuredContentId) && !groupingTypes.isArcadeTopChart(shelfToken.featuredContentId) && !groupingTypes.isGameCenterTopChart(shelfToken.featuredContentId) && !objectGraph.client.isMac) { items = pageCommon.truncateItems(items, 3); } // We don't need this in our incomplete shelf URL, so we'll preemptively remove it. delete shelfToken.maxItemCount; // Create shelf header let shelfHeader; if (groupingTypes.isGameCenterTopChart(shelfToken.featuredContentId)) { // Show Game Center eyebrow for Game Center top chart shelves shelfHeader = makeGameCenterHeader(objectGraph, shelfToken.title); } else { shelfHeader = { eyebrow: shelfToken.eyebrow, eyebrowArtwork: shelfToken.eyebrowArtwork, title: shelfToken.title, titleArtwork: shelfToken.titleArtwork, subtitle: shelfToken.subtitle, configuration: shelfToken.shelfHeaderConfiguration, }; } // Create shelf const shelf = new models.Shelf(shelfToken.shelfStyle); shelf.isHorizontal = true; shelf.items = items; shelf.shouldFilterApps = shelfToken.shouldFilter; if (groupingTypes.isGameCenterTopChart(shelfToken.featuredContentId)) { shelf.footerTitle = objectGraph.loc.string("Lockup.Footer.GamesApp"); shelf.footerAction = openGamesUIAction(objectGraph); shelf.footerStyle = { $kind: "games", bundleID: "com.apple.games", width: 16, height: 16, }; shelf.items.forEach((lockup, index) => { if (isSome(lockup) && lockup instanceof models.Lockup) { lockup.clickAction = injectCrossfireFlowForGameCenter(objectGraph, lockup.clickAction); } }); } if (shelfToken.featuredContentId === 557 /* groupingTypes.FeaturedContentID.AppStore_CategoryBreakoutMarker */) { // Configure Category Breakout shelf configureCategoryBreakoutShelf(objectGraph, shelfToken, shelf, shelfHeader, items, seeAllContents, groupingParseContext); } else if (!objectGraph.props.enabled("redownloadButtonTintUsingOfferTheme")) { shelf.items.forEach((lockup, index) => { if (isSome(lockup) && lockup instanceof models.Lockup) { lockup.redownloadButtonTint = color.named("systemBlue"); } }); } // If no items should we display placeholders for this shelf? const willHydrateShelfLater = shelf && serverData.isNullOrEmpty(shelf.items) && shelfToken.isFirstRender; if (willHydrateShelfLater && placeholders.placeholdersEnabled(objectGraph)) { placeholders.insertPlaceholdersIntoGenericPageShelf(objectGraph, shelf, shelfToken, shelfToken.featuredContentId); } // Create the action let action; if (groupingTypes.isTopChart(shelfToken.featuredContentId)) { action = new models.FlowAction("topCharts"); } else { action = new models.FlowAction("page"); // The web client makes a separate request for See All pages, so we don't sidepack for web. if (!placeholders.isPlaceholderShelf(shelf) && !objectGraph.client.isWeb) { // Create the sidepacked room page const preferredShelfStyle = preferredRoomSeeAllShelfStyle(objectGraph, shelfToken.shelfStyle); action.pageData = room.seeAllPage(objectGraph, shelfToken.title, preferredShelfStyle); } } if (objectGraph.client.isWeb) { if (groupingTypes.isTopChart(shelfToken.featuredContentId)) { const genre = (shelfToken.seeAllUrl && genreIdFromChartURL(shelfToken.seeAllUrl)) || shelfToken.pageGenreId || ""; const destination = makeChartsPageIntent({ ...getLocale(objectGraph), ...getPlatform(objectGraph), chart: shelfToken.chartIdentifier, genreId: genre.toString(), }); action.destination = destination; action.pageUrl = makeChartsPageURL(objectGraph, destination); } else { const destination = makeRoomPageIntent({ ...getLocale(objectGraph), ...getPlatform(objectGraph), id: shelfToken.id, }); action.destination = destination; action.pageUrl = room.makeCanonicalRoomPageUrl(objectGraph, destination); } } else { action.pageUrl = shelfToken.seeAllUrl; } action.title = objectGraph.loc.string("ACTION_SEE_ALL"); action.referrerUrl = shelfToken.metricsPageInformation.pageUrl; metricsHelpersClicks.addClickEventToSeeAllAction(objectGraph, action, shelfToken.seeAllUrl, { pageInformation: shelfToken.metricsPageInformation, locationTracker: shelfToken.metricsLocationTracker, }); const shouldIncludeSeeAllAction = shouldShowSeeAll(objectGraph, shelf.items, shelfToken); const seeAllActionIsValid = serverData.isDefinedNonNull(action.pageUrl) || serverData.isDefinedNonNull(action.pageData); if (shouldIncludeSeeAllAction && seeAllActionIsValid) { groupingShelfControllerCommon.replaceShelfHeaderSeeAllAction(objectGraph, shelfHeader, action); } if (shelfToken.shelfStyle === "screenshotsLockup" || shelfToken.shelfStyle === "appTrailerLockup") { // `suppressTagline` is passed from MediaAPI as a string (see rdar://89933411) const shouldShowSupplementaryText = mediaAttributes.attributeAsString(shelfToken.featuredContentData, "suppressTagline") !== "true"; if (serverData.isNull(shelf.presentationHints)) { shelf.presentationHints = { showSupplementaryText: shouldShowSupplementaryText }; } else { shelf.presentationHints = { ...shelf.presentationHints, showSupplementaryText: shouldShowSupplementaryText, }; } } // On macOS, posters should take up the full width unless the shelf has enough items. if (shelfToken.shelfStyle === "posterLockup" && objectGraph.client.isMac && shelf.items.length < 3) { if (serverData.isNull(shelf.presentationHints)) { shelf.presentationHints = { isLowDensity: true }; } else { shelf.presentationHints = { ...shelf.presentationHints, isLowDensity: true }; } } shelf.header = shelfHeader; shelf.url = groupingShelfControllerCommon.createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, shelfToken); return shelf; } // endregion // region Lockup Creation /** * Create a lockup for shelfContents to display within a grouping shelf * @param objectGraph * @param lockupData shelfContents to create lockup for. * @param shelfToken Shelf shelfToken */ export function lockupFromData(objectGraph, lockupData, shelfToken) { if (serverData.isNullOrEmpty(lockupData)) { return null; } // Set the ordinal let ordinalString; if (shelfToken.showOrdinals) { ordinalString = objectGraph.loc.decimal(shelfToken.ordinalIndex); } let offerStyle = null; if (serverData.isDefinedNonNull(shelfToken.shelfBackground) && (shelfToken.shelfBackground.type === "color" || shelfToken.shelfBackground.type === "interactive")) { offerStyle = "white"; } let clientIdentifierOverride; if (serverData.isDefinedNonNullNonEmpty(shelfToken)) { clientIdentifierOverride = shelfToken.clientIdentifierOverride; } // Create the lockup const lockupOptions = { ordinal: ordinalString, metricsOptions: { pageInformation: shelfToken.metricsPageInformation, locationTracker: shelfToken.metricsLocationTracker, recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(lockupData), isAdvert: adLockups.isAdvert(objectGraph, lockupData), }, clientIdentifierOverride: clientIdentifierOverride, artworkUseCase: content.artworkUseCaseFromShelfStyle(objectGraph, shelfToken.shelfStyle), offerStyle: offerStyle, canDisplayArcadeOfferButton: content.shelfContentTypeCanDisplayArcadeOfferButtons(objectGraph, shelfToken.shelfStyle), isContainedInPreorderExclusiveShelf: shelfToken.featuredContentId === 497 /* groupingTypes.FeaturedContentID.AppStore_ComingSoon */, shouldHideArcadeHeader: objectGraph.featureFlags.isEnabled("hide_arcade_header_on_arcade_tab") && shelfToken.isArcadePage, }; let lockup; switch (shelfToken.shelfStyle) { case "appTrailerLockup": lockup = lockups.trailersLockupFromData(objectGraph, lockupData, lockupOptions, videoDefaults.defaultVideoConfiguration(objectGraph)); break; case "screenshotsLockup": lockup = lockups.screenshotsLockupFromData(objectGraph, lockupData, lockupOptions); break; case "posterLockup": lockup = lockups.posterLockupFromData(objectGraph, lockupData, lockupOptions); break; case "inAppPurchaseLockup": case "inAppPurchaseTiledLockup": lockup = lockups.inAppPurchaseLockupFromData(objectGraph, lockupData, lockupOptions); break; case "smallImageLockup": case "mediumImageLockup": case "largeImageLockup": lockup = lockups.imageLockupFromData(objectGraph, lockupData, lockupOptions, CollectionShelfDisplayStyle.EditorialLockupLarge); break; default: lockup = lockups.lockupFromData(objectGraph, lockupData, lockupOptions); } if (serverData.isNull(lockup) || !lockup.isValid()) { return null; } return lockup; } // endregion // region See All /** * Determine whether lockup shelves should show "See All" on given platforms. * This covers both top charts shelves and lockup shelves. * Special care should be taken to not make assumptions about the hydration state of the lockup shelves. * * @param objectGraph The App Store object graph. * @param readyItems Items that will be vended as part of `shelf.items` in this shelf. This may be subset of items in shelf following subsequent fetch. * @param shelfToken Grouping shelf shelfToken for shelf. This is used for determining whether shelf is part of initial page render, and to check whether there are more items to load. * @return Whether or not a lockup shelf with given properties should have a "See All" action. */ export function shouldShowSeeAll(objectGraph, readyItems, shelfToken) { // We do not want to attach "See All" if the grouping type is a Category Breakout Marker. if (shelfToken.featuredContentId === 557 /* groupingTypes.FeaturedContentID.AppStore_CategoryBreakoutMarker */) { return false; } // We do not want to attach "See All" if the grouping type is one of the Arcade Top Chart types if (groupingTypes.isArcadeTopChart(shelfToken.featuredContentId)) { return false; } // We do not want to attach "See All" if the grouping type is one of the Game Center Top Chart types if (groupingTypes.isGameCenterTopChart(shelfToken.featuredContentId)) { return false; } // On tvOS, "See All" is exposed as a clickable cell in-line with lockup cells. We want to show "See All" cells for: // 1. Shelf that have completed loading, i.e. don't have `shelfToken.remainingItems`. // 2. Lockup shelves that have *a lot* of content, which we clip within grouping shelves and show in full only in in the "See All" page. // // *Note* that we are *NOT* showing "See All" for shelves that have a "See All" url configured on tvOS. // It appears that every shelf has a "See All" url, and that url returns the exact same contents as contents of shelf's `contents.data` as part of initial page fetch causing this: // RFW4: Grouping: Shelves shouldn't have see all unless there's more to show. // // It appears we don't have capability to know in advance whether "See All" url has more to show than what is part of `contents.data` in initial page fetch. // In our experiments, it looks like they are always the same. If this changes, we'd also want: // const hasSeeAllRoom = shelfToken.seeAllUrl?.length > 0; // if (hasSeeAllRoom) { // return true; // } // // In it's current form, we don't do (2), since we don't limit the number of lockups in a shelf we display within groupings. That is tracked under: // Grouping: Limit number of items in shelves if (objectGraph.client.isTV) { // Whether or not this shelf is a partially hydrated shelf. This means there should be a secondary fetch request which causes the shelf will grow. // We'll opt to *NEVER* show "See All" if the shelf will grow, and tack it on in the secondary shelf fetch. const willHaveSubsequentFetch = serverData.isDefinedNonNullNonEmpty(shelfToken.remainingItems); if (willHaveSubsequentFetch) { return false; } // Whether or not this shelf is a nonempty top charts. const isNonemptyTopCharts = groupingTypes.isTopChart(shelfToken.featuredContentId) && serverData.isDefinedNonNullNonEmpty(readyItems); if (isNonemptyTopCharts) { return true; } // Otherwise, don't show "See All" on tvOS. return false; } // On all other platforms, we always to attach "See All", except AppStore_ComingSoon, // which should not because the see all page won't drop lockups whose pre-order has // been released (which the AppStore_ComingSoon swoosh will). return shelfToken.featuredContentId !== 497 /* groupingTypes.FeaturedContentID.AppStore_ComingSoon */; } /** * Coerces the provided shelf content type into a type suitable for use in a 'See All' room. * @param objectGraph * @param originalShelfStyle The content type that defines the 'See All' parent. * @returns {models.ShelfContentType} The content type to use for the 'See All' room. Returns `null` if there is no * preference, at which point the room builder will provide its default. */ export function preferredRoomSeeAllShelfStyle(objectGraph, originalShelfStyle) { if (!originalShelfStyle) { return null; } switch (originalShelfStyle) { case "inAppPurchaseLockup": case "inAppPurchaseTiledLockup": return originalShelfStyle; default: return null; } } // endregion // region Category Breakout export function configureCategoryBreakoutShelf(objectGraph, shelfToken, shelf, shelfHeader, items, seeAllContents, groupingParseContext) { // If `seeAllContents` is not hydrated, fetch it from the relationship data. if (isNothing(seeAllContents)) { seeAllContents = mediaRelationship.relationshipData(objectGraph, shelfToken.featuredContentData, "see-all-contents"); } // Trailing artwork shelfHeader.configuration.includeTrailingArtwork = true; // If `seeAllContents` is sparse, add it to the remaining items to hydrate. if (!mediaAttributes.hasAttributes(seeAllContents)) { const badge = mediaAttributes.attributeAsString(shelfToken.featuredContentData, "name"); shelfHeader.eyebrow = badge; shelfHeader.title = ""; shelf.footerTitle = ""; shelfToken.isDeferring = true; shelfToken.remainingItems.push(seeAllContents); return; } // Badge const badge = mediaAttributes.attributeAsString(shelfToken.featuredContentData, "name"); shelfHeader.eyebrow = badge; // Title const editorialNotes = mediaAttributes.attributeAsDictionary(seeAllContents, "editorialNotes"); const title = serverData.asString(editorialNotes, "name"); shelfHeader.title = title; // Configure location name to the updated title metricsLocation.currentLocation(shelfToken.metricsLocationTracker).name = title; // Configure impression name to the updated title if placeholder shelf used if (serverData.isDefinedNonNullNonEmpty(shelfToken.originalPlaceholderShelfImpressionMetrics)) { shelfToken.originalPlaceholderShelfImpressionMetrics.fields.name = title; } // Artwork const editorialArtwork = mediaAttributes.attributeAsDictionary(seeAllContents, "editorialArtwork"); // Drop the shelf if there is no editorial artwork. if (isNothing(editorialArtwork)) { shelf.isHidden = true; return; } // Background artwork const backgroundArtworkDict = serverData.asDictionary(editorialArtwork, "storyBackgroundStatic16x9"); const backgroundArtworkOptions = { useCase: 28 /* content.ArtworkUseCase.CategoryBreakoutShelf */, withJoeColorPlaceholder: true, }; const backgroundArtwork = groupingShelfControllerCommon.groupingArtworkFromApiArtwork(objectGraph, backgroundArtworkDict, backgroundArtworkOptions); const backgroundStyle = color.isDarkColor(backgroundArtwork.backgroundColor) ? "dark" : "light"; shelf.background = { type: "artwork", artwork: backgroundArtwork, style: backgroundStyle, }; // Lockup offerDisplayProperties items.forEach((lockup, index) => { if (isSome(lockup) && lockup instanceof models.Lockup) { lockup.offerDisplayProperties = lockup.offerDisplayProperties.newOfferDisplayPropertiesChangingAppearance(false, "transparent", backgroundStyle); lockup.subtitleTextFilter = "plusLight"; if (!objectGraph.props.enabled("redownloadButtonTintUsingOfferTheme")) { lockup.redownloadButtonTint = color.named("white"); } } }); // Trailing artwork const trailingArtworkDict = serverData.asDictionary(editorialArtwork, "contentGraphicTrimmed"); // Drop the shelf if there is no trailing artwork. if (isNothing(trailingArtworkDict)) { shelf.isHidden = true; return; } const trailingArtworkOptions = { contentMode: modelsBase.ArtworkContentMode.scaleAspectFit, useCase: 18 /* content.ArtworkUseCase.GroupingBrick */, }; const trailingArtwork = groupingShelfControllerCommon.groupingArtworkFromApiArtwork(objectGraph, trailingArtworkDict, trailingArtworkOptions); shelfHeader.trailingArtwork = trailingArtwork; // Footer const actionUrl = serverData.asString(seeAllContents.attributes, "url"); const footerTitle = serverData.asString(seeAllContents.attributes, "breakoutCallToActionLabel"); const clickOptions = { id: seeAllContents.id, idType: "its_id", targetType: "button", actionType: "navigate", pageInformation: shelfToken.metricsPageInformation, locationTracker: shelfToken.metricsLocationTracker, }; if (isSome(actionUrl)) { // Footer title shelf.footerTitle = footerTitle; // Footer action const flowPage = objectGraph.required(pageRouter).fetchFlowPage(actionUrl); const flowAction = new models.FlowAction(flowPage); flowAction.title = footerTitle; flowAction.pageUrl = actionUrl; metricsHelpersClicks.addClickEventToAction(objectGraph, flowAction, clickOptions); shelf.footerAction = flowAction; } else { const metricsOptions = { targetType: "button", pageInformation: shelfToken.metricsPageInformation, locationTracker: shelfToken.metricsLocationTracker, recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(seeAllContents), }; const metadata = groupingShelfControllerCommon.metadataForFCData(objectGraph, seeAllContents, shelfToken, false, null, metricsOptions, groupingParseContext); if (isSome(metadata.action)) { // Clear `actionMetrics` before adding a click event with updated `title`. metadata.action.actionMetrics.clearAll(); metadata.action.title = footerTitle !== null && footerTitle !== void 0 ? footerTitle : serverData.asString(editorialNotes, "callToAction"); metricsHelpersClicks.addClickEventToAction(objectGraph, metadata.action, clickOptions); shelf.footerAction = metadata.action; shelf.footerTitle = metadata.action.title; } } } // endregion // region Ads /** * Performs `lockupFromData`, but additional with Ad stitch related side-effects. * @param objectGraph The AppStore dependency graph * @param adStitcher Stitcher to source shelfContents from. May be undefined * @param adIncidentRecorder Incident recorder for adding ads. * @param positionInfo Position this ad is being stitched into. * @param shelfToken shelfToken for shelf this ad lockup will be in. */ export function lockupFromAdStitcher(objectGraph, adStitcher, adIncidentRecorder, positionInfo, shelfToken) { const task = adStitch.consumeTask(adStitcher, positionInfo); if (serverData.isNull(task)) { return null; // no task for position } // Try to create lockup const lockupData = task.data; const lockup = lockupFromData(objectGraph, lockupData, shelfToken); if (serverData.isDefinedNonNull(lockup)) { shelfToken.includedAdAdamIds = [lockupData.id]; } else { adIncidents.recordLockupFromDataFailed(objectGraph, adIncidentRecorder, lockupData); } return lockup; } // endregion //# sourceMappingURL=grouping-lockup-shelf-controller-common.js.map