import { FetchTimingMetricsBuilder } from "@jet/environment/metrics/fetch-timing-metrics-builder"; import * as validation from "@jet/environment/json/validation"; import * as models from "../../api/models"; import * as mediaUrlMapping from "../../common/builders/url-mapping"; import * as categories from "../../common/categories"; import * as topChartsCommon from "../../common/top-charts-common"; import * as serverData from "../../foundation/json-parsing/server-data"; import { isSome } from "@jet/environment/types/optional"; import { makeChartsPageIntent } from "../../api/intents/charts-page-intent"; import { segmentFromData } from "../../common/charts/charts-page-model"; import { makeChartsPageURL } from "../../common/charts/charts-page-url"; import { getLocale } from "../../common/locale"; import { getPlatform } from "../../common/preview-platform"; import * as mediaDataStructure from "../../foundation/media/data-structure"; import * as mediaUrlBuilder from "../../foundation/media/url-builder"; import * as urls from "../../foundation/network/urls"; export async function fetchAndRenderTopChartPage(objectGraph, genreId, charts, selectedChart, ages) { if (serverData.isNullOrEmpty(charts)) { const siblingChart = topChartsCommon.lookupFallbackSiblingChart(objectGraph, selectedChart); if (siblingChart !== undefined) { charts = `${selectedChart},${siblingChart}`; // charts not provided, use hardcoded sibling along with selected chart } else { charts = selectedChart; // charts not provided, use selected chart } } else if (!charts.includes(selectedChart)) { charts += `,${selectedChart}`; // charts missing selected chart, so inject it. } // Request // Note: `FetchTimingMetricsBuilder` API currently only supports instrumenting a single primary network call. // We will choose to use the network metrics data from the top charts call. const fetchTimingMetricsBuilder = new FetchTimingMetricsBuilder(); const modifiedObjectGraph = objectGraph.addingFetchTimingMetricsBuilder(fetchTimingMetricsBuilder); const topChartsPromise = topChartsCommon.fetchTopChartsData(modifiedObjectGraph, genreId, charts, ages); let categoriesRequestPromise; // Vision currently doesn't support displaying categories on the Charts page. if (!objectGraph.client.isVision) { categoriesRequestPromise = topChartsCommon.fetchCategoriesDataForPlatform(objectGraph, charts, genreId); } // Response return await Promise.all([topChartsPromise, categoriesRequestPromise]).then(([topChartsResponse, categoriesResponse]) => { return fetchTimingMetricsBuilder.measureModelConstruction(() => { const categoriesList = categories.categoryListFromApiResponse(objectGraph, categoriesResponse); const context = serverData.isNull(categoriesList) ? categories.createContextFromGenre(genreId) : categories.createContextFromList(categoriesList); return topChartsPageFromApiResponse(modifiedObjectGraph, topChartsResponse, genreId, categoriesList, selectedChart, context); }); }); } /** * Creates a Top Charts Page model object from a server data structure. * * @param objectGraph The object graph for the App Store. * @param response The server response to parse. * @param genreId The genre identifier of the genre to which this page belongs. * @param categoriesList The list of categories for top charts picker. * @param selectedChart The selected chart, typically shown in a segemented control. * @param context The context of the charts, .e.g. All, Apps, or Games. * @returns A top charts page model object. */ export function topChartsPageFromApiResponse(objectGraph, response, genreId, categoriesList, selectedChart, context) { return validation.context("topChartsPageFromApiResponse", () => { var _a; // Categories const topChartsCategories = []; if (isSome(categoriesList)) { for (const categoryListItem of categoriesList.categories) { if (isSome(categoryListItem.genreId)) { topChartsCategories.push(convertCategoryToTopChartsCategory(objectGraph, categoryListItem)); } } } // Construct the segments and sort by chart name to prevent the order from changing const rawSegments = mediaDataStructure.chartResultsFromServerResponse(response); const segments = rawSegments .map((segment) => { return segmentFromData(objectGraph, segment, response, genreId, context); }) .sort((a, b) => { return a.chart.localeCompare(b.chart); }); const segmentHref = serverData.asString(rawSegments, "0.href"); const segmentURL = new urls.URL(segmentHref); const segmentGenreId = (_a = segmentURL.query["genre"]) !== null && _a !== void 0 ? _a : genreId; const ages = segmentURL.query["ages"]; const selectedCategory = findCategoryInTree(segmentGenreId, ages, topChartsCategories); const title = topChartsCommon.topChartsPageTitleForSegments(objectGraph, segments); // Build Page const topChartsPage = new models.TopChartsPage(segmentGenreId, selectedCategory === null || selectedCategory === void 0 ? void 0 : selectedCategory.ageBandId, title, segments, selectedCategory === null || selectedCategory === void 0 ? void 0 : selectedCategory.name, topChartsCategories); // The "web" client needs a `FlowAction` defined on each category and segment to perform navigation if (objectGraph.client.isWeb) { for (const category of topChartsCategories) { const destination = makeChartsPageIntent({ ...getLocale(objectGraph), ...getPlatform(objectGraph), genreId: category.genreId, chart: selectedChart, ageBandId: category.ageBandId, }); const pageUrl = makeChartsPageURL(objectGraph, destination); const action = new models.FlowAction("topCharts"); action.destination = destination; action.pageUrl = pageUrl; category.chartSelectAction = action; } for (const segment of segments) { const destination = makeChartsPageIntent({ ...getLocale(objectGraph), ...getPlatform(objectGraph), genreId, chart: segment.chart, ageBandId: selectedCategory === null || selectedCategory === void 0 ? void 0 : selectedCategory.ageBandId, }); const pageUrl = makeChartsPageURL(objectGraph, destination); const action = new models.FlowAction("topCharts"); action.destination = destination; action.pageUrl = pageUrl; segment.segmentSelectAction = action; } } // Find selected chart segment const selectedIndex = segments.findIndex((segment) => segment.chart === selectedChart); if (selectedIndex >= 0) { topChartsPage.initialSegmentIndex = selectedIndex; } // Decorate the final page model with network performance metrics. const fetchTimingMetricsBuilder = objectGraph.fetchTimingMetricsBuilder; if (isSome(fetchTimingMetricsBuilder)) { fetchTimingMetricsBuilder.decorate(topChartsPage); } return topChartsPage; }); } export function convertCategoryToTopChartsCategory(objectGraph, category) { const topChartsPageRequest = mediaUrlMapping.mediaApiChartsRequest(objectGraph, category.genreId, category.ageBandId); const children = category.children.map((child) => convertCategoryToTopChartsCategory(objectGraph, child)); return new models.TopChartCategory(category, mediaUrlBuilder.buildURLFromRequest(objectGraph, topChartsPageRequest).toString(), children); } /** * Recursively search through a category list to find a matching genre/age * @param genreId The genreId to search for * @param ageBandId The age band to include when searching for selected genre * @param categoryList The array of categories to search recursively through * @returns A found category, or null if not found. */ function findCategoryInTree(genreId, ageBandId, categoryList) { for (const category of categoryList) { if ((ageBandId && category.ageBandId === ageBandId) || (!ageBandId && !category.ageBandId && category.genreId === genreId)) { return category; } const result = findCategoryInTree(genreId, ageBandId, category.children); if (result) { return result; } } return null; } //# sourceMappingURL=top-charts-page.js.map