@shopgate/engage 7.32.0-beta.2 → 7.32.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,25 +1,4 @@
1
- export { PAGE_PREVIEW_PATTERN } from '../detection';
2
- /**
3
- * Whether the app is currently in page admin preview mode.
4
- *
5
- * Snapshot taken when this module is first evaluated. Prefer the isPageAdminPreviewActive()
6
- * helper, which re-evaluates on every call.
7
- */
8
- export declare const IS_PAGE_ADMIN_PREVIEW_ACTIVE: boolean;
9
- /**
10
- * Whether the app is currently in frontend settings admin preview mode.
11
- *
12
- * Snapshot taken when this module is first evaluated. Prefer the
13
- * isFrontendSettingsAdminPreviewActive() helper, which re-evaluates on every call.
14
- */
15
- export declare const IS_FRONTEND_SETTINGS_ADMIN_PREVIEW_ACTIVE: boolean;
16
- /**
17
- * Whether the app is currently in admin preview mode (pages or frontend settings).
18
- *
19
- * Snapshot taken when this module is first evaluated. Prefer the isAdminPreviewActive() helper,
20
- * which re-evaluates on every call.
21
- */
22
- export declare const IS_ADMIN_PREVIEW_ACTIVE: boolean;
1
+ export declare const PAGE_PREVIEW_PATTERN = "/shopgate-internal-page-preview";
23
2
  /**
24
3
  * List of allowed origins for cms page preview iFrame communication.
25
4
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../admin-preview/constants/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,SAA2B,CAAC;AAErE;;;;;GAKG;AACH,eAAO,MAAM,yCAAyC,SAAuC,CAAC;AAE9F;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,SAAuB,CAAC;AAE5D;;GAEG;AACH,eAAO,MAAM,6BAA6B,UAMzC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../admin-preview/constants/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,oCAAoC,CAAC;AAEtE;;GAEG;AACH,eAAO,MAAM,6BAA6B,UAMzC,CAAC"}
@@ -1,29 +1,4 @@
1
- import { detectAdminPreview, detectFrontendSettingsAdminPreview, detectPageAdminPreview } from "../detection";
2
- export { PAGE_PREVIEW_PATTERN } from "../detection";
3
-
4
- /**
5
- * Whether the app is currently in page admin preview mode.
6
- *
7
- * Snapshot taken when this module is first evaluated. Prefer the isPageAdminPreviewActive()
8
- * helper, which re-evaluates on every call.
9
- */
10
- export const IS_PAGE_ADMIN_PREVIEW_ACTIVE = detectPageAdminPreview();
11
-
12
- /**
13
- * Whether the app is currently in frontend settings admin preview mode.
14
- *
15
- * Snapshot taken when this module is first evaluated. Prefer the
16
- * isFrontendSettingsAdminPreviewActive() helper, which re-evaluates on every call.
17
- */
18
- export const IS_FRONTEND_SETTINGS_ADMIN_PREVIEW_ACTIVE = detectFrontendSettingsAdminPreview();
19
-
20
- /**
21
- * Whether the app is currently in admin preview mode (pages or frontend settings).
22
- *
23
- * Snapshot taken when this module is first evaluated. Prefer the isAdminPreviewActive() helper,
24
- * which re-evaluates on every call.
25
- */
26
- export const IS_ADMIN_PREVIEW_ACTIVE = detectAdminPreview();
1
+ export const PAGE_PREVIEW_PATTERN = '/shopgate-internal-page-preview';
27
2
 
28
3
  /**
29
4
  * List of allowed origins for cms page preview iFrame communication.
@@ -1,17 +1,19 @@
1
- export declare const PAGE_PREVIEW_PATTERN = "/shopgate-internal-page-preview";
1
+ /**
2
+ * Internal to the admin-preview package - do not import this module from anywhere else. Consume
3
+ * the isAdminPreviewActive / isPageAdminPreviewActive / isFrontendSettingsAdminPreviewActive
4
+ * helpers from @shopgate/engage/admin-preview/helpers instead.
5
+ */
2
6
  export declare const FRONTEND_SETTINGS_PREVIEW_PARAM = "frontendSettingsPreview=true";
