@healthcloudai/hc-patient-telehealth 0.0.2

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/index.cjs ADDED
@@ -0,0 +1,850 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // src/theme/defaultTheme.ts
34
+ var defaultTheme;
35
+ var init_defaultTheme = __esm({
36
+ "src/theme/defaultTheme.ts"() {
37
+ "use strict";
38
+ defaultTheme = {
39
+ colors: {
40
+ primary: "#003C71",
41
+ btnPrimary: "#1e90ff",
42
+ success: "#2ecc71",
43
+ danger: "#e74c3c",
44
+ darkBackground: "#000",
45
+ lightBackground: "#fff",
46
+ text: "#fff",
47
+ iconColor: "#fff",
48
+ textDark: "#000"
49
+ },
50
+ fonts: {
51
+ regular: "System",
52
+ bold: "System-Bold",
53
+ size: {
54
+ small: 14,
55
+ medium: 18,
56
+ large: 24
57
+ }
58
+ }
59
+ };
60
+ }
61
+ });
62
+
63
+ // src/services/AgoraService/index.web.ts
64
+ var index_web_exports = {};
65
+ __export(index_web_exports, {
66
+ default: () => index_web_default
67
+ });
68
+ var AgoraRTC, client, appId, localAudioTrack, localVideoTrack, webAgoraService, index_web_default;
69
+ var init_index_web = __esm({
70
+ "src/services/AgoraService/index.web.ts"() {
71
+ "use strict";
72
+ AgoraRTC = null;
73
+ client = null;
74
+ appId = "";
75
+ localAudioTrack = null;
76
+ localVideoTrack = null;
77
+ webAgoraService = {
78
+ // INIT SDK + CLIENT
79
+ async init(appIdParam) {
80
+ if (typeof window === "undefined") return;
81
+ if (!AgoraRTC) {
82
+ const mod = await import("agora-rtc-sdk-ng");
83
+ AgoraRTC = mod.default;
84
+ }
85
+ appId = appIdParam;
86
+ if (!client) {
87
+ client = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" });
88
+ console.log("WEB Agora client created");
89
+ this.setupEventListeners();
90
+ }
91
+ },
92
+ // JOIN CHANNEL
93
+ async joinChannel(tokenParam, channel, uidParam) {
94
+ if (!client) throw new Error("Client not initialized");
95
+ await client.join(appId, channel, tokenParam, uidParam);
96
+ console.log(" LOCAL JOIN SUCCESS", {
97
+ localUid: uidParam
98
+ });
99
+ await this.createLocalTracks();
100
+ await this.publishLocalTracks();
101
+ console.log(" LOCAL TRACKS PUBLISHED", {
102
+ audio: localAudioTrack,
103
+ video: localVideoTrack
104
+ });
105
+ },
106
+ // CREATE LOCAL TRACKS
107
+ async createLocalTracks() {
108
+ localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
109
+ localVideoTrack = await AgoraRTC.createCameraVideoTrack();
110
+ console.log("LOCAL TRACKS CREATED", {
111
+ uid: null
112
+ });
113
+ },
114
+ // PUBLISH LOCAL TRACKS
115
+ async publishLocalTracks() {
116
+ await client.publish([localAudioTrack, localVideoTrack]);
117
+ },
118
+ // EVENTS (REMOTE USERS)
119
+ setupEventListeners() {
120
+ client.on("user-joined", (user) => {
121
+ console.log(" REMOTE USER JOINED", {
122
+ remoteUid: user.uid
123
+ });
124
+ });
125
+ client.on("user-published", async (user, mediaType) => {
126
+ console.log(" REMOTE USER PUBLISHED", {
127
+ remoteUid: user.uid,
128
+ mediaType
129
+ });
130
+ await client.subscribe(user, mediaType);
131
+ console.log("SUBSCRIBED TO REMOTE", {
132
+ remoteUid: user.uid,
133
+ mediaType
134
+ });
135
+ });
136
+ client.on("user-unpublished", (user, mediaType) => {
137
+ console.log("REMOTE UNPUBLISHED", {
138
+ remoteUid: user.uid,
139
+ mediaType
140
+ });
141
+ });
142
+ client.on("user-left", (user) => {
143
+ console.log(" REMOTE USER LEFT", {
144
+ remoteUid: user.uid
145
+ });
146
+ });
147
+ },
148
+ // UI GETTERS
149
+ getClient() {
150
+ return client;
151
+ },
152
+ getLocalTracks() {
153
+ return {
154
+ audio: localAudioTrack,
155
+ video: localVideoTrack
156
+ };
157
+ },
158
+ // LEAVE CHANNEL
159
+ async leaveChannel() {
160
+ console.log(" LEAVING CHANNEL");
161
+ localAudioTrack == null ? void 0 : localAudioTrack.close();
162
+ localVideoTrack == null ? void 0 : localVideoTrack.close();
163
+ localAudioTrack = null;
164
+ localVideoTrack = null;
165
+ if (client) {
166
+ await client.leave();
167
+ }
168
+ console.log(" LEFT CHANNEL");
169
+ }
170
+ };
171
+ index_web_default = webAgoraService;
172
+ }
173
+ });
174
+
175
+ // src/services/AgoraService/index.native.ts
176
+ var index_native_exports = {};
177
+ __export(index_native_exports, {
178
+ default: () => index_native_default
179
+ });
180
+ var nativeAgoraService, index_native_default;
181
+ var init_index_native = __esm({
182
+ "src/services/AgoraService/index.native.ts"() {
183
+ "use strict";
184
+ nativeAgoraService = {
185
+ init: async () => {
186
+ },
187
+ joinChannel: async () => {
188
+ throw new Error("AgoraService is not available on native. Use VideoElement.native.");
189
+ },
190
+ getLocalTracks: () => ({ audio: null, video: null }),
191
+ createLocalTracks: async () => {
192
+ },
193
+ publishLocalTracks: async () => {
194
+ },
195
+ getClient: () => null,
196
+ setupEventListeners: () => {
197
+ },
198
+ leaveChannel: async () => {
199
+ }
200
+ };
201
+ index_native_default = nativeAgoraService;
202
+ }
203
+ });
204
+
205
+ // src/services/AgoraService/index.ts
206
+ var import_react_native, AgoraServiceImpl, AgoraService_default;
207
+ var init_AgoraService = __esm({
208
+ "src/services/AgoraService/index.ts"() {
209
+ "use strict";
210
+ import_react_native = require("react-native");
211
+ AgoraServiceImpl = import_react_native.Platform.OS === "web" ? (init_index_web(), __toCommonJS(index_web_exports)).default : (init_index_native(), __toCommonJS(index_native_exports)).default;
212
+ AgoraService_default = AgoraServiceImpl;
213
+ console.info("[hc-patient-telehealth] AgoraService platform", import_react_native.Platform.OS);
214
+ }
215
+ });
216
+
217
+ // src/VideoElement/VideoElement.web.tsx
218
+ var VideoElement_web_exports = {};
219
+ __export(VideoElement_web_exports, {
220
+ default: () => VideoElement
221
+ });
222
+ function VideoElement({
223
+ tokenClient,
224
+ autoJoin,
225
+ onJoinedChange,
226
+ onError
227
+ }) {
228
+ console.info("[hc-patient-telehealth] VideoElement.web render", {
229
+ hasTokenClient: !!tokenClient,
230
+ autoJoin
231
+ });
232
+ const localRef = (0, import_react.useRef)(null);
233
+ const remoteRef = (0, import_react.useRef)(null);
234
+ const [micOn, setMicOn] = (0, import_react.useState)(true);
235
+ const [cameraOn, setCameraOn] = (0, import_react.useState)(true);
236
+ const [chatOpen, setChatOpen] = (0, import_react.useState)(false);
237
+ const [joined, setJoined] = (0, import_react.useState)(false);
238
+ const join = async () => {
239
+ if (joined) return;
240
+ try {
241
+ console.info("[hc-patient-telehealth] VideoElement.web join start");
242
+ const session = await tokenClient.connectToTokenServer();
243
+ console.info("[hc-patient-telehealth] VideoElement.web session", {
244
+ hasToken: !!(session == null ? void 0 : session.token),
245
+ channelName: session == null ? void 0 : session.channelName,
246
+ uid: session == null ? void 0 : session.uid
247
+ });
248
+ await AgoraService_default.init(session.appId);
249
+ await AgoraService_default.joinChannel(session.token, session.channelName, session.uid);
250
+ const client2 = AgoraService_default.getClient();
251
+ client2.remoteUsers.forEach(async (user) => {
252
+ console.log("EXISTING REMOTE USER FOUND", {
253
+ remoteUid: user.uid,
254
+ hasVideo: !!user.videoTrack
255
+ });
256
+ if (user.hasVideo) {
257
+ await client2.subscribe(user, "video");
258
+ if (remoteRef.current) {
259
+ user.videoTrack.play(remoteRef.current);
260
+ }
261
+ }
262
+ if (user.hasAudio) {
263
+ await client2.subscribe(user, "audio");
264
+ user.audioTrack.play();
265
+ }
266
+ });
267
+ client2.on("user-published", async (user, mediaType) => {
268
+ console.log("REMOTE PUBLISHED", {
269
+ remoteUid: user.uid,
270
+ mediaType
271
+ });
272
+ await client2.subscribe(user, mediaType);
273
+ if (mediaType === "video" && remoteRef.current) {
274
+ user.videoTrack.play(remoteRef.current);
275
+ }
276
+ if (mediaType === "audio") {
277
+ user.audioTrack.play();
278
+ }
279
+ });
280
+ setJoined(true);
281
+ onJoinedChange == null ? void 0 : onJoinedChange(true);
282
+ } catch (err) {
283
+ console.error("[hc-patient-telehealth] VideoElement.web join error", err);
284
+ onError == null ? void 0 : onError(err);
285
+ }
286
+ };
287
+ (0, import_react.useEffect)(() => {
288
+ if (joined) {
289
+ const tracks = AgoraService_default.getLocalTracks();
290
+ const localVideoTrack2 = tracks == null ? void 0 : tracks.video;
291
+ const localAudioTrack2 = tracks == null ? void 0 : tracks.audio;
292
+ if (localVideoTrack2 && localRef.current) {
293
+ localVideoTrack2.play(localRef.current);
294
+ }
295
+ }
296
+ }, [joined]);
297
+ const leave = async () => {
298
+ console.info("[hc-patient-telehealth] VideoElement.web leave");
299
+ await AgoraService_default.leaveChannel();
300
+ setJoined(false);
301
+ onJoinedChange == null ? void 0 : onJoinedChange(false);
302
+ remoteRef.current && (remoteRef.current.innerHTML = "");
303
+ localRef.current && (localRef.current.innerHTML = "");
304
+ };
305
+ (0, import_react.useEffect)(() => {
306
+ return () => {
307
+ AgoraService_default.leaveChannel().catch(() => {
308
+ });
309
+ };
310
+ }, []);
311
+ const toggleMic = async () => {
312
+ const tracks = AgoraService_default.getLocalTracks();
313
+ if (!(tracks == null ? void 0 : tracks.audio)) return;
314
+ const next = !micOn;
315
+ await tracks.audio.setEnabled(next);
316
+ console.log("MIC", next ? "ON" : "OFF");
317
+ setMicOn(next);
318
+ };
319
+ const toggleCamera = async () => {
320
+ const tracks = AgoraService_default.getLocalTracks();
321
+ if (!(tracks == null ? void 0 : tracks.video)) return;
322
+ const next = !cameraOn;
323
+ await tracks.video.setEnabled(next);
324
+ console.log("CAMERA", next ? "ON" : "OFF");
325
+ setCameraOn(next);
326
+ if (!next && localRef.current) {
327
+ localRef.current.innerHTML = "";
328
+ } else if (next && localRef.current) {
329
+ tracks.video.play(localRef.current);
330
+ }
331
+ };
332
+ const toggleChat = () => {
333
+ setChatOpen((prev) => !prev);
334
+ };
335
+ (0, import_react.useEffect)(() => {
336
+ if (autoJoin) {
337
+ join().catch(() => {
338
+ });
339
+ }
340
+ }, [autoJoin]);
341
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.root, children: [
342
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: remoteRef, style: styles.remote }),
343
+ joined && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: localRef, style: styles.local, id: "local" }),
344
+ chatOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chat, children: "Chat" }),
345
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.footer, children: !joined ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
346
+ "button",
347
+ {
348
+ style: { ...styles.iconBtn, background: defaultTheme.colors.success },
349
+ onClick: join,
350
+ children: "Join"
351
+ }
352
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
353
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { style: styles.iconBtn, onClick: toggleMic, children: micOn ? "Mic On" : "Mic Off" }),
354
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { style: styles.iconBtn, onClick: toggleCamera, children: cameraOn ? "Cam On" : "Cam Off" }),
355
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { style: styles.iconBtn, onClick: toggleChat, children: "Chat" }),
356
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
357
+ "button",
358
+ {
359
+ style: { ...styles.iconBtn, background: defaultTheme.colors.danger },
360
+ onClick: leave,
361
+ children: "Leave"
362
+ }
363
+ )
364
+ ] }) }) })
365
+ ] });
366
+ }
367
+ var import_react, import_jsx_runtime, styles;
368
+ var init_VideoElement_web = __esm({
369
+ "src/VideoElement/VideoElement.web.tsx"() {
370
+ "use strict";
371
+ import_react = require("react");
372
+ init_defaultTheme();
373
+ init_AgoraService();
374
+ import_jsx_runtime = require("react/jsx-runtime");
375
+ styles = {
376
+ root: {
377
+ width: "100vw",
378
+ height: "100vh",
379
+ position: "relative",
380
+ background: defaultTheme.colors.darkBackground,
381
+ overflow: "hidden"
382
+ },
383
+ remote: {
384
+ width: "100%",
385
+ height: "100%",
386
+ background: defaultTheme.colors.darkBackground
387
+ },
388
+ iconBtn: {
389
+ width: 50,
390
+ height: 50,
391
+ borderRadius: "50%",
392
+ background: defaultTheme.colors.btnPrimary,
393
+ border: "none",
394
+ color: defaultTheme.colors.iconColor,
395
+ cursor: "pointer",
396
+ display: "flex",
397
+ alignItems: "center",
398
+ justifyContent: "center",
399
+ fontSize: 24
400
+ },
401
+ chat: {
402
+ position: "absolute",
403
+ right: 0,
404
+ left: 0,
405
+ bottom: 0,
406
+ borderRadius: 20,
407
+ top: 40,
408
+ background: defaultTheme.colors.lightBackground,
409
+ color: defaultTheme.colors.darkBackground,
410
+ padding: 16
411
+ },
412
+ local: {
413
+ width: 180,
414
+ height: 180,
415
+ position: "absolute",
416
+ top: 16,
417
+ right: 16,
418
+ borderRadius: 12,
419
+ overflow: "hidden",
420
+ background: defaultTheme.colors.darkBackground,
421
+ boxShadow: "0 0 12px rgba(0,0,0,0.6)"
422
+ },
423
+ footer: {
424
+ position: "absolute",
425
+ bottom: 24,
426
+ width: "100%",
427
+ display: "flex",
428
+ justifyContent: "center",
429
+ gap: 16
430
+ },
431
+ btn: {
432
+ width: 64,
433
+ height: 64,
434
+ borderRadius: "50%",
435
+ border: "none",
436
+ fontSize: 14,
437
+ fontWeight: 600,
438
+ cursor: "pointer"
439
+ }
440
+ };
441
+ }
442
+ });
443
+
444
+ // src/VideoElement/VideoElement.native.tsx
445
+ var VideoElement_native_exports = {};
446
+ __export(VideoElement_native_exports, {
447
+ default: () => VideoElement_native_default
448
+ });
449
+ var import_react2, import_react_native2, import_react_native_agora, import_jsx_runtime2, App, styles2, getPermission, VideoElement_native_default;
450
+ var init_VideoElement_native = __esm({
451
+ "src/VideoElement/VideoElement.native.tsx"() {
452
+ "use strict";
453
+ import_react2 = require("react");
454
+ init_defaultTheme();
455
+ import_react_native2 = require("react-native");
456
+ import_react_native_agora = require("react-native-agora");
457
+ import_jsx_runtime2 = require("react/jsx-runtime");
458
+ App = ({
459
+ tokenClient,
460
+ autoJoin,
461
+ onJoinedChange,
462
+ onError
463
+ }) => {
464
+ console.info("[hc-patient-telehealth] VideoElement.native render", {
465
+ hasTokenClient: !!tokenClient,
466
+ autoJoin
467
+ });
468
+ const agoraEngineRef = (0, import_react2.useRef)(null);
469
+ const [isJoined, setIsJoined] = (0, import_react2.useState)(false);
470
+ const [isHost, setIsHost] = (0, import_react2.useState)(true);
471
+ const [remoteUid, setRemoteUid] = (0, import_react2.useState)(0);
472
+ const [message, setMessage] = (0, import_react2.useState)("");
473
+ const eventHandler = (0, import_react2.useRef)(null);
474
+ const [appId2, setAppId] = (0, import_react2.useState)("");
475
+ const [token, setToken] = (0, import_react2.useState)("");
476
+ const [channelName, setChannelName] = (0, import_react2.useState)("");
477
+ const [localUid, setLocalUid] = (0, import_react2.useState)(0);
478
+ const [micOn, setMicOn] = (0, import_react2.useState)(true);
479
+ const [camOn, setCamOn] = (0, import_react2.useState)(true);
480
+ const [chatOpen, setChatOpen] = (0, import_react2.useState)(false);
481
+ const [remoteVideoOn, setRemoteVideoOn] = (0, import_react2.useState)(true);
482
+ const [ready, setReady] = (0, import_react2.useState)(false);
483
+ const fetchTelehealthSession = async () => {
484
+ console.info("[hc-patient-telehealth] VideoElement.native fetch session");
485
+ const data = await tokenClient.connectToTokenServer();
486
+ console.info("[hc-patient-telehealth] VideoElement.native session", {
487
+ hasToken: !!(data == null ? void 0 : data.token),
488
+ channelName: data == null ? void 0 : data.channelName,
489
+ uid: data == null ? void 0 : data.uid
490
+ });
491
+ console.log("TELEHEALTH SESSION:", data);
492
+ setAppId(data.appId);
493
+ setToken(data.token);
494
+ setChannelName(data.channelName);
495
+ setLocalUid(data.uid);
496
+ return data;
497
+ };
498
+ (0, import_react2.useEffect)(() => {
499
+ const init = async () => {
500
+ try {
501
+ const session = await fetchTelehealthSession();
502
+ await setupVideoSDKEngine(session.appId);
503
+ setupEventHandler();
504
+ setReady(true);
505
+ } catch (err) {
506
+ onError == null ? void 0 : onError(err);
507
+ }
508
+ };
509
+ init().catch(() => {
510
+ });
511
+ return () => {
512
+ cleanupAgoraEngine();
513
+ };
514
+ }, []);
515
+ const setupEventHandler = () => {
516
+ var _a;
517
+ eventHandler.current = {
518
+ onJoinChannelSuccess: () => {
519
+ setMessage("Successfully joined channel: " + channelName);
520
+ setupLocalVideo();
521
+ setIsJoined(true);
522
+ onJoinedChange == null ? void 0 : onJoinedChange(true);
523
+ },
524
+ // onUserJoined: (_connection: RtcConnection, uid: number) => {
525
+ onUserJoined: (_connection, uid) => {
526
+ setMessage("Remote user " + uid + " joined");
527
+ setRemoteUid(uid);
528
+ setRemoteVideoOn(true);
529
+ },
530
+ // onUserOffline: (_connection: RtcConnection, uid: number) => {
531
+ onUserOffline: (_connection, uid) => {
532
+ setMessage("Remote user " + uid + " left the channel");
533
+ setRemoteUid(uid);
534
+ setRemoteVideoOn(false);
535
+ },
536
+ // onUserStateChanged: (_connection, uid, state) => {
537
+ onUserStateChanged: (_connection, uid, state) => {
538
+ if (uid === remoteUid) {
539
+ setRemoteVideoOn(state !== 0);
540
+ }
541
+ }
542
+ };
543
+ (_a = agoraEngineRef.current) == null ? void 0 : _a.registerEventHandler(eventHandler.current);
544
+ };
545
+ const setupVideoSDKEngine = async (appIdParam) => {
546
+ try {
547
+ if (import_react_native2.Platform.OS === "android") {
548
+ await getPermission();
549
+ }
550
+ agoraEngineRef.current = (0, import_react_native_agora.createAgoraRtcEngine)();
551
+ const agoraEngine = agoraEngineRef.current;
552
+ await agoraEngine.initialize({ appId: appIdParam });
553
+ } catch (e) {
554
+ console.error(e);
555
+ }
556
+ };
557
+ const setupLocalVideo = () => {
558
+ var _a;
559
+ (_a = agoraEngineRef.current) == null ? void 0 : _a.enableVideo();
560
+ };
561
+ const join = async () => {
562
+ var _a, _b;
563
+ if (isJoined) {
564
+ return;
565
+ }
566
+ try {
567
+ console.info("[hc-patient-telehealth] VideoElement.native join start");
568
+ if (!token || !appId2 || !channelName || !localUid) {
569
+ const session = await fetchTelehealthSession();
570
+ if (!(session == null ? void 0 : session.token)) {
571
+ throw new Error("Telehealth session token missing");
572
+ }
573
+ }
574
+ if (isHost) {
575
+ (_a = agoraEngineRef.current) == null ? void 0 : _a.joinChannel(token, channelName, localUid, {
576
+ // Set channel profile to live broadcast
577
+ channelProfile: import_react_native_agora.ChannelProfileType.ChannelProfileCommunication,
578
+ // Set user role to broadcaster
579
+ clientRoleType: import_react_native_agora.ClientRoleType.ClientRoleBroadcaster,
580
+ // Publish audio collected by the microphone
581
+ publishMicrophoneTrack: true,
582
+ // Publish video collected by the camera
583
+ publishCameraTrack: true,
584
+ // Automatically subscribe to all audio streams
585
+ autoSubscribeAudio: true,
586
+ // Automatically subscribe to all video streams
587
+ autoSubscribeVideo: true
588
+ });
589
+ } else {
590
+ (_b = agoraEngineRef.current) == null ? void 0 : _b.joinChannel(token, channelName, localUid, {
591
+ // Set channel profile to live broadcast
592
+ channelProfile: import_react_native_agora.ChannelProfileType.ChannelProfileCommunication,
593
+ // Set user role to audience
594
+ clientRoleType: import_react_native_agora.ClientRoleType.ClientRoleAudience,
595
+ // Do not publish audio collected by the microphone
596
+ publishMicrophoneTrack: false,
597
+ // Do not publish video collected by the camera
598
+ publishCameraTrack: false,
599
+ // Automatically subscribe to all audio streams
600
+ autoSubscribeAudio: true,
601
+ // Automatically subscribe to all video streams
602
+ autoSubscribeVideo: true
603
+ });
604
+ }
605
+ } catch (e) {
606
+ console.log(e);
607
+ onError == null ? void 0 : onError(e);
608
+ }
609
+ };
610
+ const leave = () => {
611
+ var _a;
612
+ try {
613
+ console.info("[hc-patient-telehealth] VideoElement.native leave");
614
+ (_a = agoraEngineRef.current) == null ? void 0 : _a.leaveChannel();
615
+ setRemoteUid(0);
616
+ setIsJoined(false);
617
+ showMessage("Left the channel");
618
+ onJoinedChange == null ? void 0 : onJoinedChange(false);
619
+ } catch (e) {
620
+ console.log(e);
621
+ }
622
+ };
623
+ const toggleCamera = async () => {
624
+ if (!agoraEngineRef.current) return;
625
+ const nextCamState = !camOn;
626
+ console.log("CAMERA", nextCamState ? "ON" : "OFF");
627
+ try {
628
+ await agoraEngineRef.current.enableLocalVideo(nextCamState);
629
+ const options = {
630
+ publishCameraTrack: nextCamState
631
+ };
632
+ await agoraEngineRef.current.updateChannelMediaOptions(options);
633
+ setCamOn(nextCamState);
634
+ } catch (e) {
635
+ console.error("Camera toggle error:", e);
636
+ }
637
+ };
638
+ const toggleMic = async () => {
639
+ if (!agoraEngineRef.current) return;
640
+ await agoraEngineRef.current.muteLocalAudioStream(micOn);
641
+ setMicOn(!micOn);
642
+ };
643
+ const toggleChat = () => {
644
+ setChatOpen((prev) => !prev);
645
+ };
646
+ const cleanupAgoraEngine = () => {
647
+ var _a, _b;
648
+ (_a = agoraEngineRef.current) == null ? void 0 : _a.unregisterEventHandler(eventHandler.current);
649
+ (_b = agoraEngineRef.current) == null ? void 0 : _b.release();
650
+ };
651
+ (0, import_react2.useEffect)(() => {
652
+ if (autoJoin && ready) {
653
+ join().catch(() => {
654
+ });
655
+ }
656
+ }, [autoJoin, ready]);
657
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react_native2.SafeAreaView, { style: styles2.root, children: [
658
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles2.remoteContainer, children: isJoined && remoteUid !== 0 && remoteVideoOn ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
659
+ import_react_native_agora.RtcSurfaceView,
660
+ {
661
+ canvas: { uid: remoteUid, sourceType: import_react_native_agora.VideoSourceType.VideoSourceRemote },
662
+ style: styles2.remoteVideo
663
+ }
664
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles2.remotePlaceholder }) }),
665
+ isJoined && camOn && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles2.localContainer, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
666
+ import_react_native_agora.RtcSurfaceView,
667
+ {
668
+ canvas: { uid: localUid, sourceType: import_react_native_agora.VideoSourceType.VideoSourceCamera },
669
+ style: styles2.localVideo
670
+ }
671
+ ) }),
672
+ isJoined && !camOn && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: [styles2.localContainer, { backgroundColor: defaultTheme.colors.darkBackground }] }),
673
+ chatOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles2.chatPanel, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: { color: defaultTheme.colors.textDark, textAlign: "center" }, children: "Chat" }) }),
674
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: styles2.footer, children: !isJoined ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [styles2.button, styles2.join], onPress: join, children: "Join" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
675
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: styles2.iconBtn, onPress: toggleMic, children: micOn ? "Mic On" : "Mic Off" }),
676
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: styles2.iconBtn, onPress: toggleCamera, children: camOn ? "Cam On" : "Cam Off" }),
677
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
678
+ import_react_native2.Text,
679
+ {
680
+ style: [
681
+ styles2.iconBtn,
682
+ chatOpen && { backgroundColor: defaultTheme.colors.btnPrimary }
683
+ ],
684
+ onPress: toggleChat,
685
+ children: "Chat"
686
+ }
687
+ ),
688
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.Text, { style: [styles2.button, styles2.leave], onPress: leave, children: "Leave" })
689
+ ] }) })
690
+ ] });
691
+ function showMessage(msg) {
692
+ setMessage(msg);
693
+ }
694
+ };
695
+ styles2 = import_react_native2.StyleSheet.create({
696
+ root: {
697
+ flex: 1,
698
+ backgroundColor: defaultTheme.colors.darkBackground
699
+ },
700
+ /* REMOTE */
701
+ remoteContainer: {
702
+ flex: 1,
703
+ backgroundColor: defaultTheme.colors.darkBackground
704
+ },
705
+ remoteVideo: {
706
+ width: "100%",
707
+ height: "100%"
708
+ },
709
+ remotePlaceholder: {
710
+ flex: 1,
711
+ backgroundColor: defaultTheme.colors.darkBackground
712
+ },
713
+ /* LOCAL */
714
+ localContainer: {
715
+ position: "absolute",
716
+ top: 16,
717
+ right: 16,
718
+ width: 180,
719
+ height: 180,
720
+ borderRadius: 12,
721
+ overflow: "hidden",
722
+ backgroundColor: defaultTheme.colors.darkBackground,
723
+ elevation: 8
724
+ },
725
+ localVideo: {
726
+ width: "100%",
727
+ height: "100%"
728
+ },
729
+ /* FOOTER */
730
+ footer: {
731
+ position: "absolute",
732
+ bottom: 24,
733
+ width: "100%",
734
+ flexDirection: "row",
735
+ justifyContent: "space-evenly",
736
+ paddingVertical: 40,
737
+ paddingHorizontal: 30
738
+ },
739
+ button: {
740
+ width: 50,
741
+ height: 50,
742
+ borderRadius: 36,
743
+ textAlign: "center",
744
+ textAlignVertical: "center",
745
+ color: defaultTheme.colors.text,
746
+ fontWeight: "600",
747
+ fontSize: 14,
748
+ overflow: "hidden"
749
+ },
750
+ join: {
751
+ backgroundColor: defaultTheme.colors.success
752
+ },
753
+ leave: {
754
+ backgroundColor: defaultTheme.colors.danger
755
+ },
756
+ iconBtn: {
757
+ width: 50,
758
+ height: 50,
759
+ borderRadius: 25,
760
+ backgroundColor: defaultTheme.colors.btnPrimary,
761
+ marginHorizontal: 10,
762
+ textAlign: "center",
763
+ textAlignVertical: "center"
764
+ },
765
+ chatPanel: {
766
+ position: "absolute",
767
+ right: 0,
768
+ top: 30,
769
+ bottom: 0,
770
+ width: "100%",
771
+ backgroundColor: defaultTheme.colors.lightBackground,
772
+ padding: 16,
773
+ borderRadius: 20,
774
+ elevation: 12
775
+ }
776
+ });
777
+ getPermission = async () => {
778
+ if (import_react_native2.Platform.OS === "android") {
779
+ await import_react_native2.PermissionsAndroid.requestMultiple([
780
+ import_react_native2.PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
781
+ import_react_native2.PermissionsAndroid.PERMISSIONS.CAMERA
782
+ ]);
783
+ }
784
+ };
785
+ VideoElement_native_default = App;
786
+ }
787
+ });
788
+
789
+ // src/index.ts
790
+ var index_exports = {};
791
+ __export(index_exports, {
792
+ AuthError: () => AuthError,
793
+ ConfigError: () => ConfigError,
794
+ HCPatientTelehealth: () => HCPatientTelehealth,
795
+ HttpError: () => HttpError,
796
+ VideoElement: () => VideoElement_default,
797
+ default: () => index_default
798
+ });
799
+ module.exports = __toCommonJS(index_exports);
800
+
801
+ // src/PatientTelehealth/VideoRoom.tsx
802
+ var import_react_native4 = require("react-native");
803
+
804
+ // src/VideoElement/VideoElement.tsx
805
+ var import_react_native3 = require("react-native");
806
+ var VideoElement2 = import_react_native3.Platform.OS === "web" ? (init_VideoElement_web(), __toCommonJS(VideoElement_web_exports)).default : (init_VideoElement_native(), __toCommonJS(VideoElement_native_exports)).default;
807
+ var VideoElement_default = VideoElement2;
808
+
809
+ // src/PatientTelehealth/VideoRoom.tsx
810
+ var import_jsx_runtime3 = require("react/jsx-runtime");
811
+ function HCPatientTelehealth(props) {
812
+ console.info("[hc-patient-telehealth] HCPatientTelehealth render", {
813
+ hasTokenClient: !!(props == null ? void 0 : props.tokenClient),
814
+ autoJoin: props == null ? void 0 : props.autoJoin
815
+ });
816
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native4.View, { style: { flex: 1, backgroundColor: "black" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(VideoElement_default, { ...props }) });
817
+ }
818
+
819
+ // src/errors.ts
820
+ var ConfigError = class extends Error {
821
+ constructor(message) {
822
+ super(message);
823
+ this.name = "ConfigError";
824
+ }
825
+ };
826
+ var AuthError = class extends Error {
827
+ constructor(message) {
828
+ super(message);
829
+ this.name = "AuthError";
830
+ }
831
+ };
832
+ var HttpError = class extends Error {
833
+ constructor(status, message) {
834
+ super(message);
835
+ this.name = "HttpError";
836
+ this.status = status;
837
+ }
838
+ };
839
+
840
+ // src/index.ts
841
+ var index_default = HCPatientTelehealth;
842
+ console.info("[hc-patient-telehealth] package entry loaded");
843
+ // Annotate the CommonJS export names for ESM import in node:
844
+ 0 && (module.exports = {
845
+ AuthError,
846
+ ConfigError,
847
+ HCPatientTelehealth,
848
+ HttpError,
849
+ VideoElement
850
+ });