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() {
@@ -91,7 +102,14 @@ class TasService {
91
102
  toggleMute() {
92
103
  if (this.publisher) {
93
104
  const newMuteState = !this.isMutedSubject.getValue();
94
- this.publisher.publishAudio(!newMuteState);
105
+ const shouldEnableAudio = !newMuteState;
106
+ // Use OpenTok's publishAudio
107
+ this.publisher.publishAudio(shouldEnableAudio);
108
+ // Also directly control the MediaStreamTrack as backup
109
+ const audioTrack = this.publisher.getAudioSource?.();
110
+ if (audioTrack) {
111
+ audioTrack.enabled = shouldEnableAudio;
112
+ }
95
113
  this.isMutedSubject.next(newMuteState);
96
114
  }
97
115
  }
@@ -111,7 +129,7 @@ class TasService {
111
129
  this.subscribers = [];
112
130
  this.currentSessionId = null;
113
131
  this.currentToken = null;
114
- this.isMutedSubject.next(false);
132
+ this.isMutedSubject.next(false); // Reset mute state
115
133
  this.viewModeSubject.next(ViewMode.FULLSCREEN);
116
134
  this.callStateSubject.next(CallState.DISCONNECTED);
117
135
  }
@@ -120,21 +138,13 @@ class TasService {
120
138
  }
121
139
  // API Methods
122
140
  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) => {
141
+ return this.httpClient.post("v2/room", { body: payload, headers: {} }).pipe(map((response) => response), catchError((error) => {
128
142
  console.error("TAS Service: createRoom failed", error);
129
143
  throw error;
130
144
  }));
131
145
  }
132
146
  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) => {
147
+ return this.httpClient.post("v2/room/token", { body: payload, headers: {} }).pipe(map((response) => response), catchError((error) => {
138
148
  console.error("TAS Service: generateToken failed", error);
139
149
  throw error;
140
150
  }));
@@ -147,11 +157,6 @@ class TasService {
147
157
  this.currentSessionId = sessionId;
148
158
  this.currentToken = token;
149
159
  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
160
  if (!OT.checkSystemRequirements()) {
156
161
  this.callStateSubject.next(CallState.ERROR);
157
162
  reject(new Error("Browser not compatible with TokBox"));
@@ -260,25 +265,23 @@ class TasService {
260
265
  * Moves videos back to fullscreen containers
261
266
  */
262
267
  moveVideosToFullscreen() {
263
- this.moveSubscribersTo("subscriber-container");
264
- this.movePublisherTo("publisher-container");
268
+ this.moveSubscribersTo('subscriber-container');
269
+ this.movePublisherTo('publisher-container');
265
270
  }
266
271
  }
267
- 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 });
272
+ 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 });
268
273
  TasService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasService, providedIn: "root" });
269
274
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasService, decorators: [{
270
275
  type: Injectable,
271
- args: [{ providedIn: "root" }]
276
+ args: [{
277
+ providedIn: "root",
278
+ }]
272
279
  }], ctorParameters: function () { return [{ type: undefined, decorators: [{
273
- type: Optional
274
- }, {
275
280
  type: Inject,
276
- args: [TAS_CONFIG]
281
+ args: [TAS_HTTP_CLIENT]
277
282
  }] }, { type: undefined, decorators: [{
278
- type: Optional
279
- }, {
280
283
  type: Inject,
281
- args: [TAS_HTTP_CLIENT]
284
+ args: [TAS_CONFIG]
282
285
  }] }]; } });
283
286
 
