@@ -88,6 +88,7 @@ class TimelinePresenter(
8888 private val markAsFullyRead : MarkAsFullyRead ,
8989 private val featureFlagService : FeatureFlagService ,
9090) : Presenter<TimelineState> {
91+ private val tag = " TimelinePresenter"
9192 @AssistedFactory
9293 interface Factory {
9394 fun create (
@@ -104,14 +105,14 @@ class TimelinePresenter(
104105 )
105106 private var timelineItems by mutableStateOf<ImmutableList <TimelineItem >>(persistentListOf())
106107
108+ private val focusRequestState: MutableState <FocusRequestState > = mutableStateOf(FocusRequestState .None )
109+
107110 @Composable
108111 override fun present (): TimelineState {
109112 val localScope = rememberCoroutineScope()
110113
111114 val timelineMode = remember { timelineController.mainTimelineMode() }
112115
113- var focusRequestState: FocusRequestState by remember { mutableStateOf(FocusRequestState .None ) }
114-
115116 val lastReadReceiptId = rememberSaveable { mutableStateOf<EventId ?>(null ) }
116117
117118 val roomInfo by room.roomInfoFlow.collectAsState()
@@ -157,7 +158,7 @@ class TimelinePresenter(
157158 if (event.firstIndex == 0 ) {
158159 newEventState.value = NewEventState .None
159160 }
160- Timber .d(" ## sendReadReceiptIfNeeded firstVisibleIndex: ${event.firstIndex} " )
161+ Timber .tag(tag). d(" ## sendReadReceiptIfNeeded firstVisibleIndex: ${event.firstIndex} " )
161162 sessionCoroutineScope.sendReadReceiptIfNeeded(
162163 firstVisibleIndex = event.firstIndex,
163164 timelineItems = timelineItems,
@@ -188,14 +189,17 @@ class TimelinePresenter(
188189 is TimelineEvents .EditPoll -> {
189190 navigator.onEditPollClick(event.pollStartId)
190191 }
191- is TimelineEvents .FocusOnEvent -> {
192- focusRequestState = FocusRequestState .Requested (event.eventId, event.debounce)
193- }
192+ is TimelineEvents .FocusOnEvent -> sessionCoroutineScope.launch {
193+ focusRequestState.value = FocusRequestState .Requested (event.eventId, event.debounce)
194+ delay(event.debounce)
195+ Timber .tag(tag).d(" Started focus on ${event.eventId} " )
196+ focusOnEvent(event.eventId, focusRequestState)
197+ }.start()
194198 is TimelineEvents .OnFocusEventRender -> {
195- focusRequestState = focusRequestState.onFocusEventRender()
199+ focusRequestState.value = focusRequestState.value .onFocusEventRender()
196200 }
197201 is TimelineEvents .ClearFocusRequestState -> {
198- focusRequestState = FocusRequestState .None
202+ focusRequestState.value = FocusRequestState .None
199203 }
200204 is TimelineEvents .JumpToLive -> {
201205 timelineController.focusOnLive()
@@ -244,69 +248,19 @@ class TimelinePresenter(
244248 .launchIn(this )
245249 }
246250
247- LaunchedEffect (focusRequestState) {
248- Timber .d(" ## focusRequestState: $focusRequestState " )
249- when (val currentFocusRequestState = focusRequestState) {
250- is FocusRequestState .Requested -> {
251- delay(currentFocusRequestState.debounce)
252- if (timelineItemIndexer.isKnown(currentFocusRequestState.eventId)) {
253- val index = timelineItemIndexer.indexOf(currentFocusRequestState.eventId)
254- focusRequestState = FocusRequestState .Success (eventId = currentFocusRequestState.eventId, index = index)
255- } else {
256- focusRequestState = FocusRequestState .Loading (eventId = currentFocusRequestState.eventId)
257- }
258- }
259- is FocusRequestState .Loading -> {
260- val eventId = currentFocusRequestState.eventId
261- val threadId = room.threadRootIdForEvent(eventId).getOrElse {
262- focusRequestState = FocusRequestState .Failure (it)
263- return @LaunchedEffect
264- }
265-
266- if (timelineController.mainTimelineMode() is Timeline .Mode .Thread && threadId == null ) {
267- // We are in a thread timeline, and the event isn't part of a thread, we need to navigate back to the room
268- focusRequestState = FocusRequestState .None
269- navigator.onNavigateToRoom(room.roomId, eventId, calculateServerNamesForRoom(room))
270- } else {
271- timelineController.focusOnEvent(eventId, threadId)
272- .onSuccess { result ->
273- when (result) {
274- is EventFocusResult .FocusedOnLive -> {
275- focusRequestState = FocusRequestState .Success (eventId = eventId)
276- }
277- is EventFocusResult .IsInThread -> {
278- val currentThreadId = (timelineController.mainTimelineMode() as ? Timeline .Mode .Thread )?.threadRootId
279- if (currentThreadId == result.threadId) {
280- // It's the same thread, we just focus on the event
281- focusRequestState = FocusRequestState .Success (eventId = eventId)
282- } else {
283- focusRequestState = FocusRequestState .Success (eventId = result.threadId.asEventId())
284- // It's part of a thread we're not in, let's open it in another timeline
285- navigator.onOpenThread(result.threadId, eventId)
286- }
287- }
288- }
289- }
290- .onFailure {
291- focusRequestState = FocusRequestState .Failure (it)
292- }
293- }
294- }
295- else -> Unit
296- }
297- }
298-
299251 LaunchedEffect (timelineItems.size) {
300252 computeNewItemState(timelineItems, prevMostRecentItemId, newEventState)
301253 }
302254
303- LaunchedEffect (timelineItems.size, focusRequestState) {
304- val currentFocusRequestState = focusRequestState
255+ LaunchedEffect (timelineItems.size, focusRequestState.value ) {
256+ val currentFocusRequestState = focusRequestState.value
305257 if (currentFocusRequestState is FocusRequestState .Success && ! currentFocusRequestState.rendered) {
306258 val eventId = currentFocusRequestState.eventId
307259 if (timelineItemIndexer.isKnown(eventId)) {
308260 val index = timelineItemIndexer.indexOf(eventId)
309- focusRequestState = FocusRequestState .Success (eventId = eventId, index = index)
261+ focusRequestState.value = FocusRequestState .Success (eventId = eventId, index = index)
262+ } else {
263+ Timber .w(" Unknown timeline item for focused item, can't render focus" )
310264 }
311265 }
312266 }
@@ -327,21 +281,75 @@ class TimelinePresenter(
327281 )
328282 }
329283 }
284+
285+ LaunchedEffect (focusRequestState.value) {
286+ Timber .tag(tag).d(" Timeline: $timelineMode | focus state: ${focusRequestState.value} " )
287+ }
288+
330289 return TimelineState (
331290 timelineItems = timelineItems,
332291 timelineMode = timelineMode,
333292 timelineRoomInfo = timelineRoomInfo,
334293 renderReadReceipts = renderReadReceipts,
335294 newEventState = newEventState.value,
336295 isLive = isLive,
337- focusRequestState = focusRequestState,
296+ focusRequestState = focusRequestState.value ,
338297 messageShield = messageShield.value,
339298 resolveVerifiedUserSendFailureState = resolveVerifiedUserSendFailureState,
340299 displayThreadSummaries = displayThreadSummaries,
341300 eventSink = { handleEvents(it) }
342301 )
343302 }
344303
304+ private suspend fun focusOnEvent (
305+ eventId : EventId ,
306+ focusRequestState : MutableState <FocusRequestState >,
307+ ) {
308+ if (timelineItemIndexer.isKnown(eventId)) {
309+ val index = timelineItemIndexer.indexOf(eventId)
310+ focusRequestState.value = FocusRequestState .Success (eventId = eventId, index = index)
311+ return
312+ }
313+
314+ Timber .tag(tag).d(" Event $eventId not found in the loaded timeline, loading a focused timeline" )
315+ focusRequestState.value = FocusRequestState .Loading (eventId = eventId)
316+
317+ val threadId = room.threadRootIdForEvent(eventId).getOrElse {
318+ focusRequestState.value = FocusRequestState .Failure (it)
319+ return
320+ }
321+
322+ if (timelineController.mainTimelineMode() is Timeline .Mode .Thread && threadId == null ) {
323+ // We are in a thread timeline, and the event isn't part of a thread, we need to navigate back to the room
324+ focusRequestState.value = FocusRequestState .None
325+ navigator.onNavigateToRoom(room.roomId, eventId, calculateServerNamesForRoom(room))
326+ } else {
327+ Timber .tag(tag).d(" Focusing on event $eventId - thread $threadId " )
328+ timelineController.focusOnEvent(eventId, threadId)
329+ .onSuccess { result ->
330+ when (result) {
331+ is EventFocusResult .FocusedOnLive -> {
332+ focusRequestState.value = FocusRequestState .Success (eventId = eventId)
333+ }
334+ is EventFocusResult .IsInThread -> {
335+ val currentThreadId = (timelineController.mainTimelineMode() as ? Timeline .Mode .Thread )?.threadRootId
336+ if (currentThreadId == result.threadId) {
337+ // It's the same thread, we just focus on the event
338+ focusRequestState.value = FocusRequestState .Success (eventId = eventId)
339+ } else {
340+ focusRequestState.value = FocusRequestState .Success (eventId = result.threadId.asEventId())
341+ // It's part of a thread we're not in, let's open it in another timeline
342+ navigator.onOpenThread(result.threadId, eventId)
343+ }
344+ }
345+ }
346+ }
347+ .onFailure {
348+ focusRequestState.value = FocusRequestState .Failure (it)
349+ }
350+ }
351+ }
352+
345353 /* *
346354 * This method compute the hasNewItem state passed as a [MutableState] each time the timeline items size changes.
347355 * Basically, if we got new timeline event from sync or local, either from us or another user, we update the state so we tell we have new items.
0 commit comments