@bigbinary/neeto-media-recorder 2.7.35 → 2.7.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -11,7 +11,7 @@ import axios from 'axios';
11
11
  import { copyToClipboard } from '@bigbinary/neeto-commons-frontend/utils/general';
12
12
  import Toastr from '@bigbinary/neetoui/Toastr';
13
13
  import platform from 'platform';
14
- import { isNotNil } from 'ramda';
14
+ import { isNotNil, min } from 'ramda';
15
15
  import withT from '@bigbinary/neeto-commons-frontend/react-utils/withT';
16
16
  import Typography from '@bigbinary/neetoui/Typography';
17
17
  import Spinner from '@bigbinary/neetoui/Spinner';
@@ -507,6 +507,14 @@ var UPLOAD_STATUS = {
507
507
  insufficient_data: "insufficient_data",
508
508
  error: "error"
509
509
  };
510
+ var TOTAL_RETRIES = 15;
511
+ var INITIAL_RETRY_DELAY = 4000;
512
+ var MAX_RETRY_DELAY = 16000;
513
+ var DEFAULT_RETRY_CONFIG = {
514
+ retries: TOTAL_RETRIES,
515
+ retryDelay: INITIAL_RETRY_DELAY,
516
+ retryableStatuses: []
517
+ };
510
518
  var HAS_CHROME_NAMESPACE = (typeof chrome === "undefined" ? "undefined" : _typeof$1(chrome)) === "object";
511
519
  var IS_EXTENSION = HAS_CHROME_NAMESPACE && isNotNil(chrome.extension);
512
520
  var IS_SAFARI = platform.name === "Safari";
@@ -549,6 +557,11 @@ var getSupportedMimeType = function getSupportedMimeType() {
549
557
  })) !== null && _candidates$find !== void 0 ? _candidates$find : null;
550
558
  };
551
559
 
560
+ var sleep = function sleep(delay) {
561
+ return new Promise(function (resolve) {
562
+ return setTimeout(resolve, delay);
563
+ });
564
+ };
552
565
  var generatePublicUrl = function generatePublicUrl(recordingId) {
553
566
  var baseURL = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : window.location.origin;
554
567
  return "".concat(baseURL, "/watch/").concat(recordingId);
@@ -804,28 +817,257 @@ var copyRecordingLinkToClipboardOnStop = function copyRecordingLinkToClipboardOn
804
817
  }))();
805
818
  };
806
819
 
