Skip to content

Commit

Permalink
VIH-10946 enable audio only toggle in the hearing room (#2259)
Browse files Browse the repository at this point in the history
* VIH-10946 simplify the device stream management and enable audio only toggle in the hearing room
* update puppeteer
* ignore sonar rule typescript:S2933. readonly members from the ctor is not a style we are adopting in TS
  • Loading branch information
shaed-parkar authored Oct 24, 2024
1 parent 8325737 commit b1b74a6
Show file tree
Hide file tree
Showing 22 changed files with 696 additions and 377 deletions.
337 changes: 181 additions & 156 deletions VideoWeb/VideoWeb/ClientApp/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion VideoWeb/VideoWeb/ClientApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
"ng-mocks": "^14.12.2",
"nswag": "^14.1.0",
"prettier": "^3.3.3",
"puppeteer": "^23.5.1",
"puppeteer": "^23.6.0",
"run-script-os": "^1.1.6",
"sass": "^1.77.8",
"typescript": ">=5.2.0 <5.4.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ import { VideoWebService } from 'src/app/services/api/video-web.service';
import { ConferenceResponse, Role, UserProfileResponse } from 'src/app/services/clients/api-client';
import { ErrorService } from 'src/app/services/error.service';
import { Logger } from 'src/app/services/logging/logger-base';
import { UserMediaStreamService } from 'src/app/services/user-media-stream.service';
import { pageUrls } from 'src/app/shared/page-url.constants';
import { ConferenceTestData } from 'src/app/testing/mocks/data/conference-test-data';
import { MockLogger } from 'src/app/testing/mocks/mock-logger';
import { SwitchOnCameraMicrophoneComponent } from './switch-on-camera-microphone.component';
import { fakeAsync, flush, flushMicrotasks } from '@angular/core/testing';
import { ParticipantStatusUpdateService } from 'src/app/services/participant-status-update.service';
import { Subject, throwError } from 'rxjs';
import { of, Subject, throwError } from 'rxjs';
import { mockCamStream } from 'src/app/waiting-space/waiting-room-shared/tests/waiting-room-base-setup';
import { getSpiedPropertyGetter } from 'src/app/shared/jasmine-helpers/property-helpers';
import { UserMediaService } from 'src/app/services/user-media.service';

describe('SwitchOnCameraMicrophoneComponent', () => {
Expand All @@ -26,7 +24,6 @@ describe('SwitchOnCameraMicrophoneComponent', () => {
let profileService: jasmine.SpyObj<ProfileService>;
let activatedRoute: ActivatedRoute = <any>{ snapshot: { paramMap: convertToParamMap({ conferenceId: conference.id }) } };
let videoWebService: jasmine.SpyObj<VideoWebService>;
let userMediaStreamService: jasmine.SpyObj<UserMediaStreamService>;
let errorService: jasmine.SpyObj<ErrorService>;
let userMediaServiceSpy: jasmine.SpyObj<UserMediaService>;
const logger: Logger = new MockLogger();
Expand All @@ -35,7 +32,7 @@ describe('SwitchOnCameraMicrophoneComponent', () => {
beforeEach(async () => {
conference = new ConferenceTestData().getConferenceDetailFuture();
activatedRoute = <any>{ snapshot: { paramMap: convertToParamMap({ conferenceId: conference.id }) } };
userMediaStreamService = jasmine.createSpyObj<UserMediaStreamService>('UserMediaStreamService', [], ['currentStream$']);

currentStreamSubject = new Subject<MediaStream>();

videoWebService = jasmine.createSpyObj<VideoWebService>('VideoWebService', [
Expand All @@ -60,7 +57,7 @@ describe('SwitchOnCameraMicrophoneComponent', () => {

participantStatusUpdateService = jasmine.createSpyObj('ParticipantStatusUpdateService', ['postParticipantStatus']);

userMediaServiceSpy = jasmine.createSpyObj<UserMediaService>(['initialise']);
userMediaServiceSpy = jasmine.createSpyObj<UserMediaService>(['hasValidCameraAndMicAvailable']);

component = new SwitchOnCameraMicrophoneComponent(
router,
Expand All @@ -70,8 +67,7 @@ describe('SwitchOnCameraMicrophoneComponent', () => {
errorService,
logger,
participantStatusUpdateService,
userMediaServiceSpy,
userMediaStreamService
userMediaServiceSpy
);
component.conference = conference;
component.conferenceId = conference.id;
Expand All @@ -97,8 +93,7 @@ describe('SwitchOnCameraMicrophoneComponent', () => {
errorService,
logger,
participantStatusUpdateService,
userMediaServiceSpy,
userMediaStreamService
userMediaServiceSpy
);

component.ngOnInit();
Expand Down Expand Up @@ -163,18 +158,17 @@ describe('SwitchOnCameraMicrophoneComponent', () => {
});

it('should update mediaAccepted and userPrompted to true when request media', fakeAsync(() => {
getSpiedPropertyGetter(userMediaStreamService, 'currentStream$').and.returnValue(currentStreamSubject.asObservable());
userMediaServiceSpy.hasValidCameraAndMicAvailable.and.returnValue(of(true));

component.requestMedia();
currentStreamSubject.next(mockCamStream);
flush();
expect(component.userPrompted).toBeTrue();
expect(component.mediaAccepted).toBeTrue();
expect(userMediaServiceSpy.initialise).toHaveBeenCalledTimes(1);
}));

it('should update mediaAccepted and userPrompted to false when request media throw an error', fakeAsync(() => {
getSpiedPropertyGetter(userMediaStreamService, 'currentStream$').and.callFake(() => throwError(new Error('Fake error')));
userMediaServiceSpy.hasValidCameraAndMicAvailable.and.returnValue(throwError(new Error('Fake error')));
spyOn(component, 'postPermissionDeniedAlert');

component.requestMedia();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import { vhContactDetails } from 'src/app/shared/contact-information';
import { pageUrls } from 'src/app/shared/page-url.constants';
import { ParticipantStatusBaseDirective } from 'src/app/on-the-day/models/participant-status-base';
import { ParticipantStatusUpdateService } from 'src/app/services/participant-status-update.service';
import { UserMediaStreamService } from 'src/app/services/user-media-stream.service';
import { first } from 'rxjs/operators';
import { UserMediaService } from 'src/app/services/user-media.service';
import { HearingRole } from 'src/app/waiting-space/models/hearing-role-model';
import { catchError } from 'rxjs/operators';
import { NEVER } from 'rxjs';

@Component({
selector: 'app-switch-on-camera-microphone',
Expand Down Expand Up @@ -42,8 +42,7 @@ export class SwitchOnCameraMicrophoneComponent extends ParticipantStatusBaseDire
private errorService: ErrorService,
protected logger: Logger,
protected participantStatusUpdateService: ParticipantStatusUpdateService,
private userMediaService: UserMediaService,
private userMediaStreamService: UserMediaStreamService
private userMediaService: UserMediaService
) {
super(participantStatusUpdateService, logger);
this.userPrompted = false;
Expand Down Expand Up @@ -79,29 +78,34 @@ export class SwitchOnCameraMicrophoneComponent extends ParticipantStatusBaseDire
}
}

async requestMedia() {
this.userMediaService.initialise();
this.userMediaStreamService.currentStream$.pipe(first()).subscribe({
next: stream => {
this.mediaAccepted = true;
this.userPrompted = true;
},
error: error => {
this.mediaAccepted = false;
this.userPrompted = false;
this.logger.warn(`[SwitchOnCameraMicrophone] - ${this.participantName} denied access to camera.`, {
conference: this.conferenceId,
participant: this.participantName,
error: error
});
this.postPermissionDeniedAlert();
this.errorService.goToServiceError(
'error-camera-microphone.problem-with-camera-mic',
'error-camera-microphone.camera-mic-in-use',
false
);
}
});
requestMedia() {
this.userMediaService
.hasValidCameraAndMicAvailable()
.pipe(
catchError(error => {
this.mediaAccepted = false;
this.userPrompted = false;
this.logger.warn(`[SwitchOnCameraMicrophone] - ${this.participantName} denied access to camera.`, {
conference: this.conferenceId,
participant: this.participantName,
error: error
});
this.postPermissionDeniedAlert();
this.errorService.goToServiceError(
'error-camera-microphone.problem-with-camera-mic',
'error-camera-microphone.camera-mic-in-use',
false
);
return NEVER;
})
)
.subscribe(hasDevices => {
if (hasDevices) {
this.handleDevicesAvailable();
} else {
this.handleDevicesUnavailable();
}
});
}

goVideoTest() {
Expand Down Expand Up @@ -141,4 +145,19 @@ export class SwitchOnCameraMicrophoneComponent extends ParticipantStatusBaseDire
}
return this.conference.participants.some(x => x.user_name === this.profile.username && x.hearing_role === HearingRole.OBSERVER);
}

private handleDevicesAvailable() {
this.mediaAccepted = true;
this.userPrompted = true;
}

private handleDevicesUnavailable() {
this.mediaAccepted = false;
this.userPrompted = true;
this.errorService.goToServiceError(
'error-camera-microphone.problem-with-camera-mic',
'error-camera-microphone.camera-mic-in-use',
false
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { Subject } from 'rxjs';
import { getSpiedPropertyGetter } from '../shared/jasmine-helpers/property-helpers';
import { Logger } from './logging/logger-base';
import { NoSleepService } from './no-sleep.service';
import { UserMediaStreamService } from './user-media-stream.service';
import { UserMediaStreamServiceV2 } from './user-media-stream-v2.service';

describe('NoSleepService', () => {
let service: NoSleepService;

let userMediaStreamServiceSpy: jasmine.SpyObj<UserMediaStreamService>;
let userMediaStreamServiceSpy: jasmine.SpyObj<UserMediaStreamServiceV2>;
let renderer2FactorySpy: jasmine.SpyObj<RendererFactory2>;
let renderer2Spy: jasmine.SpyObj<Renderer2>;
let deviceServiceSpy: jasmine.SpyObj<DeviceDetectorService>;
Expand All @@ -24,7 +24,7 @@ describe('NoSleepService', () => {

beforeEach(() => {
currentStreamSubject = new Subject<MediaStream>();
userMediaStreamServiceSpy = jasmine.createSpyObj<UserMediaStreamService>([], ['currentStream$']);
userMediaStreamServiceSpy = jasmine.createSpyObj<UserMediaStreamServiceV2>([], ['currentStream$']);
getSpiedPropertyGetter(userMediaStreamServiceSpy, 'currentStream$').and.returnValue(currentStreamSubject.asObservable());

videoElementSpy = jasmine.createSpyObj<HTMLVideoElement>(['play', 'setAttribute'], ['style', 'parentElement']);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DeviceDetectorService } from 'ngx-device-detector';
import { Observable, Subject } from 'rxjs';
import { take } from 'rxjs/operators';
import { Logger } from './logging/logger-base';
import { UserMediaStreamService } from './user-media-stream.service';
import { UserMediaStreamServiceV2 } from './user-media-stream-v2.service';

@Injectable({
providedIn: 'root'
Expand All @@ -16,7 +16,7 @@ export class NoSleepService {
private touchStartSubject = new Subject<void>();

constructor(
private userMediaStreamService: UserMediaStreamService,
private userMediaStreamService: UserMediaStreamServiceV2,
renderer2Factory: RendererFactory2,
private deviceService: DeviceDetectorService,
private document: Document,
Expand Down
Loading

0 comments on commit b1b74a6

Please sign in to comment.