summaryrefslogtreecommitdiff
path: root/node_modules/@jet-app/app-store/tmp/src/common/privacy
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/@jet-app/app-store/tmp/src/common/privacy')
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-footer-shelf.js87
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-header-shelf.js427
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-suppression.js34
-rw-r--r--node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-types-shelf.js322
4 files changed, 870 insertions, 0 deletions
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-footer-shelf.js b/node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-footer-shelf.js
new file mode 100644
index 0000000..809b042
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-footer-shelf.js
@@ -0,0 +1,87 @@
+import * as validation from "@jet/environment/json/validation";
+import * as models from "../../api/models";
+import * as serverData from "../../foundation/json-parsing/server-data";
+import * as privacyHeaderShelf from "./privacy-header-shelf";
+/**
+ * Builder for the privacy footer shelf.
+ */
+export function create(objectGraph, data, pageInformation, locationTracker) {
+ return validation.context("create", () => {
+ if (serverData.isNullOrEmpty(data)) {
+ return null;
+ }
+ if (objectGraph.client.isWatch) {
+ return null;
+ }
+ const privacyTypes = privacyHeaderShelf.privacyTypesFromData(objectGraph, data, false, "detailPage", pageInformation, locationTracker);
+ const privacyDataNotProvided = (privacyTypes.length === 1 && privacyTypes[0].identifier === "DATA_NOT_PROVIDED") ||
+ privacyTypes.length === 0;
+ if (privacyDataNotProvided && objectGraph.client.deviceType !== "tv") {
+ return null;
+ }
+ const shelf = new models.Shelf("privacyFooter");
+ const privacyFooter = privacyFooterFromData(objectGraph, data, pageInformation, locationTracker);
+ shelf.items = [privacyFooter];
+ return shelf;
+ });
+}
+/**
+ * Creates a privacy footer object.
+ * @param data The data blob
+ */
+export function privacyFooterFromData(objectGraph, data, pageInformation, locationTracker) {
+ return validation.context("privacyFooterFromData", () => {
+ const bodyText = bodyTextFromData(objectGraph, data, pageInformation, locationTracker);
+ const actions = actionsFromData(objectGraph, data, pageInformation, locationTracker);
+ let privacyTypes = [];
+ if (objectGraph.client.isTV) {
+ privacyTypes = privacyHeaderShelf.privacyTypesFromData(objectGraph, data, false, "productPage", pageInformation, locationTracker);
+ }
+ return new models.PrivacyFooter(bodyText, actions, privacyTypes.length);
+ });
+}
+/**
+ * Creates the main body text for the footer.
+ * @param data The data blob
+ */
+function bodyTextFromData(objectGraph, data, pageInformation, locationTracker) {
+ let text;
+ const learnMoreLinkText = objectGraph.loc.string("PRODUCT_PRIVACY_LEARN_MORE_LINK");
+ const linkedSubstrings = {};
+ const learnMoreAction = privacyHeaderShelf.createLearnMoreAction(objectGraph, pageInformation, locationTracker);
+ if (objectGraph.client.isTV || serverData.isNull(learnMoreAction)) {
+ text = objectGraph.loc.string("PRODUCT_PRIVACY_LEARN_MORE_NO_LINK");
+ }
+ else {
+ text = objectGraph.loc.string("PRODUCT_PRIVACY_LEARN_MORE_TEMPLATE");
+ text = text.replace("{learnMoreLink}", learnMoreLinkText);
+ if (serverData.isNull(learnMoreAction)) {
+ text = objectGraph.loc.string("PRODUCT_PRIVACY_LEARN_MORE_NO_LINK");
+ }
+ else {
+ linkedSubstrings[learnMoreLinkText] = learnMoreAction;
+ }
+ }
+ const textType = "text/plain";
+ const styledText = new models.StyledText(text, textType);
+ return new models.LinkableText(styledText, linkedSubstrings);
+}
+/**
+ * Creates the actions for displaying in the privacy footer.
+ */
+function actionsFromData(objectGraph, data, pageInformation, locationTracker) {
+ if (objectGraph.client.deviceType !== "tv") {
+ return [];
+ }
+ const actions = [];
+ const learnMoreAction = privacyHeaderShelf.createLearnMoreAction(objectGraph, pageInformation, locationTracker);
+ if (serverData.isDefinedNonNull(learnMoreAction)) {
+ actions.push(learnMoreAction);
+ }
+ const privacyDefinitionsAction = privacyHeaderShelf.createPrivacyDefinitionsAction(objectGraph, pageInformation, locationTracker);
+ if (serverData.isDefinedNonNull(privacyDefinitionsAction)) {
+ actions.push(privacyDefinitionsAction);
+ }
+ return actions;
+}
+//# sourceMappingURL=privacy-footer-shelf.js.map \ No newline at end of file
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
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-suppression.js b/node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-suppression.js
new file mode 100644
index 0000000..f43144f
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-suppression.js
@@ -0,0 +1,34 @@
+import { isNothing } from "@jet/environment/types/optional";
+import * as serverData from "../../foundation/json-parsing/server-data";
+let suppressedPrivacyApps = null;
+function initialize(objectGraph) {
+ if (suppressedPrivacyApps !== null) {
+ return;
+ }
+ suppressedPrivacyApps = new Set();
+ for (const appId of objectGraph.bag.suppressedPrivacyAppIds) {
+ suppressedPrivacyApps.add(appId);
+ }
+}
+export function shouldSuppressPrivacyInformationForAdamId(objectGraph, adamId) {
+ initialize(objectGraph);
+ if (isNothing(suppressedPrivacyApps) || serverData.isNullOrEmpty(adamId)) {
+ return false;
+ }
+ return suppressedPrivacyApps.has(adamId);
+}
+export function shouldSuppressPrivacyInformationForBundleId(objectGraph, bundleId) {
+ initialize(objectGraph);
+ if (isNothing(suppressedPrivacyApps) || serverData.isNullOrEmpty(bundleId)) {
+ return false;
+ }
+ /// This handles a special case wildcard entry for macOS installers, to avoid the churn of adding new bundleIDs
+ /// for each macOS release. We specifically do not want to treat all entries in `suppressedPrivacyApps` as a regex,
+ /// as there is no expectation that any other entry will be a regex, and treating them as such may lead to false matches.
+ if (suppressedPrivacyApps.has("com.apple.InstallAssistant.*") &&
+ bundleId.startsWith("com.apple.InstallAssistant.")) {
+ return true;
+ }
+ return suppressedPrivacyApps.has(bundleId);
+}
+//# sourceMappingURL=privacy-suppression.js.map \ No newline at end of file
diff --git a/node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-types-shelf.js b/node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-types-shelf.js
new file mode 100644
index 0000000..11b58b6
--- /dev/null
+++ b/node_modules/@jet-app/app-store/tmp/src/common/privacy/privacy-types-shelf.js
@@ -0,0 +1,322 @@
+import * as validation from "@jet/environment/json/validation";
+import { isNothing, isSome } 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 contentArtwork from "../content/artwork/artwork";
+import * as content from "../content/content";
+import * as metricsHelpersImpressions from "../metrics/helpers/impressions";
+import * as metricsHelpersLocation from "../metrics/helpers/location";
+import * as privacyHeaderShelf from "./privacy-header-shelf";
+/**
+ * Builder for the privacy types shelf. This shelf is currently used
+ * on both the product page, and the privacy detail page.
+ */
+export function create(objectGraph, data, shelfMetrics) {
+ return validation.context("create", () => {
+ if (serverData.isNullOrEmpty(data) || objectGraph.client.isWatch) {
+ return null;
+ }
+ const privacyData = mediaAttributes.attributeAsDictionary(data, "privacy");
+ if (isNothing(privacyData) || serverData.isNullOrEmpty(privacyData)) {
+ return null;
+ }
+ const shelf = new models.Shelf("privacyType");
+ const title = objectGraph.loc.string("PRODUCT_PRIVACY_TITLE");
+ metricsHelpersLocation.pushContentLocation(objectGraph, {
+ pageInformation: shelfMetrics.metricsPageInformation,
+ locationTracker: shelfMetrics.locationTracker,
+ targetType: "privacyCard",
+ id: `${shelfMetrics.getSequenceId()}`,
+ idType: "sequential",
+ }, title);
+ const privacyTypes = privacyTypesFromData(objectGraph, privacyData, data, "productPage", true, shelfMetrics.metricsPageInformation, shelfMetrics.locationTracker);
+ if (privacyTypes === null || privacyTypes.length === 0) {
+ return null;
+ }
+ shelf.items = privacyTypes;
+ if (privacyTypes.length <= 2) {
+ shelf.presentationHints = { isLowDensity: true };
+ }
+ // We don't actually want a shelf title, but we populate it temporarily here
+ // so that impressions have the appropriate title without causing too much churn.
+ shelf.title = title;
+ shelfMetrics.addImpressionsToShelf(objectGraph, shelf, "appPrivacy");
+ metricsHelpersLocation.popLocation(shelfMetrics.locationTracker);
+ metricsHelpersLocation.nextPosition(shelfMetrics.locationTracker);
+ shelf.title = null;
+ return shelf;
+ });
+}
+/**
+ * Creates a list of privacy types from the given data.
+ * @param privacyData The privacy data blob
+ * @param dataContainer The response data
+ * @param style The style that the privacy types should be created with
+ * @param includeCategories Whether the privacy categories should be included or not.
+ */
+export function privacyTypesFromData(objectGraph, privacyData, dataContainer, style, includeCategories, pageInformation, locationTracker) {
+ const types = serverData.asArrayOrEmpty(privacyData, "privacyTypes");
+ const privacyTypes = [];
+ for (const type of types) {
+ const typeData = serverData.asJSONData(type);
+ if (serverData.isDefinedNonNullNonEmpty(typeData)) {
+ const privacyType = privacyTypeFromData(objectGraph, typeData, dataContainer, style, includeCategories, pageInformation, locationTracker);
+ if (serverData.isDefinedNonNull(privacyType)) {
+ privacyTypes.push(privacyType);
+ }
+ }
+ }
+ if (privacyTypes.length === 0) {
+ const noDetailsPrivacyType = noDetailsProvidedPrivacyTypeFromDataContainer(objectGraph, dataContainer, style, pageInformation, locationTracker);
+ privacyTypes.push(noDetailsPrivacyType);
+ }
+ for (const privacyType of privacyTypes) {
+ const options = {
+ id: null,
+ kind: null,
+ softwareType: null,
+ title: privacyType.title,
+ pageInformation: pageInformation,
+ locationTracker: locationTracker,
+ targetType: "privacyCard",
+ };
+ metricsHelpersImpressions.addImpressionFields(objectGraph, privacyType, options);
+ metricsHelpersLocation.nextPosition(locationTracker);
+ }
+ return privacyTypes;
+}
+/**
+ * Creates a privacy type from the given data.
+ * @param data The privacy types data blob
+ * @param dataContainer The response data
+ * @param style The style that the privacy types should have.
+ * @param includeCategories Whether the privacy categories should be included or not.
+ */
+function privacyTypeFromData(objectGraph, data, dataContainer, style, includeCategories, pageInformation, locationTracker) {
+ const identifier = serverData.asString(data, "identifier");
+ const title = serverData.asString(data, "privacyType");
+ const detail = serverData.asString(data, "description");
+ if (isNothing(identifier) ||
+ serverData.isNullOrEmpty(identifier) ||
+ isNothing(title) ||
+ serverData.isNullOrEmpty(title) ||
+ isNothing(detail) ||
+ serverData.isNullOrEmpty(detail)) {
+ return null;
+ }
+ const artworkResourceUrl = artworkResourceUrlForPrivacyTypeIdentifier(objectGraph, identifier);
+ const artwork = artworkFromData(objectGraph, data, artworkResourceUrl);
+ const categories = includeCategories ? privacyCategoriesFromData(objectGraph, data, style) : [];
+ const purposes = privacyPurposesFromData(objectGraph, data, style);
+ const clickAction = privacyTypeClickActionFromDataContainer(objectGraph, dataContainer, identifier, style, pageInformation, locationTracker);
+ return new models.PrivacyType(identifier, title, detail, artwork, style, purposes, categories, clickAction);
+}
+/**
+ * Creates a suitable click action for a privacy type.
+ * @param dataContainer The response data
+ * @param identifier The identifier for the privacy type
+ * @param style The style that the privacy types should have.
+ */
+function privacyTypeClickActionFromDataContainer(objectGraph, dataContainer, identifier, style, pageInformation, locationTracker) {
+ let clickAction = null;
+ if (style === "productPage" || style === "intermediateDetailPage") {
+ const destinationStyle = isIntermediateDetailPageEnabled(objectGraph) && style === "productPage"
+ ? "intermediateDetailPage"
+ : "detailPage";
+ clickAction = privacyHeaderShelf.privacyDetailActionFromData(objectGraph, dataContainer, destinationStyle, pageInformation, locationTracker, identifier);
+ }
+ return clickAction;
+}
+/**
+ * Creates a privacy type object to represent the case where the developer has not yet provided any privacy details.
+ *
+ * @param dataContainer The response data
+ * @param style The style that the privacy types should have.
+ */
+function noDetailsProvidedPrivacyTypeFromDataContainer(objectGraph, dataContainer, style, pageInformation, locationTracker) {
+ const identifier = "DATA_NOT_PROVIDED";
+ const title = objectGraph.loc.string("PRODUCT_PRIVACY_NO_DETAILS_PROVIDED_TITLE");
+ const detail = objectGraph.loc.string("PRODUCT_PRIVACY_NO_DETAILS_PROVIDED_BODY");
+ const artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://exclamationmark.triangle");
+ const clickAction = privacyTypeClickActionFromDataContainer(objectGraph, dataContainer, identifier, style, pageInformation, locationTracker);
+ return new models.PrivacyType(identifier, title, detail, artwork, style, [], [], clickAction);
+}
+/**
+ * Creates a list of privacy categories from the given data.
+ * @param data The privacy data blob
+ * @param privacyTypeStyle The style that the parent privacy type style will have.
+ */
+function privacyCategoriesFromData(objectGraph, data, privacyTypeStyle) {
+ const categories = serverData.asArrayOrEmpty(data, "dataCategories");
+ const privacyCategories = [];
+ for (const category of categories) {
+ const categoryData = serverData.asJSONData(category);
+ if (serverData.isDefinedNonNullNonEmpty(categoryData)) {
+ const style = privacyTypeStyle === "productPage" ? "productPage" : "detailPage";
+ const privacyCategory = privacyCategoryFromData(objectGraph, categoryData, style);
+ if (serverData.isDefinedNonNull(privacyCategory)) {
+ privacyCategories.push(privacyCategory);
+ }
+ }
+ }
+ return privacyCategories;
+}
+/**
+ * Creates a privacy category from the given data.
+ * @param data The privacy data blob
+ * @param style The style that the privacy category should have.
+ */
+function privacyCategoryFromData(objectGraph, data, style) {
+ const identifier = serverData.asString(data, "identifier");
+ const title = serverData.asString(data, "dataCategory");
+ if (isNothing(identifier) ||
+ serverData.isNullOrEmpty(identifier) ||
+ isNothing(title) ||
+ serverData.isNullOrEmpty(title)) {
+ return null;
+ }
+ const artworkResourceUrl = artworkResourceUrlForPrivacyCategoryIdentifier(objectGraph, identifier);
+ const artwork = artworkFromData(objectGraph, data, artworkResourceUrl);
+ if (serverData.isNull(artwork)) {
+ return null;
+ }
+ let dataTypes = [];
+ if (style === "detailPage") {
+ dataTypes = serverData.asArrayOrEmpty(data, "dataTypes");
+ }
+ const privacyCategory = new models.PrivacyCategory(identifier, title, artwork, style, dataTypes);
+ if (identifier === "USAGE_DATA") {
+ privacyCategory.prefersSmallArtwork = true;
+ }
+ return privacyCategory;
+}
+/**
+ * Creates a list of privacy purposes from the given data.
+ * @param data The privacy data blob
+ * @param privacyTypeStyle The style that the parent privacy type style will have.
+ */
+function privacyPurposesFromData(objectGraph, data, privacyTypeStyle) {
+ const purposes = serverData.asArrayOrEmpty(data, "purposes");
+ const privacyPurposes = [];
+ for (const purpose of purposes) {
+ const purposeData = serverData.asJSONData(purpose);
+ if (serverData.isDefinedNonNullNonEmpty(purposeData)) {
+ const privacyPurpose = privacyPurposeFromData(objectGraph, purposeData, privacyTypeStyle);
+ if (serverData.isDefinedNonNull(privacyPurpose)) {
+ privacyPurposes.push(privacyPurpose);
+ }
+ }
+ }
+ return privacyPurposes;
+}
+/**
+ * Creates a privacy purpose from the given data.
+ * @param data The privacy data blob
+ * @param privacyTypeStyle The style that the parent privacy type style will have.
+ */
+function privacyPurposeFromData(objectGraph, data, privacyTypeStyle) {
+ const identifier = serverData.asString(data, "identifier");
+ const title = serverData.asString(data, "purpose");
+ const categories = privacyCategoriesFromData(objectGraph, data, privacyTypeStyle);
+ if (isNothing(identifier) ||
+ serverData.isNullOrEmpty(identifier) ||
+ isNothing(title) ||
+ serverData.isNullOrEmpty(title) ||
+ categories.length === 0) {
+ return null;
+ }
+ return new models.PrivacyPurpose(identifier, title, categories);
+}
+/**
+ * Creates a suitable artwork object for a privacy object, from the given data blob or artwork resource url
+ * @param data The privacy object data blob
+ * @param artworkResourceUrl An optional artwork resource url
+ */
+function artworkFromData(objectGraph, data, artworkResourceUrl) {
+ let artwork = null;
+ if (isSome(artworkResourceUrl) && (artworkResourceUrl === null || artworkResourceUrl === void 0 ? void 0 : artworkResourceUrl.length) > 0) {
+ artwork = contentArtwork.createArtworkForResource(objectGraph, artworkResourceUrl);
+ }
+ if (serverData.isNull(artwork)) {
+ const artworkData = serverData.asDictionary(data, "artwork");
+ if (serverData.isDefinedNonNull(artworkData)) {
+ artwork = content.artworkFromApiArtwork(objectGraph, artworkData, {
+ useCase: 0 /* content.ArtworkUseCase.Default */,
+ allowingTransparency: true,
+ });
+ }
+ }
+ return artwork;
+}
+/**
+ * Maps a privacy type identifier to a known (SF symbol) artwork resource.
+ * @param privacyTypeIdentifier the given privacy type identifier.
+ */
+function artworkResourceUrlForPrivacyTypeIdentifier(objectGraph, privacyTypeIdentifier) {
+ switch (privacyTypeIdentifier) {
+ case "DATA_NOT_LINKED_TO_YOU":
+ return "resource://person.circle.slash";
+ case "DATA_USED_TO_TRACK_YOU":
+ return "systemimage://person.fill.viewfinder";
+ case "DATA_NOT_COLLECTED":
+ return "systemimage://checkmark.circle";
+ case "DATA_LINKED_TO_YOU":
+ return "systemimage://person.circle";
+ default:
+ return null;
+ }
+}
+/**
+ * Maps a privacy category identifier to a known (SF symbol) artwork resource.
+ * @param privacyCategoryIdentifier the given privacy category identifier.
+ */
+function artworkResourceUrlForPrivacyCategoryIdentifier(objectGraph, privacyCategoryIdentifier) {
+ switch (privacyCategoryIdentifier) {
+ case "FINANCIAL_INFO":
+ return "systemimage://creditcard.fill";
+ case "CONTACT_INFO":
+ return "systemimage://info.circle.fill";
+ case "OTHER":
+ return "systemimage://ellipsis.circle.fill";
+ case "SENSITIVE_INFO":
+ return "systemimage://eye.fill";
+ case "USAGE_DATA":
+ return "systemimage://chart.bar.fill";
+ case "CONTACTS":
+ return "systemimage://person.circle";
+ case "PURCHASES":
+ return "systemimage://bag.fill";
+ case "LOCATION":
+ return "systemimage://location.fill";
+ case "HEALTH_AND_FITNESS":
+ return "systemimage://heart.circle.fill";
+ case "IDENTIFIERS":
+ return "resource://person.crop.rectangle.line.fill";
+ case "USER_CONTENT":
+ return "systemimage://photo.fill.on.rectangle.fill";
+ case "BROWSING_HISTORY":
+ return "systemimage://clock.fill";
+ case "DIAGNOSTICS":
+ return "systemimage://gearshape.fill";
+ case "SEARCH_HISTORY":
+ return "systemimage://magnifyingglass.circle.fill";
+ case "BODY":
+ return "systemimage://figure";
+ case "SURROUNDING":
+ return "systemimage://arkit";
+ default:
+ return null;
+ }
+}
+/**
+ * Determines whether the intermediate detail privacy page is enabled.
+ */
+export function isIntermediateDetailPageEnabled(objectGraph) {
+ // Only watchOS has an intermediate detail page
+ if (objectGraph.client.deviceType !== "watch") {
+ return false;
+ }
+ return true;
+}
+//# sourceMappingURL=privacy-types-shelf.js.map \ No newline at end of file