3
- export declare const SESSION_STORAGE_KEY_PAGE_PREVIEW = "sgPageAdminPreview";
4
- export declare const SESSION_STORAGE_KEY_FRONTEND_SETTINGS_PREVIEW = "sgFrontendSettingsAdminPreview";
5
7
  /**
6
- * Detects whether the app is in page admin preview mode. Once detected, the result is remembered
7
- * for the rest of the session, because the url is not guaranteed to keep the preview marker.
8
+ * Detects whether the app is in page admin preview mode. Once detected, the result is latched for
9
+ * the lifetime of the document, because the url is not guaranteed to keep the preview marker.
8
10
  * @returns True if the app is in page admin preview mode, false otherwise.
9
11
  */
10
12
  export declare const detectPageAdminPreview: () => boolean;
11
13
  /**
12
14
  * Detects whether the app is in frontend settings admin preview mode. Once detected, the result is
13
- * remembered for the rest of the session, because the url is not guaranteed to keep the query
14
- * param.
15
+ * latched for the lifetime of the document, because the app keeps navigating while the preview is
16
+ * open and the query param only exists on the url it started on.
15
17
  * @returns True if the app is in frontend settings admin preview mode, false otherwise.
16
18
  */
17
19
  export declare const detectFrontendSettingsAdminPreview: () => boolean;
@@ -26,6 +28,8 @@ export declare const detectAdminPreview: () => boolean;
26
28
  *
27
29
  * window.SGAdminPreviewDebug()
28
30
  *
31
+ * Compare initialHref with currentHref to see whether the url changed since the app booted, and
32
+ * check latched to see whether a result came from the latch rather than the current url.
29
33
  * @returns A snapshot of everything detection depends on.
30
34
  */
