@luomus/laji-form 15.1.67 → 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 -64
  3. package/lib/ApiClient.js +68 -172
  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 -52
  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 +5 -5
  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
@@ -1,90 +1,47 @@
1
1
  import { Translations } from "./components/LajiForm";
2
2
  import { Lang } from "./types";
3
- import type { paths } from "generated/api";
4
- export declare class LajiApiError extends Error {
5
- statusCode: number;
6
- constructor(message: string, statusCode: number);
7
- }
8
3
  interface Query {
9
4
  [param: string]: any;
10
5
  }
6
+ interface Options {
7
+ failSilently?: boolean;
8
+ [option: string]: any;
9
+ }
11
10
  export interface ApiClientImplementation {
12
- fetch: (path: string, query: Query, options: any) => Promise<Response>;
11
+ fetch: (path: string, query: Query, options: any) => Promise<any>;
13
12
  }
14
- type RelaxQuery<P, K extends string> = P extends {
15
- query: infer Q;
16
- } ? Omit<P, "query"> & (Record<string, unknown> extends Omit<Q, Extract<keyof Q, K>> ? {
17
- query?: Omit<Q, Extract<keyof Q, K>> & Partial<Pick<Q, Extract<keyof Q, K>>>;
18
- } : {
19
- query: Omit<Q, Extract<keyof Q, K>> & Partial<Pick<Q, Extract<keyof Q, K>>>;
20
- }) : P;
21
- type MiddlewareInjectedKeys = "collectionID" | "formID" | "personToken";
22
- type Parameters<T> = "parameters" extends keyof T ? T["parameters"] : never;
23
- type ExtractContentIfExists<R> = R extends {
24
- content: infer C;
25
- } ? C[keyof C] : null;
26
- type ExtractRequestBodyIfExists<R> = R extends {
27
- requestBody: {
28
- content: infer C;
29
- };
30
- } | {
31
- requestBody?: {
32
- content: infer C;
33
- };
34
- } ? C[keyof C] : never;
35
- type HttpSuccessCodes = 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226;
36
- type IntersectUnionTypes<A, B> = A extends B ? A : never;
37
- type WithResponses<T> = T & {
38
- responses: unknown;
39
- };
40
- type Path = keyof paths & string;
41
- type PathWithMethod<M extends string> = keyof {
42
- [P in keyof paths as paths[P] extends Record<M, any> ? P : never]: paths[P];
43
- };
44
- type Method<P extends Path> = Exclude<keyof {
45
- [K in keyof paths[P] as paths[P][K] extends undefined | never ? never : K]: paths[P][K];
46
- }, "parameters">;
47
- type Responses<P extends Path, M extends Method<P>> = WithResponses<paths[P][M]>["responses"];
48
- type CacheNode = {
49
- branches: Map<string, CacheNode>;
50
- queries: Map<string, Promise<any>>;
51
- path: string;
52
- };
53
13
  /**
54
- * ApiClient with automatically generated typings for laji-api. An `ApiClientImplementation` must be provided, that will
55
- * perform the actual requests. The implementation acts as a middleware for injecting access token and person token.
56
- *
57
- * Get requests are automatically cached, and they will flush if the resource receives a POST/PUT/DELETE request.
14
+ * ApiClient "interface". Wraps the given apiClient as a singleton object.
15
+ * Given apiClient must implement fetch().
58
16
  */
