tas-uell-sdk 0.0.1 → 0.0.3

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.
Files changed (31) hide show
  1. package/INSTALL_AND_TEST.md +427 -0
  2. package/README.md +122 -229
  3. package/esm2020/lib/components/tas-btn/tas-btn.component.mjs +79 -95
  4. package/esm2020/lib/components/tas-floating-call/tas-floating-call.component.mjs +74 -24
  5. package/esm2020/lib/components/tas-videocall/tas-videocall.component.mjs +50 -45
  6. package/esm2020/lib/components/tas-waiting-room/tas-waiting-room.component.mjs +179 -0
  7. package/esm2020/lib/config/tas.config.mjs +10 -0
  8. package/esm2020/lib/interfaces/tas.interfaces.mjs +32 -2
  9. package/esm2020/lib/services/tas.service.mjs +27 -48
  10. package/esm2020/lib/tas-uell-sdk.module.mjs +82 -0
  11. package/esm2020/public-api.mjs +13 -12
  12. package/esm2020/tas-uell-sdk.mjs +1 -1
  13. package/fesm2015/tas-uell-sdk.mjs +440 -218
  14. package/fesm2015/tas-uell-sdk.mjs.map +1 -1
  15. package/fesm2020/tas-uell-sdk.mjs +437 -216
  16. package/fesm2020/tas-uell-sdk.mjs.map +1 -1
  17. package/lib/components/tas-btn/tas-btn.component.d.ts +15 -11
  18. package/lib/components/tas-floating-call/tas-floating-call.component.d.ts +3 -2
  19. package/lib/components/tas-videocall/tas-videocall.component.d.ts +4 -3
  20. package/lib/components/tas-waiting-room/tas-waiting-room.component.d.ts +56 -0
  21. package/lib/config/tas.config.d.ts +28 -0
  22. package/lib/interfaces/tas.interfaces.d.ts +41 -6
  23. package/lib/services/tas.service.d.ts +6 -16
  24. package/lib/tas-uell-sdk.module.d.ts +46 -0
  25. package/package.json +36 -23
  26. package/public-api.d.ts +8 -7
  27. package/src/lib/styles/tas-global.scss +37 -0
  28. package/esm2020/lib/tas.config.mjs +0 -14
  29. package/esm2020/lib/tas.module.mjs +0 -73
  30. package/lib/tas.config.d.ts +0 -51
  31. package/lib/tas.module.d.ts +0 -34
@@ -1,27 +1,40 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, Injectable, Optional, Inject, Component, Input, ViewChild, NgModule } from '@angular/core';
3
- import * as i3 from '@angular/common';
4
- import { CommonModule } from '@angular/common';
5
- import * as i1 from '@ng-bootstrap/ng-bootstrap';
6
- import { NgbModalModule } from '@ng-bootstrap/ng-bootstrap';
2
+ import { InjectionToken, Injectable, Inject, Component, Input, ViewChild, NgModule } from '@angular/core';
7
3
  import { BehaviorSubject, Subscription } from 'rxjs';
8
4
  import { map, catchError, switchMap } from 'rxjs/operators';
9
5
  import * as OT from '@opentok/client';
10
6
  import interact from 'interactjs';
7
+ import * as i1 from '@ng-bootstrap/ng-bootstrap';
8
+ import * as i3 from '@angular/common';
9
+ import { CommonModule } from '@angular/common';
11
10
 
12
11
  /**
13
12
  * Injection token for TAS configuration
14
13
  */
15
- const TAS_CONFIG = new InjectionToken("TAS_CONFIG");
16
- /**
17
- * Injection token for HTTP client
18
- */
19
- const TAS_HTTP_CLIENT = new InjectionToken("TAS_HTTP_CLIENT");
14
+ const TAS_CONFIG = new InjectionToken('TAS_CONFIG');
20
15
  /**
21
- * Injection token for user data provider
16
+ * Injection token for HTTP client adapter
22
17
  */
23
- const TAS_USER_DATA_PROVIDER = new InjectionToken("TAS_USER_DATA_PROVIDER");
18
+ const TAS_HTTP_CLIENT = new InjectionToken('TAS_HTTP_CLIENT');
24
19
 
20
+ var TasRoomType;
21
+ (function (TasRoomType) {
22
+ TasRoomType["TAS"] = "TAS";
23
+ TasRoomType["JM"] = "JM";
24
+ TasRoomType["WEBINAR"] = "WEBINAR";
25
+ })(TasRoomType || (TasRoomType = {}));
26
+ var TasSessionType;
27
+ (function (TasSessionType) {
28
+ TasSessionType["SPONTANEOUS"] = "SPONTANEOUS";
29
+ TasSessionType["SCHEDULED"] = "SCHEDULED";
30
+ })(TasSessionType || (TasSessionType = {}));
31
+ var TasUserRole;
32
+ (function (TasUserRole) {
33
+ TasUserRole["OWNER"] = "OWNER";
34
+ TasUserRole["USER"] = "USER";
35
+ TasUserRole["MODERATOR"] = "MODERATOR";
36
+ })(TasUserRole || (TasUserRole = {}));
37
+ // Enums for TAS Service state management
25
38
  var CallState;
26
39
  (function (CallState) {
27
40
  CallState["IDLE"] = "IDLE";
@@ -35,10 +48,11 @@ var ViewMode;
35
48
  ViewMode["FULLSCREEN"] = "FULLSCREEN";
36
49
  ViewMode["PIP"] = "PIP";
37
50
  })(ViewMode || (ViewMode = {}));
