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