@searpent/react-image-annotate 2.0.47 → 2.0.49

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];
@@ -686,55 +686,20 @@ var examplePhotos = [{
686
686
  "text": "[{\"label\":\"author\",\"text\":\"MET\"}]",
687
687
  "groupId": "5",
688
688
  "id": "346807506053365764"
689
- }, {
690
- "label": "title",
691
- "score": 0.99,
692
- "box": {
693
- "X1": 0.7056229,
694
- "X2": 0.9718065,
695
- "Y1": 0.049599823,
696
- "Y2": 0.18825386
697
- },
698
- "text": "[{\"label\":\"title\",\"text\":\"Za lidská práva! Vadí Čína i Katar\"}]",
699
- "groupId": "6",
700
- "id": "346807506909003776"
701
- }, {
702
- "label": "text",
703
- "score": 0.99,
704
- "box": {
705
- "X1": 0.70483536,
706
- "X2": 0.7946133,
707
- "Y1": 0.1657085,
708
- "Y2": 0.53319806
709
- },
710
- "text": "[{\"label\":\"text\",\"text\":\"\"}]",
711
- "groupId": "6",
712
- "id": "346807506909003777"
713
- }, {
714
- "label": "text",
715
- "score": 0.99,
716
- "box": {
717
- "X1": 0.7985509,
718
- "X2": 0.8875413,
719
- "Y1": 0.33028972,
720
- "Y2": 0.53432536
721
- },
722
- "text": "[{\"label\":\"text\",\"text\":\"\"}]",
723
- "groupId": "6",
724
- "id": "346807506909003778"
725
- }, {
726
- "label": "text",
727
- "score": 0.99,
728
- "box": {
729
- "X1": 0.89147896,
730
- "X2": 0.98046935,
731
- "Y1": 0.33028972,
732
- "Y2": 0.53432536
733
- },
734
- "text": "[{\"label\":\"text\",\"text\":\"\"}]",
735
- "groupId": "6",
736
- "id": "346807506913198080"
737
- }, {
689
+ }, // {
690
+ // "label": "title",
691
+ // "score": 0.99,
692
+ // "box": {
693
+ // "X1": 0.7056229,
694
+ // "X2": 0.9718065,
695
+ // "Y1": 0.049599823,
696
+ // "Y2": 0.18825386
697
+ // },
698
+ // "text": "[{\"label\":\"title\",\"text\":\"Za lidská práva! Vadí Čína i Katar\"}]",
699
+ // "groupId": "6",
700
+ // "id": "346807506909003776"
701
+ // },
702
+ {
738
703
  "label": "photo_caption",
739
704
  "score": 0.99,
740
705
  "box": {
@@ -743,33 +708,35 @@ var examplePhotos = [{
743
708
  "Y1": 0.25025365,
744
709
  "Y2": 0.3415624
745
710
  },
746
- "text": "[{\"label\":\"photo_caption\",\"text\":\"Protest z února v Indii proti konání ZOH v Pekingu\"}]",
711
+ "text": "[{\"label\":\"photo_caption\",\"text\":\"Protest n z února v Indii,\\n\\tpred proti v Pekingu.\"}]",
747
712
  "groupId": "6",
748
- "id": "346807506913198081"
749
- }, {
750
- "label": "photo_caption",
751
- "score": 0.99,
752
- "box": {
753
- "X1": 0.80248857,
754
- "X2": 0.95999366,
755
- "Y1": 0.32127157,
756
- "Y2": 0.34381694
757
- },
758
- "text": "[{\"label\":\"photo_caption\",\"text\":\"Protest z února v Indii proti konání ZOH v Pekingu\"}]",
759
- "groupId": "6",
760
- "id": "346807506913198082"
761
- }, {
762
- "label": "author",
763
- "score": 0.99,
764
- "box": {
765
- "X1": 0.9584186,
766
- "X2": 0.9733816,
767
- "Y1": 0.51628906,
768
- "Y2": 0.53319806
769
- },
770
- "text": "[{\"label\":\"author\",\"text\":\"PUR\"}]",
771
- "groupId": "6",
772
- "id": "346807506913198083"
713
+ "id": "346807506913198081" // {
714
+ // "label": "photo_caption",
715
+ // "score": 0.99,
716
+ // "box": {
717
+ // "X1": 0.80248857,
718
+ // "X2": 0.95999366,
719
+ // "Y1": 0.32127157,
720
+ // "Y2": 0.34381694
721
+ // },
722
+ // "text": "[{\"label\":\"photo_caption\",\"text\":\"Protest z února v Indii proti konání ZOH v Pekingu\"}]",
723
+ // "groupId": "6",
724
+ // "id": "346807506913198082"
725
+ // },
726
+ // {
727
+ // "label": "author",
728
+ // "score": 0.99,
729
+ // "box": {
730
+ // "X1": 0.9584186,
731
+ // "X2": 0.9733816,
732
+ // "Y1": 0.51628906,
733
+ // "Y2": 0.53319806
734
+ // },
735
+ // "text": "[{\"label\":\"author\",\"text\":\"PUR\"}]",
736
+ // "groupId": "6",
737
+ // "id": "346807506913198083"
738
+ // }
739
+
773
740
  }]
774
741
  }, {
775
742
  "modelFamily": "metadata-engine",
@@ -810,7 +777,8 @@ var examplePhotos = [{
810
777
  }, {
811
778
  "key": "mutation",
812
779
  "value": "hřensko"
813
- }]
780
+ }],
781
+ "lockedUntil": ""
814
782
  }, {
815
783
  "id": "431fc636-d6d1-4e20-a35b-cc7a201c1833",
816
784
  "fullsize": {
@@ -1361,7 +1329,8 @@ var examplePhotos = [{
1361
1329
  "metadata": [{
1362
1330
  "key": "pageNumber",
1363
1331
  "value": "2"
1364
- }]
1332
+ }],
1333
+ "lockedUntil": "2020-01-18T11:35:00.000Z"
1365
1334
  }, {
1366
1335
  "id": "45f79ec8-fd2e-4ad8-82f6-007a2eec2b97",
1367
1336
  "fullsize": {
@@ -2080,7 +2049,8 @@ var examplePhotos = [{
2080
2049
  "metadata": [{
2081
2050
  "key": "pageNumber",
2082
2051
  "value": "3"
2083
- }]
2052
+ }],
2053
+ "lockedUntil": ""
2084
2054
  }, {
2085
2055
  "id": "5494ea75-ec1b-4513-b23b-0b2eaa29b576",
2086
2056
  "fullsize": {
@@ -4,6 +4,7 @@ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread";
4
4
  import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
5
5
  import React, { useEffect, useReducer } from "react";
6
6
  import makeImmutable, { without } from "seamless-immutable";
7
+ import intersection from "lodash/intersection";
7
8
  import MainLayout from "../MainLayout";
8
9
  import SettingsProvider from "../SettingsProvider";
9
10
  import combineReducers from "./reducers/combine-reducers.js";
@@ -13,6 +14,8 @@ import historyHandler from "./reducers/history-handler.js";
13
14
  import imageReducer from "./reducers/image-reducer.js";
14
15
  import useEventCallback from "use-event-callback";
15
16
  import videoReducer from "./reducers/video-reducer.js";
17
+ import { reacalcActionsEnum } from "../utils/saveable-actions-enum";
18
+ import sleep from '../utils/sleep';
16
19
  export var Annotator = function Annotator(_ref) {
17
20
  var images = _ref.images,
18
21
  allowedArea = _ref.allowedArea,
@@ -79,8 +82,10 @@ export var Annotator = function Annotator(_ref) {
79
82
  onSelectedImageChange = _ref.onSelectedImageChange,
80
83
  albumMetadata = _ref.albumMetadata,
81
84
  metadataConfigs = _ref.metadataConfigs,
82
- _ref$lockedImages = _ref.lockedImages,
83
- lockedImages = _ref$lockedImages === void 0 ? [] : _ref$lockedImages;
85
+ _ref$save = _ref.save,
86
+ save = _ref$save === void 0 ? function () {} : _ref$save,
87
+ _ref$fetchImage = _ref.fetchImage,
88
+ fetchImage = _ref$fetchImage === void 0 ? function () {} : _ref$fetchImage;
84
89
 
85
90
  if (typeof selectedImage === "string") {
86
91
  selectedImage = (images || []).findIndex(function (img) {
@@ -126,7 +131,8 @@ export var Annotator = function Annotator(_ref) {
126
131
  imagesUpdatedAt: null,
127
132
  imagesSavedAt: null,
128
133
  albumMetadata: albumMetadata,
129
- metadataConfigs: metadataConfigs
134
+ metadataConfigs: metadataConfigs,
135
+ toPollImages: []
130
136
  }))),
131
137
  _useReducer2 = _slicedToArray(_useReducer, 2),
132
138
  state = _useReducer2[0],
@@ -255,39 +261,190 @@ export var Annotator = function Annotator(_ref) {
255
261
  type: "ADD_GROUP",
256
262
  group: group
257
263
  });
258
- }; // trigger onSelectedImageChange() hook when image changed
264
+ }; // trigger save and recalc
259
265
 
260
266
 
261
267
  useEffect(function () {
262
- var _state$lastAction;
268
+ var selectedImage = state.selectedImage,
269
+ previouslySelectedImage = state.previouslySelectedImage,
270
+ lastAction = state.lastAction;
263
271
 
264
- var createdAt = new Date();
272
+ if ((lastAction === null || lastAction === void 0 ? void 0 : lastAction.type) === 'SELECT_IMAGE' && selectedImage !== previouslySelectedImage) {
273
+ var _state$images$previou, _state$images$previou2;
265
274
 
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
- });
275
+ // metadata on album level
276
+ var saveHandler =
277
+ /*#__PURE__*/
278
+ function () {
279
+ var _ref4 = _asyncToGenerator(
280
+ /*#__PURE__*/
281
+ _regeneratorRuntime.mark(function _callee3(image, triggerRecalc, albumMetadata) {
282
+ var _ref5, lockedUntil;
283
+
284
+ return _regeneratorRuntime.wrap(function _callee3$(_context3) {
285
+ while (1) {
286
+ switch (_context3.prev = _context3.next) {
287
+ case 0:
288
+ dispatchToReducer({
289
+ type: "IMAGE_UPDATE_INIT",
290
+ imageId: image.id
291
+ });
292
+ _context3.prev = 1;
293
+ _context3.next = 4;
294
+ return save({
295
+ image: image,
296
+ triggerRecalc: triggerRecalc,
297
+ albumMetadata: albumMetadata
298
+ });
299
+
300
+ case 4:
301
+ _ref5 = _context3.sent;
302
+ lockedUntil = _ref5.lockedUntil;
303
+ dispatchToReducer({
304
+ type: "IMAGE_UPDATE_SUCCESS",
305
+ imageId: image.id,
306
+ lockedUntil: lockedUntil
307
+ });
308
+ _context3.next = 12;
309
+ break;
310
+
311
+ case 9:
312
+ _context3.prev = 9;
313
+ _context3.t0 = _context3["catch"](1);
314
+ dispatchToReducer({
315
+ type: "IMAGE_UPDATE_FAIL",
316
+ imageId: image.id,
317
+ error: _context3.t0
318
+ });
319
+
320
+ case 12:
321
+ case "end":
322
+ return _context3.stop();
323
+ }
324
+ }
325
+ }, _callee3, null, [[1, 9]]);
326
+ }));
327
+
328
+ return function saveHandler(_x3, _x4, _x5) {
329
+ return _ref4.apply(this, arguments);
330
+ };
331
+ }(); // save if previously selected image has any changes
332
+
333
+
334
+ 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) {
335
+ var _state$images$previou3, _state$images$previou4;
336
+
337
+ // decide wheather recalc is needed
338
+ var triggerRecalc = intersection(reacalcActionsEnum, state.images[previouslySelectedImage].saveableActions).length > 0; // decide wheather album metadata should be updated
339
+
340
+ var toSaveMetadata = [];
341
+
342
+ 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")) {
343
+ toSaveMetadata = state.albumMetadata;
344
+ } // save image
345
+
346
+
347
+ saveHandler(_objectSpread({}, state.images[previouslySelectedImage]), triggerRecalc, toSaveMetadata);
348
+ }
274
349
  }
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!)
350
+ }, [state.previouslySelectedImage, state.selectedImage, state.images, state, save]); // polling of images
276
351
 
