-
Notifications
You must be signed in to change notification settings - Fork 709
/
GetVoiceBroadcastStateEventLiveUseCase.kt
151 lines (144 loc) · 7.52 KB
/
GetVoiceBroadcastStateEventLiveUseCase.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
* Copyright (c) 2022 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.voicebroadcast.usecase
import im.vector.app.features.voicebroadcast.VoiceBroadcastConstants
import im.vector.app.features.voicebroadcast.model.VoiceBroadcast
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastEvent
import im.vector.app.features.voicebroadcast.model.asVoiceBroadcastEvent
import im.vector.app.features.voicebroadcast.voiceBroadcastId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.transformWhile
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional
import org.matrix.android.sdk.flow.flow
import org.matrix.android.sdk.flow.mapOptional
import timber.log.Timber
import javax.inject.Inject
class GetVoiceBroadcastStateEventLiveUseCase @Inject constructor(
private val session: Session,
private val getVoiceBroadcastStateEventUseCase: GetVoiceBroadcastStateEventUseCase,
) {
fun execute(voiceBroadcast: VoiceBroadcast): Flow<Optional<VoiceBroadcastEvent>> {
return getMostRecentVoiceBroadcastEventFlow(voiceBroadcast)
.onEach { event ->
Timber.d(
"## VoiceBroadcast | " +
"voiceBroadcastId=${event.getOrNull()?.voiceBroadcastId}, " +
"state=${event.getOrNull()?.content?.voiceBroadcastState}"
)
}
}
/**
* Get a flow of the most recent event for the given voice broadcast.
*/
private fun getMostRecentVoiceBroadcastEventFlow(voiceBroadcast: VoiceBroadcast): Flow<Optional<VoiceBroadcastEvent>> {
val room = session.getRoom(voiceBroadcast.roomId) ?: error("Unknown roomId: ${voiceBroadcast.roomId}")
val startedEventFlow = room.flow().liveTimelineEvent(voiceBroadcast.voiceBroadcastId)
// observe started event changes
return startedEventFlow
.mapOptional { it.root.asVoiceBroadcastEvent() }
.flatMapLatest { startedEvent ->
if (startedEvent.hasValue().not() || startedEvent.get().root.isRedacted()) {
// if started event is null or redacted, send null
flowOf(Optional.empty())
} else {
// otherwise, observe most recent event changes
getMostRecentRelatedEventFlow(room, voiceBroadcast)
.transformWhile { mostRecentEvent ->
val hasValue = mostRecentEvent.hasValue()
if (hasValue) {
// keep the most recent event
emit(mostRecentEvent)
} else {
// no most recent event, fallback to started event
emit(startedEvent)
}
hasValue
}
}
}
.distinctUntilChangedBy { it.getOrNull()?.content?.voiceBroadcastState }
}
/**
* Get a flow of the most recent related event.
*/
private fun getMostRecentRelatedEventFlow(room: Room, voiceBroadcast: VoiceBroadcast): Flow<Optional<VoiceBroadcastEvent>> {
val mostRecentEvent = getVoiceBroadcastStateEventUseCase.execute(voiceBroadcast).toOptional()
return if (mostRecentEvent.hasValue()) {
val stateKey = mostRecentEvent.get().root.stateKey.orEmpty()
// observe incoming voice broadcast state events
room.flow()
.liveStateEvent(VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO, QueryStringValue.Equals(stateKey))
.mapOptional { it.asVoiceBroadcastEvent() }
// drop first event sent by the matrix-sdk, we compute manually this first event
.drop(1)
// start with the computed most recent event
.onStart { emit(mostRecentEvent) }
// handle event if null or related to the given voice broadcast
.filter { it.hasValue().not() || it.get().voiceBroadcastId == voiceBroadcast.voiceBroadcastId }
// observe changes while event is not null
.transformWhile { event ->
emit(event)
event.hasValue()
}
.flatMapLatest { newMostRecentEvent ->
if (newMostRecentEvent.hasValue()) {
// observe most recent event changes
newMostRecentEvent.get().flow()
.transformWhile { event ->
// observe changes until event is null or redacted
emit(event)
event.hasValue() && event.get().root.isRedacted().not()
}
.flatMapLatest { event ->
val isRedactedOrNull = !event.hasValue() || event.get().root.isRedacted()
if (isRedactedOrNull) {
// event is null or redacted, switch to the latest not redacted event
getMostRecentRelatedEventFlow(room, voiceBroadcast)
} else {
// event is not redacted, send the event
flowOf(event)
}
}
} else {
// there is no more most recent event, just send it
flowOf(newMostRecentEvent)
}
}
} else {
// there is no more most recent event, just send it
flowOf(mostRecentEvent)
}
}
/**
* Get a flow of the given voice broadcast event changes.
*/
private fun VoiceBroadcastEvent.flow(): Flow<Optional<VoiceBroadcastEvent>> {
val room = this.root.roomId?.let { session.getRoom(it) } ?: return flowOf(Optional.empty())
return room.flow().liveTimelineEvent(root.eventId!!).mapOptional { it.root.asVoiceBroadcastEvent() }
}
}