Move subscription begging to viewmodel

pull/2536/head
Alex Baker 9 months ago
parent ea8a4b5e2d
commit db66a66578

@ -57,8 +57,6 @@ import org.tasks.ui.MainActivityEvent
import org.tasks.ui.MainActivityEventBus
import org.tasks.ui.NavigationDrawerFragment
import org.tasks.ui.NavigationDrawerFragment.Companion.newNavigationDrawer
import org.tasks.ui.TaskListEvent
import org.tasks.ui.TaskListEventBus
import timber.log.Timber
import javax.inject.Inject
@ -76,7 +74,6 @@ class MainActivity : AppCompatActivity(), TaskListFragmentCallbackHandler, Timer
@Inject lateinit var tagDataDao: TagDataDao
@Inject lateinit var alarmDao: AlarmDao
@Inject lateinit var eventBus: MainActivityEventBus
@Inject lateinit var taskListEventBus: TaskListEventBus
@Inject lateinit var playServices: PlayServices
@Inject lateinit var firebase: Firebase
@ -476,16 +473,6 @@ class MainActivity : AppCompatActivity(), TaskListFragmentCallbackHandler, Timer
actionMode = null
}
override fun onStart() {
super.onStart()
lifecycleScope.launch {
if (!inventory.hasPro && !firebase.subscribeCooldown) {
taskListEventBus.tryEmit(TaskListEvent.BegForSubscription)
}
}
}
companion object {
/** For indicating the new list screen should be launched at fragment setup time */
const val TOKEN_CREATE_NEW_LIST_NAME = "newListName" // $NON-NLS-1$

@ -24,10 +24,6 @@ import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.Toolbar
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.app.ShareCompat
import androidx.core.view.forEach
@ -49,7 +45,6 @@ import com.google.android.material.composethemeadapter.MdcTheme
import com.google.android.material.snackbar.Snackbar
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.andlib.utility.DateUtilities.now
import com.todoroo.astrid.adapter.TaskAdapter
import com.todoroo.astrid.adapter.TaskAdapterProvider
import com.todoroo.astrid.api.AstridApiConstants.EXTRAS_OLD_DUE_DATE
@ -82,15 +77,14 @@ import kotlinx.coroutines.withContext
import org.tasks.LocalBroadcastManager
import org.tasks.R
import org.tasks.ShortcutManager
import org.tasks.Tasks.Companion.IS_GOOGLE_PLAY
import org.tasks.activities.FilterSettingsActivity
import org.tasks.activities.GoogleTaskListSettingsActivity
import org.tasks.activities.PlaceSettingsActivity
import org.tasks.activities.TagSettingsActivity
import org.tasks.analytics.Firebase
import org.tasks.billing.PurchaseActivity
import org.tasks.caldav.BaseCaldavCalendarSettingsActivity
import org.tasks.compose.SubscriptionNagBanner
import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.data.CaldavDao
import org.tasks.data.TagDataDao
import org.tasks.data.TaskContainer
@ -179,7 +173,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
private lateinit var callbacks: TaskListFragmentCallbackHandler
private lateinit var binding: FragmentTaskListBinding
@OptIn(ExperimentalAnimationApi::class)
private fun process(event: TaskListEvent) = when (event) {
is TaskListEvent.TaskCreated ->
onTaskCreated(event.uuid)
@ -187,36 +180,6 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
makeSnackbar(R.string.calendar_event_created, event.title)
?.setAction(R.string.action_open) { context?.openUri(event.uri) }
?.show()
is TaskListEvent.BegForSubscription -> {
binding.banner.setContent {
var showBanner by rememberSaveable { mutableStateOf(true) }
MdcTheme {
SubscriptionNagBanner(
visible = showBanner,
subscribe = {
showBanner = false
preferences.lastSubscribeRequest = now()
purchase()
firebase.logEvent(R.string.event_banner_sub, R.string.param_click to true)
},
dismiss = {
showBanner = false
preferences.lastSubscribeRequest = now()
firebase.logEvent(R.string.event_banner_sub, R.string.param_click to false)
},
)
}
}
}
}
private fun purchase() {
if (IS_GOOGLE_PLAY) {
startActivity(Intent(context, PurchaseActivity::class.java))
} else {
preferences.lastSubscribeRequest = now()
context?.openUri(R.string.url_donate)
}
}
override fun onRefresh() {
@ -266,6 +229,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
.launchIn(viewLifecycleOwner.lifecycleScope)
}
@OptIn(ExperimentalAnimationApi::class)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FragmentTaskListBinding.inflate(inflater, container, false)
@ -292,9 +256,9 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
recyclerView.layoutManager = LinearLayoutManager(context)
lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
listViewModel.tasks.collect {
submitList(it)
if (it.isEmpty()) {
listViewModel.state.collect {
submitList(it.tasks)
if (it.tasks.isEmpty()) {
swipeRefreshLayout.visibility = View.GONE
emptyRefreshLayout.visibility = View.VISIBLE
} else {
@ -343,6 +307,16 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
}
finishActionMode()
}
binding.banner.setContent {
val showBanner = listViewModel.state.collectAsStateLifecycleAware().value.begForSubscription
MdcTheme {
SubscriptionNagBanner(
visible = showBanner,
subscribe = { listViewModel.dismissBanner(clickedPurchase = true) },
dismiss = { listViewModel.dismissBanner(clickedPurchase = false) },
)
}
}
return binding.root
}

@ -58,7 +58,7 @@ class SubtaskControlSet : TaskEditControlFragment() {
filter = viewModel.selectedList.collectAsStateLifecycleAware().value,
hasParent = viewModel.hasParent,
desaturate = preferences.desaturateDarkMode,
existingSubtasks = listViewModel.tasks.collectAsStateLifecycleAware(initial = emptyList()).value,
existingSubtasks = listViewModel.state.collectAsStateLifecycleAware().value.tasks,
newSubtasks = viewModel.newSubtasks.collectAsStateLifecycleAware().value,
openSubtask = this@SubtaskControlSet::openSubtask,
completeExistingSubtask = this@SubtaskControlSet::complete,

@ -7,5 +7,4 @@ typealias TaskListEventBus = MutableSharedFlow<TaskListEvent>
sealed interface TaskListEvent {
data class TaskCreated(val uuid: String) : TaskListEvent
data class CalendarEventCreated(val title: String?, val uri: String) : TaskListEvent
object BegForSubscription : TaskListEvent
}

@ -1,44 +1,59 @@
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 dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
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.Tasks
import org.tasks.analytics.Firebase
import org.tasks.billing.Inventory
import org.tasks.billing.PurchaseActivity
import org.tasks.compose.throttleLatest
import org.tasks.data.TaskContainer
import org.tasks.data.TaskDao
import org.tasks.data.TaskListQuery.getQuery
import org.tasks.extensions.Context.openUri
import org.tasks.preferences.Preferences
import javax.inject.Inject
@HiltViewModel
@SuppressLint("StaticFieldLeak")
class TaskListViewModel @Inject constructor(
private val preferences: Preferences,
private val taskDao: TaskDao,
private val localBroadcastManager: LocalBroadcastManager,
@ApplicationContext private val context: Context,
private val preferences: Preferences,
private val taskDao: TaskDao,
private val localBroadcastManager: LocalBroadcastManager,
private val inventory: Inventory,
private val firebase: Firebase,
) : ViewModel() {
data class State(
val filter: Filter? = null,
val now: Long = DateUtilities.now(),
val tasks: List<TaskContainer> = emptyList(),
val begForSubscription: Boolean = false,
)
private val _state = MutableStateFlow(State())
val tasks: Flow<List<TaskContainer>> =
_state
.filter { it.filter != null }
.throttleLatest(333)
.map { taskDao.fetchTasks { getQuery(preferences, it.filter!!) } }
val state = _state.asStateFlow()
private val refreshReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
@ -56,8 +71,44 @@ class TaskListViewModel @Inject constructor(
_state.update { it.copy(now = DateUtilities.now()) }
}
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)
if (clickedPurchase) {
if (Tasks.IS_GOOGLE_PLAY) {
context.startActivity(Intent(context, PurchaseActivity::class.java))
} else {
preferences.lastSubscribeRequest = DateUtilities.now()
context.openUri(R.string.url_donate)
}
}
}
init {
localBroadcastManager.registerRefreshReceiver(refreshReceiver)
_state
.filter { it.filter != null }
.throttleLatest(333)
.map { taskDao.fetchTasks { getQuery(preferences, it.filter!!) } }
.onEach { tasks ->
_state.update {
it.copy(tasks = tasks)
}
}
.flowOn(Dispatchers.Default)
.launchIn(viewModelScope)
viewModelScope.launch(Dispatchers.Default) {
if (!inventory.hasPro && !firebase.subscribeCooldown) {
_state.update {
it.copy(begForSubscription = true)
}
}
}
}
override fun onCleared() {

Loading…
Cancel
Save