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,
|
|
739
|
+
constructor(sessionId = '', ldap = '', name = '', email = '') {
|
|
740
740
|
this.sessionId = sessionId;
|
|
741
|
-
this.
|
|
741
|
+
this.ldap = ldap;
|
|
742
|
+
this.name = name;
|
|
743
|
+
this.email = email;
|
|
742
744
|
}
|
|
743
745
|
static adapt(item) {
|
|
744
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
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
|
-
|
|
4108
|
-
|
|
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.
|
|
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.
|
|
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
|
-
//
|
|
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.
|
|
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.
|
|
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 {
|