Skip to content

Commit

Permalink
Conference List UI Component (#77)
Browse files Browse the repository at this point in the history
* began conference list

* Conferences hideable and conference component functional

* Style fixes.

* Fixed rare race condition. -> slower :/

* Conference Component, Ticket reference BBB meta, attendence list.

* Show hidden conference if current user is attending.
  • Loading branch information
snicki13 authored Oct 18, 2021
1 parent 817dfea commit 585d2de
Show file tree
Hide file tree
Showing 96 changed files with 1,514 additions and 17,933 deletions.
2 changes: 0 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ plugins {
kotlin("jvm") version "1.4.32"
kotlin("plugin.spring") version "1.4.32"
kotlin("kapt") version "1.4.32"
id("org.jlleitschuh.gradle.ktlint") version "10.2.0"
id("org.jlleitschuh.gradle.ktlint-idea") version "10.2.0"
}

group = "de.thm.mni.ii"
Expand Down
1 change: 1 addition & 0 deletions examples/docker-compose/example.env
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ CLASSROOM_SSL_ENABLED=true
CLASSROOM_KEYSTORE_PATH=classpath:keystore.p12
CLASSROOM_KEYSTORE_TYPE=PKCS12
CLASSROOM_KEYSTORE_PASS=passwd
CLASSROOM_LOGGING_LEVEL=INFO
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package de.thm.mni.ii.classroom
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
import org.springframework.boot.runApplication
import org.springframework.scheduling.annotation.EnableScheduling
import reactor.core.publisher.Hooks

@SpringBootApplication
@EnableScheduling
@ConfigurationPropertiesScan("de.thm.mni.ii.classroom.properties")
class DigitalClassroomApplication

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,31 @@ class ConferenceController(private val conferenceService: ConferenceService) {

@MessageMapping("socket/conference/create")
fun createConference(@AuthenticationPrincipal userCredentials: UserCredentials, @Payload conferenceInfo: ConferenceInfo): Mono<ConferenceInfo> {
assert(userCredentials.classroomId == conferenceInfo.classroomId)
return conferenceService.createConference(userCredentials, conferenceInfo)
}

@MessageMapping("socket/conference/join")
fun joinConference(@AuthenticationPrincipal userCredentials: UserCredentials, @Payload conferenceInfo: ConferenceInfo): Mono<JoinLink> {
assert(userCredentials.classroomId == conferenceInfo.classroomId)
return conferenceService.joinConference(userCredentials, conferenceInfo)
}

@MessageMapping("socket/conference/join-user")
fun joinConferenceOfUser(@AuthenticationPrincipal joiningUserCredentials: UserCredentials, @Payload conferencingUserCredentials: UserCredentials): Mono<JoinLink> {
return conferenceService.joinConferenceOfUser(joiningUserCredentials, conferencingUserCredentials)
}

@MessageMapping("socket/conference/leave")
fun leaveConference(@AuthenticationPrincipal userCredentials: UserCredentials, @Payload conferenceInfo: ConferenceInfo): Mono<Void> {
assert(userCredentials.classroomId == conferenceInfo.classroomId)
return conferenceService.leaveConference(userCredentials, conferenceInfo)
}

@MessageMapping("socket/conference/end")
fun endConference(@AuthenticationPrincipal userCredentials: UserCredentials, @Payload conferenceInfo: ConferenceInfo): Mono<Void> {
TODO("NOT YET IMPLEMENTED")
assert(userCredentials.classroomId == conferenceInfo.classroomId)
return conferenceService.endConferenceManually(userCredentials, conferenceInfo)
}

@MessageMapping("socket/conference/invite")
fun inviteToConference(@AuthenticationPrincipal userCredentials: UserCredentials, @Payload invitationEvent: InvitationEvent): Mono<Void> {
assert(userCredentials.classroomId == invitationEvent.getClassroomId())
return conferenceService.forwardInvitation(userCredentials, invitationEvent)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class UserWebSocketController(

@MessageMapping("socket/classroom-event")
fun receiveEvent(@AuthenticationPrincipal userCredentials: UserCredentials, @Payload event: ClassroomEvent) {
assert(userCredentials.classroomId == event.getClassroomId())
classroomEventReceiverService.classroomEventReceived(userCredentials, event)
}

Expand All @@ -44,7 +45,12 @@ class UserWebSocketController(

@MessageMapping("socket/init-users")
fun initUsers(@AuthenticationPrincipal userCredentials: UserCredentials): Flux<User> {
return userService.getUserDisplays(userCredentials)
return userService.getUsers(userCredentials)
}

@MessageMapping("socket/init-offline-users")
fun initOfflineUsers(@AuthenticationPrincipal userCredentials: UserCredentials): Flux<User> {
return userService.getOfflineUsers(userCredentials)
}

@MessageMapping("socket/init-conferences")
Expand Down
11 changes: 7 additions & 4 deletions src/main/kotlin/de/thm/mni/ii/classroom/event/ClassroomEvent.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
package de.thm.mni.ii.classroom.event

import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo
import java.io.Serializable

@Suppress("unused")
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "eventName")
@JsonSubTypes(
JsonSubTypes.Type(value = MessageEvent::class, name = "MessageEvent"),
JsonSubTypes.Type(value = UserEvent::class, name = "UserEvent"),
JsonSubTypes.Type(value = TicketEvent::class, name = "TicketEvent"),
JsonSubTypes.Type(value = ConferenceEvent::class, name = "ConferenceEvent"),
JsonSubTypes.Type(value = InvitationEvent::class, name = "InvitationEvent"),
)
abstract class ClassroomEvent(@field:Suppress("unused") private val eventName: String) : Serializable

data class MessageEvent(val message: String) : ClassroomEvent(MessageEvent::class.simpleName!!)
abstract class ClassroomEvent(
private val eventName: String
) : Serializable {
@JsonIgnore abstract fun getClassroomId(): String
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package de.thm.mni.ii.classroom.event

import com.fasterxml.jackson.annotation.JsonIgnore
import de.thm.mni.ii.classroom.model.classroom.ConferenceInfo

data class ConferenceEvent(
val conferenceInfo: ConferenceInfo,
val conferenceAction: ConferenceAction
) : ClassroomEvent(ConferenceEvent::class.simpleName!!)
) : ClassroomEvent(ConferenceEvent::class.simpleName!!) {
@JsonIgnore override fun getClassroomId(): String = this.conferenceInfo.classroomId
}

enum class ConferenceAction {
CREATE,
Expand Down
12 changes: 11 additions & 1 deletion src/main/kotlin/de/thm/mni/ii/classroom/event/InvitationEvent.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
package de.thm.mni.ii.classroom.event

import com.fasterxml.jackson.annotation.JsonIgnore
import de.thm.mni.ii.classroom.model.classroom.ConferenceInfo
import de.thm.mni.ii.classroom.model.classroom.UserCredentials

data class InvitationEvent(
val inviter: UserCredentials,
val invitee: UserCredentials,
val conferenceInfo: ConferenceInfo
) : ClassroomEvent(InvitationEvent::class.simpleName!!)
) : ClassroomEvent(InvitationEvent::class.simpleName!!) {
@JsonIgnore
override fun getClassroomId(): String {
return if (inviter.classroomId !== invitee.classroomId || inviter.classroomId !== conferenceInfo.classroomId) {
"INVALID"
} else {
this.conferenceInfo.classroomId
}
}
}
7 changes: 5 additions & 2 deletions src/main/kotlin/de/thm/mni/ii/classroom/event/TicketEvent.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package de.thm.mni.ii.classroom.event

import com.fasterxml.jackson.annotation.JsonIgnore
import de.thm.mni.ii.classroom.model.classroom.Ticket

data class TicketEvent(
val ticket: Ticket,
val ticketAction: TicketAction,
) : ClassroomEvent(TicketEvent::class.simpleName!!)
) : ClassroomEvent(TicketEvent::class.simpleName!!) {
@JsonIgnore override fun getClassroomId(): String = this.ticket.classroomId
}

enum class TicketAction {
CREATE,
ASSIGN,
UPDATE,
CLOSE
}
5 changes: 4 additions & 1 deletion src/main/kotlin/de/thm/mni/ii/classroom/event/UserEvent.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package de.thm.mni.ii.classroom.event

import com.fasterxml.jackson.annotation.JsonIgnore
import de.thm.mni.ii.classroom.model.classroom.User

data class UserEvent(
val user: User,
val userAction: UserAction
) : ClassroomEvent(UserEvent::class.simpleName!!)
) : ClassroomEvent(UserEvent::class.simpleName!!) {
@JsonIgnore override fun getClassroomId(): String = this.user.classroomId
}

enum class UserAction {
JOIN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Attendees(userCredentials: Set<UserCredentials>? = null) {

@Suppress("unused")
@XmlElement(name = "attendee")
val attendees = userCredentials?.mapTo(HashSet(), ::Attendee)
val attendees = userCredentials?.mapTo(LinkedHashSet(), ::Attendee)
}

@XmlType(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,12 @@ class Meeting(digitalClassroom: DigitalClassroom? = null) {
name = "metadata",
propOrder = [
"classroomid",
"creatorid"
"creatorid",
"ticketid"
]
)
class MeetingMetaData {
@XmlElement val classroomid: String = ""
@XmlElement val creatorid: String = ""
@XmlElement val ticketid: Long? = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package de.thm.mni.ii.classroom.model.classroom

import java.net.URL

open class ClassroomInfo(
class ClassroomInfo(
val classroomId: String,
val classroomName: String,
val logoutUrl: URL?,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package de.thm.mni.ii.classroom.model.classroom

import de.thm.mni.ii.classroom.model.ClassroomDependent
import java.time.ZonedDateTime

class Conference(
val classroomId: String,
override val classroomId: String,
val conferenceId: String,
val conferenceName: String,
val attendeePassword: String,
val moderatorPassword: String,
val creator: UserCredentials,
var visible: Boolean,
val attendees: MutableSet<UserCredentials>,
val creationTimestamp: ZonedDateTime = ZonedDateTime.now()
) {
val attendees: LinkedHashSet<UserCredentials>,
val creationTimestamp: ZonedDateTime = ZonedDateTime.now(),
) : ClassroomDependent {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
Expand All @@ -31,7 +32,9 @@ class Conference(
return result
}

fun toConferenceInfo() = ConferenceInfo(this)
fun toConferenceInfo(): ConferenceInfo {
return ConferenceInfo(this)
}

fun removeUser(userCredentials: UserCredentials): Conference {
this.attendees.remove(userCredentials)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,44 @@ import de.thm.mni.ii.classroom.config.ZonedDateTimeMillisSerializer
import de.thm.mni.ii.classroom.model.ClassroomDependent
import java.time.ZonedDateTime

class ConferenceInfo(
@Suppress("unused")
open class ConferenceInfo(
override val classroomId: String,
val conferenceId: String?,
open val conferenceId: String?,
val conferenceName: String,
val creator: UserCredentials?,
val visible: Boolean,
val creator: UserCredentials,
var visible: Boolean,
@field:JsonSerialize(using = ZonedDateTimeMillisSerializer::class)
@field:JsonDeserialize(using = ZonedDateTimeMillisDeserializer::class)
val creationTimestamp: ZonedDateTime?,
val attendees: List<String>
val creationTimestamp: ZonedDateTime,
var attendeeIds: List<String>,
) : ClassroomDependent {

constructor(conference: Conference) : this(
conference.classroomId,
conference.conferenceId,
conference.conferenceName,
conference.creator,
conference.visible,
conference.creationTimestamp,
conference.attendees.map { it.userId }
conference.attendees.map { it.userId },
)

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as ConferenceInfo
if (other !is ConferenceInfo) return false

if (classroomId != other.classroomId) return false
if (conferenceId != other.conferenceId) return false
if (creator != other.creator) return false

return true
}

override fun hashCode(): Int {
var result = classroomId.hashCode()
result = 31 * result + (conferenceId?.hashCode() ?: 0)
result = 31 * result + creator.hashCode()
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.toFlux
import reactor.kotlin.core.publisher.toMono
import java.util.concurrent.ConcurrentHashMap

class ConferenceStorage {

private val usersConference = HashMap<UserCredentials, HashSet<Conference>>()
private val conferences = HashMap<String, Conference>()

fun getConferencesOfUser(userCredentials: UserCredentials) =
Flux.fromIterable(usersConference[userCredentials] ?: LinkedHashSet())
private val usersConference = ConcurrentHashMap<UserCredentials, LinkedHashSet<Conference>>()
private val conferences = ConcurrentHashMap<String, Conference>()

fun getUsersOfConference(conference: Conference): Flux<UserCredentials> {
return conferences[conference.conferenceId]?.attendees?.toFlux()
Expand All @@ -21,7 +19,7 @@ class ConferenceStorage {

fun joinUser(conference: Conference, userCredentials: UserCredentials): Mono<Conference> {
return Mono.just(userCredentials).map {
usersConference.computeIfAbsent(userCredentials) { HashSet() }
usersConference.computeIfAbsent(userCredentials) { LinkedHashSet() }
.also { it.add(conference) }
conferences.computeIfAbsent(conference.conferenceId) { conference }
.also { it.attendees.add(userCredentials) }
Expand All @@ -36,10 +34,6 @@ class ConferenceStorage {
return conferences.values.toFlux()
}

fun getUsersInConferences(): Flux<UserCredentials> {
return Flux.fromIterable(usersConference.keys)
}

fun isUserInConference(userCredentials: UserCredentials): Mono<Boolean> {
return Mono.just(usersConference.containsKey(userCredentials))
}
Expand Down Expand Up @@ -87,10 +81,14 @@ class ConferenceStorage {
usersConference.clear()
conferences.values.forEach { conference ->
conference.attendees.forEach { user ->
usersConference.computeIfAbsent(user) { HashSet() }
usersConference.computeIfAbsent(user) { LinkedHashSet() }
.also { it.add(conference) }
}
}
return Mono.empty()
}

fun getConferenceOfTicket(conferenceId: String?): Mono<Conference> {
return conferences[conferenceId].toMono()
}
}
Loading

0 comments on commit 585d2de

Please sign in to comment.