59
17
  export default class ApiClient {
60
18
  apiClient: ApiClientImplementation;
61
19
  lang: Lang;
62
20
  translations: Translations;
63
- cacheTree: CacheNode;
21
+ cache: {
22
+ [path: string]: {
23
+ [cacheKey: string]: Promise<any>;
24
+ };
25
+ };
64
26
  on: {
65
27
  [path: string]: (() => void)[];
66
28
  };
67
29
  constructor(apiClient: ApiClientImplementation, lang: Lang | undefined, translations: Translations);
68
- private flush;
69
- private getCacheNode;
70
- /** The response is cached until the resource receives a POST/PUT/DELETE request. */
71
- get<P extends PathWithMethod<"get">, R extends Responses<P, "get" extends Method<P> ? "get" : never>>(path: P, params?: RelaxQuery<Parameters<paths[P]["get"]>, MiddlewareInjectedKeys>, useCache?: boolean): Promise<ExtractContentIfExists<R[IntersectUnionTypes<keyof R, HttpSuccessCodes>]>>;
72
- /** Subscribe to a GET request. It re-emits when the resource receives a POST/PUT/DELETE request. */
73
- subscribe<P extends PathWithMethod<"get">, R extends Responses<P, "get" extends Method<P> ? "get" : never>>(path: P, params: RelaxQuery<Parameters<paths[P]["get"]>, MiddlewareInjectedKeys> | undefined, onFulfilled: (response: ExtractContentIfExists<R[IntersectUnionTypes<keyof R, HttpSuccessCodes>]>) => void, onError?: () => void,
74
- /**
75
- * Store doesn't sync it's search indices right away after data is changed, so delaying the request can be useful
76
- */
77
- delay?: number): () => void;
78
- put<P extends PathWithMethod<"put">, R extends Responses<P, "put" extends Method<P> ? "put" : never>>(path: P, params?: RelaxQuery<Parameters<paths[P]["put"]>, MiddlewareInjectedKeys>, body?: ExtractRequestBodyIfExists<paths[P]["put"]>): Promise<ExtractContentIfExists<R[IntersectUnionTypes<keyof R, HttpSuccessCodes>]>>;
79
- post<P extends PathWithMethod<"post">, R extends Responses<P, "post" extends Method<P> ? "post" : never>>(path: P, params?: RelaxQuery<Parameters<paths[P]["post"]>, MiddlewareInjectedKeys>, body?: ExtractRequestBodyIfExists<paths[P]["post"]>): Promise<ExtractContentIfExists<R[IntersectUnionTypes<keyof R, HttpSuccessCodes>]>>;
80
- delete<P extends PathWithMethod<"delete">, R extends Responses<P, "delete" extends Method<P> ? "delete" : never>>(path: P, params?: RelaxQuery<Parameters<paths[P]["delete"]>, MiddlewareInjectedKeys>): Promise<ExtractContentIfExists<R[IntersectUnionTypes<keyof R, HttpSuccessCodes>]>>;
81
30
  /**
82
31
  * Implementing apiClient must return a promise that passes the raw response as 1st arg.
83
32
  * @param path URL for GET.
84
33
  * @param query Object, where keys are param names and values are param values.
85
34
  * @returns a Promise.
86
35
  */
87
- fetch<P extends Path, M extends Method<P>, R extends Responses<P, M>>(path: P, method: M, params?: Parameters<paths[P][M]>, body?: ExtractRequestBodyIfExists<paths[P][M]>, options?: any): Promise<ExtractContentIfExists<R[IntersectUnionTypes<keyof R, HttpSuccessCodes>]>>;
36
+ fetchRaw(path: string, query?: Query, options?: any): Promise<any>;
37
+ fetch<T>(path: string, query?: Query, options?: Options): Promise<T>;
38
+ getCacheKey(query?: Query, options?: Options): string;
39
+ fetchCached<T>(path: string, query?: Query, options?: Options): Promise<T>;
40
+ invalidateCachePath(path: string): void;
41
+ invalidateCachePathQuery(path: string, query: string): void;
42
+ flushCache: () => void;
43
+ onCachePathInvalidation(path: string, callback: () => void): void;
44
+ removeOnCachePathInvalidation(path: string, callback: () => void): void;
88
45
  setLang(lang: Lang): void;
89
46
  }
90
47
  export {};
package/lib/ApiClient.js CHANGED
@@ -1,200 +1,96 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
10
12
  };
