import * as validation from "@jet/environment/json/validation"; import * as models from "../../../api/models"; import * as actions from "../../../api/models/actions"; import * as serverData from "../../../foundation/json-parsing/server-data"; import * as mediaDataFetching from "../../../foundation/media/data-fetching"; import * as mediaNetwork from "../../../foundation/media/network"; import { ResponseMetadata } from "../../../foundation/network/network"; import { Parameters, Path, Protocol } from "../../../foundation/network/url-constants"; import * as urls from "../../../foundation/network/urls"; import * as color from "../../../foundation/util/color-util"; import * as content from "../../content/content"; 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 { GroupingShelfController } from "./grouping-shelf-controller"; import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common"; import { injectCrossfireFlowForGameCenter } from "./grouping-game-center-popular-with-your-friends-shelf-controller"; export class GroupingGameCenterReengagementShelfController extends GroupingShelfController { // region Constructors constructor() { super("GroupingGameCenterReengagementShelfController"); this.batchGroupKey = "gameCenter"; this.supportedFeaturedContentIds = new Set([494 /* groupingTypes.FeaturedContentID.AppStore_GameCenterReengagement */]); } // endregion // region Shelf Builder shelfRoute(objectGraph) { return [ ...super.shelfRoute(objectGraph), { protocol: Protocol.internal, path: `/${Path.grouping}/${Path.shelf}/{token}`, query: [Parameters.isGameCenterReengagementShelf], }, ]; } // endregion // region Shelf Creation Prerequisites /** * For a given mediaApiData extract the actual shelfContents array needed to render this shelf * * @param mediaApiData The outer shelfContents object containing the shelf contents */ initialShelfDataFromGroupingMediaApiData(objectGraph, mediaApiData) { return { shelfContents: [], achievementData: null, achievementSummaryData: null, }; } /** * For a given url that this controller handles, we should return a promise that will result in the `ShelfData` * needed to render this shelf * * @param objectGraph The App Store dependency graph * @param shelfUrl The url that this controller handled on a secondary fetch * @param parameters The extracted parameters from the shelf url */ async secondaryShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters) { return await objectGraph.gameCenter.fetchRengagementDataForLocalPlayer().then(async (reengagementData) => { const adamID = serverData.asString(reengagementData, "adamId"); const achievement = serverData.asJSONData(reengagementData["achievement"]); const achievementSummary = serverData.asJSONData(reengagementData["achievementSummary"]); if (serverData.isNullOrEmpty(adamID)) { // One of the few exceptions where TS minifiers cannot derive data container type // without some help, in this case using explicit type for the value passed to promise. const emptyShelfData = { shelfContents: [], responseTimingValues: null, achievementData: null, achievementSummaryData: null, }; return await Promise.resolve(emptyShelfData); } const request = new mediaDataFetching.Request(objectGraph) .withIdOfType(adamID, "apps") .includingAgeRestrictions(); groupingShelfControllerCommon.prepareGroupingShelfRequest(objectGraph, request); return await mediaNetwork .fetchData(objectGraph, request, {}) .then((dataContainer) => { return { shelfContents: dataContainer.data, responseTimingValues: dataContainer[ResponseMetadata.timingValues], achievementData: achievement, achievementSummaryData: achievementSummary, }; }); }); } /** * For a given mediaApiData create an updated shelf token that contains all the additional data for this specific shelf type * * @param objectGraph The App Store dependency graph * @param baseShelfToken The base grouping shelf token created by the grouping-controller * @param mediaApiData The outer data object containing the FC properties and data * @param groupingParseContext The parse context for the grouping page so far */ shelfTokenFromBaseTokenAndMediaApiData(objectGraph, mediaApiData, baseShelfToken, groupingParseContext) { return baseShelfToken; } // 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. */ _createShelf(objectGraph, shelfToken, shelfData, groupingParseContext) { if (shelfToken.isFirstRender) { return this.pendingGameCenterReengagementShelf(objectGraph, shelfData, shelfToken, groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.isArcadePage); } else { return this.gameCenterReengagementShelf(objectGraph, shelfData, shelfToken, groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.isArcadePage); } } pendingGameCenterReengagementShelf(objectGraph, shelfData, shelfToken, isArcadePage) { const shelf = this.gameCenterReengagementShelf(objectGraph, shelfData, shelfToken, isArcadePage); if (!shelf) { return null; } const groupingShelfUrl = urls.URL.from(groupingShelfControllerCommon.groupingShelfUrl(shelfToken)); shelf.url = groupingShelfUrl.param(Parameters.isGameCenterReengagementShelf, "true").build(); shelf.batchGroup = this.batchGroupKey; return shelf; } gameCenterReengagementShelf(objectGraph, shelfData, shelfToken, isArcadePage = false) { return validation.context("gameCenterReengagementShelf", () => { if (!serverData.isDefinedNonNullNonEmpty(shelfData.shelfContents)) { return null; } const shelf = new models.Shelf("gameCenterReengagement"); shelf.isHorizontal = false; shelf.mergeWhenFetched = false; // Always replace shelf.batchGroup = this.batchGroupKey; const heroMetricsOptions = { id: shelfToken.id, kind: null, softwareType: isArcadePage ? "Arcade" : null, targetType: "achievements", title: "Achievements Hero", pageInformation: shelfToken.metricsPageInformation, locationTracker: shelfToken.metricsLocationTracker, idType: "its_contentId", fcKind: shelfToken.featuredContentId, badges: { gameCenter: true, }, }; // Hero Background / Artwork const artwork = content.productEditorialVideoFromData(objectGraph, shelfData.shelfContents[0], 21 /* content.ArtworkUseCase.Uber */); let backgroundColor = color.named("componentBackground"); let preview = null; if (serverData.isDefinedNonNullNonEmpty(artwork)) { preview = artwork.preview; backgroundColor = preview.backgroundColor; } // Build lockup used by reegnagement shelf const lockupListOptions = { lockupOptions: { metricsOptions: { pageInformation: shelfToken.metricsPageInformation, locationTracker: shelfToken.metricsLocationTracker, }, offerStyle: "white", artworkUseCase: content.artworkUseCaseFromShelfStyle(objectGraph, "smallLockup"), isSubtitleHidden: true, }, }; metricsHelpersLocation.pushContentLocation(objectGraph, heroMetricsOptions, heroMetricsOptions.title); const heroLockup = lockups.lockupsFromData(objectGraph, shelfData.shelfContents, lockupListOptions)[0]; heroLockup.clickAction = injectCrossfireFlowForGameCenter(objectGraph, heroLockup.clickAction); metricsHelpersLocation.popLocation(shelfToken.metricsLocationTracker); // Click action used by hero view (achievement card) let heroAction = null; if (serverData.isDefinedNonNullNonEmpty(heroLockup)) { heroAction = new actions.GameCenterAchievementsAction(heroLockup.bundleId); heroAction.title = "Achievements Hero"; // `addClickEventToAction` uses `title` for `name` field currently. metricsHelpersClicks.addClickEventToAction(objectGraph, heroAction, heroMetricsOptions); } const shelfBadge = objectGraph.loc.string("GameCenter.Reengagement.Badge.GameCenter"); const achievement = this.achievementFromData(objectGraph, shelfData.achievementData); const achievementCounts = this.achievementCountsFromData(objectGraph, shelfData.achievementSummaryData); const shelfMetadata = this.shelfMetadataForAchievement(objectGraph, achievement, achievementCounts); const heroItem = new models.GameCenterReengagement("gamecenter.fill", shelfBadge, shelfMetadata.title, shelfMetadata.subtitle, achievement, heroLockup, backgroundColor, preview, heroAction); shelf.items = [heroItem]; /** * For reengagement, the shelf is a container fully fulled by `heroItem`. * Match what breakouts do by impressing `heroItem` directly instead of the `shelf`. */ metricsHelpersImpressions.addImpressionFields(objectGraph, heroItem, heroMetricsOptions); return shelf; }); } // region Helpers achievementStatusFromData(objectGraph, data) { const statusType = serverData.asString(data, "type"); const status = new models.GameCenterAchievementStatus(statusType); status.percent = serverData.asNumber(data, "percent"); status.date = serverData.asString(data, "date"); status.artwork = new models.Artwork(serverData.asString(data, "artwork.template"), serverData.asNumber(data, "artwork.width"), serverData.asNumber(data, "artwork.height"), []); return status; } achievementFromData(objectGraph, data) { const id = serverData.asString(data, "id"); const title = serverData.asString(data, "title"); const subtitle = serverData.asString(data, "subtitle"); const status = this.achievementStatusFromData(objectGraph, serverData.asDictionary(data, "status")); return new models.GameCenterAchievement(id, title, subtitle, status); } achievementCountsFromData(objectGraph, data) { const completedAchievements = serverData.asNumber(data, "completedAchievements"); const totalAchievements = serverData.asNumber(data, "totalAchievements"); return { completed: completedAchievements, total: totalAchievements }; } shelfMetadataForAchievement(objectGraph, achievement, achievementCounts) { if (!serverData.isDefinedNonNull(achievement)) { return { title: "", subtitle: null }; } if (achievementCounts.completed === 0) { return { title: objectGraph.loc.string("GameCenter.Reengagement.Achievement.First.Title"), subtitle: objectGraph.loc.string("GameCenter.Reengagement.Achievement.First.Subtitle"), }; } switch (achievement.status.type) { case "locked": case "hidden": case "inprogress": return { title: objectGraph.loc.string("GameCenter.Reengagement.Achievement.KeepPlaying.Title"), subtitle: objectGraph.loc.string("GameCenter.Reengagement.Achievement.KeepPlaying.Subtitle"), }; case "completed": const achievementsTotalCount = objectGraph.loc.stringWithCount("GameCenter.AchievementSummary.TotalToCompleteCount", achievementCounts.total); const achievementsCompletedCount = objectGraph.loc.stringWithCount("GameCenter.AchievementSummary.NumberCompletedCount", achievementCounts.completed); const subtitle = objectGraph.loc .string("GameCenter.AchievementSummary.CompletedCount.Subtitle") .replace("@@completedCount@@", achievementsCompletedCount) .replace("@@totalCount@@", achievementsTotalCount); return { title: objectGraph.loc.string("GameCenter.Reengagement.Achievement.CompletedCount.Title"), subtitle: subtitle, }; default: return { title: "", subtitle: null }; } } } //# sourceMappingURL=grouping-game-center-reengagement-shelf-controller.js.map