mirror of https://github.com/tasks/tasks
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
5.6 KiB
Kotlin
171 lines
5.6 KiB
Kotlin
package org.tasks.ui
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.content.BroadcastReceiver
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import androidx.lifecycle.ViewModel
|
|
import androidx.lifecycle.viewModelScope
|
|
import com.todoroo.andlib.utility.DateUtilities
|
|
import com.todoroo.astrid.api.Filter
|
|
import com.todoroo.astrid.api.FilterImpl
|
|
import com.todoroo.astrid.api.SearchFilter
|
|
import com.todoroo.astrid.core.BuiltInFilterExposer
|
|
import com.todoroo.astrid.data.Task
|
|
import com.todoroo.astrid.service.TaskDeleter
|
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
import kotlinx.coroutines.flow.asStateFlow
|
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
|
import kotlinx.coroutines.flow.filter
|
|
import kotlinx.coroutines.flow.flowOn
|
|
import kotlinx.coroutines.flow.launchIn
|
|
import kotlinx.coroutines.flow.map
|
|
import kotlinx.coroutines.flow.onEach
|
|
import kotlinx.coroutines.flow.update
|
|
import kotlinx.coroutines.launch
|
|
import org.tasks.LocalBroadcastManager
|
|
import org.tasks.R
|
|
import org.tasks.analytics.Firebase
|
|
import org.tasks.billing.Inventory
|
|
import org.tasks.compose.throttleLatest
|
|
import org.tasks.data.DeletionDao
|
|
import org.tasks.data.TaskContainer
|
|
import org.tasks.data.TaskDao
|
|
import org.tasks.data.TaskListQuery.getQuery
|
|
import org.tasks.db.QueryUtils
|
|
import org.tasks.preferences.Preferences
|
|
import org.tasks.preferences.QueryPreferences
|
|
import javax.inject.Inject
|
|
|
|
@HiltViewModel
|
|
@SuppressLint("StaticFieldLeak")
|
|
class TaskListViewModel @Inject constructor(
|
|
@ApplicationContext context: Context,
|
|
private val preferences: Preferences,
|
|
private val taskDao: TaskDao,
|
|
private val taskDeleter: TaskDeleter,
|
|
private val deletionDao: DeletionDao,
|
|
private val localBroadcastManager: LocalBroadcastManager,
|
|
private val inventory: Inventory,
|
|
private val firebase: Firebase,
|
|
) : ViewModel() {
|
|
|
|
sealed interface TasksResults {
|
|
data object Loading : TasksResults
|
|
data class Results(val tasks: List<TaskContainer>) : TasksResults
|
|
}
|
|
|
|
data class State(
|
|
val filter: Filter? = null,
|
|
val now: Long = DateUtilities.now(),
|
|
val searchQuery: String? = null,
|
|
val tasks: TasksResults = TasksResults.Loading,
|
|
val begForSubscription: Boolean = false,
|
|
val syncOngoing: Boolean = false,
|
|
)
|
|
|
|
private val _state = MutableStateFlow(State())
|
|
val state = _state.asStateFlow()
|
|
|
|
private val refreshReceiver = object : BroadcastReceiver() {
|
|
override fun onReceive(context: Context?, intent: Intent?) {
|
|
invalidate()
|
|
}
|
|
}
|
|
|
|
fun setFilter(filter: Filter) {
|
|
_state.update {
|
|
it.copy(filter = filter)
|
|
}
|
|
}
|
|
|
|
fun setSearchQuery(query: String?) {
|
|
_state.update { it.copy(searchQuery = query?.trim()) }
|
|
}
|
|
|
|
fun invalidate() {
|
|
_state.update {
|
|
it.copy(
|
|
now = DateUtilities.now(),
|
|
syncOngoing = preferences.isSyncOngoing,
|
|
)
|
|
}
|
|
}
|
|
|
|
fun dismissBanner(clickedPurchase: Boolean) {
|
|
_state.update {
|
|
it.copy(begForSubscription = false)
|
|
}
|
|
preferences.lastSubscribeRequest = DateUtilities.now()
|
|
firebase.logEvent(R.string.event_banner_sub, R.string.param_click to clickedPurchase)
|
|
}
|
|
|
|
suspend fun getTasksToClear(): List<Long> {
|
|
val filter = _state.value.filter ?: return emptyList()
|
|
val deleteFilter = FilterImpl(
|
|
sql = QueryUtils.removeOrder(QueryUtils.showHiddenAndCompleted(filter.sql!!)),
|
|
)
|
|
val completed = taskDao.fetchTasks(
|
|
object : QueryPreferences by preferences {
|
|
override val showCompleted: Boolean
|
|
get() = true
|
|
},
|
|
deleteFilter
|
|
)
|
|
.filter(TaskContainer::isCompleted)
|
|
.filterNot(TaskContainer::isReadOnly)
|
|
.map(TaskContainer::id)
|
|
.toMutableList()
|
|
completed.removeAll(deletionDao.hasRecurringAncestors(completed))
|
|
return completed
|
|
}
|
|
|
|
suspend fun markDeleted(tasks: List<Long>): List<Task> =
|
|
taskDeleter.markDeleted(tasks)
|
|
|
|
init {
|
|
localBroadcastManager.registerRefreshReceiver(refreshReceiver)
|
|
|
|
_state
|
|
.filter { it.filter != null }
|
|
.map { it.copy(tasks = TasksResults.Loading) }
|
|
.distinctUntilChanged()
|
|
.throttleLatest(333)
|
|
.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 ->
|
|
_state.update {
|
|
it.copy(tasks = TasksResults.Results(tasks))
|
|
}
|
|
}
|
|
.flowOn(Dispatchers.Default)
|
|
.launchIn(viewModelScope)
|
|
|
|
viewModelScope.launch(Dispatchers.Default) {
|
|
if (!inventory.hasPro && !firebase.subscribeCooldown) {
|
|
_state.update {
|
|
it.copy(begForSubscription = true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun onCleared() {
|
|
localBroadcastManager.unregisterReceiver(refreshReceiver)
|
|
}
|
|
|
|
companion object {
|
|
fun Context.createSearchQuery(query: String): Filter =
|
|
SearchFilter(getString(R.string.FLA_search_filter, query), query)
|
|
}
|
|
}
|