/* * Copyright 2019 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.riotredesign.features.home.room.detail.timeline import android.view.View import androidx.recyclerview.widget.RecyclerView import com.airbnb.epoxy.EpoxyAsyncUtil import com.airbnb.epoxy.EpoxyModel import com.airbnb.epoxy.OnModelVisibilityStateChangedListener import com.airbnb.epoxy.VisibilityState import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.timeline.TimelineData import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.riotredesign.core.epoxy.KotlinModel import im.vector.riotredesign.core.extensions.localDateTime import im.vector.riotredesign.features.home.LoadingItemModel_ import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.riotredesign.features.home.room.detail.timeline.paging.PagedListEpoxyController class TimelineEventController(private val roomId: String, private val dateFormatter: TimelineDateFormatter, private val timelineItemFactory: TimelineItemFactory, private val timelineMediaSizeProvider: TimelineMediaSizeProvider ) : PagedListEpoxyController( EpoxyAsyncUtil.getAsyncBackgroundHandler(), EpoxyAsyncUtil.getAsyncBackgroundHandler() ) { init { setFilterDuplicates(true) } private var isLoadingForward: Boolean = false private var isLoadingBackward: Boolean = false private var hasReachedEnd: Boolean = false var callback: Callback? = null fun update(timelineData: TimelineData?) { timelineData?.let { isLoadingForward = it.isLoadingForward isLoadingBackward = it.isLoadingBackward hasReachedEnd = it.events.lastOrNull()?.root?.type == EventType.STATE_ROOM_CREATE submitList(it.events) requestModelBuild() } } override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { super.onAttachedToRecyclerView(recyclerView) timelineMediaSizeProvider.recyclerView = recyclerView } override fun buildItemModels(currentPosition: Int, items: List): List> { if (items.isNullOrEmpty()) { return emptyList() } val epoxyModels = ArrayList>() val event = items[currentPosition] ?: return emptyList() val nextEvent = if (currentPosition + 1 < items.size) items[currentPosition + 1] else null val date = event.root.localDateTime() val nextDate = nextEvent?.root?.localDateTime() val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate() timelineItemFactory.create(event, nextEvent, callback)?.also { it.id(event.localId) it.setOnVisibilityStateChanged(OnModelVisibilityStateChangedListener { model, view, visibilityState -> if (visibilityState == VisibilityState.VISIBLE) { callback?.onEventVisible(event, currentPosition) } }) epoxyModels.add(it) } if (addDaySeparator) { val formattedDay = dateFormatter.formatMessageDay(date) val daySeparatorItem = DaySeparatorItem(formattedDay).id(roomId + formattedDay) epoxyModels.add(daySeparatorItem) } return epoxyModels } override fun addModels(models: List>) { LoadingItemModel_() .id(roomId + "forward_loading_item") .addIf(isLoadingForward, this) super.add(models) LoadingItemModel_() .id(roomId + "backward_loading_item") .addIf(!hasReachedEnd, this) } interface Callback { fun onEventVisible(event: TimelineEvent, index: Int) fun onUrlClicked(url: String) } }