@aglyn/tenant-feature-instance 1.0.0-beta.288 → 1.0.0-beta.290
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/tenant-feature-instance",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.290",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,12 +25,12 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-util-email": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-util-http": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-util-timestamp": "1.0.0-beta.
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.290",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.290",
|
|
30
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.290",
|
|
31
|
+
"@aglyn/shared-util-email": "1.0.0-beta.290",
|
|
32
|
+
"@aglyn/shared-util-http": "1.0.0-beta.290",
|
|
33
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.290",
|
|
34
34
|
"@swc/helpers": "0.5.23",
|
|
35
35
|
"lodash-es": "^4.18.1",
|
|
36
36
|
"mobx": "^7.0.5"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { authorizedFetch } from '@aglyn/shared-util-http/authorized-token';
|
|
2
|
+
/**
|
|
3
|
+
* The site's Preview switch (AGL-4086), as a hook and as the switch itself,
|
|
4
|
+
* so every place that offers it — Admin → Error pages, the console's "in
|
|
5
|
+
* preview" banner, the Bring my site review — flips it the same way.
|
|
6
|
+
*
|
|
7
|
+
* It never writes the host document itself. `/api/hosts/preview` does: making
|
|
8
|
+
* a site public is a launch, so the door checks the person is a site admin,
|
|
9
|
+
* drops every cached page and reports the first publish, and the rules keep
|
|
10
|
+
* the field away from the client SDK.
|
|
11
|
+
*/
|
|
12
|
+
/** What a flip came to. */
|
|
13
|
+
export interface SitePreviewOutcome {
|
|
14
|
+
/** The state the site is in now. */
|
|
15
|
+
preview: boolean;
|
|
16
|
+
/** Whether every cached page was dropped with it. */
|
|
17
|
+
dropped: boolean;
|
|
18
|
+
/** Why it did not change, when it did not. */
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Ask the door to put the site in preview, or make it public. */
|
|
22
|
+
export declare function requestSitePreview(user: Parameters<typeof authorizedFetch>[0], hostId: string, preview: boolean): Promise<SitePreviewOutcome>;
|
|
23
|
+
/**
|
|
24
|
+
* The link that opens a site in preview on its own address (AGL-4086): a
|
|
25
|
+
* two-minute enter link the console mints for the site's team, which plants
|
|
26
|
+
* the preview cookie on that host and lands on `path`. `null` when it could
|
|
27
|
+
* not be minted.
|
|
28
|
+
*/
|
|
29
|
+
export declare function requestSitePreviewLink(user: Parameters<typeof authorizedFetch>[0], hostId: string, path?: string): Promise<string | null>;
|
|
30
|
+
/**
|
|
31
|
+
* Open a site in preview in a new tab, entering it through the minted link.
|
|
32
|
+
*
|
|
33
|
+
* The tab is opened FIRST, inside the click, and pointed at the link once it
|
|
34
|
+
* arrives: a tab opened after an await is a popup a browser blocks. When the
|
|
35
|
+
* link cannot be minted the tab goes to `fallbackHref`, the site's ordinary
|
|
36
|
+
* address, where the public answer is what anyone gets.
|
|
37
|
+
*/
|
|
38
|
+
export declare function openSitePreview(user: Parameters<typeof authorizedFetch>[0], hostId: string, fallbackHref: string, path?: string): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* The site's preview state, live, and the setter. `inPreview` is `null` while
|
|
41
|
+
* the host document loads, so a caller can hold its control rather than show
|
|
42
|
+
* a state it has not read.
|
|
43
|
+
*/
|
|
44
|
+
export declare function useSitePreviewSwitch(hostId: string | null | undefined): {
|
|
45
|
+
inPreview: boolean | null;
|
|
46
|
+
busy: boolean;
|
|
47
|
+
setPreview: (preview: boolean) => Promise<SitePreviewOutcome>;
|
|
48
|
+
};
|
|
49
|
+
export interface SitePreviewSwitchProps {
|
|
50
|
+
hostId: string;
|
|
51
|
+
/**
|
|
52
|
+
* The site's state as the caller reads it, `null` while it loads. Handed
|
|
53
|
+
* in rather than read here, so the switch draws whatever host document the
|
|
54
|
+
* page already holds and opens no second listener on it.
|
|
55
|
+
*/
|
|
56
|
+
inPreview: boolean | null;
|
|
57
|
+
/** The signed-in user, whose ID token the door checks. */
|
|
58
|
+
user: Parameters<typeof authorizedFetch>[0];
|
|
59
|
+
/**
|
|
60
|
+
* Shown off with the reason, never hidden, when the person cannot flip it
|
|
61
|
+
* — the same rule every console prerequisite follows.
|
|
62
|
+
*/
|
|
63
|
+
disabledReason?: string | null;
|
|
64
|
+
/** Told how each flip came out, for the caller's snackbar. */
|
|
65
|
+
onChanged?: (outcome: SitePreviewOutcome, asked: boolean) => void;
|
|
66
|
+
}
|
|
67
|
+
/** The words of the switch, shared with the native screen and the docs. */
|
|
68
|
+
export declare const SITE_PREVIEW_SWITCH_LABEL = "Preview \u2014 only you and your team can see the site";
|
|
69
|
+
export declare const SITE_PREVIEW_SWITCH_HELP: string;
|
|
70
|
+
/** The switch, as Admin → Error pages draws it. */
|
|
71
|
+
export declare function SitePreviewSwitch(props: SitePreviewSwitchProps): import("react").JSX.Element;
|
|
72
|
+
export declare namespace SitePreviewSwitch {
|
|
73
|
+
var displayName: string;
|
|
74
|
+
}
|
|
75
|
+
export default SitePreviewSwitch;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ 'use client';
|
|
17
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
+
import { PLATFORM_BRAND_NAME } from "@aglyn/aglyn/app-utils/platform-brand";
|
|
19
|
+
import { isHostInPreview } from "@aglyn/aglyn/app-utils/site-preview";
|
|
20
|
+
import { authorizedFetch } from "@aglyn/shared-util-http/authorized-token";
|
|
21
|
+
import { FormControlLabel, Stack, Switch, Typography } from "@mui/material";
|
|
22
|
+
import { doc } from "firebase/firestore";
|
|
23
|
+
import { useCallback, useEffect, useState } from "react";
|
|
24
|
+
import { useFirestore, useUser } from "../hooks/firebase/firebase-services.js";
|
|
25
|
+
import { useFirestoreDoc } from "../hooks/use-firestore-doc.js";
|
|
26
|
+
/** Ask the door to put the site in preview, or make it public. */ export async function requestSitePreview(user, hostId, preview) {
|
|
27
|
+
try {
|
|
28
|
+
const response = await authorizedFetch(user, '/api/hosts/preview', {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: {
|
|
31
|
+
'Content-Type': 'application/json'
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify({
|
|
34
|
+
hostId,
|
|
35
|
+
preview
|
|
36
|
+
})
|
|
37
|
+
});
|
|
38
|
+
const payload = await response.json().catch(()=>null);
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
var _ref;
|
|
41
|
+
return {
|
|
42
|
+
preview: !preview,
|
|
43
|
+
dropped: false,
|
|
44
|
+
error: String((_ref = payload == null ? void 0 : payload.error) != null ? _ref : 'The site could not be changed')
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
preview: (payload == null ? void 0 : payload.preview) === true,
|
|
49
|
+
dropped: (payload == null ? void 0 : payload.dropped) === true
|
|
50
|
+
};
|
|
51
|
+
} catch (unused) {
|
|
52
|
+
return {
|
|
53
|
+
preview: !preview,
|
|
54
|
+
dropped: false,
|
|
55
|
+
error: 'The site could not be changed'
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The link that opens a site in preview on its own address (AGL-4086): a
|
|
61
|
+
* two-minute enter link the console mints for the site's team, which plants
|
|
62
|
+
* the preview cookie on that host and lands on `path`. `null` when it could
|
|
63
|
+
* not be minted.
|
|
64
|
+
*/ export async function requestSitePreviewLink(user, hostId, path = '/') {
|
|
65
|
+
try {
|
|
66
|
+
const response = await authorizedFetch(user, '/api/site-preview/link', {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
headers: {
|
|
69
|
+
'Content-Type': 'application/json'
|
|
70
|
+
},
|
|
71
|
+
body: JSON.stringify({
|
|
72
|
+
hostId,
|
|
73
|
+
path
|
|
74
|
+
})
|
|
75
|
+
});
|
|
76
|
+
if (!response.ok) return null;
|
|
77
|
+
const payload = await response.json().catch(()=>null);
|
|
78
|
+
return typeof (payload == null ? void 0 : payload.url) === 'string' ? payload.url : null;
|
|
79
|
+
} catch (unused) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Open a site in preview in a new tab, entering it through the minted link.
|
|
85
|
+
*
|
|
86
|
+
* The tab is opened FIRST, inside the click, and pointed at the link once it
|
|
87
|
+
* arrives: a tab opened after an await is a popup a browser blocks. When the
|
|
88
|
+
* link cannot be minted the tab goes to `fallbackHref`, the site's ordinary
|
|
89
|
+
* address, where the public answer is what anyone gets.
|
|
90
|
+
*/ export async function openSitePreview(user, hostId, fallbackHref, path = '/') {
|
|
91
|
+
var _ref;
|
|
92
|
+
const tab = typeof window === 'undefined' ? null : window.open('', '_blank');
|
|
93
|
+
const url = (_ref = await requestSitePreviewLink(user, hostId, path)) != null ? _ref : fallbackHref;
|
|
94
|
+
if (!tab) {
|
|
95
|
+
if (typeof window !== 'undefined') window.location.assign(url);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// Severed before it navigates, as `rel="noopener"` would have.
|
|
99
|
+
tab.opener = null;
|
|
100
|
+
tab.location.href = url;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The site's preview state, live, and the setter. `inPreview` is `null` while
|
|
104
|
+
* the host document loads, so a caller can hold its control rather than show
|
|
105
|
+
* a state it has not read.
|
|
106
|
+
*/ export function useSitePreviewSwitch(hostId) {
|
|
107
|
+
const firestore = useFirestore();
|
|
108
|
+
const { data: user } = useUser();
|
|
109
|
+
const { data: host, status } = useFirestoreDoc(()=>hostId ? doc(firestore, 'hosts', hostId) : null, [
|
|
110
|
+
firestore,
|
|
111
|
+
hostId
|
|
112
|
+
]);
|
|
113
|
+
const [busy, setBusy] = useState(false);
|
|
114
|
+
const setPreview = useCallback(async (preview)=>{
|
|
115
|
+
if (!hostId) return {
|
|
116
|
+
preview: !preview,
|
|
117
|
+
dropped: false,
|
|
118
|
+
error: 'No site'
|
|
119
|
+
};
|
|
120
|
+
setBusy(true);
|
|
121
|
+
try {
|
|
122
|
+
return await requestSitePreview(user, hostId, preview);
|
|
123
|
+
} finally{
|
|
124
|
+
setBusy(false);
|
|
125
|
+
}
|
|
126
|
+
}, [
|
|
127
|
+
hostId,
|
|
128
|
+
user
|
|
129
|
+
]);
|
|
130
|
+
return {
|
|
131
|
+
inPreview: status === 'success' ? isHostInPreview(host) : null,
|
|
132
|
+
busy,
|
|
133
|
+
setPreview
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/** The words of the switch, shared with the native screen and the docs. */ export const SITE_PREVIEW_SWITCH_LABEL = 'Preview — only you and your team can see the site';
|
|
137
|
+
export const SITE_PREVIEW_SWITCH_HELP = 'Visitors see your 503 page, or "Coming soon", until you make the site public. ' + `You, your team and ${PLATFORM_BRAND_NAME} support see the whole site.`;
|
|
138
|
+
/** The switch, as Admin → Error pages draws it. */ export function SitePreviewSwitch(props) {
|
|
139
|
+
const { hostId, inPreview, user, disabledReason, onChanged } = props;
|
|
140
|
+
const [busy, setBusy] = useState(false);
|
|
141
|
+
// What the person just asked for, shown until the host document catches up.
|
|
142
|
+
const [asked, setAsked] = useState(null);
|
|
143
|
+
// Let go of it once the document agrees, so a later change from elsewhere
|
|
144
|
+
// shows through.
|
|
145
|
+
useEffect(()=>{
|
|
146
|
+
if (asked !== null && inPreview === asked) setAsked(null);
|
|
147
|
+
}, [
|
|
148
|
+
asked,
|
|
149
|
+
inPreview
|
|
150
|
+
]);
|
|
151
|
+
const checked = asked != null ? asked : inPreview === true;
|
|
152
|
+
const disabled = Boolean(disabledReason) || busy || inPreview === null;
|
|
153
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
154
|
+
spacing: 0.5,
|
|
155
|
+
children: [
|
|
156
|
+
/*#__PURE__*/ _jsx(FormControlLabel, {
|
|
157
|
+
control: /*#__PURE__*/ _jsx(Switch, {
|
|
158
|
+
color: "primary",
|
|
159
|
+
checked: checked,
|
|
160
|
+
disabled: disabled,
|
|
161
|
+
onChange: (event)=>{
|
|
162
|
+
const next = event.target.checked;
|
|
163
|
+
setAsked(next);
|
|
164
|
+
setBusy(true);
|
|
165
|
+
void requestSitePreview(user, hostId, next).then((outcome)=>{
|
|
166
|
+
setBusy(false);
|
|
167
|
+
setAsked(outcome.error ? null : outcome.preview);
|
|
168
|
+
onChanged == null ? void 0 : onChanged(outcome, next);
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}),
|
|
172
|
+
label: SITE_PREVIEW_SWITCH_LABEL
|
|
173
|
+
}),
|
|
174
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
175
|
+
variant: "caption",
|
|
176
|
+
color: "text.secondary",
|
|
177
|
+
children: disabledReason || SITE_PREVIEW_SWITCH_HELP
|
|
178
|
+
})
|
|
179
|
+
]
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
SitePreviewSwitch.displayName = 'SitePreviewSwitch';
|
|
183
|
+
export default SitePreviewSwitch;
|
|
184
|
+
|
|
185
|
+
//# sourceMappingURL=site-preview-switch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../libs/tenant/feature/instance/src/lib/components/site-preview-switch.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn/app-utils/platform-brand'\nimport { isHostInPreview } from '@aglyn/aglyn/app-utils/site-preview'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { FormControlLabel, Stack, Switch, Typography } from '@mui/material'\nimport { doc } from 'firebase/firestore'\nimport { useCallback, useEffect, useState } from 'react'\nimport { useFirestore, useUser } from '../hooks/firebase/firebase-services'\nimport { useFirestoreDoc } from '../hooks/use-firestore-doc'\n\n/**\n * The site's Preview switch (AGL-4086), as a hook and as the switch itself,\n * so every place that offers it — Admin → Error pages, the console's \"in\n * preview\" banner, the Bring my site review — flips it the same way.\n *\n * It never writes the host document itself. `/api/hosts/preview` does: making\n * a site public is a launch, so the door checks the person is a site admin,\n * drops every cached page and reports the first publish, and the rules keep\n * the field away from the client SDK.\n */\n\n/** What a flip came to. */\nexport interface SitePreviewOutcome {\n /** The state the site is in now. */\n preview: boolean\n /** Whether every cached page was dropped with it. */\n dropped: boolean\n /** Why it did not change, when it did not. */\n error?: string\n}\n\n/** Ask the door to put the site in preview, or make it public. */\nexport async function requestSitePreview(\n user: Parameters<typeof authorizedFetch>[0],\n hostId: string,\n preview: boolean,\n): Promise<SitePreviewOutcome> {\n try {\n const response = await authorizedFetch(user, '/api/hosts/preview', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, preview }),\n })\n const payload = (await response.json().catch(() => null)) as\n | { preview?: boolean; dropped?: boolean; error?: string }\n | null\n if (!response.ok) {\n return {\n preview: !preview,\n dropped: false,\n error: String(payload?.error ?? 'The site could not be changed'),\n }\n }\n return { preview: payload?.preview === true, dropped: payload?.dropped === true }\n } catch {\n return { preview: !preview, dropped: false, error: 'The site could not be changed' }\n }\n}\n\n/**\n * The link that opens a site in preview on its own address (AGL-4086): a\n * two-minute enter link the console mints for the site's team, which plants\n * the preview cookie on that host and lands on `path`. `null` when it could\n * not be minted.\n */\nexport async function requestSitePreviewLink(\n user: Parameters<typeof authorizedFetch>[0],\n hostId: string,\n path = '/',\n): Promise<string | null> {\n try {\n const response = await authorizedFetch(user, '/api/site-preview/link', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ hostId, path }),\n })\n if (!response.ok) return null\n const payload = (await response.json().catch(() => null)) as { url?: unknown } | null\n return typeof payload?.url === 'string' ? payload.url : null\n } catch {\n return null\n }\n}\n\n/**\n * Open a site in preview in a new tab, entering it through the minted link.\n *\n * The tab is opened FIRST, inside the click, and pointed at the link once it\n * arrives: a tab opened after an await is a popup a browser blocks. When the\n * link cannot be minted the tab goes to `fallbackHref`, the site's ordinary\n * address, where the public answer is what anyone gets.\n */\nexport async function openSitePreview(\n user: Parameters<typeof authorizedFetch>[0],\n hostId: string,\n fallbackHref: string,\n path = '/',\n): Promise<void> {\n const tab = typeof window === 'undefined' ? null : window.open('', '_blank')\n const url = (await requestSitePreviewLink(user, hostId, path)) ?? fallbackHref\n if (!tab) {\n if (typeof window !== 'undefined') window.location.assign(url)\n return\n }\n // Severed before it navigates, as `rel=\"noopener\"` would have.\n tab.opener = null\n tab.location.href = url\n}\n\n/**\n * The site's preview state, live, and the setter. `inPreview` is `null` while\n * the host document loads, so a caller can hold its control rather than show\n * a state it has not read.\n */\nexport function useSitePreviewSwitch(hostId: string | null | undefined): {\n inPreview: boolean | null\n busy: boolean\n setPreview: (preview: boolean) => Promise<SitePreviewOutcome>\n} {\n const firestore = useFirestore()\n const { data: user } = useUser()\n const { data: host, status } = useFirestoreDoc<{ preview?: boolean }>(\n () => (hostId ? doc(firestore, 'hosts', hostId) : null),\n [firestore, hostId],\n )\n const [busy, setBusy] = useState(false)\n const setPreview = useCallback(\n async (preview: boolean): Promise<SitePreviewOutcome> => {\n if (!hostId) return { preview: !preview, dropped: false, error: 'No site' }\n setBusy(true)\n try {\n return await requestSitePreview(user, hostId, preview)\n } finally {\n setBusy(false)\n }\n },\n [hostId, user],\n )\n return {\n inPreview: status === 'success' ? isHostInPreview(host) : null,\n busy,\n setPreview,\n }\n}\n\nexport interface SitePreviewSwitchProps {\n hostId: string\n /**\n * The site's state as the caller reads it, `null` while it loads. Handed\n * in rather than read here, so the switch draws whatever host document the\n * page already holds and opens no second listener on it.\n */\n inPreview: boolean | null\n /** The signed-in user, whose ID token the door checks. */\n user: Parameters<typeof authorizedFetch>[0]\n /**\n * Shown off with the reason, never hidden, when the person cannot flip it\n * — the same rule every console prerequisite follows.\n */\n disabledReason?: string | null\n /** Told how each flip came out, for the caller's snackbar. */\n onChanged?: (outcome: SitePreviewOutcome, asked: boolean) => void\n}\n\n/** The words of the switch, shared with the native screen and the docs. */\nexport const SITE_PREVIEW_SWITCH_LABEL =\n 'Preview — only you and your team can see the site'\n\nexport const SITE_PREVIEW_SWITCH_HELP =\n 'Visitors see your 503 page, or \"Coming soon\", until you make the site public. ' +\n `You, your team and ${PLATFORM_BRAND_NAME} support see the whole site.`\n\n/** The switch, as Admin → Error pages draws it. */\nexport function SitePreviewSwitch(props: SitePreviewSwitchProps) {\n const { hostId, inPreview, user, disabledReason, onChanged } = props\n const [busy, setBusy] = useState(false)\n // What the person just asked for, shown until the host document catches up.\n const [asked, setAsked] = useState<boolean | null>(null)\n // Let go of it once the document agrees, so a later change from elsewhere\n // shows through.\n useEffect(() => {\n if (asked !== null && inPreview === asked) setAsked(null)\n }, [asked, inPreview])\n const checked = asked ?? inPreview === true\n const disabled = Boolean(disabledReason) || busy || inPreview === null\n return (\n <Stack spacing={0.5}>\n <FormControlLabel\n control={\n <Switch\n color=\"primary\"\n checked={checked}\n disabled={disabled}\n onChange={(event) => {\n const next = event.target.checked\n setAsked(next)\n setBusy(true)\n void requestSitePreview(user, hostId, next).then((outcome) => {\n setBusy(false)\n setAsked(outcome.error ? null : outcome.preview)\n onChanged?.(outcome, next)\n })\n }}\n />\n }\n label={SITE_PREVIEW_SWITCH_LABEL}\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {disabledReason || SITE_PREVIEW_SWITCH_HELP}\n </Typography>\n </Stack>\n )\n}\nSitePreviewSwitch.displayName = 'SitePreviewSwitch'\n\nexport default SitePreviewSwitch\n"],"names":["PLATFORM_BRAND_NAME","isHostInPreview","authorizedFetch","FormControlLabel","Stack","Switch","Typography","doc","useCallback","useEffect","useState","useFirestore","useUser","useFirestoreDoc","requestSitePreview","user","hostId","preview","response","method","headers","body","JSON","stringify","payload","json","catch","ok","dropped","error","String","requestSitePreviewLink","path","url","openSitePreview","fallbackHref","tab","window","open","location","assign","opener","href","useSitePreviewSwitch","firestore","data","host","status","busy","setBusy","setPreview","inPreview","SITE_PREVIEW_SWITCH_LABEL","SITE_PREVIEW_SWITCH_HELP","SitePreviewSwitch","props","disabledReason","onChanged","asked","setAsked","checked","disabled","Boolean","spacing","control","color","onChange","event","next","target","then","outcome","label","variant","displayName"],"mappings":"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"}
|
|
@@ -89,6 +89,9 @@ export declare const useHostRef: ({ hostId }: {
|
|
|
89
89
|
errorScreens?: Aglyn.HostErrorScreens;
|
|
90
90
|
authScreens?: import("@aglyn/aglyn/src/lib/foundation/definitions/platform.types").HostAuthScreens;
|
|
91
91
|
maintenance?: boolean;
|
|
92
|
+
preview?: boolean;
|
|
93
|
+
previewScreens?: Record<Aglyn.ScreenUid, boolean>;
|
|
94
|
+
publicSinceMs?: number;
|
|
92
95
|
suspendedAt?: number;
|
|
93
96
|
suspendedReasonCode?: string;
|
|
94
97
|
suspendedMessage?: string;
|