import * as models from "../../api/models"; import * as serverData from "../../foundation/json-parsing/server-data"; import * as mediaAttributes from "../../foundation/media/attributes"; import * as mediaRelationship from "../../foundation/media/relationships"; import * as appPromotionCommon from "./app-promotions-common"; import * as offerFormatting from "../offers/offer-formatting"; import * as offers from "../offers/offers"; import * as color from "../../foundation/util/color-util"; import { isNothing, isSome } from "@jet/environment/types/optional"; import { contentAttributeAsDictionary } from "../content/attributes"; import * as content from "../content/content"; /** * Creates a contingent offer object. * @param data The data blob * @param parentAppData The related parent app of this app event. If not provided will be derived from `data`. * @param offerEnvironment The preferred environment for the offer * @param offerStyle The preferred style of the offer * @param baseMetricsOptions The base metrics options * @param allowEndedEvents Whether events in the past are allowed * @param includeLockupClickAction Whether to include the click action for the lockup * @param isArcadePage Whether or not this is presented on the Arcade page * @returns an ContingentOffer or null */ export function contingentOfferFromData(objectGraph, data, parentAppData, offerEnvironment, offerStyle, baseMetricsOptions, includeLockupClickAction, isArcadePage) { var _a, _b, _c; if (data.type !== "contingent-items") { return null; } // Artwork const moduleArtwork = appPromotionCommon.artworkFromPlatformData(objectGraph, data, "lockupArtwork"); if (serverData.isNull(moduleArtwork)) { return null; } const isArtworkDark = color.isDarkColor(moduleArtwork.backgroundColor); const mediaOverlayStyle = isArtworkDark ? "dark" : "light"; // Labels const badge = mediaAttributes.attributeAsString(data, "badge"); const subtitle = mediaAttributes.attributeAsString(data, "subtitle"); const label = mediaAttributes.attributeAsString(data, "label"); const additionalInfoLabel = objectGraph.loc.string("ContingentOffer.AdditionalInfoButton.Title"); if (isNothing(badge) || isNothing(label)) { return null; } // The discounted offer const branch = mediaRelationship.relationshipData(objectGraph, data, "branch"); if (isNothing(branch) || serverData.isNullOrEmpty(serverData.asDictionary(branch, "meta.contingentItemOffer"))) { return null; } const formattedText = formattedTextFromData(objectGraph, data); if (isNothing(formattedText) || isNothing(formattedText === null || formattedText === void 0 ? void 0 : formattedText.title)) { return null; } // The Apps const resolvedBranchAppData = mediaRelationship.relationshipData(objectGraph, data, "branch-app"); const supportsStreamlinedBuy = mediaAttributes.attributeAsBooleanOrFalse(resolvedBranchAppData, "supportsStreamlinedBuy"); const offerLockup = appPromotionCommon.lockupFromData(objectGraph, data, resolvedBranchAppData, "", offerEnvironment, offerStyle, false, baseMetricsOptions, includeLockupClickAction, null, isArcadePage, true); if (isNothing(offerLockup)) { return null; } // Trunk Lockup. The app you need to be subscribed to in order to get the discounted offer const resolvedTrunkAppData = mediaRelationship.relationshipData(objectGraph, data, "trunk-app"); let trunkAppIcon; if (isSome(resolvedTrunkAppData) && isSome(subtitle) && (subtitle === null || subtitle === void 0 ? void 0 : subtitle.length) > 0) { trunkAppIcon = (_a = appPromotionCommon.artworkFromPlatformData(objectGraph, resolvedTrunkAppData, "artwork")) !== null && _a !== void 0 ? _a : appPromotionCommon.artworkFromData(objectGraph, resolvedTrunkAppData, "artwork"); } // When displaying a first party offer the trunk artwork is in a different location if (isSome(subtitle) && (subtitle === null || subtitle === void 0 ? void 0 : subtitle.length) > 0) { trunkAppIcon = (_b = firstPartyTrunkLockupFromData(objectGraph, data)) !== null && _b !== void 0 ? _b : trunkAppIcon; } // Offer Strings const additionalInfo = formattedAdditionalInfoFromData(objectGraph, data, branch); const contingentOffer = new models.ContingentOffer(moduleArtwork, mediaOverlayStyle, supportsStreamlinedBuy, additionalInfoLabel, formattedText.title, subtitle !== null && subtitle !== void 0 ? subtitle : undefined, (_c = formattedText.description) !== null && _c !== void 0 ? _c : undefined, label, badge, additionalInfo, trunkAppIcon !== null && trunkAppIcon !== void 0 ? trunkAppIcon : undefined, offerLockup); /// The raw title is used for metrics purposes contingentOffer.title = formattedText.rawTitle; // Detail page click action if (serverData.isDefinedNonNull(resolvedBranchAppData)) { contingentOffer.clickAction = appPromotionCommon.detailPageClickActionFromData(objectGraph, data, resolvedBranchAppData, contingentOffer, baseMetricsOptions, includeLockupClickAction); } return contingentOffer; } /** * Creates the Title and description of the contingent offer out of a templated string * @param data A data blob for the contingent-item * @returns Creates a formatted string of the tile including the subscription price and period */ export function formattedTextFromData(objectGraph, data) { var _a, _b, _c; const branch = mediaRelationship.relationshipData(objectGraph, data, "branch"); if (isNothing(branch)) { return undefined; } const regularOffer = offers.offerDataFromData(objectGraph, branch); const discountedOffer = serverData.asDictionary(branch, "meta.contingentItemOffer"); if (serverData.isNullOrEmpty(regularOffer) || serverData.isNullOrEmpty(discountedOffer)) { return undefined; } // Price Strings // Adding \u{2060} (Word Joiner) prevents the string from breaking on the slash character const regularPrice = (_a = formattedPrice(objectGraph, serverData.asString(regularOffer, "recurringSubscriptionPeriod"), serverData.asNumber(regularOffer, "numOfPeriods"), serverData.asString(regularOffer, "priceFormatted"))) === null || _a === void 0 ? void 0 : _a.replace("/", "/\u{2060}"); const discountedPrice = (_b = formattedPrice(objectGraph, serverData.asString(discountedOffer, "recurringSubscriptionPeriod"), serverData.asNumber(discountedOffer, "numOfPeriods"), serverData.asString(discountedOffer, "priceFormatted"))) === null || _b === void 0 ? void 0 : _b.replace("/", "/\u{2060}"); if (serverData.isNullOrEmpty(regularPrice) || serverData.isNullOrEmpty(discountedPrice)) { return null; } const title = mediaAttributes.attributeAsString(data, "name"); const titleKeys = { "@@discountedPrice@@/@@recurringSubscriptionPeriod@@": discountedPrice, "@@regularPrice@@/@@recurringSubscriptionPeriod@@": regularPrice, "@@discountedPricePerRecurringSubscriptionPeriod@@": discountedPrice, "@@regularPricePerRecurringSubscriptionPeriod@@": regularPrice, }; let titleTemplate = title !== null && title !== void 0 ? title : ""; Object.keys(titleKeys).forEach((element) => { titleTemplate = titleTemplate.replace(element, titleKeys[element]); }); const htmlRegex = /<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g; const rawTitle = titleTemplate.replace(htmlRegex, ""); const formattedTitle = new models.Paragraph(titleTemplate, "text/x-apple-as3-nqml", "appPromotionTitle"); const developerDetailText = (_c = mediaAttributes.attributeAsString(data, "description.standard")) !== null && _c !== void 0 ? _c : ""; const detailTemplate = objectGraph.loc.string("ContingentOffer.Description.Format"); const descriptionKeys = { "@@BranchName@@": mediaAttributes.attributeAsString(branch, "name"), "@@RegularPrice@@": regularPrice, "@@DiscountedPrice@@": discountedPrice, }; let description = detailTemplate; Object.keys(descriptionKeys).forEach((element) => { description = description.replace(element, descriptionKeys[element]); }); const formattedDescription = [description, developerDetailText].join(" "); return { title: formattedTitle, rawTitle, description: formattedDescription, }; } /** * Creates a readable subscription price * @param recurringSubscriptionPeriod The subscription recurrence in the server format. (eg P1M) * @param price A price string in a readable format like $3.99. * @returns A formatted string eg $3.99/month */ function formattedPrice(objectGraph, recurringSubscriptionPeriod, numberOfPeriods, price) { const subscriptionRecurrence = offerFormatting.subscriptionRecurrenceForServerRecurrence(objectGraph, recurringSubscriptionPeriod, numberOfPeriods); if (isNothing(subscriptionRecurrence) || isNothing(price)) { return undefined; } return offerFormatting.priceDurationString(objectGraph, subscriptionRecurrence.type, subscriptionRecurrence.periodDuration, price); } /** * Creates the additional info text of the contingent offer out of a templated string * @param data A data blob * @param branch A data blob containing the offer also known as the branch * @param trunk A data blob containing the required subscription app also known as the trunk * @returns Creates a formatted paragraph containing the contents of the additional info page. */ function formattedAdditionalInfoFromData(objectGraph, data, branch) { const skuNameTitle = "" + mediaAttributes.attributeAsString(branch, "name") + ""; const skuDescription = mediaAttributes.attributeAsString(branch, "description.standard") + "
"; const termsTitle = "" + objectGraph.loc.string("ContingentOffer.Terms.Title") + ""; const terms = mediaAttributes.attributeAsString(data, "additionalTerms"); return new models.Paragraph([skuNameTitle, skuDescription, termsTitle, terms].join("
"), "text/x-apple-as3-nqml"); } /** * If the contingent offer has a an associated trunk use that instead of the trunk-app * @param data The data blob * @returns Creates a lockup to use as the TrunkLockup */ function firstPartyTrunkLockupFromData(objectGraph, data) { var _a, _b; const firstPartyTrunkData = serverData.asInterface(data, "meta.associations.trunks"); if (isNothing(firstPartyTrunkData)) { return null; } const firstPartyApp = firstPartyTrunkData.data[0]; const shouldUseTrunkArtwork = (_b = (_a = firstPartyApp === null || firstPartyApp === void 0 ? void 0 : firstPartyApp.meta) === null || _a === void 0 ? void 0 : _a["useTrunkArtwork"]) !== null && _b !== void 0 ? _b : false; if (shouldUseTrunkArtwork) { const editorialArtwork = contentAttributeAsDictionary(objectGraph, firstPartyApp, "editorialArtwork.brandLogo"); if (isNothing(editorialArtwork)) { return null; } return content.artworkFromApiArtwork(objectGraph, editorialArtwork, { useCase: 1 /* content.ArtworkUseCase.LockupIconSmall */, style: "roundedRect", }); } return null; } //# sourceMappingURL=contingent-offer.js.map