http-request-manager 18.5.23 → 18.6.0

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.
@@ -4507,11 +4507,15 @@ class WsMessagingComponent {
4507
4507
  this.fb = inject(FormBuilder);
4508
4508
  this.stateRequestServiceDemo = inject(StateRequestServiceDemo);
4509
4509
  this.toastService = inject(ToastMessageDisplayService);
4510
- // Only show public channels (channels starting with "PUB-")
4510
+ // Only show public channels (starting with "PUB-"), strip prefix for display
4511
4511
  // SYS- channels are private/internal and hidden from users
4512
- this.channels$ = this.stateRequestServiceDemo.channels$.pipe(map$1(channels => channels?.filter(channel => channel.startsWith('PUB-')) || []));
4513
- // Track subscribed public channels only
4514
- this.subscribedChannels$ = this.stateRequestServiceDemo.subscribedChannels$.pipe(map$1(set => Array.from(set).filter(channel => channel.startsWith('PUB-'))), startWith$1([]), distinctUntilChanged$1((prev, curr) => prev.length === curr.length && prev.every((v, i) => v === curr[i])));
4512
+ this.channels$ = this.stateRequestServiceDemo.channels$.pipe(map$1(channels => channels
4513
+ ?.filter(channel => channel.startsWith('PUB-'))
4514
+ .map(channel => channel.replace('PUB-', '')) || []));
4515
+ // Track subscribed public channels only, strip prefix for display
4516
+ this.subscribedChannels$ = this.stateRequestServiceDemo.subscribedChannels$.pipe(map$1(set => Array.from(set)
4517
+ .filter(channel => channel.startsWith('PUB-'))
4518
+ .map(channel => channel.replace('PUB-', ''))), startWith$1([]), distinctUntilChanged$1((prev, curr) => prev.length === curr.length && prev.every((v, i) => v === curr[i])));
4515
4519
  this.user$ = this.stateRequestServiceDemo.user$;
4516
4520
  this.data$ = this.stateRequestServiceDemo.data$;
4517
4521
  this.connectionStatus$ = this.stateRequestServiceDemo.connectionStatus$;
@@ -4535,6 +4539,12 @@ class WsMessagingComponent {
4535
4539
  return { user: mainUser, messages };
4536
4540
  }));
4537
4541
  }
4542
+ /**
4543
+ * Helper to ensure channel has PUB- prefix for outgoing communication
4544
+ */
4545
+ toPublicChannel(channel) {
4546
+ return channel.startsWith('PUB-') ? channel : `PUB-${channel}`;
4547
+ }
4538
4548
  get selectedChannels() {
4539
4549
  return this.messages.get('selectedChannels');
4540
4550
  }
@@ -4583,18 +4593,21 @@ class WsMessagingComponent {
4583
4593
  }
4584
4594
  /**
4585
4595
  * Subscribe to a channel to receive messages
4596
+ * Auto-adds PUB- prefix for outgoing communication
4586
4597
  */
4587
4598
  onSubscribeToChannel(channel) {
4588
- this.stateRequestServiceDemo.subscribeToChannel(channel);
4599
+ this.stateRequestServiceDemo.subscribeToChannel(this.toPublicChannel(channel));
4589
4600
  }
4590
4601
  /**
4591
4602
  * Unsubscribe from a channel
4603
+ * Auto-adds PUB- prefix for outgoing communication
4592
4604
  */
4593
4605
  onUnsubscribeFromChannel(channel) {
4594
- this.stateRequestServiceDemo.unsubscribeFromChannel(channel);
4606
+ this.stateRequestServiceDemo.unsubscribeFromChannel(this.toPublicChannel(channel));
4595
4607
  }
4596
4608
  /**
4597
4609
  * Check if currently subscribed to a channel
4610
+ * Compares display names (without PUB- prefix)
4598
4611
  */
