@luomus/laji-form 15.1.68 → 15.1.69

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 (31) hide show
  1. package/dist/laji-form.js +1 -1
  2. package/lib/ApiClient.d.ts +21 -66
  3. package/lib/ApiClient.js +68 -174
  4. package/lib/components/LajiForm.js +1 -1
  5. package/lib/components/fields/AnnotationField.js +22 -26
  6. package/lib/components/fields/AudioArrayField.js +5 -18
  7. package/lib/components/fields/AutosuggestField.d.ts +2 -2
  8. package/lib/components/fields/AutosuggestField.js +4 -4
  9. package/lib/components/fields/EnumRangeArrayField.js +1 -1
  10. package/lib/components/fields/GeocoderField.js +9 -2
  11. package/lib/components/fields/ImageArrayField.d.ts +2 -2
  12. package/lib/components/fields/ImageArrayField.js +112 -82
  13. package/lib/components/fields/MapArrayField.js +5 -13
  14. package/lib/components/fields/MapField.d.ts +1 -1
  15. package/lib/components/fields/MapField.js +5 -13
  16. package/lib/components/fields/NamedPlaceChooserField/NamedPlaceChooser.d.ts +1 -2
  17. package/lib/components/fields/NamedPlaceChooserField/NamedPlaceChooserField.d.ts +4 -9
  18. package/lib/components/fields/NamedPlaceChooserField/NamedPlaceChooserField.js +51 -51
  19. package/lib/components/fields/NamedPlaceChooserField/Popup.d.ts +1 -2
  20. package/lib/components/fields/NamedPlaceSaverField.js +18 -22
  21. package/lib/components/fields/ScopeField.js +1 -1
  22. package/lib/components/fields/SingleActiveArrayField.js +2 -2
  23. package/lib/components/fields/SortArrayField.js +1 -1
  24. package/lib/components/fields/UnitCountShorthandField.js +1 -1
  25. package/lib/components/fields/UnitListShorthandArrayField.js +2 -2
  26. package/lib/components/fields/UnitShorthandField.js +18 -14
  27. package/lib/components/widgets/AutosuggestWidget.js +66 -79
  28. package/lib/components/widgets/InformalTaxonGroupChooserWidget.js +1 -1
  29. package/lib/components/widgets/InputWithDefaultValueButtonWidget.js +2 -1
  30. package/lib/validation.js +1 -1
  31. package/package.json +3 -5
@@ -38,15 +38,6 @@ var __importStar = (this && this.__importStar) || (function () {
38
38
  return result;
39
39
  };
40
40
  })();
