summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-header-shelf.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-header-shelf.js')
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-header-shelf.js427
1 files changed, 427 insertions, 0 deletions
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-header-shelf.js b/node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-header-shelf.js
new file mode 100644
index 0000000..b911d94
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-header-shelf.js
@@ -0,0 +1,427 @@
+import * as validation from "@jet/environment/json/validation";
+import { isNothing } from "@jet/environment/types/optional";
+import * as models from "../../api/models";
+import * as serverData from "../../foundation/json-parsing/server-data";
+import * as mediaAttributes from "../../foundation/media/attributes";
+import * as mediaRelationships from "../../foundation/media/relationships";
+import { Path, Protocol } from "../../foundation/network/url-constants";
+import * as urls from "../../foundation/network/urls";
+import * as constants from "../../foundation/util/constants";
+import * as contentAttributes from "../content/attributes";
+import * as metricsHelpersClicks from "../metrics/helpers/clicks";
+import * as linksShelf from "../product-page/shelves/links-shelf";
+import * as privacyTypesShelf from "./privacy-types-shelf";
+import { makeRoutableArticlePageCanonicalUrl } from "../today/routable-article-page-url-utils";
+import { makeRoutableArticlePageIntent } from "../../api/intents/routable-article-page-intent";
+import { getPlatform } from "../preview-platform";
+import { getLocale } from "../locale";
+/**
+ * Builder for the privacy header shelf. This shelf is currently used
+ * on both the product page, and the privacy detail page.
+ */
+export function create(objectGraph, data, pageInformation, locationTracker) {
+ return validation.context("privacyShelf", () => {
+ if (serverData.isNullOrEmpty(data)) {
+ return null;
+ }
+ const shelf = new models.Shelf("privacyHeader");
+ shelf.title = objectGraph.loc.string("PRODUCT_PRIVACY_TITLE");
+ const privacyHeader = privacyHeaderFromData(objectGraph, data, false, false, pageInformation, locationTracker);
+ shelf.items = [privacyHeader];
+ if (objectGraph.client.deviceType !== "watch" && objectGraph.client.deviceType !== "tv") {
+ shelf.seeAllAction = privacyDetailActionFromData(objectGraph, data, "detailPage", pageInformation, locationTracker, null);
+ }
+ return shelf;
+ });
+}
+/**
+ * Creates a privacy header object.
+ * @param data The data blob
+ * @param isDetailHeader Whether this header is intended to be displayed on the detail page
+ * @param isDetailData Whether the included data is for the detail page
+ */
+export function privacyHeaderFromData(objectGraph, data, isDetailHeader, isDetailData, pageInformation, locationTracker) {
+ return validation.context("createPrivacyHeaderFromData", () => {
+ const bodyText = bodyTextFromData(objectGraph, data, isDetailHeader, isDetailData, pageInformation, locationTracker);
+ let seeDetailsAction;
+ let privacyPolicyAction;
+ const privacyDefinitionsText = privacyDefinitionsTextFromData(objectGraph, data, isDetailHeader, pageInformation, locationTracker);
+ let privacyDefinitionsAction;
+ let learnMoreText;
+ let learnMoreAction;
+ if (objectGraph.client.isTV || objectGraph.client.isWatch) {
+ if (!isDetailHeader) {
+ const destinationPrivacyTypeStyle = privacyTypesShelf.isIntermediateDetailPageEnabled(objectGraph)
+ ? "intermediateDetailPage"
+ : "detailPage";
+ seeDetailsAction = privacyDetailActionFromData(objectGraph, data, destinationPrivacyTypeStyle, pageInformation, locationTracker, null);
+ }
+ privacyPolicyAction = privacyPolicyActionFromData(objectGraph, data, pageInformation, locationTracker);
+ }
+ if (isDetailHeader) {
+ if (objectGraph.client.isWatch || objectGraph.client.isTV) {
+ privacyDefinitionsAction = createPrivacyDefinitionsAction(objectGraph, pageInformation, locationTracker);
+ }
+ learnMoreText = learnMoreTextFromData(objectGraph, data, isDetailHeader, pageInformation, locationTracker);
+ if (serverData.isDefinedNonNullNonEmpty(learnMoreText)) {
+ learnMoreAction = createLearnMoreAction(objectGraph, pageInformation, locationTracker);
+ }
+ }
+ const supplementaryItems = [];
+ if (serverData.isDefinedNonNull(privacyDefinitionsText)) {
+ const supplementaryItem = new models.PrivacyHeaderSupplementaryItem(privacyDefinitionsText, privacyDefinitionsAction);
+ supplementaryItems.push(supplementaryItem);
+ }
+ if (serverData.isDefinedNonNull(learnMoreText)) {
+ const supplementaryItem = new models.PrivacyHeaderSupplementaryItem(learnMoreText, learnMoreAction);
+ supplementaryItems.push(supplementaryItem);
+ }
+ let privacyTypes = [];
+ if ((objectGraph.client.isWatch || objectGraph.client.isTV) && !isDetailHeader) {
+ privacyTypes = privacyTypesFromData(objectGraph, data, isDetailData, "productPage", pageInformation, locationTracker);
+ }
+ const bodyActions = [];
+ if (objectGraph.client.isTV) {
+ if (serverData.isDefinedNonNull(seeDetailsAction)) {
+ bodyActions.push(seeDetailsAction);
+ }
+ if (serverData.isDefinedNonNull(privacyPolicyAction)) {
+ bodyActions.push(privacyPolicyAction);
+ }
+ }
+ if (objectGraph.client.isWatch) {
+ if (serverData.isDefinedNonNull(privacyPolicyAction)) {
+ bodyActions.push(privacyPolicyAction);
+ }
+ }
+ return new models.PrivacyHeader(bodyText, isDetailHeader, privacyTypes, bodyActions, supplementaryItems, seeDetailsAction);
+ });
+}
+/**
+ * Creates the main body text for the header.
+ * @param data The data blob
+ * @param isDetailHeader Whether this header is intended to be displayed on the detail page
+ * @param isDetailData Whether the included data is for the detail page
+ */
+function bodyTextFromData(objectGraph, data, isDetailHeader, isDetailData, pageInformation, locationTracker) {
+ let text;
+ let textType = "text/x-apple-as3-nqml";
+ const developer = mediaRelationships.relationshipData(objectGraph, data, "developer");
+ const isAppleOwnedDeveloperId = serverData.isDefinedNonNullNonEmpty(developer) && constants.appleOwnedDeveloperIds.indexOf(developer.id) > -1;
+ if (isDetailHeader && !isAppleOwnedDeveloperId) {
+ text = objectGraph.loc.string("PRODUCT_PRIVACY_DETAIL_HEADER_TEMPLATE");
+ }
+ else {
+ text = objectGraph.loc.string("PRODUCT_PRIVACY_HEADER_TEMPLATE");
+ }
+ const privacyTypes = privacyTypesFromData(objectGraph, data, isDetailData, "detailPage", pageInformation, locationTracker);
+ const privacyDataNotProvided = (privacyTypes.length === 1 && privacyTypes[0].identifier === "DATA_NOT_PROVIDED") || privacyTypes.length === 0;
+ if (privacyDataNotProvided) {
+ text = objectGraph.loc.string("PRODUCT_PRIVACY_HEADER_NO_DETAILS_TEMPLATE");
+ }
+ // Developer name
+ const developerName = mediaAttributes.attributeAsString(data, "artistName");
+ if (serverData.isDefinedNonNull(developerName)) {
+ text = text.replace("{developerName}", "<b>" + developerName + "</b>");
+ }
+ else {
+ // This shouldn't happen, but just in case, fallback to the text with no developer name placeholder
+ if (privacyDataNotProvided) {
+ text = objectGraph.loc.string("PRODUCT_PRIVACY_FALLBACK_HEADER_NO_DETAILS_TEMPLATE");
+ }
+ else {
+ if (isDetailHeader) {
+ text = objectGraph.loc.string("PRODUCT_PRIVACY_FALLBACK_DETAIL_HEADER_TEMPLATE");
+ }
+ else {
+ text = objectGraph.loc.string("PRODUCT_PRIVACY_FALLBACK_HEADER_TEMPLATE");
+ }
+ }
+ textType = "text/plain";
+ }
+ // Privacy policy link
+ const privacyPolicyLinkText = objectGraph.loc.string("PRODUCT_PRIVACY_SUMMARY_PRIVACY_POLICY_LINK");
+ text = text.replace("{privacyPolicyLink}", privacyPolicyLinkText);
+ const privacyPolicyAction = privacyPolicyActionFromData(objectGraph, data, pageInformation, locationTracker);
+ const linkedSubstrings = {};
+ if (serverData.isDefinedNonNull(privacyPolicyAction)) {
+ linkedSubstrings[privacyPolicyLinkText] = privacyPolicyAction;
+ }
+ // Manage choices
+ if (isDetailHeader) {
+ if (objectGraph.client.isiOS ||
+ objectGraph.client.isMac ||
+ objectGraph.client.isVision ||
+ objectGraph.client.isWeb) {
+ const managePrivacyChoicesAction = managePrivacyChoicesActionFromData(objectGraph, data, pageInformation, locationTracker);
+ if (serverData.isDefinedNonNull(managePrivacyChoicesAction)) {
+ const managePrivacyChoicesLink = objectGraph.loc.string("PRODUCT_PRIVACY_MANAGE_CHOICES_LINK");
+ text += "<br><br>";
+ text += objectGraph.loc
+ .string("PRODUCT_PRIVACY_MANAGE_CHOICES_TEMPLATE")
+ .replace("{manageChoicesLink}", managePrivacyChoicesLink);
+ managePrivacyChoicesAction.title = managePrivacyChoicesLink;
+ linkedSubstrings[managePrivacyChoicesLink] = managePrivacyChoicesAction;
+ }
+ }
+ else {
+ text += "<br><br>";
+ text += objectGraph.loc.string("PRODUCT_PRIVACY_MANAGE_CHOICES_NO_LINK");
+ }
+ }
+ const styledText = new models.StyledText(text, textType);
+ return new models.LinkableText(styledText, linkedSubstrings);
+}
+/**
+ * Creates the action for linking to the developer's privacy policy.
+ */
+function privacyPolicyActionFromData(objectGraph, data, pageInformation, locationTracker) {
+ let privacyPolicyLinkAction;
+ if (objectGraph.client.isTV) {
+ const hasPrivacyPolicy = contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, data, "hasPrivacyPolicyText");
+ if (!hasPrivacyPolicy) {
+ return null;
+ }
+ const url = linksShelf.privacyPolicyUrlFromData(objectGraph, data);
+ if (serverData.isNull(url)) {
+ return null;
+ }
+ const action = new models.FlowAction("unknown");
+ action.pageUrl = url;
+ privacyPolicyLinkAction = action;
+ }
+ else {
+ const privacyPolicyUrl = contentAttributes.contentAttributeAsString(objectGraph, data, "privacyPolicyUrl");
+ if (isNothing(privacyPolicyUrl) || serverData.isNullOrEmpty(privacyPolicyUrl)) {
+ return null;
+ }
+ privacyPolicyLinkAction = new models.ExternalUrlAction(privacyPolicyUrl, false);
+ }
+ privacyPolicyLinkAction.title = objectGraph.loc.string("PRODUCT_PRIVACY_SUMMARY_PRIVACY_POLICY_BUTTON_TITLE");
+ metricsHelpersClicks.addClickEventToAction(objectGraph, privacyPolicyLinkAction, {
+ targetType: "link",
+ id: "privacyPolicy",
+ pageInformation: pageInformation,
+ locationTracker: locationTracker,
+ });
+ return privacyPolicyLinkAction;
+}
+/**
+ * Creates the action for linking to the developer's manage privacy choices URL.
+ */
+function managePrivacyChoicesActionFromData(objectGraph, data, pageInformation, locationTracker) {
+ const privacyDetailsData = mediaAttributes.attributeAsDictionary(data, "privacyDetails");
+ const managePrivacyChoicesUrl = serverData.asString(privacyDetailsData, "managePrivacyChoicesUrl");
+ if (isNothing(managePrivacyChoicesUrl) || serverData.isNullOrEmpty(managePrivacyChoicesUrl)) {
+ return null;
+ }
+ const managePrivacyChoicesAction = new models.ExternalUrlAction(managePrivacyChoicesUrl, false);
+ metricsHelpersClicks.addClickEventToAction(objectGraph, managePrivacyChoicesAction, {
+ targetType: "link",
+ id: "managePrivacyChoices",
+ pageInformation: pageInformation,
+ locationTracker: locationTracker,
+ });
+ return managePrivacyChoicesAction;
+}
+/**
+ * Creates the text for linking to the learn more page.
+ * @param data The data blob
+ * @param isDetailHeader Whether this header is intended to be displayed on the detail page
+ */
+function learnMoreTextFromData(objectGraph, data, isDetailHeader, pageInformation, locationTracker) {
+ if (!isDetailHeader || objectGraph.client.isTV) {
+ return null;
+ }
+ const learnMoreLink = objectGraph.loc.string("PRODUCT_PRIVACY_LEARN_MORE_LINK");
+ const learnMoreAction = createLearnMoreAction(objectGraph, pageInformation, locationTracker);
+ let text;
+ const linkedSubstrings = {};
+ if (serverData.isNull(learnMoreAction) || objectGraph.client.isWatch) {
+ text = objectGraph.loc.string("PRODUCT_PRIVACY_LEARN_MORE_NO_LINK");
+ }
+ else {
+ text = objectGraph.loc.string("PRODUCT_PRIVACY_LEARN_MORE_TEMPLATE").replace("{learnMoreLink}", learnMoreLink);
+ learnMoreAction.title = learnMoreLink;
+ linkedSubstrings[learnMoreLink] = learnMoreAction;
+ }
+ const styledText = new models.StyledText(text, "text/plain");
+ return new models.LinkableText(styledText, linkedSubstrings);
+}
+/**
+ * Creates the action for linking to the learn more URL.
+ */
+export function createLearnMoreAction(objectGraph, pageInformation, locationTracker) {
+ const editorialItemId = objectGraph.bag.appPrivacyLearnMoreEditorialItemId;
+ if (isNothing(editorialItemId) || editorialItemId.length === 0) {
+ return null;
+ }
+ const learnMoreAction = new models.FlowAction("article");
+ learnMoreAction.title = objectGraph.loc.string("PRODUCT_PRIVACY_LEARN_MORE_LINK");
+ learnMoreAction.pageUrl = `https://apps.apple.com/story/id${editorialItemId}`;
+ metricsHelpersClicks.addClickEventToAction(objectGraph, learnMoreAction, {
+ targetType: "button",
+ id: "privacyLearnMore",
+ pageInformation: pageInformation,
+ locationTracker: locationTracker,
+ });
+ if (objectGraph.client.isVision) {
+ learnMoreAction.presentation = "sheetPresent";
+ }
+ if (objectGraph.client.isWeb) {
+ const destination = makeRoutableArticlePageIntent({
+ ...getLocale(objectGraph),
+ ...getPlatform(objectGraph),
+ id: editorialItemId,
+ });
+ const pageUrlString = makeRoutableArticlePageCanonicalUrl(objectGraph, destination);
+ learnMoreAction.pageUrl = pageUrlString;
+ learnMoreAction.destination = destination;
+ }
+ return learnMoreAction;
+}
+/**
+ * Creates the text for linking to the privacy definitions EI.
+ * @param data The data blob
+ * @param isDetailHeader Whether this header is intended to be displayed on the detail page
+ */
+function privacyDefinitionsTextFromData(objectGraph, data, isDetailHeader, pageInformation, locationTracker) {
+ if (!isDetailHeader) {
+ return null;
+ }
+ const privacyDefinitionsLink = objectGraph.loc.string("PRODUCT_PRIVACY_DEFINITIONS_LINK");
+ const text = objectGraph.loc
+ .string("PRODUCT_PRIVACY_DEFINITIONS_LINK_TEMPLATE")
+ .replace("{privacyDefinitionsLink}", privacyDefinitionsLink);
+ const privacyDefinitionsAction = createPrivacyDefinitionsAction(objectGraph, pageInformation, locationTracker);
+ if (serverData.isNull(privacyDefinitionsAction)) {
+ return null;
+ }
+ privacyDefinitionsAction.title = privacyDefinitionsLink;
+ const linkedSubstrings = {};
+ linkedSubstrings[privacyDefinitionsLink] = privacyDefinitionsAction;
+ const styledText = new models.StyledText(text, "text/plain");
+ return new models.LinkableText(styledText, linkedSubstrings);
+}
+/**
+ * Creates the action for linking to the privacy definitions page.
+ */
+export function createPrivacyDefinitionsAction(objectGraph, pageInformation, locationTracker) {
+ const editorialItemId = objectGraph.bag.appPrivacyDefinitionsEditorialItemId;
+ if (isNothing(editorialItemId) || editorialItemId.length === 0) {
+ return null;
+ }
+ const privacyDefinitionsAction = new models.FlowAction("article");
+ privacyDefinitionsAction.title = objectGraph.loc.string("PRODUCT_PRIVACY_DEFINITIONS_LINK");
+ privacyDefinitionsAction.pageUrl = `https://apps.apple.com/story/id${editorialItemId}`;
+ if (objectGraph.client.isWeb) {
+ const destination = makeRoutableArticlePageIntent({
+ ...getLocale(objectGraph),
+ ...getPlatform(objectGraph),
+ id: editorialItemId,
+ });
+ const pageUrlString = makeRoutableArticlePageCanonicalUrl(objectGraph, destination);
+ privacyDefinitionsAction.pageUrl = pageUrlString;
+ privacyDefinitionsAction.destination = destination;
+ }
+ metricsHelpersClicks.addClickEventToAction(objectGraph, privacyDefinitionsAction, {
+ targetType: "button",
+ id: "privacyDefinitions",
+ pageInformation: pageInformation,
+ locationTracker: locationTracker,
+ });
+ return privacyDefinitionsAction;
+}
+/**
+ * Creates a list of privacy types, suitable for associating with the header.
+ * In practice, this means that the categories will only be included when necessary,
+ * to keep the data size as small as possible.
+ */
+export function privacyTypesFromData(objectGraph, data, isDetailData, destinationPrivacyTypeStyle, pageInformation, locationTracker) {
+ var _a;
+ let privacyTypes = [];
+ const privacyDataKey = isDetailData ? "privacyDetails" : "privacy";
+ const privacyData = mediaAttributes.attributeAsDictionary(data, privacyDataKey);
+ if (serverData.isDefinedNonNullNonEmpty(privacyData)) {
+ const includeCategories = objectGraph.client.deviceType !== "watch" || destinationPrivacyTypeStyle === "intermediateDetailPage";
+ privacyTypes =
+ (_a = privacyTypesShelf.privacyTypesFromData(objectGraph, privacyData, data, destinationPrivacyTypeStyle, includeCategories, pageInformation, locationTracker)) !== null && _a !== void 0 ? _a : [];
+ // If we only have a single privacy type, with no categories, force it to display in `productPage` style
+ // even on the intermediate detail page, as it has a nicer appearance for this case.
+ if (privacyTypes.length === 1 && privacyTypes[0].categories.length === 0) {
+ privacyTypes[0].style = "productPage";
+ }
+ }
+ return privacyTypes;
+}
+/**
+ * Creates an incomplete privacy detail page, sidepacking the privacy header shelf.
+ * On watchOS, when creating the `intermediateDetailPage`, we also sidepack the privacy types themselves,
+ * resulting in a complete (intermediate) detail page.
+ */
+export function privacyDetailSidepackPageFromData(objectGraph, data, destinationPrivacyTypeStyle, pageInformation, locationTracker) {
+ const shelves = [];
+ // Always sidepack the header, unless this will be the detail page for the watch,
+ // which does not display the header
+ if (objectGraph.client.deviceType !== "watch" || destinationPrivacyTypeStyle !== "detailPage") {
+ const headerShelf = new models.Shelf("privacyHeader");
+ const privacyHeader = privacyHeaderFromData(objectGraph, data, true, false, pageInformation, locationTracker);
+ headerShelf.items = [privacyHeader];
+ headerShelf.presentationHints = { isFirstShelf: true };
+ shelves.push(headerShelf);
+ }
+ if (
+ // On the watch, we want to sidepack the privacy types if coming from the product page
+ // as we already have all the information we need, and we can avoid having an incomplete page
+ (objectGraph.client.isWatch && destinationPrivacyTypeStyle === "intermediateDetailPage") ||
+ // The "detailPage" on the "web" client is presented as a modal, so we want to sidepack all
+ // of the data so it can display is immediately
+ (objectGraph.client.isWeb && destinationPrivacyTypeStyle === "detailPage")) {
+ const privacyTypes = privacyTypesFromData(objectGraph, data, objectGraph.client.isWeb, destinationPrivacyTypeStyle, pageInformation, locationTracker);
+ const shelf = new models.Shelf("privacyType");
+ if (privacyTypes.length > 0) {
+ shelf.items = privacyTypes;
+ shelves.push(shelf);
+ }
+ }
+ const page = new models.GenericPage(shelves);
+ if (objectGraph.client.deviceType !== "watch" || destinationPrivacyTypeStyle === "detailPage") {
+ page.isIncomplete = true;
+ }
+ page.title = objectGraph.loc.string("PRODUCT_PRIVACY_TITLE");
+ if (objectGraph.client.isMac) {
+ page.presentationOptions = ["prefersLargeTitle"];
+ }
+ return page;
+}
+/**
+ * Creates the action for linking to the privacy detail page.
+ */
+export function privacyDetailActionFromData(objectGraph, data, destinationPrivacyTypeStyle, pageInformation, locationTracker, scrollFocusPrivacyTypeId) {
+ if (serverData.isNull(data.id)) {
+ return null;
+ }
+ const seeDetailsAction = new models.FlowAction("privacyDetail");
+ seeDetailsAction.title = objectGraph.loc.string("ACTION_SEE_DETAILS");
+ seeDetailsAction.pageData = privacyDetailSidepackPageFromData(objectGraph, data, destinationPrivacyTypeStyle, pageInformation, locationTracker);
+ const productType = data.type === "app-bundles" ? Path.productBundle : Path.product;
+ let query;
+ if (serverData.isDefinedNonNullNonEmpty(scrollFocusPrivacyTypeId)) {
+ query = { privacyTypeId: scrollFocusPrivacyTypeId };
+ }
+ const pageUrl = urls.URL.fromComponents(Protocol.internal, null, `/${Path.privacyDetail}/${productType}/${data.id}`, query);
+ seeDetailsAction.pageUrl = pageUrl.build();
+ const seeDetailsClickOptions = {
+ targetType: "button",
+ id: "seeDetails",
+ pageInformation: pageInformation,
+ locationTracker: locationTracker,
+ };
+ if (serverData.isDefinedNonNull(scrollFocusPrivacyTypeId)) {
+ seeDetailsClickOptions.targetType = "privacyCard";
+ seeDetailsClickOptions.id = scrollFocusPrivacyTypeId;
+ }
+ metricsHelpersClicks.addClickEventToAction(objectGraph, seeDetailsAction, seeDetailsClickOptions);
+ return seeDetailsAction;
+}
+//# sourceMappingURL=privacy-header-shelf.js.map \ No newline at end of file