sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
492 lines (491 loc) • 20.3 kB
JavaScript
import { jsx, jsxs } from "react/jsx-runtime";
import { c } from "react/compiler-runtime";
import { useTranslation, useResourceCache, useActiveReleases, useClient, useFeatureEnabled, FEATURES, useUpsellData, UpsellDialog, isCardinalityOneRelease, useReleasesToolAvailable } from "./index2.js";
import { ReleasesMetadataProvider } from "./ReleasesMetadataProvider.js";
import { Stack, Text, useToast } from "@sanity/ui";
import { useState } from "react";
import { catchError, of, map, BehaviorSubject, switchMap, tap, timer, shareReplay, firstValueFrom } from "rxjs";
import { ReleasesUpsellContext } from "sanity/_singletons";
import "history";
import "@sanity/icons/ArrowLeft";
import "@sanity/icons/Launch";
import "styled-components";
import { Dialog } from "./index.js";
import "react-i18next";
import "@sanity/util/paths";
import "react-fast-compare";
import "@sanity/id-utils";
import "@sanity/icons/Document";
import "@sanity/icons/Image";
import "@sanity/uuid";
import "rxjs/operators";
import "react-rx";
import "@sanity/client/csm";
import "quick-lru";
import "lodash-es/throttle.js";
import "lodash-es/isEqual.js";
import "@sanity/color";
import "color2k";
import "lodash-es/isString.js";
import "@sanity/icons/Copy";
import "@sanity/icons/Clipboard";
import "./createAgentBundlesStore.js";
import "@sanity/telemetry/react";
import "./version.js";
import "@sanity/comlink";
import "@sanity/mutator";
import "lodash-es/omit.js";
import "@sanity/client";
import "lodash-es/groupBy.js";
import "@isaacs/ttlcache";
import "lodash-es/partition.js";
import "shallow-equals";
import "@sanity/schema";
import "lodash-es/get.js";
import "@sanity/types";
import "lodash-es/isPlainObject.js";
import "@sanity/util/client";
import "@sanity/util/concurrency-limiter";
import "lodash-es/flatten.js";
import "lodash-es/uniqBy.js";
import "json-reduce";
import "rxjs-exhaustmap-with-trailing";
import "@sanity/util/content";
import "dataloader";
import "raf";
import "groq-js";
import "mendoza";
import "@sanity/diff";
import "use-sync-external-store/with-selector";
import "@portabletext/editor";
import "@sanity/icons/ChevronRight";
import "@sanity/icons/Revert";
import "@sanity/ui/theme";
import "date-fns/differenceInDays";
import "date-fns/differenceInHours";
import "date-fns/differenceInMinutes";
import "date-fns/differenceInMonths";
import "date-fns/differenceInSeconds";
import "date-fns/differenceInWeeks";
import "date-fns/differenceInYears";
import "@sanity/icons/AddCircle";
import "@sanity/icons/Calendar";
import "@sanity/icons/Circle";
import "@sanity/icons/Close";
import "@sanity/icons/Edit";
import "@sanity/icons/Publish";
import "@sanity/icons/Timeline";
import "@sanity/icons/Trash";
import "@sanity/icons/Unpublish";
import "@sanity/icons/ArrowDown";
import "@sanity/icons/ArrowRight";
import "@sanity/util/legacyDateFormat";
import "@sanity/diff-patch";
import "@sanity/icons/ErrorOutline";
import "@sanity/icons/InfoOutline";
import "@sanity/icons/WarningOutline";
import "@portabletext/sanity-bridge";
import "motion/react";
import "lodash-es/pick.js";
import "@algorithm.ts/lcs";
import "lodash-es/intersection.js";
import "json-stable-stringify";
import "scroll-into-view-if-needed";
import "@sanity/schema/_internal";
import "lodash-es/sortBy.js";
import "lodash-es/compact.js";
import "lodash-es/keyBy.js";
import "lodash-es/toLower.js";
import "lodash-es/union.js";
import "lodash-es/uniq.js";
import "lodash-es/words.js";
import "lodash-es/isFinite.js";
import "@sanity/icons/Add";
import "observable-callback";
import "@sanity/icons/BulbOutline";
import "@sanity/icons/Unknown";
import "@sanity/icons/ToggleArrowRight";
import "@sanity/insert-menu";
import "@dnd-kit/sortable";
import "@sanity/icons/DragHandle";
import "@sanity/icons/AddDocument";
import "@sanity/icons/InsertAbove";
import "@sanity/icons/InsertBelow";
import "@tanstack/react-virtual";
import "lodash-es/startCase.js";
import "date-fns/getMinutes";
import "date-fns/parseISO";
import "date-fns/setMinutes";
import "@sanity/icons/EarthAmericas";
import "lodash-es/noop.js";
import "@sanity/icons/Search";
import "speakingurl";
import "@sanity/icons/Reset";
import "@sanity/icons/Sync";
import "@sanity/asset-utils";
import "@sanity/icons/AccessDenied";
import "@sanity/icons/HelpCircle";
import "@sanity/image-url";
import "react-is";
import "lodash-es/pickBy.js";
import "swr";
import "@sanity/icons/ChevronDown";
import "@sanity/icons/Download";
import "@sanity/icons/Upload";
import "@sanity/icons/BinaryDocument";
import "@sanity/icons/Lock";
import "sanity/router";
import "lodash-es/castArray.js";
import "nanoid";
import "use-hot-module-reload";
import "@sanity/icons/EllipsisHorizontal";
import "@sanity/icons/EyeOpen";
import "@sanity/icons/Link";
import "rxjs-mergemap-array";
import "react-focus-lock";
import "lodash-es/memoize.js";
import "lodash-es/sortedIndex.js";
import "lodash-es/difference.js";
import "lodash-es/identity.js";
import "lodash-es/values.js";
import "date-fns/addHours";
import "date-fns/startOfHour";
import "@sanity/icons/Bolt";
import "@sanity/icons/Clock";
import "@sanity/icons/Dot";
import "@sanity/icons/EarthGlobe";
import "date-fns/format";
import "date-fns/isValid";
import "date-fns/parse";
import "lodash-es/debounce.js";
import "clsx";
import "@portabletext/react";
import "@sanity/icons/LinkRemoved";
import "lodash-es/findIndex.js";
import "@sanity/diff-match-patch";
import "@sanity/icons";
import "@sanity/icons/ChevronUp";
import "@sanity/icons/Feedback";
import "@xstate/react";
import "xstate";
import "@sanity/icons/FaceHappy";
import "@sanity/icons/FaceIndifferent";
import "@sanity/icons/FaceSad";
import "@sentry/react";
import "date-fns/isBefore";
import "date-fns/startOfMinute";
import "@sanity/icons/Documents";
import "@portabletext/toolkit";
import "@sanity/icons/User";
import "@sanity/icons/CheckmarkCircle";
import "lodash-es/deburr.js";
import "@sanity/icons/ChevronLeft";
import "@sanity/icons/Checkmark";
import "@sanity/logos";
import "is-network-error";
import "lodash-es/escapeRegExp.js";
import "@sanity/icons/Desktop";
import "@sanity/icons/Moon";
import "@sanity/icons/Sun";
import "lodash-es/isEmpty.js";
import "react-dom/server";
import "@sanity/icons/Controls";
import "@sanity/icons/Sort";
import "lodash-es/capitalize.js";
import "@sanity/icons/Undo";
import "@sanity/icons/Menu";
import "@sanity/icons/AddUser";
import "@sanity/icons/Cog";
import "@sanity/icons/Users";
import "semver";
import "@sanity/icons/Github";
import "@sanity/icons/Refresh";
import "@sanity/icons/Leave";
import "lodash-es/last.js";
import "react-dom/client";
import "date-fns/addWeeks";
import "date-fns/isAfter";
function ReleaseLimitsMisconfigurationDialog(props) {
const $ = c(18), {
onClose
} = props, {
t
} = useTranslation();
let t0;
$[0] !== onClose ? (t0 = () => {
window.open("https://www.sanity.io/contact/support", "_blank", "noopener,noreferrer"), onClose();
}, $[0] = onClose, $[1] = t0) : t0 = $[1];
const handleContactSupport = t0;
let t1;
$[2] !== t ? (t1 = t("releases.upsell.misconfiguration.header"), $[2] = t, $[3] = t1) : t1 = $[3];
let t2;
$[4] !== t ? (t2 = t("releases.upsell.misconfiguration.contact-support"), $[4] = t, $[5] = t2) : t2 = $[5];
let t3;
$[6] !== handleContactSupport || $[7] !== t2 ? (t3 = {
confirmButton: {
text: t2,
onClick: handleContactSupport,
tone: "primary"
}
}, $[6] = handleContactSupport, $[7] = t2, $[8] = t3) : t3 = $[8];
let t4;
$[9] !== t ? (t4 = t("releases.upsell.misconfiguration.message"), $[9] = t, $[10] = t4) : t4 = $[10];
let t5;
$[11] !== t4 ? (t5 = /* @__PURE__ */ jsx(Stack, { space: 4, children: /* @__PURE__ */ jsx(Text, { children: t4 }) }), $[11] = t4, $[12] = t5) : t5 = $[12];
let t6;
return $[13] !== onClose || $[14] !== t1 || $[15] !== t3 || $[16] !== t5 ? (t6 = /* @__PURE__ */ jsx(Dialog, { id: "releases-misconfiguration-dialog", header: t1, width: 1, onClose, footer: t3, children: t5 }), $[13] = onClose, $[14] = t1, $[15] = t3, $[16] = t5, $[17] = t6) : t6 = $[17], t6;
}
function isErrorAtLimits(error) {
return error.statusCode === 403 && typeof error.response.body != "string" && "data" in error.response.body;
}
function fetchReleaseLimits(client, storeOrigin) {
const {
projectId
} = client.config();
return client.withConfig({
apiVersion: "vX"
}).observable.request({
uri: `projects/${projectId}/new-content-release-allowed`,
/**
* In a particular case when both releaseLimits
* and orgActiveReleaseCount stores are empty, 2 fetch calls are made
* to guard the create/archive/revert actions - browsers behave differently.
*
* Chromium based browsers will stall the second network request until the first
* resolves by attaching the store name to the tag, this spoofs 2 unique requests
* allowing both the fire in parallel.
*/
tag: `new-content-release-allowed-${storeOrigin}`
}).pipe(catchError((error) => {
if (console.error(error), isErrorAtLimits(error))
return of(error.response.body);
throw error;
}), map(({
data
}) => data));
}
const STATE_TTL_MS = 6e4, ORG_ACTIVE_RELEASE_COUNT_RESOURCE_CACHE_NAMESPACE = "orgActiveReleaseCount";
function createOrgActiveReleaseCountStore(client, activeReleasesCount) {
const latestFetchState = new BehaviorSubject(null), staleFlag$ = new BehaviorSubject(!1), activeReleaseCountAtFetch = new BehaviorSubject(null);
return {
orgActiveReleaseCount$: latestFetchState.pipe(switchMap((state) => state === null || // oxlint-disable-next-line no-unnecessary-boolean-literal-compare
staleFlag$.getValue() === !0 || activeReleaseCountAtFetch.getValue() !== activeReleasesCount ? (staleFlag$.next(!1), fetchReleaseLimits(client, "orgActiveReleaseCount").pipe(tap(() => activeReleaseCountAtFetch.next(activeReleasesCount)), map((data) => data.orgMeteredActiveReleaseCount), catchError((error) => {
if (console.error("Failed to fetch org release count", error), !state) throw error;
return of(state);
}), switchMap((nextState) => (latestFetchState.next(nextState), timer(STATE_TTL_MS).subscribe(() => {
staleFlag$.next(!0), activeReleaseCountAtFetch.next(null);
}), of(nextState))))) : of(state)))
};
}
const useOrgActiveReleaseCount = () => {
const $ = c(7), resourceCache = useResourceCache(), {
data: activeReleases
} = useActiveReleases();
let t0;
$[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (t0 = {
apiVersion: "v2025-02-19"
}, $[0] = t0) : t0 = $[0];
const client = useClient(t0), activeReleasesCount = activeReleases?.length || 0;
let t1;
$[1] !== activeReleasesCount ? (t1 = {
activeReleasesCount
}, $[1] = activeReleasesCount, $[2] = t1) : t1 = $[2];
const count = t1;
let releaseLimitsStore;
return $[3] !== client || $[4] !== count || $[5] !== resourceCache ? (releaseLimitsStore = resourceCache.get({
dependencies: [client, count],
namespace: ORG_ACTIVE_RELEASE_COUNT_RESOURCE_CACHE_NAMESPACE
}) || createOrgActiveReleaseCountStore(client, count.activeReleasesCount), resourceCache.set({
namespace: ORG_ACTIVE_RELEASE_COUNT_RESOURCE_CACHE_NAMESPACE,
value: releaseLimitsStore,
dependencies: [client, count]
}), $[3] = client, $[4] = count, $[5] = resourceCache, $[6] = releaseLimitsStore) : releaseLimitsStore = $[6], releaseLimitsStore;
}, RELEASE_LIMITS_RESOURCE_CACHE_NAMESPACE = "ReleaseLimits";
function createReleaseLimitsStore(client) {
return {
releaseLimits$: fetchReleaseLimits(client, "releaseLimits").pipe(map((data) => ({
datasetReleaseLimit: data.datasetReleaseLimit,
orgActiveReleaseLimit: data.orgActiveReleaseLimit
})), shareReplay(1), catchError((error) => (console.error("Failed to fetch release limits", error), of(null))))
};
}
const useReleaseLimits = () => {
const $ = c(4), resourceCache = useResourceCache();
let t0;
$[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (t0 = {
apiVersion: "v2025-02-19"
}, $[0] = t0) : t0 = $[0];
const client = useClient(t0);
let releaseLimitsStore;
return $[1] !== client || $[2] !== resourceCache ? (releaseLimitsStore = resourceCache.get({
dependencies: [client],
namespace: RELEASE_LIMITS_RESOURCE_CACHE_NAMESPACE
}) || createReleaseLimitsStore(client), resourceCache.set({
namespace: RELEASE_LIMITS_RESOURCE_CACHE_NAMESPACE,
value: releaseLimitsStore,
dependencies: [client]
}), $[1] = client, $[2] = resourceCache, $[3] = releaseLimitsStore) : releaseLimitsStore = $[3], releaseLimitsStore;
};
class StudioReleaseLimitExceededError extends Error {
constructor() {
super("StudioReleaseLimitExceeded"), this.name = "StudioReleaseLimitExceededError", this.details = {
type: "releaseLimitExceededError"
};
}
}
function ReleasesUpsellProvider(props) {
const $ = c(47), [upsellDialogOpen, setUpsellDialogOpen] = useState(!1), {
data: allActiveReleases
} = useActiveReleases(), {
enabled: isReleasesFeatureEnabled
} = useFeatureEnabled(FEATURES.contentReleases);
let t0;
$[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (t0 = {
dataUri: "/journey/content-releases",
feature: "content-releases"
}, $[0] = t0) : t0 = $[0];
const {
upsellData,
telemetryLogs,
hasError
} = useUpsellData(t0), toast = useToast(), {
t
} = useTranslation();
let t1;
$[1] !== allActiveReleases ? (t1 = allActiveReleases.filter(_temp), $[1] = allActiveReleases, $[2] = t1) : t1 = $[2];
const meteredActiveReleases = t1;
let t2;
bb0: {
if (!isReleasesFeatureEnabled) {
t2 = "upsell";
break bb0;
}
t2 = "default";
}
const mode = t2;
let t3;
$[3] !== telemetryLogs ? (t3 = () => {
telemetryLogs.dialogPrimaryClicked();
}, $[3] = telemetryLogs, $[4] = t3) : t3 = $[4];
const handlePrimaryButtonClick = t3;
let t4;
$[5] !== telemetryLogs ? (t4 = () => {
telemetryLogs.dialogSecondaryClicked();
}, $[5] = telemetryLogs, $[6] = t4) : t4 = $[6];
const handleSecondaryButtonClick = t4;
let t5;
$[7] !== telemetryLogs ? (t5 = () => {
setUpsellDialogOpen(!1), telemetryLogs.dialogDismissed();
}, $[7] = telemetryLogs, $[8] = t5) : t5 = $[8];
const handleClose = t5, [releaseLimit, setReleaseLimit] = useState(null), [showMisconfigurationDialog, setShowMisconfigurationDialog] = useState(!1);
let t6;
$[9] !== hasError || $[10] !== t || $[11] !== telemetryLogs || $[12] !== toast ? (t6 = (t72) => {
const source = t72 === void 0 ? "navbar" : t72;
if (hasError) {
toast.push({
status: "error",
title: t("errors.unable-to-perform-action"),
closable: !0
});
return;
}
setUpsellDialogOpen(!0), telemetryLogs.dialogViewed(source);
}, $[9] = hasError, $[10] = t, $[11] = telemetryLogs, $[12] = toast, $[13] = t6) : t6 = $[13];
const handleOpenDialog = t6, {
releaseLimits$
} = useReleaseLimits(), {
orgActiveReleaseCount$
} = useOrgActiveReleaseCount();
let t7;
$[14] !== allActiveReleases.length || $[15] !== handleOpenDialog || $[16] !== meteredActiveReleases.length || $[17] !== mode || $[18] !== orgActiveReleaseCount$ || $[19] !== releaseLimits$ ? (t7 = async (cb, t82, whenResolved) => {
const throwError = t82 === void 0 ? !1 : t82, doUpsell = () => {
if (handleOpenDialog(), throwError)
throw new StudioReleaseLimitExceededError();
return !1;
};
if (mode === "upsell")
return whenResolved?.(!1), doUpsell();
const result = await (async () => {
try {
return await Promise.all([firstValueFrom(orgActiveReleaseCount$), firstValueFrom(releaseLimits$.pipe(tap((limit) => setReleaseLimit(limit?.orgActiveReleaseLimit || null))))]);
} catch (t92) {
return console.error("Error fetching release limits and org count for upsell:", t92), null;
}
})();
if (result === null)
return whenResolved?.(!0), cb();
const [orgMeteredActiveReleaseCount, releaseLimits] = result;
if (releaseLimits === null || orgMeteredActiveReleaseCount === null)
return whenResolved?.(!0), cb();
const {
orgActiveReleaseLimit,
datasetReleaseLimit
} = releaseLimits;
if (orgActiveReleaseLimit === 0) {
if (whenResolved?.(!1), setShowMisconfigurationDialog(!0), throwError)
throw new StudioReleaseLimitExceededError();
return !1;
}
if (orgMeteredActiveReleaseCount === null)
return whenResolved?.(!0), cb();
const meteredActiveReleaseCount = meteredActiveReleases?.length || 0, isCurrentDatasetAtAboveDatasetLimit = (allActiveReleases?.length || 0) >= datasetReleaseLimit, isCurrentDatasetAtAboveOrgLimit = orgActiveReleaseLimit !== null && meteredActiveReleaseCount >= orgActiveReleaseLimit, isOrgAtAboveOrgLimit = orgActiveReleaseLimit !== null && orgMeteredActiveReleaseCount >= orgActiveReleaseLimit;
return isCurrentDatasetAtAboveDatasetLimit || isCurrentDatasetAtAboveOrgLimit || isOrgAtAboveOrgLimit ? (whenResolved?.(!1), doUpsell()) : (whenResolved?.(!0), cb());
}, $[14] = allActiveReleases.length, $[15] = handleOpenDialog, $[16] = meteredActiveReleases.length, $[17] = mode, $[18] = orgActiveReleaseCount$, $[19] = releaseLimits$, $[20] = t7) : t7 = $[20];
const guardWithReleaseLimitUpsell = t7;
let t8;
$[21] !== handleOpenDialog ? (t8 = (limit_0) => {
setReleaseLimit(limit_0), handleOpenDialog();
}, $[21] = handleOpenDialog, $[22] = t8) : t8 = $[22];
const onReleaseLimitReached = t8;
let t9;
$[23] !== guardWithReleaseLimitUpsell || $[24] !== handleOpenDialog || $[25] !== mode || $[26] !== onReleaseLimitReached || $[27] !== telemetryLogs || $[28] !== upsellData || $[29] !== upsellDialogOpen ? (t9 = {
mode,
upsellDialogOpen,
guardWithReleaseLimitUpsell,
onReleaseLimitReached,
telemetryLogs,
upsellData,
handleOpenDialog
}, $[23] = guardWithReleaseLimitUpsell, $[24] = handleOpenDialog, $[25] = mode, $[26] = onReleaseLimitReached, $[27] = telemetryLogs, $[28] = upsellData, $[29] = upsellDialogOpen, $[30] = t9) : t9 = $[30];
const ctxValue = t9;
let t10;
$[31] !== releaseLimit ? (t10 = releaseLimit === null ? void 0 : {
releaseLimit
}, $[31] = releaseLimit, $[32] = t10) : t10 = $[32];
const interpolation = t10;
let t11;
$[33] !== handleClose || $[34] !== handlePrimaryButtonClick || $[35] !== handleSecondaryButtonClick || $[36] !== interpolation || $[37] !== upsellData || $[38] !== upsellDialogOpen ? (t11 = {
data: upsellData,
open: upsellDialogOpen,
interpolation,
onClose: handleClose,
onPrimaryClick: handlePrimaryButtonClick,
onSecondaryClick: handleSecondaryButtonClick
}, $[33] = handleClose, $[34] = handlePrimaryButtonClick, $[35] = handleSecondaryButtonClick, $[36] = interpolation, $[37] = upsellData, $[38] = upsellDialogOpen, $[39] = t11) : t11 = $[39];
const dialogProps = t11;
let t12;
$[40] !== dialogProps || $[41] !== showMisconfigurationDialog ? (t12 = showMisconfigurationDialog ? /* @__PURE__ */ jsx(ReleaseLimitsMisconfigurationDialog, { onClose: () => setShowMisconfigurationDialog(!1) }) : /* @__PURE__ */ jsx(UpsellDialog, { ...dialogProps }), $[40] = dialogProps, $[41] = showMisconfigurationDialog, $[42] = t12) : t12 = $[42];
let t13;
return $[43] !== ctxValue || $[44] !== props.children || $[45] !== t12 ? (t13 = /* @__PURE__ */ jsxs(ReleasesUpsellContext.Provider, { value: ctxValue, children: [
props.children,
t12
] }), $[43] = ctxValue, $[44] = props.children, $[45] = t12, $[46] = t13) : t13 = $[46], t13;
}
function _temp(release) {
return !isCardinalityOneRelease(release);
}
function ReleasesStudioLayout(props) {
const $ = c(6);
if (!useReleasesToolAvailable()) {
let t02;
return $[0] !== props ? (t02 = props.renderDefault(props), $[0] = props, $[1] = t02) : t02 = $[1], t02;
}
let t0;
$[2] !== props ? (t0 = props.renderDefault(props), $[2] = props, $[3] = t0) : t0 = $[3];
let t1;
return $[4] !== t0 ? (t1 = /* @__PURE__ */ jsx(ReleasesUpsellProvider, { children: /* @__PURE__ */ jsx(ReleasesMetadataProvider, { children: t0 }) }), $[4] = t0, $[5] = t1) : t1 = $[5], t1;
}
export {
ReleasesStudioLayout
};
//# sourceMappingURL=ReleasesStudioLayout.js.map