http-request-manager 18.5.19 → 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
 
@@ -2906,6 +2975,16 @@ class HTTPManagerStateService extends ComponentStore {
2906
2975
  // }
2907
2976
  this.channels.next(message.channels);
2908
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;
2909
2988
  case 'stateMangerMessage':
2910
2989
  ;
2911
2990
  if (message.data.sessionId !== this.user.value?.sessionId) {
@@ -2914,17 +2993,22 @@ class HTTPManagerStateService extends ComponentStore {
2914
2993
  this.fetchRecord(RequestOptions.adapt({ path: message.data.content.path }), message.data.content.method);
2915
2994
  }
2916
2995
  break;
2917
- // case 'channelMessage':
2918
- // console.log('💬 Message:', message.data)
2919
- // this.fetchRecord(RequestOptions.adapt({ path: message.content.path }), message.content.method)
2920
- // break;
2921
- // case 'channelCommunication':
2922
- // console.log('💬 Message Communication:', message.data)
2923
- // this.appendMessages(message.data)
2924
- // break;
2925
- // case 'channelAlerts':
2926
- // console.log('💬 Message Alerts:', message.data)
2927
- // 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;
2928
3012
  case 'usersInChannel':
2929
3013
  console.log('👥 Users:', message.data.users);
2930
3014
  this.userList.next(message.data.users);
@@ -3404,17 +3488,119 @@ class HTTPManagerStateService extends ComponentStore {
3404
3488
  }
3405
3489
  }
3406
3490
  wsMessaging(message, channels) {
3407
- const user = this.user.value || this.user.value;
3491
+ const user = this.user.value;
3408
3492
  const messageInfo = ChannelMessage.adapt({ ...message, fromUser: user });
3493
+ console.log('📤 wsMessaging called with channels:', channels);
3409
3494
  if (this.wsConnection && this.apiOptions.ws) {
3410
- const wsServer = (channels) ? channels : this.apiOptions.ws.id;
3411
- // if(messageInfo.toUser === 'allChannels') {
3412
- // this.httpManagerService.sendBroadcast(messageInfo)
3413
- // } else if(messageInfo.toUser === 'allInChannel') {
3414
- // this.httpManagerService.sendMessageInChannel(wsServer, messageInfo)
3415
- // } else {
3416
- // this.httpManagerService.sendMessageToUser(wsServer, messageInfo)
3417
- // }
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.');
3418
3604
  }
3419
3605
  }
3420
3606
  // --------------------------------------------------------------------------------------------------
@@ -4109,10 +4295,9 @@ class StateRequestServiceDemo extends HTTPManagerStateService {
4109
4295
  console.log('User Action:', user);
4110
4296
  });
4111
4297
  }
4112
- sendMessage(data) {
4113
- debugger;
4114
- console.log('sendMessage', data);
4115
- this.wsMessaging(data);
4298
+ sendMessage(data, channels) {
4299
+ console.log('sendMessage', data, 'to channels:', channels);
4300
+ this.wsMessaging(data, channels);
4116
4301
  }
4117
4302
  getAllChannels() {
4118
4303
  this.httpManagerService.getAllChannels();
@@ -4200,7 +4385,6 @@ class StateDataRequestService extends HTTPManagerStateService {
4200
4385
  this.createRecord(newData, RequestOptions.adapt({ path: this.path }));
4201
4386
  }
4202
4387
  sendMessage(data) {
4203
- debugger;
4204
4388
  console.log('sendMessage', data);
4205
4389
  this.wsMessaging(data);
4206
4390
  }
@@ -4267,11 +4451,11 @@ class WsDataControlComponent {
4267
4451
  this.stateDataRequestService.deleteData(lastRec);
4268
4452
  }
4269
4453
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsDataControlComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4270
- 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" }] }); }
4271
4455
  }
4272
4456
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsDataControlComponent, decorators: [{
4273
4457
  type: Component,
4274
- 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"] }]
4275
4459
  }], propDecorators: { server: [{
4276
4460
  type: Input
4277
4461
  }], wsServer: [{
@@ -4286,6 +4470,91 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4286
4470
  type: Input
4287
4471
  }] } });
4288
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
+
4289
4558
  class WsNotificationsComponent {
4290
4559
  constructor() { }
4291
4560
  ngOnInit() {
@@ -4385,11 +4654,11 @@ class RequestManagerWsDemoComponent {
4385
4654
  this.stateRequestServiceDemo.updateConnection(this.server, this.wsServer, this.jwtToken, this.user);
4386
4655
  }
4387
4656
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerWsDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4388
- 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" }] }); }
4389
4658
  }
4390
4659
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: RequestManagerWsDemoComponent, decorators: [{
4391
4660
  type: Component,
4392
- 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" }]
4393
4662
  }], propDecorators: { server: [{
4394
4663
  type: Input
4395
4664
  }], wsServer: [{
@@ -4822,117 +5091,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4822
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" }]
4823
5092
  }] });
