summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/common/app-promotions/contingent-offer.js
blob: 8cc3cd7b67fcd6387431140bf015ef430eb302fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
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 = "<b>" + mediaAttributes.attributeAsString(branch, "name") + "</b>";
    const skuDescription = mediaAttributes.attributeAsString(branch, "description.standard") + "<br>";
    const termsTitle = "<b>" + objectGraph.loc.string("ContingentOffer.Terms.Title") + "</b>";
    const terms = mediaAttributes.attributeAsString(data, "additionalTerms");
    return new models.Paragraph([skuNameTitle, skuDescription, termsTitle, terms].join("<br>"), "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