41
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
42
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
43
- return new (P || (P = Promise))(function (resolve, reject) {
44
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
45
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
46
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
47
- step((generator = generator.apply(thisArg, _arguments || [])).next());
48
- });
49
- };
50
41
  var __rest = (this && this.__rest) || function (s, e) {
51
42
  var t = {};
52
43
  for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
@@ -171,7 +162,7 @@ function MediaArrayField(ComposedComponent) {
171
162
  const item = this.props.formData[i];
172
163
  this.setState({ metadataModalOpen: i });
173
164
  this.fetching = item;
174
- this.apiClient.get(`/${this.ENDPOINT}/{id}`, { path: { id: item } }).then((response) => {
165
+ this.apiClient.fetch(`/${this.ENDPOINT}/${item}`).then((response) => {
175
166
  if (response.id !== this.fetching)
176
167
  return;
177
168
  this._context.metadatas[item] = response;
@@ -181,11 +172,10 @@ function MediaArrayField(ComposedComponent) {
181
172
  this.onMediaRmClick = (i) => () => {
182
173
  const id = this.props.formData[i];
183
174
  this.props.onChange((0, immutability_helper_1.default)(this.props.formData, { $splice: [[i, 1]] }));
184
- try {
185
- this.apiClient.delete(`/${this.ENDPOINT}/{id}`, { path: { id } });
186
- }
187
- catch (e) {
188
- }
175
+ this.apiClient.fetch(`/${this.ENDPOINT}/${id}`, undefined, {
176
+ method: "DELETE",
177
+ failSilently: true
178
+ });
189
179
  };
190
180
  this.hideMetadataModal = () => this.setState({ metadataModalOpen: false, metadataSaveSuccess: undefined });
191
181
  this.onMetadataFormChange = (formData) => this.setState({ modalMetadata: formData });
@@ -195,7 +185,7 @@ function MediaArrayField(ComposedComponent) {
195
185
  const metadataForm = this.state.metadataForm || {};
196
186
  if (typeof metadataModalOpen === "number" && !this.state.metadataForm) {
197
187
  const { metadataFormId = this.METADATA_FORM_ID } = (0, utils_1.getUiOptions)(this.props.uiSchema);
198
- this.apiClient.get("/forms/{id}", { path: { id: metadataFormId }, query: { lang, format: "schema" } })
188
+ this.apiClient.fetchCached(`/forms/${metadataFormId}`, { lang, format: "schema" })
199
189
  .then(metadataForm => {
200
190
  if (this.mounted) {
201
191
  this.setState({ metadataForm });
@@ -432,10 +422,16 @@ function MediaArrayField(ComposedComponent) {
432
422
  reader.readAsDataURL(file);
433
423
  });
434
424
  };
435
- this.onMediaMetadataUpdate = (_a) => __awaiter(this, [_a], void 0, function* ({ formData }) {
425
+ this.onMediaMetadataUpdate = ({ formData }) => {
436
426
  this.props.formContext.services.blocker.push();
437
- try {
438
- yield this.apiClient.put(`/${this.ENDPOINT}/{id}`, { path: { id: formData.id } }, formData);
427
+ this.apiClient.fetch(`/${this.ENDPOINT}/${formData.id}`, undefined, {
428
+ method: "PUT",
429
+ headers: {
430
+ "accept": "application/json",
431
+ "content-type": "application/json"
432
+ },
433
+ body: JSON.stringify(formData)
434
+ }).then(() => {
439
435
  this.props.formContext.services.blocker.pop();
440
436
  const notify = () => this.props.formContext.notifier.success(this.props.formContext.translations.SaveSuccess);
441
437
  if (this.mounted) {
@@ -444,12 +440,11 @@ function MediaArrayField(ComposedComponent) {
444
440
  else {
445
441
  notify();
446
442
  }
447
- }
448
- catch (e) {
443
+ }).catch(() => {
449
444
  this.props.formContext.services.blocker.pop();
450
445
  this.mounted && this.setState({ metadataSaveSuccess: false });
451
- }
452
- });
446
+ });
447
+ };
453
448
  this.getMetadataPromise = () => {
454
449
  let mediaMetadata = this.props.formContext.mediaMetadata
455
450
  || { intellectualRights: "MZ.intellectualRightsARR" };
@@ -457,7 +452,7 @@ function MediaArrayField(ComposedComponent) {
457
452
  return ("capturerVerbatim" in mediaMetadata)
458
453
  ? Promise.resolve(Object.assign(Object.assign({}, mediaMetadata), { capturerVerbatim: [mediaMetadata.capturerVerbatim] }))
459
454
  : MACode
460
- ? this.apiClient.get("/person/by-id/{personId}", { path: { personId: MACode } }).then(({ fullName = MACode }) => (Object.assign(Object.assign({ capturerVerbatim: Array.isArray(fullName) ? fullName : [fullName] }, mediaMetadata), { intellectualOwner: fullName }))).catch(() => Promise.resolve(mediaMetadata))
455
+ ? this.apiClient.fetchCached(`/person/by-id/${MACode}`).then(({ fullName = MACode }) => (Object.assign(Object.assign({ capturerVerbatim: Array.isArray(fullName) ? fullName : [fullName] }, mediaMetadata), { intellectualOwner: fullName }))).catch(() => Promise.resolve(mediaMetadata))
461
456
  : mediaMetadata;
462
457
  };
463
458
  this.getMaxFileSizeAsString = () => {
@@ -572,67 +567,105 @@ function MediaArrayField(ComposedComponent) {
572
567
  this.renderMediaAddModal()))));
573
568
  }
574
569
  saveMedias(files) {
575
- return __awaiter(this, void 0, void 0, function* () {
576
- const containerId = this.getContainerId();
577
- let tmpMedias = [];
578
- const fail = (translationKey, additionalInfo = "") => {
579
- const translation = (Array.isArray(translationKey) ? translationKey : [translationKey])
580
- .map((key) => this.props.formContext.translations[key])
581
- .join(". ");
582
- throw `${translation} ${additionalInfo}`;
583
- };
584
- try {
585
- const processedFiles = yield this.processFiles(files);
586
- let invalidFile = (files.length <= 0);
587
- let fileTooLarge = false;
588
- let noValidData = true;
589
- if (!this._context.tmpMedias[containerId]) {
590
- this._context.tmpMedias[containerId] = {};
570
+ const containerId = this.getContainerId();
571
+ let tmpMedias;
572
+ const fail = (translationKey, additionalInfo = "") => {
573
+ const translation = (Array.isArray(translationKey) ? translationKey : [translationKey])
574
+ .map((key) => this.props.formContext.translations[key])
575
+ .join(". ");
576
+ throw `${translation} ${additionalInfo}`;
577
+ };
578
+ return this.processFiles(files).then(processedFiles => {
579
+ let invalidFile = (files.length <= 0);
580
+ let fileTooLarge = false;
581
+ let noValidData = true;
582
+ if (!this._context.tmpMedias[containerId]) {
583
+ this._context.tmpMedias[containerId] = {};
584
+ }
585
+ tmpMedias = processedFiles.map(f => {
586
+ mediaUuid++;
587
+ this._context.tmpMedias[containerId][mediaUuid] = f.dataURL;
588
+ return mediaUuid;
589
+ });
590
+ this.mounted && this.setState({ tmpMedias: [...(this.state.tmpMedias || []), ...tmpMedias] });
591
+ const formDataBody = files.reduce((body, file) => {
592
+ if (!this.ALLOWED_FILE_TYPES.includes(file.type)) {
593
+ invalidFile = true;
591
594
  }
592
- tmpMedias = processedFiles.map(f => {
593
- mediaUuid++;
594
- this._context.tmpMedias[containerId][mediaUuid] = f.dataURL;
595
- return mediaUuid;
596
- });
597
- this.mounted && this.setState({ tmpMedias: [...(this.state.tmpMedias || []), ...tmpMedias] });
598
- const formData = files.reduce((body, file) => {
599
- if (!this.ALLOWED_FILE_TYPES.includes(file.type)) {
600
- invalidFile = true;
601
- }
602
- else if (file.size > this.MAX_FILE_SIZE) {
603
- fileTooLarge = true;
604
- }
605
- else {
606
- body.append("data", file);
607
- noValidData = false;
608
- }
609
- return body;
610
- }, new FormData());
611
- if (noValidData && invalidFile) {
612
- fail(this.getAllowedMediaFormatsTranslationKey(), this.getAllowedMediaFormatsAsString() + ".");
613
- return;
595
+ else if (file.size > this.MAX_FILE_SIZE) {
596
+ fileTooLarge = true;
614
597
  }
615
- else if (noValidData && fileTooLarge) {
616
- fail("AllowedFileSize", this.getMaxFileSizeAsString() + ".");
617
- return;
598
+ else {
599
+ body.append("data", file);
600
+ noValidData = false;
618
601
  }
619
- const tmpMediaEntities = yield this.apiClient.post(`/${this.ENDPOINT}`, undefined, formData);
620
- const mediaMetadata = yield this.getMetadataPromise();
621
- const mediaEntities = yield Promise.all(tmpMediaEntities.map((item) => this.apiClient.post(`/${this.ENDPOINT}/{id}`, { path: { id: item.id } }, mediaMetadata)));
622
- const ids = mediaEntities.map((item) => item ? item.id : undefined).filter(item => item !== undefined);
623
- tmpMedias.forEach(id => {
624
- delete this._context.tmpMedias[containerId][id];
602
+ return body;
603
+ }, new FormData());
604
+ if (noValidData && invalidFile) {
605
+ fail(this.getAllowedMediaFormatsTranslationKey(), this.getAllowedMediaFormatsAsString() + ".");
606
+ return;
607
+ }
608
+ else if (noValidData && fileTooLarge) {
609
+ fail("AllowedFileSize", this.getMaxFileSizeAsString() + ".");
610
+ return;
611
+ }
612
+ else {
613
+ return this.apiClient.fetchRaw(`/${this.ENDPOINT}`, undefined, {
614
+ method: "POST",
615
+ body: formDataBody
625
616
  });
626
- this.mounted && this.setState({ tmpMedias: this.state.tmpMedias.filter(id => !tmpMedias.includes(id)) });
627
- return ids;
628
617
  }
629
- catch (e) {
618
+ }).then(response => {
619
+ if (!response)
620
+ return;
621
+ if (response.status < 400) {
622
+ return response.json();
623
+ }
624
+ else if (response.status === 400) {
625
+ fail("InvalidFile");
626
+ }
627
+ else if (response.status === 503) {
628
+ fail("InsufficientSpace");
629
+ }
630
+ else {
631
+ fail(["SomethingWentWrong", "TryAgainLater"]);
632
+ }
633
+ }).then(response => {
634
+ if (!response)
635
+ return;
636
+ return this.getMetadataPromise().then(mediaMetadata => {
637
+ return Promise.all(response.map((item) => {
638
+ return this.apiClient.fetchRaw(`/${this.ENDPOINT}/${item.id}`, undefined, {
639
+ method: "POST",
640
+ headers: {
641
+ "accept": "application/json",
642
+ "content-type": "application/json"
643
+ },
644
+ body: JSON.stringify(mediaMetadata)
645
+ }).then(response => {
646
+ if (response.status < 400) {
647
+ return response.json();
648
+ }
649
+ });
650
+ }));
651
+ });
652
+ }).then(response => {
653
+ if (!response)
654
+ return;
655
+ const ids = response.map((item) => item ? item.id : undefined).filter(item => item !== undefined);
656
+ tmpMedias.forEach(id => {
657
+ delete this._context.tmpMedias[containerId][id];
658
+ });
659
+ this.mounted && this.setState({ tmpMedias: this.state.tmpMedias.filter(id => !tmpMedias.includes(id)) });
660
+ return ids;
661
+ }).catch((e) => {
662
+ if (tmpMedias) {
630
663
  tmpMedias.forEach(id => {
631
664
  delete this._context.tmpMedias[containerId][id];
632
665
  });
633
666
  this.mounted && this.setState({ tmpMedias: this.state.tmpMedias.filter(id => !tmpMedias.includes(id)) });
634
- throw e;
635
667
  }
668
+ throw e;
636
669
  });
637
670
  }
638
671
  };
@@ -673,18 +706,15 @@ function MediaArrayField(ComposedComponent) {
673
706
  class Thumbnail extends React.PureComponent {
674
707
  constructor(props) {
675
708
  super(props);
676
- this.updateURL = (_a) => __awaiter(this, [_a], void 0, function* ({ id, apiClient, apiEndpoint = "images" }) {
709
+ this.updateURL = ({ id, apiClient, apiEndpoint = "images" }) => {
677
710
  if (!id)
678
711
  return;
679
- try {
680
- const response = yield apiClient.get(`/${apiEndpoint}/{id}`, { path: { id } });
712
+ apiClient.fetchCached(`/${apiEndpoint}/${id}`, undefined, { failSilently: true }).then((response) => {
681
713
  if (!this.mounted)
682
714
  return;
683
715
  this.setState({ url: response.squareThumbnailURL, linkUrl: response.originalURL });
684
- }
685
- catch (e) {
686
- }
687
- });
716
+ });
717
+ };
688
718
  this.state = {};
689
719
  this.updateURL(props);
690
720
  }
@@ -38,15 +38,6 @@ var __importStar = (this && this.__importStar) || (function () {
38
38
  return result;
39
39
  };
40
40
  })();
41
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
42
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
43
- return new (P || (P = Promise))(function (resolve, reject) {
44
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
45
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
46
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
47
- step((generator = generator.apply(thisArg, _arguments || [])).next());
48
- });
49
- };
50
41
  var __rest = (this && this.__rest) || function (s, e) {
51
42
  var t = {};
52
43
  for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
@@ -1062,7 +1053,7 @@ function _MapArrayField(ComposedComponent) {
1062
1053
  constructor(props) {
1063
1054
  super(props);
1064
1055
  this.stretchContainerRef = React.createRef();
1065
- this.geocode = () => __awaiter(this, void 0, void 0, function* () {
1056
+ this.geocode = () => {
1066
1057
  // Zoom map to area. Area ID is accessed from schema field defined in options.areaField
1067
1058
  const item = (this.props.formData || [])[this.state.activeIdx];
1068
1059
  const { areaField } = (0, utils_2.getUiOptions)(this.props.uiSchema);
@@ -1075,10 +1066,11 @@ function _MapArrayField(ComposedComponent) {
1075
1066
  }
1076
1067
  const geometries = this.getGeometries();
1077
1068
  if (geometries.length === 0 && area && area.length > 0) {
1078
- const { name } = yield this.props.formContext.apiClient.get(`/areas/${area}`);
1079
- this.map.geocode(name, undefined, 8);
1069
+ this.props.formContext.apiClient.fetch(`/areas/${area}`, undefined, undefined).then((result) => {
1070
+ this.map.geocode(result.name, undefined, 8);
1071
+ });
1080
1072
  }
1081
- });
1073
+ };
1082
1074
  this.computeArea = () => {
1083
1075
  const { activeIdx } = this.state;
1084
1076
  if (activeIdx === undefined)
@@ -36,7 +36,7 @@ export default class MapField extends React.Component<any, any, any> {
36
36
  componentDidUpdate(prevProps: any): void;
37
37
  _lastFormData: any;
38
38
  _zoomToDataOnNextTick: boolean | undefined;
39
- geocode: (prevProps: any) => Promise<void>;
39
+ geocode: (prevProps: any) => void;
40
40
  zoomIfExternalEdit: (props: any) => void;
41
41
  setMapRef: (mapComponent: any) => void;
42
42
  map: any;
@@ -32,15 +32,6 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
- return new (P || (P = Promise))(function (resolve, reject) {
38
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
- step((generator = generator.apply(thisArg, _arguments || [])).next());
42
- });
43
- };
44
35
  var __rest = (this && this.__rest) || function (s, e) {
45
36
  var t = {};
46
37
  for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
@@ -116,7 +107,7 @@ class MapField extends React.Component {
116
107
  this.setState({ locateOn: true });
117
108
  }
118
109
  };
119
- this.geocode = (prevProps) => __awaiter(this, void 0, void 0, function* () {
110
+ this.geocode = (prevProps) => {
120
111
  let { area } = (0, utils_1.getUiOptions)(this.props.uiSchema);
121
112
  if (area instanceof Array) {
122
113
  area = area[0];
@@ -129,10 +120,11 @@ class MapField extends React.Component {
129
120
  || (geoData.type === "FeatureCollection" && geoData.features.length === 0);
130
121
  });
131
122
  if (isEmptyAndWasEmpty && area && area.length > 0) {
132
- const { name } = yield this.props.formContext.apiClient.get(`/areas/${area}`);
133
- this.map.geocode(name, undefined, 8);
123
+ this.props.formContext.apiClient.fetch(`/areas/${area}`, undefined, undefined).then((result) => {
124
+ this.map.geocode(result.name, undefined, 8);
125
+ });
134
126
  }
135
- });
127
+ };
136
128
  this.zoomIfExternalEdit = (props) => {
137
129
  if (!equals(this._lastFormData, props.formData)) {
138
130
  this.map.zoomToData();
@@ -1,8 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { ByLang, FormContext } from "../../LajiForm";
3
+ import { NamedPlace } from "@luomus/laji-schema";
3
4
  import memoize from "memoizee";
4
- import type { components } from "generated/api.d";
5
- type NamedPlace = components["schemas"]["namedPlace"];
6
5
  type Props = {
7
6
  places: NamedPlace[];
8
7
  failed?: boolean;
@@ -1,7 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { FieldProps, JSONSchemaArray, JSONSchemaObject } from "../../../types";
3
- import type { components } from "generated/api.d";
4
- type NamedPlace = components["schemas"]["namedPlace"];
3
+ import { NamedPlace } from "@luomus/laji-schema";
5
4
  type Props = FieldProps<NamedPlace, JSONSchemaObject | JSONSchemaArray<JSONSchemaObject>>;
6
5
  type State = {
7
6
  show?: boolean;
@@ -12,6 +11,7 @@ type State = {
12
11
  /** Compatible only with gatherings array and gathering object */
13
12
  export default class NamedPlaceChooserField extends React.Component<Props, State> {
14
13
  static contextType: React.Context<import("../../../ReactContext").ContextProps>;
14
+ removeIds: Record<string, boolean>;
15
15
  mounted: boolean;
16
16
  state: State;
17
17
  getUiSchema: (props: Props, buttonDefinition: any) => import("../../../types").UiSchema<any> | {
@@ -60,14 +60,9 @@ export default class NamedPlaceChooserField extends React.Component<Props, State
60
60
  };
61
61
  isGatheringsArray: (schema: JSONSchemaObject | JSONSchemaArray<JSONSchemaObject>) => schema is JSONSchemaArray<JSONSchemaObject>;
62
62
  onPlaceSelected: (place: NamedPlace) => void;
63
- onPlaceDeleted: (place: NamedPlace & {
64
- id: string;
65
- }, end: () => void) => Promise<void>;
63
+ onPlaceDeleted: (place: NamedPlace, success: () => void) => void;
66
64
  onButtonClick: () => () => void;
67
- updatePlaces: (response: {
68
- results: NamedPlace[];
69
- }) => void;
70
- getNamedPlacesUnsubscribe: () => void;
65
+ updatePlaces: () => void;
71
66
  componentDidMount(): void;
72
67
  componentWillUnmount(): void;
73
68
  onHide: () => void;
@@ -32,15 +32,6 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
- return new (P || (P = Promise))(function (resolve, reject) {
38
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
- step((generator = generator.apply(thisArg, _arguments || [])).next());
42
- });
43
- };
44
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
45
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
46
37
  };
@@ -68,6 +59,7 @@ const PLACE_DELETE_FAIL = "PLACE_DELETE_FAIL";
68
59
  class NamedPlaceChooserField extends React.Component {
69
60
  constructor() {
70
61
  super(...arguments);
62
+ this.removeIds = {};
71
63
  this.mounted = false;
72
64
  this.state = {};
73
65
  this.getUiSchema = (props, buttonDefinition) => {
@@ -130,61 +122,69 @@ class NamedPlaceChooserField extends React.Component {
130
122
  this.setState({ failed: PLACE_USE_FAIL });
131
123
  }
132
124
  };
133
- this.onPlaceDeleted = (place, end) => __awaiter(this, void 0, void 0, function* () {
134
- try {
135
- yield this.props.formContext.apiClient.delete("/named-places/{id}", { path: { id: place.id } });
136
- end();
137
- }
138
- catch (e) {
125
+ this.onPlaceDeleted = (place, success) => {
126
+ this.props.formContext.apiClient.fetchRaw(`/named-places/${place.id}`, undefined, { method: "DELETE" }).then(response => {
127
+ if (response.status < 400) {
128
+ // It takes a while for API to remove the place, so we remove it locally and then invalidate again after some time.
129
+ this.removeIds[place.id] = true;
130
+ this.props.formContext.apiClient.invalidateCachePath("/named-places");
131
+ setTimeout(() => this.props.formContext.apiClient.invalidateCachePath("/named-places"), 2000);
132
+ success();
133
+ }
134
+ }).catch(() => {
139
135
  this.setState({ failed: PLACE_DELETE_FAIL });
140
136
  this.props.formContext.notifier.error(this.props.formContext.translations.PlaceRemovalFailed);
141
- end();
142
- }
143
- });
137
+ });
138
+ };
144
139
  this.onButtonClick = () => () => {
145
140
  this.setState({ show: true });
146
141
  };
147
- this.updatePlaces = (response) => {
148
- var _a;
149
- if (!this.mounted)
150
- return;
151
- const state = { places: response.results.sort((a, b) => {
152
- if (a.name < b.name)
153
- return -1;
154
- if (a.name > b.name)
155
- return 1;
156
- return 0;
157
- }) };
158
- if (!((_a = response.results) === null || _a === void 0 ? void 0 : _a.length)) {
159
- return;
160
- }
161
- const buttonDefinition = {
162
- fn: this.onButtonClick,
163
- fnName: "addNamedPlace",
164
- glyph: "map-marker",
165
- label: this.props.formContext.translations.ChooseFromNamedPlace,
166
- id: this.props.idSchema.$id,
167
- changesFormData: true,
168
- variant: "primary"
169
- };
170
- if (this.isGatheringsArray(this.props.schema)) {
171
- buttonDefinition.rules = { canAdd: true };
172
- }
173
- else {
174
- buttonDefinition.position = "top";
175
- }
176
- state.buttonDefinition = buttonDefinition;
177
- this.setState(state);
142
+ this.updatePlaces = () => {
143
+ this.props.formContext.apiClient.fetchCached("/named-places", { includePublic: false, pageSize: 100000 }).then(response => {
144
+ var _a;
145
+ if (!this.mounted)
146
+ return;
147
+ const state = { places: response.results.filter(p => !this.removeIds[p.id]).sort((a, b) => {
148
+ if (a.name < b.name)
149
+ return -1;
150
+ if (a.name > b.name)
151
+ return 1;
152
+ return 0;
153
+ }) };
154
+ if (!((_a = response.results) === null || _a === void 0 ? void 0 : _a.length)) {
155
+ return;
156
+ }
157
+ const buttonDefinition = {
158
+ fn: this.onButtonClick,
159
+ fnName: "addNamedPlace",
160
+ glyph: "map-marker",
161
+ label: this.props.formContext.translations.ChooseFromNamedPlace,
162
+ id: this.props.idSchema.$id,
163
+ changesFormData: true,
164
+ variant: "primary"
165
+ };
166
+ if (this.isGatheringsArray(this.props.schema)) {
167
+ buttonDefinition.rules = { canAdd: true };
168
+ }
169
+ else {
170
+ buttonDefinition.position = "top";
171
+ }
172
+ state.buttonDefinition = buttonDefinition;
173
+ this.setState(state);
174
+ }).catch(() => {
175
+ this.setState({ failed: PLACES_FETCH_FAIL });
176
+ });
178
177
  };
179
178
  this.onHide = () => this.setState({ show: false });
180
179
  }
181
180
  componentDidMount() {
182
181
  this.mounted = true;
183
- this.getNamedPlacesUnsubscribe = this.props.formContext.apiClient.subscribe("/named-places", { query: { includePublic: false, pageSize: 100000 } }, this.updatePlaces, () => this.setState({ failed: PLACES_FETCH_FAIL }), 1000);
182
+ this.updatePlaces();
183
+ this.props.formContext.apiClient.onCachePathInvalidation("/named-places", this.updatePlaces);
184
184
  }
185
185
  componentWillUnmount() {
186
186
  this.mounted = false;
187
- this.getNamedPlacesUnsubscribe();
187
+ this.props.formContext.apiClient.removeOnCachePathInvalidation("/named-places", this.updatePlaces);
188
188
  }
189
189
  render() {
190
190
  const { registry: { fields: { SchemaField } }, formContext } = this.props;
@@ -1,7 +1,6 @@
1
1
  import * as React from "react";
2
+ import { NamedPlace } from "@luomus/laji-schema";
2
3
  import { FormContext } from "src/components/LajiForm";
3
- import type { components } from "generated/api.d";
4
- type NamedPlace = components["schemas"]["namedPlace"];
5
4
  type Props = {
6
5
  onPlaceSelected: (place: NamedPlace) => void;
7
6
  onPlaceDeleted: (place: NamedPlace) => void;
@@ -38,15 +38,6 @@ var __importStar = (this && this.__importStar) || (function () {
38
38
  return result;
39
39
  };
40
40
  })();
41
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
42
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
43
- return new (P || (P = Promise))(function (resolve, reject) {
44
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
45
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
46
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
47
- step((generator = generator.apply(thisArg, _arguments || [])).next());
48
- });
49
- };
50
41
  var __rest = (this && this.__rest) || function (s, e) {
51
42
  var t = {};
52
43
  for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
@@ -191,7 +182,7 @@ class PlaceSaverDialog extends React.Component {
191
182
  }
192
183
  componentDidMount() {
193
184
  this.mounted = true;
194
- this.apiClient.get("/named-places", { query: { includePublic: false, pageSize: 100000 } }).then(response => {
185
+ this.apiClient.fetchCached("/named-places", { includePublic: false, pageSize: 1000 }).then(response => {
195
186
  if (!this.mounted)
196
187
  return;
197
188
  const state = {
@@ -222,18 +213,23 @@ class PlaceSaverDialog extends React.Component {
222
213
  });
223
214
  }
224
215
  onSave(place) {
225
- return __awaiter(this, void 0, void 0, function* () {
226
- place = Object.assign(Object.assign({}, place), { geometry: place.prepopulatedDocument.gatherings[0].geometry });
227
- this.props.formContext.services.blocker.push();
228
- try {
229
- const savedPlace = yield this.apiClient[place.id ? "put" : "post"](`/named-places${place.id ? "/{id}" : ""}`, place.id ? { path: { id: place.id } } : undefined, place);
230
- this.props.formContext.services.blocker.pop();
231
- yield this.props.onSave(savedPlace);
232
- }
233
- catch (e) {
234
- this.props.formContext.services.blocker.pop();
235
- this.setState({ failed: SAVE });
236
- }
216
+ place = Object.assign(Object.assign({}, place), { geometry: place.prepopulatedDocument.gatherings[0].geometry });
217
+ this.props.formContext.services.blocker.push();
218
+ this.apiClient.fetchRaw(`/named-places${place.id ? `/${place.id}` : ""}`, undefined, {
219
+ method: place.id ? "PUT" : "POST",
220
+ headers: {
221
+ "accept": "application/json",
222
+ "content-type": "application/json"
223
+ },
224
+ body: JSON.stringify(place)
225
+ }).then(response => {
226
+ this.props.formContext.services.blocker.pop();
227
+ this.apiClient.invalidateCachePath("/named-places");
228
+ return response.json();
229
+ }).then(this.props.onSave)
230
+ .catch(() => {
231
+ this.props.formContext.services.blocker.pop();
232
+ this.setState({ failed: SAVE });
237
233
  });
238
234
  }
239
235
  render() {
@@ -50,7 +50,7 @@ const utils_2 = require("@rjsf/utils");
50
50
  const scopeFieldSettings = {
51
51
  taxonGroups: {
52
52
  translate: (props, taxonGroup) => {
53
- return props.formContext.apiClient.get("/informal-taxon-groups/" + taxonGroup).then((response) => {
53
+ return props.formContext.apiClient.fetchCached("/informal-taxon-groups/" + taxonGroup).then((response) => {
54
54
  return response.name;
55
55
  }).catch(() => {
56
56
  return "";