277
352
  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
- }
353
+ if (state.toPollImages.length > 0) {
354
+ var polledImages = state.toPollImages.reduce(function (acc, imageId) {
355
+ function pollImage(_x6, _x7) {
356
+ return _pollImage.apply(this, arguments);
357
+ } // make recursive calling of polling function
358
+
281
359
 
282
- if (onImagesChange) {
283
- onImagesChange(state.images);
360
+ function _pollImage() {
361
+ _pollImage = _asyncToGenerator(
362
+ /*#__PURE__*/
363
+ _regeneratorRuntime.mark(function _callee4(fetchImage, imageId) {
364
+ var tries,
365
+ _ref6,
366
+ image,
367
+ _args4 = arguments;
368
+
369
+ return _regeneratorRuntime.wrap(function _callee4$(_context4) {
370
+ while (1) {
371
+ switch (_context4.prev = _context4.next) {
372
+ case 0:
373
+ tries = _args4.length > 2 && _args4[2] !== undefined ? _args4[2] : 5;
374
+
375
+ if (!(tries === 0)) {
376
+ _context4.next = 4;
377
+ break;
378
+ }
379
+
380
+ dispatchToReducer({
381
+ type: "IMAGE_POLL_TIMEOUT",
382
+ imageId: imageId
383
+ });
384
+ return _context4.abrupt("return");
385
+
386
+ case 4:
387
+ _context4.next = 6;
388
+ return fetchImage({
389
+ imageId: imageId
390
+ });
391
+
392
+ case 6:
393
+ _ref6 = _context4.sent;
394
+ image = _ref6.image;
395
+
396
+ if (image.lockedUntil) {
397
+ _context4.next = 11;
398
+ break;
399
+ }
400
+
401
+ dispatchToReducer({
402
+ type: "IMAGE_POLL_SUCCESS",
403
+ image: image
404
+ });
405
+ return _context4.abrupt("return");
406
+
407
+ case 11:
408
+ _context4.next = 13;
409
+ return sleep(5000);
410
+
411
+ case 13:
412
+ _context4.next = 15;
413
+ return pollImage(fetchImage, imageId, tries - 1);
414
+
415
+ case 15:
416
+ return _context4.abrupt("return", _context4.sent);
417
+
418
+ case 16:
419
+ case "end":
420
+ return _context4.stop();
421
+ }
422
+ }
423
+ }, _callee4);
424
+ }));
425
+ return _pollImage.apply(this, arguments);
426
+ }
427
+
428
+ pollImage(fetchImage, imageId, 10);
429
+ return imageId;
430
+ }, []);
431
+ dispatchToReducer({
432
+ type: "IMAGE_POLL_INIT",
433
+ imageIds: polledImages
434
+ });
284
435
  }
436
+ }, [fetchImage, state.toPollImages]); // // TODO: delete this when work done
437
+ // useEffect(() => {
438
+ // if (!state.lastAction || !["BEGIN_BOX_TRANSFORM", "CHANGE_REGION", "DELETE_REGION", "DELETE_SELECTED_REGION", "UPDATE_METADATA", "SELECT_CLASSIFICATION"].includes(state.lastAction.type)) { return }
439
+ // if (onImagesChange) {
440
+ // onImagesChange(state.images)
441
+ // }
442
+ // dispatchToReducer({
443
+ // type: "IMAGES_UPDATED",
444
+ // updatedAt: new Date()
445
+ // })
446
+ // }, [onImagesChange, state.images, state.lastAction])
285
447
 