31
35
  export declare const getAdminPreviewDebugInfo: () => {
@@ -36,7 +40,7 @@ export declare const getAdminPreviewDebugInfo: () => {
36
40
  urlChangedSinceBoot: boolean;
37
41
  isInIframe: boolean;
38
42
  referrer: string;
39
- stickyFlags: {
43
+ latched: {
40
44
  page: boolean;
41
45
  frontendSettings: boolean;
42
46
  };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detection.d.ts","sourceRoot":"","sources":["../../../../admin-preview/helpers/_internal/detection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,eAAO,MAAM,+BAA+B,iCAAiC,CAAC;AAsB9E;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAAO,OAQzC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kCAAkC,QAAO,OAWrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAO,OAC4B,CAAC;AAEnE;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;CAiBnC,CAAC"}
@@ -0,0 +1,93 @@
1
+ import "core-js/modules/es.array.includes.js";
2
+ /**
3
+ * Internal to the admin-preview package - do not import this module from anywhere else. Consume
4
+ * the isAdminPreviewActive / isPageAdminPreviewActive / isFrontendSettingsAdminPreviewActive
5
+ * helpers from @shopgate/engage/admin-preview/helpers instead.
6
+ */
7
+
8
+ import { PAGE_PREVIEW_PATTERN } from "../../constants";
9
+ export const FRONTEND_SETTINGS_PREVIEW_PARAM = 'frontendSettingsPreview=true';
10
+
11
+ /**
12
+ * The url as it was when this module was first evaluated. Captured here because detection can run
13
+ * again later, when the url may already have been rewritten.
14
+ */
15
+ const INITIAL_HREF = window.location.href;
16
+
17
+ /**
18
+ * Preview mode is latched in memory rather than persisted. Session storage is scoped per origin
19
+ * per tab, not per iframe - the admin shows the preview in same-origin iframes on several screens
20
+ * within one tab, so a persisted flag from one screen would leak into the next. Module state is
21
+ * per document, so each iframe gets its own.
22
+ *
23
+ * The latches only ever go false -> true. Detection must not fall back to re-reading the url,
24
+ * because the frontend settings preview navigates the app normally: once the admin opens another
25
+ * page, the query param is gone from the url even though preview mode is still on.
26
+ */
27
+ let pageAdminPreviewLatched = false;
28
+ let frontendSettingsAdminPreviewLatched = false;
29
+
30
+ /**
31
+ * Detects whether the app is in page admin preview mode. Once detected, the result is latched for
32
+ * the lifetime of the document, because the url is not guaranteed to keep the preview marker.
33
+ * @returns True if the app is in page admin preview mode, false otherwise.
34
+ */
35
+ export const detectPageAdminPreview = () => {
36
+ if (pageAdminPreviewLatched) {
37
+ return true;
38
+ }
39
+ pageAdminPreviewLatched = window.location.pathname.startsWith(PAGE_PREVIEW_PATTERN);
40
+ return pageAdminPreviewLatched;
41
+ };
42
+
43
+ /**
44
+ * Detects whether the app is in frontend settings admin preview mode. Once detected, the result is
45
+ * latched for the lifetime of the document, because the app keeps navigating while the preview is
46
+ * open and the query param only exists on the url it started on.
47
+ * @returns True if the app is in frontend settings admin preview mode, false otherwise.
48
+ */
49
+ export const detectFrontendSettingsAdminPreview = () => {
50
+ if (frontendSettingsAdminPreviewLatched) {
51
+ return true;
52
+ }
53
+ frontendSettingsAdminPreviewLatched = window.location.search.includes(FRONTEND_SETTINGS_PREVIEW_PARAM);
54
+ return frontendSettingsAdminPreviewLatched;
55
+ };
56
+
57
+ /**
58
+ * Detects whether the app is in admin preview mode (pages or frontend settings).
59
+ * @returns True if the app is in admin preview mode, false otherwise.
60
+ */
61
+ export const detectAdminPreview = () => detectPageAdminPreview() || detectFrontendSettingsAdminPreview();
62
+
63
+ /**
64
+ * Returns the current detection state. Exposed on window as SGAdminPreviewDebug so the values can
65
+ * be inspected on a deployed build, from the console of the preview iframe:
66
+ *
67
+ * window.SGAdminPreviewDebug()
68
+ *
69
+ * Compare initialHref with currentHref to see whether the url changed since the app booted, and
70
+ * check latched to see whether a result came from the latch rather than the current url.
71
+ * @returns A snapshot of everything detection depends on.
72
+ */
73
+ export const getAdminPreviewDebugInfo = () => ({
74
+ initialHref: INITIAL_HREF,
75
+ currentHref: window.location.href,
76
+ pathname: window.location.pathname,
77
+ search: window.location.search,
78
+ urlChangedSinceBoot: INITIAL_HREF !== window.location.href,
79
+ isInIframe: window.parent !== window,
80
+ referrer: document.referrer,
81
+ latched: {
82
+ page: pageAdminPreviewLatched,
83
+ frontendSettings: frontendSettingsAdminPreviewLatched
84
+ },
85
+ detected: {
86
+ page: detectPageAdminPreview(),
87
+ frontendSettings: detectFrontendSettingsAdminPreview(),
88
+ any: detectAdminPreview()
89
+ }
90
+ });
91
+
92
+ // @ts-expect-error - debug-only global, intentionally not on the Window type.
93
+ window.SGAdminPreviewDebug = getAdminPreviewDebugInfo;
@@ -13,4 +13,11 @@ export declare const isPageAdminPreviewActive: () => boolean;
13
13
  * @returns True if the app is in frontend settings admin preview mode, false otherwise.
14
14
  */
15
15
  export declare const isFrontendSettingsAdminPreviewActive: () => boolean;
16
+ /**
17
+ * Checks if navigation is blocked. Page admin preview renders a single page in an iframe, so
18
+ * leaving that page would only ever show the admin something it did not ask to preview.
19
+ * Frontend settings preview is not affected - it previews styling on the regular app.
20
+ * @returns True if the app must not navigate, false otherwise.
21
+ */
22
+ export declare const isNavigationBlocked: () => boolean;
16
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../admin-preview/helpers/index.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAAO,OAClB,CAAC;AAEvB;;;GAGG;AACH,eAAO,MAAM,wBAAwB,QAAO,OAClB,CAAC;AAE3B;;;GAGG;AACH,eAAO,MAAM,oCAAoC,QAAO,OAClB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../admin-preview/helpers/index.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAAO,OAClB,CAAC;AAEvB;;;GAGG;AACH,eAAO,MAAM,wBAAwB,QAAO,OAClB,CAAC;AAE3B;;;GAGG;AACH,eAAO,MAAM,oCAAoC,QAAO,OAClB,CAAC;AAEvC;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,QAAO,OACX,CAAC"}
@@ -1,4 +1,4 @@
1
- import { detectAdminPreview, detectFrontendSettingsAdminPreview, detectPageAdminPreview } from "../detection";
1
+ import { detectAdminPreview, detectFrontendSettingsAdminPreview, detectPageAdminPreview } from "./_internal/detection";
2
2
 
3
3
  /**
4
4
  * Checks if the app is currently in admin preview mode (pages or frontend settings).
@@ -16,4 +16,12 @@ export const isPageAdminPreviewActive = () => detectPageAdminPreview();
16
16
  * Checks if the app is currently in frontend settings admin preview mode.
17
17
  * @returns True if the app is in frontend settings admin preview mode, false otherwise.
18
18
  */
19
- export const isFrontendSettingsAdminPreviewActive = () => detectFrontendSettingsAdminPreview();
19
+ export const isFrontendSettingsAdminPreviewActive = () => detectFrontendSettingsAdminPreview();
20
+
21
+ /**
22
+ * Checks if navigation is blocked. Page admin preview renders a single page in an iframe, so
23
+ * leaving that page would only ever show the admin something it did not ask to preview.
24
+ * Frontend settings preview is not affected - it previews styling on the regular app.
25
+ * @returns True if the app must not navigate, false otherwise.
26
+ */
27
+ export const isNavigationBlocked = () => isPageAdminPreviewActive();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shopgate/engage",
3
- "version": "7.32.0-beta.2",
3
+ "version": "7.32.0-beta.3",
4
4
  "description": "Shopgate's ENGAGE library.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Shopgate <support@shopgate.com>",
@@ -18,12 +18,12 @@
18
18
  "dependencies": {
19
19
  "@emotion/react": "^11.14.0",
20
20
  "@shopgate/native-modules": "1.0.0-beta.25",
21
- "@shopgate/pwa-common": "7.32.0-beta.2",
22
- "@shopgate/pwa-common-commerce": "7.32.0-beta.2",
23
- "@shopgate/pwa-core": "7.32.0-beta.2",
24
- "@shopgate/pwa-ui-ios": "7.32.0-beta.2",
25
- "@shopgate/pwa-ui-material": "7.32.0-beta.2",
26
- "@shopgate/pwa-ui-shared": "7.32.0-beta.2",
21
+ "@shopgate/pwa-common": "7.32.0-beta.3",
22
+ "@shopgate/pwa-common-commerce": "7.32.0-beta.3",
23
+ "@shopgate/pwa-core": "7.32.0-beta.3",
24
+ "@shopgate/pwa-ui-ios": "7.32.0-beta.3",
25
+ "@shopgate/pwa-ui-material": "7.32.0-beta.3",
26
+ "@shopgate/pwa-ui-shared": "7.32.0-beta.3",
27
27
  "@stripe/react-stripe-js": "^1.16.5",
28
28
  "@stripe/stripe-js": "^1.44.1",
29
29
  "@virtuous/conductor": "~2.5.0",
@@ -46,7 +46,7 @@
46
46
  "validate.js": "^0.13.1"
47
47
  },
48
48
  "devDependencies": {
49
- "@shopgate/pwa-unit-test": "7.32.0-beta.2",
49
+ "@shopgate/pwa-unit-test": "7.32.0-beta.3",
50
50
  "@types/color": "^4.2.0",
51
51
  "@types/lodash": "^4.17.24",
52
52
  "@types/react-dom": "^17.0.2",
@@ -2,7 +2,7 @@ import React, { useMemo, useState, useEffect } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { makeStyles, useResponsiveValue } from '@shopgate/engage/styles';
4
4
  import { useParallax } from 'react-scroll-parallax';
5
- import { IS_PAGE_PREVIEW_ACTIVE } from "../../constants";
5
+ import { isAdminPreviewActive } from '@shopgate/engage/admin-preview/helpers';
6
6
  import { parseImageUrl } from "../../helpers";
7
7
 
8
8
  /**
@@ -143,7 +143,7 @@ const ResponsiveWidgetImage = ({
143
143
  // height 0 until a resize forces the browser to re-evaluate. Load eagerly there; keep lazy
144
144
  // loading for the live app.
145
145
  ,
146
- loading: IS_PAGE_PREVIEW_ACTIVE ? 'eager' : 'lazy',
146
+ loading: isAdminPreviewActive() ? 'eager' : 'lazy',
147
147
  className: cx(classes.preventSave, classes.image, {
148
148
  [classes.banner]: isBanner
149
149
  }, className),
@@ -1,4 +1,4 @@
1
- import { IS_ADMIN_PREVIEW_ACTIVE } from '@shopgate/engage/admin-preview/constants';
1
+ import { isAdminPreviewActive } from '@shopgate/engage/admin-preview/helpers';
2
2
  import { PAGE_PATH, PAGE_PATTERN } from '@shopgate/pwa-common/constants/RoutePaths';
3
3
  export { PAGE_PREVIEW_PATTERN } from '@shopgate/engage/admin-preview/constants';
4
4
  export * from '@shopgate/pwa-common/constants/PageIDs';
@@ -15,9 +15,11 @@ export const PAGE_PREVIEW_SLUG = 'page_preview';
15
15
  /**
16
16
  * Checks if the app is currently in page preview mode.
17
17
  * @todo For backwards compatibility, this constant is still exported from the page package and
18
- * mapped to the IS_ADMIN_PREVIEW_ACTIVE constant. It should be removed in the future.
18
+ * mapped to the isAdminPreviewActive helper. It should be removed in the future. Note that this
19
+ * is a snapshot taken when this module is evaluated - consumers that can call
20
+ * isAdminPreviewActive() at usage time should do that instead.
19
21
  */
20
- export const IS_PAGE_PREVIEW_ACTIVE = IS_ADMIN_PREVIEW_ACTIVE;
22
+ export const IS_PAGE_PREVIEW_ACTIVE = isAdminPreviewActive();
21
23
 
22
24
  /**
23
25
  * One hour in milliseconds
@@ -1,7 +1,7 @@
1
1
  import { createContext, memo, useMemo, useLayoutEffect } from 'react';
2
2
  import useLocalStorage from '@shopgate/engage/core/hooks/useLocalStorage';
3
3
  import { GlobalStyles } from '@shopgate/engage/styles';
4
- import { IS_FRONTEND_SETTINGS_ADMIN_PREVIEW_ACTIVE } from '@shopgate/engage/admin-preview/constants';
4
+ import { isFrontendSettingsAdminPreviewActive } from '@shopgate/engage/admin-preview/helpers';
5
5
  import { FrontendSettingsPreviewBridge } from '@shopgate/engage/admin-preview/components';
6
6
  import ActiveBreakpointProvider from "./ActiveBreakpointProvider";
7
7
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -32,7 +32,7 @@ const ThemeProvider = ({
32
32
  const styleSheets = useMemo(() => theme.generateStyleSheets(), [theme]);
33
33
  return /*#__PURE__*/_jsxs(ColorSchemeContext.Provider, {
34
34
  value: colorSchemeContextValue,
35
- children: [IS_FRONTEND_SETTINGS_ADMIN_PREVIEW_ACTIVE && /*#__PURE__*/_jsx(FrontendSettingsPreviewBridge, {}), /*#__PURE__*/_jsxs(ThemeContext.Provider, {
35
+ children: [isFrontendSettingsAdminPreviewActive() && /*#__PURE__*/_jsx(FrontendSettingsPreviewBridge, {}), /*#__PURE__*/_jsxs(ThemeContext.Provider, {
36
36
  value: theme,
37
37
  children: [/*#__PURE__*/_jsx(ActiveBreakpointProvider, {
38
38
  children: children
@@ -1 +0,0 @@
1
- {"version":3,"file":"detection.d.ts","sourceRoot":"","sources":["../../admin-preview/detection.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,oBAAoB,oCAAoC,CAAC;AAEtE,eAAO,MAAM,+BAA+B,iCAAiC,CAAC;AAE9E,eAAO,MAAM,gCAAgC,uBAAuB,CAAC;AAErE,eAAO,MAAM,6CAA6C,mCAAmC,CAAC;AAyF9F;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,QAAO,OAYzC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kCAAkC,QAAO,OAYrD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAO,OAC4B,CAAC;AAEnE;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;CAiBnC,CAAC"}
@@ -1,166 +0,0 @@
1
- import "core-js/modules/es.array.includes.js";
2
- // The logger is imported from @shopgate/pwa-core/helpers rather than @shopgate/engage/core/helpers
3
- // on purpose: that barrel pulls in scrollContainer, which imports this module back. Since detection
4
- // runs while the constants module is evaluated, the cycle would resolve to undefined at boot.
5
- import { logger } from '@shopgate/pwa-core/helpers';
6
- export const PAGE_PREVIEW_PATTERN = '/shopgate-internal-page-preview';
7
- export const FRONTEND_SETTINGS_PREVIEW_PARAM = 'frontendSettingsPreview=true';
8
- export const SESSION_STORAGE_KEY_PAGE_PREVIEW = 'sgPageAdminPreview';
9
- export const SESSION_STORAGE_KEY_FRONTEND_SETTINGS_PREVIEW = 'sgFrontendSettingsAdminPreview';
10
- const LOG_PREFIX = '[AdminPreview]';
11
-
12
- /**
13
- * The url as it was when this module was first evaluated. Captured here because detection can run
14
- * again later, when the url may already have been rewritten.
15
- */
16
- const INITIAL_HREF = window.location.href;
17
-
18
- /**
19
- * Remembers the last logged result per detector, so repeated calls only log on a real change.
20
- * isAdminPreviewActive() is called from hot paths like breakpoints and scroll handling.
21
- */
22
- const lastLoggedResults = {};
23
-
24
- /**
25
- * Reads a sticky preview flag. Storage access throws in a sandboxed iframe without
26
- * "allow-same-origin", and is blocked by some browsers in cross-origin iframes - the preview
27
- * runs in exactly that context, so a throw here would break app boot for everyone.
28
- * @param key The session storage key to read.
29
- * @returns True when the flag was persisted before, false otherwise.
30
- */
31
- const readStickyFlag = key => {
32
- try {
33
- return window.sessionStorage.getItem(key) === '1';
34
- } catch (error) {
35
- logger.warn(`${LOG_PREFIX} session storage is not readable`, {
36
- key,
37
- error
38
- });
39
- return false;
40
- }
41
- };
42
-
43
- /**
44
- * Persists a sticky preview flag, so preview mode survives a reload or a url rewrite that drops
45
- * the query string. See readStickyFlag for why this can throw.
46
- * @param key The session storage key to write.
47
- */
48
- const writeStickyFlag = key => {
49
- try {
50
- window.sessionStorage.setItem(key, '1');
51
- } catch (error) {
52
- logger.warn(`${LOG_PREFIX} session storage is not writable`, {
53
- key,
54
- error
55
- });
56
- }
57
- };
58
-
59
- /**
60
- * Logs how a detection result was reached, but only when it differs from the previous result for
61
- * that detector. Kept verbose on purpose: it exists to investigate why preview mode is not picked
62
- * up on deployed builds.
63
- * @param name The detector that produced the result.
64
- * @param result The detected result.
65
- * @param matchedUrl Whether the current url carries the preview marker.
66
- * @param matchedStickyFlag Whether the persisted sticky flag was set.
67
- */
68
- const logDetection = (name, result, matchedUrl, matchedStickyFlag) => {
69
- if (lastLoggedResults[name] === result) {
70
- return;
71
- }
72
- lastLoggedResults[name] = result;
73
- logger.log(`${LOG_PREFIX} ${name} -> ${result}`, {
74
- result,
75
- // True when the url still carries the marker.
76
- matchedUrl,
77
- // True when the result came from the persisted flag - i.e. the url lost the marker.
78
- matchedStickyFlag,
79
- // Compare these two to see whether the url changed since the app booted.
80
- initialHref: INITIAL_HREF,
81
- currentHref: window.location.href,
82
- pathname: window.location.pathname,
83
- search: window.location.search,
84
- // A cross-origin parent means the app really is embedded in the admin.
85
- isInIframe: window.parent !== window,
86
- referrer: document.referrer
87
- });
88
- };
89
-
90
- /**
91
- * Detects whether the app is in page admin preview mode. Once detected, the result is remembered
92
- * for the rest of the session, because the url is not guaranteed to keep the preview marker.
93
- * @returns True if the app is in page admin preview mode, false otherwise.
94
- */
95
- export const detectPageAdminPreview = () => {
96
- const matchedUrl = window.location.pathname.startsWith(PAGE_PREVIEW_PATTERN);
97
- if (matchedUrl) {
98
- writeStickyFlag(SESSION_STORAGE_KEY_PAGE_PREVIEW);
99
- logDetection('detectPageAdminPreview', true, true, false);
100
- return true;
101
- }
102
- const matchedStickyFlag = readStickyFlag(SESSION_STORAGE_KEY_PAGE_PREVIEW);
103
- logDetection('detectPageAdminPreview', matchedStickyFlag, false, matchedStickyFlag);
104
- return matchedStickyFlag;
105
- };
106
-
107
- /**
108
- * Detects whether the app is in frontend settings admin preview mode. Once detected, the result is
109
- * remembered for the rest of the session, because the url is not guaranteed to keep the query
110
- * param.
111
- * @returns True if the app is in frontend settings admin preview mode, false otherwise.
112
- */
113
- export const detectFrontendSettingsAdminPreview = () => {
114
- const matchedUrl = window.location.search.includes(FRONTEND_SETTINGS_PREVIEW_PARAM);
115
- if (matchedUrl) {
116
- writeStickyFlag(SESSION_STORAGE_KEY_FRONTEND_SETTINGS_PREVIEW);
117
- logDetection('detectFrontendSettingsAdminPreview', true, true, false);
118
- return true;
119
- }
120
- const matchedStickyFlag = readStickyFlag(SESSION_STORAGE_KEY_FRONTEND_SETTINGS_PREVIEW);
121
- logDetection('detectFrontendSettingsAdminPreview', matchedStickyFlag, false, matchedStickyFlag);
122
- return matchedStickyFlag;
123
- };
124
-
125
- /**
126
- * Detects whether the app is in admin preview mode (pages or frontend settings).
127
- * @returns True if the app is in admin preview mode, false otherwise.
128
- */
129
- export const detectAdminPreview = () => detectPageAdminPreview() || detectFrontendSettingsAdminPreview();
130
-
131
- /**
132
- * Returns the current detection state. Exposed on window as SGAdminPreviewDebug so the values can
133
- * be inspected on a deployed build, from the console of the preview iframe:
134
- *
135
- * window.SGAdminPreviewDebug()
136
- *
137
- * @returns A snapshot of everything detection depends on.
138
- */
139
- export const getAdminPreviewDebugInfo = () => ({
140
- initialHref: INITIAL_HREF,
141
- currentHref: window.location.href,
142
- pathname: window.location.pathname,
143
- search: window.location.search,
144
- urlChangedSinceBoot: INITIAL_HREF !== window.location.href,
145
- isInIframe: window.parent !== window,
146
- referrer: document.referrer,
147
- stickyFlags: {
148
- page: readStickyFlag(SESSION_STORAGE_KEY_PAGE_PREVIEW),
149
- frontendSettings: readStickyFlag(SESSION_STORAGE_KEY_FRONTEND_SETTINGS_PREVIEW)
150
- },
151
- detected: {
152
- page: detectPageAdminPreview(),
153
- frontendSettings: detectFrontendSettingsAdminPreview(),
154
- any: detectAdminPreview()
155
- }
156
- });
157
- logger.log(`${LOG_PREFIX} module evaluated`, {
158
- href: INITIAL_HREF,
159
- search: window.location.search,
160
- hasParamAtBoot: window.location.search.includes(FRONTEND_SETTINGS_PREVIEW_PARAM),
161
- isInIframe: window.parent !== window,
162
- referrer: document.referrer
163
- });
164
-
165
- // @ts-expect-error - debug-only global, intentionally not on the Window type.
166
- window.SGAdminPreviewDebug = getAdminPreviewDebugInfo;