@searpent/react-image-annotate 2.0.75 → 2.0.77

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.
Files changed (224) hide show
  1. package/.babelrc +6 -0
  2. package/.env +1 -0
  3. package/.flowconfig +2 -0
  4. package/.github/workflows/release-on-master.yml +32 -0
  5. package/.github/workflows/test.yml +16 -0
  6. package/.prettierrc +3 -0
  7. package/.releaserc.js +18 -0
  8. package/.storybook/addons.js +2 -0
  9. package/.storybook/config.js +16 -0
  10. package/LICENSE +21 -0
  11. package/package.json +1 -1
  12. package/public/favicon.ico +0 -0
  13. package/public/index.html +38 -0
  14. package/src/Annotator/bike-pic.png +0 -0
  15. package/src/Annotator/bike-pic2.png +0 -0
  16. package/src/Annotator/dab-keyframes.story.json +1 -0
  17. package/src/Annotator/exampleImages.js +48 -0
  18. package/src/Annotator/examplePhotos.js +7603 -0
  19. package/src/Annotator/index.js +380 -0
  20. package/src/Annotator/index.story.js +899 -0
  21. package/src/Annotator/poses.story.js +150 -0
  22. package/src/Annotator/reducers/combine-reducers.js +7 -0
  23. package/src/Annotator/reducers/convert-expanding-line-to-polygon.js +53 -0
  24. package/{Annotator → src/Annotator}/reducers/fix-twisted.js +5 -3
  25. package/src/Annotator/reducers/general-reducer.js +1228 -0
  26. package/src/Annotator/reducers/get-active-image.js +21 -0
  27. package/src/Annotator/reducers/get-implied-video-regions.js +115 -0
  28. package/src/Annotator/reducers/history-handler.js +60 -0
  29. package/src/Annotator/reducers/image-reducer.js +23 -0
  30. package/src/Annotator/reducers/video-reducer.js +85 -0
  31. package/src/Annotator/video.story.js +51 -0
  32. package/src/ClassSelectionMenu/index.js +112 -0
  33. package/src/Crosshairs/index.js +64 -0
  34. package/src/DebugSidebarBox/index.js +36 -0
  35. package/src/DemoSite/Editor.js +235 -0
  36. package/src/DemoSite/ErrorBoundaryDialog.js +34 -0
  37. package/src/DemoSite/index.js +41 -0
  38. package/src/DemoSite/index.story.js +10 -0
  39. package/src/DemoSite/simple-segmentation-example.json +572 -0
  40. package/{Editor → src/Editor}/annotation-plugin/annotation.css +20 -0
  41. package/src/Editor/annotation-plugin/annotation.js +546 -0
  42. package/src/Editor/index.js +50 -0
  43. package/src/Editor/readOnly.js +31 -0
  44. package/{Editor → src/Editor}/tools.js +3 -2
  45. package/src/Errorer/index.js +13 -0
  46. package/src/FullImageSegmentationAnnotator/hard1.story.jpg +0 -0
  47. package/src/FullImageSegmentationAnnotator/hard2.story.jpg +0 -0
  48. package/src/FullImageSegmentationAnnotator/hard3.story.jpg +0 -0
  49. package/src/FullImageSegmentationAnnotator/index.js +7 -0
  50. package/src/FullImageSegmentationAnnotator/index.story.js +177 -0
  51. package/src/FullImageSegmentationAnnotator/orange.story.png +0 -0
  52. package/src/GroupSelectorSidebarBox/index.js +48 -0
  53. package/src/GroupsEditorSidebarBox/index.js +108 -0
  54. package/src/HelpSidebarBox/index.js +43 -0
  55. package/src/HighlightBox/index.js +143 -0
  56. package/src/HistorySidebarBox/index.js +78 -0
  57. package/src/ImageCanvas/dancing-man.story.jpg +0 -0
  58. package/src/ImageCanvas/index.js +515 -0
  59. package/src/ImageCanvas/index.story.js +314 -0
  60. package/src/ImageCanvas/mouse_mask.story.png +0 -0
  61. package/src/ImageCanvas/region-tools.js +171 -0
  62. package/src/ImageCanvas/seves_desk.story.jpg +0 -0
  63. package/{ImageCanvas → src/ImageCanvas}/styles.js +8 -12
  64. package/src/ImageCanvas/use-mouse.js +168 -0
  65. package/src/ImageCanvas/use-project-box.js +23 -0
  66. package/src/ImageCanvas/use-wasd-mode.js +50 -0
  67. package/src/ImageMask/index.js +127 -0
  68. package/src/ImageMask/load-image.js +32 -0
  69. package/src/ImageSelectorSidebarBox/index.js +54 -0
  70. package/src/KeyframeTimeline/get-time-string.js +25 -0
  71. package/src/KeyframeTimeline/index.js +223 -0
  72. package/src/KeyframesSelectorSidebarBox/index.js +93 -0
  73. package/src/LandingPage/content.md +57 -0
  74. package/src/LandingPage/github-markdown.css +964 -0
  75. package/src/LandingPage/index.js +147 -0
  76. package/src/Locker/index.js +13 -0
  77. package/src/MainLayout/RightSidebarItemsWrapper.js +21 -0
  78. package/src/MainLayout/icon-dictionary.js +79 -0
  79. package/src/MainLayout/index.js +564 -0
  80. package/src/MainLayout/index.story.js +240 -0
  81. package/{MainLayout → src/MainLayout}/styles.js +7 -6
  82. package/src/MainLayout/types.js +171 -0
  83. package/src/MainLayout/use-implied-video-regions.js +17 -0
  84. package/src/MetadataEditorSidebarBox/index.js +160 -0
  85. package/src/PageSelector/index.js +159 -0
  86. package/src/PointDistances/index.js +90 -0
  87. package/src/PreventScrollToParents/index.js +48 -0
  88. package/src/PreventScrollToParents/index.story.js +23 -0
  89. package/src/RegionLabel/index.js +236 -0
  90. package/{RegionLabel → src/RegionLabel}/styles.js +15 -12
  91. package/src/RegionSelectAndTransformBoxes/index.js +236 -0
  92. package/src/RegionSelectorSidebarBox/index.js +220 -0
  93. package/{RegionSelectorSidebarBox → src/RegionSelectorSidebarBox}/styles.js +14 -13
  94. package/src/RegionShapes/index.js +254 -0
  95. package/src/RegionTags/index.js +136 -0
  96. package/src/SettingsDialog/index.js +58 -0
  97. package/src/SettingsProvider/index.js +57 -0
  98. package/src/Shortcuts/ShortcutField.js +44 -0
  99. package/src/Shortcuts/index.js +129 -0
  100. package/src/ShortcutsManager/index.js +162 -0
  101. package/src/Sidebar/index.js +117 -0
  102. package/src/SidebarBoxContainer/index.js +93 -0
  103. package/src/SmallToolButton/index.js +57 -0
  104. package/src/TagsSidebarBox/index.js +93 -0
  105. package/src/TaskDescriptionSidebarBox/index.js +43 -0
  106. package/src/Theme/index.js +36 -0
  107. package/src/VideoOrImageCanvasBackground/index.js +170 -0
  108. package/src/colors.js +32 -0
  109. package/src/hooks/use-colors.js +95 -0
  110. package/src/hooks/use-event-callback.js +11 -0
  111. package/src/hooks/use-exclude-pattern.js +27 -0
  112. package/src/hooks/use-load-image.js +21 -0
  113. package/src/hooks/use-window-size.js +46 -0
  114. package/{hooks → src/hooks}/xpattern.js +1 -1
  115. package/src/hooks/xpattern.png +0 -0
  116. package/src/index.js +18 -0
  117. package/src/lib.js +7 -0
  118. package/src/screenshot.png +0 -0
  119. package/src/site.css +5 -0
  120. package/src/stories.js +2 -0
  121. package/src/utils/blocks-to-article.js +61 -0
  122. package/{utils → src/utils}/blocks-to-article.test.js +8 -5
  123. package/{utils → src/utils}/default-locked-until.js +1 -2
  124. package/{utils → src/utils}/filter-only-unique.js +1 -1
  125. package/src/utils/get-from-local-storage.js +7 -0
  126. package/src/utils/get-hotkey-help-text.js +11 -0
  127. package/src/utils/get-landmarks-with-transform.js +23 -0
  128. package/src/utils/photosToImages.js +67 -0
  129. package/src/utils/regions-groups.js +19 -0
  130. package/src/utils/regions-to-blocks.js +16 -0
  131. package/src/utils/saveable-actions-enum.js +5 -0
  132. package/src/utils/set-in-local-storage.js +6 -0
  133. package/src/utils/sleep.js +3 -0
  134. package/src/utils/uuid-to-hash.js +5 -0
  135. package/Annotator/exampleImages.js +0 -41
  136. package/Annotator/examplePhotos.js +0 -6980
  137. package/Annotator/index.js +0 -417
  138. package/Annotator/reducers/combine-reducers.js +0 -14
  139. package/Annotator/reducers/convert-expanding-line-to-polygon.js +0 -73
  140. package/Annotator/reducers/general-reducer.js +0 -1430
  141. package/Annotator/reducers/get-active-image.js +0 -27
  142. package/Annotator/reducers/get-implied-video-regions.js +0 -180
  143. package/Annotator/reducers/history-handler.js +0 -38
  144. package/Annotator/reducers/image-reducer.js +0 -20
  145. package/Annotator/reducers/video-reducer.js +0 -88
  146. package/ClassSelectionMenu/index.js +0 -140
  147. package/Crosshairs/index.js +0 -53
  148. package/DebugSidebarBox/index.js +0 -20
  149. package/DemoSite/Editor.js +0 -194
  150. package/DemoSite/ErrorBoundaryDialog.js +0 -64
  151. package/DemoSite/index.js +0 -40
  152. package/Editor/annotation-plugin/annotation.js +0 -647
  153. package/Editor/index.js +0 -93
  154. package/Editor/readOnly.js +0 -73
  155. package/Errorer/index.js +0 -11
  156. package/FullImageSegmentationAnnotator/index.js +0 -7
  157. package/GroupSelectorSidebarBox/index.js +0 -63
  158. package/GroupsEditorSidebarBox/index.js +0 -138
  159. package/HelpSidebarBox/index.js +0 -58
  160. package/HighlightBox/index.js +0 -102
  161. package/HistorySidebarBox/index.js +0 -71
  162. package/ImageCanvas/index.js +0 -441
  163. package/ImageCanvas/region-tools.js +0 -165
  164. package/ImageCanvas/use-mouse.js +0 -180
  165. package/ImageCanvas/use-project-box.js +0 -27
  166. package/ImageCanvas/use-wasd-mode.js +0 -62
  167. package/ImageMask/index.js +0 -133
  168. package/ImageMask/load-image.js +0 -25
  169. package/ImageSelectorSidebarBox/index.js +0 -60
  170. package/KeyframeTimeline/get-time-string.js +0 -27
  171. package/KeyframeTimeline/index.js +0 -233
  172. package/KeyframesSelectorSidebarBox/index.js +0 -93
  173. package/LandingPage/index.js +0 -159
  174. package/Locker/index.js +0 -11
  175. package/MainLayout/RightSidebarItemsWrapper.js +0 -19
  176. package/MainLayout/icon-dictionary.js +0 -104
  177. package/MainLayout/index.js +0 -526
  178. package/MainLayout/types.js +0 -0
  179. package/MainLayout/use-implied-video-regions.js +0 -13
  180. package/MetadataEditorSidebarBox/index.js +0 -231
  181. package/PageSelector/index.js +0 -180
  182. package/PointDistances/index.js +0 -73
  183. package/PreventScrollToParents/index.js +0 -51
  184. package/RegionLabel/index.js +0 -232
  185. package/RegionSelectAndTransformBoxes/index.js +0 -169
  186. package/RegionSelectorSidebarBox/index.js +0 -254
  187. package/RegionShapes/index.js +0 -294
  188. package/RegionTags/index.js +0 -144
  189. package/SettingsDialog/index.js +0 -52
  190. package/SettingsProvider/index.js +0 -60
  191. package/Shortcuts/ShortcutField.js +0 -46
  192. package/Shortcuts/index.js +0 -133
  193. package/ShortcutsManager/index.js +0 -155
  194. package/Sidebar/index.js +0 -69
  195. package/SidebarBoxContainer/index.js +0 -93
  196. package/SmallToolButton/index.js +0 -42
  197. package/TagsSidebarBox/index.js +0 -105
  198. package/TaskDescriptionSidebarBox/index.js +0 -58
  199. package/Theme/index.js +0 -30
  200. package/VideoOrImageCanvasBackground/index.js +0 -151
  201. package/colors.js +0 -14
  202. package/hooks/use-colors.js +0 -97
  203. package/hooks/use-event-callback.js +0 -10
  204. package/hooks/use-exclude-pattern.js +0 -24
  205. package/hooks/use-load-image.js +0 -26
  206. package/hooks/use-window-size.js +0 -46
  207. package/index.js +0 -3
  208. package/lib.js +0 -3
  209. package/stories.js +0 -5
  210. package/utils/blocks-to-article.js +0 -60
  211. package/utils/get-from-local-storage.js +0 -7
  212. package/utils/get-hotkey-help-text.js +0 -9
  213. package/utils/get-landmarks-with-transform.js +0 -40
  214. package/utils/photosToImages.js +0 -85
  215. package/utils/regions-groups.js +0 -28
  216. package/utils/regions-to-blocks.js +0 -18
  217. package/utils/saveable-actions-enum.js +0 -3
  218. package/utils/set-in-local-storage.js +0 -3
  219. package/utils/sleep.js +0 -7
  220. package/utils/uuid-to-hash.js +0 -5
  221. /package/{Errorer → src/Errorer}/errorer.css +0 -0
  222. /package/{Locker → src/Locker}/locker.css +0 -0
  223. /package/{PageSelector → src/PageSelector}/page-selector.css +0 -0
  224. /package/{utils → src/utils}/next-group-id.js +0 -0
