diff --git a/wearos/src/main/java/com/habitrpg/wearos/habitica/modules/AppModule.kt b/wearos/src/main/java/com/habitrpg/wearos/habitica/modules/AppModule.kt index c1dab7e00..74e670bc2 100644 --- a/wearos/src/main/java/com/habitrpg/wearos/habitica/modules/AppModule.kt +++ b/wearos/src/main/java/com/habitrpg/wearos/habitica/modules/AppModule.kt @@ -45,19 +45,24 @@ class AppModule { } @Provides - fun providesConverterFactory(): Converter.Factory { + fun providesConverterFactory(moshi: Moshi): Converter.Factory { return MoshiConverterFactory.create( - Moshi.Builder() - .add(WrappedTasklistAdapter()) - .add(customDateAdapter) - .add(FrequencyAdapter()) - .add(TaskTypeAdapter()) - .add(AttributeAdapter()) - .addLast(KotlinJsonAdapterFactory()) - .build() + moshi ).asLenient() } + @Provides + fun providesMoshi(): Moshi { + return Moshi.Builder() + .add(WrappedTasklistAdapter()) + .add(customDateAdapter) + .add(FrequencyAdapter()) + .add(TaskTypeAdapter()) + .add(AttributeAdapter()) + .addLast(KotlinJsonAdapterFactory()) + .build() + } + @Provides @Singleton fun providesApiHClient( diff --git a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/TaskListActivity.kt b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/TaskListActivity.kt index e27ecbda2..d3fefdfac 100644 --- a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/TaskListActivity.kt +++ b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/activities/TaskListActivity.kt @@ -67,7 +67,9 @@ class TaskListActivity : BaseActivity Unit) { + //Clear shared pref values for saved to-do tasks + sharedPreferences.edit { putString("to_do_tasks", null) } viewModelScope.launch(exceptionBuilder.userFacing(this)) { appStateManager.startLoading() for (task in tasksToComplete) { diff --git a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/viewmodels/TaskListViewModel.kt b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/viewmodels/TaskListViewModel.kt index a69ff7478..4fc09fc5d 100644 --- a/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/viewmodels/TaskListViewModel.kt +++ b/wearos/src/main/java/com/habitrpg/wearos/habitica/ui/viewmodels/TaskListViewModel.kt @@ -1,5 +1,7 @@ package com.habitrpg.wearos.habitica.ui.viewmodels +import android.content.SharedPreferences +import androidx.core.content.edit import androidx.lifecycle.MutableLiveData import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.asLiveData @@ -12,11 +14,15 @@ import com.habitrpg.wearos.habitica.data.repositories.UserRepository import com.habitrpg.wearos.habitica.managers.AppStateManager import com.habitrpg.wearos.habitica.models.tasks.Task import com.habitrpg.wearos.habitica.util.ExceptionHandlerBuilder +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch +import java.lang.reflect.Type import javax.inject.Inject @HiltViewModel @@ -24,26 +30,27 @@ class TaskListViewModel @Inject constructor( savedStateHandle: SavedStateHandle, taskRepository: TaskRepository, userRepository: UserRepository, + val sharedPreferences: SharedPreferences, + moshi: Moshi, exceptionBuilder: ExceptionHandlerBuilder, appStateManager: AppStateManager ) : BaseViewModel(userRepository, taskRepository, exceptionBuilder, appStateManager) { + var type: Type = Types.newParameterizedType( + MutableList::class.java, + Task::class.java + ) + private val moshiAdapter: JsonAdapter> = moshi.adapter(type) val taskType = TaskType.from(savedStateHandle.get("task_type")) val taskCount = MutableLiveData(0) + val completedToDos: MutableList by lazy { + val tasksString = sharedPreferences.getString("to_do_tasks", null) ?: return@lazy mutableListOf() + return@lazy moshiAdapter.fromJson(tasksString) ?: mutableListOf() + } val tasks = taskRepository.getTasks(taskType ?: TaskType.HABIT) .map { - if (taskType == TaskType.DAILY || taskType == TaskType.TODO) { - val taskList: MutableList = it.filter { it.isDue == true || it.type == TaskType.TODO }.sortedBy { it.completed }.toMutableList() - val firstCompletedIndex = taskList.indexOfFirst { it is Task && it.completed } - if (firstCompletedIndex >= 0) { - // since this is the index of the first completed task, this is also the number of incomplete tasks - taskCount.value = firstCompletedIndex - taskList.add(firstCompletedIndex, "Done today") - } else { - taskCount.value = taskList.size - } - taskList - } else { - taskCount.value = it.size - it + when(taskType) { + TaskType.DAILY -> mapDaily(it) + TaskType.TODO -> mapTodos(it) + else -> map(it) } } .asLiveData() @@ -51,7 +58,22 @@ class TaskListViewModel @Inject constructor( .asLiveData() fun scoreTask(task: Task, direction: TaskDirection, onResult: (TaskScoringResult?) -> Unit) { - viewModelScope.launch(exceptionBuilder.userFacing(this)) { + if (taskType == TaskType.TODO) { + if (direction == TaskDirection.UP) { + completedToDos.add(task) + } else { + completedToDos.remove(task) + } + } + viewModelScope.launch(exceptionBuilder.userFacing(this) { + if (taskType == TaskType.TODO) { + if (direction == TaskDirection.UP) { + completedToDos.remove(task) + } else { + completedToDos.add(task) + } + } + }) { val result = taskRepository.scoreTask( userRepository.getUser().first(), task, @@ -65,4 +87,45 @@ class TaskListViewModel @Inject constructor( onResult(result) } } + + private fun map(tasks: List): List { + taskCount.value = tasks.size + return tasks + } + + private fun mapDaily(tasks: List): MutableList { + val taskList: MutableList = tasks.filter { it.isDue == true || it.type == TaskType.TODO }.sortedBy { it.completed }.toMutableList() + val firstCompletedIndex = taskList.indexOfFirst { it is Task && it.completed } + if (firstCompletedIndex >= 0) { + // since this is the index of the first completed task, this is also the number of incomplete tasks + taskCount.value = firstCompletedIndex + taskList.add(firstCompletedIndex, "Done today") + } else { + taskCount.value = taskList.size + } + return taskList + } + + override fun onCleared() { + saveCurrentToDos() + super.onCleared() + } + + private fun mapTodos(tasks: List): List { + val taskList: MutableList = tasks.filter { !it.completed }.toMutableList() + taskCount.value = taskList.size + if (completedToDos.isNotEmpty()) { + taskList.add("Done today") + taskList.addAll(completedToDos) + } + + return taskList + } + + private fun saveCurrentToDos() { + sharedPreferences.edit { + putString("to_do_tasks", moshiAdapter.toJson(completedToDos)) + } + } + } \ No newline at end of file diff --git a/wearos/src/main/java/com/habitrpg/wearos/habitica/util/HabiticaCoroutineExceptionhandler.kt b/wearos/src/main/java/com/habitrpg/wearos/habitica/util/HabiticaCoroutineExceptionhandler.kt index 45a722f22..6ac8d780e 100644 --- a/wearos/src/main/java/com/habitrpg/wearos/habitica/util/HabiticaCoroutineExceptionhandler.kt +++ b/wearos/src/main/java/com/habitrpg/wearos/habitica/util/HabiticaCoroutineExceptionhandler.kt @@ -18,7 +18,7 @@ class ExceptionHandlerBuilder @Inject constructor(val appStateManager: AppStateM } } - fun userFacing(errorPresenter: ErrorPresenter): CoroutineExceptionHandler { + fun userFacing(errorPresenter: ErrorPresenter, handler: ((Throwable) -> Unit)? = null): CoroutineExceptionHandler { return CoroutineExceptionHandler { _, throwable -> Log.e("Coroutine Error", "Error: ${throwable.cause}", throwable) if (throwable is IOException) { @@ -30,7 +30,7 @@ class ExceptionHandlerBuilder @Inject constructor(val appStateManager: AppStateM DisplayedError(R.drawable.error, it) } } - + handler?.invoke(throwable) appStateManager.endLoading() } }