summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers')
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/arcade-download-pack-shelf-controller.js295
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-app-event-shelf-controller.js260
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-arcade-footer-shelf-controller.js160
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-brick-shelf-controller.js313
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-category-shelf-controller.js207
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-editorial-card-shelf-controller.js229
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-editorial-story-card-shelf-controller.js143
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-activity-feed-shelf-controller.js223
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-continue-playing-shelf-controller.js332
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-popular-with-your-friends-shelf-controller.js314
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-reengagement-shelf-controller.js254
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-suggested-friends-shelf-controller.js279
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-hero-carousel-shelf-controller.js182
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-horizontal-card-shelf-controller.js122
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-large-breakout-shelf-controller.js193
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-link-shelf-controller.js169
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-lockup-shelf-controller-common.js608
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-lockup-shelf-controller.js104
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-personalized-lockup-shelf-controller.js368
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-ribbon-bar-shelf-controller.js269
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-shelf-controller-common.js992
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-shelf-controller.js395
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-small-breakout-shelf-controller.js168
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-tag-brick-shelf-controller.js246
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-tags-header-shelf-controller.js92
25 files changed, 6917 insertions, 0 deletions
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/arcade-download-pack-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/arcade-download-pack-shelf-controller.js
new file mode 100644
index 0000000..0e89233
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/arcade-download-pack-shelf-controller.js
@@ -0,0 +1,295 @@
+import * as models from "../../../api/models";
+import * as mediaDataFetching from "../../../foundation/media/data-fetching";
+import * as mediaNetwork from "../../../foundation/media/network";
+import * as urls from "../../../foundation/network/urls";
+import * as lockups from "../../lockups/lockups";
+import * as metricsHelpersLocation from "../../metrics/helpers/location";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+import * as validation from "@jet/environment/json/validation";
+import { ResponseMetadata } from "../../../foundation/network/network";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+import { Parameters, Path, Protocol } from "../../../foundation/network/url-constants";
+import * as content from "../../content/content";
+import { developerAttributes } from "../../../common/developer/developer-request";
+import { lockupShelfTokenFromBaseTokenAndMediaApiData } from "./grouping-lockup-shelf-controller-common";
+import { isNothing, isSome } from "@jet/environment/types/optional";
+import { arcadeOnboardingSubscriptionStatusFromString } from "../../../api/models";
+import * as metricsHelpersImpressions from "../../metrics/helpers/impressions";
+export class ArcadeDownloadPackShelfController extends GroupingShelfController {
+ constructor() {
+ super("ArcadeDownloadPackShelfController");
+ // Stable identifier of the shelf to match in native.
+ // Used to make a shelf refresh request after user finishes onboarding.
+ this.shelfId = "arcadeDownloadPackShelf";
+ this.supportedFeaturedContentIds = new Set([566 /* groupingTypes.FeaturedContentID.AppStore_ArcadeDownloadPackMarker */]);
+ }
+ shelfRoute(objectGraph) {
+ return [
+ ...super.shelfRoute(objectGraph),
+ {
+ protocol: Protocol.internal,
+ path: `/${Path.grouping}/${Path.shelf}/{token}`,
+ query: [Parameters.isArcadeDownloadPackShelfPlaceholder],
+ },
+ ];
+ }
+ shelfTokenFromBaseTokenAndMediaApiData(objectGraph, mediaApiData, baseShelfToken, groupingParseContext) {
+ return lockupShelfTokenFromBaseTokenAndMediaApiData(objectGraph, mediaApiData, baseShelfToken, groupingParseContext);
+ }
+ initialShelfDataFromGroupingMediaApiData(objectGraph, mediaApiData) {
+ return {
+ shelfContents: [],
+ categoriesContents: [],
+ apps: [],
+ title: "",
+ };
+ }
+ async secondaryShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters) {
+ const downloadPackData = objectGraph.arcade.getDownloadPackGames(objectGraph.bag.arcadeDownloadPackShelfTTLInSeconds);
+ if (isNothing(downloadPackData) || downloadPackData.apps.length === 0) {
+ return {
+ shelfContents: [],
+ categoriesContents: [],
+ apps: [],
+ title: "",
+ };
+ }
+ const downloadPackApps = downloadPackData.apps;
+ const shelfTitle = this.shelfTitle(objectGraph, arcadeOnboardingSubscriptionStatusFromString(downloadPackData.subscriptionStatus));
+ // Check whether it was a call from native to render a placeholder.
+ if (isSome(shelfUrl.query[Parameters.isArcadeDownloadPackShelfPlaceholder])) {
+ return {
+ shelfContents: [],
+ categoriesContents: [],
+ apps: downloadPackApps,
+ title: shelfTitle,
+ };
+ }
+ // Do hydration call otherwise.
+ const adamIDs = downloadPackApps.map((app) => app.adamId);
+ const categoryIDs = downloadPackApps.map((app) => app.categoryId);
+ const appsType = "apps";
+ const categoriesType = "editorial-items";
+ const batchRequest = new mediaDataFetching.Request(objectGraph)
+ .addingQuery(`ids[${appsType}]`, Array.from(adamIDs).join(","))
+ .addingQuery(`ids[${categoriesType}]`, Array.from(categoryIDs).join(","))
+ .includingAdditionalPlatforms(mediaDataFetching.defaultAdditionalPlatformsForClient(objectGraph))
+ .includingAttributes(developerAttributes(objectGraph));
+ return await mediaNetwork
+ .fetchData(objectGraph, batchRequest)
+ .then(async (dataContainer) => {
+ const shelfData = {
+ shelfContents: dataContainer.data.filter((data) => {
+ return data.type === appsType;
+ }),
+ categoriesContents: dataContainer.data.filter((data) => {
+ return data.type === categoriesType;
+ }),
+ apps: downloadPackApps,
+ title: shelfTitle,
+ responseTimingValues: dataContainer[ResponseMetadata.timingValues],
+ };
+ return shelfData;
+ });
+ }
+ _createShelf(objectGraph, shelfToken, shelfData, groupingParseContext) {
+ if (shelfToken.isFirstRender) {
+ const downloadPackData = objectGraph.arcade.getDownloadPackGames(objectGraph.bag.arcadeDownloadPackShelfTTLInSeconds);
+ if (isNothing(downloadPackData) || downloadPackData.apps.length === 0) {
+ // No suggested games to display. Adding an empty hidden shelf with `refreshUrl` for future refresh from native when onboarding ends.
+ const shelf = this.emptyShelfWithRefreshUrl(objectGraph);
+ shelf.refreshUrl = this.shelfRefreshURL(shelfToken);
+ // Even though the shelf is hidden, need to increase position here, so when it becomes visible the index stays the same.
+ metricsHelpersLocation.nextPosition(groupingParseContext.metricsLocationTracker);
+ return shelf;
+ }
+ else {
+ const shelf = this.placeholderShelf(objectGraph, shelfToken, downloadPackData.apps, this.shelfTitle(objectGraph, arcadeOnboardingSubscriptionStatusFromString(downloadPackData.subscriptionStatus)));
+ metricsHelpersLocation.nextPosition(groupingParseContext.metricsLocationTracker);
+ return shelf;
+ }
+ }
+ else {
+ // If `shelfData` contains only `records` then it was a request for a placeholder shelf from native.
+ if (shelfData.apps.length > 0 &&
+ (isNothing(shelfData.shelfContents) || shelfData.shelfContents.length === 0)) {
+ return this.placeholderShelf(objectGraph, shelfToken, shelfData.apps, shelfData.title);
+ }
+ else {
+ const shelfMetricsOptions = this.shelfMetrics(shelfData.title, shelfToken);
+ metricsHelpersLocation.pushContentLocation(objectGraph, shelfMetricsOptions, shelfData.title);
+ const shelf = this.downloadPackShelf(objectGraph, shelfToken, shelfData);
+ // `refreshUrl` allows to update the shelf content if the onboarding experience was triggered again.
+ shelf.refreshUrl = this.shelfRefreshURL(shelfToken);
+ metricsHelpersLocation.popLocation(shelfToken.metricsLocationTracker);
+ metricsHelpersImpressions.addImpressionFields(objectGraph, shelf, shelfMetricsOptions);
+ return shelf;
+ }
+ }
+ }
+ shelfMetricsOptionsFromBaseMetricsOptions(objectGraph, shelfToken, baseMetricsOptions) {
+ // Otherwise `baseMetricsOptions` will be used, however, at this point shelf doesn't have a title.
+ return null;
+ }
+ emptyShelfWithRefreshUrl(objectGraph) {
+ const shelf = new models.Shelf(this.useCustomDownloadPackCardShelf(objectGraph) ? "arcadeDownloadPackCard" : "smallLockup");
+ shelf.id = this.shelfId;
+ shelf.isHidden = true;
+ return shelf;
+ }
+ placeholderShelf(objectGraph, shelfToken, downloadPackApps, title) {
+ const shelf = this.useCustomDownloadPackCardShelf(objectGraph)
+ ? this.downloadPackCardPlaceholderShelf(objectGraph, shelfToken, downloadPackApps.length)
+ : this.smallLockupsPlaceholderShelf(objectGraph, shelfToken, downloadPackApps);
+ shelf.url = urls.URL.from(groupingShelfControllerCommon.groupingShelfUrl(shelfToken)).build();
+ shelf.title = title;
+ return shelf;
+ }
+ downloadPackCardPlaceholderShelf(objectGraph, shelfToken, expectedItemCount) {
+ const shelf = new models.Shelf("arcadeDownloadPackCard");
+ shelf.id = this.shelfId;
+ // For `arcadeDownloadPackCard` the same `ArcadeDownloadPackCard` model is used to render lockup placeholders.
+ const card = new models.ArcadeDownloadPackCard();
+ card.numberOfPlaceholders = expectedItemCount;
+ shelf.items = [card];
+ // See `shelfFetchShouldMergeWhenFetched` where `shelfToken.shelfStyle` is used to override `shelf.mergeWhenFetched` flag.
+ shelfToken.shelfStyle = shelf.contentType;
+ return shelf;
+ }
+ smallLockupsPlaceholderShelf(objectGraph, shelfToken, downloadPackApps) {
+ const shelf = new models.Shelf("smallLockup");
+ shelf.id = this.shelfId;
+ shelf.items = [];
+ shelf.isHorizontal = true;
+ shelf.rowsPerColumn = 2;
+ // Use standard small lockup placeholders for iPad shelf.
+ shelf.items = Array(downloadPackApps.length).fill(new models.Placeholder());
+ shelf.placeholderContentType = shelf.contentType;
+ shelf.contentType = "placeholder";
+ shelfToken.showingPlaceholders = true;
+ shelfToken.remainingItems = downloadPackApps.map((value) => {
+ return {
+ id: value.adamId,
+ type: "apps",
+ };
+ });
+ return shelf;
+ }
+ downloadPackShelf(objectGraph, shelfToken, shelfData) {
+ const categories = this.categoriesMapFromResponse(objectGraph, shelfData.categoriesContents, shelfData.apps);
+ const useCustomShelf = this.useCustomDownloadPackCardShelf(objectGraph);
+ const contentType = useCustomShelf ? "arcadeDownloadPackCard" : "smallLockup";
+ const apps = this.lockupsFromResponse(objectGraph, shelfToken, categories, useCustomShelf, // In the custom `arcadeDownloadPack` shelf app lockups the game category goes into lockup header, otherwise, in a standard shelf, it goes into subtitle.
+ useCustomShelf, // In the custom `arcadeDownloadPack` shelf app lockups have dark environment.
+ content.artworkUseCaseFromShelfStyle(objectGraph, contentType), isSome(shelfData.shelfContents) ? shelfData.shelfContents : [], shelfData.apps);
+ const shelf = new models.Shelf(contentType);
+ shelf.id = this.shelfId;
+ shelf.title = shelfData.title;
+ if (useCustomShelf) {
+ const card = new models.ArcadeDownloadPackCard();
+ card.lockups = apps;
+ shelf.items = [card];
+ }
+ else {
+ shelf.items = apps;
+ shelf.isHorizontal = true;
+ shelf.rowsPerColumn = 2;
+ }
+ shelf.isHidden = apps.length === 0;
+ return shelf;
+ }
+ // On iPhone there is a bespoke 'arcadeDownloadPackCard' games card shelf.
+ // On iPad, always use `smallLockup` instead of `shelfToken.shelfStyle` from server
+ // to make editorial configuration work easier.
+ useCustomDownloadPackCardShelf(objectGraph) {
+ return objectGraph.client.isPhone;
+ }
+ shelfRefreshURL(shelfToken) {
+ return urls.URL.from(groupingShelfControllerCommon.groupingShelfUrl(shelfToken))
+ .param(Parameters.isArcadeDownloadPackShelfPlaceholder, "true")
+ .build();
+ }
+ lockupsFromResponse(objectGraph, shelfToken, categoryTitlesForAdamIDs, showCategoryHeader, darkEnvironment, artworkUseCase, responseData, records) {
+ return validation.context("lockupsFromResponse", () => {
+ const lockupDataMap = new Map();
+ for (const lockupData of responseData) {
+ lockupDataMap.set(lockupData.id, lockupData);
+ }
+ const locationTracker = shelfToken.metricsLocationTracker;
+ const pageInformation = shelfToken.metricsPageInformation;
+ // Order of the apps from the local storage (records) must be preserved,
+ // so user always sees the same list of the apps like it was on the onboarding game suggestions screen.
+ const items = [];
+ for (const app of records) {
+ const lockupData = lockupDataMap.get(app.adamId);
+ if (isNothing(lockupData)) {
+ continue;
+ }
+ const lockup = lockups.lockupFromData(objectGraph, lockupData, {
+ offerStyle: darkEnvironment ? "transparent" : undefined,
+ offerEnvironment: darkEnvironment ? "dark" : undefined,
+ metricsOptions: {
+ pageInformation: pageInformation,
+ locationTracker: locationTracker,
+ badges: {
+ categoryId: app.categoryId,
+ },
+ },
+ metricsClickOptions: {
+ id: lockupData.id,
+ pageInformation: pageInformation,
+ locationTracker: locationTracker,
+ badges: {
+ categoryId: app.categoryId,
+ },
+ },
+ artworkUseCase: artworkUseCase,
+ shouldHideArcadeHeader: true,
+ isSubtitleHidden: showCategoryHeader,
+ });
+ if (isNothing(lockup)) {
+ continue;
+ }
+ lockups.cleanupArcadeDownloadPackLockupMetricsIfNeeded(lockup, objectGraph);
+ metricsHelpersLocation.nextPosition(locationTracker);
+ if (showCategoryHeader) {
+ lockup.heading = categoryTitlesForAdamIDs.get(lockup.adamId);
+ }
+ else {
+ lockup.subtitle = categoryTitlesForAdamIDs.get(lockup.adamId);
+ }
+ items.push(lockup);
+ }
+ return items;
+ });
+ }
+ categoriesMapFromResponse(objectGraph, responseData, records) {
+ const result = new Map();
+ for (const app of records) {
+ const category = responseData.find((data) => data.id === app.categoryId);
+ if (!category) {
+ continue;
+ }
+ const editorialNotesName = content.editorialNotesFromData(objectGraph, category, "name");
+ result.set(app.adamId, editorialNotesName);
+ }
+ return result;
+ }
+ shelfTitle(objectGraph, subscriptionStatus) {
+ return objectGraph.loc.string(subscriptionStatus === "new" ? "Arcade.DownloadPack.ShelfTitle.NewUser" : "Arcade.DownloadPack.ShelfTitle");
+ }
+ shelfMetrics(title, shelfToken) {
+ return {
+ id: shelfToken.id,
+ kind: null,
+ softwareType: "Arcade",
+ targetType: "swoosh",
+ title: title,
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ idType: "its_contentId",
+ fcKind: shelfToken.featuredContentId,
+ };
+ }
+}
+//# sourceMappingURL=arcade-download-pack-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-app-event-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-app-event-shelf-controller.js
new file mode 100644
index 0000000..6acbb2a
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-app-event-shelf-controller.js
@@ -0,0 +1,260 @@
+import { isSome } from "@jet/environment";
+import * as models from "../../../api/models";
+import * as serverData from "../../../foundation/json-parsing/server-data";
+import * as mediaDataFetching from "../../../foundation/media/data-fetching";
+import * as mediaDataStructure from "../../../foundation/media/data-structure";
+import { fetchData } from "../../../foundation/media/network";
+import * as mediaRelationship from "../../../foundation/media/relationships";
+import * as appPromotionCommon from "../../app-promotions/app-promotions-common";
+import * as metricsHelpersUtil from "../../metrics/helpers/util";
+import * as onDevicePersonalization from "../../personalization/on-device-personalization";
+import * as placeholders from "../../placeholders/placeholders";
+import { addVariantParametersToRequestForItems } from "../../product-page/product-page-variants";
+import * as refresh from "../../refresh/page-refresh-controller";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+import { attributeAsBooleanOrFalse } from "../../../foundation/media/attributes";
+import { ResponseMetadata } from "../../../foundation/network/network";
+import { shelfTitleAttributePathForFeaturedContentId } from "./grouping-shelf-controller-common";
+import * as mediaAttributes from "../../../foundation/media/attributes";
+export class GroupingAppEventShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingAppEventShelfController");
+ this.supportedFeaturedContentIds = new Set([
+ 519 /* groupingTypes.FeaturedContentID.AppStore_AppEventsMarker */,
+ 518 /* groupingTypes.FeaturedContentID.AppStore_PersonalizedAppEventsMarker */,
+ ]);
+ }
+ // 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: mediaRelationship.relationshipCollection(mediaApiData, "contents") };
+ }
+ /**
+ * 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) {
+ var _a;
+ if (((_a = shelfToken.recommendationsHref) === null || _a === void 0 ? void 0 : _a.length) > 0) {
+ try {
+ const mediaApiData = await GroupingShelfController.secondaryGroupingShelfMediaApiData(objectGraph, shelfUrl, shelfToken, parameters);
+ const shelfResponseData = mediaDataStructure.dataFromDataContainer(objectGraph, mediaApiData);
+ const shelfContents = this.initialShelfDataFromGroupingMediaApiData(objectGraph, shelfResponseData);
+ shelfContents.responseTimingValues = mediaApiData[ResponseMetadata.timingValues];
+ const title = mediaAttributes.attributeAsString(shelfResponseData, shelfTitleAttributePathForFeaturedContentId(objectGraph, shelfToken.featuredContentId));
+ if (serverData.isDefinedNonNull(title) && (title === null || title === void 0 ? void 0 : title.length) > 0) {
+ shelfContents.shelfTitle = title;
+ }
+ return shelfContents;
+ }
+ catch {
+ return { shelfContents: [] };
+ }
+ }
+ else {
+ const appEvents = [];
+ const contingentItems = [];
+ const offerItems = [];
+ for (const remainingItem of shelfToken.remainingItems) {
+ switch (remainingItem.type) {
+ case "contingent-items":
+ contingentItems.push(remainingItem);
+ break;
+ case "offer-items":
+ offerItems.push(remainingItem);
+ break;
+ case "app-events":
+ appEvents.push(remainingItem);
+ break;
+ default:
+ break;
+ }
+ }
+ const appEventsRequest = new mediaDataFetching.Request(objectGraph, appEvents);
+ addVariantParametersToRequestForItems(objectGraph, appEventsRequest, appEvents);
+ const contingentItemsRequest = new mediaDataFetching.Request(objectGraph, contingentItems);
+ addVariantParametersToRequestForItems(objectGraph, contingentItemsRequest, contingentItems);
+ const offerItemsRequest = new mediaDataFetching.Request(objectGraph, offerItems);
+ addVariantParametersToRequestForItems(objectGraph, offerItemsRequest, offerItems);
+ const hydrationResults = await Promise.all([
+ this.fetchRemainingItems(objectGraph, appEventsRequest),
+ this.fetchRemainingItems(objectGraph, contingentItemsRequest),
+ this.fetchRemainingItems(objectGraph, offerItemsRequest),
+ ]);
+ const hydratedDataMap = { ...hydrationResults[0], ...hydrationResults[1], ...hydrationResults[2] };
+ const shelfContents = [];
+ for (const remainingItem of shelfToken.remainingItems) {
+ const data = hydratedDataMap[remainingItem.id];
+ if (isSome(data)) {
+ shelfContents.push(data);
+ }
+ }
+ groupingShelfControllerCommon.flushRequestedItemsFromShelfToken(shelfToken, new Set([...offerItemsRequest.ids, ...contingentItemsRequest.ids, ...appEventsRequest.ids]));
+ return { shelfContents: shelfContents };
+ }
+ }
+ async fetchRemainingItems(objectGraph, remainingItemsRequest) {
+ const apiDataMap = {};
+ const addDataToMap = (data) => {
+ for (const item of data.data) {
+ apiDataMap[item.id] = item;
+ }
+ };
+ if (remainingItemsRequest.ids.size > 0) {
+ groupingShelfControllerCommon.prepareGroupingShelfRequest(objectGraph, remainingItemsRequest);
+ try {
+ await fetchData(objectGraph, remainingItemsRequest).then((mediaApiData) => {
+ addDataToMap(mediaApiData);
+ });
+ }
+ catch (fetchError) {
+ objectGraph.console.error("Error fetching remaining items", remainingItemsRequest.ids);
+ }
+ }
+ return apiDataMap;
+ }
+ /**
+ * 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) {
+ const shouldPersonalizeData = baseShelfToken.featuredContentId === 518 /* groupingTypes.FeaturedContentID.AppStore_PersonalizedAppEventsMarker */;
+ let personalizedDataResult = null;
+ const shelfData = this.initialShelfDataFromGroupingMediaApiData(objectGraph, mediaApiData);
+ if (shouldPersonalizeData && serverData.isDefinedNonNullNonEmpty(shelfData.shelfContents)) {
+ personalizedDataResult = this.personalizedDataResultFromDataItems(objectGraph, shelfData.shelfContents);
+ }
+ const appEventShelfToken = {
+ ...baseShelfToken,
+ shouldPersonalizeData: shouldPersonalizeData,
+ personalizedDataResult: personalizedDataResult,
+ };
+ const hasContents = serverData.isDefinedNonNullNonEmpty(shelfData.shelfContents);
+ const shelfPersonalizationAvailable = !attributeAsBooleanOrFalse(mediaApiData, "noPersonalizationAvailable");
+ if (!hasContents && shelfPersonalizationAvailable) {
+ appEventShelfToken.recommendationsHref = mediaApiData.href;
+ appEventShelfToken.isValidRecommendationsShelf = true;
+ }
+ else {
+ appEventShelfToken.isValidRecommendationsShelf = hasContents;
+ }
+ return appEventShelfToken;
+ }
+ // endregion
+ // region Metrics
+ /**
+ * Return the shelf metrics options to use for this specific shelf. Using the base options from the grouping
+ * page controller
+ *
+ * @param objectGraph The App Store dependency graph
+ * @param shelfToken The shelf shelfToken for this current shelf creation request
+ * @param baseMetricsOptions The minimum set of metrics options for this shelf, created by the
+ * grouping page controller
+ */
+ shelfMetricsOptionsFromBaseMetricsOptions(objectGraph, shelfToken, baseMetricsOptions) {
+ const shelfMetricsOptions = { ...baseMetricsOptions };
+ if (serverData.isDefinedNonNullNonEmpty(shelfToken.personalizedDataResult)) {
+ const recoMetricsData = metricsHelpersUtil.combinedRecoMetricsDataFromMetricsData(baseMetricsOptions.recoMetricsData, shelfToken.personalizedDataResult.processingType, null);
+ shelfMetricsOptions.recoMetricsData = recoMetricsData;
+ }
+ return shelfMetricsOptions;
+ }
+ // 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) {
+ var _a;
+ if (!appPromotionCommon.appEventsAreEnabled(objectGraph)) {
+ return null;
+ }
+ if (!shelfToken.isValidRecommendationsShelf) {
+ return null;
+ }
+ const metricsOptions = {
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ recoMetricsData: shelfToken.metricsPageInformation.recoMetricsData,
+ };
+ // First personalize the data, as this may cause things to be re-ordered
+ let sortedShelfContents = shelfData.shelfContents;
+ // Only try to use the personalized data the first render, second renders the shelfContents has already been personalized
+ if (serverData.isDefinedNonNullNonEmpty(shelfToken.personalizedDataResult) && shelfToken.isFirstRender) {
+ sortedShelfContents = shelfToken.personalizedDataResult.personalizedData;
+ }
+ // Now find which items are hydrated, and add the rest to the token
+ const hydratedShelfContents = [];
+ for (const data of sortedShelfContents) {
+ if (serverData.isNull(data.attributes) || groupingShelfControllerCommon.shouldDefer(shelfToken)) {
+ shelfToken.isDeferring = true;
+ shelfToken.remainingItems.push(data);
+ continue;
+ }
+ hydratedShelfContents.push(data);
+ }
+ // Now create our shelf from the hydrated items
+ const displayableAppEvents = appPromotionCommon.appPromotionsFromData(objectGraph, hydratedShelfContents, null, false, false, metricsOptions, false, true, shelfToken.isArcadePage, false);
+ refresh.addNextPreferredContentRefreshDate(displayableAppEvents.nextAppEventPromotionStartDate, groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.refreshController);
+ const appPromotions = displayableAppEvents.appPromotions;
+ const shelfType = "appPromotion";
+ const shelf = new models.Shelf(shelfType);
+ shelf.isHorizontal = true;
+ shelf.title = (_a = shelfData.shelfTitle) !== null && _a !== void 0 ? _a : shelfToken.title;
+ shelf.items = appPromotions;
+ const willHydrateShelfLater = serverData.isNullOrEmpty(shelf.items) && shelfToken.isFirstRender;
+ if (willHydrateShelfLater && placeholders.placeholdersEnabled(objectGraph)) {
+ placeholders.insertPlaceholdersIntoGenericPageShelf(objectGraph, shelf, shelfToken, shelfToken.featuredContentId);
+ }
+ shelf.url = groupingShelfControllerCommon.createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, shelfToken);
+ if (serverData.isNullOrEmpty(shelf.items) && serverData.isNullOrEmpty(shelf.url)) {
+ return shelfToken.isFirstRender ? null : GroupingAppEventShelfController.makeHiddenShelf(shelfToken);
+ }
+ return shelf;
+ }
+ // region Helpers
+ /**
+ * Personalizes the input list of app event data items.
+ * @param objectGraph
+ * @param dataItems The array of app event data items
+ */
+ personalizedDataResultFromDataItems(objectGraph, dataItems) {
+ // Collect the app IDs we are interested in
+ const appIds = new Set();
+ for (const data of dataItems) {
+ const appId = serverData.asString(data, "meta.personalizationData.appId");
+ if ((appId === null || appId === void 0 ? void 0 : appId.length) > 0) {
+ appIds.add(appId);
+ }
+ }
+ // Now personalize the data
+ const personalizationDataContainer = onDevicePersonalization.personalizationDataContainerForAppIds(objectGraph, appIds);
+ return onDevicePersonalization.personalizeDataItems(objectGraph, "groupingAppEvent", dataItems, false, personalizationDataContainer, null, null, null, true);
+ }
+ static makeHiddenShelf(shelfToken) {
+ const hiddenShelf = new models.Shelf(shelfToken.shelfStyle);
+ hiddenShelf.isHidden = true;
+ return hiddenShelf;
+ }
+}
+//# sourceMappingURL=grouping-app-event-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-arcade-footer-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-arcade-footer-shelf-controller.js
new file mode 100644
index 0000000..d9f324d
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-arcade-footer-shelf-controller.js
@@ -0,0 +1,160 @@
+import * as models from "../../../api/models";
+import * as serverData from "../../../foundation/json-parsing/server-data";
+import * as mediaDataStructure from "../../../foundation/media/data-structure";
+import * as mediaNetwork from "../../../foundation/media/network";
+import { ResponseMetadata } from "../../../foundation/network/network";
+import * as color from "../../../foundation/util/color-util";
+import * as arcadeCommon from "../../arcade/arcade-common";
+import * as content from "../../content/content";
+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";
+export class GroupingArcadeFooterShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingArcadeFooterShelfController");
+ this.supportedFeaturedContentIds = new Set([-1 /* groupingTypes.FeaturedContentID.Native_GroupingShelf */]);
+ this.supportedNativeGroupingShelfIds = new Set([1 /* groupingTypes.NativeGroupingShelfID.Arcade_SeeAllGamesFooter */]);
+ }
+ // endregion
+ // region Metrics
+ shouldImpressShelf() {
+ return false;
+ }
+ // 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: mediaDataStructure.dataCollectionFromResultsListContainer(mediaApiData),
+ responseTimingValues: 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) {
+ const footerRequest = arcadeCommon.arcadeAppsRequestForIcons(objectGraph, this.numberOfIconsForArcadeAppGrid(objectGraph.client.deviceType));
+ return await mediaNetwork
+ .fetchData(objectGraph, footerRequest)
+ .then((mediaApiData) => {
+ const shelfData = this.initialShelfDataFromGroupingMediaApiData(objectGraph, mediaApiData);
+ shelfData.responseTimingValues = mediaApiData[ResponseMetadata.timingValues];
+ return shelfData;
+ });
+ }
+ /**
+ * 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) {
+ const footerShelfToken = {
+ ...baseShelfToken,
+ shouldIncludeShelfUrl: baseShelfToken.isFirstRender,
+ };
+ footerShelfToken.showingPlaceholders = baseShelfToken.isFirstRender;
+ return footerShelfToken;
+ }
+ // 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) {
+ const footer = new models.ArcadeFooter();
+ const shelf = new models.Shelf("arcadeFooter");
+ shelf.items = [footer];
+ const impressionOptions = {
+ targetType: "arcadeSeeAllGamesFooter",
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ title: objectGraph.loc.string("Arcade.SeeAllGames.Button.Title"),
+ id: shelfToken.id,
+ kind: "footer",
+ softwareType: "Arcade",
+ };
+ metricsHelpersImpressions.addImpressionFields(objectGraph, footer, impressionOptions);
+ metricsHelpersLocation.pushContentLocation(objectGraph, impressionOptions, impressionOptions.title);
+ footer.buttonAction = arcadeCommon.seeAllArcadeGamesPageFlowAction(objectGraph, "releaseDate", shelfToken.metricsPageInformation, shelfToken.metricsLocationTracker);
+ const buttonImpressionOptions = {
+ targetType: "button",
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ title: footer.buttonAction.title,
+ id: "arcade-see-all-games-button",
+ kind: "button",
+ softwareType: "Arcade",
+ };
+ metricsHelpersImpressions.addImpressionFields(objectGraph, footer.buttonAction, buttonImpressionOptions);
+ metricsHelpersLocation.popLocation(impressionOptions.locationTracker);
+ const termsAndConditionsUrl = objectGraph.bag.termsAndConditionsURL;
+ const shouldAddTermsAndConditions = !serverData.isNull(termsAndConditionsUrl) && objectGraph.client.deviceType !== "tv";
+ if (objectGraph.client.isiOS) {
+ if (shouldAddTermsAndConditions) {
+ const termsAndConditionTitle = objectGraph.loc.string("TermsAndConditions.Title");
+ const urlAction = new models.ExternalUrlAction(termsAndConditionsUrl);
+ const footnote = new models.Footnote(termsAndConditionTitle);
+ footnote.clickAction = urlAction;
+ footnote.presentationStyle = ["hasChevron", "textLightensOnHighlight", "hasSeparator"];
+ footer.footnote = footnote;
+ }
+ shelf.background = {
+ type: "color",
+ color: color.named("placeholderBackground"),
+ };
+ }
+ if (serverData.isDefinedNonNullNonEmpty(shelfData.shelfContents)) {
+ const metricsOptions = {
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ };
+ footer.icons = content.impressionableAppIconsFromDataCollection(objectGraph, shelfData.shelfContents, metricsOptions, {
+ useCase: 2 /* content.ArtworkUseCase.LockupIconMedium */,
+ });
+ }
+ else {
+ footer.icons = [];
+ }
+ if (shelfToken.shouldIncludeShelfUrl) {
+ shelf.url = groupingShelfControllerCommon.groupingShelfUrl(shelfToken);
+ }
+ return shelf;
+ }
+ // region Helpers
+ /**
+ * The maximum number of icons that can be displayed in a device's icon grid, i.e. Arcade Grid Footer.
+ * This is used to limit the number of icon data to fetch to what we'll actually use.
+ * Note that views are expected to handle situations where we have less than the max number of icons.
+ *
+ * @param deviceType The device type that arcade icon grid is being displayed in.
+ * @returns The maximum number of icons to fetch.
+ */
+ numberOfIconsForArcadeAppGrid(deviceType) {
+ switch (deviceType) {
+ case "phone":
+ return 9;
+ default: // iPad, provided `footerShowsIconsForPlatform == true`.
+ return 20;
+ }
+ }
+}
+//# sourceMappingURL=grouping-arcade-footer-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-brick-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-brick-shelf-controller.js
new file mode 100644
index 0000000..671aac3
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-brick-shelf-controller.js
@@ -0,0 +1,313 @@
+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 content from "../../content/content";
+import * as flowPreview from "../../content/flow-preview";
+import * as lockupsEditorialContext from "../../lockups/editorial-context";
+import * as metricsHelpersClicks from "../../metrics/helpers/clicks";
+import * as metricsHelpersImpressions from "../../metrics/helpers/impressions";
+import * as metricsHelpersLocation from "../../metrics/helpers/location";
+import * as placeholders from "../../placeholders/placeholders";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+import * as metricsLocation from "../../../common/metrics/helpers/location";
+import { defaultLayoutSize } from "../../../foundation/media/data-fetching";
+import * as color from "../../../foundation/util/color-util";
+import { isSome } from "@jet/environment";
+export class GroupingBrickShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingBrickShelfController");
+ this.supportedFeaturedContentIds = new Set([
+ 421 /* groupingTypes.FeaturedContentID.AppStore_BrickRow */,
+ 422 /* groupingTypes.FeaturedContentID.AppStore_Brick */,
+ 423 /* groupingTypes.FeaturedContentID.AppStore_CustomBrick */,
+ 261 /* groupingTypes.FeaturedContentID.Sundance_BrickRow */,
+ ]);
+ }
+ // 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: mediaRelationship.relationshipCollection(mediaApiData, "children") };
+ }
+ /**
+ * 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 GroupingShelfController.secondaryGroupingShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters).then((shelfData) => {
+ return {
+ shelfContents: groupingShelfControllerCommon.mergeContentDataIntoEditorialData(shelfData.shelfContents, mediaRelationship.relationshipCollection(shelfToken.featuredContentData, "children")),
+ };
+ });
+ }
+ /**
+ * 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) {
+ // If suppress text is not provided, default to hiding.
+ let suppressText = mediaAttributes.attributeAsBoolean(mediaApiData, "suppressText");
+ if (serverData.isNull(suppressText)) {
+ suppressText = true;
+ }
+ const brickShelfToken = {
+ ...baseShelfToken,
+ showSupplementaryText: !suppressText,
+ };
+ brickShelfToken.clientIdentifierOverride = lockupsEditorialContext.clientIdentifierForEditorialContextInData(objectGraph, mediaApiData);
+ return brickShelfToken;
+ }
+ // 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) {
+ const items = [];
+ const remainingBricks = []; // Data where metadata was missing.
+ const displayStyle = serverData.asString(shelfToken.featuredContentData.attributes, "displayStyle");
+ const isCategoryBrick = displayStyle === "small";
+ let shelf;
+ if (isCategoryBrick) {
+ // i.e. category bricks shelf
+ shelf = new models.Shelf("categoryBrick");
+ const layoutSize = serverData.asNumber(shelfToken.featuredContentData.attributes, "layoutStyle.layoutSize");
+ shelf.rowsPerColumn = layoutSize !== null && layoutSize !== void 0 ? layoutSize : defaultLayoutSize(objectGraph);
+ metricsLocation.currentLocation(shelfToken.metricsLocationTracker).name = "Browse Categories";
+ }
+ else {
+ // i.e. "medium", also the default bricks shelf
+ shelf = new models.Shelf("brick");
+ }
+ shelf.isHorizontal = true;
+ for (const brickData of shelfData.shelfContents) {
+ const brickModel = GroupingBrickShelfController.createBrick(objectGraph, brickData, isCategoryBrick, shelfToken.metricsPageInformation, shelfToken.metricsLocationTracker, shelfToken, groupingParseContext);
+ if (!brickModel) {
+ remainingBricks.push(brickData);
+ continue;
+ }
+ items.push(brickModel);
+ metricsHelpersLocation.nextPosition(shelfToken.metricsLocationTracker);
+ }
+ if (serverData.isDefinedNonNull(shelfToken.presentationHints)) {
+ shelf.presentationHints = shelfToken.presentationHints;
+ }
+ if (serverData.isDefinedNonNull(shelfToken.showSupplementaryText)) {
+ shelf.presentationHints = {
+ ...shelf.presentationHints,
+ showSupplementaryText: shelfToken.showSupplementaryText,
+ };
+ }
+ // We don't need this in our incomplete shelf URL, so we'll preemptively remove it.
+ delete shelfToken.maxItemCount;
+ // Override `featuredContentData` to only have remaining bricks that must be fetched.
+ if (serverData.isDefinedNonNull(serverData.traverse(shelfToken.featuredContentData, "relationships.children.data"))) {
+ shelfToken.featuredContentData.relationships["children"].data = remainingBricks;
+ }
+ // Set metadata
+ shelf.title = shelfToken.title;
+ shelf.subtitle = shelfToken.subtitle;
+ if (isCategoryBrick) {
+ const displayCount = serverData.asNumber(shelfToken.featuredContentData.attributes, "displayCount");
+ shelf.items = items.slice(0, displayCount !== null && displayCount !== void 0 ? displayCount : items.length);
+ }
+ else {
+ shelf.items = items;
+ }
+ // See all
+ const hasSeeAll = serverData.asBooleanOrFalse(shelfToken.featuredContentData.attributes, "hasSeeAll");
+ if (isCategoryBrick && hasSeeAll && !objectGraph.client.isWeb) {
+ // Setup shelf
+ const seeAllShelf = new models.Shelf("categoryBrick");
+ seeAllShelf.items = this.sortCategories(objectGraph, items);
+ seeAllShelf.presentationHints = { isSeeAllContext: true };
+ // Setup Page
+ const seeAllPage = new models.GenericPage([seeAllShelf]);
+ seeAllPage.title = shelfToken.title;
+ // Setup action
+ const seeAllAction = new models.FlowAction("page");
+ seeAllAction.title = objectGraph.loc.string("ACTION_SEE_ALL");
+ seeAllAction.pageData = seeAllPage;
+ // Connect action
+ shelf.seeAllAction = seeAllAction;
+ // Metrics
+ metricsHelpersClicks.addClickEventToSeeAllAction(objectGraph, seeAllAction, null, {
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ });
+ }
+ // 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);
+ }
+ if (!willHydrateShelfLater &&
+ GroupingBrickShelfController.shouldDisplayChooseYourFavoritesBrick(objectGraph, shelfToken, isCategoryBrick, groupingParseContext)) {
+ const brickModel = GroupingBrickShelfController.createChooseYourFavoritesBrick(objectGraph, shelfToken.metricsPageInformation, shelfToken.metricsLocationTracker);
+ shelf.items.splice(0, 0, brickModel);
+ }
+ shelfToken.presentationHints = shelf.presentationHints;
+ shelf.url = groupingShelfControllerCommon.createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, shelfToken);
+ return shelf;
+ }
+ // region Static Helpers
+ // Whether to display Choose Your Favorites brick.
+ // It will return TRUE when:
+ // - This is an Arcade page
+ // - This is a Category brick
+ // - Feature flag is enabled
+ // - The user is subscribed or trial enrolled
+ static shouldDisplayChooseYourFavoritesBrick(objectGraph, shelfToken, isCategoryBrick, groupingParseContext) {
+ return (shelfToken.isArcadePage &&
+ isCategoryBrick &&
+ objectGraph.featureFlags.isEnabled("arcade_choose_your_favorites_brick_Future") &&
+ isSome(groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.additionalShelfParameters) &&
+ ((groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.additionalShelfParameters.isSubscribed) === "true" ||
+ (groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.additionalShelfParameters.isTrialEnrolled) === "true"));
+ }
+ static createBrick(objectGraph, brickData, searchCategoryBricks, metricsPageInformation, metricsLocationTracker, shelfToken, groupingParseContext) {
+ const metricsOptions = {
+ targetType: searchCategoryBricks ? "tile" : "brick",
+ pageInformation: metricsPageInformation,
+ locationTracker: metricsLocationTracker,
+ recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(brickData),
+ };
+ const metadata = groupingShelfControllerCommon.metadataForFCData(objectGraph, brickData, shelfToken, false, null, metricsOptions, groupingParseContext);
+ if (!metadata) {
+ return null;
+ }
+ const brickModel = new models.Brick();
+ // Setup Artwork
+ const artworkOptions = {
+ useCase: 18 /* content.ArtworkUseCase.GroupingBrick */,
+ };
+ if (searchCategoryBricks) {
+ const artworks = content.searchChartOrCategoryArtworkFromData(objectGraph, metadata.content, content.SearchChartOrCategoryBrickUseCase.categoryBreakout, models.GenericSearchPageShelfDisplayStyleDensity.Density1);
+ brickModel.artworks = artworks;
+ }
+ else if (metadata.artwork &&
+ (shelfToken === null || shelfToken === void 0 ? void 0 : shelfToken.featuredContentId) !== 261 /* groupingTypes.FeaturedContentID.Sundance_BrickRow */) {
+ let artworkDict = serverData.asDictionary(metadata.artwork, "subscriptionHero");
+ if (!artworkDict) {
+ artworkDict = serverData.asDictionary(metadata.artwork, "originalFlowcaseBrick");
+ }
+ const artwork = groupingShelfControllerCommon.groupingArtworkFromApiArtwork(objectGraph, artworkDict, artworkOptions);
+ brickModel.artworks = [artwork];
+ }
+ else {
+ const artwork = groupingShelfControllerCommon.artworkFromFC(objectGraph, brickData, 1060, 520, artworkOptions);
+ brickModel.artworks = [artwork];
+ }
+ brickModel.accessibilityLabel = metadata.title;
+ // Set supplementary text.
+ brickModel.shortEditorialDescription = metadata.title;
+ // Set action
+ brickModel.clickAction = metadata.action;
+ // Set personalization
+ const brickFeaturedContentId = mediaAttributes.attributeAsNumber(brickData, "editorialElementKind");
+ if (brickFeaturedContentId === 435 /* groupingTypes.FeaturedContentID.AppStore_MSOBrickMarker */) {
+ brickModel.personalizationStyle = "mso";
+ }
+ // Set flow preview actions
+ const contentData = mediaRelationship.relationshipData(objectGraph, brickData, "contents");
+ if (serverData.isDefinedNonNull(contentData)) {
+ const metricsClickOptions = metricsHelpersClicks.clickOptionsForLockup(objectGraph, contentData, metricsOptions);
+ brickModel.flowPreviewActionsConfiguration = flowPreview.flowPreviewActionsConfigurationForProductFromData(objectGraph, brickData, true, shelfToken === null || shelfToken === void 0 ? void 0 : shelfToken.clientIdentifierOverride, brickModel.clickAction, metricsOptions, metricsClickOptions);
+ }
+ // Configure impressions
+ const impressionOptions = metricsHelpersImpressions.impressionOptions(objectGraph, brickData, metadata.title, metricsOptions);
+ metricsHelpersImpressions.addImpressionFields(objectGraph, brickModel, impressionOptions);
+ // Safe area
+ brickModel.artworkSafeArea = models.ChartOrCategorySafeArea.defaultTileArtworkSafeArea;
+ brickModel.textSafeArea = models.ChartOrCategorySafeArea.defaultTileTextSafeArea;
+ if (!brickModel.isValid()) {
+ return null;
+ }
+ return brickModel;
+ }
+ /// Creates and returns Choose Your Favorites brick.
+ static createChooseYourFavoritesBrick(objectGraph, metricsPageInformation, metricsLocationTracker) {
+ const brickModel = new models.Brick();
+ // Artwork
+ const artwork = new models.Artwork("", 1060, 520, [], color.fromHex("efac78"));
+ brickModel.artworks = [artwork];
+ // Labels
+ const title = objectGraph.loc.string("ARCADE_CHOOSE_YOUR_FAVORITES_BRICK_TITLE");
+ brickModel.accessibilityLabel = title;
+ brickModel.shortEditorialDescription = title;
+ // Action
+ const flowAction = new models.FlowAction("arcadeDownloadPackCategories");
+ flowAction.presentationContext = "presentModalFormSheet";
+ flowAction.pageData = "unknown";
+ brickModel.clickAction = flowAction;
+ // Impressions
+ const metricsOptions = {
+ targetType: "brick",
+ pageInformation: metricsPageInformation,
+ locationTracker: metricsLocationTracker,
+ recoMetricsData: null,
+ };
+ const impressionOptions = metricsHelpersImpressions.impressionOptionsForArcadeChooseYourFavoritesBrick(metricsOptions);
+ metricsHelpersImpressions.addImpressionFields(objectGraph, brickModel, impressionOptions);
+ // Safe area
+ brickModel.artworkSafeArea = models.ChartOrCategorySafeArea.defaultTileArtworkSafeArea;
+ brickModel.textSafeArea = models.ChartOrCategorySafeArea.defaultTileTextSafeArea;
+ return brickModel;
+ }
+ // endregion
+ // region Metrics
+ /**
+ * Return the shelf metrics options to use for this specific shelf. Using the base options from the grouping
+ * page controller
+ *
+ * @param objectGraph The App Store dependency graph
+ * @param shelfToken The shelf shelfToken for this current shelf creation request
+ * @param baseMetricsOptions The minimum set of metrics options for this shelf, created by the
+ * grouping page controller
+ */
+ shelfMetricsOptionsFromBaseMetricsOptions(objectGraph, shelfToken, baseMetricsOptions) {
+ const shelfMetricsOptions = { ...baseMetricsOptions };
+ const displayStyle = serverData.asString(shelfToken.featuredContentData.attributes, "displayStyle");
+ // If this is a Category Bricks shelf, configure metrics title accordingly.
+ if (displayStyle === "small") {
+ shelfMetricsOptions.title = "Browse Categories";
+ }
+ return shelfMetricsOptions;
+ }
+ // endregion
+ // region Sort
+ sortCategories(objectGraph, categories) {
+ return categories.sort((category1, category2) => {
+ try {
+ return category1.shortEditorialDescription.localeCompare(category2.shortEditorialDescription, objectGraph.loc.safeIdentifier, {
+ usage: "sort",
+ });
+ }
+ catch (e) {
+ return 0;
+ }
+ });
+ }
+}
+//# sourceMappingURL=grouping-brick-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-category-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-category-shelf-controller.js
new file mode 100644
index 0000000..38d8324
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-category-shelf-controller.js
@@ -0,0 +1,207 @@
+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 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";
+export class GroupingCategoryShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingCategoryShelfController");
+ this.supportedFeaturedContentIds = new Set([425 /* groupingTypes.FeaturedContentID.AppStore_GenreStack */]);
+ }
+ // 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) {
+ const childListBoxes = mediaRelationship.relationshipData(objectGraph, mediaApiData, "children");
+ if (childListBoxes) {
+ return { shelfContents: mediaRelationship.relationshipCollection(childListBoxes, "children") };
+ }
+ else {
+ return { shelfContents: [] };
+ }
+ }
+ /**
+ * 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 GroupingShelfController.secondaryGroupingShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters).then((shelfData) => {
+ const childListBoxes = mediaRelationship.relationshipData(objectGraph, shelfToken.featuredContentData, "children");
+ if (childListBoxes) {
+ const hydratedListBoxSections = groupingShelfControllerCommon.mergeContentDataIntoEditorialData(shelfData.shelfContents, mediaRelationship.relationshipCollection(childListBoxes, "children"));
+ return { shelfContents: hydratedListBoxSections };
+ }
+ else {
+ return { shelfContents: [] };
+ }
+ });
+ }
+ /**
+ * 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) {
+ var _a;
+ if (serverData.isNullOrEmpty(shelfData.shelfContents)) {
+ return null;
+ }
+ const items = [];
+ let itemsHaveRectangularArtwork = false;
+ for (const category of shelfData.shelfContents) {
+ const grouping = mediaRelationship.relationshipData(objectGraph, category, "grouping");
+ if (serverData.isNull(grouping)) {
+ continue;
+ }
+ const adamId = groupingShelfControllerCommon.contentIdFromContentItem(objectGraph, grouping);
+ if (serverData.isNull(category.attributes) ||
+ serverData.isNull(grouping.attributes) ||
+ groupingShelfControllerCommon.shouldDefer(shelfToken)) {
+ shelfToken.isDeferring = true;
+ shelfToken.remainingItems.push(grouping);
+ continue;
+ }
+ const metricsBase = {
+ targetType: "listItem",
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(category),
+ };
+ const action = lockups.actionFromData(objectGraph, grouping, { ...metricsBase, id: adamId }, shelfToken.clientIdentifierOverride);
+ metricsHelpersImpressions.addImpressionFields(objectGraph, action, {
+ ...metricsBase,
+ kind: "link",
+ softwareType: serverData.asBooleanOrFalse(groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.isArcadePage) ? "Arcade" : null,
+ title: action.title,
+ id: adamId,
+ });
+ const artwork = mediaAttributes.attributeAsDictionary(grouping, "artwork");
+ if (artwork.width > artwork.height) {
+ itemsHaveRectangularArtwork = true;
+ }
+ if (serverData.isDefinedNonNullNonEmpty(artwork)) {
+ action.artwork = content.artworkFromApiArtwork(objectGraph, artwork, {
+ allowingTransparency: true,
+ useCase: 20 /* content.ArtworkUseCase.CategoryIcon */,
+ });
+ }
+ items.push(action);
+ metricsHelpersLocation.nextPosition(shelfToken.metricsLocationTracker);
+ }
+ // The top level shelf on mac should always be sorted
+ // <rdar://problem/40954563> LOC: Global: MAS: Order of categories appears in English alphabetical order instead of in each language's alphabetical order.
+ if (objectGraph.client.isMac) {
+ this.sortCategories(objectGraph, items);
+ }
+ const shelf = this.shelfForCategoryActions(objectGraph, items, shelfToken);
+ shelf.title = shelfToken.title;
+ shelf.subtitle = shelfToken.subtitle;
+ shelf.url = groupingShelfControllerCommon.createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, shelfToken);
+ if (itemsHaveRectangularArtwork) {
+ const existingPresentationHints = (_a = shelf.presentationHints) !== null && _a !== void 0 ? _a : {};
+ shelf.presentationHints = {
+ ...existingPresentationHints,
+ itemsHaveRectangularArtwork: true,
+ };
+ }
+ return shelf;
+ }
+ shelfForCategoryActions(objectGraph, categories, shelfToken) {
+ const shelf = new models.Shelf("action");
+ // Limit for number of items (`null` when there's no limit).
+ let itemLimit;
+ // Configure shelf orientation and limit
+ switch (objectGraph.client.deviceType) {
+ case "tv":
+ shelf.isHorizontal = true;
+ itemLimit = 8;
+ break;
+ case "mac":
+ shelf.isHorizontal = false;
+ itemLimit = null;
+ break;
+ case "web":
+ shelf.isHorizontal = true;
+ shelf.rowsPerColumn = 3;
+ itemLimit = 24;
+ break;
+ default:
+ shelf.isHorizontal = false;
+ itemLimit = 6;
+ break;
+ }
+ // Apply limit (if any)
+ if (itemLimit !== null && categories.length > itemLimit) {
+ shelf.items = categories.slice(0, itemLimit);
+ const allCategoriesAction = new models.FlowAction("page");
+ allCategoriesAction.title = objectGraph.loc.string("ACTION_SEE_ALL");
+ metricsHelpersClicks.addClickEventToSeeAllAction(objectGraph, allCategoriesAction, null, {
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ });
+ shelf.seeAllAction = allCategoriesAction;
+ const allCategoriesShelf = new models.Shelf("action");
+ allCategoriesShelf.isHorizontal = false;
+ this.sortCategories(objectGraph, categories);
+ allCategoriesShelf.items = categories;
+ const allCategoriesPage = new models.GenericPage([allCategoriesShelf]);
+ allCategoriesPage.title = objectGraph.loc.string("PAGE_TITLE_CATEGORIES");
+ allCategoriesAction.pageData = allCategoriesPage;
+ }
+ else {
+ shelf.items = categories;
+ }
+ return shelf;
+ }
+ // region Helpers
+ /**
+ * Sort the category actions alphabetically
+ * @param objectGraph
+ * @param categories The cateogries we're going to be displaying
+ */
+ sortCategories(objectGraph, categories) {
+ categories.sort((category1, category2) => {
+ try {
+ return category1.title.localeCompare(category2.title, objectGraph.loc.safeIdentifier, {
+ usage: "sort",
+ });
+ }
+ catch (e) {
+ return 0;
+ }
+ });
+ }
+}
+//# sourceMappingURL=grouping-category-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-editorial-card-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-editorial-card-shelf-controller.js
new file mode 100644
index 0000000..12dc94f
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-editorial-card-shelf-controller.js
@@ -0,0 +1,229 @@
+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 color from "../../../foundation/util/color-util";
+import * as flowPreview from "../../content/flow-preview";
+import * as metricsHelpersClicks from "../../metrics/helpers/clicks";
+import * as metricsHelpersImpressions from "../../metrics/helpers/impressions";
+import * as metricsHelpersLocation from "../../metrics/helpers/location";
+import * as metricsHelpersUtil from "../../metrics/helpers/util";
+import * as onDevicePersonalization from "../../personalization/on-device-personalization";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+export class GroupingEditorialCardShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingEditorialCardShelfController");
+ this.supportedFeaturedContentIds = new Set([
+ 415 /* groupingTypes.FeaturedContentID.AppStore_HeroList */,
+ 416 /* groupingTypes.FeaturedContentID.AppStore_Hero */,
+ 501 /* groupingTypes.FeaturedContentID.AppStore_PersonalizedHeroMarker */,
+ 417 /* groupingTypes.FeaturedContentID.AppStore_CustomHero */,
+ 258 /* groupingTypes.FeaturedContentID.Sundance_Flowcase */,
+ ]);
+ }
+ // 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: mediaRelationship.relationshipCollection(mediaApiData, "children") };
+ }
+ /**
+ * 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 GroupingShelfController.secondaryGroupingShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters);
+ }
+ /**
+ * 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) {
+ const shelf = new models.Shelf("editorialCard");
+ shelf.isHorizontal = true;
+ const personalizationDataContainer = this.personalizationDataContainerForEditorialCardItemsDataArray(objectGraph, shelfData.shelfContents);
+ const items = [];
+ for (const data of shelfData.shelfContents) {
+ const card = GroupingEditorialCardShelfController.makeEditorialCard(objectGraph, data, personalizationDataContainer, groupingParseContext, shelfToken);
+ if (serverData.isNullOrEmpty(card) || !card.isValid()) {
+ continue;
+ }
+ items.push(card);
+ metricsHelpersLocation.nextPosition(shelfToken.metricsLocationTracker);
+ }
+ // We don't need this in our incomplete shelf URL, so we'll preemptively remove it.
+ delete shelfToken.maxItemCount;
+ if (objectGraph.client.isVision) {
+ shelf.presentationHints = { ...shelf.presentationHints, showSupplementaryText: true };
+ }
+ shelf.items = items;
+ shelf.url = groupingShelfControllerCommon.createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, shelfToken);
+ return shelf;
+ }
+ static makeEditorialCard(objectGraph, itemData, personalizationDataContainer, groupingParseContext, shelfToken) {
+ var _a, _b, _c;
+ const metricsOptions = {
+ targetType: "hero",
+ pageInformation: shelfToken === null || shelfToken === void 0 ? void 0 : shelfToken.metricsPageInformation,
+ locationTracker: shelfToken === null || shelfToken === void 0 ? void 0 : shelfToken.metricsLocationTracker,
+ recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(itemData),
+ id: itemData.id,
+ idType: "editorial_id",
+ };
+ const featuredContentId = mediaAttributes.attributeAsNumber(itemData, "editorialElementKind");
+ const shouldPersonalizeContent = featuredContentId === 501 /* groupingTypes.FeaturedContentID.AppStore_PersonalizedHeroMarker */;
+ const metadata = groupingShelfControllerCommon.metadataForFCData(objectGraph, itemData, shelfToken, shouldPersonalizeContent, personalizationDataContainer, metricsOptions, groupingParseContext, () => {
+ shelfToken === null || shelfToken === void 0 ? void 0 : shelfToken.remainingItems.push(itemData);
+ });
+ if (!metadata) {
+ return null;
+ }
+ const hasContentId = ((_b = (_a = metadata.content) === null || _a === void 0 ? void 0 : _a.id) === null || _b === void 0 ? void 0 : _b.length) > 0;
+ if (hasContentId) {
+ metricsOptions.id = metadata.content.id;
+ metricsOptions.idType = "its_id";
+ metricsOptions.adamId = metadata.content.id;
+ }
+ const card = new models.EditorialCard();
+ // Set caption
+ let caption = mediaAttributes.attributeAsString(itemData, "designBadge");
+ if (!caption) {
+ caption = metadata.caption;
+ }
+ card.caption = caption;
+ // Set title
+ let title = mediaAttributes.attributeAsString(itemData, "title");
+ if (!title) {
+ title = metadata.title;
+ }
+ card.title = title;
+ // Set subtitle
+ let subtitle = groupingShelfControllerCommon.unescapeHtmlString(mediaAttributes.attributeAsString(itemData, "designTag"));
+ if (!subtitle) {
+ subtitle = metadata.subtitle;
+ }
+ card.subtitle = subtitle;
+ // Setup Artwork
+ const artworkOptions = {
+ useCase: 19 /* content.ArtworkUseCase.GroupingHero */,
+ withJoeColorPlaceholder: true,
+ };
+ if (metadata.artwork && (shelfToken === null || shelfToken === void 0 ? void 0 : shelfToken.featuredContentId) !== 258 /* groupingTypes.FeaturedContentID.Sundance_Flowcase */) {
+ let artworkDict = serverData.asDictionary(metadata.artwork, "subscriptionHero");
+ if (serverData.isNull(artworkDict) && serverData.isDefinedNonNull(metadata.appEvent)) {
+ artworkDict = serverData.asDictionary(metadata.artwork, "eventCard");
+ }
+ card.artwork = groupingShelfControllerCommon.groupingArtworkFromApiArtwork(objectGraph, artworkDict, artworkOptions);
+ }
+ else {
+ card.artwork = groupingShelfControllerCommon.artworkFromFC(objectGraph, itemData, 416, 204, artworkOptions);
+ }
+ // Set action
+ card.clickAction = metadata.action;
+ // App event formatted dates
+ if (serverData.isDefinedNonNull(metadata.appEvent)) {
+ card.appEventFormattedDates = metadata.appEvent.formattedDates;
+ }
+ // Lockup
+ card.lockup = metadata.lockup;
+ // Overlay style
+ if (serverData.isDefinedNonNull(card.artwork) && serverData.isDefinedNonNull(card.artwork.backgroundColor)) {
+ const isArtworkDark = color.isDarkColor(card.artwork.backgroundColor);
+ card.mediaOverlayStyle = isArtworkDark ? "dark" : "light";
+ if (serverData.isDefinedNonNull(card.lockup) &&
+ serverData.isDefinedNonNull(card.lockup.offerDisplayProperties) &&
+ objectGraph.host.isiOS) {
+ const offerEnvironment = isArtworkDark ? "dark" : "light";
+ card.lockup.offerDisplayProperties =
+ card.lockup.offerDisplayProperties.newOfferDisplayPropertiesChangingAppearance(false, "transparent", offerEnvironment);
+ }
+ }
+ // Set adamId
+ card.adamId = serverData.asString(metadata.content, "id");
+ // Set flow preview actions
+ const contentData = mediaRelationship.relationshipData(objectGraph, itemData, "contents");
+ if (serverData.isDefinedNonNull(contentData)) {
+ const metricsClickOptions = metricsHelpersClicks.clickOptionsForLockup(objectGraph, contentData, metricsOptions);
+ metricsClickOptions.targetType = metricsOptions.targetType;
+ card.flowPreviewActionsConfiguration = flowPreview.flowPreviewActionsConfigurationForProductFromData(objectGraph, itemData, false, shelfToken === null || shelfToken === void 0 ? void 0 : shelfToken.clientIdentifierOverride, card.clickAction, metricsOptions, metricsClickOptions);
+ }
+ // Configure impressions
+ const impressionOptions = metricsHelpersImpressions.impressionOptions(objectGraph, (_c = metadata.content) !== null && _c !== void 0 ? _c : itemData, metadata.title, metricsOptions);
+ if (serverData.isDefinedNonNull(metadata.onDevicePersonalizationDataProcessingType)) {
+ const recoMetricsData = metricsHelpersUtil.combinedRecoMetricsDataFromMetricsData(impressionOptions.recoMetricsData, metadata.onDevicePersonalizationDataProcessingType, null);
+ impressionOptions.recoMetricsData = recoMetricsData;
+ }
+ if (serverData.isDefinedNonNull(metadata.appEvent)) {
+ impressionOptions.inAppEventId = metadata.appEvent.appEventId;
+ if (serverData.isDefinedNonNull(metadata.appEvent.lockup)) {
+ impressionOptions.relatedSubjectIds = [metadata.appEvent.lockup.adamId];
+ }
+ }
+ if (serverData.isDefinedNonNull(shelfToken)) {
+ metricsHelpersImpressions.addImpressionFields(objectGraph, card, impressionOptions);
+ }
+ return card;
+ }
+ // region Helpers
+ /**
+ * Iterates through all the editorial cards data, and creates a set of personalization data that is targetted only to these cards.
+ *
+ * @param objectGraph
+ * @param dataArray The input array of editorial card data items
+ * @returns Any relevant OnDevicePersonalizaionData
+ */
+ personalizationDataContainerForEditorialCardItemsDataArray(objectGraph, dataArray) {
+ var _a, _b;
+ if (!onDevicePersonalization.isPersonalizationAvailable(objectGraph)) {
+ return null;
+ }
+ // First iterate through and extract any relevant App IDs from meta.personalizationData for each card's contents.
+ const appIds = new Set();
+ for (const data of dataArray) {
+ const featuredContentId = mediaAttributes.attributeAsNumber(data, "editorialElementKind");
+ const shouldPersonalizeContent = featuredContentId === 501 /* groupingTypes.FeaturedContentID.AppStore_PersonalizedHeroMarker */;
+ const isLinkNode = ((_a = serverData.asString(data, "url")) === null || _a === void 0 ? void 0 : _a.length) > 0;
+ const containsLinkNode = ((_b = mediaAttributes.attributeAsString(data, "link.url")) === null || _b === void 0 ? void 0 : _b.length) > 0;
+ const isContentNode = mediaRelationship.hasRelationship(data, "contents", false);
+ if (shouldPersonalizeContent && !isLinkNode && !containsLinkNode && isContentNode) {
+ const contentDataItems = mediaRelationship.relationshipCollection(data, "contents");
+ for (const contentData of contentDataItems) {
+ const appId = serverData.asString(contentData, "meta.personalizationData.appId");
+ if ((appId === null || appId === void 0 ? void 0 : appId.length) > 0) {
+ appIds.add(appId);
+ }
+ }
+ }
+ }
+ return onDevicePersonalization.personalizationDataContainerForAppIds(objectGraph, appIds);
+ }
+}
+//# sourceMappingURL=grouping-editorial-card-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-editorial-story-card-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-editorial-story-card-shelf-controller.js
new file mode 100644
index 0000000..73a0725
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-editorial-story-card-shelf-controller.js
@@ -0,0 +1,143 @@
+import * as models from "../../../api/models";
+import * as serverData from "../../../foundation/json-parsing/server-data";
+import * as mediaAttributes from "../../../foundation/media/attributes";
+import * as mediaRelationship from "../../../foundation/media/relationships";
+import { ShelfParameters } from "../../../foundation/network/url-constants";
+import * as color from "../../../foundation/util/color-util";
+import * as content from "../../content/content";
+import * as metricsHelpersLocation from "../../metrics/helpers/location";
+import { createTodayBaseCard } from "../../today/cards/today-base-card-builder";
+import { TodayParseContext } from "../../today/today-types";
+import { GroupingShelfController, routesForFeaturedContentIds } from "./grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+export class GroupingEditorialStoryCardShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingEditorialStoryCardShelfController");
+ this.supportedFeaturedContentIds = new Set([475 /* groupingTypes.FeaturedContentID.AppStore_HorizontalCardSwoosh */]);
+ }
+ // endregion
+ // region Shelf Builder
+ shelfRoute(objectGraph) {
+ return routesForFeaturedContentIds(this.supportedFeaturedContentIds, [
+ `${ShelfParameters.contentType}=editorialStoryCard`,
+ ]);
+ }
+ // endregion
+ // region GroupingShelfController
+ _supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId) {
+ if (!super._supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId)) {
+ return false;
+ }
+ const displayStyle = mediaAttributes.attributeAsString(mediaApiData, "displayStyle");
+ const contentType = groupingShelfControllerCommon.contentTypeForHorizontalCardDisplayStyle(objectGraph, displayStyle);
+ return contentType === "editorialStoryCard";
+ }
+ // 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: mediaRelationship.relationshipCollection(mediaApiData, "children") };
+ }
+ /**
+ * 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 GroupingShelfController.secondaryGroupingShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters);
+ }
+ /**
+ * 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) {
+ const shelfToken = { ...baseShelfToken };
+ const displayStyle = mediaAttributes.attributeAsString(mediaApiData, "displayStyle");
+ shelfToken.shelfStyle = groupingShelfControllerCommon.contentTypeForHorizontalCardDisplayStyle(objectGraph, displayStyle);
+ return shelfToken;
+ }
+ // 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) {
+ const items = [];
+ for (const card of shelfData.shelfContents) {
+ if (!mediaAttributes.hasAttributes(card) || groupingShelfControllerCommon.shouldDefer(shelfToken)) {
+ shelfToken.remainingItems.push(card);
+ shelfToken.isDeferring = true;
+ continue;
+ }
+ const cardModel = GroupingEditorialStoryCardShelfController.makeStoryCard(objectGraph, card, shelfToken);
+ if (serverData.isNullOrEmpty(cardModel)) {
+ continue;
+ }
+ items.push(cardModel);
+ metricsHelpersLocation.nextPosition(shelfToken.metricsLocationTracker);
+ }
+ const shelf = new models.Shelf(shelfToken.shelfStyle);
+ shelf.title = shelfToken.title;
+ shelf.items = items;
+ shelf.isHorizontal = true;
+ shelf.background = {
+ type: "interactive",
+ };
+ shelf.url = groupingShelfControllerCommon.createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, shelfToken);
+ shelf.isHorizontal = true;
+ return shelf;
+ }
+ static makeStoryCard(objectGraph, itemData, shelfToken) {
+ // Prefer `subscriptionHero` if it's available for grouping pages, and fallback to `mediaCard` if not.
+ let artworkData = mediaAttributes.attributeAsDictionary(itemData, "editorialArtwork.subscriptionHero");
+ if (serverData.isNullOrEmpty(artworkData)) {
+ artworkData = mediaAttributes.attributeAsDictionary(itemData, "editorialArtwork.mediaCard");
+ }
+ const artwork = content.artworkFromApiArtwork(objectGraph, artworkData, {
+ cropCode: "fn",
+ withJoeColorPlaceholder: true,
+ useCase: 16 /* content.ArtworkUseCase.TodaySmallStoryCard */,
+ });
+ if (serverData.isNull(artwork)) {
+ return null;
+ }
+ const title = mediaAttributes.attributeAsString(itemData, "editorialNotes.name");
+ const heading = mediaAttributes.attributeAsString(itemData, "label");
+ const description = mediaAttributes.attributeAsString(itemData, "editorialNotes.short");
+ const cardModel = new models.EditorialStoryCard(title, artwork, null, heading, {
+ type: "text",
+ title: heading,
+ }, description);
+ const basicCard = createTodayBaseCard(objectGraph, itemData, null, new TodayParseContext(shelfToken === null || shelfToken === void 0 ? void 0 : shelfToken.metricsPageInformation, shelfToken === null || shelfToken === void 0 ? void 0 : shelfToken.metricsLocationTracker));
+ if (serverData.isDefinedNonNull(basicCard)) {
+ cardModel.clickAction = basicCard.clickAction;
+ }
+ const backgroundStyle = color.isDarkColor(artwork.backgroundColor)
+ ? "dark"
+ : "light";
+ cardModel.shelfBackground = {
+ type: "artwork",
+ artwork: artwork,
+ style: backgroundStyle,
+ };
+ return cardModel;
+ }
+}
+//# sourceMappingURL=grouping-editorial-story-card-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-activity-feed-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-activity-feed-shelf-controller.js
new file mode 100644
index 0000000..4fa27fe
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-activity-feed-shelf-controller.js
@@ -0,0 +1,223 @@
+import * as models from "../../../api/models";
+import { Parameters, Path, Protocol } from "../../../foundation/network/url-constants";
+import * as urls from "../../../foundation/network/urls";
+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 { ActionMetrics } from "../../../api/models";
+import { makeGameCenterHeader } from "../../arcade/arcade-common";
+export class GroupingGameCenterActivityFeedController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingGameCenterActivityFeedController");
+ this.batchGroupKey = "gameCenter";
+ this.supportedFeaturedContentIds = new Set([
+ 548 /* groupingTypes.FeaturedContentID.AppStore_GameCenterActivityFeedMarker */,
+ ]);
+ }
+ // endregion
+ // region Shelf Builder
+ shelfRoute(objectGraph) {
+ return [
+ ...super.shelfRoute(objectGraph),
+ {
+ protocol: Protocol.internal,
+ path: `/${Path.grouping}/${Path.shelf}/{token}`,
+ query: [Parameters.isGameCenterActivityFeedShelf],
+ },
+ ];
+ }
+ // 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: [],
+ activities: [],
+ };
+ }
+ /**
+ * 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) {
+ const filter = this.gameCategoryFilter(shelfToken.gamesFilter);
+ const activityLimit = 20;
+ return await objectGraph.gameCenter.fetchActivityFeedCards(filter, activityLimit).then((activities) => {
+ return {
+ shelfContents: [],
+ activities: activities,
+ };
+ });
+ }
+ /**
+ * 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;
+ }
+ incompleteShelfFetchStrategy(objectGraph) {
+ return models.IncompleteShelfFetchStrategy.OnPageLoad;
+ }
+ // 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.pendingActivityFeedShelfForGrouping(objectGraph, shelfToken, groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.isArcadePage);
+ }
+ else {
+ return this.activityFeedShelfForGrouping(objectGraph, shelfData, shelfToken, groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.isArcadePage);
+ }
+ }
+ pendingActivityFeedShelfForGrouping(objectGraph, shelfToken, isArcadePage) {
+ if (objectGraph.client.deviceType !== "phone" && objectGraph.client.deviceType !== "pad") {
+ return null;
+ }
+ const shelf = this.activityFeedShelfForGrouping(objectGraph, {
+ shelfContents: [],
+ activities: [],
+ }, shelfToken, isArcadePage);
+ const groupingShelfUrl = urls.URL.from(groupingShelfControllerCommon.groupingShelfUrl(shelfToken));
+ shelf.url = groupingShelfUrl.param(Parameters.isGameCenterActivityFeedShelf, "true").build();
+ shelf.isHidden = shelf.items.length === 0;
+ shelf.batchGroup = this.batchGroupKey;
+ return shelf;
+ }
+ activityFeedShelfForGrouping(objectGraph, shelfData, shelfToken, isArcadePage) {
+ if (objectGraph.client.deviceType !== "phone" && objectGraph.client.deviceType !== "pad") {
+ return null;
+ }
+ const shelf = this.activityFeedShelf(objectGraph, shelfData.activities, shelfToken, isArcadePage);
+ const title = objectGraph.loc.string("Arcade.ActivityFeed.RecentActivity");
+ shelf.header = makeGameCenterHeader(objectGraph, title);
+ // Connect the shelf's seeAllAction
+ groupingShelfControllerCommon.replaceShelfSeeAllAction(objectGraph, shelf, shelf.seeAllAction);
+ shelf.batchGroup = this.batchGroupKey;
+ // Hide when empty.
+ shelf.isHidden = shelf.items.length === 0;
+ return shelf;
+ }
+ // region Helpers
+ activityFeedShelf(objectGraph, activities, token, isArcadePage = false) {
+ const shelf = new models.Shelf("gameCenterActivityFeedCard");
+ shelf.isHorizontal = true;
+ shelf.mergeWhenFetched = true;
+ shelf.batchGroup = this.batchGroupKey;
+ shelf.items = activities;
+ shelf.isHidden = shelf.items.length === 0;
+ activities.forEach((item, index) => {
+ const metricsImpressionOptions = {
+ id: "friendActivity",
+ idType: "static",
+ targetType: "chiclet",
+ kind: null,
+ softwareType: isArcadePage ? "Arcade" : null,
+ title: "",
+ pageInformation: token.metricsPageInformation,
+ locationTracker: token.metricsLocationTracker,
+ };
+ metricsHelpersImpressions.addImpressionFields(objectGraph, item, metricsImpressionOptions);
+ // Create action metrics for click events.
+ const profileActionMetrics = new ActionMetrics();
+ const profileAvatarActionMetrics = new ActionMetrics();
+ const leaderboardActionMetrics = new ActionMetrics();
+ const achievementActionMetrics = new ActionMetrics();
+ const appActionMetrics = new ActionMetrics();
+ // Create targetId and metrics array.
+ const targetIdAndMetricsArray = [
+ {
+ targetId: "playerName",
+ metrics: profileActionMetrics,
+ },
+ {
+ targetId: "profileImage",
+ metrics: profileAvatarActionMetrics,
+ },
+ {
+ targetId: "leaderboardAchievement",
+ metrics: leaderboardActionMetrics,
+ },
+ {
+ targetId: "achievement",
+ metrics: achievementActionMetrics,
+ },
+ {
+ targetId: item.adamID || "gameIcon",
+ metrics: appActionMetrics,
+ },
+ ];
+ // Loop through `targetIdAndMetricsArray` and call `addClickEventToActivityFeedMetrics` for each metrics.
+ targetIdAndMetricsArray.forEach((targetIdAndMetricsDictionary) => metricsHelpersClicks.addClickEventToActivityFeedMetrics(objectGraph, targetIdAndMetricsDictionary.metrics, token.title, targetIdAndMetricsDictionary.targetId, {
+ pageInformation: token.metricsPageInformation,
+ locationTracker: token.metricsLocationTracker,
+ }));
+ // Assign action metrics to `item`.
+ item.profileActionMetrics = profileActionMetrics;
+ item.profileAvatarActionMetrics = profileAvatarActionMetrics;
+ item.leaderboardActionMetrics = leaderboardActionMetrics;
+ item.achievementActionMetrics = achievementActionMetrics;
+ item.appActionMetrics = appActionMetrics;
+ // Proceed to next position.
+ metricsHelpersLocation.nextPosition(token.metricsLocationTracker);
+ });
+ // Setup see all action
+ let seeAllAction;
+ if (!objectGraph.featureFlags.isGSEUIEnabled("de7bbd8e")) {
+ seeAllAction = new models.GameCenterDashboardAction();
+ seeAllAction.title = objectGraph.loc.string("Arcade.ActivityFeed.AllActivity", objectGraph.loc.string("ACTION_SEE_ALL"));
+ metricsHelpersClicks.addClickEventToSeeAllAction(objectGraph, seeAllAction, null, {
+ pageInformation: token.metricsPageInformation,
+ locationTracker: token.metricsLocationTracker,
+ });
+ }
+ shelf.seeAllAction = seeAllAction;
+ return shelf;
+ }
+ // endregion
+ /**
+ * Convert GameCategoryFilter to GamesFilter. Ideally these would be the same, but seed 1 has already left the station.
+ * @param gamesFilter
+ */
+ gameCategoryFilter(gamesFilter) {
+ if (gamesFilter === "nonArcade") {
+ return "nonarcade";
+ }
+ return gamesFilter;
+ }
+ // endregion
+ // region Metrics
+ shelfMetricsOptionsFromBaseMetricsOptions(objectGraph, shelfToken, baseMetricsOptions) {
+ return {
+ ...baseMetricsOptions,
+ title: "Friend Activity",
+ badges: {
+ gameCenter: true,
+ },
+ idType: "shelf_id",
+ };
+ }
+}
+//# sourceMappingURL=grouping-game-center-activity-feed-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-continue-playing-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-continue-playing-shelf-controller.js
new file mode 100644
index 0000000..6a85582
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-continue-playing-shelf-controller.js
@@ -0,0 +1,332 @@
+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 mediaDataFetching from "../../../foundation/media/data-fetching";
+import * as mediaNetwork from "../../../foundation/media/network";
+import * as mediaRelationship from "../../../foundation/media/relationships";
+import { ResponseMetadata } from "../../../foundation/network/network";
+import { Parameters, Path, Protocol } from "../../../foundation/network/url-constants";
+import * as urls from "../../../foundation/network/urls";
+import * as contentAttributes from "../../content/attributes";
+import * as content from "../../content/content";
+import * as flowPreview from "../../content/flow-preview";
+import * as filtering from "../../filtering";
+import * as lockups from "../../lockups/lockups";
+import * as metricsHelpersClicks from "../../metrics/helpers/clicks";
+import * as metricsHelpersImpressions from "../../metrics/helpers/impressions";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+import { makeGameCenterHeader } from "../../arcade/arcade-common";
+import { injectCrossfireFlowForGameCenter } from "./grouping-game-center-popular-with-your-friends-shelf-controller";
+export class GroupingGameCenterContinuePlayingShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingGameCenterContinuePlayingShelfController");
+ this.batchGroupKey = "gameCenterContinuePlaying";
+ this.supportedFeaturedContentIds = new Set([500 /* groupingTypes.FeaturedContentID.AppStore_ContinuePlayingMarker */]);
+ }
+ // endregion
+ // region GroupingShelfController
+ _supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId) {
+ return (super._supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId) &&
+ this.supportsVideoCardShelf(objectGraph, objectGraph.host.platform));
+ }
+ // endregion
+ // region Shelf Builder
+ shelfRoute(objectGraph) {
+ return [
+ ...super.shelfRoute(objectGraph),
+ {
+ protocol: Protocol.internal,
+ path: `/${Path.grouping}/${Path.shelf}/{token}`,
+ query: [Parameters.isGameCenterContinuePlayingShelf],
+ },
+ ];
+ }
+ // 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: mediaRelationship.relationshipCollection(mediaApiData, "contents") };
+ }
+ /**
+ * 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) {
+ const startTime = Date.now();
+ const maxNumberOfGames = this.maximumNumberOfRecentGamesToRequest();
+ const recentlyPlayedGamesPromise = objectGraph.gameCenter.fetchRecentlyPlayedGamesWithinSeconds(this.gameCategoryFilter(shelfToken.gamesFilter), maxNumberOfGames, objectGraph.bag.recentlyPlayedGamesWindowInSeconds);
+ return await recentlyPlayedGamesPromise.then(async (recentlyPlayedIds) => {
+ const endTime = Date.now();
+ objectGraph.console.log("grouping-gamecenter-builder: requestForContinuePlaying NATIVE took " +
+ (endTime - startTime).toString(10) +
+ " milliseconds.");
+ let shelfDataPromise;
+ if (recentlyPlayedIds.length === 0) {
+ // 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: [] };
+ return await Promise.resolve(emptyShelfData);
+ }
+ else {
+ const request = new mediaDataFetching.Request(objectGraph)
+ .withIdsOfType(recentlyPlayedIds.slice(0, this.maximumNumberOfRecentGamesToShow()), "apps")
+ .includingAgeRestrictions();
+ groupingShelfControllerCommon.prepareGroupingShelfRequest(objectGraph, request);
+ shelfDataPromise = mediaNetwork
+ .fetchData(objectGraph, request, {})
+ .then((dataContainer) => {
+ const shelfData = {
+ shelfContents: dataContainer.data,
+ responseTimingValues: dataContainer[ResponseMetadata.timingValues],
+ };
+ return shelfData;
+ });
+ }
+ return await shelfDataPromise;
+ });
+ }
+ /**
+ * 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;
+ }
+ incompleteShelfFetchStrategy(objectGraph) {
+ return models.IncompleteShelfFetchStrategy.OnPageLoad;
+ }
+ // 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.pendingContinuePlayingForGrouping(objectGraph, shelfToken);
+ }
+ else {
+ return this.continuePlayingShelfForGrouping(objectGraph, shelfData.shelfContents, shelfToken);
+ }
+ }
+ pendingContinuePlayingForGrouping(objectGraph, shelfToken) {
+ const shelf = this.continuePlayingShelfForGrouping(objectGraph, [], shelfToken);
+ if (!shelf) {
+ return null;
+ }
+ const groupingShelfUrl = urls.URL.from(groupingShelfControllerCommon.groupingShelfUrl(shelfToken));
+ shelf.url = groupingShelfUrl.param(Parameters.isGameCenterContinuePlayingShelf, "true").build();
+ shelf.batchGroup = this.batchGroupKey;
+ return shelf;
+ }
+ continuePlayingShelfForGrouping(objectGraph, shelfContents, shelfToken) {
+ return validation.context("continuePlayingShelfForGrouping", () => {
+ const shelf = this.videoCardContinuePlayingShelf(objectGraph, shelfContents, shelfToken);
+ shelf.mergeWhenFetched = false; // Always replace
+ shelf.batchGroup = this.batchGroupKey;
+ shelf.isHidden = shelf.items.length === 0;
+ shelf.header = makeGameCenterHeader(objectGraph, objectGraph.loc.string("GameCenter.ContinuePlayingShelf.Title"), shelfToken.subtitle);
+ return shelf;
+ });
+ }
+ // endregion
+ // region Video Card Shelf
+ supportsVideoCardShelf(objectGraph, platform) {
+ switch (platform) {
+ case "iOS":
+ case "tvOS":
+ case "macOS":
+ return true;
+ default:
+ return false;
+ }
+ }
+ videoCardContinuePlayingShelf(objectGraph, dataArray, shelfToken) {
+ return validation.context("videoCardContinuePlayingShelf", () => {
+ const shelf = new models.Shelf("videoCard");
+ shelf.isHorizontal = true;
+ shelf.batchGroup = this.batchGroupKey;
+ const items = [];
+ for (const data of dataArray) {
+ // Filter out unwanted content
+ if (filtering.shouldFilter(objectGraph, data)) {
+ continue;
+ }
+ const item = this.editorialSplashVideoCardForContinuePlaying(objectGraph, data, shelfToken);
+ if (item) {
+ items.push(item);
+ }
+ }
+ shelf.items = items;
+ return shelf;
+ });
+ }
+ /**
+ * Create a `VideoCard` configured for CP Shelf.
+ * Specifically, it uses:
+ * - TV Top Shelf Static Image Still
+ * - Arcade Product Page Uber Video
+ *
+ * @param objectGraph
+ * @param data
+ * @param shelfToken
+ */
+ editorialSplashVideoCardForContinuePlaying(objectGraph, data, shelfToken) {
+ return validation.context("editorialSplashVideoCardForContinuePlaying", () => {
+ var _a;
+ const lockupMetricsOptions = {
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ targetType: "lockup",
+ };
+ const shouldHideArcadeHeader = objectGraph.featureFlags.isEnabled("hide_arcade_header_on_arcade_tab") &&
+ serverData.asBooleanOrFalse(shelfToken.isArcadePage);
+ const isArcadeLockup = content.isArcadeSupported(objectGraph, data);
+ const lockupOptions = {
+ metricsOptions: lockupMetricsOptions,
+ artworkUseCase: 1 /* content.ArtworkUseCase.LockupIconSmall */,
+ offerEnvironment: "dark",
+ offerStyle: "white",
+ canDisplayArcadeOfferButton: true,
+ shouldHideArcadeHeader: shouldHideArcadeHeader,
+ isSubtitleHidden: isArcadeLockup && !shouldHideArcadeHeader,
+ };
+ const video = this.editorialSplashVideoWithTopShelfStill(objectGraph, data);
+ if (!video || !video.preview) {
+ return null;
+ }
+ const lockup = lockups.lockupFromData(objectGraph, data, lockupOptions);
+ if (!lockup) {
+ return null;
+ }
+ lockup.clickAction = injectCrossfireFlowForGameCenter(objectGraph, lockup.clickAction);
+ const clickAction = this.clickActionForVideoCard(objectGraph, data, objectGraph.host.platform, lockupMetricsOptions, shelfToken.clientIdentifierOverride);
+ if (!clickAction) {
+ return null;
+ }
+ const videoCard = new models.VideoCard();
+ videoCard.video = video;
+ videoCard.lockup = lockup;
+ videoCard.overlayStyle = "dark";
+ videoCard.clickAction = clickAction;
+ // Set flow preview actions
+ const metricsClickOptions = metricsHelpersClicks.clickOptionsForLockup(objectGraph, data, lockupMetricsOptions);
+ videoCard.flowPreviewActionsConfiguration = flowPreview.flowPreviewActionsConfigurationForProductFromData(objectGraph, data, true, shelfToken.clientIdentifierOverride, videoCard.clickAction, lockupMetricsOptions, metricsClickOptions);
+ // Configure impressions borrowing lockup values for now.
+ const impressionOptions = metricsHelpersImpressions.impressionOptions(objectGraph, data, lockup.title, lockupMetricsOptions);
+ metricsHelpersImpressions.addImpressionFields(objectGraph, videoCard, impressionOptions);
+ (_a = videoCard.impressionMetrics) === null || _a === void 0 ? true : delete _a.fields["impressionIndex"];
+ return videoCard;
+ });
+ }
+ /**
+ * Return a video to use for continue playing with:
+ * - Product Uber
+ * - TV Top Shelf Static Still
+ */
+ editorialSplashVideoWithTopShelfStill(objectGraph, data) {
+ return validation.context("editorialSplashVideoWithTopShelfStill", () => {
+ // TV Top Shelf Still (If Any):
+ let previewOverride = null;
+ const artworkData = contentAttributes.contentAttributeAsDictionary(objectGraph, data, "editorialArtwork.topShelf");
+ if (serverData.isDefinedNonNull(artworkData)) {
+ previewOverride = content.artworkFromApiArtwork(objectGraph, artworkData, {
+ withJoeColorPlaceholder: true,
+ useCase: 23 /* content.ArtworkUseCase.VideoCardStill */,
+ cropCode: "sr",
+ });
+ }
+ return content.editorialSplashVideoFromData(objectGraph, data, previewOverride);
+ });
+ }
+ // endregion
+ // region Helpers
+ /**
+ * Returns the click action to use for given platform for video card
+ */
+ clickActionForVideoCard(objectGraph, data, platform, metricsOptions, clientIdentifierOverride) {
+ const lockupClickMetricsOptions = metricsHelpersClicks.clickOptionsForLockup(objectGraph, data, metricsOptions);
+ let productPageAction = lockups.actionFromData(objectGraph, data, lockupClickMetricsOptions, clientIdentifierOverride);
+ productPageAction = injectCrossfireFlowForGameCenter(objectGraph, productPageAction);
+ // Wrap in Open for tv only.
+ if (platform === "tvOS") {
+ const openAppAction = new models.OpenAppAction(data.id, "app");
+ const openAppClickOptions = {
+ actionType: "open",
+ id: data.id,
+ contextualAdamId: data.id,
+ anonymizationOptions: metricsOptions.anonymizationOptions,
+ pageInformation: metricsOptions.pageInformation,
+ locationTracker: metricsOptions.locationTracker,
+ };
+ metricsHelpersClicks.addClickEventToAction(objectGraph, openAppAction, openAppClickOptions);
+ const stateAction = new models.OfferStateAction(data.id, productPageAction);
+ stateAction.openAction = openAppAction;
+ // If the app is downloading and the user clicks this item, take them to the product page instead of cancelling the download.
+ stateAction.cancelAction = productPageAction;
+ return stateAction;
+ }
+ else {
+ return productPageAction;
+ }
+ }
+ /**
+ * The maximum number of games we should ask GameCenterServer for. Note that some of the games it fetches will
+ * not be Arcade nor platform-compatible games. For this reason, you should ask for more than you need.
+ * As this call is performant enough with a time limit, we just request the maximum amount.
+ *
+ * The maximum that GameCenterServer will accept is 200.
+ */
+ maximumNumberOfRecentGamesToRequest() {
+ return 200;
+ }
+ /**
+ * The maximum number of games to display on the shelf.
+ *
+ * The maximum that Media API will accept is 100.
+ */
+ maximumNumberOfRecentGamesToShow() {
+ return 10;
+ }
+ /**
+ * Convert GameCategoryFilter to GamesFilter. Ideally these would be the same, but seed 1 has already left the station.
+ * @param gamesFilter
+ */
+ gameCategoryFilter(gamesFilter) {
+ if (gamesFilter === "nonArcade") {
+ return "nonarcade";
+ }
+ return gamesFilter;
+ }
+ // endregion
+ // region Metrics
+ shelfMetricsOptionsFromBaseMetricsOptions(objectGraph, shelfToken, baseMetricsOptions) {
+ return {
+ ...baseMetricsOptions,
+ badges: {
+ gameCenter: true,
+ },
+ idType: "its_contentId",
+ title: objectGraph.loc.string("GameCenter.ContinuePlayingShelf.Title"),
+ };
+ }
+}
+//# sourceMappingURL=grouping-game-center-continue-playing-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-popular-with-your-friends-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-popular-with-your-friends-shelf-controller.js
new file mode 100644
index 0000000..a3eb652
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-popular-with-your-friends-shelf-controller.js
@@ -0,0 +1,314 @@
+import * as models from "../../../api/models";
+import * as serverData from "../../../foundation/json-parsing/server-data";
+import * as mediaAttributes from "../../../foundation/media/attributes";
+import * as mediaDataFetching from "../../../foundation/media/data-fetching";
+import * as mediaDataStructure from "../../../foundation/media/data-structure";
+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 contentAttributes from "../../content/attributes";
+import * as content from "../../content/content";
+import * as lockups from "../../lockups/lockups";
+import * as metricsHelpersClicks from "../../metrics/helpers/clicks";
+import * as metricsHelpersLocation from "../../metrics/helpers/location";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+import { makeGameCenterHeader, openGamesUIAction } from "../../arcade/arcade-common";
+export class GroupingGameCenterPopularWithYourFriendsController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingGameCenterPopularWithYourFriendsController");
+ this.batchGroupKey = "gameCenter";
+ this.supportedFeaturedContentIds = new Set([
+ 495 /* groupingTypes.FeaturedContentID.AppStore_PopularWithYourFriendsMarker */,
+ ]);
+ }
+ // endregion
+ // region Shelf Builder
+ shelfRoute(objectGraph) {
+ return [
+ ...super.shelfRoute(objectGraph),
+ {
+ protocol: Protocol.internal,
+ path: `/${Path.grouping}/${Path.shelf}/{token}`,
+ query: [Parameters.isGameCenterPopularWithYourFriendsShelf],
+ },
+ ];
+ }
+ // 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: [],
+ };
+ }
+ /**
+ * 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) {
+ const popularWithYourFriendsPromise = objectGraph.gameCenter.fetchGamesPopularWithFriends(this.gameCategoryFilter(shelfToken.gamesFilter), 30);
+ return await popularWithYourFriendsPromise.then(async (response) => {
+ const gameplayRecords = response
+ .map((item) => this.gameplayHistoryFromData(item))
+ .sort((a, b) => b.records.length - a.records.length);
+ const adamIds = gameplayRecords
+ .filter((record) => this.isCompatibleGameCenterPlatform(objectGraph, record.platformId))
+ .map((record) => record.adamId);
+ if (adamIds.length === 0) {
+ // 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: [] };
+ return await Promise.resolve(emptyShelfData);
+ }
+ // <rdar://problem/62490641> Send payload to MAPI to allow reco to reorder the "Popular with friends" shelf.
+ const request = new mediaDataFetching.Request(objectGraph)
+ // MAPI will refuse to serve this request if there are more than 100 items.
+ .withIdsOfType(adamIds.slice(0, 100), "apps")
+ .includingAgeRestrictions();
+ groupingShelfControllerCommon.prepareGroupingShelfRequest(objectGraph, request);
+ return await mediaNetwork
+ .fetchData(objectGraph, request, {})
+ .then((dataContainer) => {
+ const shelfData = {
+ shelfContents: dataContainer.data,
+ responseTimingValues: dataContainer[ResponseMetadata.timingValues],
+ };
+ return shelfData;
+ });
+ });
+ }
+ /**
+ * 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;
+ }
+ incompleteShelfFetchStrategy(objectGraph) {
+ return models.IncompleteShelfFetchStrategy.OnPageLoad;
+ }
+ // 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.pendingPopularWithFriendsShelfForGrouping(objectGraph, shelfData, shelfToken);
+ }
+ else {
+ return this.popularWithFriendsShelfForGrouping(objectGraph, shelfData, shelfToken);
+ }
+ }
+ pendingPopularWithFriendsShelfForGrouping(objectGraph, shelfData, shelfToken) {
+ const shelf = this.popularWithFriendsShelfForGrouping(objectGraph, shelfData, shelfToken);
+ const groupingShelfUrl = urls.URL.from(groupingShelfControllerCommon.groupingShelfUrl(shelfToken));
+ shelf.url = groupingShelfUrl.param(Parameters.isGameCenterPopularWithYourFriendsShelf, "true").build();
+ return shelf;
+ }
+ popularWithFriendsShelfForGrouping(objectGraph, shelfData, shelfToken) {
+ const shelf = this.popularWithFriendsShelf(objectGraph, shelfData.shelfContents, shelfToken);
+ shelf.mergeWhenFetched = true;
+ shelf.batchGroup = this.batchGroupKey;
+ // Hide when empty.
+ shelf.isHidden = shelf.items.length === 0;
+ // Configure header
+ shelf.header.title = shelfToken.title;
+ shelf.header.subtitle = shelfToken.subtitle;
+ return shelf;
+ }
+ popularWithFriendsShelf(objectGraph, shelfContents, shelfToken) {
+ const shelfStyle = shelfToken.shelfStyle || "mediumLockup";
+ const shelf = new models.Shelf(shelfStyle);
+ shelf.isHorizontal = true;
+ const maxNumberOfPlayersBeforeSeeAll = objectGraph.client.isTV ? 20 : 12;
+ const items = [];
+ for (let index = 0; index < shelfContents.length; index++) {
+ const data = shelfContents[index];
+ const lockupOptions = {
+ metricsOptions: {
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(data),
+ anonymizationOptions: {
+ anonymizationString: `"GAME"${index + 1}`,
+ },
+ },
+ artworkUseCase: content.artworkUseCaseFromShelfStyle(objectGraph, shelfStyle),
+ canDisplayArcadeOfferButton: content.shelfContentTypeCanDisplayArcadeOfferButtons(objectGraph, shelfStyle),
+ shouldHideArcadeHeader: objectGraph.featureFlags.isEnabled("hide_arcade_header_on_arcade_tab") && shelfToken.isArcadePage,
+ shouldShowFriendsPlayingShowcase: true,
+ };
+ // GameCenter should *not* be sending us IDs for non-GameCenter apps, but in the odd case that they do, we
+ // we check against the app's metadata to make sure we are displaying GC apps only here.
+ const isGameCenterEnabled = contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, data, "isGameCenterEnabled");
+ // It's possible that a pre-order has become available to friends, but not you yet (due to CDN/timezone reasons).
+ const isPreorder = mediaAttributes.attributeAsBooleanOrFalse(data, "isPreorder");
+ if (isPreorder || !isGameCenterEnabled) {
+ continue;
+ }
+ const lockup = lockups.lockupFromData(objectGraph, data, lockupOptions);
+ lockup.clickAction = injectCrossfireFlowForGameCenter(objectGraph, lockup.clickAction);
+ if (serverData.isDefinedNonNull(lockup)) {
+ items.push(lockup);
+ metricsHelpersLocation.nextPosition(lockupOptions.metricsOptions.locationTracker);
+ }
+ }
+ let thresholdForPlatform;
+ switch (objectGraph.client.deviceType) {
+ case "phone":
+ thresholdForPlatform = 2;
+ break;
+ case "pad":
+ thresholdForPlatform = 6;
+ break;
+ case "mac":
+ thresholdForPlatform = 6;
+ break;
+ case "tv":
+ thresholdForPlatform = 6;
+ break;
+ default:
+ thresholdForPlatform = 0;
+ }
+ shelf.header = makeGameCenterHeader(objectGraph);
+ if (items.length < thresholdForPlatform) {
+ shelf.isHidden = true;
+ return shelf;
+ }
+ shelf.items = items.slice(0, maxNumberOfPlayersBeforeSeeAll);
+ shelf.isHidden = false;
+ shelf.batchGroup = "gameCenter";
+ if (items.length > maxNumberOfPlayersBeforeSeeAll) {
+ // The shelf for the see all page
+ const shelfForSeeAllItems = new models.Shelf("mediumLockup");
+ shelfForSeeAllItems.items = items;
+ shelfForSeeAllItems.rowsPerColumn = 1;
+ // See all page
+ const seeAllPage = new models.GenericPage([shelfForSeeAllItems]);
+ seeAllPage.title = shelfToken.title;
+ // The action that opens the see all page
+ const seeAllAction = new models.FlowAction("page");
+ seeAllAction.title = objectGraph.loc.string("ACTION_SEE_ALL");
+ seeAllAction.pageData = seeAllPage;
+ // Metrics
+ metricsHelpersClicks.addClickEventToSeeAllAction(objectGraph, seeAllAction, null, {
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ });
+ // Connect the shelf's seeAllAction
+ groupingShelfControllerCommon.replaceShelfSeeAllAction(objectGraph, shelf, seeAllAction);
+ }
+ shelf.footerTitle = objectGraph.loc.string("Lockup.Footer.GamesApp");
+ shelf.footerAction = openGamesUIAction(objectGraph);
+ shelf.footerStyle = {
+ $kind: "games",
+ bundleID: "com.apple.games",
+ width: 16,
+ height: 16,
+ };
+ return shelf;
+ }
+ // endregion
+ // region Helpers
+ /**
+ * Maps GKGamePlatform to client's `deviceType`
+ * @param objectGraph
+ * @param platformId The platform ID as defined by GameCenter's GKGamePlatform
+ */
+ isCompatibleGameCenterPlatform(objectGraph, platformId) {
+ switch (platformId) {
+ case 1:
+ return objectGraph.client.isiOS;
+ case 2:
+ return objectGraph.client.isMac;
+ case 3:
+ return objectGraph.client.isTV;
+ case 4:
+ return objectGraph.client.isWatch;
+ default:
+ return false;
+ }
+ }
+ gameplayHistoryFromData(data) {
+ const adamId = serverData.asString(data, "adamId");
+ const platformId = serverData.asNumber(data, "platformId");
+ const isArcade = serverData.asBooleanOrFalse(data, "isArcade");
+ const records = this.gameplayHistoryRecordFromData(serverData.asArrayOrEmpty(data, "records"));
+ return new models.GameCenterGameplayHistory(adamId, platformId, isArcade, records);
+ }
+ gameplayHistoryRecordFromData(data) {
+ return data.map((recordData) => {
+ const playerId = serverData.asString(recordData, "playerId");
+ const timestamp = serverData.asNumber(recordData, "timestamp");
+ return new models.GameCenterGameplayHistoryRecord(playerId, timestamp);
+ });
+ }
+ /**
+ * Convert GameCategoryFilter to GamesFilter. Ideally these would be the same, but seed 1 has already left the station.
+ * @param gamesFilter
+ */
+ gameCategoryFilter(gamesFilter) {
+ if (gamesFilter === "nonArcade") {
+ return "nonarcade";
+ }
+ return gamesFilter;
+ }
+ // endregion
+ // region Metrics
+ shelfMetricsOptionsFromBaseMetricsOptions(objectGraph, shelfToken, baseMetricsOptions) {
+ return {
+ ...baseMetricsOptions,
+ badges: {
+ gameCenter: true,
+ },
+ idType: "its_contentId",
+ };
+ }
+}
+/**
+ * For evaluating how we attribute referral from Game Center shelves to the product page
+ * We going to inject a hardcoded refApp `com.apple.gamecenter.from.browse` through crossfire pipeline for tracking the page event and buy action
+ *
+ * - Modify the FlowAction to include the GameCenter ReferrerData.
+ * - Replace the click action with a compound action of CrossfireReferralAction + FlowAction
+ */
+export function injectCrossfireFlowForGameCenter(objectGraph, clickAction) {
+ if (["iOS", "macOS", "tvOS"].includes(objectGraph.host.platform) && clickAction instanceof models.FlowAction) {
+ const gameCenterCrossfireReferrerData = {
+ app: "com.apple.gamecenter.from.browse",
+ kind: {
+ name: "gameCenter",
+ },
+ };
+ clickAction.referrerData = gameCenterCrossfireReferrerData;
+ return new models.CompoundAction([
+ new models.CrossfireReferralAction(gameCenterCrossfireReferrerData),
+ clickAction,
+ ]);
+ }
+ else {
+ return clickAction;
+ }
+}
+//# sourceMappingURL=grouping-game-center-popular-with-your-friends-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-reengagement-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-reengagement-shelf-controller.js
new file mode 100644
index 0000000..f4b3d74
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-reengagement-shelf-controller.js
@@ -0,0 +1,254 @@
+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 \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-suggested-friends-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-suggested-friends-shelf-controller.js
new file mode 100644
index 0000000..e9eb3f4
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-game-center-suggested-friends-shelf-controller.js
@@ -0,0 +1,279 @@
+import * as models from "../../../api/models";
+import * as actions from "../../../api/models/actions";
+import { Parameters, Path, Protocol } from "../../../foundation/network/url-constants";
+import * as urls from "../../../foundation/network/urls";
+import { makeGameCenterHeader, openGamesUIAction } from "../../arcade/arcade-common";
+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";
+export class GroupingGameCenterSuggestedFriendsController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingGameCenterSuggestedFriendsController");
+ this.batchGroupKey = "gameCenter";
+ this.supportedFeaturedContentIds = new Set([496 /* groupingTypes.FeaturedContentID.AppStore_SuggestedFriendsMarker */]);
+ }
+ // endregion
+ // region Shelf Builder
+ shelfRoute(objectGraph) {
+ return [
+ ...super.shelfRoute(objectGraph),
+ {
+ protocol: Protocol.internal,
+ path: `/${Path.grouping}/${Path.shelf}/{token}`,
+ query: [Parameters.isGameCenterSuggestedFriendsShelf],
+ },
+ ];
+ }
+ // 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 {
+ $kind: "friendingViaPush",
+ shelfContents: [],
+ suggestedFriends: [],
+ };
+ }
+ /**
+ * 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.fetchSuggestedFriends(10).then((suggestions) => {
+ if (objectGraph.props.enabled("gameCenterFriendingViaPush")) {
+ return {
+ $kind: "friendingViaPush",
+ shelfContents: [],
+ suggestedFriends: suggestions,
+ };
+ }
+ else {
+ return {
+ $kind: "legacy",
+ shelfContents: [],
+ suggestedFriends: suggestions,
+ };
+ }
+ });
+ }
+ /**
+ * 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;
+ }
+ incompleteShelfFetchStrategy(objectGraph) {
+ return models.IncompleteShelfFetchStrategy.OnPageLoad;
+ }
+ // 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.pendingSuggestedFriendsShelfForGrouping(objectGraph, shelfToken, groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.isArcadePage);
+ }
+ else {
+ return this.suggestedFriendsShelfForGrouping(objectGraph, shelfData, shelfToken, groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.isArcadePage);
+ }
+ }
+ pendingSuggestedFriendsShelfForGrouping(objectGraph, shelfToken, isArcadePage) {
+ if (objectGraph.client.deviceType !== "phone" && objectGraph.client.deviceType !== "pad") {
+ return null;
+ }
+ const shelf = this.suggestedFriendsShelfForGrouping(objectGraph, {
+ $kind: objectGraph.props.enabled("gameCenterFriendingViaPush") ? "friendingViaPush" : "legacy",
+ shelfContents: [],
+ suggestedFriends: [],
+ }, shelfToken, isArcadePage);
+ const groupingShelfUrl = urls.URL.from(groupingShelfControllerCommon.groupingShelfUrl(shelfToken));
+ shelf.url = groupingShelfUrl.param(Parameters.isGameCenterSuggestedFriendsShelf, "true").build();
+ shelf.isHidden = shelf.items.length === 0;
+ shelf.batchGroup = this.batchGroupKey;
+ return shelf;
+ }
+ suggestedFriendsShelfForGrouping(objectGraph, shelfData, shelfToken, isArcadePage) {
+ if (objectGraph.client.deviceType !== "phone" && objectGraph.client.deviceType !== "pad") {
+ return null;
+ }
+ let shelf;
+ if (shelfData.$kind === "friendingViaPush") {
+ shelf = this.suggestedFriendsShelf(objectGraph, shelfData.suggestedFriends, shelfToken, isArcadePage);
+ }
+ else {
+ shelf = this.legacySuggestedFriendsShelf(objectGraph, shelfData.suggestedFriends, shelfToken, isArcadePage);
+ }
+ shelf.header = makeGameCenterHeader(objectGraph, shelfToken.title, shelfToken.subtitle);
+ shelf.batchGroup = this.batchGroupKey;
+ // Hide when empty.
+ shelf.isHidden = shelf.items.length === 0;
+ shelf.footerTitle = objectGraph.loc.string("Lockup.Footer.GamesApp");
+ shelf.footerAction = openGamesUIAction(objectGraph);
+ shelf.footerStyle = {
+ $kind: "games",
+ bundleID: "com.apple.games",
+ width: 16,
+ height: 16,
+ };
+ return shelf;
+ }
+ // region Helpers
+ suggestedFriendsShelf(objectGraph, suggestions, token, isArcadePage = false) {
+ const suggestionPrefix = "FRIEND_SUGGESTION";
+ if (objectGraph.client.deviceType !== "phone" && objectGraph.client.deviceType !== "pad") {
+ return null;
+ }
+ const shelf = new models.Shelf("smallContactCard");
+ shelf.isHorizontal = true;
+ shelf.mergeWhenFetched = true;
+ shelf.batchGroup = "gameCenter";
+ const enrichedSuggestions = [];
+ for (let index = 0; index < suggestions.length; index++) {
+ const suggestionId = `${suggestionPrefix}${index + 1}`;
+ const suggestedFriend = suggestions[index];
+ const buttonText = objectGraph.loc.string("INVITE");
+ const subtitle = objectGraph.loc.string("FROM_CONTACTS");
+ const metricsClickOptions = {
+ pageInformation: token.metricsPageInformation,
+ locationTracker: token.metricsLocationTracker,
+ id: suggestionId,
+ anonymizationOptions: {
+ anonymizationString: suggestionId,
+ },
+ };
+ let invitationType;
+ let shouldShowMessagesBadge;
+ if (suggestedFriend.supportsFriendingViaPush && suggestedFriend.contactAssociationID) {
+ invitationType = {
+ contact: {
+ contactID: suggestedFriend.contactID,
+ contactAssociationID: suggestedFriend.contactAssociationID,
+ },
+ };
+ shouldShowMessagesBadge = false;
+ }
+ else {
+ invitationType = {
+ messages: {
+ contactID: suggestedFriend.contactID,
+ },
+ };
+ shouldShowMessagesBadge = true;
+ }
+ const buttonAction = new actions.GameCenterInvitePlayerAction(invitationType);
+ metricsHelpersClicks.addClickEventToAction(objectGraph, buttonAction, {
+ ...metricsClickOptions,
+ actionType: "inviteFriend",
+ });
+ const removeButtonAction = new actions.GameCenterDenylistPlayerAction(suggestedFriend.contactID);
+ metricsHelpersClicks.addClickEventToAction(objectGraph, removeButtonAction, {
+ ...metricsClickOptions,
+ actionType: "removeFriendSuggestion",
+ });
+ const metricsImpressionOptions = {
+ pageInformation: token.metricsPageInformation,
+ locationTracker: token.metricsLocationTracker,
+ title: suggestionId,
+ id: suggestionId,
+ kind: "friendSuggestion",
+ softwareType: isArcadePage ? "Arcade" : null,
+ anonymizationOptions: {
+ anonymizationString: suggestionId,
+ },
+ };
+ const enrichedSuggestion = new models.SmallContactCard(suggestedFriend.contactID, suggestedFriend.fullName, subtitle, buttonText, suggestedFriend.contactID, buttonAction, removeButtonAction, shouldShowMessagesBadge);
+ metricsHelpersImpressions.addImpressionFields(objectGraph, enrichedSuggestion, metricsImpressionOptions);
+ enrichedSuggestions.push(enrichedSuggestion);
+ metricsHelpersLocation.nextPosition(token.metricsLocationTracker);
+ }
+ shelf.items = enrichedSuggestions;
+ shelf.isHidden = shelf.items.length === 0;
+ return shelf;
+ }
+ legacySuggestedFriendsShelf(objectGraph, cards, token, isArcadePage = false) {
+ const suggestionPrefix = "FRIEND_SUGGESTION";
+ if (objectGraph.client.deviceType !== "phone" && objectGraph.client.deviceType !== "pad") {
+ return null;
+ }
+ const shelf = new models.Shelf("smallContactCard");
+ shelf.isHorizontal = true;
+ shelf.mergeWhenFetched = true;
+ shelf.batchGroup = "gameCenter";
+ const enrichedSuggestions = [];
+ for (let index = 0; index < cards.length; index++) {
+ const suggestionId = `${suggestionPrefix}${index + 1}`;
+ const smallContactCard = cards[index];
+ smallContactCard.buttonText = objectGraph.loc.string("INVITE");
+ smallContactCard.subtitle = objectGraph.loc.string("FROM_CONTACTS");
+ const metricsClickOptions = {
+ pageInformation: token.metricsPageInformation,
+ locationTracker: token.metricsLocationTracker,
+ id: suggestionId,
+ anonymizationOptions: {
+ anonymizationString: suggestionId,
+ },
+ };
+ smallContactCard.buttonAction = new actions.LegacyGameCenterInvitePlayerAction(smallContactCard.contactId);
+ metricsHelpersClicks.addClickEventToAction(objectGraph, smallContactCard.buttonAction, {
+ ...metricsClickOptions,
+ actionType: "inviteFriend",
+ });
+ smallContactCard.removeButtonAction = new actions.GameCenterDenylistPlayerAction(smallContactCard.contactId);
+ metricsHelpersClicks.addClickEventToAction(objectGraph, smallContactCard.removeButtonAction, {
+ ...metricsClickOptions,
+ actionType: "removeFriendSuggestion",
+ });
+ const metricsImpressionOptions = {
+ pageInformation: token.metricsPageInformation,
+ locationTracker: token.metricsLocationTracker,
+ title: suggestionId,
+ id: suggestionId,
+ kind: "friendSuggestion",
+ softwareType: isArcadePage ? "Arcade" : null,
+ anonymizationOptions: {
+ anonymizationString: suggestionId,
+ },
+ };
+ metricsHelpersImpressions.addImpressionFields(objectGraph, smallContactCard, metricsImpressionOptions);
+ enrichedSuggestions.push(smallContactCard);
+ metricsHelpersLocation.nextPosition(token.metricsLocationTracker);
+ }
+ shelf.items = enrichedSuggestions;
+ shelf.isHidden = shelf.items.length === 0;
+ return shelf;
+ }
+ // endregion
+ // region Metrics
+ shelfMetricsOptionsFromBaseMetricsOptions(objectGraph, shelfToken, baseMetricsOptions) {
+ return {
+ ...baseMetricsOptions,
+ badges: {
+ gameCenter: true,
+ },
+ idType: "its_contentId",
+ };
+ }
+}
+//# sourceMappingURL=grouping-game-center-suggested-friends-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-hero-carousel-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-hero-carousel-shelf-controller.js
new file mode 100644
index 0000000..b7a406c
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-hero-carousel-shelf-controller.js
@@ -0,0 +1,182 @@
+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 breakoutsCommon from "../../arcade/breakouts-common";
+import * as contentAttributes from "../../content/attributes";
+import * as content from "../../content/content";
+import * as metricsHelpersClicks from "../../metrics/helpers/clicks";
+import * as metricsHelpersImpressions from "../../metrics/helpers/impressions";
+import * as metricsHelpersLocation from "../../metrics/helpers/location";
+import * as article from "../../today/article";
+import * as heroCarouselOverlayCommon from "../hero/hero-carousel-overlay-common";
+import * as heroCommon from "../hero/hero-common";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+export class GroupingHeroCarouselShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingHeroCarouselShelfController");
+ this.supportedFeaturedContentIds = new Set([480 /* groupingTypes.FeaturedContentID.AppStore_Breakout */]);
+ }
+ // endregion
+ // region GroupingShelfController
+ _supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId) {
+ if (!super._supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId)) {
+ return false;
+ }
+ const breakoutStyle = mediaAttributes.attributeAsString(mediaApiData, "displayStyle");
+ return breakoutStyle === "hero";
+ }
+ // 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: mediaRelationship.relationshipCollection(mediaApiData, "contents") };
+ }
+ /**
+ * 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 GroupingShelfController.secondaryGroupingShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters);
+ }
+ /**
+ * 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 Metrics
+ shouldImpressShelf() {
+ return false;
+ }
+ // 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 (groupingParseContext.shelves.length !== 0) {
+ return null;
+ }
+ const shelf = new models.Shelf("heroCarousel");
+ const mediaApiData = shelfToken.featuredContentData;
+ const heroCarouselMetricsOptions = {
+ targetType: "swoosh",
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(mediaApiData),
+ };
+ const heroCarousel = new models.HeroCarousel();
+ heroCarousel.autoScrollConfiguration = {
+ isAutoScrollEnabled: objectGraph.bag.heroCarouselAutoScrollDuration > 0.0,
+ autoScrollInterval: objectGraph.bag.heroCarouselAutoScrollDuration,
+ };
+ const heroCarouselImpressionOptions = metricsHelpersImpressions.impressionOptions(objectGraph, mediaApiData, "heroCarousel", heroCarouselMetricsOptions);
+ heroCarouselImpressionOptions.autoAdvanceInterval = heroCarousel.autoScrollConfiguration.autoScrollInterval;
+ metricsHelpersImpressions.addImpressionFields(objectGraph, shelf, heroCarouselImpressionOptions);
+ metricsHelpersLocation.pushContentLocation(objectGraph, heroCarouselImpressionOptions, "heroCarousel");
+ for (const itemData of shelfData.shelfContents) {
+ if (serverData.isNull(itemData.attributes) || groupingShelfControllerCommon.shouldDefer(shelfToken)) {
+ shelfToken.isDeferring = true;
+ shelfToken.remainingItems.push(itemData);
+ continue;
+ }
+ const primaryContent = content.primaryContentForData(objectGraph, itemData);
+ if (breakoutsCommon.requiresPrimaryContent(objectGraph, itemData) &&
+ !mediaAttributes.hasAttributes(primaryContent)) {
+ shelfToken.isDeferring = true;
+ shelfToken.remainingItems.push(itemData);
+ shelfToken.relationshipToFetch = "primary-content";
+ continue;
+ }
+ const heroCarouselItemMetricsOptions = {
+ targetType: "largeBreakout",
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(itemData),
+ };
+ const heroVideoData = heroCommon.heroVideoFromData(objectGraph, itemData);
+ const heroArtworkData = heroCommon.heroArtworkFromData(objectGraph, itemData);
+ if (serverData.isNullOrEmpty(heroVideoData.video) && serverData.isNullOrEmpty(heroArtworkData.artwork)) {
+ continue;
+ }
+ const heroCarouselItem = new models.HeroCarouselItem();
+ const productData = article.productDataFromArticle(objectGraph, itemData);
+ const metricsTitle = heroCarouselOverlayCommon.heroCarouselItemTitleFromData(objectGraph, itemData);
+ const heroCarouselItemImpressionOptions = metricsHelpersImpressions.impressionOptions(objectGraph, itemData, metricsTitle, heroCarouselItemMetricsOptions);
+ heroCarouselItemImpressionOptions.isPreorder = contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, productData, "isPreorder");
+ metricsHelpersImpressions.addImpressionFields(objectGraph, heroCarouselItem, heroCarouselItemImpressionOptions);
+ // Push the heroCarouselItem here so that the click action has the item in its location
+ // but we do not want to add it to the overall location tracker, so pop it right after adding it to the button
+ // action
+ // <rdar://problem/60883269> Metrics: Arcade: Container values requested in Location field
+ metricsHelpersLocation.pushContentLocation(objectGraph, heroCarouselItemImpressionOptions, metricsTitle);
+ const titleEffectArtwork = heroVideoData.artworkData || heroArtworkData.artworkData;
+ const backgroundColor = heroVideoData.backgroundColor || heroArtworkData.backgroundColor;
+ const overlayRequirements = {
+ metricsPageInformation: shelfToken.metricsPageInformation,
+ metricsLocationTracker: shelfToken.metricsLocationTracker,
+ canDisplayArcadeOfferButton: content.shelfContentTypeCanDisplayArcadeOfferButtons(objectGraph, shelfToken.shelfStyle),
+ lockupArtworkUseCase: content.artworkUseCaseFromShelfStyle(objectGraph, shelfToken.shelfStyle),
+ isContainedInPreorderExclusiveShelf: shelfToken.featuredContentId === 497 /* groupingTypes.FeaturedContentID.AppStore_ComingSoon */,
+ };
+ heroCarouselItem.overlay = heroCarouselOverlayCommon.overlayFromData(objectGraph, itemData, overlayRequirements);
+ heroCarouselItem.backgroundColor = backgroundColor;
+ if (!objectGraph.client.isMac || objectGraph.props.isNotEnabled("macOSArcadeHeaderUpdates")) {
+ heroCarouselItem.titleEffect = breakoutsCommon.titleEffectFromArtwork(objectGraph, titleEffectArtwork);
+ }
+ heroCarouselItem.artwork = heroArtworkData.artwork;
+ heroCarouselItem.video = heroVideoData.video;
+ if (objectGraph.client.isWeb) {
+ const portraitVideo = heroCommon.heroVideoFromData(objectGraph, itemData, false, true);
+ heroCarouselItem.portraitVideo = portraitVideo === null || portraitVideo === void 0 ? void 0 : portraitVideo.video;
+ }
+ const heroCarouselItemAction = breakoutsCommon.actionFromData(objectGraph, itemData);
+ const heroCarouselItemClickOptions = {
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(itemData),
+ targetType: "largeBreakout",
+ id: itemData.id,
+ };
+ if (heroCarouselItemAction) {
+ metricsHelpersClicks.addClickEventToAction(objectGraph, heroCarouselItemAction, heroCarouselItemClickOptions);
+ heroCarouselItem.clickAction = heroCarouselItemAction;
+ }
+ heroCarousel.items.push(heroCarouselItem);
+ metricsHelpersLocation.popLocation(heroCarouselItemImpressionOptions.locationTracker);
+ metricsHelpersLocation.nextPosition(heroCarouselItemImpressionOptions.locationTracker);
+ }
+ if (serverData.isDefinedNonNullNonEmpty(heroCarousel.items)) {
+ shelf.items = [heroCarousel];
+ groupingParseContext.pageTitleEffect = heroCarousel.items[0].titleEffect;
+ }
+ shelf.url = groupingShelfControllerCommon.createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, shelfToken);
+ metricsHelpersLocation.popLocation(heroCarouselImpressionOptions.locationTracker);
+ metricsHelpersLocation.nextPosition(heroCarouselImpressionOptions.locationTracker);
+ return shelf;
+ }
+}
+//# sourceMappingURL=grouping-hero-carousel-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-horizontal-card-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-horizontal-card-shelf-controller.js
new file mode 100644
index 0000000..d02a4b6
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-horizontal-card-shelf-controller.js
@@ -0,0 +1,122 @@
+import * as models from "../../../api/models";
+import * as mediaAttributes from "../../../foundation/media/attributes";
+import * as mediaRelationship from "../../../foundation/media/relationships";
+import * as todayHorizontalCardUtil from "../../today/today-horizontal-card-util";
+import { TodayParseContext } from "../../today/today-types";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+export class GroupingHorizontalCardShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingHorizontalCardShelfController");
+ this.supportedFeaturedContentIds = new Set([475 /* groupingTypes.FeaturedContentID.AppStore_HorizontalCardSwoosh */]);
+ }
+ // endregion
+ // region GroupingShelfController
+ _supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId) {
+ if (!super._supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId)) {
+ return false;
+ }
+ const displayStyle = mediaAttributes.attributeAsString(mediaApiData, "displayStyle");
+ const contentType = groupingShelfControllerCommon.contentTypeForHorizontalCardDisplayStyle(objectGraph, displayStyle);
+ return contentType !== "editorialStoryCard";
+ }
+ // 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: mediaRelationship.relationshipCollection(mediaApiData, "contents") };
+ }
+ /**
+ * 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 GroupingShelfController.secondaryGroupingShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters);
+ }
+ /**
+ * 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) {
+ const shelfToken = { ...baseShelfToken };
+ const displayStyle = mediaAttributes.attributeAsString(mediaApiData, "displayStyle");
+ shelfToken.shelfStyle = groupingShelfControllerCommon.contentTypeForHorizontalCardDisplayStyle(objectGraph, displayStyle);
+ return shelfToken;
+ }
+ // 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) {
+ var _a;
+ const cardUnavailable = function (cardData) {
+ shelfToken.remainingItems.push(cardData);
+ return false;
+ };
+ let shelf;
+ if (objectGraph.client.isiOS && objectGraph.featureFlags.isEnabled("mini_today_cards_grouping")) {
+ const todayParseContext = new TodayParseContext(shelfToken.metricsPageInformation, shelfToken.metricsLocationTracker);
+ shelf = todayHorizontalCardUtil.shelfForMiniTodayCards(objectGraph, (_a = shelfData.shelfContents) !== null && _a !== void 0 ? _a : [], shelfToken.title, shelfToken.subtitle, todayParseContext);
+ }
+ else {
+ const cardContext = {
+ metricsLocationTracker: shelfToken.metricsLocationTracker,
+ metricsPageInformation: shelfToken.metricsPageInformation,
+ };
+ let resolvedContentType;
+ // First check if the client explicitly supports small story cards.
+ // If it does, don't change the content type.
+ const isSmallStoryCardsSupported = objectGraph.host.isiOS || objectGraph.host.isMac || objectGraph.host.isWeb;
+ if (isSmallStoryCardsSupported && shelfToken.shelfStyle === "smallStoryCard") {
+ resolvedContentType = shelfToken.shelfStyle;
+ }
+ else {
+ switch (objectGraph.client.deviceType) {
+ case "mac":
+ case "tv":
+ case "web":
+ resolvedContentType = shelfToken.shelfStyle;
+ break;
+ case "watch":
+ resolvedContentType = "todayCard";
+ break;
+ default:
+ resolvedContentType = "todayBrick";
+ break;
+ }
+ }
+ shelf = todayHorizontalCardUtil.shelfForHorizontalCardItems(objectGraph, shelfData.shelfContents, resolvedContentType, shelfToken.title, shelfToken.subtitle, cardContext, cardUnavailable);
+ if (shelf.contentType === "smallStoryCard" && Array.isArray(shelf.items)) {
+ shelf.items = shelf.items.filter((item) => {
+ if (!(item instanceof models.TodayCard)) {
+ return true;
+ }
+ return todayHorizontalCardUtil.isHorizontalCardSupportedForKind(objectGraph, item.media.kind, "smallStoryCard");
+ });
+ }
+ }
+ shelf.url = groupingShelfControllerCommon.createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, shelfToken);
+ shelf.isHorizontal = true;
+ return shelf;
+ }
+}
+//# sourceMappingURL=grouping-horizontal-card-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-large-breakout-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-large-breakout-shelf-controller.js
new file mode 100644
index 0000000..7b14c97
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-large-breakout-shelf-controller.js
@@ -0,0 +1,193 @@
+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 breakoutsCommon from "../../arcade/breakouts-common";
+import * as contentAttributes from "../../content/attributes";
+import * as content from "../../content/content";
+import * as flowPreview from "../../content/flow-preview";
+import * as metricsHelpersClicks from "../../metrics/helpers/clicks";
+import * as metricsHelpersImpressions from "../../metrics/helpers/impressions";
+import * as metricsHelpersLocation from "../../metrics/helpers/location";
+import * as article from "../../today/article";
+import * as heroCommon from "../hero/hero-common";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+export class GroupingLargeBreakoutShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor(builderClass = null) {
+ super(builderClass || "GroupingLargeBreakoutShelfController");
+ this.supportedFeaturedContentIds = new Set([480 /* groupingTypes.FeaturedContentID.AppStore_Breakout */]);
+ }
+ // endregion
+ // region GroupingShelfController
+ _supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId) {
+ if (!super._supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId)) {
+ return false;
+ }
+ const breakoutStyle = mediaAttributes.attributeAsString(mediaApiData, "displayStyle");
+ return breakoutStyle === "large";
+ }
+ // 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: mediaRelationship.relationshipCollection(mediaApiData, "contents") };
+ }
+ /**
+ * 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 GroupingShelfController.secondaryGroupingShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters);
+ }
+ /**
+ * 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 Metrics
+ shouldImpressShelf() {
+ return false;
+ }
+ // endregion
+ // region Shelf Creation
+ isInHeroPosition() {
+ return false;
+ }
+ /**
+ *
+ * @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) {
+ const isFirstShelf = serverData.isDefinedNonNullNonEmpty(groupingParseContext) &&
+ serverData.isNullOrEmpty(groupingParseContext.pageTitleEffect) &&
+ groupingParseContext.shelves.length === 0;
+ const trimmedShelfContents = serverData.isDefinedNonNullNonEmpty(shelfData.shelfContents)
+ ? [shelfData.shelfContents[0]]
+ : [];
+ const items = [];
+ for (const data of trimmedShelfContents) {
+ if (serverData.isNull(data.attributes) || groupingShelfControllerCommon.shouldDefer(shelfToken)) {
+ shelfToken.isDeferring = true;
+ shelfToken.remainingItems.push(data);
+ continue;
+ }
+ const metricsOptions = {
+ targetType: this.isInHeroPosition() ? "heroBreakout" : "largeBreakout",
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(data),
+ };
+ const heroVideoData = heroCommon.heroVideoFromData(objectGraph, data);
+ const heroArtworkData = heroCommon.heroArtworkFromData(objectGraph, data);
+ const titleEffectArtwork = heroVideoData.artworkData || heroArtworkData.artworkData;
+ const largeHeroBreakout = GroupingLargeBreakoutShelfController.createLargeBreakout(objectGraph, data, shelfToken, this.isInHeroPosition(), isFirstShelf, metricsOptions);
+ if (serverData.isNullOrEmpty(largeHeroBreakout)) {
+ continue;
+ }
+ items.push(largeHeroBreakout);
+ if (isFirstShelf) {
+ groupingParseContext.pageTitleEffect = breakoutsCommon.titleEffectFromArtwork(objectGraph, titleEffectArtwork);
+ }
+ }
+ const shelf = new models.Shelf("largeHeroBreakout");
+ shelf.isHorizontal = false;
+ shelf.items = items;
+ shelf.url = groupingShelfControllerCommon.createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, shelfToken);
+ if (groupingParseContext.shelves.length === 0) {
+ shelf.presentationHints = { isFirstShelf: true };
+ }
+ return shelf;
+ }
+ static createLargeBreakout(objectGraph, data, shelfToken, isHeroPosition, isFirstShelf, metricsOptions) {
+ const primaryContent = content.primaryContentForData(objectGraph, data);
+ if (breakoutsCommon.requiresPrimaryContent(objectGraph, data) &&
+ !mediaAttributes.hasAttributes(primaryContent)) {
+ shelfToken.isDeferring = true;
+ shelfToken.remainingItems.push(data);
+ shelfToken.relationshipToFetch = "primary-content";
+ return null;
+ }
+ const heroVideoData = heroCommon.heroVideoFromData(objectGraph, data);
+ const heroArtworkData = heroCommon.heroArtworkFromData(objectGraph, data);
+ if (serverData.isNullOrEmpty(heroVideoData.video) && serverData.isNullOrEmpty(heroArtworkData.artwork)) {
+ return null;
+ }
+ const backgroundColor = heroVideoData.backgroundColor || heroArtworkData.backgroundColor;
+ const heading = isHeroPosition ? null : mediaAttributes.attributeAsString(data, "breakoutBadge");
+ // If this EI can and should show the expected release date of an app, override the badge.
+ let badgeTitle;
+ const fallbackLabel = mediaAttributes.attributeAsString(data, "label");
+ if (contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, data, "showExpectedReleaseDate")) {
+ badgeTitle = objectGraph.loc.uppercased(content.dynamicPreorderDateFromData(objectGraph, primaryContent, fallbackLabel));
+ }
+ else {
+ badgeTitle = fallbackLabel;
+ }
+ let badge = { type: "none" };
+ if ((badgeTitle === null || badgeTitle === void 0 ? void 0 : badgeTitle.length) > 0) {
+ badge = {
+ type: "text",
+ title: badgeTitle,
+ };
+ }
+ const title = content.editorialNotesFromData(objectGraph, data, "name") ||
+ contentAttributes.contentAttributeAsString(objectGraph, primaryContent, "name");
+ const description = content.editorialNotesFromData(objectGraph, data, "short") ||
+ contentAttributes.contentAttributeAsString(objectGraph, primaryContent, "tagline");
+ const backgroundStyle = breakoutsCommon.detailBackgroundStyleFromData(objectGraph, data, true, isFirstShelf, false);
+ const detailPosition = breakoutsCommon.detailPositionFromData(objectGraph, data, isFirstShelf, false);
+ const details = new models.BreakoutDetails(title, description, badge, null, backgroundStyle, breakoutsCommon.detailTextAlignmentForDetailPosition(objectGraph, detailPosition, data));
+ const largeHeroBreakout = new models.LargeHeroBreakout(details, {
+ position: detailPosition || "leading",
+ }, heading, heroArtworkData.artwork, heroVideoData.video, null, backgroundColor);
+ const impressionOptions = metricsHelpersImpressions.impressionOptions(objectGraph, data, largeHeroBreakout.details.title, metricsOptions);
+ const productData = article.productDataFromArticle(objectGraph, data);
+ const isPreorder = contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, productData, "isPreorder");
+ impressionOptions.isPreorder = isPreorder;
+ metricsHelpersImpressions.addImpressionFields(objectGraph, largeHeroBreakout, impressionOptions);
+ // Push the breakout here so that the click action has the breakout in its location
+ // but we do not want to add it to the overall location tracker, so pop it right after adding it to the button
+ // action
+ // <rdar://problem/60883269> Metrics: Arcade: Container values requested in Location field
+ metricsHelpersLocation.pushContentLocation(objectGraph, impressionOptions, largeHeroBreakout.details.title);
+ const breakoutAction = breakoutsCommon.actionFromData(objectGraph, data);
+ const breakoutClickOptions = {
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(data),
+ targetType: "button",
+ id: data.id,
+ };
+ metricsHelpersClicks.addClickEventToAction(objectGraph, breakoutAction, breakoutClickOptions);
+ largeHeroBreakout.details.callToActionButtonAction = breakoutAction;
+ largeHeroBreakout.clickAction = breakoutAction;
+ metricsHelpersLocation.popLocation(metricsOptions.locationTracker);
+ // Set flow preview actions
+ largeHeroBreakout.flowPreviewActionsConfiguration =
+ flowPreview.flowPreviewActionsConfigurationForProductFromData(objectGraph, data, true, shelfToken.clientIdentifierOverride, breakoutAction, metricsOptions, breakoutClickOptions);
+ return largeHeroBreakout;
+ }
+}
+//# sourceMappingURL=grouping-large-breakout-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-link-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-link-shelf-controller.js
new file mode 100644
index 0000000..09865f5
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-link-shelf-controller.js
@@ -0,0 +1,169 @@
+import { isNothing } from "@jet/environment";
+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 { createArtworkForResource } from "../../content/artwork/artwork";
+import * as lockupsEditorialContext from "../../lockups/editorial-context";
+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";
+export class GroupingLinkShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingLinkShelfController");
+ this.supportedFeaturedContentIds = new Set([
+ 437 /* groupingTypes.FeaturedContentID.AppStore_LinkList */,
+ 265 /* groupingTypes.FeaturedContentID.Sundance_LinkList */,
+ ]);
+ }
+ // 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) {
+ let shelfContents = mediaRelationship.relationshipCollection(mediaApiData, "children");
+ if (serverData.isNullOrEmpty(shelfContents)) {
+ shelfContents = mediaAttributes.attributeAsArrayOrEmpty(mediaApiData, "links");
+ }
+ return { shelfContents: shelfContents };
+ }
+ /**
+ * 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 GroupingShelfController.secondaryGroupingShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters);
+ }
+ /**
+ * 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) {
+ const linkShelfToken = {
+ ...baseShelfToken,
+ shouldHideShelf: mediaAttributes.attributeAsBooleanOrFalse(mediaApiData, "hide"),
+ areContentLinks: serverData.isDefinedNonNullNonEmpty(mediaRelationship.relationshipCollection(mediaApiData, "children")),
+ };
+ linkShelfToken.clientIdentifierOverride = lockupsEditorialContext.clientIdentifierForEditorialContextInData(objectGraph, mediaApiData);
+ return linkShelfToken;
+ }
+ // 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.shouldHideShelf) {
+ return null;
+ }
+ const items = [];
+ for (let linkIndex = 0; linkIndex < shelfData.shelfContents.length; linkIndex++) {
+ const link = shelfData.shelfContents[linkIndex];
+ const metricsBase = {
+ targetType: "link",
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ };
+ if (shelfToken.areContentLinks) {
+ metricsBase.recoMetricsData = mediaDataStructure.metricsFromMediaApiObject(link);
+ }
+ let action = null;
+ if (shelfToken.isSearchLandingPage) {
+ const searchAdAction = this.trendingSearchLinkFromData(objectGraph, link, shelfToken.metricsLocationTracker);
+ if (serverData.isNull(searchAdAction) || serverData.isNull(searchAdAction.action)) {
+ continue;
+ }
+ metricsHelpersImpressions.addImpressionFields(objectGraph, searchAdAction === null || searchAdAction === void 0 ? void 0 : searchAdAction.action, {
+ ...metricsBase,
+ kind: "link",
+ softwareType: serverData.asBooleanOrFalse(groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.isArcadePage) ? "Arcade" : null,
+ title: searchAdAction.action.title,
+ id: `${linkIndex}`,
+ idType: "sequential",
+ });
+ action = searchAdAction;
+ }
+ else {
+ const metadata = groupingShelfControllerCommon.metadataForFCData(objectGraph, link, shelfToken, false, null, metricsBase, null);
+ if (serverData.isNullOrEmpty(metadata) || serverData.isNullOrEmpty(metadata.action.title)) {
+ continue;
+ }
+ action = metadata.action;
+ const contentId = groupingShelfControllerCommon.contentIdFromContentItem(objectGraph, link);
+ if (contentId) {
+ metricsHelpersImpressions.addImpressionFields(objectGraph, action, {
+ ...metricsBase,
+ kind: "link",
+ softwareType: serverData.asBooleanOrFalse(groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.isArcadePage) ? "Arcade" : null,
+ title: action.title,
+ id: contentId,
+ });
+ }
+ }
+ if (serverData.isNullOrEmpty(action)) {
+ continue;
+ }
+ items.push(action);
+ metricsHelpersLocation.nextPosition(shelfToken.metricsLocationTracker);
+ }
+ if (serverData.isNullOrEmpty(items)) {
+ return null;
+ }
+ const actionsShouldHaveArtwork = shelfToken.isSearchLandingPage &&
+ (objectGraph.client.isPhone || objectGraph.client.isVision) &&
+ items.length >= 6;
+ if (actionsShouldHaveArtwork) {
+ for (const item of items) {
+ if (item instanceof models.SearchAdAction) {
+ item.action.artwork = createArtworkForResource(objectGraph, "systemimage://magnifyingglass");
+ }
+ }
+ }
+ const shelf = new models.Shelf("action");
+ shelf.isHorizontal = false;
+ shelf.items = items;
+ shelf.title = shelfToken.title;
+ shelf.subtitle = shelfToken.subtitle;
+ shelf.presentationHints = { isWidthConstrained: true };
+ shelf.url = groupingShelfControllerCommon.createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, shelfToken);
+ if (shelfToken.isSearchLandingPage && (objectGraph.client.isPhone || objectGraph.client.isPad)) {
+ shelf.contentsMetadata = {
+ type: "searchLandingTrendingSection",
+ numberOfColumns: items.length >= 6 ? 2 : 1,
+ };
+ }
+ return shelf;
+ }
+ // region Helpers
+ trendingSearchLinkFromData(objectGraph, link, locationTracker) {
+ const term = serverData.asString(link, "label");
+ if (isNothing(term) || term.length === 0) {
+ return null;
+ }
+ const searchAction = new models.SearchAction(term, term, null, "suggested");
+ metricsHelpersClicks.addEventsToSearchAction(objectGraph, searchAction, "button", locationTracker);
+ metricsHelpersLocation.nextPosition(locationTracker);
+ return new models.SearchAdAction(searchAction);
+ }
+}
+//# sourceMappingURL=grouping-link-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-lockup-shelf-controller-common.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-lockup-shelf-controller-common.js
new file mode 100644
index 0000000..5c9273f
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-lockup-shelf-controller-common.js
@@ -0,0 +1,608 @@
+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:
+ // <rdar://problem/50230334> 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:
+ // <rdar://problem/50671635> 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 \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-lockup-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-lockup-shelf-controller.js
new file mode 100644
index 0000000..211db36
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-lockup-shelf-controller.js
@@ -0,0 +1,104 @@
+import * as serverData from "../../../foundation/json-parsing/server-data";
+import * as mediaRelationship from "../../../foundation/media/relationships";
+import * as mediaAttributes from "../../../foundation/media/attributes";
+import * as groupingTypes from "../grouping-types";
+import { createLockupShelf, lockupShelfTokenFromBaseTokenAndMediaApiData, } from "./grouping-lockup-shelf-controller-common";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+export class GroupingLockupShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor(builderClass = null) {
+ super(builderClass || "GroupingLockupShelfController");
+ this.supportedFeaturedContentIds = new Set([
+ ...groupingTypes.topChartFeaturedContentIds,
+ ...groupingTypes.lockupShelfFeaturedContentIds,
+ ]);
+ }
+ // 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) {
+ const shelfContents = mediaRelationship.relationship(mediaApiData, "contents");
+ let shelfData = shelfContents ? shelfContents.data : null;
+ if (!shelfData || shelfData.length === 0) {
+ shelfData = mediaRelationship.relationshipCollection(mediaApiData, "children");
+ }
+ return { shelfContents: shelfData };
+ }
+ /**
+ * 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 GroupingShelfController.secondaryGroupingShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters);
+ }
+ /**
+ * 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 lockupShelfTokenFromBaseTokenAndMediaApiData(objectGraph, mediaApiData, baseShelfToken, groupingParseContext);
+ }
+ // endregion
+ // region Metrics
+ /**
+ * Return the shelf metrics options to use for this specific shelf. Using the base options from the grouping
+ * page controller
+ *
+ * @param objectGraph The App Store dependency graph
+ * @param shelfToken The shelf shelfToken for this current shelf creation request
+ * @param baseMetricsOptions The minimum set of metrics options for this shelf, created by the
+ * grouping page controller
+ */
+ shelfMetricsOptionsFromBaseMetricsOptions(objectGraph, shelfToken, baseMetricsOptions) {
+ const shelfMetricsOptions = { ...baseMetricsOptions };
+ shelfMetricsOptions.displayStyle = shelfToken.shelfStyle;
+ // Reconfigure Category Breakout metrics options
+ if (shelfToken.featuredContentId === 557 /* groupingTypes.FeaturedContentID.AppStore_CategoryBreakoutMarker */) {
+ const seeAllContents = mediaRelationship.relationshipData(objectGraph, shelfToken.featuredContentData, "see-all-contents");
+ const editorialNotes = mediaAttributes.attributeAsDictionary(seeAllContents, "editorialNotes");
+ const title = serverData.asString(editorialNotes, "name");
+ shelfMetricsOptions.title = title;
+ shelfMetricsOptions.idType = "its_contentId";
+ shelfMetricsOptions.badges = { forYou: true };
+ shelfMetricsOptions.targetType = "swooshBreakout";
+ }
+ return shelfMetricsOptions;
+ }
+ // 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) {
+ const isCategoryBreakoutShelf = shelfToken.featuredContentId === 557 /* groupingTypes.FeaturedContentID.AppStore_CategoryBreakoutMarker */;
+ if (isCategoryBreakoutShelf) {
+ const hasNoContents = !serverData.isDefinedNonNullNonEmpty(shelfData.shelfContents);
+ const isClientPad = objectGraph.client.isPad;
+ // Drop Category Breakout IF
+ // - It has no contents OR
+ // - Client is an iPad OR
+ if (hasNoContents || isClientPad) {
+ return null;
+ }
+ }
+ return createLockupShelf(objectGraph, shelfToken, shelfData, groupingParseContext);
+ }
+}
+//# sourceMappingURL=grouping-lockup-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-personalized-lockup-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-personalized-lockup-shelf-controller.js
new file mode 100644
index 0000000..7e28d30
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-personalized-lockup-shelf-controller.js
@@ -0,0 +1,368 @@
+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 mediaDataStructure from "../../../foundation/media/data-structure";
+import * as mediaNetwork from "../../../foundation/media/network";
+import * as mediaRelationship from "../../../foundation/media/relationships";
+import { ResponseMetadata } from "../../../foundation/network/network";
+import { Parameters, Path, Protocol } from "../../../foundation/network/url-constants";
+import * as urls from "../../../foundation/network/urls";
+import * as metricsHelpersImpressions from "../../metrics/helpers/impressions";
+import * as metricsHelpersLocation from "../../metrics/helpers/location";
+import * as odpCommon from "../../personalization/on-device-recommendations-common";
+import { defaultRoomShelfContentType } from "../../room/room-page";
+import { platformPrefersLargeTitles } from "../../room/room-common";
+import * as groupingTypes from "../grouping-types";
+import { createLockupShelf, lockupShelfTokenFromBaseTokenAndMediaApiData, } from "./grouping-lockup-shelf-controller-common";
+import { GroupingShelfController, routesForFeaturedContentIds } from "./grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+import { isNothing, isSome } from "@jet/environment";
+export class GroupingPersonalizedLockupShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingPersonalizedLockupShelfController");
+ this.supportedFeaturedContentIds = groupingTypes.recommendationsLockupShelfFeaturedContentIds;
+ }
+ // endregion
+ // region Shelf Builder
+ shelfRoute(objectGraph) {
+ return routesForFeaturedContentIds(this.supportedFeaturedContentIds, [
+ `${Parameters.isOnDeviceRecommendationsShelf}?`,
+ `${Parameters.onDeviceRecommendationsUseCase}?`,
+ ]);
+ }
+ // 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) {
+ const shelfContents = mediaRelationship.relationship(mediaApiData, "contents");
+ let shelfData = shelfContents ? shelfContents.data : null;
+ if (!shelfData || shelfData.length === 0) {
+ shelfData = mediaRelationship.relationshipCollection(mediaApiData, "children");
+ }
+ const recoMetrics = mediaDataStructure.metricsFromMediaApiObject(shelfContents);
+ return {
+ shelfContents: shelfData || [],
+ containsODPShelfContents: false,
+ recoMetrics: recoMetrics,
+ candidates: null,
+ isHiddenShelf: objectGraph.client.isWeb,
+ responseTimingValues: 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) {
+ var _a;
+ const onDeviceRecommendationsUseCase = parameters[Parameters.onDeviceRecommendationsUseCase];
+ if ((onDeviceRecommendationsUseCase === null || onDeviceRecommendationsUseCase === void 0 ? void 0 : onDeviceRecommendationsUseCase.length) > 0) {
+ return await odpCommon
+ .recommendedAppsForUseCase(objectGraph, onDeviceRecommendationsUseCase, "shelf")
+ .then((response) => {
+ return {
+ shelfContents: mediaDataStructure.dataCollectionFromDataContainer(response.dataContainer),
+ containsODPShelfContents: true,
+ recoMetrics: response.recoMetrics,
+ candidates: response.candidates,
+ isHiddenShelf: false,
+ };
+ })
+ .catch(async (error) => {
+ if (error instanceof odpCommon.NoODPCandidatesError) {
+ return await GroupingPersonalizedLockupShelfController.fetchODPFallbackContent(objectGraph, shelfUrl, shelfToken, parameters).catch((fallbackError) => {
+ return GroupingPersonalizedLockupShelfController.makeHiddenShelfData(shelfToken);
+ });
+ }
+ else {
+ return GroupingPersonalizedLockupShelfController.makeHiddenShelfData(shelfToken);
+ }
+ });
+ }
+ else if (((_a = shelfToken.recommendationsHref) === null || _a === void 0 ? void 0 : _a.length) > 0) {
+ return await GroupingShelfController.secondaryGroupingShelfMediaApiData(objectGraph, shelfUrl, shelfToken, parameters).then((mediaApiData) => {
+ const personalizedFeaturedContentData = mediaDataStructure.dataFromDataContainer(objectGraph, mediaApiData);
+ const personalizedLockupShelfData = this.initialShelfDataFromGroupingMediaApiData(objectGraph, personalizedFeaturedContentData);
+ personalizedLockupShelfData.responseTimingValues = mediaApiData[ResponseMetadata.timingValues];
+ personalizedLockupShelfData.shelfTitle = mediaAttributes.attributeAsString(personalizedFeaturedContentData, "name");
+ return personalizedLockupShelfData;
+ });
+ }
+ else {
+ return await GroupingShelfController.secondaryGroupingShelfMediaApiData(objectGraph, shelfUrl, shelfToken, parameters).then((mediaApiData) => {
+ const personalizedFeaturedContentData = mediaDataStructure.dataCollectionFromDataContainer(mediaApiData);
+ const personalizedLockupShelfData = {
+ shelfContents: personalizedFeaturedContentData || [],
+ containsODPShelfContents: false,
+ recoMetrics: null,
+ candidates: null,
+ isHiddenShelf: false,
+ responseTimingValues: mediaApiData[ResponseMetadata.timingValues],
+ };
+ personalizedLockupShelfData.shelfTitle = shelfToken.title;
+ return personalizedLockupShelfData;
+ });
+ }
+ }
+ /**
+ * 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) {
+ const personalizedLockupShelfToken = lockupShelfTokenFromBaseTokenAndMediaApiData(objectGraph, mediaApiData, baseShelfToken, groupingParseContext);
+ this.addPersonalizationValuesToShelfToken(objectGraph, personalizedLockupShelfToken, mediaApiData, groupingParseContext);
+ return personalizedLockupShelfToken;
+ }
+ // 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) {
+ var _a, _b;
+ if (!shelfToken.isValidRecommendationsShelf) {
+ return null;
+ }
+ let shelf = null;
+ if (isNothing(shelfToken.title)) {
+ // If we've fetched the title in a secondary lookup, update the title here
+ // in the token, since other places may rely on it.
+ shelfToken.title = shelfData.shelfTitle;
+ }
+ if (shelfData.containsODPShelfContents) {
+ shelf = this.personalizedShelf(objectGraph, shelfData, shelfToken, groupingParseContext);
+ const seeAllUrl = new urls.URL()
+ .set("protocol", Protocol.internal)
+ .path(`${Path.onDeviceRecommendations}`)
+ .param(Parameters.onDeviceRecommendationsUseCase, shelfToken.onDeviceRecommendationsUseCase)
+ .param(Parameters.token, JSON.stringify(shelfToken))
+ .build();
+ const seeAllAction = new models.FlowAction("page", seeAllUrl);
+ seeAllAction.title = objectGraph.loc.string("ACTION_SEE_ALL");
+ seeAllAction.pageData = this.odpSeeAllPage(objectGraph, shelfData.shelfTitle, defaultRoomShelfContentType);
+ groupingShelfControllerCommon.replaceShelfSeeAllAction(objectGraph, shelf, seeAllAction);
+ shelf.mergeWhenFetched = false;
+ }
+ else if (shelfData.isHiddenShelf) {
+ shelf = GroupingPersonalizedLockupShelfController.makeHiddenShelf(shelfToken);
+ }
+ else if (serverData.isDefinedNonNull(shelfToken.recommendationsHref)) {
+ shelf = this.personalizedShelf(objectGraph, shelfData, shelfToken, groupingParseContext);
+ shelf.url = this.addOnDeviceQueryParamsIfNecessary(objectGraph, shelf.url, shelfToken);
+ }
+ else {
+ shelf = createLockupShelf(objectGraph, shelfToken, shelfData, groupingParseContext);
+ shelf.url = this.addOnDeviceQueryParamsIfNecessary(objectGraph, shelf.url, shelfToken);
+ }
+ const isUsingHeader = isSome(shelf.header);
+ const isMissingHeaderTitle = isUsingHeader && serverData.isNullOrEmpty((_a = shelf.header) === null || _a === void 0 ? void 0 : _a.title);
+ const isMissingLegacyShelfTitle = !isUsingHeader && serverData.isNullOrEmpty(shelf.title);
+ const hasShelfDataTitle = ((_b = shelfData.shelfTitle) === null || _b === void 0 ? void 0 : _b.length) > 0;
+ if (isMissingHeaderTitle && hasShelfDataTitle) {
+ shelf.header.title = shelfData.shelfTitle;
+ }
+ else if (isMissingLegacyShelfTitle && hasShelfDataTitle) {
+ shelf.title = shelfData.shelfTitle;
+ }
+ return shelf;
+ }
+ // endregion
+ // region Helpers
+ /**
+ *
+ * @param objectGraph The App Store dependency graph
+ * @param shelfData The media api shelfContents array for this shelf
+ * @param shelfToken The shelf shelfToken for this current shelf creation request
+ * @param groupingParseContext The parse context used to generate the grouping page on the initial page load,
+ * @private
+ */
+ personalizedShelf(objectGraph, shelfData, shelfToken, groupingParseContext) {
+ /**
+ * Update shelf impressions for personalized shelves.
+ * <rdar://problem/69308786> reco_algo_id doesn't get passed to Figaro for out of band personalization marker
+ *
+ * By happenstance, the location tracker encoded in the shelf token usually contains the swoosh location, given the happy path fetches *more* items.
+ * For Personalized shelves, we need to pop the "empty" shelf location and replace it with the fully populated one.
+ */
+ const location = metricsHelpersLocation.currentLocation(shelfToken.metricsLocationTracker);
+ if (location && location.fcKind === shelfToken.featuredContentId) {
+ metricsHelpersLocation.popLocation(shelfToken.metricsLocationTracker);
+ }
+ const shelfMetricsOptions = {
+ id: shelfToken.id,
+ kind: null,
+ softwareType: serverData.asBooleanOrFalse(groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.isArcadePage) ? "Arcade" : null,
+ targetType: "swoosh",
+ title: shelfToken.title,
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ idType: "its_contentId",
+ fcKind: shelfToken.featuredContentId,
+ recoMetricsData: shelfData.recoMetrics,
+ displayStyle: shelfToken.shelfStyle,
+ };
+ metricsHelpersLocation.pushContentLocation(objectGraph, shelfMetricsOptions, shelfToken.title);
+ const shelf = createLockupShelf(objectGraph, shelfToken, shelfData, groupingParseContext);
+ metricsHelpersLocation.popLocation(shelfToken.metricsLocationTracker);
+ metricsHelpersImpressions.addImpressionFields(objectGraph, shelf, shelfMetricsOptions);
+ return shelf;
+ }
+ /**
+ * Check in the media api data for this shelf and add any of the necessary personalization fields to the shelfToken.
+ *
+ * @param objectGrpah
+ * @param shelfToken The shelf token in its current state
+ * @param mediaApiData The media api data for this section of the grouping page
+ * @param groupingParseContext The parse context for the grouping page so far
+ * @private
+ */
+ addPersonalizationValuesToShelfToken(objectGrpah, shelfToken, mediaApiData, groupingParseContext) {
+ const isPersonalizedShelfMarker = shelfToken.featuredContentId === 476 /* groupingTypes.FeaturedContentID.AppStore_PersonalizedShelfMarker */;
+ // If we don't have a logged in user don't show personalized shelves
+ // For search grouping we allow these shelves even if there is no logged in user.
+ // For the personalized shelf markers we are going to allow reco to send back a fallback list of ids so that
+ // even logged out users can see these shelves.
+ // rdar://64005675 (Client to not drop personalized marker response for non-signed users (dsId - 0))
+ if (!groupingParseContext.hasAuthenticatedUser &&
+ !shelfToken.isSearchLandingPage &&
+ !isPersonalizedShelfMarker) {
+ objectGrpah.console.log(`Skipping recommendations shelf with fcID ${shelfToken.featuredContentId}: No user logged-in`);
+ return;
+ }
+ const onDevicePersonalizationUseCase = mediaAttributes.attributeAsString(mediaApiData, "onDevicePersonalizationUseCase");
+ const usingOnDevicePersonalization = (onDevicePersonalizationUseCase === null || onDevicePersonalizationUseCase === void 0 ? void 0 : onDevicePersonalizationUseCase.length) > 0;
+ if (usingOnDevicePersonalization) {
+ shelfToken.onDeviceRecommendationsUseCase = onDevicePersonalizationUseCase;
+ shelfToken.recommendationsHref = mediaApiData.href;
+ shelfToken.isValidRecommendationsShelf = true;
+ return;
+ }
+ const shelfContents = mediaRelationship.relationshipCollection(mediaApiData, "contents");
+ const hasContents = serverData.isDefinedNonNullNonEmpty(shelfContents);
+ const shelfPersonalizationAvailable = !mediaAttributes.attributeAsBooleanOrFalse(mediaApiData, "noPersonalizationAvailable");
+ if (!hasContents && shelfPersonalizationAvailable) {
+ shelfToken.recommendationsHref = mediaApiData.href;
+ shelfToken.isValidRecommendationsShelf = true;
+ // tslint:disable-next-line:no-redundant-jump
+ }
+ else {
+ shelfToken.isValidRecommendationsShelf = hasContents;
+ }
+ }
+ /**
+ * For a given shelf url, add the on device personalization query param if its needed
+ *
+ * @param objectGraph
+ * @param urlString The original url string for this shelf
+ * @param shelfToken The shelf shelfToken for this current shelf creation request
+ */
+ addOnDeviceQueryParamsIfNecessary(objectGraph, urlString, shelfToken) {
+ var _a;
+ if (serverData.isNullOrEmpty(urlString)) {
+ return null;
+ }
+ const isOnDeviceRecommendationsGamesForYouEnabled = objectGraph.host.isiOS;
+ if (!isOnDeviceRecommendationsGamesForYouEnabled) {
+ return urlString;
+ }
+ if (((_a = shelfToken.onDeviceRecommendationsUseCase) === null || _a === void 0 ? void 0 : _a.length) > 0) {
+ const url = new urls.URL(urlString);
+ url.param(Parameters.isOnDeviceRecommendationsShelf, "true");
+ url.param(Parameters.onDeviceRecommendationsUseCase, shelfToken.onDeviceRecommendationsUseCase);
+ return url.build();
+ }
+ else {
+ return urlString;
+ }
+ }
+ /**
+ * Creates a page that can be used for side-packing see all pages into a room.
+ *
+ * @param objectGraph
+ * @param {string} title The title of the destination page
+ * @param {ShelfContentType} preferredShelfContentType The content type to use for the page
+ * @returns {GenericPage} A GenericPage which will use the parentShelfItems from the see all to render the initial room
+ */
+ odpSeeAllPage(objectGraph, title, preferredShelfContentType) {
+ const shelf = new models.Shelf(preferredShelfContentType || defaultRoomShelfContentType);
+ shelf.isHorizontal = false;
+ shelf.items = "parentShelfItems";
+ const page = new models.GenericPage([shelf]);
+ page.isIncomplete = true;
+ page.title = title;
+ if (platformPrefersLargeTitles(objectGraph)) {
+ page.presentationOptions = ["prefersLargeTitle"];
+ }
+ return page;
+ }
+ // endregion
+ // region ODP Fallback
+ static async fetchODPFallbackContent(objectGraph, url, shelfToken, parameters) {
+ return await new Promise((resolve, reject) => {
+ const fallbackRequest = groupingShelfControllerCommon.generateShelfRequest(objectGraph, shelfToken, parameters);
+ if (!fallbackRequest) {
+ const errorMessage = `OnDeviceRecommendationsShelfController: Could not construct media API request for: ${url}`;
+ validation.unexpectedType("defaultValue", errorMessage, null);
+ reject(new Error(errorMessage));
+ return;
+ }
+ groupingShelfControllerCommon.prepareGroupingShelfRequest(objectGraph, fallbackRequest);
+ fallbackRequest.attributingTo(url.build());
+ mediaNetwork
+ .fetchData(objectGraph, fallbackRequest)
+ .then((dataContainer) => {
+ const shelfData = mediaDataStructure.dataFromDataContainer(objectGraph, dataContainer);
+ const shelfContents = mediaRelationship.relationship(shelfData, "contents");
+ const recoMetrics = mediaDataStructure.metricsFromMediaApiObject(shelfContents);
+ const fallbackShelfData = {
+ shelfContents: mediaDataStructure.dataCollectionFromDataContainer(shelfContents),
+ containsODPShelfContents: false,
+ recoMetrics: recoMetrics,
+ candidates: null,
+ responseTimingValues: dataContainer[ResponseMetadata.timingValues],
+ };
+ resolve(fallbackShelfData);
+ })
+ .catch((error) => {
+ const errorMessage = `OnDeviceRecommendationsShelfController: Failed to fetch fallback shelf contents: ${url}`;
+ validation.unexpectedType("defaultValue", errorMessage, null);
+ reject(error);
+ });
+ });
+ }
+ static makeHiddenShelfData(shelfToken) {
+ const hiddenShelfData = {
+ shelfContents: [],
+ containsODPShelfContents: false,
+ recoMetrics: null,
+ candidates: null,
+ isHiddenShelf: true,
+ };
+ return hiddenShelfData;
+ }
+ static makeHiddenShelf(shelfToken) {
+ const hiddenShelf = new models.Shelf(shelfToken.shelfStyle);
+ hiddenShelf.isHidden = true;
+ return hiddenShelf;
+ }
+}
+//# sourceMappingURL=grouping-personalized-lockup-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-ribbon-bar-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-ribbon-bar-shelf-controller.js
new file mode 100644
index 0000000..bc0211c
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-ribbon-bar-shelf-controller.js
@@ -0,0 +1,269 @@
+import { attributeAsString } from "@apple-media-services/media-api";
+import { isNothing, isSome } from "@jet/environment";
+import * as models from "../../../api/models";
+import * as serverData from "../../../foundation/json-parsing/server-data";
+import * as mediaDataStructure from "../../../foundation/media/data-structure";
+import * as mediaRelationship from "../../../foundation/media/relationships";
+import { seeAllArcadeGamesPageFlowAction } from "../../arcade/arcade-common";
+import { pageRouter } from "../../builders/routing";
+import { hrefToRoutableUrl } from "../../builders/url-mapping";
+import { categoryArtworkData } from "../../categories";
+import * as artworkBuilder from "../../content/artwork/artwork";
+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 { areAppTagsEnabled } from "../../util/app-tags-util";
+import * as lottery from "../../util/lottery";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+export class GroupingRibbonBarShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingRibbonBarShelfController");
+ this.supportedFeaturedContentIds = new Set([556 /* groupingTypes.FeaturedContentID.AppStore_RibbonBarMarker */]);
+ }
+ // 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: mediaRelationship.relationshipCollection(mediaApiData, "contents") };
+ }
+ /**
+ * 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 GroupingShelfController.secondaryGroupingShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters);
+ }
+ /**
+ * 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) {
+ var _a;
+ const items = [];
+ const shelf = new models.Shelf("ribbonBar");
+ shelf.isHorizontal = true;
+ const isSAGUpliftEnabledForCurrentUser = lottery.isFeatureEnabledForCurrentUser(objectGraph, objectGraph.bag.arcadeCategoryBarSAGUpliftDisplayRate);
+ // Display See All Games facet when:
+ // - This is an Arcade page AND
+ // - This is the first render of the shelf AND
+ // - Bag is enabled for current user
+ if (shelfToken.isArcadePage && shelfToken.isFirstRender && isSAGUpliftEnabledForCurrentUser) {
+ const seeAllGamesRibbonItem = GroupingRibbonBarShelfController.createSeeAllGamesRibbonItem(objectGraph, shelfToken.metricsPageInformation, shelfToken.metricsLocationTracker);
+ items.push(seeAllGamesRibbonItem);
+ metricsHelpersLocation.nextPosition(shelfToken.metricsLocationTracker);
+ }
+ // If we have any hydrated items stored in the token, it means we found an unhydrated priorized item. In this
+ // case, we combine the initial hydrated items with the secondary hydrated items, and build the entire set
+ // of ribbon items from this list. As we are manipulating the order, we also need to replace the original
+ // shelf rather than merge.
+ let combinedShelfContents = (_a = shelfData.shelfContents) !== null && _a !== void 0 ? _a : [];
+ if (isSome(shelfToken.initialHydratedItems) && shelfToken.initialHydratedItems.length > 0) {
+ combinedShelfContents = shelfToken.initialHydratedItems.concat(combinedShelfContents);
+ }
+ // Tracks the initial set of hydrated items from the initial shelf load. This will be stored in the shelf
+ // token only if we find a prioritized item that is deferred.
+ const initialHydratedItems = [];
+ let isPrioritizedItemDeferred = false;
+ for (const ribbonData of combinedShelfContents) {
+ if (serverData.isNull(ribbonData.attributes) || groupingShelfControllerCommon.shouldDefer(shelfToken)) {
+ shelfToken.isDeferring = true;
+ shelfToken.remainingItems.push(ribbonData);
+ if (GroupingRibbonBarShelfController.shouldPriorizeItemWithData(objectGraph, ribbonData)) {
+ isPrioritizedItemDeferred = true;
+ }
+ continue;
+ }
+ let isTextOnly = false;
+ if (isSome(shelfToken.featuredContentData)) {
+ const displayStyle = attributeAsString(shelfToken.featuredContentData, "displayStyle");
+ isTextOnly = displayStyle === "textOnly";
+ }
+ const ribbonModel = GroupingRibbonBarShelfController.createRibbonItem(objectGraph, ribbonData, shelfToken.metricsPageInformation, shelfToken.metricsLocationTracker, isTextOnly, shelfToken, groupingParseContext);
+ if (isSome(ribbonModel)) {
+ if (GroupingRibbonBarShelfController.shouldPriorizeItemWithData(objectGraph, ribbonData)) {
+ items.unshift(ribbonModel);
+ }
+ else {
+ items.push(ribbonModel);
+ }
+ initialHydratedItems.push(ribbonData);
+ }
+ metricsHelpersLocation.nextPosition(shelfToken.metricsLocationTracker);
+ }
+ if (objectGraph.client.isiOS ||
+ objectGraph.featureFlags.isEnabled("shelves_2_0_arcade") ||
+ objectGraph.featureFlags.isEnabled("shelves_2_0_generic")) {
+ shelf.items = items;
+ if (isPrioritizedItemDeferred && initialHydratedItems.length > 0) {
+ shelfToken.initialHydratedItems = initialHydratedItems;
+ }
+ }
+ else {
+ // Only set `shelf.items` if there are any `items` present
+ // so that hydrated items reload when they are fetched.
+ if (items.length > 0) {
+ const ribbonBar = new models.RibbonBar(items);
+ shelf.items = [ribbonBar];
+ }
+ }
+ shelf.url = groupingShelfControllerCommon.createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, shelfToken);
+ return shelf;
+ }
+ // region Static Helpers
+ static createRibbonItem(objectGraph, itemData, metricsPageInformation, metricsLocationTracker, isTextOnly, shelfToken, groupingParseContext) {
+ var _a, _b, _c;
+ const metricsOptions = {
+ targetType: "facet",
+ pageInformation: metricsPageInformation,
+ locationTracker: metricsLocationTracker,
+ recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(itemData),
+ };
+ let metadata;
+ if (itemData.type === "tags") {
+ if (!areAppTagsEnabled(objectGraph, "grouping")) {
+ return null;
+ }
+ metadata = groupingShelfControllerCommon.metadataForTag(objectGraph, itemData, shelfToken, metricsOptions);
+ }
+ else {
+ metadata = groupingShelfControllerCommon.metadataForFCData(objectGraph, itemData, shelfToken, false, null, metricsOptions, groupingParseContext);
+ }
+ const metricsClickOptions = metricsHelpersClicks.clickOptionsForLockup(objectGraph, itemData, metricsOptions);
+ metricsClickOptions.targetType = metricsOptions.targetType;
+ const actionFromData = lockups.actionFromData(objectGraph, itemData, metricsClickOptions, shelfToken === null || shelfToken === void 0 ? void 0 : shelfToken.clientIdentifierOverride);
+ const action = (_a = metadata === null || metadata === void 0 ? void 0 : metadata.action) !== null && _a !== void 0 ? _a : actionFromData;
+ const editorialNotesName = (_b = content.editorialNotesFromData(objectGraph, itemData, "name")) !== null && _b !== void 0 ? _b : attributeAsString(itemData, "name");
+ const title = (_c = metadata === null || metadata === void 0 ? void 0 : metadata.title) !== null && _c !== void 0 ? _c : editorialNotesName;
+ const ribbonModel = new models.RibbonBarItem(title, action);
+ // Setup Artwork
+ const artworkDict = categoryArtworkData(objectGraph, itemData);
+ let artwork;
+ if (isTextOnly) {
+ artwork = null;
+ }
+ else {
+ if (isSome(artworkDict)) {
+ artwork = content.artworkFromApiArtwork(objectGraph, artworkDict, {
+ useCase: 29 /* content.ArtworkUseCase.RibbonBarFacet */,
+ });
+ }
+ else {
+ let resource;
+ if (shelfToken.isArcadePage) {
+ resource = "resource://arcade-ribbon-bar-fallback-icon";
+ }
+ else {
+ resource = "resource://appstore-ribbon-bar-fallback-icon";
+ }
+ artwork = artworkBuilder.createArtworkForResource(objectGraph, resource, 36, 36);
+ }
+ }
+ ribbonModel.artwork = artwork;
+ ribbonModel.accessibilityLabel = title;
+ // Configure impressions
+ const impressionOptions = metricsHelpersImpressions.impressionOptions(objectGraph, itemData, title, metricsOptions);
+ metricsHelpersImpressions.addImpressionFields(objectGraph, ribbonModel, impressionOptions);
+ if (!ribbonModel.isValid()) {
+ return null;
+ }
+ return ribbonModel;
+ }
+ /// Creates and returns Arcade See All Games ribbon item.
+ static createSeeAllGamesRibbonItem(objectGraph, metricsPageInformation, metricsLocationTracker) {
+ const title = objectGraph.loc.string("Arcade.CategoryBar.AllGames.Title");
+ const action = seeAllArcadeGamesPageFlowAction(objectGraph, "releaseDate", metricsPageInformation, metricsLocationTracker, title, "AllGames", "none", "facet");
+ const ribbonModel = new models.RibbonBarItem(title, action);
+ // Setup Artwork
+ // To update this artwork, run `cat artwork.png | base64` and prefix with `data:image/png;base64,`.
+ const resource = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIIAAACACAYAAADHy7H2AAABdWlDQ1BrQ0dDb2xvclNwYWNlRGlzcGxheVAzAAAokXWQvUvDUBTFT6tS0DqIDh0cMolD1NIKdnFoKxRFMFQFq1OafgltfCQpUnETVyn4H1jBWXCwiFRwcXAQRAcR3Zw6KbhoeN6XVNoi3sfl/Ticc7lcwBtQGSv2AijplpFMxKS11Lrke4OHnlOqZrKooiwK/v276/PR9d5PiFlNu3YQ2U9cl84ul3aeAlN//V3Vn8maGv3f1EGNGRbgkYmVbYsJ3iUeMWgp4qrgvMvHgtMunzuelWSc+JZY0gpqhrhJLKc79HwHl4plrbWD2N6f1VeXxRzqUcxhEyYYilBRgQQF4X/8044/ji1yV2BQLo8CLMpESRETssTz0KFhEjJxCEHqkLhz634PrfvJbW3vFZhtcM4v2tpCAzidoZPV29p4BBgaAG7qTDVUR+qh9uZywPsJMJgChu8os2HmwiF3e38M6Hvh/GMM8B0CdpXzryPO7RqFn4Er/QcXKWq8UwZBywAAAARjSUNQDA0AAW4D4+8AAAB4ZVhJZk1NACoAAAAIAAUBEgADAAAAAQABAAABGgAFAAAAAQAAAEoBGwAFAAAAAQAAAFIBKAADAAAAAQACAACHaQAEAAAAAQAAAFoAAAAAAAAASAAAAAEAAABIAAAAAQACoAIABAAAAAEAAACCoAMABAAAAAEAAACAAAAAACBAcQEAAAAJcEhZcwAACxMAAAsTAQCanBgAAAIGaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIgogICAgICAgICAgICB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4yMTk2PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjIxNjA8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KIUPY5gAAGmtJREFUeAHtXXuMJMV5r+qZ2Qe7h299bA7O3B0GGwOXBwEsbMXCC1H+QJjHgpbHEcyBEiLl4cgJxLKElIkUS4DtQCCSpZOSEAx3hjW+YwmclAfa4MQScfBL2Tg5ED5uyfFYw0Lu9nZvHl35/b7qmu3Z65numZ3dndnpupvt7np+31e/+qq6uuorpVKXSiCVQCqBVAKpBFIJpBJIJZBKIJVAKoF6EtD1ApcbZpRa0fxJHwpAMe3hVprfleS1ZRVlRkayqr8/ozbPa7Wh36gTW3118KBRw8NG7djR2sqamrJ0D73mqdmztRqY8QQKc8M+yirpfN5fSWgYA/zdOJarKnfoNQNabLkrwe/MjFbnnqvV9LRXkfGm+bLOT5ZaweuygWCuvLKXhOgDB060gqDl5mHyylNTY6SpoMfHy8vNL5ze5PPIe2pF8g6X08i9GRvrUacf0/qR5cm/aSCIUA5N9uhHJxcc4ebma7aoTHabMmYLNPZpaDcbAJE+hGfg13RZLn939Y0xnucVlPGPI/9ZlPUO7t9Qpb7XUfnHXDyza6QvTJ/zb+YqgD9woAAmRLuhAvpVdmG70t6ZyujTkecQaBnwld/jad0yXgNayyjjBCB+FGX8HH5vqkJxWo8/e9jxIoCYmfH1ZHMaoimCTR7dgJr0dV755o6xYVUo3YiKvgJEfgJyolAAANWjPKuxhdimSnJsLrm6jgY6GuVSHc/jbhbiP4xq+gGENqH37vtHpjK7dgEMj1bAuiSnRI8EgdN45pbRzyDX6wDyS5D4LFw/jDIHlNYefja/leCVOVt+i7gj2N/B7xXw+qLKZZ/Uj40LKAgINIYCwhpyDZNs7rorp3fvJjHK3Dx6p9LmbuXp86XSSajvs5oYTrXMSqIvZcRLSxwydCIH0lABymThw58UpMrl4xDQ8ypT/rJ+fOLV5WgGJ1gzduWwyvU+AAauA68bAQBwB5aMKYOeEoq2/LaQz4qwOCYBZ/ixZWVQak5D7Qjrvoj4Z/B/WD+x/yFcVRi4fE7imHlixwGhUz1m57V/CUK+IML3/XmQeYI1LjVkQDDIRNVL/ga1whsE0xvRws8n+yMWo8KFuxP6uWcXbiPhr0iDCRApi7JOUdlMRpXK00iyC9rhhWbA4Pg1t157AYh/Gt3eeQAZKxxdklQ8LsIjKygkS0un47eaboYt5WWRLysbKyOXOa+ShhLlP2SIH3lmmTkIfQCNERSZx1Qx+9vUCA7ACE/kyEAiZzgI4xsAnNl53UMqm/0CxF6EBkAfrUogrQe13YP7HpCHrgPjAotgkEgcoOO0fad9to8MQqDghF2rPCM9gYSfe+YVz4xHvyDfIF4GVwhDyu1FIHk6BhB8gJa7FTL/trn1+k9xrMBuAmGJHMdABL0Zu3obyj2gvAxB8AESUy2TPvLJH8smDSw3+JFGchPQKtfwPdPjORwuz6xOGw+PEkF4DvyQP8uB5pMye3GlvAENM6vK/hy6iM/72eJfw08JGKQL51O8o1ATOSCsH5nPm1tHb0eCR1E4+6HjATEWo4lyWoVI5Mq2nKKocd9Mqb7yp/XfTBwNd21JKDE7R19QWe9yAOs9xOcbAytbGkSS9Csex/JKFcOBbAYNdFCVS3ejm/g6/LTK53WS12kyFesCNTNvdl6FkbH5kiTQAQgokvYRi5DG/gEioIiyxi/PoZ3tUAven0hg4SA1VV1n7ro4xwgCeguC9/FIbWLFzsB2cVb27FuoHU5Aa6HT0F80O2/4OPyMOnIkll+ykggIiGcz0z03Ievz8eMoPFEBLGS1HWsrACdVbRndFwcPNxLI0kXw3bue2/2ynaQx5nehCRiTKjnIsl7CNQ0jJPqkm8hlPoLX6RtIDQf28qofQ1osEKANMuwSmA9e1H5d3g6UKUCwmBuIyX2tgwUR0oqL6HC3o9FcJiQtLHAME+lkgAhWzW2jv4gIv4IfB4bCq80uMlm7eLI+MYhExWhzBTW5JWwytp5jIyAjafnm86ObIMxz2bTg7LDfltK+fy1Q2UkUMd5C/24uFmLPss08kvDhYSuTkvokQM8xgaiEyLjt6ekFWuw8lfPPFBKPHI3FcDwQBgZsnEIZo2ezOQBCe4ogmiq+dPky8DbqYxJl06Xx6tIzO6Kza2NfO4eBhmvYdaOu/K1CrfsGUof0eCDMzVk0efoM5DOIH/vPWITVKXO1g0gr1CWclqlgJaPoyRrqkh+P6Hy9XUDfSZxaylGnmm9LPZiXxlQ/3FPjnPqqy0k8EAYGbPZKnYYsOXKmqqybKcLbz0mXZjaaO6/h9LfCnEg071uutl2BNsMSL5gUk/vO+MO6KXNGIqPVJpKMLt2+TNehP1oY4QRvv22BoD0KkGqns0RTBVnTr+Yx60h37FhVCL3YakLv3IP4tkXvTnPkCxqQF3Nqhfg/y5/EbyUMN/FAGBwMpGH6pJ/l9GhnQYH8BjzoHIa5diRtRwthWSiVt5qOb0pIgVlKbbuU6ljt/WSrO6gh+fKbiN54ICxmY+cN7IBk0bez7sCvtnxE0p23vjuwCESmjIl59x0gMkGbe9bjtZr0RoBQnbJLnoCIjuwfpHoaWAOSAiEW0J2sEWKZq0RIgVARRfRN56qDaH5q+aZAqCEZ+25Ud6BdI2VneqdAqFtv3aIPkrw+1hVUGrheJJBqhPVSk8vkIwXCMgW4XpKnQFgvNblMPlIgLFOA6yV5CoT1UpPL5CMFwjIFuF6Sp0BYLzW5TD5SICxTgOsleQqE9VKTy+Sju4DQPZ8OGoZFdwGhgU8Hss4P4uwW7HQXEBpuJ0zQwQtTGuA3BUIDwlrPUZMDwV8XLQOWG7z4DmKSVe747eAVSl7yxbfxQKgs+4YtBDqsjxVJxotTorfJH9fVl1WxYDe4vhpBWT5vuZqcxN4QE2zkcYCIiN++XpZf2q9I6OKB4JZ9e3oOwuFSf2ydIBwSlrDW0cKAhdE/1Z+z9pQqy/SrCUR0jBMF7tz42ylcLjJh+Q3Wmpu5xYD6d/FAOLrZCsMo2ggoQEpMExZv/RLaKVTDKtnMAi2ewAba0Ek8CADGxgKZwFobIdF5jnx5sl1PLM5JZdFgxkn8hlmLB4Lb+6j020jIVsJ9AXUzDRfQJvdYlS6V+q6zjqZmZ2ttXrH7HgxM2DFNJ+3jIIta9nlmxWCGljrDxp08OalbZ/FAGJgJMtBv4IatJD5Nm9R+QAbpB8286NfpJxZRxsejgRCMiXxP2VFEwD3TdYTjzm+Y+AHZ7wMM/ys019rwG2IovlJ7zrWbQgc3TQNVh4OWFYOvUAlrfSstBDt+YKUT28T/S8iZPZuW16KrOBg7QDA/oqUVOETtEGdHbtgA62UxxH0dw/ppofzcVthH2L27tGiASv8gkF8WBUS3qDaTmd3Harjf8V2A+HtC3uIO75OphS1n8SzkXkI/+xbQwrTRoDk59Zr5hAiELRvAWOvv673P0kor9HhgI7oOdbEaQVrO7CwrHq68Hy3r/3DD7fEwPkG/tnacBOD4gHz+u96z/0cQmFZnnVXTMil3Q7PrgLmg95DuOZ3xepCmRF5Dwm47pjkIgCOd/apYQgdRfpYeYgoogU3qWCAwM1g8PyF2B/dMvIiCngPiOKBKsOteUq/lH2qtHrRsvE+bB4WQsbG+0Nb3aNoqLShzP/rZBcgYecB0RnTs9vC13cIJmNfjtv/n1HkX/b0QNpKMvERAEMG99JKYnINhx7vxO4TZBNpLoBptx4ZCmggCDxZYqdof0Xue+SdnJhDPdR20QVmMiO15+hXg/R4YsuxHAlgdsR1N3cSrHCjCtwiF9Vt9GrTBDOrmXtFstMuc0Ix/IiCQN752iRnbb00cwSBqDGJBgd6HQIgTOgXPe6GNaVbRuXJJQ0AHbDNnswOqWN6LLuGPA1rswDcBYQQDoyHtX6lS8avI61T0MZQX8hdArDG/jgZcjRgLg3FwQwOot+vH9/04eDNKPLPYsLZzBp/NTaPnm4x5XGcyF0F9oiJgRl7DAB/JWn0wOD5YUbCJCGMYtNquzdf1E8+IoU1p4Qn6SqSvODKFjMmPglX2P4KQv4IfbRlyjEGgIIwVsurT0MAk/uG0AvQIOdRBBlr6FYwM7szt3fevYj0/P1l2tJP+OOcEGBevKjxQsbS1qNXO0buN9u+EMM6B9WDbfTC2AJaCopDwv6WOZBtmbMdIwUjJ1gnen43C24F/v+aYhjGtrcjE2iBMKsvgM4UK49y/hLt7UfTleMR5FEHBlldGY3T88L+lzvKLLDnwreSM+X5W9mF471XHyvfpCZgYXi0z/Y6KxVdKsE3DjtnSZyGDSxF+Pn4fAXE018uBC8HBltpqh/GJwakx+igynsH1Z3j+iSrr7+on9/2UhQld4+NFCGtZNYPEWo2N8U1C3jbMbVd/VJUzn0URF+J3NkJ/AddTUQrMC0Ej2YEbvFrmSD/VPFU/p/rfRBn/A2h+X/WbSdqYZknNgoBpF+HFpwadPcXlEE5xOflgDDnxZMg7RRVP6VWZEx5U17LKqiItA42Yy5RUrjyvtl00t/QtgK9MPF+qMp1clbj5BwEWvlG48yrCOSGMpgf7wWuupbyykIH+sppTBTUwcDxS1jipRs0NF924JkxX0vuWVA6EkAGROcXvEkNDpShBJSWomXgCyHfxVvPWIDVPWbVAC9SjQwDBsQin35dZAfXKqRUmA0HMjkr4zEzRnaFRK34S/5YAYWlBUjGc3+apZLOzK1KGfD3cglPkpnCGBL4boJBlqf+lPCR9DroNT9EA15EV5pen5o2M+PySuFb8JpVLGi+VQCqBVAKpBFIJpBJYHxJYkYFcOlhcAXBwaV0wWFz6utyK0loChPT1sbVHD8dVbNu9Pq7phFJ/L05lObGgtk1gQkk+NFXkl04oVUSR+KZpjVA1xcyDwjf2XYYp5k+h5PPwSzLFzLL57i9Xvo/jxj3DuzIv4OLRT1wQKXKKuYAp5t6VnmLeecPZmLe6DMQ0MsXs+JAvVHwIXBXPITm4cF4Zp/2mmDmzxnl30QgHf3gPiNyFejwHVdnAR6eKXAI+cRHn/Gs9O3/5umM/wbgPMfbjz/uQ2ve0b+7T33rmu4zNrqvZ6VdWDPPAH2NuueaX8SX6Xjxejt+m6I9OFQAzWcg5vsJXBrOOnV8oetWthPNP1UcnUMT1INOQxB5V6LkPPB5zdVOVPMEDM2/IVT5D3zZ6Po6gfVy182doo76m9+7/EhlsBgwEAQTEmsJn6Ou4puErWPTRiwoIfYaWcInDeKvkKp+hQWIOdYDP0OVXcLzEHfqJiX9b8c/Q7nxlc/M1l+CI3OchlGHj++GjUByw3HWV5FJphaEK4cKUTB9W7OwFGHaSkGbAIOluvfYBlcndo0ol7uvA52xZCsmyHJ/uyuir4Ryf0IE8cE9jraL+EACKownNGFZjPS8DSpxfCcJc3Lp0Jf48LJqA5yv/5vVnYGXSU/iwPIyVSh+gIAqB+fDHe/5W27lyQ3RAbZZKOC85cwuO9f2LgCCutUzkCBpGxGmwv6eyAgIu2uWaBpQhfVGb8Esc4FO/MVxsewq+gP8dzu6+UO9+uchP5+QhiSMzsU7GAoOD7I+w3sP/GhavfhRL1fgNnF/A1qLiSUk9R5rIm48zEKnGfx8rjH5DxjU447peQoY5zWFuuu4cCPirqljkfknml0hezGO1nAjftvl+0PpznctywcyfBzxgHIdP8glcMsampno52IIwP4M8P4fv7XaZFrHY3o78FaTFaPNFIXV8fEGAXY/uodesXDycg53JADhchqcS7Kevl+kKh9mteb3Qgsexcukq1Vu+SkqcTFZuLBAANlljINlpM4pxAU8Osy0kUe+TjJAVikWgYo8T1i8q9UmMbX4VHkYdOsSVzZGOIKFalQPRtQboRaNkk/W0kVmuimewWi6L67zOZqG4/WtYMNcquG6uHiGxQFB3XZyVg6a56kepi4OegN1EfNp6Ja9WGLUWV/na171PS7GVjb0RRLjT1U0Pl93x0FN2LaL52ln9OdpwxSY/n+rrEnPL1TyrEzu/Aw0nD9F/4iuzsMEOsLZs2oYstqIfsjm5kqPzbR9f2nPQ2H/Bv0rbY36HXsOCX1u5JxE6Pe1kciEGxQxuf73nmLDdA/Z5+iW812zHC8VWCTq4IZYHx7TL6uTr3HBQ5cUzEci181SzneRIP2gmDhTBrIIRdTTvFQMa5uOdxGSFVmu/4gS43YjxDWZ44bCqqSbwg4TRwggC5VLZMJrZjMy4KpkDxQAc4YhtfY/XbTYKc5osqiWpQ0O1eCd/5PCMQPt1Dq/ConTZJYAA7OJQd7oYIxmMUksYDLNugzsSWG2ERPBeqjsRCJYXowfVcB9XG2OH8MlrKaXVVOwmmKEACDZt5/wlcLG5hxfwAIc7Q2MZvK/l4oHwapDUNwPInHmybdneqFau7eQvUggI0qpXzRe5kzvybGh6C4PCn+Z8A9tYZzlb3cHOCj2QlPh4IFT6zOCDEiAgZdkCk5azpvFYuQEBGZXrsRMsH4sgybWakRFum2M8gN6yGxG7nb0sv+GPgDHUxgPBZeB1pEAc9e7KV6t4CI8wusO7fDV26Tvr6pvE9Zs4YmdJIKW2UQmkQIiV2LrQhLFcdhcQ4juFisAwOJDYbnBRCVinN90FhG6p1SbA2l1AaEJA3ZIkBUK31HQMnykQYgTULcEpELqlpmP4TIEQI6BuCU6B0C01HcNnCoQYAXVLcAqEbqnpGD5TIMQIqFuCUyB0S03H8JkCIUZA3RKcAqFGTWMtVld9mUiBUAMIzruBD5YuSUdeUyDEVlsHr1CK5W0xQgqERVlE3nXomkXLi04O4uRA8GU/A5eBdrK2xEYXY/ctRFZ73vrSrK8cm8f9csmFGZnlmnrW47WasHgguLOhPb1g1/lj6VbnDaQC8OJsJy3WTlRwqmO1NPJ2gChmdjSOD1bJF39WZ7SGT8H6ZcuJsccfJyAnHgib3ZHAPu0hYPNrh42nq8b+eh6G9GFVBK6yTF+e5A/QYipb5nl8sGwSWQzvkDtyjHrlRdO4h3V/mq+ShPN213ggLO4c5hmC3BnMTbF1M3WZt9VVKlW/7w65UDMz0Xs4jzxrN/362p6Z2Hnaj3XDA09xsTzI+suYDj0eCG7vo2/eRM7OSkonAYG0Cp8wlPAW7pW0epq7j3KzZ1uRaWNPve0kTi3l4AubkXy/gObKOlPqxjGP2i6KXednd/24p6jr3JwVWE/msCqZd6AuN9uxQlTktvTj8Naenq6DkQEO+dCPHMAYoI7TeqpOaHsG2YE8BsOG2/pgds+bFkKtfYQ6g+Qkm2Dt7melH9v3LiB1MOg3NTdAtr2zLYRvADQ2hfOE9MtC86GsVf9RDLguwy//ByzGua4wKmZb+qFafFiTI20/VUXvDSFySwvsI4jtpMAAFeT6z7TGAcT14L7c9i+SFqz8S+tih1SP+RcRTF8fLb5EOncsjt478RPw+GNEsqYA8NABjvv50A2SWPMCjYdZmmt0gyGG4scINrJVK8Xckyjkv6FyqHrqqppQGat+K/VvK46nt2YCyydP6Uf3vy+2IisCiiaN5oUlxJhv4OBP3pbr97DR+ayyLzlegMYegkGtN2A752mWL6aScd51HC2JgEBkmV27+nB9Txn/gSDTUyAcWiyzAIwraRXD5QWBHQJed7WXGQDN/6mKOUt3z9FYALvDyfTeZ/4WBjsnoWo3gkm+k7NHtBBbRX7qFmWp4V+2fpyoh27B6Af14xOvCq1btsTyy/wTAYERFU5YF9t9FI5RD0M4tEwGIWPixQTawYHCXSVh6I8l2nq4OLX8wuHuninj4kMM+EfjWbCb5G1EPz+L1vFbAPEx0QY0RJnAVeYTcrnbYatxGjaYPsw8UTwFbodlji53XZqvo9VdGe7uw2ncvQtbGs/lG4onlcxnmQEVK3dZlcsNQht8U+/dZw2M5kcySc92CBftiqt5lSPoYa6NEWDd8yGooT+UwaPv44BqAQRbDF9VZG42EBdjyzytna519+TBysVeOf6kOVk6N60b3oDqwl2YROQfJpeBS3DPkdIAgJqRCjR6FwTzgjMfjLDEzvGLE2AvAE3fhtlh2J8uc2x0HKW6lkaS2aBCsnQ8ktZqHuxz2H/pPcljGscvnwOeOafBf4s8s8wc6mBApF72vwnNR9Cz0YrhdKZO4kLEJ4kOCtB/OtVpbrn2DhBxN34XSD9MGPgyk8FWR0HZCmrxpAyKCQAjAyNUgsnChz/IDL9ymbOHBzCv8mWNE9/NH1zZG/u6WIN9J1AxVedl7kc1wNakNyTVTlZxLC/oKaFYxy8hwcpqnVvUPwRcBuXhPGiUyDPLOXg35hAKfVjv2fcgC3WG03mf1DUMBGZszbqO+FQ7ENSw6indCGKuADGfQCgNONEoZ08wSGMSCqd1zomZwBPVaOaROWwR493ZmB8CDBP6if3/wAJlbBNxUm0jxISBZG69/tcg/FGUdQnyOAtXWJqDBtKoFYKQbiV4Zb7CL76XwPAPfu/g9wqUx4so9ym95zuv45nmgxvSBExD1zTJ0ocemsRxwJOVDxuwbLpFZbLbQPAWkASbwGoDiuAbBs5LDqtIKbv5P2LGycNA1UfL17Mo6x0MCN9Qpb7XORZwGTfTHbi0S69ije3SA0WdFy1Hgfer7MJ2tMozURmnIz4MV0FFa8M3DrbcVjq8tcAMsIeZXaPeRcUfUYXitB5/9rArhADgtLl7/XX+Sa9NA8EV4MzVtfocZpd/o1c0Go0pVYKvAFC4frzRbCLjC/hhl3ol8o4sMIGnAOD0Y7rZrs8VsWwguIzcOUpq8zz0QD/eJbb6cirZML7t0+3Y4RS6S9L8dWrK0s2pU34bGJixLXBu2Ec5paQj5WYJELD9zl1ZVThoZyhZ7tBrBrTY9/VW8koiye8MjhzmEcu0DOtkvGm+rPN28N4sLy5dy4DgMgxfUfMrmj/LQgGtA1iY+CbuV5rfduK1CfGkSVIJpBJIJZBKIJVAKoFUAqkEUgmkEuhoCfw/vJfHMO4YqncAAAAASUVORK5CYII=";
+ const artwork = artworkBuilder.createArtworkForResource(objectGraph, resource, 36, 36);
+ ribbonModel.artwork = artwork;
+ ribbonModel.accessibilityLabel = title;
+ // Configure impressions
+ const metricsOptions = {
+ targetType: "facet",
+ pageInformation: metricsPageInformation,
+ locationTracker: metricsLocationTracker,
+ recoMetricsData: null,
+ };
+ const impressionOptions = metricsHelpersImpressions.impressionOptionsForArcadeSeeAllGamesRibbonItem(metricsOptions);
+ metricsHelpersImpressions.addImpressionFields(objectGraph, ribbonModel, impressionOptions);
+ return ribbonModel;
+ }
+ static createTagsRibbonShelf(objectGraph, data) {
+ const tagsData = mediaRelationship.relationshipViewsCollection(data, "categorizations");
+ const ribbonBarShelf = new models.Shelf("ribbonFlow");
+ const ribbonItems = [];
+ for (const tagData of tagsData) {
+ const name = attributeAsString(tagData, "name");
+ let pageUrl = null;
+ switch (tagData.type) {
+ case "genres":
+ pageUrl = attributeAsString(tagData, "url");
+ break;
+ case "tags":
+ const href = serverData.asString(tagData, "href");
+ pageUrl = hrefToRoutableUrl(objectGraph, href);
+ break;
+ default:
+ break;
+ }
+ if (isNothing(pageUrl)) {
+ continue;
+ }
+ const flowPage = objectGraph.required(pageRouter).fetchFlowPage(pageUrl);
+ const flowAction = new models.FlowAction(flowPage);
+ flowAction.pageUrl = pageUrl;
+ if (isSome(name)) {
+ const ribbonItem = new models.RibbonBarItem(name, flowAction);
+ ribbonItems.push(ribbonItem);
+ }
+ }
+ ribbonBarShelf.items = ribbonItems;
+ ribbonBarShelf.isHorizontal = true;
+ return ribbonBarShelf;
+ }
+ /**
+ * Determines if an item should be given priority and moved to the front of the list.
+ * @param objectGraph Current object graph
+ * @param data The data for the item
+ * @returns True if the item should be moved to the front of the list.
+ */
+ static shouldPriorizeItemWithData(objectGraph, data) {
+ var _a, _b;
+ const displayDeviceDrivenContent = (_b = (_a = objectGraph.userDefaults) === null || _a === void 0 ? void 0 : _a.bool("displayDeviceDrivenContent")) !== null && _b !== void 0 ? _b : false;
+ return (displayDeviceDrivenContent &&
+ objectGraph.bag.ribbonBarVisionEditorialItemIds.includes(data.id) &&
+ objectGraph.bag.enableDeviceDrivenDiscoveryContent);
+ }
+}
+//# sourceMappingURL=grouping-ribbon-bar-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-shelf-controller-common.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-shelf-controller-common.js
new file mode 100644
index 0000000..a1b05cd
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-shelf-controller-common.js
@@ -0,0 +1,992 @@
+import { isNothing, isSome } from "@jet/environment";
+import * as models from "../../../api/models";
+import * as serverData from "../../../foundation/json-parsing/server-data";
+import * as mediaAttributes from "../../../foundation/media/attributes";
+import * as mediaDataFetching from "../../../foundation/media/data-fetching";
+import * as mediaDataStructure from "../../../foundation/media/data-structure";
+import * as mediaRelationship from "../../../foundation/media/relationships";
+import { Parameters, Path, Protocol } from "../../../foundation/network/url-constants";
+import * as urls from "../../../foundation/network/urls";
+import * as appEvents from "../../app-promotions/app-event";
+import * as appPromotionsCommon from "../../app-promotions/app-promotions-common";
+import { pageRouter } from "../../builders/routing";
+import * as legacyArtwork from "../../content/artwork/legacy-artwork";
+import * as contentAttributes from "../../content/attributes";
+import * as content from "../../content/content";
+import * as externalDeepLink from "../../linking/external-deep-link";
+import * as lockups from "../../lockups/lockups";
+import * as metricsHelpersClicks from "../../metrics/helpers/clicks";
+import * as metricsHelpersLocation from "../../metrics/helpers/location";
+import * as onDevicePersonalization from "../../personalization/on-device-personalization";
+import * as productPageVariants from "../../product-page/product-page-variants";
+import * as refresh from "../../refresh/page-refresh-controller";
+import * as mediaRequestUtils from "../../builders/url-mapping-utils";
+import { defaultTodayCardConfiguration, lockupsForRelatedContent } from "../../today/today-card-util";
+import { isLockupShelf, } from "../grouping-types";
+import { categoryArtworkData } from "../../categories";
+import { hrefToRoutableUrl } from "../../builders/url-mapping";
+import { clientIdentifierForEditorialContextInData } from "../../lockups/editorial-context";
+import { areAppTagsEnabled } from "../../util/app-tags-util";
+import { AppEventsAttributes } from "../../../gameservicesui/src/foundation/media-api/requests/recommendation-request-types";
+/**
+ * Create the base 2 requirements to start parsing a grouping page shelf. These include the base shelf token and the
+ * base metrics options
+ *
+ * @param objectGraph The App Store dependency graph for making native calls and viewing properties
+ * @param mediaApiData The media api data for this specific shelf
+ * @param groupingParseContext The grouping page parsing context.
+ */
+export function createBaseShelfRequirements(objectGraph, mediaApiData, groupingParseContext) {
+ const featuredContentId = mediaAttributes.attributeAsNumber(mediaApiData, "editorialElementKind");
+ // Populate the gamesFilter, if it is provided.
+ let gamesFilter = null;
+ const rawGamesFilter = mediaAttributes.attributeAsString(mediaApiData, "gamesFilter");
+ switch (rawGamesFilter) {
+ case "arcade":
+ case "nonArcade":
+ case "all":
+ gamesFilter = rawGamesFilter;
+ break;
+ default:
+ if (featuredContentId === 495 /* FeaturedContentID.AppStore_PopularWithYourFriendsMarker */ ||
+ featuredContentId === 500 /* FeaturedContentID.AppStore_ContinuePlayingMarker */) {
+ gamesFilter = "arcade"; // Defaulting to Arcade because this param won't be passed until 20I.
+ }
+ break;
+ }
+ // Build Shelf eyebrow, title, and subtitle
+ let eyebrow = null;
+ let title = mediaAttributes.attributeAsString(mediaApiData, shelfTitleAttributePathForFeaturedContentId(objectGraph, featuredContentId));
+ let titleArtwork = null;
+ let badges = null;
+ let subtitle = mediaAttributes.attributeAsString(mediaApiData, "tagline");
+ const shelfHeaderConfiguration = {};
+ // 'Similar To' Personalised Shelf
+ // Check if personalised shelf has a badge-content, if it's got valid content, and the clients support eyebrows and title artwork:
+ // - Reco shelf title becomes the eyebrow
+ // - Featured App in badge-content becomes the shelf title, and we add the icon as title artwork
+ // - No subtitle should be shown
+ // - Flag to use the eyebrown name for metrics
+ let wantsEyebrowNameForMetrics = false;
+ const badgeContent = mediaRelationship.relationshipCollection(mediaApiData, "badge-content")[0];
+ if (featuredContentId === 476 /* FeaturedContentID.AppStore_PersonalizedShelfMarker */ &&
+ serverData.isDefinedNonNullNonEmpty(badgeContent)) {
+ eyebrow = objectGraph.loc.uppercased(mediaAttributes.attributeAsString(mediaApiData, shelfTitleAttributePathForFeaturedContentId(objectGraph, featuredContentId)));
+ subtitle = null;
+ const dataType = badgeContent.type;
+ if (dataType === "collections") {
+ title = content.notesFromData(objectGraph, badgeContent, "name");
+ const artworkData = categoryArtworkData(objectGraph, badgeContent, false);
+ // Reconfigure eyebrow text color only if there is eyebrow artwork.
+ if (isSome(artworkData)) {
+ titleArtwork = content.artworkFromApiArtwork(objectGraph, artworkData, {
+ useCase: 1 /* content.ArtworkUseCase.LockupIconSmall */,
+ style: "unadorned",
+ });
+ const eyebrowColor = { type: "named", name: "secondaryText" };
+ shelfHeaderConfiguration.eyebrowColor = eyebrowColor;
+ }
+ badges = { forYou: true };
+ }
+ else {
+ wantsEyebrowNameForMetrics = true;
+ title = mediaAttributes.attributeAsString(badgeContent, shelfTitleAttributePathForFeaturedContentId(objectGraph, featuredContentId));
+ titleArtwork = content.iconFromData(objectGraph, badgeContent, {
+ useCase: 1 /* content.ArtworkUseCase.LockupIconSmall */,
+ });
+ }
+ }
+ const shelfToken = {
+ featuredContentId,
+ id: serverData.asString(mediaApiData, "id"),
+ presentationHints: {},
+ metricsPageInformation: groupingParseContext.metricsPageInformation,
+ metricsLocationTracker: groupingParseContext.metricsLocationTracker,
+ pageGenreId: groupingParseContext.pageGenreId,
+ featuredContentData: mediaApiData,
+ title: title,
+ subtitle: subtitle,
+ eyebrow: eyebrow,
+ titleArtwork: titleArtwork,
+ shelfHeaderConfiguration: shelfHeaderConfiguration,
+ shouldFilter: false,
+ gamesFilter: gamesFilter,
+ remainingItems: [],
+ isFirstRender: true,
+ isDeferring: false,
+ showOrdinals: false,
+ hasExistingContent: false,
+ showingPlaceholders: false,
+ ordinalIndex: 1,
+ isSearchLandingPage: groupingParseContext.isSearchLandingPage,
+ isArcadePage: groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.isArcadePage,
+ };
+ const shelfMetricsOptions = {
+ id: shelfToken.id,
+ kind: null,
+ softwareType: serverData.asBooleanOrFalse(groupingParseContext === null || groupingParseContext === void 0 ? void 0 : groupingParseContext.isArcadePage) ? "Arcade" : null,
+ targetType: "swoosh",
+ title: wantsEyebrowNameForMetrics ? shelfToken.eyebrow : shelfToken.title,
+ badges: badges,
+ pageInformation: groupingParseContext.metricsPageInformation,
+ locationTracker: groupingParseContext.metricsLocationTracker,
+ idType: "its_contentId",
+ fcKind: featuredContentId,
+ recoMetricsData: recoMetricsDataForFCData(objectGraph, mediaApiData),
+ };
+ return {
+ shelfToken: shelfToken,
+ metricsOptions: shelfMetricsOptions,
+ };
+}
+// endregion
+// region Shelf Tokens
+/**
+ * Whether we should defer building the rest of a shelf given a shelf token
+ * @param token
+ */
+export function shouldDefer(token) {
+ return token && token.isDeferring && token.isFirstRender;
+}
+/**
+ * Due to upstream oddities, we get the shelf title in the form of `title` for these markers. These are
+ * unique markers in that they are not filled by reco.
+ *
+ * For personalization markers, they are programmed in DJ with the name field hidden, and it is filled in by
+ * Reco. Without wanting to expose loc strings to that name field, we unfortunately have to use this other
+ * attribute key.
+ *
+ * This will all go away once these markers are replaced with proper Reco equivalents (i.e. when popular-
+ * with-your-friends and suggested-friends move to Reco).
+ */
+export function shelfTitleAttributePathForFeaturedContentId(objectGraph, id) {
+ switch (id) {
+ case 548 /* FeaturedContentID.AppStore_GameCenterActivityFeedMarker */:
+ case 495 /* FeaturedContentID.AppStore_PopularWithYourFriendsMarker */:
+ case 496 /* FeaturedContentID.AppStore_SuggestedFriendsMarker */:
+ return "title";
+ default:
+ return "name";
+ }
+}
+/**
+ * Returns the URL schema for grouping shelves that may need to fetch additional content.
+ * Grouping-Related builders can extend on this scheme if needed, e.g. query param on Continue Playing shelves.
+ * @param token Token to encode in URL for subsequent fetch.
+ */
+export function groupingShelfUrl(token) {
+ let shelfUrl = new urls.URL()
+ .set("protocol", Protocol.internal)
+ .append("pathname", Path.grouping)
+ .append("pathname", Path.shelf)
+ .append("pathname", encodeURIComponent(JSON.stringify(token)))
+ .param(Parameters.groupingFeaturedContentId, `${token.featuredContentId}`);
+ if (isSome(token.nativeGroupingShelfId)) {
+ shelfUrl = shelfUrl.param(Parameters.nativeGroupingShelfId, `${token.nativeGroupingShelfId}`);
+ }
+ return shelfUrl.build();
+}
+/**
+ * Configure `url` on a standard grouping shelf if it needs to fetch more content.
+ * @param shelf Shelf to add url to.
+ * @param token Token tode encode in URL for subsequent fetch.
+ */
+export function createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, token) {
+ if (serverData.isNullOrEmpty(token)) {
+ return null;
+ }
+ // Web does not curently support pagination via "fetch more", so shelf token URLs are not needed.
+ // Note: removing these URLs for web reduces view model size by ~45%.
+ if (objectGraph.client.isWeb) {
+ return null;
+ }
+ // Ensure the token has a `shelfStyle` so we can construct an empty shelf in the case of a hydration error,
+ // or the shelf being empty upon hydration.
+ if (serverData.isNull(token.shelfStyle)) {
+ token.shelfStyle = shelf.contentType;
+ }
+ const hasNonPlaceholderItems = shelf.contentType !== "placeholder" && serverData.isDefinedNonNullNonEmpty(shelf.items);
+ token.hasExistingContent = token.hasExistingContent || (hasNonPlaceholderItems && token.isFirstRender);
+ const shouldAddFirstRenderUrl = token.remainingItems.length || token.recommendationsHref || token.onDeviceRecommendationsUseCase;
+ if (shouldAddFirstRenderUrl && token.isFirstRender) {
+ return groupingShelfUrl(token);
+ }
+ else {
+ return null;
+ }
+}
+/**
+ * Updates a shelf URL based on the provided token.
+ * @param shelf Shelf to add url to.
+ * @param token Token to encode in the url.
+ */
+export function updateShelfUrlWithNewToken(objectGraph, shelf, token) {
+ const originalShelfUrl = urls.URL.from(shelf.url);
+ const updatedUrl = urls.URL.from(createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, token));
+ // Add missing query params to the updated URL from the original.
+ for (const key of Object.keys(originalShelfUrl.query)) {
+ if (serverData.isNull(updatedUrl.query[key])) {
+ updatedUrl.query[key] = originalShelfUrl.query[key];
+ }
+ }
+ // Finally, update the shelf's URL.
+ shelf.url = updatedUrl.build();
+}
+/**
+ * From a grouping shelf token determine the list of unhydrated MAPI data to fetch
+ * this will also handle the case were whe need to fetch additional relationship data
+ *
+ * This method will also hoist up ids of content needed to be fetched from an unhydrated relationship if needed. For <rdar://problem/42797176>.
+ *
+ * This is necessary to accommodate some MAPI objects which have a nested relationship that isn't hydrated and cannot
+ * be hydrated by re-fetching the content at parent ID.
+ *
+ * @param token The shelfToken for the shelf we're fetching data for
+ */
+export function unhydratedRemainingItemsFromShelfToken(objectGraph, token) {
+ var _a;
+ const shouldFetchRelationshipItems = ((_a = token.relationshipToFetch) === null || _a === void 0 ? void 0 : _a.length) > 0;
+ let remainingItems = token.remainingItems;
+ if (shouldFetchRelationshipItems) {
+ remainingItems = token.remainingItems.map((remainingItem) => {
+ return mediaRelationship.relationshipData(objectGraph, remainingItem, token.relationshipToFetch);
+ });
+ }
+ return remainingItems;
+}
+/**
+ * Given a MAPI response for a list of unhydrated shelf items, and the original grouping shelf token determine
+ * the list of hydrated MAPI data. This also handles the case where we need to hoist up relationship data
+ *
+ * If ids to be fetched were hoisted from nested relationships, replace the original unhydrated nested
+ * relationship on parent with hydrated data.
+ *
+ * @param token
+ * @param mediaApiData
+ */
+export function hydratedRemainingItemsForShelfTokenFromMediaApiData(objectGraph, token, mediaApiData) {
+ var _a;
+ const didFetchRelationshipItems = ((_a = token.relationshipToFetch) === null || _a === void 0 ? void 0 : _a.length) > 0;
+ let hyrdatedItems = mediaDataStructure.dataCollectionFromDataContainer(mediaApiData);
+ if (didFetchRelationshipItems) {
+ const dataMapping = {};
+ for (const dataItem of mediaApiData.data) {
+ dataMapping[dataItem.id] = dataItem;
+ }
+ hyrdatedItems = [];
+ for (const remainingItem of token.remainingItems) {
+ const unhydratedRelationshipData = mediaRelationship.relationshipData(objectGraph, remainingItem, token.relationshipToFetch);
+ if (serverData.isDefinedNonNullNonEmpty(unhydratedRelationshipData)) {
+ remainingItem.relationships[token.relationshipToFetch].data = [
+ dataMapping[unhydratedRelationshipData.id],
+ ];
+ }
+ hyrdatedItems.push(remainingItem);
+ }
+ }
+ return hyrdatedItems;
+}
+/**
+ * Deletes all remainingItems that have been requested for hydration from the shelfToken.
+ *
+ * @param shelfToken
+ * @param requestedItems
+ */
+export function flushRequestedItemsFromShelfToken(shelfToken, requestedItemIds) {
+ shelfToken.remainingItems = shelfToken.remainingItems.filter((remainingItem) => {
+ return !requestedItemIds.has(remainingItem.id);
+ });
+}
+// endregion
+// region Shelf Requests
+/**
+ * Generates a media API request for fetching a shelf's data
+ * @param objectGraph
+ * @param {URL} url The URL of the page being requested
+ * @param {Parameters} parameters The parameters that were extracted from the URL
+ * @returns {Request} A media API request
+ */
+export function generateShelfRequest(objectGraph, token, parameters) {
+ var _a;
+ // Whether or not token is for fetching additional items that were unhydated.
+ // These tokens are used when a server returned explicit IDs for the contents of shelves, and some (or all) of those IDs were fully unhydrated.
+ const isFetchingAdditionalContent = serverData.isDefinedNonNullNonEmpty(token.remainingItems);
+ // Whether or not token is for fetching personalized recommendations.
+ // Recommendation shelves can sometimes return with no IDs provided within its shelf. We are expected to use the `recommendationsHref` to fetch in this case.
+ // Note that server can sometimes choose to prepopulate IDs for personalized shelves, even if `recommendationsHref` is present.
+ const isPersonalizedRefetch = !isFetchingAdditionalContent && ((_a = token.recommendationsHref) === null || _a === void 0 ? void 0 : _a.length) > 0;
+ if (isFetchingAdditionalContent) {
+ // Remaining items to fetch, since they were unhydrated in original page response.
+ const remainingItemsToFetch = unhydratedRemainingItemsFromShelfToken(objectGraph, token);
+ // MAPI Request
+ const mediaApiRequest = new mediaDataFetching.Request(objectGraph, remainingItemsToFetch, true);
+ productPageVariants.addVariantParametersToRequestForItems(objectGraph, mediaApiRequest, remainingItemsToFetch);
+ prepareGroupingShelfRequest(objectGraph, mediaApiRequest);
+ return mediaApiRequest;
+ }
+ else if (isPersonalizedRefetch) {
+ const mediaApiRequest = new mediaDataFetching.Request(objectGraph, token.recommendationsHref).includingAgeRestrictions();
+ prepareGroupingShelfRequest(objectGraph, mediaApiRequest);
+ if (appPromotionsCommon.appEventsAreEnabled(objectGraph)) {
+ mediaApiRequest.enablingFeature("appEvents");
+ mediaApiRequest.includingMetaKeys("editorial-elements:contents", ["personalizationData", "cppData"]);
+ mediaApiRequest.includingScopedAttributes("app-events", AppEventsAttributes);
+ mediaApiRequest.includingScopedRelationships("app-events", ["app"]);
+ }
+ if (appPromotionsCommon.appContingentItemsAreEnabled(objectGraph)) {
+ mediaApiRequest.enablingFeature("contingentItems");
+ mediaRequestUtils.configureContingentItemsForGroupingRequest(mediaApiRequest);
+ }
+ if (appPromotionsCommon.appOfferItemsAreEnabled(objectGraph)) {
+ mediaApiRequest.enablingFeature("offerItems");
+ mediaRequestUtils.configureOfferItemsForMediaRequest(mediaApiRequest);
+ }
+ if (areAppTagsEnabled(objectGraph, "grouping")) {
+ mediaRequestUtils.configureTagsForMediaRequest(mediaApiRequest);
+ }
+ return mediaApiRequest;
+ }
+ return null;
+}
+/**
+ * Modify request for a items fetched for grouping shelf items. (i.e. individual IDs).
+ * This should be ideally be in grouping builder's `prepareRequest`, but that would change how url-driven requests are configured.
+ * @param objectGraph
+ * @param request Request to modify.
+ */
+export function prepareGroupingShelfRequest(objectGraph, request) {
+ request
+ .includingAdditionalPlatforms(mediaDataFetching.defaultAdditionalPlatformsForClient(objectGraph))
+ .includingRelationshipsForUpsell(true)
+ .includingMacOSCompatibleIOSAppsWhenSupported(true)
+ .usingCustomAttributes(productPageVariants.shouldFetchCustomAttributes(objectGraph));
+ let attributes = ["editorialArtwork", "editorialVideo", "minimumOSVersion"];
+ if (request.includesResourceType("app-events") && appPromotionsCommon.appEventsAreEnabled(objectGraph)) {
+ request.enablingFeature("appEvents");
+ request.includingMetaKeys("editorial-elements:contents", ["personalizationData", "cppData"]);
+ request.includingScopedAttributes("app-events", AppEventsAttributes);
+ request.includingScopedRelationships("app-events", ["app"]);
+ }
+ if (request.includesResourceType("contingent-items") &&
+ appPromotionsCommon.appContingentItemsAreEnabled(objectGraph)) {
+ request.enablingFeature("contingentItems");
+ mediaRequestUtils.configureContingentItemsForGroupingRequest(request);
+ attributes = [];
+ }
+ if (request.includesResourceType("offer-items") && appPromotionsCommon.appOfferItemsAreEnabled(objectGraph)) {
+ request.enablingFeature("offerItems");
+ mediaRequestUtils.configureOfferItemsForMediaRequest(request);
+ attributes = [];
+ }
+ if (request.includesResourceType("apps") || request.includesResourceType("app-events")) {
+ attributes = attributes.concat("screenshotsByType", "videoPreviewsByType", "expectedReleaseDateDisplayFormat");
+ }
+ if (areAppTagsEnabled(objectGraph, "grouping")) {
+ mediaRequestUtils.configureTagsForMediaRequest(request);
+ }
+ request.includingAttributes(attributes);
+}
+// endregion
+// region Metrics
+export function recoMetricsDataForFCData(objectGraph, mediaApiData) {
+ const featuredContentId = mediaAttributes.attributeAsNumber(mediaApiData, "editorialElementKind");
+ switch (featuredContentId) {
+ // All of these kinds just require walking their children
+ case 425 /* FeaturedContentID.AppStore_GenreStack */:
+ case 415 /* FeaturedContentID.AppStore_HeroList */:
+ case 416 /* FeaturedContentID.AppStore_Hero */:
+ case 417 /* FeaturedContentID.AppStore_CustomHero */:
+ case 501 /* FeaturedContentID.AppStore_PersonalizedHeroMarker */:
+ case 258 /* FeaturedContentID.Sundance_Flowcase */:
+ case 421 /* FeaturedContentID.AppStore_BrickRow */:
+ case 422 /* FeaturedContentID.AppStore_Brick */:
+ case 423 /* FeaturedContentID.AppStore_CustomBrick */:
+ case 261 /* FeaturedContentID.Sundance_BrickRow */:
+ case 584 /* FeaturedContentID.AppStore_TagsBrick */:
+ case 587 /* FeaturedContentID.AppStore_PersonalizedTagsBrick */: {
+ const childrenRelationship = mediaRelationship.relationship(mediaApiData, "children");
+ return mediaDataStructure.metricsFromMediaApiObject(childrenRelationship);
+ }
+ case 437 /* FeaturedContentID.AppStore_LinkList */:
+ case 265 /* FeaturedContentID.Sundance_LinkList */: {
+ const contentRelationship = mediaRelationship.relationship(mediaApiData, "children");
+ const textLinks = mediaAttributes.attributeAsArrayOrEmpty(mediaApiData, "links");
+ if (serverData.isDefinedNonNullNonEmpty(contentRelationship)) {
+ return mediaDataStructure.metricsFromMediaApiObject(contentRelationship);
+ }
+ else if (serverData.isDefinedNonNullNonEmpty(textLinks)) {
+ return mediaDataStructure.metricsFromMediaApiObject(mediaApiData);
+ }
+ return null;
+ }
+ case 414 /* FeaturedContentID.AppStore_TabRoot */:
+ case 424 /* FeaturedContentID.AppStore_ChartSet */:
+ case 566 /* FeaturedContentID.AppStore_ArcadeDownloadPackMarker */: {
+ return null; // unapplicable fckinds
+ }
+ default: {
+ if (isLockupShelf(featuredContentId)) {
+ let childrenRelationship = mediaRelationship.relationship(mediaApiData, "contents");
+ if (serverData.isNull(childrenRelationship)) {
+ return null;
+ }
+ const contentItems = childrenRelationship.data;
+ if (!contentItems || contentItems.length === 0) {
+ childrenRelationship = mediaRelationship.relationship(mediaApiData, "children");
+ }
+ return mediaDataStructure.metricsFromMediaApiObject(childrenRelationship);
+ }
+ else {
+ objectGraph.console.warn("Unknown featured content ID:", featuredContentId);
+ return null;
+ }
+ }
+ }
+}
+// endregion
+// region Artwork
+/**
+ * Creating an artwork model with the crop required for a grouping page piece of art
+ */
+export function groupingArtworkFromApiArtwork(objectGraph, artworkData, options) {
+ const artwork = content.artworkFromApiArtwork(objectGraph, artworkData, options);
+ if (artwork) {
+ artwork.crop = "sr";
+ }
+ return artwork;
+}
+export function artworkFromFC(objectGraph, node, width, height, options) {
+ const artworkData = mediaAttributes.attributeAsDictionary(node, "artwork");
+ if (artworkData instanceof Array) {
+ const artwork = legacyArtwork.closestArtworkMatchingSize(objectGraph, artworkData, width, height);
+ artwork.crop = "bb";
+ return artwork;
+ }
+ else if (artworkData != null) {
+ return groupingArtworkFromApiArtwork(objectGraph, artworkData, options);
+ }
+ return null;
+}
+export function artworkForTags(objectGraph, node, width, height, options, metricsOptions) {
+ const lockupData = serverData.asArrayOrEmpty(node.meta, "associations.apps.data");
+ const artworks = [];
+ if (isSome(lockupData)) {
+ for (const lockup of lockupData) {
+ const lockupOptions = {
+ artworkUseCase: 1 /* content.ArtworkUseCase.LockupIconSmall */,
+ metricsOptions: metricsOptions,
+ useJoeColorIconPlaceholder: true,
+ joeColorPlaceholderSelectionLogic: content.bestJoeColorPlaceholderSelectionLogic,
+ };
+ const lockupFromData = lockups.lockupFromData(objectGraph, lockup, lockupOptions);
+ const lockupIcon = lockupFromData === null || lockupFromData === void 0 ? void 0 : lockupFromData.icon;
+ if (isSome(lockupIcon)) {
+ artworks.push(lockupIcon);
+ }
+ }
+ }
+ return artworks;
+}
+// endregion
+// region FC Metadata
+export function metadataForFCData(objectGraph, fcData, token, shouldPersonalizeContent, personalizationDataContainer, metricsOptions, context, unavailableCallback) {
+ var _a, _b, _c;
+ const callUnavailable = function (contentData) {
+ if (unavailableCallback) {
+ unavailableCallback();
+ }
+ else {
+ token === null || token === void 0 ? void 0 : token.remainingItems.push(contentData);
+ }
+ };
+ const isLinkNode = ((_a = serverData.asString(fcData, "url")) === null || _a === void 0 ? void 0 : _a.length) > 0;
+ const containsLinkNode = ((_b = mediaAttributes.attributeAsString(fcData, "link.url")) === null || _b === void 0 ? void 0 : _b.length) > 0;
+ const isContentNodeUsingPrimaryContent = mediaRelationship.hasRelationship(fcData, "primary-content", false);
+ const isContentNode = mediaRelationship.hasRelationship(fcData, "contents", false) || isContentNodeUsingPrimaryContent;
+ // Define whether data is a Category Grouping kind.
+ let isCategoryGroupingKind = mediaAttributes.attributeAsString(fcData, "kind") === "CategoryGrouping";
+ if (containsLinkNode || isLinkNode) {
+ return metadataForLink(objectGraph, fcData, token, metricsOptions, unavailableCallback);
+ }
+ else if (isContentNode) {
+ let contentData;
+ // Define category grouping content to set when content is a Category Grouping kind.
+ let categoryGroupingContent;
+ let personalizedDataResult;
+ if (shouldPersonalizeContent && !isContentNodeUsingPrimaryContent) {
+ const contentDataItems = mediaRelationship.relationshipCollection(fcData, "contents");
+ personalizedDataResult = onDevicePersonalization.personalizeDataItems(objectGraph, "groupingCommon", contentDataItems, true, personalizationDataContainer, false, 1);
+ const personalizedContentDataItems = personalizedDataResult.personalizedData;
+ if (personalizedContentDataItems.length === 0) {
+ return null;
+ }
+ contentData = personalizedContentDataItems[0];
+ }
+ else {
+ contentData = isContentNodeUsingPrimaryContent
+ ? mediaRelationship.relationshipData(objectGraph, fcData, "primary-content")
+ : mediaRelationship.relationshipData(objectGraph, fcData, "contents");
+ }
+ // Check whether content is a Category Grouping kind.
+ if (mediaAttributes.attributeAsString(contentData, "kind") === "CategoryGrouping") {
+ categoryGroupingContent = contentData;
+ // Update content to its primary content.
+ contentData = mediaRelationship.relationshipData(objectGraph, contentData, "primary-content");
+ isCategoryGroupingKind = true;
+ }
+ // If the content data is null don't even bother with the call unavailable since we have no way of knowing how to fetch it
+ if (serverData.isNull(contentData)) {
+ return null;
+ }
+ else if (serverData.isNull(contentData.attributes) || shouldDefer(token)) {
+ if (serverData.isDefinedNonNullNonEmpty(token)) {
+ token.isDeferring = true;
+ }
+ callUnavailable(contentData);
+ return null;
+ }
+ // Generate the subtitle
+ let subtitle = content.notesFromData(objectGraph, contentData, "tagline") ||
+ lockups.subtitleFromData(objectGraph, contentData);
+ // Generate the click action
+ // Using the fcData here because the ids need to match the id used for the impressions, otherwise the reporting
+ // heat maps dont work.
+ // rdar://61527868 (Metrics: Arcade Data Mismatch (Location and Impressions have different name fields))
+ const metricsClickOptions = metricsHelpersClicks.clickOptionsForLockup(objectGraph, fcData, metricsOptions);
+ metricsClickOptions.targetType = metricsOptions.targetType;
+ let action = lockups.actionFromData(objectGraph, contentData, metricsClickOptions, token === null || token === void 0 ? void 0 : token.clientIdentifierOverride);
+ // Understand if we're dealing with an article here, so when we go to generate app events, we know if we
+ // should use the action we've created above or the app event action.
+ const isArticle = mediaAttributes.attributeAsBooleanOrFalse(contentData, "isCanvasAvailable");
+ // Find the artwork and title depending on the content type
+ let artwork = null;
+ let caption = null;
+ // Find lockup (if any)
+ let lockup = null;
+ // Find app event (if any)
+ let appEvent;
+ const shortEditorialDescription = mediaAttributes.attributeAsString(contentData, "itunesNotes.short");
+ const hasContentId = ((_c = contentData.id) === null || _c === void 0 ? void 0 : _c.length) > 0;
+ const contentMetricsOptions = {
+ ...metricsOptions,
+ id: hasContentId ? contentData.id : fcData.id,
+ idType: hasContentId ? "its_id" : "editorial_id",
+ };
+ switch (contentData.type) {
+ case "groupings": {
+ artwork = mediaAttributes.attributeAsDictionary(contentData, "artwork");
+ // If data is a Category Grouping kind, reconfigure content.
+ if (isCategoryGroupingKind) {
+ contentData = categoryGroupingContent !== null && categoryGroupingContent !== void 0 ? categoryGroupingContent : fcData;
+ }
+ break;
+ }
+ case "editorial-items": {
+ // Check for an app-event relationship
+ const relatedCardContents = mediaRelationship.relationshipData(objectGraph, contentData, "card-contents");
+ if (serverData.isDefinedNonNullNonEmpty(relatedCardContents)) {
+ const clickOptions = {
+ ...contentMetricsOptions,
+ inAppEventId: relatedCardContents.id,
+ };
+ const parentAppData = mediaRelationship.relationshipData(objectGraph, relatedCardContents, "app");
+ if (serverData.isDefinedNonNull(parentAppData)) {
+ clickOptions.relatedSubjectIds = [parentAppData.id];
+ }
+ const appEventOrDate = appEvents.appEventOrPromotionStartDateFromData(objectGraph, relatedCardContents, null, false, true, "dark", "white", false, clickOptions, false, true, null, token.isArcadePage, false);
+ const cardDisplayStyle = mediaAttributes.attributeAsString(contentData, "cardDisplayStyle");
+ if (cardDisplayStyle === "AppEventCard") {
+ if (appEventOrDate instanceof Date) {
+ // If we get a date back, we have a valid app event, but it starts in the future.
+ // We don't want the object containing this event to render yet, so return early.
+ refresh.addNextPreferredContentRefreshDate(appEventOrDate, context.refreshController);
+ return null;
+ }
+ else if (isNothing(appEventOrDate)) {
+ return null;
+ }
+ else {
+ appEvent = appEventOrDate;
+ if (!isArticle) {
+ action = appEvent.clickAction;
+ }
+ if (serverData.isNullOrEmpty(subtitle)) {
+ subtitle = content.notesFromData(objectGraph, relatedCardContents, "short");
+ }
+ }
+ }
+ }
+ caption = mediaAttributes.attributeAsString(contentData, "label");
+ if (caption) {
+ // This is a hack for AOTD/GOTD
+ caption = caption.replace(/\n/g, " ");
+ }
+ const relatedContent = mediaRelationship.relationshipData(objectGraph, contentData, "contents");
+ const tagline = serverData.asString(contentData, "editorialNotes.tagline");
+ if (serverData.isNullOrEmpty(subtitle)) {
+ if (tagline) {
+ subtitle = tagline;
+ }
+ else if (relatedContent) {
+ subtitle = content.notesFromData(objectGraph, relatedContent, "short");
+ }
+ }
+ if (serverData.isNullOrEmpty(subtitle) && serverData.isDefinedNonNull(appEvent)) {
+ subtitle = appEvent.subtitle;
+ }
+ let crossLinkSubtitle = mediaAttributes.attributeAsString(contentData, "editorialNotes.short");
+ if (isNothing(crossLinkSubtitle) || crossLinkSubtitle.length === 0) {
+ crossLinkSubtitle = subtitle;
+ }
+ const cardConfig = defaultTodayCardConfiguration(objectGraph);
+ if (serverData.isNull(appEvent) &&
+ externalDeepLink.deepLinkUrlFromData(objectGraph, contentData) &&
+ !objectGraph.client.isiOS) {
+ cardConfig.crossLinkSubtitle = crossLinkSubtitle;
+ }
+ cardConfig.clientIdentifierOverride = clientIdentifierForEditorialContextInData(objectGraph, contentData);
+ if (serverData.isDefinedNonNull(appEvent)) {
+ lockup = appEvent.lockup;
+ }
+ else {
+ // On iOS and Web, we always attempt to create a lockup. On other platforms, only do this is there is a cross link.
+ // iOS offer style will be determined later based on artwork.
+ const offerEnvironment = objectGraph.client.isiOS ? null : "dark";
+ const offerStyle = objectGraph.client.isiOS ? null : "white";
+ if (objectGraph.client.isiOS ||
+ objectGraph.client.isWeb ||
+ externalDeepLink.deepLinkUrlFromData(objectGraph, contentData)) {
+ metricsHelpersLocation.pushContentLocation(objectGraph, contentMetricsOptions, token === null || token === void 0 ? void 0 : token.title);
+ const relatedLockups = lockupsForRelatedContent(objectGraph, mediaRelationship.relationshipCollection(contentData, "card-contents"), cardConfig, metricsOptions.pageInformation, metricsOptions.locationTracker, offerEnvironment, offerStyle, externalDeepLink.deepLinkUrlFromData(objectGraph, contentData));
+ if (relatedLockups.length === 1) {
+ lockup = relatedLockups[0];
+ }
+ metricsHelpersLocation.popLocation(contentMetricsOptions.locationTracker);
+ }
+ }
+ }
+ // falls through
+ default: {
+ // Create a lockup if possible on iOS
+ const validLockupContentTypes = [
+ "apps",
+ "arcade-apps",
+ "app-bundles",
+ "in-apps",
+ ];
+ if (serverData.isNull(lockup) &&
+ validLockupContentTypes.indexOf(contentData.type) > -1 &&
+ objectGraph.host.isiOS) {
+ metricsHelpersLocation.pushContentLocation(objectGraph, contentMetricsOptions, token === null || token === void 0 ? void 0 : token.title);
+ const lockupOptions = {
+ metricsOptions: {
+ pageInformation: metricsOptions.pageInformation,
+ locationTracker: metricsOptions.locationTracker,
+ recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(contentData),
+ },
+ clientIdentifierOverride: token === null || token === void 0 ? void 0 : token.clientIdentifierOverride,
+ artworkUseCase: content.artworkUseCaseFromShelfStyle(objectGraph, token === null || token === void 0 ? void 0 : token.shelfStyle),
+ canDisplayArcadeOfferButton: true,
+ shouldHideArcadeHeader: objectGraph.featureFlags.isEnabled("hide_arcade_header_on_arcade_tab") &&
+ token.isArcadePage,
+ };
+ lockup = lockups.lockupFromData(objectGraph, contentData, lockupOptions);
+ metricsHelpersLocation.popLocation(contentMetricsOptions.locationTracker);
+ }
+ artwork =
+ contentAttributes.contentAttributeAsDictionary(objectGraph, contentData, "editorialArtwork") ||
+ mediaAttributes.attributeAsDictionary(contentData, "editorialArtwork");
+ if (serverData.isNullOrEmpty(subtitle) && serverData.isDefinedNonNull(lockup)) {
+ subtitle = lockup.subtitle;
+ }
+ break;
+ }
+ }
+ if (serverData.isDefinedNonNull(action)) {
+ action.presentationStyle = ["textFollowsTintColor"];
+ // If data is NOT a Category Grouping kind, reconfigure action title.
+ if (!isCategoryGroupingKind) {
+ // Prefer design tag or editorial name for title when available
+ const designTag = unescapeHtmlString(mediaAttributes.attributeAsString(fcData, "designTag"));
+ const editorialTitle = content.notesFromData(objectGraph, contentData, "name");
+ action.title = designTag || editorialTitle || action.title || subtitle || caption;
+ }
+ }
+ return {
+ action: action,
+ caption: caption,
+ title: action === null || action === void 0 ? void 0 : action.title,
+ subtitle: subtitle,
+ artwork: artwork,
+ shortEditorialDescription: shortEditorialDescription,
+ content: contentData,
+ lockup: lockup,
+ appEvent: appEvent,
+ onDevicePersonalizationDataProcessingType: personalizedDataResult === null || personalizedDataResult === void 0 ? void 0 : personalizedDataResult.processingType,
+ };
+ }
+ return null;
+}
+/// Gets the action and title for a brick that is backed by tags.
+export function metadataForTag(objectGraph, fcData, token, metricsOptions) {
+ const shortEditorialDescription = mediaAttributes.attributeAsString(fcData, "name");
+ const href = serverData.asString(fcData, "href");
+ const url = hrefToRoutableUrl(objectGraph, href);
+ const flowPage = objectGraph.required(pageRouter).fetchFlowPage(url);
+ const flowAction = new models.FlowAction(flowPage);
+ flowAction.pageUrl = url;
+ return {
+ action: flowAction,
+ caption: "null",
+ title: shortEditorialDescription,
+ subtitle: "null",
+ artwork: null,
+ shortEditorialDescription: shortEditorialDescription,
+ };
+}
+function metadataForLink(objectGraph, fcData, token, metricsOptions, unavailableCallback) {
+ const isFCLinkData = serverData.isDefinedNonNull(serverData.asString(fcData, "url"));
+ const linkData = isFCLinkData ? fcData : mediaAttributes.attributeAsDictionary(fcData, "link");
+ const callUnavailable = function (contentData) {
+ if (unavailableCallback) {
+ unavailableCallback();
+ }
+ else {
+ token === null || token === void 0 ? void 0 : token.remainingItems.push(contentData);
+ }
+ };
+ if (serverData.isNull(linkData) || shouldDefer(token)) {
+ callUnavailable(fcData);
+ return null;
+ }
+ const target = serverData.asString(linkData, "target");
+ const url = serverData.asString(linkData, "url");
+ // Prefer design tag for title when available
+ const label = serverData.asString(linkData, "label");
+ const designTag = unescapeHtmlString(mediaAttributes.attributeAsString(fcData, "designTag"));
+ const title = designTag || label;
+ let action = null;
+ if (target === "external") {
+ action = new models.ExternalUrlAction(url);
+ action.title = title;
+ }
+ else {
+ const flowPage = objectGraph.required(pageRouter).fetchFlowPage(url);
+ const flowAction = new models.FlowAction(flowPage);
+ flowAction.pageUrl = url;
+ flowAction.title = title;
+ action = flowAction;
+ }
+ action.presentationStyle = ["textFollowsTintColor"];
+ // Configure metrics
+ const clickOptions = {
+ ...metricsOptions,
+ id: "",
+ };
+ metricsHelpersClicks.addClickEventToAction(objectGraph, action, clickOptions);
+ return {
+ action: action,
+ caption: null,
+ title: title,
+ subtitle: null,
+ artwork: null,
+ shortEditorialDescription: null,
+ };
+}
+// endregion
+// region Editorial Data Merging
+export function mergeContentDataIntoEditorialData(contentDataArray, editorialItemsDataArray) {
+ const contentDataMap = {};
+ for (const contentData of contentDataArray) {
+ contentDataMap[contentData.id] = contentData;
+ }
+ // The relationships we're interested in merging fetched content for
+ const relationShipsToMerge = ["contents", "grouping"];
+ const mergedEditorialData = [];
+ // Steps for merging
+ // 1. Loop through the editorial items array
+ // 2. For each editorial item loop through the relationships we're interested in
+ // 3. If this editorial item has any of these relationships loop through that relationsip looking in our content map
+ // for the hydrated version of each piece of data in the relationship
+ // 4. If all the content has been fetched for each relationship we can add it to our resulting array of hydrated editorial items.
+ for (const editorialItemData of editorialItemsDataArray) {
+ let hasHydratedAllRelationships = true;
+ for (const relationshipType of relationShipsToMerge) {
+ const unhydratedRelationshipCollection = mediaRelationship.relationshipCollection(editorialItemData, relationshipType);
+ if (serverData.isDefinedNonNull(unhydratedRelationshipCollection)) {
+ const hydratedRelationship = [];
+ for (const unhydratedData of unhydratedRelationshipCollection) {
+ const hydratedData = contentDataMap[unhydratedData.id];
+ if (serverData.isDefinedNonNullNonEmpty(hydratedData)) {
+ hydratedRelationship.push(hydratedData);
+ }
+ }
+ if (hydratedRelationship.length === unhydratedRelationshipCollection.length) {
+ editorialItemData.relationships[relationshipType] = { data: hydratedRelationship };
+ }
+ else {
+ hasHydratedAllRelationships = false;
+ }
+ }
+ }
+ if (hasHydratedAllRelationships) {
+ mergedEditorialData.push(editorialItemData);
+ }
+ }
+ return mergedEditorialData;
+}
+// endregion
+// region Shelf Info
+/**
+ * For incomplete shelf fetches via a secondary lookup, whether a given shelf w/ token should merge or replace.
+ * @param objectGraph
+ * @param token The token corresponding to the shelf being built
+ */
+export function shelfFetchShouldMergeWhenFetched(objectGraph, token) {
+ /**
+ * <rdar://problem/60069585> [POLISH] Arcade Coming Soon: If too few items, the coming soon swoosh should show larger items.
+ * Always reload posterLockup shelves on macOS to adapt if the presentationHint "isLowDensity" changed.
+ */
+ if (token.shelfStyle === "posterLockup" && objectGraph.client.isMac) {
+ return false;
+ }
+ else if (token.showingPlaceholders) {
+ return false;
+ }
+ // Always reload Arcade download pack shelf
+ // as there is always only a single cell (card) that contains lockups or their placeholders.
+ if (token.shelfStyle === "arcadeDownloadPackCard") {
+ return false;
+ }
+ if (token.shelfStyle === "ribbonBar" &&
+ isSome(token.initialHydratedItems) &&
+ token.initialHydratedItems.length > 0) {
+ return false;
+ }
+ // Generally true to support partially hydrated shelves.
+ return true;
+}
+// endregion
+// region Search Landing Shelves
+/**
+ * Modify `shelf` in place for global customization for all SLP shelves.
+ * @param objectGraph
+ * @param shelf
+ * @param token
+ */
+export function modifyShelfForSearchLandingGrouping(objectGraph, shelf, token) {
+ shelf.seeAllAction = null;
+ shelf.isHorizontal = false;
+ if (shelf.shouldFilterApps) {
+ // <rdar://problem/64772261> App Store: SLP: Installed app 'Open' button show up inconsistently (Filtering is sometimes not applied)
+ shelf.filteredItemsMinimumCount = 0;
+ shelf.filteringExcludedItems = token.includedAdAdamIds;
+ }
+}
+// endregion
+// region Card Shelves
+/**
+ * The shelf content type to use for the inline card display style.
+ * @param objectGraph
+ * @param {HorizontalCardDisplayStyle} style The display style for the inline card.
+ * @returns {ShelfContentType} The shelf content type to use.
+ */
+export function contentTypeForHorizontalCardDisplayStyle(objectGraph, style) {
+ switch (style) {
+ case "small":
+ return "smallStoryCard";
+ case "medium":
+ return "mediumStoryCard";
+ case "large":
+ return "largeStoryCard";
+ case "card":
+ if (objectGraph.client.isiOS) {
+ return "editorialStoryCard";
+ }
+ else {
+ return null;
+ }
+ default:
+ return null;
+ }
+}
+// endregion
+/**
+ * Determine the best content id to use given a media api data object
+ * @param objectGraph
+ * @param contentItem The media api data model to find the id from
+ */
+export function contentIdFromContentItem(objectGraph, contentItem) {
+ let contentId = mediaAttributes.attributeAsString(contentItem, "adamId");
+ if (!contentId) {
+ contentId = mediaAttributes.attributeAsString(contentItem, "contentId");
+ }
+ if (!contentId) {
+ contentId = mediaAttributes.attributeAsString(contentItem, "id");
+ }
+ return contentId;
+}
+export function unescapeHtmlString(str) {
+ if (serverData.isNull(str)) {
+ return null;
+ }
+ const escapedString = str
+ .replace(/&amp;/g, "&")
+ .replace(/&gt;/g, ">")
+ .replace(/&lt;/g, "<")
+ .replace(/&quot;/g, '"')
+ .replace(/&#39;/g, "'")
+ .replace(/&#96;/g, "`")
+ .replace(/\r\n/g, " ")
+ .replace(/&nbsp;/g, " ")
+ .replace(/<span>/g, "")
+ .replace(/<\/span>/g, "")
+ .replace(/<br>/g, " ")
+ .replace(/\u23ce/g, "")
+ .replace(/<i>/g, "")
+ .replace(/<\/i>/g, "")
+ .replace(/<b>/g, "")
+ .replace(/<\/b>/g, "");
+ if (escapedString.match(/^\s*$/)) {
+ return null;
+ }
+ return escapedString;
+}
+/**
+ * Update the shelf header to use the provided seeAll action.
+ * @param objectGraph The App Store object graph used to check feature flags
+ * @param shelfHeader The shelf header to update
+ * @param seeAllAction The "See All" action to apply
+ */
+export function replaceShelfHeaderSeeAllAction(objectGraph, shelfHeader, seeAllAction) {
+ if (objectGraph.featureFlags.isEnabled("shelf_header")) {
+ // Modern headers make title tappable with a chevron (>).
+ shelfHeader.titleAction = seeAllAction;
+ }
+ else {
+ // Legacy headers show "See All" textual button on trailing edge.
+ shelfHeader.accessoryAction = seeAllAction;
+ }
+}
+/**
+ * Update the shelf header to use the provided seeAll action.
+ * @param objectGraph The App Store object graph used to check feature flags
+ * @param shelf The shelf whose header needs updating
+ * @param seeAllAction The see all action to apply
+ */
+export function replaceShelfSeeAllAction(objectGraph, shelf, seeAllAction) {
+ if (objectGraph.featureFlags.isEnabled("shelf_header")) {
+ if (isSome(shelf.header)) {
+ replaceShelfHeaderSeeAllAction(objectGraph, shelf.header, seeAllAction);
+ }
+ else {
+ shelf.header = {
+ titleAction: seeAllAction,
+ };
+ }
+ }
+ else {
+ shelf.seeAllAction = seeAllAction;
+ }
+}
+//# sourceMappingURL=grouping-shelf-controller-common.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-shelf-controller.js
new file mode 100644
index 0000000..fabf85c
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-shelf-controller.js
@@ -0,0 +1,395 @@
+import { isSome } from "@jet/environment";
+import * as models from "../../../api/models";
+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 mediaUrlMapping from "../../builders/url-mapping";
+import { shouldUsePrerenderedIconArtwork } from "../../content/content";
+import * as metricsHelpersImpressions from "../../metrics/helpers/impressions";
+import * as metricsHelpersLocation from "../../metrics/helpers/location";
+import * as impressionDemotion from "../../personalization/on-device-impression-demotion";
+import * as productVariants from "../../product-page/product-page-variants";
+import * as refresh from "../../refresh/page-refresh-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+/**
+ * A GroupingShelfController is responsible for all the logic around parsing and rending
+ * a single grouping page shelf.
+ *
+ * The `ShelfMetadata` is a type the is specific for a given shelf and has some additional data needed to render
+ * that shelf.
+ */
+export class GroupingShelfController {
+ // endregion
+ // region AnyGroupingShelfController
+ /**
+ * Indicates whether this grouping shelf controller can create a shelf for the given mediaApiData.
+ * @param objectGraph The App Store dependency graph
+ * @param mediaApiData The outer data object containing the FC properties and data
+ * @param featuredContentId The featured content id for this shelf data
+ * @param nativeGroupingShelfId The id of the custom shelf type, one not defined on the server
+ */
+ supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId) {
+ return this._supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId);
+ }
+ /**
+ * Indicates whether this grouping shelf controller can create a shelf for the given mediaApiData.
+ * @param objectGraph The App Store dependency graph
+ * @param mediaApiData The outer data object containing the FC properties and data
+ * @param featuredContentId The featured content id for this shelf data
+ * @param nativeGroupingShelfId The id of the custom shelf type, one not defined on the server
+ */
+ _supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId) {
+ const isFeaturedContentIdSupported = this.supportedFeaturedContentIds.has(featuredContentId);
+ let isNativeGroupingShelfIdSupported;
+ if (serverData.isDefinedNonNull(nativeGroupingShelfId)) {
+ isNativeGroupingShelfIdSupported = this.supportedNativeGroupingShelfIds.has(nativeGroupingShelfId);
+ }
+ else {
+ isNativeGroupingShelfIdSupported = true;
+ }
+ return isFeaturedContentIdSupported && isNativeGroupingShelfIdSupported;
+ }
+ /**
+ * This method will return a grouping page shelf regardless of the type of controller
+ * @param objectGraph The App Store dependency graph
+ * @param groupingParseContext The parse context for the grouping page so far
+ * @param mediaApiData The outer data object containing the FC properties and data
+ * @param baseShelfToken The base grouping shelf token created by the grouping-controller
+ * @param baseMetricsOptions The minimum set of metrics options for this shelf, created by the
+ * grouping page controller
+ */
+ createShelf(objectGraph, mediaApiData, groupingParseContext, baseShelfToken, baseMetricsOptions) {
+ var _a, _b, _c;
+ const typedMediaApiData = mediaApiData;
+ const shelfData = this.initialShelfDataFromGroupingMediaApiData(objectGraph, typedMediaApiData);
+ const shelfToken = this.shelfTokenFromBaseTokenAndMediaApiData(objectGraph, typedMediaApiData, baseShelfToken, groupingParseContext);
+ const shelfMetricsOptions = this.shelfMetricsOptionsFromBaseMetricsOptions(objectGraph, shelfToken, baseMetricsOptions);
+ const hasShelfMetricsOptions = serverData.isDefinedNonNullNonEmpty(shelfMetricsOptions);
+ if (hasShelfMetricsOptions && this.shouldImpressShelf()) {
+ metricsHelpersLocation.pushContentLocation(objectGraph, shelfMetricsOptions, shelfToken.title);
+ }
+ /// Reorder the shelf contents based on the impression data if available
+ if (serverData.isDefinedNonNullNonEmpty(shelfData.shelfContents)) {
+ shelfData.shelfContents = impressionDemotion.personalizeDataItems(shelfData.shelfContents, (_a = groupingParseContext.recoImpressionData) !== null && _a !== void 0 ? _a : {}, (_b = baseMetricsOptions === null || baseMetricsOptions === void 0 ? void 0 : baseMetricsOptions.recoMetricsData) !== null && _b !== void 0 ? _b : {});
+ }
+ const shelf = this._createShelf(objectGraph, shelfToken, shelfData, groupingParseContext);
+ if (hasShelfMetricsOptions && this.shouldImpressShelf()) {
+ metricsHelpersLocation.popLocation(shelfMetricsOptions.locationTracker);
+ if (serverData.isDefinedNonNull(shelf)) {
+ metricsHelpersImpressions.addImpressionFields(objectGraph, shelf, shelfMetricsOptions);
+ // rdar://84952935 (Placeholder shelves are not being impressed
+ // For placeholder shelves we end up replacing the entire shelf, so we need to make sure the original
+ // impression metrics are included in the token, so they can be added when the real content is fetched
+ // We're doing this here because this is where we decide whether the original shelf should be impressed
+ if (((_c = shelf.url) === null || _c === void 0 ? void 0 : _c.length) > 0 &&
+ serverData.isDefinedNonNullNonEmpty(shelf.impressionMetrics) &&
+ shelfToken.showingPlaceholders) {
+ const originalShelfUrlString = shelf.url;
+ try {
+ // Extract the token from the URL.
+ // Note: Although we have access to the shelfToken here, we do not know that
+ // the url was constructed from token in its current state. To be safe,
+ // if not efficient, we reverse engineer the URL to get the token.
+ const originalShelfUrl = urls.URL.from(originalShelfUrlString);
+ const encodedToken = originalShelfUrl.pathComponents().pop();
+ const shelfTokenFromUrl = JSON.parse(decodeURIComponent(encodedToken));
+ // Modify the token to include the impressions metrics.
+ shelfTokenFromUrl.originalPlaceholderShelfImpressionMetrics = shelf.impressionMetrics;
+ groupingShelfControllerCommon.updateShelfUrlWithNewToken(objectGraph, shelf, shelfTokenFromUrl);
+ }
+ catch {
+ shelf.url = originalShelfUrlString;
+ }
+ }
+ }
+ }
+ this.finalizeInitialShelfForDisplay(objectGraph, shelf, shelfToken, shelfData, groupingParseContext);
+ if (hasShelfMetricsOptions && this.shouldPrepareLocationTrackerForNextPosition()) {
+ metricsHelpersLocation.nextPosition(groupingParseContext.metricsLocationTracker);
+ }
+ return shelf;
+ }
+ /**
+ * Initialize a builder with globally unique name.
+ *
+ * @param {string} builderClass Globally unique name.
+ */
+ constructor(builderClass) {
+ // region Supported Types
+ this.supportedFeaturedContentIds = new Set([]);
+ this.supportedNativeGroupingShelfIds = new Set([]);
+ this.builderClass = builderClass;
+ }
+ /**
+ * Determines the strategy for fetching incomplete shelves based on feature flags and shelf type
+ *
+ * @param objectGraph - The application store object graph.
+ * @returns The strategy for fetching incomplete shelves, either on shelf appearance or on page load.
+ */
+ incompleteShelfFetchStrategy(objectGraph) {
+ if (objectGraph.client.isiOS) {
+ return models.IncompleteShelfFetchStrategy.OnShelfWillAppear;
+ }
+ else {
+ return models.IncompleteShelfFetchStrategy.OnPageLoad;
+ }
+ }
+ // endregion
+ // region Metrics
+ /**
+ * Return the shelf metrics options to use for this specific shelf. Using the base options from the grouping
+ * page controller
+ *
+ * @param objectGraph The App Store dependency graph
+ * @param shelfToken The shelf shelfToken for this current shelf creation request
+ * @param baseMetricsOptions The minimum set of metrics options for this shelf, created by the
+ * grouping page controller
+ */
+ shelfMetricsOptionsFromBaseMetricsOptions(objectGraph, shelfToken, baseMetricsOptions) {
+ return baseMetricsOptions;
+ }
+ /**
+ * Whether the shelf itself should be impressed, there are some cases where the shelf itself
+ * does not get impressed, just the contents.
+ */
+ shouldImpressShelf() {
+ return true;
+ }
+ /**
+ * Whether we should move the location tracker to the next position after creating our shelf
+ */
+ shouldPrepareLocationTrackerForNextPosition() {
+ return true;
+ }
+ // endregion
+ // Shelf Finalization
+ /**
+ * This method will set any required fields on our shelf once it has created as part of the initial page rendering.
+ * This includes things like timing metrics, hiding empty shelves etc.
+ *
+ * @param objectGraph The App Store dependency graph
+ * @param shelf The created shelf
+ * @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 for the grouping page so far
+ * @private
+ */
+ finalizeInitialShelfForDisplay(objectGraph, shelf, shelfToken, shelfData, groupingParseContext) {
+ var _a, _b;
+ if (serverData.isNullOrEmpty(shelf)) {
+ return;
+ }
+ // Should not show see all links on search groupings
+ if (shelfToken.isSearchLandingPage) {
+ groupingShelfControllerCommon.modifyShelfForSearchLandingGrouping(objectGraph, shelf, shelfToken);
+ }
+ if (((_a = shelf.url) === null || _a === void 0 ? void 0 : _a.length) > 0 &&
+ serverData.isDefinedNonNullNonEmpty(groupingParseContext.additionalShelfParameters)) {
+ shelf.url = urls.URL.from(shelf.url)
+ .append("query", groupingParseContext.additionalShelfParameters)
+ .build();
+ }
+ // If we're on iOS and no prior fetch strategy has been defined, set the fetchStrategy to OnShelfWillAppear.
+ shelf.fetchStrategy = this.incompleteShelfFetchStrategy(objectGraph);
+ // Shelf will fetch content after sending follow-up fetch request.
+ const willFetchShelfContent = isSome(shelf) && ((_b = shelf.url) === null || _b === void 0 ? void 0 : _b.length) > 0;
+ if (serverData.isNullOrEmpty(shelf.items) && !willFetchShelfContent) {
+ shelf.isHidden = true;
+ }
+ shelf.accessibilityMetadata = createShelfAccessibilityMetadata(objectGraph, shelf);
+ }
+ /**
+ * This method will set any required fields on our shelf once it has been fetched as a result of a secondary fetch.
+ * This includes things like timing metrics, hiding empty shelves etc.
+ *
+ * @param objectGraph The AppStore dependency graph
+ * @param shelf The created shelf
+ * @param shelfToken The shelf shelfToken for this current shelf creation request
+ * @param shelfData The media api shelfContents array for this shelf
+ * @private
+ */
+ finalizeSecondaryShelfForDisplay(objectGraph, shelf, shelfToken, shelfData) {
+ if (serverData.isNullOrEmpty(shelf)) {
+ return;
+ }
+ if (shelfToken.remainingItems.length) {
+ const remainingIds = shelfToken.remainingItems.map((data) => {
+ return data.id;
+ });
+ objectGraph.console.warn("Could not load items for: " + remainingIds.join(","));
+ }
+ if (shelf) {
+ shelf.mergeWhenFetched = groupingShelfControllerCommon.shelfFetchShouldMergeWhenFetched(objectGraph, shelfToken);
+ shelf.networkTimingMetrics = shelfData.responseTimingValues;
+ shelf.nextPreferredContentRefreshDate = refresh.nextPreferredContentRefreshDateForController(refresh.newPageRefreshController());
+ }
+ // Merge the original impression metrics with the newly created impression metrics.
+ if (serverData.isDefinedNonNullNonEmpty(shelfToken.originalPlaceholderShelfImpressionMetrics)) {
+ // If the `shelf.impressionMetrics` is null, we just defer to the original metrics.
+ if (serverData.isNull(shelf.impressionMetrics)) {
+ shelf.impressionMetrics = shelfToken.originalPlaceholderShelfImpressionMetrics;
+ }
+ else {
+ for (const key in shelfToken.originalPlaceholderShelfImpressionMetrics.fields) {
+ if (Object.prototype.hasOwnProperty.call(shelfToken.originalPlaceholderShelfImpressionMetrics.fields, key)) {
+ shelf.impressionMetrics.fields[key] =
+ shelfToken.originalPlaceholderShelfImpressionMetrics.fields[key];
+ }
+ }
+ }
+ }
+ if (!shelfToken.hasExistingContent && serverData.isNullOrEmpty(shelf.items)) {
+ shelf.isHidden = true;
+ }
+ // Should not show see all links on search groupings
+ if (shelfToken.isSearchLandingPage) {
+ groupingShelfControllerCommon.modifyShelfForSearchLandingGrouping(objectGraph, shelf, shelfToken);
+ }
+ shelf.accessibilityMetadata = createShelfAccessibilityMetadata(objectGraph, shelf);
+ }
+ // endregion
+ // region ShelfBuilder
+ async handleShelf(objectGraph, url, parameters, matchedRuleIdentifier) {
+ const tokenJson = parameters["token"];
+ const shelfToken = JSON.parse(tokenJson);
+ shelfToken.isFirstRender = false;
+ try {
+ const shelfData = await this.secondaryShelfDataForShelfUrl(objectGraph, url, shelfToken, parameters);
+ const shelf = this._createShelf(objectGraph, shelfToken, shelfData, null);
+ this.finalizeSecondaryShelfForDisplay(objectGraph, shelf, shelfToken, shelfData);
+ return shelf;
+ }
+ catch (error) {
+ if (shelfToken && !shelfToken.hasExistingContent) {
+ const hiddenShelf = new models.Shelf(shelfToken.shelfStyle);
+ hiddenShelf.isHidden = true;
+ return hiddenShelf;
+ }
+ else {
+ throw error;
+ }
+ }
+ }
+ shelfRoute(objectGraph) {
+ if (serverData.isDefinedNonNullNonEmpty(this.supportedNativeGroupingShelfIds)) {
+ return routesForNativeGroupingShelfIds(this.supportedNativeGroupingShelfIds);
+ }
+ else {
+ return routesForFeaturedContentIds(this.supportedFeaturedContentIds);
+ }
+ }
+ // endregion
+ // region Static Base Helpers
+ /**
+ * This is a standard default implementation for the secondary shelf data fetch. This can be used for all the
+ * grouping shelf controls that dont implement a custom ShelfDataType
+ * @param objectGraph
+ * @param shelfUrl
+ * @param parameters
+ */
+ static async secondaryGroupingShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters) {
+ return await GroupingShelfController.secondaryGroupingShelfMediaApiData(objectGraph, shelfUrl, shelfToken, parameters).then((mediaApiData) => {
+ const hydratedItems = groupingShelfControllerCommon.hydratedRemainingItemsForShelfTokenFromMediaApiData(objectGraph, shelfToken, mediaApiData);
+ return {
+ shelfContents: hydratedItems,
+ responseTimingValues: mediaApiData[ResponseMetadata.timingValues],
+ };
+ });
+ }
+ /**
+ * This is a standard default implementation for the media api request, for an incomplete grouping shelf.
+ *
+ * @param objectGraph
+ * @param shelfUrl
+ * @param parameters
+ */
+ static async secondaryGroupingShelfMediaApiData(objectGraph, shelfUrl, shelfToken, parameters) {
+ const urlString = shelfUrl.build();
+ let request;
+ if (mediaUrlMapping.isMediaUrl(objectGraph, shelfUrl)) {
+ request = new mediaDataFetching.Request(objectGraph, urlString);
+ }
+ else {
+ request = groupingShelfControllerCommon.generateShelfRequest(objectGraph, shelfToken, parameters);
+ }
+ if (!request) {
+ return await Promise.reject(new Error(`Could not construct media API request for: ${shelfUrl}`));
+ }
+ request.includingAdditionalPlatforms(defaultMediaApiPlatforms(objectGraph));
+ request.includingAttributes(defaultMediaApiAttributes(objectGraph));
+ request.usingCustomAttributes(productVariants.shouldFetchCustomAttributes(objectGraph));
+ request.attributingTo(shelfUrl.build());
+ return await mediaNetwork.fetchData(objectGraph, request).then((mediaApiData) => {
+ groupingShelfControllerCommon.flushRequestedItemsFromShelfToken(shelfToken, request.ids);
+ return mediaApiData;
+ });
+ }
+}
+export function createShelfAccessibilityMetadata(objectGraph, shelf) {
+ var _a;
+ let accessibilityLabel = objectGraph.loc.string("Shelves.Accessibility.Label");
+ if (isSome(shelf.title)) {
+ accessibilityLabel = `${shelf.title}, ${accessibilityLabel}`;
+ }
+ else if (isSome((_a = shelf.header) === null || _a === void 0 ? void 0 : _a.title)) {
+ accessibilityLabel = `${shelf.header.title}, ${accessibilityLabel}`;
+ }
+ const accessibilityRoleDescription = objectGraph.loc.string("Shelves.Accessibility.RoleDescription");
+ return {
+ label: accessibilityLabel,
+ roleDescription: accessibilityRoleDescription,
+ };
+}
+function routeForFeaturedContentId(featuredContentId, nativeGroupingShelfId, additionalQueryParams) {
+ const query = serverData.isDefinedNonNullNonEmpty(additionalQueryParams)
+ ? [...additionalQueryParams]
+ : [];
+ query.push(`${Parameters.groupingFeaturedContentId}=${featuredContentId}`);
+ if (serverData.isDefinedNonNullNonEmpty(nativeGroupingShelfId)) {
+ query.push(`${Parameters.nativeGroupingShelfId}=${nativeGroupingShelfId}`);
+ }
+ return {
+ protocol: Protocol.internal,
+ path: `/${Path.grouping}/${Path.shelf}/{token}`,
+ query: query,
+ };
+}
+export function routesForFeaturedContentIds(featuredContentIds, additonalQueryParams) {
+ const routes = [];
+ for (const featuredContentId of featuredContentIds) {
+ routes.push(routeForFeaturedContentId(featuredContentId, null, additonalQueryParams));
+ }
+ return routes;
+}
+export function routesForNativeGroupingShelfIds(nativeGroupingShelfIds, additonalQueryParams) {
+ const routes = [];
+ for (const nativeGroupingShelfId of nativeGroupingShelfIds) {
+ routes.push(routeForFeaturedContentId(-1 /* FeaturedContentID.Native_GroupingShelf */, nativeGroupingShelfId, additonalQueryParams));
+ }
+ return routes;
+}
+// region Media Api Attributes
+function defaultMediaApiPlatforms(objectGraph) {
+ return mediaDataFetching.defaultAdditionalPlatformsForClient(objectGraph);
+}
+function defaultMediaApiAttributes(objectGraph) {
+ const attributes = ["editorialArtwork", "isAppleWatchSupported", "requiredCapabilities", "badge-content"];
+ if (objectGraph.appleSilicon.isSupportEnabled) {
+ attributes.push("macRequiredCapabilities");
+ }
+ if (objectGraph.client.isMac) {
+ attributes.push("hasMacIPAPackage");
+ }
+ if (objectGraph.bag.enableUpdatedAgeRatings) {
+ attributes.push("ageRating");
+ }
+ if (shouldUsePrerenderedIconArtwork(objectGraph)) {
+ attributes.push("iconArtwork");
+ }
+ return attributes;
+}
+// endregion
+//# sourceMappingURL=grouping-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-small-breakout-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-small-breakout-shelf-controller.js
new file mode 100644
index 0000000..d7eac20
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-small-breakout-shelf-controller.js
@@ -0,0 +1,168 @@
+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 mediaPlatformAttributes from "../../../foundation/media/platform-attributes";
+import * as mediaRelationship from "../../../foundation/media/relationships";
+import * as color from "../../../foundation/util/color-util";
+import * as breakoutsCommon from "../../arcade/breakouts-common";
+import * as contentAttributes from "../../content/attributes";
+import * as content from "../../content/content";
+import * as flowPreview from "../../content/flow-preview";
+import * as metricsHelpersClicks from "../../metrics/helpers/clicks";
+import * as metricsHelpersImpressions from "../../metrics/helpers/impressions";
+import * as metricsHelpersLocation from "../../metrics/helpers/location";
+import * as article from "../../today/article";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+export class GroupingSmallBreakoutShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingSmallBreakoutShelfController");
+ this.supportedFeaturedContentIds = new Set([480 /* groupingTypes.FeaturedContentID.AppStore_Breakout */]);
+ }
+ // endregion
+ // region GroupingShelfController
+ _supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId) {
+ if (!super._supports(objectGraph, mediaApiData, featuredContentId, nativeGroupingShelfId)) {
+ return false;
+ }
+ const breakoutStyle = mediaAttributes.attributeAsString(mediaApiData, "displayStyle");
+ return breakoutStyle === "small";
+ }
+ // 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: mediaRelationship.relationshipCollection(mediaApiData, "contents") };
+ }
+ /**
+ * 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 GroupingShelfController.secondaryGroupingShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters);
+ }
+ /**
+ * 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 Metrics
+ shouldImpressShelf() {
+ return false;
+ }
+ // 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) {
+ const items = [];
+ for (const data of shelfData.shelfContents) {
+ if (serverData.isNull(data.attributes) || groupingShelfControllerCommon.shouldDefer(shelfToken)) {
+ shelfToken.isDeferring = true;
+ shelfToken.remainingItems.push(data);
+ continue;
+ }
+ const primaryContent = content.primaryContentForData(objectGraph, data);
+ if (breakoutsCommon.requiresPrimaryContent(objectGraph, data) &&
+ !mediaAttributes.hasAttributes(primaryContent)) {
+ shelfToken.isDeferring = true;
+ shelfToken.remainingItems.push(data);
+ shelfToken.relationshipToFetch = "primary-content";
+ continue;
+ }
+ const metricsOptions = {
+ targetType: "smallBreakout",
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(data),
+ };
+ // If this EI can and should show the expected release date of an arcade app, override the badge.
+ let badgeTitle;
+ const fallbackLabel = mediaAttributes.attributeAsString(data, "label");
+ if (contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, data, "showExpectedReleaseDate")) {
+ badgeTitle = objectGraph.loc.uppercased(content.dynamicPreorderDateFromData(objectGraph, primaryContent, fallbackLabel));
+ }
+ else {
+ badgeTitle = fallbackLabel;
+ }
+ let badge = { type: "none" };
+ if ((badgeTitle === null || badgeTitle === void 0 ? void 0 : badgeTitle.length) > 0) {
+ badge = {
+ type: "text",
+ title: badgeTitle,
+ };
+ }
+ const title = content.editorialNotesFromData(objectGraph, data, "name") ||
+ contentAttributes.contentAttributeAsString(objectGraph, primaryContent, "name");
+ const description = content.editorialNotesFromData(objectGraph, data, "short") ||
+ contentAttributes.contentAttributeAsString(objectGraph, primaryContent, "tagline");
+ const artwork = content.iconFromData(objectGraph, primaryContent, {
+ useCase: 5 /* content.ArtworkUseCase.ArcadeSmallBreakout */,
+ withJoeColorPlaceholder: true,
+ });
+ const attributePlatform = contentAttributes.bestAttributePlatformFromData(objectGraph, data);
+ const artworkData = mediaPlatformAttributes.platformAttributeAsDictionary(primaryContent, attributePlatform, "artwork");
+ const backgroundColor = color.fromHex(serverData.asString(artworkData, "textColor4")) || (artwork === null || artwork === void 0 ? void 0 : artwork.backgroundColor);
+ const details = new models.BreakoutDetails(title, description, badge, null, breakoutsCommon.detailBackgroundStyleFromColor(objectGraph, backgroundColor), null);
+ const smallBreakout = new models.SmallBreakout(details, artwork, backgroundColor);
+ const impressionOptions = metricsHelpersImpressions.impressionOptions(objectGraph, data, smallBreakout.details.title, metricsOptions);
+ const productData = article.productDataFromArticle(objectGraph, data);
+ const isPreorder = contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, productData, "isPreorder");
+ impressionOptions.isPreorder = isPreorder;
+ metricsHelpersImpressions.addImpressionFields(objectGraph, smallBreakout, impressionOptions);
+ // Push the breakout here so that the click action has the breakout in its location
+ // but we do not want to add it to the overall location tracker, so pop it right after adding it to the button
+ // action
+ // <rdar://problem/60883269> Metrics: Arcade: Container values requested in Location field
+ metricsHelpersLocation.pushContentLocation(objectGraph, impressionOptions, smallBreakout.details.title);
+ const breakoutAction = breakoutsCommon.actionFromData(objectGraph, data);
+ const breakoutClickOptions = {
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ recoMetricsData: mediaDataStructure.metricsFromMediaApiObject(data),
+ targetType: "button",
+ id: data.id,
+ };
+ metricsHelpersClicks.addClickEventToAction(objectGraph, breakoutAction, breakoutClickOptions);
+ smallBreakout.details.callToActionButtonAction = breakoutAction;
+ smallBreakout.clickAction = breakoutAction;
+ metricsHelpersLocation.popLocation(metricsOptions.locationTracker);
+ // Set flow preview actions
+ smallBreakout.flowPreviewActionsConfiguration =
+ flowPreview.flowPreviewActionsConfigurationForProductFromData(objectGraph, data, true, shelfToken === null || shelfToken === void 0 ? void 0 : shelfToken.clientIdentifierOverride, breakoutAction, metricsOptions, breakoutClickOptions);
+ items.push(smallBreakout);
+ }
+ const shelf = new models.Shelf("smallBreakout");
+ shelf.isHorizontal = false;
+ shelf.items = items;
+ shelf.url = groupingShelfControllerCommon.createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, shelfToken);
+ if (groupingParseContext.shelves.length === 0) {
+ shelf.presentationHints = { isFirstShelf: true };
+ }
+ return shelf;
+ }
+}
+//# sourceMappingURL=grouping-small-breakout-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-tag-brick-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-tag-brick-shelf-controller.js
new file mode 100644
index 0000000..66baba8
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-tag-brick-shelf-controller.js
@@ -0,0 +1,246 @@
+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 content from "../../content/content";
+import * as flowPreview from "../../content/flow-preview";
+import * as lockupsEditorialContext from "../../lockups/editorial-context";
+import * as metricsHelpersClicks from "../../metrics/helpers/clicks";
+import * as metricsHelpersImpressions from "../../metrics/helpers/impressions";
+import * as metricsHelpersLocation from "../../metrics/helpers/location";
+import * as placeholders from "../../placeholders/placeholders";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./grouping-shelf-controller-common";
+import { defaultLayoutSize } from "../../../foundation/media/data-fetching";
+import { isSome } from "@jet/environment";
+export class GroupingTagBrickShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingTagBrickShelfController");
+ this.supportedFeaturedContentIds = new Set([
+ 584 /* groupingTypes.FeaturedContentID.AppStore_TagsBrick */,
+ 587 /* groupingTypes.FeaturedContentID.AppStore_PersonalizedTagsBrick */,
+ ]);
+ }
+ // 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) {
+ var _a;
+ if (isSome(mediaApiData)) {
+ const shelfContents = (_a = mediaRelationship.relationshipCollection(mediaApiData, "contents")) !== null && _a !== void 0 ? _a : null;
+ return { shelfContents: shelfContents };
+ }
+ return { shelfContents: 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 GroupingShelfController.secondaryGroupingShelfMediaApiData(objectGraph, shelfUrl, shelfToken, parameters).then((shelfData) => {
+ return {
+ shelfContents: shelfData.data,
+ };
+ });
+ }
+ /**
+ * 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) {
+ // If suppress text is not provided, default to hiding.
+ let suppressText = mediaAttributes.attributeAsBoolean(mediaApiData, "suppressText");
+ if (serverData.isNull(suppressText)) {
+ suppressText = true;
+ }
+ const brickShelfToken = {
+ ...baseShelfToken,
+ showSupplementaryText: !suppressText,
+ };
+ brickShelfToken.clientIdentifierOverride = lockupsEditorialContext.clientIdentifierForEditorialContextInData(objectGraph, mediaApiData);
+ return brickShelfToken;
+ }
+ // 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) {
+ const items = [];
+ const remainingBricks = []; // Data where metadata was missing.
+ const displayStyle = serverData.asString(shelfToken.featuredContentData.attributes, "displayStyle");
+ const isCategoryBrick = displayStyle === "small";
+ const shelf = new models.Shelf("tagBrick");
+ const layoutSize = serverData.asNumber(shelfToken.featuredContentData.attributes, "layoutStyle.layoutSize");
+ shelf.rowsPerColumn = layoutSize !== null && layoutSize !== void 0 ? layoutSize : defaultLayoutSize(objectGraph);
+ shelf.isHorizontal = true;
+ if (isSome(shelfData.shelfContents)) {
+ for (const tagData of shelfData.shelfContents) {
+ const brickModel = GroupingTagBrickShelfController.createBrick(objectGraph, tagData, isCategoryBrick, shelfToken.metricsPageInformation, shelfToken.metricsLocationTracker, shelfToken, groupingParseContext);
+ if (!isSome(brickModel === null || brickModel === void 0 ? void 0 : brickModel.shortEditorialDescription)) {
+ shelfToken.remainingItems.push(tagData);
+ remainingBricks.push(tagData);
+ continue;
+ }
+ if (isSome(brickModel)) {
+ items.push(brickModel);
+ }
+ metricsHelpersLocation.nextPosition(shelfToken.metricsLocationTracker);
+ }
+ }
+ if (serverData.isDefinedNonNull(shelfToken.presentationHints)) {
+ shelf.presentationHints = shelfToken.presentationHints;
+ }
+ if (serverData.isDefinedNonNull(shelfToken.showSupplementaryText)) {
+ shelf.presentationHints = {
+ ...shelf.presentationHints,
+ showSupplementaryText: shelfToken.showSupplementaryText,
+ };
+ }
+ // We don't need this in our incomplete shelf URL, so we'll preemptively remove it.
+ delete shelfToken.maxItemCount;
+ // Override `featuredContentData` to only have remaining bricks that must be fetched.
+ if (serverData.isDefinedNonNull(serverData.traverse(shelfToken.featuredContentData, "relationships.contents.data")) &&
+ isSome(shelfToken.featuredContentData.relationships)) {
+ shelfToken.featuredContentData.relationships["contents"].data = remainingBricks;
+ }
+ // Set metadata
+ shelf.title = shelfToken.title;
+ shelf.subtitle = shelfToken.subtitle;
+ if (isCategoryBrick) {
+ const displayCount = serverData.asNumber(shelfToken.featuredContentData.attributes, "displayCount");
+ shelf.items = items.slice(0, displayCount !== null && displayCount !== void 0 ? displayCount : items.length);
+ }
+ else {
+ shelf.items = items;
+ }
+ // See all
+ const hasSeeAll = serverData.asBooleanOrFalse(shelfToken.featuredContentData.attributes, "hasSeeAll");
+ if (isCategoryBrick && hasSeeAll) {
+ // Setup shelf
+ const seeAllShelf = new models.Shelf("categoryBrick");
+ seeAllShelf.items = items;
+ seeAllShelf.presentationHints = { isSeeAllContext: true };
+ // Setup Page
+ const seeAllPage = new models.GenericPage([seeAllShelf]);
+ seeAllPage.title = shelfToken.title;
+ // Setup action
+ const seeAllAction = new models.FlowAction("page");
+ seeAllAction.title = objectGraph.loc.string("ACTION_SEE_ALL");
+ seeAllAction.pageData = seeAllPage;
+ // Connect action
+ shelf.seeAllAction = seeAllAction;
+ // Metrics
+ metricsHelpersClicks.addClickEventToSeeAllAction(objectGraph, seeAllAction, "", {
+ pageInformation: shelfToken.metricsPageInformation,
+ locationTracker: shelfToken.metricsLocationTracker,
+ });
+ }
+ // 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);
+ }
+ shelfToken.presentationHints = shelf.presentationHints;
+ shelf.url = groupingShelfControllerCommon.createShelfTokenUrlIfNecessaryForShelf(objectGraph, shelf, shelfToken);
+ return shelf;
+ }
+ // region Static Helpers
+ static createBrick(objectGraph, brickData, searchCategoryBricks, metricsPageInformation, metricsLocationTracker, shelfToken, groupingParseContext) {
+ var _a, _b, _c;
+ const metricsBrickData = (_a = mediaDataStructure.metricsFromMediaApiObject(brickData)) !== null && _a !== void 0 ? _a : undefined;
+ const metricsOptions = {
+ targetType: searchCategoryBricks ? "tile" : "brick",
+ pageInformation: metricsPageInformation,
+ locationTracker: metricsLocationTracker,
+ recoMetricsData: metricsBrickData,
+ };
+ const metadata = groupingShelfControllerCommon.metadataForTag(objectGraph, brickData, shelfToken !== null && shelfToken !== void 0 ? shelfToken : null, metricsOptions);
+ if (!metadata) {
+ const brickModel = new models.Brick();
+ brickModel.shortEditorialDescription = mediaAttributes.attributeAsString(brickData, "name");
+ return brickModel;
+ }
+ const brickModel = new models.Brick();
+ // Setup Artwork
+ const artworkOptions = {
+ useCase: 18 /* content.ArtworkUseCase.GroupingBrick */,
+ };
+ const collectionIcons = groupingShelfControllerCommon.artworkForTags(objectGraph, brickData, 1060, 520, artworkOptions, metricsOptions);
+ if (collectionIcons.length > 0) {
+ const collectionIconBackgroundColor = collectionIcons[0].backgroundColor;
+ brickModel.collectionIcons = collectionIcons;
+ if (isSome(collectionIconBackgroundColor) && (collectionIconBackgroundColor === null || collectionIconBackgroundColor === void 0 ? void 0 : collectionIconBackgroundColor.type) === "rgb") {
+ brickModel.backgroundColor =
+ (_b = content.closestTagBackgroundColorForIcon(collectionIconBackgroundColor)) !== null && _b !== void 0 ? _b : undefined;
+ }
+ }
+ brickModel.accessibilityLabel = metadata.title;
+ // Set supplementary text.
+ brickModel.shortEditorialDescription = metadata.shortEditorialDescription;
+ // Set action
+ brickModel.clickAction = metadata.action;
+ // Set personalization
+ const brickFeaturedContentId = mediaAttributes.attributeAsNumber(brickData, "editorialElementKind");
+ if (brickFeaturedContentId === 435 /* groupingTypes.FeaturedContentID.AppStore_MSOBrickMarker */) {
+ brickModel.personalizationStyle = "mso";
+ }
+ // Set flow preview actions
+ const contentData = mediaRelationship.relationshipData(objectGraph, brickData, "contents");
+ if (serverData.isDefinedNonNull(contentData)) {
+ const metricsClickOptions = metricsHelpersClicks.clickOptionsForLockup(objectGraph, contentData, metricsOptions);
+ const clientIdentifierOverride = (_c = shelfToken === null || shelfToken === void 0 ? void 0 : shelfToken.clientIdentifierOverride) !== null && _c !== void 0 ? _c : null;
+ brickModel.flowPreviewActionsConfiguration = flowPreview.flowPreviewActionsConfigurationForProductFromData(objectGraph, brickData, true, clientIdentifierOverride, brickModel.clickAction, metricsOptions, metricsClickOptions);
+ }
+ // Configure impressions
+ const impressionOptions = metricsHelpersImpressions.impressionOptions(objectGraph, brickData, metadata.title, metricsOptions);
+ metricsHelpersImpressions.addImpressionFields(objectGraph, brickModel, impressionOptions);
+ // Safe area
+ brickModel.artworkSafeArea = models.ChartOrCategorySafeArea.defaultTileArtworkSafeArea;
+ brickModel.textSafeArea = models.ChartOrCategorySafeArea.defaultTileTextSafeArea;
+ if (!brickModel.isValid()) {
+ return null;
+ }
+ return brickModel;
+ }
+ // endregion
+ // region Metrics
+ /**
+ * Return the shelf metrics options to use for this specific shelf. Using the base options from the grouping
+ * page controller
+ *
+ * @param objectGraph The App Store dependency graph
+ * @param shelfToken The shelf shelfToken for this current shelf creation request
+ * @param baseMetricsOptions The minimum set of metrics options for this shelf, created by the
+ * grouping page controller
+ */
+ shelfMetricsOptionsFromBaseMetricsOptions(objectGraph, shelfToken, baseMetricsOptions) {
+ const shelfMetricsOptions = { ...baseMetricsOptions };
+ const displayStyle = serverData.asString(shelfToken.featuredContentData.attributes, "displayStyle");
+ // If this is a Category Bricks shelf, configure metrics title accordingly.
+ if (displayStyle === "small") {
+ shelfMetricsOptions.title = "Browse Categories";
+ }
+ return shelfMetricsOptions;
+ }
+}
+//# sourceMappingURL=grouping-tag-brick-shelf-controller.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-tags-header-shelf-controller.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-tags-header-shelf-controller.js
new file mode 100644
index 0000000..bb6a950
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/shelf-controllers/grouping-tags-header-shelf-controller.js
@@ -0,0 +1,92 @@
+import * as models from "../../../api/models";
+import * as mediaRelationship from "../../../foundation/media/relationships";
+import { areAppTagsEnabled } from "../../util/app-tags-util";
+import { GroupingShelfController } from "./grouping-shelf-controller";
+import { attributeAsDictionary } from "@apple-media-services/media-api";
+import * as serverData from "../../../foundation/json-parsing/server-data";
+import { isSome } from "@jet/environment";
+import * as content from "../../content/content";
+import { artworkDictionaryFromData } from "../../arcade/breakouts-common";
+import * as color from "../../../foundation/util/color-util";
+/**
+ * This groupings controller adds the logic to display a header shelf on the groupings page. Th primary expereince
+ * is artwork and the secondary experience includes fallback icons.
+ */
+export class GroupingMediaPageHeaderShelfController extends GroupingShelfController {
+ // region Constructors
+ constructor() {
+ super("GroupingMediaPageHeaderShelfController");
+ this.supportedFeaturedContentIds = new Set([585 /* groupingTypes.FeaturedContentID.AppStore_MediaPageHeader */]);
+ }
+ // 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: mediaRelationship.relationshipCollection(mediaApiData, "contents") };
+ }
+ /**
+ * 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 GroupingShelfController.secondaryGroupingShelfDataForShelfUrl(objectGraph, shelfUrl, shelfToken, parameters);
+ }
+ /**
+ * 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) {
+ var _a;
+ if (!areAppTagsEnabled(objectGraph, "grouping")) {
+ return null;
+ }
+ const headerData = shelfData.shelfContents[0];
+ let title;
+ if (isSome(headerData)) {
+ const editorialNotes = attributeAsDictionary(headerData, "editorialNotes");
+ title = (_a = serverData.asString(editorialNotes, "name")) !== null && _a !== void 0 ? _a : null;
+ const artworkDictionary = artworkDictionaryFromData(objectGraph, headerData);
+ const artworkData = serverData.asDictionary(artworkDictionary, "categoryDetailStatic16x9");
+ const artworkFromData = content.artworkFromApiArtwork(objectGraph, artworkData, {
+ withJoeColorPlaceholder: true,
+ useCase: 0 /* content.ArtworkUseCase.Default */,
+ cropCode: "CDS.ApTCHM01",
+ });
+ if (isSome(title) && isSome(artworkFromData)) {
+ const isArtworkDark = color.isDarkColor(artworkFromData.backgroundColor);
+ const pageHeader = new models.MediaPageHeader(null, title, null, artworkFromData, null, null, false, null, null, isArtworkDark ? "dark" : "light");
+ const shelf = new models.Shelf("mediaPageHeader");
+ shelf.items = [pageHeader];
+ return shelf;
+ }
+ return null;
+ }
+ return null;
+ }
+}
+//# sourceMappingURL=grouping-tags-header-shelf-controller.js.map \ No newline at end of file