@@ -0,0 +1,380 @@
1
+ // @flow
2
+
3
+ import type {
4
+ Action,
5
+ Image,
6
+ MainLayoutState,
7
+ Mode,
8
+ ToolEnum,
9
+ Metadata,
10
+ MetadataConfig
11
+ } from "../MainLayout/types"
12
+ import React, { useCallback, useEffect, useReducer } from "react"
13
+ import makeImmutable, { without } from "seamless-immutable"
14
+ import intersection from "lodash/intersection"
15
+ import type { KeypointsDefinition } from "../ImageCanvas/region-tools"
16
+ import MainLayout from "../MainLayout"
17
+ import type { Node } from "react"
18
+ import SettingsProvider from "../SettingsProvider"
19
+ import combineReducers from "./reducers/combine-reducers.js"
20
+ import generalReducer from "./reducers/general-reducer.js"
21
+ import getFromLocalStorage from "../utils/get-from-local-storage"
22
+ import historyHandler from "./reducers/history-handler.js"
23
+ import imageReducer from "./reducers/image-reducer.js"
24
+ import useEventCallback from "use-event-callback"
25
+ import videoReducer from "./reducers/video-reducer.js"
26
+ import { reacalcActionsEnum } from "../utils/saveable-actions-enum"
27
+ import sleep from '../utils/sleep';
28
+
29
+ type Props = {
30
+ taskDescription?: string,
31
+ help?: string,
32
+ allowedArea?: { x: number, y: number, w: number, h: number },
33
+ regionTagList?: Array<string>,
34
+ regionClsList?: Array<string>,
35
+ imageTagList?: Array<string>,
36
+ imageClsList?: Array<string>,
37
+ enabledTools?: Array<string>,
38
+ selectedTool?: String,
39
+ showTags?: boolean,
40
+ selectedImage?: string | number,
41
+ images?: Array<Image>,
42
+ showPointDistances?: boolean,
43
+ pointDistancePrecision?: number,
44
+ RegionEditLabel?: Node,
45
+ onExit: (MainLayoutState) => any,
46
+ videoTime?: number,
47
+ videoSrc?: string,
48
+ keyframes?: Object,
49
+ videoName?: string,
50
+ keypointDefinitions: KeypointsDefinition,
51
+ fullImageSegmentationMode?: boolean,
52
+ autoSegmentationOptions?:
53
+ | {| type: "simple" |}
54
+ | {| type: "autoseg", maxClusters ?: number, slicWeightFactor ?: number |},
55
+ hideHeader ?: boolean,
56
+ hideHeaderText ?: boolean,
57
+ hideNext ?: boolean,
58
+ hidePrev ?: boolean,
59
+ hideClone ?: boolean,
60
+ hideSettings ?: boolean,
61
+ hideFullScreen ?: boolean,
62
+ hideSave ?: boolean,
63
+ onImagesChange ?: (any) => any,
64
+ groups ?: Array < any >,
65
+ onGroupSelect ?: (any) => any,
66
+ hideHistory ?: boolean,
67
+ hideNotEditingLabel ?: boolean,
68
+ showEditor ?: boolean,
69
+ showPageSelector ?: boolean,
70
+ clsColors ?: Object,
71
+ groupColors ?: Array < string >,
72
+ onSelectedImageChange ?: (any) => any,
73
+ albumMetadata ?: Array < Metadata >,
74
+ metadataConfigs ? : Array < MetadataConfig >,
75
+ save : (any) => any,
76
+ fetchImage : (any) => any,
77
+ }
78
+
79
+ export const Annotator = ({
80
+ images,
81
+ allowedArea,
82
+ selectedImage = images && images.length > 0 ? 0 : undefined,
83
+ showPointDistances,
84
+ pointDistancePrecision,
85
+ showTags = getFromLocalStorage("showTags", true),
86
+ enabledTools = [
87
+ "select",
88
+ "create-point",
89
+ "create-box",
90
+ "create-polygon",
91
+ "create-line",
92
+ "create-expanding-line",
93
+ "show-mask",
94
+ ],
95
+ selectedTool = "select",
96
+ regionTagList = [],
97
+ regionClsList = [],
98
+ imageTagList = [],
99
+ imageClsList = [],
100
+ keyframes = {},
101
+ taskDescription = "",
102
+ help = "",
103
+ fullImageSegmentationMode = false,
104
+ RegionEditLabel,
105
+ videoSrc,
106
+ videoTime = 0,
107
+ videoName,
108
+ onExit,
109
+ onNextImage,
110
+ onPrevImage,
111
+ keypointDefinitions,
112
+ autoSegmentationOptions = { type: "autoseg" },
113
+ hideHeader,
114
+ hideHeaderText,
115
+ hideNext,
116
+ hidePrev,
117
+ hideClone,
118
+ hideSettings,
119
+ hideFullScreen,
120
+ hideSave,
121
+ allowComments,
122
+ onImagesChange,
123
+ groups,
124
+ onGroupSelect,
125
+ hideHistory,
126
+ hideNotEditingLabel,
127
+ showEditor,
128
+ showPageSelector,
129
+ clsColors = {},
130
+ groupColors,
131
+ onSelectedImageChange,
132
+ albumMetadata,
133
+ metadataConfigs,
134
+ save = () => { },
135
+ fetchImage = () => { },
136
+ }: Props) => {
137
+ if (typeof selectedImage === "string") {
138
+ selectedImage = (images || []).findIndex((img) => img.src === selectedImage)
139
+ if (selectedImage === -1) selectedImage = undefined
140
+ }
141
+ const annotationType = images ? "image" : "video"
142
+ const [state, dispatchToReducer] = useReducer(
143
+ historyHandler(
144
+ combineReducers(
145
+ annotationType === "image" ? imageReducer : videoReducer,
146
+ generalReducer
147
+ )
148
+ ),
149
+ makeImmutable({
150
+ annotationType,
151
+ showTags,
152
+ allowedArea,
153
+ showPointDistances,
154
+ pointDistancePrecision,
155
+ selectedTool,
156
+ fullImageSegmentationMode: fullImageSegmentationMode,
157
+ autoSegmentationOptions,
158
+ mode: null,
159
+ taskDescription,
160
+ help,
161
+ showMask: true,
162
+ labelImages: imageClsList.length > 0 || imageTagList.length > 0,
163
+ regionClsList,
164
+ regionTagList,
165
+ imageClsList,
166
+ imageTagList,
167
+ currentVideoTime: videoTime,
168
+ enabledTools,
169
+ history: [],
170
+ videoName,
171
+ keypointDefinitions,
172
+ allowComments,
173
+ ...(annotationType === "image"
174
+ ? {
175
+ selectedImage,
176
+ images,
177
+ selectedImageFrameTime:
178
+ images && images.length > 0 ? images[0].frameTime : undefined,
179
+ }
180
+ : {
181
+ videoSrc,
182
+ keyframes,
183
+ }),
184
+ imagesUpdatedAt: null,
185
+ imagesSavedAt: null,
186
+ albumMetadata,
187
+ metadataConfigs,
188
+ toPollImages: [...images.filter(i => i.lockedUntil).map(i => i.id)],
189
+ toSaveImage: null,
190
+ })
191
+ )
192
+
193
+ const dispatch = useEventCallback((action: Action) => {
194
+ if (action.type === "HEADER_BUTTON_CLICKED") {
195
+ if (["Exit", "Done", "Save", "Complete"].includes(action.buttonName)) {
196
+ return onExit(without(state, "history"))
197
+ } else if (action.buttonName === "Next" && onNextImage) {
198
+ return onNextImage(without(state, "history"))
199
+ } else if (action.buttonName === "Prev" && onPrevImage) {
200
+ return onPrevImage(without(state, "history"))
201
+ }
202
+ }
203
+ dispatchToReducer(action)
204
+ })
205
+
206
+ const onRegionClassAdded = useEventCallback((cls) => {
207
+ dispatchToReducer({
208
+ type: "ON_CLS_ADDED",
209
+ cls: cls,
210
+ })
211
+ })
212
+
213
+ const handleMetadataChange = (params) => {
214
+ dispatchToReducer({
215
+ type: "UPDATE_METADATA",
216
+ ...params
217
+ })
218
+ }
219
+
220
+ const handleAddGroup = (group) => {
221
+ dispatchToReducer({
222
+ type: "ADD_GROUP",
223
+ group
224
+ })
225
+ }
226
+
227
+ const handleRecalcClicked = useCallback(({ imageId }) => {
228
+ dispatchToReducer({
229
+ type: "RECALC_CLICKED",
230
+ imageId
231
+ })
232
+ }, [dispatchToReducer])
233
+
234
+ // detect on page change if anything changed, if so trigger save and recalc
235
+ useEffect(() => {
236
+ const { selectedImage, previouslySelectedImage, lastAction } = state;
237
+ if (lastAction?.type === 'SELECT_IMAGE' && selectedImage !== previouslySelectedImage) {
238
+ // save if previously selected image has any changes
239
+ if (state.images[previouslySelectedImage]?.saveableActions?.length > 0) {
240
+ // decide wheather recalc is needed
241
+ const triggerRecalc = intersection(reacalcActionsEnum, state.images[previouslySelectedImage].saveableActions).length > 0;
242
+
243
+ // decide whether album metadata should be updated
244
+ let toSaveMetadata = [];
245
+ if (state.images[previouslySelectedImage]?.saveableActions?.includes("UPDATE_ALBUM_METADATA")) {
246
+ toSaveMetadata = state.albumMetadata
247
+ }
248
+
249
+ // set this image to be saved
250
+ dispatchToReducer({
251
+ type: "SAVE_IMAGE",
252
+ image: { ...state.images[previouslySelectedImage] },
253
+ triggerRecalc,
254
+ toSaveMetadata
255
+ })
256
+ }
257
+ }
258
+ }, [state.previouslySelectedImage, state.selectedImage, state.images, state, save])
259
+
260
+ // handle save of image
261
+ useEffect(() => {
262
+ if (state.toSaveImage !== null) {
263
+ // metadata on album level
264
+ const saveHandler = async (image, triggerRecalc, albumMetadata) => {
265
+ dispatchToReducer({
266
+ type: "IMAGE_UPDATE_INIT",
267
+ imageId: image.id
268
+ })
269
+
270
+ try {
271
+ const { lockedUntil } = await save({ image, triggerRecalc, albumMetadata });
272
+ dispatchToReducer({
273
+ type: "IMAGE_UPDATE_SUCCESS",
274
+ imageId: image.id,
275
+ lockedUntil,
276
+ })
277
+ } catch (error) {
278
+ dispatchToReducer({
279
+ type: "IMAGE_UPDATE_FAIL",
280
+ imageId: image.id,
281
+ error,
282
+ })
283
+ }
284
+ }
285
+
286
+ const { image, triggerRecalc, toSaveMetadata } = state.toSaveImage;
287
+ saveHandler(image, triggerRecalc, toSaveMetadata);
288
+ }
289
+ }, [save, state.toSaveImage])
290
+
291
+ // polling of images
292
+ useEffect(() => {
293
+ if (state.toPollImages.length > 0) {
294
+ const polledImages = state.toPollImages.reduce((acc, imageId) => {
295
+
296
+ async function pollImage(fetchImage, imageId, tries = 5) {
297
+ if (tries === 0) {
298
+ dispatchToReducer({
299
+ type: "IMAGE_POLL_TIMEOUT",
300
+ imageId,
301
+ })
302
+ return;
303
+ }
304
+
305
+ const { image } = await fetchImage({ imageId });
306
+ if (!image.lockedUntil) {
307
+ dispatchToReducer({
308
+ type: "IMAGE_POLL_SUCCESS",
309
+ image,
310
+ })
311
+ return;
312
+ }
313
+
314
+ await sleep(5000);
315
+
316
+ return await pollImage(fetchImage, imageId, tries - 1);
317
+ }
318
+
319
+ // make recursive calling of polling function
320
+ pollImage(fetchImage, imageId, 10);
321
+
322
+ return imageId;
323
+ }, [])
324
+
325
+ dispatchToReducer({
326
+ type: "IMAGE_POLL_INIT",
327
+ imageIds: polledImages
328
+ })
329
+ }
330
+ }, [fetchImage, state.toPollImages])
331
+
332
+ useEffect(() => {
333
+ if (selectedImage === undefined) return
334
+ dispatchToReducer({
335
+ type: "SELECT_IMAGE",
336
+ imageIndex: selectedImage,
337
+ image: state.images[selectedImage],
338
+ })
339
+ // eslint-disable-next-line react-hooks/exhaustive-deps
340
+ }, [onImagesChange, selectedImage])
341
+
342
+ if (!images && !videoSrc)
343
+ return 'Missing required prop "images" or "videoSrc"'
344
+
345
+ const [recalcActive, saveActive] = state.imagesSavedAt < state.imagesUpdatedAt ? [true, true] : [false, false];
346
+
347
+ return (
348
+ <SettingsProvider clsColors={clsColors} groupColors={groupColors}>
349
+ <MainLayout
350
+ RegionEditLabel={RegionEditLabel}
351
+ alwaysShowNextButton={Boolean(onNextImage)}
352
+ alwaysShowPrevButton={Boolean(onPrevImage)}
353
+ state={state}
354
+ dispatch={dispatch}
355
+ onRegionClassAdded={onRegionClassAdded}
356
+ hideHeader={hideHeader}
357
+ hideHeaderText={hideHeaderText}
358
+ hideNext={hideNext}
359
+ hidePrev={hidePrev}
360
+ hideClone={hideClone}
361
+ hideSettings={hideSettings}
362
+ hideFullScreen={hideFullScreen}
363
+ hideSave={hideSave}
364
+ groups={groups}
365
+ onGroupSelect={onGroupSelect}
366
+ hideHistory={hideHistory}
367
+ hideNotEditingLabel={hideNotEditingLabel}
368
+ showEditor={showEditor}
369
+ showPageSelector={showPageSelector}
370
+ saveActive={recalcActive}
371
+ recalcActive={saveActive}
372
+ onMetadataChange={handleMetadataChange}
373
+ onAddGroup={handleAddGroup}
374
+ onRecalcClick={handleRecalcClicked}
375
+ />
376
+ </SettingsProvider>
377
+ )
378
+ }
379
+
380
+ export default Annotator