@searpent/react-image-annotate 2.3.5 → 2.3.6

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.
@@ -85,7 +85,8 @@ export var Annotator = function Annotator(_ref) {
85
85
  save = _ref$save === void 0 ? function () {} : _ref$save,
86
86
  _ref$fetchImage = _ref.fetchImage,
87
87
  fetchImage = _ref$fetchImage === void 0 ? function () {} : _ref$fetchImage,
88
- updatedBy = _ref.updatedBy;
88
+ updatedBy = _ref.updatedBy,
89
+ mediaPresenterLeaseUntil = _ref.mediaPresenterLeaseUntil;
89
90
 
90
91
  if (typeof selectedImage === "string") {
91
92
  selectedImage = (images || []).findIndex(function (img) {
@@ -447,7 +448,8 @@ export var Annotator = function Annotator(_ref) {
447
448
  onMetadataChange: handleMetadataChange,
448
449
  onAddGroup: handleAddGroup,
449
450
  onRecalcClick: handleRecalcClicked,
450
- updatedBy: updatedBy
451
+ updatedBy: updatedBy,
452
+ mediaPresenterLeaseUntil: mediaPresenterLeaseUntil
451
453
  }))
452
454
  );
453
455
  };
@@ -134,7 +134,8 @@ export var MainLayout = function MainLayout(_ref5) {
134
134
  onMetadataChange = _ref5.onMetadataChange,
135
135
  onAddGroup = _ref5.onAddGroup,
136
136
  onRecalcClick = _ref5.onRecalcClick,
137
- updatedBy = _ref5.updatedBy;
137
+ updatedBy = _ref5.updatedBy,
138
+ mediaPresenterLeaseUntil = _ref5.mediaPresenterLeaseUntil;
138
139
  var classes = useStyles();
139
140
  var settings = useSettings();
140
141
  var fullScreenHandle = useFullScreenHandle();
@@ -377,7 +378,8 @@ export var MainLayout = function MainLayout(_ref5) {
377
378
  onMetadataChange: onMetadataChange,
378
379
  metadataConfigs: state.metadataConfigs || [],
379
380
  onRecalcClick: onRecalcClick,
380
- updatedBy: updatedBy
381
+ updatedBy: updatedBy,
382
+ mediaPresenterLeaseUntil: mediaPresenterLeaseUntil
381
383
  }),
382
384
  /*#__PURE__*/
