mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-01-31 19:42:44 +01:00
Add messages sorting (#1262)
This commit is contained in:
parent
d73aa605f9
commit
b61e63249c
@ -4,6 +4,8 @@ import android.graphics.Typeface
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.CompoundButton
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.recyclerview.widget.RecyclerView.NO_POSITION
|
import androidx.recyclerview.widget.RecyclerView.NO_POSITION
|
||||||
@ -11,60 +13,114 @@ import io.github.wulkanowy.R
|
|||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
import io.github.wulkanowy.data.enums.MessageFolder
|
import io.github.wulkanowy.data.enums.MessageFolder
|
||||||
import io.github.wulkanowy.databinding.ItemMessageBinding
|
import io.github.wulkanowy.databinding.ItemMessageBinding
|
||||||
|
import io.github.wulkanowy.databinding.ItemMessageChipsBinding
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class MessageTabAdapter @Inject constructor() :
|
class MessageTabAdapter @Inject constructor() :
|
||||||
RecyclerView.Adapter<MessageTabAdapter.ItemViewHolder>() {
|
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
var onClickListener: (Message, position: Int) -> Unit = { _, _ -> }
|
enum class ViewType { HEADER, ITEM }
|
||||||
|
|
||||||
|
var onItemClickListener: (Message, position: Int) -> Unit = { _, _ -> }
|
||||||
|
var onHeaderClickListener: (chip: CompoundButton, isChecked: Boolean) -> Unit = { _, _ -> }
|
||||||
|
|
||||||
var onChangesDetectedListener = {}
|
var onChangesDetectedListener = {}
|
||||||
|
|
||||||
private var items = mutableListOf<Message>()
|
private var items = mutableListOf<MessageTabDataItem>()
|
||||||
|
private var onlyUnread: Boolean? = null
|
||||||
|
private var onlyWithAttachments = false
|
||||||
|
|
||||||
fun setDataItems(data: List<Message>) {
|
fun setDataItems(
|
||||||
|
data: List<MessageTabDataItem>,
|
||||||
|
onlyUnread: Boolean?,
|
||||||
|
onlyWithAttachments: Boolean
|
||||||
|
) {
|
||||||
if (items.size != data.size) onChangesDetectedListener()
|
if (items.size != data.size) onChangesDetectedListener()
|
||||||
val diffResult = DiffUtil.calculateDiff(MessageTabDiffUtil(items, data))
|
val diffResult = DiffUtil.calculateDiff(MessageTabDiffUtil(items, data))
|
||||||
items = data.toMutableList()
|
items = data.toMutableList()
|
||||||
|
this.onlyUnread = onlyUnread
|
||||||
|
this.onlyWithAttachments = onlyWithAttachments
|
||||||
diffResult.dispatchUpdatesTo(this)
|
diffResult.dispatchUpdatesTo(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return when (position) {
|
||||||
|
0 -> ViewType.HEADER.ordinal
|
||||||
|
else -> ViewType.ITEM.ordinal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getItemCount() = items.size
|
override fun getItemCount() = items.size
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
ItemMessageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
)
|
return when (viewType) {
|
||||||
|
ViewType.ITEM.ordinal -> ItemViewHolder(
|
||||||
|
ItemMessageBinding.inflate(inflater, parent, false)
|
||||||
|
)
|
||||||
|
ViewType.HEADER.ordinal -> HeaderViewHolder(
|
||||||
|
ItemMessageChipsBinding.inflate(inflater, parent, false)
|
||||||
|
)
|
||||||
|
else -> throw IllegalStateException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
val item = items[position]
|
when (holder) {
|
||||||
|
is ItemViewHolder -> {
|
||||||
|
val item = (items[position] as MessageTabDataItem.MessageItem).message
|
||||||
|
|
||||||
with(holder.binding) {
|
with(holder.binding) {
|
||||||
val style = if (item.unread) Typeface.BOLD else Typeface.NORMAL
|
val style = if (item.unread) Typeface.BOLD else Typeface.NORMAL
|
||||||
|
|
||||||
messageItemAuthor.run {
|
messageItemAuthor.run {
|
||||||
text = if (item.folderId == MessageFolder.SENT.id) item.recipient else item.sender
|
text =
|
||||||
setTypeface(null, style)
|
if (item.folderId == MessageFolder.SENT.id) item.recipient else item.sender
|
||||||
|
setTypeface(null, style)
|
||||||
|
}
|
||||||
|
messageItemSubject.run {
|
||||||
|
text =
|
||||||
|
if (item.subject.isNotBlank()) item.subject else context.getString(R.string.message_no_subject)
|
||||||
|
setTypeface(null, style)
|
||||||
|
}
|
||||||
|
messageItemDate.run {
|
||||||
|
text = item.date.toFormattedString()
|
||||||
|
setTypeface(null, style)
|
||||||
|
}
|
||||||
|
messageItemAttachmentIcon.visibility =
|
||||||
|
if (item.hasAttachments) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
|
root.setOnClickListener {
|
||||||
|
holder.bindingAdapterPosition.let {
|
||||||
|
if (it != NO_POSITION) onItemClickListener(item, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
messageItemSubject.run {
|
is HeaderViewHolder -> {
|
||||||
text = if (item.subject.isNotBlank()) item.subject else context.getString(R.string.message_no_subject)
|
with(holder.binding) {
|
||||||
setTypeface(null, style)
|
if (onlyUnread == null) chipUnread.isVisible = false
|
||||||
}
|
else {
|
||||||
messageItemDate.run {
|
chipUnread.isVisible = true
|
||||||
text = item.date.toFormattedString()
|
chipUnread.isChecked = onlyUnread!!
|
||||||
setTypeface(null, style)
|
chipUnread.setOnCheckedChangeListener(onHeaderClickListener)
|
||||||
}
|
}
|
||||||
messageItemAttachmentIcon.visibility = if (item.hasAttachments) View.VISIBLE else View.GONE
|
chipAttachments.isChecked = onlyWithAttachments
|
||||||
|
chipAttachments.setOnCheckedChangeListener(onHeaderClickListener)
|
||||||
root.setOnClickListener {
|
}
|
||||||
holder.bindingAdapterPosition.let { if (it != NO_POSITION) onClickListener(item, it) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ItemViewHolder(val binding: ItemMessageBinding) : RecyclerView.ViewHolder(binding.root)
|
class ItemViewHolder(val binding: ItemMessageBinding) : RecyclerView.ViewHolder(binding.root)
|
||||||
|
class HeaderViewHolder(val binding: ItemMessageChipsBinding) :
|
||||||
|
RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
private class MessageTabDiffUtil(private val old: List<Message>, private val new: List<Message>) :
|
private class MessageTabDiffUtil(
|
||||||
|
private val old: List<MessageTabDataItem>,
|
||||||
|
private val new: List<MessageTabDataItem>
|
||||||
|
) :
|
||||||
DiffUtil.Callback() {
|
DiffUtil.Callback() {
|
||||||
override fun getOldListSize(): Int = old.size
|
override fun getOldListSize(): Int = old.size
|
||||||
|
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.message.tab
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
|
|
||||||
|
sealed class MessageTabDataItem {
|
||||||
|
data class MessageItem(val message: Message) : MessageTabDataItem() {
|
||||||
|
override val id = message.id
|
||||||
|
}
|
||||||
|
|
||||||
|
object Header : MessageTabDataItem() {
|
||||||
|
override val id = Long.MIN_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract val id: Long
|
||||||
|
}
|
@ -7,6 +7,7 @@ import android.view.View
|
|||||||
import android.view.View.GONE
|
import android.view.View.GONE
|
||||||
import android.view.View.INVISIBLE
|
import android.view.View.INVISIBLE
|
||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
|
import android.widget.CompoundButton
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
@ -48,6 +49,10 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
|||||||
override val isViewEmpty
|
override val isViewEmpty
|
||||||
get() = tabAdapter.itemCount == 0
|
get() = tabAdapter.itemCount == 0
|
||||||
|
|
||||||
|
override var onlyUnread: Boolean? = false
|
||||||
|
|
||||||
|
override var onlyWithAttachments = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
@ -58,26 +63,33 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding = FragmentMessageTabBinding.bind(view)
|
binding = FragmentMessageTabBinding.bind(view)
|
||||||
messageContainer = binding.messageTabRecycler
|
messageContainer = binding.messageTabRecycler
|
||||||
presenter.onAttachView(this, MessageFolder.valueOf(
|
|
||||||
(savedInstanceState ?: arguments)?.getString(MESSAGE_TAB_FOLDER_ID).orEmpty()
|
val folder = MessageFolder.valueOf(
|
||||||
))
|
(savedInstanceState ?: requireArguments()).getString(MESSAGE_TAB_FOLDER_ID).orEmpty()
|
||||||
|
)
|
||||||
|
presenter.onAttachView(this, folder)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
with(tabAdapter) {
|
with(tabAdapter) {
|
||||||
onClickListener = presenter::onMessageItemSelected
|
onItemClickListener = presenter::onMessageItemSelected
|
||||||
|
onHeaderClickListener = ::onChipChecked
|
||||||
onChangesDetectedListener = ::resetListPosition
|
onChangesDetectedListener = ::resetListPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
with(binding.messageTabRecycler) {
|
with(binding.messageTabRecycler) {
|
||||||
layoutManager = LinearLayoutManager(context)
|
layoutManager = LinearLayoutManager(context)
|
||||||
adapter = tabAdapter
|
adapter = tabAdapter
|
||||||
addItemDecoration(DividerItemDecoration(context))
|
addItemDecoration(DividerItemDecoration(context, false))
|
||||||
}
|
}
|
||||||
with(binding) {
|
with(binding) {
|
||||||
messageTabSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
messageTabSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||||
messageTabSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
messageTabSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||||
messageTabSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
messageTabSwipe.setProgressBackgroundColorSchemeColor(
|
||||||
|
requireContext().getThemeAttrColor(
|
||||||
|
R.attr.colorSwipeRefresh
|
||||||
|
)
|
||||||
|
)
|
||||||
messageTabErrorRetry.setOnClickListener { presenter.onRetry() }
|
messageTabErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
}
|
}
|
||||||
@ -99,8 +111,9 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateData(data: List<Message>) {
|
override fun updateData(data: List<MessageTabDataItem>, hide: Boolean) {
|
||||||
tabAdapter.setDataItems(data)
|
if (hide) onlyUnread = null
|
||||||
|
tabAdapter.setDataItems(data, onlyUnread, onlyWithAttachments)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showProgress(show: Boolean) {
|
override fun showProgress(show: Boolean) {
|
||||||
@ -143,8 +156,19 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
|||||||
(parentFragment as? MessageFragment)?.onChildFragmentLoaded()
|
(parentFragment as? MessageFragment)?.onChildFragmentLoaded()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onParentLoadData(forceRefresh: Boolean) {
|
fun onParentLoadData(
|
||||||
presenter.onParentViewLoadData(forceRefresh)
|
forceRefresh: Boolean,
|
||||||
|
onlyUnread: Boolean? = this.onlyUnread,
|
||||||
|
onlyWithAttachments: Boolean = this.onlyWithAttachments
|
||||||
|
) {
|
||||||
|
presenter.onParentViewLoadData(forceRefresh, onlyUnread, onlyWithAttachments)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onChipChecked(chip: CompoundButton, isChecked: Boolean) {
|
||||||
|
when (chip.id) {
|
||||||
|
R.id.chip_unread -> presenter.onUnreadFilterSelected(isChecked)
|
||||||
|
R.id.chip_attachments -> presenter.onAttachmentsFilterSelected(isChecked)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onParentDeleteMessage() {
|
fun onParentDeleteMessage() {
|
||||||
|
@ -55,15 +55,15 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
|
|
||||||
fun onSwipeRefresh() {
|
fun onSwipeRefresh() {
|
||||||
Timber.i("Force refreshing the $folder message")
|
Timber.i("Force refreshing the $folder message")
|
||||||
onParentViewLoadData(true)
|
view?.run { onParentViewLoadData(true, onlyUnread, onlyWithAttachments) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onRetry() {
|
fun onRetry() {
|
||||||
view?.run {
|
view?.run {
|
||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
showProgress(true)
|
showProgress(true)
|
||||||
|
loadData(true, onlyUnread == true, onlyWithAttachments)
|
||||||
}
|
}
|
||||||
loadData(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onDetailsClick() {
|
fun onDetailsClick() {
|
||||||
@ -71,11 +71,15 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onDeleteMessage() {
|
fun onDeleteMessage() {
|
||||||
loadData(true)
|
view?.run { loadData(true, onlyUnread == true, onlyWithAttachments) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onParentViewLoadData(forceRefresh: Boolean) {
|
fun onParentViewLoadData(
|
||||||
loadData(forceRefresh)
|
forceRefresh: Boolean,
|
||||||
|
onlyUnread: Boolean? = view?.onlyUnread,
|
||||||
|
onlyWithAttachments: Boolean = view?.onlyWithAttachments == true
|
||||||
|
) {
|
||||||
|
loadData(forceRefresh, onlyUnread == true, onlyWithAttachments)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onMessageItemSelected(message: Message, position: Int) {
|
fun onMessageItemSelected(message: Message, position: Int) {
|
||||||
@ -83,7 +87,25 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
view?.openMessage(message)
|
view?.openMessage(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadData(forceRefresh: Boolean) {
|
fun onUnreadFilterSelected(isChecked: Boolean) {
|
||||||
|
view?.run {
|
||||||
|
onlyUnread = isChecked
|
||||||
|
onParentViewLoadData(false, onlyUnread, onlyWithAttachments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onAttachmentsFilterSelected(isChecked: Boolean) {
|
||||||
|
view?.run {
|
||||||
|
onlyWithAttachments = isChecked
|
||||||
|
onParentViewLoadData(false, onlyUnread, onlyWithAttachments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadData(
|
||||||
|
forceRefresh: Boolean,
|
||||||
|
onlyUnread: Boolean,
|
||||||
|
onlyWithAttachments: Boolean
|
||||||
|
) {
|
||||||
Timber.i("Loading $folder message data started")
|
Timber.i("Loading $folder message data started")
|
||||||
|
|
||||||
flowWithResourceIn {
|
flowWithResourceIn {
|
||||||
@ -100,7 +122,15 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
showProgress(false)
|
showProgress(false)
|
||||||
showContent(true)
|
showContent(true)
|
||||||
messages = it.data
|
messages = it.data
|
||||||
updateData(getFilteredData(lastSearchQuery))
|
val filteredData = getFilteredData(
|
||||||
|
lastSearchQuery,
|
||||||
|
onlyUnread,
|
||||||
|
onlyWithAttachments
|
||||||
|
)
|
||||||
|
val newItems = listOf(MessageTabDataItem.Header) + filteredData.map {
|
||||||
|
MessageTabDataItem.MessageItem(it)
|
||||||
|
}
|
||||||
|
updateData(newItems, folder.id == MessageFolder.SENT.id)
|
||||||
notifyParentDataLoaded()
|
notifyParentDataLoaded()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,7 +138,7 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
Status.SUCCESS -> {
|
Status.SUCCESS -> {
|
||||||
Timber.i("Loading $folder message result: Success")
|
Timber.i("Loading $folder message result: Success")
|
||||||
messages = it.data!!
|
messages = it.data!!
|
||||||
updateData(getFilteredData(lastSearchQuery))
|
updateData(getFilteredData(lastSearchQuery, onlyUnread, onlyWithAttachments))
|
||||||
analytics.logEvent(
|
analytics.logEvent(
|
||||||
"load_data",
|
"load_data",
|
||||||
"type" to "messages",
|
"type" to "messages",
|
||||||
@ -166,24 +196,42 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFilteredData(query: String): List<Message> {
|
private fun getFilteredData(
|
||||||
return if (query.trim().isEmpty()) {
|
query: String,
|
||||||
messages.sortedByDescending { it.date }
|
onlyUnread: Boolean = false,
|
||||||
|
onlyWithAttachments: Boolean = false
|
||||||
|
): List<Message> {
|
||||||
|
if (query.trim().isEmpty()) {
|
||||||
|
val sortedMessages = messages.sortedByDescending { it.date }
|
||||||
|
return when {
|
||||||
|
onlyUnread && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments }
|
||||||
|
onlyUnread -> sortedMessages.filter { it.unread == onlyUnread }
|
||||||
|
onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments }
|
||||||
|
else -> sortedMessages
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
messages
|
val sortedMessages = messages
|
||||||
.map { it to calculateMatchRatio(it, query) }
|
.map { it to calculateMatchRatio(it, query) }
|
||||||
.sortedByDescending { it.second }
|
.sortedWith(compareBy<Pair<Message, Int>> { -it.second }.thenByDescending { it.first.date })
|
||||||
.filter { it.second > 5000 }
|
.filter { it.second > 6000 }
|
||||||
.map { it.first }
|
.map { it.first }
|
||||||
|
return when {
|
||||||
|
onlyUnread && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments }
|
||||||
|
onlyUnread -> sortedMessages.filter { it.unread == onlyUnread }
|
||||||
|
onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments }
|
||||||
|
else -> sortedMessages
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateData(data: List<Message>) {
|
private fun updateData(data: List<Message>) {
|
||||||
view?.run {
|
view?.run {
|
||||||
showEmpty(data.isEmpty())
|
showEmpty(data.isEmpty())
|
||||||
showContent(data.isNotEmpty())
|
showContent(true)
|
||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
updateData(data)
|
val newItems =
|
||||||
|
listOf(MessageTabDataItem.Header) + data.map { MessageTabDataItem.MessageItem(it) }
|
||||||
|
updateData(newItems, folder.id == MessageFolder.SENT.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,14 +252,6 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
FuzzySearch.ratio(
|
FuzzySearch.ratio(
|
||||||
query.lowercase(),
|
query.lowercase(),
|
||||||
message.date.toFormattedString("dd.MM.yyyy").lowercase()
|
message.date.toFormattedString("dd.MM.yyyy").lowercase()
|
||||||
),
|
|
||||||
FuzzySearch.ratio(
|
|
||||||
query.lowercase(),
|
|
||||||
message.date.toFormattedString("d MMMM").lowercase()
|
|
||||||
),
|
|
||||||
FuzzySearch.ratio(
|
|
||||||
query.lowercase(),
|
|
||||||
message.date.toFormattedString("d MMMM yyyy").lowercase()
|
|
||||||
)
|
)
|
||||||
).maxOrNull() ?: 0
|
).maxOrNull() ?: 0
|
||||||
|
|
||||||
|
@ -7,11 +7,15 @@ interface MessageTabView : BaseView {
|
|||||||
|
|
||||||
val isViewEmpty: Boolean
|
val isViewEmpty: Boolean
|
||||||
|
|
||||||
|
var onlyUnread: Boolean?
|
||||||
|
|
||||||
|
var onlyWithAttachments: Boolean
|
||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
|
||||||
fun resetListPosition()
|
fun resetListPosition()
|
||||||
|
|
||||||
fun updateData(data: List<Message>)
|
fun updateData(data: List<MessageTabDataItem>, hide: Boolean)
|
||||||
|
|
||||||
fun showProgress(show: Boolean)
|
fun showProgress(show: Boolean)
|
||||||
|
|
||||||
|
@ -5,7 +5,10 @@ import android.graphics.Canvas
|
|||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
class DividerItemDecoration(context: Context) : DividerItemDecoration(context, VERTICAL) {
|
class DividerItemDecoration(
|
||||||
|
context: Context,
|
||||||
|
private val showDividerWithFirstItem: Boolean = true
|
||||||
|
) : DividerItemDecoration(context, VERTICAL) {
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||||
canvas.save()
|
canvas.save()
|
||||||
@ -13,6 +16,8 @@ class DividerItemDecoration(context: Context) : DividerItemDecoration(context, V
|
|||||||
val dividerRight = parent.width - parent.paddingRight
|
val dividerRight = parent.width - parent.paddingRight
|
||||||
|
|
||||||
for (i in 0..parent.childCount - 2) {
|
for (i in 0..parent.childCount - 2) {
|
||||||
|
if (!showDividerWithFirstItem && i == 0) continue
|
||||||
|
|
||||||
val child = parent.getChildAt(i)
|
val child = parent.getChildAt(i)
|
||||||
val params = child.layoutParams as RecyclerView.LayoutParams
|
val params = child.layoutParams as RecyclerView.LayoutParams
|
||||||
val dividerTop = child.bottom + params.bottomMargin
|
val dividerTop = child.bottom + params.bottomMargin
|
||||||
|
40
app/src/main/res/layout/item_message_chips.xml
Normal file
40
app/src/main/res/layout/item_message_chips.xml
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/messageChipsLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
tools:context=".ui.modules.message.tab.MessageTabAdapter">
|
||||||
|
|
||||||
|
<com.google.android.material.chip.ChipGroup
|
||||||
|
android:id="@+id/messageChipGroup"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/chip_unread"
|
||||||
|
style="@style/Widget.MaterialComponents.Chip.Choice"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/message_chip_only_unread"
|
||||||
|
app:checkedIcon="@drawable/ic_mtrl_chip_checked_black"
|
||||||
|
app:checkedIconEnabled="true"
|
||||||
|
app:checkedIconTint="@color/mtrl_choice_chip_text_color" />
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/chip_attachments"
|
||||||
|
style="@style/Widget.MaterialComponents.Chip.Choice"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/message_chip_only_with_attachments"
|
||||||
|
app:checkedIcon="@drawable/ic_mtrl_chip_checked_black"
|
||||||
|
app:checkedIconEnabled="true"
|
||||||
|
app:checkedIconTint="@color/mtrl_choice_chip_text_color" />
|
||||||
|
</com.google.android.material.chip.ChipGroup>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -234,6 +234,8 @@
|
|||||||
<string name="message_not_exists">Message does not exist</string>
|
<string name="message_not_exists">Message does not exist</string>
|
||||||
<string name="message_required_recipients">You need to choose at least 1 recipient</string>
|
<string name="message_required_recipients">You need to choose at least 1 recipient</string>
|
||||||
<string name="message_content_min_length">The message content must be at least 3 characters</string>
|
<string name="message_content_min_length">The message content must be at least 3 characters</string>
|
||||||
|
<string name="message_chip_only_unread">Only unread</string>
|
||||||
|
<string name="message_chip_only_with_attachments">Only with attachments</string>
|
||||||
<plurals name="message_number_item">
|
<plurals name="message_number_item">
|
||||||
<item quantity="one">%d message</item>
|
<item quantity="one">%d message</item>
|
||||||
<item quantity="other">%d messages</item>
|
<item quantity="other">%d messages</item>
|
||||||
|
@ -36,6 +36,15 @@
|
|||||||
<item name="android:textColor">?android:textColorPrimary</item>
|
<item name="android:textColor">?android:textColorPrimary</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="Widget.Wulkanowy.Chip.Choice" parent="Widget.MaterialComponents.Chip.Choice">
|
||||||
|
...
|
||||||
|
<item name="materialThemeOverlay">@style/ThemeOverlay.Wulkanowy.Chip.Choice</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ThemeOverlay.Wulkanowy.Chip.Choice" parent="">
|
||||||
|
<item name="elevationOverlayEnabled">false</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="WulkanowyTheme.TextAppearanceBottomNavigation">
|
<style name="WulkanowyTheme.TextAppearanceBottomNavigation">
|
||||||
<item name="android:textSize">11sp</item>
|
<item name="android:textSize">11sp</item>
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user