summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/common/app-promotions/offer-item.js
diff options
context:
space:
mode:
authorrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
committerrxliuli <rxliuli@gmail.com>2025-11-04 05:03:50 +0800
commitbce557cc2dc767628bed6aac87301a1be7c5431b (patch)
treeb51a051228d01fe3306cd7626d4a96768aadb944 /node_modules/@jet-app/app-store/tmp/src/common/app-promotions/offer-item.js
init commit
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/common/app-promotions/offer-item.js')
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/app-promotions/offer-item.js206
1 files changed, 206 insertions, 0 deletions
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/app-promotions/offer-item.js b/node_modules/@jet-app/app-store/tmp/src/common/app-promotions/offer-item.js
new file mode 100644
index 0000000..989ab6d
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/app-promotions/offer-item.js
@@ -0,0 +1,206 @@
+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 dateUtil from "../../foundation/util/date-util";
+import { isNothing, isSome } from "@jet/environment/types/optional";
+import * as offerFormatting from "../offers/offer-formatting";
+import * as offers from "../offers/offers";
+import * as content from "../content/content";
+import * as contentArtwork from "../content/artwork/artwork";
+/**
+ * Creates an offer item object.
+ * @param offerItemData 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 Offer Item or null
+ */
+export function offerItemFromData(objectGraph, offerItemData, parentAppData, offerEnvironment, offerStyle, baseMetricsOptions, includeLockupClickAction, isArcadePage) {
+ var _a, _b, _c, _d, _e, _f;
+ if (offerItemData.type !== "offer-items") {
+ return null;
+ }
+ const kind = mediaAttributes.attributeAsString(offerItemData, "kind");
+ if (kind !== "resubscription" && kind !== "winback") {
+ // Only resubscriptions are supported
+ return null;
+ }
+ const isArtworkDark = false;
+ const mediaOverlayStyle = isArtworkDark ? "dark" : "light";
+ // The Offers
+ const discountedIAP = mediaRelationship.relationshipData(objectGraph, offerItemData, "salables");
+ if (isNothing(discountedIAP)) {
+ return null;
+ }
+ const discountedOffer = serverData.asDictionary(discountedIAP, "meta.discountOffer");
+ const regularOffer = offers.offerDataFromData(objectGraph, discountedIAP);
+ if (isNothing(discountedOffer)) {
+ return null;
+ }
+ const resolvedParentApp = (_a = parentAppData !== null && parentAppData !== void 0 ? parentAppData : mediaRelationship.relationshipData(objectGraph, offerItemData, "app")) !== null && _a !== void 0 ? _a : mediaRelationship.relationshipData(objectGraph, discountedIAP, "app");
+ const supportsStreamlinedBuy = mediaAttributes.attributeAsBooleanOrFalse(resolvedParentApp, "supportsStreamlinedBuy");
+ // Offer Validity
+ const endDateString = mediaAttributes.attributeAsString(offerItemData, "redemptionExpirationDate");
+ if (isNothing(endDateString) || !isOfferValid(endDateString)) {
+ return null;
+ }
+ const endDate = new Date(endDateString);
+ let formattedExpiryDateShortString = formattedExpiryDate(objectGraph, endDate);
+ if (isNothing(formattedExpiryDateShortString)) {
+ formattedExpiryDateShortString = objectGraph.loc.string("OfferItems.Available.Now");
+ }
+ // IAP Artwork
+ let iapArtwork = content.iconFromData(objectGraph, discountedIAP, {
+ useCase: 3 /* content.ArtworkUseCase.LockupIconLarge */,
+ withJoeColorPlaceholder: true,
+ overrideTextColorKey: "textColor4",
+ });
+ /// A fallback artwork for rare cases where no artwork is found.
+ /// This should not happen in production
+ if (serverData.isNullOrEmpty(iapArtwork)) {
+ const blackColor = {
+ type: "rgb",
+ red: 0.0 / 255,
+ green: 0.0 / 255,
+ blue: 0.0 / 255,
+ alpha: 1,
+ };
+ iapArtwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://questionmark.circle", 200, 200, blackColor);
+ iapArtwork.style = "iap";
+ }
+ const subscriptionRecurrence = offerFormatting.subscriptionRecurrenceForServerRecurrence(objectGraph, serverData.asString(discountedOffer, "recurringSubscriptionPeriod"), serverData.asNumber(discountedOffer, "numOfPeriods"));
+ const discountDuration = isSome(subscriptionRecurrence)
+ ? offerFormatting.durationCountString(objectGraph, subscriptionRecurrence.type, subscriptionRecurrence.periodDuration * subscriptionRecurrence.periodCount)
+ : undefined;
+ // Price Strings
+ // Adding \u{2060} (Word Joiner) prevents the string from breaking on the slash character
+ const regularPrice = (_b = formattedPrice(objectGraph, serverData.asString(regularOffer, "recurringSubscriptionPeriod"), serverData.asNumber(regularOffer, "numOfPeriods"), serverData.asString(regularOffer, "priceFormatted"))) === null || _b === void 0 ? void 0 : _b.replace("/", "/\u{2060}");
+ const discountedPrice = (_c = formattedPrice(objectGraph, serverData.asString(discountedOffer, "recurringSubscriptionPeriod"), serverData.asNumber(discountedOffer, "numOfPeriods"), serverData.asString(discountedOffer, "priceFormatted"))) === null || _c === void 0 ? void 0 : _c.replace("/", "/\u{2060}");
+ /// A string map of the templated string in an offer
+ /// Fallback to the templated string if the string does not exist
+ const redemptionDateFormat = objectGraph.loc.string("OfferItems.FormattedDate.RedemptionDate.DateFormat");
+ const templateKeyMap = {
+ "@@redemptionDate@@": objectGraph.loc.formatDate(redemptionDateFormat, endDate),
+ "@@skuName@@": (_d = mediaAttributes.attributeAsString(discountedIAP, "name")) !== null && _d !== void 0 ? _d : "@@skuName@@",
+ "@@discountedPrice@@": discountedPrice !== null && discountedPrice !== void 0 ? discountedPrice : "@@discountedPrice@@",
+ "@@regularPricePerDuration@@": regularPrice !== null && regularPrice !== void 0 ? regularPrice : "@@regularPricePerDuration@@",
+ "@@discountDuration@@": discountDuration !== null && discountDuration !== void 0 ? discountDuration : "@@discountDuration@@",
+ "@@payUpfrontPrice@@": (_e = serverData.asString(discountedOffer, "priceFormatted")) !== null && _e !== void 0 ? _e : "@@payUpfrontPrice@@",
+ };
+ // Labels
+ const badge = mediaAttributes.attributeAsString(offerItemData, "badge");
+ if (isNothing(badge)) {
+ return null;
+ }
+ const title = appPromotionCommon.replacingTemplatedKeys(mediaAttributes.attributeAsString(offerItemData, "title"), templateKeyMap);
+ const formattedTitle = new models.Paragraph(title, "text/x-apple-as3-nqml", "appPromotionTitle");
+ const subtitle = appPromotionCommon.replacingTemplatedKeys(mediaAttributes.attributeAsString(offerItemData, "subtitle"), templateKeyMap);
+ const description = appPromotionCommon.replacingTemplatedKeys((_f = mediaAttributes.attributeAsString(offerItemData, "details")) !== null && _f !== void 0 ? _f : "", templateKeyMap);
+ // Offer Lockup
+ let offerLockup;
+ if (serverData.isDefinedNonNull(resolvedParentApp)) {
+ offerLockup = appPromotionCommon.lockupFromData(objectGraph, offerItemData, resolvedParentApp, "", offerEnvironment, offerStyle, false, baseMetricsOptions, includeLockupClickAction, null, isArcadePage, true);
+ }
+ if (isNothing(offerLockup)) {
+ return null;
+ }
+ const offerItem = new models.OfferItem(null, null, mediaOverlayStyle, supportsStreamlinedBuy, formattedTitle, formattedExpiryDateShortString, subtitle, description, badge, endDate, iapArtwork, offerLockup);
+ offerItem.title = title;
+ // Detail page click action
+ if (serverData.isDefinedNonNull(resolvedParentApp)) {
+ offerItem.clickAction = appPromotionCommon.detailPageClickActionFromData(objectGraph, offerItemData, resolvedParentApp, offerItem, baseMetricsOptions, includeLockupClickAction);
+ }
+ return offerItem;
+}
+/**
+ * Creates a readable subscription price
+ * @param recurringSubscriptionPeriod The subscription recurrence in the server format. (eg P1M)
+ * @param numberOfPeriods The number of periods the subscription recurs for.
+ * @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 null;
+ }
+ return offerFormatting.priceDurationString(objectGraph, subscriptionRecurrence === null || subscriptionRecurrence === void 0 ? void 0 : subscriptionRecurrence.type, subscriptionRecurrence === null || subscriptionRecurrence === void 0 ? void 0 : subscriptionRecurrence.periodDuration, price);
+}
+/**
+ * Checks the validity of a app promotion based on end date
+ * @param endDateString A string representation of the end date
+ * @returns bool of if the offer is valid or not
+ */
+function isOfferValid(endDateString) {
+ if (isNothing(endDateString) || serverData.isNullOrEmpty(endDateString)) {
+ return false;
+ }
+ const endDate = new Date(endDateString);
+ if (serverData.isNull(endDate)) {
+ return false;
+ }
+ const todayDate = new Date();
+ const hasEventEnded = endDate.getTime() <= todayDate.getTime();
+ if (hasEventEnded) {
+ return false;
+ }
+ return true;
+}
+/**
+ * Creates the availability label for a app promotion
+ * @param startDate The start date
+ * @param endDate The end date
+ * @returns string of the format the date should be shown
+ */
+function formattedExpiryDate(objectGraph, endDate) {
+ if (isNothing(endDate)) {
+ return null;
+ }
+ const endMidnight = dateUtil.convertLocalDateToLocalMidnight(endDate);
+ const currentDate = new Date();
+ const daysLeft = dateUtil.numberOfDaysBetween(currentDate, endMidnight);
+ if (isNothing(daysLeft)) {
+ return null;
+ }
+ const isSameDate = dateUtil.areLocalDatesSameDay(currentDate, endDate);
+ if (daysLeft > 90) {
+ return objectGraph.loc.string("OfferItems.Available.Now");
+ }
+ // Event ends 5+ days from now
+ // Example: EXPIRES DEC 31
+ if (daysLeft > 5) {
+ const expiryDateFormat = currentDate.getFullYear() !== endDate.getFullYear()
+ ? objectGraph.loc.string("OfferItems.FormattedDate.NextYear.DateFormat")
+ : objectGraph.loc.string("OfferItems.FormattedDate.FiveDaysOrMore.DateFormat");
+ const expiryDateString = objectGraph.loc.uppercased(objectGraph.loc.formatDate(expiryDateFormat, endDate));
+ if (isNothing(expiryDateString)) {
+ return null;
+ }
+ return objectGraph.loc
+ .string("OfferItems.FormattedDate.FiveDaysOrMore.Title")
+ .replace("@@date@@", expiryDateString);
+ }
+ // Event ends 2-5 days from now
+ // Example: EXPIRES IN 3 Days
+ if (daysLeft > 1) {
+ const fiveDaysPriorDateFormat = objectGraph.loc
+ .string("OfferItems.FormattedDate.FiveDaysOrLess.Title")
+ .replace("@@count@@", objectGraph.loc.formattedCount(daysLeft));
+ return fiveDaysPriorDateFormat;
+ }
+ // Event ends 1 day from now
+ // Example: EXPIRES TOMORROW
+ if (daysLeft === 1 && !isSameDate) {
+ return objectGraph.loc.string("OfferItems.FormattedDate.Tomorrow.Title");
+ }
+ // Event ends 1 day from now
+ // Example: EXPIRES TONIGHT
+ return objectGraph.loc.string("OfferItems.FormattedDate.Today.Title");
+}
+//# sourceMappingURL=offer-item.js.map \ No newline at end of file