Skip to content

Commit

Permalink
VIH-11147 refactor participant panel component to read from the redux…
Browse files Browse the repository at this point in the history
… store (#2307)

* add transfer direction to participant
* all participants in consultation or waiting room now treated as in WR
  • Loading branch information
shaed-parkar authored Nov 18, 2024
1 parent dff3615 commit e8f4b76
Show file tree
Hide file tree
Showing 30 changed files with 330 additions and 1,032 deletions.
2 changes: 1 addition & 1 deletion VideoWeb/VideoWeb.EventHub/VideoWeb.EventHub.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VideoWeb.Common\VideoWeb.Common.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,18 @@ public async Task should_return_the_statuses_for_the_conference()
}

[Test]
public async Task should_return_NoContent_status_code_if_video_control_statuses_is_null()
public async Task should_return_empty_dictionary_if_video_control_statuses_is_null()
{

var conferenceVideoControlStatuses = new ConferenceVideoControlStatuses();

_mocker.Mock<IConferenceVideoControlStatusService>()
.Setup(x => x.GetVideoControlStateForConference(It.IsAny<Guid>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(() => default);

var response = await _sut.GetVideoControlStatusesForConference(_conferenceId) as NoContentResult;
var response = await _sut.GetVideoControlStatusesForConference(_conferenceId);

ClassicAssert.AreEqual(response.StatusCode, (int)HttpStatusCode.NoContent);

response.Should().BeAssignableTo<OkObjectResult>().Which.Value.Should().BeEquivalentTo(conferenceVideoControlStatuses);
}

[Test]
Expand Down
9 changes: 3 additions & 6 deletions VideoWeb/VideoWeb.UnitTests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -2240,11 +2240,8 @@
},
"System.Text.Json": {
"type": "Transitive",
"resolved": "8.0.4",
"contentHash": "bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==",
"dependencies": {
"System.Text.Encodings.Web": "8.0.0"
}
"resolved": "8.0.5",
"contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg=="
},
"System.Text.RegularExpressions": {
"type": "Transitive",
Expand Down Expand Up @@ -2407,7 +2404,7 @@
"Microsoft.ApplicationInsights.AspNetCore": "[2.22.0, )",
"Microsoft.AspNetCore.SignalR": "[1.1.0, )",
"Microsoft.Extensions.Caching.Abstractions": "[8.0.0, )",
"System.Text.Json": "[8.0.4, )",
"System.Text.Json": "[8.0.5, )",
"VideoWeb.Common": "[1.0.0, )",
"VideoWeb.Contract": "[1.0.0, )"
}
Expand Down
32 changes: 16 additions & 16 deletions VideoWeb/VideoWeb/ClientApp/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion VideoWeb/VideoWeb/ClientApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
"ng-mocks": "^14.12.2",
"nswag": "^14.1.0",
"prettier": "^3.3.3",
"puppeteer": "^23.7.1",
"puppeteer": "^23.8.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 @@ -20,6 +20,7 @@ describe('AudioRecordingService', () => {
isRemoteMuted: false,
isSpotlighted: false,
handRaised: false,
isVideoMuted: false,
pexipDisplayName: 'vh-wowza',
uuid: 'unique-identifier',
callTag: 'call-tag',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2019,15 +2019,6 @@ export class ApiClient extends ApiClientBase {
return _observableOf(result200);
})
);
} else if (status === 204) {
return blobToText(responseBlob).pipe(
_observableMergeMap(_responseText => {
let result204: any = null;
let resultData204 = _responseText === '' ? null : JSON.parse(_responseText, this.jsonParseReviver);
result204 = ConferenceVideoControlStatuses.fromJS(resultData204);
return _observableOf(result204);
})
);
} else if (status === 401) {
return blobToText(responseBlob).pipe(
_observableMergeMap(_responseText => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,7 @@ describe('ParticipantService', () => {
callTag: undefined,
isRemoteMuted: false,
isSpotlighted: false,
isVideoMuted: false,
handRaised: false,
isAudioOnlyCall: false,
isVideoCall: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ export class ParticipantService {
this.logger.debug(`${this.loggerPrefix} loading participants and VMRs`);

const conferenceId = this.conferenceService.currentConferenceId;
if (!conferenceId) {
return;
}
return zip(
this.conferenceService.getParticipantsForConference(conferenceId),
this.conferenceService.getEndpointsForConference(conferenceId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export class HearingLayoutService {

getCurrentRecommendedLayout(): Observable<HearingLayout> {
return this.conferenceService.currentConference$.pipe(
filter(conference => conference !== null),
take(1),
map(conference => conference.id),
mergeMap(conferenceId => this.apiClient.getRecommendedLayoutForHearing(conferenceId))
Expand All @@ -135,6 +136,7 @@ export class HearingLayoutService {
updateCurrentLayout(layout: HearingLayout) {
this.conferenceService.currentConference$
.pipe(
filter(conference => conference !== null),
take(1),
map(conference => conference.id)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,37 @@ import { PanelModel } from 'src/app/waiting-space/models/panel-model-base';
import { ParticipantPanelModel } from 'src/app/waiting-space/models/participant-panel-model';
import { ParticipantModel } from '../models/participant';
import { HearingRole } from 'src/app/waiting-space/models/hearing-role-model';
import { VHParticipant } from 'src/app/waiting-space/store/models/vh-conference';

export class ParticipantPanelModelMapper {
mapFromVHParticipants(participants: VHParticipant[]): PanelModel[] {
const panelModels: PanelModel[] = [];
participants.forEach(p => {
const panelModel = new ParticipantPanelModel(
p.id,
p.displayName,
p.role,
p.tiledDisplayName,
p.hearingRole,
p.representee,
p.status
);
if (p.pexipInfo) {
panelModel.assignPexipId(p.pexipInfo.uuid);
panelModel.updateParticipant(
p.pexipInfo.isRemoteMuted,
p.pexipInfo.handRaised,
p.pexipInfo.isSpotlighted,
p.id,
p.localMediaStatus?.isMicrophoneMuted,
p.localMediaStatus?.isCameraOff
);
}
panelModels.push(panelModel);
});
return panelModels;
}

mapFromParticipantUserResponseArray(pats: ParticipantForUserResponse[]): PanelModel[] {
const participants: PanelModel[] = [];
pats.forEach(x => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const mockWowzaAgent: VHPexipParticipant = {
handRaised: false,
isAudioOnlyCall: true,
isRemoteMuted: false,
isVideoMuted: false,
isSpotlighted: false,
isVideoCall: false,
pexipDisplayName: 'vh-wowza',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,7 @@ describe('HearingControlsBaseComponent', () => {
pexipDisplayName: undefined,
isRemoteMuted: false,
isSpotlighted: true,
isVideoMuted: false,
handRaised: false,
uuid: undefined,
callTag: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ <h1 #roomTitleLabel class="room-title-label">{{ getCaseNameAndNumber() }}</h1>
[roomLabel]="this.participant.current_room?.label"
>
</app-private-consultation-participants>
<app-participants-panel *ngIf="!isPrivateConsultation"
[isCountdownCompleted]='countdownComplete'></app-participants-panel>
<app-participants-panel></app-participants-panel>
</div>
<div *ngIf="hearing && !isPrivateConsultation" class="panel-wrapper"
[ngClass]="{ 'hide-panel': !panelStates['Chat'] }">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class ParticipantPanelModel extends IndividualPanelModel {
}

isAvailable(): boolean {
return (this.isQuickLinkUser && this.status === ParticipantStatus.InConsultation) || this.status === ParticipantStatus.Available;
return this.status === ParticipantStatus.InConsultation || this.status === ParticipantStatus.Available;
}

isInConsultation(): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ describe('ParticipantPanelModel', () => {
expect(model.isAvailable()).toBe(true);
});

it('returns isAvailable: false when participant is not quick link user and status is in consultation', () => {
it('returns isAvailable: true when participant is not quick link user and status is in consultation', () => {
participant.status = ParticipantStatus.InConsultation;
model = mapper.mapFromParticipantUserResponse(participant);
spyOnProperty(model, 'isQuickLinkUser').and.returnValue(false);
expect(model.isAvailable()).toBe(false);
expect(model.isAvailable()).toBe(true);
});

it('returns isAvailable: true when participant is quick link user and status is available', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,30 @@ export class ParticipantDeleted {
}

export class ParticipantUpdated {
/** Has been administratively audio muted. */
public isRemoteMuted: boolean;
/** Is spotlighted. */
public isSpotlighted: boolean;
/** Is hand raised. */
public handRaised: boolean;
/** Pexip display name (string delimited values). */
public pexipDisplayName: string;
/** Pexip UUID of participant. */
public uuid: string;
/** Participant call tag. */
public callTag: string;
/** Is the participant an audio only call. */
public isAudioOnlyCall: boolean;
/** IDoes the participant have video capability. */
public isVideoCall: boolean;
public protocol: string;
/** The audio mix this participant is receiving e.g. "main". */
public receivingAudioMix: string;
/** The audio mixes this participant is sending to. */
public sentAudioMixes: PexipAudioMix[];
public role: PexipParticipantRole;
/** Has the participant muted their video */
public isVideoMuted: boolean;

private constructor(
isRemoteMuted: string,
Expand Down Expand Up @@ -61,7 +73,7 @@ export class ParticipantUpdated {
}

static fromPexipParticipant(pexipParticipant: PexipParticipant) {
return new ParticipantUpdated(
const p = new ParticipantUpdated(
pexipParticipant.is_muted,
pexipParticipant.buzz_time,
pexipParticipant.display_name,
Expand All @@ -75,6 +87,10 @@ export class ParticipantUpdated {
pexipParticipant.receive_from_audio_mix,
pexipParticipant.send_to_audio_mixes
);

p.isVideoMuted = pexipParticipant.is_video_muted;

return p;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { EndpointStatus, Role, VideoEndpointResponse } from '../../services/clients/api-client';
import { VHEndpoint } from '../store/models/vh-conference';
import { IndividualPanelModel } from './individual-panel-model';

export class VideoEndpointPanelModel extends IndividualPanelModel {
public status: EndpointStatus;

constructor(endpoint: VideoEndpointResponse) {
super(endpoint.id, endpoint.display_name, Role.Individual, endpoint.pexip_display_name, 'Video access point', '');
constructor(endpoint: VideoEndpointResponse | VHEndpoint) {
if (endpoint instanceof VideoEndpointResponse) {
super(endpoint.id, endpoint.display_name, Role.Individual, endpoint.pexip_display_name, 'Video access point', '');
} else {
super(endpoint.id, endpoint.displayName, Role.Individual, endpoint?.pexipInfo?.pexipDisplayName, 'Video access point', '');
}
this.status = endpoint.status;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,19 @@
<img
*ngIf="participant.isMicRemoteMuted()"
appTooltip
[text]="participant.isMicRemoteMuted() ? 'participants-panel.unmute-lock' : ('participants-panel.mute-participant' | translate)"
[text]="
participant.isMicRemoteMuted()
? ('participants-panel.unmute-lock' | translate)
: ('participants-panel.mute-participant' | translate)
"
colour="grey"
[src]="participant.isMicRemoteMuted() ? '/assets/images/mic_remote_mute.png' : '/assets/images/mic.png'"
class="panel-icon"
[attr.alt]="
(participant.isMicRemoteMuted() ? 'participants-panel.microphone-muted-icon' : 'participants-panel.microphone-on-icon')
| translate
(participant.isMicRemoteMuted()
? ('participants-panel.microphone-muted-icon' | translate)
: 'participants-panel.microphone-on-icon'
) | translate
"
(click)="toggleParticipantMute(); $event.stopPropagation()"
tabindex="0"
Expand Down
Loading

0 comments on commit e8f4b76

Please sign in to comment.