51
+
38
52
  class TasService {
39
- constructor(config, httpClient) {
40
- this.config = config;
53
+ constructor(httpClient, config) {
41
54
  this.httpClient = httpClient;
55
+ this.config = config;
42
56
  this.session = null;
43
57
  this.publisher = null;
44
58
  this.subscribers = [];
@@ -51,9 +65,6 @@ class TasService {
51
65
  // Session info for PiP mode restoration
52
66
  this.currentSessionId = null;
53
67
  this.currentToken = null;
54
- if (!this.config || !this.httpClient) {
55
- console.warn("TasService: Configuration not provided. Make sure to use TasModule.forRoot()");
56
- }
57
68
  }
58
69
  // Getters
59
70
  get currentSession() {
@@ -89,9 +100,17 @@ class TasService {
89
100
  }
90
101
  // Audio Control
91
102
  toggleMute() {
103
+ var _a, _b;
92
104
  if (this.publisher) {
93
105
  const newMuteState = !this.isMutedSubject.getValue();
94
- this.publisher.publishAudio(!newMuteState);
106
+ const shouldEnableAudio = !newMuteState;
107
+ // Use OpenTok's publishAudio
108
+ this.publisher.publishAudio(shouldEnableAudio);
109
+ // Also directly control the MediaStreamTrack as backup
110
+ const audioTrack = (_b = (_a = this.publisher).getAudioSource) === null || _b === void 0 ? void 0 : _b.call(_a);
111
+ if (audioTrack) {
112
+ audioTrack.enabled = shouldEnableAudio;
113
+ }
95
114
  this.isMutedSubject.next(newMuteState);
96
115
  }
97
116
  }
@@ -111,7 +130,7 @@ class TasService {
111
130
  this.subscribers = [];
112
131
  this.currentSessionId = null;
113
132
  this.currentToken = null;
114
- this.isMutedSubject.next(false);
133
+ this.isMutedSubject.next(false); // Reset mute state
115
134
  this.viewModeSubject.next(ViewMode.FULLSCREEN);
116
135
  this.callStateSubject.next(CallState.DISCONNECTED);
117
136
  }
@@ -120,21 +139,13 @@ class TasService {
120
139
  }
121
140
  // API Methods
122
141
  createRoom(payload) {
123
- if (!this.config || !this.httpClient) {
124
- throw new Error("TasService not configured. Use TasModule.forRoot()");
125
- }
126
- const url = `${this.config.apiBaseUrl}/v2/room`;
127
- return this.httpClient.post(url, payload).pipe(map((response) => response), catchError((error) => {
142
+ return this.httpClient.post("v2/room", { body: payload, headers: {} }).pipe(map((response) => response), catchError((error) => {
128
143
  console.error("TAS Service: createRoom failed", error);
129
144
  throw error;
130
145
  }));
131
146
  }
132
147
  generateToken(payload) {
133
- if (!this.config || !this.httpClient) {
134
- throw new Error("TasService not configured. Use TasModule.forRoot()");
135
- }
136
- const url = `${this.config.apiBaseUrl}/v2/room/token`;
137
- return this.httpClient.post(url, payload).pipe(map((response) => response), catchError((error) => {
148
+ return this.httpClient.post("v2/room/token", { body: payload, headers: {} }).pipe(map((response) => response), catchError((error) => {
138
149
  console.error("TAS Service: generateToken failed", error);
139
150
  throw error;
140
151
  }));
@@ -147,11 +158,6 @@ class TasService {
147
158
  this.currentSessionId = sessionId;
148
159
  this.currentToken = token;
149
160
  return new Promise((resolve, reject) => {
150
- if (!this.config) {
151
- this.callStateSubject.next(CallState.ERROR);
152
- reject(new Error("TasService not configured. Use TasModule.forRoot()"));
153
- return;
154
- }
155
161
  if (!OT.checkSystemRequirements()) {
156
162
  this.callStateSubject.next(CallState.ERROR);
157
163
  reject(new Error("Browser not compatible with TokBox"));
@@ -263,26 +269,24 @@ class TasService {
263
269
  * Moves videos back to fullscreen containers
264
270
  */
265
271
  moveVideosToFullscreen() {
266
- this.moveSubscribersTo("subscriber-container");
267
- this.movePublisherTo("publisher-container");
272
+ this.moveSubscribersTo('subscriber-container');
273
+ this.movePublisherTo('publisher-container');
268
274
  }
269
275
  }
270
- TasService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasService, deps: [{ token: TAS_CONFIG, optional: true }, { token: TAS_HTTP_CLIENT, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
276
+ TasService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasService, deps: [{ token: TAS_HTTP_CLIENT }, { token: TAS_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable });
271
277
  TasService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasService, providedIn: "root" });
272
278
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasService, decorators: [{
273
279
  type: Injectable,
274
- args: [{ providedIn: "root" }]
280
+ args: [{
281
+ providedIn: "root",
282
+ }]
275
283
  }], ctorParameters: function () {
276
284
  return [{ type: undefined, decorators: [{
277
- type: Optional
278
- }, {
279
285
  type: Inject,
280
- args: [TAS_CONFIG]
286
+ args: [TAS_HTTP_CLIENT]
281
287
  }] }, { type: undefined, decorators: [{
282
- type: Optional
283
- }, {
284
288
  type: Inject,
285
- args: [TAS_HTTP_CLIENT]
289
+ args: [TAS_CONFIG]
286
290
  }] }];
287
291
  } });
288
292
 
@@ -305,10 +309,11 @@ class TasVideocallComponent {
305
309
  }
306
310
  ngOnDestroy() {
307
311
  this.subscriptions.unsubscribe();
312
+ // Only disconnect if not in PiP mode (keep session alive for floating window)
308
313
  if (!this.tasService.isPipMode()) {
309
314
  this.tasService.disconnectSession();
310
315
  }
311
- interact(".publisher-view").unset();
316
+ interact('.publisher-view').unset();
312
317
  }
313
318
  // Public Methods
314
319
  hangUp() {
@@ -318,7 +323,8 @@ class TasVideocallComponent {
318
323
  this.tasService.toggleMute();
319
324
  }
320
325
  minimize() {
321
- this.tasService.moveMainVideoTo("pip-main-video");
326
+ this.tasService.moveMainVideoTo('pip-main-video');
327
+ // Small delay to ensure video is moved before closing modal
322
328
  setTimeout(() => this.tasService.enterPipMode(), 50);
323
329
  }
324
330
  toggleSwap() {
@@ -331,30 +337,33 @@ class TasVideocallComponent {
331
337
  }
332
338
  // Private Methods
333
339
  setupSubscriptions() {
334
- this.subscriptions.add(this.tasService.callState$.subscribe((state) => {
340
+ // Call state subscription
341
+ this.subscriptions.add(this.tasService.callState$.subscribe(state => {
335
342
  this.callState = state;
336
343
  if (state === CallState.DISCONNECTED) {
337
- this.activeModal.close("hangup");
344
+ this.activeModal.close('hangup');
338
345
  }
339
346
  }));
340
- this.subscriptions.add(this.tasService.viewMode$.subscribe((mode) => {
347
+ // View mode subscription
348
+ this.subscriptions.add(this.tasService.viewMode$.subscribe(mode => {
341
349
  if (mode === ViewMode.PIP) {
342
- this.activeModal.close("pip");
350
+ this.activeModal.close('pip');
343
351
  }
344
352
  }));
345
- this.subscriptions.add(this.tasService.isMuted$.subscribe((muted) => {
353
+ // Mute state subscription
354
+ this.subscriptions.add(this.tasService.isMuted$.subscribe(muted => {
346
355
  this.isMuted = muted;
347
356
  }));
348
357
  }
349
358
  initializeCall() {
350
359
  if (this.isReturningFromPip) {
360
+ // Returning from PiP - just move videos back
351
361
  setTimeout(() => this.tasService.moveVideosToFullscreen(), 100);
352
362
  }
353
363
  else if (this.sessionId && this.token) {
354
- this.tasService
355
- .connectSession(this.sessionId, this.token, "publisher-container", "subscriber-container")
356
- .catch((err) => {
357
- console.error("Error connecting to video call:", err);
364
+ // New call - connect to session
365
+ this.tasService.connectSession(this.sessionId, this.token, 'publisher-container', 'subscriber-container').catch(err => {
366
+ console.error('Error connecting to video call:', err);
358
367
  });
359
368
  }
360
369
  }
@@ -363,69 +372,69 @@ class TasVideocallComponent {
363
372
  const publisherEl = (_a = this.publisherContainer) === null || _a === void 0 ? void 0 : _a.nativeElement;
364
373
  const subscriberEl = (_b = this.subscriberContainer) === null || _b === void 0 ? void 0 : _b.nativeElement;
365
374
  if (publisherEl) {
366
- publisherEl.removeAttribute("style");
367
- publisherEl.removeAttribute("data-x");
368
- publisherEl.removeAttribute("data-y");
375
+ publisherEl.removeAttribute('style');
376
+ publisherEl.removeAttribute('data-x');
377
+ publisherEl.removeAttribute('data-y');
369
378
  }
370
379
  if (subscriberEl) {
371
- subscriberEl.removeAttribute("style");
372
- subscriberEl.removeAttribute("data-x");
373
- subscriberEl.removeAttribute("data-y");
380
+ subscriberEl.removeAttribute('style');
381
+ subscriberEl.removeAttribute('data-x');
382
+ subscriberEl.removeAttribute('data-y');
374
383
  }
375
384
  }
376
385
  initInteract() {
377
- interact(".publisher-view").unset();
378
- interact(".publisher-view")
386
+ interact('.publisher-view').unset();
387
+ interact('.publisher-view')
379
388
  .draggable({
380
389
  inertia: true,
381
390
  modifiers: [
382
391
  interact.modifiers.restrictRect({
383
- restriction: "parent",
384
- endOnly: true,
385
- }),
392
+ restriction: 'parent',
393
+ endOnly: true
394
+ })
386
395
  ],
387
396
  autoScroll: true,
388
397
  listeners: {
389
398
  move: (event) => {
390
399
  const target = event.target;
391
- const x = (parseFloat(target.getAttribute("data-x")) || 0) + event.dx;
392
- const y = (parseFloat(target.getAttribute("data-y")) || 0) + event.dy;
400
+ const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
401
+ const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
393
402
  target.style.transform = `translate(${x}px, ${y}px)`;
394
- target.setAttribute("data-x", String(x));
395
- target.setAttribute("data-y", String(y));
396
- },
397
- },
403
+ target.setAttribute('data-x', String(x));
404
+ target.setAttribute('data-y', String(y));
405
+ }
406
+ }
398
407
  })
399
408
  .resizable({
400
409
  edges: { left: false, right: true, bottom: true, top: false },
401
410
  listeners: {
402
411
  move: (event) => {
403
412
  const target = event.target;
404
- let x = parseFloat(target.getAttribute("data-x")) || 0;
405
- let y = parseFloat(target.getAttribute("data-y")) || 0;
413
+ let x = parseFloat(target.getAttribute('data-x')) || 0;
414
+ let y = parseFloat(target.getAttribute('data-y')) || 0;
406
415
  target.style.width = `${event.rect.width}px`;
407
416
  target.style.height = `${event.rect.height}px`;
408
417
  x += event.deltaRect.left;
409
418
  y += event.deltaRect.top;
410
419
  target.style.transform = `translate(${x}px, ${y}px)`;
411
- target.setAttribute("data-x", String(x));
412
- target.setAttribute("data-y", String(y));
413
- },
420
+ target.setAttribute('data-x', String(x));
421
+ target.setAttribute('data-y', String(y));
422
+ }
414
423
  },
415
424
  modifiers: [
416
- interact.modifiers.restrictEdges({ outer: "parent" }),
425
+ interact.modifiers.restrictEdges({ outer: 'parent' }),
417
426
  interact.modifiers.restrictSize({ min: { width: 150, height: 100 } }),
418
- interact.modifiers.aspectRatio({ ratio: "preserve" }),
427
+ interact.modifiers.aspectRatio({ ratio: 'preserve' })
419
428
  ],
420
- inertia: true,
429
+ inertia: true
421
430
  });
422
431
  }
423
432
  }
424
433
  TasVideocallComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasVideocallComponent, deps: [{ token: i1.NgbActiveModal }, { token: TasService }], target: i0.ɵɵFactoryTarget.Component });
425
- TasVideocallComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: TasVideocallComponent, selector: "tas-videocall", inputs: { sessionId: "sessionId", token: "token", isReturningFromPip: "isReturningFromPip" }, viewQueries: [{ propertyName: "publisherContainer", first: true, predicate: ["publisherContainer"], descendants: true }, { propertyName: "subscriberContainer", first: true, predicate: ["subscriberContainer"], descendants: true }], ngImport: i0, template: "<div class=\"tas-videocall-container\">\n <div\n id=\"subscriber-container\"\n [class.subscriber-view]=\"isPublisherSmall\"\n [class.publisher-view]=\"!isPublisherSmall\"\n #subscriberContainer\n (dblclick)=\"onDoubleClick()\"\n ></div>\n\n <div\n id=\"publisher-container\"\n [class.publisher-view]=\"isPublisherSmall\"\n [class.subscriber-view]=\"!isPublisherSmall\"\n #publisherContainer\n (dblclick)=\"onDoubleClick()\"\n ></div>\n\n <div class=\"controls-container\">\n <button class=\"btn swap-btn\" (click)=\"toggleSwap()\" title=\"Cambiar vista\">\n <i class=\"fa fa-refresh\"></i>\n </button>\n <button\n class=\"btn pip-btn\"\n (click)=\"minimize()\"\n title=\"Minimizar (Picture in Picture)\"\n >\n <i class=\"fa fa-compress\"></i>\n </button>\n <button\n class=\"btn mute-btn\"\n [class.muted]=\"isMuted\"\n (click)=\"toggleMute()\"\n [title]=\"isMuted ? 'Activar micr\u00F3fono' : 'Silenciar micr\u00F3fono'\"\n >\n <i\n class=\"fa\"\n [class.fa-microphone]=\"!isMuted\"\n [class.fa-microphone-slash]=\"isMuted\"\n ></i>\n </button>\n <button class=\"btn hangup-btn\" (click)=\"hangUp()\" title=\"Colgar\">\n <i class=\"fa fa-phone\" style=\"transform: rotate(135deg)\"></i>\n </button>\n </div>\n</div>\n", styles: [".tas-videocall-container{position:relative;width:100vw;height:100vh;background-color:#000;overflow:hidden}.tas-videocall-container ::ng-deep .OT_edge-bar-item,.tas-videocall-container ::ng-deep .OT_mute,.tas-videocall-container ::ng-deep .OT_audio-level-meter,.tas-videocall-container ::ng-deep .OT_bar,.tas-videocall-container ::ng-deep .OT_name{display:none!important}.tas-videocall-container .subscriber-view{width:100%;height:100%;z-index:1}.tas-videocall-container .publisher-view{position:absolute;top:20px;right:20px;width:200px;height:150px;z-index:2;border:2px solid #fff;border-radius:8px;background-color:#333}.tas-videocall-container .controls-container{display:flex;flex-direction:row;gap:20px;position:absolute;bottom:30px;left:50%;transform:translate(-50%);z-index:3;background-color:#00000080;padding:15px 25px;border-radius:50px;backdrop-filter:blur(5px)}.tas-videocall-container .controls-container .hangup-btn,.tas-videocall-container .controls-container .swap-btn,.tas-videocall-container .controls-container .pip-btn,.tas-videocall-container .controls-container .mute-btn{width:60px;height:60px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:24px;border:none;box-shadow:0 4px 6px #0000004d;transition:all .2s ease}.tas-videocall-container .controls-container .hangup-btn i,.tas-videocall-container .controls-container .swap-btn i,.tas-videocall-container .controls-container .pip-btn i,.tas-videocall-container .controls-container .mute-btn i{color:#fff}.tas-videocall-container .controls-container .hangup-btn{background:#dc3545}.tas-videocall-container .controls-container .hangup-btn:hover{background:#c82333;transform:scale(1.05)}.tas-videocall-container .controls-container .swap-btn{background:rgba(255,255,255,.2)}.tas-videocall-container .controls-container .swap-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.05)}.tas-videocall-container .controls-container .pip-btn{background:rgba(255,255,255,.2)}.tas-videocall-container .controls-container .pip-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.05)}.tas-videocall-container .controls-container .mute-btn{background:rgba(255,255,255,.2)}.tas-videocall-container .controls-container .mute-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.05)}.tas-videocall-container .controls-container .mute-btn.muted{background:#f39c12}.tas-videocall-container .controls-container .mute-btn.muted:hover{background:#e67e22}\n"] });
434
+ TasVideocallComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: TasVideocallComponent, selector: "tas-videocall", inputs: { sessionId: "sessionId", token: "token", isReturningFromPip: "isReturningFromPip" }, viewQueries: [{ propertyName: "publisherContainer", first: true, predicate: ["publisherContainer"], descendants: true }, { propertyName: "subscriberContainer", first: true, predicate: ["subscriberContainer"], descendants: true }], ngImport: i0, template: "<div class=\"tas-videocall-container\">\n\t<div id=\"subscriber-container\" \n\t\t[class.subscriber-view]=\"isPublisherSmall\" \n\t\t[class.publisher-view]=\"!isPublisherSmall\"\n\t\t#subscriberContainer\n\t\t(dblclick)=\"onDoubleClick()\">\n\t</div>\n\n\t<div id=\"publisher-container\" \n\t\t[class.publisher-view]=\"isPublisherSmall\" \n\t\t[class.subscriber-view]=\"!isPublisherSmall\"\n\t\t#publisherContainer \n\t\t(dblclick)=\"onDoubleClick()\">\n\t</div>\n\t\n\t<div class=\"controls-container\">\n\t\t<button class=\"btn swap-btn\" (click)=\"toggleSwap()\" title=\"Swap view\">\n\t\t\t<i class=\"fa fa-refresh\"></i>\n\t\t</button>\n\t\t<button class=\"btn pip-btn\" (click)=\"minimize()\" title=\"Minimize (Picture in Picture)\">\n\t\t\t<i class=\"fa fa-compress\"></i>\n\t\t</button>\n\t\t<button class=\"btn mute-btn\" [class.muted]=\"isMuted\" (click)=\"toggleMute()\" [title]=\"isMuted ? 'Unmute microphone' : 'Mute microphone'\">\n\t\t\t<i class=\"fa\" [class.fa-microphone]=\"!isMuted\" [class.fa-microphone-slash]=\"isMuted\"></i>\n\t\t</button>\n\t\t<button class=\"btn hangup-btn\" (click)=\"hangUp()\" title=\"Hang up\">\n\t\t\t<i class=\"fa fa-phone\" style=\"transform: rotate(135deg);\"></i>\n\t\t</button>\n\t</div>\n</div>\n\n", styles: [".tas-videocall-container{position:relative;width:100vw;height:100vh;background-color:#000;overflow:hidden}.tas-videocall-container ::ng-deep .OT_edge-bar-item,.tas-videocall-container ::ng-deep .OT_mute,.tas-videocall-container ::ng-deep .OT_audio-level-meter,.tas-videocall-container ::ng-deep .OT_bar,.tas-videocall-container ::ng-deep .OT_name{display:none!important}.tas-videocall-container .subscriber-view{width:100%;height:100%;z-index:1}.tas-videocall-container .publisher-view{position:absolute;top:20px;right:20px;width:200px;height:150px;z-index:2;border:2px solid #fff;border-radius:8px;background-color:#333}.tas-videocall-container .controls-container{display:flex;flex-direction:row;gap:20px;position:absolute;bottom:30px;left:50%;transform:translate(-50%);z-index:3;background-color:#00000080;padding:15px 25px;border-radius:50px;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.tas-videocall-container .controls-container .hangup-btn,.tas-videocall-container .controls-container .swap-btn,.tas-videocall-container .controls-container .pip-btn,.tas-videocall-container .controls-container .mute-btn{width:60px;height:60px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:24px;border:none;box-shadow:0 4px 6px #0000004d;transition:all .2s ease}.tas-videocall-container .controls-container .hangup-btn i,.tas-videocall-container .controls-container .swap-btn i,.tas-videocall-container .controls-container .pip-btn i,.tas-videocall-container .controls-container .mute-btn i{color:#fff}.tas-videocall-container .controls-container .hangup-btn{background:#dc3545}.tas-videocall-container .controls-container .hangup-btn:hover{background:#c82333;transform:scale(1.05)}.tas-videocall-container .controls-container .swap-btn{background:rgba(255,255,255,.2)}.tas-videocall-container .controls-container .swap-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.05)}.tas-videocall-container .controls-container .pip-btn{background:rgba(255,255,255,.2)}.tas-videocall-container .controls-container .pip-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.05)}.tas-videocall-container .controls-container .mute-btn{background:rgba(255,255,255,.2)}.tas-videocall-container .controls-container .mute-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.05)}.tas-videocall-container .controls-container .mute-btn.muted{background:#f39c12}.tas-videocall-container .controls-container .mute-btn.muted:hover{background:#e67e22}\n"] });
426
435
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasVideocallComponent, decorators: [{
427
436
  type: Component,
428
- args: [{ selector: "tas-videocall", template: "<div class=\"tas-videocall-container\">\n <div\n id=\"subscriber-container\"\n [class.subscriber-view]=\"isPublisherSmall\"\n [class.publisher-view]=\"!isPublisherSmall\"\n #subscriberContainer\n (dblclick)=\"onDoubleClick()\"\n ></div>\n\n <div\n id=\"publisher-container\"\n [class.publisher-view]=\"isPublisherSmall\"\n [class.subscriber-view]=\"!isPublisherSmall\"\n #publisherContainer\n (dblclick)=\"onDoubleClick()\"\n ></div>\n\n <div class=\"controls-container\">\n <button class=\"btn swap-btn\" (click)=\"toggleSwap()\" title=\"Cambiar vista\">\n <i class=\"fa fa-refresh\"></i>\n </button>\n <button\n class=\"btn pip-btn\"\n (click)=\"minimize()\"\n title=\"Minimizar (Picture in Picture)\"\n >\n <i class=\"fa fa-compress\"></i>\n </button>\n <button\n class=\"btn mute-btn\"\n [class.muted]=\"isMuted\"\n (click)=\"toggleMute()\"\n [title]=\"isMuted ? 'Activar micr\u00F3fono' : 'Silenciar micr\u00F3fono'\"\n >\n <i\n class=\"fa\"\n [class.fa-microphone]=\"!isMuted\"\n [class.fa-microphone-slash]=\"isMuted\"\n ></i>\n </button>\n <button class=\"btn hangup-btn\" (click)=\"hangUp()\" title=\"Colgar\">\n <i class=\"fa fa-phone\" style=\"transform: rotate(135deg)\"></i>\n </button>\n </div>\n</div>\n", styles: [".tas-videocall-container{position:relative;width:100vw;height:100vh;background-color:#000;overflow:hidden}.tas-videocall-container ::ng-deep .OT_edge-bar-item,.tas-videocall-container ::ng-deep .OT_mute,.tas-videocall-container ::ng-deep .OT_audio-level-meter,.tas-videocall-container ::ng-deep .OT_bar,.tas-videocall-container ::ng-deep .OT_name{display:none!important}.tas-videocall-container .subscriber-view{width:100%;height:100%;z-index:1}.tas-videocall-container .publisher-view{position:absolute;top:20px;right:20px;width:200px;height:150px;z-index:2;border:2px solid #fff;border-radius:8px;background-color:#333}.tas-videocall-container .controls-container{display:flex;flex-direction:row;gap:20px;position:absolute;bottom:30px;left:50%;transform:translate(-50%);z-index:3;background-color:#00000080;padding:15px 25px;border-radius:50px;backdrop-filter:blur(5px)}.tas-videocall-container .controls-container .hangup-btn,.tas-videocall-container .controls-container .swap-btn,.tas-videocall-container .controls-container .pip-btn,.tas-videocall-container .controls-container .mute-btn{width:60px;height:60px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:24px;border:none;box-shadow:0 4px 6px #0000004d;transition:all .2s ease}.tas-videocall-container .controls-container .hangup-btn i,.tas-videocall-container .controls-container .swap-btn i,.tas-videocall-container .controls-container .pip-btn i,.tas-videocall-container .controls-container .mute-btn i{color:#fff}.tas-videocall-container .controls-container .hangup-btn{background:#dc3545}.tas-videocall-container .controls-container .hangup-btn:hover{background:#c82333;transform:scale(1.05)}.tas-videocall-container .controls-container .swap-btn{background:rgba(255,255,255,.2)}.tas-videocall-container .controls-container .swap-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.05)}.tas-videocall-container .controls-container .pip-btn{background:rgba(255,255,255,.2)}.tas-videocall-container .controls-container .pip-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.05)}.tas-videocall-container .controls-container .mute-btn{background:rgba(255,255,255,.2)}.tas-videocall-container .controls-container .mute-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.05)}.tas-videocall-container .controls-container .mute-btn.muted{background:#f39c12}.tas-videocall-container .controls-container .mute-btn.muted:hover{background:#e67e22}\n"] }]
437
+ args: [{ selector: 'tas-videocall', template: "<div class=\"tas-videocall-container\">\n\t<div id=\"subscriber-container\" \n\t\t[class.subscriber-view]=\"isPublisherSmall\" \n\t\t[class.publisher-view]=\"!isPublisherSmall\"\n\t\t#subscriberContainer\n\t\t(dblclick)=\"onDoubleClick()\">\n\t</div>\n\n\t<div id=\"publisher-container\" \n\t\t[class.publisher-view]=\"isPublisherSmall\" \n\t\t[class.subscriber-view]=\"!isPublisherSmall\"\n\t\t#publisherContainer \n\t\t(dblclick)=\"onDoubleClick()\">\n\t</div>\n\t\n\t<div class=\"controls-container\">\n\t\t<button class=\"btn swap-btn\" (click)=\"toggleSwap()\" title=\"Swap view\">\n\t\t\t<i class=\"fa fa-refresh\"></i>\n\t\t</button>\n\t\t<button class=\"btn pip-btn\" (click)=\"minimize()\" title=\"Minimize (Picture in Picture)\">\n\t\t\t<i class=\"fa fa-compress\"></i>\n\t\t</button>\n\t\t<button class=\"btn mute-btn\" [class.muted]=\"isMuted\" (click)=\"toggleMute()\" [title]=\"isMuted ? 'Unmute microphone' : 'Mute microphone'\">\n\t\t\t<i class=\"fa\" [class.fa-microphone]=\"!isMuted\" [class.fa-microphone-slash]=\"isMuted\"></i>\n\t\t</button>\n\t\t<button class=\"btn hangup-btn\" (click)=\"hangUp()\" title=\"Hang up\">\n\t\t\t<i class=\"fa fa-phone\" style=\"transform: rotate(135deg);\"></i>\n\t\t</button>\n\t</div>\n</div>\n\n", styles: [".tas-videocall-container{position:relative;width:100vw;height:100vh;background-color:#000;overflow:hidden}.tas-videocall-container ::ng-deep .OT_edge-bar-item,.tas-videocall-container ::ng-deep .OT_mute,.tas-videocall-container ::ng-deep .OT_audio-level-meter,.tas-videocall-container ::ng-deep .OT_bar,.tas-videocall-container ::ng-deep .OT_name{display:none!important}.tas-videocall-container .subscriber-view{width:100%;height:100%;z-index:1}.tas-videocall-container .publisher-view{position:absolute;top:20px;right:20px;width:200px;height:150px;z-index:2;border:2px solid #fff;border-radius:8px;background-color:#333}.tas-videocall-container .controls-container{display:flex;flex-direction:row;gap:20px;position:absolute;bottom:30px;left:50%;transform:translate(-50%);z-index:3;background-color:#00000080;padding:15px 25px;border-radius:50px;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.tas-videocall-container .controls-container .hangup-btn,.tas-videocall-container .controls-container .swap-btn,.tas-videocall-container .controls-container .pip-btn,.tas-videocall-container .controls-container .mute-btn{width:60px;height:60px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:24px;border:none;box-shadow:0 4px 6px #0000004d;transition:all .2s ease}.tas-videocall-container .controls-container .hangup-btn i,.tas-videocall-container .controls-container .swap-btn i,.tas-videocall-container .controls-container .pip-btn i,.tas-videocall-container .controls-container .mute-btn i{color:#fff}.tas-videocall-container .controls-container .hangup-btn{background:#dc3545}.tas-videocall-container .controls-container .hangup-btn:hover{background:#c82333;transform:scale(1.05)}.tas-videocall-container .controls-container .swap-btn{background:rgba(255,255,255,.2)}.tas-videocall-container .controls-container .swap-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.05)}.tas-videocall-container .controls-container .pip-btn{background:rgba(255,255,255,.2)}.tas-videocall-container .controls-container .pip-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.05)}.tas-videocall-container .controls-container .mute-btn{background:rgba(255,255,255,.2)}.tas-videocall-container .controls-container .mute-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.05)}.tas-videocall-container .controls-container .mute-btn.muted{background:#f39c12}.tas-videocall-container .controls-container .mute-btn.muted:hover{background:#e67e22}\n"] }]
429
438
  }], ctorParameters: function () { return [{ type: i1.NgbActiveModal }, { type: TasService }]; }, propDecorators: { sessionId: [{
430
439
  type: Input
431
440
  }], token: [{
@@ -434,126 +443,281 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
434
443
  type: Input
435
444
  }], publisherContainer: [{
436
445
  type: ViewChild,
437
- args: ["publisherContainer"]
446
+ args: ['publisherContainer']
438
447
  }], subscriberContainer: [{
439
448
  type: ViewChild,
440
- args: ["subscriberContainer"]
449
+ args: ['subscriberContainer']
441
450
  }] } });
442
451
 
443
- class TasButtonComponent {
444
- constructor(tasService, modalService, userDataProvider) {
452
+ var WaitingRoomState;
453
+ (function (WaitingRoomState) {
454
+ WaitingRoomState["IDLE"] = "IDLE";
455
+ WaitingRoomState["CREATING_ROOM"] = "CREATING_ROOM";
456
+ WaitingRoomState["GETTING_TOKEN"] = "GETTING_TOKEN";
457
+ WaitingRoomState["READY"] = "READY";
458
+ WaitingRoomState["ERROR"] = "ERROR";
459
+ })(WaitingRoomState || (WaitingRoomState = {}));
460
+ class TasWaitingRoomComponent {
461
+ constructor(activeModal, tasService, modalService) {
462
+ this.activeModal = activeModal;
445
463
  this.tasService = tasService;
446
464
  this.modalService = modalService;
447
- this.userDataProvider = userDataProvider;
448
- this.isLoading = false;
449
- this.userData = null;
450
- this.tenantId = null;
465
+ // Room configuration inputs
466
+ this.appointmentId = 1;
467
+ this.product = 'uell';
468
+ this.tenantId = '';
469
+ this.regularUserIds = [];
470
+ this.moderatorUserIds = [];
471
+ // Component state
472
+ this.state = WaitingRoomState.IDLE;
473
+ this.WaitingRoomState = WaitingRoomState; // Expose enum to template
474
+ this.errorMessage = '';
475
+ // Session data
476
+ this.sessionId = '';
477
+ this.token = '';
478
+ this.users = [];
451
479
  this.subscriptions = new Subscription();
452
- this.currentModalRef = null;
480
+ this.videoCallModalRef = null;
453
481
  }
454
482
  ngOnInit() {
455
- this.loadUserData();
483
+ this.buildUsersArray();
456
484
  this.setupViewModeSubscription();
457
485
  }
458
486
  ngOnDestroy() {
459
487
  this.subscriptions.unsubscribe();
460
488
  }
461
- onClick() {
462
- if (!this.userData || !this.tenantId) {
463
- console.error("User data or tenant ID not available");
489
+ /**
490
+ * Creates the room and fetches the token
491
+ */
492
+ createRoom() {
493
+ var _a;
494
+ if (!this.tenantId || !((_a = this.currentUser) === null || _a === void 0 ? void 0 : _a.name)) {
495
+ this.state = WaitingRoomState.ERROR;
496
+ this.errorMessage = 'Missing configuration data (tenant or user)';
464
497
  return;
465
498
  }
466
- this.isLoading = true;
467
- this.subscriptions.add(this.tasService
468
- .createRoom({
499
+ this.state = WaitingRoomState.CREATING_ROOM;
500
+ this.errorMessage = '';
501
+ const body = {
502
+ roomType: TasRoomType.TAS,
503
+ type: TasSessionType.SPONTANEOUS,
469
504
  tenant: this.tenantId,
470
- userId: this.userData.id.toString(),
471
- product: "Uell",
472
- record: true,
473
- roomType: "TAS",
474
- type: "SPONTANEOUS",
475
- })
476
- .pipe(switchMap((response) => {
477
- const sessionId = response.content.sessionId;
478
- return this.tasService
479
- .generateToken({
480
- sessionId: sessionId,
481
- name: this.userData.name,
482
- lastname: this.userData.surname,
483
- })
484
- .pipe(switchMap((tokenResponse) => {
485
- return [
486
- {
487
- sessionId,
488
- token: tokenResponse.content.token,
489
- },
490
- ];
491
- }));
492
- }))
493
- .subscribe({
494
- next: ({ sessionId, token }) => {
495
- this.isLoading = false;
496
- this.openVideoCallModal(sessionId, token);
505
+ appointmentId: this.appointmentId,
506
+ users: this.users,
507
+ product: this.product
508
+ };
509
+ this.subscriptions.add(this.tasService.createRoom(body).pipe(switchMap(response => {
510
+ this.sessionId = response.content.sessionId;
511
+ this.state = WaitingRoomState.GETTING_TOKEN;
512
+ return this.tasService.generateToken({
513
+ sessionId: this.sessionId,
514
+ name: this.currentUser.name,
515
+ lastname: this.currentUser.lastname,
516
+ roleVC: this.currentUser.role
517
+ });
518
+ })).subscribe({
519
+ next: (tokenResponse) => {
520
+ this.token = tokenResponse.content.token;
521
+ this.state = WaitingRoomState.READY;
497
522
  },
498
523
  error: (err) => {
499
- console.error("Error starting video call:", err);
500
- this.isLoading = false;
501
- },
524
+ console.error('Error creating room or getting token:', err);
525
+ this.state = WaitingRoomState.ERROR;
526
+ this.errorMessage = 'Error creating room. Please try again.';
527
+ }
502
528
  }));
503
529
  }
504
- // Private Methods
505
- loadUserData() {
506
- if (!this.userDataProvider) {
507
- console.warn("TasButtonComponent: UserDataProvider not configured");
530
+ /**
531
+ * Joins the video call session
532
+ */
533
+ joinSession() {
534
+ if (!this.sessionId || !this.token) {
535
+ this.errorMessage = 'Cannot join session. Incomplete data.';
508
536
  return;
509
537
  }
510
- this.userData = this.userDataProvider.getUserData();
511
- this.tenantId = this.userDataProvider.getTenantId();
538
+ // Close waiting room and open video call
539
+ this.activeModal.close('joining');
540
+ this.openVideoCallModal();
541
+ }
542
+ /**
543
+ * Closes the waiting room
544
+ */
545
+ cancel() {
546
+ this.activeModal.dismiss('cancel');
547
+ }
548
+ /**
549
+ * Retry after an error
550
+ */
551
+ retry() {
552
+ this.state = WaitingRoomState.IDLE;
553
+ this.errorMessage = '';
554
+ this.sessionId = '';
555
+ this.token = '';
556
+ }
557
+ // Private Methods
558
+ buildUsersArray() {
559
+ this.users = [];
560
+ // Add owners from input
561
+ this.ownerUserIds.forEach(id => {
562
+ this.users.push({ userExternalId: id, rol: TasUserRole.OWNER });
563
+ });
564
+ // Add regular users from input
565
+ this.regularUserIds.forEach(id => {
566
+ this.users.push({ userExternalId: id, rol: TasUserRole.USER });
567
+ });
568
+ // Add moderators from input
569
+ this.moderatorUserIds.forEach(id => {
570
+ this.users.push({ userExternalId: id, rol: TasUserRole.MODERATOR });
571
+ });
512
572
  }
513
573
  setupViewModeSubscription() {
514
- this.subscriptions.add(this.tasService.viewMode$.subscribe((mode) => {
574
+ this.subscriptions.add(this.tasService.viewMode$.subscribe(mode => {
575
+ // Re-open video call modal when returning from PiP mode
576
+ if (mode === ViewMode.FULLSCREEN &&
577
+ this.tasService.isCallActive() &&
578
+ !this.videoCallModalRef) {
579
+ const sessionId = this.tasService.sessionId;
580
+ const token = this.tasService.token;
581
+ if (sessionId && token) {
582
+ this.openVideoCallModal(true);
583
+ }
584
+ }
585
+ }));
586
+ }
587
+ openVideoCallModal(isReturningFromPip = false) {
588
+ this.videoCallModalRef = this.modalService.open(TasVideocallComponent, {
589
+ size: 'xl',
590
+ windowClass: 'tas-video-modal',
591
+ backdrop: 'static',
592
+ keyboard: false
593
+ });
594
+ const sessionIdToUse = this.sessionId || this.tasService.sessionId;
595
+ const tokenToUse = this.token || this.tasService.token;
596
+ this.videoCallModalRef.componentInstance.sessionId = sessionIdToUse;
597
+ this.videoCallModalRef.componentInstance.token = tokenToUse;
598
+ this.videoCallModalRef.componentInstance.isReturningFromPip = isReturningFromPip;
599
+ this.videoCallModalRef.result.then(() => { this.videoCallModalRef = null; }, () => { this.videoCallModalRef = null; });
600
+ }
601
+ }
602
+ TasWaitingRoomComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasWaitingRoomComponent, deps: [{ token: i1.NgbActiveModal }, { token: TasService }, { token: i1.NgbModal }], target: i0.ɵɵFactoryTarget.Component });
603
+ TasWaitingRoomComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: TasWaitingRoomComponent, selector: "tas-waiting-room", inputs: { appointmentId: "appointmentId", product: "product", tenantId: "tenantId", currentUser: "currentUser", ownerUserIds: "ownerUserIds", regularUserIds: "regularUserIds", moderatorUserIds: "moderatorUserIds" }, ngImport: i0, template: "<div class=\"tas-waiting-room\">\n\t<!-- Header -->\n\t<div class=\"waiting-room-header\">\n\t\t<div class=\"header-icon\">\n\t\t\t<i class=\"fa fa-video-camera\"></i>\n\t\t</div>\n\t\t<h2 class=\"header-title\">Waiting Room</h2>\n\t\t<p class=\"header-subtitle\">Prepare for your video call</p>\n\t\t<button type=\"button\" class=\"close-btn\" (click)=\"cancel()\" aria-label=\"Close\">\n\t\t\t<span aria-hidden=\"true\">&times;</span>\n\t\t</button>\n\t</div>\n\n\t<!-- Content -->\n\t<div class=\"waiting-room-content\">\n\t\t<!-- IDLE State -->\n\t\t<div class=\"state-container\" *ngIf=\"state === WaitingRoomState.IDLE\">\n\t\t\t<div class=\"state-icon idle\">\n\t\t\t\t<i class=\"fa fa-plus-circle\"></i>\n\t\t\t</div>\n\t\t\t<p class=\"state-message\">\n\t\t\t\tCreate a new video call room\n\t\t\t</p>\n\t\t\t<p class=\"state-submessage\">\n\t\t\t\tPress the button to begin\n\t\t\t</p>\n\t\t\t<button \n\t\t\t\ttype=\"button\" \n\t\t\t\tclass=\"btn action-btn create-btn\"\n\t\t\t\t(click)=\"createRoom()\">\n\t\t\t\t<i class=\"fa fa-plus\"></i>\n\t\t\t\tCreate Room\n\t\t\t</button>\n\t\t</div>\n\n\t\t<!-- CREATING_ROOM State -->\n\t\t<div class=\"state-container\" *ngIf=\"state === WaitingRoomState.CREATING_ROOM\">\n\t\t\t<div class=\"state-icon loading\">\n\t\t\t\t<div class=\"spinner\"></div>\n\t\t\t</div>\n\t\t\t<p class=\"state-message\">\n\t\t\t\tCreating video call room...\n\t\t\t</p>\n\t\t\t<div class=\"progress-steps\">\n\t\t\t\t<div class=\"step active\">\n\t\t\t\t\t<span class=\"step-indicator\"></span>\n\t\t\t\t\t<span class=\"step-label\">Creating</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"step\">\n\t\t\t\t\t<span class=\"step-indicator\"></span>\n\t\t\t\t\t<span class=\"step-label\">Access</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"step\">\n\t\t\t\t\t<span class=\"step-indicator\"></span>\n\t\t\t\t\t<span class=\"step-label\">Ready</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- GETTING_TOKEN State -->\n\t\t<div class=\"state-container\" *ngIf=\"state === WaitingRoomState.GETTING_TOKEN\">\n\t\t\t<div class=\"state-icon loading\">\n\t\t\t\t<div class=\"spinner\"></div>\n\t\t\t</div>\n\t\t\t<p class=\"state-message\">\n\t\t\t\tPreparing room access...\n\t\t\t</p>\n\t\t\t<div class=\"progress-steps\">\n\t\t\t\t<div class=\"step completed\">\n\t\t\t\t\t<span class=\"step-indicator\"><i class=\"fa fa-check\"></i></span>\n\t\t\t\t\t<span class=\"step-label\">Created</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"step active\">\n\t\t\t\t\t<span class=\"step-indicator\"></span>\n\t\t\t\t\t<span class=\"step-label\">Access</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"step\">\n\t\t\t\t\t<span class=\"step-indicator\"></span>\n\t\t\t\t\t<span class=\"step-label\">Ready</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- READY State -->\n\t\t<div class=\"state-container\" *ngIf=\"state === WaitingRoomState.READY\">\n\t\t\t<div class=\"state-icon ready\">\n\t\t\t\t<i class=\"fa fa-check-circle\"></i>\n\t\t\t</div>\n\t\t\t<p class=\"state-message success\">\n\t\t\t\tRoom is ready!\n\t\t\t</p>\n\t\t\t<p class=\"state-submessage\">\n\t\t\t\tYou can join the video call when ready\n\t\t\t</p>\n\t\t\t<div class=\"progress-steps\">\n\t\t\t\t<div class=\"step completed\">\n\t\t\t\t\t<span class=\"step-indicator\"><i class=\"fa fa-check\"></i></span>\n\t\t\t\t\t<span class=\"step-label\">Created</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"step completed\">\n\t\t\t\t\t<span class=\"step-indicator\"><i class=\"fa fa-check\"></i></span>\n\t\t\t\t\t<span class=\"step-label\">Access</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"step completed\">\n\t\t\t\t\t<span class=\"step-indicator\"><i class=\"fa fa-check\"></i></span>\n\t\t\t\t\t<span class=\"step-label\">Ready</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<button \n\t\t\t\ttype=\"button\" \n\t\t\t\tclass=\"btn action-btn join-btn\"\n\t\t\t\t(click)=\"joinSession()\">\n\t\t\t\t<i class=\"fa fa-sign-in\"></i>\n\t\t\t\tJoin Session\n\t\t\t</button>\n\t\t</div>\n\n\t\t<!-- ERROR State -->\n\t\t<div class=\"state-container\" *ngIf=\"state === WaitingRoomState.ERROR\">\n\t\t\t<div class=\"state-icon error\">\n\t\t\t\t<i class=\"fa fa-exclamation-triangle\"></i>\n\t\t\t</div>\n\t\t\t<p class=\"state-message error\">\n\t\t\t\tAn error occurred\n\t\t\t</p>\n\t\t\t<p class=\"error-details\" *ngIf=\"errorMessage\">\n\t\t\t\t{{ errorMessage }}\n\t\t\t</p>\n\t\t\t<button \n\t\t\t\ttype=\"button\" \n\t\t\t\tclass=\"btn action-btn retry-btn\"\n\t\t\t\t(click)=\"retry()\">\n\t\t\t\t<i class=\"fa fa-refresh\"></i>\n\t\t\t\tRetry\n\t\t\t</button>\n\t\t</div>\n\t</div>\n\n\t<!-- Footer -->\n\t<div class=\"waiting-room-footer\">\n\t\t<button \n\t\t\ttype=\"button\" \n\t\t\tclass=\"btn cancel-btn\"\n\t\t\t(click)=\"cancel()\">\n\t\t\tCancel\n\t\t</button>\n\t</div>\n</div>\n\n", styles: [".tas-waiting-room{display:flex;flex-direction:column;min-height:420px;background:#ffffff;border-radius:5px;overflow:hidden}.waiting-room-header{position:relative;padding:32px 40px 24px;text-align:center;border-bottom:1px solid #e9ecef}.waiting-room-header .header-icon{width:72px;height:72px;margin:0 auto 16px;background:linear-gradient(135deg,#1da4b1 0%,#0077b3 100%);border-radius:50%;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 16px #1da4b140}.waiting-room-header .header-icon i{font-size:28px;color:#fff}.waiting-room-header .header-title{margin:0 0 8px;font-size:20px;font-weight:700;line-height:28px;color:#212529}.waiting-room-header .header-subtitle{margin:0;font-size:14px;color:#6c757d;font-weight:400}.waiting-room-header .close-btn{position:absolute;top:16px;right:16px;width:32px;height:32px;border:none;background:transparent;border-radius:4px;color:#6c757d;cursor:pointer;transition:all .2s ease;font-size:20px}.waiting-room-header .close-btn:hover{background:#f8f9fa;color:#212529}.waiting-room-content{flex:1;display:flex;align-items:center;justify-content:center;padding:32px 40px;background:#ffffff}.state-container{text-align:center;max-width:360px;width:100%}.state-icon{width:80px;height:80px;margin:0 auto 24px;border-radius:50%;display:flex;align-items:center;justify-content:center}.state-icon i{font-size:36px}.state-icon.idle{background:rgba(29,164,177,.1);border:2px dashed #1da4b1}.state-icon.idle i{color:#1da4b1}.state-icon.loading{background:rgba(29,164,177,.1);border:2px solid #1da4b1}.state-icon.ready{background:linear-gradient(135deg,#1da4b1 0%,#38b89a 100%);box-shadow:0 4px 16px #1da4b14d}.state-icon.ready i{color:#fff}.state-icon.error{background:rgba(238,49,107,.1);border:2px solid #ee316b}.state-icon.error i{color:#ee316b}.spinner{width:40px;height:40px;border:3px solid #e9ecef;border-top-color:#1da4b1;border-radius:50%;animation:spin 1s linear infinite}.state-message{font-size:16px;font-weight:600;margin:0 0 8px;color:#212529;line-height:24px}.state-message.success{color:#1da4b1}.state-message.error{color:#ee316b}.state-submessage{font-size:14px;color:#6c757d;margin:0 0 24px;font-weight:400}.error-details{font-size:13px;color:#ee316b;margin:0 0 24px;padding:12px 16px;background:rgba(238,49,107,.08);border-radius:8px;border:1px solid rgba(238,49,107,.2)}.progress-steps{display:flex;justify-content:center;gap:24px;margin:24px 0 32px}.step{display:flex;flex-direction:column;align-items:center;gap:8px}.step .step-indicator{width:32px;height:32px;border-radius:50%;background:#f8f9fa;border:2px solid #e9ecef;display:flex;align-items:center;justify-content:center;transition:all .3s ease}.step .step-indicator i{font-size:12px;color:#fff}.step .step-label{font-size:11px;color:#6c757d;text-transform:uppercase;letter-spacing:.5px;font-weight:500}.step.active .step-indicator{background:rgba(29,164,177,.1);border-color:#1da4b1;animation:pulse-active 1.5s infinite}.step.active .step-label{color:#1da4b1;font-weight:600}.step.completed .step-indicator{background:#1da4b1;border-color:#1da4b1}.step.completed .step-label{color:#1da4b1;font-weight:600}.action-btn{padding:12px 32px;font-size:16px;font-weight:600;border-radius:4px;border:none;cursor:pointer;transition:all .2s ease;display:inline-flex;align-items:center;gap:10px}.action-btn i{font-size:16px}.action-btn.create-btn{background:#0077b3;color:#fff;box-shadow:0 2px 8px #0077b340}.action-btn.create-btn:hover{background:#005c8a;box-shadow:0 4px 12px #0077b359}.action-btn.create-btn:active{transform:translateY(1px)}.action-btn.join-btn{background:#1da4b1;color:#fff;box-shadow:0 2px 8px #1da4b140;animation:pulse-ready 2s infinite}.action-btn.join-btn:hover{background:#17848e;box-shadow:0 4px 12px #1da4b159}.action-btn.join-btn:active{transform:translateY(1px)}.action-btn.retry-btn{background:transparent;color:#6c757d;border:1px solid #e9ecef}.action-btn.retry-btn:hover{background:#f8f9fa;border-color:#6c757d;color:#212529}.waiting-room-footer{padding:16px 40px 24px;background:#ffffff;border-top:1px solid #e9ecef;display:flex;justify-content:center}.waiting-room-footer .cancel-btn{padding:10px 24px;font-size:14px;font-weight:600;border-radius:4px;background:transparent;color:#6c757d;border:none;cursor:pointer;transition:all .2s ease}.waiting-room-footer .cancel-btn:hover{background:#f8f9fa;color:#212529}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse-active{0%,to{box-shadow:0 0 #1da4b166}50%{box-shadow:0 0 0 8px #1da4b100}}@keyframes pulse-ready{0%,to{box-shadow:0 2px 8px #1da4b140}50%{box-shadow:0 4px 16px #1da4b166}}@media (max-width: 576px){.tas-waiting-room{min-height:380px}.waiting-room-header{padding:24px 24px 20px}.waiting-room-header .header-icon{width:56px;height:56px}.waiting-room-header .header-icon i{font-size:22px}.waiting-room-header .header-title{font-size:18px}.waiting-room-content{padding:24px}.state-icon{width:64px;height:64px}.state-icon i{font-size:28px}.spinner{width:32px;height:32px}.progress-steps{gap:12px}.step .step-indicator{width:28px;height:28px}.step .step-label{font-size:9px}.action-btn{padding:10px 24px;font-size:14px}.waiting-room-footer{padding:16px 24px 20px}}\n"], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
604
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasWaitingRoomComponent, decorators: [{
605
+ type: Component,
606
+ args: [{ selector: 'tas-waiting-room', template: "<div class=\"tas-waiting-room\">\n\t<!-- Header -->\n\t<div class=\"waiting-room-header\">\n\t\t<div class=\"header-icon\">\n\t\t\t<i class=\"fa fa-video-camera\"></i>\n\t\t</div>\n\t\t<h2 class=\"header-title\">Waiting Room</h2>\n\t\t<p class=\"header-subtitle\">Prepare for your video call</p>\n\t\t<button type=\"button\" class=\"close-btn\" (click)=\"cancel()\" aria-label=\"Close\">\n\t\t\t<span aria-hidden=\"true\">&times;</span>\n\t\t</button>\n\t</div>\n\n\t<!-- Content -->\n\t<div class=\"waiting-room-content\">\n\t\t<!-- IDLE State -->\n\t\t<div class=\"state-container\" *ngIf=\"state === WaitingRoomState.IDLE\">\n\t\t\t<div class=\"state-icon idle\">\n\t\t\t\t<i class=\"fa fa-plus-circle\"></i>\n\t\t\t</div>\n\t\t\t<p class=\"state-message\">\n\t\t\t\tCreate a new video call room\n\t\t\t</p>\n\t\t\t<p class=\"state-submessage\">\n\t\t\t\tPress the button to begin\n\t\t\t</p>\n\t\t\t<button \n\t\t\t\ttype=\"button\" \n\t\t\t\tclass=\"btn action-btn create-btn\"\n\t\t\t\t(click)=\"createRoom()\">\n\t\t\t\t<i class=\"fa fa-plus\"></i>\n\t\t\t\tCreate Room\n\t\t\t</button>\n\t\t</div>\n\n\t\t<!-- CREATING_ROOM State -->\n\t\t<div class=\"state-container\" *ngIf=\"state === WaitingRoomState.CREATING_ROOM\">\n\t\t\t<div class=\"state-icon loading\">\n\t\t\t\t<div class=\"spinner\"></div>\n\t\t\t</div>\n\t\t\t<p class=\"state-message\">\n\t\t\t\tCreating video call room...\n\t\t\t</p>\n\t\t\t<div class=\"progress-steps\">\n\t\t\t\t<div class=\"step active\">\n\t\t\t\t\t<span class=\"step-indicator\"></span>\n\t\t\t\t\t<span class=\"step-label\">Creating</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"step\">\n\t\t\t\t\t<span class=\"step-indicator\"></span>\n\t\t\t\t\t<span class=\"step-label\">Access</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"step\">\n\t\t\t\t\t<span class=\"step-indicator\"></span>\n\t\t\t\t\t<span class=\"step-label\">Ready</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- GETTING_TOKEN State -->\n\t\t<div class=\"state-container\" *ngIf=\"state === WaitingRoomState.GETTING_TOKEN\">\n\t\t\t<div class=\"state-icon loading\">\n\t\t\t\t<div class=\"spinner\"></div>\n\t\t\t</div>\n\t\t\t<p class=\"state-message\">\n\t\t\t\tPreparing room access...\n\t\t\t</p>\n\t\t\t<div class=\"progress-steps\">\n\t\t\t\t<div class=\"step completed\">\n\t\t\t\t\t<span class=\"step-indicator\"><i class=\"fa fa-check\"></i></span>\n\t\t\t\t\t<span class=\"step-label\">Created</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"step active\">\n\t\t\t\t\t<span class=\"step-indicator\"></span>\n\t\t\t\t\t<span class=\"step-label\">Access</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"step\">\n\t\t\t\t\t<span class=\"step-indicator\"></span>\n\t\t\t\t\t<span class=\"step-label\">Ready</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- READY State -->\n\t\t<div class=\"state-container\" *ngIf=\"state === WaitingRoomState.READY\">\n\t\t\t<div class=\"state-icon ready\">\n\t\t\t\t<i class=\"fa fa-check-circle\"></i>\n\t\t\t</div>\n\t\t\t<p class=\"state-message success\">\n\t\t\t\tRoom is ready!\n\t\t\t</p>\n\t\t\t<p class=\"state-submessage\">\n\t\t\t\tYou can join the video call when ready\n\t\t\t</p>\n\t\t\t<div class=\"progress-steps\">\n\t\t\t\t<div class=\"step completed\">\n\t\t\t\t\t<span class=\"step-indicator\"><i class=\"fa fa-check\"></i></span>\n\t\t\t\t\t<span class=\"step-label\">Created</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"step completed\">\n\t\t\t\t\t<span class=\"step-indicator\"><i class=\"fa fa-check\"></i></span>\n\t\t\t\t\t<span class=\"step-label\">Access</span>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"step completed\">\n\t\t\t\t\t<span class=\"step-indicator\"><i class=\"fa fa-check\"></i></span>\n\t\t\t\t\t<span class=\"step-label\">Ready</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<button \n\t\t\t\ttype=\"button\" \n\t\t\t\tclass=\"btn action-btn join-btn\"\n\t\t\t\t(click)=\"joinSession()\">\n\t\t\t\t<i class=\"fa fa-sign-in\"></i>\n\t\t\t\tJoin Session\n\t\t\t</button>\n\t\t</div>\n\n\t\t<!-- ERROR State -->\n\t\t<div class=\"state-container\" *ngIf=\"state === WaitingRoomState.ERROR\">\n\t\t\t<div class=\"state-icon error\">\n\t\t\t\t<i class=\"fa fa-exclamation-triangle\"></i>\n\t\t\t</div>\n\t\t\t<p class=\"state-message error\">\n\t\t\t\tAn error occurred\n\t\t\t</p>\n\t\t\t<p class=\"error-details\" *ngIf=\"errorMessage\">\n\t\t\t\t{{ errorMessage }}\n\t\t\t</p>\n\t\t\t<button \n\t\t\t\ttype=\"button\" \n\t\t\t\tclass=\"btn action-btn retry-btn\"\n\t\t\t\t(click)=\"retry()\">\n\t\t\t\t<i class=\"fa fa-refresh\"></i>\n\t\t\t\tRetry\n\t\t\t</button>\n\t\t</div>\n\t</div>\n\n\t<!-- Footer -->\n\t<div class=\"waiting-room-footer\">\n\t\t<button \n\t\t\ttype=\"button\" \n\t\t\tclass=\"btn cancel-btn\"\n\t\t\t(click)=\"cancel()\">\n\t\t\tCancel\n\t\t</button>\n\t</div>\n</div>\n\n", styles: [".tas-waiting-room{display:flex;flex-direction:column;min-height:420px;background:#ffffff;border-radius:5px;overflow:hidden}.waiting-room-header{position:relative;padding:32px 40px 24px;text-align:center;border-bottom:1px solid #e9ecef}.waiting-room-header .header-icon{width:72px;height:72px;margin:0 auto 16px;background:linear-gradient(135deg,#1da4b1 0%,#0077b3 100%);border-radius:50%;display:flex;align-items:center;justify-content:center;box-shadow:0 4px 16px #1da4b140}.waiting-room-header .header-icon i{font-size:28px;color:#fff}.waiting-room-header .header-title{margin:0 0 8px;font-size:20px;font-weight:700;line-height:28px;color:#212529}.waiting-room-header .header-subtitle{margin:0;font-size:14px;color:#6c757d;font-weight:400}.waiting-room-header .close-btn{position:absolute;top:16px;right:16px;width:32px;height:32px;border:none;background:transparent;border-radius:4px;color:#6c757d;cursor:pointer;transition:all .2s ease;font-size:20px}.waiting-room-header .close-btn:hover{background:#f8f9fa;color:#212529}.waiting-room-content{flex:1;display:flex;align-items:center;justify-content:center;padding:32px 40px;background:#ffffff}.state-container{text-align:center;max-width:360px;width:100%}.state-icon{width:80px;height:80px;margin:0 auto 24px;border-radius:50%;display:flex;align-items:center;justify-content:center}.state-icon i{font-size:36px}.state-icon.idle{background:rgba(29,164,177,.1);border:2px dashed #1da4b1}.state-icon.idle i{color:#1da4b1}.state-icon.loading{background:rgba(29,164,177,.1);border:2px solid #1da4b1}.state-icon.ready{background:linear-gradient(135deg,#1da4b1 0%,#38b89a 100%);box-shadow:0 4px 16px #1da4b14d}.state-icon.ready i{color:#fff}.state-icon.error{background:rgba(238,49,107,.1);border:2px solid #ee316b}.state-icon.error i{color:#ee316b}.spinner{width:40px;height:40px;border:3px solid #e9ecef;border-top-color:#1da4b1;border-radius:50%;animation:spin 1s linear infinite}.state-message{font-size:16px;font-weight:600;margin:0 0 8px;color:#212529;line-height:24px}.state-message.success{color:#1da4b1}.state-message.error{color:#ee316b}.state-submessage{font-size:14px;color:#6c757d;margin:0 0 24px;font-weight:400}.error-details{font-size:13px;color:#ee316b;margin:0 0 24px;padding:12px 16px;background:rgba(238,49,107,.08);border-radius:8px;border:1px solid rgba(238,49,107,.2)}.progress-steps{display:flex;justify-content:center;gap:24px;margin:24px 0 32px}.step{display:flex;flex-direction:column;align-items:center;gap:8px}.step .step-indicator{width:32px;height:32px;border-radius:50%;background:#f8f9fa;border:2px solid #e9ecef;display:flex;align-items:center;justify-content:center;transition:all .3s ease}.step .step-indicator i{font-size:12px;color:#fff}.step .step-label{font-size:11px;color:#6c757d;text-transform:uppercase;letter-spacing:.5px;font-weight:500}.step.active .step-indicator{background:rgba(29,164,177,.1);border-color:#1da4b1;animation:pulse-active 1.5s infinite}.step.active .step-label{color:#1da4b1;font-weight:600}.step.completed .step-indicator{background:#1da4b1;border-color:#1da4b1}.step.completed .step-label{color:#1da4b1;font-weight:600}.action-btn{padding:12px 32px;font-size:16px;font-weight:600;border-radius:4px;border:none;cursor:pointer;transition:all .2s ease;display:inline-flex;align-items:center;gap:10px}.action-btn i{font-size:16px}.action-btn.create-btn{background:#0077b3;color:#fff;box-shadow:0 2px 8px #0077b340}.action-btn.create-btn:hover{background:#005c8a;box-shadow:0 4px 12px #0077b359}.action-btn.create-btn:active{transform:translateY(1px)}.action-btn.join-btn{background:#1da4b1;color:#fff;box-shadow:0 2px 8px #1da4b140;animation:pulse-ready 2s infinite}.action-btn.join-btn:hover{background:#17848e;box-shadow:0 4px 12px #1da4b159}.action-btn.join-btn:active{transform:translateY(1px)}.action-btn.retry-btn{background:transparent;color:#6c757d;border:1px solid #e9ecef}.action-btn.retry-btn:hover{background:#f8f9fa;border-color:#6c757d;color:#212529}.waiting-room-footer{padding:16px 40px 24px;background:#ffffff;border-top:1px solid #e9ecef;display:flex;justify-content:center}.waiting-room-footer .cancel-btn{padding:10px 24px;font-size:14px;font-weight:600;border-radius:4px;background:transparent;color:#6c757d;border:none;cursor:pointer;transition:all .2s ease}.waiting-room-footer .cancel-btn:hover{background:#f8f9fa;color:#212529}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse-active{0%,to{box-shadow:0 0 #1da4b166}50%{box-shadow:0 0 0 8px #1da4b100}}@keyframes pulse-ready{0%,to{box-shadow:0 2px 8px #1da4b140}50%{box-shadow:0 4px 16px #1da4b166}}@media (max-width: 576px){.tas-waiting-room{min-height:380px}.waiting-room-header{padding:24px 24px 20px}.waiting-room-header .header-icon{width:56px;height:56px}.waiting-room-header .header-icon i{font-size:22px}.waiting-room-header .header-title{font-size:18px}.waiting-room-content{padding:24px}.state-icon{width:64px;height:64px}.state-icon i{font-size:28px}.spinner{width:32px;height:32px}.progress-steps{gap:12px}.step .step-indicator{width:28px;height:28px}.step .step-label{font-size:9px}.action-btn{padding:10px 24px;font-size:14px}.waiting-room-footer{padding:16px 24px 20px}}\n"] }]
607
+ }], ctorParameters: function () { return [{ type: i1.NgbActiveModal }, { type: TasService }, { type: i1.NgbModal }]; }, propDecorators: { appointmentId: [{
608
+ type: Input
609
+ }], product: [{
610
+ type: Input
611
+ }], tenantId: [{
612
+ type: Input
613
+ }], currentUser: [{
614
+ type: Input
615
+ }], ownerUserIds: [{
616
+ type: Input
617
+ }], regularUserIds: [{
618
+ type: Input
619
+ }], moderatorUserIds: [{
620
+ type: Input
621
+ }] } });
622
+
623
+ class TasButtonComponent {
624
+ constructor(modalService, tasService) {
625
+ this.modalService = modalService;
626
+ this.tasService = tasService;
627
+ this.appointmentId = 1;
628
+ this.product = "uell";
629
+ this.tenantId = "";
630
+ this.regularUserIds = [];
631
+ this.moderatorUserIds = [];
632
+ this.isLoading = false;
633
+ this.subscriptions = new Subscription();
634
+ this.currentModalRef = null;
635
+ this.videoCallModalRef = null;
636
+ }
637
+ ngOnInit() {
638
+ if (!this.ownerUserIds || this.ownerUserIds.length !== 1) {
639
+ throw new Error('tas-btn: ownerUserIds input is required and must contain exactly one user');
640
+ }
641
+ // Subscribe to viewMode to handle PiP return
642
+ this.subscriptions.add(this.tasService.viewMode$.subscribe(mode => {
643
+ // Reopen video call modal when returning from PiP
515
644
  if (mode === ViewMode.FULLSCREEN &&
516
645
  this.tasService.isCallActive() &&
517
- !this.currentModalRef) {
646
+ !this.videoCallModalRef) {
518
647
  const sessionId = this.tasService.sessionId;
519
648
  const token = this.tasService.token;
520
649
  if (sessionId && token) {
521
- this.openVideoCallModal(sessionId, token, true);
650
+ this.openVideoCallModal(true);
522
651
  }
523
652
  }
653
+ // When entering PiP, clear the videoCallModalRef since modal will close
654
+ if (mode === ViewMode.PIP) {
655
+ this.videoCallModalRef = null;
656
+ }
524
657
  }));
525
658
  }
526
- openVideoCallModal(sessionId, token, isReturningFromPip = false) {
527
- this.currentModalRef = this.modalService.open(TasVideocallComponent, {
528
- size: "xl",
529
- windowClass: "tas-video-modal",
659
+ ngOnDestroy() {
660
+ this.subscriptions.unsubscribe();
661
+ }
662
+ onClick() {
663
+ var _a;
664
+ if (!this.tenantId || !((_a = this.currentUser) === null || _a === void 0 ? void 0 : _a.name)) {
665
+ console.error("Tenant ID or current user not available");
666
+ return;
667
+ }
668
+ this.openWaitingRoomModal();
669
+ }
670
+ openWaitingRoomModal() {
671
+ this.currentModalRef = this.modalService.open(TasWaitingRoomComponent, {
672
+ size: "lg",
673
+ windowClass: "tas-waiting-room-modal",
530
674
  backdrop: "static",
531
675
  keyboard: false,
676
+ centered: true
532
677
  });
533
- this.currentModalRef.componentInstance.sessionId = sessionId;
534
- this.currentModalRef.componentInstance.token = token;
535
- this.currentModalRef.componentInstance.isReturningFromPip =
536
- isReturningFromPip;
537
- this.currentModalRef.result.then(() => {
538
- this.currentModalRef = null;
539
- }, () => {
540
- this.currentModalRef = null;
678
+ // Pass all necessary inputs to the waiting room component
679
+ this.currentModalRef.componentInstance.appointmentId = this.appointmentId;
680
+ this.currentModalRef.componentInstance.product = this.product;
681
+ this.currentModalRef.componentInstance.tenantId = this.tenantId;
682
+ this.currentModalRef.componentInstance.currentUser = this.currentUser;
683
+ this.currentModalRef.componentInstance.ownerUserIds = this.ownerUserIds;
684
+ this.currentModalRef.componentInstance.regularUserIds = this.regularUserIds;
685
+ this.currentModalRef.componentInstance.moderatorUserIds = this.moderatorUserIds;
686
+ this.currentModalRef.result.then(() => { this.currentModalRef = null; }, () => { this.currentModalRef = null; });
687
+ }
688
+ openVideoCallModal(isReturningFromPip = false) {
689
+ this.videoCallModalRef = this.modalService.open(TasVideocallComponent, {
690
+ size: 'xl',
691
+ windowClass: 'tas-video-modal',
692
+ backdrop: 'static',
693
+ keyboard: false
541
694
  });
695
+ this.videoCallModalRef.componentInstance.sessionId = this.tasService.sessionId;
696
+ this.videoCallModalRef.componentInstance.token = this.tasService.token;
697
+ this.videoCallModalRef.componentInstance.isReturningFromPip = isReturningFromPip;
698
+ this.videoCallModalRef.result.then(() => { this.videoCallModalRef = null; }, () => { this.videoCallModalRef = null; });
542
699
  }
543
700
  }
544
- TasButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasButtonComponent, deps: [{ token: TasService }, { token: i1.NgbModal }, { token: TAS_USER_DATA_PROVIDER, optional: true }], target: i0.ɵɵFactoryTarget.Component });
545
- TasButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: TasButtonComponent, selector: "tas-btn", ngImport: i0, template: "<button\n type=\"button\"\n class=\"btn btn-primary boton\"\n (click)=\"onClick()\"\n [disabled]=\"isLoading\"\n>\n <i class=\"fa fa-video-camera\" aria-hidden=\"true\" *ngIf=\"!isLoading\"></i>\n <span *ngIf=\"!isLoading\"> Iniciar TAS</span>\n <span *ngIf=\"isLoading\"> Processing...</span>\n</button>\n", styles: [":host{display:inline-block}.boton{background-color:#ee316b!important;color:#fff!important;margin-right:24px}.boton:disabled{background-color:#ccc!important;border-color:#ccc!important;cursor:not-allowed}.boton i{margin-right:5px}\n"], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
701
+ TasButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasButtonComponent, deps: [{ token: i1.NgbModal }, { token: TasService }], target: i0.ɵɵFactoryTarget.Component });
702
+ TasButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: TasButtonComponent, selector: "tas-btn", inputs: { appointmentId: "appointmentId", product: "product", tenantId: "tenantId", currentUser: "currentUser", ownerUserIds: "ownerUserIds", regularUserIds: "regularUserIds", moderatorUserIds: "moderatorUserIds" }, ngImport: i0, template: "<button\n\ttype=\"button\"\n\tclass=\"btn btn-primary tas-btn\"\n\t(click)=\"onClick()\"\n\t[disabled]=\"isLoading\"\n>\n\t<i class=\"fa fa-video-camera\" aria-hidden=\"true\" *ngIf=\"!isLoading\"></i>\n\t<span *ngIf=\"!isLoading\"> Iniciar TAS</span>\n\t<span *ngIf=\"isLoading\"> Processing...</span>\n</button>\n\n", styles: [":host{display:inline-block}.tas-btn{background-color:#ee316b!important;color:#fff!important;border-color:#ee316b!important;margin-right:24px}.tas-btn:disabled{background-color:#ccc!important;border-color:#ccc!important;cursor:not-allowed}.tas-btn:hover:not(:disabled){background-color:#d62a5f!important;border-color:#d62a5f!important}.tas-btn i{margin-right:5px}\n"], directives: [{ type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
546
703
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasButtonComponent, decorators: [{
547
704
  type: Component,
548
- args: [{ selector: "tas-btn", template: "<button\n type=\"button\"\n class=\"btn btn-primary boton\"\n (click)=\"onClick()\"\n [disabled]=\"isLoading\"\n>\n <i class=\"fa fa-video-camera\" aria-hidden=\"true\" *ngIf=\"!isLoading\"></i>\n <span *ngIf=\"!isLoading\"> Iniciar TAS</span>\n <span *ngIf=\"isLoading\"> Processing...</span>\n</button>\n", styles: [":host{display:inline-block}.boton{background-color:#ee316b!important;color:#fff!important;margin-right:24px}.boton:disabled{background-color:#ccc!important;border-color:#ccc!important;cursor:not-allowed}.boton i{margin-right:5px}\n"] }]
549
- }], ctorParameters: function () {
550
- return [{ type: TasService }, { type: i1.NgbModal }, { type: undefined, decorators: [{
551
- type: Optional
552
- }, {
553
- type: Inject,
554
- args: [TAS_USER_DATA_PROVIDER]
555
- }] }];
556
- } });
705
+ args: [{ selector: "tas-btn", template: "<button\n\ttype=\"button\"\n\tclass=\"btn btn-primary tas-btn\"\n\t(click)=\"onClick()\"\n\t[disabled]=\"isLoading\"\n>\n\t<i class=\"fa fa-video-camera\" aria-hidden=\"true\" *ngIf=\"!isLoading\"></i>\n\t<span *ngIf=\"!isLoading\"> Iniciar TAS</span>\n\t<span *ngIf=\"isLoading\"> Processing...</span>\n</button>\n\n", styles: [":host{display:inline-block}.tas-btn{background-color:#ee316b!important;color:#fff!important;border-color:#ee316b!important;margin-right:24px}.tas-btn:disabled{background-color:#ccc!important;border-color:#ccc!important;cursor:not-allowed}.tas-btn:hover:not(:disabled){background-color:#d62a5f!important;border-color:#d62a5f!important}.tas-btn i{margin-right:5px}\n"] }]
706
+ }], ctorParameters: function () { return [{ type: i1.NgbModal }, { type: TasService }]; }, propDecorators: { appointmentId: [{
707
+ type: Input
708
+ }], product: [{
709
+ type: Input
710
+ }], tenantId: [{
711
+ type: Input
712
+ }], currentUser: [{
713
+ type: Input
714
+ }], ownerUserIds: [{
715
+ type: Input
716
+ }], regularUserIds: [{
717
+ type: Input
718
+ }], moderatorUserIds: [{
719
+ type: Input
720
+ }] } });
557
721
 
558
722
  class TasFloatingCallComponent {
559
723
  constructor(tasService) {
@@ -561,13 +725,15 @@ class TasFloatingCallComponent {
561
725
  this.isVisible = false;
562
726
  this.isMuted = false;
563
727
  this.subscriptions = new Subscription();
728
+ // Margin from screen edges (in pixels)
729
+ this.PIP_MARGIN = 20;
564
730
  }
565
731
  ngOnInit() {
566
732
  this.setupSubscriptions();
567
733
  }
568
734
  ngOnDestroy() {
569
735
  this.subscriptions.unsubscribe();
570
- interact(".tas-floating-container").unset();
736
+ interact('.tas-floating-container').unset();
571
737
  }
572
738
  // Public Methods
573
739
  onExpand() {
@@ -581,115 +747,171 @@ class TasFloatingCallComponent {
581
747
  }
582
748
  // Private Methods
583
749
  setupSubscriptions() {
584
- this.subscriptions.add(this.tasService.callState$.subscribe((state) => {
750
+ // Call state subscription
751
+ this.subscriptions.add(this.tasService.callState$.subscribe(state => {
585
752
  if (state === CallState.DISCONNECTED) {
586
753
  this.isVisible = false;
587
754
  }
588
755
  }));
589
- this.subscriptions.add(this.tasService.viewMode$.subscribe((mode) => {
590
- this.isVisible =
591
- mode === ViewMode.PIP && this.tasService.isCallActive();
756
+ // View mode subscription
757
+ this.subscriptions.add(this.tasService.viewMode$.subscribe(mode => {
758
+ this.isVisible = mode === ViewMode.PIP && this.tasService.isCallActive();
592
759
  if (this.isVisible) {
593
760
  setTimeout(() => this.initInteract(), 100);
594
761
  }
595
762
  }));
596
- this.subscriptions.add(this.tasService.isMuted$.subscribe((muted) => {
763
+ // Mute state subscription
764
+ this.subscriptions.add(this.tasService.isMuted$.subscribe(muted => {
597
765
  this.isMuted = muted;
598
766
  }));
599
767
  }
600
768
  initInteract() {
601
- interact(".tas-floating-container").unset();
602
- interact(".tas-floating-container").draggable({
769
+ interact('.tas-floating-container').unset();
770
+ // Create restriction area with margin
771
+ const margin = this.PIP_MARGIN;
772
+ const restrictToBodyWithMargin = {
773
+ restriction: () => {
774
+ return {
775
+ left: margin,
776
+ top: margin,
777
+ right: window.innerWidth - margin,
778
+ bottom: window.innerHeight - margin
779
+ };
780
+ },
781
+ elementRect: { left: 0, right: 1, top: 0, bottom: 1 }
782
+ };
783
+ interact('.tas-floating-container')
784
+ .draggable({
603
785
  inertia: true,
604
786
  modifiers: [
605
- interact.modifiers.restrictRect({
606
- restriction: "body",
607
- endOnly: true,
608
- }),
787
+ interact.modifiers.restrict(restrictToBodyWithMargin)
609
788
  ],
610
789
  autoScroll: false,
611
790
  listeners: {
612
791
  move: (event) => {
613
792
  const target = event.target;
614
- const x = (parseFloat(target.getAttribute("data-x")) || 0) + event.dx;
615
- const y = (parseFloat(target.getAttribute("data-y")) || 0) + event.dy;
793
+ const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
794
+ const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
795
+ target.style.transform = `translate(${x}px, ${y}px)`;
796
+ target.setAttribute('data-x', String(x));
797
+ target.setAttribute('data-y', String(y));
798
+ }
799
+ }
800
+ })
801
+ .resizable({
802
+ edges: { left: false, right: true, bottom: true, top: false },
803
+ listeners: {
804
+ move: (event) => {
805
+ const target = event.target;
806
+ let x = parseFloat(target.getAttribute('data-x')) || 0;
807
+ let y = parseFloat(target.getAttribute('data-y')) || 0;
808
+ // Update element size
809
+ target.style.width = `${event.rect.width}px`;
810
+ target.style.height = `${event.rect.height}px`;
811
+ // Translate when resizing from top or left edges
812
+ x += event.deltaRect.left;
813
+ y += event.deltaRect.top;
616
814
  target.style.transform = `translate(${x}px, ${y}px)`;
617
- target.setAttribute("data-x", String(x));
618
- target.setAttribute("data-y", String(y));
619
- },
815
+ target.setAttribute('data-x', String(x));
816
+ target.setAttribute('data-y', String(y));
817
+ }
620
818
  },
819
+ modifiers: [
820
+ interact.modifiers.restrictEdges({
821
+ outer: {
822
+ left: margin,
823
+ top: margin,
824
+ right: window.innerWidth - margin,
825
+ bottom: window.innerHeight - margin
826
+ }
827
+ }),
828
+ interact.modifiers.restrictSize({
829
+ min: { width: 200, height: 130 },
830
+ max: { width: 500, height: 350 }
831
+ }),
832
+ interact.modifiers.aspectRatio({ ratio: 'preserve' })
833
+ ],
834
+ inertia: true
621
835
  });
622
836
  }
623
837
  }
624
838
  TasFloatingCallComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasFloatingCallComponent, deps: [{ token: TasService }], target: i0.ɵɵFactoryTarget.Component });
625
- TasFloatingCallComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: TasFloatingCallComponent, selector: "tas-floating-call", ngImport: i0, template: "<div class=\"tas-floating-container\" [class.visible]=\"isVisible\">\n <div class=\"floating-content\">\n <div id=\"pip-main-video\" class=\"pip-main-video\"></div>\n\n <div class=\"floating-controls\">\n <button\n class=\"action-btn expand-btn\"\n (click)=\"onExpand()\"\n title=\"Expandir a pantalla completa\"\n >\n <i class=\"fa fa-expand\"></i>\n </button>\n <button\n class=\"action-btn mute-btn\"\n [class.muted]=\"isMuted\"\n (click)=\"toggleMute()\"\n [title]=\"isMuted ? 'Activar micr\u00F3fono' : 'Silenciar micr\u00F3fono'\"\n >\n <i\n class=\"fa\"\n [class.fa-microphone]=\"!isMuted\"\n [class.fa-microphone-slash]=\"isMuted\"\n ></i>\n </button>\n <button\n class=\"action-btn hangup-btn\"\n (click)=\"onHangUp()\"\n title=\"Colgar llamada\"\n >\n <i class=\"fa fa-phone\" style=\"transform: rotate(135deg)\"></i>\n </button>\n </div>\n </div>\n</div>\n", styles: [".tas-floating-container{position:fixed;bottom:20px;right:20px;width:280px;height:180px;background:#000;border-radius:12px;box-shadow:0 8px 32px #00000080;z-index:9999;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none;transition:opacity .3s ease,visibility .3s ease;opacity:0;visibility:hidden;pointer-events:none}.tas-floating-container.visible{opacity:1;visibility:visible;pointer-events:auto}.floating-content{position:relative;width:100%;height:100%;overflow:hidden}.pip-main-video{position:absolute;top:0;left:0;width:100%;height:100%;background:#000}.pip-main-video ::ng-deep video{width:100%;height:100%;object-fit:cover}.pip-main-video ::ng-deep .OT_subscriber,.pip-main-video ::ng-deep .OT_publisher{width:100%!important;height:100%!important}.pip-main-video ::ng-deep .OT_edge-bar-item,.pip-main-video ::ng-deep .OT_mute,.pip-main-video ::ng-deep .OT_audio-level-meter,.pip-main-video ::ng-deep .OT_bar,.pip-main-video ::ng-deep .OT_name{display:none!important}.floating-controls{position:absolute;bottom:10px;left:50%;transform:translate(-50%);display:flex;gap:12px;padding:6px 14px;background:rgba(0,0,0,.7);border-radius:24px;backdrop-filter:blur(8px)}.action-btn{width:32px;height:32px;border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:13px;transition:all .2s ease}.action-btn.expand-btn{background:rgba(255,255,255,.2);color:#fff}.action-btn.expand-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.1)}.action-btn.mute-btn{background:rgba(255,255,255,.2);color:#fff}.action-btn.mute-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.1)}.action-btn.mute-btn.muted{background:#f39c12;color:#fff}.action-btn.mute-btn.muted:hover{background:#e67e22}.action-btn.hangup-btn{background:#dc3545;color:#fff}.action-btn.hangup-btn:hover{background:#c82333;transform:scale(1.1)}\n"] });
839
+ TasFloatingCallComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: TasFloatingCallComponent, selector: "tas-floating-call", ngImport: i0, template: "<div class=\"tas-floating-container\" [class.visible]=\"isVisible\">\n\t<!-- Video content area - shows main video only -->\n\t<div class=\"floating-content\">\n\t\t<!-- Main video container (subscriber if available, otherwise publisher) -->\n\t\t<div id=\"pip-main-video\" class=\"pip-main-video\"></div>\n\n\t\t<!-- Bottom controls -->\n\t\t<div class=\"floating-controls\">\n\t\t\t<button class=\"action-btn expand-btn\" (click)=\"onExpand()\" title=\"Expand to fullscreen\">\n\t\t\t\t<i class=\"fa fa-expand\"></i>\n\t\t\t</button>\n\t\t\t<button class=\"action-btn mute-btn\" [class.muted]=\"isMuted\" (click)=\"toggleMute()\" [title]=\"isMuted ? 'Unmute microphone' : 'Mute microphone'\">\n\t\t\t\t<i class=\"fa\" [class.fa-microphone]=\"!isMuted\" [class.fa-microphone-slash]=\"isMuted\"></i>\n\t\t\t</button>\n\t\t\t<button class=\"action-btn hangup-btn\" (click)=\"onHangUp()\" title=\"Hang up call\">\n\t\t\t\t<i class=\"fa fa-phone\" style=\"transform: rotate(135deg);\"></i>\n\t\t\t</button>\n\t\t</div>\n\t</div>\n</div>\n\n", styles: [".tas-floating-container{position:fixed;bottom:20px;right:20px;width:280px;height:180px;background:#000;border-radius:12px;box-shadow:0 8px 32px #00000080;z-index:9999;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none;transition:opacity .3s ease,visibility .3s ease,box-shadow .2s ease;opacity:0;visibility:hidden;pointer-events:none}.tas-floating-container.visible{opacity:1;visibility:visible;pointer-events:auto}.tas-floating-container:hover{box-shadow:0 8px 32px #00000080,0 0 0 2px #ffffff4d}.floating-content{position:relative;width:100%;height:100%;overflow:hidden}.pip-main-video{position:absolute;top:0;left:0;width:100%;height:100%;background:#000}.pip-main-video ::ng-deep video{width:100%;height:100%;object-fit:cover}.pip-main-video ::ng-deep .OT_subscriber,.pip-main-video ::ng-deep .OT_publisher{width:100%!important;height:100%!important}.pip-main-video ::ng-deep .OT_edge-bar-item,.pip-main-video ::ng-deep .OT_mute,.pip-main-video ::ng-deep .OT_audio-level-meter,.pip-main-video ::ng-deep .OT_bar,.pip-main-video ::ng-deep .OT_name{display:none!important}.floating-controls{position:absolute;bottom:10px;left:50%;transform:translate(-50%);display:flex;gap:12px;padding:6px 14px;background:rgba(0,0,0,.7);border-radius:24px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);opacity:0;visibility:hidden;transition:opacity .3s ease,visibility .3s ease}.tas-floating-container:hover .floating-controls{opacity:1;visibility:visible}.action-btn{width:32px;height:32px;border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:13px;transition:all .2s ease}.action-btn.expand-btn{background:rgba(255,255,255,.2);color:#fff}.action-btn.expand-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.1)}.action-btn.mute-btn{background:rgba(255,255,255,.2);color:#fff}.action-btn.mute-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.1)}.action-btn.mute-btn.muted{background:#f39c12;color:#fff}.action-btn.mute-btn.muted:hover{background:#e67e22}.action-btn.hangup-btn{background:#dc3545;color:#fff}.action-btn.hangup-btn:hover{background:#c82333;transform:scale(1.1)}\n"] });
626
840
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasFloatingCallComponent, decorators: [{
627
841
  type: Component,
628
- args: [{ selector: "tas-floating-call", template: "<div class=\"tas-floating-container\" [class.visible]=\"isVisible\">\n <div class=\"floating-content\">\n <div id=\"pip-main-video\" class=\"pip-main-video\"></div>\n\n <div class=\"floating-controls\">\n <button\n class=\"action-btn expand-btn\"\n (click)=\"onExpand()\"\n title=\"Expandir a pantalla completa\"\n >\n <i class=\"fa fa-expand\"></i>\n </button>\n <button\n class=\"action-btn mute-btn\"\n [class.muted]=\"isMuted\"\n (click)=\"toggleMute()\"\n [title]=\"isMuted ? 'Activar micr\u00F3fono' : 'Silenciar micr\u00F3fono'\"\n >\n <i\n class=\"fa\"\n [class.fa-microphone]=\"!isMuted\"\n [class.fa-microphone-slash]=\"isMuted\"\n ></i>\n </button>\n <button\n class=\"action-btn hangup-btn\"\n (click)=\"onHangUp()\"\n title=\"Colgar llamada\"\n >\n <i class=\"fa fa-phone\" style=\"transform: rotate(135deg)\"></i>\n </button>\n </div>\n </div>\n</div>\n", styles: [".tas-floating-container{position:fixed;bottom:20px;right:20px;width:280px;height:180px;background:#000;border-radius:12px;box-shadow:0 8px 32px #00000080;z-index:9999;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none;transition:opacity .3s ease,visibility .3s ease;opacity:0;visibility:hidden;pointer-events:none}.tas-floating-container.visible{opacity:1;visibility:visible;pointer-events:auto}.floating-content{position:relative;width:100%;height:100%;overflow:hidden}.pip-main-video{position:absolute;top:0;left:0;width:100%;height:100%;background:#000}.pip-main-video ::ng-deep video{width:100%;height:100%;object-fit:cover}.pip-main-video ::ng-deep .OT_subscriber,.pip-main-video ::ng-deep .OT_publisher{width:100%!important;height:100%!important}.pip-main-video ::ng-deep .OT_edge-bar-item,.pip-main-video ::ng-deep .OT_mute,.pip-main-video ::ng-deep .OT_audio-level-meter,.pip-main-video ::ng-deep .OT_bar,.pip-main-video ::ng-deep .OT_name{display:none!important}.floating-controls{position:absolute;bottom:10px;left:50%;transform:translate(-50%);display:flex;gap:12px;padding:6px 14px;background:rgba(0,0,0,.7);border-radius:24px;backdrop-filter:blur(8px)}.action-btn{width:32px;height:32px;border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:13px;transition:all .2s ease}.action-btn.expand-btn{background:rgba(255,255,255,.2);color:#fff}.action-btn.expand-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.1)}.action-btn.mute-btn{background:rgba(255,255,255,.2);color:#fff}.action-btn.mute-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.1)}.action-btn.mute-btn.muted{background:#f39c12;color:#fff}.action-btn.mute-btn.muted:hover{background:#e67e22}.action-btn.hangup-btn{background:#dc3545;color:#fff}.action-btn.hangup-btn:hover{background:#c82333;transform:scale(1.1)}\n"] }]
842
+ args: [{ selector: 'tas-floating-call', template: "<div class=\"tas-floating-container\" [class.visible]=\"isVisible\">\n\t<!-- Video content area - shows main video only -->\n\t<div class=\"floating-content\">\n\t\t<!-- Main video container (subscriber if available, otherwise publisher) -->\n\t\t<div id=\"pip-main-video\" class=\"pip-main-video\"></div>\n\n\t\t<!-- Bottom controls -->\n\t\t<div class=\"floating-controls\">\n\t\t\t<button class=\"action-btn expand-btn\" (click)=\"onExpand()\" title=\"Expand to fullscreen\">\n\t\t\t\t<i class=\"fa fa-expand\"></i>\n\t\t\t</button>\n\t\t\t<button class=\"action-btn mute-btn\" [class.muted]=\"isMuted\" (click)=\"toggleMute()\" [title]=\"isMuted ? 'Unmute microphone' : 'Mute microphone'\">\n\t\t\t\t<i class=\"fa\" [class.fa-microphone]=\"!isMuted\" [class.fa-microphone-slash]=\"isMuted\"></i>\n\t\t\t</button>\n\t\t\t<button class=\"action-btn hangup-btn\" (click)=\"onHangUp()\" title=\"Hang up call\">\n\t\t\t\t<i class=\"fa fa-phone\" style=\"transform: rotate(135deg);\"></i>\n\t\t\t</button>\n\t\t</div>\n\t</div>\n</div>\n\n", styles: [".tas-floating-container{position:fixed;bottom:20px;right:20px;width:280px;height:180px;background:#000;border-radius:12px;box-shadow:0 8px 32px #00000080;z-index:9999;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none;transition:opacity .3s ease,visibility .3s ease,box-shadow .2s ease;opacity:0;visibility:hidden;pointer-events:none}.tas-floating-container.visible{opacity:1;visibility:visible;pointer-events:auto}.tas-floating-container:hover{box-shadow:0 8px 32px #00000080,0 0 0 2px #ffffff4d}.floating-content{position:relative;width:100%;height:100%;overflow:hidden}.pip-main-video{position:absolute;top:0;left:0;width:100%;height:100%;background:#000}.pip-main-video ::ng-deep video{width:100%;height:100%;object-fit:cover}.pip-main-video ::ng-deep .OT_subscriber,.pip-main-video ::ng-deep .OT_publisher{width:100%!important;height:100%!important}.pip-main-video ::ng-deep .OT_edge-bar-item,.pip-main-video ::ng-deep .OT_mute,.pip-main-video ::ng-deep .OT_audio-level-meter,.pip-main-video ::ng-deep .OT_bar,.pip-main-video ::ng-deep .OT_name{display:none!important}.floating-controls{position:absolute;bottom:10px;left:50%;transform:translate(-50%);display:flex;gap:12px;padding:6px 14px;background:rgba(0,0,0,.7);border-radius:24px;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);opacity:0;visibility:hidden;transition:opacity .3s ease,visibility .3s ease}.tas-floating-container:hover .floating-controls{opacity:1;visibility:visible}.action-btn{width:32px;height:32px;border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:13px;transition:all .2s ease}.action-btn.expand-btn{background:rgba(255,255,255,.2);color:#fff}.action-btn.expand-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.1)}.action-btn.mute-btn{background:rgba(255,255,255,.2);color:#fff}.action-btn.mute-btn:hover{background:rgba(255,255,255,.35);transform:scale(1.1)}.action-btn.mute-btn.muted{background:#f39c12;color:#fff}.action-btn.mute-btn.muted:hover{background:#e67e22}.action-btn.hangup-btn{background:#dc3545;color:#fff}.action-btn.hangup-btn:hover{background:#c82333;transform:scale(1.1)}\n"] }]
629
843
  }], ctorParameters: function () { return [{ type: TasService }]; } });
630
844
 
631
- class TasModule {
845
+ class TasUellSdkModule {
632
846
  /**
633
- * Use this method in your app module to configure the TAS SDK
847
+ * Use forRoot() to configure the TAS SDK module with required dependencies.
848
+ * This should only be called once in your root AppModule.
634
849
  *
635
850
  * @example
636
851
  * ```typescript
637
- * TasModule.forRoot({
638
- * config: {
639
- * tokBoxApiKey: environment.tokBox,
640
- * apiBaseUrl: environment.apiUrl
641
- * },
642
- * httpClient: MyHttpClientAdapter,
643
- * userDataProvider: MyUserDataProvider
852
+ * import { TasUellSdkModule, TasHttpClient } from 'tas-uell-sdk';
853
+ *
854
+ * // Create an adapter that implements TasHttpClient
855
+ * @Injectable({ providedIn: 'root' })
856
+ * export class MyHttpAdapter implements TasHttpClient {
857
+ * constructor(private http: HttpClient) {}
858
+ * post<T>(url: string, options: { body: any; headers?: Record<string, string> }): Observable<T> {
859
+ * return this.http.post<T>(url, options.body, { headers: options.headers });
860
+ * }
861
+ * }
862
+ *
863
+ * // In your AppModule
864
+ * @NgModule({
865
+ * imports: [
866
+ * TasUellSdkModule.forRoot({
867
+ * config: { tokBoxApiKey: 'YOUR_TOKBOX_API_KEY' },
868
+ * httpClient: MyHttpAdapter
869
+ * })
870
+ * ]
644
871
  * })
872
+ * export class AppModule { }
645
873
  * ```
646
874
  */
647
- static forRoot(moduleConfig) {
648
- const providers = [
649
- {
650
- provide: TAS_CONFIG,
651
- useValue: moduleConfig.config,
652
- },
653
- ];
654
- if (moduleConfig.httpClient) {
655
- providers.push({
656
- provide: TAS_HTTP_CLIENT,
657
- useClass: moduleConfig.httpClient,
658
- });
659
- }
660
- if (moduleConfig.userDataProvider) {
661
- providers.push({
662
- provide: TAS_USER_DATA_PROVIDER,
663
- useClass: moduleConfig.userDataProvider,
664
- });
665
- }
875
+ static forRoot(options) {
666
876
  return {
667
- ngModule: TasModule,
668
- providers,
877
+ ngModule: TasUellSdkModule,
878
+ providers: [
879
+ { provide: TAS_CONFIG, useValue: options.config },
880
+ { provide: TAS_HTTP_CLIENT, useClass: options.httpClient },
881
+ TasService
882
+ ]
669
883
  };
670
884
  }
671
885
  }
672
- TasModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
673
- TasModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasModule, declarations: [TasButtonComponent,
886
+ TasUellSdkModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasUellSdkModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
887
+ TasUellSdkModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasUellSdkModule, declarations: [TasButtonComponent,
674
888
  TasVideocallComponent,
675
- TasFloatingCallComponent], imports: [CommonModule, NgbModalModule], exports: [TasButtonComponent,
889
+ TasFloatingCallComponent,
890
+ TasWaitingRoomComponent], imports: [CommonModule], exports: [TasButtonComponent,
676
891
  TasVideocallComponent,
677
- TasFloatingCallComponent] });
678
- TasModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasModule, imports: [[CommonModule, NgbModalModule]] });
679
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasModule, decorators: [{
892
+ TasFloatingCallComponent,
893
+ TasWaitingRoomComponent] });
894
+ TasUellSdkModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasUellSdkModule, imports: [[
895
+ CommonModule,
896
+ ]] });
897
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasUellSdkModule, decorators: [{
680
898
  type: NgModule,
681
899
  args: [{
682
900
  declarations: [
683
901
  TasButtonComponent,
684
902
  TasVideocallComponent,
685
903
  TasFloatingCallComponent,
904
+ TasWaitingRoomComponent
905
+ ],
906
+ imports: [
907
+ CommonModule,
686
908
  ],
687
- imports: [CommonModule, NgbModalModule],
688
909
  exports: [
689
910
  TasButtonComponent,
690
911
  TasVideocallComponent,
691
912
  TasFloatingCallComponent,
692
- ],
913
+ TasWaitingRoomComponent
914
+ ]
693
915
  }]
694
916
  }] });
695
917
 
@@ -701,5 +923,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
701
923
  * Generated bundle index. Do not edit.
702
924
  */
703
925
 
704
- export { CallState, TAS_CONFIG, TAS_HTTP_CLIENT, TAS_USER_DATA_PROVIDER, TasButtonComponent, TasFloatingCallComponent, TasModule, TasService, TasVideocallComponent, ViewMode };
926
+ export { CallState, TAS_CONFIG, TAS_HTTP_CLIENT, TasButtonComponent, TasFloatingCallComponent, TasRoomType, TasService, TasSessionType, TasUellSdkModule, TasUserRole, TasVideocallComponent, TasWaitingRoomComponent, ViewMode, WaitingRoomState };
705
927
  //# sourceMappingURL=tas-uell-sdk.mjs.map