Move search to viewmodel

pull/2546/head
Alex Baker 9 months ago
parent e70f5f3b24
commit 94b6d7569b

@ -55,7 +55,6 @@ import com.todoroo.astrid.api.CaldavFilter
import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.GtasksFilter import com.todoroo.astrid.api.GtasksFilter
import com.todoroo.astrid.api.IdListFilter import com.todoroo.astrid.api.IdListFilter
import com.todoroo.astrid.api.SearchFilter
import com.todoroo.astrid.api.TagFilter import com.todoroo.astrid.api.TagFilter
import com.todoroo.astrid.core.BuiltInFilterExposer import com.todoroo.astrid.core.BuiltInFilterExposer
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
@ -69,9 +68,7 @@ import com.todoroo.astrid.service.TaskMover
import com.todoroo.astrid.timers.TimerPlugin import com.todoroo.astrid.timers.TimerPlugin
import com.todoroo.astrid.utility.Flags import com.todoroo.astrid.utility.Flags
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -119,6 +116,7 @@ import org.tasks.ui.TaskEditEventBus
import org.tasks.ui.TaskListEvent import org.tasks.ui.TaskListEvent
import org.tasks.ui.TaskListEventBus import org.tasks.ui.TaskListEventBus
import org.tasks.ui.TaskListViewModel import org.tasks.ui.TaskListViewModel
import org.tasks.ui.TaskListViewModel.Companion.createSearchQuery
import java.time.format.FormatStyle import java.time.format.FormatStyle
import java.util.Locale import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
@ -159,9 +157,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
private lateinit var taskAdapter: TaskAdapter private lateinit var taskAdapter: TaskAdapter
private var recyclerAdapter: DragAndDropRecyclerAdapter? = null private var recyclerAdapter: DragAndDropRecyclerAdapter? = null
private lateinit var filter: Filter private lateinit var filter: Filter
private var searchJob: Job? = null
private lateinit var search: MenuItem private lateinit var search: MenuItem
private var searchQuery: String? = null
private var mode: ActionMode? = null private var mode: ActionMode? = null
lateinit var themeColor: ThemeColor lateinit var themeColor: ThemeColor
private lateinit var callbacks: TaskListFragmentCallbackHandler private lateinit var callbacks: TaskListFragmentCallbackHandler
@ -219,7 +215,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
val selectedTaskIds: List<Long> = taskAdapter.getSelected() val selectedTaskIds: List<Long> = taskAdapter.getSelected()
outState.putLongArray(EXTRA_SELECTED_TASK_IDS, selectedTaskIds.toLongArray()) outState.putLongArray(EXTRA_SELECTED_TASK_IDS, selectedTaskIds.toLongArray())
outState.putString(EXTRA_SEARCH, searchQuery)
outState.putLongArray(EXTRA_COLLAPSED, taskAdapter.getCollapsed().toLongArray()) outState.putLongArray(EXTRA_COLLAPSED, taskAdapter.getCollapsed().toLongArray())
} }
@ -258,10 +253,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
// set up list adapters // set up list adapters
taskAdapter = taskAdapterProvider.createTaskAdapter(filter) taskAdapter = taskAdapterProvider.createTaskAdapter(filter)
taskAdapter.setCollapsed(savedInstanceState?.getLongArray(EXTRA_COLLAPSED)) taskAdapter.setCollapsed(savedInstanceState?.getLongArray(EXTRA_COLLAPSED))
if (savedInstanceState != null) { listViewModel.setFilter(filter)
searchQuery = savedInstanceState.getString(EXTRA_SEARCH)
}
listViewModel.setFilter((if (searchQuery == null) filter else createSearchFilter(searchQuery!!)))
(recyclerView.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false (recyclerView.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
recyclerView.layoutManager = LinearLayoutManager(context) recyclerView.layoutManager = LinearLayoutManager(context)
lifecycleScope.launch { lifecycleScope.launch {
@ -394,25 +386,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
} }
} }
private fun searchByQuery(query: String?) {
searchJob?.cancel()
searchJob = lifecycleScope.launch {
delay(SEARCH_DEBOUNCE_TIMEOUT)
searchQuery = query?.trim { it <= ' ' } ?: ""
if (searchQuery?.isEmpty() == true) {
listViewModel.setFilter(
BuiltInFilterExposer.getMyTasksFilter(requireContext().resources))
} else {
val savedFilter = createSearchFilter(searchQuery!!)
listViewModel.setFilter(savedFilter)
}
}
}
private fun createSearchFilter(query: String): Filter {
return SearchFilter(getString(R.string.FLA_search_filter, query), query)
}
override fun onMenuItemClick(item: MenuItem): Boolean { override fun onMenuItemClick(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.menu_voice_add -> { R.id.menu_voice_add -> {
@ -657,9 +630,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
override fun onMenuItemActionExpand(item: MenuItem): Boolean { override fun onMenuItemActionExpand(item: MenuItem): Boolean {
onBackPressed.isEnabled = true onBackPressed.isEnabled = true
search.setOnQueryTextListener(this) search.setOnQueryTextListener(this)
if (searchQuery == null) { listViewModel.setSearchQuery("")
searchByQuery("")
}
if (preferences.isTopAppBar) { if (preferences.isTopAppBar) {
binding.toolbar.menu.forEach { it.isVisible = false } binding.toolbar.menu.forEach { it.isVisible = false }
} }
@ -670,8 +641,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
onBackPressed.isEnabled = false onBackPressed.isEnabled = false
search.setOnQueryTextListener(null) search.setOnQueryTextListener(null)
listViewModel.setFilter(filter) listViewModel.setFilter(filter)
searchJob?.cancel() listViewModel.setSearchQuery(null)
searchQuery = null
if (preferences.isTopAppBar) { if (preferences.isTopAppBar) {
setupMenu(binding.toolbar) setupMenu(binding.toolbar)
} }
@ -679,13 +649,13 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
} }
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
openFilter(createSearchFilter(query.trim { it <= ' ' })) openFilter(requireContext().createSearchQuery(query.trim()))
search.collapseActionView() search.collapseActionView()
return true return true
} }
override fun onQueryTextChange(query: String): Boolean { override fun onQueryTextChange(query: String): Boolean {
searchByQuery(query) listViewModel.setSearchQuery(query)
return true return true
} }
@ -988,7 +958,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
const val ACTION_RELOAD = "action_reload" const val ACTION_RELOAD = "action_reload"
const val ACTION_DELETED = "action_deleted" const val ACTION_DELETED = "action_deleted"
private const val EXTRA_SELECTED_TASK_IDS = "extra_selected_task_ids" private const val EXTRA_SELECTED_TASK_IDS = "extra_selected_task_ids"
private const val EXTRA_SEARCH = "extra_search"
private const val EXTRA_COLLAPSED = "extra_collapsed" private const val EXTRA_COLLAPSED = "extra_collapsed"
private const val VOICE_RECOGNITION_REQUEST_CODE = 1234 private const val VOICE_RECOGNITION_REQUEST_CODE = 1234
private const val EXTRA_FILTER = "extra_filter" private const val EXTRA_FILTER = "extra_filter"
@ -997,7 +966,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
private const val FRAG_TAG_PRIORITY_PICKER = "frag_tag_priority_picker" private const val FRAG_TAG_PRIORITY_PICKER = "frag_tag_priority_picker"
private const val REQUEST_LIST_SETTINGS = 10101 private const val REQUEST_LIST_SETTINGS = 10101
private const val REQUEST_TAG_TASKS = 10106 private const val REQUEST_TAG_TASKS = 10106
private const val SEARCH_DEBOUNCE_TIMEOUT = 300L
fun newTaskListFragment(context: Context, filter: Filter?): TaskListFragment { fun newTaskListFragment(context: Context, filter: Filter?): TaskListFragment {
val fragment = TaskListFragment() val fragment = TaskListFragment()
val bundle = Bundle() val bundle = Bundle()

@ -8,6 +8,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.todoroo.andlib.utility.DateUtilities import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.SearchFilter
import com.todoroo.astrid.core.BuiltInFilterExposer
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -48,6 +50,7 @@ class TaskListViewModel @Inject constructor(
data class State( data class State(
val filter: Filter? = null, val filter: Filter? = null,
val now: Long = DateUtilities.now(), val now: Long = DateUtilities.now(),
val searchQuery: String? = null,
val tasks: List<TaskContainer> = emptyList(), val tasks: List<TaskContainer> = emptyList(),
val begForSubscription: Boolean = false, val begForSubscription: Boolean = false,
val syncOngoing: Boolean = false, val syncOngoing: Boolean = false,
@ -68,6 +71,10 @@ class TaskListViewModel @Inject constructor(
} }
} }
fun setSearchQuery(query: String?) {
_state.update { it.copy(searchQuery = query?.trim()) }
}
fun invalidate() { fun invalidate() {
_state.update { _state.update {
it.copy( it.copy(
@ -99,7 +106,14 @@ class TaskListViewModel @Inject constructor(
_state _state
.filter { it.filter != null } .filter { it.filter != null }
.throttleLatest(333) .throttleLatest(333)
.map { taskDao.fetchTasks { getQuery(preferences, it.filter!!) } } .map {
val filter = when {
it.searchQuery == null -> it.filter!!
it.searchQuery.isBlank() -> BuiltInFilterExposer.getMyTasksFilter(context.resources)
else -> context.createSearchQuery(it.searchQuery)
}
taskDao.fetchTasks { getQuery(preferences, filter) }
}
.onEach { tasks -> .onEach { tasks ->
_state.update { _state.update {
it.copy(tasks = tasks) it.copy(tasks = tasks)
@ -120,4 +134,9 @@ class TaskListViewModel @Inject constructor(
override fun onCleared() { override fun onCleared() {
localBroadcastManager.unregisterReceiver(refreshReceiver) localBroadcastManager.unregisterReceiver(refreshReceiver)
} }
companion object {
fun Context.createSearchQuery(query: String): Filter =
SearchFilter(getString(R.string.FLA_search_filter, query), query)
}
} }

Loading…
Cancel
Save