Skip to content

Commit

Permalink
add category daily goal count
Browse files Browse the repository at this point in the history
  • Loading branch information
Razeeman committed Nov 5, 2023
1 parent b76a99b commit 367757b
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class GetCurrentRecordsDurationInteractor @Inject constructor(
val range = getRange(RangeLength.Day)
val rangeRecords = recordInteractor.getFromRange(range)

return typesMap.map { (typeId, type) ->
return typesMap.map { (typeId, _) ->
typeId to getRangeCurrent(
typeId = typeId,
runningRecord = runningRecords.firstOrNull { it.id == typeId },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ interface RecordTypeGoalDao {
@Query("SELECT * FROM recordTypeGoals WHERE category_id = :categoryId")
suspend fun getByCategory(categoryId: Long): List<RecordTypeGoalDBO>

@Transaction
@Query("SELECT * FROM recordTypeGoals WHERE category_id IN (:categoryIds)")
suspend fun getByCategories(categoryIds: List<Long>): List<RecordTypeGoalDBO>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(recordTypeGoal: RecordTypeGoalDBO): Long

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ class RecordTypeGoalRepoImpl @Inject constructor(
.map(mapper::map)
}

override suspend fun getByCategories(categoryIds: List<Long>): List<RecordTypeGoal> = withContext(Dispatchers.IO) {
Timber.d("getByCategories")
dao.getByCategories(categoryIds)
.map(mapper::map)
}

override suspend fun add(recordTypeGoal: RecordTypeGoal): Long = withContext(Dispatchers.IO) {
Timber.d("add")
return@withContext dao.insert(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ class RecordTypeGoalInteractor @Inject constructor(
return repo.getByCategory(categoryId)
}

suspend fun getByCategories(categoryIds: List<Long>): List<RecordTypeGoal> {
return repo.getByCategories(categoryIds)
}

suspend fun add(recordTypeGoal: RecordTypeGoal) {
repo.add(recordTypeGoal)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ interface RecordTypeGoalRepo {

suspend fun getByCategory(categoryId: Long): List<RecordTypeGoal>

suspend fun getByCategories(categoryIds: List<Long>): List<RecordTypeGoal>

suspend fun add(recordTypeGoal: RecordTypeGoal): Long

suspend fun remove(id: Long)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import com.example.util.simpletimetracker.domain.extension.getMonthlyCount
import com.example.util.simpletimetracker.domain.extension.getWeeklyCount
import com.example.util.simpletimetracker.domain.extension.value
import com.example.util.simpletimetracker.domain.interactor.NotificationGoalCountInteractor
import com.example.util.simpletimetracker.domain.interactor.RecordTypeCategoryInteractor
import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor
import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor
import com.example.util.simpletimetracker.domain.interactor.RunningRecordInteractor
import com.example.util.simpletimetracker.domain.model.RecordType
import com.example.util.simpletimetracker.domain.model.RecordTypeCategory
import com.example.util.simpletimetracker.domain.model.RecordTypeGoal
import com.example.util.simpletimetracker.feature_notification.goalTime.manager.NotificationGoalTimeManager
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import javax.inject.Inject

class NotificationGoalCountInteractorImpl @Inject constructor(
Expand All @@ -22,14 +23,29 @@ class NotificationGoalCountInteractorImpl @Inject constructor(
private val getCurrentRecordsDurationInteractor: GetCurrentRecordsDurationInteractor,
private val manager: NotificationGoalTimeManager,
private val notificationGoalParamsInteractor: NotificationGoalParamsInteractor,
private val recordTypeCategoryInteractor: RecordTypeCategoryInteractor,
) : NotificationGoalCountInteractor {

override suspend fun checkAndShow(typeId: Long) {
val recordType = recordTypeInteractor.get(typeId)
val runningRecord = runningRecordInteractor.get(typeId)
val goals = recordTypeGoalInteractor.getByType(typeId)
checkAndShowType(typeId)
checkAndShowCategory(typeId)
}

override fun cancel(typeId: Long) {
listOf(
RecordTypeGoal.Range.Session,
RecordTypeGoal.Range.Daily,
RecordTypeGoal.Range.Weekly,
RecordTypeGoal.Range.Monthly,
).forEach {
manager.hide(typeId, it)
}
}

if (recordType == null || runningRecord == null) return
private suspend fun checkAndShowType(typeId: Long) {
val runningRecord = runningRecordInteractor.get(typeId) ?: return
val goals = recordTypeGoalInteractor.getByType(typeId)
if (goals.isEmpty()) return

cancel(typeId)

Expand All @@ -39,7 +55,10 @@ class NotificationGoalCountInteractorImpl @Inject constructor(
val dailyCurrent = getCurrentRecordsDurationInteractor.getDailyCurrent(runningRecord)

if (dailyGoalCount == dailyCurrent.count) {
show(typeId, RecordTypeGoal.Range.Daily)
show(
idData = RecordTypeGoal.IdData.Type(typeId),
goalRange = RecordTypeGoal.Range.Daily,
)
}
}

Expand All @@ -49,7 +68,10 @@ class NotificationGoalCountInteractorImpl @Inject constructor(
val weeklyCurrent = getCurrentRecordsDurationInteractor.getWeeklyCurrent(runningRecord)

if (weeklyGoalCount == weeklyCurrent.count) {
show(typeId, RecordTypeGoal.Range.Weekly)
show(
idData = RecordTypeGoal.IdData.Type(typeId),
goalRange = RecordTypeGoal.Range.Weekly,
)
}
}

Expand All @@ -59,29 +81,77 @@ class NotificationGoalCountInteractorImpl @Inject constructor(
val monthlyCurrent = getCurrentRecordsDurationInteractor.getMonthlyCurrent(runningRecord)

if (monthlyGoalCount == monthlyCurrent.count) {
show(typeId, RecordTypeGoal.Range.Monthly)
show(
idData = RecordTypeGoal.IdData.Type(typeId),
goalRange = RecordTypeGoal.Range.Monthly,
)
}
}
}

override fun cancel(typeId: Long) {
listOf(
RecordTypeGoal.Range.Session,
RecordTypeGoal.Range.Daily,
RecordTypeGoal.Range.Weekly,
RecordTypeGoal.Range.Monthly,
).forEach {
manager.hide(typeId, it)
private suspend fun checkAndShowCategory(typeId: Long) {
// Find all categories that hold this type.
val categories = recordTypeCategoryInteractor.getAll()
.groupBy(RecordTypeCategory::categoryId)
val categoriesWithThisType = categories
.mapValues { it.value.map(RecordTypeCategory::recordTypeId) }
.filterValues { typeId in it }
if (categoriesWithThisType.isEmpty()) return

// Find all goals that set for these categories.
val goals = recordTypeGoalInteractor.getByCategories(categoriesWithThisType.keys.toList())
if (goals.isEmpty()) return

// For each goal check current results.
val runningRecords = runningRecordInteractor.getAll()
val typesMap = recordTypeInteractor.getAll().associateBy(RecordType::id)

// Daily
val dailyGoals = goals.filter {
it.range is RecordTypeGoal.Range.Daily &&
it.type is RecordTypeGoal.Type.Count &&
it.value > 1
}
}
if (dailyGoals.isNotEmpty()) {
val allDailyCurrents = getCurrentRecordsDurationInteractor.getAllDailyCurrents(
typesMap = typesMap,
runningRecords = runningRecords,
)

val dailyCurrents = categoriesWithThisType.mapNotNull { (categoryId, typeIds) ->
val dailyCurrents = allDailyCurrents
.filter { it.key in typeIds }
.values
.toList()
val dailyCurrent = GetCurrentRecordsDurationInteractor.Result(
range = allDailyCurrents.values.firstOrNull()?.range ?: return@mapNotNull null,
duration = dailyCurrents.sumOf { it.duration },
count = dailyCurrents.sumOf { it.count },
durationDiffersFromCurrent = dailyCurrents.any { it.durationDiffersFromCurrent },
)
categoryId to dailyCurrent
}.toMap()

private fun show(typeId: Long, goalRange: RecordTypeGoal.Range) {
GlobalScope.launch {
notificationGoalParamsInteractor.execute(
typeId = typeId,
range = goalRange,
type = NotificationGoalParamsInteractor.Type.Count,
)?.let(manager::show)
dailyGoals.forEach { goal ->
val categoryId = (goal.idData as? RecordTypeGoal.IdData.Category)?.value
?: return@forEach
val current = dailyCurrents[categoryId]?.count
?: return
if (current == goal.value) {
show(
idData = RecordTypeGoal.IdData.Category(categoryId),
goalRange = RecordTypeGoal.Range.Daily,
)
}
}
}
}

private suspend fun show(idData: RecordTypeGoal.IdData, goalRange: RecordTypeGoal.Range) {
notificationGoalParamsInteractor.execute(
idData = idData,
range = goalRange,
type = NotificationGoalParamsInteractor.Type.Count,
).let(manager::show)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.util.simpletimetracker.feature_notification.goalTime.interactor

import android.graphics.Color
import com.example.util.simpletimetracker.core.mapper.ColorMapper
import com.example.util.simpletimetracker.core.mapper.IconMapper
import com.example.util.simpletimetracker.core.mapper.TimeMapper
Expand All @@ -12,18 +13,22 @@ import com.example.util.simpletimetracker.domain.extension.getSessionCount
import com.example.util.simpletimetracker.domain.extension.getSessionDuration
import com.example.util.simpletimetracker.domain.extension.getWeeklyCount
import com.example.util.simpletimetracker.domain.extension.getWeeklyDuration
import com.example.util.simpletimetracker.domain.extension.orZero
import com.example.util.simpletimetracker.domain.extension.value
import com.example.util.simpletimetracker.domain.interactor.CategoryInteractor
import com.example.util.simpletimetracker.domain.interactor.PrefsInteractor
import com.example.util.simpletimetracker.domain.interactor.RecordTypeGoalInteractor
import com.example.util.simpletimetracker.domain.interactor.RecordTypeInteractor
import com.example.util.simpletimetracker.domain.model.RecordTypeGoal
import com.example.util.simpletimetracker.feature_notification.R
import com.example.util.simpletimetracker.feature_notification.goalTime.manager.NotificationGoalTimeParams
import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon
import javax.inject.Inject

class NotificationGoalParamsInteractor @Inject constructor(
private val resourceRepo: ResourceRepo,
private val recordTypeInteractor: RecordTypeInteractor,
private val categoryInteractor: CategoryInteractor,
private val recordTypeGoalInteractor: RecordTypeGoalInteractor,
private val prefsInteractor: PrefsInteractor,
private val timeMapper: TimeMapper,
Expand All @@ -32,12 +37,22 @@ class NotificationGoalParamsInteractor @Inject constructor(
) {

suspend fun execute(
typeId: Long,
idData: RecordTypeGoal.IdData,
range: RecordTypeGoal.Range,
type: Type,
): NotificationGoalTimeParams? {
val recordType = recordTypeInteractor.get(typeId) ?: return null
val goals = recordTypeGoalInteractor.getByType(typeId)
): NotificationGoalTimeParams {
val typeId = (idData as? RecordTypeGoal.IdData.Type)?.value
val recordType = recordTypeInteractor.get(typeId.orZero())
val categoryId = (idData as? RecordTypeGoal.IdData.Category)?.value
val category = categoryInteractor.get(categoryId.orZero())
val goals = when (idData) {
is RecordTypeGoal.IdData.Type -> {
recordTypeGoalInteractor.getByType(typeId.orZero())
}
is RecordTypeGoal.IdData.Category -> {
recordTypeGoalInteractor.getByCategory(categoryId.orZero())
}
}
val isDarkTheme = prefsInteractor.getDarkMode()

val goalValueString = when (type) {
Expand Down Expand Up @@ -80,13 +95,15 @@ class NotificationGoalParamsInteractor @Inject constructor(
goalTypeString

return NotificationGoalTimeParams(
typeId = recordType.id,
idData = idData,
goalRange = range,
icon = recordType.icon
.let(iconMapper::mapIcon),
color = recordType.color
.let { colorMapper.mapToColorInt(it, isDarkTheme) },
text = recordType.name,
icon = recordType?.icon
?.let(iconMapper::mapIcon)
?: RecordTypeIcon.Text(""),
color = (recordType?.color ?: category?.color)
?.let { colorMapper.mapToColorInt(it, isDarkTheme) }
?: Color.TRANSPARENT,
text = recordType?.name ?: category?.name ?: "",
description = description,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class NotificationGoalTimeInteractorImpl @Inject constructor(

override suspend fun show(typeId: Long, goalRange: RecordTypeGoal.Range) {
notificationGoalParamsInteractor.execute(
typeId = typeId,
idData = RecordTypeGoal.IdData.Type(typeId),
range = goalRange,
type = NotificationGoalParamsInteractor.Type.Duration,
)?.let(manager::show)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class NotificationGoalTimeManager @Inject constructor(
createAndroidNotificationChannel()
notificationManager.notify(
getNotificationTag(params.goalRange),
params.typeId.toInt(),
params.idData.value.toInt(),
notification,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.example.util.simpletimetracker.domain.model.RecordTypeGoal
import com.example.util.simpletimetracker.feature_views.viewData.RecordTypeIcon

data class NotificationGoalTimeParams(
val typeId: Long,
val idData: RecordTypeGoal.IdData,
val goalRange: RecordTypeGoal.Range,
val icon: RecordTypeIcon,
val color: Int,
Expand Down

0 comments on commit 367757b

Please sign in to comment.