UNPKG

sanity-plugin-goth-vimeo-input

Version:

A Sanity Plugin for Inputting Vimeo Videos by their URL and Pre-Loading oEmbed Data.

361 lines (291 loc) 17.3 kB
"use strict"; function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _uniqueId2 = _interopRequireDefault(require("lodash/uniqueId")); var _react = _interopRequireWildcard(require("react")); var _axios = _interopRequireDefault(require("axios")); var _warningIcon = _interopRequireDefault(require("part:@sanity/base/warning-icon")); var _alert = _interopRequireDefault(require("part:@sanity/components/alerts/alert")); var _default2 = _interopRequireDefault(require("part:@sanity/components/buttons/default")); var _default3 = _interopRequireDefault(require("part:@sanity/components/textinputs/default")); var _default4 = _interopRequireDefault(require("part:@sanity/components/formfields/default")); var _default5 = _interopRequireDefault(require("part:@sanity/components/fieldsets/default")); var _formBuilder = require("part:@sanity/form-builder"); var _ConfigFieldsInput = _interopRequireDefault(require("./components/ConfigFieldsInput")); var _Label = require("./components/Label"); var _VimeoInput = _interopRequireDefault(require("./styles/VimeoInput.css")); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } var O_EMBED_CONFIG_DATA_FIELD_NAME = "oEmbedConfigData"; var O_EMBED_DATA_FIELD_NAME = "oEmbedData"; var client = _axios.default.create({ baseURL: "https://vimeo.com/api" }); var VimeoInput = /*#__PURE__*/_react.default.forwardRef(function (props, ref) { var focusPath = props.focusPath, level = props.level, markers = props.markers, onChange = props.onChange, type = props.type, value = props.value; var configFieldsInputId = (0, _react.useMemo)(function () { return (0, _uniqueId2.default)("video-o-embed-data__config-fields-input"); }, []); var urlSearchInputId = (0, _react.useMemo)(function () { return (0, _uniqueId2.default)("video-o-embed-data__url-search-input"); }, []); var videoIdInputId = (0, _react.useMemo)(function () { return (0, _uniqueId2.default)("video-o-embed-data__video-id-input"); }, []); var videoTitleInputId = (0, _react.useMemo)(function () { return (0, _uniqueId2.default)("video-o-embed-data__video-title-input"); }, []); var oEmbedConfigDataField = type.fields.find(function (field) { return field.name === O_EMBED_CONFIG_DATA_FIELD_NAME; }); var oEmbedDataField = type.fields.find(function (field) { return field.name === O_EMBED_DATA_FIELD_NAME; }); var initialUrl = value && value.url ? value.url : ""; var _useState = (0, _react.useState)(false), _useState2 = _slicedToArray(_useState, 2), didErrorFetching = _useState2[0], setDidErrorFetching = _useState2[1]; var _useState3 = (0, _react.useState)(false), _useState4 = _slicedToArray(_useState3, 2), fetching = _useState4[0], setFetching = _useState4[1]; var _useState5 = (0, _react.useState)(null), _useState6 = _slicedToArray(_useState5, 2), fetchTrigger = _useState6[0], setFetchTrigger = _useState6[1]; // `resolvedUrl` is used for keeping track of the initial url and updates to // the url that take place upon patch events (e.g.; when the search URL form // is submitted). var _useState7 = (0, _react.useState)(initialUrl), _useState8 = _slicedToArray(_useState7, 2), resolvedUrl = _useState8[0], setResolvedUrl = _useState8[1]; // `url` is used for keeping track of the url as represented in the search URL // form as the user types within it. var _useState9 = (0, _react.useState)(initialUrl), _useState10 = _slicedToArray(_useState9, 2), url = _useState10[0], setUrl = _useState10[1]; // `configIsDirty` is used to keep track of the saved state of the config // against the `resolvedUrl`. This is important because the returned `oEmbed` // data we get back for the `resolvedUrl` will have details that differ // according to the passed configuration payload, and we want to know when // our config values change so that we can prompt the user to re-resolve the // search URL. var _useState11 = (0, _react.useState)(false), _useState12 = _slicedToArray(_useState11, 2), configIsDirty = _useState12[0], setConfigIsDirty = _useState12[1]; // `configFieldValues` is used to hold the transient states of configuration // values, initially set from the persisted `oEmbedConfigDataField`. We dont // actually update the `oEmbedConfigDataField` until _after_ the search URL // is re-resolved so that we can always prompt our user to changes while // accurately showing config values that reflect the saved `oEmbed` data // (e.g.; a page refresh should refresh config values to their saved state // until the search URL is re-resolved (searched)). var _useState13 = (0, _react.useState)(function () { var initialConfigFieldValues = value && value[O_EMBED_CONFIG_DATA_FIELD_NAME] ? value[O_EMBED_CONFIG_DATA_FIELD_NAME] : {}; var configFieldValues = {}; if (type.options && type.options.configurableFields) { configFieldValues = type.options.configurableFields.reduce(function (obj, configurableField) { obj[configurableField] = initialConfigFieldValues[configurableField]; if (obj[configurableField] == null && type.options.defaultFieldValues[configurableField] != null) { obj[configurableField] = type.options.defaultFieldValues[configurableField]; } return obj; }, {}); } return configFieldValues; }), _useState14 = _slicedToArray(_useState13, 2), configFieldValues = _useState14[0], setConfigFieldValues = _useState14[1]; var urlRef = (0, _react.useRef)(url); var searchUrlInputRef = (0, _react.useRef)(null); (0, _react.useImperativeHandle)(ref, function () { return { focus: focusSearchUrlInput }; }); (0, _react.useEffect)(function () { urlRef.current = url; }, [url]); (0, _react.useEffect)(function () { if (!fetchTrigger) { return; } var cancelSource = _axios.default.CancelToken.source(); var artificialTimeout; // Used to add a little tactile feeling to button. var fetch = function fetch() { var url = urlRef.current; var configuration = {}; if (type.options && type.options.defaultFieldValues) { configuration = type.options.defaultFieldValues; } if (configFieldValues) { configuration = _objectSpread(_objectSpread({}, configuration), configFieldValues); } configuration.background = !configuration.controls; client.get("/oembed.json", { params: _objectSpread({ url: url }, configuration), options: { cancelToken: cancelSource.cancelToken } }).then(function (res) { return handleFetchSuccess(url, configuration, res); }).catch(handleFetchError).finally(function () { setFetching(false); }); }; setFetching(true); artificialTimeout = setTimeout(fetch, 500); return function () { cancelSource.cancel(); clearTimeout(artificialTimeout); }; }, [fetchTrigger]); var handleFetchSuccess = (0, _react.useCallback)(function (url, configuration, _ref) { var data = _ref.data, status = _ref.status; if (!status || status !== 200) { setDidErrorFetching(true); return; } var doc = { _type: type.name, url: url, oEmbedDataLastFetchedAt: new Date().toISOString(), oEmbedDataJsonResponse: JSON.stringify(data) }; // Set values for oEmbedConfigData. doc[O_EMBED_CONFIG_DATA_FIELD_NAME] = Object.entries(configuration).reduce(function (obj, _ref2) { var _ref3 = _slicedToArray(_ref2, 2), configFieldName = _ref3[0], configFieldValue = _ref3[1]; var configSchemaField = oEmbedConfigDataField.type.fields.find(function (field) { return field.name === configFieldName; }); if (!configSchemaField) { console.log("throwing away %s", configFieldName); return obj; } if (configSchemaField.type.jsonType === "number") { configFieldValue = Number(configFieldValue); } obj[configFieldName] = configFieldValue; return obj; }, {}); // Set values for oEmbedData. doc[O_EMBED_DATA_FIELD_NAME] = oEmbedDataField.type.fields.reduce(function (obj, field) { var fieldValue = data[field.name]; if (!fieldValue) { return obj; } if (field.type.jsonType === "number") { fieldValue = Number(fieldValue); } obj[field.name] = fieldValue; return obj; }, {}); onChange(_formBuilder.PatchEvent.from(_formBuilder.patches.set(doc))); setResolvedUrl(url); setConfigIsDirty(false); setDidErrorFetching(false); }, [oEmbedDataField]); var handleFetchError = (0, _react.useCallback)(function (err) { console.error(err); onChange(_formBuilder.PatchEvent.from(_formBuilder.patches.unset())); setResolvedUrl(""); setDidErrorFetching(true); }, []); var handleConfigFieldValueChange = function handleConfigFieldValueChange(_ref4) { var field = _ref4.field, value = _ref4.value; setConfigFieldValues(_objectSpread(_objectSpread({}, configFieldValues), {}, _defineProperty({}, field, value))); setConfigIsDirty(true); }; var handleUrlSearchChange = function handleUrlSearchChange(_ref5) { var value = _ref5.target.value; setUrl(value); }; var handleFetchClicked = function handleFetchClicked() { setFetchTrigger(+new Date()); }; var focusSearchUrlInput = function focusSearchUrlInput() { searchUrlInputRef.current.focus(); }; return /*#__PURE__*/_react.default.createElement(_default5.default, { description: type.description, legend: type.title, markers: markers || [] }, /*#__PURE__*/_react.default.createElement(_ConfigFieldsInput.default, { inputId: configFieldsInputId, configFields: configFieldValues, disabled: fetching, defaultFieldValues: type.options && type.options.defaultFieldValues ? type.options.defaultFieldValues : {}, onFieldValueChange: handleConfigFieldValueChange }), configIsDirty && /*#__PURE__*/_react.default.createElement("div", { className: _VimeoInput.default.warningMessage }, /*#__PURE__*/_react.default.createElement(_alert.default, { color: "warning", icon: _warningIcon.default, title: "Must Reload Vimeo Video!" }, "Changing configuration data requires that you re-load the Vimeo video. If you are using the same video as before and just updating configuration values, you just need to click \"Load\" again below.")), /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Label.Label, { htmlFor: urlSearchInputId }, /*#__PURE__*/_react.default.createElement(_Label.Title, null, "Load Vimeo Video by URL"), /*#__PURE__*/_react.default.createElement(_Label.Description, null, "Input a valid Vimeo video URL and click to \"Load\" it. Once loaded, data necessary for embedding the video will be populated and may be saved.")), /*#__PURE__*/_react.default.createElement("div", { className: _VimeoInput.default.search }, /*#__PURE__*/_react.default.createElement("div", { className: _VimeoInput.default.input }, /*#__PURE__*/_react.default.createElement(_default3.default, { ref: searchUrlInputRef, disabled: fetching, inputId: urlSearchInputId, onChange: handleUrlSearchChange, placeholder: "Load Vimeo Video by URL", type: "url", value: url === undefined ? "" : url })), /*#__PURE__*/_react.default.createElement(_default2.default, { className: _VimeoInput.default.button, disabled: fetching, kind: "default", loading: fetching, onClick: handleFetchClicked }, fetching ? "Loading..." : "Load")), didErrorFetching && /*#__PURE__*/_react.default.createElement("div", { className: _VimeoInput.default.warningMessage }, /*#__PURE__*/_react.default.createElement(_alert.default, { color: "warning", icon: _warningIcon.default, title: "Failed to load Vimeo Video!" }, "The Vimeo video URL you attempted to load is either invalid or could not be found."))), resolvedUrl && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Label.Label, { htmlFor: videoTitleInputId }, /*#__PURE__*/_react.default.createElement(_Label.Title, null, "Vimeo Video Title")), /*#__PURE__*/_react.default.createElement(_default3.default, { disabled: true, inputId: videoTitleInputId, type: "text", readOnly: true, value: value && value[O_EMBED_DATA_FIELD_NAME] ? value[O_EMBED_DATA_FIELD_NAME]["title"] : "" })), /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Label.Label, { htmlFor: videoIdInputId }, /*#__PURE__*/_react.default.createElement(_Label.Title, null, "Vimeo Video Thumbnail")), /*#__PURE__*/_react.default.createElement("img", { alt: "Video Thumbnail", className: _VimeoInput.default.thumbnail, src: value && value[O_EMBED_DATA_FIELD_NAME] ? value[O_EMBED_DATA_FIELD_NAME]["thumbnail_url"] : "" })))); }); var _default = VimeoInput; exports.default = _default;