frontend: implement layout selector feature with conditional rendering in settings and toolbar

This commit is contained in:
Carlos Santos 2025-11-24 19:16:20 +01:00
parent c3ca84ad66
commit bd021c9576
7 changed files with 60 additions and 18 deletions

View File

@ -40,6 +40,11 @@ export class MeetingLayoutComponent {
return this.meetingContextService.canModerateRoom() && remoteParticipants.length === 0;
});
/**
* Whether the layout selector feature is enabled
*/
protected readonly showLayoutSelector = this.meetingContextService.showLayoutSelector;
/**
* Tracks the order of active speakers (most recent last)
*/
@ -48,9 +53,16 @@ export class MeetingLayoutComponent {
/**
* Computed signal that provides the filtered list of participants to display.
* Automatically reacts to changes in layout service configuration.
* When showLayoutSelector is false, returns all remote participants (default behavior).
*/
readonly filteredRemoteParticipants = computed(() => {
const remoteParticipants = this.meetingContextService.remoteParticipants();
// If layout selector is disabled, use default behavior (show all participants)
if (!this.showLayoutSelector()) {
return remoteParticipants;
}
const isLastSpeakersMode = this.layoutService.isSmartMosaicEnabled();
if (!isLastSpeakersMode) {
@ -91,13 +103,16 @@ export class MeetingLayoutComponent {
constructor() {
effect(() => {
if (this.meetingContextService.lkRoom()) {
// Only setup active speakers if layout selector is enabled
if (this.showLayoutSelector() && this.meetingContextService.lkRoom()) {
this.setupActiveSpeakersListener();
}
});
// Effect to handle active speakers cleanup when participants leave
effect(() => {
// Skip if layout selector is disabled
if (!this.showLayoutSelector()) return;
if (!this.layoutService.isSmartMosaicEnabled()) return;
const remoteParticipants = this.meetingContextService.remoteParticipants();

View File

@ -1,9 +1,10 @@
<!-- Grid Layout Configuration Section -->
<div class="layout-section">
<div class="section-header">
<mat-icon class="section-icon material-symbols-outlined">browse</mat-icon>
<h4 class="section-title">Layout settings</h4>
</div>
@if (showLayoutSelector()) {
<div class="layout-section">
<div class="section-header">
<mat-icon class="section-icon material-symbols-outlined">browse</mat-icon>
<h4 class="section-title">Layout settings</h4>
</div>
<!-- Layout Mode Selection -->
<div class="layout-mode-container">
@ -55,4 +56,5 @@
</div>
</div>
}
</div>
</div>
}

View File

@ -9,6 +9,7 @@ import { MatSelectModule } from '@angular/material/select';
import { FormsModule } from '@angular/forms';
import { MeetLayoutMode } from '../../../models/layout.model';
import { MeetLayoutService } from '../../../services/layout.service';
import { MeetingContextService } from '../../../services/meeting/meeting-context.service';
/**
* Component for additional settings in the Settings Panel.
@ -30,12 +31,18 @@ import { MeetLayoutService } from '../../../services/layout.service';
})
export class MeetingSettingsPanelComponent {
private readonly layoutService = inject(MeetLayoutService);
protected readonly meetingContextService = inject(MeetingContextService);
/**
* Expose LayoutMode enum to template
*/
readonly LayoutMode = MeetLayoutMode;
/**
* Whether the layout selector feature is enabled
*/
protected readonly showLayoutSelector = this.meetingContextService.showLayoutSelector;
/**
* Current layout mode
*/

View File

@ -1,10 +1,12 @@
<!-- Grid Layout Settings Button -->
<button
mat-menu-item
id="grid-layout-settings-btn"
(click)="onOpenSettings()"
[matTooltip]="isMobileView() ? '' : 'Configure grid layout'"
>
<mat-icon class="material-symbols-outlined">browser</mat-icon>
<span>Adjust Layout</span>
</button>
@if (showLayoutSelector()) {
<button
mat-menu-item
id="grid-layout-settings-btn"
(click)="onOpenSettings()"
[matTooltip]="isMobileView() ? '' : 'Configure grid layout'"
>
<mat-icon class="material-symbols-outlined">browser</mat-icon>
<span>Adjust Layout</span>
</button>
}

View File

@ -5,6 +5,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatMenuModule } from '@angular/material/menu';
import { MatTooltipModule } from '@angular/material/tooltip';
import { PanelService, ViewportService, PanelType } from 'openvidu-components-angular';
import { MeetingContextService } from '../../../services/meeting/meeting-context.service';
/**
* Component for additional menu items in the toolbar's "More Options" menu.
@ -36,6 +37,11 @@ export class MeetingToolbarMoreOptionsButtonsComponent {
*/
private panelService = inject(PanelService);
/**
* Meeting context service for feature flags
*/
private meetingContextService = inject(MeetingContextService);
/**
* Computed properties for responsive button behavior
* These follow the same pattern as toolbar-media-buttons component
@ -44,6 +50,11 @@ export class MeetingToolbarMoreOptionsButtonsComponent {
readonly isTabletView = computed(() => this.viewportService.isTablet());
readonly isDesktopView = computed(() => this.viewportService.isDesktop());
/**
* Whether the layout selector feature is enabled
*/
readonly showLayoutSelector = computed(() => this.meetingContextService.showLayoutSelector());
/**
* Opens the settings panel to allow users to change grid layout
*/

View File

@ -28,6 +28,7 @@ export interface ApplicationFeatures {
showSettings: boolean;
showFullscreen: boolean;
showThemeSelector: boolean;
showLayoutSelector: boolean;
// Permissions
canModerateRoom: boolean;
@ -56,6 +57,7 @@ const DEFAULT_FEATURES: ApplicationFeatures = {
showSettings: true,
showFullscreen: true,
showThemeSelector: true,
showLayoutSelector: false,
canModerateRoom: false,
canRecordRoom: false,

View File

@ -98,13 +98,16 @@ export class MeetingContextService {
/**
* Computed signal for whether the current user can moderate the room
* Derived from FeatureConfigurationService
*/
readonly canModerateRoom = computed(() => this.featureConfigService.features().canModerateRoom);
/**
* Computed signal for whether the layout selector feature is enabled
*/
readonly showLayoutSelector = computed(() => this.featureConfigService.features().showLayoutSelector);
/**
* Computed signal for whether the device is mobile
* Derived from ViewportService for responsive UI
*/
readonly isMobile = computed(() => this.viewportService.isMobile());