@hipnation-truth/sdk 0.6.0 → 0.7.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/react.js CHANGED
@@ -47,7 +47,11 @@ __export(react_exports, {
47
47
  useAppointment: () => useAppointment,
48
48
  useAppointmentByElationId: () => useAppointmentByElationId,
49
49
  useAppointments: () => useAppointments,
50
+ useConversationByPhonePair: () => useConversationByPhonePair,
50
51
  useConversationMessages: () => useConversationMessages,
52
+ useConversations: () => useConversations,
53
+ useMessages: () => useMessages,
54
+ useNotifications: () => useNotifications,
51
55
  usePatient: () => usePatient,
52
56
  usePatientBasic: () => usePatientBasic,
53
57
  usePatientByElationId: () => usePatientByElationId,
@@ -58,87 +62,145 @@ __export(react_exports, {
58
62
  usePharmacyByNcpdpId: () => usePharmacyByNcpdpId,
59
63
  usePhysicianByElationId: () => usePhysicianByElationId,
60
64
  usePhysiciansByElationIds: () => usePhysiciansByElationIds,
61
- useTruth: () => useTruth
65
+ useTruth: () => useTruth,
66
+ useUnreadCount: () => useUnreadCount
62
67
  });
63
68
  module.exports = __toCommonJS(react_exports);
64
69
 
65
- // src/react/hooks.ts
70
+ // src/react/conversations.ts
66
71
  var import_react = require("convex/react");
67
- var import_react2 = require("react");
68
72
  var import_server = require("convex/server");
69
- var patientsListRef = (0, import_server.makeFunctionReference)("patients:list");
70
- var patientsGetRef = (0, import_server.makeFunctionReference)("patients:get");
71
- var patientsByElationIdRef = (0, import_server.makeFunctionReference)("patients:getByElationId");
72
- var patientsByHintIdRef = (0, import_server.makeFunctionReference)("patients:getByHintId");
73
- var appointmentsListRef = (0, import_server.makeFunctionReference)("appointments:list");
74
- var appointmentsGetRef = (0, import_server.makeFunctionReference)("appointments:get");
75
- var appointmentsByElationIdRef = (0, import_server.makeFunctionReference)("appointments:getByElationId");
73
+ var conversationsListForUserRef = (0, import_server.makeFunctionReference)("conversations:listForUser");
74
+ var conversationsGetUnreadTotalForUserRef = (0, import_server.makeFunctionReference)("conversations:getUnreadTotalForUser");
75
+ var conversationsGetByPhonePairRef = (0, import_server.makeFunctionReference)("conversations:getByPhonePair");
76
+ var conversationMessagesGetByConversationIdRef = (0, import_server.makeFunctionReference)("conversationMessages:getByConversationId");
77
+ var SKIP = "skip";
78
+ function toResult(value, skipped) {
79
+ if (skipped) {
80
+ return { data: void 0, loading: false, error: void 0 };
81
+ }
82
+ return {
83
+ data: value,
84
+ loading: value === void 0,
85
+ error: void 0
86
+ };
87
+ }
88
+ function useConversations(filters) {
89
+ const skipped = !filters.userId;
90
+ const result = (0, import_react.useQuery)(
91
+ conversationsListForUserRef,
92
+ skipped ? SKIP : {
93
+ userId: filters.userId,
94
+ limit: filters.limit
95
+ }
96
+ );
97
+ return toResult(result, skipped);
98
+ }
99
+ function useConversationByPhonePair(phonePair) {
100
+ const skipped = !phonePair;
101
+ const result = (0, import_react.useQuery)(
102
+ conversationsGetByPhonePairRef,
103
+ skipped ? SKIP : { phonePair }
104
+ );
105
+ return toResult(result, skipped);
106
+ }
107
+ function useMessages(conversationId, options) {
108
+ const skipped = !conversationId;
109
+ const result = (0, import_react.useQuery)(
110
+ conversationMessagesGetByConversationIdRef,
111
+ skipped ? SKIP : {
112
+ conversationId,
113
+ limit: options == null ? void 0 : options.limit
114
+ }
115
+ );
116
+ return toResult(result, skipped);
117
+ }
118
+ function useUnreadCount(userId) {
119
+ const skipped = !userId;
120
+ const result = (0, import_react.useQuery)(
121
+ conversationsGetUnreadTotalForUserRef,
122
+ skipped ? SKIP : { userId }
123
+ );
124
+ return toResult(result, skipped);
125
+ }
126
+
127
+ // src/react/hooks.ts
128
+ var import_react2 = require("convex/react");
129
+ var import_react3 = require("react");
130
+ var import_server2 = require("convex/server");
131
+ var patientsListRef = (0, import_server2.makeFunctionReference)("patients:list");
132
+ var patientsGetRef = (0, import_server2.makeFunctionReference)("patients:get");
133
+ var patientsByElationIdRef = (0, import_server2.makeFunctionReference)("patients:getByElationId");
134
+ var patientsByHintIdRef = (0, import_server2.makeFunctionReference)("patients:getByHintId");
135
+ var appointmentsListRef = (0, import_server2.makeFunctionReference)("appointments:list");
136
+ var appointmentsGetRef = (0, import_server2.makeFunctionReference)("appointments:get");
137
+ var appointmentsByElationIdRef = (0, import_server2.makeFunctionReference)("appointments:getByElationId");
76
138
  function usePatients(options) {
77
- return (0, import_react.useQuery)(patientsListRef, options != null ? options : {});
139
+ return (0, import_react2.useQuery)(patientsListRef, options != null ? options : {});
78
140
  }
79
141
  function usePatient(id) {
80
- return (0, import_react.useQuery)(patientsGetRef, { id });
142
+ return (0, import_react2.useQuery)(patientsGetRef, { id });
81
143
  }
82
144
  function usePatientByElationId(elationId) {
83
- return (0, import_react.useQuery)(patientsByElationIdRef, {
145
+ return (0, import_react2.useQuery)(patientsByElationIdRef, {
84
146
  elationId
85
147
  });
86
148
  }
87
149
  function usePatientByHintId(hintId) {
88
- return (0, import_react.useQuery)(patientsByHintIdRef, {
150
+ return (0, import_react2.useQuery)(patientsByHintIdRef, {
89
151
  hintId
90
152
  });
91
153
  }
92
154
  function useAppointments(options) {
93
- return (0, import_react.useQuery)(
155
+ return (0, import_react2.useQuery)(
94
156
  appointmentsListRef,
95
157
  options != null ? options : {}
96
158
  );
97
159
  }
98
160
  function useAppointment(id) {
99
- return (0, import_react.useQuery)(appointmentsGetRef, { id });
161
+ return (0, import_react2.useQuery)(appointmentsGetRef, { id });
100
162
  }
101
163
  function useAppointmentByElationId(elationId) {
102
- return (0, import_react.useQuery)(appointmentsByElationIdRef, {
164
+ return (0, import_react2.useQuery)(appointmentsByElationIdRef, {
103
165
  elationId
104
166
  });
105
167
  }
106
- var physiciansGetByElationIdsRef = (0, import_server.makeFunctionReference)("physicians:getByElationIds");
107
- var physiciansGetByElationIdRef = (0, import_server.makeFunctionReference)("physicians:getByElationId");
168
+ var physiciansGetByElationIdsRef = (0, import_server2.makeFunctionReference)("physicians:getByElationIds");
169
+ var physiciansGetByElationIdRef = (0, import_server2.makeFunctionReference)("physicians:getByElationId");
108
170
  function usePhysiciansByElationIds(ids) {
109
- return (0, import_react.useQuery)(
171
+ return (0, import_react2.useQuery)(
110
172
  physiciansGetByElationIdsRef,
111
173
  ids && ids.length > 0 ? { ids } : "skip"
112
174
  );
113
175
  }
114
176
  function usePhysicianByElationId(id) {
115
- return (0, import_react.useQuery)(
177
+ return (0, import_react2.useQuery)(
116
178
  physiciansGetByElationIdRef,
117
179
  id !== void 0 ? { id } : "skip"
118
180
  );
119
181
  }
120
- var medicationsByPatientRef = (0, import_server.makeFunctionReference)("medicalRecords:getMedicationsByElationPatient");
121
- var problemsByPatientRef = (0, import_server.makeFunctionReference)("medicalRecords:getProblemsByElationPatient");
122
- var allergiesByPatientRef = (0, import_server.makeFunctionReference)("medicalRecords:getAllergiesByElationPatient");
123
- var appointmentsByPatientRef = (0, import_server.makeFunctionReference)("medicalRecords:getAppointmentsByElationPatient");
182
+ var medicationsByPatientRef = (0, import_server2.makeFunctionReference)("medicalRecords:getMedicationsByElationPatient");
183
+ var problemsByPatientRef = (0, import_server2.makeFunctionReference)("medicalRecords:getProblemsByElationPatient");
184
+ var allergiesByPatientRef = (0, import_server2.makeFunctionReference)("medicalRecords:getAllergiesByElationPatient");
185
+ var appointmentsByPatientRef = (0, import_server2.makeFunctionReference)("medicalRecords:getAppointmentsByElationPatient");
124
186
  function usePatientMedical(elationId, options) {
125
- const medications = (0, import_react.useQuery)(
187
+ const medications = (0, import_react2.useQuery)(
126
188
  medicationsByPatientRef,
127
189
  elationId !== void 0 ? { elationPatientId: elationId } : "skip"
128
190
  );
129
- const problems = (0, import_react.useQuery)(
191
+ const problems = (0, import_react2.useQuery)(
130
192
  problemsByPatientRef,
131
193
  elationId !== void 0 ? { elationPatientId: elationId } : "skip"
132
194
  );
133
- const allergies = (0, import_react.useQuery)(
195
+ const allergies = (0, import_react2.useQuery)(
134
196
  allergiesByPatientRef,
135
197
  elationId !== void 0 ? { elationPatientId: elationId } : "skip"
136
198
  );
137
- const appointments = (0, import_react.useQuery)(
199
+ const appointments = (0, import_react2.useQuery)(
138
200
  appointmentsByPatientRef,
139
201
  elationId !== void 0 ? { elationPatientId: elationId } : "skip"
140
202
  );
141
- (0, import_react2.useEffect)(() => {
203
+ (0, import_react3.useEffect)(() => {
142
204
  if (elationId === void 0 || (options == null ? void 0 : options.skipRefresh)) {
143
205
  return;
144
206
  }
@@ -162,26 +224,32 @@ function usePatientMedical(elationId, options) {
162
224
  }, [elationId, options == null ? void 0 : options.apiBaseUrl, options == null ? void 0 : options.apiKey, options == null ? void 0 : options.skipRefresh]);
163
225
  return { medications, problems, allergies, appointments };
164
226
  }
165
- var elationPatientByIdRef = (0, import_server.makeFunctionReference)("elationPatients:getByElationId");
166
- var hintPatientByIdRef = (0, import_server.makeFunctionReference)("hintPatients:getByHintId");
167
- var pharmacyByNcpdpRef = (0, import_server.makeFunctionReference)("elationPharmacies:getByNcpdpId");
168
- var patientPhotoByIdRef = (0, import_server.makeFunctionReference)("elationPatientPhotos:getByElationPatientId");
227
+ var elationPatientByIdRef = (0, import_server2.makeFunctionReference)("elationPatients:getByElationId");
228
+ var hintPatientByIdRef = (0, import_server2.makeFunctionReference)("hintPatients:getByHintId");
229
+ var pharmacyByNcpdpRef = (0, import_server2.makeFunctionReference)("elationPharmacies:getByNcpdpId");
230
+ var patientPhotoByIdRef = (0, import_server2.makeFunctionReference)("elationPatientPhotos:getByElationPatientId");
169
231
  function usePatientBasic(input, options) {
170
232
  var _a, _b;
171
- const elationRow = (0, import_react.useQuery)(
233
+ const elationRow = (0, import_react2.useQuery)(
172
234
  elationPatientByIdRef,
173
235
  input.elationId !== void 0 ? { elationId: input.elationId } : "skip"
174
236
  );
175
- const hintRow = (0, import_react.useQuery)(
237
+ const hintRow = (0, import_react2.useQuery)(
176
238
  hintPatientByIdRef,
177
239
  input.hintId !== void 0 ? { hintId: input.hintId } : "skip"
178
240
  );
179
- (0, import_react2.useEffect)(() => {
180
- if (options == null ? void 0 : options.skipRefresh) return;
181
- if (!input.hintId && input.elationId === void 0) return;
241
+ (0, import_react3.useEffect)(() => {
242
+ if (options == null ? void 0 : options.skipRefresh) {
243
+ return;
244
+ }
245
+ if (!input.hintId && input.elationId === void 0) {
246
+ return;
247
+ }
182
248
  const apiBaseUrl = options == null ? void 0 : options.apiBaseUrl;
183
249
  const apiKey = options == null ? void 0 : options.apiKey;
184
- if (!apiBaseUrl || !apiKey) return;
250
+ if (!apiBaseUrl || !apiKey) {
251
+ return;
252
+ }
185
253
  const controller = new AbortController();
186
254
  void fetch(`${apiBaseUrl}/api/patients/basic/refresh`, {
187
255
  method: "POST",
@@ -217,22 +285,28 @@ function usePatientBasic(input, options) {
217
285
  };
218
286
  }
219
287
  function usePharmacyByNcpdpId(ncpdpId) {
220
- return (0, import_react.useQuery)(
288
+ return (0, import_react2.useQuery)(
221
289
  pharmacyByNcpdpRef,
222
290
  ncpdpId ? { ncpdpId } : "skip"
223
291
  );
224
292
  }
225
293
  function usePatientPhoto(elationId, options) {
226
- const photo = (0, import_react.useQuery)(
294
+ const photo = (0, import_react2.useQuery)(
227
295
  patientPhotoByIdRef,
228
296
  elationId !== void 0 ? { elationPatientId: elationId } : "skip"
229
297
  );
230
- (0, import_react2.useEffect)(() => {
231
- if (options == null ? void 0 : options.skipRefresh) return;
232
- if (elationId === void 0) return;
298
+ (0, import_react3.useEffect)(() => {
299
+ if (options == null ? void 0 : options.skipRefresh) {
300
+ return;
301
+ }
302
+ if (elationId === void 0) {
303
+ return;
304
+ }
233
305
  const apiBaseUrl = options == null ? void 0 : options.apiBaseUrl;
234
306
  const apiKey = options == null ? void 0 : options.apiKey;
235
- if (!apiBaseUrl || !apiKey) return;
307
+ if (!apiBaseUrl || !apiKey) {
308
+ return;
309
+ }
236
310
  const controller = new AbortController();
237
311
  void fetch(`${apiBaseUrl}/api/patients/photo/refresh`, {
238
312
  method: "POST",
@@ -248,11 +322,11 @@ function usePatientPhoto(elationId, options) {
248
322
  }, [elationId, options == null ? void 0 : options.apiBaseUrl, options == null ? void 0 : options.apiKey, options == null ? void 0 : options.skipRefresh]);
249
323
  return photo;
250
324
  }
251
- var messagesByPhonesRef = (0, import_server.makeFunctionReference)("conversationMessages:getByPhones");
252
- var messagesByConversationIdRef = (0, import_server.makeFunctionReference)("conversationMessages:getByConversationId");
325
+ var messagesByPhonesRef = (0, import_server2.makeFunctionReference)("conversationMessages:getByPhones");
326
+ var messagesByConversationIdRef = (0, import_server2.makeFunctionReference)("conversationMessages:getByConversationId");
253
327
  function useConversationMessages(input, options) {
254
328
  const hasPair = !!input.phoneA && !!input.phoneB;
255
- const byPair = (0, import_react.useQuery)(
329
+ const byPair = (0, import_react2.useQuery)(
256
330
  messagesByPhonesRef,
257
331
  hasPair ? {
258
332
  phoneA: input.phoneA,
@@ -260,16 +334,398 @@ function useConversationMessages(input, options) {
260
334
  limit: options == null ? void 0 : options.limit
261
335
  } : "skip"
262
336
  );
263
- const byConvo = (0, import_react.useQuery)(
337
+ const byConvo = (0, import_react2.useQuery)(
264
338
  messagesByConversationIdRef,
265
339
  !hasPair && input.conversationId ? { conversationId: input.conversationId, limit: options == null ? void 0 : options.limit } : "skip"
266
340
  );
267
341
  return hasPair ? byPair : byConvo;
268
342
  }
269
343
 
270
- // src/react/provider.ts
271
- var import_react3 = require("convex/react");
344
+ // src/react/notifications.ts
272
345
  var import_react4 = require("react");
346
+
347
+ // src/web-push.ts
348
+ function isWebPushSupported() {
349
+ return typeof window !== "undefined" && "serviceWorker" in navigator && "PushManager" in window;
350
+ }
351
+ function registerServiceWorker(path = "/truth-sw.js") {
352
+ return __async(this, null, function* () {
353
+ return navigator.serviceWorker.register(path);
354
+ });
355
+ }
356
+ function subscribeToPush(registration, vapidPublicKey) {
357
+ return __async(this, null, function* () {
358
+ const existing = yield registration.pushManager.getSubscription();
359
+ if (existing) {
360
+ return existing;
361
+ }
362
+ return registration.pushManager.subscribe({
363
+ userVisibleOnly: true,
364
+ applicationServerKey: urlBase64ToUint8Array(
365
+ vapidPublicKey
366
+ )
367
+ });
368
+ });
369
+ }
370
+ function subscriptionToJSON(sub) {
371
+ var _a, _b, _c, _d;
372
+ const json = sub.toJSON();
373
+ return {
374
+ endpoint: sub.endpoint,
375
+ keys: {
376
+ p256dh: (_b = (_a = json.keys) == null ? void 0 : _a.p256dh) != null ? _b : "",
377
+ auth: (_d = (_c = json.keys) == null ? void 0 : _c.auth) != null ? _d : ""
378
+ }
379
+ };
380
+ }
381
+ function urlBase64ToUint8Array(base64String) {
382
+ const padding = "=".repeat((4 - base64String.length % 4) % 4);
383
+ const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
384
+ const rawData = atob(base64);
385
+ const outputArray = new Uint8Array(rawData.length);
386
+ for (let i = 0; i < rawData.length; ++i) {
387
+ outputArray[i] = rawData.charCodeAt(i);
388
+ }
389
+ return outputArray;
390
+ }
391
+
392
+ // src/react/notifications.ts
393
+ function loadExpo() {
394
+ return __async(this, null, function* () {
395
+ try {
396
+ const modName = "expo-notifications";
397
+ return yield import(
398
+ /* @vite-ignore */
399
+ /* webpackIgnore: true */
400
+ modName
401
+ );
402
+ } catch (e) {
403
+ return null;
404
+ }
405
+ });
406
+ }
407
+ function useNotifications(options) {
408
+ var _a;
409
+ const [permissionStatus, setPermissionStatus] = (0, import_react4.useState)("unknown");
410
+ const [devicePushToken, setDevicePushToken] = (0, import_react4.useState)(null);
411
+ const expoRef = (0, import_react4.useRef)(null);
412
+ const isWebRef = (0, import_react4.useRef)(false);
413
+ const vapidKeyRef = (0, import_react4.useRef)((_a = options.vapidPublicKey) != null ? _a : null);
414
+ (0, import_react4.useEffect)(() => {
415
+ let mounted = true;
416
+ void (() => __async(null, null, function* () {
417
+ var _a2;
418
+ const expo = yield loadExpo();
419
+ if (!mounted) {
420
+ return;
421
+ }
422
+ expoRef.current = expo;
423
+ if (expo) {
424
+ try {
425
+ const perm = yield expo.getPermissionsAsync();
426
+ if (!mounted) {
427
+ return;
428
+ }
429
+ setPermissionStatus(mapStatus(perm == null ? void 0 : perm.status));
430
+ } catch (e) {
431
+ setPermissionStatus("unknown");
432
+ }
433
+ return;
434
+ }
435
+ if (isWebPushSupported()) {
436
+ isWebRef.current = true;
437
+ if (!vapidKeyRef.current) {
438
+ try {
439
+ const res = yield fetch(
440
+ `${options.apiBaseUrl}/api/notifications/vapid-key`,
441
+ {
442
+ headers: {
443
+ Accept: "application/json",
444
+ "X-API-Key": options.apiKey
445
+ }
446
+ }
447
+ );
448
+ if (res.ok) {
449
+ const data = yield res.json();
450
+ vapidKeyRef.current = (_a2 = data.vapidPublicKey) != null ? _a2 : null;
451
+ }
452
+ } catch (e) {
453
+ }
454
+ }
455
+ if (!mounted) {
456
+ return;
457
+ }
458
+ const webPerm = typeof Notification !== "undefined" ? Notification.permission : "default";
459
+ setPermissionStatus(
460
+ webPerm === "granted" ? "granted" : webPerm === "denied" ? "denied" : "undetermined"
461
+ );
462
+ } else {
463
+ setPermissionStatus("unknown");
464
+ }
465
+ }))();
466
+ return () => {
467
+ mounted = false;
468
+ };
469
+ }, [options.apiBaseUrl, options.apiKey]);
470
+ const register = (0, import_react4.useCallback)(() => __async(null, null, function* () {
471
+ var _a2, _b;
472
+ if (!options.userId) {
473
+ return { ok: false, reason: "missing_userId" };
474
+ }
475
+ if (isWebRef.current) {
476
+ const vapidKey = vapidKeyRef.current;
477
+ if (!vapidKey) {
478
+ return { ok: false, reason: "no_vapid_key" };
479
+ }
480
+ const webPerm = yield Notification.requestPermission();
481
+ setPermissionStatus(
482
+ webPerm === "granted" ? "granted" : webPerm === "denied" ? "denied" : "undetermined"
483
+ );
484
+ if (webPerm !== "granted") {
485
+ return { ok: false, reason: "permission_denied" };
486
+ }
487
+ try {
488
+ const swPath = (_a2 = options.serviceWorkerPath) != null ? _a2 : "/truth-sw.js";
489
+ const registration = yield registerServiceWorker(swPath);
490
+ yield navigator.serviceWorker.ready;
491
+ const subscription = yield subscribeToPush(registration, vapidKey);
492
+ const subJSON = subscriptionToJSON(subscription);
493
+ setDevicePushToken(subscription.endpoint);
494
+ const res2 = yield fetch(
495
+ `${options.apiBaseUrl}/api/notifications/devices/register`,
496
+ {
497
+ method: "POST",
498
+ headers: {
499
+ "Content-Type": "application/json",
500
+ "X-API-Key": options.apiKey
501
+ },
502
+ body: JSON.stringify({
503
+ userId: options.userId,
504
+ platform: "web",
505
+ webPushSubscription: subJSON,
506
+ appVersion: options.appVersion,
507
+ locale: navigator.language,
508
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
509
+ })
510
+ }
511
+ );
512
+ if (!res2.ok) {
513
+ const text = yield res2.text().catch(() => "");
514
+ return {
515
+ ok: false,
516
+ reason: `register_failed_${res2.status}: ${text.slice(0, 120)}`
517
+ };
518
+ }
519
+ return { ok: true };
520
+ } catch (err) {
521
+ return {
522
+ ok: false,
523
+ reason: `web_push_error: ${err instanceof Error ? err.message : String(err)}`
524
+ };
525
+ }
526
+ }
527
+ const expo = (_b = expoRef.current) != null ? _b : yield loadExpo();
528
+ expoRef.current = expo;
529
+ if (!expo) {
530
+ return { ok: false, reason: "expo_notifications_missing" };
531
+ }
532
+ let perm = yield expo.getPermissionsAsync();
533
+ if ((perm == null ? void 0 : perm.status) !== "granted") {
534
+ perm = yield expo.requestPermissionsAsync();
535
+ }
536
+ setPermissionStatus(mapStatus(perm == null ? void 0 : perm.status));
537
+ if ((perm == null ? void 0 : perm.status) !== "granted") {
538
+ return { ok: false, reason: "permission_denied" };
539
+ }
540
+ const tokenResp = yield expo.getDevicePushTokenAsync();
541
+ const nativeToken = tokenResp == null ? void 0 : tokenResp.data;
542
+ const platform = detectPlatform(tokenResp == null ? void 0 : tokenResp.type);
543
+ if (!nativeToken || platform !== "ios" && platform !== "android") {
544
+ return { ok: false, reason: "no_native_token" };
545
+ }
546
+ setDevicePushToken(nativeToken);
547
+ const res = yield fetch(
548
+ `${options.apiBaseUrl}/api/notifications/devices/register`,
549
+ {
550
+ method: "POST",
551
+ headers: {
552
+ "Content-Type": "application/json",
553
+ "X-API-Key": options.apiKey
554
+ },
555
+ body: JSON.stringify({
556
+ userId: options.userId,
557
+ platform,
558
+ nativeToken,
559
+ appVersion: options.appVersion,
560
+ locale: typeof navigator !== "undefined" ? navigator.language : void 0,
561
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
562
+ })
563
+ }
564
+ );
565
+ if (!res.ok) {
566
+ const text = yield res.text().catch(() => "");
567
+ return {
568
+ ok: false,
569
+ reason: `register_failed_${res.status}: ${text.slice(0, 120)}`
570
+ };
571
+ }
572
+ return { ok: true };
573
+ }), [
574
+ options.apiBaseUrl,
575
+ options.apiKey,
576
+ options.userId,
577
+ options.appVersion,
578
+ options.serviceWorkerPath
579
+ ]);
580
+ const unregister = (0, import_react4.useCallback)(() => __async(null, null, function* () {
581
+ if (!devicePushToken) {
582
+ return;
583
+ }
584
+ yield fetch(`${options.apiBaseUrl}/api/notifications/devices/unregister`, {
585
+ method: "POST",
586
+ headers: {
587
+ "Content-Type": "application/json",
588
+ "X-API-Key": options.apiKey
589
+ },
590
+ body: JSON.stringify({ nativeToken: devicePushToken })
591
+ }).catch(() => {
592
+ });
593
+ setDevicePushToken(null);
594
+ }), [options.apiBaseUrl, options.apiKey, devicePushToken]);
595
+ const addReceivedListener = (0, import_react4.useCallback)(
596
+ (listener) => {
597
+ if (isWebRef.current) {
598
+ if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) {
599
+ return () => {
600
+ };
601
+ }
602
+ const handler = (event) => {
603
+ var _a2;
604
+ if (((_a2 = event.data) == null ? void 0 : _a2.type) === "TRUTH_PUSH_RECEIVED") {
605
+ listener(event.data.payload);
606
+ }
607
+ };
608
+ navigator.serviceWorker.addEventListener("message", handler);
609
+ return () => navigator.serviceWorker.removeEventListener("message", handler);
610
+ }
611
+ const expo = expoRef.current;
612
+ if (!(expo == null ? void 0 : expo.addNotificationReceivedListener)) {
613
+ return () => {
614
+ };
615
+ }
616
+ const sub = expo.addNotificationReceivedListener(listener);
617
+ return () => {
618
+ var _a2;
619
+ return (_a2 = sub.remove) == null ? void 0 : _a2.call(sub);
620
+ };
621
+ },
622
+ []
623
+ );
624
+ const addResponseListener = (0, import_react4.useCallback)(
625
+ (listener) => {
626
+ if (isWebRef.current) {
627
+ if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) {
628
+ return () => {
629
+ };
630
+ }
631
+ const handler = (event) => {
632
+ var _a2;
633
+ if (((_a2 = event.data) == null ? void 0 : _a2.type) === "TRUTH_PUSH_TAPPED") {
634
+ listener(event.data.payload);
635
+ }
636
+ };
637
+ navigator.serviceWorker.addEventListener("message", handler);
638
+ return () => navigator.serviceWorker.removeEventListener("message", handler);
639
+ }
640
+ const expo = expoRef.current;
641
+ if (!(expo == null ? void 0 : expo.addNotificationResponseReceivedListener)) {
642
+ return () => {
643
+ };
644
+ }
645
+ const sub = expo.addNotificationResponseReceivedListener(listener);
646
+ return () => {
647
+ var _a2;
648
+ return (_a2 = sub.remove) == null ? void 0 : _a2.call(sub);
649
+ };
650
+ },
651
+ []
652
+ );
653
+ const getBadgeCount = (0, import_react4.useCallback)(() => __async(null, null, function* () {
654
+ var _a2;
655
+ const expo = expoRef.current;
656
+ if (!(expo == null ? void 0 : expo.getBadgeCountAsync)) {
657
+ return 0;
658
+ }
659
+ return (_a2 = yield expo.getBadgeCountAsync()) != null ? _a2 : 0;
660
+ }), []);
661
+ const setBadgeCount = (0, import_react4.useCallback)((count) => __async(null, null, function* () {
662
+ const expo = expoRef.current;
663
+ if (!(expo == null ? void 0 : expo.setBadgeCountAsync)) {
664
+ return;
665
+ }
666
+ yield expo.setBadgeCountAsync(count);
667
+ }), []);
668
+ const autoRegister = options.autoRegister !== false;
669
+ (0, import_react4.useEffect)(() => {
670
+ if (!autoRegister) {
671
+ return;
672
+ }
673
+ if (permissionStatus !== "granted") {
674
+ return;
675
+ }
676
+ if (devicePushToken) {
677
+ return;
678
+ }
679
+ if (!options.userId) {
680
+ return;
681
+ }
682
+ void register();
683
+ }, [
684
+ autoRegister,
685
+ permissionStatus,
686
+ devicePushToken,
687
+ options.userId,
688
+ register
689
+ ]);
690
+ return {
691
+ permissionStatus,
692
+ devicePushToken,
693
+ register,
694
+ unregister,
695
+ addReceivedListener,
696
+ addResponseListener,
697
+ getBadgeCount,
698
+ setBadgeCount
699
+ };
700
+ }
701
+ function mapStatus(status) {
702
+ if (status === "granted") {
703
+ return "granted";
704
+ }
705
+ if (status === "denied") {
706
+ return "denied";
707
+ }
708
+ if (status === "undetermined") {
709
+ return "undetermined";
710
+ }
711
+ return "unknown";
712
+ }
713
+ function detectPlatform(tokenType) {
714
+ if (tokenType === "apns") {
715
+ return "ios";
716
+ }
717
+ if (tokenType === "fcm") {
718
+ return "android";
719
+ }
720
+ if (tokenType === "web") {
721
+ return "web";
722
+ }
723
+ return "unknown";
724
+ }
725
+
726
+ // src/react/provider.ts
727
+ var import_react5 = require("convex/react");
728
+ var import_react6 = require("react");
273
729
  var CONVEX_URLS = {
274
730
  local: "https://courteous-duck-623.convex.cloud",
275
731
  staging: "https://courteous-duck-623.convex.cloud",
@@ -278,19 +734,26 @@ var CONVEX_URLS = {
278
734
  uat: "https://gallant-gecko-217.convex.cloud",
279
735
  production: "https://gallant-gecko-217.convex.cloud"
280
736
  };
737
+ function resolveConvexUrl(environment, override) {
738
+ var _a;
739
+ if (override) {
740
+ return override;
741
+ }
742
+ const env = environment != null ? environment : "sandbox";
743
+ return (_a = CONVEX_URLS[env]) != null ? _a : CONVEX_URLS.sandbox;
744
+ }
281
745
  function TruthProvider({
282
746
  environment = "sandbox",
283
747
  convexUrl,
284
748
  children
285
749
  }) {
286
- var _a;
287
- const url = (_a = convexUrl != null ? convexUrl : CONVEX_URLS[environment]) != null ? _a : CONVEX_URLS.sandbox;
288
- const client = (0, import_react4.useMemo)(() => new import_react3.ConvexReactClient(url), [url]);
289
- return (0, import_react4.createElement)(import_react3.ConvexProvider, { client }, children);
750
+ const url = resolveConvexUrl(environment, convexUrl);
751
+ const client = (0, import_react6.useMemo)(() => new import_react5.ConvexReactClient(url), [url]);
752
+ return (0, import_react6.createElement)(import_react5.ConvexProvider, { client }, children);
290
753
  }
291
754
 
292
755
  // src/react/tracking.ts
293
- var import_react5 = require("react");
756
+ var import_react7 = require("react");
294
757
 
295
758
  // src/tracking/tracker.ts
296
759
  function generateUuidV7() {
@@ -485,7 +948,7 @@ function sleep(ms) {
485
948
  }
486
949
 
487
950
  // src/react/tracking.ts
488
- var TruthTrackingContext = (0, import_react5.createContext)(
951
+ var TruthTrackingContext = (0, import_react7.createContext)(
489
952
  null
490
953
  );
491
954
  function TruthTrackingProvider({
@@ -496,7 +959,7 @@ function TruthTrackingProvider({
496
959
  apiKey = "",
497
960
  children
498
961
  }) {
499
- const value = (0, import_react5.useMemo)(() => {
962
+ const value = (0, import_react7.useMemo)(() => {
500
963
  const tracker = new Tracker({
501
964
  apiKey,
502
965
  environment,
@@ -515,10 +978,10 @@ function TruthTrackingProvider({
515
978
  }
516
979
  };
517
980
  }, [apiKey, environment, source, sourceVersion, tenantId]);
518
- return (0, import_react5.createElement)(TruthTrackingContext.Provider, { value }, children);
981
+ return (0, import_react7.createElement)(TruthTrackingContext.Provider, { value }, children);
519
982
  }
520
983
  function useTruth() {
521
- const ctx = (0, import_react5.useContext)(TruthTrackingContext);
984
+ const ctx = (0, import_react7.useContext)(TruthTrackingContext);
522
985
  if (!ctx) {
523
986
  throw new Error("useTruth must be used within a TruthTrackingProvider");
524
987
  }
@@ -531,7 +994,11 @@ function useTruth() {
531
994
  useAppointment,
532
995
  useAppointmentByElationId,
533
996
  useAppointments,
997
+ useConversationByPhonePair,
534
998
  useConversationMessages,
999
+ useConversations,
1000
+ useMessages,
1001
+ useNotifications,
535
1002
  usePatient,
536
1003
  usePatientBasic,
537
1004
  usePatientByElationId,
@@ -542,6 +1009,7 @@ function useTruth() {
542
1009
  usePharmacyByNcpdpId,
543
1010
  usePhysicianByElationId,
544
1011
  usePhysiciansByElationIds,
545
- useTruth
1012
+ useTruth,
1013
+ useUnreadCount
546
1014
  });
547
1015
  //# sourceMappingURL=react.js.map