@evoke-platform/context 1.0.0-dev.99 → 1.0.1-dev.0

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.
package/README.md CHANGED
@@ -17,7 +17,8 @@ available and no further installation is necessary.
17
17
 
18
18
  - [Working With Objects](#working-with-objects)
19
19
  - [REST API Calls](#rest-api-calls)
20
- - [SignalR Connection](#signalr-connection)
20
+ - [Authentication Context](#authentication-context)
21
+ - [Notifications](#notifications)
21
22
 
22
23
  ### Working With Objects
23
24
 
@@ -160,7 +161,16 @@ Returns a function that can be used to navigate to another page. The returned fu
160
161
 
161
162
  #### `useApp()`
162
163
 
163
- Returns the currently loaded Evoke app.
164
+ Returns the currently loaded Evoke App as well as the following function.
165
+
166
+ - `findDefaultPageSlugFor`: An asynchronous function that takes an `objectId` as a parameter and returns the default page slug for that object, if no default page slug is found and the object is a subtype, the page slug of the first ancestor with a default page will be used. It returns `undefined` if no default page slug is found for any ancestor type.
167
+
168
+ Example usage:
169
+
170
+ ```javascript
171
+ const { id: appId, findDefaultPageSlugFor } = useApp();
172
+ const defaultPageSlug = await findDefaultPageSlugFor(objectId);
173
+ ```
164
174
 
165
175
  ### REST API calls
166
176
 
@@ -197,26 +207,46 @@ absolute URL.
197
207
 
198
208
  ##### `delete(url, options)`
199
209
 
200
- ### SignalR Connection
210
+ ### Authentication Context
211
+
212
+ - [useAuthenticationContext](#useauthenticationcontext)
213
+
214
+ #### `useAuthenticationContext()`
215
+
216
+ Hook to obtain the authentication context based on the current logged-in user.
201
217
 
202
- Deprecated
218
+ The authentication context includes the following property and functions.
203
219
 
204
- - [useSignalRConnection](#usesignalrconnection)
220
+ - `account` _[object]_
221
+ - The account of the currently logged-in user. This includes both the user's `id` and `name`.
222
+ - `logout()`
223
+ - A function that logs out the currently logged-in user. The user will be redirected to Evoke's logout page upon logout.
224
+ - `getAccessToken()`
225
+ - A function that returns an access token that is associated to the currently logged-in user. This token can be used to make API calls to Evoke's APIs to authenticate the API call.
226
+ - Note: As a general recommendation, the [ApiService](#class-apiservices) class should be used to make API calls as it will take care
227
+ of appending an access token to the call.
228
+
229
+ ### Notifications
230
+
231
+ - [useNofitication](#usenotification)
205
232
  - [documentChanges](#documentchangessubscribeobjectidinstanceid-data-documentchange)
206
233
  - [instanceChanges](#instancechangessubscribeobjectid-instanceids-instancechange)
234
+ - [useSignalRConnection](#usesignalrconnection) (_Deprecated_)
235
+ - [documentChanges](#documentchangessubscribeobjectidinstanceid-data-documentchange) (_Deprecated_)
236
+ - [instanceChanges](#instancechangessubscribeobjectid-instanceids-instancechange) (_Deprecated_)
207
237
 
208
- #### `useSignalRConnection()`
238
+ #### `useNofitication()`
209
239
 
210
- Hook used to obtain an instanceChanges instance of `SignalRConnection` and a documentChanges instance of `SignalRConnection`.
240
+ Hook used to obtain an instanceChanges instance and a documentChanges instance.
211
241
 
212
- ##### `documentChanges.subscribe('{objectId}/{instanceId}', (data: DocumentChange[]]) => {})`
242
+ ##### `documentChanges.subscribe(objectId, instanceId, (data: DocumentChange[]]) => {})`
213
243
 
214
- Subscribe to the specified object instance document changes.
244
+ Subscribe to the specified object instance changes.
215
245
 
216
246
  ```javascript
217
- const { documentChanges } = useSignalRConnection();
247
+ const { documentChanges } = useNotification();
218
248
 
219
- documentChanges.subscribe('myObjectId/myInstanceId', (data) => {
249
+ documentChanges.subscribe('myObjectId', 'myInstanceId', (data) => {
220
250
  console.log(data);
221
251
  });
222
252
  ```
@@ -233,53 +263,61 @@ following data:
233
263
  - `type`
234
264
  - The type of update. Possible values are `BlobCreated`, `BlobDeleted`, and `BlobMetadataUpdated`.
235
265
 
236
- ##### `documentChanges.unsubscribe('{objectId}/{instanceId}', (data: DocumentChange[]) => {})`
266
+ ##### `documentChanges.unsubscribe(objectId, instanceId, (changes: DocumentChange[]) => {})`
237
267
 
238
- Unsubscribe to the specified object instance document changes.
268
+ Unsubscribe to the specified object instance changes.
239
269
 
240
270
  Callback function is optional.
271
+ If callback function is not defined, all subscriptions will be removed.
241
272
  If callback function is defined, you must pass the exact same Function instance as was previously passed to `documentChanges.subscribe`.
242
273
  Passing a different instance (even if the function body is the same) will not remove the subscription.
243
274
 
244
275
  ```javascript
245
- const { documentChanges } = useSignalRConnection();
276
+ const { documentChanges } = useNotification();
246
277
 
247
- const callback = (data: DocumentChange[]) => {
248
- console.log(data);
278
+ const callback = (changes: DocumentChange[]) => {
279
+ console.log(changes);
249
280
  };
250
281
 
251
- documentChanges.subscribe('myObjectId/myInstanceId', callback);
282
+ documentChanges.subscribe('myObjectId', 'myInstanceId', callback);
252
283
 
253
- documentChanges.unsubscribe('myObjectId/myInstanceId', callback);
284
+ documentChanges.unsubscribe('myObjectId', 'myInstanceId', callback);
254
285
  ```
255
286
 
256
- ##### `instanceChanges.subscribe('{objectId}', (instanceIds: InstanceChange[]) => {})`
287
+ ##### `instanceChanges.subscribe(objectId, (changes: InstanceChange[]) => {})`
257
288
 
258
- Subscribe to the specified object instance changes.
289
+ Subscribe to the specified object changes.
259
290
 
260
291
  ```javascript
261
- const { instanceChanges } = useSignalRConnection();
292
+ const { instanceChanges } = useNotification();
262
293
 
263
- instanceChanges.subscribe('myObjectId', (instanceIds) => {
264
- console.log(instanceIds);
294
+ instanceChanges.subscribe('myObjectId', (changes) => {
295
+ console.log(changes);
265
296
  });
266
297
  ```
267
298
 
268
- The data provided to the callback will be an array of instance IDs that were updated.
299
+ The data provided to the callback will be an array of `InstanceChange` which contains the
300
+ following data:
269
301
 
270
- ##### `instanceChanges.unsubscribe('{objectId}', (instanceIds: InstanceChange[]) => {})`
302
+ - `objectId`
303
+ - Object describing the instance associated with the updated document.
304
+ - `instanceId`
305
+ - Instance that the updated document is associated with.
271
306
 
272
- Unsubscribe to the specified object instance changes.
307
+ ##### `instanceChanges.unsubscribe(objectId, (changes: InstanceChange[]) => {})`
308
+
309
+ Unsubscribe to the specified object changes.
273
310
 
274
311
  Callback function is optional.
312
+ If callback function is not defined, all subscriptions will be removed.
275
313
  If callback function is defined, you must pass the exact same Function instance as was previously passed to `instanceChanges.subscribe`.
276
314
  Passing a different instance (even if the function body is the same) will not remove the subscription.
277
315
 
278
316
  ```javascript
279
- const { instanceChanges } = useSignalRConnection();
317
+ const { instanceChanges } = useNotification();
280
318
 
281
- const callback = (instanceIds: InstanceChange[]) => {
282
- console.log(instanceIds);
319
+ const callback = (changes: InstanceChange[]) => {
320
+ console.log(changes);
283
321
  };
284
322
 
285
323
  instanceChanges.subscribe('myObjectId', callback);
@@ -287,24 +325,24 @@ instanceChanges.subscribe('myObjectId', callback);
287
325
  instanceChanges.unsubscribe('myObjectId', callback);
288
326
  ```
289
327
 
290
- ### Notification
328
+ #### `useSignalRConnection()`
291
329
 
292
- - [useNofitication](#usenotification)
293
- - [documentChanges](#documentchangessubscribeobjectidinstanceid-data-documentchange)
294
- - [instanceChanges](#instancechangessubscribeobjectid-instanceids-instancechange)
330
+ > **Deprecated**
331
+ > This has been deprecated in favor of [useNotification](#usenofitication).
295
332
 
296
- #### `useNofitication()`
333
+ Hook used to obtain an instanceChanges instance of `SignalRConnection` and a documentChanges instance of `SignalRConnection`.
297
334
 
298
- Hook used to obtain an instanceChanges instance and a documentChanges instance.
335
+ ##### `documentChanges.subscribe('{objectId}/{instanceId}', (data: DocumentChange[]]) => {})`
299
336
 
300
- ##### `documentChanges.subscribe(objectId, instanceId, (data: DocumentChange[]]) => {})`
337
+ > **Deprecated**
338
+ > This has been deprecated in favor of [useNotification](#usenofitication).
301
339
 
302
- Subscribe to the specified object instance changes.
340
+ Subscribe to the specified object instance document changes.
303
341
 
304
342
  ```javascript
305
- const { documentChanges } = useNotification();
343
+ const { documentChanges } = useSignalRConnection();
306
344
 
307
- documentChanges.subscribe('myObjectId', 'myInstanceId', (data) => {
345
+ documentChanges.subscribe('myObjectId/myInstanceId', (data) => {
308
346
  console.log(data);
309
347
  });
310
348
  ```
@@ -321,61 +359,62 @@ following data:
321
359
  - `type`
322
360
  - The type of update. Possible values are `BlobCreated`, `BlobDeleted`, and `BlobMetadataUpdated`.
323
361
 
324
- ##### `documentChanges.unsubscribe(objectId, instanceId, (changes: DocumentChange[]) => {})`
362
+ ##### `documentChanges.unsubscribe('{objectId}/{instanceId}', (data: DocumentChange[]) => {})`
325
363
 
326
- Unsubscribe to the specified object instance changes.
364
+ > **Deprecated**
365
+ > This has been deprecated in favor of [useNotification](#usenofitication).
366
+
367
+ Unsubscribe to the specified object instance document changes.
327
368
 
328
369
  Callback function is optional.
329
- If callback function is not defined, all subscriptions will be removed.
330
370
  If callback function is defined, you must pass the exact same Function instance as was previously passed to `documentChanges.subscribe`.
331
371
  Passing a different instance (even if the function body is the same) will not remove the subscription.
332
372
 
333
373
  ```javascript
334
- const { documentChanges } = useNotification();
374
+ const { documentChanges } = useSignalRConnection();
335
375
 
336
- const callback = (changes: DocumentChange[]) => {
337
- console.log(changes);
376
+ const callback = (data: DocumentChange[]) => {
377
+ console.log(data);
338
378
  };
339
379
 
340
- documentChanges.subscribe('myObjectId', 'myInstanceId', callback);
380
+ documentChanges.subscribe('myObjectId/myInstanceId', callback);
341
381
 
342
- documentChanges.unsubscribe('myObjectId', 'myInstanceId', callback);
382
+ documentChanges.unsubscribe('myObjectId/myInstanceId', callback);
343
383
  ```
344
384
 
345
- ##### `instanceChanges.subscribe(objectId, (changes: InstanceChange[]) => {})`
385
+ ##### `instanceChanges.subscribe('{objectId}', (instanceIds: InstanceChange[]) => {})`
346
386
 
347
- Subscribe to the specified object changes.
387
+ > **Deprecated**
388
+ > This has been deprecated in favor of [useNotification](#usenofitication).
389
+
390
+ Subscribe to the specified object instance changes.
348
391
 
349
392
  ```javascript
350
- const { instanceChanges } = useNotification();
393
+ const { instanceChanges } = useSignalRConnection();
351
394
 
352
- instanceChanges.subscribe('myObjectId', (changes) => {
353
- console.log(changes);
395
+ instanceChanges.subscribe('myObjectId', (instanceIds) => {
396
+ console.log(instanceIds);
354
397
  });
355
398
  ```
356
399
 
357
- The data provided to the callback will be an array of `InstanceChange` which contains the
358
- following data:
400
+ The data provided to the callback will be an array of instance IDs that were updated.
359
401
 
360
- - `objectId`
361
- - Object describing the instance associated with the updated document.
362
- - `instanceId`
363
- - Instance that the updated document is associated with.
402
+ ##### `instanceChanges.unsubscribe('{objectId}', (instanceIds: InstanceChange[]) => {})`
364
403
 
365
- ##### `instanceChanges.unsubscribe(objectId, (changes: InstanceChange[]) => {})`
404
+ > **Deprecated**
405
+ > This has been deprecated in favor of [useNotification](#usenofitication).
366
406
 
367
- Unsubscribe to the specified object changes.
407
+ Unsubscribe to the specified object instance changes.
368
408
 
369
409
  Callback function is optional.
370
- If callback function is not defined, all subscriptions will be removed.
371
410
  If callback function is defined, you must pass the exact same Function instance as was previously passed to `instanceChanges.subscribe`.
372
411
  Passing a different instance (even if the function body is the same) will not remove the subscription.
373
412
 
374
413
  ```javascript
375
- const { instanceChanges } = useNotification();
414
+ const { instanceChanges } = useSignalRConnection();
376
415
 
377
- const callback = (changes: InstanceChange[]) => {
378
- console.log(changes);
416
+ const callback = (instanceIds: InstanceChange[]) => {
417
+ console.log(instanceIds);
379
418
  };
380
419
 
381
420
  instanceChanges.subscribe('myObjectId', callback);
@@ -11,9 +11,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  };
12
12
  import axios from 'axios';
13
13
  import { useMemo } from 'react';
14
+ import { v4 as uuidv4 } from 'uuid';
14
15
  import { useAuthenticationContext } from '../authentication/AuthenticationContextProvider.js';
15
16
  import { useApiBaseUrl } from './ApiBaseUrlProvider.js';
16
- import { v4 as uuidv4 } from 'uuid';
17
+ import { paramsSerializer } from './paramsSerializer.js';
17
18
  const sessionId = uuidv4();
18
19
  export class ApiServices {
19
20
  constructor(api, authContext) {
@@ -143,6 +144,6 @@ export class ApiServices {
143
144
  export function useApiServices() {
144
145
  const authContext = useAuthenticationContext();
145
146
  const baseURL = useApiBaseUrl();
146
- const apiServices = useMemo(() => new ApiServices(axios.create({ baseURL }), authContext), [authContext, baseURL]);
147
+ const apiServices = useMemo(() => new ApiServices(axios.create({ baseURL, paramsSerializer }), authContext), [authContext, baseURL]);
147
148
  return apiServices;
148
149
  }
@@ -0,0 +1,2 @@
1
+ import { ParamsSerializerOptions } from 'axios';
2
+ export declare function paramsSerializer(params: Record<string, unknown>, options?: ParamsSerializerOptions): string;
@@ -0,0 +1,16 @@
1
+ export function paramsSerializer(params, options) {
2
+ const searchParams = new URLSearchParams();
3
+ for (const [key, value] of Object.entries(params)) {
4
+ if (!validateParamKey(key) || !validateParamValue(value)) {
5
+ continue;
6
+ }
7
+ searchParams.append(key, typeof value !== 'string' ? JSON.stringify(value) : value);
8
+ }
9
+ return searchParams.toString();
10
+ }
11
+ function validateParamKey(item) {
12
+ return item.length;
13
+ }
14
+ function validateParamValue(item) {
15
+ return item !== undefined;
16
+ }
@@ -38,10 +38,19 @@ export type NavigationItem = {
38
38
  pageId: string;
39
39
  pageName: string;
40
40
  };
41
+ export type AppExtended = App & {
42
+ /**
43
+ * Looks up the default page slug for a given object or its nearest type ancestor.
44
+ *
45
+ * @param {string} objectId - The ID of the object to start the search from.
46
+ * @returns {Promise<string | undefined>} The default page slug, or `undefined` if no default page is found.
47
+ */
48
+ findDefaultPageSlugFor: (objectId: string) => Promise<string | undefined>;
49
+ };
41
50
  export type AppProviderProps = {
42
51
  app: App;
43
52
  children?: ReactNode;
44
53
  };
45
54
  declare function AppProvider(props: AppProviderProps): import("react/jsx-runtime").JSX.Element;
46
- export declare function useApp(): App;
55
+ export declare function useApp(): AppExtended;
47
56
  export default AppProvider;
@@ -1,13 +1,60 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  import { jsx as _jsx } from "react/jsx-runtime";
2
- // Copyright (c) 2023 System Automation Corporation.
3
- // This file is licensed under the MIT License.
4
- import { createContext, useContext } from 'react';
5
- const defaultApp = { id: '_evoke', name: 'Evoke Platform', type: 'public' };
6
- const AppContext = createContext(defaultApp);
11
+ import { createContext, useCallback, useContext } from 'react';
12
+ import { useApiServices } from '../api/index.js';
13
+ const defaultApp = {
14
+ id: '_evoke',
15
+ name: 'Evoke Platform',
16
+ type: 'public',
17
+ };
18
+ const defaultAppExtended = Object.assign(Object.assign({}, defaultApp), { findDefaultPageSlugFor: (objectId) => Promise.resolve(undefined) });
19
+ const AppContext = createContext(defaultAppExtended);
7
20
  AppContext.displayName = 'AppContext';
8
21
  function AppProvider(props) {
9
22
  const { app, children } = props;
10
- return _jsx(AppContext.Provider, { value: app, children: children });
23
+ const apiServices = useApiServices();
24
+ const appExtended = Object.assign(Object.assign({}, app), { findDefaultPageSlugFor: useCallback((objectId) => __awaiter(this, void 0, void 0, function* () {
25
+ var _a, _b, _c, _d;
26
+ let defaultPageId;
27
+ let currentObjectId = objectId;
28
+ while (currentObjectId !== undefined) {
29
+ if ((_a = app.defaultPages) === null || _a === void 0 ? void 0 : _a[currentObjectId]) {
30
+ defaultPageId = app.defaultPages[currentObjectId];
31
+ break;
32
+ }
33
+ const effectiveObject = yield apiServices.get(`data/objects/${currentObjectId}/effective`, {
34
+ params: { filter: { fields: ['baseObject'] } },
35
+ });
36
+ currentObjectId = (_c = (_b = effectiveObject === null || effectiveObject === void 0 ? void 0 : effectiveObject.baseObject) === null || _b === void 0 ? void 0 : _b.objectId) !== null && _c !== void 0 ? _c : undefined;
37
+ }
38
+ let defaultPage;
39
+ if (defaultPageId) {
40
+ const pageId = defaultPageId.includes('/')
41
+ ? defaultPageId.split('/').slice(2).join('/')
42
+ : defaultPageId;
43
+ try {
44
+ defaultPage = yield apiServices.get(`/webContent/apps/${app.id}/pages/${encodeURIComponent(encodeURIComponent(pageId))}`);
45
+ }
46
+ catch (error) {
47
+ const err = error;
48
+ if (((_d = err.response) === null || _d === void 0 ? void 0 : _d.status) === 404) {
49
+ defaultPage = undefined;
50
+ }
51
+ }
52
+ }
53
+ if (defaultPage === null || defaultPage === void 0 ? void 0 : defaultPage.slug) {
54
+ return `/${app.id}/${defaultPage.slug}`;
55
+ }
56
+ }), [app]) });
57
+ return _jsx(AppContext.Provider, { value: appExtended, children: children });
11
58
  }
12
59
  export function useApp() {
13
60
  return useContext(AppContext);
@@ -31,7 +31,10 @@ function AuthenticationContextProvider(props) {
31
31
  ? {
32
32
  account: { id: account.localAccountId, name: account.name },
33
33
  logout: () => {
34
- msal.instance.logoutRedirect({ account });
34
+ msal.instance.logoutRedirect({
35
+ account,
36
+ postLogoutRedirectUri: `/logout?p=${encodeURIComponent(window.location.pathname + window.location.search)}`,
37
+ });
35
38
  },
36
39
  getAccessToken,
37
40
  }
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { jsx as _jsx } from "react/jsx-runtime";
11
- import { HubConnectionBuilder, LogLevel, } from '@microsoft/signalr/dist/esm/index.js';
11
+ import { HubConnectionBuilder, LogLevel, } from '@microsoft/signalr';
12
12
  import { createContext, useContext, useEffect, useState } from 'react';
13
13
  import { useApiServices } from '../api/index.js';
14
14
  export const NotificationContext = createContext({});
@@ -4,12 +4,28 @@ export type BaseObjReference = {
4
4
  objectId: string;
5
5
  discriminatorValue: unknown;
6
6
  };
7
+ export type ViewLayoutEntityReference = {
8
+ id: string;
9
+ objectId: string;
10
+ };
11
+ type ViewLayoutEntity = {
12
+ id: string;
13
+ name: string;
14
+ objectId: string;
15
+ };
16
+ export type TableViewLayoutEntity = ViewLayoutEntity & TableViewLayout;
17
+ export type DropdownViewLayoutEntity = ViewLayoutEntity & DropdownViewLayout;
7
18
  export type ViewLayout = {
8
19
  table?: TableViewLayout;
9
20
  dropdown?: DropdownViewLayout;
10
21
  };
22
+ export type DropdownViewLayoutSort = {
23
+ propertyId: string;
24
+ direction?: 'asc' | 'desc';
25
+ };
11
26
  export type DropdownViewLayout = {
12
27
  secondaryTextExpression: string;
28
+ sort?: DropdownViewLayoutSort;
13
29
  };
14
30
  export type TableViewLayout = {
15
31
  properties: PropertyReference[];
@@ -35,7 +51,7 @@ export type Obj = {
35
51
  export type ObjWithRoot = Obj & {
36
52
  rootObjectId: string;
37
53
  };
38
- export type PropertyType = 'address' | 'array' | 'boolean' | 'collection' | 'date' | 'date-time' | 'image' | 'integer' | 'number' | 'object' | 'richText' | 'string' | 'time' | 'user';
54
+ export type PropertyType = 'address' | 'array' | 'boolean' | 'collection' | 'criteria' | 'date' | 'date-time' | 'document' | 'image' | 'integer' | 'number' | 'object' | 'richText' | 'string' | 'time' | 'user';
39
55
  export type NumericValidation = {
40
56
  errorMessage?: string;
41
57
  minimum?: number;
@@ -51,12 +67,14 @@ export type CriteriaValidation = {
51
67
  };
52
68
  export type StringValidation = {
53
69
  operator: 'any' | 'all';
54
- rules?: {
55
- regex: string;
56
- errorMessage?: string;
57
- }[];
70
+ rules?: RegexValidation[];
71
+ };
72
+ export type DocumentValidation = {
73
+ errorMessage?: string;
74
+ maxDocuments?: number;
75
+ minDocuments?: number;
58
76
  };
59
- export type PropertyValidation = StringValidation | NumericValidation | DateValidation | CriteriaValidation;
77
+ export type PropertyValidation = StringValidation | NumericValidation | DateValidation | CriteriaValidation | DocumentValidation;
60
78
  export type Property = {
61
79
  id: string;
62
80
  name: string;
@@ -109,7 +127,7 @@ export type ObjectInstance = {
109
127
  };
110
128
  export type RegexValidation = {
111
129
  regex: string;
112
- errorMessage: string;
130
+ errorMessage?: string;
113
131
  };
114
132
  export type SelectOption = {
115
133
  label: string;
@@ -144,12 +162,18 @@ export type DisplayConfiguration = {
144
162
  mode?: 'default' | 'existingOnly';
145
163
  relatedObjectDisplay?: 'dropdown' | 'dialogBox';
146
164
  visibility?: VisibilityConfiguration | string;
165
+ viewLayout?: ViewLayoutEntityReference;
166
+ choicesDisplay?: {
167
+ type: 'dropdown' | 'radioButton';
168
+ sortBy?: 'ASC' | 'DESC' | 'NONE';
169
+ };
147
170
  };
148
171
  export type InputParameterReference = {
149
172
  type: 'input';
150
173
  parameterId: string;
151
174
  display?: DisplayConfiguration;
152
175
  enumWithLabels?: SelectOption[];
176
+ documentMetadata?: Record<string, string>;
153
177
  };
154
178
  export type Content = {
155
179
  type: 'content';
@@ -178,7 +202,7 @@ export type FormEntry = InputParameterReference | Columns | Sections | Content;
178
202
  export type Form = {
179
203
  entries?: FormEntry[];
180
204
  };
181
- export type ActionInputType = 'button' | 'Section' | 'Columns' | 'Content' | 'Select' | 'TextField' | 'DateTime' | 'RepeatableField' | 'MultiSelect' | 'Decimal' | 'RichText' | 'Date' | 'Integer' | 'Image' | 'Object' | 'Time' | 'User';
205
+ export type ActionInputType = 'button' | 'Section' | 'Columns' | 'Content' | 'Criteria' | 'Select' | 'TextField' | 'DateTime' | 'Document' | 'RepeatableField' | 'ManyToManyRepeatableField' | 'MultiSelect' | 'Decimal' | 'RichText' | 'Date' | 'Integer' | 'Image' | 'Object' | 'Time' | 'User';
182
206
  /**
183
207
  * Represents an object action inputProperty object.
184
208
  */
@@ -187,7 +211,7 @@ export type ActionInput = {
187
211
  label?: string;
188
212
  type?: ActionInputType;
189
213
  key?: string;
190
- initialValue?: string | number | SelectOption[] | SelectOption;
214
+ initialValue?: string | string[] | number | RelatedObjectDefaultValue | SelectOption[] | SelectOption;
191
215
  defaultToCurrentDate?: boolean;
192
216
  defaultToCurrentTime?: boolean;
193
217
  defaultValueCriteria?: object;
@@ -210,7 +234,7 @@ export type ActionInput = {
210
234
  inputMaskPlaceholderChar?: string;
211
235
  tableView?: boolean;
212
236
  mode?: 'default' | 'existingOnly';
213
- displayOption?: 'dropdown' | 'dialogBox';
237
+ displayOption?: 'dropdown' | 'dialogBox' | 'radioButton';
214
238
  rows?: number;
215
239
  showCharCount?: boolean;
216
240
  readOnly?: boolean;
@@ -224,9 +248,9 @@ export type ActionInput = {
224
248
  when?: string;
225
249
  eq?: string | number | boolean;
226
250
  };
227
- property?: {
228
- id: string;
229
- };
251
+ property?: InputParameter;
252
+ viewLayout?: ViewLayoutEntityReference;
253
+ documentMetadata?: Record<string, string>;
230
254
  validate?: {
231
255
  required?: boolean;
232
256
  criteria?: object;
@@ -240,6 +264,8 @@ export type ActionInput = {
240
264
  maxTime?: string;
241
265
  min?: number;
242
266
  max?: number;
267
+ minDocuments?: number;
268
+ maxDocuments?: number;
243
269
  customMessage?: string;
244
270
  };
245
271
  /**
@@ -306,3 +332,4 @@ export declare class ObjectStore {
306
332
  instanceAction<T extends ObjectInstance = ObjectInstance>(id: string, input: ActionRequest, cb: Callback<T>): void;
307
333
  }
308
334
  export declare function useObject(objectId: string): ObjectStore;
335
+ export {};
@@ -1,142 +1,53 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { jsx as _jsx } from "react/jsx-runtime";
11
2
  // Copyright (c) 2023 System Automation Corporation.
12
3
  // This file is licensed under the MIT License.
13
- import { HubConnectionBuilder, LogLevel } from '@microsoft/signalr/dist/esm/index.js';
14
- import { createContext, useContext, useEffect, useState } from 'react';
15
- import { useApiServices } from '../api/index.js';
4
+ import { createContext, useContext, useState } from 'react';
5
+ import { useNotification } from '../notification/index.js';
16
6
  export const SignalRConnectionContext = createContext({});
17
7
  SignalRConnectionContext.displayName = 'SignalRConnectionContext';
18
8
  function SignalRConnectionProvider({ children }) {
19
- const [instancesSignalRConnection, setInstancesSignalRConnection] = useState();
20
- const [documentsSignalRConnection, setDocumentsSignalRConnection] = useState();
21
- const api = useApiServices();
22
- useEffect(() => {
23
- const getConnectionInfo = (hubName) => {
24
- return api.post(`/signalr/hubs/${hubName}/negotiate`);
25
- };
26
- const getConnection = () => __awaiter(this, void 0, void 0, function* () {
27
- try {
28
- const instancesConnectionInfo = yield getConnectionInfo('instanceChanges');
29
- const documentsConnectionInfo = yield getConnectionInfo('documentChanges');
30
- if (instancesConnectionInfo) {
31
- const options = {
32
- accessTokenFactory: () => __awaiter(this, void 0, void 0, function* () {
33
- if (instancesConnectionInfo.accessToken) {
34
- return instancesConnectionInfo.accessToken;
35
- }
36
- else {
37
- return getConnection();
38
- }
39
- }),
40
- };
41
- const connection = new HubConnectionBuilder()
42
- .withUrl(instancesConnectionInfo.url, options)
43
- .configureLogging(LogLevel.Error)
44
- .withAutomaticReconnect()
45
- .build();
46
- setInstancesSignalRConnection(connection);
47
- }
48
- if (documentsConnectionInfo) {
49
- const options = {
50
- accessTokenFactory: () => __awaiter(this, void 0, void 0, function* () {
51
- if (documentsConnectionInfo.accessToken) {
52
- return documentsConnectionInfo.accessToken;
53
- }
54
- else {
55
- return getConnection();
56
- }
57
- }),
58
- };
59
- const connection = new HubConnectionBuilder()
60
- .withUrl(documentsConnectionInfo.url, options)
61
- .configureLogging(LogLevel.Error)
62
- .withAutomaticReconnect()
63
- .build();
64
- setDocumentsSignalRConnection(connection);
65
- }
66
- // eslint-disable-next-line no-empty
67
- }
68
- catch (err) { }
69
- });
70
- getConnection();
71
- }, []);
72
- useEffect(() => {
73
- let documentsConnectionStopped = false;
74
- const startConnection = (connection, numOfAttempts) => __awaiter(this, void 0, void 0, function* () {
75
- yield connection.start().catch((error) => {
76
- if (numOfAttempts < 4 && !documentsConnectionStopped) {
77
- setTimeout(() => {
78
- if (!documentsConnectionStopped) {
79
- startConnection(connection, numOfAttempts + 1);
80
- }
81
- }, 2000);
82
- }
83
- else {
84
- console.warn(`Cannot start connection to SignalR due to error "${error}"`);
85
- }
86
- });
87
- });
88
- if (documentsSignalRConnection) {
89
- startConnection(documentsSignalRConnection, 0);
90
- }
91
- return () => {
92
- documentsSignalRConnection === null || documentsSignalRConnection === void 0 ? void 0 : documentsSignalRConnection.stop();
93
- documentsConnectionStopped = true;
94
- };
95
- }, [documentsSignalRConnection]);
96
- useEffect(() => {
97
- let instancesConnectionStopped = false;
98
- const startConnection = (connection, numOfAttempts) => __awaiter(this, void 0, void 0, function* () {
99
- yield connection.start().catch((error) => {
100
- if (numOfAttempts < 4 && !instancesConnectionStopped) {
101
- setTimeout(() => {
102
- if (!instancesConnectionStopped) {
103
- startConnection(connection, numOfAttempts + 1);
104
- }
105
- }, 2000);
106
- }
107
- else {
108
- console.warn(`Cannot start connection to SignalR due to error "${error}"`);
109
- }
110
- });
111
- });
112
- if (instancesSignalRConnection) {
113
- startConnection(instancesSignalRConnection, 0);
114
- }
115
- return () => {
116
- instancesSignalRConnection === null || instancesSignalRConnection === void 0 ? void 0 : instancesSignalRConnection.stop();
117
- instancesConnectionStopped = true;
118
- };
119
- }, [instancesSignalRConnection]);
9
+ const notifications = useNotification();
10
+ const [instanceCallbacks] = useState(
11
+ // Map provided callbacks to our wrappers that are sent to the underlying
12
+ // notification provider.
13
+ new WeakMap());
120
14
  return (_jsx(SignalRConnectionContext.Provider, { value: {
121
- documentChanges: documentsSignalRConnection
122
- ? {
123
- subscribe: (topicName, callback) => documentsSignalRConnection.on(topicName, callback),
124
- unsubscribe: (topicName, callback) => callback
125
- ? documentsSignalRConnection.off(topicName, callback)
126
- : documentsSignalRConnection.off(topicName),
127
- }
128
- : undefined,
129
- instanceChanges: instancesSignalRConnection
130
- ? {
131
- subscribe: (topicName, callback) => instancesSignalRConnection.on(topicName, callback),
132
- unsubscribe: (topicName, callback) => callback
133
- ? instancesSignalRConnection.off(topicName, callback)
134
- : instancesSignalRConnection.off(topicName),
135
- }
136
- : undefined,
15
+ documentChanges: {
16
+ subscribe: (topicName, callback) => {
17
+ var _a;
18
+ const [objectId, instanceId] = topicName.split('/');
19
+ (_a = notifications.documentChanges) === null || _a === void 0 ? void 0 : _a.subscribe(objectId, instanceId, callback);
20
+ },
21
+ unsubscribe: (topicName, callback) => {
22
+ var _a;
23
+ const [objectId, instanceId] = topicName.split('/');
24
+ (_a = notifications.documentChanges) === null || _a === void 0 ? void 0 : _a.unsubscribe(objectId, instanceId, callback);
25
+ },
26
+ },
27
+ instanceChanges: {
28
+ subscribe: (objectId, callback) => {
29
+ var _a;
30
+ // If there is already a wrapper for the given callback, we must reuse the
31
+ // same one. Otherwise, if we overwrite the entry in our cache, we'll lose
32
+ // track of the original wrapper.
33
+ let wrapper = instanceCallbacks.get(callback);
34
+ if (!wrapper) {
35
+ wrapper = (...changes) => {
36
+ callback(...changes.map((change) => change.instanceId));
37
+ };
38
+ instanceCallbacks.set(callback, wrapper);
39
+ }
40
+ (_a = notifications.instanceChanges) === null || _a === void 0 ? void 0 : _a.subscribe(objectId, wrapper);
41
+ },
42
+ unsubscribe: (objectId, callback) => {
43
+ var _a;
44
+ (_a = notifications.instanceChanges) === null || _a === void 0 ? void 0 : _a.unsubscribe(objectId, callback && instanceCallbacks.get(callback));
45
+ },
46
+ },
137
47
  }, children: children }));
138
48
  }
139
49
  export function useSignalRConnection() {
50
+ console.warn('Use of useSignalRConnection is deprecated. Use useNotification instead.');
140
51
  return useContext(SignalRConnectionContext);
141
52
  }
142
53
  export default SignalRConnectionProvider;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evoke-platform/context",
3
- "version": "1.0.0-dev.99",
3
+ "version": "1.0.1-dev.0",
4
4
  "description": "Utilities that provide context to Evoke platform widgets",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -35,24 +35,29 @@
35
35
  "!dist/tests"
36
36
  ],
37
37
  "devDependencies": {
38
- "@azure/msal-browser": "^2.38.2",
38
+ "@azure/msal-browser": "^2.38.4",
39
39
  "@azure/msal-react": "^1.5.9",
40
+ "@testing-library/dom": "^10.4.0",
41
+ "@testing-library/react": "^16.0.1",
40
42
  "@types/chai": "^4.3.11",
41
43
  "@types/dirty-chai": "^2.0.4",
42
44
  "@types/mocha": "^10.0.6",
43
45
  "@types/node": "^18.15.7",
44
46
  "@types/react": "^18.2.28",
45
- "@types/uuid": "^9.0.8",
46
47
  "@types/sinon": "^17.0.3",
47
- "chai": "^4.3.10",
48
+ "@types/uuid": "^9.0.8",
49
+ "chai": "^4.4.1",
48
50
  "commit-and-tag-version": "^12.1.0",
49
51
  "dirty-chai": "^2.0.1",
50
52
  "eslint-plugin-react": "^7.33.2",
53
+ "global-jsdom": "^25.0.0",
54
+ "jsdom": "^25.0.1",
51
55
  "mocha": "^10.2.0",
52
56
  "msw": "^1.3.1",
53
57
  "react": "^18.2.0",
58
+ "react-dom": "^18.3.1",
54
59
  "react-router-dom": "^6.16.0",
55
- "sinon": "^17.0.1",
60
+ "sinon": "^18.0.0",
56
61
  "typescript": "^5.3.3"
57
62
  },
58
63
  "peerDependencies": {
@@ -63,7 +68,7 @@
63
68
  },
64
69
  "dependencies": {
65
70
  "@microsoft/signalr": "^7.0.12",
66
- "axios": "^1.6.7",
71
+ "axios": "^1.7.9",
67
72
  "uuid": "^9.0.1"
68
73
  }
69
74
  }