11
13
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.LajiApiError = void 0;
13
- class LajiApiError extends Error {
14
- constructor(message, statusCode) {
15
- super(message);
16
- this.statusCode = statusCode;
17
- }
18
- }
19
- exports.LajiApiError = LajiApiError;
20
- const splitAndResolvePath = (path, params) => {
21
- // parse path into segments based on path parameters
22
- // eg. "/person/{personToken}/profile" -> ["/person", "/{personToken}", "/profile"]
23
- const segments = path.split(/(\/\{[^}]+\})/).filter(Boolean);
24
- if (!params) {
25
- return segments;
26
- }
27
- // resolve path parameters (eg. replace '/{personToken}' with params.path.personToken)
28
- for (let i = 1; i < segments.length; i++) {
29
- const segment = segments[i];
30
- segments[i] = segment.replace(/\{([^}]+)\}/, (match, p1) => { var _a; return (_a = params["path"][p1]) !== null && _a !== void 0 ? _a : ""; });
31
- }
32
- return segments;
33
- };
34
14
  /**
35
- * ApiClient with automatically generated typings for laji-api. An `ApiClientImplementation` must be provided, that will
36
- * perform the actual requests. The implementation acts as a middleware for injecting access token and person token.
37
- *
38
- * Get requests are automatically cached, and they will flush if the resource receives a POST/PUT/DELETE request.
15
+ * ApiClient "interface". Wraps the given apiClient as a singleton object.
16
+ * Given apiClient must implement fetch().
39
17
  */