4824
5093
 
4825
- class WsMessagingComponent {
4826
- constructor() {
4827
- this.fb = inject(FormBuilder);
4828
- this.stateRequestServiceDemo = inject(StateRequestServiceDemo);
4829
- this.channels$ = this.stateRequestServiceDemo.channels$;
4830
- this.user$ = this.stateRequestServiceDemo.user$;
4831
- this.users$ = this.stateRequestServiceDemo.userList$;
4832
- this.data$ = this.stateRequestServiceDemo.data$;
4833
- this.connectionStatus$ = this.stateRequestServiceDemo.connectionStatus$;
4834
- this.messages = this.fb.group({
4835
- channels: this.fb.control(null, Validators.required),
4836
- toUsers: this.fb.control(null, Validators.required),
4837
- content: this.fb.control(null, Validators.required),
4838
- });
4839
- this.communicationMessages$ = this.stateRequestServiceDemo.communicationMessages$;
4840
- this.latestCommunicationMessages$ = this.stateRequestServiceDemo.latestCommunicationMessages$;
4841
- this.chat$ = combineLatest([this.user$, this.communicationMessages$])
4842
- .pipe(map$1(([user, messages]) => ({ user, messages })), map$1(obj => {
4843
- if (!obj.user)
4844
- return EMPTY;
4845
- // const mainUser = UserName.adapt({
4846
- // id: obj.user.username,
4847
- // first: obj.user?.name?.first,
4848
- // last: obj.user?.name?.last,
4849
- // })
4850
- const mainUser = '';
4851
- const messages = obj.messages.map((item) => {
4852
- if (item.toUser === 'allChannels') {
4853
- // item.toUser = UserName.adapt({
4854
- // id: '',
4855
- // first: 'All Users',
4856
- // last: '',
4857
- // })
4858
- }
4859
- // return Message.adapt({
4860
- // fromUser: {
4861
- // id: item.fromUser.username,
4862
- // first: item.fromUser?.name?.first,
4863
- // last: item.fromUser?.name?.last,
4864
- // },
4865
- // toUser: {
4866
- // id: item.toUser.username,
4867
- // first: item.toUser?.name?.first,
4868
- // last: item.toUser?.name?.last,
4869
- // },
4870
- // content: item.content,
4871
- // date: item.issued,
4872
- // })
4873
- });
4874
- return { user: mainUser, messages };
4875
- }));
4876
- }
4877
- get channels() {
4878
- return this.messages.get('channels');
4879
- }
4880
- get toUsers() {
4881
- return this.messages.get('toUsers');
4882
- }
4883
- get content() {
4884
- return this.messages.get('content');
4885
- }
4886
- get isValid() {
4887
- return this.toUsers.valid;
4888
- }
4889
- ngOnInit() {
4890
- this.stateRequestServiceDemo.updateConnection(this.server, this.wsServer, this.jwtToken, this.user);
4891
- // this.stateRequestServiceDemo.getData()
4892
- if (this.connectionStatus$) {
4893
- this.connectionStatus$
4894
- .subscribe(status => {
4895
- this.stateRequestServiceDemo.getAllChannels();
4896
- });
4897
- }
4898
- }
4899
- onSendMessage(user) {
4900
- this.messages.markAllAsTouched();
4901
- if (this.messages.invalid)
4902
- return;
4903
- const message = ChannelMessage.adapt({
4904
- message: this.messages.value.content,
4905
- ...user,
4906
- users: this.toUsers.value,
4907
- });
4908
- this.stateRequestServiceDemo.sendMessage(message);
4909
- this.content.reset();
4910
- }
4911
- onSendAlert() {
4912
- debugger;
4913
- this.messages.markAllAsTouched();
4914
- if (this.messages.invalid)
4915
- return;
4916
- const message = ChannelMessage.adapt({ ...this.messages.value, type: CommunicationType.ALERT });
4917
- this.stateRequestServiceDemo.sendMessage(message);
4918
- this.content.reset();
4919
- }
4920
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsMessagingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4921
- 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" }] }); }
4922
- }
4923
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsMessagingComponent, decorators: [{
4924
- type: Component,
4925
- 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" }]
4926
- }], propDecorators: { server: [{
4927
- type: Input
4928
- }], wsServer: [{
4929
- type: Input
4930
- }], jwtToken: [{
4931
- type: Input
4932
- }], user: [{
4933
- type: Input
4934
- }] } });
4935
-
4936
5094
  // import { MessengerChatModule } from 'src/app/components/messenger-chat/messenger-chat.module';
4937
5095
  // import { StoreStateManagerModule } from "src/app/beta_components/store-state-manager/store-state-manager.module";
4938
5096
  class HttpRequestManagerModule {