UNPKG

cozy-dataproxy-lib

Version:

Library meant to be by Cozy Cloud's DataProxy apps for data manipulation

507 lines (407 loc) 20.5 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.useDataProxy = exports.DataProxyProvider = exports.DataProxyContext = void 0; var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var Comlink = _interopRequireWildcard(require("comlink")); var _react = _interopRequireWildcard(require("react")); var _cozyClient = require("cozy-client"); var _cozyDeviceHelper = require("cozy-device-helper"); var _cozyFlags = _interopRequireDefault(require("cozy-flags")); var _cozyIntent = require("cozy-intent"); var _cozyMinilog = _interopRequireDefault(require("cozy-minilog")); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && 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 ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); 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 = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } var log = (0, _cozyMinilog.default)('👷‍♂️ [DataProxyProvider]'); var noop = /*#__PURE__*/function () { var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() { return _regenerator.default.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: throw new Error('[DataProxy] not ready'); case 1: case "end": return _context.stop(); } } }, _callee); })); return function noop() { return _ref.apply(this, arguments); }; }(); var defaultValue = Object.freeze({ dataProxyServicesAvailable: false, ready: false, search: noop, recents: noop, requestLink: noop }); var DataProxyContext = /*#__PURE__*/_react.default.createContext(defaultValue); exports.DataProxyContext = DataProxyContext; var useDataProxy = function useDataProxy() { var context = (0, _react.useContext)(DataProxyContext); if (!context) { throw new Error('useDataProxy must be used within a DataProxyProvider'); } return context; }; // See https://legacy.reactjs.org/docs/error-boundaries.html exports.useDataProxy = useDataProxy; var ErrorBoundary = /*#__PURE__*/function (_React$Component) { (0, _inherits2.default)(ErrorBoundary, _React$Component); var _super = _createSuper(ErrorBoundary); function ErrorBoundary(props) { var _this; (0, _classCallCheck2.default)(this, ErrorBoundary); _this = _super.call(this, props); _this.state = { hasError: false }; return _this; } (0, _createClass2.default)(ErrorBoundary, [{ key: "componentDidCatch", value: function componentDidCatch(e) { log.error('[DataProxy iframe errors]', e); } }, { key: "render", value: function render() { var _this$props$children; if (this.state.hasError) { return null; } return (_this$props$children = this.props.children) !== null && _this$props$children !== void 0 ? _this$props$children : null; } }], [{ key: "getDerivedStateFromError", value: function getDerivedStateFromError() { return { hasError: true }; } }]); return ErrorBoundary; }(_react.default.Component); var DEFAULT_OPTS = {}; var DataProxyProvider = /*#__PURE__*/_react.default.memo(function (_ref2) { var children = _ref2.children, _ref2$options = _ref2.options, options = _ref2$options === void 0 ? DEFAULT_OPTS : _ref2$options; var client = (0, _cozyClient.useClient)(); var webviewIntent = (0, _cozyIntent.useWebviewIntent)(); var _useState = (0, _react.useState)(), _useState2 = (0, _slicedToArray2.default)(_useState, 2), iframeUrl = _useState2[0], setIframeUrl = _useState2[1]; var _useState3 = (0, _react.useState)(), _useState4 = (0, _slicedToArray2.default)(_useState3, 2), dataProxyCom = _useState4[0], setDataProxyCom = _useState4[1]; var _useState5 = (0, _react.useState)(), _useState6 = (0, _slicedToArray2.default)(_useState5, 2), dataProxy = _useState6[0], setDataProxy = _useState6[1]; var _useState7 = (0, _react.useState)(undefined), _useState8 = (0, _slicedToArray2.default)(_useState7, 2), dataProxyServicesAvailable = _useState8[0], setDataProxyServicesAvailable = _useState8[1]; var _useState9 = (0, _react.useState)(0), _useState10 = (0, _slicedToArray2.default)(_useState9, 2), iframeVersion = _useState10[0], setIframeVersion = _useState10[1]; (0, _react.useEffect)(function () { if (!client) { return; } var initIframe = /*#__PURE__*/function () { var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() { var _result$data, _result$data$attribut, _result$data$attribut2, _result$data$attribut3, _result$data$attribut4, result; return _regenerator.default.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: _context2.prev = 0; log.log('Initializing DataProxy intent'); _context2.next = 4; return client.stackClient.fetchJSON('POST', '/intents', { data: { type: 'io.cozy.intents', attributes: { action: 'OPEN', type: 'io.cozy.dataproxy', permissions: ['GET'] } } }); case 4: result = _context2.sent; if ((_result$data = result.data) !== null && _result$data !== void 0 && (_result$data$attribut = _result$data.attributes) !== null && _result$data$attribut !== void 0 && (_result$data$attribut2 = _result$data$attribut.services) !== null && _result$data$attribut2 !== void 0 && (_result$data$attribut3 = _result$data$attribut2[0]) !== null && _result$data$attribut3 !== void 0 && _result$data$attribut3.href) { _context2.next = 9; break; } log.log('No dataproxy intent available, dataproxy features will be disabled'); setDataProxyServicesAvailable(false); return _context2.abrupt("return"); case 9: setIframeUrl((_result$data$attribut4 = result.data.attributes.services[0]) === null || _result$data$attribut4 === void 0 ? void 0 : _result$data$attribut4.href); setDataProxyServicesAvailable(true); _context2.next = 17; break; case 13: _context2.prev = 13; _context2.t0 = _context2["catch"](0); setDataProxyServicesAvailable(false); log.error('Error while initializing Search intent, dataproxy features will be disabled', _context2.t0); case 17: case "end": return _context2.stop(); } } }, _callee2, null, [[0, 13]]); })); return function initIframe() { return _ref3.apply(this, arguments); }; }(); var initFlagship = /*#__PURE__*/function () { var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3() { var _yield$webviewIntent$, isSearchAvailable; return _regenerator.default.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: _context3.prev = 0; if (webviewIntent) { _context3.next = 3; break; } return _context3.abrupt("return"); case 3: log.log('Initializing DataProxy intent in Flagship app'); _context3.next = 6; return webviewIntent.call('isAvailable', 'search'); case 6: _context3.t1 = _yield$webviewIntent$ = _context3.sent; _context3.t0 = _context3.t1 !== null; if (!_context3.t0) { _context3.next = 10; break; } _context3.t0 = _yield$webviewIntent$ !== void 0; case 10: if (!_context3.t0) { _context3.next = 14; break; } _context3.t2 = _yield$webviewIntent$; _context3.next = 15; break; case 14: _context3.t2 = false; case 15: isSearchAvailable = _context3.t2; if (isSearchAvailable) { _context3.next = 20; break; } log.log('Dataproxy features will be disabled due to feature not supported by Flagship app'); setDataProxyServicesAvailable(false); return _context3.abrupt("return"); case 20: setDataProxyCom(function () { return { search: function search(_search, searchOptions) { return webviewIntent === null || webviewIntent === void 0 ? void 0 : webviewIntent.call('search', _search, _objectSpread(_objectSpread({}, options), searchOptions)); } }; }); setDataProxyServicesAvailable(isSearchAvailable); _context3.next = 28; break; case 24: _context3.prev = 24; _context3.t3 = _context3["catch"](0); setDataProxyServicesAvailable(false); log.error("Error while initializing Flagship's Search, dataproxy features will be disabled", _context3.t3); case 28: case "end": return _context3.stop(); } } }, _callee3, null, [[0, 24]]); })); return function initFlagship() { return _ref4.apply(this, arguments); }; }(); if (!(0, _cozyFlags.default)('cozy.search.enabled')) { log.log('Dataproxy features will be disabled due to missing feature flags'); // eslint-disable-next-line react-hooks/set-state-in-effect setDataProxyServicesAvailable(false); return; } if ((0, _cozyDeviceHelper.isFlagshipApp)()) { initFlagship(); } else { initIframe(); } }, [client, webviewIntent, options]); var onIframeLoaded = (0, _react.useCallback)(function () { var ifr = document.getElementById('DataProxy'); var remote = Comlink.wrap(Comlink.windowEndpoint(ifr.contentWindow)); setDataProxyCom(function () { return remote; }); }, [setDataProxyCom]); var onReceiveMessage = (0, _react.useCallback)(function (event) { try { if (typeof event.origin !== 'string') return; if (!event.origin.includes('dataproxy')) return; var d = event === null || event === void 0 ? void 0 : event.data; if (d && typeof d === 'object' && d.type === 'DATAPROXYMESSAGE' && d.payload === 'READY') { onIframeLoaded(); } } catch (e) { log.error('[DataProxy] onReceiveMessage error', e); } }, [onIframeLoaded]); (0, _react.useEffect)(function () { window.addEventListener('message', onReceiveMessage); return function () { window.removeEventListener('message', onReceiveMessage); }; }, [onReceiveMessage]); (0, _react.useEffect)(function () { var doAsync = /*#__PURE__*/function () { var _ref5 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee6() { var search, recents, requestLink, newDataProxy; return _regenerator.default.wrap(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: // Make a global search search = /*#__PURE__*/function () { var _ref6 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee4(_search2, searchOptions) { var result; return _regenerator.default.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: log.log('Send search query to DataProxy: ', _search2, searchOptions); _context4.next = 3; return dataProxyCom.search(_search2, _objectSpread(_objectSpread({}, options), searchOptions)); case 3: result = _context4.sent; return _context4.abrupt("return", result); case 5: case "end": return _context4.stop(); } } }, _callee4); })); return function search(_x, _x2) { return _ref6.apply(this, arguments); }; }(); recents = function recents() { log.log('Send recents query to DataProxy: '); return dataProxyCom.recents(); }; // Request through cozy-client requestLink = /*#__PURE__*/function () { var _ref7 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee5(operation, options) { var _dataProxyCom$request; return _regenerator.default.wrap(function _callee5$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: log.log('Send request to DataProxy : ', operation); if (options !== null && options !== void 0 && options.fetchPolicy) { // Functions cannot be serialized and thus passed to the iframe delete options.fetchPolicy; } return _context5.abrupt("return", (_dataProxyCom$request = dataProxyCom.requestLink) === null || _dataProxyCom$request === void 0 ? void 0 : _dataProxyCom$request.call(dataProxyCom, operation, options)); case 3: case "end": return _context5.stop(); } } }, _callee5); })); return function requestLink(_x3, _x4) { return _ref7.apply(this, arguments); }; }(); newDataProxy = { dataProxyServicesAvailable: dataProxyServicesAvailable, ready: Boolean(dataProxyCom), search: search, recents: recents, requestLink: requestLink }; client.links.forEach(function (link) { if (link.registerDataProxy) { // This is required as the DataProxy is not ready when the DataProxyLink is created link.registerDataProxy(newDataProxy); } }); setDataProxy(newDataProxy); case 6: case "end": return _context6.stop(); } } }, _callee6); })); return function doAsync() { return _ref5.apply(this, arguments); }; }(); if (dataProxyCom && client !== null && client !== void 0 && client.links) { doAsync(); } }, [dataProxyCom, client, dataProxyServicesAvailable, options]); // eslint-disable-next-line react-hooks/preserve-manual-memoization var reloadIframe = (0, _react.useCallback)(function () { setIframeVersion(function (v) { return v + 1; }); log.log('Reload iframe with new version ', iframeVersion); }, []); var iframeKey = "".concat(iframeUrl, "::").concat(iframeVersion); // Useful to force iframe reload when key change return /*#__PURE__*/_react.default.createElement(DataProxyContext.Provider, { value: dataProxy || defaultValue }, children !== null && children !== void 0 ? children : null, /*#__PURE__*/_react.default.createElement(ErrorBoundary, null, iframeUrl ? /*#__PURE__*/_react.default.createElement("iframe", { key: iframeKey, id: "DataProxy", src: iframeUrl, width: 0, height: 0, style: { width: 0, height: 0, border: 0 }, sandbox: "allow-same-origin allow-scripts", onError: function onError() { log.error('[DataProxy] iframe load error'); reloadIframe(); } }) : null)); }); exports.DataProxyProvider = DataProxyProvider; DataProxyProvider.displayName = 'DataProxyProvider';