820
+ function _createClass(e, r, t) {
821
+ return Object.defineProperty(e, "prototype", {
822
+ writable: !1
823
+ }), e;
824
+ }
825
+
826
+ function _classCallCheck(a, n) {
827
+ if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
828
+ }
829
+
830
+ function _setPrototypeOf(t, e) {
831
+ return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) {
832
+ return t.__proto__ = e, t;
833
+ }, _setPrototypeOf(t, e);
834
+ }
835
+
836
+ function _inherits(t, e) {
837
+ if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function");
838
+ t.prototype = Object.create(e && e.prototype, {
839
+ constructor: {
840
+ value: t,
841
+ writable: !0,
842
+ configurable: !0
843
+ }
844
+ }), Object.defineProperty(t, "prototype", {
845
+ writable: !1
846
+ }), e && _setPrototypeOf(t, e);
847
+ }
848
+
849
+ function _assertThisInitialized(e) {
850
+ if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
851
+ return e;
852
+ }
853
+
854
+ function _possibleConstructorReturn(t, e) {
855
+ if (e && ("object" == _typeof$1(e) || "function" == typeof e)) return e;
856
+ if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined");
857
+ return _assertThisInitialized(t);
858
+ }
859
+
860
+ function _getPrototypeOf(t) {
861
+ return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) {
862
+ return t.__proto__ || Object.getPrototypeOf(t);
863
+ }, _getPrototypeOf(t);
864
+ }
865
+
866
+ function _isNativeFunction(t) {
867
+ try {
868
+ return -1 !== Function.toString.call(t).indexOf("[native code]");
869
+ } catch (n) {
870
+ return "function" == typeof t;
871
+ }
872
+ }
873
+
874
+ function _isNativeReflectConstruct$1() {
875
+ try {
876
+ var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
877
+ } catch (t) {}
878
+ return (_isNativeReflectConstruct$1 = function _isNativeReflectConstruct() {
879
+ return !!t;
880
+ })();
881
+ }
882
+
883
+ function _construct(t, e, r) {
884
+ if (_isNativeReflectConstruct$1()) return Reflect.construct.apply(null, arguments);
885
+ var o = [null];
886
+ o.push.apply(o, e);
887
+ var p = new (t.bind.apply(t, o))();
888
+ return r && _setPrototypeOf(p, r.prototype), p;
889
+ }
890
+
891
+ function _wrapNativeSuper(t) {
892
+ var r = "function" == typeof Map ? new Map() : void 0;
893
+ return _wrapNativeSuper = function _wrapNativeSuper(t) {
894
+ if (null === t || !_isNativeFunction(t)) return t;
895
+ if ("function" != typeof t) throw new TypeError("Super expression must either be null or a function");
896
+ if (void 0 !== r) {
897
+ if (r.has(t)) return r.get(t);
898
+ r.set(t, Wrapper);
899
+ }
900
+ function Wrapper() {
901
+ return _construct(t, arguments, _getPrototypeOf(this).constructor);
902
+ }
903
+ return Wrapper.prototype = Object.create(t.prototype, {
904
+ constructor: {
905
+ value: Wrapper,
906
+ enumerable: !1,
907
+ writable: !0,
908
+ configurable: !0
909
+ }
910
+ }), _setPrototypeOf(Wrapper, t);
911
+ }, _wrapNativeSuper(t);
912
+ }
913
+
914
+ function _createSuper(t) { var r = _isNativeReflectConstruct(); return function () { var e, o = _getPrototypeOf(t); if (r) { var s = _getPrototypeOf(this).constructor; e = Reflect.construct(o, arguments, s); } else e = o.apply(this, arguments); return _possibleConstructorReturn(this, e); }; }
915
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
916
+ var RetryError = /*#__PURE__*/function (_Error) {
917
+ _inherits(RetryError, _Error);
918
+ var _super = _createSuper(RetryError);
919
+ function RetryError(message) {
920
+ _classCallCheck(this, RetryError);
921
+ return _super.call(this, message);
922
+ }
923
+ return _createClass(RetryError);
924
+ }( /*#__PURE__*/_wrapNativeSuper(Error));
925
+ var buildRetryableApi = function buildRetryableApi(apiConnector) {
926
+ var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_RETRY_CONFIG,
927
+ _ref$retries = _ref.retries,
928
+ retries = _ref$retries === void 0 ? TOTAL_RETRIES : _ref$retries,
929
+ _ref$retryDelay = _ref.retryDelay,
930
+ retryDelay = _ref$retryDelay === void 0 ? INITIAL_RETRY_DELAY : _ref$retryDelay;
931
+ return /*#__PURE__*/function () {
932
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(apiConnectorArgs) {
933
+ var _config$signal, _config$signal2;
934
+ var _apiConnectorArgs$con, config, remainingAttempts, shouldCancelRetries, _error$config, _error$response, _error$response2;
935
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
936
+ while (1) switch (_context.prev = _context.next) {
937
+ case 0:
938
+ _apiConnectorArgs$con = apiConnectorArgs.config, config = _apiConnectorArgs$con === void 0 ? null : _apiConnectorArgs$con; // If the signal is already aborted before we start, addEventListener("abort")
939
+ // will never fire — so we'd otherwise run the full retry/backoff schedule
940
+ // against an aborted request.
941
+ if (!(config !== null && config !== void 0 && (_config$signal = config.signal) !== null && _config$signal !== void 0 && _config$signal.aborted)) {
942
+ _context.next = 3;
943
+ break;
944
+ }
945
+ throw new RetryError("Request cancelled or request retries exhausted");
946
+ case 3:
947
+ remainingAttempts = retries + 1;
948
+ shouldCancelRetries = false;
949
+ config === null || config === void 0 ? void 0 : (_config$signal2 = config.signal) === null || _config$signal2 === void 0 ? void 0 : _config$signal2.addEventListener("abort", function () {
950
+ shouldCancelRetries = true;
951
+ });
952
+ case 6:
953
+ if (!(remainingAttempts-- && !shouldCancelRetries)) {
954
+ _context.next = 30;
955
+ break;
956
+ }
957
+ _context.prev = 7;
958
+ _context.next = 10;
959
+ return apiConnector(apiConnectorArgs);
960
+ case 10:
961
+ return _context.abrupt("return", _context.sent);
962
+ case 13:
963
+ _context.prev = 13;
964
+ _context.t0 = _context["catch"](7);
965
+ if (!axios.isCancel(_context.t0)) {
966
+ _context.next = 17;
967
+ break;
968
+ }
969
+ throw _context.t0;
970
+ case 17:
971
+ logger.error("buildRetryableApi -> Failed to connect, remainingAttempts:", _context.t0 === null || _context.t0 === void 0 ? void 0 : (_error$config = _context.t0.config) === null || _error$config === void 0 ? void 0 : _error$config.url, (_error$response = _context.t0.response) === null || _error$response === void 0 ? void 0 : _error$response.status, remainingAttempts);
972
+ if (!(((_error$response2 = _context.t0.response) === null || _error$response2 === void 0 ? void 0 : _error$response2.status) !== 401 && remainingAttempts > 0)) {
973
+ _context.next = 27;
974
+ break;
975
+ }
976
+ logger.info("buildRetryableApi -> Failed to connect -> retry in:", retryDelay);
977
+ _context.next = 22;
978
+ return sleep(retryDelay);
979
+ case 22:
980
+ retryDelay = min(retryDelay * 2, MAX_RETRY_DELAY);
981
+ if (!shouldCancelRetries) {
982
+ _context.next = 25;
983
+ break;
984
+ }
985
+ throw _context.t0;
986
+ case 25:
987
+ _context.next = 28;
988
+ break;
989
+ case 27:
990
+ throw _context.t0;
991
+ case 28:
992
+ _context.next = 6;
993
+ break;
994
+ case 30:
995
+ throw new RetryError("Request cancelled or request retries exhausted");
996
+ case 31:
997
+ case "end":
998
+ return _context.stop();
999
+ }
1000
+ }, _callee, null, [[7, 13]]);
1001
+ }));
1002
+ return function (_x) {
1003
+ return _ref2.apply(this, arguments);
1004
+ };
1005
+ }();
1006
+ };
1007
+
807
1008
  function ownKeys$2(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
808
1009
  function _objectSpread$2(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$2(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$2(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
809
1010
  var baseUrl = "/api/v1/recordings";
810
- var create = function create(payload) {
1011
+ var create = function create() {
1012
+ var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
1013
+ payload = _ref.payload,
1014
+ config = _ref.config;
811
1015
  return axios.post(baseUrl, {
812
1016
  recording: _objectSpread$2(_objectSpread$2({}, payload), {}, {
813
1017
  isMp4: isMp4Supported(payload.deviceInfo.mimeType)
814
1018
  })
815
- });
1019
+ }, config);
816
1020
  };
1021
+ var retryableCreate = buildRetryableApi(create);
817
1022
  var recordingsApi = {
818
- create: create
1023
+ create: retryableCreate
819
1024
  };
820
1025
 
821
1026
  function ownKeys$1(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
822
1027
  function _objectSpread$1(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$1(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$1(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
823
1028
  var useCreateRecording = function useCreateRecording(options) {
824
1029
  return useMutation(_objectSpread$1({
825
- mutationFn: recordingsApi.create
1030
+ mutationFn: function mutationFn(payload) {
1031
+ return recordingsApi.create({
1032
+ payload: payload
1033
+ });
1034
+ }
826
1035
  }, options));
827
1036
  };
828
1037
 
1038
+ var createPrefixedLogger = function createPrefixedLogger(prefix) {
1039
+ return {
1040
+ info: function info() {
1041
+ var _logger;
1042
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
1043
+ args[_key] = arguments[_key];
1044
+ }
1045
+ return (_logger = logger).info.apply(_logger, [prefix].concat(args));
1046
+ },
1047
+ error: function error() {
1048
+ var _logger2;
1049
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
1050
+ args[_key2] = arguments[_key2];
1051
+ }
1052
+ return (_logger2 = logger).error.apply(_logger2, [prefix].concat(args));
1053
+ },
1054
+ warn: function warn() {
1055
+ var _logger3;
1056
+ for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
1057
+ args[_key3] = arguments[_key3];
1058
+ }
1059
+ return (_logger3 = logger).warn.apply(_logger3, [prefix].concat(args));
1060
+ },
1061
+ debug: function debug() {
1062
+ var _logger4;
1063
+ for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
1064
+ args[_key4] = arguments[_key4];
1065
+ }
1066
+ return (_logger4 = logger).debug.apply(_logger4, [prefix].concat(args));
1067
+ }
1068
+ };
1069
+ };
1070
+
829
1071
  var AbortUpload = withT(function (_ref) {
830
1072
  var t = _ref.t;
831
1073
  return /*#__PURE__*/jsx("div", {
@@ -1775,6 +2017,7 @@ var UploadingInProgress = function UploadingInProgress(_ref) {
1775
2017
  };
1776
2018
 
1777
2019
  var _window;
2020
+ var recorderLogger = createPrefixedLogger("[MediaRecorder]");
1778
2021
  var useRecorderStore = screenRecorder.useRecorderStore,
1779
2022
  restartRecording = screenRecorder.restartRecording,
1780
2023
  discardRecording = screenRecorder.discardRecording;
@@ -1829,6 +2072,11 @@ var MediaRecorder$1 = function MediaRecorder(_ref, ref) {
1829
2072
  setBaseURL = _useState10[1];
1830
2073
  var recordingRef = useRef({});
1831
2074
  var baseURLRef = useRef("");
2075
+ // Cleanup tied to the current screenRecorder/multipartS3Uploader wiring.
2076
+ // Lifted out of the wiring useEffect so a restart can tear down the old
2077
+ // wiring synchronously instead of waiting on the aborted uploader's
2078
+ // onAbort callback (which can be 30+ seconds on a flaky network).
2079
+ var activeCleanupRef = useRef(noop);
1832
2080
  useEffect(function () {
1833
2081
  var fetchBaseURL = /*#__PURE__*/function () {
1834
2082
  var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
@@ -1941,7 +2189,19 @@ var MediaRecorder$1 = function MediaRecorder(_ref, ref) {
1941
2189
  }
1942
2190
  }, [elapsedTime]);
1943
2191
  var handleCreateRecordingError = function handleCreateRecordingError(error) {
1944
- if (isCancelledError(error)) return;
2192
+ var _error$response, _error$response2;
2193
+ if (isCancelledError(error)) {
2194
+ recorderLogger.info("handleCreateRecording -> cancelled");
2195
+ return;
2196
+ }
2197
+ recorderLogger.error("handleCreateRecording -> failed", {
2198
+ name: error === null || error === void 0 ? void 0 : error.name,
2199
+ message: error === null || error === void 0 ? void 0 : error.message,
2200
+ code: error === null || error === void 0 ? void 0 : error.code,
2201
+ responseStatus: error === null || error === void 0 ? void 0 : (_error$response = error.response) === null || _error$response === void 0 ? void 0 : _error$response.status,
2202
+ responseStatusText: error === null || error === void 0 ? void 0 : (_error$response2 = error.response) === null || _error$response2 === void 0 ? void 0 : _error$response2.statusText,
2203
+ stack: error === null || error === void 0 ? void 0 : error.stack
2204
+ });
1945
2205
  screenRecorder.setError(SCREEN_RECORDER_ERROR.CreateRecordingError);
1946
2206
  onError(SCREEN_RECORDER_ERROR.CreateRecordingError);
1947
2207
  };
@@ -2035,6 +2295,13 @@ var MediaRecorder$1 = function MediaRecorder(_ref, ref) {
2035
2295
  }();
2036
2296
  var handleRestartRecording = function handleRestartRecording() {
2037
2297
  if (!upload) return;
2298
+ // Detach the old uploader's push/onStop/etc. from the screenRecorder
2299
+ // before abortUpload runs. Otherwise the restarted MediaRecorder's
2300
+ // chunks would fan out to both the aborting old uploader and the new
2301
+ // one — and the old uploader wouldn't release its push registration
2302
+ // until its onAbort callback fired.
2303
+ activeCleanupRef.current();
2304
+ activeCleanupRef.current = noop;
2038
2305
  getMultipartS3Uploader().abortUpload();
2039
2306
  };
2040
2307
  var onDiscardAlertClose = function onDiscardAlertClose() {
@@ -2105,9 +2372,11 @@ var MediaRecorder$1 = function MediaRecorder(_ref, ref) {
2105
2372
  requested: requested
2106
2373
  });
2107
2374
  useEffect(function () {
2108
- var _cleanupFunction = noop;
2109
2375
  var handleOnScreenRecorderStart = function handleOnScreenRecorderStart() {
2110
2376
  var _recorderCallbacks, _uploaderCallbacks2;
2377
+ // Tear down any wiring left over from a previous start before adding
2378
+ // a fresh set. Safe to call even on the very first start (noop).
2379
+ activeCleanupRef.current();
2111
2380
  var multipartS3Uploader = upload ? getMultipartS3Uploader({
2112
2381
  initialize: true
2113
2382
  }) : null;
@@ -2144,21 +2413,33 @@ var MediaRecorder$1 = function MediaRecorder(_ref, ref) {
2144
2413
  Object.keys(recorderCallbacks).forEach(function (key) {
2145
2414
  screenRecorder.addCallback(key, recorderCallbacks[key]);
2146
2415
  });
2147
- if (!upload) return;
2416
+ if (!upload) {
2417
+ activeCleanupRef.current = function () {
2418
+ Object.keys(recorderCallbacks).forEach(function (key) {
2419
+ screenRecorder.removeCallback(key, recorderCallbacks[key]);
2420
+ });
2421
+ activeCleanupRef.current = noop;
2422
+ };
2423
+ return;
2424
+ }
2148
2425
  var uploaderCallbacks = (_uploaderCallbacks2 = {}, _defineProperty(_uploaderCallbacks2, UPLOAD_EVENT.onComplete, handleUploadComplete), _defineProperty(_uploaderCallbacks2, UPLOAD_EVENT.onError, handleUploadError), _defineProperty(_uploaderCallbacks2, UPLOAD_EVENT.onProgress, handleUploadProgress), _uploaderCallbacks2);
2149
2426
  Object.keys(uploaderCallbacks).forEach(function (key) {
2150
2427
  multipartS3Uploader.addCallback(key, uploaderCallbacks[key]);
2151
2428
  });
2152
- _cleanupFunction = function cleanupFunction() {
2429
+ var cleanupFunction = function cleanupFunction() {
2153
2430
  Object.keys(recorderCallbacks).forEach(function (key) {
2154
2431
  screenRecorder.removeCallback(key, recorderCallbacks[key]);
2155
2432
  });
2156
2433
  Object.keys(uploaderCallbacks).forEach(function (key) {
2157
2434
  multipartS3Uploader.removeCallback(key, uploaderCallbacks[key]);
2158
2435
  });
2159
- multipartS3Uploader.removeCallback(UPLOAD_EVENT.onAbort, _cleanupFunction);
2436
+ multipartS3Uploader.removeCallback(UPLOAD_EVENT.onAbort, cleanupFunction);
2437
+ if (activeCleanupRef.current === cleanupFunction) {
2438
+ activeCleanupRef.current = noop;
2439
+ }
2160
2440
  };
2161
- multipartS3Uploader.addCallback(UPLOAD_EVENT.onAbort, _cleanupFunction);
2441
+ activeCleanupRef.current = cleanupFunction;
2442
+ multipartS3Uploader.addCallback(UPLOAD_EVENT.onAbort, cleanupFunction);
2162
2443
  handleCreateRecording();
2163
2444
  };
2164
2445
  screenRecorder.addCallback(SCREEN_RECORDER_EVENT.onStart, handleOnScreenRecorderStart);
@@ -2166,7 +2447,7 @@ var MediaRecorder$1 = function MediaRecorder(_ref, ref) {
2166
2447
  return function () {
2167
2448
  screenRecorder.revokePermissions();
2168
2449
  screenRecorder.resetState();
2169
- _cleanupFunction();
2450
+ activeCleanupRef.current();
2170
2451
  };
2171
2452
  }, []);
2172
2453
  useEffect(function () {