mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-09-20 06:34:00 +00:00
feat(jam-session): add connection state management and update UI feedback for session status
This commit is contained in:
parent
0eb07ef9b6
commit
60e38f4868
@ -74,6 +74,9 @@ class JamSessionService(
|
|||||||
private val _localParticipantId = MutableStateFlow<String?>(null)
|
private val _localParticipantId = MutableStateFlow<String?>(null)
|
||||||
val localParticipantId: StateFlow<String?> = _localParticipantId.asStateFlow()
|
val localParticipantId: StateFlow<String?> = _localParticipantId.asStateFlow()
|
||||||
|
|
||||||
|
private val _isConnected = MutableStateFlow(false)
|
||||||
|
val isConnected: StateFlow<Boolean> = _isConnected.asStateFlow()
|
||||||
|
|
||||||
private val _incomingMessages = MutableSharedFlow<JamMessage>(extraBufferCapacity = 64)
|
private val _incomingMessages = MutableSharedFlow<JamMessage>(extraBufferCapacity = 64)
|
||||||
val incomingMessages = _incomingMessages.asSharedFlow()
|
val incomingMessages = _incomingMessages.asSharedFlow()
|
||||||
|
|
||||||
@ -174,7 +177,7 @@ class JamSessionService(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun joinSession(offerSdp: String): String {
|
suspend fun joinSession(offerSdp: String, hostName: String? = null): String {
|
||||||
log.i { "Joining jam session" }
|
log.i { "Joining jam session" }
|
||||||
val participantName = resolveParticipantName(defaultPrefix = "Guest")
|
val participantName = resolveParticipantName(defaultPrefix = "Guest")
|
||||||
|
|
||||||
@ -186,6 +189,13 @@ class JamSessionService(
|
|||||||
hostConnection = pc
|
hostConnection = pc
|
||||||
_role.value = JamRole.Guest
|
_role.value = JamRole.Guest
|
||||||
_localParticipantId.value = "guest"
|
_localParticipantId.value = "guest"
|
||||||
|
_participants.value = listOf(
|
||||||
|
JamParticipant(
|
||||||
|
id = "host",
|
||||||
|
displayName = hostName?.ifBlank { null } ?: "Host",
|
||||||
|
isHost = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
_isActive.value = true
|
_isActive.value = true
|
||||||
|
|
||||||
// The data channel arrives in-band from the host's offer via on_data_channel;
|
// The data channel arrives in-band from the host's offer via on_data_channel;
|
||||||
@ -226,6 +236,7 @@ class JamSessionService(
|
|||||||
_role.value = null
|
_role.value = null
|
||||||
_participants.value = emptyList()
|
_participants.value = emptyList()
|
||||||
_isActive.value = false
|
_isActive.value = false
|
||||||
|
_isConnected.value = false
|
||||||
_localParticipantId.value = null
|
_localParticipantId.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,6 +314,7 @@ class JamSessionService(
|
|||||||
|
|
||||||
override fun onDataChannelOpen(label: String) {
|
override fun onDataChannelOpen(label: String) {
|
||||||
log.i { "[$guestId] Data channel '$label' open" }
|
log.i { "[$guestId] Data channel '$label' open" }
|
||||||
|
_isConnected.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDataChannelMessage(label: String, data: String) {
|
override fun onDataChannelMessage(label: String, data: String) {
|
||||||
@ -329,6 +341,7 @@ class JamSessionService(
|
|||||||
|
|
||||||
override fun onDataChannelOpen(label: String) {
|
override fun onDataChannelOpen(label: String) {
|
||||||
log.i { "Data channel '$label' open" }
|
log.i { "Data channel '$label' open" }
|
||||||
|
_isConnected.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDataChannelMessage(label: String, data: String) {
|
override fun onDataChannelMessage(label: String, data: String) {
|
||||||
|
|||||||
@ -295,23 +295,35 @@ private fun GuestSessionView(
|
|||||||
ParticipantsSection(state.participants)
|
ParticipantsSection(state.participants)
|
||||||
|
|
||||||
val answerLink = state.answerLink
|
val answerLink = state.answerLink
|
||||||
if (answerLink == null) {
|
when {
|
||||||
Text(
|
state.isConnected -> {
|
||||||
text = "Connecting to the session...",
|
Text(
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
text = "Connected to the session",
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
)
|
color = MaterialTheme.colorScheme.primary,
|
||||||
} else {
|
)
|
||||||
Text(
|
}
|
||||||
text = "Almost there! Send your answer back to the host:",
|
|
||||||
style = MaterialTheme.typography.titleSmall,
|
answerLink == null -> {
|
||||||
)
|
Text(
|
||||||
ShareableLinkBox(
|
text = "Connecting to the session...",
|
||||||
label = "Answer link",
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
link = answerLink,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
onCopy = { clipboard.setText(AnnotatedString(answerLink)) },
|
)
|
||||||
onShare = { onShare(answerLink) },
|
}
|
||||||
)
|
|
||||||
|
else -> {
|
||||||
|
Text(
|
||||||
|
text = "Almost there! Send your answer back to the host:",
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
)
|
||||||
|
ShareableLinkBox(
|
||||||
|
label = "Answer link",
|
||||||
|
link = answerLink,
|
||||||
|
onCopy = { clipboard.setText(AnnotatedString(answerLink)) },
|
||||||
|
onShare = { onShare(answerLink) },
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LeaveButton(onLeave)
|
LeaveButton(onLeave)
|
||||||
|
|||||||
@ -37,6 +37,7 @@ import kotlinx.coroutines.launch
|
|||||||
|
|
||||||
data class JamUiState(
|
data class JamUiState(
|
||||||
val isActive: Boolean = false,
|
val isActive: Boolean = false,
|
||||||
|
val isConnected: Boolean = false,
|
||||||
val role: JamRole? = null,
|
val role: JamRole? = null,
|
||||||
val participants: List<JamParticipant> = emptyList(),
|
val participants: List<JamParticipant> = emptyList(),
|
||||||
/** Host: deep link containing this session's SDP offer, ready to share. */
|
/** Host: deep link containing this session's SDP offer, ready to share. */
|
||||||
@ -69,6 +70,7 @@ class JamViewModel(
|
|||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
isActive = active,
|
isActive = active,
|
||||||
|
isConnected = jamSession.isConnected.value,
|
||||||
role = jamSession.role.value,
|
role = jamSession.role.value,
|
||||||
participants = jamSession.participants.value,
|
participants = jamSession.participants.value,
|
||||||
inviteLink = if (!active) null else it.inviteLink,
|
inviteLink = if (!active) null else it.inviteLink,
|
||||||
@ -84,6 +86,11 @@ class JamViewModel(
|
|||||||
_uiState.update { it.copy(participants = participants) }
|
_uiState.update { it.copy(participants = participants) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
viewModelScope.launch {
|
||||||
|
jamSession.isConnected.collect { connected ->
|
||||||
|
_uiState.update { it.copy(isConnected = connected) }
|
||||||
|
}
|
||||||
|
}
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
deepLinks.pendingLink.collect { link ->
|
deepLinks.pendingLink.collect { link ->
|
||||||
handleDeepLink(link)
|
handleDeepLink(link)
|
||||||
@ -119,16 +126,17 @@ class JamViewModel(
|
|||||||
|
|
||||||
fun joinWithIncomingInvite() {
|
fun joinWithIncomingInvite() {
|
||||||
val sdp = _uiState.value.incomingOfferSdp ?: return
|
val sdp = _uiState.value.incomingOfferSdp ?: return
|
||||||
join(sdp)
|
join(sdp, _uiState.value.incomingHostName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun joinWithPasted(input: String) {
|
fun joinWithPasted(input: String) {
|
||||||
val sdp = JamInviteCodec.extractSdp(input)
|
val parsed = JamInviteCodec.parse(input)
|
||||||
|
val sdp = parsed?.sdp ?: JamInviteCodec.extractSdp(input)
|
||||||
if (sdp == null) {
|
if (sdp == null) {
|
||||||
_uiState.update { it.copy(error = "That doesn't look like a valid jam invite.") }
|
_uiState.update { it.copy(error = "That doesn't look like a valid jam invite.") }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
join(sdp)
|
join(sdp, (parsed as? JamInviteLink.HostInvite)?.peerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,10 +179,10 @@ class JamViewModel(
|
|||||||
_uiState.update { it.copy(incomingOfferSdp = null, incomingHostName = null) }
|
_uiState.update { it.copy(incomingOfferSdp = null, incomingHostName = null) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun join(offerSdp: String) {
|
private fun join(offerSdp: String, hostName: String? = null) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
runCatching {
|
runCatching {
|
||||||
val answer = jamSession.joinSession(offerSdp)
|
val answer = jamSession.joinSession(offerSdp, hostName)
|
||||||
JamInviteCodec.buildGuestAnswer(localName(), answer)
|
JamInviteCodec.buildGuestAnswer(localName(), answer)
|
||||||
}.onSuccess { link ->
|
}.onSuccess { link ->
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user