@luomus/laji-form 15.1.65 → 15.1.67

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 (33) hide show
  1. package/dist/laji-form.js +1 -1
  2. package/lib/ApiClient.d.ts +64 -21
  3. package/lib/ApiClient.js +172 -68
  4. package/lib/components/LajiForm.js +1 -1
  5. package/lib/components/components/DeleteButton.d.ts +1 -0
  6. package/lib/components/components/DeleteButton.js +6 -1
  7. package/lib/components/fields/AnnotationField.js +26 -22
  8. package/lib/components/fields/AudioArrayField.js +18 -5
  9. package/lib/components/fields/AutosuggestField.d.ts +2 -2
  10. package/lib/components/fields/AutosuggestField.js +4 -4
  11. package/lib/components/fields/EnumRangeArrayField.js +1 -1
  12. package/lib/components/fields/GeocoderField.js +2 -9
  13. package/lib/components/fields/ImageArrayField.d.ts +3 -2
  14. package/lib/components/fields/ImageArrayField.js +85 -113
  15. package/lib/components/fields/MapArrayField.js +13 -5
  16. package/lib/components/fields/MapField.d.ts +1 -1
  17. package/lib/components/fields/MapField.js +13 -5
  18. package/lib/components/fields/NamedPlaceChooserField/NamedPlaceChooser.d.ts +2 -1
  19. package/lib/components/fields/NamedPlaceChooserField/NamedPlaceChooserField.d.ts +9 -4
  20. package/lib/components/fields/NamedPlaceChooserField/NamedPlaceChooserField.js +52 -51
  21. package/lib/components/fields/NamedPlaceChooserField/Popup.d.ts +2 -1
  22. package/lib/components/fields/NamedPlaceSaverField.js +22 -18
  23. package/lib/components/fields/ScopeField.js +1 -1
  24. package/lib/components/fields/SingleActiveArrayField.js +2 -2
  25. package/lib/components/fields/SortArrayField.js +1 -1
  26. package/lib/components/fields/UnitCountShorthandField.js +1 -1
  27. package/lib/components/fields/UnitListShorthandArrayField.js +2 -2
  28. package/lib/components/fields/UnitShorthandField.js +4 -4
  29. package/lib/components/widgets/AutosuggestWidget.js +79 -66
  30. package/lib/components/widgets/InformalTaxonGroupChooserWidget.js +1 -1
  31. package/lib/components/widgets/InputWithDefaultValueButtonWidget.js +1 -2
  32. package/lib/validation.js +1 -1
  33. package/package.json +5 -3
@@ -1,47 +1,90 @@
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
+ }
3
8
  interface Query {
4
9
  [param: string]: any;
5
10
  }
6
- interface Options {
7
- failSilently?: boolean;
8
- [option: string]: any;
9
- }
10
11
  export interface ApiClientImplementation {
11
- fetch: (path: string, query: Query, options: any) => Promise<any>;
12
+ fetch: (path: string, query: Query, options: any) => Promise<Response>;
12
13
  }
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
+ };
13
53
  /**
14
- * ApiClient "interface". Wraps the given apiClient as a singleton object.
15
- * Given apiClient must implement fetch().
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.
16
58
  */
17
59
  export default class ApiClient {
18
60
  apiClient: ApiClientImplementation;
19
61
  lang: Lang;
20
62
  translations: Translations;
21
- cache: {
22
- [path: string]: {
23
- [cacheKey: string]: Promise<any>;
24
- };
25
- };
63
+ cacheTree: CacheNode;
26
64
  on: {
27
65
  [path: string]: (() => void)[];
28
66
  };
29
67
  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>]>>;
30
81
  /**
31
82
  * Implementing apiClient must return a promise that passes the raw response as 1st arg.
32
83
  * @param path URL for GET.
33
84
  * @param query Object, where keys are param names and values are param values.
34
85
  * @returns a Promise.
35
86
  */
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;
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>]>>;
45
88
  setLang(lang: Lang): void;
46
89
  }
47
90
  export {};
package/lib/ApiClient.js CHANGED
@@ -1,96 +1,200 @@
1
1
  "use strict";
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;
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
+ });
12
10
  };
13
11
  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
+ };
14
34
  /**
15
- * ApiClient "interface". Wraps the given apiClient as a singleton object.
16
- * Given apiClient must implement fetch().
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.
17
39
  */
18
40
  class ApiClient {
19
41
  constructor(apiClient, lang = "en", translations) {
20
- this.cache = {};
42
+ this.lang = "en";
43
+ this.cacheTree = { branches: new Map(), queries: new Map(), path: "" };
21
44
  this.on = {};
22
- this.flushCache = () => {
23
- this.cache = {};
24
- };
25
45
  this.apiClient = apiClient;
26
46
  this.lang = lang;
27
47
  this.translations = translations;
28
48
  }
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;
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());
39
68
  }
40
- return this.apiClient.fetch(path, _query, options).catch(() => {
41
- throw new Error(this.translations[this.lang].RequestFailed);
42
- });
43
69
  }
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");
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);
49
78
  }
