summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/common/search/content
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/common/search/content')
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/search/content/search-categories.js334
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/search/content/search-content-common.js31
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/search/content/search-lockup-collection.js171
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/search/content/search-results.js723
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/search/content/search-shelves.js53
5 files changed, 1312 insertions, 0 deletions
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-categories.js b/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-categories.js
new file mode 100644
index 0000000..927de7c
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-categories.js
@@ -0,0 +1,334 @@
+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 mediaRelationships from "../../../foundation/media/relationships";
+import { pageRouter } from "../../builders/routing";
+import { hrefToRoutableUrl } from "../../builders/url-mapping";
+import * as content from "../../content/content";
+import { addClickEventToAction, addClickEventToSeeAllAction } from "../../metrics/helpers/clicks";
+import { addImpressionFields } from "../../metrics/helpers/impressions";
+import * as metricsHelpersLocation from "../../metrics/helpers/location";
+import { addMetricsEventsToPageWithInformation, metricsPageInformationFromMediaApiResponse, } from "../../metrics/helpers/page";
+import { createClickMetricsOptionsForChartOrCategory, createMetricsOptionsForChartOrCategory, } from "../../metrics/helpers/search/search-shelves";
+import { combinedRecoMetricsDataFromMetricsData } from "../../metrics/helpers/util";
+import * as onDevicePersonalization from "../../personalization/on-device-personalization";
+import * as searchShelves from "./search-shelves";
+import * as groupingShelfControllerCommon from "../../grouping/shelf-controllers/grouping-shelf-controller-common";
+/**
+ * Takes the raw page data and creates a charts and categories page
+ * @param objectGraph The App Store Object Graph
+ * @param resultData The data representing the charts and categories page
+ * @returns A charts and categories page
+ */
+export function searchChartsAndCategoriesPageFromData(objectGraph, resultData) {
+ var _a, _b;
+ /// Get the page's metadata
+ const pageTitle = mediaAttributes.attributeAsString(resultData, "title");
+ const selectedTabId = serverData.asString(resultData, "meta.autoSelectedTabId");
+ const sourceShelfCanonicalId = serverData.asString(resultData, "meta.sourceShelfCanonicalId");
+ const pageInformation = metricsPageInformationFromMediaApiResponse(objectGraph, "SearchLanding", sourceShelfCanonicalId, resultData);
+ const onDevicePersonalizationMetricsData = onDevicePersonalization.metricsData(objectGraph);
+ pageInformation.recoMetricsData = combinedRecoMetricsDataFromMetricsData(pageInformation.recoMetricsData, null, onDevicePersonalizationMetricsData);
+ /// Generate a new SearchPageContext (mainly for making shelf contexts)
+ const searchPageContext = {
+ shelves: [],
+ metricsLocationTracker: metricsHelpersLocation.newLocationTracker(),
+ metricsPageInformation: pageInformation,
+ adStitcher: null,
+ adIncidentRecorder: null,
+ pageType: searchShelves.SearchPageType.ChartsAndCategories,
+ };
+ /// Prep the shelves data, mappings, orderings, and page tabs list
+ const shelvesData = serverData.asArrayOrEmpty(resultData, "data");
+ const shelvesMapping = {};
+ const shelfOrdering = [];
+ const pageTabsCollector = [];
+ const pageTabsLocationTracker = metricsHelpersLocation.newLocationTracker();
+ for (const shelfData of shelvesData) {
+ /// Generate the shelf attributes for the shelf
+ const shelfAttributes = searchShelves.shelfAttributesFromData(objectGraph, shelfData, models.SearchLandingPageContentKind.CategoriesAndCharts, models.SearchPageKind.CategoriesAndCharts);
+ /// Since the shelves act as their own page, we need to set a new location tracker for each shelf
+ const shelfPageContext = {
+ ...searchPageContext,
+ metricsLocationTracker: metricsHelpersLocation.newLocationTracker(),
+ };
+ /// Generate the shelf context
+ const shelfContext = searchShelves.baseShelfContext(objectGraph, shelfData, shelfAttributes, shelfPageContext);
+ /// Push the shelf content location so each shelf has the correct parent and starting index
+ metricsHelpersLocation.pushContentLocation(objectGraph, shelfContext.metricsOptions, shelfAttributes.title);
+ /// Make the shelf
+ const shelf = createChartsCategoryShelf(objectGraph, shelfData, true, shelfAttributes, shelfPageContext, shelfContext);
+ metricsHelpersLocation.popLocation(shelfContext.metricsOptions.locationTracker);
+ if (isNothing(shelf)) {
+ continue;
+ }
+ /// Add the shelf to the shelves mapping for the shelf id
+ shelvesMapping[shelf.id] = shelf;
+ /// Add the shelf id to the shelf ordering (to maintain ordered shelves)
+ shelfOrdering.push(shelf.id);
+ /// Make a pageTab metadata model from the shelf and action
+ const pageTab = new models.PageTab();
+ const action = new models.PageTabChangeAction(shelfData.id, shelfAttributes.title);
+ /// Generate the click actions
+ const pageTabClickActionOptions = {
+ id: shelfAttributes.title,
+ canonicalId: (_a = serverData.asString(shelfData.meta, "canonicalId")) !== null && _a !== void 0 ? _a : undefined,
+ actionType: "navigate",
+ targetType: "button",
+ pageInformation: searchPageContext.metricsPageInformation,
+ locationTracker: pageTabsLocationTracker,
+ };
+ addClickEventToAction(objectGraph, action, pageTabClickActionOptions);
+ pageTab.action = action;
+ pageTab.id = shelf.id;
+ pageTab.title = `${(_b = shelf.title) !== null && _b !== void 0 ? _b : ""}`; /// We need a deep copy of the string as we will remove the reference possibly later.
+ pageTabsCollector.push(pageTab);
+ metricsHelpersLocation.nextPosition(pageTabsLocationTracker);
+ }
+ /// Return an empty page if there are no real shelves to show
+ if (!serverData.isDefinedNonNullNonEmpty(shelfOrdering)) {
+ return new models.SearchChartsAndCategoriesPage();
+ }
+ /// Create the page tabs
+ const pageTabs = new models.PageTabs();
+ /// The id needs to be static but it isn't tied to the payload
+ pageTabs.id = objectGraph.random.nextUUID();
+ /// Add the pageTabs as its own shelf so it can be independent of the chart and category shelves
+ /// as well as to control the segmented control for swapping between category shelf orderings (e.g. category containers)
+ const pageTabsShelf = new models.Shelf("pageTabs");
+ pageTabsShelf.items = [pageTabs];
+ shelvesMapping[pageTabs.id] = pageTabsShelf;
+ /// Make the page and add the shelf mappings (which includes our pageTabs shelf)
+ const page = new models.SearchChartsAndCategoriesPage();
+ page.shelfMapping = shelvesMapping;
+ const shelfOrderings = {};
+ /** Normally, we would have the shelf orderings be based on some identifier in the payload on a container which contains
+ an array of shelves. E.g.
+ {
+ Container {
+ containerId: string
+ shelves: [
+ {
+ shelfId1: string,
+ ...
+ },
+ ...
+ ]
+ }
+ }
+ shelf orderings = { containerId: [shelfId1, ...]}
+
+ However, the current protocol has no container, just a loose array of shelves.
+ {
+ shelves: [
+ {
+ shelfId1: string,
+ ...
+ },
+ ...
+ ]
+ }
+
+ Therefore, we have to fake our ordering by having the ordering be the shelves' own identifiers.
+
+ shelf orderings = { shelfId1: [shelfId1], shelfId2: [shelfId2], ...}
+
+ We also will append the pageTabs shelf if we have multiple shelves representing multiple tabs on the page.
+ */
+ for (const shelfOrderingElement of shelfOrdering) {
+ if (pageTabsCollector.length > 1) {
+ shelfOrderings[shelfOrderingElement] = [pageTabs.id, shelfOrderingElement];
+ }
+ else {
+ shelfOrderings[shelfOrderingElement] = [shelfOrderingElement];
+ }
+ }
+ for (const shelf of Object.values(shelvesMapping)) {
+ shelf.title = undefined;
+ }
+ /// Set the data on the page
+ page.title = pageTitle;
+ page.pageTabs = pageTabs;
+ page.columnCount = 2;
+ page.shelfOrderings = shelfOrderings;
+ /// If we don't have a reco selected default tab, just default to the first one
+ page.defaultShelfOrdering = shelfOrdering.includes(selectedTabId) ? selectedTabId : shelfOrdering[0];
+ /// Set the data on the page tabs
+ pageTabs.tabs = pageTabsCollector;
+ pageTabs.selectedTabId = page.defaultShelfOrdering;
+ /// Add the page metrics
+ addMetricsEventsToPageWithInformation(objectGraph, page, searchPageContext.metricsPageInformation);
+ return page;
+}
+/**
+ * Creates a charts and categories shelf from the shelf data
+ * @param objectGraph The App Store Object Graph
+ * @param data The shelf data object
+ * @param isForSeeAllPage Whether or not this shelf will be displayed on the see-all page
+ * @param shelfAttributes The shelf's attributes
+ * @param searchPageContext The context for the page this shelf belongs to
+ * @param searchShelfContext The shelf's context
+ * @returns A charts and categories shelf
+ */
+export function createChartsCategoryShelf(objectGraph, data, isForSeeAllPage, shelfAttributes, searchPageContext, searchShelfContext) {
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
+ const items = [];
+ const chartsAndCategoriesData = mediaRelationships.relationshipCollection(data, "contents");
+ const shelf = new models.Shelf("searchChartsAndCategories");
+ shelf.isHorizontal = false;
+ shelf.id = data.id;
+ shelf.title = shelfAttributes.title;
+ shelf.presentationHints = { isWidthConstrained: true };
+ if (serverData.isNumber((_a = shelfAttributes.displayStyle) === null || _a === void 0 ? void 0 : _a.layoutSize)) {
+ shelf.contentsMetadata = {
+ type: "searchLandingChartsAndCategoriesSection",
+ numberOfColumns: shelfAttributes.displayStyle.layoutSize,
+ };
+ }
+ if (shelfAttributes.hasSeeAll) {
+ const action = new models.FlowAction("searchChartsAndCategories");
+ action.pageUrl = shelfAttributes.seeAllLink;
+ action.title = objectGraph.loc.string("ACTION_SEE_ALL");
+ const seeAllMetricsOptions = {
+ ...searchShelfContext.metricsOptions,
+ targetType: "button",
+ };
+ addClickEventToSeeAllAction(objectGraph, action, action.pageUrl, seeAllMetricsOptions);
+ shelf.seeAllAction = action;
+ }
+ addImpressionFields(objectGraph, shelf, searchShelfContext.metricsOptions);
+ const brickUseCase = isForSeeAllPage
+ ? content.SearchChartOrCategoryBrickUseCase.seeAllPage
+ : content.SearchChartOrCategoryBrickUseCase.other;
+ for (const chartOrCategory of chartsAndCategoriesData) {
+ let name = null;
+ let badge = null;
+ if (chartOrCategory.type === "tags") {
+ const brickName = (_b = mediaAttributes.attributeAsString(chartOrCategory, "name")) !== null && _b !== void 0 ? _b : "tagbrick";
+ name = brickName;
+ }
+ else {
+ const editorialNotes = mediaAttributes.attributeAsDictionary(chartOrCategory, "editorialNotes");
+ if (serverData.isDefinedNonNullNonEmpty(editorialNotes)) {
+ name = serverData.asString(editorialNotes, "name");
+ badge = serverData.asString(editorialNotes, "badge");
+ }
+ }
+ const artwork = content.searchChartOrCategoryArtworkFromData(objectGraph, chartOrCategory, brickUseCase, (_c = shelfAttributes === null || shelfAttributes === void 0 ? void 0 : shelfAttributes.displayStyle) === null || _c === void 0 ? void 0 : _c.layoutDensity);
+ const kind = mediaAttributes.attributeAsString(chartOrCategory, "kind");
+ const linkData = mediaAttributes.attributeAsDictionary(chartOrCategory, "link");
+ const linkUrl = serverData.asString(linkData, "url");
+ const dataCollection = mediaRelationships.relationshipCollection(chartOrCategory, "primary-content");
+ let isTitleRequired = true;
+ let action = null;
+ if (chartOrCategory.type === "tags") {
+ const href = serverData.asString(chartOrCategory, "href");
+ const url = hrefToRoutableUrl(objectGraph, href);
+ const flowPage = objectGraph.required(pageRouter).fetchFlowPage(url);
+ const flowAction = new models.FlowAction(flowPage);
+ flowAction.pageUrl = url;
+ flowAction.title = name;
+ action = flowAction;
+ }
+ else if (isSome(linkUrl)) {
+ switch (kind) {
+ case "CategoryChart":
+ const topChartAction = new models.FlowAction("topCharts");
+ topChartAction.pageUrl = linkUrl;
+ topChartAction.title = name;
+ action = topChartAction;
+ break;
+ case "External":
+ // For external links, we don't require a title we can just show an image
+ isTitleRequired = false;
+ const target = serverData.asString(linkData, "target");
+ if (target === "external") {
+ action = new models.ExternalUrlAction(linkUrl);
+ action.title = name !== null && name !== void 0 ? name : "";
+ }
+ else {
+ const flowPage = objectGraph.required(pageRouter).fetchFlowPage(linkUrl);
+ const linkAction = new models.FlowAction(flowPage);
+ linkAction.pageUrl = linkUrl;
+ linkAction.title = name !== null && name !== void 0 ? name : "";
+ action = linkAction;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else if (serverData.isDefinedNonNullNonEmpty(dataCollection)) {
+ const chartMetadata = dataCollection[0];
+ const chartHref = chartMetadata.href;
+ const url = hrefToRoutableUrl(objectGraph, chartHref);
+ if ((url === null || url === void 0 ? void 0 : url.length) > 0) {
+ const flowAction = new models.FlowAction("page");
+ flowAction.pageUrl = url;
+ flowAction.title = name;
+ action = flowAction;
+ }
+ else {
+ continue;
+ }
+ }
+ else {
+ continue;
+ }
+ const chartClickOptions = createClickMetricsOptionsForChartOrCategory(objectGraph, (_d = shelfAttributes === null || shelfAttributes === void 0 ? void 0 : shelfAttributes.displayStyle) === null || _d === void 0 ? void 0 : _d.layoutDensity, chartOrCategory, searchShelfContext);
+ addClickEventToAction(objectGraph, action, chartClickOptions);
+ if (isTitleRequired && serverData.isNullOrEmpty(name)) {
+ continue;
+ }
+ let chartOrCategoryModel = new models.SearchChartOrCategory(name, artwork, null, null, badge, action, (_e = shelfAttributes === null || shelfAttributes === void 0 ? void 0 : shelfAttributes.displayStyle) === null || _e === void 0 ? void 0 : _e.layoutDensity, artworkSafeArea((_f = shelfAttributes === null || shelfAttributes === void 0 ? void 0 : shelfAttributes.displayStyle) === null || _f === void 0 ? void 0 : _f.layoutDensity), textSafeArea((_g = shelfAttributes === null || shelfAttributes === void 0 ? void 0 : shelfAttributes.displayStyle) === null || _g === void 0 ? void 0 : _g.layoutDensity));
+ let chartModelMetricsOptions = createMetricsOptionsForChartOrCategory(objectGraph, chartOrCategoryModel, chartOrCategory, searchShelfContext);
+ const artworkOptions = {
+ useCase: 18 /* content.ArtworkUseCase.GroupingBrick */,
+ };
+ const collectionIcons = groupingShelfControllerCommon.artworkForTags(objectGraph, chartOrCategory, 1060, 520, artworkOptions, chartModelMetricsOptions);
+ if (isSome(collectionIcons) && collectionIcons.length > 0) {
+ const collectionIconBackgroundColor = collectionIcons[0].backgroundColor;
+ if (isSome(collectionIconBackgroundColor) && (collectionIconBackgroundColor === null || collectionIconBackgroundColor === void 0 ? void 0 : collectionIconBackgroundColor.type) === "rgb") {
+ chartOrCategoryModel = new models.SearchChartOrCategory(name, null, collectionIcons, (_h = content.closestTagBackgroundColorForIcon(collectionIconBackgroundColor)) !== null && _h !== void 0 ? _h : undefined, badge, action, (_j = shelfAttributes === null || shelfAttributes === void 0 ? void 0 : shelfAttributes.displayStyle) === null || _j === void 0 ? void 0 : _j.layoutDensity, artworkSafeArea((_k = shelfAttributes === null || shelfAttributes === void 0 ? void 0 : shelfAttributes.displayStyle) === null || _k === void 0 ? void 0 : _k.layoutDensity), textSafeArea((_l = shelfAttributes === null || shelfAttributes === void 0 ? void 0 : shelfAttributes.displayStyle) === null || _l === void 0 ? void 0 : _l.layoutDensity));
+ chartModelMetricsOptions = createMetricsOptionsForChartOrCategory(objectGraph, chartOrCategoryModel, chartOrCategory, searchShelfContext);
+ }
+ }
+ addImpressionFields(objectGraph, chartOrCategoryModel, chartModelMetricsOptions);
+ items.push(chartOrCategoryModel);
+ metricsHelpersLocation.nextPosition(searchShelfContext.metricsOptions.locationTracker);
+ }
+ if (serverData.isNullOrEmpty(items)) {
+ return null;
+ }
+ shelf.items = items;
+ return shelf;
+}
+function artworkSafeArea(density) {
+ switch (density) {
+ // Tile
+ case models.GenericSearchPageShelfDisplayStyleDensity.Density1:
+ return models.ChartOrCategorySafeArea.defaultTileArtworkSafeArea;
+ // Pill
+ case models.GenericSearchPageShelfDisplayStyleDensity.Density2:
+ return models.ChartOrCategorySafeArea.defaultPillArtworkSafeArea;
+ // Round
+ case models.GenericSearchPageShelfDisplayStyleDensity.Density3:
+ return undefined;
+ default:
+ return undefined;
+ }
+}
+function textSafeArea(density) {
+ switch (density) {
+ // Tile
+ case models.GenericSearchPageShelfDisplayStyleDensity.Density1:
+ return models.ChartOrCategorySafeArea.defaultTileTextSafeArea;
+ // Pill
+ case models.GenericSearchPageShelfDisplayStyleDensity.Density2:
+ return models.ChartOrCategorySafeArea.defaultPillTextSafeArea;
+ default:
+ return undefined;
+ }
+}
+//# sourceMappingURL=search-categories.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-content-common.js b/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-content-common.js
new file mode 100644
index 0000000..59b2fe5
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-content-common.js
@@ -0,0 +1,31 @@
+/**
+ * Common functions for search results content.
+ */
+import { isDefinedNonNullNonEmpty } from "../../../foundation/json-parsing/server-data";
+import { attributeAsBooleanOrFalse, attributeAsString } from "../../../foundation/media/attributes";
+// region Editorial Content
+/**
+ * Returns the headline / tagline for Editorial Search Results
+ * @param resultData Data to determine tagline for.
+ */
+export function editorialSearchResultTagline(objectGraph, resultData) {
+ // Flag to disable showing specific headings, e.g. "App of the Day" that may be unnatural in search result
+ const showLabelInSearch = attributeAsBooleanOrFalse(resultData, "showLabelInSearch");
+ if (!showLabelInSearch) {
+ return null;
+ }
+ // <rdar://problem/40468686> LOC: AOTD & GOTD badges in Search result for the Editorial Item Card AOTD & GOTD stories do not show up correctly for JA-JP and TH-TH
+ // Always use alternative label, if one is provided.
+ const alternateLabel = attributeAsString(resultData, "alternateLabel");
+ if (isDefinedNonNullNonEmpty(alternateLabel)) {
+ return alternateLabel; // No newline flattening needed for alternativeLabel
+ }
+ // Otherwise, fallback to franchise label, if any.
+ const label = attributeAsString(resultData, "label");
+ if (isDefinedNonNullNonEmpty(label)) {
+ return label.replace(/\n/g, " ");
+ }
+ return null;
+}
+// endregion
+//# sourceMappingURL=search-content-common.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-lockup-collection.js b/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-lockup-collection.js
new file mode 100644
index 0000000..c5c85e7
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-lockup-collection.js
@@ -0,0 +1,171 @@
+import { TodayCardDisplayStyle } from "./../../today/today-types";
+/**
+ * Builder for SearchLockupCollection.
+ */
+import { SearchLockupCollection } from "../../../api/models";
+import { isDefinedNonNullNonEmpty, isNullOrEmpty } from "../../../foundation/json-parsing/server-data";
+import { attributeAsArrayOrEmpty, attributeAsString } from "../../../foundation/media/attributes";
+import { relationshipCollection } from "../../../foundation/media/relationships";
+import { createArtworkForResource } from "../../content/artwork/artwork";
+import { notesFromData } from "../../content/content";
+import { currentEditorialCollectionTreatment, } from "../../../foundation/experimentation/search-results-experiments";
+import { actionFromData, lockupsFromData } from "../../lockups/lockups";
+import { addImpressionFields, impressionOptions } from "../../metrics/helpers/impressions";
+import { popLocation, pushContentLocation } from "../../metrics/helpers/location";
+import { cardDisplayStyleFromData } from "../../today/today-card-util";
+import { editorialSearchResultTagline } from "./search-content-common";
+/**
+ * Whether or not a given result data should be rendered as `SearchLockupCollection` model.
+ * Semantically, every `collection` should be rendered as a lockup collection, but collections are currently backed by many different types of articles.
+ * @param resultData Data from results endpoint.
+ * @param searchResponseMetadata Meta blob from initial search request.
+ */
+export function resultDataShouldRenderLockupCollection(objectGraph, resultData, searchResponseMetadata) {
+ // Filter supported platforms
+ if (!(objectGraph.host.isiOS || objectGraph.host.isVision)) {
+ return false;
+ }
+ if (currentEditorialCollectionTreatment(objectGraph, searchResponseMetadata) !==
+ 1 /* EditorialCollectionExperimentType.Swoosh */) {
+ return false;
+ }
+ switch (resultData.type) {
+ case "groupings":
+ // Groupings can only be rendered if `contentIds` is present
+ const collectionAdamIds = attributeAsArrayOrEmpty(resultData, "contentIds");
+ return isDefinedNonNullNonEmpty(collectionAdamIds);
+ case "rooms":
+ case "multirooms":
+ // Rooms, and multirooms always render as collection.
+ return true;
+ case "editorial-items":
+ // Some subtypes of editorial items render as collection.
+ const cardDisplayStyle = cardDisplayStyleFromData(resultData);
+ switch (cardDisplayStyle) {
+ case TodayCardDisplayStyle.List:
+ case TodayCardDisplayStyle.NumberedList:
+ case TodayCardDisplayStyle.Grid:
+ case TodayCardDisplayStyle.River:
+ return true;
+ default:
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+/**
+ * Create a `SearchLockupCollection` from search results.
+ * @param resultData Data from results endpoint.
+ * @param lockupOptions Options for lockups shared throughout search results page.
+ * @param metricsOptions Metrics context.
+ */
+export function lockupCollectionFromResultData(objectGraph, resultData, metricsOptions) {
+ // Text
+ const heading = lockupCollectionHeadingFromResultData(objectGraph, resultData);
+ const headingArtwork = lockupCollectionHeadingArtworkFromResultData(objectGraph, resultData);
+ const title = lockupCollectionTitleFromResultData(objectGraph, resultData);
+ // Metrics for impression + location
+ const lockupCollectionMetrics = impressionOptions(objectGraph, resultData, title, {
+ targetType: "card",
+ pageInformation: metricsOptions.pageInformation,
+ locationTracker: metricsOptions.locationTracker,
+ });
+ pushContentLocation(objectGraph, lockupCollectionMetrics, title);
+ // Click Action ("See All")
+ const clickMetrics = {
+ actionType: "navigate",
+ targetType: "button",
+ pageInformation: metricsOptions.pageInformation,
+ locationTracker: metricsOptions.locationTracker,
+ id: "See All",
+ idType: "sequential",
+ };
+ const seeAllAction = actionFromData(objectGraph, resultData, clickMetrics, objectGraph.host.clientIdentifier);
+ seeAllAction.title = objectGraph.loc.string("ACTION_SEE_ALL");
+ // Lockups
+ const listOptions = {
+ lockupOptions: {
+ metricsOptions: {
+ pageInformation: metricsOptions.pageInformation,
+ locationTracker: metricsOptions.locationTracker,
+ targetType: "lockup",
+ },
+ skipDefaultClickAction: objectGraph.client.isVision,
+ artworkUseCase: 8 /* ArtworkUseCase.SearchIcon */,
+ hideCompatibilityBadge: objectGraph.client.isVision,
+ },
+ filter: 128 /* Filter.UnsupportedPlatform */,
+ };
+ let lockupData = relationshipCollection(resultData, "card-contents");
+ if (isNullOrEmpty(lockupData)) {
+ lockupData = relationshipCollection(resultData, "top-apps");
+ }
+ const items = lockupsFromData(objectGraph, lockupData, listOptions);
+ popLocation(metricsOptions.locationTracker);
+ // Lockup Collection
+ const lockupCollection = new SearchLockupCollection(heading, title, items, seeAllAction, headingArtwork);
+ addImpressionFields(objectGraph, lockupCollection, lockupCollectionMetrics);
+ if (isNullOrEmpty(items)) {
+ return null;
+ }
+ return lockupCollection;
+}
+// endregion
+// region Heading
+/**
+ * Returns the title for given editorial results search results data.
+ * @param resultData The result data to get a title for.
+ */
+function lockupCollectionTitleFromResultData(objectGraph, resultData) {
+ const resultType = resultData.type;
+ switch (resultType) {
+ case "developers":
+ return attributeAsString(resultData, "name");
+ default:
+ return notesFromData(objectGraph, resultData, "name");
+ }
+}
+/**
+ * Returns the heading for given editorial results search results data.
+ * This builds on `editorialSearchResultTagline` with some additional search-specific logic
+ * @param resultData The result data to get a heading for.
+ */
+function lockupCollectionHeadingFromResultData(objectGraph, resultData) {
+ const resultType = resultData.type;
+ if (resultType === "developers") {
+ return objectGraph.loc.string("EDITORIAL_SEARCH_RESULT_TYPE_DEVELOPER_TITLE_CASE");
+ }
+ const editorialTagline = editorialSearchResultTagline(objectGraph, resultData);
+ if (isDefinedNonNullNonEmpty(editorialTagline)) {
+ return editorialTagline;
+ }
+ if (objectGraph.client.isVision) {
+ return objectGraph.loc.string("EDITORIAL_SEARCH_RESULT_TYPE_COLLECTION_TITLE_CASE");
+ }
+ else {
+ return objectGraph.loc.string("Search.EditorialSearchResultType.Heading.Collection");
+ }
+}
+/**
+ * Returns the heading artwork to use for a given editorial search results data.
+ * @param resultData The result data to get heading artwork for.
+ */
+function lockupCollectionHeadingArtworkFromResultData(objectGraph, resultData) {
+ if (!objectGraph.client.isVision) {
+ return null;
+ }
+ const resultType = resultData.type;
+ let imageName;
+ switch (resultType) {
+ case "developers":
+ imageName = "person.crop.square";
+ break;
+ default:
+ imageName = "appstore";
+ break;
+ }
+ return createArtworkForResource(objectGraph, `systemimage://${imageName}`);
+}
+// endregion
+//# sourceMappingURL=search-lockup-collection.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-results.js b/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-results.js
new file mode 100644
index 0000000..5f363b3
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-results.js
@@ -0,0 +1,723 @@
+/**
+ * A builder for all SearchResultsContainers variants except for `SearchLockupCollection`.
+ * To be cleaned in the future.
+ */
+import * as validation from "@jet/environment/json/validation";
+import { isSome } from "@jet/environment/types/optional";
+import * as models from "../../../api/models";
+import { EditorialSearchResult } 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 errors from "../../../foundation/util/errors";
+import * as client from "../../../foundation/wrappers/client";
+import * as appEvents from "../../app-promotions/app-event";
+import * as appPromotionsCommon from "../../app-promotions/app-promotions-common";
+import { createArtworkForResource } from "../../content/artwork/artwork";
+import * as content from "../../content/content";
+import { editorialDisplayOptionsFromClientParams, extractEditorialClientParams, } from "../../editorial-pages/editorial-data-util";
+import * as filtering from "../../filtering";
+import * as lockups from "../../lockups/lockups";
+import * as metricsHelpersImpressions from "../../metrics/helpers/impressions";
+import * as metricsHelpersUtil from "../../metrics/helpers/util";
+import * as onDevicePersonalization from "../../personalization/on-device-personalization";
+import { defaultTodayCardConfiguration, todayCardFromData } from "../../today/today-card-util";
+import { TodayCardDisplayStyle, TodayParseContext } from "../../today/today-types";
+import * as common from "./search-content-common";
+import * as searchLockupCollection from "./search-lockup-collection";
+export function searchResultFromData(objectGraph, resultData, searchResponseMetadata, personalizationDataContainer, metricsOptions, isNetworkConstrained, searchEntity, searchExperimentsData) {
+ return validation.context("searchResultFromData", () => {
+ let searchResult = null;
+ const resultType = resultData.type;
+ const standardLockupOptions = {
+ metricsOptions: {
+ pageInformation: metricsOptions.pageInformation,
+ locationTracker: metricsOptions.locationTracker,
+ targetType: "card",
+ createUniqueImpressionId: true,
+ },
+ hideZeroRatings: true,
+ artworkUseCase: 8 /* content.ArtworkUseCase.SearchIcon */,
+ isNetworkConstrained: isNetworkConstrained,
+ canDisplayArcadeOfferButton: content.shelfContentTypeCanDisplayArcadeOfferButtons(objectGraph, "mixedMediaLockup"),
+ clientIdentifierOverride: clientIdentifierOverrideForSearchEntity(objectGraph, searchEntity),
+ isMultilineTertiaryTitleAllowed: false,
+ };
+ const condensedBehavior = condensedBehaviorForData(objectGraph, resultData, searchExperimentsData);
+ switch (resultType) {
+ case "rooms":
+ case "multirooms":
+ case "developers":
+ case "editorial-items":
+ case "groupings":
+ if (resultType !== "editorial-items" && resultType !== "developers" && objectGraph.client.isVision) {
+ return null;
+ }
+ const todayCardConfig = defaultTodayCardConfiguration(objectGraph);
+ todayCardConfig.isSearchContext = true;
+ const todayCard = todayCardFromData(objectGraph, resultData, todayCardConfig, new TodayParseContext(metricsOptions.pageInformation, metricsOptions.locationTracker));
+ if (todayCard && todayCard.media && todayCard.media.kind === "inAppPurchase") {
+ if (objectGraph.client.isVision) {
+ return null;
+ }
+ const iapMedia = todayCard.media;
+ const todayCardInAppPurchaseLockup = iapMedia.lockup;
+ todayCardInAppPurchaseLockup.theme = "dark";
+ searchResult = new models.InAppPurchaseSearchResult(todayCardInAppPurchaseLockup);
+ }
+ else if (searchLockupCollection.resultDataShouldRenderLockupCollection(objectGraph, resultData, searchResponseMetadata)) {
+ const lockupCollection = searchLockupCollection.lockupCollectionFromResultData(objectGraph, resultData, metricsOptions);
+ if (lockupCollection) {
+ searchResult = lockupCollection;
+ }
+ }
+ else {
+ const editorialSearchResult = editorialSearchResultFromData(objectGraph, resultData, standardLockupOptions.metricsOptions, condensedBehavior);
+ if (editorialSearchResult) {
+ if (editorialSearchResult.title) {
+ editorialSearchResult.title = editorialSearchResult.title.replace(/\n/g, " ");
+ }
+ if (editorialSearchResult instanceof EditorialSearchResult && editorialSearchResult.subtitle) {
+ editorialSearchResult.subtitle = editorialSearchResult.subtitle.replace(/\n/g, " ");
+ }
+ searchResult = editorialSearchResult;
+ }
+ }
+ break;
+ case "in-apps":
+ if (objectGraph.client.isVision || objectGraph.client.isWeb) {
+ return null;
+ }
+ // Ensure the parent app data is available before proceeding with building the lockup.
+ const parentData = lockups.parentDataFromInAppData(objectGraph, resultData);
+ if (serverData.isNullOrEmpty(parentData)) {
+ return null;
+ }
+ const inAppPurchaseLockup = lockups.inAppPurchaseLockupFromData(objectGraph, resultData, standardLockupOptions);
+ inAppPurchaseLockup.theme = "dark";
+ modifyMetadataBadgeForSearchExperiment(objectGraph, inAppPurchaseLockup, searchExperimentsData);
+ searchResult = new models.InAppPurchaseSearchResult(inAppPurchaseLockup);
+ break;
+ case "apps":
+ case "app-bundles":
+ default:
+ // There should never be an iad in the non-ad search results, so remove it here
+ // before creating the lockups.
+ delete resultData.attributes["iad"];
+ if (resultType === "app-bundles") {
+ if (objectGraph.client.isVision) {
+ return null;
+ }
+ standardLockupOptions.shouldIncludeScreenshotsForChildren =
+ objectGraph.featureFlags.isEnabled("voyager_bundles_2025A");
+ const bundleLockup = lockups.lockupFromData(objectGraph, resultData, standardLockupOptions);
+ bundleLockup.showMetadataInformationInLockup = true;
+ modifyMetadataBadgeForSearchExperiment(objectGraph, bundleLockup, searchExperimentsData);
+ searchResult = new models.BundleSearchResult(bundleLockup);
+ }
+ else {
+ const lockup = lockups.mixedMediaLockupFromData(objectGraph, resultData, standardLockupOptions, {
+ canPlayFullScreen: false,
+ playbackControls: {},
+ }, searchExperimentsData);
+ modifyMetadataBadgeForSearchExperiment(objectGraph, lockup, searchExperimentsData);
+ // Extract out any associated app event from meta
+ const appEventSearchResult = appEventSearchResultFromData(objectGraph, resultData, lockup, standardLockupOptions, personalizationDataContainer, metricsOptions);
+ if (serverData.isDefinedNonNull(appEventSearchResult)) {
+ searchResult = appEventSearchResult;
+ searchResult.condensedBehavior = "never";
+ }
+ else {
+ // If no app event in meta, fallback to a regular mixed media lockup
+ searchResult = new models.AppSearchResult(lockup);
+ }
+ }
+ break;
+ }
+ if (serverData.isDefinedNonNull(searchResult) && serverData.isNull(searchResult.condensedBehavior)) {
+ searchResult.condensedBehavior = condensedBehavior;
+ }
+ return searchResult;
+ });
+}
+/**
+ * Indicates whether a search result will display an app event
+ * @param objectGraph The object graph
+ * @param searchResultData The data for the search result
+ * @param installStates A mapping of adamIDs to their respective install states that is used to determine if the app is currently installed by the user
+ * @param appStates A mapping of adamIDs to their respective app states that is used to determine if the app has been installed by the user in the past
+ * @param metricsOptions Metrics options for built lockups
+ * @param personalizationDataContainer Personalization criteria used for sorting and filtering app events
+ * @returns a boolean result
+ */
+export function searchResultWillUseAppEventDisplay(objectGraph, searchResultData, installStates, appStates, metricsOptions, personalizationDataContainer) {
+ const appEventEligibleToDisplay = searchResultIsEligibleToDisplayAppEvent(objectGraph, searchResultData);
+ if (!appEventEligibleToDisplay) {
+ return false;
+ }
+ const { dataItems } = selectedAppEventDataItems(objectGraph, searchResultData, personalizationDataContainer);
+ let appEvent;
+ for (const appEventDataItem of dataItems) {
+ appEvent = sanitizedAppEvent(objectGraph, appEventDataItem, searchResultData, metricsOptions, undefined, undefined);
+ if (serverData.isDefinedNonNull(appEvent)) {
+ break;
+ }
+ }
+ const appIsInstalled = serverData.isDefinedNonNull(installStates) && serverData.isDefinedNonNull(installStates[searchResultData.id])
+ ? installStates[searchResultData.id]
+ : false;
+ const appHasBeenInstalled = serverData.isDefinedNonNull(appStates) && serverData.isDefinedNonNull(appStates[searchResultData.id])
+ ? ["downloadable"].includes(appStates[searchResultData.id])
+ : false;
+ // App Events are only displayed in native when the user has installed the app previously and there is a valid app event
+ return (appIsInstalled || appHasBeenInstalled) && serverData.isDefinedNonNull(appEvent);
+}
+/**
+ * Derive the condensed behavior from a search result
+ * @param objectGraph The global object graph instance
+ * @param resultData The data blob for this specific search result
+ * @param pageSearchExperimentsData The page level search experiments data object
+ */
+function condensedBehaviorForData(objectGraph, resultData, pageSearchExperimentsData) {
+ var _a, _b;
+ /// Guard early against incompatible client devices
+ if (!canHaveCondensedBehaviorForClient(objectGraph)) {
+ return "never";
+ }
+ const itemSearchExperimentData = resultData.meta;
+ const itemCondensedStyle = (_a = itemSearchExperimentData === null || itemSearchExperimentData === void 0 ? void 0 : itemSearchExperimentData.displayStyle) === null || _a === void 0 ? void 0 : _a.condensed;
+ if (serverData.isDefinedNonNull(itemCondensedStyle)) {
+ return condensedBehaviorFromStyle(objectGraph, itemCondensedStyle);
+ }
+ const pageCondensedStyle = (_b = pageSearchExperimentsData === null || pageSearchExperimentsData === void 0 ? void 0 : pageSearchExperimentsData.displayStyle) === null || _b === void 0 ? void 0 : _b.condensed;
+ if (serverData.isDefinedNonNull(pageCondensedStyle)) {
+ return condensedBehaviorFromStyle(objectGraph, pageCondensedStyle);
+ }
+ return defaultCondensedBehaviorForClient(objectGraph);
+}
+/**
+ * Gets the condensed behavior from a given condensed style, or a default behavior based on the client type
+ * @param objectGraph The global object graph instance
+ * @param condensedStyle The condensed style on the data model
+ * @returns The condensed behavior for the native search result view
+ */
+function condensedBehaviorFromStyle(objectGraph, condensedStyle) {
+ switch (condensedStyle) {
+ case "always":
+ return "always";
+ case "never":
+ return "never";
+ case "when-installed":
+ return "whenInstalled";
+ default:
+ return defaultCondensedBehaviorForClient(objectGraph);
+ }
+}
+/**
+ * Determines the default condensed behavior based for a given client type
+ * @param objectGraph The global object graph instance
+ * @param condensedStyle The condensed style on the data model
+ * @returns The condensed behavior for the native search result view
+ */
+function defaultCondensedBehaviorForClient(objectGraph) {
+ switch (objectGraph.client.deviceType) {
+ case "phone":
+ return "whenInstalled";
+ default:
+ return "never";
+ }
+}
+/**
+ * Determines if the current client type supports condensed behavior
+ * @param objectGraph The global object graph instance
+ * @returns Whether condensed behavior is allowed
+ */
+function canHaveCondensedBehaviorForClient(objectGraph) {
+ switch (objectGraph.client.deviceType) {
+ case "phone":
+ return true;
+ default:
+ return false;
+ }
+}
+/**
+ * Modifies the editor's choice badge based on whether the search experiment overrides the metadataPrecendence
+ * for the badge
+ * @param objectGraph App Store ObjectGraph
+ * @param lockup The lockup we need to modify for the experiment
+ * @param searchExperimentData The experiment data to pull the metadata info from
+ */
+function modifyMetadataBadgeForSearchExperiment(objectGraph, lockup, searchExperimentData) {
+ const shouldShowEditorsChoice = metadataPrecedenceTypePreceedsType(objectGraph, searchExperimentData, "editorialBadgeInfo", "userRating");
+ if (serverData.isDefinedNonNull(shouldShowEditorsChoice)) {
+ lockup.isEditorsChoice = lockup.isEditorsChoice && shouldShowEditorsChoice;
+ }
+}
+/**
+ * Determines whether the first metadata type should precede the second type for the given experiment data
+ * @param objectGraph App Store ObjectGraph
+ * @param experimentData The experiment data to pull the metadata info from
+ * @param firstType Does this precede the second type in the experiment order
+ * @param secondType Does this succeed the second type in the experiment order
+ * @returns whether the first type precedes the second type
+ */
+function metadataPrecedenceTypePreceedsType(objectGraph, experimentData, firstType, secondType) {
+ var _a;
+ if (serverData.isNull(experimentData) || !objectGraph.client.isPhone) {
+ return null;
+ }
+ const order = (_a = experimentData === null || experimentData === void 0 ? void 0 : experimentData.displayStyle) === null || _a === void 0 ? void 0 : _a.metadataPrecedenceOrder;
+ if (!serverData.isDefinedNonNullNonEmpty(order)) {
+ return null;
+ }
+ const firstIndex = order.indexOf(firstType);
+ const secondIndex = order.indexOf(secondType);
+ if (firstIndex === -1 && secondIndex === -1) {
+ return null;
+ }
+ if (firstIndex === -1) {
+ return false;
+ }
+ if (secondIndex === -1) {
+ return true;
+ }
+ return firstIndex < secondIndex;
+}
+function editorialSearchResultFromData(objectGraph, resultData, metricsOptions, resultCondensedBehavior) {
+ return validation.context("editorialSearchResultFromData", () => {
+ let searchResult;
+ const title = mediaAttributes.attributeAsString(resultData, "name");
+ const resultType = resultData.type;
+ switch (resultType) {
+ case "groupings": {
+ const editorialSearchResult = new models.EditorialSearchResult(title);
+ const collectionAdamIds = mediaAttributes.attributeAsArrayOrEmpty(resultData, "contentIds");
+ if (serverData.isDefinedNonNullNonEmpty(collectionAdamIds)) {
+ editorialSearchResult.collectionAdamIds = collectionAdamIds;
+ }
+ else {
+ const iconArtwork = content.artworkFromApiArtwork(objectGraph, mediaAttributes.attributeAsDictionary(resultData, "artwork"), {
+ useCase: 9 /* content.ArtworkUseCase.SearchEditorialResult */,
+ allowingTransparency: true,
+ });
+ editorialSearchResult.iconArtwork = iconArtwork;
+ }
+ editorialSearchResult.type = "category";
+ searchResult = editorialSearchResult;
+ break;
+ }
+ case "rooms":
+ case "multirooms": {
+ const editorialSearchResult = new models.EditorialSearchResult(title);
+ editorialSearchResult.artwork = content.artworkFromApiArtwork(objectGraph, mediaAttributes.attributeAsDictionary(resultData, "artwork"), {
+ useCase: 9 /* content.ArtworkUseCase.SearchEditorialResult */,
+ cropCode: "sr",
+ });
+ editorialSearchResult.collectionAdamIds = mediaAttributes.attributeAsArrayOrEmpty(resultData, "contentIds");
+ editorialSearchResult.type = "collection";
+ searchResult = editorialSearchResult;
+ break;
+ }
+ case "editorial-items": {
+ if (objectGraph.bag.searchFilterEditorialItemIds.has(resultData.id)) {
+ return null;
+ }
+ // Bridge objects to Today builder.
+ const todayParseContext = new TodayParseContext(metricsOptions.pageInformation, metricsOptions.locationTracker);
+ const shouldResultBeCondensed = resultCondensedBehavior === "always";
+ const editorialSearchResult = editorialSearchResultFromTodayCardData(objectGraph, resultData, todayParseContext, shouldResultBeCondensed);
+ searchResult = editorialSearchResult;
+ break;
+ }
+ case "developers": {
+ const editorialSearchResult = new models.EditorialSearchResult(title);
+ editorialSearchResult.artwork = content.artworkFromApiArtwork(objectGraph, mediaAttributes.attributeAsDictionary(resultData, "editorialArtwork.bannerUber"), {
+ useCase: 9 /* content.ArtworkUseCase.SearchEditorialResult */,
+ cropCode: "sr",
+ });
+ editorialSearchResult.type = "developer";
+ if (isSome(editorialSearchResult.artwork)) {
+ searchResult = editorialSearchResult;
+ }
+ else {
+ let topApps = mediaRelationship.relationshipCollection(resultData, "top-apps");
+ topApps = topApps.filter((topApp) => {
+ return !filtering.shouldFilter(objectGraph, topApp, 76532 /* filtering.Filter.DeveloperPage */);
+ });
+ const topAppIds = [];
+ const topAppArtwork = [];
+ topApps.forEach((app) => {
+ topAppIds.push(app.id);
+ const appIcon = content.iconFromData(objectGraph, app, {
+ useCase: 9 /* content.ArtworkUseCase.SearchEditorialResult */,
+ });
+ if (serverData.isDefinedNonNull(appIcon)) {
+ topAppArtwork.push(appIcon);
+ }
+ });
+ editorialSearchResult.collectionAdamIds = topAppIds;
+ editorialSearchResult.collectionAppIcons = topAppArtwork;
+ // On visionOS, the developer result falls back to a lockup collection if no art is available.
+ if (objectGraph.client.isVision) {
+ const lockupCollection = searchLockupCollection.lockupCollectionFromResultData(objectGraph, resultData, metricsOptions);
+ searchResult = lockupCollection;
+ }
+ else {
+ searchResult = editorialSearchResult;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (serverData.isNull(searchResult)) {
+ return null;
+ }
+ if (searchResult instanceof EditorialSearchResult) {
+ if (searchResult.collectionAdamIds != null && searchResult.collectionAdamIds.length) {
+ const lockupCount = searchResult.collectionAdamIds.length;
+ if (lockupCount <= 5) {
+ searchResult.artworkGridType = "extraLarge";
+ }
+ else if (lockupCount <= 8) {
+ searchResult.artworkGridType = "large";
+ }
+ else if (lockupCount <= 16) {
+ searchResult.artworkGridType = "mixed";
+ }
+ else {
+ searchResult.artworkGridType = "small";
+ }
+ }
+ if (objectGraph.client.isVision) {
+ let badgeText;
+ let badgeArtwork = createArtworkForResource(objectGraph, "systemimage://appstore");
+ switch (searchResult.type) {
+ case "developer":
+ badgeText = objectGraph.loc.string("EDITORIAL_SEARCH_RESULT_TYPE_DEVELOPER_TITLE_CASE");
+ badgeArtwork = createArtworkForResource(objectGraph, "systemimage://person.crop.square");
+ break;
+ case "category":
+ badgeText = objectGraph.loc.string("EDITORIAL_SEARCH_RESULT_TYPE_CATEGORY_TITLE_CASE");
+ break;
+ case "collection":
+ badgeText = objectGraph.loc.string("EDITORIAL_SEARCH_RESULT_TYPE_COLLECTION_TITLE_CASE");
+ break;
+ case "story":
+ badgeText = objectGraph.loc.string("EDITORIAL_SEARCH_RESULT_TYPE_STORY_TITLE_CASE");
+ break;
+ default:
+ break;
+ }
+ searchResult.badgeText = badgeText;
+ searchResult.badgeArtwork = badgeArtwork;
+ }
+ }
+ const impressionOptions = metricsHelpersImpressions.impressionOptions(objectGraph, resultData, searchResult.title, metricsOptions);
+ searchResult.clickAction = lockups.actionFromData(objectGraph, resultData, impressionOptions, null);
+ metricsHelpersImpressions.addImpressionFields(objectGraph, searchResult, impressionOptions);
+ return searchResult;
+ });
+}
+function editorialSearchResultFromTodayCardData(objectGraph, cardData, todayParseContext, shouldResultBeCondensed) {
+ // Card configuration to use for building TodayCards that will be converted into editorial search results.
+ const cardConfig = defaultTodayCardConfiguration(objectGraph);
+ cardConfig.isSearchContext = true;
+ if (!objectGraph.client.isVision) {
+ cardConfig.prevailingCropCodes =
+ shouldResultBeCondensed && objectGraph.client.isPhone
+ ? { defaultCrop: "DMGE.AppST01" }
+ : { defaultCrop: "fo" };
+ }
+ // If we are on visionOS, drop any EIs that have a not-compatible app as the primary content.
+ if (objectGraph.client.isVision) {
+ const primaryContent = content.primaryContentForData(objectGraph, cardData);
+ if (isSome(primaryContent)) {
+ const runnableOnDevice = content.runnableOnDeviceWithData(objectGraph, primaryContent, objectGraph.client.deviceType, objectGraph.appleSilicon.isSupportEnabled);
+ if (!runnableOnDevice) {
+ return null;
+ }
+ }
+ }
+ const todayCard = todayCardFromData(objectGraph, cardData, cardConfig, todayParseContext);
+ if (!todayCard) {
+ return null;
+ }
+ const editorialSearchResult = new models.EditorialSearchResult(todayCard.title);
+ editorialSearchResult.type = "story";
+ editorialSearchResult.clickAction = todayCard.clickAction;
+ let collectionLockups = null;
+ if (todayCard.media) {
+ switch (todayCard.media.kind) {
+ case "brandedSingleApp":
+ const mediaSingleApp = todayCard.media;
+ editorialSearchResult.artwork = mediaSingleApp.artworks[0];
+ if (serverData.isNull(editorialSearchResult.artwork)) {
+ editorialSearchResult.iconArtwork = mediaSingleApp.icon;
+ }
+ const cardDisplayStyle = mediaAttributes.attributeAsString(cardData, "cardDisplayStyle");
+ switch (cardDisplayStyle) {
+ case TodayCardDisplayStyle.AppOfTheDay:
+ case TodayCardDisplayStyle.GameOfTheDay:
+ const relatedContentData = mediaRelationship.relationshipData(objectGraph, cardData, "card-contents");
+ if (relatedContentData) {
+ editorialSearchResult.title =
+ mediaAttributes.attributeAsString(relatedContentData, "name") ||
+ editorialSearchResult.title;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case "list":
+ const mediaList = todayCard.media;
+ collectionLockups = mediaList.lockups;
+ break;
+ case "river":
+ const mediaRiver = todayCard.media;
+ collectionLockups = mediaRiver.lockups;
+ break;
+ case "artwork":
+ const mediaArtwork = todayCard.media;
+ editorialSearchResult.artwork = mediaArtwork.artworks[0];
+ break;
+ case "grid":
+ const mediaGrid = todayCard.media;
+ collectionLockups = mediaGrid.lockups;
+ break;
+ case "multiApp":
+ const multiApp = todayCard.media;
+ collectionLockups = multiApp.lockups;
+ break;
+ case "video":
+ const mediaVideo = todayCard.media;
+ editorialSearchResult.artwork = mediaVideo.videos[0].preview;
+ editorialSearchResult.video = mediaVideo.videos[0];
+ if (todayCard.overlay instanceof models.TodayCardThreeLineOverlay) {
+ const overlay = todayCard.overlay;
+ editorialSearchResult.title = overlay.title;
+ editorialSearchResult.subtitle = overlay.description;
+ }
+ else {
+ editorialSearchResult.subtitle = mediaVideo.description;
+ }
+ break;
+ case "appEvent":
+ const media = todayCard.media;
+ editorialSearchResult.artwork = media.artworks[0];
+ editorialSearchResult.appEventFormattedDates = media.formattedDates;
+ editorialSearchResult.subtitle = todayCard.inlineDescription;
+ editorialSearchResult.tintColor = media.tintColor;
+ editorialSearchResult.type = "appEventStory";
+ if (serverData.isDefinedNonNull(todayCard.style)) {
+ switch (todayCard.style) {
+ case "light":
+ case "white":
+ editorialSearchResult.mediaOverlayStyle = "light";
+ break;
+ case "dark":
+ editorialSearchResult.mediaOverlayStyle = "dark";
+ break;
+ default:
+ errors.unreachable(todayCard.style);
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (todayCard.overlay) {
+ switch (todayCard.overlay.kind) {
+ case "lockup":
+ const cardOverlayLockup = todayCard.overlay;
+ if (!editorialSearchResult.artwork || objectGraph.client.isVision) {
+ collectionLockups = [cardOverlayLockup.lockup];
+ }
+ break;
+ case "lockupList":
+ const cardOverlayList = todayCard.overlay;
+ collectionLockups = cardOverlayList.lockups;
+ break;
+ case "paragraph":
+ const cardOverlayParagraph = todayCard.overlay;
+ editorialSearchResult.subtitle = cardOverlayParagraph.paragraph.text;
+ break;
+ default:
+ break;
+ }
+ }
+ if (serverData.isDefinedNonNull(collectionLockups)) {
+ editorialSearchResult.collectionAdamIds = [];
+ editorialSearchResult.collectionAppIcons = [];
+ for (const lockup of collectionLockups) {
+ editorialSearchResult.collectionAdamIds.push(lockup.adamId);
+ editorialSearchResult.collectionAppIcons.push(lockup.icon);
+ }
+ if (collectionLockups.length === 1) {
+ editorialSearchResult.lockup = collectionLockups[0];
+ }
+ }
+ const editorialClientParams = extractEditorialClientParams(objectGraph, cardData);
+ editorialSearchResult.editorialDisplayOptions = editorialDisplayOptionsFromClientParams(editorialClientParams);
+ /**
+ * Editorial tagline
+ */
+ const storyTagline = common.editorialSearchResultTagline(objectGraph, cardData);
+ if ((storyTagline === null || storyTagline === void 0 ? void 0 : storyTagline.length) > 0 && storyTagline !== editorialSearchResult.title) {
+ editorialSearchResult.tagline = storyTagline;
+ }
+ const heroMedia = todayCard.heroMedia;
+ if (serverData.isDefinedNonNullNonEmpty(heroMedia)) {
+ if (serverData.isDefinedNonNullNonEmpty(heroMedia.artworks[0])) {
+ editorialSearchResult.artwork = heroMedia.artworks[0];
+ editorialSearchResult.artwork.crop = "em";
+ }
+ else if (serverData.isDefinedNonNullNonEmpty(heroMedia.videos[0])) {
+ editorialSearchResult.video = heroMedia.videos[0];
+ }
+ }
+ if (editorialSearchResult.video) {
+ editorialSearchResult.video.canPlayFullScreen = false;
+ editorialSearchResult.video.playbackControls = {};
+ }
+ if (!editorialSearchResult.collectionAdamIds &&
+ !editorialSearchResult.artwork &&
+ !editorialSearchResult.iconArtwork) {
+ return null;
+ }
+ return editorialSearchResult;
+}
+/// Gets the client identifier (or null) that should be used when building lockups for a given search entity.
+function clientIdentifierOverrideForSearchEntity(objectGraph, searchEntity) {
+ return searchEntity === "watch" ? client.watchIdentifier : null;
+}
+/**
+ * Indicates whether a search result meets basic sanity requirements to display app events. This does not include business rules for massaging a valid app event. For that, see `sanitizedAppEvent` function
+ * @param objectGraph The object graph
+ * @param searchResultData The data for the search result
+ * @returns a boolean result
+ */
+function searchResultIsEligibleToDisplayAppEvent(objectGraph, searchResultData) {
+ if (!appPromotionsCommon.appEventsAreEnabled(objectGraph) || serverData.isNull(searchResultData.meta)) {
+ return false;
+ }
+ // The first organic can't use CPP data from the ad if it has an app event that will be displayed.
+ // In this case, it should continue to use DPP assets
+ const appEventDataItems = serverData.asArrayOrEmpty(searchResultData.meta, "associations.app-events.data");
+ const hasInAppEvents = appEventDataItems.length > 0;
+ // App events can be displayed on most search result types, except for these ones
+ const typesIneligibleToDisplayAppEvent = [
+ "rooms",
+ "multirooms",
+ "developers",
+ "editorial-items",
+ "groupings",
+ "in-apps",
+ "app-bundles",
+ ];
+ const typeIsEligibleToDisplayAppEvent = !typesIneligibleToDisplayAppEvent.includes(searchResultData.type);
+ return typeIsEligibleToDisplayAppEvent && hasInAppEvents;
+}
+/**
+ * Sorts and filters app event for personalization, if applicable. May filter app events, and may select just the best one
+ * @param objectGraph The object graph
+ * @param resultData The data for the search result
+ * @param personalizationDataContainer Personalization criteria used for sorting and filtering app events
+ * @returns an object representing the personalized app events for a user, along with the personalization result
+ */
+function selectedAppEventDataItems(objectGraph, resultData, personalizationDataContainer) {
+ const alwaysShowAppEvent = serverData.asBooleanOrFalse(resultData.meta, "associations.app-events.attributes.forceAppEvent");
+ const appEventDataItems = serverData.asArrayOrEmpty(resultData.meta, "associations.app-events.data");
+ if (alwaysShowAppEvent) {
+ // In this scenario, there should always be only one event to choose from,
+ // and we don't want to do any personalization.
+ return { dataItems: [appEventDataItems[0]] };
+ }
+ const personalizedDataResult = onDevicePersonalization.personalizeDataItems(objectGraph, "search", appEventDataItems, false, personalizationDataContainer, false, undefined, resultData.id);
+ const personalizedDataItems = personalizedDataResult.personalizedData;
+ if (personalizedDataItems.length <= 0) {
+ return { dataItems: [] };
+ }
+ return { dataItems: personalizedDataItems, personalizationData: personalizedDataResult };
+}
+/**
+ * Sanitizes a raw app event metadata into an AppEvent model. If the event is not hydratable or has not started, this returns null.
+ * @param objectGraph The object graph
+ * @param appEventDataItem The app event that needs to be processed
+ * @param resultData The data for the search result
+ * @param baseMetricsOptions Metrics options for built lockups
+ * @param offerEnvironment Offer environment for the lockup. Typically comes from lockup options
+ * @param offerStyle Offer style for the lockup. Typically comes from lockup options
+ * @returns the processed app event with all business rules applied, or null if the app event was disqualified
+ */
+function sanitizedAppEvent(objectGraph, appEventDataItem, resultData, baseMetricsOptions, offerEnvironment, offerStyle) {
+ const metricsOptions = {
+ ...baseMetricsOptions,
+ targetType: "eventModule",
+ };
+ const appEventOrDate = appEvents.appEventOrPromotionStartDateFromData(objectGraph, appEventDataItem, resultData, false, true, offerEnvironment, offerStyle, false, metricsOptions, false, true, null, false, false);
+ // Ignore a future AppEvent promotionStartDate here.
+ if (serverData.isNull(appEventOrDate) || appEventOrDate instanceof Date) {
+ return null;
+ }
+ else {
+ return appEventOrDate;
+ }
+}
+/**
+ * Creates an app event search result from the data and associated lockup, if an app event exists in the metadata
+ * @param resultData The data blob
+ * @param lockup The associated mixed media lockup
+ * @param standardLockupOptions: The standard lockup options for search results
+ * @param personalizationDataContainer The data container to use for personalizing the selected app event
+ * @param baseMetricsOptions Base metrics options for this result
+ * @returns {AppEventSearchResult} The app event search result, or null
+ */
+function appEventSearchResultFromData(objectGraph, resultData, lockup, standardLockupOptions, personalizationDataContainer, baseMetricsOptions) {
+ return validation.context("appEventSearchResultFromData", () => {
+ const appEventEligibleToDisplay = searchResultIsEligibleToDisplayAppEvent(objectGraph, resultData);
+ if (!appEventEligibleToDisplay) {
+ return null;
+ }
+ const { dataItems, personalizationData } = selectedAppEventDataItems(objectGraph, resultData, personalizationDataContainer);
+ let appEvent;
+ let parentAppData;
+ for (const appEventDataItem of dataItems) {
+ const appEventOrNull = sanitizedAppEvent(objectGraph, appEventDataItem, resultData, baseMetricsOptions, standardLockupOptions.offerEnvironment, standardLockupOptions.offerStyle);
+ if (serverData.isDefinedNonNull(appEventOrNull)) {
+ appEvent = appEventOrNull;
+ parentAppData = resultData !== null && resultData !== void 0 ? resultData : mediaRelationship.relationshipData(objectGraph, appEventDataItem, "app");
+ break;
+ }
+ }
+ if (serverData.isNull(appEvent)) {
+ return null;
+ }
+ const alwaysShowAppEvent = serverData.asBooleanOrFalse(resultData.meta, "associations.app-events.attributes.forceAppEvent");
+ const searchResult = new models.AppEventSearchResult();
+ searchResult.lockup = lockup;
+ searchResult.appEvent = appEvent;
+ searchResult.alwaysShowAppEvent = alwaysShowAppEvent;
+ searchResult.clickAction = lockup.clickAction;
+ const recoMetricsData = metricsHelpersUtil.combinedRecoMetricsDataFromMetricsData(null, personalizationData === null || personalizationData === void 0 ? void 0 : personalizationData.processingType, null);
+ const impressionOptions = {
+ ...baseMetricsOptions,
+ id: appEvent.appEventId,
+ kind: "inAppEvent",
+ targetType: "eventModule",
+ title: appEvent.title,
+ softwareType: null,
+ recoMetricsData: recoMetricsData,
+ };
+ if (serverData.isDefinedNonNull(parentAppData)) {
+ impressionOptions.relatedSubjectIds = [parentAppData.id];
+ }
+ metricsHelpersImpressions.addImpressionFields(objectGraph, searchResult, impressionOptions);
+ return searchResult;
+ });
+}
+//# sourceMappingURL=search-results.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-shelves.js b/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-shelves.js
new file mode 100644
index 0000000..03df66d
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/search/content/search-shelves.js
@@ -0,0 +1,53 @@
+import * as models from "../../../api/models";
+import * as mediaAttributes from "../../../foundation/media/attributes";
+import { asInterface } from "../../../foundation/json-parsing/server-data";
+import { createMetricsOptionsForGenericSearchPageShelf } from "../../metrics/helpers/search/search-shelves";
+import { isNothing } from "@jet/environment";
+// The types of search pages shelves can render in
+export var SearchPageType;
+(function (SearchPageType) {
+ SearchPageType[SearchPageType["Landing"] = 0] = "Landing";
+ SearchPageType[SearchPageType["Results"] = 1] = "Results";
+ SearchPageType[SearchPageType["ChartsAndCategories"] = 2] = "ChartsAndCategories";
+ SearchPageType[SearchPageType["Focus"] = 3] = "Focus";
+})(SearchPageType || (SearchPageType = {}));
+/**
+ * Collections the shelf's attributes
+ * @param objectGraph The App Store Object Graph
+ * @param data The shelf data object
+ * @returns The attributes for the shelf
+ */
+export function shelfAttributesFromData(objectGraph, data, shelfKind = undefined, pageKind = models.SearchPageKind.Default) {
+ var _a, _b, _c;
+ const title = (_a = mediaAttributes.attributeAsString(data, "title")) !== null && _a !== void 0 ? _a : undefined;
+ let displayStyle = (_b = mediaAttributes.attributeAsInterface(data, "displayStyle")) !== null && _b !== void 0 ? _b : undefined;
+ /// If we are on the see-all page, we currently don't get back a displayStyle object, so we need to manually provide one
+ /// for the tile brick type as see-all is hard-coded for that density everywhere else (natively)
+ if (isNothing(displayStyle) && pageKind === models.SearchPageKind.CategoriesAndCharts) {
+ displayStyle = {
+ layoutDensity: models.GenericSearchPageShelfDisplayStyleDensity.Density1,
+ layout: undefined,
+ layoutSize: undefined,
+ };
+ }
+ const itemDisplayStyleAttributes = mediaAttributes.attributeAsDictionary(data, "itemDisplayStyle");
+ const itemDisplayStyle = asInterface(itemDisplayStyleAttributes);
+ const hasSeeAll = mediaAttributes.attributeAsBooleanOrFalse(data, "hasSeeAll");
+ const displayCount = (_c = mediaAttributes.attributeAsNumber(data, "displayCount")) !== null && _c !== void 0 ? _c : undefined;
+ const seeAllURL = hasSeeAll ? data.href : undefined;
+ return new models.SearchShelfAttributes(data.id, title, displayStyle, displayCount, hasSeeAll, seeAllURL, itemDisplayStyle, shelfKind);
+}
+/**
+ * Creates a base shelf context for the search shelf
+ * @param objectGraph The App Store Object Graph
+ * @param data The shelf data object
+ * @param shelfAttributes The shelf's attributes
+ * @param searchPageContext The context for the page containing the shelf
+ * @returns A standard shelf context for the shelf
+ */
+export function baseShelfContext(objectGraph, data, shelfAttributes, searchPageContext) {
+ return {
+ metricsOptions: createMetricsOptionsForGenericSearchPageShelf(objectGraph, data, shelfAttributes, searchPageContext),
+ };
+}
+//# sourceMappingURL=search-shelves.js.map \ No newline at end of file