286
- dispatchToReducer({
287
- type: "IMAGES_UPDATED",
288
- updatedAt: new Date()
289
- });
290
- }, [onImagesChange, state.images, state.lastAction]);
291
448
  useEffect(function () {
292
449
  if (selectedImage === undefined) return;
293
450
  dispatchToReducer({
@@ -298,10 +455,10 @@ export var Annotator = function Annotator(_ref) {
298
455
  }, [onImagesChange, selectedImage]);
299
456
  if (!images && !videoSrc) return 'Missing required prop "images" or "videoSrc"';
300
457
 
301
- var _ref4 = state.imagesSavedAt < state.imagesUpdatedAt ? [true, true] : [false, false],
302
- _ref5 = _slicedToArray(_ref4, 2),
303
- recalcActive = _ref5[0],
304
- saveActive = _ref5[1];
458
+ var _ref7 = state.imagesSavedAt < state.imagesUpdatedAt ? [true, true] : [false, false],
459
+ _ref8 = _slicedToArray(_ref7, 2),
460
+ recalcActive = _ref8[0],
461
+ saveActive = _ref8[1];
305
462
 
306
463
  return React.createElement(SettingsProvider, {
307
464
  clsColors: clsColors,
@@ -332,8 +489,7 @@ export var Annotator = function Annotator(_ref) {
332
489
  saveActive: recalcActive,
333
490
  recalcActive: saveActive,
334
491
  onMetadataChange: handleMetadataChange,
335
- onAddGroup: handleAddGroup,
336
- lockedImages: lockedImages
492
+ onAddGroup: handleAddGroup
337
493
  }));
338
494
  };
339
495
  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
  }
@@ -4,9 +4,26 @@ import _createClass from "@babel/runtime/helpers/esm/createClass";
4
4
  /**
5
5
  * Build styles
6
6
  */
7
- import './annotation.css'; // Possible classes
7
+ import './annotation.css';
8
+
9
+ function whitespaceCharactersToHTML() {
10
+ var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
11
+ return str // new line whitespace
12
+ .replaceAll(/[ ]\n[ ]/gm, '&nbsp;<br>&nbsp;') // space both sides adds &nbsp;
13
+ .replaceAll(/\n[ ]/gm, '<br>&nbsp;') // space right side adds &nbsp;
14
+ .replaceAll(/[ ]\n/gm, '&nbsp;<br>') // space left side adds &nbsp;
15
+ .replaceAll(/\n/gm, '<br>') // no spaces
16
+ // tab whitespace
17
+ .replaceAll(/\t/gm, '&nbsp;&nbsp;&nbsp;&nbsp;'); // no spaces
18
+ }
19
+
20
+ function HTMLToWhitespaceCharacters() {
21
+ var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
22
+ return str.replaceAll(/((&nbsp;|\s){4})/gm, '\t').replaceAll('&nbsp;', ' ').replaceAll('<br>', '\n'); // new line whitespace
23
+ } // Possible classes
8
24
  // ================
9
25
 
26
+
10
27
  var Annotation =
11
28
  /*#__PURE__*/
12
29
  function () {
@@ -92,7 +109,7 @@ function () {
92
109
  data = {};
93
110
  }
94
111
 
95
- newData.text = data.text || '';
112
+ newData.text = whitespaceCharactersToHTML(data.text);
96
113
  newData.labelName = data.labelName || this.defaultLabel.labelName;
97
114
  return newData;
98
115
  }
@@ -234,7 +251,7 @@ function () {
234
251
  key: "save",
235
252
  value: function save(toolsContent) {
236
253
  return {
237
- text: toolsContent.innerHTML,
254
+ text: HTMLToWhitespaceCharacters(toolsContent.innerHTML),
238
255
  labelName: this.currentLabel.labelName
239
256
  };
240
257
  }
@@ -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.47",
3
+ "version": "2.0.49",
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;