@@ -28,6 +28,7 @@ import io.element.android.features.messages.impl.timeline.factories.TimelineItem
2828import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactoryConfig
2929import io.element.android.features.messages.impl.timeline.model.NewEventState
3030import io.element.android.features.messages.impl.timeline.model.TimelineItem
31+ import io.element.android.features.messages.impl.typing.TypingNotificationState
3132import io.element.android.features.messages.impl.voicemessages.timeline.RedactedVoiceMessageManager
3233import io.element.android.features.poll.api.actions.EndPollAction
3334import io.element.android.features.poll.api.actions.SendPollResponseAction
@@ -54,12 +55,12 @@ import kotlinx.coroutines.flow.launchIn
5455import kotlinx.coroutines.flow.onEach
5556import kotlinx.coroutines.launch
5657import kotlinx.coroutines.withContext
58+ import timber.log.Timber
5759
5860const val FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS = 200L
5961
6062class TimelinePresenter @AssistedInject constructor(
6163 timelineItemsFactoryCreator : TimelineItemsFactory .Creator ,
62- private val timelineItemIndexer : TimelineItemIndexer ,
6364 private val room : MatrixRoom ,
6465 private val dispatchers : CoroutineDispatchers ,
6566 private val appScope : CoroutineScope ,
@@ -69,7 +70,9 @@ class TimelinePresenter @AssistedInject constructor(
6970 private val endPollAction : EndPollAction ,
7071 private val sessionPreferencesStore : SessionPreferencesStore ,
7172 private val timelineController : TimelineController ,
73+ private val timelineItemIndexer : TimelineItemIndexer = TimelineItemIndexer (),
7274 private val resolveVerifiedUserSendFailurePresenter : Presenter <ResolveVerifiedUserSendFailureState >,
75+ private val typingNotificationPresenter : Presenter <TypingNotificationState >,
7376) : Presenter<TimelineState> {
7477 @AssistedFactory
7578 interface Factory {
@@ -87,13 +90,7 @@ class TimelinePresenter @AssistedInject constructor(
8790 @Composable
8891 override fun present (): TimelineState {
8992 val localScope = rememberCoroutineScope()
90- val focusRequestState: MutableState <FocusRequestState > = remember {
91- mutableStateOf(FocusRequestState .None )
92- }
93-
94- LaunchedEffect (Unit ) {
95- timelineItemsFactory.timelineItems.collect { timelineItems = it }
96- }
93+ var focusRequestState: FocusRequestState by remember { mutableStateOf(FocusRequestState .None ) }
9794
9895 val lastReadReceiptId = rememberSaveable { mutableStateOf<EventId ?>(null ) }
9996
@@ -152,13 +149,13 @@ class TimelinePresenter @AssistedInject constructor(
152149 navigator.onEditPollClick(event.pollStartId)
153150 }
154151 is TimelineEvents .FocusOnEvent -> {
155- focusRequestState.value = FocusRequestState .Requested (event.eventId, event.debounce)
152+ focusRequestState = FocusRequestState .Requested (event.eventId, event.debounce)
156153 }
157154 is TimelineEvents .OnFocusEventRender -> {
158- focusRequestState.value = focusRequestState.value .onFocusEventRender()
155+ focusRequestState = focusRequestState.onFocusEventRender()
159156 }
160157 is TimelineEvents .ClearFocusRequestState -> {
161- focusRequestState.value = FocusRequestState .None
158+ focusRequestState = FocusRequestState .None
162159 }
163160 is TimelineEvents .JumpToLive -> {
164161 timelineController.focusOnLive()
@@ -171,28 +168,46 @@ class TimelinePresenter @AssistedInject constructor(
171168 }
172169 }
173170
174- LaunchedEffect (focusRequestState.value) {
175- when (val currentFocusRequestState = focusRequestState.value) {
171+ LaunchedEffect (Unit ) {
172+ timelineItemsFactory.timelineItems
173+ .onEach { newTimelineItems ->
174+ timelineItemIndexer.process(newTimelineItems)
175+ timelineItems = newTimelineItems
176+ }
177+ .launchIn(this )
178+
179+ combine(timelineController.timelineItems(), room.membersStateFlow) { items, membersState ->
180+ timelineItemsFactory.replaceWith(
181+ timelineItems = items,
182+ roomMembers = membersState.roomMembers().orEmpty()
183+ )
184+ items
185+ }
186+ .onEach(redactedVoiceMessageManager::onEachMatrixTimelineItem)
187+ .launchIn(this )
188+ }
189+
190+ LaunchedEffect (focusRequestState) {
191+ Timber .d(" ## focusRequestState: $focusRequestState " )
192+ when (val currentFocusRequestState = focusRequestState) {
176193 is FocusRequestState .Requested -> {
177194 delay(currentFocusRequestState.debounce)
178195 if (timelineItemIndexer.isKnown(currentFocusRequestState.eventId)) {
179196 val index = timelineItemIndexer.indexOf(currentFocusRequestState.eventId)
180- focusRequestState.value = FocusRequestState .Success (eventId = currentFocusRequestState.eventId, index = index)
197+ focusRequestState = FocusRequestState .Success (eventId = currentFocusRequestState.eventId, index = index)
181198 } else {
182- focusRequestState.value = FocusRequestState .Loading (eventId = currentFocusRequestState.eventId)
199+ focusRequestState = FocusRequestState .Loading (eventId = currentFocusRequestState.eventId)
183200 }
184201 }
185202 is FocusRequestState .Loading -> {
186203 val eventId = currentFocusRequestState.eventId
187204 timelineController.focusOnEvent(eventId)
188- .fold(
189- onSuccess = {
190- focusRequestState.value = FocusRequestState .Success (eventId = eventId)
191- },
192- onFailure = {
193- focusRequestState.value = FocusRequestState .Failure (throwable = it)
194- }
195- )
205+ .onSuccess {
206+ focusRequestState = FocusRequestState .Success (eventId = eventId)
207+ }
208+ .onFailure {
209+ focusRequestState = FocusRequestState .Failure (it)
210+ }
196211 }
197212 else -> Unit
198213 }
@@ -202,30 +217,19 @@ class TimelinePresenter @AssistedInject constructor(
202217 computeNewItemState(timelineItems, prevMostRecentItemId, newEventState)
203218 }
204219
205- LaunchedEffect (timelineItems.size, focusRequestState.value ) {
206- val currentFocusRequestState = focusRequestState.value
207- if (currentFocusRequestState is FocusRequestState .Success && ! currentFocusRequestState.isIndexed ) {
220+ LaunchedEffect (timelineItems.size, focusRequestState) {
221+ val currentFocusRequestState = focusRequestState
222+ if (currentFocusRequestState is FocusRequestState .Success && ! currentFocusRequestState.rendered ) {
208223 val eventId = currentFocusRequestState.eventId
209224 if (timelineItemIndexer.isKnown(eventId)) {
210225 val index = timelineItemIndexer.indexOf(eventId)
211- focusRequestState.value = FocusRequestState .Success (eventId = eventId, index = index)
226+ focusRequestState = FocusRequestState .Success (eventId = eventId, index = index)
212227 }
213228 }
214229 }
215230
216- LaunchedEffect (Unit ) {
217- combine(timelineController.timelineItems(), room.membersStateFlow) { items, membersState ->
218- timelineItemsFactory.replaceWith(
219- timelineItems = items,
220- roomMembers = membersState.roomMembers().orEmpty()
221- )
222- items
223- }
224- .onEach(redactedVoiceMessageManager::onEachMatrixTimelineItem)
225- .launchIn(this )
226- }
227-
228- val timelineRoomInfo by remember {
231+ val typingNotificationState = typingNotificationPresenter.present()
232+ val timelineRoomInfo by remember(typingNotificationState) {
229233 derivedStateOf {
230234 TimelineRoomInfo (
231235 name = room.displayName,
@@ -234,6 +238,7 @@ class TimelinePresenter @AssistedInject constructor(
234238 userHasPermissionToSendReaction = userHasPermissionToSendReaction,
235239 isCallOngoing = roomInfo?.hasRoomCall.orFalse(),
236240 pinnedEventIds = roomInfo?.pinnedEventIds.orEmpty(),
241+ typingNotificationState = typingNotificationState,
237242 )
238243 }
239244 }
@@ -243,7 +248,7 @@ class TimelinePresenter @AssistedInject constructor(
243248 renderReadReceipts = renderReadReceipts,
244249 newEventState = newEventState.value,
245250 isLive = isLive,
246- focusRequestState = focusRequestState.value ,
251+ focusRequestState = focusRequestState,
247252 messageShield = messageShield.value,
248253 resolveVerifiedUserSendFailureState = resolveVerifiedUserSendFailureState,
249254 eventSink = { handleEvents(it) }
0 commit comments