4599
4612
  isSubscribed(channel, subscribedChannels) {
4600
4613
  return subscribedChannels.includes(channel);
@@ -4606,6 +4619,8 @@ class WsMessagingComponent {
4606
4619
  const channelsToSend = this.selectedChannels.value;
4607
4620
  if (channelsToSend.length === 0)
4608
4621
  return;
4622
+ // Add PUB- prefix for outgoing channel communication
4623
+ const publicChannels = channelsToSend.map(ch => this.toPublicChannel(ch));
4609
4624
  const message = ChannelMessage.adapt({
4610
4625
  sessionId: {
4611
4626
  id: user.id,
@@ -4615,15 +4630,15 @@ class WsMessagingComponent {
4615
4630
  },
4616
4631
  content: { message: this.messages.value.content },
4617
4632
  });
4618
- this.stateRequestServiceDemo.sendMessage(message, channelsToSend);
4633
+ this.stateRequestServiceDemo.sendMessage(message, publicChannels);
4619
4634
  this.content.reset();
4620
4635
  }
4621
4636
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsMessagingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4622
- 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\n <!-- Channel Creation Section -->\n <div style=\"padding: 1rem; background: #e3f2fd; border-radius: 4px;\">\n <strong>Create New Channel</strong>\n <div style=\"display: flex; gap: 0.5rem; margin-top: 0.5rem; align-items: center;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Channel Name</mat-label>\n <input matInput [formControl]=\"newChannelName\" placeholder=\"Enter channel name\"\n (keydown.enter)=\"onCreateChannel()\">\n </mat-form-field>\n <button mat-raised-button color=\"primary\" (click)=\"onCreateChannel()\"\n [disabled]=\"newChannelName.invalid\">\n Create Channel\n </button>\n </div>\n </div>\n\n <!-- Messaging Section - only show when subscribed to channels -->\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n @if (subscribedChannels.length > 0) {\n <div style=\"padding: 1rem; background: #fff3e0; border-radius: 4px;\" [formGroup]=\"messages\">\n <strong>Send Message</strong>\n\n <!-- Channel Selection with Checkboxes -->\n <div style=\"margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Select Channels</mat-label>\n <mat-select formControlName=\"selectedChannels\" multiple>\n <mat-select-trigger>\n @if (selectedChannels.value?.length === 1) {\n {{ selectedChannels.value[0] }}\n } @else if (selectedChannels.value?.length > 1) {\n {{ selectedChannels.value[0] }} (+{{ selectedChannels.value.length - 1 }} {{ selectedChannels.value.length === 2 ? 'other' : 'others' }})\n }\n </mat-select-trigger>\n @for (channel of subscribedChannels; track channel) {\n <mat-option [value]=\"channel\">{{ channel }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- Message Input -->\n <div style=\"display: flex; gap: 0.5rem; align-items: flex-start;\">\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>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 <button mat-raised-button color=\"primary\" (click)=\"onSendMessage(user)\"\n [disabled]=\"messages.invalid\">\n Send\n </button>\n </div>\n </div>\n }\n }\n\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 }\n }\n\n <!-- Channel List with Subscription Controls -->\n @if ((channels$ | async); as channels) {\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n <div style=\"margin: 1rem; padding: 1rem; background: #f5f5f5; border-radius: 4px;\">\n <strong>Available Channels</strong>\n <div style=\"margin-top: 0.5rem;\">\n @if (channels.length === 0) {\n <div style=\"color: #666; font-style: italic;\">No channels available. Create one above.</div>\n } @else {\n <div style=\"display: flex; flex-wrap: wrap; gap: 0.5rem;\">\n @for (channel of channels; track channel) {\n <div style=\"display: flex; align-items: center; gap: 0.25rem; padding: 0.5rem; background: white; border-radius: 4px; border: 1px solid #ddd;\">\n <span style=\"font-weight: 500;\">{{ channel }}</span>\n @if (isSubscribed(channel, subscribedChannels)) {\n <mat-icon style=\"color: green; font-size: 18px; width: 18px; height: 18px;\">check_circle</mat-icon>\n <button mat-stroked-button color=\"warn\" (click)=\"onUnsubscribeFromChannel(channel)\" style=\"margin-left: 0.25rem;\">\n Unsubscribe\n </button>\n } @else {\n <button mat-stroked-button color=\"primary\" (click)=\"onSubscribeToChannel(channel)\" style=\"margin-left: 0.25rem;\">\n Subscribe\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n\n @if (subscribedChannels.length > 0) {\n <div style=\"margin-top: 1rem; padding-top: 0.5rem; border-top: 1px solid #ddd;\">\n <strong>Your Subscriptions:</strong>\n <span style=\"color: #1976d2; margin-left: 0.5rem;\">{{ subscribedChannels.join(', ') }}</span>\n </div>\n }\n </div>\n }\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.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["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: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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" }] }); }
4637
+ 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=\"display: flex; gap: 1rem; flex-direction: column; margin-top: 1rem;\">\n\n <!-- Channel Creation Section -->\n <div style=\"padding: 1rem; background: #e3f2fd; border-radius: 4px;\">\n <strong>Create New Channel</strong>\n <div style=\"display: flex; gap: 0.5rem; margin-top: 0.5rem; align-items: center;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Channel Name</mat-label>\n <input matInput [formControl]=\"newChannelName\" placeholder=\"Enter channel name\"\n (keydown.enter)=\"onCreateChannel()\">\n </mat-form-field>\n <button mat-raised-button color=\"primary\" (click)=\"onCreateChannel()\"\n [disabled]=\"newChannelName.invalid\">\n Create Channel\n </button>\n </div>\n </div>\n\n <!-- Messaging Section - only show when subscribed to channels -->\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n @if (subscribedChannels.length > 0) {\n <div style=\"padding: 1rem; background: #fff3e0; border-radius: 4px;\" [formGroup]=\"messages\">\n <strong>Send Message</strong>\n\n <!-- Channel Selection with Checkboxes -->\n <div style=\"margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Select Channels</mat-label>\n <mat-select formControlName=\"selectedChannels\" multiple>\n <mat-select-trigger>\n @if (selectedChannels.value?.length === 1) {\n {{ selectedChannels.value[0] }}\n } @else if (selectedChannels.value?.length > 1) {\n {{ selectedChannels.value[0] }} (+{{ selectedChannels.value.length - 1 }} {{ selectedChannels.value.length === 2 ? 'other' : 'others' }})\n }\n </mat-select-trigger>\n @for (channel of subscribedChannels; track channel) {\n <mat-option [value]=\"channel\">{{ channel }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- Message Input -->\n <div style=\"display: flex; gap: 0.5rem; align-items: flex-start;\">\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>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 <button mat-raised-button color=\"primary\" (click)=\"onSendMessage(user)\"\n [disabled]=\"messages.invalid\">\n Send\n </button>\n </div>\n </div>\n }\n }\n\n </div>\n }\n }\n\n <!-- Channel List with Subscription Controls -->\n @if ((channels$ | async); as channels) {\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n <div style=\"padding: 1rem; background: #f5f5f5; border-radius: 4px; margin-top: 1rem;\">\n <strong>Available Channels</strong>\n <div style=\"margin-top: 0.5rem;\">\n @if (channels.length === 0) {\n <div style=\"color: #666; font-style: italic;\">No channels available. Create one above.</div>\n } @else {\n <div style=\"display: flex; flex-wrap: wrap; gap: 0.5rem;\">\n @for (channel of channels; track channel) {\n <div style=\"display: flex; align-items: center; gap: 0.25rem; padding: 0.5rem; background: white; border-radius: 4px; border: 1px solid #ddd;\">\n <span style=\"font-weight: 500;\">{{ channel }}</span>\n @if (isSubscribed(channel, subscribedChannels)) {\n <mat-icon style=\"color: green; font-size: 18px; width: 18px; height: 18px;\">check_circle</mat-icon>\n <button mat-stroked-button color=\"warn\" (click)=\"onUnsubscribeFromChannel(channel)\" style=\"margin-left: 0.25rem;\">\n Unsubscribe\n </button>\n } @else {\n <button mat-stroked-button color=\"primary\" (click)=\"onSubscribeToChannel(channel)\" style=\"margin-left: 0.25rem;\">\n Subscribe\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n\n @if (subscribedChannels.length > 0) {\n <div style=\"margin-top: 1rem; padding-top: 0.5rem; border-top: 1px solid #ddd;\">\n <strong>Your Subscriptions:</strong>\n <span style=\"color: #1976d2; margin-left: 0.5rem;\">{{ subscribedChannels.join(', ') }}</span>\n </div>\n }\n </div>\n }\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.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["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: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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" }] }); }
4623
4638
  }
4624
4639
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: WsMessagingComponent, decorators: [{
4625
4640
  type: Component,
4626
- 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\n <!-- Channel Creation Section -->\n <div style=\"padding: 1rem; background: #e3f2fd; border-radius: 4px;\">\n <strong>Create New Channel</strong>\n <div style=\"display: flex; gap: 0.5rem; margin-top: 0.5rem; align-items: center;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Channel Name</mat-label>\n <input matInput [formControl]=\"newChannelName\" placeholder=\"Enter channel name\"\n (keydown.enter)=\"onCreateChannel()\">\n </mat-form-field>\n <button mat-raised-button color=\"primary\" (click)=\"onCreateChannel()\"\n [disabled]=\"newChannelName.invalid\">\n Create Channel\n </button>\n </div>\n </div>\n\n <!-- Messaging Section - only show when subscribed to channels -->\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n @if (subscribedChannels.length > 0) {\n <div style=\"padding: 1rem; background: #fff3e0; border-radius: 4px;\" [formGroup]=\"messages\">\n <strong>Send Message</strong>\n\n <!-- Channel Selection with Checkboxes -->\n <div style=\"margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Select Channels</mat-label>\n <mat-select formControlName=\"selectedChannels\" multiple>\n <mat-select-trigger>\n @if (selectedChannels.value?.length === 1) {\n {{ selectedChannels.value[0] }}\n } @else if (selectedChannels.value?.length > 1) {\n {{ selectedChannels.value[0] }} (+{{ selectedChannels.value.length - 1 }} {{ selectedChannels.value.length === 2 ? 'other' : 'others' }})\n }\n </mat-select-trigger>\n @for (channel of subscribedChannels; track channel) {\n <mat-option [value]=\"channel\">{{ channel }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- Message Input -->\n <div style=\"display: flex; gap: 0.5rem; align-items: flex-start;\">\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>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 <button mat-raised-button color=\"primary\" (click)=\"onSendMessage(user)\"\n [disabled]=\"messages.invalid\">\n Send\n </button>\n </div>\n </div>\n }\n }\n\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 }\n }\n\n <!-- Channel List with Subscription Controls -->\n @if ((channels$ | async); as channels) {\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n <div style=\"margin: 1rem; padding: 1rem; background: #f5f5f5; border-radius: 4px;\">\n <strong>Available Channels</strong>\n <div style=\"margin-top: 0.5rem;\">\n @if (channels.length === 0) {\n <div style=\"color: #666; font-style: italic;\">No channels available. Create one above.</div>\n } @else {\n <div style=\"display: flex; flex-wrap: wrap; gap: 0.5rem;\">\n @for (channel of channels; track channel) {\n <div style=\"display: flex; align-items: center; gap: 0.25rem; padding: 0.5rem; background: white; border-radius: 4px; border: 1px solid #ddd;\">\n <span style=\"font-weight: 500;\">{{ channel }}</span>\n @if (isSubscribed(channel, subscribedChannels)) {\n <mat-icon style=\"color: green; font-size: 18px; width: 18px; height: 18px;\">check_circle</mat-icon>\n <button mat-stroked-button color=\"warn\" (click)=\"onUnsubscribeFromChannel(channel)\" style=\"margin-left: 0.25rem;\">\n Unsubscribe\n </button>\n } @else {\n <button mat-stroked-button color=\"primary\" (click)=\"onSubscribeToChannel(channel)\" style=\"margin-left: 0.25rem;\">\n Subscribe\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n\n @if (subscribedChannels.length > 0) {\n <div style=\"margin-top: 1rem; padding-top: 0.5rem; border-top: 1px solid #ddd;\">\n <strong>Your Subscriptions:</strong>\n <span style=\"color: #1976d2; margin-left: 0.5rem;\">{{ subscribedChannels.join(', ') }}</span>\n </div>\n }\n </div>\n }\n }\n" }]
4641
+ args: [{ selector: 'app-ws-messaging', standalone: false, template: "\n @if ((data$ | async); as data) {\n @if ((user$ | async); as user) {\n <div style=\"display: flex; gap: 1rem; flex-direction: column; margin-top: 1rem;\">\n\n <!-- Channel Creation Section -->\n <div style=\"padding: 1rem; background: #e3f2fd; border-radius: 4px;\">\n <strong>Create New Channel</strong>\n <div style=\"display: flex; gap: 0.5rem; margin-top: 0.5rem; align-items: center;\">\n <mat-form-field appearance=\"outline\" style=\"flex: 1;\">\n <mat-label>Channel Name</mat-label>\n <input matInput [formControl]=\"newChannelName\" placeholder=\"Enter channel name\"\n (keydown.enter)=\"onCreateChannel()\">\n </mat-form-field>\n <button mat-raised-button color=\"primary\" (click)=\"onCreateChannel()\"\n [disabled]=\"newChannelName.invalid\">\n Create Channel\n </button>\n </div>\n </div>\n\n <!-- Messaging Section - only show when subscribed to channels -->\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n @if (subscribedChannels.length > 0) {\n <div style=\"padding: 1rem; background: #fff3e0; border-radius: 4px;\" [formGroup]=\"messages\">\n <strong>Send Message</strong>\n\n <!-- Channel Selection with Checkboxes -->\n <div style=\"margin-top: 0.5rem;\">\n <mat-form-field appearance=\"outline\" style=\"width: 100%;\">\n <mat-label>Select Channels</mat-label>\n <mat-select formControlName=\"selectedChannels\" multiple>\n <mat-select-trigger>\n @if (selectedChannels.value?.length === 1) {\n {{ selectedChannels.value[0] }}\n } @else if (selectedChannels.value?.length > 1) {\n {{ selectedChannels.value[0] }} (+{{ selectedChannels.value.length - 1 }} {{ selectedChannels.value.length === 2 ? 'other' : 'others' }})\n }\n </mat-select-trigger>\n @for (channel of subscribedChannels; track channel) {\n <mat-option [value]=\"channel\">{{ channel }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n </div>\n\n <!-- Message Input -->\n <div style=\"display: flex; gap: 0.5rem; align-items: flex-start;\">\n <mat-form-field style=\"flex:1\" appearance=\"outline\">\n <mat-label>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 <button mat-raised-button color=\"primary\" (click)=\"onSendMessage(user)\"\n [disabled]=\"messages.invalid\">\n Send\n </button>\n </div>\n </div>\n }\n }\n\n </div>\n }\n }\n\n <!-- Channel List with Subscription Controls -->\n @if ((channels$ | async); as channels) {\n @if ((subscribedChannels$ | async); as subscribedChannels) {\n <div style=\"padding: 1rem; background: #f5f5f5; border-radius: 4px; margin-top: 1rem;\">\n <strong>Available Channels</strong>\n <div style=\"margin-top: 0.5rem;\">\n @if (channels.length === 0) {\n <div style=\"color: #666; font-style: italic;\">No channels available. Create one above.</div>\n } @else {\n <div style=\"display: flex; flex-wrap: wrap; gap: 0.5rem;\">\n @for (channel of channels; track channel) {\n <div style=\"display: flex; align-items: center; gap: 0.25rem; padding: 0.5rem; background: white; border-radius: 4px; border: 1px solid #ddd;\">\n <span style=\"font-weight: 500;\">{{ channel }}</span>\n @if (isSubscribed(channel, subscribedChannels)) {\n <mat-icon style=\"color: green; font-size: 18px; width: 18px; height: 18px;\">check_circle</mat-icon>\n <button mat-stroked-button color=\"warn\" (click)=\"onUnsubscribeFromChannel(channel)\" style=\"margin-left: 0.25rem;\">\n Unsubscribe\n </button>\n } @else {\n <button mat-stroked-button color=\"primary\" (click)=\"onSubscribeToChannel(channel)\" style=\"margin-left: 0.25rem;\">\n Subscribe\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n\n @if (subscribedChannels.length > 0) {\n <div style=\"margin-top: 1rem; padding-top: 0.5rem; border-top: 1px solid #ddd;\">\n <strong>Your Subscriptions:</strong>\n <span style=\"color: #1976d2; margin-left: 0.5rem;\">{{ subscribedChannels.join(', ') }}</span>\n </div>\n }\n </div>\n }\n }\n" }]
4627
4642
  }], propDecorators: { server: [{
4628
4643
  type: Input
4629
4644
  }], wsServer: [{