383
385
  React.createElement(WorkspaceWrapper, null,
@@ -1,29 +1,74 @@
1
- import React from "react";
2
- import classnames from "classnames";
1
+ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
2
+ import React, { useEffect, useMemo, useState } from "react";
3
3
  export function isUpdatedByAggregator(updatedBy) {
4
4
  if (updatedBy == null) return false;
5
5
  return String(updatedBy).trim() === "AGGREGATOR";
6
6
  }
7
+ /** Non-empty lease string or null (treat undefined / "" / whitespace as no lock). */
8
+
9
+ export function normalizeMediaPresenterLeaseUntil(raw) {
10
+ if (raw == null) return null;
11
+ var s = String(raw).trim();
12
+ return s === "" ? null : s;
13
+ }
14
+ /**
15
+ * True when Dynamo `mediaPresenterLeaseUntil` (ISO UTC string) is still in the future.
16
+ * Matches the media-presenter webhook lease written by BasicLinkingFunction.
17
+ */
18
+
19
+ export function isMediaPresenterLockActive(mediaPresenterLeaseUntil) {
20
+ var s = normalizeMediaPresenterLeaseUntil(mediaPresenterLeaseUntil);
21
+ if (s == null) return false;
22
+ var t = Date.parse(s);
23
+ if (Number.isNaN(t)) return false;
24
+ return t > Date.now();
25
+ }
7
26
  /**
8
27
  * Case-level “UPDATED BY” indicator for the page strip (left panel).
9
- * Red (left) / green (right) discs, left-aligned with the page-strip switch column; “Ready” after them.
10
- * Green lit only when value is exactly AGGREGATOR.
28
+ * Red (left) / green (right) discs, left-aligned with the page-strip switch column; status label after them.
29
+ * Green lit only when `updatedBy` is exactly AGGREGATOR and the media-presenter Dynamo lease is not active.
11
30
  * `selectionKey` should change per selected page so the control remounts when paging.
12
31
  */
13
32
 
14
33
  export default function UpdatedBySemaphore(_ref) {
15
34
  var updatedBy = _ref.updatedBy,
35
+ mediaPresenterLeaseUntil = _ref.mediaPresenterLeaseUntil,
16
36
  selectionKey = _ref.selectionKey;
17
- var green = isUpdatedByAggregator(updatedBy);
37
+ var leaseStr = useMemo(function () {
38
+ return normalizeMediaPresenterLeaseUntil(mediaPresenterLeaseUntil);
39
+ }, [mediaPresenterLeaseUntil]); // While a lease string is present, re-evaluate every second so expiry clears without a parent refetch.
40
+
41
+ var _useState = useState(0),
42
+ _useState2 = _slicedToArray(_useState, 2),
43
+ setLeasePulse = _useState2[1];
44
+
45
+ useEffect(function () {
46
+ if (leaseStr == null) return undefined;
47
+ var id = window.setInterval(function () {
48
+ return setLeasePulse(function (n) {
49
+ return n + 1;
50
+ });
51
+ }, 1000);
52
+ return function () {
53
+ return window.clearInterval(id);
54
+ };
55
+ }, [leaseStr]);
56
+ var lockActive = isMediaPresenterLockActive(leaseStr);
57
+ var green = isUpdatedByAggregator(updatedBy) && !lockActive;
18
58
  var source = updatedBy == null || updatedBy === "" ? "—" : String(updatedBy);
59
+ var leaseHint = lockActive ? " Media presenter lock active (lease until ".concat(leaseStr, ").") : "";
60
+ var statusLabel = green ? "Ready" : "Do not modify";
61
+ var title = "Case last updated by: ".concat(source, ".").concat(leaseHint, " Status: ").concat(statusLabel, ". Green lamp: safe to modify (AGGREGATOR and no active media-presenter lease). Red lamp: do not modify (other updater and/or lock).");
62
+ var redClass = "ps-semaphore-lamp ps-semaphore-red" + (green ? "" : " ps-semaphore-lit");
63
+ var greenClass = "ps-semaphore-lamp ps-semaphore-green" + (green ? " ps-semaphore-lit" : "");
19
64
  return (
20
65
  /*#__PURE__*/
21
66
  React.createElement("div", {
22
- key: selectionKey,
67
+ key: "".concat(selectionKey, "|").concat(leaseStr !== null && leaseStr !== void 0 ? leaseStr : ""),
23
68
  className: "ps-updated-by-semaphore",
24
- title: "Case last updated by: ".concat(source, ". Green disc: AGGREGATOR \u2014 OK to start manual edits. Red disc: other source \u2014 review before editing."),
69
+ title: title,
25
70
  role: "img",
26
- "aria-label": green ? "OK to start manual edits (AGGREGATOR)" : "Review before manual edits (not AGGREGATOR)"
71
+ "aria-label": statusLabel
27
72
  },
28
73
  /*#__PURE__*/
29
74
  React.createElement("span", {
@@ -36,19 +81,15 @@ export default function UpdatedBySemaphore(_ref) {
36
81
  },
37
82
  /*#__PURE__*/
38
83
  React.createElement("span", {
39
- className: classnames("ps-semaphore-lamp", "ps-semaphore-red", {
40
- "ps-semaphore-lit": !green
41
- })
84
+ className: redClass
42
85
  }),
43
86
  /*#__PURE__*/
44
87
  React.createElement("span", {
45
- className: classnames("ps-semaphore-lamp", "ps-semaphore-green", {
46
- "ps-semaphore-lit": green
47
- })
88
+ className: greenClass
48
89
  }))),
49
90
  /*#__PURE__*/
50
91
  React.createElement("span", {
51
92
  className: "ps-top-bar-label ps-updated-by-semaphore__label"
52
- }, "Ready"))
93
+ }, statusLabel))
53
94
  );
54
95
  }
@@ -4,7 +4,7 @@ import classnames from "classnames";
4
4
  import './page-selector.css';
5
5
  import Locker from '../Locker';
6
6
  import Errorer from '../Errorer';
7
- import UpdatedBySemaphore from './UpdatedBySemaphore';
7
+ import UpdatedBySemaphore, { normalizeMediaPresenterLeaseUntil } from './UpdatedBySemaphore';
8
8
 
