diff options
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/common/content/flow-preview.js')
| -rw-r--r-- | node_modules/@jet-app/app-store/tmp/src/common/content/flow-preview.js | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/content/flow-preview.js b/node_modules/@jet-app/app-store/tmp/src/common/content/flow-preview.js new file mode 100644 index 0000000..1cc3e7d --- /dev/null +++ b/node_modules/@jet-app/app-store/tmp/src/common/content/flow-preview.js @@ -0,0 +1,485 @@ +import * as validation from "@jet/environment/json/validation"; +import * as models from "../../api/models"; +import * as base from "../../api/models/base"; +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 http from "../../foundation/network/http"; +import * as urls from "../../foundation/network/urls"; +import * as externalDeepLink from "../linking/external-deep-link"; +import * as lockups from "../lockups/lockups"; +import * as metricsUtil from "../metrics/helpers/util"; +import * as offers from "../offers/offers"; +import * as reviews from "../product-page/reviews"; +import * as sharing from "../sharing"; +import * as contentArtwork from "./artwork/artwork"; +import * as content from "./content"; +import * as contentDeviceFamily from "./device-family"; +import { isSome } from "@jet/environment"; +import { Request } from "../../foundation/media/data-fetching"; +import { buildURLFromRequest } from "../../foundation/media/url-builder"; +/** + * Create and return the flow preview actions configuration for a product. + * @param objectGraph + * @param data The response data to read from. + * @param includeOfferAction Whether to include an offer action, where possible + * @param clientIdentifierOverride The suggested client identifier for the given product + * @param clickAction The flow action for viewing the product + * @param metricsOptions The metrics options to use for reporting metrics actions. + * @param metricsClickOptions TThe metrics click options for the product + * @param externalDeepLinkUrl + * @returns A configuration object for flow preview actions that can be taken on the product + */ +export function flowPreviewActionsConfigurationForProductFromData(objectGraph, data, includeOfferAction, clientIdentifierOverride, clickAction, metricsOptions, metricsClickOptions, externalDeepLinkUrl, lockupSubtitle, lockupTitle) { + return validation.context("flowPreviewActionsConfigurationForProductFromData", () => { + // Flow preview is only supported on iOS + if (objectGraph.client.deviceType !== "phone" && objectGraph.client.deviceType !== "pad") { + return null; + } + const productData = productDataFromData(objectGraph, data); + if (!serverData.isDefinedNonNullNonEmpty(productData)) { + return null; + } + const actions = []; + // Offer + let offerActionIndex = null; + let offerDisplayProperties = null; + const isPreorder = mediaAttributes.attributeAsBooleanOrFalse(productData, "isPreorder"); + if (includeOfferAction) { + const isArcade = content.isArcadeSupported(objectGraph, productData); + const offerType = lockups.offerTypeForMediaType(objectGraph, productData.type, isArcade); + const offerAction = offerActionFromData(objectGraph, productData, isPreorder, isArcade, offerType, clientIdentifierOverride, metricsClickOptions); + offerDisplayProperties = offers.displayPropertiesFromOfferAction(objectGraph, offerAction, offerType, productData, isPreorder, false, null, null, null, null, "flowPreview"); + const wrappedOfferAction = wrappedOfferActionFromData(objectGraph, productData, offerAction, isPreorder, clientIdentifierOverride, metricsClickOptions, metricsOptions, externalDeepLinkUrl); + if (serverData.isDefinedNonNull(wrappedOfferAction) && + serverData.isDefinedNonNull(offerDisplayProperties)) { + offerActionIndex = actions.length; + wrappedOfferAction.artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://arrow.down.circle"); + actions.push(wrappedOfferAction); + } + } + // Share + const shareAction = shareActionFromData(objectGraph, productData, metricsOptions); + if (serverData.isDefinedNonNull(shareAction)) { + actions.push(shareAction); + } + // Reviews + const shouldSuppressReviews = reviews.shouldSuppressReviews(objectGraph, productData); + const shouldShowRatingsAndReviews = !isPreorder && !shouldSuppressReviews; + if (shouldShowRatingsAndReviews) { + // See ratings & reviews + if (serverData.isDefinedNonNull(clickAction) && + clickAction instanceof models.FlowAction && + (clickAction.pageData instanceof models.ProductPage || + clickAction.pageData instanceof models.ShelfBasedProductPage)) { + const seeRatingsAndReviewsAction = seeRatingsAndReviewsActionFromData(objectGraph, productData, clickAction); + if (serverData.isDefinedNonNull(seeRatingsAndReviewsAction)) { + actions.push(seeRatingsAndReviewsAction); + } + } + // Write a review + const isTvOnlyApp = contentDeviceFamily.dataOnlyHasDeviceFamily(objectGraph, data, "tvos"); + if (!isTvOnlyApp) { + const writeReviewAction = writeReviewActionFromData(objectGraph, productData, lockupSubtitle, lockupTitle); + if (serverData.isDefinedNonNull(writeReviewAction)) { + actions.push(writeReviewAction); + } + } + } + return new models.FlowPreviewActionsConfiguration(actions, offerDisplayProperties, offerActionIndex); + }); +} +/** + * Create and return the flow preview configuration for a review object + * @param data The response data to read from. + * @param deviceId The UUID for the user's device. + * @param adamId The adam id of the product associated with the review + * @returns A configuration object for flow preview actions that can be taken on the review + */ +export function flowPreviewActionsConfigurationForReviewFromData(objectGraph, data, deviceId, adamId, reviewText) { + var _a; + if (serverData.isNullOrEmpty(data) || + (objectGraph.client.deviceType !== "phone" && + objectGraph.client.deviceType !== "pad" && + objectGraph.client.deviceType !== "mac")) { + return null; + } + const actions = [ + voteActionFromData(objectGraph, data, deviceId, true), + voteActionFromData(objectGraph, data, deviceId, false), + ]; + // Workaround for missing report concern url in bag + if (((_a = objectGraph.bag.reportConcernUrl) === null || _a === void 0 ? void 0 : _a.length) > 0) { + actions.push(reportConcernActionFromData(objectGraph, data, deviceId)); + } + if ((reviewText === null || reviewText === void 0 ? void 0 : reviewText.length) > 0) { + actions.push(copyReviewAction(objectGraph, reviewText)); + } + return new models.FlowPreviewActionsConfiguration(actions); +} +/** + * Create and return the flow preview configuration for a review summary object + * @param data The response data to read from. + * @param adamId The adam ID of the app + * @param appName The name of the app + * @param reviewSummaryId The ID of the review summary + * @param reviewSummaryText The text of the review summary + * @param deviceId The UUID for the customer's device. + * @returns A configuration object for flow preview actions that can be taken on the review summary + */ +export function flowPreviewActionsConfigurationForReviewSummaryFromData(objectGraph, data, adamId, appName, reviewSummaryId, reviewSummaryText, deviceId) { + if (!objectGraph.client.isiOS) { + return null; + } + const actions = []; + const reportConcernAction = reviewSummaryReportConcernActionFromData(objectGraph, data, reviewSummaryId, deviceId); + if (isSome(reportConcernAction)) { + actions.push(reportConcernAction); + } + const learnMoreAction = reviews.reviewSummaryLearnMoreAction(objectGraph); + if (isSome(learnMoreAction)) { + actions.push(learnMoreAction); + } + const fileRadarAction = fileReviewSummaryRadarAction(objectGraph, adamId, appName, reviewSummaryId, reviewSummaryText); + if (isSome(fileRadarAction)) { + actions.push(fileRadarAction); + } + return new models.FlowPreviewActionsConfiguration(actions); +} +/** + * Creates a report a concern action for a given review + * @param data The response data to read from. + * @param deviceId The UUID for the user's device. + * @returns A report concern action + */ +export function reviewSummaryReportConcernActionFromData(objectGraph, data, reviewSummaryId, deviceId) { + return validation.context("reviewSummaryReportConcernActionFromApiRow", () => { + const isEnabled = serverData.asBoolean(data, "enabled"); + if (!isEnabled) { + return null; + } + const sendAction = createReviewSummaryReportConcernHtmlTemplateAction(objectGraph, reviewSummaryId); + const concerns = serverData.asArrayOrEmpty(data, "concerns"); + const reviewSummaryConcerns = concerns.map((concernData) => { + let name; + let uppercaseName; + const concernKind = serverData.asString(concernData, "kind"); + switch (concernKind) { + case "OFFENSIVE": + case "OFFENSIVE_OR_HARMFUL": + name = objectGraph.loc.string("ProductPage.ReviewSummary.ReportAConcern.Offensive.Name"); + uppercaseName = objectGraph.loc.string("ProductPage.ReviewSummary.ReportAConcern.Offensive.UppercaseName"); + break; + case "MISREPRESENTING_THE_APP": + case "MISREPRESENT": + name = objectGraph.loc.string("ProductPage.ReviewSummary.ReportAConcern.Misrepresent.Name"); + uppercaseName = objectGraph.loc.string("ProductPage.ReviewSummary.ReportAConcern.Misrepresent.UppercaseName"); + break; + case "SOMETHING_ELSE": + name = objectGraph.loc.string("ProductPage.ReviewSummary.ReportAConcern.SomethingElse.Name"); + uppercaseName = objectGraph.loc.string("ProductPage.ReviewSummary.ReportAConcern.SomethingElse.UppercaseName"); + break; + default: + break; + } + return new base.ReportConcernReason(concernKind, name, uppercaseName); + }); + if (reviewSummaryConcerns.length === 0) { + return null; + } + const title = objectGraph.loc.string("ACTION_REVIEW_REPORT"); + const explanation = objectGraph.loc.string("ProductPage.ReviewSummary.ReportAConcern.Explanation"); + const action = new models.ReviewSummaryReportConcernAction(reviewSummaryConcerns, title, explanation, sendAction); + action.artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://exclamationmark.circle"); + return action; + }); +} +function createReviewSummaryReportConcernHtmlTemplateAction(objectGraph, reviewSummaryId) { + const request = new Request(objectGraph); + request.includeAppBinaryTraitsAttribute = false; + request.resourceType = "concerns"; + const url = buildURLFromRequest(objectGraph, request); + const sendAction = new models.HttpTemplateAction(url.toString()); + sendAction.method = "POST"; + sendAction.disableCache = true; + sendAction.needsMediaToken = true; + sendAction.headers = { "Content-Type": "application/json" }; + sendAction.bodyDictionary = { + report: { contentId: reviewSummaryId, contentKind: "review-summaries", concerns: null }, + }; + const successToast = new models.AlertAction("toast"); + successToast.title = objectGraph.loc.string("TOAST_CONCERN_REPORTED_TITLE"); + successToast.message = objectGraph.loc.string("TOAST_CONCERN_REPORTED_DESCRIPTION"); + successToast.artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://exclamationmark.circle"); + sendAction.successAction = successToast; + const failureAlert = new models.AlertAction("default"); + failureAlert.title = objectGraph.loc.string("Alert.GenericError.Title"); + failureAlert.message = objectGraph.loc.string("Alert.GenericError.Message"); + failureAlert.isCancelable = true; + sendAction.failureAction = failureAlert; + return sendAction; +} +/** + * Walks through the given data to extract the data blob for a product + * @param data The response data to read from. + * @returns Response data for a product + */ +function productDataFromData(objectGraph, data) { + return validation.context(`productDataFromData: ${data.type}`, () => { + switch (data.type) { + case "apps": + case "app-bundles": { + return data; + } + case "editorial-items": { + const cardContents = mediaRelationship.relationshipCollection(data, "card-contents"); + if (serverData.isDefinedNonNullNonEmpty(cardContents) && cardContents.length === 1) { + const cardContentsData = cardContents[0]; + return productDataFromData(objectGraph, cardContentsData); + } + break; + } + case "editorial-elements": { + const contents = mediaRelationship.relationshipCollection(data, "contents"); + if (serverData.isDefinedNonNullNonEmpty(contents) && contents.length === 1) { + const contentData = contents[0]; + return productDataFromData(objectGraph, contentData); + } + break; + } + default: { + return null; + } + } + return null; + }); +} +/** + * Create an offer action for the provided product data + * @param data The response data to read from. + * @param isPreorder Whether the product is a pre-order + * @param isArcade Whether the product is an Arcade product + * @param offerType The type of offer for the product + * @param clientIdentifierOverride The suggested client identifier for the given product + * @param metricsClickOptions TThe metrics click options for the product + * @returns An offer action + */ +function offerActionFromData(objectGraph, data, isPreorder, isArcade, offerType, clientIdentifierOverride, metricsClickOptions) { + if (serverData.isNull(data) || data.type !== "apps") { + return null; + } + const offerData = offers.offerDataFromData(objectGraph, data); + const appIcon = content.iconFromData(objectGraph, data, null, clientIdentifierOverride); + const metricsPlatformDisplayStyle = metricsUtil.metricsPlatformDisplayStyleFromData(objectGraph, data, appIcon, clientIdentifierOverride); + const offerAction = offers.offerActionFromOfferData(objectGraph, offerData, data, isPreorder, false, metricsPlatformDisplayStyle, metricsClickOptions, "flowPreview"); + return offerAction; +} +/** + * Wraps an offer action if required + * @param data The response data to read from. + * @param offerAction The provided offer action to wrap + * @param clientIdentifierOverride The suggested client identifier for the given product + * @param metricsClickOptions TThe metrics click options for the product + * @param metricsOptions The metrics options to use for reporting metrics actions. + * @param externalDeepLink The promotional deep link url to use on the product's offer. + * @returns A wrapped offer action + */ +function wrappedOfferActionFromData(objectGraph, data, offerAction, isPreorder, clientIdentifierOverride, metricsClickOptions, metricsOptions, externalDeepLinkUrl) { + if (serverData.isNull(offerAction)) { + return null; + } + let wrappedOfferAction = offers.wrapOfferActionIfNeeded(objectGraph, offerAction, data, isPreorder, metricsClickOptions, "flowPreview", clientIdentifierOverride); + if ((externalDeepLinkUrl === null || externalDeepLinkUrl === void 0 ? void 0 : externalDeepLinkUrl.length) > 0) { + // Configure cross link as well as deep link action. + wrappedOfferAction = externalDeepLink.deepLinkActionWrappingAction(objectGraph, wrappedOfferAction, offerAction.adamId, null, externalDeepLinkUrl, false, metricsClickOptions); + } + return wrappedOfferAction; +} +/** + * Creates and returns an action for sharing a product + * @param data The response data to read from. + * @param metricsOptions The metrics options to use for reporting metrics actions. + * @returns A share action or null + */ +function shareActionFromData(objectGraph, data, metricsOptions) { + const shareAction = sharing.shareProductActionFromData(objectGraph, data, metricsOptions.pageInformation, metricsOptions.locationTracker); + if (serverData.isDefinedNonNull(shareAction)) { + shareAction.title = objectGraph.loc.string("FLOW_PREVIEW_ACTION_SHARE"); + shareAction.artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://square.and.arrow.up"); + return shareAction; + } + return shareAction; +} +/** + * Creates and returns an action for navigating to a product's ratings & reviews + * @param data The response data to read from. + * @param clickAction The flow action for viewing the product + * @returns An action or null + */ +function seeRatingsAndReviewsActionFromData(objectGraph, data, clickAction) { + const seeRatingsAndReviewsAction = reviews.seeRatingsAndReviewsActionFromClickAction(objectGraph, data.id, clickAction); + if (serverData.isDefinedNonNull(seeRatingsAndReviewsAction)) { + seeRatingsAndReviewsAction.title = objectGraph.loc.string("FLOW_PREVIEW_ACTION_SEE_RATINGS_AND_REVIEWS"); + seeRatingsAndReviewsAction.artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://star"); + seeRatingsAndReviewsAction.animationBehavior = "never"; + } + return seeRatingsAndReviewsAction; +} +/** + * Creates and returns an action for writing a product review + * @param data The response data to read from. + * @returns An action or null + */ +function writeReviewActionFromData(objectGraph, data, lockupSubtitle, lockupTitle) { + const writeReviewAction = reviews.writeReviewActionFromData(objectGraph, data, lockupSubtitle, lockupTitle); + if (serverData.isDefinedNonNull(writeReviewAction)) { + writeReviewAction.title = objectGraph.loc.string("FLOW_PREVIEW_ACTION_WRITE_REVIEW"); + writeReviewAction.artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://square.and.pencil"); + } + return writeReviewAction; +} +/** + * Create and returns an action for voting whether a review was helpful or not + * @param data The response data to read from. + * @param deviceId The UUID for the user's device. + * @param helpful Whether or not the vote is indicating the review was helpful, or not helpful. + * @returns A new vote action. + */ +export function voteActionFromData(objectGraph, data, deviceId, helpful) { + const baseVoteUrl = objectGraph.bag.voteUrl; + const reviewId = serverData.asString(data, "id", "coercible"); + const voteUrl = new urls.URL(baseVoteUrl).param("userReviewId", reviewId); + if (objectGraph.client.isVision) { + // We only attach this parameter for visionOS, as the other platforms have legacy handling in place + // that we do not want to interrupt. + voteUrl.param("version", "2"); + } + const action = new models.HttpAction(voteUrl.build()); + const successToast = new models.AlertAction("toast"); + if (helpful) { + action.title = objectGraph.loc.string("ACTION_REVIEW_HELPFUL"); + successToast.title = objectGraph.loc.string("TOAST_HELPFUL_TITLE"); + successToast.message = objectGraph.loc.string("TOAST_HELPFUL_DESCRIPTION"); + action.artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://hand.thumbsup"); + successToast.artwork = contentArtwork.createArtworkForResource(objectGraph, objectGraph.client.isVision ? "systemimage://hand.thumbsup.fill" : "systemimage://hand.thumbsup"); + } + else { + action.title = objectGraph.loc.string("ACTION_REVIEW_NOT_HELPFUL"); + successToast.title = objectGraph.loc.string("TOAST_NOT_HELPFUL_TITLE"); + successToast.message = objectGraph.loc.string("TOAST_NOT_HELPFUL_DESCRIPTION"); + action.artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://hand.thumbsdown"); + successToast.artwork = contentArtwork.createArtworkForResource(objectGraph, objectGraph.client.isVision ? "systemimage://hand.thumbsdown.fill" : "systemimage://hand.thumbsdown"); + } + action.method = "POST"; + action.isStoreRequest = true; + action.disableCache = true; + action.headers = { "Content-Type": http.FormBuilder.contentType }; + action.body = new http.FormBuilder() + .param("vote", helpful ? "1" : "0") + .param("guid", deviceId) + .build(); + action.successAction = successToast; + return action; +} +/** + * Creates a report a concern action for a given review + * @param data The response data to read from. + * @param deviceId The UUID for the user's device. + * @returns A report concern action + */ +export function reportConcernActionFromData(objectGraph, data, deviceId) { + return validation.context("reportConcernActionFromApiRow", () => { + const reviewId = serverData.asString(data, "id", "coercible"); + const reportConcernUrl = objectGraph.bag.reportConcernUrl; + const sendAction = new models.HttpTemplateAction(reportConcernUrl); + sendAction.method = "POST"; + sendAction.isStoreRequest = true; + sendAction.disableCache = true; + sendAction.needsAuthentication = true; + sendAction.headers = { "Content-Type": http.FormBuilder.contentType }; + sendAction.body = new http.FormBuilder().param("userReviewId", reviewId).param("guid", deviceId).build(); + const reasonParameter = new models.HttpTemplateParameter("selectedReason", "formBody", "decimalPad"); + const explanationParameter = new models.HttpTemplateParameter("explanation", "formBody", "text"); + sendAction.parameters = [reasonParameter, explanationParameter]; + if (!objectGraph.client.isVision) { + const successToast = new models.AlertAction("toast"); + successToast.title = objectGraph.loc.string("TOAST_CONCERN_REPORTED_TITLE"); + successToast.message = objectGraph.loc.string("TOAST_CONCERN_REPORTED_DESCRIPTION"); + successToast.artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://exclamationmark.circle"); + sendAction.successAction = successToast; + } + const failureAlert = new models.AlertAction("default"); + failureAlert.title = objectGraph.loc.string("Alert.GenericError.Title"); + failureAlert.message = objectGraph.loc.string("Alert.GenericError.Message"); + failureAlert.isCancelable = true; + sendAction.failureAction = failureAlert; + let concernReasons = objectGraph.bag.reportConcernReasons; + if (serverData.isNullOrEmpty(concernReasons)) { + // Not available until 18G + concernReasons = [ + { + reasonId: "1", + name: "It contains offensive material", + upperCaseName: "IT CONTAINS OFFENSIVE MATERIAL", + }, + { + reasonId: "8", + name: "It's off-topic", + upperCaseName: "IT\u2019S OFF-TOPIC", + }, + { + reasonId: "111003", + name: "It looks like spam", + upperCaseName: "IT LOOKS LIKE SPAM", + }, + { + reasonId: "7", + name: "Something else", + upperCaseName: "SOMETHING ELSE", + }, + ]; + } + const reasons = concernReasons.map((reasonData) => { + return new base.ReportConcernReason(serverData.asString(reasonData, "reasonId"), serverData.asString(reasonData, "name"), serverData.asString(reasonData, "upperCaseName")); + }); + const action = new models.ReportConcernAction(reasons); + action.title = objectGraph.loc.string("ACTION_REVIEW_REPORT"); + action.explanation = objectGraph.bag.reportConcernExplanation; + if (serverData.isNullOrEmpty(action.explanation)) { + // Not available until 18G + action.explanation = "Tell us a little more (Optional)"; + } + action.artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://exclamationmark.circle"); + action.sendAction = sendAction; + return action; + }); +} +export function copyReviewAction(objectGraph, reviewText) { + const copyTextAction = new models.CopyTextAction(reviewText); + copyTextAction.title = objectGraph.loc.string("ACTION_REVIEW_COPY"); + copyTextAction.artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://doc.on.doc"); + return copyTextAction; +} +/** + * Creates an action for filing a radar against a Review Summary + * @param objectGraph Current object graph + * @param adamId The ID of the app + * @param appName The name of the app + * @param reviewSummaryId The ID of the review summary + * @param reviewSummaryText The text of the review summary + * @returns An action that will deep link to Radar + */ +function fileReviewSummaryRadarAction(objectGraph, adamId, appName, reviewSummaryId, reviewSummaryText) { + if (!["debug", "internal"].includes(objectGraph.client.buildType)) { + return null; + } + const componentId = "999915"; // ASE App Store | Recommendations + const title = `Review Summary Feedback: ${appName}`; + const description = `App ID: ${adamId}\nApp name: ${appName}\nReview summary ID: ${reviewSummaryId}\nReview summary: ${reviewSummaryText}\n\nFeedback: `; + const url = `tap-to-radar://new/problem?componentid=${componentId}&title=${title}&description=${description}`; + const action = new models.ExternalUrlAction(url, true); + action.title = objectGraph.loc.string("Action.ProvideFeedback"); + action.artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://ant.circle"); + return action; +} +//# sourceMappingURL=flow-preview.js.map
\ No newline at end of file |
