@duffel/react-native-components-assistant 0.3.0 → 0.4.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.
@@ -4,6 +4,7 @@ import React from 'react';
4
4
  import { Modal, Platform, StyleSheet, View } from 'react-native';
5
5
  import { WebView } from 'react-native-webview';
6
6
  import { hasJsonWebTokenFormat } from "./lib/hasJsonWebTokenFormat.js";
7
+ import { getClientKeyPayload } from "./lib/getClientKeyPayload.js";
7
8
  import { jsx as _jsx } from "react/jsx-runtime";
8
9
  export const DuffelAssistant = ({
9
10
  isOpen,
@@ -14,6 +15,24 @@ export const DuffelAssistant = ({
14
15
  if (!hasJsonWebTokenFormat(properties.clientKey)) {
15
16
  throw new Error('Duffel Assistant client key format must be a valid JWT.');
16
17
  }
18
+ const clientKeyPayload = getClientKeyPayload(properties.clientKey);
19
+ if (!clientKeyPayload) {
20
+ throw new Error('The client key property is invalid. A client key must follow the JWT format.');
21
+ }
22
+ const clientKeyIncludesResource = clientKeyPayload.order_id !== undefined || clientKeyPayload.booking_id !== undefined;
23
+ if (!('user_id' in clientKeyPayload)) {
24
+ throw new Error('The client key in the props does not include a user_id. Make sure a user_id is included in the payload of the client key creation.');
25
+ }
26
+ if (typeof properties.issueType !== 'undefined') {
27
+ console.warn('The issueType prop has been deprecated and will no longer be supported in the future. Please use the context prop instead.');
28
+ }
29
+ if (!clientKeyIncludesResource && typeof properties.issueType !== 'undefined') {
30
+ console.warn('The issueType prop is only supported when the client key includes a resource id, it will be ignored.');
31
+ delete properties.issueType;
32
+ }
33
+ if (!clientKeyIncludesResource && properties.context) {
34
+ throw new Error('The context prop is only supported when the client key includes a resource id.');
35
+ }
17
36
  return /*#__PURE__*/_jsx(Modal, {
18
37
  visible: isOpen,
19
38
  onRequestClose: () => onClose(),
@@ -1 +1 @@
1
- {"version":3,"names":["React","Modal","Platform","StyleSheet","View","WebView","hasJsonWebTokenFormat","jsx","_jsx","DuffelAssistant","isOpen","onClose","onNewMessage","properties","clientKey","Error","visible","onRequestClose","animationType","children","style","styles","container","OS","injectedJavaScriptObject","source","uri","onMessage","event","nativeEvent","data","parsedData","JSON","parse","type","userId","resourceId","undefined","error","injectedJavaScript","stringify","create","height","width","paddingHorizontal","paddingTop","paddingBottom"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,KAAK,EAAEC,QAAQ,EAAEC,UAAU,EAAEC,IAAI,QAAQ,cAAc;AAChE,SAASC,OAAO,QAAQ,sBAAsB;AAE9C,SAASC,qBAAqB,QAAQ,gCAA6B;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAIpE,OAAO,MAAMC,eAA+C,GAAGA,CAAC;EAC9DC,MAAM;EACNC,OAAO;EACPC,YAAY;EACZ,GAAGC;AACL,CAAC,KAAK;EACJ,IAAI,CAACP,qBAAqB,CAACO,UAAU,CAACC,SAAS,CAAC,EAAE;IAChD,MAAM,IAAIC,KAAK,CAAC,yDAAyD,CAAC;EAC5E;EAEA,oBACEP,IAAA,CAACP,KAAK;IACJe,OAAO,EAAEN,MAAO;IAChBO,cAAc,EAAEA,CAAA,KAAMN,OAAO,CAAC,CAAE;IAChCO,aAAa,EAAC,OAAO;IAAAC,QAAA,eAErBX,IAAA,CAACJ,IAAI;MAACgB,KAAK,EAAEC,MAAM,CAACC,SAAU;MAAAH,QAAA,EAC3BjB,QAAQ,CAACqB,EAAE,KAAK,KAAK,gBACpBf,IAAA,CAACH,OAAO;QACNmB,wBAAwB,EAAEX;QAC1B;QACA;QACA;QACA;QAAA;QACAY,MAAM,EAAE;UAAEC,GAAG,EAAE;QAAkD,CAAE;QACnEC,SAAS,EAAGC,KAAK,IAAK;UACpB,IAAIA,KAAK,CAACC,WAAW,CAACC,IAAI,KAAK,wBAAwB,EAAE;YACvDnB,OAAO,CAAC,CAAC;UACX;UACA,IAAI;YACF,MAAMoB,UAAU,GAAGC,IAAI,CAACC,KAAK,CAACL,KAAK,CAACC,WAAW,CAACC,IAAI,CAAC;YACrD,IACEC,UAAU,CAACG,IAAI,KAAK,8BAA8B,IAClD,OAAOH,UAAU,CAACI,MAAM,KAAK,QAAQ,IACrC,OAAOJ,UAAU,CAACK,UAAU,KAAK,QAAQ,IACzCxB,YAAY,KAAKyB,SAAS,EAC1B;cACAzB,YAAY,CAAC;gBACXuB,MAAM,EAAEJ,UAAU,CAACI,MAAM;gBACzBC,UAAU,EAAEL,UAAU,CAACK;cACzB,CAAC,CAAC;YACJ;UACF,CAAC,CAAC,OAAOE,KAAK,EAAE;YACd;UAAA;QAEJ;MAAE,CACH,CAAC,gBAEF9B,IAAA,CAACH;MACC;MAAA;QACAkC,kBAAkB,EAAE;AAChC;AACA;AACA;AACA,4BAA4BP,IAAI,CAACQ,SAAS,CAAC3B,UAAU,CAAC;AACtD;AACA;AACA;AACA,gBAAiB;QACLY,MAAM,EAAE;UAAEC,GAAG,EAAE;QAAkD,CAAE;QACnEC,SAAS,EAAGC,KAAK,IAAK;UACpB,IAAIA,KAAK,CAACC,WAAW,CAACC,IAAI,KAAK,wBAAwB,EAAE;YACvDnB,OAAO,CAAC,CAAC;UACX;QACF;MAAE,CACH;IACF,CACG;EAAC,CACF,CAAC;AAEZ,CAAC;AAED,MAAMU,MAAM,GAAGlB,UAAU,CAACsC,MAAM,CAAC;EAC/BnB,SAAS,EAAE;IACToB,MAAM,EAAE,MAAM;IACdC,KAAK,EAAE,MAAM;IACbC,iBAAiB,EAAE,EAAE;IACrBC,UAAU,EAAE,EAAE;IACdC,aAAa,EAAE;EACjB;AACF,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["React","Modal","Platform","StyleSheet","View","WebView","hasJsonWebTokenFormat","getClientKeyPayload","jsx","_jsx","DuffelAssistant","isOpen","onClose","onNewMessage","properties","clientKey","Error","clientKeyPayload","clientKeyIncludesResource","order_id","undefined","booking_id","issueType","console","warn","context","visible","onRequestClose","animationType","children","style","styles","container","OS","injectedJavaScriptObject","source","uri","onMessage","event","nativeEvent","data","parsedData","JSON","parse","type","userId","resourceId","error","injectedJavaScript","stringify","create","height","width","paddingHorizontal","paddingTop","paddingBottom"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ export function getClientKeyPayload(clientKey) {
4
+ if (typeof clientKey !== 'string') {
5
+ return null;
6
+ }
7
+ const payload = clientKey.split('.')[1];
8
+ if (!payload) {
9
+ throw new Error('Malformed client key. Make sure the client key given to props has a valid JWT format.');
10
+ }
11
+ try {
12
+ const decodedPayload = atob(payload);
13
+ const json = JSON.parse(decodedPayload);
14
+ const data = {
15
+ ...(json.exp && {
16
+ exp: json.exp
17
+ }),
18
+ ...(json.user_id && {
19
+ user_id: json.user_id
20
+ }),
21
+ ...(json.live_mode && {
22
+ live_mode: json.live_mode
23
+ }),
24
+ ...(json.organisation_id && {
25
+ organisation_id: json.organisation_id
26
+ }),
27
+ ...(json.order_id && {
28
+ order_id: json.order_id
29
+ }),
30
+ ...(json.booking_id && {
31
+ booking_id: json.booking_id
32
+ })
33
+ };
34
+ return data;
35
+ } catch (error) {
36
+ return null;
37
+ }
38
+ }
39
+ //# sourceMappingURL=getClientKeyPayload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["getClientKeyPayload","clientKey","payload","split","Error","decodedPayload","atob","json","JSON","parse","data","exp","user_id","live_mode","organisation_id","order_id","booking_id","error"],"sourceRoot":"../../../src","sources":["lib/getClientKeyPayload.ts"],"mappings":";;AAgCA,OAAO,SAASA,mBAAmBA,CACjCC,SAAiB,EACQ;EACzB,IAAI,OAAOA,SAAS,KAAK,QAAQ,EAAE;IACjC,OAAO,IAAI;EACb;EAEA,MAAMC,OAAO,GAAGD,SAAS,CAACE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;EACvC,IAAI,CAACD,OAAO,EAAE;IACZ,MAAM,IAAIE,KAAK,CACb,uFACF,CAAC;EACH;EAEA,IAAI;IACF,MAAMC,cAAc,GAAGC,IAAI,CAACJ,OAAO,CAAC;IAEpC,MAAMK,IAAI,GAAGC,IAAI,CAACC,KAAK,CAACJ,cAAc,CAAC;IACvC,MAAMK,IAAI,GAAG;MACX,IAAIH,IAAI,CAACI,GAAG,IAAI;QAAEA,GAAG,EAAEJ,IAAI,CAACI;MAAI,CAAC,CAAC;MAClC,IAAIJ,IAAI,CAACK,OAAO,IAAI;QAAEA,OAAO,EAAEL,IAAI,CAACK;MAAQ,CAAC,CAAC;MAC9C,IAAIL,IAAI,CAACM,SAAS,IAAI;QAAEA,SAAS,EAAEN,IAAI,CAACM;MAAU,CAAC,CAAC;MACpD,IAAIN,IAAI,CAACO,eAAe,IAAI;QAC1BA,eAAe,EAAEP,IAAI,CAACO;MACxB,CAAC,CAAC;MACF,IAAIP,IAAI,CAACQ,QAAQ,IAAI;QAAEA,QAAQ,EAAER,IAAI,CAACQ;MAAS,CAAC,CAAC;MACjD,IAAIR,IAAI,CAACS,UAAU,IAAI;QAAEA,UAAU,EAAET,IAAI,CAACS;MAAW,CAAC;IACxD,CAAC;IACD,OAAON,IAAI;EACb,CAAC,CAAC,OAAOO,KAAK,EAAE;IACd,OAAO,IAAI;EACb;AACF","ignoreList":[]}
@@ -1,5 +1,28 @@
1
+ export type SupportIssueType = 'cancellation' | 'change' | 'other';
2
+ export type DuffelAssistantContext = {
3
+ /**
4
+ * A summary of the customer's request for handover to Duffel Support.
5
+ * This will only be displayed to the agent handling the chat.
6
+ *
7
+ * @default undefined
8
+ */
9
+ summary?: string;
10
+ /**
11
+ * When the intent is set to "chat", you may use this value to immediately open a support case for a specific issue type.
12
+ * When no issue type is provided, the user will be able to select the issue type in the chat UI.
13
+ *
14
+ * @default undefined
15
+ */
16
+ issueType?: SupportIssueType;
17
+ };
1
18
  export interface DuffelAssistantProps {
19
+ /**
20
+ * This prop is used to control whether the Assistant is open/visible or closed.
21
+ */
2
22
  isOpen: boolean;
23
+ /**
24
+ * This function callback is called when the user click to close/minimise the Assistant.
25
+ */
3
26
  onClose: () => void;
4
27
  /**
5
28
  * This is used to authenticate requests to the Duffel API.
@@ -21,13 +44,9 @@ export interface DuffelAssistantProps {
21
44
  */
22
45
  environment?: 'staging' | 'production' | 'development';
23
46
  /**
24
- * When the clientKey include both the user and resource IDs, the assistant will open directly on the chat window.
25
- * You may use `issueType` to immediately open a support case with a specific issue type.
26
- * When no issue type is provided, the user will be able to select the issue type in the chat UI.
27
- *
28
- * @default undefined
47
+ * @deprecated Put this in `context.issueType` instead.
29
48
  */
30
- issueType?: 'cancellation' | 'change' | 'other';
49
+ issueType?: SupportIssueType;
31
50
  /**
32
51
  * This callback is called when a new message is received from the Assistant.
33
52
  * You can react to this to notify users that a new message has been received.
@@ -38,5 +57,11 @@ export interface DuffelAssistantProps {
38
57
  userId: string;
39
58
  resourceId: string;
40
59
  }) => void;
60
+ /**
61
+ * The context of the customer's request for handover to Duffel Support. This is used to pass additional information to the Assistant.
62
+ *
63
+ * @default undefined
64
+ */
65
+ context?: DuffelAssistantContext;
41
66
  }
42
67
  //# sourceMappingURL=DuffelAssistantProps.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DuffelAssistantProps.d.ts","sourceRoot":"","sources":["../../../src/DuffelAssistantProps.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,SAAS,GAAG,YAAY,GAAG,aAAa,CAAC;IAEvD;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,cAAc,GAAG,QAAQ,GAAG,OAAO,CAAC;IAEhD;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC/E"}
1
+ {"version":3,"file":"DuffelAssistantProps.d.ts","sourceRoot":"","sources":["../../../src/DuffelAssistantProps.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEnE,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAC9B,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,SAAS,GAAG,YAAY,GAAG,aAAa,CAAC;IAEvD;;OAEG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAE7B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,YAAY,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAE9E;;;;OAIG;IACH,OAAO,CAAC,EAAE,sBAAsB,CAAC;CAClC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,KAAK,oBAAoB,IAAI,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAGnG,MAAM,MAAM,oBAAoB,GAAG,4BAA4B,CAAC;AAEhE,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAsE1D,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,KAAK,oBAAoB,IAAI,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAInG,MAAM,MAAM,oBAAoB,GAAG,4BAA4B,CAAC;AAEhE,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA6G1D,CAAC"}
@@ -0,0 +1,28 @@
1
+ export type ClientKeyPayload = {
2
+ /**
3
+ * When the token expires, in unix time.
4
+ */
5
+ exp: number;
6
+ /**
7
+ * The customer user ID.
8
+ */
9
+ user_id: string;
10
+ /**
11
+ * The live mode flag.
12
+ */
13
+ live_mode: boolean;
14
+ /**
15
+ * The organisation ID.
16
+ */
17
+ organisation_id: string;
18
+ /**
19
+ * The ID of an order when integrating the chat only mode.
20
+ */
21
+ order_id?: string;
22
+ /**
23
+ * The ID of a booking when integrating the chat only mode.
24
+ */
25
+ booking_id?: string;
26
+ };
27
+ export declare function getClientKeyPayload(clientKey: string): ClientKeyPayload | null;
28
+ //# sourceMappingURL=getClientKeyPayload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getClientKeyPayload.d.ts","sourceRoot":"","sources":["../../../../src/lib/getClientKeyPayload.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;OAEG;IACH,eAAe,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,GAChB,gBAAgB,GAAG,IAAI,CA8BzB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duffel/react-native-components-assistant",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Duffel Assistant component in React Native",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -1,5 +1,32 @@
1
+ export type SupportIssueType = 'cancellation' | 'change' | 'other';
2
+
3
+ export type DuffelAssistantContext = {
4
+ /**
5
+ * A summary of the customer's request for handover to Duffel Support.
6
+ * This will only be displayed to the agent handling the chat.
7
+ *
8
+ * @default undefined
9
+ */
10
+ summary?: string;
11
+
12
+ /**
13
+ * When the intent is set to "chat", you may use this value to immediately open a support case for a specific issue type.
14
+ * When no issue type is provided, the user will be able to select the issue type in the chat UI.
15
+ *
16
+ * @default undefined
17
+ */
18
+ issueType?: SupportIssueType;
19
+ };
20
+
1
21
  export interface DuffelAssistantProps {
22
+ /**
23
+ * This prop is used to control whether the Assistant is open/visible or closed.
24
+ */
2
25
  isOpen: boolean;
26
+
27
+ /**
28
+ * This function callback is called when the user click to close/minimise the Assistant.
29
+ */
3
30
  onClose: () => void;
4
31
 
5
32
  /**
@@ -25,13 +52,9 @@ export interface DuffelAssistantProps {
25
52
  environment?: 'staging' | 'production' | 'development';
26
53
 
27
54
  /**
28
- * When the clientKey include both the user and resource IDs, the assistant will open directly on the chat window.
29
- * You may use `issueType` to immediately open a support case with a specific issue type.
30
- * When no issue type is provided, the user will be able to select the issue type in the chat UI.
31
- *
32
- * @default undefined
55
+ * @deprecated Put this in `context.issueType` instead.
33
56
  */
34
- issueType?: 'cancellation' | 'change' | 'other';
57
+ issueType?: SupportIssueType;
35
58
 
36
59
  /**
37
60
  * This callback is called when a new message is received from the Assistant.
@@ -40,4 +63,11 @@ export interface DuffelAssistantProps {
40
63
  * @param eventPayload - The event payload containing the user ID and resource ID associated with the message.
41
64
  */
42
65
  onNewMessage?: (eventPayload: { userId: string; resourceId: string }) => void;
66
+
67
+ /**
68
+ * The context of the customer's request for handover to Duffel Support. This is used to pass additional information to the Assistant.
69
+ *
70
+ * @default undefined
71
+ */
72
+ context?: DuffelAssistantContext;
43
73
  }
package/src/index.tsx CHANGED
@@ -3,6 +3,7 @@ import { Modal, Platform, StyleSheet, View } from 'react-native';
3
3
  import { WebView } from 'react-native-webview';
4
4
  import { type DuffelAssistantProps as DuffelAssistantPropsImported } from './DuffelAssistantProps';
5
5
  import { hasJsonWebTokenFormat } from './lib/hasJsonWebTokenFormat';
6
+ import { getClientKeyPayload } from './lib/getClientKeyPayload';
6
7
 
7
8
  export type DuffelAssistantProps = DuffelAssistantPropsImported;
8
9
 
@@ -16,6 +17,45 @@ export const DuffelAssistant: React.FC<DuffelAssistantProps> = ({
16
17
  throw new Error('Duffel Assistant client key format must be a valid JWT.');
17
18
  }
18
19
 
20
+ const clientKeyPayload = getClientKeyPayload(properties.clientKey);
21
+ if (!clientKeyPayload) {
22
+ throw new Error(
23
+ 'The client key property is invalid. A client key must follow the JWT format.'
24
+ );
25
+ }
26
+
27
+ const clientKeyIncludesResource =
28
+ clientKeyPayload.order_id !== undefined ||
29
+ clientKeyPayload.booking_id !== undefined;
30
+
31
+ if (!('user_id' in clientKeyPayload)) {
32
+ throw new Error(
33
+ 'The client key in the props does not include a user_id. Make sure a user_id is included in the payload of the client key creation.'
34
+ );
35
+ }
36
+
37
+ if (typeof properties.issueType !== 'undefined') {
38
+ console.warn(
39
+ 'The issueType prop has been deprecated and will no longer be supported in the future. Please use the context prop instead.'
40
+ );
41
+ }
42
+
43
+ if (
44
+ !clientKeyIncludesResource &&
45
+ typeof properties.issueType !== 'undefined'
46
+ ) {
47
+ console.warn(
48
+ 'The issueType prop is only supported when the client key includes a resource id, it will be ignored.'
49
+ );
50
+ delete properties.issueType;
51
+ }
52
+
53
+ if (!clientKeyIncludesResource && properties.context) {
54
+ throw new Error(
55
+ 'The context prop is only supported when the client key includes a resource id.'
56
+ );
57
+ }
58
+
19
59
  return (
20
60
  <Modal
21
61
  visible={isOpen}
@@ -0,0 +1,65 @@
1
+ export type ClientKeyPayload = {
2
+ /**
3
+ * When the token expires, in unix time.
4
+ */
5
+ exp: number;
6
+
7
+ /**
8
+ * The customer user ID.
9
+ */
10
+ user_id: string;
11
+
12
+ /**
13
+ * The live mode flag.
14
+ */
15
+ live_mode: boolean;
16
+
17
+ /**
18
+ * The organisation ID.
19
+ */
20
+ organisation_id: string;
21
+
22
+ /**
23
+ * The ID of an order when integrating the chat only mode.
24
+ */
25
+ order_id?: string;
26
+
27
+ /**
28
+ * The ID of a booking when integrating the chat only mode.
29
+ */
30
+ booking_id?: string;
31
+ };
32
+
33
+ export function getClientKeyPayload(
34
+ clientKey: string
35
+ ): ClientKeyPayload | null {
36
+ if (typeof clientKey !== 'string') {
37
+ return null;
38
+ }
39
+
40
+ const payload = clientKey.split('.')[1];
41
+ if (!payload) {
42
+ throw new Error(
43
+ 'Malformed client key. Make sure the client key given to props has a valid JWT format.'
44
+ );
45
+ }
46
+
47
+ try {
48
+ const decodedPayload = atob(payload);
49
+
50
+ const json = JSON.parse(decodedPayload);
51
+ const data = {
52
+ ...(json.exp && { exp: json.exp }),
53
+ ...(json.user_id && { user_id: json.user_id }),
54
+ ...(json.live_mode && { live_mode: json.live_mode }),
55
+ ...(json.organisation_id && {
56
+ organisation_id: json.organisation_id,
57
+ }),
58
+ ...(json.order_id && { order_id: json.order_id }),
59
+ ...(json.booking_id && { booking_id: json.booking_id }),
60
+ };
61
+ return data as ClientKeyPayload;
62
+ } catch (error) {
63
+ return null;
64
+ }
65
+ }