mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-06 03:50:23 +02:00
Fix granting extra time for another day after task completion
This commit is contained in:
parent
c2e83bc11a
commit
cba90b69e3
10 changed files with 122 additions and 23 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -45,7 +45,7 @@ interface ChildTaskDao {
|
|||
@Query("SELECT child_task.* FROM child_task JOIN category ON (child_task.category_id = category.id) WHERE category.child_id = :userId")
|
||||
fun getTasksByUserIdSync(userId: String): List<ChildTask>
|
||||
|
||||
@Query("SELECT child_task.*, category.title as category_title, user.name as child_name FROM child_task JOIN category ON (child_task.category_id = category.id) JOIN user ON (category.child_id = user.id) WHERE child_task.pending_request = 1")
|
||||
@Query("SELECT child_task.*, category.title as category_title, user.name as child_name, user.timezone AS child_timezone FROM child_task JOIN category ON (child_task.category_id = category.id) JOIN user ON (category.child_id = user.id) WHERE child_task.pending_request = 1")
|
||||
fun getPendingTasks(): LiveData<List<FullChildTask>>
|
||||
|
||||
@Query("SELECT * FROM child_task WHERE category_id = :categoryId")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -26,5 +26,7 @@ data class FullChildTask(
|
|||
@ColumnInfo(name = "category_title")
|
||||
val categoryTitle: String,
|
||||
@ColumnInfo(name = "child_name")
|
||||
val childName: String
|
||||
val childName: String,
|
||||
@ColumnInfo(name = "child_timezone")
|
||||
val childTimezone: String
|
||||
)
|
|
@ -88,6 +88,7 @@ class AppLogic(
|
|||
val backgroundTaskLogic = BackgroundTaskLogic(this)
|
||||
val appSetupLogic = AppSetupLogic(this)
|
||||
val syncNotificationLogic = SyncNotificationLogic(this)
|
||||
val serverApiLevelLogic = ServerApiLevelLogic(this)
|
||||
|
||||
private val isConnectedInternal = MutableLiveData<Boolean>().apply { value = false }
|
||||
val isConnected = isConnectedInternal.castDown()
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package io.timelimit.android.logic
|
||||
|
||||
import io.timelimit.android.livedata.liveDataFromNonNullValue
|
||||
import io.timelimit.android.livedata.map
|
||||
import io.timelimit.android.livedata.switchMap
|
||||
|
||||
class ServerApiLevelLogic(logic: AppLogic) {
|
||||
val infoLive = logic.database.config().getDeviceAuthTokenAsync().switchMap { authToken ->
|
||||
if (authToken.isEmpty())
|
||||
liveDataFromNonNullValue(ServerApiLevelInfo.Offline)
|
||||
else
|
||||
logic.database.config().getServerApiLevelLive().map { apiLevel ->
|
||||
ServerApiLevelInfo.Online(serverLevel = apiLevel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ServerApiLevelInfo {
|
||||
abstract fun hasLevelOrIsOffline(level: Int): Boolean
|
||||
|
||||
data class Online(val serverLevel: Int): ServerApiLevelInfo() {
|
||||
override fun hasLevelOrIsOffline(level: Int) = serverLevel >= level
|
||||
}
|
||||
|
||||
object Offline: ServerApiLevelInfo() {
|
||||
override fun hasLevelOrIsOffline(level: Int) = true
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -1024,16 +1024,18 @@ data class DeleteChildTaskAction(val taskId: String): ParentAction() {
|
|||
}
|
||||
}
|
||||
|
||||
data class ReviewChildTaskAction(val taskId: String, val ok: Boolean, val time: Long): ParentAction() {
|
||||
data class ReviewChildTaskAction(val taskId: String, val ok: Boolean, val time: Long, val day: Int?): ParentAction() {
|
||||
companion object {
|
||||
private const val TYPE_VALUE = "REVIEW_CHILD_TASK"
|
||||
private const val TASK_ID = "taskId"
|
||||
private const val OK = "ok"
|
||||
private const val TIME = "time"
|
||||
private const val DAY = "day"
|
||||
}
|
||||
|
||||
init {
|
||||
if (time <= 0) throw IllegalArgumentException()
|
||||
if (day != null && day < 0) throw IllegalArgumentException()
|
||||
IdGenerator.assertIdValid(taskId)
|
||||
}
|
||||
|
||||
|
@ -1045,6 +1047,10 @@ data class ReviewChildTaskAction(val taskId: String, val ok: Boolean, val time:
|
|||
writer.name(OK).value(ok)
|
||||
writer.name(TIME).value(time)
|
||||
|
||||
if (day != null) {
|
||||
writer.name(DAY).value(day)
|
||||
}
|
||||
|
||||
writer.endObject()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -841,11 +841,21 @@ object LocalDatabaseParentActionDispatcher {
|
|||
if (action.ok) {
|
||||
val category = database.category().getCategoryByIdSync(task.categoryId)!!
|
||||
|
||||
if (category.extraTimeDay != 0 && category.extraTimeInMillis > 0) {
|
||||
// if the current time is daily, then extend the daily time only
|
||||
database.category().updateCategoryExtraTime(categoryId = category.id, extraTimeDay = category.extraTimeDay, newExtraTime = category.extraTimeInMillis + task.extraTimeDuration)
|
||||
val resetDayBoundExtraTime = category.extraTimeDay != -1 && action.day != null &&
|
||||
category.extraTimeDay != action.day
|
||||
|
||||
if (resetDayBoundExtraTime) {
|
||||
database.category().updateCategoryExtraTime(
|
||||
categoryId = category.id,
|
||||
extraTimeDay = -1,
|
||||
newExtraTime = task.extraTimeDuration.toLong()
|
||||
)
|
||||
} else {
|
||||
database.category().updateCategoryExtraTime(categoryId = category.id, extraTimeDay = -1, newExtraTime = category.extraTimeInMillis + task.extraTimeDuration)
|
||||
database.category().updateCategoryExtraTime(
|
||||
categoryId = category.id,
|
||||
extraTimeDay = category.extraTimeDay,
|
||||
newExtraTime = category.extraTimeInMillis + task.extraTimeDuration
|
||||
)
|
||||
}
|
||||
|
||||
database.childTasks().updateItemSync(task.copy(pendingRequest = false, lastGrantTimestamp = action.time))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -27,14 +27,17 @@ import io.timelimit.android.async.Threads
|
|||
import io.timelimit.android.coroutines.CoroutineFragment
|
||||
import io.timelimit.android.data.model.*
|
||||
import io.timelimit.android.databinding.FragmentOverviewBinding
|
||||
import io.timelimit.android.date.DateInTimezone
|
||||
import io.timelimit.android.livedata.waitForNonNullValue
|
||||
import io.timelimit.android.logic.AppLogic
|
||||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
import io.timelimit.android.logic.ServerApiLevelInfo
|
||||
import io.timelimit.android.sync.actions.ReviewChildTaskAction
|
||||
import io.timelimit.android.ui.main.ActivityViewModel
|
||||
import io.timelimit.android.ui.main.getActivityViewModel
|
||||
import io.timelimit.android.ui.payment.RequiresPurchaseDialogFragment
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
|
||||
class OverviewFragment : CoroutineFragment() {
|
||||
private val handlers: OverviewFragmentParentHandlers by lazy { parentFragment as OverviewFragmentParentHandlers }
|
||||
|
@ -97,13 +100,17 @@ class OverviewFragment : CoroutineFragment() {
|
|||
model.showMoreDevices(level)
|
||||
}
|
||||
|
||||
override fun onTaskConfirmed(task: ChildTask, hasPremium: Boolean) {
|
||||
override fun onTaskConfirmed(task: ChildTask, hasPremium: Boolean, timezone: TimeZone, serverApiLevel: ServerApiLevelInfo) {
|
||||
if (hasPremium) {
|
||||
val time = logic.timeApi.getCurrentTimeInMillis()
|
||||
val day = DateInTimezone.newInstance(time, timezone).dayOfEpoch
|
||||
|
||||
auth.tryDispatchParentAction(
|
||||
ReviewChildTaskAction(
|
||||
taskId = task.taskId,
|
||||
ok = true,
|
||||
time = logic.timeApi.getCurrentTimeInMillis()
|
||||
time = time,
|
||||
day = if (serverApiLevel.hasLevelOrIsOffline(2)) day else null
|
||||
)
|
||||
)
|
||||
} else RequiresPurchaseDialogFragment().show(parentFragmentManager)
|
||||
|
@ -114,7 +121,8 @@ class OverviewFragment : CoroutineFragment() {
|
|||
ReviewChildTaskAction(
|
||||
taskId = task.taskId,
|
||||
ok = false,
|
||||
time = logic.timeApi.getCurrentTimeInMillis()
|
||||
time = logic.timeApi.getCurrentTimeInMillis(),
|
||||
day = null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -26,8 +26,10 @@ import io.timelimit.android.data.model.Device
|
|||
import io.timelimit.android.data.model.User
|
||||
import io.timelimit.android.data.model.UserType
|
||||
import io.timelimit.android.databinding.*
|
||||
import io.timelimit.android.logic.ServerApiLevelInfo
|
||||
import io.timelimit.android.ui.util.DateUtil
|
||||
import io.timelimit.android.util.TimeTextUtil
|
||||
import java.util.*
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class OverviewFragmentAdapter : RecyclerView.Adapter<OverviewFragmentViewHolder>() {
|
||||
|
@ -260,7 +262,15 @@ class OverviewFragmentAdapter : RecyclerView.Adapter<OverviewFragmentViewHolder>
|
|||
it.lastGrant = if (item.task.lastGrantTimestamp == 0L) null else DateUtil.formatAbsoluteDate(it.root.context, item.task.lastGrantTimestamp)
|
||||
it.taskTitle = item.task.taskTitle
|
||||
|
||||
it.yesButton.setOnClickListener { handlers?.onTaskConfirmed(task = item.task, hasPremium = item.hasPremium) }
|
||||
it.yesButton.setOnClickListener {
|
||||
handlers?.onTaskConfirmed(
|
||||
task = item.task,
|
||||
hasPremium = item.hasPremium,
|
||||
timezone = item.childTimezone,
|
||||
serverApiLevel = item.serverApiLevel
|
||||
)
|
||||
}
|
||||
|
||||
it.noButton.setOnClickListener { handlers?.onTaskRejected(item.task) }
|
||||
it.skipButton.setOnClickListener { handlers?.onSkipTaskReviewClicked(item.task) }
|
||||
}
|
||||
|
@ -305,6 +315,6 @@ interface OverviewFragmentHandlers {
|
|||
fun onShowAllUsersClicked()
|
||||
fun onSetDeviceListVisibility(level: DeviceListItemVisibility)
|
||||
fun onSkipTaskReviewClicked(task: ChildTask)
|
||||
fun onTaskConfirmed(task: ChildTask, hasPremium: Boolean)
|
||||
fun onTaskConfirmed(task: ChildTask, hasPremium: Boolean, timezone: TimeZone, serverApiLevel: ServerApiLevelInfo)
|
||||
fun onTaskRejected(task: ChildTask)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -20,6 +20,8 @@ import io.timelimit.android.data.model.Device
|
|||
import io.timelimit.android.data.model.User
|
||||
import io.timelimit.android.data.model.UserType
|
||||
import io.timelimit.android.integration.platform.RuntimePermissionStatus
|
||||
import io.timelimit.android.logic.ServerApiLevelInfo
|
||||
import java.util.*
|
||||
|
||||
sealed class OverviewFragmentItem
|
||||
object OverviewFragmentHeaderUsers: OverviewFragmentItem()
|
||||
|
@ -41,4 +43,11 @@ sealed class ShowMoreOverviewFragmentItem: OverviewFragmentItem() {
|
|||
object ShowAllUsers: ShowMoreOverviewFragmentItem()
|
||||
data class ShowMoreDevices(val level: DeviceListItemVisibility): ShowMoreOverviewFragmentItem()
|
||||
}
|
||||
data class TaskReviewOverviewItem(val task: ChildTask, val childTitle: String, val categoryTitle: String, val hasPremium: Boolean): OverviewFragmentItem()
|
||||
data class TaskReviewOverviewItem(
|
||||
val task: ChildTask,
|
||||
val childTitle: String,
|
||||
val categoryTitle: String,
|
||||
val hasPremium: Boolean,
|
||||
val childTimezone: TimeZone,
|
||||
val serverApiLevel: ServerApiLevelInfo
|
||||
): OverviewFragmentItem()
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -26,6 +26,7 @@ import io.timelimit.android.livedata.liveDataFromFunction
|
|||
import io.timelimit.android.livedata.map
|
||||
import io.timelimit.android.livedata.switchMap
|
||||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
import java.util.*
|
||||
|
||||
class OverviewFragmentModel(application: Application): AndroidViewModel(application) {
|
||||
private val logic = DefaultAppLogic.with(application)
|
||||
|
@ -124,9 +125,18 @@ class OverviewFragmentModel(application: Application): AndroidViewModel(applicat
|
|||
}
|
||||
private val hasPremiumLive = logic.fullVersion.shouldProvideFullVersionFunctions
|
||||
private val pendingTaskItemLive = hasPremiumLive.switchMap { hasPremium ->
|
||||
logic.serverApiLevelLogic.infoLive.switchMap { serverApiLevel ->
|
||||
pendingTasksToShowLive.map { tasks ->
|
||||
tasks.firstOrNull()?.let {
|
||||
TaskReviewOverviewItem(task = it.childTask, childTitle = it.childName, categoryTitle = it.categoryTitle, hasPremium = hasPremium)
|
||||
TaskReviewOverviewItem(
|
||||
task = it.childTask,
|
||||
childTitle = it.childName,
|
||||
categoryTitle = it.categoryTitle,
|
||||
hasPremium = hasPremium,
|
||||
childTimezone = TimeZone.getTimeZone(it.childTimezone),
|
||||
serverApiLevel = serverApiLevel
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue