import * as validation from "@jet/environment/json/validation"; import { Shelf, Trailers, Video, } from "../../api/models"; import { metricsFromMediaApiObject, } from "../../foundation/media/data-structure"; import { hasAttributes } from "../../foundation/media/attributes"; import { isDefinedNonNullNonEmpty, isNullOrEmpty } from "../../foundation/json-parsing/server-data"; import { Parameters, Path, Protocol } from "../../foundation/network/url-constants"; import { URL } from "../../foundation/network/urls"; import * as lockups from "../../common/lockups/lockups"; import * as metricsHelpersImpressions from "../../common/metrics/helpers/impressions"; import * as videoDefaults from "../../common/constants/video-constants"; import * as content from "../../common/content/content"; import { newLocationTracker, nextPosition, popLocation, pushContentLocation, } from "../metrics/helpers/location"; export function createArcadeSeeAllGamesPaginationToken(_objectGraph, requestDescriptor, pageInformation) { const locationTracker = newLocationTracker(); const paginationUrl = URL.fromComponents(Protocol.internal, null, `/${Path.arcadeSeeAllGames}/${Path.arcadeSeeAllGamesLoadMore}`).build(); const paginationToken = { url: paginationUrl, metricsPageInformation: pageInformation, metricsLocationTracker: locationTracker, remainingGroups: [], lastShelfIndex: 0, isCompactMode: requestDescriptor.isCompactMode, }; return paginationToken; } export function createShelves(objectGraph, groups, paginationToken) { return validation.context("createShelves", () => { const shelves = []; if (groups.length === 0) { return shelves; } let shouldStartPagination = false; for (const group of groups) { // Start pagination if we hit a group who's first item has not been hydrated shouldStartPagination = shouldStartPagination || !hasAttributes(group.data[0]); if (shouldStartPagination) { paginationToken.remainingGroups.push(group); } else { const shelfToken = { index: paginationToken.lastShelfIndex, title: shelfTitleForGroup(objectGraph, group), contentType: shelfContentTypeForMode(objectGraph, paginationToken.isCompactMode), shouldFilter: false, remainingContent: group.data, groupKind: group.kind, isCompactMode: paginationToken.isCompactMode, hasExistingContent: false, isFirstRender: true, metricsPageInformation: paginationToken.metricsPageInformation, metricsLocationTracker: paginationToken.metricsLocationTracker, }; const shelf = createShelf(objectGraph, shelfToken); shelves.push(shelf); paginationToken.lastShelfIndex++; } } return shelves; }); } const defaultRowsPerColumn = 3; export function createShelf(objectGraph, shelfToken) { const shelfItems = []; const shelf = new Shelf(shelfToken.contentType); shelf.title = shelfToken.title; shelf.presentationHints = { showSupplementaryText: false }; if (objectGraph.client.isVision) { shelf.isHorizontal = (shelfToken.groupKind === "comingSoonGrouping" && !shelfToken.isCompactMode) || shelfToken.groupKind === "category"; shelf.presentationHints = { ...shelf.presentationHints, isSAGComingSoon: true, }; } else { shelf.isHorizontal = shelfToken.groupKind === "comingSoonGrouping" || shelfToken.groupKind === "category"; } const shelfMetricsOptions = { id: `${shelfToken.index}`, kind: null, softwareType: null, targetType: "swoosh", title: shelf.title, pageInformation: shelfToken.metricsPageInformation, locationTracker: shelfToken.metricsLocationTracker, idType: "sequential", }; /// add impression fields metricsHelpersImpressions.addImpressionFields(objectGraph, shelf, shelfMetricsOptions); pushContentLocation(objectGraph, shelfMetricsOptions, shelf.title); while (isDefinedNonNullNonEmpty(shelfToken.remainingContent) && hasAttributes(shelfToken.remainingContent[0])) { const lockupData = shelfToken.remainingContent.shift(); const lockupOptions = { metricsOptions: { pageInformation: shelfToken.metricsPageInformation, locationTracker: shelfToken.metricsLocationTracker, recoMetricsData: metricsFromMediaApiObject(lockupData), }, artworkUseCase: 1 /* content.ArtworkUseCase.LockupIconSmall */, canDisplayArcadeOfferButton: content.shelfContentTypeCanDisplayArcadeOfferButtons(objectGraph, shelfToken.contentType), shouldHideArcadeHeader: objectGraph.featureFlags.isEnabled("hide_arcade_header_on_arcade_tab"), isContainedInPreorderExclusiveShelf: shelfToken.groupKind === "comingSoonGrouping", }; const cropCode = objectGraph.client.isVision ? "sr" : null; const lockup = lockups.mixedMediaLockupFromData(objectGraph, lockupData, lockupOptions, videoDefaults.defaultVideoConfiguration(objectGraph), null, cropCode); const lockupHasVideos = ensureLockupHasVideos(objectGraph, lockup, lockupData); if (!lockupHasVideos) { continue; } lockup.editorialTagline = null; lockup.developerTagline = null; shelfItems.push(lockup); nextPosition(shelfToken.metricsLocationTracker); } shelf.mergeWhenFetched = shelfToken.hasExistingContent; shelfToken.hasExistingContent = shelfToken.hasExistingContent || shelfItems.length > 0; shelf.items = shelfItems; if (isDefinedNonNullNonEmpty(shelfToken.remainingContent)) { shelf.url = shelfUrlForToken(shelfToken); } else if (objectGraph.client.deviceType !== "tv" && shelfToken.isCompactMode && shelf.items.length < defaultRowsPerColumn) { shelf.rowsPerColumn = shelf.items.length; } popLocation(shelfToken.metricsLocationTracker); return shelf; } function shelfContentTypeForMode(objectGraph, isCompactMode) { if (isCompactMode) { return "smallLockup"; } else if (objectGraph.client.isTV) { return "mixedMediaLockup"; } else { return "appTrailerLockup"; } } function shelfTitleForGroup(objectGraph, group) { let shelfTitle; let dateComponents = null; let groupDateForTitle = null; let sentence; // rdar://104719340 (Arcade See All Games section has a missing name) // Temporary(?) work around for MAPI sometimes not returning a group.name if ((!group.name || group.name.length === 0) && group.kind !== "comingSoonGrouping") { return ""; } switch (group.kind) { case "releaseDateByDay": dateComponents = group.name.split("-"); groupDateForTitle = new Date(parseInt(dateComponents[0]), parseInt(dateComponents[1]) - 1, parseInt(dateComponents[2])); sentence = objectGraph.loc.string("ALL_GAMES_SECTION_TITLE_RELEASE_DATE_SENTENCE"); shelfTitle = objectGraph.loc.formatDateInSentence(sentence, "MMMM d, y", groupDateForTitle); break; case "lastUpdatedByDay": dateComponents = group.name.split("-"); groupDateForTitle = new Date(parseInt(dateComponents[0]), parseInt(dateComponents[1]) - 1, parseInt(dateComponents[2])); sentence = objectGraph.loc.string("ALL_GAMES_SECTION_TITLE_LAST_UPDATED_SENTENCE"); shelfTitle = objectGraph.loc.formatDateInSentence(sentence, "MMMM d, y", groupDateForTitle); break; case "comingSoonGrouping": shelfTitle = objectGraph.loc.string("Arcade.SeeAllGames.ComingSoonShelf.Title"); break; default: shelfTitle = group.name; break; } return shelfTitle; } function shelfUrlForToken(shelfToken) { if (isNullOrEmpty(shelfToken.remainingContent)) { return null; } return `${Protocol.internal}:/${Path.arcadeSeeAllGames}/${Path.shelf}/?${Parameters.token}=${encodeURIComponent(JSON.stringify(shelfToken))}`; } // region Fallback Data function ensureLockupHasVideos(objectGraph, lockup, lockupData) { // In visionOS, the bin-compat apps usually have trailer from iPad which using 4:3 video // But SAG lockup preferred 16:9 video, so even we already have lockup.trailers, we still want to override it // with the editorial splash video `splashVideo16x9`. // If there is no editorial splash video found, we fallback to the current `lockup.trailers` or create a dump video. if (isNullOrEmpty(lockup.trailers) || objectGraph.client.isVision) { const uberVideo = content.editorialSplashVideoFromData(objectGraph, lockupData); if (isDefinedNonNullNonEmpty(uberVideo)) { const uberTrailer = new Trailers(); uberVideo.playbackControls = videoDefaults.standardControls(objectGraph); uberVideo.autoPlayPlaybackControls = videoDefaults.autoPlayControls(objectGraph); uberVideo.canPlayFullScreen = true; uberTrailer.videos = [uberVideo]; lockup.trailers = [uberTrailer]; } } if (isNullOrEmpty(lockup.trailers)) { /** * See All Games: Bring back screenshots * In the very rare case that an arcade game has absolutely no videos, * that means no app trailer, no editorial video in any form. We're going to create a fake * video that uses the first screenshot as a preview frame. This allows us to * use the same AppTrailersLockup and get uniform sizing for lockups. */ addFakeVideoToMixedMediaLockup(lockup); } return isDefinedNonNullNonEmpty(lockup.trailers); } const fakeVideoURL = "x-as3-internal:/today/test"; function addFakeVideoToMixedMediaLockup(lockup) { const screenshots = lockup.screenshots[0]; if (isNullOrEmpty(screenshots)) { return; } const firstScreenshot = screenshots.artwork[0]; const fakeVideo = new Video(fakeVideoURL, firstScreenshot, { allowsAutoPlay: false, looping: false, canPlayFullScreen: false, autoPlayPlaybackControls: {}, playbackControls: {}, }); const fakeTrailers = new Trailers(); fakeTrailers.videos = [fakeVideo]; lockup.trailers = [fakeTrailers]; } // endregion //# sourceMappingURL=render-arcade-see-all-games-page.js.map