Skip to content

Commit

Permalink
Merge pull request #13470 from wordpress-mobile/issue/13268-on-item-c…
Browse files Browse the repository at this point in the history
…lick

Issue/13268 on item click
  • Loading branch information
ashiagr authored Dec 1, 2020
2 parents 0b0d4b3 + ff93099 commit b9c9634
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.wordpress.android.ui.activitylog.list.filter

import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView.Adapter
import org.wordpress.android.ui.activitylog.list.filter.ActivityLogTypeFilterViewModel.ListItemUiState
import org.wordpress.android.ui.activitylog.list.filter.ActivityLogTypeFilterViewModel.ListItemUiState.ActivityType
Expand Down Expand Up @@ -35,8 +36,38 @@ class ActivityLogTypeFilterAdapter(private val uiHelpers: UiHelpers) : Adapter<A
override fun getItemCount(): Int = items.size

fun update(newItems: List<ListItemUiState>) {
val diffResult = DiffUtil.calculateDiff(TypeFilterDiffUtil(items, newItems))
items.clear()
items.addAll(newItems)
notifyDataSetChanged()
diffResult.dispatchUpdatesTo(this)
}

private class TypeFilterDiffUtil(
val oldItems: List<ListItemUiState>,
val newItems: List<ListItemUiState>
) : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
if (oldItem::class != newItem::class) {
return false
}
return when (oldItem) {
is SectionHeader -> true
is ActivityType -> oldItem.title == (newItem as ActivityType).title
}
}

override fun getOldListSize(): Int {
return oldItems.size
}

override fun getNewListSize(): Int {
return newItems.size
}

