@sendoracloud/sdk-react-native 1.2.0 → 1.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.
- package/dist/contact-widget.cjs +99 -4
- package/dist/contact-widget.d.cts +71 -3
- package/dist/contact-widget.d.ts +71 -3
- package/dist/contact-widget.js +96 -3
- package/package.json +1 -1
package/dist/contact-widget.cjs
CHANGED
|
@@ -31,7 +31,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var contact_widget_exports = {};
|
|
32
32
|
__export(contact_widget_exports, {
|
|
33
33
|
ContactWidget: () => ContactWidget,
|
|
34
|
-
|
|
34
|
+
TicketHistory: () => TicketHistory,
|
|
35
|
+
useContactWidget: () => useContactWidget,
|
|
36
|
+
useTicketHistory: () => useTicketHistory
|
|
35
37
|
});
|
|
36
38
|
module.exports = __toCommonJS(contact_widget_exports);
|
|
37
39
|
var React = __toESM(require("react"), 1);
|
|
@@ -45,6 +47,8 @@ function ContactWidget({
|
|
|
45
47
|
theme = "auto",
|
|
46
48
|
prefillName,
|
|
47
49
|
prefillEmail,
|
|
50
|
+
prefillUserId,
|
|
51
|
+
lockEmail,
|
|
48
52
|
onClose
|
|
49
53
|
}) {
|
|
50
54
|
const didFire = React.useRef(false);
|
|
@@ -55,6 +59,8 @@ function ContactWidget({
|
|
|
55
59
|
const params = new URLSearchParams({ widgetId, theme });
|
|
56
60
|
if (prefillName) params.set("prefillName", prefillName);
|
|
57
61
|
if (prefillEmail) params.set("prefillEmail", prefillEmail);
|
|
62
|
+
if (prefillUserId) params.set("prefillUserId", prefillUserId);
|
|
63
|
+
if (lockEmail && prefillEmail) params.set("lockEmail", "1");
|
|
58
64
|
const uri = `${workerHost}/embed/contact?${params.toString()}`;
|
|
59
65
|
const handleClose = (result) => {
|
|
60
66
|
if (didFire.current) return;
|
|
@@ -103,25 +109,114 @@ function useContactWidget(widgetId, defaults) {
|
|
|
103
109
|
const [lastResult, setLastResult] = React.useState(null);
|
|
104
110
|
const open = React.useCallback(() => setVisible(true), []);
|
|
105
111
|
const close = React.useCallback(() => setVisible(false), []);
|
|
112
|
+
const sdkUser = defaults?.user ?? null;
|
|
113
|
+
const resolvedName = defaults?.prefillName ?? sdkUser?.name ?? void 0;
|
|
114
|
+
const resolvedEmail = defaults?.prefillEmail ?? sdkUser?.email ?? void 0;
|
|
115
|
+
const resolvedUserId = defaults?.prefillUserId ?? sdkUser?.id ?? void 0;
|
|
116
|
+
const resolvedLockEmail = defaults?.lockEmail ?? (sdkUser != null && sdkUser.email != null && sdkUser.emailVerified && !sdkUser.isAnonymous);
|
|
106
117
|
const View2 = React.useCallback(
|
|
107
118
|
() => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
108
119
|
ContactWidget,
|
|
109
120
|
{
|
|
110
|
-
|
|
121
|
+
workerHost: defaults?.workerHost,
|
|
122
|
+
theme: defaults?.theme,
|
|
111
123
|
widgetId,
|
|
112
124
|
visible,
|
|
125
|
+
prefillName: resolvedName,
|
|
126
|
+
prefillEmail: resolvedEmail,
|
|
127
|
+
prefillUserId: resolvedUserId,
|
|
128
|
+
lockEmail: resolvedLockEmail,
|
|
113
129
|
onClose: (r) => {
|
|
114
130
|
setLastResult(r);
|
|
115
131
|
setVisible(false);
|
|
116
132
|
}
|
|
117
133
|
}
|
|
118
134
|
),
|
|
119
|
-
[defaults, widgetId, visible]
|
|
135
|
+
[defaults, widgetId, visible, resolvedName, resolvedEmail, resolvedUserId, resolvedLockEmail]
|
|
120
136
|
);
|
|
121
137
|
return { open, close, View: View2, lastResult };
|
|
122
138
|
}
|
|
139
|
+
function TicketHistory({
|
|
140
|
+
visible,
|
|
141
|
+
widgetId,
|
|
142
|
+
accessToken,
|
|
143
|
+
workerHost = "https://go.sendoracloud.com",
|
|
144
|
+
theme = "auto",
|
|
145
|
+
onClose
|
|
146
|
+
}) {
|
|
147
|
+
const didFire = React.useRef(false);
|
|
148
|
+
React.useEffect(() => {
|
|
149
|
+
if (visible) didFire.current = false;
|
|
150
|
+
}, [visible]);
|
|
151
|
+
if (!widgetId || !accessToken) return null;
|
|
152
|
+
const params = new URLSearchParams({ widgetId, theme });
|
|
153
|
+
const uri = `${workerHost}/embed/tickets?${params.toString()}#access=${encodeURIComponent(accessToken)}`;
|
|
154
|
+
const handleClose = (result) => {
|
|
155
|
+
if (didFire.current) return;
|
|
156
|
+
didFire.current = true;
|
|
157
|
+
onClose(result);
|
|
158
|
+
};
|
|
159
|
+
const onNav = (req) => {
|
|
160
|
+
const url = req.url || "";
|
|
161
|
+
if (url.startsWith("sendora://close")) {
|
|
162
|
+
handleClose({ ticketId: "", portalUrl: null, submitted: false });
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
if (!url.startsWith("https://") && !url.startsWith("about:")) return false;
|
|
166
|
+
return true;
|
|
167
|
+
};
|
|
168
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
169
|
+
import_react_native.Modal,
|
|
170
|
+
{
|
|
171
|
+
visible,
|
|
172
|
+
animationType: "slide",
|
|
173
|
+
presentationStyle: "pageSheet",
|
|
174
|
+
onRequestClose: () => handleClose({ ticketId: "", portalUrl: null, submitted: false }),
|
|
175
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
176
|
+
import_react_native_webview.WebView,
|
|
177
|
+
{
|
|
178
|
+
source: { uri },
|
|
179
|
+
onShouldStartLoadWithRequest: onNav,
|
|
180
|
+
originWhitelist: ["https://*", "about:*", "sendora://*"],
|
|
181
|
+
setSupportMultipleWindows: false,
|
|
182
|
+
javaScriptEnabled: true,
|
|
183
|
+
domStorageEnabled: true,
|
|
184
|
+
allowsBackForwardNavigationGestures: false
|
|
185
|
+
}
|
|
186
|
+
) })
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
function useTicketHistory(widgetId, getAccessToken, defaults) {
|
|
191
|
+
const [visible, setVisible] = React.useState(false);
|
|
192
|
+
const [token, setToken] = React.useState(null);
|
|
193
|
+
const open = React.useCallback(async () => {
|
|
194
|
+
const t = await getAccessToken();
|
|
195
|
+
if (!t) return;
|
|
196
|
+
setToken(t);
|
|
197
|
+
setVisible(true);
|
|
198
|
+
}, [getAccessToken]);
|
|
199
|
+
const close = React.useCallback(() => setVisible(false), []);
|
|
200
|
+
const View2 = React.useCallback(
|
|
201
|
+
() => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
202
|
+
TicketHistory,
|
|
203
|
+
{
|
|
204
|
+
widgetId,
|
|
205
|
+
visible,
|
|
206
|
+
accessToken: token,
|
|
207
|
+
workerHost: defaults?.workerHost,
|
|
208
|
+
theme: defaults?.theme,
|
|
209
|
+
onClose: () => setVisible(false)
|
|
210
|
+
}
|
|
211
|
+
),
|
|
212
|
+
[widgetId, visible, token, defaults]
|
|
213
|
+
);
|
|
214
|
+
return { open, close, View: View2 };
|
|
215
|
+
}
|
|
123
216
|
// Annotate the CommonJS export names for ESM import in node:
|
|
124
217
|
0 && (module.exports = {
|
|
125
218
|
ContactWidget,
|
|
126
|
-
|
|
219
|
+
TicketHistory,
|
|
220
|
+
useContactWidget,
|
|
221
|
+
useTicketHistory
|
|
127
222
|
});
|
|
@@ -44,6 +44,20 @@ interface ContactWidgetProps {
|
|
|
44
44
|
prefillName?: string;
|
|
45
45
|
/** Pre-fill email field for signed-in users. */
|
|
46
46
|
prefillEmail?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Wave 68 — stitch the ticket to the same profile as analytics
|
|
49
|
+
* events. Pass the signed-in user's id (anon or identified). Hint
|
|
50
|
+
* only, not an auth boundary — backend stores it without verifying
|
|
51
|
+
* because the WebView is in customer-app code.
|
|
52
|
+
*/
|
|
53
|
+
prefillUserId?: string;
|
|
54
|
+
/**
|
|
55
|
+
* When true (and `prefillEmail` is set) the email field is hidden +
|
|
56
|
+
* read-only. Use for identified users so the agent inbox can trust
|
|
57
|
+
* the email field came from the customer's verified account, not
|
|
58
|
+
* free text.
|
|
59
|
+
*/
|
|
60
|
+
lockEmail?: boolean;
|
|
47
61
|
/**
|
|
48
62
|
* Fired when the user closes the sheet — either after a successful
|
|
49
63
|
* submit (`submitted = true`) or via the close button
|
|
@@ -51,7 +65,7 @@ interface ContactWidgetProps {
|
|
|
51
65
|
*/
|
|
52
66
|
onClose: (result: ContactWidgetResult) => void;
|
|
53
67
|
}
|
|
54
|
-
declare function ContactWidget({ visible, widgetId, workerHost, theme, prefillName, prefillEmail, onClose, }: ContactWidgetProps): React.ReactElement | null;
|
|
68
|
+
declare function ContactWidget({ visible, widgetId, workerHost, theme, prefillName, prefillEmail, prefillUserId, lockEmail, onClose, }: ContactWidgetProps): React.ReactElement | null;
|
|
55
69
|
/**
|
|
56
70
|
* Helper hook so the host doesn't have to wire visibility state by
|
|
57
71
|
* hand.
|
|
@@ -59,12 +73,66 @@ declare function ContactWidget({ visible, widgetId, workerHost, theme, prefillNa
|
|
|
59
73
|
* const contact = useContactWidget("widget-uuid");
|
|
60
74
|
* <Button onPress={contact.open} title="Contact us" />
|
|
61
75
|
* <contact.View />
|
|
76
|
+
*
|
|
77
|
+
* Auto-resolves identity from the SDK when the host passes the
|
|
78
|
+
* current `AuthUser` via the `user` default. Identified users get the
|
|
79
|
+
* locked-email mode + userId attached automatically. Anonymous SDK
|
|
80
|
+
* rows get the userId attached without locking. No user = standard
|
|
81
|
+
* form.
|
|
82
|
+
*
|
|
83
|
+
* import SendoraCloud from "@sendoracloud/sdk-react-native";
|
|
84
|
+
* const user = SendoraCloud.auth.getCurrentUser();
|
|
85
|
+
* const contact = useContactWidget("widget-uuid", { user });
|
|
62
86
|
*/
|
|
63
|
-
declare function useContactWidget(widgetId: string, defaults?: Omit<ContactWidgetProps, "visible" | "widgetId" | "onClose">
|
|
87
|
+
declare function useContactWidget(widgetId: string, defaults?: Omit<ContactWidgetProps, "visible" | "widgetId" | "onClose"> & {
|
|
88
|
+
/** Pass `SendoraCloud.auth.getCurrentUser()` here to auto-stitch identity. */
|
|
89
|
+
user?: {
|
|
90
|
+
id: string;
|
|
91
|
+
email: string | null;
|
|
92
|
+
emailVerified: boolean;
|
|
93
|
+
name: string | null;
|
|
94
|
+
isAnonymous: boolean;
|
|
95
|
+
} | null;
|
|
96
|
+
}): {
|
|
64
97
|
open: () => void;
|
|
65
98
|
close: () => void;
|
|
66
99
|
View: () => React.ReactElement | null;
|
|
67
100
|
lastResult: ContactWidgetResult | null;
|
|
68
101
|
};
|
|
102
|
+
interface TicketHistoryProps {
|
|
103
|
+
/** Whether the modal sheet is visible. */
|
|
104
|
+
visible: boolean;
|
|
105
|
+
/** Widget UUID that scopes the org. */
|
|
106
|
+
widgetId: string;
|
|
107
|
+
/**
|
|
108
|
+
* Current SDK access token (from `SendoraCloud.auth.getAccessToken()`).
|
|
109
|
+
* Passed in the URL hash fragment so it never appears in server
|
|
110
|
+
* logs / referer headers. Embed scrubs the hash on load.
|
|
111
|
+
* The widget is no-op when this is null/empty.
|
|
112
|
+
*/
|
|
113
|
+
accessToken: string | null | undefined;
|
|
114
|
+
workerHost?: string;
|
|
115
|
+
theme?: "auto" | "light" | "dark";
|
|
116
|
+
onClose: (result: ContactWidgetResult) => void;
|
|
117
|
+
}
|
|
118
|
+
declare function TicketHistory({ visible, widgetId, accessToken, workerHost, theme, onClose, }: TicketHistoryProps): React.ReactElement | null;
|
|
119
|
+
/**
|
|
120
|
+
* Convenience hook for the ticket history view. Takes the SDK's
|
|
121
|
+
* `getAccessToken` helper (deferred — called lazily on open) so the
|
|
122
|
+
* host doesn't have to thread token state by hand.
|
|
123
|
+
*
|
|
124
|
+
* import SendoraCloud from "@sendoracloud/sdk-react-native";
|
|
125
|
+
* const history = useTicketHistory("widget-uuid", () => SendoraCloud.auth.getAccessToken());
|
|
126
|
+
* <Button onPress={history.open} title="My tickets" />
|
|
127
|
+
* <history.View />
|
|
128
|
+
*/
|
|
129
|
+
declare function useTicketHistory(widgetId: string, getAccessToken: () => Promise<string | null>, defaults?: {
|
|
130
|
+
workerHost?: string;
|
|
131
|
+
theme?: "auto" | "light" | "dark";
|
|
132
|
+
}): {
|
|
133
|
+
open: () => void;
|
|
134
|
+
close: () => void;
|
|
135
|
+
View: () => React.ReactElement | null;
|
|
136
|
+
};
|
|
69
137
|
|
|
70
|
-
export { ContactWidget, type ContactWidgetProps, type ContactWidgetResult, useContactWidget };
|
|
138
|
+
export { ContactWidget, type ContactWidgetProps, type ContactWidgetResult, TicketHistory, type TicketHistoryProps, useContactWidget, useTicketHistory };
|
package/dist/contact-widget.d.ts
CHANGED
|
@@ -44,6 +44,20 @@ interface ContactWidgetProps {
|
|
|
44
44
|
prefillName?: string;
|
|
45
45
|
/** Pre-fill email field for signed-in users. */
|
|
46
46
|
prefillEmail?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Wave 68 — stitch the ticket to the same profile as analytics
|
|
49
|
+
* events. Pass the signed-in user's id (anon or identified). Hint
|
|
50
|
+
* only, not an auth boundary — backend stores it without verifying
|
|
51
|
+
* because the WebView is in customer-app code.
|
|
52
|
+
*/
|
|
53
|
+
prefillUserId?: string;
|
|
54
|
+
/**
|
|
55
|
+
* When true (and `prefillEmail` is set) the email field is hidden +
|
|
56
|
+
* read-only. Use for identified users so the agent inbox can trust
|
|
57
|
+
* the email field came from the customer's verified account, not
|
|
58
|
+
* free text.
|
|
59
|
+
*/
|
|
60
|
+
lockEmail?: boolean;
|
|
47
61
|
/**
|
|
48
62
|
* Fired when the user closes the sheet — either after a successful
|
|
49
63
|
* submit (`submitted = true`) or via the close button
|
|
@@ -51,7 +65,7 @@ interface ContactWidgetProps {
|
|
|
51
65
|
*/
|
|
52
66
|
onClose: (result: ContactWidgetResult) => void;
|
|
53
67
|
}
|
|
54
|
-
declare function ContactWidget({ visible, widgetId, workerHost, theme, prefillName, prefillEmail, onClose, }: ContactWidgetProps): React.ReactElement | null;
|
|
68
|
+
declare function ContactWidget({ visible, widgetId, workerHost, theme, prefillName, prefillEmail, prefillUserId, lockEmail, onClose, }: ContactWidgetProps): React.ReactElement | null;
|
|
55
69
|
/**
|
|
56
70
|
* Helper hook so the host doesn't have to wire visibility state by
|
|
57
71
|
* hand.
|
|
@@ -59,12 +73,66 @@ declare function ContactWidget({ visible, widgetId, workerHost, theme, prefillNa
|
|
|
59
73
|
* const contact = useContactWidget("widget-uuid");
|
|
60
74
|
* <Button onPress={contact.open} title="Contact us" />
|
|
61
75
|
* <contact.View />
|
|
76
|
+
*
|
|
77
|
+
* Auto-resolves identity from the SDK when the host passes the
|
|
78
|
+
* current `AuthUser` via the `user` default. Identified users get the
|
|
79
|
+
* locked-email mode + userId attached automatically. Anonymous SDK
|
|
80
|
+
* rows get the userId attached without locking. No user = standard
|
|
81
|
+
* form.
|
|
82
|
+
*
|
|
83
|
+
* import SendoraCloud from "@sendoracloud/sdk-react-native";
|
|
84
|
+
* const user = SendoraCloud.auth.getCurrentUser();
|
|
85
|
+
* const contact = useContactWidget("widget-uuid", { user });
|
|
62
86
|
*/
|
|
63
|
-
declare function useContactWidget(widgetId: string, defaults?: Omit<ContactWidgetProps, "visible" | "widgetId" | "onClose">
|
|
87
|
+
declare function useContactWidget(widgetId: string, defaults?: Omit<ContactWidgetProps, "visible" | "widgetId" | "onClose"> & {
|
|
88
|
+
/** Pass `SendoraCloud.auth.getCurrentUser()` here to auto-stitch identity. */
|
|
89
|
+
user?: {
|
|
90
|
+
id: string;
|
|
91
|
+
email: string | null;
|
|
92
|
+
emailVerified: boolean;
|
|
93
|
+
name: string | null;
|
|
94
|
+
isAnonymous: boolean;
|
|
95
|
+
} | null;
|
|
96
|
+
}): {
|
|
64
97
|
open: () => void;
|
|
65
98
|
close: () => void;
|
|
66
99
|
View: () => React.ReactElement | null;
|
|
67
100
|
lastResult: ContactWidgetResult | null;
|
|
68
101
|
};
|
|
102
|
+
interface TicketHistoryProps {
|
|
103
|
+
/** Whether the modal sheet is visible. */
|
|
104
|
+
visible: boolean;
|
|
105
|
+
/** Widget UUID that scopes the org. */
|
|
106
|
+
widgetId: string;
|
|
107
|
+
/**
|
|
108
|
+
* Current SDK access token (from `SendoraCloud.auth.getAccessToken()`).
|
|
109
|
+
* Passed in the URL hash fragment so it never appears in server
|
|
110
|
+
* logs / referer headers. Embed scrubs the hash on load.
|
|
111
|
+
* The widget is no-op when this is null/empty.
|
|
112
|
+
*/
|
|
113
|
+
accessToken: string | null | undefined;
|
|
114
|
+
workerHost?: string;
|
|
115
|
+
theme?: "auto" | "light" | "dark";
|
|
116
|
+
onClose: (result: ContactWidgetResult) => void;
|
|
117
|
+
}
|
|
118
|
+
declare function TicketHistory({ visible, widgetId, accessToken, workerHost, theme, onClose, }: TicketHistoryProps): React.ReactElement | null;
|
|
119
|
+
/**
|
|
120
|
+
* Convenience hook for the ticket history view. Takes the SDK's
|
|
121
|
+
* `getAccessToken` helper (deferred — called lazily on open) so the
|
|
122
|
+
* host doesn't have to thread token state by hand.
|
|
123
|
+
*
|
|
124
|
+
* import SendoraCloud from "@sendoracloud/sdk-react-native";
|
|
125
|
+
* const history = useTicketHistory("widget-uuid", () => SendoraCloud.auth.getAccessToken());
|
|
126
|
+
* <Button onPress={history.open} title="My tickets" />
|
|
127
|
+
* <history.View />
|
|
128
|
+
*/
|
|
129
|
+
declare function useTicketHistory(widgetId: string, getAccessToken: () => Promise<string | null>, defaults?: {
|
|
130
|
+
workerHost?: string;
|
|
131
|
+
theme?: "auto" | "light" | "dark";
|
|
132
|
+
}): {
|
|
133
|
+
open: () => void;
|
|
134
|
+
close: () => void;
|
|
135
|
+
View: () => React.ReactElement | null;
|
|
136
|
+
};
|
|
69
137
|
|
|
70
|
-
export { ContactWidget, type ContactWidgetProps, type ContactWidgetResult, useContactWidget };
|
|
138
|
+
export { ContactWidget, type ContactWidgetProps, type ContactWidgetResult, TicketHistory, type TicketHistoryProps, useContactWidget, useTicketHistory };
|
package/dist/contact-widget.js
CHANGED
|
@@ -10,6 +10,8 @@ function ContactWidget({
|
|
|
10
10
|
theme = "auto",
|
|
11
11
|
prefillName,
|
|
12
12
|
prefillEmail,
|
|
13
|
+
prefillUserId,
|
|
14
|
+
lockEmail,
|
|
13
15
|
onClose
|
|
14
16
|
}) {
|
|
15
17
|
const didFire = React.useRef(false);
|
|
@@ -20,6 +22,8 @@ function ContactWidget({
|
|
|
20
22
|
const params = new URLSearchParams({ widgetId, theme });
|
|
21
23
|
if (prefillName) params.set("prefillName", prefillName);
|
|
22
24
|
if (prefillEmail) params.set("prefillEmail", prefillEmail);
|
|
25
|
+
if (prefillUserId) params.set("prefillUserId", prefillUserId);
|
|
26
|
+
if (lockEmail && prefillEmail) params.set("lockEmail", "1");
|
|
23
27
|
const uri = `${workerHost}/embed/contact?${params.toString()}`;
|
|
24
28
|
const handleClose = (result) => {
|
|
25
29
|
if (didFire.current) return;
|
|
@@ -68,24 +72,113 @@ function useContactWidget(widgetId, defaults) {
|
|
|
68
72
|
const [lastResult, setLastResult] = React.useState(null);
|
|
69
73
|
const open = React.useCallback(() => setVisible(true), []);
|
|
70
74
|
const close = React.useCallback(() => setVisible(false), []);
|
|
75
|
+
const sdkUser = defaults?.user ?? null;
|
|
76
|
+
const resolvedName = defaults?.prefillName ?? sdkUser?.name ?? void 0;
|
|
77
|
+
const resolvedEmail = defaults?.prefillEmail ?? sdkUser?.email ?? void 0;
|
|
78
|
+
const resolvedUserId = defaults?.prefillUserId ?? sdkUser?.id ?? void 0;
|
|
79
|
+
const resolvedLockEmail = defaults?.lockEmail ?? (sdkUser != null && sdkUser.email != null && sdkUser.emailVerified && !sdkUser.isAnonymous);
|
|
71
80
|
const View2 = React.useCallback(
|
|
72
81
|
() => /* @__PURE__ */ jsx(
|
|
73
82
|
ContactWidget,
|
|
74
83
|
{
|
|
75
|
-
|
|
84
|
+
workerHost: defaults?.workerHost,
|
|
85
|
+
theme: defaults?.theme,
|
|
76
86
|
widgetId,
|
|
77
87
|
visible,
|
|
88
|
+
prefillName: resolvedName,
|
|
89
|
+
prefillEmail: resolvedEmail,
|
|
90
|
+
prefillUserId: resolvedUserId,
|
|
91
|
+
lockEmail: resolvedLockEmail,
|
|
78
92
|
onClose: (r) => {
|
|
79
93
|
setLastResult(r);
|
|
80
94
|
setVisible(false);
|
|
81
95
|
}
|
|
82
96
|
}
|
|
83
97
|
),
|
|
84
|
-
[defaults, widgetId, visible]
|
|
98
|
+
[defaults, widgetId, visible, resolvedName, resolvedEmail, resolvedUserId, resolvedLockEmail]
|
|
85
99
|
);
|
|
86
100
|
return { open, close, View: View2, lastResult };
|
|
87
101
|
}
|
|
102
|
+
function TicketHistory({
|
|
103
|
+
visible,
|
|
104
|
+
widgetId,
|
|
105
|
+
accessToken,
|
|
106
|
+
workerHost = "https://go.sendoracloud.com",
|
|
107
|
+
theme = "auto",
|
|
108
|
+
onClose
|
|
109
|
+
}) {
|
|
110
|
+
const didFire = React.useRef(false);
|
|
111
|
+
React.useEffect(() => {
|
|
112
|
+
if (visible) didFire.current = false;
|
|
113
|
+
}, [visible]);
|
|
114
|
+
if (!widgetId || !accessToken) return null;
|
|
115
|
+
const params = new URLSearchParams({ widgetId, theme });
|
|
116
|
+
const uri = `${workerHost}/embed/tickets?${params.toString()}#access=${encodeURIComponent(accessToken)}`;
|
|
117
|
+
const handleClose = (result) => {
|
|
118
|
+
if (didFire.current) return;
|
|
119
|
+
didFire.current = true;
|
|
120
|
+
onClose(result);
|
|
121
|
+
};
|
|
122
|
+
const onNav = (req) => {
|
|
123
|
+
const url = req.url || "";
|
|
124
|
+
if (url.startsWith("sendora://close")) {
|
|
125
|
+
handleClose({ ticketId: "", portalUrl: null, submitted: false });
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
if (!url.startsWith("https://") && !url.startsWith("about:")) return false;
|
|
129
|
+
return true;
|
|
130
|
+
};
|
|
131
|
+
return /* @__PURE__ */ jsx(
|
|
132
|
+
Modal,
|
|
133
|
+
{
|
|
134
|
+
visible,
|
|
135
|
+
animationType: "slide",
|
|
136
|
+
presentationStyle: "pageSheet",
|
|
137
|
+
onRequestClose: () => handleClose({ ticketId: "", portalUrl: null, submitted: false }),
|
|
138
|
+
children: /* @__PURE__ */ jsx(View, { style: { flex: 1 }, children: /* @__PURE__ */ jsx(
|
|
139
|
+
WebView,
|
|
140
|
+
{
|
|
141
|
+
source: { uri },
|
|
142
|
+
onShouldStartLoadWithRequest: onNav,
|
|
143
|
+
originWhitelist: ["https://*", "about:*", "sendora://*"],
|
|
144
|
+
setSupportMultipleWindows: false,
|
|
145
|
+
javaScriptEnabled: true,
|
|
146
|
+
domStorageEnabled: true,
|
|
147
|
+
allowsBackForwardNavigationGestures: false
|
|
148
|
+
}
|
|
149
|
+
) })
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
function useTicketHistory(widgetId, getAccessToken, defaults) {
|
|
154
|
+
const [visible, setVisible] = React.useState(false);
|
|
155
|
+
const [token, setToken] = React.useState(null);
|
|
156
|
+
const open = React.useCallback(async () => {
|
|
157
|
+
const t = await getAccessToken();
|
|
158
|
+
if (!t) return;
|
|
159
|
+
setToken(t);
|
|
160
|
+
setVisible(true);
|
|
161
|
+
}, [getAccessToken]);
|
|
162
|
+
const close = React.useCallback(() => setVisible(false), []);
|
|
163
|
+
const View2 = React.useCallback(
|
|
164
|
+
() => /* @__PURE__ */ jsx(
|
|
165
|
+
TicketHistory,
|
|
166
|
+
{
|
|
167
|
+
widgetId,
|
|
168
|
+
visible,
|
|
169
|
+
accessToken: token,
|
|
170
|
+
workerHost: defaults?.workerHost,
|
|
171
|
+
theme: defaults?.theme,
|
|
172
|
+
onClose: () => setVisible(false)
|
|
173
|
+
}
|
|
174
|
+
),
|
|
175
|
+
[widgetId, visible, token, defaults]
|
|
176
|
+
);
|
|
177
|
+
return { open, close, View: View2 };
|
|
178
|
+
}
|
|
88
179
|
export {
|
|
89
180
|
ContactWidget,
|
|
90
|
-
|
|
181
|
+
TicketHistory,
|
|
182
|
+
useContactWidget,
|
|
183
|
+
useTicketHistory
|
|
91
184
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendoracloud/sdk-react-native",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Sendora Cloud React Native + Expo SDK — analytics, identity, push-token registration, auth, deep links (Branch / Firebase Dynamic Links parity). Auth + analytics + deep links work in Expo Go; push token registration requires a Dev Client (EAS Build or `npx expo prebuild`).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|