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
JavaScript
;
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;