cozy-dataproxy-lib
Version:
Library meant to be by Cozy Cloud's DataProxy apps for data manipulation
507 lines (407 loc) • 20.5 kB
JavaScript
;
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';