50
- if (response.status === 204) {
51
- return undefined;
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;
52
89
  }
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);
58
- });
59
- }
60
- getCacheKey(query, options) {
61
- return JSON.stringify(query) + JSON.stringify(options);
62
- }
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];
69
- }
70
- invalidateCachePath(path) {
71
- delete this.cache[path];
72
- if (this.on[path]) {
73
- this.on[path].forEach(callback => callback());
74
90
  }
91
+ return node;
75
92
  }
76
- //TODO on invalidation callbacks
77
- invalidateCachePathQuery(path, query) {
78
- if (this.cache[path])
79
- delete this.cache[path][query];
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
+ }
105
+ }
106
+ const result = this.fetch(path, "get", params);
107
+ if (useCache) {
108
+ cacheNode.queries = cacheNode.queries;
109
+ cacheNode.queries.set(serializedQuery, result);
110
+ }
111
+ return result;
112
+ });
80
113
  }
81
- onCachePathInvalidation(path, callback) {
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) {
82
120
  if (!this.on[path])
83
121
  this.on[path] = [];
84
- this.on[path].push(callback);
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
+ };
85
130
  }
86
- removeOnCachePathInvalidation(path, callback) {
87
- if (this.on[path]) {
88
- this.on[path] = this.on[path].filter(fn => fn !== callback);
89
- }
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
+ });
137
+ }
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
+ });
144
+ }
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
+ });
151
+ }
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
+ });
90
179
  }
91
180
  setLang(lang) {
92
181
  this.lang = lang;
93
- this.flushCache();
182
+ this.cacheTree = { branches: new Map(), queries: new Map(), path: "" };
94
183
  }
95
184
  }
96
185
  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) {
513
+ if (props.apiClient && props.apiClient !== this.apiClient.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) {
@@ -24,6 +24,7 @@ export declare class DeleteButton extends React.Component<Props, State> {
24
24
  onButtonKeyDown: ({ key }: React.KeyboardEvent) => void;
25
25
  onHideConfirm: () => void;
26
26
  onShowConfirm: (e: React.MouseEvent) => void;
27
+ onCancelClick: (e: React.MouseEvent) => void;
27
28
  onConfirmedClick: (e?: React.KeyboardEvent | React.MouseEvent) => void;
28
29
  onClick: (e: React.MouseEvent) => void;
29
30
  setConfirmAutofocus: (elem?: React.ReactInstance) => void;
@@ -74,6 +74,11 @@ class DeleteButton extends React.Component {
74
74
  }
75
75
  });
76
76
  };
77
+ this.onCancelClick = (e) => {
78
+ e.preventDefault();
79
+ e.stopPropagation();
80
+ this.onHideConfirm();
81
+ };
77
82
  this.onConfirmedClick = (e) => {
78
83
  e && e.preventDefault();
79
84
  e && e.stopPropagation();
@@ -130,7 +135,7 @@ class DeleteButton extends React.Component {
130
135
  React.createElement("span", null, translations.ConfirmRemove),
131
136
  React.createElement(ButtonGroup, null,
132
137
  React.createElement(components_1.Button, { variant: "danger", onClick: this.onConfirmedClick, ref: this.setConfirmAutofocus, id: `${this.props.id}-delete-confirm-yes` }, translations.Remove),
133
- React.createElement(components_1.Button, { variant: "default", onClick: this.onHideConfirm, id: `${this.props.id}-delete-confirm-no` }, translations.Cancel)))));
138
+ React.createElement(components_1.Button, { variant: "default", onClick: this.onCancelClick, id: `${this.props.id}-delete-confirm-no` }, translations.Cancel)))));
134
139
  }
135
140
  browserConfirm() {
136
141
  const choice = confirm(this.props.translations.ConfirmRemove);
@@ -38,6 +38,15 @@ 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
+ };
41
50
  var __importDefault = (this && this.__importDefault) || function (mod) {
42
51
  return (mod && mod.__esModule) ? mod : { "default": mod };
43
52
  };
@@ -134,29 +143,24 @@ exports.default = AnnotationField;
134
143
  class AnnotationBox extends React.Component {
135
144
  constructor(props) {
136
145
  super(props);
137
- this.onAnnotationSubmit = ({ formData }) => {
146
+ this.onAnnotationSubmit = (_a) => __awaiter(this, [_a], void 0, function* ({ formData }) {
138
147
  const { type } = this.getAddOptions();
139
148
  const rootID = this.props.formContext.services.rootInstance.getFormData().id;
140
149
  this.props.formContext.services.blocker.push();
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 => {
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);
150
153
  this.props.formContext.services.blocker.pop();
151
154
  const annotationContext = (0, Context_1.default)(`${this.props.formContext.contextId}_ANNOTATIONS`);
152
155
  const annotations = [annotation];
153
156
  annotationContext[this.props.id] = annotations;
154
157
  this.setState({ annotations: annotations, fail: false });
155
- }).catch(() => {
158
+ }
159
+ catch (e) {
156
160
  this.props.formContext.services.blocker.pop();
157
161
  this.setState({ fail: true });
158
- });
159
- };
162
+ }
163
+ });
160
164
  this.onAnnotationChange = (formData) => {
161
165
  let state = {};
162
166
  if (this.state.fail !== undefined) {
@@ -213,30 +217,30 @@ class AnnotationBox extends React.Component {
213
217
  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,
214
218
  this.state.fail !== undefined &&
215
219
  React.createElement(Alert, { variant: this.state.fail ? "danger" : "success" }, translations[this.state.fail ? "SaveFail" : "SaveSuccess"]),
216
- renderSubmit && React.createElement(components_1.Button, { id: "submit", type: "submit" }, translations.Submit)))) : null;
220
+ renderSubmit && React.createElement(components_1.Button, { id: "submit", type: "submit", onClick: this.onAnnotationSubmit }, translations.Submit)))) : null;
217
221
  };