9
9
  function PageThumbnail(_ref) {
10
10
  var _metadata$find, _metadata$find$call;
@@ -148,6 +148,12 @@ function isLocked(page) {
148
148
  return false;
149
149
  }
150
150
 
151
+ function showUpdatedBySemaphore(updatedBy, mediaPresenterLeaseUntil) {
152
+ if (updatedBy !== undefined && updatedBy !== null) return true;
153
+ if (normalizeMediaPresenterLeaseUntil(mediaPresenterLeaseUntil) != null) return true;
154
+ return false;
155
+ }
156
+
151
157
  function PageSelector(_ref3) {
152
158
  var _ref4, _ref5;
153
159
 
@@ -156,7 +162,8 @@ function PageSelector(_ref3) {
156
162
  onMetadataChange = _ref3.onMetadataChange,
157
163
  metadataConfigs = _ref3.metadataConfigs,
158
164
  onRecalcClick = _ref3.onRecalcClick,
159
- updatedBy = _ref3.updatedBy;
165
+ updatedBy = _ref3.updatedBy,
166
+ mediaPresenterLeaseUntil = _ref3.mediaPresenterLeaseUntil;
160
167
 
161
168
  var _useState = useState(false),
162
169
  _useState2 = _slicedToArray(_useState, 2),
@@ -208,7 +215,7 @@ function PageSelector(_ref3) {
208
215
  /*#__PURE__*/
209
216
  React.createElement("label", {
210
217
  className: "ps-top-bar-label"
211
- }, "Metadata")), updatedBy !== undefined && updatedBy !== null &&
218
+ }, "Metadata")), showUpdatedBySemaphore(updatedBy, mediaPresenterLeaseUntil) &&
212
219
  /*#__PURE__*/
213
220
  React.createElement("div", {
214
221
  className: "ps-semaphore-below-metadata"
@@ -216,6 +223,7 @@ function PageSelector(_ref3) {
216
223
  /*#__PURE__*/
217
224
  React.createElement(UpdatedBySemaphore, {
218
225
  updatedBy: updatedBy,
226
+ mediaPresenterLeaseUntil: mediaPresenterLeaseUntil,
219
227
  selectionKey: selectionKey
220
228
  })))),
221
229
  /*#__PURE__*/
@@ -185,9 +185,9 @@
185
185
  flex-shrink: 0;
186
186
  }
187
187
 
188
- /* Inactive: still a faint tint of the lamp’s hue */
188
+ /* Inactive lamps read as “off” (neutral); lit lamp pops. */
189
189
  .ps-semaphore-lamp:not(.ps-semaphore-lit) {
190
- opacity: 0.72;
190
+ opacity: 0.88;
191
191
  }
192
192
 
193
193
  .ps-semaphore-lamp.ps-semaphore-lit {
@@ -195,13 +195,14 @@
195
195
  transform: scale(1.02);
196
196
  }
197
197
 
198
- /* Inactive red: soft rose / dusty red */
199
- .ps-semaphore-red {
200
- background: linear-gradient(165deg, #f0d4d4 0%, #d9a8a8 45%, #c08080 100%);
201
- border-color: rgba(180, 90, 90, 0.35);
198
+ /* Off state: neutral gray so only the lit lamp reads as red/green (traffic-light style). */
199
+ .ps-semaphore-red:not(.ps-semaphore-lit),
200
+ .ps-semaphore-green:not(.ps-semaphore-lit) {
201
+ background: linear-gradient(165deg, #ececec 0%, #d4d4d4 45%, #bdbdbd 100%);
202
+ border-color: rgba(0, 0, 0, 0.12);
202
203
  box-shadow:
203
- inset 0 2px 5px rgba(255, 255, 255, 0.35),
204
- inset 0 -2px 6px rgba(160, 60, 60, 0.2);
204
+ inset 0 2px 4px rgba(255, 255, 255, 0.55),
205
+ inset 0 -2px 5px rgba(0, 0, 0, 0.1);
205
206
  }
206
207
 
207
208
  .ps-semaphore-red.ps-semaphore-lit {
@@ -212,15 +213,6 @@
212
213
  inset 0 -2px 5px rgba(0, 0, 0, 0.18);
213
214
  }
214
215
 
215
- /* Inactive green: soft mint / dusty green */
216
- .ps-semaphore-green {
217
- background: linear-gradient(165deg, #dff2df 0%, #b8dcb8 45%, #92c592 100%);
218
- border-color: rgba(80, 140, 80, 0.35);
219
- box-shadow:
220
- inset 0 2px 5px rgba(255, 255, 255, 0.4),
221
- inset 0 -2px 6px rgba(60, 120, 60, 0.18);
222
- }
223
-
224
216
  .ps-semaphore-green.ps-semaphore-lit {
225
217
  background: linear-gradient(180deg, #b9f6ca 0%, #00e676 40%, #00c853 100%);
226
218
  border-color: rgba(200, 255, 220, 0.9);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@searpent/react-image-annotate",
3
- "version": "2.3.5",
3
+ "version": "2.3.6",
4
4
  "dependencies": {
5
5
  "@editorjs/editorjs": "^2.25.0",
6
6
  "@editorjs/paragraph": "^2.8.0",