ov-components: enhance screenshare button functionality for Firefox

- Updated the screenshare button in the toolbar to behave differently based on the browser type (Firefox vs others).
- Added an input property `isFirefoxBrowser` to the `ToolbarMediaButtonsComponent` to determine the browser type.
- Modified the HTML template to conditionally render the screenshare button and its menu based on the browser.
- Improved the `switchScreenshare` method in the `ParticipantService` to handle errors more gracefully and ensure the local participant is defined before proceeding.
- Refactored the `switchScreenshare` method in the `ParticipantModel` to reject promises with meaningful messages when no active screen share track is found.
master
CSantosM 2026-03-16 13:15:13 +01:00
parent 87b3ac3716
commit 34e34ee078
7 changed files with 2678 additions and 1541 deletions

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,19 @@
<!-- Screenshare button - Only visible on tablet+ or when active -->
@if (showScreenshareButtonDirect()) {
@if (isFirefoxBrowser) {
<button
mat-icon-button
id="screenshare-btn"
[disabled]="isConnectionLost"
[class.active-btn]="isScreenShareEnabled"
[class.mobile-btn]="isMobileView()"
matTooltip="{{ isScreenShareEnabled ? ('TOOLBAR.DISABLE_SCREEN' | translate) : ('TOOLBAR.ENABLE_SCREEN' | translate) }}"
(click)="onScreenShareToggle()"
>
<mat-icon>screen_share</mat-icon>
</button>
} @else {
<button
mat-icon-button
id="screenshare-btn"
@ -61,6 +74,7 @@
<span>{{ 'TOOLBAR.DISABLE_SCREEN' | translate }}</span>
</button>
</mat-menu>
}
}
<!-- Additional buttons injection from parent component (desktop/tablet only) -->

View File

@ -28,6 +28,7 @@ export class ToolbarMediaButtonsComponent {
// Screenshare related inputs
@Input() showScreenshareButton: boolean = true;
@Input() isScreenShareEnabled: boolean = false;
@Input() isFirefoxBrowser: boolean = false;
// Device availability inputs
@Input() hasVideoDevices: boolean = true;

View File

@ -49,6 +49,7 @@
[microphoneMuteChanging]="microphoneMuteChanging"
[showScreenshareButton]="showScreenshareButton"
[isScreenShareEnabled]="isScreenShareEnabled"
[isFirefoxBrowser]="isFirefoxBrowser"
[hasVideoDevices]="hasVideoDevices"
[hasAudioDevices]="hasAudioDevices"
[isConnectionLost]="isConnectionLost"

View File

@ -344,6 +344,11 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
*/
showCaptionsButton: boolean = true;
/**
* @internal
*/
isFirefoxBrowser: boolean = false;
/**
* @ignore
*/
@ -450,6 +455,7 @@ export class ToolbarComponent implements OnInit, OnDestroy, AfterViewInit {
private templateManagerService: TemplateManagerService
) {
this.log = this.loggerSrv.get('ToolbarComponent');
this.isFirefoxBrowser = this.platformService.isFirefox();
// Effect to react to local participant changes
effect(() => {

View File

@ -436,14 +436,19 @@ export class ParticipantModel {
* @internal
*/
async switchScreenshare(newTrack: LocalTrack): Promise<void> {
if (this.participant instanceof LocalParticipant) {
const screenTrack = this.tracks.find((track) => track.source === Track.Source.ScreenShare);
if (screenTrack) {
await (screenTrack.videoTrack as LocalTrack).replaceTrack(newTrack.mediaStreamTrack);
return Promise.resolve();
}
if (!(this.participant instanceof LocalParticipant)) {
return Promise.reject("Remote participant can't switch screen share");
}
const screenTrack = this.tracks.find((track) => track.source === Track.Source.ScreenShare);
if (!screenTrack || !screenTrack.videoTrack) {
return Promise.reject('No active screen share track to switch');
}
const currentTrack = screenTrack.videoTrack as LocalTrack;
await currentTrack.replaceTrack(newTrack.mediaStreamTrack);
return Promise.resolve();
}
/**

View File

@ -189,19 +189,26 @@ export class ParticipantService {
* Switches the active screen share track showing a native browser dialog to select a screen or window.
*/
async switchScreenShare(): Promise<void> {
if (this.localParticipant) {
if (!this.localParticipant) {
this.log.e('Local participant is undefined when switching screenshare');
return;
}
// Chrome / Safari: seamless replaceTrack keeps the same publication SID.
const options = this.getScreenCaptureOptions();
const [newTrack] = await this.localParticipant.createScreenTracks(options);
if (newTrack) {
newTrack?.addListener('ended', async () => {
newTrack.addListener('ended', async () => {
this.log.d('Clicked native stop button. Stopping screen sharing');
await this.setScreenShareEnabled(false);
});
try {
await this.localParticipant.switchScreenshare(newTrack);
} catch (error) {
newTrack.stop();
throw error;
}
} else {
this.log.e('Local participant is undefined when switching screenshare');
}
// this.updateLocalParticipant();
@ -459,7 +466,6 @@ export class ParticipantService {
}
}
/**
* Returns the participant with the given identity.
* @param identity