Przycisk mikrofonu przy kazdym uczestniku daje i odbiera glos bez jego prosby. Idzie przez kolejke zgloszen (SpeakRequestService.setVoice): powstaje od razu przyznane zgloszenie albo zostaje przyznane oczekujace, wiec kolejka, napis "Masz glos" i automatyczne wlaczenie mikrofonu zgadzaja sie ze stanem. Serwer pokoju zmienia uprawnienia od razu. Uczestnik bez wlasnego zgloszenia rozpoznaje zdarzenie jako swoje po numerze zapisu z sesji, zamiast czekac na odpytanie co 10 sekund. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+19
@@ -90,6 +90,25 @@ public class SpeakRequestAdminController {
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Glos dla konkretnego uczestnika bez jego zgloszenia, z karty "Na sali".
|
||||
*
|
||||
* Po zatwierdzeniu transakcji serwer pokoju otwiera albo zamyka mikrofon
|
||||
* od razu, tak samo jak przy przyznaniu z kolejki.
|
||||
*/
|
||||
@PostMapping("/participants/{registrationId}/voice")
|
||||
@Operation(summary = "Danie albo odebranie glosu uczestnikowi bez zgloszenia")
|
||||
public SpeakRequestView setVoice(@PathVariable Long id,
|
||||
@PathVariable Long registrationId,
|
||||
@RequestBody VoiceRequest request) {
|
||||
SpeakRequestView view = speakRequests.setVoice(id, registrationId, request.value());
|
||||
permissionSync.syncParticipant(id, registrationId);
|
||||
return view;
|
||||
}
|
||||
|
||||
public record VoiceRequest(boolean value) {
|
||||
}
|
||||
|
||||
public record OpenMicRequest(boolean openMic) {
|
||||
}
|
||||
|
||||
|
||||
@@ -244,6 +244,73 @@ public class SpeakRequestService {
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Glos dany albo odebrany przez prowadzacego bez prosby uczestnika.
|
||||
*
|
||||
* NIE ustawia samej roli. Idzie przez zgloszenie, zeby kolejka w panelu,
|
||||
* napis "Masz glos" u uczestnika i automatyczne wlaczenie mikrofonu
|
||||
* mowily to samo: glos bez zgloszenia bylby widoczny wylacznie w bazie,
|
||||
* a prowadzacy nie mialby go czym odebrac w kolejce.
|
||||
*
|
||||
* Dawanie: oczekujace zgloszenie zostaje przyznane, a gdy go nie ma,
|
||||
* powstaje od razu przyznane. Odbieranie: tak jak "Odbierz" w kolejce,
|
||||
* a rola wraca do VIEWER takze wtedy, gdy zgloszenia nie bylo.
|
||||
*
|
||||
* @return biezace zgloszenie uczestnika albo null po odebraniu bez zgloszenia
|
||||
*/
|
||||
@Transactional
|
||||
public SpeakRequestView setVoice(Long webinarId, Long registrationId, boolean voice) {
|
||||
Registration registration = registrationRepository.findById(registrationId)
|
||||
.filter(r -> r.getWebinar().getId().equals(webinarId))
|
||||
.orElseThrow(() -> new NotFoundException("Nie znaleziono uczestnika tego webinaru"));
|
||||
|
||||
var open = speakRequestRepository.findOpenByRegistration(registrationId);
|
||||
|
||||
if (!voice) {
|
||||
SpeakRequestView view = null;
|
||||
if (open.isPresent()) {
|
||||
SpeakRequest request = open.get();
|
||||
request.setStatus(request.getStatus() == SpeakRequestStatus.GRANTED
|
||||
? SpeakRequestStatus.REVOKED
|
||||
: SpeakRequestStatus.DECLINED);
|
||||
request.setResolvedAt(OffsetDateTime.now());
|
||||
speakRequestRepository.save(request);
|
||||
view = SpeakRequestView.of(request);
|
||||
}
|
||||
setRole(registration, RegistrationRoomRole.VIEWER);
|
||||
if (view != null) {
|
||||
broadcaster.speakRequest(webinarId, "SPEAK_REVOKED", view);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
SpeakRequest request;
|
||||
if (open.isPresent()) {
|
||||
request = open.get();
|
||||
} else {
|
||||
request = new SpeakRequest();
|
||||
request.setWebinar(registration.getWebinar());
|
||||
request.setRegistration(registration);
|
||||
}
|
||||
request.setStatus(SpeakRequestStatus.GRANTED);
|
||||
request.setResolvedAt(OffsetDateTime.now());
|
||||
try {
|
||||
request = speakRequestRepository.saveAndFlush(request);
|
||||
} catch (DataIntegrityViolationException e) {
|
||||
// uczestnik zglosil sie w tej samej chwili: unikalny indeks odrzucil
|
||||
// drugie otwarte zgloszenie. Ponowne klikniecie znajdzie juz jego
|
||||
// zgloszenie i je przyzna, wiec wystarczy poprosic o powtorzenie
|
||||
throw new ValidationException("Uczestnik właśnie zgłosił się do głosu. Spróbuj ponownie.");
|
||||
}
|
||||
setRole(registration, RegistrationRoomRole.SPEAKER);
|
||||
|
||||
log.info("Prowadzacy dal glos bez zgloszenia zapisowi {} na webinarze {}", registrationId, webinarId);
|
||||
|
||||
SpeakRequestView view = SpeakRequestView.of(request);
|
||||
broadcaster.speakRequest(webinarId, "SPEAK_GRANTED", view);
|
||||
return view;
|
||||
}
|
||||
|
||||
private SpeakRequest require(Long webinarId, Long requestId) {
|
||||
SpeakRequest request = speakRequestRepository.findById(requestId)
|
||||
.orElseThrow(() -> new NotFoundException("Nie znaleziono zgłoszenia"));
|
||||
|
||||
@@ -273,17 +273,18 @@ public class RoomPresenceService {
|
||||
result.add(new ParticipantView(identity, fullName(registration), KIND_PARTICIPANT,
|
||||
registration.getId(), registration.getEmail(), joinedAt, audio, video, screen,
|
||||
registration.isChatMuted(), registration.isRoomBanned(),
|
||||
registration.isCameraAllowed(), registration.isScreenAllowed()));
|
||||
registration.isCameraAllowed(), registration.isScreenAllowed(),
|
||||
voiceOf(registration)));
|
||||
} else if (speaker != null) {
|
||||
result.add(new ParticipantView(identity, speaker.getFullName(), KIND_SPEAKER,
|
||||
null, speaker.getEmail(), joinedAt, audio, video, screen, false, false,
|
||||
true, true));
|
||||
true, true, true));
|
||||
} else {
|
||||
// polaczenie spoza naszej sciezki wejscia: nie ma go w bazie
|
||||
String name = info.getName().isBlank() ? identity : info.getName();
|
||||
result.add(new ParticipantView(identity, name, KIND_OTHER,
|
||||
null, null, joinedAt, audio, video, screen, false, false,
|
||||
false, false));
|
||||
false, false, false));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -302,11 +303,12 @@ public class RoomPresenceService {
|
||||
if (r != null) {
|
||||
view = new ParticipantView("reg-" + r.getId(), fullName(r), KIND_PARTICIPANT,
|
||||
r.getId(), r.getEmail(), session.getJoinedAt(), false, false, false,
|
||||
r.isChatMuted(), r.isRoomBanned(), r.isCameraAllowed(), r.isScreenAllowed());
|
||||
r.isChatMuted(), r.isRoomBanned(), r.isCameraAllowed(), r.isScreenAllowed(),
|
||||
voiceOf(r));
|
||||
} else if (s != null) {
|
||||
view = new ParticipantView("spk-" + s.getId(), s.getFullName(), KIND_SPEAKER,
|
||||
null, s.getEmail(), session.getJoinedAt(), false, false, false, false, false,
|
||||
true, true);
|
||||
true, true, true);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
@@ -350,6 +352,11 @@ public class RoomPresenceService {
|
||||
}
|
||||
}
|
||||
|
||||
/** Glos przyznany indywidualnie. Otwarty mikrofon calej sali sie tu nie liczy. */
|
||||
private static boolean voiceOf(Registration r) {
|
||||
return r.getRoomRole() == com.softspm.webinar.domain.RegistrationRoomRole.SPEAKER;
|
||||
}
|
||||
|
||||
private static String fullName(Registration r) {
|
||||
return ((r.getFirstName() == null ? "" : r.getFirstName()) + " "
|
||||
+ (r.getLastName() == null ? "" : r.getLastName())).trim();
|
||||
@@ -382,7 +389,9 @@ public class RoomPresenceService {
|
||||
/** Indywidualna zgoda prowadzacego na kamere (prelegent zawsze true). */
|
||||
boolean cameraAllowed,
|
||||
/** Indywidualna zgoda prowadzacego na ekran (prelegent zawsze true). */
|
||||
boolean screenAllowed) {
|
||||
boolean screenAllowed,
|
||||
/** Przyznany glos: z kolejki albo bez zgloszenia (prelegent zawsze true). */
|
||||
boolean voiceGranted) {
|
||||
}
|
||||
|
||||
public record BannedView(Long registrationId, String name, String email) {
|
||||
|
||||
Reference in New Issue
Block a user