diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-custom-layout/meeting-custom-layout.component.html b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-custom-layout/meeting-custom-layout.component.html
index ad07a49a..362b125f 100644
--- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-custom-layout/meeting-custom-layout.component.html
+++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-custom-layout/meeting-custom-layout.component.html
@@ -1,8 +1,8 @@
@if (meetingContextService.lkRoom()) {
-
+
@if (shouldShowLinkOverlay()) {
@@ -43,7 +43,7 @@
- @if (shouldShowCaptions()) {
+ @if (areCaptionsEnabledByUser()) {
}
}
diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-custom-layout/meeting-custom-layout.component.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-custom-layout/meeting-custom-layout.component.ts
index 2f5b4d44..04364919 100644
--- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-custom-layout/meeting-custom-layout.component.ts
+++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-custom-layout/meeting-custom-layout.component.ts
@@ -50,7 +50,7 @@ export class MeetingCustomLayoutComponent {
return this.meetingContextService.canModerateRoom() && hasNoRemotes;
});
- protected readonly shouldShowCaptions = computed(() => this.captionsService.areCaptionsEnabled());
+ protected readonly areCaptionsEnabledByUser = computed(() => this.captionsService.areCaptionsEnabledByUser());
protected readonly captions = computed(() => this.captionsService.captions());
diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-toolbar-extra-buttons/meeting-toolbar-extra-buttons.component.html b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-toolbar-extra-buttons/meeting-toolbar-extra-buttons.component.html
index bd189af0..3610b706 100644
--- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-toolbar-extra-buttons/meeting-toolbar-extra-buttons.component.html
+++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-toolbar-extra-buttons/meeting-toolbar-extra-buttons.component.html
@@ -1,25 +1,25 @@
-@if (isMobile()) {
-
-
-} @else {
-
+@if (showCaptionsButton()) {
+ @if (isMobile()) {
+
+
+ } @else {
+
+ }
}
diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-toolbar-extra-buttons/meeting-toolbar-extra-buttons.component.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-toolbar-extra-buttons/meeting-toolbar-extra-buttons.component.ts
index 7843c626..2b3b2929 100644
--- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-toolbar-extra-buttons/meeting-toolbar-extra-buttons.component.ts
+++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/customization/meeting-toolbar-extra-buttons/meeting-toolbar-extra-buttons.component.ts
@@ -33,12 +33,17 @@ export class MeetingToolbarExtraButtonsComponent {
*/
protected showCopyLinkButton = computed(() => this.meetingContextService.canModerateRoom());
+ /**
+ * Whether to show the captions button
+ */
+ protected showCaptionsButton = computed(() => this.meetingContextService.areCaptionsAllowed());
+
/**
* Whether the device is mobile (affects button style)
*/
protected isMobile = computed(() => this.meetingContextService.isMobile());
- protected areCaptionsEnabled = computed(() => this.captionService.areCaptionsEnabled());
+ protected areCaptionsEnabledByUser = computed(() => this.captionService.areCaptionsEnabledByUser());
onCopyLinkClick(): void {
const room = this.meetingContextService.meetRoom();
@@ -51,6 +56,6 @@ export class MeetingToolbarExtraButtonsComponent {
}
onCaptionsClick(): void {
- this.captionService.areCaptionsEnabled() ? this.captionService.disable() : this.captionService.enable();
+ this.captionService.areCaptionsEnabledByUser() ? this.captionService.disable() : this.captionService.enable();
}
}
diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-captions.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-captions.service.ts
index ad523233..5094763b 100644
--- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-captions.service.ts
+++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-captions.service.ts
@@ -37,11 +37,16 @@ export class MeetingCaptionsService {
// Reactive state
private readonly _captions = signal
([]);
- private readonly _isEnabled = signal(false);
+ private readonly _areCaptionsEnabledByUser = signal(false);
- // Public readonly signals
+ /**
+ * Current list of active captions
+ */
readonly captions = this._captions.asReadonly();
- readonly areCaptionsEnabled = this._isEnabled.asReadonly();
+ /**
+ * Whether captions are enabled by the user
+ */
+ readonly areCaptionsEnabledByUser = this._areCaptionsEnabledByUser.asReadonly();
// Map to track expiration timeouts
private expirationTimeouts = new Map>();
@@ -83,7 +88,7 @@ export class MeetingCaptionsService {
return;
}
- if (this._isEnabled()) {
+ if (this._areCaptionsEnabledByUser()) {
this.logger.d('Captions already enabled');
return;
}
@@ -91,7 +96,7 @@ export class MeetingCaptionsService {
// Register the LiveKit transcription handler
this.room.registerTextStreamHandler('lk.transcription', this.handleTranscription.bind(this));
- this._isEnabled.set(true);
+ this._areCaptionsEnabledByUser.set(true);
this.logger.d('Captions enabled');
}
@@ -100,7 +105,7 @@ export class MeetingCaptionsService {
* This is called when the user deactivates captions.
*/
disable(): void {
- if (!this._isEnabled()) {
+ if (!this._areCaptionsEnabledByUser()) {
this.logger.d('Captions already disabled');
return;
}
@@ -108,7 +113,7 @@ export class MeetingCaptionsService {
// Clear all active captions
this.clearAllCaptions();
- this._isEnabled.set(false);
+ this._areCaptionsEnabledByUser.set(false);
this.room?.unregisterTextStreamHandler('lk.transcription');
this.logger.d('Captions disabled');
}
@@ -119,7 +124,7 @@ export class MeetingCaptionsService {
destroy(): void {
this.clearAllCaptions();
this.room = null;
- this._isEnabled.set(false);
+ this._areCaptionsEnabledByUser.set(false);
this.logger.d('Meeting Captions service destroyed');
}
diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-context.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-context.service.ts
index 7c5e144b..d62a162a 100644
--- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-context.service.ts
+++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/meeting/services/meeting-context.service.ts
@@ -186,6 +186,14 @@ export class MeetingContextService {
return this._e2eeKey().length > 0;
}
+ /**
+ * Returns whether captions feature is allowed in the room
+ * @returns true if captions feature is allowed, false otherwise
+ */
+ areCaptionsAllowed(): boolean {
+ return this.featureConfigService.features().showCaptions;
+ }
+
/**
* Sets the room secret in context
* @param secret The room secret
diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/pages/room-wizard/steps/room-config/room-config.component.html b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/pages/room-wizard/steps/room-config/room-config.component.html
index fdc50563..504c7e1c 100644
--- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/pages/room-wizard/steps/room-config/room-config.component.html
+++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/pages/room-wizard/steps/room-config/room-config.component.html
@@ -61,6 +61,30 @@
+
+
+
+
+
+
+
diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/pages/room-wizard/steps/room-config/room-config.component.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/pages/room-wizard/steps/room-config/room-config.component.ts
index 24a2a773..05190dd2 100644
--- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/pages/room-wizard/steps/room-config/room-config.component.ts
+++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/pages/room-wizard/steps/room-config/room-config.component.ts
@@ -44,6 +44,9 @@ export class RoomConfigComponent implements OnDestroy {
},
e2ee: {
enabled: formValue.e2eeEnabled ?? false
+ },
+ captions: {
+ enabled: formValue.captionsEnabled ?? false
}
}
};
@@ -102,6 +105,11 @@ export class RoomConfigComponent implements OnDestroy {
this.configForm.patchValue({ virtualBackgroundEnabled: isEnabled });
}
+ onCaptionsToggleChange(event: any): void {
+ const isEnabled = event.checked;
+ this.configForm.patchValue({ captionsEnabled: isEnabled });
+ }
+
get chatEnabled(): boolean {
return this.configForm.value.chatEnabled || false;
}
@@ -113,4 +121,8 @@ export class RoomConfigComponent implements OnDestroy {
get e2eeEnabled(): boolean {
return this.configForm.value.e2eeEnabled ?? false;
}
+
+ get captionsEnabled(): boolean {
+ return this.configForm.value.captionsEnabled ?? false;
+ }
}
diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/wizard-state.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/wizard-state.service.ts
index 5b8bccff..31d00ea2 100644
--- a/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/wizard-state.service.ts
+++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/domains/rooms/services/wizard-state.service.ts
@@ -19,7 +19,8 @@ const DEFAULT_CONFIG: MeetRoomConfig = {
},
chat: { enabled: true },
virtualBackground: { enabled: true },
- e2ee: { enabled: false }
+ e2ee: { enabled: false },
+ captions: { enabled: false }
};
/**
@@ -192,7 +193,8 @@ export class RoomWizardStateService {
formGroup: this.formBuilder.group({
chatEnabled: initialRoomOptions.config!.chat!.enabled,
virtualBackgroundEnabled: initialRoomOptions.config!.virtualBackground!.enabled,
- e2eeEnabled: initialRoomOptions.config!.e2ee!.enabled
+ e2eeEnabled: initialRoomOptions.config!.e2ee!.enabled,
+ captionsEnabled: initialRoomOptions.config!.captions!.enabled
})
}
];
@@ -267,6 +269,10 @@ export class RoomWizardStateService {
...currentOptions.config?.e2ee,
...stepData.config?.e2ee
},
+ captions: {
+ ...currentOptions.config?.captions,
+ ...stepData.config?.captions
+ },
recording: {
...currentOptions.config?.recording,
// If recording is explicitly set in stepData, use it
diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/models/app.model.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/models/app.model.ts
index e5d848e9..d6d2787e 100644
--- a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/models/app.model.ts
+++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/models/app.model.ts
@@ -31,6 +31,7 @@ export interface ApplicationFeatures {
showRecordingPanel: boolean;
showChat: boolean;
showBackgrounds: boolean;
+ showCaptions: boolean;
showParticipantList: boolean;
showSettings: boolean;
showFullscreen: boolean;
diff --git a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/feature-configuration.service.ts b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/feature-configuration.service.ts
index a83af6a3..ae930f1d 100644
--- a/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/feature-configuration.service.ts
+++ b/meet-ce/frontend/projects/shared-meet-components/src/lib/shared/services/feature-configuration.service.ts
@@ -22,6 +22,7 @@ const DEFAULT_FEATURES: ApplicationFeatures = {
showRecordingPanel: true,
showChat: true,
showBackgrounds: true,
+ showCaptions: false,
showParticipantList: true,
showSettings: true,
showFullscreen: true,
@@ -115,6 +116,7 @@ export class FeatureConfigurationService {
features.showRecordingPanel = roomConfig.recording.enabled;
features.showChat = roomConfig.chat.enabled;
features.showBackgrounds = roomConfig.virtualBackground.enabled;
+ features.showCaptions = roomConfig.captions?.enabled ?? false;
}
// Apply room member permissions (these can restrict enabled features)
@@ -130,7 +132,6 @@ export class FeatureConfigurationService {
if (features.showBackgrounds) {
features.showBackgrounds = permissions.meet.canChangeVirtualBackground;
}
-
// Media features
const canPublish = permissions.livekit.canPublish;
const canPublishSources = permissions.livekit.canPublishSources ?? [];