Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 8211505e authored by Rafael Tonholo's avatar Rafael Tonholo
Browse files

fix(message-list): message list skipping first item and load more footer also doesn't show up

parent 3394a487
Loading
Loading
Loading
Loading
+78 −68
Original line number Diff line number Diff line
@@ -11,7 +11,6 @@ import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.NO_POSITION
import app.k9mail.feature.launcher.FeatureLauncherActivity
import app.k9mail.feature.launcher.FeatureLauncherTarget
import app.k9mail.legacy.message.controller.MessageReference
@@ -31,6 +30,7 @@ import net.thunderbird.core.ui.theme.api.FeatureThemeProvider
import net.thunderbird.feature.notification.api.ui.action.NotificationAction

private const val FOOTER_ID = 1L
private const val IN_APP_NOTIFICATION_BANNER_INLINE_LIST_ID = -1L

private const val TYPE_MESSAGE = 0
private const val TYPE_FOOTER = 1
@@ -51,14 +51,15 @@ class MessageListAdapter internal constructor(

    val colors: MessageViewHolderColors = MessageViewHolderColors.resolveColors(theme)

    var messages: List<MessageListItem> = emptyList()
    var viewItems: List<MessageListViewItem> = emptyList()
        @SuppressLint("NotifyDataSetChanged")
        set(value) {
            val oldMessageList = field

            field = value
            accountUuids = value.map { it.account.uuid }.toSet()
            messagesMap = value.associateBy { it.uniqueId }
            val messages = value.filterMessageListItem()
            accountUuids = messages.map { it.account.uuid }.toSet()
            messagesMap = messages.associateBy { it.uniqueId }

            if (selected.isNotEmpty()) {
                val uniqueIds = messagesMap.keys
@@ -71,6 +72,7 @@ class MessageListAdapter internal constructor(
            diffResult.dispatchUpdatesTo(this)
        }

    private val messages get() = viewItems.filterMessageListItem()
    private var messagesMap = emptyMap<Long, MessageListItem>()
    private var accountUuids = emptySet<String>()

@@ -122,34 +124,6 @@ class MessageListAdapter internal constructor(
    var selectedCount: Int = 0
        private set

    var footerText: String? = null
        set(value) {
            if (field == value) return

            val hadFooterText = field != null
            val previousFooterPosition = footerPosition
            field = value

            if (hadFooterText) {
                if (value == null) {
                    notifyItemRemoved(previousFooterPosition)
                } else {
                    notifyItemChanged(footerPosition)
                }
            } else {
                notifyItemInserted(footerPosition)
            }
        }

    private val hasFooter: Boolean
        get() = footerText != null

    private val lastMessagePosition: Int
        get() = messages.lastIndex

    private val footerPosition: Int
        get() = if (hasFooter) lastMessagePosition + 1 else NO_POSITION

    private val messageClickedListener = OnClickListener { view: View ->
        val messageListItem = getItemFromView(view) ?: return@OnClickListener
        listItemListener.onMessageClicked(messageListItem)
@@ -185,32 +159,26 @@ class MessageListAdapter internal constructor(
        setHasStableIds(true)
    }

    override fun getItemCount(): Int = messages.size + if (hasFooter) 1 else 0
    override fun getItemCount(): Int = viewItems.size

    override fun getItemId(position: Int): Long {
        return if (position <= lastMessagePosition) {
            messages[position].uniqueId
        } else {
            FOOTER_ID
        }
        return viewItems[position].viewId
    }

    override fun getItemViewType(position: Int): Int {
        return when {
            position == 0 && isInAppNotificationEnabled -> TYPE_IN_APP_NOTIFICATION_BANNER_INLINE_LIST
            position <= lastMessagePosition -> TYPE_MESSAGE
            else -> TYPE_FOOTER
        }
        return viewItems[position].viewType
    }

    private fun getItem(position: Int): MessageListItem = messages[position]
    private fun getItem(position: Int): MessageListItem = (viewItems[position] as MessageListViewItem.Message).item

    fun getItemById(uniqueId: Long): MessageListItem? {
        return messagesMap[uniqueId]
    }

    fun getItem(messageReference: MessageReference): MessageListItem? {
        return messages.firstOrNull {
        return viewItems
            .filterMessageListItem()
            .firstOrNull {
                it.account.uuid == messageReference.accountUuid &&
                    it.folderId == messageReference.folderId &&
                    it.messageUid == messageReference.uid
@@ -218,15 +186,21 @@ class MessageListAdapter internal constructor(
    }

    fun getPosition(messageListItem: MessageListItem): Int? {
        return messages.indexOf(messageListItem).takeIf { it != -1 }
        return viewItems
            .map { (it as? MessageListViewItem.Message)?.item }
            .indexOf(messageListItem).takeIf { it != -1 }
    }

    private fun getPosition(messageReference: MessageReference?): Int? {
        if (messageReference == null) return null

        return messages.indexOfFirst {
        return viewItems
            .map { (it as? MessageListViewItem.Message)?.item }
            .indexOfFirst {
                it != null &&
                    messageReference.equals(it.account.uuid, it.folderId, it.messageUid)
        }.takeIf { it != -1 }
            }
            .takeIf { it != -1 }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageListViewHolder {
@@ -241,6 +215,7 @@ class MessageListAdapter internal constructor(
            }

            TYPE_FOOTER -> FooterViewHolder.create(layoutInflater, parent, footerClickListener)

            TYPE_IN_APP_NOTIFICATION_BANNER_INLINE_LIST if isInAppNotificationEnabled ->
                BannerInlineListInAppNotificationViewHolder(
                    view = ComposeView(context = parent.context),
@@ -325,7 +300,8 @@ class MessageListAdapter internal constructor(

            TYPE_FOOTER -> {
                val footerViewHolder = holder as FooterViewHolder
                footerViewHolder.bind(footerText)
                val footer = viewItems[position] as MessageListViewItem.Footer
                footerViewHolder.bind(footer.text)
            }

            else -> {
@@ -386,19 +362,18 @@ class MessageListAdapter internal constructor(
    }

    private fun calculateSelectionCount(): Int {
        if (selected.isEmpty()) {
            return 0
        }

        if (!appearance.showingThreadedList) {
            return selected.size
        }

        return messages
        return when {
            selected.isEmpty() -> 0
            !appearance.showingThreadedList -> selected.size
            else ->
                viewItems
                    .asSequence()
                    .filterIsInstance<MessageListViewItem.Message>()
                    .map { it.item }
                    .filter { it.uniqueId in selected }
                    .sumOf { it.threadCount.coerceAtLeast(1) }
        }
    }

    private fun getItemFromView(view: View): MessageListItem? {
        if (featureFlagProvider.provide(UseComposeForMessageListItems).isEnabled()) {
@@ -409,18 +384,33 @@ class MessageListAdapter internal constructor(
            return getItemById(messageViewHolder.uniqueId)
        }
    }

    private fun List<MessageListViewItem>.filterMessageListItem(): List<MessageListItem> =
        filterIsInstance<MessageListViewItem.Message>()
            .map { it.item }
}

private class MessageListDiffCallback(
    private val oldMessageList: List<MessageListItem>,
    private val newMessageList: List<MessageListItem>,
    private val oldMessageList: List<MessageListViewItem>,
    private val newMessageList: List<MessageListViewItem>,
) : DiffUtil.Callback() {
    override fun getOldListSize(): Int = oldMessageList.size

    override fun getNewListSize(): Int = newMessageList.size

    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldMessageList[oldItemPosition].uniqueId == newMessageList[newItemPosition].uniqueId
        val oldItem = oldMessageList[oldItemPosition]
        val newItem = newMessageList[newItemPosition]
        return when (oldItem) {
            is MessageListViewItem.InAppNotificationBannerList
            if newItem is MessageListViewItem.InAppNotificationBannerList -> true

            is MessageListViewItem.Message
            if newItem is MessageListViewItem.Message -> oldItem.item.uniqueId == newItem.item.uniqueId

            is MessageListViewItem.Footer if newItem is MessageListViewItem.Footer -> true
            else -> false
        }
    }

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
@@ -434,3 +424,23 @@ interface MessageListItemActionListener {
    fun onToggleMessageFlag(item: MessageListItem)
    fun onFooterClicked()
}

sealed interface MessageListViewItem {
    val viewId: Long
    val viewType: Int

    data object InAppNotificationBannerList : MessageListViewItem {
        override val viewId: Long = IN_APP_NOTIFICATION_BANNER_INLINE_LIST_ID
        override val viewType: Int = TYPE_IN_APP_NOTIFICATION_BANNER_INLINE_LIST
    }

    data class Message(val item: MessageListItem) : MessageListViewItem {
        override val viewId: Long get() = item.uniqueId
        override val viewType: Int = TYPE_MESSAGE
    }

    data class Footer(val text: String) : MessageListViewItem {
        override val viewId: Long = FOOTER_ID
        override val viewType: Int = TYPE_FOOTER
    }
}
+20 −3
Original line number Diff line number Diff line
@@ -1131,11 +1131,20 @@ class MessageListFragment :
    }

    fun updateFooterText(text: String?) {
        adapter.footerText = text
        val currentItems = adapter
            .viewItems
            .filter { it !is MessageListViewItem.Footer }
            .toMutableList()

        if (!text.isNullOrEmpty()) {
            currentItems.add(MessageListViewItem.Footer(text))
        }

        adapter.viewItems = currentItems
    }

    private fun selectAll() {
        if (adapter.messages.isEmpty()) {
        if (adapter.viewItems.isEmpty()) {
            // Nothing to do if there are no messages
            return
        }
@@ -1452,6 +1461,7 @@ class MessageListFragment :
                        destinationFolderId,
                    )
                }

                FolderOperation.MOVE -> {
                    messagingController.moveMessages(
                        account.id,
@@ -1460,6 +1470,7 @@ class MessageListFragment :
                        destinationFolderId,
                    )
                }

                FolderOperation.COPY if showingThreadedList -> {
                    messagingController.copyMessagesInThread(
                        account.id,
@@ -1468,6 +1479,7 @@ class MessageListFragment :
                        destinationFolderId,
                    )
                }

                FolderOperation.COPY -> {
                    messagingController.copyMessages(
                        account.id,
@@ -1735,7 +1747,12 @@ class MessageListFragment :
            }
        }

        adapter.messages = messageListItems
        adapter.viewItems = buildList {
            if (featureFlagProvider.provide(FeatureFlagKey.DisplayInAppNotifications).isEnabled()) {
                add(MessageListViewItem.InAppNotificationBannerList)
            }
            addAll(messageListItems.map { MessageListViewItem.Message(it) })
        }

        rememberedSelected?.let {
            rememberedSelected = null
+1 −1
Original line number Diff line number Diff line
@@ -531,7 +531,7 @@ class MessageListAdapterTest : RobolectricTest() {
    }

    fun MessageListAdapter.createAndBindView(item: MessageListItem = createMessageListItem()): View {
        messages = listOf(item)
        viewItems = listOf(MessageListViewItem.Message(item))
        val holder = onCreateViewHolder(LinearLayout(context), 0)
        onBindViewHolder(holder, 0)
        return holder.itemView