284
287
  class TasVideocallComponent {
@@ -300,10 +303,11 @@ class TasVideocallComponent {
300
303
  }
301
304
  ngOnDestroy() {
302
305
  this.subscriptions.unsubscribe();
306
+ // Only disconnect if not in PiP mode (keep session alive for floating window)
303
307
  if (!this.tasService.isPipMode()) {
304
308
  this.tasService.disconnectSession();
305
309
  }
306
- interact(".publisher-view").unset();
310
+ interact('.publisher-view').unset();
307
311
  }
308
312
  // Public Methods
309
313
  hangUp() {
@@ -313,7 +317,8 @@ class TasVideocallComponent {
313
317
  this.tasService.toggleMute();
314
318
  }
315
319
  minimize() {
316
- this.tasService.moveMainVideoTo("pip-main-video");
320
+ this.tasService.moveMainVideoTo('pip-main-video');
321
+ // Small delay to ensure video is moved before closing modal
317
322
  setTimeout(() => this.tasService.enterPipMode(), 50);
318
323
  }
319
324
  toggleSwap() {
@@ -326,30 +331,33 @@ class TasVideocallComponent {
326
331
  }
327
332
  // Private Methods
328
333
  setupSubscriptions() {
329
- this.subscriptions.add(this.tasService.callState$.subscribe((state) => {
334
+ // Call state subscription
335
+ this.subscriptions.add(this.tasService.callState$.subscribe(state => {
330
336
  this.callState = state;
331
337
  if (state === CallState.DISCONNECTED) {
332
- this.activeModal.close("hangup");
338
+ this.activeModal.close('hangup');
333
339
  }
334
340
  }));
335
- this.subscriptions.add(this.tasService.viewMode$.subscribe((mode) => {
341
+ // View mode subscription
342
+ this.subscriptions.add(this.tasService.viewMode$.subscribe(mode => {
336
343
  if (mode === ViewMode.PIP) {
337
- this.activeModal.close("pip");
344
+ this.activeModal.close('pip');
338
345
  }
339
346
  }));
340
- this.subscriptions.add(this.tasService.isMuted$.subscribe((muted) => {
347
+ // Mute state subscription
348
+ this.subscriptions.add(this.tasService.isMuted$.subscribe(muted => {
341
349
  this.isMuted = muted;
342
350
  }));
343
351
  }
344
352
  initializeCall() {
345
353
  if (this.isReturningFromPip) {
354
+ // Returning from PiP - just move videos back
346
355
  setTimeout(() => this.tasService.moveVideosToFullscreen(), 100);
347
356
  }
348
357
  else if (this.sessionId && this.token) {
349
- this.tasService
350
- .connectSession(this.sessionId, this.token, "publisher-container", "subscriber-container")
351
- .catch((err) => {
352
- console.error("Error connecting to video call:", err);
358
+ // New call - connect to session
359
+ this.tasService.connectSession(this.sessionId, this.token, 'publisher-container', 'subscriber-container').catch(err => {
360
+ console.error('Error connecting to video call:', err);
353
361
  });
354
362
  }
355
363
  }
@@ -357,69 +365,69 @@ class TasVideocallComponent {
357
365
  const publisherEl = this.publisherContainer?.nativeElement;
358
366
  const subscriberEl = this.subscriberContainer?.nativeElement;
359
367
  if (publisherEl) {
360
- publisherEl.removeAttribute("style");
361
- publisherEl.removeAttribute("data-x");
362
- publisherEl.removeAttribute("data-y");
368
+ publisherEl.removeAttribute('style');
369
+ publisherEl.removeAttribute('data-x');
370
+ publisherEl.removeAttribute('data-y');
363
371
  }
364
372
  if (subscriberEl) {
365
- subscriberEl.removeAttribute("style");
366
- subscriberEl.removeAttribute("data-x");
367
- subscriberEl.removeAttribute("data-y");
373
+ subscriberEl.removeAttribute('style');
374
+ subscriberEl.removeAttribute('data-x');
375
+ subscriberEl.removeAttribute('data-y');
368
376
  }
369
377
  }
370
378
  initInteract() {
371
- interact(".publisher-view").unset();
372
- interact(".publisher-view")
379
+ interact('.publisher-view').unset();
380
+ interact('.publisher-view')
373
381
  .draggable({
374
382
  inertia: true,
375
383
  modifiers: [
376
384
  interact.modifiers.restrictRect({
377
- restriction: "parent",
378
- endOnly: true,
379
- }),
385
+ restriction: 'parent',
386
+ endOnly: true
387
+ })
380
388
  ],
381
389
  autoScroll: true,
382
390
  listeners: {
383
391
  move: (event) => {
384
392
  const target = event.target;
385
- const x = (parseFloat(target.getAttribute("data-x")) || 0) + event.dx;
386
- const y = (parseFloat(target.getAttribute("data-y")) || 0) + event.dy;
393
+ const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
394
+ const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
387
395
  target.style.transform = `translate(${x}px, ${y}px)`;
388
- target.setAttribute("data-x", String(x));
389
- target.setAttribute("data-y", String(y));
390
- },
391
- },
396
+ target.setAttribute('data-x', String(x));
397
+ target.setAttribute('data-y', String(y));
398
+ }
399
+ }
392
400
  })
393
401
  .resizable({
394
402
  edges: { left: false, right: true, bottom: true, top: false },
395
403
  listeners: {
396
404
  move: (event) => {
397
405
  const target = event.target;
398
- let x = parseFloat(target.getAttribute("data-x")) || 0;
399
- let y = parseFloat(target.getAttribute("data-y")) || 0;
406
+ let x = parseFloat(target.getAttribute('data-x')) || 0;
407
+ let y = parseFloat(target.getAttribute('data-y')) || 0;
400
408
  target.style.width = `${event.rect.width}px`;
401
409
  target.style.height = `${event.rect.height}px`;
402
410
  x += event.deltaRect.left;
403
411
  y += event.deltaRect.top;
404
412
  target.style.transform = `translate(${x}px, ${y}px)`;
405
- target.setAttribute("data-x", String(x));
406
- target.setAttribute("data-y", String(y));
407
- },
413
+ target.setAttribute('data-x', String(x));
414
+ target.setAttribute('data-y', String(y));
415
+ }
408
416
  },
409
417
  modifiers: [
410
- interact.modifiers.restrictEdges({ outer: "parent" }),
418
+ interact.modifiers.restrictEdges({ outer: 'parent' }),
411
419
  interact.modifiers.restrictSize({ min: { width: 150, height: 100 } }),
412
- interact.modifiers.aspectRatio({ ratio: "preserve" }),
420
+ interact.modifiers.aspectRatio({ ratio: 'preserve' })
413
421
  ],
414
- inertia: true,
422
+ inertia: true
415
423
  });
416
424
  }
417
425
  }
418
426
  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 });
419
- 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"] });
427
+ 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"] });
420
428
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasVideocallComponent, decorators: [{
421
429
  type: Component,
422
- 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"] }]
430
+ 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"] }]
423
431
  }], ctorParameters: function () { return [{ type: i1.NgbActiveModal }, { type: TasService }]; }, propDecorators: { sessionId: [{
424
432
  type: Input
425
433
  }], token: [{
@@ -428,124 +436,279 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
428
436
  type: Input
429
437
  }], publisherContainer: [{
430
438
  type: ViewChild,
431
- args: ["publisherContainer"]
439
+ args: ['publisherContainer']
432
440
  }], subscriberContainer: [{
433
441
  type: ViewChild,
434
- args: ["subscriberContainer"]
442
+ args: ['subscriberContainer']
435
443
  }] } });
436
444
 
437
- class TasButtonComponent {
438
- constructor(tasService, modalService, userDataProvider) {
445
+ var WaitingRoomState;
446
+ (function (WaitingRoomState) {
447
+ WaitingRoomState["IDLE"] = "IDLE";
448
+ WaitingRoomState["CREATING_ROOM"] = "CREATING_ROOM";
449
+ WaitingRoomState["GETTING_TOKEN"] = "GETTING_TOKEN";
450
+ WaitingRoomState["READY"] = "READY";
451
+ WaitingRoomState["ERROR"] = "ERROR";
452
+ })(WaitingRoomState || (WaitingRoomState = {}));
453
+ class TasWaitingRoomComponent {
454
+ constructor(activeModal, tasService, modalService) {
455
+ this.activeModal = activeModal;
439
456
  this.tasService = tasService;
440
457
  this.modalService = modalService;
441
- this.userDataProvider = userDataProvider;
442
- this.isLoading = false;
443
- this.userData = null;
444
- this.tenantId = null;
458
+ // Room configuration inputs
459
+ this.appointmentId = 1;
460
+ this.product = 'uell';
461
+ this.tenantId = '';
462
+ this.regularUserIds = [];
463
+ this.moderatorUserIds = [];
464
+ // Component state
465
+ this.state = WaitingRoomState.IDLE;
466
+ this.WaitingRoomState = WaitingRoomState; // Expose enum to template
467
+ this.errorMessage = '';
468
+ // Session data
469
+ this.sessionId = '';
470
+ this.token = '';
471
+ this.users = [];
445
472
  this.subscriptions = new Subscription();
446
- this.currentModalRef = null;
473
+ this.videoCallModalRef = null;
447
474
  }
448
475
  ngOnInit() {
449
- this.loadUserData();
476
+ this.buildUsersArray();
450
477
  this.setupViewModeSubscription();
451
478
  }
452
479
  ngOnDestroy() {
453
480
  this.subscriptions.unsubscribe();
454
481
  }
455
- onClick() {
456
- if (!this.userData || !this.tenantId) {
457
- console.error("User data or tenant ID not available");
482
+ /**
483
+ * Creates the room and fetches the token
484
+ */
485
+ createRoom() {
486
+ if (!this.tenantId || !this.currentUser?.name) {
487
+ this.state = WaitingRoomState.ERROR;
488
+ this.errorMessage = 'Missing configuration data (tenant or user)';
458
489
  return;
459
490
  }
460
- this.isLoading = true;
461
- this.subscriptions.add(this.tasService
462
- .createRoom({
491
+ this.state = WaitingRoomState.CREATING_ROOM;
492
+ this.errorMessage = '';
493
+ const body = {
494
+ roomType: TasRoomType.TAS,
495
+ type: TasSessionType.SPONTANEOUS,
463
496
  tenant: this.tenantId,
464
- userId: this.userData.id.toString(),
465
- product: "Uell",
466
- record: true,
467
- roomType: "TAS",
468
- type: "SPONTANEOUS",
469
- })
470
- .pipe(switchMap((response) => {
471
- const sessionId = response.content.sessionId;
472
- return this.tasService
473
- .generateToken({
474
- sessionId: sessionId,
475
- name: this.userData.name,
476
- lastname: this.userData.surname,
477
- })
478
- .pipe(switchMap((tokenResponse) => {
479
- return [
480
- {
481
- sessionId,
482
- token: tokenResponse.content.token,
483
- },
484
- ];
485
- }));
486
- }))
487
- .subscribe({
488
- next: ({ sessionId, token }) => {
489
- this.isLoading = false;
490
- this.openVideoCallModal(sessionId, token);
497
+ appointmentId: this.appointmentId,
498
+ users: this.users,
499
+ product: this.product
500
+ };
501
+ this.subscriptions.add(this.tasService.createRoom(body).pipe(switchMap(response => {
502
+ this.sessionId = response.content.sessionId;
503
+ this.state = WaitingRoomState.GETTING_TOKEN;
504
+ return this.tasService.generateToken({
505
+ sessionId: this.sessionId,
506
+ name: this.currentUser.name,
507
+ lastname: this.currentUser.lastname,
508
+ roleVC: this.currentUser.role
509
+ });
510
+ })).subscribe({
511
+ next: (tokenResponse) => {
512
+ this.token = tokenResponse.content.token;
513
+ this.state = WaitingRoomState.READY;
491
514
  },
492
515
  error: (err) => {
493
- console.error("Error starting video call:", err);
494
- this.isLoading = false;
495
- },
516
+ console.error('Error creating room or getting token:', err);
517
+ this.state = WaitingRoomState.ERROR;
518
+ this.errorMessage = 'Error creating room. Please try again.';
519
+ }
496
520
  }));
497
521
  }
498
- // Private Methods
499
- loadUserData() {
500
- if (!this.userDataProvider) {
501
- console.warn("TasButtonComponent: UserDataProvider not configured");
522
+ /**
523
+ * Joins the video call session
524
+ */
525
+ joinSession() {
526
+ if (!this.sessionId || !this.token) {
527
+ this.errorMessage = 'Cannot join session. Incomplete data.';
502
528
  return;
503
529
  }
504
- this.userData = this.userDataProvider.getUserData();
505
- this.tenantId = this.userDataProvider.getTenantId();
530
+ // Close waiting room and open video call
531
+ this.activeModal.close('joining');
532
+ this.openVideoCallModal();
533
+ }
534
+ /**
535
+ * Closes the waiting room
536
+ */
537
+ cancel() {
538
+ this.activeModal.dismiss('cancel');
539
+ }
540
+ /**
541
+ * Retry after an error
542
+ */
543
+ retry() {
544
+ this.state = WaitingRoomState.IDLE;
545
+ this.errorMessage = '';
546
+ this.sessionId = '';
547
+ this.token = '';
548
+ }
549
+ // Private Methods
550
+ buildUsersArray() {
551
+ this.users = [];
552
+ // Add owners from input
553
+ this.ownerUserIds.forEach(id => {
554
+ this.users.push({ userExternalId: id, rol: TasUserRole.OWNER });
555
+ });
556
+ // Add regular users from input
557
+ this.regularUserIds.forEach(id => {
558
+ this.users.push({ userExternalId: id, rol: TasUserRole.USER });
559
+ });
560
+ // Add moderators from input
561
+ this.moderatorUserIds.forEach(id => {
562
+ this.users.push({ userExternalId: id, rol: TasUserRole.MODERATOR });
563
+ });
506
564
  }
507
565
  setupViewModeSubscription() {
508
- this.subscriptions.add(this.tasService.viewMode$.subscribe((mode) => {
566
+ this.subscriptions.add(this.tasService.viewMode$.subscribe(mode => {
567
+ // Re-open video call modal when returning from PiP mode
509
568
  if (mode === ViewMode.FULLSCREEN &&
510
569
  this.tasService.isCallActive() &&
511
- !this.currentModalRef) {
570
+ !this.videoCallModalRef) {
512
571
  const sessionId = this.tasService.sessionId;
513
572
  const token = this.tasService.token;
514
573
  if (sessionId && token) {
515
- this.openVideoCallModal(sessionId, token, true);
574
+ this.openVideoCallModal(true);
516
575
  }
517
576
  }
518
577
  }));
519
578
  }
520
- openVideoCallModal(sessionId, token, isReturningFromPip = false) {
521
- this.currentModalRef = this.modalService.open(TasVideocallComponent, {
522
- size: "xl",
523
- windowClass: "tas-video-modal",
579
+ openVideoCallModal(isReturningFromPip = false) {
580
+ this.videoCallModalRef = this.modalService.open(TasVideocallComponent, {
581
+ size: 'xl',
582
+ windowClass: 'tas-video-modal',
583
+ backdrop: 'static',
584
+ keyboard: false
585
+ });
586
+ const sessionIdToUse = this.sessionId || this.tasService.sessionId;
587
+ const tokenToUse = this.token || this.tasService.token;
588
+ this.videoCallModalRef.componentInstance.sessionId = sessionIdToUse;
589
+ this.videoCallModalRef.componentInstance.token = tokenToUse;
590
+ this.videoCallModalRef.componentInstance.isReturningFromPip = isReturningFromPip;
591
+ this.videoCallModalRef.result.then(() => { this.videoCallModalRef = null; }, () => { this.videoCallModalRef = null; });
592
+ }
593
+ }
594
+ 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 });
595
+ 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"] }] });
596
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasWaitingRoomComponent, decorators: [{
597
+ type: Component,
598
+ 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"] }]
599
+ }], ctorParameters: function () { return [{ type: i1.NgbActiveModal }, { type: TasService }, { type: i1.NgbModal }]; }, propDecorators: { appointmentId: [{
600
+ type: Input
601
+ }], product: [{
602
+ type: Input
603
+ }], tenantId: [{
604
+ type: Input
605
+ }], currentUser: [{
606
+ type: Input
607
+ }], ownerUserIds: [{
608
+ type: Input
609
+ }], regularUserIds: [{
610
+ type: Input
611
+ }], moderatorUserIds: [{
612
+ type: Input
613
+ }] } });
614
+
615
+ class TasButtonComponent {
616
+ constructor(modalService, tasService) {
617
+ this.modalService = modalService;
618
+ this.tasService = tasService;
619
+ this.appointmentId = 1;
620
+ this.product = "uell";
621
+ this.tenantId = "";
622
+ this.regularUserIds = [];
623
+ this.moderatorUserIds = [];
624
+ this.isLoading = false;
625
+ this.subscriptions = new Subscription();
626
+ this.currentModalRef = null;
627
+ this.videoCallModalRef = null;
628
+ }
629
+ ngOnInit() {
630
+ if (!this.ownerUserIds || this.ownerUserIds.length !== 1) {
631
+ throw new Error('tas-btn: ownerUserIds input is required and must contain exactly one user');
632
+ }
633
+ // Subscribe to viewMode to handle PiP return
634
+ this.subscriptions.add(this.tasService.viewMode$.subscribe(mode => {
635
+ // Reopen video call modal when returning from PiP
636
+ if (mode === ViewMode.FULLSCREEN &&
637
+ this.tasService.isCallActive() &&
638
+ !this.videoCallModalRef) {
639
+ const sessionId = this.tasService.sessionId;
640
+ const token = this.tasService.token;
641
+ if (sessionId && token) {
642
+ this.openVideoCallModal(true);
643
+ }
644
+ }
645
+ // When entering PiP, clear the videoCallModalRef since modal will close
646
+ if (mode === ViewMode.PIP) {
647
+ this.videoCallModalRef = null;
648
+ }
649
+ }));
650
+ }
651
+ ngOnDestroy() {
652
+ this.subscriptions.unsubscribe();
653
+ }
654
+ onClick() {
655
+ if (!this.tenantId || !this.currentUser?.name) {
656
+ console.error("Tenant ID or current user not available");
657
+ return;
658
+ }
659
+ this.openWaitingRoomModal();
660
+ }
661
+ openWaitingRoomModal() {
662
+ this.currentModalRef = this.modalService.open(TasWaitingRoomComponent, {
663
+ size: "lg",
664
+ windowClass: "tas-waiting-room-modal",
524
665
  backdrop: "static",
525
666
  keyboard: false,
667
+ centered: true
526
668
  });
527
- this.currentModalRef.componentInstance.sessionId = sessionId;
528
- this.currentModalRef.componentInstance.token = token;
529
- this.currentModalRef.componentInstance.isReturningFromPip =
530
- isReturningFromPip;
531
- this.currentModalRef.result.then(() => {
532
- this.currentModalRef = null;
533
- }, () => {
534
- this.currentModalRef = null;
669
+ // Pass all necessary inputs to the waiting room component
670
+ this.currentModalRef.componentInstance.appointmentId = this.appointmentId;
671
+ this.currentModalRef.componentInstance.product = this.product;
672
+ this.currentModalRef.componentInstance.tenantId = this.tenantId;
673
+ this.currentModalRef.componentInstance.currentUser = this.currentUser;
674
+ this.currentModalRef.componentInstance.ownerUserIds = this.ownerUserIds;
675
+ this.currentModalRef.componentInstance.regularUserIds = this.regularUserIds;
676
+ this.currentModalRef.componentInstance.moderatorUserIds = this.moderatorUserIds;
677
+ this.currentModalRef.result.then(() => { this.currentModalRef = null; }, () => { this.currentModalRef = null; });
678
+ }
679
+ openVideoCallModal(isReturningFromPip = false) {
680
+ this.videoCallModalRef = this.modalService.open(TasVideocallComponent, {
681
+ size: 'xl',
682
+ windowClass: 'tas-video-modal',
683
+ backdrop: 'static',
684
+ keyboard: false
535
685
  });
686
+ this.videoCallModalRef.componentInstance.sessionId = this.tasService.sessionId;
687
+ this.videoCallModalRef.componentInstance.token = this.tasService.token;
688
+ this.videoCallModalRef.componentInstance.isReturningFromPip = isReturningFromPip;
689
+ this.videoCallModalRef.result.then(() => { this.videoCallModalRef = null; }, () => { this.videoCallModalRef = null; });
536
690
  }
537
691
  }
538
- 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 });
539
- 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"] }] });
692
+ 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 });
693
+ 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"] }] });
540
694
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasButtonComponent, decorators: [{
541
695
  type: Component,
542
- 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"] }]
543
- }], ctorParameters: function () { return [{ type: TasService }, { type: i1.NgbModal }, { type: undefined, decorators: [{
544
- type: Optional
545
- }, {
546
- type: Inject,
547
- args: [TAS_USER_DATA_PROVIDER]
548
- }] }]; } });
696
+ 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"] }]
697
+ }], ctorParameters: function () { return [{ type: i1.NgbModal }, { type: TasService }]; }, propDecorators: { appointmentId: [{
698
+ type: Input
699
+ }], product: [{
700
+ type: Input
701
+ }], tenantId: [{
702
+ type: Input
703
+ }], currentUser: [{
704
+ type: Input
705
+ }], ownerUserIds: [{
706
+ type: Input
707
+ }], regularUserIds: [{
708
+ type: Input
709
+ }], moderatorUserIds: [{
710
+ type: Input
711
+ }] } });
549
712
 
550
713
  class TasFloatingCallComponent {
551
714
  constructor(tasService) {
@@ -553,13 +716,15 @@ class TasFloatingCallComponent {
553
716
  this.isVisible = false;
554
717
  this.isMuted = false;
555
718
  this.subscriptions = new Subscription();
719
+ // Margin from screen edges (in pixels)
720
+ this.PIP_MARGIN = 20;
556
721
  }
557
722
  ngOnInit() {
558
723
  this.setupSubscriptions();
559
724
  }
560
725
  ngOnDestroy() {
561
726
  this.subscriptions.unsubscribe();
562
- interact(".tas-floating-container").unset();
727
+ interact('.tas-floating-container').unset();
563
728
  }
564
729
  // Public Methods
565
730
  onExpand() {
@@ -573,115 +738,171 @@ class TasFloatingCallComponent {
573
738
  }
574
739
  // Private Methods
575
740
  setupSubscriptions() {
576
- this.subscriptions.add(this.tasService.callState$.subscribe((state) => {
741
+ // Call state subscription
742
+ this.subscriptions.add(this.tasService.callState$.subscribe(state => {
577
743
  if (state === CallState.DISCONNECTED) {
578
744
  this.isVisible = false;
579
745
  }
580
746
  }));
581
- this.subscriptions.add(this.tasService.viewMode$.subscribe((mode) => {
582
- this.isVisible =
583
- mode === ViewMode.PIP && this.tasService.isCallActive();
747
+ // View mode subscription
748
+ this.subscriptions.add(this.tasService.viewMode$.subscribe(mode => {
749
+ this.isVisible = mode === ViewMode.PIP && this.tasService.isCallActive();
584
750
  if (this.isVisible) {
585
751
  setTimeout(() => this.initInteract(), 100);
586
752
  }
587
753
  }));
588
- this.subscriptions.add(this.tasService.isMuted$.subscribe((muted) => {
754
+ // Mute state subscription
755
+ this.subscriptions.add(this.tasService.isMuted$.subscribe(muted => {
589
756
  this.isMuted = muted;
590
757
  }));
591
758
  }
592
759
  initInteract() {
593
- interact(".tas-floating-container").unset();
594
- interact(".tas-floating-container").draggable({
760
+ interact('.tas-floating-container').unset();
761
+ // Create restriction area with margin
762
+ const margin = this.PIP_MARGIN;
763
+ const restrictToBodyWithMargin = {
764
+ restriction: () => {
765
+ return {
766
+ left: margin,
767
+ top: margin,
768
+ right: window.innerWidth - margin,
769
+ bottom: window.innerHeight - margin
770
+ };
771
+ },
772
+ elementRect: { left: 0, right: 1, top: 0, bottom: 1 }
773
+ };
774
+ interact('.tas-floating-container')
775
+ .draggable({
595
776
  inertia: true,
596
777
  modifiers: [
597
- interact.modifiers.restrictRect({
598
- restriction: "body",
599
- endOnly: true,
600
- }),
778
+ interact.modifiers.restrict(restrictToBodyWithMargin)
601
779
  ],
602
780
  autoScroll: false,
603
781
  listeners: {
604
782
  move: (event) => {
605
783
  const target = event.target;
606
- const x = (parseFloat(target.getAttribute("data-x")) || 0) + event.dx;
607
- const y = (parseFloat(target.getAttribute("data-y")) || 0) + event.dy;
784
+ const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
785
+ const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
786
+ target.style.transform = `translate(${x}px, ${y}px)`;
787
+ target.setAttribute('data-x', String(x));
788
+ target.setAttribute('data-y', String(y));
789
+ }
790
+ }
791
+ })
792
+ .resizable({
793
+ edges: { left: false, right: true, bottom: true, top: false },
794
+ listeners: {
795
+ move: (event) => {
796
+ const target = event.target;
797
+ let x = parseFloat(target.getAttribute('data-x')) || 0;
798
+ let y = parseFloat(target.getAttribute('data-y')) || 0;
799
+ // Update element size
800
+ target.style.width = `${event.rect.width}px`;
801
+ target.style.height = `${event.rect.height}px`;
802
+ // Translate when resizing from top or left edges
803
+ x += event.deltaRect.left;
804
+ y += event.deltaRect.top;
608
805
  target.style.transform = `translate(${x}px, ${y}px)`;
609
- target.setAttribute("data-x", String(x));
610
- target.setAttribute("data-y", String(y));
611
- },
806
+ target.setAttribute('data-x', String(x));
807
+ target.setAttribute('data-y', String(y));
808
+ }
612
809
  },
810
+ modifiers: [
811
+ interact.modifiers.restrictEdges({
812
+ outer: {
813
+ left: margin,
814
+ top: margin,
815
+ right: window.innerWidth - margin,
816
+ bottom: window.innerHeight - margin
817
+ }
818
+ }),
819
+ interact.modifiers.restrictSize({
820
+ min: { width: 200, height: 130 },
821
+ max: { width: 500, height: 350 }
822
+ }),
823
+ interact.modifiers.aspectRatio({ ratio: 'preserve' })
824
+ ],
825
+ inertia: true
613
826
  });
614
827
  }
615
828
  }
616
829
  TasFloatingCallComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasFloatingCallComponent, deps: [{ token: TasService }], target: i0.ɵɵFactoryTarget.Component });
617
- 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"] });
830
+ 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"] });
618
831
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasFloatingCallComponent, decorators: [{
619
832
  type: Component,
620
- 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"] }]
833
+ 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"] }]
621
834
  }], ctorParameters: function () { return [{ type: TasService }]; } });
622
835
 
623
- class TasModule {
836
+ class TasUellSdkModule {
624
837
  /**
625
- * Use this method in your app module to configure the TAS SDK
838
+ * Use forRoot() to configure the TAS SDK module with required dependencies.
839
+ * This should only be called once in your root AppModule.
626
840
  *
627
841
  * @example
628
842
  * ```typescript
629
- * TasModule.forRoot({
630
- * config: {
631
- * tokBoxApiKey: environment.tokBox,
632
- * apiBaseUrl: environment.apiUrl
633
- * },
634
- * httpClient: MyHttpClientAdapter,
635
- * userDataProvider: MyUserDataProvider
843
+ * import { TasUellSdkModule, TasHttpClient } from 'tas-uell-sdk';
844
+ *
845
+ * // Create an adapter that implements TasHttpClient
846
+ * @Injectable({ providedIn: 'root' })
847
+ * export class MyHttpAdapter implements TasHttpClient {
848
+ * constructor(private http: HttpClient) {}
849
+ * post<T>(url: string, options: { body: any; headers?: Record<string, string> }): Observable<T> {
850
+ * return this.http.post<T>(url, options.body, { headers: options.headers });
851
+ * }
852
+ * }
853
+ *
854
+ * // In your AppModule
855
+ * @NgModule({
856
+ * imports: [
857
+ * TasUellSdkModule.forRoot({
858
+ * config: { tokBoxApiKey: 'YOUR_TOKBOX_API_KEY' },
859
+ * httpClient: MyHttpAdapter
860
+ * })
861
+ * ]
636
862
  * })
863
+ * export class AppModule { }
637
864
  * ```
638
865
  */
639
- static forRoot(moduleConfig) {
640
- const providers = [
641
- {
642
- provide: TAS_CONFIG,
643
- useValue: moduleConfig.config,
644
- },
645
- ];
646
- if (moduleConfig.httpClient) {
647
- providers.push({
648
- provide: TAS_HTTP_CLIENT,
649
- useClass: moduleConfig.httpClient,
650
- });
651
- }
652
- if (moduleConfig.userDataProvider) {
653
- providers.push({
654
- provide: TAS_USER_DATA_PROVIDER,
655
- useClass: moduleConfig.userDataProvider,
656
- });
657
- }
866
+ static forRoot(options) {
658
867
  return {
659
- ngModule: TasModule,
660
- providers,
868
+ ngModule: TasUellSdkModule,
869
+ providers: [
870
+ { provide: TAS_CONFIG, useValue: options.config },
871
+ { provide: TAS_HTTP_CLIENT, useClass: options.httpClient },
872
+ TasService
873
+ ]
661
874
  };
662
875
  }
663
876
  }
664
- TasModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
665
- TasModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasModule, declarations: [TasButtonComponent,
877
+ TasUellSdkModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasUellSdkModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
878
+ TasUellSdkModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasUellSdkModule, declarations: [TasButtonComponent,
666
879
  TasVideocallComponent,
667
- TasFloatingCallComponent], imports: [CommonModule, NgbModalModule], exports: [TasButtonComponent,
880
+ TasFloatingCallComponent,
881
+ TasWaitingRoomComponent], imports: [CommonModule], exports: [TasButtonComponent,
668
882
  TasVideocallComponent,
669
- TasFloatingCallComponent] });
670
- TasModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasModule, imports: [[CommonModule, NgbModalModule]] });
671
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasModule, decorators: [{
883
+ TasFloatingCallComponent,
884
+ TasWaitingRoomComponent] });
885
+ TasUellSdkModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasUellSdkModule, imports: [[
886
+ CommonModule,
887
+ ]] });
888
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: TasUellSdkModule, decorators: [{
672
889
  type: NgModule,
673
890
  args: [{
674
891
  declarations: [
675
892
  TasButtonComponent,
676
893
  TasVideocallComponent,
677
894
  TasFloatingCallComponent,
895
+ TasWaitingRoomComponent
896
+ ],
897
+ imports: [
898
+ CommonModule,
678
899
  ],
679
- imports: [CommonModule, NgbModalModule],
680
900
  exports: [
681
901
  TasButtonComponent,
682
902
  TasVideocallComponent,
683
903
  TasFloatingCallComponent,
684
- ],
904
+ TasWaitingRoomComponent
905
+ ]
685
906
  }]
686
907
  }] });
687
908
 
@@ -693,5 +914,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
693
914
  * Generated bundle index. Do not edit.
694
915
  */
695
916
 
696
- export { CallState, TAS_CONFIG, TAS_HTTP_CLIENT, TAS_USER_DATA_PROVIDER, TasButtonComponent, TasFloatingCallComponent, TasModule, TasService, TasVideocallComponent, ViewMode };
917
+ export { CallState, TAS_CONFIG, TAS_HTTP_CLIENT, TasButtonComponent, TasFloatingCallComponent, TasRoomType, TasService, TasSessionType, TasUellSdkModule, TasUserRole, TasVideocallComponent, TasWaitingRoomComponent, ViewMode, WaitingRoomState };
697
918
  //# sourceMappingURL=tas-uell-sdk.mjs.map