UNPKG

sanity-plugin-media

Version:

This version of `sanity-plugin-media` is for Sanity Studio V3.

1,458 lines 218 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: !0 }); var jsxRuntime = require("react/jsx-runtime"), sanity = require("sanity"), icons = require("@sanity/icons"), ui = require("@sanity/ui"), react = require("react"), groq = require("groq"), reactRedux = require("react-redux"), toolkit = require("@reduxjs/toolkit"), nanoid = require("nanoid"), reduxObservable = require("redux-observable"), rxjs = require("rxjs"), operators$1 = require("rxjs/operators"), uuid = require("@sanity/uuid"), styledComponents = require("styled-components"), pluralize = require("pluralize"), reactNprogress = require("@tanem/react-nprogress"), color = require("@sanity/color"), Select = require("react-select"), reactVirtuoso = require("react-virtuoso"), zod = require("@hookform/resolvers/zod"), reactHookForm = require("react-hook-form"), z = require("zod"), format = require("date-fns/format"), filesize = require("filesize"), copy = require("copy-to-clipboard"), router = require("sanity/router"), reactFileIcon = require("react-file-icon"), CreatableSelect = require("react-select/creatable"), formatRelative = require("date-fns/formatRelative"), reactDropzone = require("react-dropzone"); function _interopDefaultCompat(e) { return e && typeof e == "object" && "default" in e ? e : { default: e }; } function _interopNamespaceCompat(e) { if (e && typeof e == "object" && "default" in e) return e; var n = /* @__PURE__ */ Object.create(null); return e && Object.keys(e).forEach(function(k) { if (k !== "default") { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: !0, get: function() { return e[k]; } }); } }), n.default = e, Object.freeze(n); } var groq__default = /* @__PURE__ */ _interopDefaultCompat(groq), pluralize__default = /* @__PURE__ */ _interopDefaultCompat(pluralize), Select__default = /* @__PURE__ */ _interopDefaultCompat(Select), z__namespace = /* @__PURE__ */ _interopNamespaceCompat(z), format__default = /* @__PURE__ */ _interopDefaultCompat(format), filesize__default = /* @__PURE__ */ _interopDefaultCompat(filesize), copy__default = /* @__PURE__ */ _interopDefaultCompat(copy), CreatableSelect__default = /* @__PURE__ */ _interopDefaultCompat(CreatableSelect), formatRelative__default = /* @__PURE__ */ _interopDefaultCompat(formatRelative); function getDefaultExportFromCjs(x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x.default : x; } var lib = {}, hasRequiredLib; function requireLib() { if (hasRequiredLib) return lib; hasRequiredLib = 1, Object.defineProperty(lib, "__esModule", { value: !0 }); for (var IS_MAC = typeof window < "u" && /Mac|iPod|iPhone|iPad/.test(window.navigator.platform), MODIFIERS = { alt: "altKey", control: "ctrlKey", meta: "metaKey", shift: "shiftKey" }, ALIASES = { add: "+", break: "pause", cmd: "meta", command: "meta", ctl: "control", ctrl: "control", del: "delete", down: "arrowdown", esc: "escape", ins: "insert", left: "arrowleft", mod: IS_MAC ? "meta" : "control", opt: "alt", option: "alt", return: "enter", right: "arrowright", space: " ", spacebar: " ", up: "arrowup", win: "meta", windows: "meta" }, CODES = { backspace: 8, tab: 9, enter: 13, shift: 16, control: 17, alt: 18, pause: 19, capslock: 20, escape: 27, " ": 32, pageup: 33, pagedown: 34, end: 35, home: 36, arrowleft: 37, arrowup: 38, arrowright: 39, arrowdown: 40, insert: 45, delete: 46, meta: 91, numlock: 144, scrolllock: 145, ";": 186, "=": 187, ",": 188, "-": 189, ".": 190, "/": 191, "`": 192, "[": 219, "\\": 220, "]": 221, "'": 222 }, f = 1; f < 20; f++) CODES["f" + f] = 111 + f; function isHotkey2(hotkey, options, event) { options && !("byKey" in options) && (event = options, options = null), Array.isArray(hotkey) || (hotkey = [hotkey]); var array = hotkey.map(function(string) { return parseHotkey(string, options); }), check = function(e) { return array.some(function(object) { return compareHotkey(object, e); }); }, ret = event == null ? check : check(event); return ret; } function isCodeHotkey(hotkey, event) { return isHotkey2(hotkey, event); } function isKeyHotkey(hotkey, event) { return isHotkey2(hotkey, { byKey: !0 }, event); } function parseHotkey(hotkey, options) { var byKey = options && options.byKey, ret = {}; hotkey = hotkey.replace("++", "+add"); var values = hotkey.split("+"), length = values.length; for (var k in MODIFIERS) ret[MODIFIERS[k]] = !1; var _iteratorNormalCompletion = !0, _didIteratorError = !1, _iteratorError = void 0; try { for (var _iterator = values[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = !0) { var value = _step.value, optional = value.endsWith("?") && value.length > 1; optional && (value = value.slice(0, -1)); var name = toKeyName(value), modifier = MODIFIERS[name]; if (value.length > 1 && !modifier && !ALIASES[value] && !CODES[name]) throw new TypeError('Unknown modifier: "' + value + '"'); (length === 1 || !modifier) && (byKey ? ret.key = name : ret.which = toKeyCode(value)), modifier && (ret[modifier] = optional ? null : !0); } } catch (err) { _didIteratorError = !0, _iteratorError = err; } finally { try { !_iteratorNormalCompletion && _iterator.return && _iterator.return(); } finally { if (_didIteratorError) throw _iteratorError; } } return ret; } function compareHotkey(object, event) { for (var key in object) { var expected = object[key], actual = void 0; if (expected != null && (key === "key" && event.key != null ? actual = event.key.toLowerCase() : key === "which" ? actual = expected === 91 && event.which === 93 ? 91 : event.which : actual = event[key], !(actual == null && expected === !1) && actual !== expected)) return !1; } return !0; } function toKeyCode(name) { name = toKeyName(name); var code = CODES[name] || name.toUpperCase().charCodeAt(0); return code; } function toKeyName(name) { return name = name.toLowerCase(), name = ALIASES[name] || name, name; } return lib.default = isHotkey2, lib.isHotkey = isHotkey2, lib.isCodeHotkey = isCodeHotkey, lib.isKeyHotkey = isKeyHotkey, lib.parseHotkey = parseHotkey, lib.compareHotkey = compareHotkey, lib.toKeyCode = toKeyCode, lib.toKeyName = toKeyName, lib; } var libExports = requireLib(), isHotkey = /* @__PURE__ */ getDefaultExportFromCjs(libExports); const useKeyPress = (hotkey, onPress) => { const keyPressed = react.useRef(!1), downHandler = react.useCallback( (e) => { isHotkey(hotkey, e) && (keyPressed.current = !0, onPress && onPress()); }, [hotkey, onPress] ), upHandler = react.useCallback(() => { keyPressed.current = !1; }, []); return react.useEffect(() => (window.addEventListener("keydown", downHandler), window.addEventListener("keyup", upHandler), () => { window.removeEventListener("keydown", downHandler), window.removeEventListener("keyup", upHandler); }), [downHandler, upHandler]), keyPressed; }, divider = { type: "divider" }, inputs = { altText: { assetTypes: ["file", "image"], field: "altText", name: "altText", operatorType: "empty", operatorTypes: ["empty", "notEmpty", null, "includes", "doesNotInclude"], title: "Alt text", type: "string", value: "" }, creditLine: { assetTypes: ["file", "image"], field: "creditLine", name: "creditLine", operatorType: "empty", operatorTypes: ["empty", "notEmpty", null, "includes", "doesNotInclude"], title: "Credit", type: "string", value: "" }, description: { assetTypes: ["file", "image"], field: "description", name: "description", operatorType: "empty", operatorTypes: ["empty", "notEmpty", null, "includes", "doesNotInclude"], title: "Description", type: "string", value: "" }, fileName: { assetTypes: ["file", "image"], field: "originalFilename", name: "filename", operatorType: "includes", operatorTypes: ["includes", "doesNotInclude"], title: "File name", type: "string", value: "" }, height: { assetTypes: ["image"], field: "metadata.dimensions.height", name: "height", operatorType: "greaterThan", operatorTypes: [ "greaterThan", "greaterThanOrEqualTo", "lessThan", "lessThanOrEqualTo", null, "equalTo" ], title: "Height", type: "number", value: 400 }, inCurrentDocument: { assetTypes: ["file", "image"], name: "inCurrentDocument", operatorType: "is", options: [ { name: "true", title: "True", value: groq__default.default`_id in $documentAssetIds` }, { name: "false", title: "False", value: groq__default.default`!(_id in $documentAssetIds)` } ], selectOnly: !0, title: "In use in current document", type: "select", value: "true" }, inUse: { assetTypes: ["file", "image"], name: "inUse", operatorType: "is", options: [ { name: "true", title: "True", value: groq__default.default`count(*[references(^._id)]) > 0` }, { name: "false", title: "False", value: groq__default.default`count(*[references(^._id)]) == 0` } ], title: "In use", type: "select", value: "true" }, isOpaque: { assetTypes: ["image"], field: "metadata.isOpaque", name: "isOpaque", operatorType: "equalTo", options: [ { name: "true", title: "True", value: "false" }, { name: "false", title: "False", value: "true" } ], title: "Has transparency", type: "select", value: "true" }, orientation: { assetTypes: ["image"], name: "orientation", operatorType: "is", operatorTypes: ["is", "isNot"], options: [ { name: "portrait", title: "Portrait", value: "metadata.dimensions.aspectRatio < 1" }, { name: "landscape", title: "Landscape", value: "metadata.dimensions.aspectRatio > 1" }, { name: "square", title: "Square", value: "metadata.dimensions.aspectRatio == 1" } ], title: "Orientation", type: "select", value: "portrait" }, size: { assetTypes: ["file", "image"], field: "size", modifier: "kb", modifiers: [ { name: "kb", title: "KB", fieldModifier: (fieldName) => `round(${fieldName} / 1000)` }, { name: "mb", title: "MB", fieldModifier: (fieldName) => `round(${fieldName} / 1000000)` } ], name: "size", operatorType: "greaterThan", operatorTypes: [ "greaterThan", "greaterThanOrEqualTo", "lessThan", "lessThanOrEqualTo", null, "equalTo" ], title: "File size", type: "number", value: 0 }, tag: { assetTypes: ["file", "image"], field: "opt.media.tags", name: "tag", operatorType: "references", operatorTypes: ["references", "doesNotReference", null, "empty", "notEmpty"], title: "Tags", type: "searchable" }, title: { assetTypes: ["file", "image"], field: "title", name: "title", operatorType: "empty", operatorTypes: ["empty", "notEmpty", null, "includes", "doesNotInclude"], title: "Title", type: "string", value: "" }, type: { assetTypes: ["file", "image"], name: "type", operatorType: "is", operatorTypes: ["is", "isNot"], options: [ { name: "image", title: "Image", value: 'mimeType match "image*"' }, { name: "video", title: "Video", value: 'mimeType match "video*"' }, { name: "audio", title: "Audio", value: 'mimeType match "audio*"' }, { name: "pdf", title: "PDF", value: 'mimeType == "application/pdf"' } ], title: "File type", type: "select", value: "image" }, width: { assetTypes: ["image"], field: "metadata.dimensions.width", name: "width", operatorType: "greaterThan", operatorTypes: [ "greaterThan", "greaterThanOrEqualTo", "lessThan", "lessThanOrEqualTo", null, "equalTo" ], title: "Width", type: "number", value: 400 } }, operators = { doesNotInclude: { fn: (value, field) => value ? `!(${field} match '*${value}*')` : void 0, label: "does not include" }, doesNotReference: { fn: (value, _field) => value ? `!references('${value}')` : void 0, label: "does not include" }, empty: { fn: (_value, field) => `!defined(${field})`, hideInput: !0, label: "is empty" }, equalTo: { fn: (value, field) => value ? `${field} == ${value}` : void 0, label: "is equal to" }, greaterThan: { fn: (value, field) => value ? `${field} > ${value}` : void 0, label: "is greater than" }, greaterThanOrEqualTo: { fn: (value, field) => value ? `${field} >= ${value}` : void 0, label: "is greater than or equal to" }, includes: { fn: (value, field) => value ? `${field} match '*${value}*'` : void 0, label: "includes" }, is: { fn: (value, _field) => `${value}`, label: "is" }, isNot: { fn: (value, _field) => `!(${value})`, label: "is not" }, lessThan: { fn: (value, field) => value ? `${field} < ${value}` : void 0, label: "is less than" }, lessThanOrEqualTo: { fn: (value, field) => value ? `${field} <= ${value}` : void 0, label: "is less than or equal to" }, notEmpty: { fn: (_value, field) => `defined(${field})`, hideInput: !0, label: "is not empty" }, references: { fn: (value, _field) => value ? `references('${value}')` : void 0, label: "includes" } }, ORDER_OPTIONS = [ { direction: "desc", field: "_createdAt" }, { direction: "asc", field: "_createdAt" }, // Divider null, { direction: "desc", field: "_updatedAt" }, { direction: "asc", field: "_updatedAt" }, // Divider null, { direction: "asc", field: "originalFilename" }, { direction: "desc", field: "originalFilename" }, // Divider null, { direction: "desc", field: "size" }, { direction: "asc", field: "size" } ], FACETS = [ inputs.tag, divider, inputs.inUse, inputs.inCurrentDocument, divider, inputs.title, inputs.altText, inputs.creditLine, inputs.description, divider, inputs.isOpaque, divider, inputs.fileName, inputs.size, inputs.type, divider, inputs.orientation, inputs.width, inputs.height ], GRID_TEMPLATE_COLUMNS = { SMALL: "3rem 100px auto 1.5rem", LARGE: "3rem 100px auto 5.5rem 5.5rem 3.5rem 8.5rem 4.75rem 2rem" }, PANEL_HEIGHT = 32, TAG_DOCUMENT_NAME = "media.tag", TAGS_PANEL_WIDTH = 250, AssetSourceDispatchContext = react.createContext(void 0), AssetBrowserDispatchProvider = (props) => { const { children, onSelect } = props, contextValue = { onSelect }; return /* @__PURE__ */ jsxRuntime.jsx(AssetSourceDispatchContext.Provider, { value: contextValue, children }); }, useAssetSourceActions = () => { const context = react.useContext(AssetSourceDispatchContext); if (context === void 0) throw new Error("useAssetSourceActions must be used within an AssetSourceDispatchProvider"); return context; }, useVersionedClient = () => sanity.useClient({ apiVersion: "2022-10-01" }), ORDER_DICTIONARY = { _createdAt: { asc: "Last created: Oldest first", desc: "Last created: Newest first" }, _updatedAt: { asc: "Last updated: Oldest first", desc: "Last updated: Newest first" }, mimeType: { asc: "MIME type: A to Z", desc: "MIME type: Z to A" }, originalFilename: { asc: "File name: A to Z", desc: "File name: Z to A" }, size: { asc: "File size: Smallest first", desc: "File size: Largest first" } }, getOrderTitle = (field, direction) => ORDER_DICTIONARY[field][direction], debugThrottle = (throttled) => function(source) { return rxjs.iif( () => !!throttled, source.pipe( operators$1.delay(3e3), operators$1.mergeMap((v) => Math.random() > 0.5 ? rxjs.throwError({ message: "Test error", statusCode: 500 }) : rxjs.of(v)) ), source ); }, constructFilter = ({ assetTypes, searchFacets, searchQuery }) => { const documentAssetTypes = assetTypes.map((type) => `sanity.${type}Asset`), baseFilter = groq__default.default` _type in ${JSON.stringify(documentAssetTypes)} && !(_id in path("drafts.**")) `, searchFacetFragments = searchFacets.reduce((acc, facet) => { if (facet.type === "number") { const { field, modifier, modifiers, operatorType, value } = facet, operator = operators[operatorType], currentModifier = modifiers?.find((m) => m.name === modifier), facetField = currentModifier?.fieldModifier ? currentModifier.fieldModifier(field) : field, fragment = operator.fn(value, facetField); fragment && acc.push(fragment); } if (facet.type === "searchable") { const { field, operatorType, value } = facet, fragment = operators[operatorType].fn(value?.value, field); fragment && acc.push(fragment); } if (facet.type === "select") { const { field, operatorType, options, value } = facet, operator = operators[operatorType], currentOptionValue = options?.find((l) => l.name === value)?.value, fragment = operator.fn(currentOptionValue, field); fragment && acc.push(fragment); } if (facet.type === "string") { const { field, operatorType, value } = facet, fragment = operators[operatorType].fn(value, field); fragment && acc.push(fragment); } return acc; }, []); return [ // Base filter baseFilter, // Search query (if present) // NOTE: Currently this only searches direct fields on sanity.fileAsset/sanity.imageAsset and NOT referenced tags // It's possible to add this by adding the following line to the searchQuery, but it's quite slow // references(*[_type == "media.tag" && name.current == "${searchQuery.trim()}"]._id) ...searchQuery ? [ groq__default.default`[_id, altText, assetId, creditLine, description, originalFilename, title, url] match '*${searchQuery.trim()}*'` ] : [], // Search facets ...searchFacetFragments ].join(" && "); }, checkTagName = (client, name) => function(source) { return source.pipe( operators$1.mergeMap(() => rxjs.from( client.fetch(groq__default.default`count(*[_type == "${TAG_DOCUMENT_NAME}" && name.current == $name])`, { name }) )), operators$1.mergeMap((existingTagCount) => existingTagCount > 0 ? rxjs.throwError({ message: "Tag already exists", statusCode: 409 }) : rxjs.of(!0)) ); }, getTagSelectOptions = (tags) => tags.reduce((acc, val) => { const tag = val?.tag; return tag && acc.push({ label: tag?.name?.current, value: tag?._id }), acc; }, []), ASSETS_ACTIONS = { tagsAddComplete: toolkit.createAction( "actions/tagsAddComplete", function({ assets, tag }) { return { payload: { assets, tag } }; } ), tagsAddError: toolkit.createAction( "actions/tagsAddError", function({ assets, error, tag }) { return { payload: { assets, error, tag } }; } ), tagsAddRequest: toolkit.createAction( "actions/tagsAddRequest", function({ assets, tag }) { return { payload: { assets, tag } }; } ), tagsRemoveComplete: toolkit.createAction( "actions/tagsRemoveComplete", function({ assets, tag }) { return { payload: { assets, tag } }; } ), tagsRemoveError: toolkit.createAction( "actions/tagsRemoveError", function({ assets, error, tag }) { return { payload: { assets, error, tag } }; } ), tagsRemoveRequest: toolkit.createAction( "actions/tagsRemoveRequest", function({ assets, tag }) { return { payload: { assets, tag } }; } ) }, DIALOG_ACTIONS = { showTagCreate: toolkit.createAction("dialog/showTagCreate"), showTagEdit: toolkit.createAction("dialog/showTagEdit", function({ tagId }) { return { payload: { tagId } }; }) }, initialState$7 = { allIds: [], byIds: {}, creating: !1, creatingError: void 0, fetchCount: -1, fetching: !1, fetchingError: void 0, panelVisible: !0 }, tagsSlice = toolkit.createSlice({ name: "tags", initialState: initialState$7, extraReducers: (builder) => { builder.addCase(DIALOG_ACTIONS.showTagCreate, (state) => { delete state.creatingError; }).addCase(DIALOG_ACTIONS.showTagEdit, (state, action) => { const { tagId } = action.payload; delete state.byIds[tagId].error; }).addMatcher( toolkit.isAnyOf( ASSETS_ACTIONS.tagsAddComplete, ASSETS_ACTIONS.tagsAddError, ASSETS_ACTIONS.tagsRemoveComplete, ASSETS_ACTIONS.tagsRemoveError ), (state, action) => { const { tag } = action.payload; state.byIds[tag._id].updating = !1; } ).addMatcher( toolkit.isAnyOf(ASSETS_ACTIONS.tagsAddRequest, ASSETS_ACTIONS.tagsRemoveRequest), (state, action) => { const { tag } = action.payload; state.byIds[tag._id].updating = !0; } ); }, reducers: { createComplete(state, action) { const { tag } = action.payload; state.creating = !1, state.allIds.includes(tag._id) || state.allIds.push(tag._id), state.byIds[tag._id] = { _type: "tag", picked: !1, tag, updating: !1 }; }, createError(state, action) { state.creating = !1, state.creatingError = action.payload.error; }, createRequest(state, _action) { state.creating = !0, delete state.creatingError; }, deleteComplete(state, action) { const { tagId } = action.payload, deleteIndex = state.allIds.indexOf(tagId); deleteIndex >= 0 && state.allIds.splice(deleteIndex, 1), delete state.byIds[tagId]; }, deleteError(state, action) { const { error, tag } = action.payload, tagId = tag?._id; state.byIds[tagId].error = error, state.byIds[tagId].updating = !1; }, deleteRequest(state, action) { const tagId = action.payload?.tag?._id; state.byIds[tagId].picked = !1, state.byIds[tagId].updating = !0, Object.keys(state.byIds).forEach((key) => { delete state.byIds[key].error; }); }, fetchComplete(state, action) { const { tags } = action.payload; tags?.forEach((tag) => { state.allIds.push(tag._id), state.byIds[tag._id] = { _type: "tag", picked: !1, tag, updating: !1 }; }), state.fetching = !1, state.fetchCount = tags.length || 0, delete state.fetchingError; }, fetchError(state, action) { const { error } = action.payload; state.fetching = !1, state.fetchingError = error; }, fetchRequest: { reducer: (state, _action) => { state.fetching = !0, delete state.fetchingError; }, prepare: () => ({ payload: { query: groq__default.default` { "items": *[ _type == "${TAG_DOCUMENT_NAME}" && !(_id in path("drafts.**")) ] { _createdAt, _updatedAt, _id, _rev, _type, name } | order(name.current asc), } ` } }) }, // Queue batch tag creation listenerCreateQueue(_state, _action) { }, // Apply created tags (via sanity real-time events) listenerCreateQueueComplete(state, action) { const { tags } = action.payload; tags?.forEach((tag) => { state.byIds[tag._id] = { _type: "tag", picked: !1, tag, updating: !1 }, state.allIds.includes(tag._id) || state.allIds.push(tag._id); }); }, // Queue batch tag deletion listenerDeleteQueue(_state, _action) { }, // Apply deleted tags (via sanity real-time events) listenerDeleteQueueComplete(state, action) { const { tagIds } = action.payload; tagIds?.forEach((tagId) => { const deleteIndex = state.allIds.indexOf(tagId); deleteIndex >= 0 && state.allIds.splice(deleteIndex, 1), delete state.byIds[tagId]; }); }, // Queue batch tag updates listenerUpdateQueue(_state, _action) { }, // Apply updated tags (via sanity real-time events) listenerUpdateQueueComplete(state, action) { const { tags } = action.payload; tags?.forEach((tag) => { state.byIds[tag._id] && (state.byIds[tag._id].tag = tag); }); }, // Set tag panel visibility panelVisibleSet(state, action) { const { panelVisible } = action.payload; state.panelVisible = panelVisible; }, // Sort all tags by name sort(state) { state.allIds.sort((a, b) => { const tagA = state.byIds[a].tag.name.current, tagB = state.byIds[b].tag.name.current; return tagA < tagB ? -1 : tagA > tagB ? 1 : 0; }); }, updateComplete(state, action) { const { tag } = action.payload; state.byIds[tag._id].tag = tag, state.byIds[tag._id].updating = !1; }, updateError(state, action) { const { error, tag } = action.payload, tagId = tag?._id; state.byIds[tagId].error = error, state.byIds[tagId].updating = !1; }, updateRequest(state, action) { const { tag } = action.payload; state.byIds[tag?._id].updating = !0; } } }), tagsCreateEpic = (action$, state$, { client }) => action$.pipe( operators$1.filter(tagsSlice.actions.createRequest.match), operators$1.withLatestFrom(state$), operators$1.mergeMap(([action, state]) => { const { assetId, name } = action.payload; return rxjs.of(action).pipe( debugThrottle(state.debug.badConnection), checkTagName(client, name), operators$1.mergeMap( () => client.observable.create({ _type: TAG_DOCUMENT_NAME, name: { _type: "slug", current: name } }) ), operators$1.mergeMap((result) => rxjs.of(tagsSlice.actions.createComplete({ assetId, tag: result }))), operators$1.catchError( (error) => rxjs.of( tagsSlice.actions.createError({ error: { message: error?.message || "Internal error", statusCode: error?.statusCode || 500 }, name }) ) ) ); }) ), tagsDeleteEpic = (action$, state$, { client }) => action$.pipe( operators$1.filter(tagsSlice.actions.deleteRequest.match), operators$1.withLatestFrom(state$), operators$1.mergeMap(([action, state]) => { const { tag } = action.payload; return rxjs.of(action).pipe( // Optionally throttle debugThrottle(state.debug.badConnection), // Fetch assets which reference this tag operators$1.mergeMap( () => client.observable.fetch( groq__default.default`*[ _type in ["sanity.fileAsset", "sanity.imageAsset"] && references(*[_type == "media.tag" && name.current == $tagName]._id) ] { _id, _rev, opt }`, { tagName: tag.name.current } ) ), // Create transaction which remove tag references from all matched assets and delete tag operators$1.mergeMap((assets) => { const transaction = assets.map((asset) => ({ id: asset._id, patch: { // this will cause the transaction to fail if the document has been modified since it was fetched. ifRevisionID: asset._rev, unset: [`opt.media.tags[_ref == "${tag._id}"]`] } })).reduce( (tx, patch) => tx.patch(patch.id, patch.patch), client.transaction() ); return transaction.delete(tag._id), rxjs.from(transaction.commit()); }), // Dispatch complete action operators$1.mergeMap(() => rxjs.of(tagsSlice.actions.deleteComplete({ tagId: tag._id }))), operators$1.catchError( (error) => rxjs.of( tagsSlice.actions.deleteError({ error: { message: error?.message || "Internal error", statusCode: error?.statusCode || 500 }, tag }) ) ) ); }) ), tagsFetchEpic = (action$, state$, { client }) => action$.pipe( operators$1.filter(tagsSlice.actions.fetchRequest.match), operators$1.withLatestFrom(state$), operators$1.switchMap(([action, state]) => { const { query } = action.payload; return rxjs.of(action).pipe( // Optionally throttle debugThrottle(state.debug.badConnection), // Fetch tags operators$1.mergeMap( () => client.observable.fetch(query) ), // Dispatch complete action operators$1.mergeMap((result) => { const { items } = result; return rxjs.of(tagsSlice.actions.fetchComplete({ tags: items })); }), operators$1.catchError( (error) => rxjs.of( tagsSlice.actions.fetchError({ error: { message: error?.message || "Internal error", statusCode: error?.statusCode || 500 } }) ) ) ); }) ), tagsListenerCreateQueueEpic = (action$) => action$.pipe( operators$1.filter(tagsSlice.actions.listenerCreateQueue.match), operators$1.bufferTime(2e3), operators$1.filter((actions) => actions.length > 0), operators$1.mergeMap((actions) => { const tags = actions?.map((action) => action.payload.tag); return rxjs.of(tagsSlice.actions.listenerCreateQueueComplete({ tags })); }) ), tagsListenerDeleteQueueEpic = (action$) => action$.pipe( operators$1.filter(tagsSlice.actions.listenerDeleteQueue.match), operators$1.bufferTime(2e3), operators$1.filter((actions) => actions.length > 0), operators$1.mergeMap((actions) => { const tagIds = actions?.map((action) => action.payload.tagId); return rxjs.of(tagsSlice.actions.listenerDeleteQueueComplete({ tagIds })); }) ), tagsListenerUpdateQueueEpic = (action$) => action$.pipe( operators$1.filter(tagsSlice.actions.listenerUpdateQueue.match), operators$1.bufferTime(2e3), operators$1.filter((actions) => actions.length > 0), operators$1.mergeMap((actions) => { const tags = actions?.map((action) => action.payload.tag); return rxjs.of(tagsSlice.actions.listenerUpdateQueueComplete({ tags })); }) ), tagsSortEpic = (action$) => action$.pipe( reduxObservable.ofType( tagsSlice.actions.listenerCreateQueueComplete.type, tagsSlice.actions.listenerUpdateQueueComplete.type ), operators$1.bufferTime(1e3), operators$1.filter((actions) => actions.length > 0), operators$1.mergeMap(() => rxjs.of(tagsSlice.actions.sort())) ), tagsUpdateEpic = (action$, state$, { client }) => action$.pipe( operators$1.filter(tagsSlice.actions.updateRequest.match), operators$1.withLatestFrom(state$), operators$1.mergeMap(([action, state]) => { const { closeDialogId, formData, tag } = action.payload; return rxjs.of(action).pipe( // Optionally throttle debugThrottle(state.debug.badConnection), // Check if tag name is available, throw early if not checkTagName(client, formData?.name?.current), // Patch document (Update tag) operators$1.mergeMap( () => rxjs.from( client.patch(tag._id).set({ name: { _type: "slug", current: formData?.name.current } }).commit() ) ), // Dispatch complete action operators$1.mergeMap((updatedTag) => rxjs.of( tagsSlice.actions.updateComplete({ closeDialogId, tag: updatedTag }) )), operators$1.catchError( (error) => rxjs.of( tagsSlice.actions.updateError({ error: { message: error?.message || "Internal error", statusCode: error?.statusCode || 500 }, tag }) ) ) ); }) ), selectTagsByIds = (state) => state.tags.byIds, selectTagsAllIds = (state) => state.tags.allIds, selectTags = toolkit.createSelector( [selectTagsByIds, selectTagsAllIds], (byIds, allIds) => allIds.map((id) => byIds[id]) ), selectTagById = toolkit.createSelector( [selectTagsByIds, (_state, tagId) => tagId], (byIds, tagId) => byIds[tagId] ), selectTagSelectOptions = (asset) => (state) => { const tags = asset?.opt?.media?.tags?.reduce((acc, v) => { const tagItem = state.tags.byIds[v._ref]; return tagItem?.tag && acc.push(tagItem), acc; }, []); return tags && tags?.length > 0 ? getTagSelectOptions(tags) : null; }, tagsActions = { ...tagsSlice.actions }; var tagsReducer = tagsSlice.reducer; const initialState$6 = { facets: [], query: "" }, searchSlice = toolkit.createSlice({ name: "search", initialState: initialState$6, reducers: { // Add search facet facetsAdd(state, action) { state.facets.push({ ...action.payload.facet, id: uuid.uuid() }); }, // Clear all search facets facetsClear(state) { state.facets = []; }, // Remove search facet by name facetsRemoveByName(state, action) { state.facets = state.facets.filter((facet) => facet.name !== action.payload.facetName); }, // Remove search facet by name facetsRemoveByTag(state, action) { state.facets = state.facets.filter( (facet) => !(facet.name === "tag" && facet.type === "searchable" && (facet.operatorType === "references" || facet.operatorType === "doesNotReference") && facet.value?.value === action.payload.tagId) ); }, // Remove search facet by name facetsRemoveById(state, action) { state.facets = state.facets.filter((facet) => facet.id !== action.payload.facetId); }, // Update an existing search facet facetsUpdate(state, action) { const { modifier, name, operatorType, value } = action.payload, facet = state.facets.find((f) => f.name === name); facet && (facet.type === "number" && modifier && (facet.modifier = modifier), operatorType && (facet.operatorType = operatorType), typeof value < "u" && (facet.value = value), state.facets = state.facets.filter((f) => f.name !== facet.name || f.id === facet.id)); }, // Update an existing search facet facetsUpdateById(state, action) { const { modifier, id, operatorType, value } = action.payload; state.facets.forEach((facet, index) => { facet.id === id && (facet.type === "number" && modifier && (facet.modifier = modifier), operatorType && (facet.operatorType = operatorType), typeof value < "u" && (state.facets[index].value = value)); }); }, // Update existing search query querySet(state, action) { state.query = action.payload?.searchQuery; } } }), searchFacetTagUpdateEpic = (action$, state$) => action$.pipe( operators$1.filter(tagsActions.updateComplete.match), operators$1.withLatestFrom(state$), operators$1.mergeMap(([action, state]) => { const { tag } = action.payload, currentSearchFacetTag = state.search.facets?.find((facet) => facet.name === "tag"), tagItem = state.tags.byIds[tag._id]; return currentSearchFacetTag?.type === "searchable" && currentSearchFacetTag.value?.value === tag._id ? rxjs.of( searchSlice.actions.facetsUpdate({ name: "tag", value: { label: tagItem?.tag?.name?.current, value: tagItem?.tag?._id } }) ) : rxjs.EMPTY; }) ), selectIsSearchFacetTag = toolkit.createSelector( [ (state) => state.search.facets, (_state, tagId) => tagId ], (searchFacets, tagId) => searchFacets.some( (facet) => facet.name === "tag" && facet.type === "searchable" && (facet.operatorType === "references" || facet.operatorType === "doesNotReference") && facet.value?.value === tagId ) ), searchActions = { ...searchSlice.actions }; var searchReducer = searchSlice.reducer; const UPLOADS_ACTIONS = { uploadComplete: toolkit.createAction( "uploads/uploadComplete", function({ asset }) { return { payload: { asset } }; } ) }, defaultOrder = ORDER_OPTIONS[0], initialState$5 = { allIds: [], assetTypes: [], byIds: {}, fetchCount: -1, fetching: !1, fetchingError: void 0, lastPicked: void 0, order: { direction: defaultOrder.direction, field: defaultOrder.field, title: getOrderTitle(defaultOrder.field, defaultOrder.direction) }, pageIndex: 0, pageSize: 100, // totalCount: -1, view: "grid" }, assetsSlice = toolkit.createSlice({ name: "assets", initialState: initialState$5, extraReducers: (builder) => { builder.addCase(UPLOADS_ACTIONS.uploadComplete, (state, action) => { const { asset } = action.payload; state.byIds[asset._id] = { _type: "asset", asset, picked: !1, updating: !1 }; }).addCase(ASSETS_ACTIONS.tagsAddComplete, (state, action) => { const { assets } = action.payload; assets.forEach((asset) => { state.byIds[asset.asset._id].updating = !1; }); }).addCase(ASSETS_ACTIONS.tagsAddError, (state, action) => { const { assets } = action.payload; assets.forEach((asset) => { state.byIds[asset.asset._id].updating = !1; }); }).addCase(ASSETS_ACTIONS.tagsAddRequest, (state, action) => { const { assets } = action.payload; assets.forEach((asset) => { state.byIds[asset.asset._id].updating = !0; }); }).addCase(ASSETS_ACTIONS.tagsRemoveComplete, (state, action) => { const { assets } = action.payload; assets.forEach((asset) => { state.byIds[asset.asset._id].updating = !1; }); }).addCase(ASSETS_ACTIONS.tagsRemoveError, (state, action) => { const { assets } = action.payload; assets.forEach((asset) => { state.byIds[asset.asset._id].updating = !1; }); }).addCase(ASSETS_ACTIONS.tagsRemoveRequest, (state, action) => { const { assets } = action.payload; assets.forEach((asset) => { state.byIds[asset.asset._id].updating = !0; }); }); }, reducers: { // Clear asset order clear(state) { state.allIds = []; }, // Remove assets and update page index deleteComplete(state, action) { const { assetIds } = action.payload; assetIds?.forEach((id) => { const deleteIndex = state.allIds.indexOf(id); deleteIndex >= 0 && state.allIds.splice(deleteIndex, 1), delete state.byIds[id]; }), state.pageIndex = Math.floor(state.allIds.length / state.pageSize) - 1; }, deleteError(state, action) { const { assetIds, error } = action.payload, itemErrors = error?.response?.body?.error?.items?.map( (item) => item.error ); assetIds?.forEach((id) => { state.byIds[id].updating = !1; }), itemErrors?.forEach((item) => { state.byIds[item.id].error = item.description; }); }, deleteRequest(state, action) { const { assets } = action.payload; assets.forEach((asset) => { state.byIds[asset?._id].updating = !0; }), Object.keys(state.byIds).forEach((key) => { delete state.byIds[key].error; }); }, fetchComplete(state, action) { const assets = action.payload?.assets || []; assets && assets.forEach((asset) => { state.allIds.includes(asset._id) || state.allIds.push(asset._id), state.byIds[asset._id] = { _type: "asset", asset, picked: !1, updating: !1 }; }), state.fetching = !1, state.fetchCount = assets.length || 0, delete state.fetchingError; }, fetchError(state, action) { const error = action.payload; state.fetching = !1, state.fetchingError = error; }, fetchRequest: { reducer: (state, _action) => { state.fetching = !0, delete state.fetchingError; }, prepare: ({ params = {}, queryFilter, selector = "", sort = groq__default.default`order(_updatedAt desc)` }) => { const query = groq__default.default` { "items": *[${queryFilter}] { _id, _type, _createdAt, _updatedAt, altText, creditLine, description, extension, metadata { dimensions, exif, isOpaque, }, mimeType, opt { media }, originalFilename, size, source { name }, title, url } ${sort || selector ? "|" : ""} ${sort} ${selector}, } `; return { payload: { params, query } }; } }, insertUploads(state, action) { const { results } = action.payload; Object.entries(results).forEach(([hash, assetId]) => { assetId && !state.allIds.includes(hash) && state.allIds.push(assetId); }); }, listenerCreateQueue(_state, _action) { }, listenerCreateQueueComplete(state, action) { const { assets } = action.payload; assets?.forEach((asset) => { state.byIds[asset?._id]?.asset && (state.byIds[asset._id].asset = asset); }); }, listenerDeleteQueue(_state, _action) { }, listenerDeleteQueueComplete(state, action) { const { assetIds } = action.payload; assetIds?.forEach((assetId) => { const deleteIndex = state.allIds.indexOf(assetId); deleteIndex >= 0 && state.allIds.splice(deleteIndex, 1), delete state.byIds[assetId]; }); }, listenerUpdateQueue(_state, _action) { }, listenerUpdateQueueComplete(state, action) { const { assets } = action.payload; assets?.forEach((asset) => { state.byIds[asset?._id]?.asset && (state.byIds[asset._id].asset = asset); }); }, loadNextPage() { }, loadPageIndex(state, action) { state.pageIndex = action.payload.pageIndex; }, orderSet(state, action) { state.order = action.payload?.order, state.pageIndex = 0; }, pick(state, action) { const { assetId, picked } = action.payload; state.byIds[assetId].picked = picked, state.lastPicked = picked ? assetId : void 0; }, pickAll(state) { state.allIds.forEach((id) => { state.byIds[id].picked = !0; }); }, pickClear(state) { state.lastPicked = void 0, Object.values(state.byIds).forEach((asset) => { state.byIds[asset.asset._id].picked = !1; }); }, pickRange(state, action) { const startIndex = state.allIds.findIndex((id) => id === action.payload.startId), endIndex = state.allIds.findIndex((id) => id === action.payload.endId), indices = [startIndex, endIndex].sort((a, b) => a - b); state.allIds.slice(indices[0], indices[1] + 1).forEach((key) => { state.byIds[key].picked = !0; }), state.lastPicked = state.allIds[endIndex]; }, sort(state) { state.allIds.sort((a, b) => { const tagA = state.byIds[a].asset[state.order.field], tagB = state.byIds[b].asset[state.order.field]; return tagA < tagB ? state.order.direction === "asc" ? -1 : 1 : tagA > tagB ? state.order.direction === "asc" ? 1 : -1 : 0; }); }, updateComplete(state, action) { const { asset } = action.payload; state.byIds[asset._id].updating = !1, state.byIds[asset._id].asset = asset; }, updateError(state, action) { const { asset, error } = action.payload, assetId = asset?._id; state.byIds[assetId].error = error.message, state.byIds[assetId].updating = !1; }, updateRequest(state, action) { const assetId = action.payload?.asset?._id; state.byIds[assetId].updating = !0; }, viewSet(state, action) { state.view = action.payload?.view; } } }), assetsDeleteEpic = (action$, _state$, { client }) => action$.pipe( operators$1.filter(assetsActions.deleteRequest.match), operators$1.mergeMap((action) => { const { assets } = action.payload, assetIds = assets.map((asset) => asset._id); return rxjs.of(assets).pipe( operators$1.mergeMap( () => client.observable.delete({ query: groq__default.default`*[_id in ${JSON.stringify(assetIds)}]` }) ), operators$1.mergeMap(() => rxjs.of(assetsActions.deleteComplete({ assetIds }))), operators$1.catchError((error) => rxjs.of(assetsActions.deleteError({ assetIds, error }))) ); }) ), assetsFetchEpic = (action$, state$, { client }) => action$.pipe( operators$1.filter(assetsActions.fetchRequest.match), operators$1.withLatestFrom(state$), operators$1.switchMap(([action, state]) => { const params = action.payload?.params, query = action.payload?.query; return rxjs.of(action).pipe( debugThrottle(state.debug.badConnection), operators$1.mergeMap( () => client.observable.fetch(query, params) ), operators$1.mergeMap((result) => { const { items // totalCount } = result; return rxjs.of(assetsActions.fetchComplete({ assets: items })); }), operators$1.catchError( (error) => rxjs.of( assetsActions.fetchError({ message: error?.message || "Internal error", statusCode: error?.statusCode || 500 }) ) ) ); }) ), assetsFetchPageIndexEpic = (action$, state$) => action$.pipe( operators$1.filter(assetsActions.loadPageIndex.match), operators$1.withLatestFrom(state$), operators$1.switchMap(([action, state]) => { const pageSize = state.assets.pageSize, start = action.payload.pageIndex * pageSize, end = start + pageSize, documentId = state?.selected.document?._id, documentAssetIds = state?.selected?.documentAssetIds, constructedFilter = constructFilter({ assetTypes: state.assets.assetTypes, searchFacets: state.search.facets, searchQuery: state.search.query }), params = { ...documentId ? { documentId } : {}, documentAssetIds }; return rxjs.of( assetsActions.fetchRequest({ params, queryFilter: constructedFilter, selector: groq__default.default`[${start}...${end}]`, sort: groq__default.default`order(${state.assets?.order?.field} ${state.assets?.order?.direction})` }) ); }) ), assetsFetchNextPageEpic = (action$, state$) => action$.pipe( operators$1.filter(assetsActions.loadNextPage.match), operators$1.withLatestFrom(state$), operators$1.switchMap( ([_action, state]) => rxjs.of(assetsActions.loadPageIndex({ pageIndex: state.assets.pageIndex + 1 })) ) ), assetsFetchAfterDeleteAllEpic = (action$, state$) => action$.pipe( operators$1.filter(assetsActions.deleteComplete.match), operators$1.withLatestFrom(state$), operators$1.switchMap(([_action, state]) => { if (state.assets.allIds.length === 0) { const nextPageIndex = Math.floor(state.assets.allIds.length / state.assets.pageSize); return rxjs.of(assetsActions.loadPageIndex({ pageIndex: nextPageIndex })); } return rxjs.EMPTY; }) ), filterAssetWithoutTag = (tag) => (asset) => (asset?.asset?.opt?.media?.tags?.findIndex((t) => t._ref === tag?._id) ?? -1) < 0, patchOperationTagAppend = ({ tag }) => (patch) => patch.setIfMissing({ opt: {} }).setIfMissing({ "opt.media": {} }).setIfMissing({ "opt.media.tags": [] }).append("opt.media.tags", [{ _key: nanoid.nanoid(), _ref: tag?._id, _type: "reference", _weak: !0 }]), patchOperationTagUnset = ({ asset, tag }) => (patch) => patch.ifRevisionId(asset?.asset?._rev).unset([`opt.media.tags[_ref == "${tag._id}"]`]), assetsOrderSetEpic = (action$) => action$.pipe( operators$1.filter(assetsActions.orderSet.match), operators$1.mergeMap(() => rxjs.of( assetsActions.clear(), // assetsActions.loadPageIndex({ pageIndex: 0 }) )) ), assetsSearchEpic = (action$) => action$.pipe( reduxObservable.ofType( searchActions.facetsAdd.type, searchActions.facetsClear.type, searchActions.facetsRemoveById.type, searchActions.facetsRemoveByName.type, searchActions.facetsRemoveByTag.type, searchActions.facetsUpdate.type, searchActions.facetsUpdateById.type, searchActions.querySet.type ), operators$1.debounceTime(400), operators$1.mergeMap(() => rxjs.of( assetsActions.clear(), // assetsActions.loadPageIndex({ pageIndex: 0 }) )) ), assetsListenerCreateQueueEpic = (action$) => action$.pipe( operators$1.filter(assetsActions.listenerCreateQueue.match), operators$1.bufferTime(2e3), operators$1.filter((actions) => actions.length > 0), operators$1.mergeMap((actions) => { const assets = actions?.map((action) => action.payload.asset); return rxjs.of(assetsActions.listenerCreateQueueComplete({ assets })); }) ), assetsListenerDeleteQueueEpic = (action$) => action$.pipe( operators$1.filter(assetsActions.listenerDeleteQueue.match), operators$1.bufferTime(2e3), operators$1.filter((actions) => actions.length > 0), opera