override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldItems[oldItemPosition] == newItems[newItemPosition]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ sealed class ActivityLogTypeFilterViewHolder(internal val parent: ViewGroup, @La
parent: ViewGroup,
private val uiHelpers: UiHelpers
) : ActivityLogTypeFilterViewHolder(parent, R.layout.activity_log_type_filter_item) {
private val container = itemView.findViewById<ViewGroup>(R.id.container)
private val activityType = itemView.findViewById<TextView>(R.id.activity_type)
private val checkbox = itemView.findViewById<CheckBox>(R.id.checkbox)

override fun onBind(uiState: ListItemUiState) {
uiState as ActivityType
uiHelpers.setTextOrHide(activityType, uiState.title)
checkbox.isChecked = uiState.checked
container.setOnClickListener {
uiState.onClick.invoke()
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import org.wordpress.android.R
import org.wordpress.android.fluxc.model.LocalOrRemoteId.RemoteId
import org.wordpress.android.modules.BG_THREAD
import org.wordpress.android.modules.UI_THREAD
import org.wordpress.android.ui.activitylog.list.filter.ActivityLogTypeFilterViewModel.ListItemUiState.SectionHeader
import org.wordpress.android.ui.activitylog.list.filter.ActivityLogTypeFilterViewModel.UiState.Content
import org.wordpress.android.ui.activitylog.list.filter.ActivityLogTypeFilterViewModel.UiState.FullscreenLoading
import org.wordpress.android.ui.activitylog.list.filter.DummyActivityTypesProvider.DummyActivityType
Expand Down Expand Up @@ -57,10 +56,13 @@ class ActivityLogTypeFilterViewModel @Inject constructor(
private suspend fun buildContentUiState(activityTypes: List<DummyActivityType>): Content {
return withContext(bgDispatcher) {
// TODO malinjir replace the hardcoded header title
val headerListItem = SectionHeader(UiStringText("Test"))
val headerListItem = ListItemUiState.SectionHeader(UiStringText("Test"))
// TODO malinjir replace "it.toString()" with activity type name
val activityTypeListItems: List<ListItemUiState.ActivityType> = activityTypes
.map { ListItemUiState.ActivityType(title = UiStringText(it.toString())) }
.map {
ListItemUiState.ActivityType(id = it.id, title = UiStringText(it.toString()))
.apply { onClick = { onItemClicked(it.id) } }
}
Content(
listOf(headerListItem) + activityTypeListItems,
primaryAction = Action(label = UiStringRes(R.string.activity_log_activity_type_filter_apply))
Expand All @@ -71,6 +73,19 @@ class ActivityLogTypeFilterViewModel @Inject constructor(
}
}

private fun onItemClicked(itemId: Int) {
(_uiState.value as? Content)?.let { content ->
val updatedList = content.items.map { itemUiState ->
if (itemUiState is ListItemUiState.ActivityType && itemUiState.id == itemId) {
itemUiState.copy(checked = !itemUiState.checked).apply { onClick = itemUiState.onClick }
} else {
itemUiState
}
}
_uiState.postValue(content.copy(items = updatedList))
}
}

private fun onApplyClicked() {
}

Expand Down Expand Up @@ -126,9 +141,12 @@ class ActivityLogTypeFilterViewModel @Inject constructor(
) : ListItemUiState()

data class ActivityType(
val id: Int,
val title: UiString,
val checked: Boolean = false
) : ListItemUiState()
) : ListItemUiState() {
lateinit var onClick: (() -> Unit)
}
}

data class Action(val label: UiString) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ class DummyActivityTypesProvider @Inject constructor() {
delay(1000)
return DummyAvailableActivityTypesResponse(
false, listOf(
DummyActivityType("Dummy Users"),
DummyActivityType("Dummy Backup"),
DummyActivityType("Dummy Comments"),
DummyActivityType("Dummy Posts")
DummyActivityType(1, "Dummy Users"),
DummyActivityType(2, "Dummy Backup"),
DummyActivityType(3, "Dummy Comments"),
DummyActivityType(4, "Dummy Posts")
)
)
}
Expand All @@ -26,5 +26,5 @@ class DummyActivityTypesProvider @Inject constructor() {
val activityTypes: List<DummyActivityType> = listOf()
)

data class DummyActivityType(val name: String)
data class DummyActivityType(val id: Int, val name: String)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:background="?attr/selectableItemBackground"
android:layout_width="match_parent"
android:layout_height="?attr/listPreferredItemHeight">

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import org.wordpress.android.TEST_DISPATCHER
import org.wordpress.android.fluxc.model.LocalOrRemoteId.RemoteId
import org.wordpress.android.test
import org.wordpress.android.ui.activitylog.list.filter.ActivityLogTypeFilterViewModel.ListItemUiState
import org.wordpress.android.ui.activitylog.list.filter.ActivityLogTypeFilterViewModel.ListItemUiState.ActivityType
import org.wordpress.android.ui.activitylog.list.filter.ActivityLogTypeFilterViewModel.UiState
import org.wordpress.android.ui.activitylog.list.filter.ActivityLogTypeFilterViewModel.UiState.Content
import org.wordpress.android.ui.activitylog.list.filter.DummyActivityTypesProvider.DummyActivityType
import org.wordpress.android.ui.activitylog.list.filter.DummyActivityTypesProvider.DummyAvailableActivityTypesResponse

Expand Down Expand Up @@ -52,7 +54,7 @@ class ActivityLogTypeFilterViewModelTest : BaseUnitTest() {

startVM()

assertThat((viewModel.uiState.value as UiState.Content).items[0])
assertThat((viewModel.uiState.value as Content).items[0])
.isInstanceOf(ListItemUiState.SectionHeader::class.java)
}

Expand All @@ -62,7 +64,7 @@ class ActivityLogTypeFilterViewModelTest : BaseUnitTest() {

startVM()

assertThat(viewModel.uiState.value).isInstanceOf(UiState.Content::class.java)
assertThat(viewModel.uiState.value).isInstanceOf(Content::class.java)
}

@Test
Expand All @@ -79,7 +81,7 @@ class ActivityLogTypeFilterViewModelTest : BaseUnitTest() {
init(successResponse = false)
startVM()

(viewModel.uiState.value as UiState.Error).retryAction.action!!.invoke()
(viewModel.uiState.value as UiState.Error).retryAction.action.invoke()

verify(dummyActivityTypesProvider, times(2)).fetchAvailableActivityTypes(anyOrNull())
}
Expand All @@ -90,9 +92,9 @@ class ActivityLogTypeFilterViewModelTest : BaseUnitTest() {
startVM()
init(successResponse = true)

(viewModel.uiState.value as UiState.Error).retryAction.action!!.invoke()
(viewModel.uiState.value as UiState.Error).retryAction.action.invoke()

assertThat(viewModel.uiState.value).isInstanceOf(UiState.Content::class.java)
assertThat(viewModel.uiState.value).isInstanceOf(Content::class.java)
}

@Test
Expand All @@ -102,7 +104,28 @@ class ActivityLogTypeFilterViewModelTest : BaseUnitTest() {

startVM()

assertThat((viewModel.uiState.value as UiState.Content).items.size).isEqualTo(1 + activityTypeCount)
assertThat((viewModel.uiState.value as Content).items.size).isEqualTo(1 + activityTypeCount)
}

@Test
fun `item is checked, when the user clicks on it`() = test {
val uiStates = init().uiStates
startVM()

((uiStates.last() as Content).items[1] as ActivityType).onClick.invoke()

assertThat(((uiStates.last() as Content).items[1] as ActivityType).checked).isTrue()
}

@Test
fun `item is unchecked, when the user clicks on it twice`() = test {
val uiStates = init().uiStates
startVM()

((uiStates.last() as Content).items[1] as ActivityType).onClick.invoke()
((uiStates.last() as Content).items[1] as ActivityType).onClick.invoke()

assertThat(((uiStates.last() as Content).items[1] as ActivityType).checked).isFalse()
}

private suspend fun init(successResponse: Boolean = true, activityTypeCount: Int = 5): Observers {
Expand All @@ -127,7 +150,7 @@ class ActivityLogTypeFilterViewModelTest : BaseUnitTest() {
}

private fun generateActivityTypes(count: Int): List<DummyActivityType> {
return (1..count).asSequence().map { DummyActivityType(it.toString()) }.toList()
return (1..count).asSequence().map { DummyActivityType(it, it.toString()) }.toList()
}

private data class Observers(val uiStates: List<UiState>)
Expand Down

0 comments on commit b9c9634

Please sign in to comment.