@searpent/react-image-annotate 2.0.48 → 2.0.50

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.
@@ -0,0 +1,41 @@
1
+ var mockedImage = {
2
+ "id": "NA",
3
+ "src": "https://www.gannett-cdn.com/presto/2022/01/12/NJDN/8088720b-446d-4693-be62-4dfdf1c3046f-DailyNewsSaturday.jpg?width=660&height=1253&fit=crop&format=pjpg&auto=webp",
4
+ "thumbnail": "https://www.gannett-cdn.com/presto/2022/01/12/NJDN/8088720b-446d-4693-be62-4dfdf1c3046f-DailyNewsSaturday.jpg?width=660&height=1253&fit=crop&format=pjpg&auto=webp",
5
+ "name": "https://www.gannett-cdn.com/presto/2022/01/12/NJDN/8088720b-446d-4693-be62-4dfdf1c3046f-DailyNewsSaturday.jpg?width=660&height=1253&fit=crop&format=pjpg&auto=webp",
6
+ "regions": [{
7
+ "id": "346807698131517440",
8
+ "type": "box",
9
+ "visible": true,
10
+ "cls": "title",
11
+ "highlighted": false,
12
+ "groupHighlighted": false,
13
+ "x": 0.5189794,
14
+ "y": 0.043963477,
15
+ "w": 0.46070240000000007,
16
+ "h": 0.13752678299999999,
17
+ "groupId": "0",
18
+ "text": "komerční příloha čtvrtek 29. dubna 2021 Robotické sekačky na trávu - nový pohled na sekání trávy"
19
+ }, {
20
+ "id": "346807698131517441",
21
+ "type": "box",
22
+ "visible": true,
23
+ "cls": "subtitle",
24
+ "highlighted": false,
25
+ "groupHighlighted": false,
26
+ "x": 0.51976687,
27
+ "y": 0.17134483,
28
+ "w": 0.18034333000000002,
29
+ "h": 0.10370872999999997,
30
+ "groupId": "0",
31
+ "text": "Udržet trávník v perfektním stavu, znamená věnovat mu ohromné množství času a cítit se neustále odpovědný za jeho stav. Znamená to nepopulární, mnohdy obtížnou, časově náročnou práci a také starost o likvidaci zahradního odpadu. Dobrou zprávou je, že robotická sekačka na trávu všechny tyto věci obstará za Vás."
32
+ }],
33
+ "metadata": [{
34
+ "key": "page",
35
+ "value": "66"
36
+ }, {
37
+ "key": "mockedBy",
38
+ "value": "artificial server"
39
+ }]
40
+ };
41
+ export default [mockedImage];
@@ -777,7 +777,8 @@ var examplePhotos = [{
777
777
  }, {
778
778
  "key": "mutation",
779
779
  "value": "hřensko"
780
- }]
780
+ }],
781
+ "lockedUntil": ""
781
782
  }, {
782
783
  "id": "431fc636-d6d1-4e20-a35b-cc7a201c1833",
783
784
  "fullsize": {
@@ -1328,7 +1329,8 @@ var examplePhotos = [{
1328
1329
  "metadata": [{
1329
1330
  "key": "pageNumber",
1330
1331
  "value": "2"
1331
- }]
1332
+ }],
1333
+ "lockedUntil": "2023-01-28T11:35:00.000Z"
1332
1334
  }, {
1333
1335
  "id": "45f79ec8-fd2e-4ad8-82f6-007a2eec2b97",
1334
1336
  "fullsize": {
@@ -2047,7 +2049,8 @@ var examplePhotos = [{
2047
2049
  "metadata": [{
2048
2050
  "key": "pageNumber",
2049
2051
  "value": "3"
2050
- }]
2052
+ }],
2053
+ "lockedUntil": ""
2051
2054
  }, {
2052
2055
  "id": "5494ea75-ec1b-4513-b23b-0b2eaa29b576",
2053
2056
  "fullsize": {
@@ -1,9 +1,11 @@
1
1
  import _regeneratorRuntime from "@babel/runtime/regenerator";
2
2
  import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
3
+ import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
3
4
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread";
4
5
  import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
5
6
  import React, { useEffect, useReducer } from "react";
6
7
  import makeImmutable, { without } from "seamless-immutable";
8
+ import intersection from "lodash/intersection";
7
9
  import MainLayout from "../MainLayout";
8
10
  import SettingsProvider from "../SettingsProvider";
9
11
  import combineReducers from "./reducers/combine-reducers.js";
@@ -13,6 +15,8 @@ import historyHandler from "./reducers/history-handler.js";
13
15
  import imageReducer from "./reducers/image-reducer.js";
14
16
  import useEventCallback from "use-event-callback";
15
17
  import videoReducer from "./reducers/video-reducer.js";
18
+ import { reacalcActionsEnum } from "../utils/saveable-actions-enum";
19
+ import sleep from '../utils/sleep';
16
20
  export var Annotator = function Annotator(_ref) {
17
21
  var images = _ref.images,
18
22
  allowedArea = _ref.allowedArea,
@@ -79,8 +83,10 @@ export var Annotator = function Annotator(_ref) {
79
83
  onSelectedImageChange = _ref.onSelectedImageChange,
80
84
  albumMetadata = _ref.albumMetadata,
81
85
  metadataConfigs = _ref.metadataConfigs,
82
- _ref$lockedImages = _ref.lockedImages,
83
- lockedImages = _ref$lockedImages === void 0 ? [] : _ref$lockedImages;
86
+ _ref$save = _ref.save,
87
+ save = _ref$save === void 0 ? function () {} : _ref$save,
88
+ _ref$fetchImage = _ref.fetchImage,
89
+ fetchImage = _ref$fetchImage === void 0 ? function () {} : _ref$fetchImage;
84
90
 
85
91
  if (typeof selectedImage === "string") {
86
92
  selectedImage = (images || []).findIndex(function (img) {
@@ -126,7 +132,12 @@ export var Annotator = function Annotator(_ref) {
126
132
  imagesUpdatedAt: null,
127
133
  imagesSavedAt: null,
128
134
  albumMetadata: albumMetadata,
129
- metadataConfigs: metadataConfigs
135
+ metadataConfigs: metadataConfigs,
136
+ toPollImages: _toConsumableArray(images.filter(function (i) {
137
+ return i.lockedUntil;
138
+ }).map(function (i) {
139
+ return i.id;
140
+ }))
130
141
  }))),
131
142
  _useReducer2 = _slicedToArray(_useReducer, 2),
132
143
  state = _useReducer2[0],
@@ -255,39 +266,190 @@ export var Annotator = function Annotator(_ref) {
255
266
  type: "ADD_GROUP",
256
267
  group: group
257
268
  });
258
- }; // trigger onSelectedImageChange() hook when image changed
269
+ }; // trigger save and recalc
259
270
 
260
271
 
261
272
  useEffect(function () {
262
- var _state$lastAction;
273
+ var selectedImage = state.selectedImage,
274
+ previouslySelectedImage = state.previouslySelectedImage,
275
+ lastAction = state.lastAction;
263
276
 
264
- var createdAt = new Date();
277
+ if ((lastAction === null || lastAction === void 0 ? void 0 : lastAction.type) === 'SELECT_IMAGE' && selectedImage !== previouslySelectedImage) {
278
+ var _state$images$previou, _state$images$previou2;
265
279
 
266
- if (typeof onSelectedImageChange === 'function' && ((_state$lastAction = state.lastAction) === null || _state$lastAction === void 0 ? void 0 : _state$lastAction.type) === 'SELECT_IMAGE' && (state === null || state === void 0 ? void 0 : state.selectedImage) !== state.previouslySelectedImage) {
267
- onSelectedImageChange({
268
- selectedImage: state === null || state === void 0 ? void 0 : state.selectedImage,
269
- previouslySelectedImage: state.previouslySelectedImage,
270
- images: state.images,
271
- albumMetadata: state.albumMetadata,
272
- createdAt: createdAt
273
- });
280
+ // metadata on album level
281
+ var saveHandler =
282
+ /*#__PURE__*/
283
+ function () {
284
+ var _ref4 = _asyncToGenerator(
285
+ /*#__PURE__*/
286
+ _regeneratorRuntime.mark(function _callee3(image, triggerRecalc, albumMetadata) {
287
+ var _ref5, lockedUntil;
288
+
289
+ return _regeneratorRuntime.wrap(function _callee3$(_context3) {
290
+ while (1) {
291
+ switch (_context3.prev = _context3.next) {
292
+ case 0:
293
+ dispatchToReducer({
294
+ type: "IMAGE_UPDATE_INIT",
295
+ imageId: image.id
296
+ });
297
+ _context3.prev = 1;
298
+ _context3.next = 4;
299
+ return save({
300
+ image: image,
301
+ triggerRecalc: triggerRecalc,
302
+ albumMetadata: albumMetadata
303
+ });
304
+
305
+ case 4:
306
+ _ref5 = _context3.sent;
307
+ lockedUntil = _ref5.lockedUntil;
308
+ dispatchToReducer({
309
+ type: "IMAGE_UPDATE_SUCCESS",
310
+ imageId: image.id,
311
+ lockedUntil: lockedUntil
312
+ });
313
+ _context3.next = 12;
314
+ break;
315
+
316
+ case 9:
317
+ _context3.prev = 9;
318
+ _context3.t0 = _context3["catch"](1);
319
+ dispatchToReducer({
320
+ type: "IMAGE_UPDATE_FAIL",
321
+ imageId: image.id,
322
+ error: _context3.t0
323
+ });
324
+
325
+ case 12:
326
+ case "end":
327
+ return _context3.stop();
328
+ }
329
+ }
330
+ }, _callee3, null, [[1, 9]]);
331
+ }));
332
+
333
+ return function saveHandler(_x3, _x4, _x5) {
334
+ return _ref4.apply(this, arguments);
335
+ };
336
+ }(); // save if previously selected image has any changes
337
+
338
+
339
+ if (((_state$images$previou = state.images[previouslySelectedImage]) === null || _state$images$previou === void 0 ? void 0 : (_state$images$previou2 = _state$images$previou.saveableActions) === null || _state$images$previou2 === void 0 ? void 0 : _state$images$previou2.length) > 0) {
340
+ var _state$images$previou3, _state$images$previou4;
341
+
342
+ // decide wheather recalc is needed
343
+ var triggerRecalc = intersection(reacalcActionsEnum, state.images[previouslySelectedImage].saveableActions).length > 0; // decide wheather album metadata should be updated
344
+
345
+ var toSaveMetadata = [];
346
+
347
+ if ((_state$images$previou3 = state.images[previouslySelectedImage]) === null || _state$images$previou3 === void 0 ? void 0 : (_state$images$previou4 = _state$images$previou3.saveableActions) === null || _state$images$previou4 === void 0 ? void 0 : _state$images$previou4.includes("UPDATE_ALBUM_METADATA")) {
348
+ toSaveMetadata = state.albumMetadata;
349
+ } // save image
350
+
351
+
352
+ saveHandler(_objectSpread({}, state.images[previouslySelectedImage]), triggerRecalc, toSaveMetadata);
353
+ }
274
354
  }
275
- }, [state.previouslySelectedImage, state.selectedImage, onSelectedImageChange, state.images, state.albumMetadata, state]); // trigger this on every BBox manipulation (there is currently no way to detect adding of new box!)
355
+ }, [state.previouslySelectedImage, state.selectedImage, state.images, state, save]); // polling of images
276
356
 
277
357
  useEffect(function () {
278
- if (!state.lastAction || !["BEGIN_BOX_TRANSFORM", "CHANGE_REGION", "DELETE_REGION", "DELETE_SELECTED_REGION", "UPDATE_METADATA", "SELECT_CLASSIFICATION"].includes(state.lastAction.type)) {
279
- return;
280
- }
358
+ if (state.toPollImages.length > 0) {
359
+ var polledImages = state.toPollImages.reduce(function (acc, imageId) {
360
+ function pollImage(_x6, _x7) {
361
+ return _pollImage.apply(this, arguments);
362
+ } // make recursive calling of polling function
363
+
281
364
 
282
- if (onImagesChange) {
283
- onImagesChange(state.images);
365
+ function _pollImage() {
366
+ _pollImage = _asyncToGenerator(
367
+ /*#__PURE__*/
368
+ _regeneratorRuntime.mark(function _callee4(fetchImage, imageId) {
369
+ var tries,
370
+ _ref6,
371
+ image,
372
+ _args4 = arguments;
373
+
374
+ return _regeneratorRuntime.wrap(function _callee4$(_context4) {
375
+ while (1) {
376
+ switch (_context4.prev = _context4.next) {
377
+ case 0:
378
+ tries = _args4.length > 2 && _args4[2] !== undefined ? _args4[2] : 5;
379
+
380
+ if (!(tries === 0)) {
381
+ _context4.next = 4;
382
+ break;
383
+ }
384
+
385
+ dispatchToReducer({
386
+ type: "IMAGE_POLL_TIMEOUT",
387
+ imageId: imageId
388
+ });
389
+ return _context4.abrupt("return");
390
+
391
+ case 4:
392
+ _context4.next = 6;
393
+ return fetchImage({
394
+ imageId: imageId
395
+ });
396
+
397
+ case 6:
398
+ _ref6 = _context4.sent;
399
+ image = _ref6.image;
400
+
401
+ if (image.lockedUntil) {
402
+ _context4.next = 11;
403
+ break;
404
+ }
405
+
406
+ dispatchToReducer({
407
+ type: "IMAGE_POLL_SUCCESS",
408
+ image: image
409
+ });
410
+ return _context4.abrupt("return");
411
+
412
+ case 11:
413
+ _context4.next = 13;
414
+ return sleep(5000);
415
+
416
+ case 13:
417
+ _context4.next = 15;
418
+ return pollImage(fetchImage, imageId, tries - 1);
419
+
420
+ case 15:
421
+ return _context4.abrupt("return", _context4.sent);
422
+
423
+ case 16:
424
+ case "end":
425
+ return _context4.stop();
426
+ }
427
+ }
428
+ }, _callee4);
429
+ }));
430
+ return _pollImage.apply(this, arguments);
431
+ }
432
+
433
+ pollImage(fetchImage, imageId, 10);
434
+ return imageId;
435
+ }, []);
436
+ dispatchToReducer({
437
+ type: "IMAGE_POLL_INIT",
438
+ imageIds: polledImages
439
+ });
284
440
  }
441
+ }, [fetchImage, state.toPollImages]); // // TODO: delete this when work done
442
+ // useEffect(() => {
443
+ // if (!state.lastAction || !["BEGIN_BOX_TRANSFORM", "CHANGE_REGION", "DELETE_REGION", "DELETE_SELECTED_REGION", "UPDATE_METADATA", "SELECT_CLASSIFICATION"].includes(state.lastAction.type)) { return }
444
+ // if (onImagesChange) {
445
+ // onImagesChange(state.images)
446
+ // }
447
+ // dispatchToReducer({
448
+ // type: "IMAGES_UPDATED",
449
+ // updatedAt: new Date()
450
+ // })
451
+ // }, [onImagesChange, state.images, state.lastAction])
285
452
 
286
- dispatchToReducer({
287
- type: "IMAGES_UPDATED",
288
- updatedAt: new Date()
289
- });
290
- }, [onImagesChange, state.images, state.lastAction]);
291
453
  useEffect(function () {
292
454
  if (selectedImage === undefined) return;
293
455
  dispatchToReducer({
@@ -298,10 +460,10 @@ export var Annotator = function Annotator(_ref) {
298
460
  }, [onImagesChange, selectedImage]);
299
461
  if (!images && !videoSrc) return 'Missing required prop "images" or "videoSrc"';
300
462
 
301
- var _ref4 = state.imagesSavedAt < state.imagesUpdatedAt ? [true, true] : [false, false],
302
- _ref5 = _slicedToArray(_ref4, 2),
303
- recalcActive = _ref5[0],
304
- saveActive = _ref5[1];
463
+ var _ref7 = state.imagesSavedAt < state.imagesUpdatedAt ? [true, true] : [false, false],
464
+ _ref8 = _slicedToArray(_ref7, 2),
465
+ recalcActive = _ref8[0],
466
+ saveActive = _ref8[1];
305
467
 
306
468
  return React.createElement(SettingsProvider, {
307
469
  clsColors: clsColors,
@@ -332,8 +494,7 @@ export var Annotator = function Annotator(_ref) {
332
494
  saveActive: recalcActive,
333
495
  recalcActive: saveActive,
334
496
  onMetadataChange: handleMetadataChange,
335
- onAddGroup: handleAddGroup,
336
- lockedImages: lockedImages
497
+ onAddGroup: handleAddGroup
337
498
  }));
338
499
  };
339
500
  export default Annotator;
@@ -5,6 +5,7 @@ import { moveRegion } from "../../ImageCanvas/region-tools.js";
5
5
  import { getIn, setIn, updateIn } from "seamless-immutable";
6
6
  import moment from "moment";
7
7
  import isEqual from "lodash/isEqual";
8
+ import uniq from "lodash/uniq";
8
9
  import getActiveImage from "./get-active-image";
9
10
  import { saveToHistory } from "./history-handler.js";
10
11
  import colors from "../../colors";
@@ -16,6 +17,7 @@ import setInLocalStorage from "../../utils/set-in-local-storage";
16
17
  import onlyUnique from "../../utils/filter-only-unique";
17
18
  import regionsGroups from '../../utils/regions-groups';
18
19
  import nextGroupId from "../../utils/next-group-id";
20
+ import defaultLockedUntil from "../../utils/default-locked-until";
19
21
 
20
22
  var getRandomId = function getRandomId() {
21
23
  return Math.random().toString().split(".")[1];
@@ -107,6 +109,11 @@ export default (function (state, action) {
107
109
  }));
108
110
  };
109
111
 
112
+ var addSaveableAction = function addSaveableAction(state, action) {
113
+ if (currentImageIndex === null) return state;
114
+ return setIn(state, [].concat(_toConsumableArray(pathToActiveImage), ["saveableActions"]), [].concat(_toConsumableArray(activeImage.saveableActions || []), [action]));
115
+ };
116
+
110
117
  var setNewImage = function setNewImage(img, index) {
111
118
  var _ref = typeof img === "object" ? img : {
112
119
  src: img
@@ -151,6 +158,7 @@ export default (function (state, action) {
151
158
  state = setIn(state, [].concat(_toConsumableArray(pathToActiveImage), ["regions"]), newRegions);
152
159
  }
153
160
 
161
+ state = addSaveableAction(state, "SELECT_CLASSIFICATION");
154
162
  return setIn(state, ["selectedCls"], action.cls);
155
163
  }
156
164
 
@@ -178,6 +186,7 @@ export default (function (state, action) {
178
186
  state = saveToHistory(state, "Change Region Comment");
179
187
  }
180
188
 
189
+ state = addSaveableAction(state, "CHANGE_REGION");
181
190
  return setIn(state, [].concat(_toConsumableArray(pathToActiveImage), ["regions", regionIndex]), action.region);
182
191
  }
183
192
 
@@ -846,11 +855,13 @@ export default (function (state, action) {
846
855
 
847
856
  if (state.mode.isNew) {
848
857
  if (Math.abs(state.mode.original.x - _x2) < 0.002 || Math.abs(state.mode.original.y - _y2) < 0.002) {
858
+ state = addSaveableAction(state, "MOUSE_UP_RESIZE_BOX");
849
859
  return setIn(modifyRegion(state.mode.regionId, null), ["mode"], null);
850
860
  }
851
861
  }
852
862
 
853
863
  if (state.mode.editLabelEditorAfter) {
864
+ state = addSaveableAction(state, "MOUSE_UP_RESIZE_BOX");
854
865
  return _objectSpread({}, modifyRegion(state.mode.regionId, {
855
866
  editingLabels: true
856
867
  }), {
@@ -863,6 +874,7 @@ export default (function (state, action) {
863
874
  case "RESIZE_KEYPOINTS":
864
875
  case "MOVE_POLYGON_POINT":
865
876
  {
877
+ state = addSaveableAction(state, "MOUSE_UP_MOVE_REGION");
866
878
  return _objectSpread({}, state, {
867
879
  mode: null
868
880
  });
@@ -965,6 +977,7 @@ export default (function (state, action) {
965
977
 
966
978
  if (_regionIndex18 === null) return state;
967
979
  state = saveToHistory(state, "Delete region");
980
+ state = addSaveableAction(state, "DELETE_REGION");
968
981
  return setIn(state, [].concat(_toConsumableArray(pathToActiveImage), ["regions"]), (activeImage.regions || []).filter(function (r) {
969
982
  return r.id !== action.region.id;
970
983
  }));
@@ -975,6 +988,7 @@ export default (function (state, action) {
975
988
  var _groupId = action.groupId;
976
989
  if (_groupId === null || _groupId === undefined) return state;
977
990
  state = saveToHistory(state, "Delete group");
991
+ state = addSaveableAction(state, "DELETE_GROUP");
978
992
  return setIn(state, [].concat(_toConsumableArray(pathToActiveImage), ["regions"]), (activeImage.regions || []).filter(function (r) {
979
993
  return r.groupId !== _groupId;
980
994
  }));
@@ -1141,6 +1155,7 @@ export default (function (state, action) {
1141
1155
  });
1142
1156
  }); // TODO: add mutation of order and deletion of regions - SI-1967
1143
1157
 
1158
+ state = addSaveableAction(state, "UPDATE_REGIONS");
1144
1159
  return setIn(state, ["images", imageIndex, "regions"], updatedRegions);
1145
1160
  }
1146
1161
 
@@ -1182,6 +1197,7 @@ export default (function (state, action) {
1182
1197
  return state;
1183
1198
  }
1184
1199
 
1200
+ state = addSaveableAction(state, "UPDATE_ALBUM_METADATA");
1185
1201
  return setIn(state, ["albumMetadata", metadataIndex], {
1186
1202
  key: name,
1187
1203
  value: value
@@ -1214,6 +1230,7 @@ export default (function (state, action) {
1214
1230
  }
1215
1231
 
1216
1232
  articleMetadata[toBeUpdatedMetadataIdx].value = value;
1233
+ state = addSaveableAction(state, "UPDATE_METADATA");
1217
1234
  return setIn(state, ["images", _imageIndex, "regions", articleMetadataRegionIdx], _objectSpread({}, articleRegionToUpdate, {
1218
1235
  text: JSON.stringify(articleMetadata)
1219
1236
  }));
@@ -1229,6 +1246,7 @@ export default (function (state, action) {
1229
1246
  return state;
1230
1247
  }
1231
1248
 
1249
+ state = addSaveableAction(state, "UPDATE_METADATA");
1232
1250
  return setIn(state, ["images", _imageIndex, "metadata", _metadataIndex], {
1233
1251
  key: name,
1234
1252
  value: value
@@ -1248,6 +1266,111 @@ export default (function (state, action) {
1248
1266
  // )
1249
1267
  }
1250
1268
 
1269
+ case "IMAGE_UPDATE_INIT":
1270
+ {
1271
+ var imageId = action.imageId;
1272
+ var imageIdx = state.images.findIndex(function (i) {
1273
+ return i.id === imageId;
1274
+ });
1275
+
1276
+ if (imageIdx < 0) {
1277
+ throw new Error("failed to find index of image with id ".concat(imageId));
1278
+ }
1279
+
1280
+ return setIn(state, ["images", imageIdx], _objectSpread({}, state.images[imageIdx], {
1281
+ lockedUntil: defaultLockedUntil(),
1282
+ syncError: null,
1283
+ saveableActions: []
1284
+ }));
1285
+ }
1286
+
1287
+ case "IMAGE_UPDATE_SUCCESS":
1288
+ {
1289
+ var _imageId = action.imageId,
1290
+ lockedUntil = action.lockedUntil;
1291
+
1292
+ var _imageIdx = state.images.findIndex(function (i) {
1293
+ return i.id === _imageId;
1294
+ });
1295
+
1296
+ if (_imageIdx < 0) {
1297
+ throw new Error("failed to find index of image with id ".concat(_imageId));
1298
+ } // if there is lockedUntil set, it means we need to poll for updated
1299
+
1300
+
1301
+ if (lockedUntil) {
1302
+ state = setIn(state, ["toPollImages"], uniq([].concat(_toConsumableArray(state.toPollImages), [_imageId])));
1303
+ }
1304
+
1305
+ return setIn(state, ["images", _imageIdx], _objectSpread({}, state.images[_imageIdx], {
1306
+ lockedUntil: lockedUntil,
1307
+ syncError: null
1308
+ }));
1309
+ }
1310
+
1311
+ case "IMAGE_UPDATE_FAIL":
1312
+ {
1313
+ var _imageId2 = action.imageId,
1314
+ error = action.error;
1315
+
1316
+ var _imageIdx2 = state.images.findIndex(function (i) {
1317
+ return i.id === _imageId2;
1318
+ });
1319
+
1320
+ if (_imageIdx2 < 0) {
1321
+ throw new Error("failed to find index of image with id ".concat(_imageId2));
1322
+ }
1323
+
1324
+ return setIn(state, ["images", _imageIdx2], _objectSpread({}, state.images[_imageIdx2], {
1325
+ lockedUntil: null,
1326
+ syncError: error
1327
+ }));
1328
+ }
1329
+
1330
+ case "IMAGE_POLL_INIT":
1331
+ {
1332
+ var imageIds = action.imageIds;
1333
+ return setIn(state, ["toPollImages"], state.toPollImages.filter(function (i) {
1334
+ return !imageIds.includes(i);
1335
+ }));
1336
+ }
1337
+
1338
+ case "IMAGE_POLL_SUCCESS":
1339
+ {
1340
+ var image = action.image;
1341
+
1342
+ var _imageIdx3 = state.images.findIndex(function (i) {
1343
+ return i.id === image.id;
1344
+ });
1345
+
1346
+ if (_imageIdx3 < 0) {
1347
+ throw new Error("failed to find index of image with id ".concat(image.id));
1348
+ }
1349
+
1350
+ return setIn(state, ["images", _imageIdx3], _objectSpread({}, state.images[_imageIdx3], {
1351
+ lockedUntil: null,
1352
+ syncError: null
1353
+ }, image));
1354
+ }
1355
+
1356
+ case "IMAGE_POLL_TIMEOUT":
1357
+ {
1358
+ var _imageId3 = action.imageId;
1359
+
1360
+ var _imageIdx4 = state.images.findIndex(function (i) {
1361
+ return i.id === _imageId3;
1362
+ });
1363
+
1364
+ if (_imageIdx4 < 0) {
1365
+ throw new Error("failed to find index of image with id ".concat(_imageId3));
1366
+ }
1367
+
1368
+ return setIn(state, ["images", _imageIdx4], _objectSpread({}, state.images[_imageIdx4], {
1369
+ lockedUntil: null,
1370
+ syncError: new Error("polling timeout")
1371
+ }));
1372
+ }
1373
+
1251
1374
  default:
1252
1375
  break;
1253
1376
  }
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import ErrorIcon from '@mui/icons-material/Error';
3
+ import './errorer.css';
4
+
5
+ function Errorer() {
6
+ return React.createElement("div", {
7
+ className: "errorer"
8
+ }, React.createElement(ErrorIcon, null));
9
+ }
10
+
11
+ export default Errorer;
@@ -85,7 +85,7 @@ var EditorWrapper = styled("div")(function (_ref4) {
85
85
  };
86
86
  });
87
87
  export var MainLayout = function MainLayout(_ref5) {
88
- var _state$images$state$s, _state$images$state$s2, _state$images$state$s3;
88
+ var _state$images$state$s, _state$images$state$s2;
89
89
 
90
90
  var state = _ref5.state,
91
91
  dispatch = _ref5.dispatch,
@@ -130,8 +130,7 @@ export var MainLayout = function MainLayout(_ref5) {
130
130
  _ref5$saveActive = _ref5.saveActive,
131
131
  saveActive = _ref5$saveActive === void 0 ? false : _ref5$saveActive,
132
132
  onMetadataChange = _ref5.onMetadataChange,
133
- onAddGroup = _ref5.onAddGroup,
134
- lockedImages = _ref5.lockedImages;
133
+ onAddGroup = _ref5.onAddGroup;
135
134
  var classes = useStyles();
136
135
  var settings = useSettings();
137
136
  var fullScreenHandle = useFullScreenHandle();
@@ -304,7 +303,9 @@ export var MainLayout = function MainLayout(_ref5) {
304
303
  pageNumber: (i === null || i === void 0 ? void 0 : (_i$metadata = i.metadata) === null || _i$metadata === void 0 ? void 0 : (_i$metadata$find = _i$metadata.find(function (md) {
305
304
  return md.key === "pageNumber";
306
305
  })) === null || _i$metadata$find === void 0 ? void 0 : _i$metadata$find.value) || null,
307
- metadata: i.metadata || []
306
+ metadata: i.metadata || [],
307
+ lockedUntil: i.lockedUntil,
308
+ syncError: i.syncError || null
308
309
  };
309
310
  });
310
311
 
@@ -315,7 +316,7 @@ export var MainLayout = function MainLayout(_ref5) {
315
316
  });
316
317
  };
317
318
 
318
- var isSelectedImageLocked = lockedImages.includes(state === null || state === void 0 ? void 0 : (_state$images$state$s3 = state.images[state.selectedImage]) === null || _state$images$state$s3 === void 0 ? void 0 : _state$images$state$s3.id);
319
+ var isSelectedImageLocked = false;
319
320
  return React.createElement(ThemeProvider, {
320
321
  theme: theme
321
322
  }, React.createElement(FullScreenContainer, null, React.createElement(FullScreen, {
@@ -341,7 +342,6 @@ export var MainLayout = function MainLayout(_ref5) {
341
342
  }
342
343
  }, showPageSelector && React.createElement(PageSelector, {
343
344
  pages: pages,
344
- lockedImages: lockedImages,
345
345
  onPageClick: handlePageClick,
346
346
  onRecalc: onRecalc,
347
347
  onSave: onSave,
@@ -3,6 +3,7 @@ import React, { useState } from 'react';
3
3
  import classnames from "classnames";
4
4
  import './page-selector.css';
5
5
  import Locker from '../Locker';
6
+ import Errorer from '../Errorer';
6
7
 
7
8
  function PageThumbnail(_ref) {
8
9
  var _metadata$find, _metadata$find$call;
@@ -16,7 +17,8 @@ function PageThumbnail(_ref) {
16
17
  onMetadataChange = _ref.onMetadataChange,
17
18
  _ref$metadataConfigs = _ref.metadataConfigs,
18
19
  metadataConfigs = _ref$metadataConfigs === void 0 ? [] : _ref$metadataConfigs,
19
- isLocked = _ref.isLocked;
20
+ isLocked = _ref.isLocked,
21
+ error = _ref.error;
20
22
 
21
23
  var handleChange = function handleChange(e) {
22
24
  e.preventDefault();
@@ -41,7 +43,9 @@ function PageThumbnail(_ref) {
41
43
  'ps-page-thumbnail-disabled': isLocked
42
44
  }),
43
45
  onClick: onClick
44
- }, isLocked && React.createElement(Locker, null), React.createElement("div", {
46
+ }, isLocked && React.createElement(Locker, null), error && React.createElement(Errorer, {
47
+ errorMessage: error.message
48
+ }), React.createElement("div", {
45
49
  className: "ps-page-thumbnail-image-wrapper"
46
50
  }, React.createElement("img", {
47
51
  src: src,
@@ -83,6 +87,18 @@ function PageThumbnail(_ref) {
83
87
  })));
84
88
  }
85
89
 
90
+ function isLocked(page) {
91
+ var _Date$parse;
92
+
93
+ var lockedUntil = page.lockedUntil; // needs to be defined and greater than current time
94
+
95
+ if (((_Date$parse = Date.parse(lockedUntil)) === null || _Date$parse === void 0 ? void 0 : _Date$parse.valueOf()) > new Date().valueOf()) {
96
+ return true;
97
+ }
98
+
99
+ return false;
100
+ }
101
+
86
102
  function PageSelector(_ref3) {
87
103
  var pages = _ref3.pages,
88
104
  onPageClick = _ref3.onPageClick,
@@ -91,9 +107,7 @@ function PageSelector(_ref3) {
91
107
  recalcActive = _ref3.recalcActive,
92
108
  saveActive = _ref3.saveActive,
93
109
  onMetadataChange = _ref3.onMetadataChange,
94
- metadataConfigs = _ref3.metadataConfigs,
95
- _ref3$lockedImages = _ref3.lockedImages,
96
- lockedImages = _ref3$lockedImages === void 0 ? [] : _ref3$lockedImages;
110
+ metadataConfigs = _ref3.metadataConfigs;
97
111
 
98
112
  var _useState = useState(false),
99
113
  _useState2 = _slicedToArray(_useState, 2),
@@ -134,7 +148,8 @@ function PageSelector(_ref3) {
134
148
  }, pages.map(function (page, idx) {
135
149
  return React.createElement(PageThumbnail, {
136
150
  key: "".concat(page.id),
137
- isLocked: lockedImages.includes(page.id),
151
+ isLocked: isLocked(page),
152
+ error: page.syncError,
138
153
  src: page.src,
139
154
  isActive: page.isActive,
140
155
  onClick: function onClick() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@searpent/react-image-annotate",
3
- "version": "2.0.48",
3
+ "version": "2.0.50",
4
4
  "dependencies": {
5
5
  "@editorjs/editorjs": "^2.25.0",
6
6
  "@editorjs/paragraph": "^2.8.0",
@@ -0,0 +1,6 @@
1
+ export default function defaultLockedUntil() {
2
+ var now = new Date();
3
+ now.setDate(now.getDate() + 1); // Add 1 day to current date
4
+
5
+ return now;
6
+ }
@@ -76,7 +76,8 @@ function photosToImages(photos) {
76
76
  thumbnail: photo.thumbnail.key,
77
77
  name: photo.fullsize.key,
78
78
  regions: modelResultsToRegions(photo.modelResults.v1),
79
- metadata: photo.metadata
79
+ metadata: photo.metadata,
80
+ lockedUntil: photo.lockedUntil
80
81
  };
81
82
  });
82
83
  }
@@ -0,0 +1,3 @@
1
+ var reacalcActionsEnum = ["MOUSE_UP_RESIZE_BOX", "DELETE_REGION", "DELETE_GROUP", "MOUSE_UP_MOVE_REGION"];
2
+ var saveableActionsEnum = [reacalcActionsEnum, "SELECT_CLASSIFICATION", "CHANGE_REGION", "UPDATE_REGIONS", "UPDATE_METADATA", "UPDATE_ALBUM_METADATA"];
3
+ export { saveableActionsEnum, reacalcActionsEnum };
package/utils/sleep.js ADDED
@@ -0,0 +1,7 @@
1
+ var sleep = function sleep(ms) {
2
+ return new Promise(function (resolve) {
3
+ return setTimeout(resolve, ms);
4
+ });
5
+ };
6
+
7
+ export default sleep;