http-request-manager 18.5.18 → 18.5.20

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.
@@ -4,7 +4,7 @@ import { ComponentStore } from '@ngrx/component-store';
4
4
  import { map, catchError, filter, finalize, takeWhile, retry, startWith, tap, mergeMap, takeUntil, withLatestFrom, switchMap, delay, concatMap, take, scan, distinctUntilChanged } from 'rxjs/operators';
5
5
  import { HttpClient, HttpHeaders, HttpEventType, HttpHeaderResponse, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
6
6
  import * as CryptoJS from 'crypto-js';
7
- import { from, BehaviorSubject, EMPTY, throwError, defer, interval, timer, Subject, merge, of, Subscription, take as take$1, catchError as catchError$1, switchMap as switchMap$1, map as map$1, startWith as startWith$1, combineLatest, tap as tap$1, ReplaySubject } from 'rxjs';
7
+ import { from, BehaviorSubject, EMPTY, throwError, defer, interval, timer, Subject, merge, of, Subscription, take as take$1, catchError as catchError$1, switchMap as switchMap$1, map as map$1, startWith as startWith$1, combineLatest, filter as filter$1, takeUntil as takeUntil$1, tap as tap$1, ReplaySubject } from 'rxjs';
8
8
  import { ToastMessageDisplayService, ToastDisplay, ToastColors, ToastMessageDisplayModule } from 'toast-message-display';
9
9
  import Dexie from 'dexie';
10
10
  import * as i1 from '@ngx-translate/core';
@@ -736,12 +736,23 @@ class ChannelInfo {
736
736
  }
737
737
 
738
738
  class WSUser {
739
- constructor(sessionId, content) {
739
+ constructor(sessionId = '', ldap = '', name = '', email = '') {
740
740
  this.sessionId = sessionId;
741
- this.content = content;
741
+ this.ldap = ldap;
742
+ this.name = name;
743
+ this.email = email;
742
744
  }
743
745
  static adapt(item) {
744
- return new WSUser(item?.sessionId, item?.content);
746
+ const user = new WSUser(item?.sessionId, item?.ldap, item?.name, item?.email);
747
+ // Copy any additional properties
748
+ if (item) {
749
+ Object.keys(item).forEach(key => {
750
+ if (!['sessionId', 'ldap', 'name', 'email'].includes(key)) {
751
+ user[key] = item[key];
752
+ }
753
+ });
754
+ }
755
+ return user;
745
756
  }
746
757
  }
747
758
 
@@ -760,6 +771,11 @@ class WebsocketService {
760
771
  this.connectionStatus = new BehaviorSubject(false);
761
772
  this.connectionStatus$ = this.connectionStatus.asObservable();
762
773
  this.isSubscribed = false;
774
+ // Track currently subscribed channels
775
+ this.subscribedChannels = new BehaviorSubject(new Set());
776
+ this.subscribedChannels$ = this.subscribedChannels.asObservable();
777
+ // Store last options for reconnection
778
+ this.lastOptions = null;
763
779
  }
764
780
  getSessionId() {
765
781
  return sessionStorage.getItem('WSID') ?? (() => {
@@ -806,7 +822,26 @@ class WebsocketService {
806
822
  this.socket.onopen = () => {
807
823
  console.log(`📡 Connected to WebSocket`);
808
824
  this.connectionStatus.next(true);
825
+ this.lastOptions = options;
826
+ // Subscribe to primary channel
809
827
  this.sendSubscribe(options.id, options.user);
828
+ // Auto-subscribe to additional channels from options.channels[]
829
+ if (options.channels && options.channels.length > 0) {
830
+ options.channels.forEach(channel => {
831
+ if (channel !== options.id) {
832
+ this.subscribeToChannel(channel);
833
+ }
834
+ });
835
+ }
836
+ // Re-subscribe to any previously subscribed channels (reconnection scenario)
837
+ const previousChannels = this.subscribedChannels.value;
838
+ if (previousChannels.size > 0) {
839
+ previousChannels.forEach(channel => {
840
+ if (channel !== options.id && (!options.channels || !options.channels.includes(channel))) {
841
+ this.subscribeToChannel(channel);
842
+ }
843
+ });
844
+ }
810
845
  };
811
846
  this.socket.onmessage = (event) => {
812
847
  try {
@@ -848,20 +883,44 @@ class WebsocketService {
848
883
  content: {}
849
884
  };
850
885
  this.socket.send(JSON.stringify(message));
886
+ // Track locally
887
+ const current = this.subscribedChannels.value;
888
+ current.add(channelName);
889
+ this.subscribedChannels.next(current);
890
+ console.log(`📝 Subscribed to channel: ${channelName}`);
851
891
  }
852
892
  else {
853
893
  console.warn('Cannot subscribe: WebSocket not yet open.');
854
894
  }
855
895
  }
856
- unsubscribeToChannel(channel) {
896
+ subscribeToChannels(channelNames) {
897
+ if (this.socket?.readyState === WebSocket.OPEN) {
898
+ channelNames.forEach(channel => this.subscribeToChannel(channel));
899
+ }
900
+ else {
901
+ console.warn('Cannot subscribe: WebSocket not yet open.');
902
+ }
903
+ }
904
+ unsubscribeFromChannel(channel) {
857
905
  if (this.socket?.readyState === WebSocket.OPEN) {
858
906
  this.socket.send(JSON.stringify({ type: 'unsubscribe', subscribedChannel: channel }));
859
- console.log(`💬 Unsubscribed to channel: ${channel}`);
907
+ // Remove from local tracking
908
+ const current = this.subscribedChannels.value;
909
+ current.delete(channel);
910
+ this.subscribedChannels.next(current);
911
+ console.log(`💬 Unsubscribed from channel: ${channel}`);
860
912
  }
861
913
  else {
862
- console.error('WebSocket is not open. Cannot unsubscribed to channel.');
914
+ console.error('WebSocket is not open. Cannot unsubscribe from channel.');
863
915
  }
864
916
  }
917
+ unsubscribeToChannel(channel) {
918
+ // Deprecated: Use unsubscribeFromChannel instead
919
+ this.unsubscribeFromChannel(channel);
920
+ }
921
+ getSubscribedChannels() {
922
+ return this.subscribedChannels.value;
923
+ }
865
924
  sendBroadcast(content) {
866
925
  if (this.socket?.readyState === WebSocket.OPEN) {
867
926
  this.socket.send(JSON.stringify({ type: 'broadcast', content }));
@@ -880,6 +939,19 @@ class WebsocketService {
880
939
  console.error('WebSocket is not open. Cannot send message.');
881
940
  }
882
941
  }
942
+ /**
943
+ * Send a message to a specific channel for channel-based messaging
944
+ * This uses the 'message' type which broadcasts to all subscribers in the channel
945
+ */
946
+ sendChannelMessage(channel, content) {
947
+ if (this.socket?.readyState === WebSocket.OPEN) {
948
+ this.socket.send(JSON.stringify({ type: 'message', subscribedChannel: channel, content }));
949
+ console.log(`💬 Send channel message to [${channel}]:`, content);
950
+ }
951
+ else {
952
+ console.error('WebSocket is not open. Cannot send channel message.');
953
+ }
954
+ }
883
955
  sendMessageToUser(user, content) {
884
956
  if (this.socket?.readyState === WebSocket.OPEN) {
885
957
  this.socket.send(JSON.stringify({ type: 'userMessage', subscribedChannel: user, content }));
@@ -2812,15 +2884,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2812
2884
  }], ctorParameters: () => [] });
2813
2885
 
2814
2886
  class ChannelMessage {
2815
- constructor(sessionId = '', content, message = '', users = [], issued = Math.floor(new Date().getTime() / 1000)) {
2887
+ constructor(sessionId = '', content = null) {
2816
2888
  this.sessionId = sessionId;
2817
2889
  this.content = content;
2818
- this.message = message;
2819
- this.users = users;
2820
- this.issued = issued;
2821
2890
  }
2822
2891
  static adapt(item) {
2823
- return new ChannelMessage(item?.sessionId, item?.content, item?.message, (item?.users) ? item?.users.map((userItem) => WSUser.adapt(userItem)) : [], item?.issued);
2892
+ return new ChannelMessage(item?.sessionId, item?.content);
2824
2893
  }
2825
2894
  }
2826
2895
 
@@ -2869,6 +2938,8 @@ class HTTPManagerStateService extends ComponentStore {
2869
2938
  this.userAction$ = this.userAction.asObservable();
2870
2939
  this.wsConnection = false;
2871
2940
  this.wsOptions = WSOptions.adapt();
2941
+ // Expose raw WS connection status directly to UI
2942
+ this.connectionStatus$ = this.httpManagerService.connectionStatus$;
2872
2943
  // WebSocket
2873
2944
  this.initWS = this.effect((wsOptions$) => wsOptions$.pipe(
2874
2945
  // tap((wsOptions) => { debugger
@@ -2904,6 +2975,16 @@ class HTTPManagerStateService extends ComponentStore {
2904
2975
  // }
2905
2976
  this.channels.next(message.channels);
2906
2977
  break;
2978
+ case 'subscribed':
2979
+ console.log(`✅ Subscription confirmed: ${message.channel}`);
2980
+ break;
2981
+ case 'unsubscribed':
2982
+ console.log(`🔓 Unsubscription confirmed: ${message.channel}`);
2983
+ break;
2984
+ case 'info':
2985
+ // Already subscribed or other info messages
2986
+ console.log(`ℹ️ Info: ${message.message}`);
2987
+ break;
2907
2988
  case 'stateMangerMessage':
2908
2989
  ;
2909
2990
  if (message.data.sessionId !== this.user.value?.sessionId) {
@@ -2912,17 +2993,22 @@ class HTTPManagerStateService extends ComponentStore {
2912
2993
  this.fetchRecord(RequestOptions.adapt({ path: message.data.content.path }), message.data.content.method);
2913
2994
  }
2914
2995
  break;
2915
- // case 'channelMessage':
2916
- // console.log('💬 Message:', message.data)
2917
- // this.fetchRecord(RequestOptions.adapt({ path: message.content.path }), message.content.method)
2918
- // break;
2919
- // case 'channelCommunication':
2920
- // console.log('💬 Message Communication:', message.data)
2921
- // this.appendMessages(message.data)
2922
- // break;
2923
- // case 'channelAlerts':
2924
- // console.log('💬 Message Alerts:', message.data)
2925
- // break;
2996
+ case 'channelMessage':
2997
+ // Handle channel-based messages (from sendChannelMessage)
2998
+ // Structure: { type: 'channelMessage', channel, data: { sessionId, content: ChannelMessage } }
2999
+ // Skip messages from self
3000
+ const senderSessionId = message.data?.content?.sessionId?.sessionId || message.data?.sessionId;
3001
+ if (senderSessionId === this.user.value?.sessionId) {
3002
+ break;
3003
+ }
3004
+ console.log('💬 Channel Message received:', message);
3005
+ if (message.data?.content) {
3006
+ this.appendMessages(ChannelMessage.adapt({
3007
+ sessionId: message.data.content.sessionId,
3008
+ content: message.data.content.content,
3009
+ }));
3010
+ }
3011
+ break;
2926
3012
  case 'usersInChannel':
2927
3013
  console.log('👥 Users:', message.data.users);
2928
3014
  this.userList.next(message.data.users);
@@ -3227,7 +3313,8 @@ class HTTPManagerStateService extends ComponentStore {
3227
3313
  this.databaseOptions = database;
3228
3314
  this.maxRetries = this.apiOptions.ws?.retry?.times || 3;
3229
3315
  this.retryDelay = (this.apiOptions.ws?.retry?.delay && this.apiOptions.ws.retry.delay * 1000) || 5 * 1000;
3230
- this.wsNextRetry = new BehaviorSubject(this.retryDelay);
3316
+ // Start next retry countdown at 0 to avoid showing 5000 pre-connection
3317
+ this.wsNextRetry = new BehaviorSubject(0);
3231
3318
  this.wsNextRetry$ = this.wsNextRetry.asObservable();
3232
3319
  this.setApiRequestOptions(apiOptions, dataType, database);
3233
3320
  if (this.databaseOptions && this.databaseOptions.table) {
@@ -3262,8 +3349,8 @@ class HTTPManagerStateService extends ComponentStore {
3262
3349
  console.error('WSOptions invalid: wsServer is missing or empty');
3263
3350
  return;
3264
3351
  }
3265
- // Setup connection status monitoring
3266
- this.connectionStatus$ = this.setupConnectionStatus();
3352
+ // Setup connection status monitoring (internal subscription to drive retry counters)
3353
+ this.setupConnectionStatus().subscribe();
3267
3354
  // Make initial connection attempt
3268
3355
  console.log('🔄 Initial WebSocket connection attempt...');
3269
3356
  this.httpManagerService.connect(this.apiOptions.ws, this.apiOptions.ws.jwtToken || '');
@@ -3293,6 +3380,9 @@ class HTTPManagerStateService extends ComponentStore {
3293
3380
  if (!this.shouldRetry)
3294
3381
  return of(false);
3295
3382
  const countdownEnder$ = new Subject();
3383
+ // Immediately reflect upcoming retry delay in seconds on UI
3384
+ const seconds = this.retryDelay / 1000;
3385
+ this.wsNextRetry.next(seconds);
3296
3386
  return timer(0, this.retryDelay)
3297
3387
  .pipe(take(this.maxRetries), tap(i => {
3298
3388
  const attempt = i + 1;
@@ -3398,17 +3488,119 @@ class HTTPManagerStateService extends ComponentStore {
3398
3488
  }
3399
3489
  }
3400
3490
  wsMessaging(message, channels) {
3401
- const user = this.user.value || this.user.value;
3491
+ const user = this.user.value;
3402
3492
  const messageInfo = ChannelMessage.adapt({ ...message, fromUser: user });
3493
+ console.log('📤 wsMessaging called with channels:', channels);
3403
3494
  if (this.wsConnection && this.apiOptions.ws) {
3404
- const wsServer = (channels) ? channels : this.apiOptions.ws.id;
3405
- // if(messageInfo.toUser === 'allChannels') {
3406
- // this.httpManagerService.sendBroadcast(messageInfo)
3407
- // } else if(messageInfo.toUser === 'allInChannel') {
3408
- // this.httpManagerService.sendMessageInChannel(wsServer, messageInfo)
3409
- // } else {
3410
- // this.httpManagerService.sendMessageToUser(wsServer, messageInfo)
3411
- // }
3495
+ // If specific channels provided, send to each channel
3496
+ if (channels && channels.length > 0) {
3497
+ console.log(`📤 Sending to ${channels.length} channel(s):`, channels);
3498
+ channels.forEach(channel => {
3499
+ if (channel === 'allChannels') {
3500
+ this.httpManagerService.sendBroadcast(messageInfo);
3501
+ }
3502
+ else {
3503
+ this.httpManagerService.sendChannelMessage(channel, messageInfo);
3504
+ }
3505
+ });
3506
+ }
3507
+ else {
3508
+ // Fallback to the primary WS channel
3509
+ const wsChannel = this.apiOptions.ws.id;
3510
+ console.log(`📤 Sending to fallback channel:`, wsChannel);
3511
+ this.httpManagerService.sendChannelMessage(wsChannel, messageInfo);
3512
+ }
3513
+ }
3514
+ }
3515
+ // --------------------------------------------------------------------------------------------------
3516
+ // CHANNEL MANAGEMENT
3517
+ /**
3518
+ * Subscribe to a single channel
3519
+ */
3520
+ subscribeToChannel(channel) {
3521
+ if (this.wsConnection) {
3522
+ this.httpManagerService.subscribeToChannel(channel);
3523
+ }
3524
+ else {
3525
+ console.warn('Cannot subscribe: WebSocket not connected.');
3526
+ }
3527
+ }
3528
+ /**
3529
+ * Subscribe to multiple channels at once
3530
+ */
3531
+ subscribeToChannels(channels) {
3532
+ if (this.wsConnection) {
3533
+ this.httpManagerService.subscribeToChannels(channels);
3534
+ }
3535
+ else {
3536
+ console.warn('Cannot subscribe: WebSocket not connected.');
3537
+ }
3538
+ }
3539
+ /**
3540
+ * Unsubscribe from a channel
3541
+ */
3542
+ unsubscribeFromChannel(channel) {
3543
+ if (this.wsConnection) {
3544
+ this.httpManagerService.unsubscribeFromChannel(channel);
3545
+ }
3546
+ else {
3547
+ console.warn('Cannot unsubscribe: WebSocket not connected.');
3548
+ }
3549
+ }
3550
+ /**
3551
+ * Get observable of currently subscribed channels
3552
+ */
3553
+ get subscribedChannels$() {
3554
+ return this.httpManagerService.subscribedChannels$;
3555
+ }
3556
+ /**
3557
+ * Get current subscribed channels synchronously
3558
+ */
3559
+ getSubscribedChannels() {
3560
+ return this.httpManagerService.getSubscribedChannels();
3561
+ }
3562
+ /**
3563
+ * Create a new channel on the server
3564
+ */
3565
+ createChannel(channel) {
3566
+ if (this.wsConnection) {
3567
+ this.httpManagerService.createChannel(channel);
3568
+ }
3569
+ else {
3570
+ console.warn('Cannot create channel: WebSocket not connected.');
3571
+ }
3572
+ }
3573
+ /**
3574
+ * Delete a channel from the server
3575
+ */
3576
+ deleteChannel(channel) {
3577
+ if (this.wsConnection) {
3578
+ this.httpManagerService.deleteChannel(channel);
3579
+ }
3580
+ else {
3581
+ console.warn('Cannot delete channel: WebSocket not connected.');
3582
+ }
3583
+ }
3584
+ /**
3585
+ * Request list of all channels from server
3586
+ */
3587
+ getAllChannels() {
3588
+ if (this.wsConnection) {
3589
+ this.httpManagerService.getAllChannels();
3590
+ }
3591
+ else {
3592
+ console.warn('Cannot get channels: WebSocket not connected.');
3593
+ }
3594
+ }
3595
+ /**
3596
+ * Get users in a specific channel
3597
+ */
3598
+ getUsersInChannel(channel) {
3599
+ if (this.wsConnection) {
3600
+ this.httpManagerService.getUsersInChannel(channel);
3601
+ }
3602
+ else {
3603
+ console.warn('Cannot get users: WebSocket not connected.');
3412
3604
  }
3413
3605
  }
3414
3606
  // --------------------------------------------------------------------------------------------------
@@ -4103,10 +4295,9 @@ class StateRequestServiceDemo extends HTTPManagerStateService {
4103
4295
  console.log('User Action:', user);
4104
4296
  });
4105
4297
  }
4106
- sendMessage(data) {
4107
- debugger;
4108
- console.log('sendMessage', data);
4109
- this.wsMessaging(data);
4298
+ sendMessage(data, channels) {
4299
+ console.log('sendMessage', data, 'to channels:', channels);
4300
+ this.wsMessaging(data, channels);
4110
4301
  }
4111
4302
  getAllChannels() {
4112
4303
  this.httpManagerService.getAllChannels();
@@ -4194,7 +4385,6 @@ class StateDataRequestService extends HTTPManagerStateService {
4194
4385
  this.createRecord(newData, RequestOptions.adapt({ path: this.path }));
4195
4386
  }
4196
4387
  sendMessage(data) {
4197
- debugger;
4198
4388
  console.log('sendMessage', data);
4199
4389
  this.wsMessaging(data);
4200
4390
  }
@@ -4261,11 +4451,11 @@ class WsDataControlComponent {
4261
4451
  this.stateDataRequestService.deleteData(lastRec);
4262
4452
  }
4263
4453
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsDataControlComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4264
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: WsDataControlComponent, selector: "app-ws-data-control", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user", path: "path", wsChannel: "wsChannel" }, ngImport: i0, template: "@if ((data$ | async); as data) {\n <div style=\"margin: 1rem;\">\n @if ((users$ |async); as users) {\n <div>\n @if (users.length > 0) {\n <h3 style=\"margin: 0;\">Connected Users</h3>\n } @else {\n <h3 style=\"margin: 0;\">No Users</h3>\n }\n <mat-chip-set>\n @for (user of users; track user) {\n <mat-chip\n [class.user-chip--primary]=\"isUser(user, (user$ | async))\"\n [style.color]=\"isUser(user, (user$ | async)) ? '#fff' : null\"\n [disableRipple]=\"true\"\n >\n {{ user.content.name }}\n </mat-chip>\n }\n </mat-chip-set>\n </div>\n }\n <div style=\"margin-top: 1rem; margin-bottom: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div class=\"box\" style=\"margin-bottom: 1rem;\" *ngIf=\"(userAction$ | async) as userAction\">\n {{ userAction?.content.user.name }} has {{ userAction?.content.method }}\n </div>\n\n <h3 style=\"margin: 0;\">Data Actions</h3>\n <div style=\"display: flex; gap: 1rem; margin-bottom: 1rem;\">\n <button mat-stroked-button (click)=\"onGetData()\">Get Data</button>\n <div style=\"flex:1\"></div>\n <button mat-stroked-button color=\"accent\" (click)=\"onUpdateData(data)\">Update Data</button>\n <button mat-stroked-button color=\"warn\" (click)=\"onRemoveData(data)\">Remove Data</button>\n <button mat-stroked-button color=\"primary\" (click)=\"onAddData()\">Add Data</button>\n </div>\n @if (data.length > 0) {\n <div>\n <table mat-table [dataSource]=\"data\" style=\"border: 1px solid grey;\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n <ng-container matColumnDef=\"spiffe\">\n <th mat-header-cell *matHeaderCellDef> Spiffe </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.spiffe}} </td>\n </ng-container>\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.first_name}} {{element.last_name}}</td>\n </ng-container>\n <ng-container matColumnDef=\"email\">\n <th mat-header-cell *matHeaderCellDef> Email </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.email}} </td>\n </ng-container>\n <tr mat-header-row *matHeaderRowDef=\"['id', 'spiffe', 'name', 'email']\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: ['id', 'spiffe', 'name', 'email'];\"></tr>\n </table>\n <div style=\"border: 1px solid grey; padding: .5rem; border-top: none;\">\n <h3 style=\"margin: 0;\">Total Records {{ data.length }}</h3>\n </div>\n </div>\n } @else {\n <div style=\"margin-top: 1rem; font-style: italic;\">\n No Data Available\n </div>\n }\n </div>\n}\n\n", styles: [".user-chip--primary{background-color:var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5))!important;color:#fff!important;--mdc-evolution-chip-container-color: var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5));--mdc-evolution-chip-label-text-color: #fff}.user-chip--primary :is(.mdc-evolution-chip__text-label,.mdc-evolution-chip__action,.mdc-evolution-chip__cell,.mat-mdc-chip-action-label){color:#fff!important}.user-chip--primary,.user-chip--primary *{color:#fff!important}:host ::ng-deep .user-chip--primary{background-color:var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5))!important;color:#fff!important;--mdc-evolution-chip-container-color: var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5));--mdc-evolution-chip-label-text-color: #fff}:host ::ng-deep .user-chip--primary .mdc-evolution-chip__text-label,:host ::ng-deep .user-chip--primary .mdc-evolution-chip__action,:host ::ng-deep .user-chip--primary .mdc-evolution-chip__cell,:host ::ng-deep .user-chip--primary .mat-mdc-chip-action-label,:host ::ng-deep .user-chip--primary *{color:#fff!important}.box{padding:.5rem;border:1px solid rgb(174,174,13);background-color:#ececaf}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3$1.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "component", type: i3$1.MatChipSet, selector: "mat-chip-set", inputs: ["disabled", "role", "tabIndex"] }, { kind: "component", type: i8.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i8.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i8.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i8.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i8.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i8.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i8.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i8.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i8.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i8.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i11.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] }); }
4454
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: WsDataControlComponent, selector: "app-ws-data-control", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user", path: "path", wsChannel: "wsChannel" }, ngImport: i0, template: "@if ((data$ | async); as data) {\n <div style=\"margin: 1rem;\">\n @if ((users$ |async); as users) {\n <div>\n @if (users.length > 0) {\n <h3 style=\"margin: 0;\">Connected Users</h3>\n } @else {\n <h3 style=\"margin: 0;\">No Users</h3>\n }\n <mat-chip-set>\n @for (user of users; track user.sessionId) {\n <mat-chip\n [class.user-chip--primary]=\"isUser(user, (user$ | async))\"\n [style.color]=\"isUser(user, (user$ | async)) ? '#fff' : null\"\n [disableRipple]=\"true\"\n >\n {{ user.name }}\n </mat-chip>\n }\n </mat-chip-set>\n </div>\n }\n <div style=\"margin-top: 1rem; margin-bottom: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div class=\"box\" style=\"margin-bottom: 1rem;\" *ngIf=\"(userAction$ | async) as userAction\">\n {{ userAction?.content?.user?.name }} has {{ userAction?.content?.method }}\n </div>\n\n <h3 style=\"margin: 0;\">Data Actions</h3>\n <div style=\"display: flex; gap: 1rem; margin-bottom: 1rem;\">\n <button mat-stroked-button (click)=\"onGetData()\">Get Data</button>\n <div style=\"flex:1\"></div>\n <button mat-stroked-button color=\"accent\" (click)=\"onUpdateData(data)\">Update Data</button>\n <button mat-stroked-button color=\"warn\" (click)=\"onRemoveData(data)\">Remove Data</button>\n <button mat-stroked-button color=\"primary\" (click)=\"onAddData()\">Add Data</button>\n </div>\n @if (data.length > 0) {\n <div>\n <table mat-table [dataSource]=\"data\" style=\"border: 1px solid grey;\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n <ng-container matColumnDef=\"spiffe\">\n <th mat-header-cell *matHeaderCellDef> Spiffe </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.spiffe}} </td>\n </ng-container>\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.first_name}} {{element.last_name}}</td>\n </ng-container>\n <ng-container matColumnDef=\"email\">\n <th mat-header-cell *matHeaderCellDef> Email </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.email}} </td>\n </ng-container>\n <tr mat-header-row *matHeaderRowDef=\"['id', 'spiffe', 'name', 'email']\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: ['id', 'spiffe', 'name', 'email'];\"></tr>\n </table>\n <div style=\"border: 1px solid grey; padding: .5rem; border-top: none;\">\n <h3 style=\"margin: 0;\">Total Records {{ data.length }}</h3>\n </div>\n </div>\n } @else {\n <div style=\"margin-top: 1rem; font-style: italic;\">\n No Data Available\n </div>\n }\n </div>\n}\n\n", styles: [".user-chip--primary{background-color:var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5))!important;color:#fff!important;--mdc-evolution-chip-container-color: var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5));--mdc-evolution-chip-label-text-color: #fff}.user-chip--primary :is(.mdc-evolution-chip__text-label,.mdc-evolution-chip__action,.mdc-evolution-chip__cell,.mat-mdc-chip-action-label){color:#fff!important}.user-chip--primary,.user-chip--primary *{color:#fff!important}:host ::ng-deep .user-chip--primary{background-color:var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5))!important;color:#fff!important;--mdc-evolution-chip-container-color: var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5));--mdc-evolution-chip-label-text-color: #fff}:host ::ng-deep .user-chip--primary .mdc-evolution-chip__text-label,:host ::ng-deep .user-chip--primary .mdc-evolution-chip__action,:host ::ng-deep .user-chip--primary .mdc-evolution-chip__cell,:host ::ng-deep .user-chip--primary .mat-mdc-chip-action-label,:host ::ng-deep .user-chip--primary *{color:#fff!important}.box{padding:.5rem;border:1px solid rgb(174,174,13);background-color:#ececaf}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3$1.MatChip, selector: "mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]", inputs: ["role", "id", "aria-label", "aria-description", "value", "color", "removable", "highlighted", "disableRipple", "disabled"], outputs: ["removed", "destroyed"], exportAs: ["matChip"] }, { kind: "component", type: i3$1.MatChipSet, selector: "mat-chip-set", inputs: ["disabled", "role", "tabIndex"] }, { kind: "component", type: i8.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i8.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i8.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i8.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i8.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i8.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i8.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i8.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i8.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i8.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i11.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] }); }
4265
4455
  }
4266
4456
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsDataControlComponent, decorators: [{
4267
4457
  type: Component,
4268
- args: [{ selector: 'app-ws-data-control', standalone: false, template: "@if ((data$ | async); as data) {\n <div style=\"margin: 1rem;\">\n @if ((users$ |async); as users) {\n <div>\n @if (users.length > 0) {\n <h3 style=\"margin: 0;\">Connected Users</h3>\n } @else {\n <h3 style=\"margin: 0;\">No Users</h3>\n }\n <mat-chip-set>\n @for (user of users; track user) {\n <mat-chip\n [class.user-chip--primary]=\"isUser(user, (user$ | async))\"\n [style.color]=\"isUser(user, (user$ | async)) ? '#fff' : null\"\n [disableRipple]=\"true\"\n >\n {{ user.content.name }}\n </mat-chip>\n }\n </mat-chip-set>\n </div>\n }\n <div style=\"margin-top: 1rem; margin-bottom: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div class=\"box\" style=\"margin-bottom: 1rem;\" *ngIf=\"(userAction$ | async) as userAction\">\n {{ userAction?.content.user.name }} has {{ userAction?.content.method }}\n </div>\n\n <h3 style=\"margin: 0;\">Data Actions</h3>\n <div style=\"display: flex; gap: 1rem; margin-bottom: 1rem;\">\n <button mat-stroked-button (click)=\"onGetData()\">Get Data</button>\n <div style=\"flex:1\"></div>\n <button mat-stroked-button color=\"accent\" (click)=\"onUpdateData(data)\">Update Data</button>\n <button mat-stroked-button color=\"warn\" (click)=\"onRemoveData(data)\">Remove Data</button>\n <button mat-stroked-button color=\"primary\" (click)=\"onAddData()\">Add Data</button>\n </div>\n @if (data.length > 0) {\n <div>\n <table mat-table [dataSource]=\"data\" style=\"border: 1px solid grey;\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n <ng-container matColumnDef=\"spiffe\">\n <th mat-header-cell *matHeaderCellDef> Spiffe </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.spiffe}} </td>\n </ng-container>\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.first_name}} {{element.last_name}}</td>\n </ng-container>\n <ng-container matColumnDef=\"email\">\n <th mat-header-cell *matHeaderCellDef> Email </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.email}} </td>\n </ng-container>\n <tr mat-header-row *matHeaderRowDef=\"['id', 'spiffe', 'name', 'email']\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: ['id', 'spiffe', 'name', 'email'];\"></tr>\n </table>\n <div style=\"border: 1px solid grey; padding: .5rem; border-top: none;\">\n <h3 style=\"margin: 0;\">Total Records {{ data.length }}</h3>\n </div>\n </div>\n } @else {\n <div style=\"margin-top: 1rem; font-style: italic;\">\n No Data Available\n </div>\n }\n </div>\n}\n\n", styles: [".user-chip--primary{background-color:var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5))!important;color:#fff!important;--mdc-evolution-chip-container-color: var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5));--mdc-evolution-chip-label-text-color: #fff}.user-chip--primary :is(.mdc-evolution-chip__text-label,.mdc-evolution-chip__action,.mdc-evolution-chip__cell,.mat-mdc-chip-action-label){color:#fff!important}.user-chip--primary,.user-chip--primary *{color:#fff!important}:host ::ng-deep .user-chip--primary{background-color:var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5))!important;color:#fff!important;--mdc-evolution-chip-container-color: var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5));--mdc-evolution-chip-label-text-color: #fff}:host ::ng-deep .user-chip--primary .mdc-evolution-chip__text-label,:host ::ng-deep .user-chip--primary .mdc-evolution-chip__action,:host ::ng-deep .user-chip--primary .mdc-evolution-chip__cell,:host ::ng-deep .user-chip--primary .mat-mdc-chip-action-label,:host ::ng-deep .user-chip--primary *{color:#fff!important}.box{padding:.5rem;border:1px solid rgb(174,174,13);background-color:#ececaf}\n"] }]
4458
+ args: [{ selector: 'app-ws-data-control', standalone: false, template: "@if ((data$ | async); as data) {\n <div style=\"margin: 1rem;\">\n @if ((users$ |async); as users) {\n <div>\n @if (users.length > 0) {\n <h3 style=\"margin: 0;\">Connected Users</h3>\n } @else {\n <h3 style=\"margin: 0;\">No Users</h3>\n }\n <mat-chip-set>\n @for (user of users; track user.sessionId) {\n <mat-chip\n [class.user-chip--primary]=\"isUser(user, (user$ | async))\"\n [style.color]=\"isUser(user, (user$ | async)) ? '#fff' : null\"\n [disableRipple]=\"true\"\n >\n {{ user.name }}\n </mat-chip>\n }\n </mat-chip-set>\n </div>\n }\n <div style=\"margin-top: 1rem; margin-bottom: 1rem;\">\n <mat-divider></mat-divider>\n </div>\n\n <div class=\"box\" style=\"margin-bottom: 1rem;\" *ngIf=\"(userAction$ | async) as userAction\">\n {{ userAction?.content?.user?.name }} has {{ userAction?.content?.method }}\n </div>\n\n <h3 style=\"margin: 0;\">Data Actions</h3>\n <div style=\"display: flex; gap: 1rem; margin-bottom: 1rem;\">\n <button mat-stroked-button (click)=\"onGetData()\">Get Data</button>\n <div style=\"flex:1\"></div>\n <button mat-stroked-button color=\"accent\" (click)=\"onUpdateData(data)\">Update Data</button>\n <button mat-stroked-button color=\"warn\" (click)=\"onRemoveData(data)\">Remove Data</button>\n <button mat-stroked-button color=\"primary\" (click)=\"onAddData()\">Add Data</button>\n </div>\n @if (data.length > 0) {\n <div>\n <table mat-table [dataSource]=\"data\" style=\"border: 1px solid grey;\">\n <ng-container matColumnDef=\"id\">\n <th mat-header-cell *matHeaderCellDef> ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.id}} </td>\n </ng-container>\n <ng-container matColumnDef=\"spiffe\">\n <th mat-header-cell *matHeaderCellDef> Spiffe </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.spiffe}} </td>\n </ng-container>\n <ng-container matColumnDef=\"name\">\n <th mat-header-cell *matHeaderCellDef> Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.first_name}} {{element.last_name}}</td>\n </ng-container>\n <ng-container matColumnDef=\"email\">\n <th mat-header-cell *matHeaderCellDef> Email </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.email}} </td>\n </ng-container>\n <tr mat-header-row *matHeaderRowDef=\"['id', 'spiffe', 'name', 'email']\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: ['id', 'spiffe', 'name', 'email'];\"></tr>\n </table>\n <div style=\"border: 1px solid grey; padding: .5rem; border-top: none;\">\n <h3 style=\"margin: 0;\">Total Records {{ data.length }}</h3>\n </div>\n </div>\n } @else {\n <div style=\"margin-top: 1rem; font-style: italic;\">\n No Data Available\n </div>\n }\n </div>\n}\n\n", styles: [".user-chip--primary{background-color:var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5))!important;color:#fff!important;--mdc-evolution-chip-container-color: var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5));--mdc-evolution-chip-label-text-color: #fff}.user-chip--primary :is(.mdc-evolution-chip__text-label,.mdc-evolution-chip__action,.mdc-evolution-chip__cell,.mat-mdc-chip-action-label){color:#fff!important}.user-chip--primary,.user-chip--primary *{color:#fff!important}:host ::ng-deep .user-chip--primary{background-color:var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5))!important;color:#fff!important;--mdc-evolution-chip-container-color: var(--mdc-theme-primary, var(--md-sys-color-primary, #3f51b5));--mdc-evolution-chip-label-text-color: #fff}:host ::ng-deep .user-chip--primary .mdc-evolution-chip__text-label,:host ::ng-deep .user-chip--primary .mdc-evolution-chip__action,:host ::ng-deep .user-chip--primary .mdc-evolution-chip__cell,:host ::ng-deep .user-chip--primary .mat-mdc-chip-action-label,:host ::ng-deep .user-chip--primary *{color:#fff!important}.box{padding:.5rem;border:1px solid rgb(174,174,13);background-color:#ececaf}\n"] }]
4269
4459
  }], propDecorators: { server: [{
4270
4460
  type: Input
4271
4461
  }], wsServer: [{
@@ -4280,6 +4470,91 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4280
4470
  type: Input
4281
4471
  }] } });
4282
4472
 
4473
+ class WsMessagingComponent {
4474
+ constructor() {
4475
+ this.destroy$ = new Subject();
4476
+ this.fb = inject(FormBuilder);
4477
+ this.stateRequestServiceDemo = inject(StateRequestServiceDemo);
4478
+ // Filter out private stateManager channels (channels starting with "WS-")
4479
+ this.channels$ = this.stateRequestServiceDemo.channels$.pipe(map$1(channels => channels?.filter(channel => !channel.startsWith('WS-')) || []));
4480
+ this.user$ = this.stateRequestServiceDemo.user$;
4481
+ this.data$ = this.stateRequestServiceDemo.data$;
4482
+ this.connectionStatus$ = this.stateRequestServiceDemo.connectionStatus$;
4483
+ this.messages = this.fb.group({
4484
+ channels: this.fb.control(null, Validators.required),
4485
+ content: this.fb.control(null, Validators.required),
4486
+ });
4487
+ this.communicationMessages$ = this.stateRequestServiceDemo.communicationMessages$;
4488
+ this.latestCommunicationMessages$ = this.stateRequestServiceDemo.latestCommunicationMessages$;
4489
+ this.chat$ = combineLatest([this.user$, this.communicationMessages$])
4490
+ .pipe(map$1(([user, messages]) => ({ user, messages })), map$1(obj => {
4491
+ if (!obj.user)
4492
+ return EMPTY;
4493
+ const mainUser = '';
4494
+ const messages = obj.messages.map((item) => {
4495
+ // Message transformation logic
4496
+ });
4497
+ return { user: mainUser, messages };
4498
+ }));
4499
+ }
4500
+ get channels() {
4501
+ return this.messages.get('channels');
4502
+ }
4503
+ get content() {
4504
+ return this.messages.get('content');
4505
+ }
4506
+ get isValid() {
4507
+ return this.channels.valid;
4508
+ }
4509
+ get selectedChannels() {
4510
+ return this.channels.value || [];
4511
+ }
4512
+ ngOnInit() {
4513
+ this.stateRequestServiceDemo.updateConnection(this.server, this.wsServer, this.jwtToken, this.user);
4514
+ // Only trigger once when connection becomes true
4515
+ this.connectionStatus$.pipe(filter$1(status => status === true), take$1(1), takeUntil$1(this.destroy$)).subscribe(() => {
4516
+ // Create a public messaging channel when connected
4517
+ this.stateRequestServiceDemo.createChannel('general');
4518
+ this.stateRequestServiceDemo.getAllChannels();
4519
+ });
4520
+ }
4521
+ ngOnDestroy() {
4522
+ this.destroy$.next();
4523
+ this.destroy$.complete();
4524
+ }
4525
+ onSendMessage(user) {
4526
+ this.messages.markAllAsTouched();
4527
+ if (this.messages.invalid)
4528
+ return;
4529
+ const selectedChannels = this.channels.value;
4530
+ const message = ChannelMessage.adapt({
4531
+ sessionId: {
4532
+ sessionId: user.sessionId,
4533
+ ldap: user.ldap,
4534
+ name: user.name,
4535
+ email: user.email,
4536
+ },
4537
+ content: { message: this.messages.value.content },
4538
+ });
4539
+ this.stateRequestServiceDemo.sendMessage(message, selectedChannels);
4540
+ this.content.reset();
4541
+ }
4542
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsMessagingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4543
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: WsMessagingComponent, selector: "app-ws-messaging", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user" }, ngImport: i0, template: "\n @if ((data$ | async); as data) {\n @if ((user$ | async); as user) {\n <div style=\"margin: 1rem; display: flex; gap: 1rem; flex-direction: column;\">\n <div>\n\n <div>\n <div style=\"display: flex; gap: 1rem\">\n <div style=\"flex:1\" [formGroup]=\"messages\">\n\n @if ((channels$ | async); as channels) {\n <div style=\"display:flex; gap: 1rem\">\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>Channels</mat-label>\n <mat-select formControlName=\"channels\" multiple #channelsSelect>\n <mat-select-trigger>\n {{ (channelsSelect.value?.[0] === 'allChannels' ? 'All Channels' : channelsSelect.value?.[0]) || '' }}\n @if ((channelsSelect.value?.length || 0) > 1) {\n <span>\n (+{{(channelsSelect.value?.length || 0) - 1}} {{channelsSelect.value?.length === 2 ? 'other' : 'others'}})\n </span>\n }\n </mat-select-trigger>\n <mat-option value=\"allChannels\">\n All Channels\n </mat-option>\n @for (channel of channels; track channel) {\n <mat-option [value]=\"channel\">\n {{ channel }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n\n @if (selectedChannels.length > 0) {\n @if ((channels$ | async); as channels) {\n <div style=\"display:flex\">\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>Send a Message</mat-label>\n <textarea\n matInput placeholder=\"Type your message...\"\n formControlName=\"content\"\n (keydown.enter)=\"onSendMessage(user); $event.preventDefault()\"\n ></textarea>\n </mat-form-field>\n </div>\n }\n <div style=\"display:flex; gap: .5rem;\">\n <div style=\"flex:1\"></div>\n <button mat-stroked-button (click)=\"onSendMessage(user)\">\n Send Message\n </button>\n </div>\n }\n </div>\n </div>\n @if ((chat$ | async); as chat) {\n <div style=\"border: thin gray solid; padding: 1rem; margin-top: 1rem;\">\n <!-- <app-messenger-chat\n [user]=\"chat.user\"\n [messages]=\"chat.messages\"\n ></app-messenger-chat> -->\n </div>\n }\n </div>\n\n </div>\n </div>\n }\n }\n\n @if ((channels$ | async); as channels) {\n <div style=\"margin: 1rem; padding: 1rem; background: #f5f5f5; border-radius: 4px;\">\n <strong>Available Channels:</strong> {{ channels | json }}\n </div>\n }\n", styles: [""], dependencies: [{ kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i2$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "directive", type: i4.MatSelectTrigger, selector: "mat-select-trigger" }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i12.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] }); }
4544
+ }
4545
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsMessagingComponent, decorators: [{
4546
+ type: Component,
4547
+ args: [{ selector: 'app-ws-messaging', standalone: false, template: "\n @if ((data$ | async); as data) {\n @if ((user$ | async); as user) {\n <div style=\"margin: 1rem; display: flex; gap: 1rem; flex-direction: column;\">\n <div>\n\n <div>\n <div style=\"display: flex; gap: 1rem\">\n <div style=\"flex:1\" [formGroup]=\"messages\">\n\n @if ((channels$ | async); as channels) {\n <div style=\"display:flex; gap: 1rem\">\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>Channels</mat-label>\n <mat-select formControlName=\"channels\" multiple #channelsSelect>\n <mat-select-trigger>\n {{ (channelsSelect.value?.[0] === 'allChannels' ? 'All Channels' : channelsSelect.value?.[0]) || '' }}\n @if ((channelsSelect.value?.length || 0) > 1) {\n <span>\n (+{{(channelsSelect.value?.length || 0) - 1}} {{channelsSelect.value?.length === 2 ? 'other' : 'others'}})\n </span>\n }\n </mat-select-trigger>\n <mat-option value=\"allChannels\">\n All Channels\n </mat-option>\n @for (channel of channels; track channel) {\n <mat-option [value]=\"channel\">\n {{ channel }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n }\n\n @if (selectedChannels.length > 0) {\n @if ((channels$ | async); as channels) {\n <div style=\"display:flex\">\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>Send a Message</mat-label>\n <textarea\n matInput placeholder=\"Type your message...\"\n formControlName=\"content\"\n (keydown.enter)=\"onSendMessage(user); $event.preventDefault()\"\n ></textarea>\n </mat-form-field>\n </div>\n }\n <div style=\"display:flex; gap: .5rem;\">\n <div style=\"flex:1\"></div>\n <button mat-stroked-button (click)=\"onSendMessage(user)\">\n Send Message\n </button>\n </div>\n }\n </div>\n </div>\n @if ((chat$ | async); as chat) {\n <div style=\"border: thin gray solid; padding: 1rem; margin-top: 1rem;\">\n <!-- <app-messenger-chat\n [user]=\"chat.user\"\n [messages]=\"chat.messages\"\n ></app-messenger-chat> -->\n </div>\n }\n </div>\n\n </div>\n </div>\n }\n }\n\n @if ((channels$ | async); as channels) {\n <div style=\"margin: 1rem; padding: 1rem; background: #f5f5f5; border-radius: 4px;\">\n <strong>Available Channels:</strong> {{ channels | json }}\n </div>\n }\n" }]
4548
+ }], propDecorators: { server: [{
4549
+ type: Input
4550
+ }], wsServer: [{
4551
+ type: Input
4552
+ }], jwtToken: [{
4553
+ type: Input
4554
+ }], user: [{
4555
+ type: Input
4556
+ }] } });
4557
+
4283
4558
  class WsNotificationsComponent {
4284
4559
  constructor() { }
4285
4560
  ngOnInit() {
@@ -4375,14 +4650,15 @@ class RequestManagerWsDemoComponent {
4375
4650
  // })
4376
4651
  // );
4377
4652
  ngOnInit() {
4378
- // this.stateRequestServiceDemo.updateConnection(this.server, this.wsServer, this.jwtToken, this.user);
4653
+ // Initialize WS connection and retry driver
4654
+ this.stateRequestServiceDemo.updateConnection(this.server, this.wsServer, this.jwtToken, this.user);
4379
4655
  }
4380
4656
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerWsDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4381
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RequestManagerWsDemoComponent, selector: "app-request-manager-ws-demo", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user", path: "path", wsChannel: "wsChannel" }, ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2 style=\"display: flex;\">\n <span style=\"flex:1\">HTTP Request State Manager - Websockets</span>\n @if ((connectionStatus$ | async); as connected) {\n <span>\n WS -\n <span style=\"color: green;\">Connected</span>\n </span>\n } @else {\n <span style=\"color: red;\">Disconnected {{ attempts$ | async }} - {{ nextRetry$ |async }}</span>\n }\n </h2>\n\n <div>\n\n @if ((user$ | async); as userInfo) {\n <div>\n <mat-toolbar>\n <div style=\"display: flex; flex:1\">\n <div style=\"flex:1\">{{ userInfo.content.name }}</div>\n <div>({{ userInfo.content.ldap }})</div>\n </div>\n </mat-toolbar>\n </div>\n }\n\n @if ((isPending$ | async)) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n\n <mat-tab-group animationDuration=\"0ms\" [selectedIndex]=\"1\">\n\n <mat-tab label=\"WS - Data Control\">\n <!-- DATA CONTROL -->\n <app-ws-data-control\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n [wsChannel]=\"wsChannel\"\n ></app-ws-data-control>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Messaging\">\n <!-- MESSAGING -->\n <!-- <app-ws-messaging\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n ></app-ws-messaging> -->\n\n </mat-tab>\n\n <mat-tab label=\"WS - Notifications\" [disabled]=\"true\">\n <!-- WS - Custom Messaging -->\n <app-ws-notifications></app-ws-notifications>\n </mat-tab>\n\n <mat-tab label=\"WS - AI Messaging\" [disabled]=\"true\">\n <!-- WS - AI Messaging -->\n <app-ws-ai-messaging></app-ws-ai-messaging>\n </mat-tab>\n\n </mat-tab-group>\n</div>\n\n</div>\n\n", styles: [""], dependencies: [{ kind: "component", type: i1$3.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i1$3.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i9.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i3$2.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "component", type: WsDataControlComponent, selector: "app-ws-data-control", inputs: ["server", "wsServer", "jwtToken", "user", "path", "wsChannel"] }, { kind: "component", type: WsNotificationsComponent, selector: "app-ws-notifications" }, { kind: "component", type: WsAiMessagingComponent, selector: "app-ws-ai-messaging" }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] }); }
4657
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: RequestManagerWsDemoComponent, selector: "app-request-manager-ws-demo", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user", path: "path", wsChannel: "wsChannel" }, ngImport: i0, template: "<div style=\"margin: 2rem;\">\n\n <h2 style=\"display: flex;\">\n <span style=\"flex:1\">HTTP Request State Manager - Websockets</span>\n @if ((connectionStatus$ | async); as connected) {\n <span>\n WS -\n <span style=\"color: green;\">Connected</span>\n </span>\n } @else {\n <span style=\"color: red;\">Disconnected {{ attempts$ | async }} - {{ nextRetry$ |async }}</span>\n }\n </h2>\n\n <div>\n\n @if ((user$ | async); as userInfo) {\n <div>\n <mat-toolbar>\n <div style=\"display: flex; flex:1\">\n <div style=\"flex:1\">{{ userInfo.name }}</div>\n <div>({{ userInfo.ldap }})</div>\n </div>\n </mat-toolbar>\n </div>\n }\n\n @if ((isPending$ | async)) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n\n <mat-tab-group animationDuration=\"0ms\" [selectedIndex]=\"1\">\n\n <mat-tab label=\"WS - Data Control\">\n <!-- DATA CONTROL -->\n <app-ws-data-control\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n [wsChannel]=\"wsChannel\"\n ></app-ws-data-control>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Messaging\">\n <!-- MESSAGING -->\n <app-ws-messaging\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n ></app-ws-messaging>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Notifications\" [disabled]=\"true\">\n <!-- WS - Custom Messaging -->\n <app-ws-notifications></app-ws-notifications>\n </mat-tab>\n\n <mat-tab label=\"WS - AI Messaging\" [disabled]=\"true\">\n <!-- WS - AI Messaging -->\n <app-ws-ai-messaging></app-ws-ai-messaging>\n </mat-tab>\n\n </mat-tab-group>\n</div>\n\n</div>\n\n", styles: [""], dependencies: [{ kind: "component", type: i1$3.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i1$3.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i9.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: i3$2.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "component", type: WsDataControlComponent, selector: "app-ws-data-control", inputs: ["server", "wsServer", "jwtToken", "user", "path", "wsChannel"] }, { kind: "component", type: WsMessagingComponent, selector: "app-ws-messaging", inputs: ["server", "wsServer", "jwtToken", "user"] }, { kind: "component", type: WsNotificationsComponent, selector: "app-ws-notifications" }, { kind: "component", type: WsAiMessagingComponent, selector: "app-ws-ai-messaging" }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] }); }
4382
4658
  }
4383
4659
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerWsDemoComponent, decorators: [{
4384
4660
  type: Component,
4385
- args: [{ selector: 'app-request-manager-ws-demo', standalone: false, template: "<div style=\"margin: 2rem;\">\n\n <h2 style=\"display: flex;\">\n <span style=\"flex:1\">HTTP Request State Manager - Websockets</span>\n @if ((connectionStatus$ | async); as connected) {\n <span>\n WS -\n <span style=\"color: green;\">Connected</span>\n </span>\n } @else {\n <span style=\"color: red;\">Disconnected {{ attempts$ | async }} - {{ nextRetry$ |async }}</span>\n }\n </h2>\n\n <div>\n\n @if ((user$ | async); as userInfo) {\n <div>\n <mat-toolbar>\n <div style=\"display: flex; flex:1\">\n <div style=\"flex:1\">{{ userInfo.content.name }}</div>\n <div>({{ userInfo.content.ldap }})</div>\n </div>\n </mat-toolbar>\n </div>\n }\n\n @if ((isPending$ | async)) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n\n <mat-tab-group animationDuration=\"0ms\" [selectedIndex]=\"1\">\n\n <mat-tab label=\"WS - Data Control\">\n <!-- DATA CONTROL -->\n <app-ws-data-control\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n [wsChannel]=\"wsChannel\"\n ></app-ws-data-control>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Messaging\">\n <!-- MESSAGING -->\n <!-- <app-ws-messaging\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n ></app-ws-messaging> -->\n\n </mat-tab>\n\n <mat-tab label=\"WS - Notifications\" [disabled]=\"true\">\n <!-- WS - Custom Messaging -->\n <app-ws-notifications></app-ws-notifications>\n </mat-tab>\n\n <mat-tab label=\"WS - AI Messaging\" [disabled]=\"true\">\n <!-- WS - AI Messaging -->\n <app-ws-ai-messaging></app-ws-ai-messaging>\n </mat-tab>\n\n </mat-tab-group>\n</div>\n\n</div>\n\n" }]
4661
+ args: [{ selector: 'app-request-manager-ws-demo', standalone: false, template: "<div style=\"margin: 2rem;\">\n\n <h2 style=\"display: flex;\">\n <span style=\"flex:1\">HTTP Request State Manager - Websockets</span>\n @if ((connectionStatus$ | async); as connected) {\n <span>\n WS -\n <span style=\"color: green;\">Connected</span>\n </span>\n } @else {\n <span style=\"color: red;\">Disconnected {{ attempts$ | async }} - {{ nextRetry$ |async }}</span>\n }\n </h2>\n\n <div>\n\n @if ((user$ | async); as userInfo) {\n <div>\n <mat-toolbar>\n <div style=\"display: flex; flex:1\">\n <div style=\"flex:1\">{{ userInfo.name }}</div>\n <div>({{ userInfo.ldap }})</div>\n </div>\n </mat-toolbar>\n </div>\n }\n\n @if ((isPending$ | async)) {\n <div>\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n </div>\n }\n\n <mat-tab-group animationDuration=\"0ms\" [selectedIndex]=\"1\">\n\n <mat-tab label=\"WS - Data Control\">\n <!-- DATA CONTROL -->\n <app-ws-data-control\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n [path]=\"path\"\n [wsChannel]=\"wsChannel\"\n ></app-ws-data-control>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Messaging\">\n <!-- MESSAGING -->\n <app-ws-messaging\n [server]=\"server\"\n [wsServer]=\"wsServer\"\n [jwtToken]=\"jwtToken\"\n [user]=\"user\"\n ></app-ws-messaging>\n\n </mat-tab>\n\n <mat-tab label=\"WS - Notifications\" [disabled]=\"true\">\n <!-- WS - Custom Messaging -->\n <app-ws-notifications></app-ws-notifications>\n </mat-tab>\n\n <mat-tab label=\"WS - AI Messaging\" [disabled]=\"true\">\n <!-- WS - AI Messaging -->\n <app-ws-ai-messaging></app-ws-ai-messaging>\n </mat-tab>\n\n </mat-tab-group>\n</div>\n\n</div>\n\n" }]
4386
4662
  }], propDecorators: { server: [{
4387
4663
  type: Input
4388
4664
  }], wsServer: [{
@@ -4815,117 +5091,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4815
5091
  args: [{ selector: 'app-store-state-manager-demo', providers: [SettingsStateService], standalone: false, template: "<div style=\"margin: 2rem;\">\n\n <h2>\n <span>Store State Manager</span>\n <span style=\"margin-left: .5rem;\">\n <mat-icon color=\"accent\">fiber_new</mat-icon>\n </span>\n </h2>\n\n {{ dataState$ | async | json}}\n\n <div style=\"display: flex; gap:.5rem; margin-top: 1rem; margin-bottom: 1rem;\">\n <button mat-stroked-button (click)=\"onUpdateEnum_1()\">Update Start</button>\n <button mat-stroked-button (click)=\"onUpdateEnum_2()\">Update End</button>\n <button mat-stroked-button (click)=\"onGetEnum_1()\">Dump</button>\n </div>\n\n <div>\n <h3 style=\"margin: 0;\">Enum 1</h3>\n {{ dataEnum$ | async | json}}\n </div>\n\n</div>\n" }]
4816
5092
  }] });
4817
5093
 
4818
- class WsMessagingComponent {
4819
- constructor() {
4820
- this.fb = inject(FormBuilder);
4821
- this.stateRequestServiceDemo = inject(StateRequestServiceDemo);
4822
- this.channels$ = this.stateRequestServiceDemo.channels$;
4823
- this.user$ = this.stateRequestServiceDemo.user$;
4824
- this.users$ = this.stateRequestServiceDemo.userList$;
4825
- this.data$ = this.stateRequestServiceDemo.data$;
4826
- this.connectionStatus$ = this.stateRequestServiceDemo.connectionStatus$;
4827
- this.messages = this.fb.group({
4828
- channels: this.fb.control(null, Validators.required),
4829
- toUsers: this.fb.control(null, Validators.required),
4830
- content: this.fb.control(null, Validators.required),
4831
- });
4832
- this.communicationMessages$ = this.stateRequestServiceDemo.communicationMessages$;
4833
- this.latestCommunicationMessages$ = this.stateRequestServiceDemo.latestCommunicationMessages$;
4834
- this.chat$ = combineLatest([this.user$, this.communicationMessages$])
4835
- .pipe(map$1(([user, messages]) => ({ user, messages })), map$1(obj => {
4836
- if (!obj.user)
4837
- return EMPTY;
4838
- // const mainUser = UserName.adapt({
4839
- // id: obj.user.username,
4840
- // first: obj.user?.name?.first,
4841
- // last: obj.user?.name?.last,
4842
- // })
4843
- const mainUser = '';
4844
- const messages = obj.messages.map((item) => {
4845
- if (item.toUser === 'allChannels') {
4846
- // item.toUser = UserName.adapt({
4847
- // id: '',
4848
- // first: 'All Users',
4849
- // last: '',
4850
- // })
4851
- }
4852
- // return Message.adapt({
4853
- // fromUser: {
4854
- // id: item.fromUser.username,
4855
- // first: item.fromUser?.name?.first,
4856
- // last: item.fromUser?.name?.last,
4857
- // },
4858
- // toUser: {
4859
- // id: item.toUser.username,
4860
- // first: item.toUser?.name?.first,
4861
- // last: item.toUser?.name?.last,
4862
- // },
4863
- // content: item.content,
4864
- // date: item.issued,
4865
- // })
4866
- });
4867
- return { user: mainUser, messages };
4868
- }));
4869
- }
4870
- get channels() {
4871
- return this.messages.get('channels');
4872
- }
4873
- get toUsers() {
4874
- return this.messages.get('toUsers');
4875
- }
4876
- get content() {
4877
- return this.messages.get('content');
4878
- }
4879
- get isValid() {
4880
- return this.toUsers.valid;
4881
- }
4882
- ngOnInit() {
4883
- this.stateRequestServiceDemo.updateConnection(this.server, this.wsServer, this.jwtToken, this.user);
4884
- // this.stateRequestServiceDemo.getData()
4885
- if (this.connectionStatus$) {
4886
- this.connectionStatus$
4887
- .subscribe(status => {
4888
- this.stateRequestServiceDemo.getAllChannels();
4889
- });
4890
- }
4891
- }
4892
- onSendMessage(user) {
4893
- this.messages.markAllAsTouched();
4894
- if (this.messages.invalid)
4895
- return;
4896
- const message = ChannelMessage.adapt({
4897
- message: this.messages.value.content,
4898
- ...user,
4899
- users: this.toUsers.value,
4900
- });
4901
- this.stateRequestServiceDemo.sendMessage(message);
4902
- this.content.reset();
4903
- }
4904
- onSendAlert() {
4905
- debugger;
4906
- this.messages.markAllAsTouched();
4907
- if (this.messages.invalid)
4908
- return;
4909
- const message = ChannelMessage.adapt({ ...this.messages.value, type: CommunicationType.ALERT });
4910
- this.stateRequestServiceDemo.sendMessage(message);
4911
- this.content.reset();
4912
- }
4913
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsMessagingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4914
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: WsMessagingComponent, selector: "app-ws-messaging", inputs: { server: "server", wsServer: "wsServer", jwtToken: "jwtToken", user: "user" }, ngImport: i0, template: "\n @if ((data$ | async); as data) {\n <div style=\"margin: 1rem; display: flex; gap: 1rem; flex-direction: column;\">\n <div>\n\n <div>\n <div style=\"display: flex; gap: 1rem\">\n <div style=\"flex:1\" [formGroup]=\"messages\">\n\n <div style=\"display:flex; gap: 1rem\">\n @if ((channels$ | async); as channels) {\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>Channels</mat-label>\n <mat-select formControlName=\"channels\" multiple #channelsSelect>\n <mat-select-trigger>\n {{ (channelsSelect.value?.[0] === 'allChannels' ? 'All Channels' : channelsSelect.value?.[0]?.content?.name) || '' }}\n @if ((channelsSelect.value?.length || 0) > 1) {\n <span>\n (+{{(channelsSelect.value?.length || 0) - 1}} {{channelsSelect.value?.length === 2 ? 'other' : 'others'}})\n </span>\n }\n </mat-select-trigger>\n <mat-option value=\"allChannels\">\n All Channels\n </mat-option>\n @for (channel of channels; track channel) {\n <mat-option [value]=\"channel\">\n {{ channel }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n @if ((users$ | async); as users) {\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>Users</mat-label>\n <mat-select formControlName=\"toUsers\" multiple #userSelect>\n <mat-select-trigger>\n {{ (userSelect.value?.[0] === 'allUsers' ? 'All Users' : userSelect.value?.[0]?.content?.name) || '' }}\n @if ((userSelect.value?.length || 0) > 1) {\n <span>\n (+{{(userSelect.value?.length || 0) - 1}} {{userSelect.value?.length === 2 ? 'other' : 'others'}})\n </span>\n }\n </mat-select-trigger>\n <mat-option value=\"allChannels\">\n All Users\n </mat-option>\n @for (user of users; track user) {\n <mat-option [value]=\"user\">\n {{ user.content.name }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n </div>\n\n @if ((users$ | async); as users) {\n <div style=\"display:flex\">\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>Send a Message</mat-label>\n <textarea\n matInput placeholder=\"Ex. It makes me feel...\"\n formControlName=\"content\"\n (keydown.enter)=\"onSendMessage(user); $event.preventDefault()\"\n [disabled]=\"!isValid\"\n ></textarea>\n </mat-form-field>\n </div>\n }\n\n <div style=\"display:flex; gap: .5rem;\">\n <div style=\"flex:1\"></div>\n <button mat-stroked-button (click)=\"onSendAlert()\" color=\"warn\">\n Send Alert\n </button>\n <button mat-stroked-button (click)=\"onSendMessage(user)\">\n Send Message\n </button>\n </div>\n </div>\n </div>\n @if ((chat$ | async); as chat) {\n <div style=\"border: thin gray solid; padding: 1rem; margin-top: 1rem;\">\n <!-- <app-messenger-chat\n [user]=\"chat.user\"\n [messages]=\"chat.messages\"\n ></app-messenger-chat> -->\n </div>\n }\n </div>\n\n </div>\n </div>\n }\n\n\n {{ channels$ | async | json }}\n", styles: [""], dependencies: [{ kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i2$1.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "component", type: i4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "directive", type: i4.MatSelectTrigger, selector: "mat-select-trigger" }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i12.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] }); }
4915
- }
4916
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsMessagingComponent, decorators: [{
4917
- type: Component,
4918
- args: [{ selector: 'app-ws-messaging', standalone: false, template: "\n @if ((data$ | async); as data) {\n <div style=\"margin: 1rem; display: flex; gap: 1rem; flex-direction: column;\">\n <div>\n\n <div>\n <div style=\"display: flex; gap: 1rem\">\n <div style=\"flex:1\" [formGroup]=\"messages\">\n\n <div style=\"display:flex; gap: 1rem\">\n @if ((channels$ | async); as channels) {\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>Channels</mat-label>\n <mat-select formControlName=\"channels\" multiple #channelsSelect>\n <mat-select-trigger>\n {{ (channelsSelect.value?.[0] === 'allChannels' ? 'All Channels' : channelsSelect.value?.[0]?.content?.name) || '' }}\n @if ((channelsSelect.value?.length || 0) > 1) {\n <span>\n (+{{(channelsSelect.value?.length || 0) - 1}} {{channelsSelect.value?.length === 2 ? 'other' : 'others'}})\n </span>\n }\n </mat-select-trigger>\n <mat-option value=\"allChannels\">\n All Channels\n </mat-option>\n @for (channel of channels; track channel) {\n <mat-option [value]=\"channel\">\n {{ channel }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n @if ((users$ | async); as users) {\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>Users</mat-label>\n <mat-select formControlName=\"toUsers\" multiple #userSelect>\n <mat-select-trigger>\n {{ (userSelect.value?.[0] === 'allUsers' ? 'All Users' : userSelect.value?.[0]?.content?.name) || '' }}\n @if ((userSelect.value?.length || 0) > 1) {\n <span>\n (+{{(userSelect.value?.length || 0) - 1}} {{userSelect.value?.length === 2 ? 'other' : 'others'}})\n </span>\n }\n </mat-select-trigger>\n <mat-option value=\"allChannels\">\n All Users\n </mat-option>\n @for (user of users; track user) {\n <mat-option [value]=\"user\">\n {{ user.content.name }}\n </mat-option>\n }\n </mat-select>\n </mat-form-field>\n }\n </div>\n\n @if ((users$ | async); as users) {\n <div style=\"display:flex\">\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>Send a Message</mat-label>\n <textarea\n matInput placeholder=\"Ex. It makes me feel...\"\n formControlName=\"content\"\n (keydown.enter)=\"onSendMessage(user); $event.preventDefault()\"\n [disabled]=\"!isValid\"\n ></textarea>\n </mat-form-field>\n </div>\n }\n\n <div style=\"display:flex; gap: .5rem;\">\n <div style=\"flex:1\"></div>\n <button mat-stroked-button (click)=\"onSendAlert()\" color=\"warn\">\n Send Alert\n </button>\n <button mat-stroked-button (click)=\"onSendMessage(user)\">\n Send Message\n </button>\n </div>\n </div>\n </div>\n @if ((chat$ | async); as chat) {\n <div style=\"border: thin gray solid; padding: 1rem; margin-top: 1rem;\">\n <!-- <app-messenger-chat\n [user]=\"chat.user\"\n [messages]=\"chat.messages\"\n ></app-messenger-chat> -->\n </div>\n }\n </div>\n\n </div>\n </div>\n }\n\n\n {{ channels$ | async | json }}\n" }]
4919
- }], propDecorators: { server: [{
4920
- type: Input
4921
- }], wsServer: [{
4922
- type: Input
4923
- }], jwtToken: [{
4924
- type: Input
4925
- }], user: [{
4926
- type: Input
4927
- }] } });
4928
-
4929
5094
  // import { MessengerChatModule } from 'src/app/components/messenger-chat/messenger-chat.module';
4930
5095
  // import { StoreStateManagerModule } from "src/app/beta_components/store-state-manager/store-state-manager.module";
4931
5096
  class HttpRequestManagerModule {