summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/common/grouping/grouping-common.js
diff options
context:
space:
mode:
authorrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
committerrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
commitbce557cc2dc767628bed6aac87301a1be7c5431b (patch)
treeb51a051228d01fe3306cd7626d4a96768aadb944 /node_modules/@jet-app/app-store/tmp/src/common/grouping/grouping-common.js
init commit
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/common/grouping/grouping-common.js')
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/grouping/grouping-common.js319
1 files changed, 319 insertions, 0 deletions
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/grouping/grouping-common.js b/node_modules/@jet-app/app-store/tmp/src/common/grouping/grouping-common.js
new file mode 100644
index 0000000..53294d3
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/grouping/grouping-common.js
@@ -0,0 +1,319 @@
+import { isSome } from "@jet/environment";
+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 mediaRelationship from "../../foundation/media/relationships";
+import * as artworkBuilder from "../content/artwork/artwork";
+import * as metricsHelpersClicks from "../metrics/helpers/clicks";
+import * as types from "./grouping-types";
+import { GroupingAppEventShelfController } from "./shelf-controllers/grouping-app-event-shelf-controller";
+import { GroupingArcadeFooterShelfController } from "./shelf-controllers/grouping-arcade-footer-shelf-controller";
+import { GroupingBrickShelfController } from "./shelf-controllers/grouping-brick-shelf-controller";
+import { GroupingRibbonBarShelfController } from "./shelf-controllers/grouping-ribbon-bar-shelf-controller";
+import { GroupingCategoryShelfController } from "./shelf-controllers/grouping-category-shelf-controller";
+import { GroupingEditorialCardShelfController } from "./shelf-controllers/grouping-editorial-card-shelf-controller";
+import { GroupingEditorialStoryCardShelfController } from "./shelf-controllers/grouping-editorial-story-card-shelf-controller";
+import { GroupingGameCenterActivityFeedController } from "./shelf-controllers/grouping-game-center-activity-feed-shelf-controller";
+import { GroupingGameCenterContinuePlayingShelfController } from "./shelf-controllers/grouping-game-center-continue-playing-shelf-controller";
+import { GroupingGameCenterPopularWithYourFriendsController } from "./shelf-controllers/grouping-game-center-popular-with-your-friends-shelf-controller";
+import { GroupingGameCenterReengagementShelfController } from "./shelf-controllers/grouping-game-center-reengagement-shelf-controller";
+import { GroupingGameCenterSuggestedFriendsController } from "./shelf-controllers/grouping-game-center-suggested-friends-shelf-controller";
+import { GroupingHeroCarouselShelfController } from "./shelf-controllers/grouping-hero-carousel-shelf-controller";
+import { GroupingHorizontalCardShelfController } from "./shelf-controllers/grouping-horizontal-card-shelf-controller";
+import { GroupingLargeBreakoutShelfController } from "./shelf-controllers/grouping-large-breakout-shelf-controller";
+import { GroupingLinkShelfController } from "./shelf-controllers/grouping-link-shelf-controller";
+import { GroupingLockupShelfController } from "./shelf-controllers/grouping-lockup-shelf-controller";
+import { GroupingPersonalizedLockupShelfController } from "./shelf-controllers/grouping-personalized-lockup-shelf-controller";
+import { createShelfAccessibilityMetadata, } from "./shelf-controllers/grouping-shelf-controller";
+import * as groupingShelfControllerCommon from "./shelf-controllers/grouping-shelf-controller-common";
+import { GroupingSmallBreakoutShelfController } from "./shelf-controllers/grouping-small-breakout-shelf-controller";
+import { ArcadeDownloadPackShelfController } from "./shelf-controllers/arcade-download-pack-shelf-controller";
+import { flowActionForAccountURL } from "../account/account-links-regex-parser";
+import { GroupingMediaPageHeaderShelfController } from "./shelf-controllers/grouping-tags-header-shelf-controller";
+import { applySearchAdMissedOpportunityToShelvesIfNeeded } from "../ads/ad-common";
+import { GroupingTagBrickShelfController } from "./shelf-controllers/grouping-tag-brick-shelf-controller";
+// region Constants
+/// The controllers that know how to render specific shelves
+const shelfControllers = [
+ new GroupingAppEventShelfController(),
+ new GroupingArcadeFooterShelfController(),
+ new GroupingBrickShelfController(),
+ new GroupingRibbonBarShelfController(),
+ new GroupingCategoryShelfController(),
+ new GroupingEditorialCardShelfController(),
+ new GroupingEditorialStoryCardShelfController(),
+ new GroupingGameCenterContinuePlayingShelfController(),
+ new GroupingGameCenterPopularWithYourFriendsController(),
+ new GroupingGameCenterReengagementShelfController(),
+ new GroupingGameCenterSuggestedFriendsController(),
+ new GroupingGameCenterActivityFeedController(),
+ new GroupingHeroCarouselShelfController(),
+ new GroupingHorizontalCardShelfController(),
+ new GroupingLargeBreakoutShelfController(),
+ new GroupingLinkShelfController(),
+ new GroupingLockupShelfController(),
+ new GroupingPersonalizedLockupShelfController(),
+ new GroupingSmallBreakoutShelfController(),
+ new ArcadeDownloadPackShelfController(),
+ new GroupingMediaPageHeaderShelfController(),
+ new GroupingTagBrickShelfController(),
+];
+/**
+ * For a given flattened grouping go through and use our controllers to render the associated shelves, this should be used
+ * for the initial page load. The second fetches for these shelves are handled by the routing system and the routed
+ * shelf controller. This method will add all the shelves to the current groupingParseContext.
+ *
+ * @param objectGraph The App Store dependency graph
+ * @param flattenedGrouping The original grouping page response data from MAPI flattened into the list of featured content
+ * @param groupingParseContext The current parse context for this rendering of the grouping.
+ * @protected
+ */
+export function insertInitialShelvesIntoGroupingParseContext(objectGraph, flattenedGrouping, groupingParseContext) {
+ validation.catchingContext(`parseGrouping`, () => {
+ for (const mediaApiData of flattenedGrouping.data) {
+ const featuredContentId = mediaAttributes.attributeAsNumber(mediaApiData, "editorialElementKind");
+ if (types.isTopChart(featuredContentId)) {
+ const editorialId = serverData.asString(mediaApiData, "id");
+ groupingParseContext.chartSet = lookupChartSetForEditorialId(editorialId, flattenedGrouping.editorialChartSets);
+ }
+ else {
+ groupingParseContext.chartSet = null;
+ }
+ const baseRequirements = groupingShelfControllerCommon.createBaseShelfRequirements(objectGraph, mediaApiData, groupingParseContext);
+ const token = baseRequirements.shelfToken;
+ const baseMetricsOptions = baseRequirements.metricsOptions;
+ let shelf;
+ for (const shelfController of shelfControllers) {
+ if (shelfController.supports(objectGraph, mediaApiData, featuredContentId)) {
+ shelf = validation.catchingContext(`parseGroupingShelf`, () => {
+ return shelfController.createShelf(objectGraph, mediaApiData, groupingParseContext, token, baseMetricsOptions);
+ });
+ break;
+ }
+ }
+ if (serverData.isDefinedNonNull(shelf)) {
+ groupingParseContext.shelves.push(shelf);
+ applySearchAdMissedOpportunityToShelvesIfNeeded(objectGraph, groupingParseContext.shelves, "searchLanding", baseMetricsOptions.id, groupingParseContext.metricsPageInformation);
+ }
+ }
+ });
+}
+/**
+ * Parse a single editorially programmed chart from media API data.
+ * @returns The chart model or null.
+ */
+function makeEditorialChartFromMediaApiData(chartData) {
+ const fcKind = mediaAttributes.attributeAsNumber(chartData, "editorialElementKind");
+ if (!types.isTopChart(fcKind)) {
+ return null;
+ }
+ return {
+ id: serverData.asString(chartData, "id"),
+ href: mediaAttributes.attributeAsString(chartData, "chartHref"),
+ name: mediaAttributes.attributeAsString(chartData, "name"),
+ type: mediaAttributes.attributeAsString(chartData, "chart"),
+ };
+}
+/**
+ * Parse a collection of charts, programmed by editorial, from media api data.
+ * @returns The chart set model, or null.
+ */
+function makeEditorialChartSetFromMediaApiData(chartSetData) {
+ const fcKind = mediaAttributes.attributeAsNumber(chartSetData, "editorialElementKind");
+ if (fcKind !== 424 /* types.FeaturedContentID.AppStore_ChartSet */) {
+ return null;
+ }
+ return {
+ id: serverData.asString(chartSetData, "id"),
+ name: mediaAttributes.attributeAsString(chartSetData, "name"),
+ editorialCharts: mediaRelationship
+ .relationshipCollection(chartSetData, "children")
+ .map((chartData) => makeEditorialChartFromMediaApiData(chartData))
+ .filter((chart) => isSome(chart)),
+ };
+}
+/**
+ * Given a list of chart sets, find the chart set that has the provided editorial identifier.
+ *
+ * @param editorialId The chart or chart set identifier from editorial.
+ * @param chartSets The list of all chart sets from the grouping.
+ * @returns The charts associated with the editorial item from the grouping.
+ */
+function lookupChartSetForEditorialId(editorialId, editorialChartSets) {
+ for (const chartSet of editorialChartSets) {
+ if (chartSet.id === editorialId) {
+ return chartSet; // editorialId is for a chart set itself
+ }
+ for (const chart of chartSet.editorialCharts) {
+ if (chart.id === editorialId) {
+ return chartSet; // / editorialId is for a specific chart from the chart set.
+ }
+ }
+ }
+ return null;
+}
+/**
+ * Take the media API data for a single grouping, and flatten that tree structure into a single list of media api data objects.
+ *
+ * @param mediaApiGroupingData The original media API data for a single grouping
+ */
+export function flattenMediaApiGroupingData(objectGraph, mediaApiGroupingData) {
+ const groupingData = [];
+ const chartSets = [];
+ function addMediaApiDataToGroupingData(mediaApiData) {
+ const featuredContentId = mediaAttributes.attributeAsNumber(mediaApiData, "editorialElementKind");
+ validation.catchingContext(`flattenGroupingTree.addMediaApiDataToGroupingData: ${featuredContentId}`, () => {
+ const groupingTabData = mediaRelationship.relationshipData(objectGraph, mediaApiData, "tabs");
+ if (serverData.isDefinedNonNull(groupingTabData)) {
+ addMediaApiDataToGroupingData(groupingTabData);
+ }
+ else if (types.shouldContinueToWalkChildren(featuredContentId)) {
+ const featuredContentChildren = mediaRelationship.relationshipCollection(mediaApiData, "children");
+ for (const featuredContentChild of featuredContentChildren) {
+ addMediaApiDataToGroupingData(featuredContentChild);
+ }
+ }
+ else if (featuredContentId === 424 /* types.FeaturedContentID.AppStore_ChartSet */) {
+ // Keep track of chartSets programmed by editorial, e.g. "Top Charts" includes "Top Free Apps" and "Top Paid Apps".
+ const editorialChartSet = makeEditorialChartSetFromMediaApiData(mediaApiData);
+ if (isSome(editorialChartSet)) {
+ chartSets.push(editorialChartSet);
+ }
+ // Add charts from the chartSet, as defined by editorial, to grouping page data.
+ const chartSetChildren = mediaRelationship.relationshipCollection(mediaApiData, "children");
+ for (const chartSetChild of chartSetChildren) {
+ addMediaApiDataToGroupingData(chartSetChild);
+ }
+ }
+ else {
+ groupingData.push(mediaApiData);
+ }
+ });
+ }
+ addMediaApiDataToGroupingData(mediaApiGroupingData);
+ return {
+ data: groupingData,
+ editorialChartSets: chartSets,
+ originalGroupingData: mediaApiGroupingData,
+ };
+}
+// endregion
+// region Shelves
+/**
+ * Create a shelf for footer buttons, e.g "Send Gift" and "Redeem".
+ *
+ * @return Shelf configured to display `FooterButtons`s.
+ */
+export function shelfForFooterButtons(objectGraph, metricsPageInformation, metricsLocationTracker) {
+ var _a, _b, _c, _d, _e, _f;
+ if (objectGraph.user && objectGraph.user.isManagedAppleID) {
+ return null;
+ }
+ // Watch App Store does not support titled button stacks,
+ // and has different footer buttons.
+ if (objectGraph.client.isWatch) {
+ const shelf = new models.Shelf("action");
+ const items = [];
+ const accountFlowAction = new models.FlowAction("account");
+ accountFlowAction.title = objectGraph.loc.string("ACCOUNT", "Account");
+ accountFlowAction.artwork = artworkBuilder.createArtworkForResource(objectGraph, "systemimage://person.crop.circle");
+ const accountFlowClickOptions = {
+ targetType: "button",
+ id: "account",
+ pageInformation: metricsPageInformation,
+ locationTracker: metricsLocationTracker,
+ };
+ metricsHelpersClicks.addClickEventToAction(objectGraph, accountFlowAction, accountFlowClickOptions);
+ items.push(accountFlowAction);
+ shelf.items = items;
+ shelf.accessibilityMetadata = createShelfAccessibilityMetadata(objectGraph, shelf);
+ return shelf;
+ }
+ else {
+ const items = [];
+ const redeemTitle = objectGraph.loc.string("FOOTER_REDEEM", "Redeem");
+ const redeemActionUrl = objectGraph.client.isVision
+ ? objectGraph.bag.redeemCodeLanding
+ : objectGraph.bag.redeemUrl;
+ const redeemFlowAction = flowActionForAccountURL(objectGraph, redeemActionUrl);
+ const redeemButton = new models.TitledButton(redeemTitle, redeemFlowAction);
+ redeemButton.id = "redeem";
+ items.push(redeemButton);
+ if (objectGraph.bag.isMonetaryGiftingEnabled) {
+ const giftTitle = objectGraph.loc.string("FOOTER_SEND_GIFT", "Send Gift");
+ const giftActionUrl = "gift";
+ const giftFlowAction = flowActionForAccountURL(objectGraph, giftActionUrl);
+ const giftButton = new models.TitledButton(giftTitle, giftFlowAction);
+ giftButton.id = "gift";
+ items.push(giftButton);
+ }
+ const topUpUrl = objectGraph.bag.accountTopUpURL;
+ if (isSome(topUpUrl)) {
+ const topUpTitle = (_a = objectGraph.bag.accountTopUpTitle) !== null && _a !== void 0 ? _a : objectGraph.loc.string("FOOTER_ADD_MONEY");
+ const topUpFlowAction = flowActionForAccountURL(objectGraph, topUpUrl);
+ const topUpButton = new models.TitledButton(topUpTitle, topUpFlowAction);
+ topUpButton.id = "topUp";
+ items.push(topUpButton);
+ }
+ let shelf;
+ if (objectGraph.client.isVision) {
+ shelf = new models.Shelf("titledButton");
+ shelf.items = items;
+ }
+ else {
+ shelf = new models.Shelf("titledButtonStack");
+ const stack = new models.TitledButtonStack(items);
+ // On compact width, we want to place a line break after each button.
+ stack.compactLineBreaks = stack.buttons.map((value, index) => index);
+ shelf.items = [stack];
+ const accessibilityLabel = objectGraph.loc.string("Shelves.Accessibility.Label");
+ const roleDescription = objectGraph.loc.string("Shelves.Accessibility.RoleDescription");
+ shelf.accessibilityMetadata = {
+ label: (_d = (_b = shelf.title) !== null && _b !== void 0 ? _b : (_c = shelf.header) === null || _c === void 0 ? void 0 : _c.title) !== null && _d !== void 0 ? _d : accessibilityLabel,
+ roleDescription: roleDescription.replace("%d", `${(_f = (_e = stack.buttons) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : 0}`),
+ };
+ }
+ return shelf;
+ }
+}
+/**
+ * Create a T&C shelf that on click opens an external url.
+ *
+ * @param objectGraph
+ * @param destinationUrl Url to open when terms and conditions is tapped.
+ * @param hasSeparator Whether the footnote has a separator above it.
+ * @return Shelf configured to display Terms and Conditions.
+ */
+export function shelfForTermsAndConditions(objectGraph, destinationUrl, hasSeparator = true) {
+ const urlAction = new models.ExternalUrlAction(destinationUrl);
+ const termsAndConditionTitle = objectGraph.loc.string("TermsAndConditions.Title");
+ const footnote = new models.Footnote(termsAndConditionTitle);
+ footnote.clickAction = urlAction;
+ footnote.presentationStyle = ["hasChevron", "textLightensOnHighlight"];
+ if (hasSeparator) {
+ footnote.presentationStyle.push("hasSeparator");
+ }
+ const shelf = new models.Shelf("footnote");
+ shelf.items = [footnote];
+ if (objectGraph.bag.emailSupportLinkURL) {
+ const emailSupportAction = new models.ExternalUrlAction(objectGraph.bag.emailSupportLinkURL);
+ const emailSupport = new models.Footnote("Email Support");
+ emailSupport.clickAction = emailSupportAction;
+ emailSupport.presentationStyle = ["hasChevron", "textLightensOnHighlight"];
+ if (hasSeparator) {
+ emailSupport.presentationStyle.push("hasSeparator");
+ }
+ shelf.items.push(emailSupport);
+ }
+ return shelf;
+}
+export function shelfForUnifiedMessage(objectGraph, placement, context, deliveryMethod) {
+ const shelf = new models.Shelf("unifiedMessage");
+ shelf.id = placement;
+ shelf.items = [new models.UnifiedMessage(placement, context, deliveryMethod)];
+ shelf.isHidden = true;
+ return shelf;
+}
+// endregion
+//# sourceMappingURL=grouping-common.js.map \ No newline at end of file