From 0b699fd89d879628c476d7bafb21a9fb056b9f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frans-Lukas=20L=C3=B6venvald?= Date: Mon, 8 Jun 2020 15:34:38 +0200 Subject: [PATCH] #2119 re-re-write of History activity --- .../kiwix/kiwixmobile/core/data/Repository.kt | 18 +- .../core/extensions/HeaderizableList.kt | 4 +- .../core/history/HistoryActivity.kt | 27 +- .../core/history/viewmodel/Action.kt | 11 +- .../history/viewmodel/HistoryViewModel.kt | 247 +++++++++--------- .../core/history/viewmodel/State.kt | 60 ++++- ...lHistoryItems.kt => DeleteHistoryItems.kt} | 15 +- .../effects/ShowDeleteHistoryDialog.kt | 11 +- ...Prefs.kt => UpdateAllHistoryPreference.kt} | 7 +- .../core/utils/SharedPreferenceUtil.java | 16 +- .../history/viewmodel/HistoryViewModelTest.kt | 79 +++--- .../DeleteSelectedOrAllHistoryItemsTest.kt | 5 +- .../effects/ShowDeleteHistoryDialogTest.kt | 4 +- ...HistorySwitchAndSaveItsStateToPrefsTest.kt | 4 +- 14 files changed, 270 insertions(+), 238 deletions(-) rename core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/{DeleteSelectedOrAllHistoryItems.kt => DeleteHistoryItems.kt} (68%) rename core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/{ToggleShowAllHistorySwitchAndSaveItsStateToPrefs.kt => UpdateAllHistoryPreference.kt} (59%) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt index b309e023f..4abfd490c 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt @@ -67,10 +67,9 @@ class Repository @Inject internal constructor( override fun booksOnDiskAsListItems(): Flowable> = bookDao.books() .map { it.sortedBy { bookOnDisk -> bookOnDisk.book.language + bookOnDisk.book.title } } .map { - HeaderizableList(it as List).foldOverAddingHeaders( - { bookOnDisk -> LanguageItem((bookOnDisk as BookOnDisk).locale) }, - { current, next -> - (current as BookOnDisk).locale.displayName != (next as BookOnDisk).locale.displayName }) + HeaderizableList(it).foldOverAddingHeaders( + { bookOnDisk -> LanguageItem(bookOnDisk.locale) }, + { current, next -> current.locale.displayName != next.locale.displayName }) } .map { it.toList() } @@ -93,11 +92,12 @@ class Repository @Inject internal constructor( zimReaderContainer.zimCanonicalPath ) ).map { - HeaderizableList(it as List).foldOverAddingHeaders( - { historyItem -> DateItem((historyItem as HistoryItem).dateString) }, - { current, next -> - (current as HistoryItem).dateString != (next as HistoryItem).dateString }) - } + HeaderizableList(it).foldOverAddingHeaders( + { historyItem -> DateItem(historyItem.dateString) }, + { current, next -> + current.dateString != next.dateString + }) + } .subscribeOn(io) .observeOn(mainThread) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/HeaderizableList.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/HeaderizableList.kt index b1106c107..330d0da0a 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/HeaderizableList.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/HeaderizableList.kt @@ -1,6 +1,6 @@ package org.kiwix.kiwixmobile.core.extensions -inline class HeaderizableList( +inline class HeaderizableList( val list: List ) { fun foldOverAddingHeaders( @@ -13,7 +13,7 @@ inline class HeaderizableList( } acc.add(currentItem) if (index < list.size - 1) { - val nextItem = list.get(index + 1) + val nextItem = list[index + 1] if (criteriaToAddHeader.invoke(currentItem, nextItem)) { acc.add(headerConstructor.invoke(nextItem)) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/history/HistoryActivity.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/history/HistoryActivity.kt index a3838c2df..570062642 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/history/HistoryActivity.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/history/HistoryActivity.kt @@ -18,7 +18,6 @@ import kotlinx.android.synthetic.main.activity_history.no_history import kotlinx.android.synthetic.main.activity_history.recycler_view import kotlinx.android.synthetic.main.layout_toolbar.toolbar import org.kiwix.kiwixmobile.core.R -import org.kiwix.kiwixmobile.core.R.id import org.kiwix.kiwixmobile.core.R.string import org.kiwix.kiwixmobile.core.base.BaseActivity import org.kiwix.kiwixmobile.core.di.components.CoreComponent @@ -33,12 +32,11 @@ import org.kiwix.kiwixmobile.core.history.viewmodel.Action import org.kiwix.kiwixmobile.core.history.viewmodel.Action.ExitHistory import org.kiwix.kiwixmobile.core.history.viewmodel.Action.Filter import org.kiwix.kiwixmobile.core.history.viewmodel.Action.OnItemLongClick -import org.kiwix.kiwixmobile.core.history.viewmodel.Action.RequestDeleteAllHistoryItems -import org.kiwix.kiwixmobile.core.history.viewmodel.Action.RequestDeleteSelectedHistoryItems import org.kiwix.kiwixmobile.core.history.viewmodel.Action.ToggleShowHistoryFromAllBooks +import org.kiwix.kiwixmobile.core.history.viewmodel.Action.UserClickedDeleteButton +import org.kiwix.kiwixmobile.core.history.viewmodel.Action.UserClickedDeleteSelectedHistoryItems import org.kiwix.kiwixmobile.core.history.viewmodel.HistoryViewModel import org.kiwix.kiwixmobile.core.history.viewmodel.State -import org.kiwix.kiwixmobile.core.history.viewmodel.State.NoResults import org.kiwix.kiwixmobile.core.history.viewmodel.State.Results import org.kiwix.kiwixmobile.core.history.viewmodel.State.SelectionResults import org.kiwix.kiwixmobile.core.utils.SimpleTextListener @@ -60,13 +58,11 @@ class HistoryActivity : OnItemClickListener, BaseActivity() { return true } - override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - return false - } + override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean = false override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { if (item.itemId == R.id.menu_context_delete) { - historyViewModel.actions.offer(RequestDeleteSelectedHistoryItems) + historyViewModel.actions.offer(UserClickedDeleteSelectedHistoryItems) return true } historyViewModel.actions.offer(Action.ExitActionModeMenu) @@ -101,7 +97,7 @@ class HistoryActivity : OnItemClickListener, BaseActivity() { history_switch.setOnCheckedChangeListener { _, isChecked -> historyViewModel.actions.offer(ToggleShowHistoryFromAllBooks(isChecked)) } - history_switch.isChecked = !sharedPreferenceUtil.showHistoryCurrentBook + history_switch.isChecked = !sharedPreferenceUtil.showHistoryAllBooks compositeDisposable.add(historyViewModel.effects.subscribe { it.invokeWith(this) }) } @@ -127,7 +123,7 @@ class HistoryActivity : OnItemClickListener, BaseActivity() { historyViewModel.actions.offer(ExitHistory) } if (item.itemId == R.id.menu_history_clear) { - historyViewModel.actions.offer(RequestDeleteAllHistoryItems) + historyViewModel.actions.offer(UserClickedDeleteButton) } return super.onOptionsItemSelected(item) } @@ -136,23 +132,18 @@ class HistoryActivity : OnItemClickListener, BaseActivity() { when (state) { is Results -> { actionMode?.finish() - state.historyItems?.let { historyAdapter.items = it } + state.historyListItems.let { historyAdapter.items = it } history_switch.isEnabled = true no_history.visibility = View.GONE } is SelectionResults -> { - if (state.historyItems?.filterIsInstance()?.any { it.isSelected } == true && - actionMode == null) { + if (state.historyItems.any(HistoryItem::isSelected) && actionMode == null) { actionMode = startSupportActionMode(actionModeCallback) } - state.historyItems?.let { historyAdapter.items = it } + state.historyListItems.let { historyAdapter.items = it } history_switch.isEnabled = false no_history.visibility = View.GONE } - is NoResults -> { - state.historyItems?.let { historyAdapter.items = it } - no_history.visibility = View.VISIBLE - } } override fun onItemClick(favicon: ImageView, history: HistoryItem) { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/Action.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/Action.kt index 546c38845..a168e5403 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/Action.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/Action.kt @@ -18,18 +18,19 @@ package org.kiwix.kiwixmobile.core.history.viewmodel -import org.kiwix.kiwixmobile.core.history.adapter.HistoryListItem import org.kiwix.kiwixmobile.core.history.adapter.HistoryListItem.HistoryItem sealed class Action { object ExitHistory : Action() object ExitActionModeMenu : Action() - object DeleteHistoryItems : Action() - object RequestDeleteAllHistoryItems : Action() - object RequestDeleteSelectedHistoryItems : Action() + object UserClickedDelete : Action() + object UserClickedDeleteButton : Action() + object UserClickedDeleteSelectedHistoryItems : Action() - data class OnItemClick(val historyListItem: HistoryListItem) : Action() + data class OnItemClick(val historyItem: HistoryItem) : Action() data class OnItemLongClick(val historyItem: HistoryItem) : Action() data class ToggleShowHistoryFromAllBooks(val isChecked: Boolean) : Action() + data class AllHistoryPreferenceChanged(val showAll: Boolean) : Action() data class Filter(val searchTerm: String) : Action() + data class UpdateHistory(val history: List) : Action() } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/HistoryViewModel.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/HistoryViewModel.kt index 43c2191ad..ec1a143ef 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/HistoryViewModel.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/HistoryViewModel.kt @@ -1,41 +1,31 @@ package org.kiwix.kiwixmobile.core.history.viewmodel -import DeleteSelectedOrAllHistoryItems import OpenHistoryItem import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import io.reactivex.Flowable import io.reactivex.disposables.CompositeDisposable -import io.reactivex.functions.Function3 -import io.reactivex.processors.BehaviorProcessor import io.reactivex.processors.PublishProcessor import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.dao.HistoryDao -import org.kiwix.kiwixmobile.core.extensions.HeaderizableList -import org.kiwix.kiwixmobile.core.history.adapter.HistoryListItem -import org.kiwix.kiwixmobile.core.history.adapter.HistoryListItem.DateItem -import org.kiwix.kiwixmobile.core.history.adapter.HistoryListItem.HistoryItem -import org.kiwix.kiwixmobile.core.history.viewmodel.Action.DeleteHistoryItems +import org.kiwix.kiwixmobile.core.history.viewmodel.Action.AllHistoryPreferenceChanged import org.kiwix.kiwixmobile.core.history.viewmodel.Action.ExitActionModeMenu import org.kiwix.kiwixmobile.core.history.viewmodel.Action.ExitHistory import org.kiwix.kiwixmobile.core.history.viewmodel.Action.Filter import org.kiwix.kiwixmobile.core.history.viewmodel.Action.OnItemClick import org.kiwix.kiwixmobile.core.history.viewmodel.Action.OnItemLongClick -import org.kiwix.kiwixmobile.core.history.viewmodel.Action.RequestDeleteAllHistoryItems -import org.kiwix.kiwixmobile.core.history.viewmodel.Action.RequestDeleteSelectedHistoryItems import org.kiwix.kiwixmobile.core.history.viewmodel.Action.ToggleShowHistoryFromAllBooks -import org.kiwix.kiwixmobile.core.history.viewmodel.State.NoResults +import org.kiwix.kiwixmobile.core.history.viewmodel.Action.UpdateHistory +import org.kiwix.kiwixmobile.core.history.viewmodel.Action.UserClickedDelete +import org.kiwix.kiwixmobile.core.history.viewmodel.Action.UserClickedDeleteButton +import org.kiwix.kiwixmobile.core.history.viewmodel.Action.UserClickedDeleteSelectedHistoryItems import org.kiwix.kiwixmobile.core.history.viewmodel.State.Results import org.kiwix.kiwixmobile.core.history.viewmodel.State.SelectionResults +import org.kiwix.kiwixmobile.core.history.viewmodel.effects.DeleteHistoryItems import org.kiwix.kiwixmobile.core.history.viewmodel.effects.ShowDeleteHistoryDialog -import org.kiwix.kiwixmobile.core.history.viewmodel.effects.ToggleShowAllHistorySwitchAndSaveItsStateToPrefs +import org.kiwix.kiwixmobile.core.history.viewmodel.effects.UpdateAllHistoryPreference import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import org.kiwix.kiwixmobile.core.search.viewmodel.effects.Finish -import org.kiwix.kiwixmobile.core.utils.KiwixDialog.DeleteAllHistory -import org.kiwix.kiwixmobile.core.utils.KiwixDialog.DeleteSelectedHistory import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil -import java.text.SimpleDateFormat -import java.util.Locale import javax.inject.Inject class HistoryViewModel @Inject constructor( @@ -43,133 +33,142 @@ class HistoryViewModel @Inject constructor( private val zimReaderContainer: ZimReaderContainer, private val sharedPreferenceUtil: SharedPreferenceUtil ) : ViewModel() { - val state = MutableLiveData().apply { value = NoResults(emptyList()) } + val state = MutableLiveData().apply { + value = Results(emptyList(), sharedPreferenceUtil.showHistoryAllBooks) + } val effects = PublishProcessor.create>() val actions = PublishProcessor.create() - private val filter = BehaviorProcessor.createDefault("") private val compositeDisposable = CompositeDisposable() - val showAllSwitchToggle = - BehaviorProcessor.createDefault(!sharedPreferenceUtil.showHistoryCurrentBook) - private val dateFormatter = SimpleDateFormat("d MMM yyyy", Locale.getDefault()) - private val deselectAllItems = BehaviorProcessor.createDefault(false) init { compositeDisposable.addAll( - viewStateReducer(), - actionMapper(), - deselectAllItems() + historyDao.history().subscribe { actions.offer(UpdateHistory(it)) }, + sharedPreferenceUtil.showAllHistoryToggleSwitch.subscribe { + actions.offer(AllHistoryPreferenceChanged(it)) + }, + viewStateReducer() ) } - private fun deselectAllItems() = deselectAllItems.subscribe { - state.value?.historyItems?.filterIsInstance()?.forEach { it.isSelected = false } - } - private fun viewStateReducer() = - Flowable.combineLatest( - filter, - showAllSwitchToggle, - historyDao.history(), - Function3(::searchResults) - ).subscribe { state.postValue(updateResultsState(it)) } + actions.map { reduce(it, state.value!!) }.subscribe(state::postValue) - private fun updateResultsState( - historyListWithDateItems: List + private fun reduce(action: Action, state: State): State = + when (action) { + ExitHistory -> finishHistoryActivity(state) + ExitActionModeMenu -> deselectAllHistoryItems(state) + UserClickedDelete -> offerDeletionOfItems(state) + UserClickedDeleteButton -> offerShowDeleteDialog(state) + is OnItemClick -> handleItemClick(state, action) + is OnItemLongClick -> handleItemLongClick(state, action) + is ToggleShowHistoryFromAllBooks -> offerUpdateToShowAllToggle(action, state) + is Filter -> updateHistoryItemsBasedOnFilter(state, action) + is UpdateHistory -> updateHistoryList(state, action) + UserClickedDeleteSelectedHistoryItems -> offerShowDeleteDialog(state) + is AllHistoryPreferenceChanged -> changeShowHistoryToggle(state, action) + } + + private fun updateHistoryItemsBasedOnFilter(state: State, action: Filter) = + when (state) { + is Results -> state.copy(searchTerm = action.searchTerm) + is SelectionResults -> state.copy(searchTerm = action.searchTerm) + } + + private fun changeShowHistoryToggle( + state: State, + action: AllHistoryPreferenceChanged ): State { - return when { - historyListWithDateItems.isEmpty() -> NoResults(historyListWithDateItems) - historyListWithDateItems.filterIsInstance() - .any { it.isSelected } -> SelectionResults( - historyListWithDateItems - ) - else -> Results(historyListWithDateItems) + return when (state) { + is SelectionResults -> state + is Results -> state.copy(showAll = action.showAll) } } - private fun searchResults( - searchString: String, - showAllToggle: Boolean, - historyList: List - ): List = HeaderizableList(historyList - .filterIsInstance() - .filter { h -> - h.historyTitle.contains(searchString, true) && - (h.zimName == zimReaderContainer.name || showAllToggle) + private fun updateHistoryList( + state: State, + action: UpdateHistory + ): State { + return when (state) { + is Results -> state.copy(historyItems = action.history) + is SelectionResults -> { + val selectedItems = state.selectedItems + state.copy(historyItems = action.history.map { + if (selectedItems.contains(it)) it.copy(isSelected = true) else it + }) } - .sortedByDescending { dateFormatter.parse(it.dateString) } as List) - .foldOverAddingHeaders( - { historyItem -> DateItem((historyItem as HistoryItem).dateString) }, - { current, next -> (current as HistoryItem).dateString != (next as HistoryItem).dateString } + } + } + + private fun offerUpdateToShowAllToggle( + action: ToggleShowHistoryFromAllBooks, + state: State + ): State { + effects.offer( + UpdateAllHistoryPreference( + sharedPreferenceUtil, + action.isChecked ) + ) + return state + } + + private fun handleItemLongClick( + state: State, + action: OnItemLongClick + ): State { + return when (state) { + is Results -> state.toggleSelectionOfItem(action.historyItem) + else -> state + } + } + + private fun handleItemClick( + state: State, + action: OnItemClick + ): State { + return when (state) { + is Results -> { + effects.offer(OpenHistoryItem(action.historyItem, zimReaderContainer)) + state + } + is SelectionResults -> state.toggleSelectionOfItem(action.historyItem) + } + } + + private fun offerShowDeleteDialog(state: State): State { + effects.offer(ShowDeleteHistoryDialog(actions)) + return state + } + + private fun offerDeletionOfItems(state: State): State { + return when (state) { + is Results -> { + effects.offer(DeleteHistoryItems(state.historyItems, historyDao)) + state + } + is SelectionResults -> { + effects.offer(DeleteHistoryItems(state.selectedItems, historyDao)) + state + } + } + } + + private fun deselectAllHistoryItems(state: State): State { + return when (state) { + is SelectionResults -> { + state.copy(historyItems = state.historyItems.map { it.copy(isSelected = false) }) + } + else -> state + } + } + + private fun finishHistoryActivity(state: State): State { + effects.offer(Finish) + return state + } override fun onCleared() { compositeDisposable.clear() super.onCleared() } - - private fun toggleSelectionOfItem(historyItem: HistoryItem): State = - when (state.value) { - is Results -> SelectionResults(toggleGivenItemAndReturnListOfResultingItems(historyItem)) - is SelectionResults -> { - if (toggleGivenItemAndReturnListOfResultingItems(historyItem) - ?.filterIsInstance() - ?.any { it.isSelected } == true) { - SelectionResults(toggleGivenItemAndReturnListOfResultingItems(historyItem)) - } else { - Results(toggleGivenItemAndReturnListOfResultingItems(historyItem)) - } - } - is NoResults -> NoResults(emptyList()) - null -> NoResults(emptyList()) - } - - private fun toggleGivenItemAndReturnListOfResultingItems(historyItem: HistoryItem): - List? { - return state.value - ?.historyItems - ?.map { - if (it.id == historyItem.id && it is HistoryItem) - it.copy(isSelected = !it.isSelected) else it - } - } - - private fun actionMapper() = actions.map { - when (it) { - ExitHistory -> effects.offer(Finish) - is Filter -> filter.offer(it.searchTerm) - is ToggleShowHistoryFromAllBooks -> - toggleShowAllHistorySwitchAndSaveItsStateToPrefs(it.isChecked) - is OnItemLongClick -> state.postValue(toggleSelectionOfItem(it.historyItem)) - is OnItemClick -> appendItemToSelectionOrOpenIt(it) - is RequestDeleteAllHistoryItems -> - effects.offer(ShowDeleteHistoryDialog(actions, DeleteAllHistory)) - is RequestDeleteSelectedHistoryItems -> - effects.offer(ShowDeleteHistoryDialog(actions, DeleteSelectedHistory)) - ExitActionModeMenu -> state.postValue(Results( - state.value - ?.historyItems - ?.map { item -> if (item is HistoryItem) item.copy(isSelected = false) else item }) - ) - DeleteHistoryItems -> effects.offer(DeleteSelectedOrAllHistoryItems(state, historyDao)) - } - }.subscribe({}, Throwable::printStackTrace) - - private fun toggleShowAllHistorySwitchAndSaveItsStateToPrefs(isChecked: Boolean) { - effects.offer( - ToggleShowAllHistorySwitchAndSaveItsStateToPrefs( - showAllSwitchToggle, - sharedPreferenceUtil, - isChecked - ) - ) - } - - private fun appendItemToSelectionOrOpenIt(onItemClick: OnItemClick) { - val historyItem = onItemClick.historyListItem as HistoryItem - if (state.value?.containsSelectedItems() == true) { - state.postValue(toggleSelectionOfItem(historyItem)) - } else { - effects.offer(OpenHistoryItem(historyItem, zimReaderContainer)) - } - } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/State.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/State.kt index e4121e208..54ec21566 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/State.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/State.kt @@ -18,26 +18,64 @@ package org.kiwix.kiwixmobile.core.history.viewmodel +import org.kiwix.kiwixmobile.core.extensions.HeaderizableList import org.kiwix.kiwixmobile.core.history.adapter.HistoryListItem +import org.kiwix.kiwixmobile.core.history.adapter.HistoryListItem.DateItem import org.kiwix.kiwixmobile.core.history.adapter.HistoryListItem.HistoryItem +import java.text.SimpleDateFormat +import java.util.Locale sealed class State( - open val historyItems: List? + open val historyItems: List, + open val showAll: Boolean, + open val searchTerm: String = "" ) { - fun containsSelectedItems(): Boolean { - return historyItems?.filterIsInstance()?.any { it.isSelected } == true + private val dateFormatter = SimpleDateFormat("d MMM yyyy", Locale.getDefault()) + + val historyListItems: List by lazy { + HeaderizableList(historyItems + .filter { it.historyTitle.contains(searchTerm, true) } + .sortedByDescending { dateFormatter.parse(it.dateString) }) + .foldOverAddingHeaders( + { historyItem -> DateItem(historyItem.dateString) }, + { current, next -> current.dateString != next.dateString } + ) } - data class Results( - override val historyItems: List? - ) : State(historyItems) + fun toggleSelectionOfItem(historyListItem: HistoryItem): State { + val newList = historyItems.map { + if (it.id == historyListItem.id) it.apply { + isSelected = !isSelected + } else it + } + if (newList.isEmpty()) { + return Results(newList, showAll) + } + return SelectionResults(newList, showAll, searchTerm) + } - data class NoResults( - override val historyItems: List? - ) : State(historyItems) + // fun filterBasedOnSearchTerm(searchTerm: String): State = + // copy(historyListItems = historyItems.filter { + // it.historyTitle.contains( + // searchTerm, + // true + // ) + // }) + + data class Results( + override val historyItems: List, + override val showAll: Boolean, + override val searchTerm: String = "" + ) : State(historyItems, showAll, searchTerm) data class SelectionResults( - override val historyItems: List? - ) : State(historyItems) + override val historyItems: List, + override val showAll: Boolean, + override val searchTerm: String + ) : State(historyItems, showAll, searchTerm) { + + val selectedItems: List = + historyListItems.filterIsInstance().filter(HistoryItem::isSelected) + } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/DeleteSelectedOrAllHistoryItems.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/DeleteHistoryItems.kt similarity index 68% rename from core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/DeleteSelectedOrAllHistoryItems.kt rename to core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/DeleteHistoryItems.kt index b45f1ca7c..df5e3f5a2 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/DeleteSelectedOrAllHistoryItems.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/DeleteHistoryItems.kt @@ -1,3 +1,5 @@ +package org.kiwix.kiwixmobile.core.history.viewmodel.effects + /* * Kiwix Android * Copyright (c) 2020 Kiwix @@ -17,22 +19,15 @@ */ import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.MutableLiveData import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.dao.HistoryDao import org.kiwix.kiwixmobile.core.history.adapter.HistoryListItem.HistoryItem -import org.kiwix.kiwixmobile.core.history.viewmodel.State -data class DeleteSelectedOrAllHistoryItems( - private val state: MutableLiveData, +data class DeleteHistoryItems( + private val itemsToDelete: List, private val historyDao: HistoryDao ) : SideEffect { override fun invokeWith(activity: AppCompatActivity) { - val historyItems = state.value?.historyItems?.filterIsInstance() - if (historyItems?.any { it.isSelected } == true) { - historyDao.deleteHistory(historyItems.filter { it.isSelected }) - } else if (historyItems != null) { - historyDao.deleteHistory(historyItems) - } + historyDao.deleteHistory(itemsToDelete) } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ShowDeleteHistoryDialog.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ShowDeleteHistoryDialog.kt index 968c97366..27be7ca8a 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ShowDeleteHistoryDialog.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ShowDeleteHistoryDialog.kt @@ -23,20 +23,19 @@ import io.reactivex.processors.PublishProcessor import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.history.HistoryActivity import org.kiwix.kiwixmobile.core.history.viewmodel.Action -import org.kiwix.kiwixmobile.core.history.viewmodel.Action.DeleteHistoryItems +import org.kiwix.kiwixmobile.core.history.viewmodel.Action.UserClickedDelete import org.kiwix.kiwixmobile.core.utils.DialogShower -import org.kiwix.kiwixmobile.core.utils.KiwixDialog +import org.kiwix.kiwixmobile.core.utils.KiwixDialog.DeleteAllHistory import javax.inject.Inject data class ShowDeleteHistoryDialog( - private val actions: PublishProcessor, - private val dialogType: KiwixDialog + private val actions: PublishProcessor ) : SideEffect { @Inject lateinit var dialogShower: DialogShower override fun invokeWith(activity: AppCompatActivity) { (activity as HistoryActivity).activityComponent.inject(this) - dialogShower.show(dialogType, { - actions.offer(DeleteHistoryItems) + dialogShower.show(DeleteAllHistory, { + actions.offer(UserClickedDelete) }) } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ToggleShowAllHistorySwitchAndSaveItsStateToPrefs.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/UpdateAllHistoryPreference.kt similarity index 59% rename from core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ToggleShowAllHistorySwitchAndSaveItsStateToPrefs.kt rename to core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/UpdateAllHistoryPreference.kt index 45672622d..02be63866 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ToggleShowAllHistorySwitchAndSaveItsStateToPrefs.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/UpdateAllHistoryPreference.kt @@ -1,17 +1,14 @@ package org.kiwix.kiwixmobile.core.history.viewmodel.effects import androidx.appcompat.app.AppCompatActivity -import io.reactivex.processors.BehaviorProcessor import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil -data class ToggleShowAllHistorySwitchAndSaveItsStateToPrefs( - private val showAllSwitchToggle: BehaviorProcessor, +data class UpdateAllHistoryPreference( private val sharedPreferenceUtil: SharedPreferenceUtil, private val isChecked: Boolean ) : SideEffect { override fun invokeWith(activity: AppCompatActivity) { - showAllSwitchToggle.offer(isChecked) - sharedPreferenceUtil.showHistoryCurrentBook = !isChecked + sharedPreferenceUtil.showHistoryAllBooks = !isChecked } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.java b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.java index fb2729369..b770b46e0 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.java +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.java @@ -55,11 +55,12 @@ public class SharedPreferenceUtil { private static final String PREF_EXTERNAL_LINK_POPUP = "pref_external_link_popup"; private static final String PREF_IS_FIRST_RUN = "isFirstRun"; private static final String PREF_SHOW_BOOKMARKS_CURRENT_BOOK = "show_bookmarks_current_book"; - private static final String PREF_SHOW_HISTORY_CURRENT_BOOK = "show_history_current_book"; + private static final String PREF_SHOW_HISTORY_ALL_BOOKS = "show_history_current_book"; private static final String PREF_HOSTED_BOOKS = "hosted_books"; public static final String PREF_NIGHT_MODE = "pref_night_mode"; private SharedPreferences sharedPreferences; private final PublishProcessor prefStorages = PublishProcessor.create(); + public final PublishProcessor showAllHistoryToggleSwitch = PublishProcessor.create(); private final PublishProcessor nightModes = PublishProcessor.create(); @Inject @@ -152,6 +153,10 @@ public class SharedPreferenceUtil { return prefStorages.startWith(getPrefStorage()); } + public Flowable getShowAllHistoryToggleSwitches() { + return showAllHistoryToggleSwitch.startWith(getShowHistoryAllBooks()); + } + public void putPrefFullScreen(boolean fullScreen) { sharedPreferences.edit().putBoolean(PREF_FULLSCREEN, fullScreen).apply(); } @@ -168,14 +173,15 @@ public class SharedPreferenceUtil { sharedPreferences.edit().putBoolean(PREF_SHOW_INTRO, false).apply(); } - public boolean getShowHistoryCurrentBook() { - return sharedPreferences.getBoolean(PREF_SHOW_HISTORY_CURRENT_BOOK, true); + public boolean getShowHistoryAllBooks() { + return sharedPreferences.getBoolean(PREF_SHOW_HISTORY_ALL_BOOKS, true); } - public void setShowHistoryCurrentBook(boolean prefShowHistoryCurrentBook) { + public void setShowHistoryAllBooks(boolean prefShowHistoryAllBooks) { sharedPreferences.edit() - .putBoolean(PREF_SHOW_HISTORY_CURRENT_BOOK, prefShowHistoryCurrentBook) + .putBoolean(PREF_SHOW_HISTORY_ALL_BOOKS, prefShowHistoryAllBooks) .apply(); + showAllHistoryToggleSwitch.offer(prefShowHistoryAllBooks); } public boolean getShowBookmarksCurrentBook() { diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/HistoryViewModelTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/HistoryViewModelTest.kt index af5c2c4dc..eddebea4b 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/HistoryViewModelTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/HistoryViewModelTest.kt @@ -1,6 +1,5 @@ package org.kiwix.kiwixmobile.core.history.viewmodel -import DeleteSelectedOrAllHistoryItems import OpenHistoryItem import com.jraska.livedata.test import io.mockk.clearAllMocks @@ -18,24 +17,22 @@ import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.dao.HistoryDao import org.kiwix.kiwixmobile.core.history.adapter.HistoryListItem.DateItem import org.kiwix.kiwixmobile.core.history.adapter.HistoryListItem.HistoryItem -import org.kiwix.kiwixmobile.core.history.viewmodel.Action.DeleteHistoryItems import org.kiwix.kiwixmobile.core.history.viewmodel.Action.ExitActionModeMenu import org.kiwix.kiwixmobile.core.history.viewmodel.Action.ExitHistory -import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer -import org.kiwix.kiwixmobile.core.history.viewmodel.State.NoResults -import org.kiwix.kiwixmobile.core.history.viewmodel.State.Results import org.kiwix.kiwixmobile.core.history.viewmodel.Action.Filter import org.kiwix.kiwixmobile.core.history.viewmodel.Action.OnItemClick import org.kiwix.kiwixmobile.core.history.viewmodel.Action.OnItemLongClick -import org.kiwix.kiwixmobile.core.history.viewmodel.Action.RequestDeleteAllHistoryItems -import org.kiwix.kiwixmobile.core.history.viewmodel.Action.RequestDeleteSelectedHistoryItems import org.kiwix.kiwixmobile.core.history.viewmodel.Action.ToggleShowHistoryFromAllBooks +import org.kiwix.kiwixmobile.core.history.viewmodel.Action.UserClickedDelete +import org.kiwix.kiwixmobile.core.history.viewmodel.Action.UserClickedDeleteButton +import org.kiwix.kiwixmobile.core.history.viewmodel.Action.UserClickedDeleteSelectedHistoryItems +import org.kiwix.kiwixmobile.core.history.viewmodel.State.Results import org.kiwix.kiwixmobile.core.history.viewmodel.State.SelectionResults +import org.kiwix.kiwixmobile.core.history.viewmodel.effects.DeleteHistoryItems import org.kiwix.kiwixmobile.core.history.viewmodel.effects.ShowDeleteHistoryDialog -import org.kiwix.kiwixmobile.core.history.viewmodel.effects.ToggleShowAllHistorySwitchAndSaveItsStateToPrefs +import org.kiwix.kiwixmobile.core.history.viewmodel.effects.UpdateAllHistoryPreference +import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import org.kiwix.kiwixmobile.core.search.viewmodel.effects.Finish -import org.kiwix.kiwixmobile.core.utils.KiwixDialog.DeleteAllHistory -import org.kiwix.kiwixmobile.core.utils.KiwixDialog.DeleteSelectedHistory import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.sharedFunctions.InstantExecutorExtension import org.kiwix.sharedFunctions.setScheduler @@ -62,7 +59,7 @@ internal class HistoryViewModelTest { clearAllMocks() every { zimReaderContainer.id } returns "id" every { zimReaderContainer.name } returns "zimName" - every { sharedPreferenceUtil.showHistoryCurrentBook } returns true + every { sharedPreferenceUtil.showHistoryAllBooks } returns true every { historyDao.history() } returns itemsFromDb.distinctUntilChanged() viewModel = HistoryViewModel(historyDao, zimReaderContainer, sharedPreferenceUtil) } @@ -86,7 +83,7 @@ internal class HistoryViewModelTest { @Test fun `initial state is Initialising`() { - viewModel.state.test().assertValue(NoResults(listOf())) + viewModel.state.test().assertValue(Results(listOf(), true)) } @Test @@ -101,7 +98,7 @@ internal class HistoryViewModelTest { searchTerm = searchTerm, databaseResults = listOf(item) ) - resultsIn(Results((listOf(date, item)))) + resultsIn(Results(listOf(item), true)) } @Test @@ -114,7 +111,7 @@ internal class HistoryViewModelTest { ) ) ) - resultsIn(NoResults(emptyList())) + resultsIn(Results(emptyList(), true)) } @Test @@ -126,7 +123,7 @@ internal class HistoryViewModelTest { searchTerm = "", databaseResults = listOf(item) ) - resultsIn(Results(listOf(date, item))) + resultsIn(Results(listOf(item), true)) } @Test @@ -135,7 +132,7 @@ internal class HistoryViewModelTest { searchTerm = "", databaseResults = emptyList() ) - resultsIn(NoResults(emptyList())) + resultsIn(Results(emptyList(), true)) } @Test @@ -150,7 +147,7 @@ internal class HistoryViewModelTest { databaseResults = listOf(item) ) viewModel.actions.offer(Filter(searchString2)) - resultsIn(Results(listOf(date, item))) + resultsIn(Results(listOf(item), true)) } @Test @@ -166,7 +163,7 @@ internal class HistoryViewModelTest { searchTerm = "b", databaseResults = listOf(item) ) - resultsIn(Results(listOf(date, item))) + resultsIn(Results(listOf(item), true)) } @Test @@ -184,7 +181,7 @@ internal class HistoryViewModelTest { searchTerm = "b", databaseResults = listOf(item) ) - resultsIn(SelectionResults(listOf(date, item))) + resultsIn(SelectionResults(listOf(item), true)) } @Test @@ -208,7 +205,7 @@ internal class HistoryViewModelTest { searchTerm = "", databaseResults = listOf(item2, item3, item1) ) - resultsIn(Results(listOf(date3, item3, date1, item1, date2, item2))) + resultsIn(Results(listOf(item3, item1, item2), true)) } @Test @@ -231,7 +228,7 @@ internal class HistoryViewModelTest { searchTerm = "", databaseResults = listOf(item2, item3, item1) ) - resultsIn(Results(listOf(date1, item2, item1, date3, item3))) + resultsIn(Results(listOf(item2, item1, item3), true)) } @Test @@ -247,7 +244,7 @@ internal class HistoryViewModelTest { ) viewModel.actions.offer(OnItemLongClick(item1)) item1.isSelected = true - resultsIn(SelectionResults(listOf(date, item1))) + resultsIn(SelectionResults(listOf(item1), true)) } @Test @@ -267,8 +264,9 @@ internal class HistoryViewModelTest { ) viewModel.actions.offer(OnItemLongClick(item1)) viewModel.actions.offer(OnItemClick(item1)) - resultsIn(Results(listOf(date, item1, item2))) + resultsIn(Results(listOf(item1, item2), true)) } + @Test fun `Deselection via OnItemLongClick exits selection state if last item is deselected`() { val item1 = @@ -286,7 +284,7 @@ internal class HistoryViewModelTest { ) viewModel.actions.offer(OnItemLongClick(item1)) viewModel.actions.offer(OnItemLongClick(item1)) - resultsIn(Results(listOf(date, item1, item2))) + resultsIn(Results(listOf(item1, item2), true)) } @Test @@ -307,7 +305,7 @@ internal class HistoryViewModelTest { viewModel.actions.offer(ExitActionModeMenu) item1.isSelected = false item2.isSelected = false - resultsIn(Results(listOf(date, item1, item2))) + resultsIn(Results(listOf(item1, item2), true)) } } @@ -368,12 +366,17 @@ internal class HistoryViewModelTest { private fun assertItemIsSelected(item: HistoryItem) { assertTrue( - (viewModel.state.value?.historyItems?.find { it.id == item.id } as HistoryItem).isSelected + (viewModel.state.value?.historyListItems?.find { + it.id == item.id + } as HistoryItem).isSelected ) } + private fun assertItemIsDeselected(item: HistoryItem) { assertFalse( - (viewModel.state.value?.historyItems?.find { it.id == item.id } as HistoryItem).isSelected + (viewModel.state.value?.historyListItems?.find { + it.id == item.id + } as HistoryItem).isSelected ) } @@ -429,31 +432,35 @@ internal class HistoryViewModelTest { fun `ToggleShowHistoryFromAllBooks switches show all books toggle`() { actionResultsInEffects( ToggleShowHistoryFromAllBooks(true), - ToggleShowAllHistorySwitchAndSaveItsStateToPrefs( - viewModel.showAllSwitchToggle, + UpdateAllHistoryPreference( sharedPreferenceUtil, - true)) + true + ) + ) } @Test fun `RequestDeleteAllHistoryItems opens dialog to request deletion`() { actionResultsInEffects( - RequestDeleteAllHistoryItems, - ShowDeleteHistoryDialog(viewModel.actions, DeleteAllHistory)) + UserClickedDeleteButton, + ShowDeleteHistoryDialog(viewModel.actions) + ) } @Test fun `RequestDeleteSelectedHistoryItems opens dialog to request deletion`() { actionResultsInEffects( - RequestDeleteSelectedHistoryItems, - ShowDeleteHistoryDialog(viewModel.actions, DeleteSelectedHistory)) + UserClickedDeleteSelectedHistoryItems, + ShowDeleteHistoryDialog(viewModel.actions) + ) } @Test fun `DeleteHistoryItems calls DeleteSelectedOrAllHistoryItems side effect`() { actionResultsInEffects( - DeleteHistoryItems, - DeleteSelectedOrAllHistoryItems(viewModel.state, historyDao)) + UserClickedDelete, + DeleteHistoryItems(viewModel.state.value!!.historyItems, historyDao) + ) } private fun actionResultsInEffects( diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/DeleteSelectedOrAllHistoryItemsTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/DeleteSelectedOrAllHistoryItemsTest.kt index 47599685c..8e74d1f89 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/DeleteSelectedOrAllHistoryItemsTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/DeleteSelectedOrAllHistoryItemsTest.kt @@ -1,6 +1,5 @@ package org.kiwix.kiwixmobile.core.history.viewmodel.effects -import DeleteSelectedOrAllHistoryItems import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.MutableLiveData import io.mockk.every @@ -22,7 +21,7 @@ internal class DeleteSelectedOrAllHistoryItemsTest { every { state.value } returns SelectionResults(listOf(item1, item2)) val historyDao: HistoryDao = mockk() val activity: AppCompatActivity = mockk() - DeleteSelectedOrAllHistoryItems(state, historyDao).invokeWith(activity) + DeleteHistoryItems(state, historyDao).invokeWith(activity) verify { historyDao.deleteHistory(listOf(item1)) } } @@ -34,7 +33,7 @@ internal class DeleteSelectedOrAllHistoryItemsTest { every { state.value } returns SelectionResults(listOf(item1, item2)) val historyDao: HistoryDao = mockk() val activity: AppCompatActivity = mockk() - DeleteSelectedOrAllHistoryItems(state, historyDao).invokeWith(activity) + DeleteHistoryItems(state, historyDao).invokeWith(activity) verify { historyDao.deleteHistory(listOf(item1, item2)) } } } diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ShowDeleteHistoryDialogTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ShowDeleteHistoryDialogTest.kt index 04f4960c2..237e6492f 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ShowDeleteHistoryDialogTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ShowDeleteHistoryDialogTest.kt @@ -8,7 +8,7 @@ import io.reactivex.processors.PublishProcessor import org.junit.jupiter.api.Test import org.kiwix.kiwixmobile.core.history.HistoryActivity import org.kiwix.kiwixmobile.core.history.viewmodel.Action -import org.kiwix.kiwixmobile.core.history.viewmodel.Action.DeleteHistoryItems +import org.kiwix.kiwixmobile.core.history.viewmodel.Action.UserClickedDelete import org.kiwix.kiwixmobile.core.utils.DialogShower import org.kiwix.kiwixmobile.core.utils.KiwixDialog.DeleteSelectedHistory @@ -30,6 +30,6 @@ internal class ShowDeleteHistoryDialogTest { dialogShower.show(DeleteSelectedHistory, capture(lambdaSlot)) } lambdaSlot.captured.invoke() - verify { actions.offer(DeleteHistoryItems) } + verify { actions.offer(UserClickedDelete) } } } diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ToggleShowAllHistorySwitchAndSaveItsStateToPrefsTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ToggleShowAllHistorySwitchAndSaveItsStateToPrefsTest.kt index 85fc8e10e..874beeca8 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ToggleShowAllHistorySwitchAndSaveItsStateToPrefsTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/history/viewmodel/effects/ToggleShowAllHistorySwitchAndSaveItsStateToPrefsTest.kt @@ -15,14 +15,14 @@ internal class ToggleShowAllHistorySwitchAndSaveItsStateToPrefsTest { every { toggleSwitch.offer(true) } returns true val sharedPreferenceUtil: SharedPreferenceUtil = mockk() val activity: AppCompatActivity = mockk() - ToggleShowAllHistorySwitchAndSaveItsStateToPrefs( + UpdateAllHistoryPreference( toggleSwitch, sharedPreferenceUtil, true ).invokeWith(activity) verify { toggleSwitch.offer(true) - sharedPreferenceUtil.showHistoryCurrentBook = false + sharedPreferenceUtil.showHistoryAllBooks = false } } }