@sendoracloud/sdk-react-native 1.3.0 → 1.5.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 +83 -2
- package/dist/contact-widget.d.cts +36 -1
- package/dist/contact-widget.d.ts +36 -1
- package/dist/contact-widget.js +80 -1
- package/dist/index.cjs +16 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +16 -0
- 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);
|
|
@@ -134,8 +136,87 @@ function useContactWidget(widgetId, defaults) {
|
|
|
134
136
|
);
|
|
135
137
|
return { open, close, View: View2, lastResult };
|
|
136
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
|
+
}
|
|
137
216
|
// Annotate the CommonJS export names for ESM import in node:
|
|
138
217
|
0 && (module.exports = {
|
|
139
218
|
ContactWidget,
|
|
140
|
-
|
|
219
|
+
TicketHistory,
|
|
220
|
+
useContactWidget,
|
|
221
|
+
useTicketHistory
|
|
141
222
|
});
|
|
@@ -99,5 +99,40 @@ declare function useContactWidget(widgetId: string, defaults?: Omit<ContactWidge
|
|
|
99
99
|
View: () => React.ReactElement | null;
|
|
100
100
|
lastResult: ContactWidgetResult | null;
|
|
101
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
|
+
};
|
|
102
137
|
|
|
103
|
-
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
|
@@ -99,5 +99,40 @@ declare function useContactWidget(widgetId: string, defaults?: Omit<ContactWidge
|
|
|
99
99
|
View: () => React.ReactElement | null;
|
|
100
100
|
lastResult: ContactWidgetResult | null;
|
|
101
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
|
+
};
|
|
102
137
|
|
|
103
|
-
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
|
@@ -99,7 +99,86 @@ function useContactWidget(widgetId, defaults) {
|
|
|
99
99
|
);
|
|
100
100
|
return { open, close, View: View2, lastResult };
|
|
101
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
|
+
}
|
|
102
179
|
export {
|
|
103
180
|
ContactWidget,
|
|
104
|
-
|
|
181
|
+
TicketHistory,
|
|
182
|
+
useContactWidget,
|
|
183
|
+
useTicketHistory
|
|
105
184
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -2308,6 +2308,22 @@ var SendoraSDK = class {
|
|
|
2308
2308
|
const parsed = await res.json();
|
|
2309
2309
|
if (!parsed.success || !parsed.data) throw new Error("[sendoracloud] support.submitCsat: bad response");
|
|
2310
2310
|
return parsed.data;
|
|
2311
|
+
},
|
|
2312
|
+
getUnreadCount: async (widgetId) => {
|
|
2313
|
+
if (!self.config || !widgetId) return 0;
|
|
2314
|
+
const token = await self.auth.getAccessToken();
|
|
2315
|
+
if (!token) return 0;
|
|
2316
|
+
try {
|
|
2317
|
+
const res = await fetch(
|
|
2318
|
+
`${self.config.apiUrl}/api/v1/widgets/${encodeURIComponent(widgetId)}/my-tickets/unread-count`,
|
|
2319
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
2320
|
+
);
|
|
2321
|
+
if (!res.ok) return 0;
|
|
2322
|
+
const parsed = await res.json();
|
|
2323
|
+
return parsed?.data?.count ?? 0;
|
|
2324
|
+
} catch {
|
|
2325
|
+
return 0;
|
|
2326
|
+
}
|
|
2311
2327
|
}
|
|
2312
2328
|
};
|
|
2313
2329
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1008,6 +1008,13 @@ declare class SendoraSDK {
|
|
|
1008
1008
|
rating: number;
|
|
1009
1009
|
comment: string | null;
|
|
1010
1010
|
}>;
|
|
1011
|
+
/**
|
|
1012
|
+
* Wave 73 — unread support-ticket count for the in-app badge.
|
|
1013
|
+
* Bearer JWT auth via the SDK's current end-user session.
|
|
1014
|
+
* Returns 0 if no auth (anon or identified) is available.
|
|
1015
|
+
* Recommended cadence: AppState foreground + every 60s.
|
|
1016
|
+
*/
|
|
1017
|
+
getUnreadCount: (widgetId: string) => Promise<number>;
|
|
1011
1018
|
};
|
|
1012
1019
|
/**
|
|
1013
1020
|
* Knowledge-base search namespace. Mirrors backend `GET /kb/search`
|
package/dist/index.d.ts
CHANGED
|
@@ -1008,6 +1008,13 @@ declare class SendoraSDK {
|
|
|
1008
1008
|
rating: number;
|
|
1009
1009
|
comment: string | null;
|
|
1010
1010
|
}>;
|
|
1011
|
+
/**
|
|
1012
|
+
* Wave 73 — unread support-ticket count for the in-app badge.
|
|
1013
|
+
* Bearer JWT auth via the SDK's current end-user session.
|
|
1014
|
+
* Returns 0 if no auth (anon or identified) is available.
|
|
1015
|
+
* Recommended cadence: AppState foreground + every 60s.
|
|
1016
|
+
*/
|
|
1017
|
+
getUnreadCount: (widgetId: string) => Promise<number>;
|
|
1011
1018
|
};
|
|
1012
1019
|
/**
|
|
1013
1020
|
* Knowledge-base search namespace. Mirrors backend `GET /kb/search`
|
package/dist/index.js
CHANGED
|
@@ -2270,6 +2270,22 @@ var SendoraSDK = class {
|
|
|
2270
2270
|
const parsed = await res.json();
|
|
2271
2271
|
if (!parsed.success || !parsed.data) throw new Error("[sendoracloud] support.submitCsat: bad response");
|
|
2272
2272
|
return parsed.data;
|
|
2273
|
+
},
|
|
2274
|
+
getUnreadCount: async (widgetId) => {
|
|
2275
|
+
if (!self.config || !widgetId) return 0;
|
|
2276
|
+
const token = await self.auth.getAccessToken();
|
|
2277
|
+
if (!token) return 0;
|
|
2278
|
+
try {
|
|
2279
|
+
const res = await fetch(
|
|
2280
|
+
`${self.config.apiUrl}/api/v1/widgets/${encodeURIComponent(widgetId)}/my-tickets/unread-count`,
|
|
2281
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
2282
|
+
);
|
|
2283
|
+
if (!res.ok) return 0;
|
|
2284
|
+
const parsed = await res.json();
|
|
2285
|
+
return parsed?.data?.count ?? 0;
|
|
2286
|
+
} catch {
|
|
2287
|
+
return 0;
|
|
2288
|
+
}
|
|
2273
2289
|
}
|
|
2274
2290
|
};
|
|
2275
2291
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendoracloud/sdk-react-native",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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",
|