40
18
  class ApiClient {
41
19
  constructor(apiClient, lang = "en", translations) {
42
- this.lang = "en";
43
- this.cacheTree = { branches: new Map(), queries: new Map(), path: "" };
20
+ this.cache = {};
44
21
  this.on = {};
22
+ this.flushCache = () => {
23
+ this.cache = {};
24
+ };
45
25
  this.apiClient = apiClient;
46
26
  this.lang = lang;
47
27
  this.translations = translations;
48
28
  }
49
- flush(path, params) {
50
- var _a;
51
- const segments = splitAndResolvePath(path, params);
52
- let node = this.cacheTree;
53
- const nodesToFlush = [];
54
- for (let i = 0; i < segments.length; i++) {
55
- const segment = segments[i];
56
- if (node.branches.has(segment)) {
57
- node = node.branches.get(segment);
58
- nodesToFlush.push(node);
59
- }
60
- else {
61
- break;
62
- }
63
- }
64
- for (let i = 0; i < nodesToFlush.length; i++) {
65
- const node = nodesToFlush[i];
66
- node.queries = new Map();
67
- (_a = this.on[node.path]) === null || _a === void 0 ? void 0 : _a.forEach(fn => fn());
68
- }
69
- }
70
- getCacheNode(path, params) {
71
- const segments = splitAndResolvePath(path, params);
72
- const serializedQuery = hashRecord((params === null || params === void 0 ? void 0 : params.query) || {});
73
- let node = this.cacheTree;
74
- for (let i = 0; i < segments.length; i++) {
75
- const segment = segments[i];
76
- if (node.branches.has(segment)) {
77
- node = node.branches.get(segment);
78
- }
79
- else {
80
- const newNode = { branches: new Map(), queries: new Map(), path: segments.join("") };
81
- node.branches.set(segment, newNode);
82
- node = newNode;
83
- }
84
- if (i === segments.length - 1) {
85
- continue;
86
- }
87
- if (node.queries.has(serializedQuery)) {
88
- return node;
89
- }
29
+ /**
30
+ * Implementing apiClient must return a promise that passes the raw response as 1st arg.
31
+ * @param path URL for GET.
32
+ * @param query Object, where keys are param names and values are param values.
33
+ * @returns a Promise.
34
+ */
35
+ fetchRaw(path, query, options) {
36
+ const _query = Object.assign({ lang: this.lang }, (query || {}));
37
+ if (!_query.lang) {
38
+ delete _query.lang;
90
39
  }
91
- return node;
40
+ return this.apiClient.fetch(path, _query, options).catch(() => {
41
+ throw new Error(this.translations[this.lang].RequestFailed);
42
+ });
92
43
  }
93
- ;
94
- /** The response is cached until the resource receives a POST/PUT/DELETE request. */
95
- get(path_1, params_1) {
96
- return __awaiter(this, arguments, void 0, function* (path, params, useCache = true) {
97
- let cacheNode;
98
- let serializedQuery;
99
- if (useCache) {
100
- serializedQuery = hashRecord(params === null || params === void 0 ? void 0 : params.query);
101
- cacheNode = this.getCacheNode(path, params);
102
- if (cacheNode.queries.has(serializedQuery)) {
103
- return cacheNode.queries.get(serializedQuery);
104
- }
44
+ fetch(path, query, options = {}) {
45
+ const { failSilently = false } = options, fetchOptions = __rest(options, ["failSilently"]);
46
+ return this.fetchRaw(path, query, fetchOptions).then(response => {
47
+ if (!failSilently && response.status >= 400) {
48
+ throw new Error("Request failed");
105
49
  }
106
- const result = this.fetch(path, "get", params);
107
- if (useCache) {
108
- cacheNode.queries = cacheNode.queries;
109
- cacheNode.queries.set(serializedQuery, result);
50
+ if (response.status === 204) {
51
+ return undefined;
110
52
  }
111
- return result;
53
+ return response.json();
54
+ }).catch(() => {
55
+ if (this.cache[path])
56
+ delete this.cache[path][this.getCacheKey(query, options)];
57
+ throw new Error(this.translations[this.lang].RequestFailed);
112
58
  });
113
59
  }
114
- /** Subscribe to a GET request. It re-emits when the resource receives a POST/PUT/DELETE request. */
115
- subscribe(path, params, onFulfilled, onError,
116
- /**
117
- * Store doesn't sync it's search indices right away after data is changed, so delaying the request can be useful
118
- */
119
- delay) {
120
- if (!this.on[path])
121
- this.on[path] = [];
122
- const fn = delay
123
- ? () => { setTimeout(() => this.get(path, params).then(onFulfilled, onError), delay); }
124
- : () => { this.get(path, params).then(onFulfilled, onError); };
125
- this.on[path].push(fn);
126
- this.get(path, params).then(onFulfilled, onError);
127
- return () => {
128
- this.on[path] = this.on[path].filter(_fn => _fn !== fn);
129
- };
60
+ getCacheKey(query, options) {
61
+ return JSON.stringify(query) + JSON.stringify(options);
130
62
  }
131
- put(path, params, body) {
132
- return __awaiter(this, void 0, void 0, function* () {
133
- const result = yield this.fetch(path, "put", params, body);
134
- this.flush(path, params);
135
- return result;
136
- });
63
+ fetchCached(path, query, options) {
64
+ const cacheKey = this.getCacheKey(query, options);
65
+ if (!this.cache[path])
66
+ this.cache[path] = {};
67
+ this.cache[path][cacheKey] = cacheKey in this.cache[path] ? this.cache[path][cacheKey] : this.fetch(path, query, options);
68
+ return this.cache[path][cacheKey];
137
69
  }
138
- post(path, params, body) {
139
- return __awaiter(this, void 0, void 0, function* () {
140
- const result = this.fetch(path, "post", params, body);
141
- this.flush(path, params);
142
- return result;
143
- });
70
+ invalidateCachePath(path) {
71
+ delete this.cache[path];
72
+ if (this.on[path]) {
73
+ this.on[path].forEach(callback => callback());
74
+ }
144
75
  }
145
- delete(path, params) {
146
- return __awaiter(this, void 0, void 0, function* () {
147
- const result = this.fetch(path, "delete", params);
148
- this.flush(path, params);
149
- return result;
150
- });
76
+ //TODO on invalidation callbacks
77
+ invalidateCachePathQuery(path, query) {
78
+ if (this.cache[path])
79
+ delete this.cache[path][query];
151
80
  }
152
- /**
153
- * Implementing apiClient must return a promise that passes the raw response as 1st arg.
154
- * @param path URL for GET.
155
- * @param query Object, where keys are param names and values are param values.
156
- * @returns a Promise.
157
- */
158
- fetch(path_1, method_1, params_1, body_1) {
159
- return __awaiter(this, arguments, void 0, function* (path, method, params, body, options = {}) {
160
- const _query = Object.assign({ lang: this.lang }, ((params === null || params === void 0 ? void 0 : params.query) || {}));
161
- if (!_query.lang) {
162
- delete _query.lang;
163
- }
164
- const pathSegments = splitAndResolvePath(path, params);
165
- options = Object.assign(Object.assign({}, options), { headers: Object.assign(Object.assign({}, (options.header || {})), { "API-Version": 1, "Accept-Language": this.lang }) });
166
- if (body && !otherThanJSON(body)) {
167
- options = Object.assign(Object.assign({}, options), { headers: Object.assign(Object.assign({}, (options.header || {})), { "Content-Type": "application/json", "Accept": "application/json" }) });
168
- body = JSON.stringify(body);
169
- }
170
- const response = yield this.apiClient.fetch(pathSegments.join(""), _query, Object.assign({ method, body }, options));
171
- if (response.status >= 400) {
172
- const error = yield response.json();
173
- throw new LajiApiError(error === null || error === void 0 ? void 0 : error.message, response.status);
174
- }
175
- return (response.headers.get("Content-Type") || "").includes("application/json")
176
- ? response.json()
177
- : response.text();
178
- });
81
+ onCachePathInvalidation(path, callback) {
82
+ if (!this.on[path])
83
+ this.on[path] = [];
84
+ this.on[path].push(callback);
85
+ }
86
+ removeOnCachePathInvalidation(path, callback) {
87
+ if (this.on[path]) {
88
+ this.on[path] = this.on[path].filter(fn => fn !== callback);
89
+ }
179
90
  }
180
91
  setLang(lang) {
181
92
  this.lang = lang;
182
- this.cacheTree = { branches: new Map(), queries: new Map(), path: "" };
93
+ this.flushCache();
183
94
  }
184
95
  }
185
96
  exports.default = ApiClient;
186
- const otherThanJSON = (body) => body instanceof FormData ||
187
- body instanceof Blob ||
188
- body instanceof ArrayBuffer ||
189
- body instanceof URLSearchParams ||
190
- body instanceof ReadableStream;
191
- function isRecord(value) {
192
- return typeof value === "object" && value !== null && !Array.isArray(value);
193
- }
194
- const sortRecordRecursively = (record) => (Object.entries(record)
195
- .sort(([aKey, aVal], [bKey, bVal]) => aKey > bKey ? 1 : aKey < bKey ? -1 : 0)
196
- .reduce((acc, [key, value]) => {
197
- acc[key] = isRecord(value) ? sortRecordRecursively(value) : value;
198
- return acc;
199
- }, Object.create(null)));
200
- const hashRecord = (record) => JSON.stringify(sortRecordRecursively(record || {}));
@@ -510,7 +510,7 @@ class LajiForm extends React.Component {
510
510
  this.state = this.getStateFromProps(props);
511
511
  }
512
512
  UNSAFE_componentWillReceiveProps(props) {
513
- if (props.apiClient && props.apiClient !== this.apiClient.apiClient) {
513
+ if (props.apiClient && props.apiClient !== this.apiClient) {
514
514
  this.apiClient = new ApiClient_1.default(props.apiClient, props.lang, this.translations);
515
515
  }
516
516
  if (this.apiClient && "lang" in props && this.props.lang !== props.lang) {
@@ -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 __importDefault = (this && this.__importDefault) || function (mod) {
51
42
  return (mod && mod.__esModule) ? mod : { "default": mod };
52
43
  };
@@ -143,24 +134,29 @@ exports.default = AnnotationField;
143
134
  class AnnotationBox extends React.Component {
144
135
  constructor(props) {
145
136
  super(props);
146
- this.onAnnotationSubmit = (_a) => __awaiter(this, [_a], void 0, function* ({ formData }) {
137
+ this.onAnnotationSubmit = ({ formData }) => {
147
138
  const { type } = this.getAddOptions();
148
139
  const rootID = this.props.formContext.services.rootInstance.getFormData().id;
149
140
  this.props.formContext.services.blocker.push();
150
- const body = Object.assign(Object.assign({}, formData), { targetID: this.props.id, rootID, type, byRole: "MMAN.formAdmin" });
151
- try {
152
- const annotation = yield this.props.formContext.apiClient.post("/annotations", undefined, body);
141
+ this.props.formContext.apiClient.fetchRaw("/annotations", undefined, {
142
+ method: "POST",
143
+ body: JSON.stringify(Object.assign(Object.assign({}, formData), { targetID: this.props.id, rootID, type, byRole: "MMAN.formAdmin" }))
144
+ }).then(response => {
145
+ if (response.status >= 400) {
146
+ throw new Error("Request failed");
147
+ }
148
+ return response.json();
149
+ }).then(annotation => {
153
150
  this.props.formContext.services.blocker.pop();
154
151
  const annotationContext = (0, Context_1.default)(`${this.props.formContext.contextId}_ANNOTATIONS`);
155
152
  const annotations = [annotation];
156
153
  annotationContext[this.props.id] = annotations;
157
154
  this.setState({ annotations: annotations, fail: false });
158
- }
159
- catch (e) {
155
+ }).catch(() => {
160
156
  this.props.formContext.services.blocker.pop();
161
157
  this.setState({ fail: true });
162
- }
163
- });
158
+ });
159
+ };
164
160
  this.onAnnotationChange = (formData) => {
165
161
  let state = {};
166
162
  if (this.state.fail !== undefined) {
@@ -217,30 +213,30 @@ class AnnotationBox extends React.Component {
217
213
  return add && addSchema ? (React.createElement(LajiForm_1.default, Object.assign({}, metadataForm, { schema: addSchema, uiSchema: addUiSchema || _uiSchema, onSubmit: this.onAnnotationSubmit, onChange: this.onAnnotationChange, renderSubmit: renderSubmit, formData: addFormData, lang: lang, apiClient: this.props.formContext.apiClient.apiClient, uiSchemaContext: this.props.formContext.uiSchemaContext }), React.createElement("div", null,
218
214
  this.state.fail !== undefined &&
219
215
  React.createElement(Alert, { variant: this.state.fail ? "danger" : "success" }, translations[this.state.fail ? "SaveFail" : "SaveSuccess"]),
220
- renderSubmit && React.createElement(components_1.Button, { id: "submit", type: "submit", onClick: this.onAnnotationSubmit }, translations.Submit)))) : null;
216
+ renderSubmit && React.createElement(components_1.Button, { id: "submit", type: "submit" }, translations.Submit)))) : null;
221
217
  };
222
218
  this.getUiSchema = () => {
223
219
  const { uiSchema } = this.props;
224
220
  const { metadataForm = {} } = this.state;
225
221
  return uiSchema || Object.assign(Object.assign({}, metadataForm.uiSchema), { "ui:shortcuts": Object.assign(Object.assign({}, ((metadataForm.uiSchema || {})["ui:shorcuts"] || {})), (this.props.formContext.services.keyHandler.shortcuts)), "ui:showShortcutsButton": false });
226
222
  };
227
- this.onDelete = (id) => () => __awaiter(this, void 0, void 0, function* () {
228
- try {
229
- yield this.props.formContext.apiClient.delete("/annotations/{id}", { path: { id } });
223
+ this.onDelete = (id) => () => {
224
+ this.props.formContext.apiClient.fetchRaw(`/annotations/${id}`, undefined, {
225
+ method: "DELETE"
226
+ }).then(() => {
230
227
  const annotationContext = (0, Context_1.default)(`${this.props.formContext.contextId}_ANNOTATIONS`);
231
228
  const annotations = this.state.annotations.filter(({ id: _id }) => _id !== id);
232
229
  annotationContext[this.props.id] = annotations;
233
230
  this.setState({ deleteFail: false, annotations });
234
- }
235
- catch (e) {
231
+ }).catch(() => {
236
232
  this.setState({ deleteFail: true });
237
- }
238
- });
233
+ });
234
+ };
239
235
  this.state = { annotations: props.annotations || [] };
240
236
  }
241
237
  componentDidMount() {
242
238
  this.mounted = true;
243
- this.props.formContext.apiClient.get(`/forms/${this.props.formId}`, { query: { format: "schema" } })
239
+ this.props.formContext.apiClient.fetchCached(`/forms/${this.props.formId}`, { lang: this.props.lang, format: "schema" })
244
240
  .then(metadataForm => {
245
241
  if (!this.mounted)
246
242
  return;
@@ -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 __importDefault = (this && this.__importDefault) || function (mod) {
51
42
  return (mod && mod.__esModule) ? mod : { "default": mod };
52
43
  };
@@ -127,18 +118,14 @@ const LajiAudio = React.forwardRef((props, ref) => {
127
118
  const [mp3Url, setMp3Url] = useState(null);
128
119
  const [wavUrl, setWavUrl] = useState(null);
129
120
  const [flacUrl, setFlacUrl] = useState(null);
130
- useEffect(() => __awaiter(void 0, void 0, void 0, function* () {
131
- let response;
132
- try {
133
- response = yield props.apiClient.get(`/audio/${props.id}`);
121
+ useEffect(() => {
122
+ props.apiClient.fetchCached(`/audio/${props.id}`, undefined, { failSilently: true }).then(response => {
134
123
  setMp3Url(response.mp3URL);
135
124
  setWavUrl(response.wavURL);
136
125
  setFlacUrl(response.flacURL);
137
- }
138
- catch (e) {
139
- }
140
- props.onLoaded && props.onLoaded(true);
141
- }));
126
+ props.onLoaded && props.onLoaded(true);
127
+ });
128
+ });
142
129
  return !mp3Url ? React.createElement(react_spinner_1.default, { style: props.style }) : (React.createElement("div", null,
143
130
  React.createElement("audio", { controls: true, style: props.style, ref: ref, onPause: props.onStop },
144
131
  React.createElement("source", { src: mp3Url })),
@@ -7,8 +7,8 @@
7
7
  * suggestionValueField: <fieldName> (the field which the value for autosuggest is pulled from)
8
8
  * suggestionReceivers: {
9
9
  * <fieldName>: <suggestion path>, (when an autosuggestion is selected, these fields receive the autosuggestions value defined by suggestion path.
10
- * <fieldName2>: <suggestion path 2>, Example: autosuggestion = {key: "MLV.2", value: "kalalokki", informalGroups: ["linnut"]}
11
- * } suggestionReceivers: {someFieldName: "key", someFieldName2: "/informalgroups/0"}
10
+ * <fieldName2>: <suggestion path 2>, Example: autosuggestion = {key: "MLV.2", value: "kalalokki", payload: {informalGroups: ["linnut"]}}
11
+ * } suggestionReceivers: {someFieldName: "key", someFieldName2: "/payload/informalgroups/0"}
12
12
  * If fieldName start with '$', then a function from autosuggestFieldSettings parses the suggestion. Example: $taxonGroup
13
13
  * If fieldName start with '/', it is handled as a JSON pointer.
14
14
  * uiSchema: <uiSchema> (uiSchema which is passed to inner SchemaField)
@@ -43,7 +43,7 @@ const Context_1 = __importDefault(require("../../Context"));
43
43
  const deepmerge_1 = __importDefault(require("deepmerge"));
44
44
  const suggestionParsers = {
45
45
  taxonGroup: suggestion => {
46
- return (suggestion.informalGroups || []).map(item => typeof item === "string" ? item : item.id);
46
+ return suggestion.payload ? suggestion.payload.informalTaxonGroups.map(item => typeof item === "string" ? item : item.id) : [];
47
47
  }
48
48
  };
49
49
  const parseQuery = (query, props, taxonGroups) => {
@@ -70,8 +70,8 @@ const parseQuery = (query, props, taxonGroups) => {
70
70
  * suggestionValueField: <fieldName> (the field which the value for autosuggest is pulled from)
71
71
  * suggestionReceivers: {
72
72
  * <fieldName>: <suggestion path>, (when an autosuggestion is selected, these fields receive the autosuggestions value defined by suggestion path.
73
- * <fieldName2>: <suggestion path 2>, Example: autosuggestion = {key: "MLV.2", value: "kalalokki", informalGroups: ["linnut"]}
74
- * } suggestionReceivers: {someFieldName: "key", someFieldName2: "/informalgroups/0"}
73
+ * <fieldName2>: <suggestion path 2>, Example: autosuggestion = {key: "MLV.2", value: "kalalokki", payload: {informalGroups: ["linnut"]}}
74
+ * } suggestionReceivers: {someFieldName: "key", someFieldName2: "/payload/informalgroups/0"}
75
75
  * If fieldName start with '$', then a function from autosuggestFieldSettings parses the suggestion. Example: $taxonGroup
76
76
  * If fieldName start with '/', it is handled as a JSON pointer.
77
77
  * uiSchema: <uiSchema> (uiSchema which is passed to inner SchemaField)
@@ -168,7 +168,7 @@ class AutosuggestField extends React.Component {
168
168
  return formData;
169
169
  };
170
170
  if (autosuggestField === "unit") {
171
- let { unit } = suggestion;
171
+ let { unit } = suggestion.payload;
172
172
  if (unit.unitType) {
173
173
  unit.informalTaxonGroups = unit.unitType;
174
174
  delete unit.unitType;
@@ -60,7 +60,7 @@ function EnumRangeArrayField(props) {
60
60
  propsOnChange(value);
61
61
  }, [propsOnChange]);
62
62
  const getEnumOptionsAsync = (0, react_1.useCallback)(() => __awaiter(this, void 0, void 0, function* () {
63
- const enums = yield props.formContext.apiClient.get(`/metadata/ranges/${range}`);
63
+ const enums = yield props.formContext.apiClient.fetchCached(`/metadata/ranges/${range}`);
64
64
  return enums.map(({ value }) => ({ value, label: value }));
65
65
  }), [props.formContext.apiClient, range]);
66
66
  const { Label } = props.formContext;
@@ -292,8 +292,15 @@ let GeocoderField = class GeocoderField extends React.Component {
292
292
  }, 30 * 1000);
293
293
  !bounds.overlaps(globals_1.FINLAND_BOUNDS)
294
294
  ? fetchForeign()
295
- : this.props.formContext.apiClient.post("/coordinates/location", undefined, geometry).then().then(handleResponse(props.formContext.translations.Finland, "municipality", "biologicalProvince", "biogeographicalProvince")).catch((e) => {
296
- fail(typeof e === "string" ? e : e.message);
295
+ : this.props.formContext.apiClient.fetchRaw("/coordinates/location", undefined, {
296
+ method: "POST",
297
+ headers: {
298
+ "accept": "application/json",
299
+ "content-type": "application/json"
300
+ },
301
+ body: JSON.stringify(geometry)
302
+ }).then(response => response.json()).then(handleResponse(props.formContext.translations.Finland, "municipality", "biologicalProvince", "biogeographicalProvince")).catch((e) => {
303
+ fail(e.message);
297
304
  });
298
305
  }));
299
306
  };
@@ -99,7 +99,7 @@ export declare function MediaArrayField<LFC extends Constructor<React.Component<
99
99
  processFile: (file: File) => Promise<ProcessedFile>;
100
100
  onMediaMetadataUpdate: ({ formData }: {
101
101
  formData: any;
102
- }) => Promise<void>;
102
+ }) => void;
103
103
  getMetadataPromise: () => Promise<MediaMetadataSchema>;
104
104
  getMaxFileSizeAsString: () => string;
105
105
  getAllowedMediaFormatsTranslationKey: () => "AllowedFileFormat" | "AllowedFileFormats";
@@ -170,7 +170,7 @@ export declare class Thumbnail extends React.PureComponent<ThumbnailProps, Thumb
170
170
  componentDidMount(): void;
171
171
  componentWillUnmount(): void;
172
172
  UNSAFE_componentWillReceiveProps(props: ThumbnailProps): void;
173
- updateURL: ({ id, apiClient, apiEndpoint }: ThumbnailProps) => Promise<void>;
173
+ updateURL: ({ id, apiClient, apiEndpoint }: ThumbnailProps) => void;
174
174
  render(): JSX.Element | null;
175
175
  }
176
176
  export {};