218
222
  this.getUiSchema = () => {
219
223
  const { uiSchema } = this.props;
220
224
  const { metadataForm = {} } = this.state;
221
225
  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 });
222
226
  };
223
- this.onDelete = (id) => () => {
224
- this.props.formContext.apiClient.fetchRaw(`/annotations/${id}`, undefined, {
225
- method: "DELETE"
226
- }).then(() => {
227
+ this.onDelete = (id) => () => __awaiter(this, void 0, void 0, function* () {
228
+ try {
229
+ yield this.props.formContext.apiClient.delete("/annotations/{id}", { path: { id } });
227
230
  const annotationContext = (0, Context_1.default)(`${this.props.formContext.contextId}_ANNOTATIONS`);
228
231
  const annotations = this.state.annotations.filter(({ id: _id }) => _id !== id);
229
232
  annotationContext[this.props.id] = annotations;
230
233
  this.setState({ deleteFail: false, annotations });
231
- }).catch(() => {
234
+ }
235
+ catch (e) {
232
236
  this.setState({ deleteFail: true });
233
- });
234
- };
237
+ }
238
+ });
235
239
  this.state = { annotations: props.annotations || [] };
236
240
  }
237
241
  componentDidMount() {
238
242
  this.mounted = true;
239
- this.props.formContext.apiClient.fetchCached(`/forms/${this.props.formId}`, { lang: this.props.lang, format: "schema" })
243
+ this.props.formContext.apiClient.get(`/forms/${this.props.formId}`, { query: { format: "schema" } })
240
244
  .then(metadataForm => {
241
245
  if (!this.mounted)
242
246
  return;
@@ -38,6 +38,15 @@ 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
+ };
41
50
  var __importDefault = (this && this.__importDefault) || function (mod) {
42
51
  return (mod && mod.__esModule) ? mod : { "default": mod };
43
52
  };
@@ -118,14 +127,18 @@ const LajiAudio = React.forwardRef((props, ref) => {
118
127
  const [mp3Url, setMp3Url] = useState(null);
119
128
  const [wavUrl, setWavUrl] = useState(null);
120
129
  const [flacUrl, setFlacUrl] = useState(null);
121
- useEffect(() => {
122
- props.apiClient.fetchCached(`/audio/${props.id}`, undefined, { failSilently: true }).then(response => {
130
+ useEffect(() => __awaiter(void 0, void 0, void 0, function* () {
131
+ let response;
132
+ try {
133
+ response = yield props.apiClient.get(`/audio/${props.id}`);
123
134
  setMp3Url(response.mp3URL);
124
135
  setWavUrl(response.wavURL);
125
136
  setFlacUrl(response.flacURL);
126
- props.onLoaded && props.onLoaded(true);
127
- });
128
- });
137
+ }
138
+ catch (e) {
139
+ }
140
+ props.onLoaded && props.onLoaded(true);
141
+ }));
129
142
  return !mp3Url ? React.createElement(react_spinner_1.default, { style: props.style }) : (React.createElement("div", null,
130
143
  React.createElement("audio", { controls: true, style: props.style, ref: ref, onPause: props.onStop },
131
144
  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", payload: {informalGroups: ["linnut"]}}
11
- * } suggestionReceivers: {someFieldName: "key", someFieldName2: "/payload/informalgroups/0"}
10
+ * <fieldName2>: <suggestion path 2>, Example: autosuggestion = {key: "MLV.2", value: "kalalokki", informalGroups: ["linnut"]}
11
+ * } suggestionReceivers: {someFieldName: "key", someFieldName2: "/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.payload ? suggestion.payload.informalTaxonGroups.map(item => typeof item === "string" ? item : item.id) : [];
46
+ return (suggestion.informalGroups || []).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", payload: {informalGroups: ["linnut"]}}
74
- * } suggestionReceivers: {someFieldName: "key", someFieldName2: "/payload/informalgroups/0"}
73
+ * <fieldName2>: <suggestion path 2>, Example: autosuggestion = {key: "MLV.2", value: "kalalokki", informalGroups: ["linnut"]}
74
+ * } suggestionReceivers: {someFieldName: "key", someFieldName2: "/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.payload;
171
+ let { unit } = suggestion;
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.fetchCached(`/metadata/ranges/${range}`);
63
+ const enums = yield props.formContext.apiClient.get(`/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,15 +292,8 @@ 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.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);
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);
304
297
  });
305
298
  }));
306
299
  };