Skip to content

Commit 50711c3

Browse files
committed
Order on users who raise the hand + Check hand raised users are connected + XMLHttpRequest (deprecated in Chrome) -> fetch keepalive
1 parent babb32f commit 50711c3

File tree

10 files changed

+117
-31
lines changed

10 files changed

+117
-31
lines changed

backend/src/main/java/urjc/ovteaching/rooms/Room.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -107,23 +107,18 @@ public String getName() {
107107
return name;
108108
}
109109

110-
@SuppressWarnings("unchecked")
111-
public int addHandRaisedUser(String nickname, String avatar, String connectionId) {
112-
JSONObject json = new JSONObject();
113-
json.put("nickname", nickname);
114-
json.put("avatar", avatar);
115-
json.put("connectionId", connectionId);
116-
if(this.handRaisedUsers.contains(json)) {
110+
public int addHandRaisedUser(JSONObject handRaisedUser) {
111+
if(this.handRaisedUsers.contains(handRaisedUser)) {
117112
return -1;
118113
} else {
119-
this.handRaisedUsers.add(json);
114+
this.handRaisedUsers.add(handRaisedUser);
120115
return this.handRaisedUsers.size();
121116
}
122117
}
123118

124-
public boolean removeHandRaisedUser(String connectionId) {
119+
public boolean removeHandRaisedUser(JSONObject connectionId) {
125120
for(JSONObject jsonUser : this.handRaisedUsers) {
126-
if(jsonUser.get("connectionId").equals(connectionId)) {
121+
if(jsonUser.get("connectionId").equals(connectionId.get("connectionId"))) {
127122
this.handRaisedUsers.remove(jsonUser);
128123
return true;
129124
}

backend/src/main/java/urjc/ovteaching/rooms/RoomService.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package urjc.ovteaching.rooms;
22

3+
import java.util.Iterator;
34
import java.util.List;
45
import java.util.Optional;
6+
import java.util.Set;
57

8+
import org.json.simple.JSONObject;
69
import org.springframework.beans.factory.annotation.Autowired;
710
import org.springframework.stereotype.Service;
811

12+
import urjc.ovteaching.OpenViduComponent;
913
import urjc.ovteaching.users.User;
1014
import urjc.ovteaching.users.UserService;
1115

@@ -17,6 +21,9 @@ public class RoomService {
1721

1822
@Autowired
1923
private UserService userServ;
24+
25+
@Autowired
26+
private OpenViduComponent openviduComponent;
2027

2128
public Optional<Room> findOne(long id) {
2229
return roomRep.findById(id);
@@ -65,4 +72,15 @@ public Room findByInviteCode(String code) {
6572
return roomRep.findByCodeParticipant(code);
6673
}
6774
}
75+
76+
public void checkConnectedHandRaisedUsers(Room room) {
77+
Iterator<JSONObject> it = room.getHandRaisedUsers().iterator();
78+
while(it.hasNext()) {
79+
JSONObject nextUser = it.next();
80+
User fakeUser = new User((String) nextUser.get("username"), "pass", "USER_ROLE");
81+
if(!this.openviduComponent.getConnectedAssistants(room).contains(fakeUser)) {
82+
it.remove();
83+
}
84+
}
85+
}
6886
}

backend/src/main/java/urjc/ovteaching/rooms/controllers/RoomController.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,9 @@ public ResponseEntity<JSONObject> getAssistants(@PathVariable String roomName, H
216216
*
217217
* @return the position in the queue
218218
*/
219-
@JsonView(User.NameOnly.class)
219+
@SuppressWarnings("unchecked")
220220
@PostMapping("/room/{roomName}/raiseHand")
221-
public ResponseEntity<Integer> raiseHand(@PathVariable String roomName, @RequestBody String nickname, @RequestBody String avatar, @RequestBody String connectionId, HttpServletRequest request) {
221+
public ResponseEntity<Integer> raiseHand(@PathVariable String roomName, @RequestBody JSONObject handRaisedUser, HttpServletRequest request) {
222222
if (!request.isUserInRole("USER")) {
223223
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
224224
}
@@ -229,7 +229,9 @@ public ResponseEntity<Integer> raiseHand(@PathVariable String roomName, @Request
229229
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
230230
}
231231
if (room.isInRoom(user)) {
232-
Integer position = room.addHandRaisedUser(nickname, avatar, connectionId);
232+
handRaisedUser.put("username", user.getName());
233+
this.roomServ.checkConnectedHandRaisedUsers(room);
234+
Integer position = room.addHandRaisedUser(handRaisedUser);
233235
if(position.equals(-1)) {
234236
return new ResponseEntity<>(HttpStatus.CONFLICT);
235237
}
@@ -245,9 +247,8 @@ public ResponseEntity<Integer> raiseHand(@PathVariable String roomName, @Request
245247
*
246248
* @return the httpStatus of the operation
247249
*/
248-
@JsonView(User.NameOnly.class)
249250
@DeleteMapping("/room/{roomName}/raiseHand")
250-
public ResponseEntity<?> lowerHand(@PathVariable String roomName, @RequestBody String connectionId, HttpServletRequest request) {
251+
public ResponseEntity<?> lowerHand(@PathVariable String roomName, @RequestBody JSONObject connectionId, HttpServletRequest request) {
251252
if (!request.isUserInRole("USER")) {
252253
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
253254
}
@@ -262,20 +263,18 @@ public ResponseEntity<?> lowerHand(@PathVariable String roomName, @RequestBody S
262263
this.roomServ.save(room);
263264
if(wasRemoved) {
264265
return new ResponseEntity<>(HttpStatus.OK);
265-
} else {
266-
return new ResponseEntity<>(HttpStatus.CONFLICT);
267266
}
267+
return new ResponseEntity<>(HttpStatus.CONFLICT);
268268
} else {
269269
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
270270
}
271271
}
272272

273273
/**
274-
* Makes the user lower their hand
274+
* Gets the users who are raising their hand in that room
275275
*
276-
* @return the httpStatus of the operation
276+
* @return the list of the users raising their hand
277277
*/
278-
@JsonView(User.NameOnly.class)
279278
@GetMapping("/room/{roomName}/raiseHand")
280279
public ResponseEntity<List<JSONObject>> getRaisedHands(@PathVariable String roomName, HttpServletRequest request) {
281280
if (!request.isUserInRole("USER")) {

backend/src/main/java/urjc/ovteaching/users/User.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ public Collection<Room> getPresentedRooms() {
125125
return presentedRooms;
126126
}
127127

128+
@Override
129+
public int hashCode() {
130+
final int prime = 31;
131+
int result = 1;
132+
result = prime * result + ((name == null) ? 0 : name.hashCode());
133+
return result;
134+
}
135+
128136
@Override
129137
public boolean equals(Object obj) {
130138
if (this == obj)

frontend/src/app/shared/components/toolbar/toolbar.component.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,9 @@
8686
#raise_hand {
8787
max-width: 50%;
8888
max-height: 50%;
89+
}
90+
91+
::ng-deep #raise_hand_button > .mat-badge-content {
92+
right: -8px;
93+
top: 1px;
8994
}

frontend/src/app/shared/components/toolbar/toolbar.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
<button mat-icon-button (click)="getInviteURL('participant')" *ngIf="localUser && this.userService.isModOfRoom(this.mySessionId)">
3838
<mat-icon matTooltip="Invite URL for participants">link</mat-icon>
3939
</button>
40-
<button mat-icon-button (click)="raiseHand()" *ngIf="localUser">
40+
<button mat-icon-button (click)="raiseHand()" *ngIf="localUser" id="raise_hand_button" matBadge="{{positionInHandRaiseQueue}}" [matBadgeHidden]="!localUser.isHandRaised()" matBadgePosition="below after" matBadgeSize="medium" matBadgeColor="warn">
4141
<img *ngIf="localUser && !localUser.isHandRaised()" src="../../../../assets/images/raise_hand.png" matTooltip="Raise your hand" id="raise_hand">
4242
<img *ngIf="localUser && localUser.isHandRaised()" src="../../../../assets/images/lower_hand.png" matTooltip="Lower your hand" id="raise_hand">
4343
</button>

frontend/src/app/shared/components/toolbar/toolbar.component.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { MatSnackBar } from '@angular/material';
1313
})
1414
export class ToolbarComponent implements OnInit {
1515
fullscreenIcon = 'fullscreen';
16+
positionInHandRaiseQueue: number = 0;
1617

1718
@Input() lightTheme: boolean;
1819
@Input() mySessionId: string;
@@ -75,7 +76,23 @@ export class ToolbarComponent implements OnInit {
7576
}
7677

7778
raiseHand() {
78-
this.raiseHandClicked.emit();
79+
if(!this.localUser.isHandRaised()) {
80+
this.roomSrv.raiseHand(this.mySessionId, this.localUser.getNickname(), this.localUser.getAvatar(), this.localUser.getConnectionId()).subscribe(
81+
position => {
82+
this.positionInHandRaiseQueue = position;
83+
this.raiseHandClicked.emit();
84+
},
85+
error => console.error(error)
86+
);
87+
} else {
88+
this.roomSrv.lowerHand(this.mySessionId, this.localUser.getConnectionId()).subscribe(
89+
(_) => {
90+
this.raiseHandClicked.emit();
91+
},
92+
error => console.error(error)
93+
);
94+
}
95+
7996
}
8097

8198
getInviteURL(role:string){

frontend/src/app/shared/services/open-vidu.service.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ export class OpenViduService {
3636
}
3737

3838
syncRemoveUser(roomName: string) {
39-
let xhr = new XMLHttpRequest()
40-
xhr.open("DELETE",this.baseURL + '/room/' + roomName + '/user',false);
41-
xhr.send();
39+
fetch(this.baseURL + '/room/' + roomName + '/user', {
40+
method: 'DELETE',
41+
keepalive: true
42+
});
4243
}
4344

4445
getToken(mySessionId: string): Promise<string> {

frontend/src/app/shared/services/room.service.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Room } from './../models/room-model';
22
import { UserService, User } from './user.service';
33
import { Injectable } from "@angular/core";
4-
import { HttpClient } from "@angular/common/http";
4+
import { HttpClient, HttpHeaders } from "@angular/common/http";
55
import 'rxjs/Rx';
66
import { Observable } from 'rxjs';
77
import { map, catchError } from "rxjs/operators";
@@ -54,13 +54,46 @@ export class RoomService {
5454
);
5555
}
5656

57-
removeUser(roomName: string) {
58-
return this.http.delete<any>(this.baseURL + '/room/' + roomName + '/user/session').pipe(
59-
map((_) => {}),
57+
getHandRaisedUsers(roomName: string): Observable<any> {
58+
return this.http.get<any>(this.baseURL + '/room/' + roomName + '/raiseHand').pipe(
59+
map(users => { return users }),
60+
catchError((error) => this.handleError(error))
61+
);
62+
}
63+
64+
raiseHand(roomName: string, nickname: string, avatar: string, connectionId: string): Observable<number> {
65+
let body = {
66+
"nickname": nickname,
67+
"avatar": avatar,
68+
"connectionId": connectionId
69+
};
70+
return this.http.post<any>(this.baseURL + '/room/' + roomName + '/raiseHand', body).pipe(
71+
map(users => { return users }),
6072
catchError((error) => this.handleError(error))
6173
);
6274
}
6375

76+
lowerHand(roomName: string, connectionId: string): Observable<any> {
77+
let body = {
78+
"connectionId": connectionId
79+
};
80+
return this.http.request<any>('delete', this.baseURL + '/room/' + roomName + '/raiseHand', { body: body }).pipe(
81+
map(response => { return response }),
82+
catchError((error) => this.handleError(error))
83+
);
84+
}
85+
86+
syncLowerHand(roomName: string, connectionId: string) {
87+
let body = {
88+
"connectionId": connectionId
89+
};
90+
fetch(this.baseURL + '/room/' + roomName + '/raiseHand', {
91+
method: 'DELETE',
92+
body: JSON.stringify(body),
93+
keepalive: true
94+
});
95+
}
96+
6497
private handleError(error: any) {
6598
console.error(error);
6699
return Observable.throw("Server error (" + error.status + "): " + error.message)

frontend/src/app/video-room/video-room.component.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,11 @@ export class VideoRoomComponent implements OnInit, OnDestroy {
118118

119119
@HostListener('window:beforeunload')
120120
beforeunloadHandler() {
121-
this.exitSession();
122121
this.openViduSrv.syncRemoveUser(this.mySessionId);
122+
if(this.localUsers[0].isHandRaised()) {
123+
this.roomService.syncLowerHand(this.mySessionId, this.localUsers[0].getConnectionId());
124+
}
125+
this.exitSession();
123126
}
124127

125128
@HostListener('window:resize')
@@ -288,6 +291,12 @@ export class VideoRoomComponent implements OnInit, OnDestroy {
288291
this.session.disconnect();
289292
}
290293
if(this.OV != null) {
294+
if(this.localUsers[0].isHandRaised()) {
295+
this.roomService.lowerHand(this.roomName, this.localUsers[0].getConnectionId()).subscribe(
296+
(_) => {},
297+
error => console.error(error)
298+
);
299+
}
291300
this.OV = null;
292301
this.OVScreen = null;
293302
this.session = null;
@@ -718,7 +727,8 @@ export class VideoRoomComponent implements OnInit, OnDestroy {
718727
}
719728
const connection = event.connection;
720729
const userDisconnected = this.remoteUsers.filter((user) => user.getConnectionId() === connection.connectionId)[0];
721-
this.handsRaised = this.handsRaised.filter((handRaisedUser) => handRaisedUser.connectionId !== connection.connectionId);
730+
userDisconnected.setHandRaised(false);
731+
this.raiseOrLowerHand(userDisconnected);
722732
this.remoteUsers = this.remoteUsers.filter((user) => user.getConnectionId() !== connection.connectionId);
723733
this.showConnectionPopup(userDisconnected.getNickname(), false, userDisconnected.getAvatar());
724734
this.updateModConnections();

0 commit comments

Comments
 (0)