Skip to content

Commit

Permalink
Merge pull request #366 from skaengus2012/feature/issue344_data
Browse files Browse the repository at this point in the history
Add required data features to the Schedule screen
  • Loading branch information
skaengus2012 authored Jan 4, 2025
2 parents d4fa0ba + 4c3fb19 commit b88b5a1
Show file tree
Hide file tree
Showing 31 changed files with 597 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.nlab.reminder.core.component.tag.di

import com.nlab.reminder.core.component.tag.edit.TagEditDelegate
import com.nlab.reminder.core.data.repository.ScheduleTagListRepository
import com.nlab.reminder.core.data.repository.TagRepository
import com.nlab.reminder.core.domain.TryUpdateTagNameUseCase
import dagger.Module
Expand All @@ -35,10 +36,12 @@ internal class ViewModelScopeTagComponentModule {
@Provides
fun provideTagEditDelegate(
tagRepository: TagRepository,
scheduleTagListRepository: ScheduleTagListRepository,
tryUpdateTagNameUseCase: TryUpdateTagNameUseCase,
): TagEditDelegate = TagEditDelegate(
initialState = null,
tagRepository = tagRepository,
scheduleTagListRepository = scheduleTagListRepository,
tryUpdateTagNameUseCase = tryUpdateTagNameUseCase,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.nlab.reminder.core.component.tag.edit

import com.nlab.reminder.core.data.model.Tag
import com.nlab.reminder.core.data.repository.SaveTagQuery
import com.nlab.reminder.core.data.repository.ScheduleTagListRepository
import com.nlab.reminder.core.data.repository.TagRepository
import com.nlab.reminder.core.domain.TagGroupSource
import com.nlab.reminder.core.domain.TryUpdateTagNameResult
Expand All @@ -36,6 +37,7 @@ import kotlinx.coroutines.flow.update
class TagEditDelegate(
initialState: TagEditState?,
private val tagRepository: TagRepository,
private val scheduleTagListRepository: ScheduleTagListRepository,
private val tryUpdateTagNameUseCase: TryUpdateTagNameUseCase,
) {
private val _state = MutableStateFlow(initialState)
Expand All @@ -47,7 +49,7 @@ class TagEditDelegate(
}

suspend fun startRename(): Result<Unit> = _state.updateIfIntro(
getUsageCount = { tag -> tagRepository.getUsageCount(id = tag.id) },
getUsageCount = { tag -> scheduleTagListRepository.getTagUsageCount(tagId = tag.id) },
transform = { tag, usageCount ->
TagEditState.Rename(
tag = tag,
Expand Down Expand Up @@ -129,7 +131,7 @@ class TagEditDelegate(
}

suspend fun startDelete(): Result<Unit> = _state.updateIfIntro(
getUsageCount = { tag -> tagRepository.getUsageCount(id = tag.id) },
getUsageCount = { tag -> scheduleTagListRepository.getTagUsageCount(tagId = tag.id) },
transform = { tag, usageCount -> TagEditState.Delete(tag = tag, usageCount = usageCount) }
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.nlab.reminder.core.component.tag.edit
import com.nlab.reminder.core.data.model.genTag
import com.nlab.reminder.core.data.model.genTags
import com.nlab.reminder.core.data.repository.SaveTagQuery
import com.nlab.reminder.core.data.repository.ScheduleTagListRepository
import com.nlab.reminder.core.data.repository.TagRepository
import com.nlab.reminder.core.domain.TagGroupSource
import com.nlab.reminder.core.domain.TryUpdateTagNameResult
Expand Down Expand Up @@ -88,8 +89,8 @@ class TagEditDelegateTest {
)
val delegate = genTagEditDelegate(
initState = initState,
tagRepository = mock {
whenever(mock.getUsageCount(initState.tag.id)) doReturn Result.Success(usageCount)
scheduleTagListRepository = mock {
whenever(mock.getTagUsageCount(initState.tag.id)) doReturn Result.Success(usageCount)
}
)

Expand Down Expand Up @@ -320,8 +321,8 @@ class TagEditDelegateTest {
)
val delegate = genTagEditDelegate(
initState = initState,
tagRepository = mock {
whenever(mock.getUsageCount(initState.tag.id)) doReturn Result.Success(usageCount)
scheduleTagListRepository = mock {
whenever(mock.getTagUsageCount(initState.tag.id)) doReturn Result.Success(usageCount)
}
)

Expand Down Expand Up @@ -385,5 +386,6 @@ class TagEditDelegateTest {
private fun genTagEditDelegate(
initState: TagEditState?,
tagRepository: TagRepository = mock(),
scheduleTagListRepository: ScheduleTagListRepository = mock(),
tryUpdateTagNameUseCase: TryUpdateTagNameUseCase = mock(),
): TagEditDelegate = TagEditDelegate(initState, tagRepository, tryUpdateTagNameUseCase)
): TagEditDelegate = TagEditDelegate(initState, tagRepository, scheduleTagListRepository, tryUpdateTagNameUseCase)
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ import dagger.hilt.components.SingletonComponent
import com.nlab.reminder.core.data.qualifiers.ScheduleDataOption.*
import com.nlab.reminder.core.data.repository.CompletedScheduleShownRepository
import com.nlab.reminder.core.data.repository.ScheduleRepository
import com.nlab.reminder.core.data.repository.ScheduleTagListRepository
import com.nlab.reminder.core.data.repository.TagRepository
import com.nlab.reminder.core.data.repository.impl.CompletedScheduleShownRepositoryImpl
import com.nlab.reminder.core.data.repository.impl.LocalScheduleRepository
import com.nlab.reminder.core.data.repository.impl.LocalScheduleTagListRepository
import com.nlab.reminder.core.data.repository.impl.LocalTagRepository
import com.nlab.reminder.core.data.util.SystemTimestampProvider
import com.nlab.reminder.core.data.util.TimestampProvider
Expand Down Expand Up @@ -63,11 +65,17 @@ internal class AppScopeDataModule {
@Reusable
fun provideTagRepository(
tagDAO: TagDAO,
tagRelationDAO: TagRelationDAO,
scheduleTagListDAO: ScheduleTagListDAO,
tagRelationDAO: TagRelationDAO
): TagRepository = LocalTagRepository(
tagDAO = tagDAO,
tagRelationDAO = tagRelationDAO,
tagRelationDAO = tagRelationDAO
)

@Provides
@Reusable
fun provideScheduleTagListRepository(
scheduleTagListDAO: ScheduleTagListDAO,
): ScheduleTagListRepository = LocalScheduleTagListRepository(
scheduleTagListDAO = scheduleTagListDAO,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ package com.nlab.reminder.core.data.di

import com.nlab.reminder.core.data.repository.LinkMetadataRepository
import com.nlab.reminder.core.data.repository.ScheduleCompleteMarkRepository
import com.nlab.reminder.core.data.repository.ScheduleDetailRepository
import com.nlab.reminder.core.data.repository.ScheduleRepository
import com.nlab.reminder.core.data.repository.ScheduleSelectedIdRepository
import com.nlab.reminder.core.data.repository.ScheduleTagListRepository
import com.nlab.reminder.core.data.repository.TagRepository
import com.nlab.reminder.core.data.repository.impl.DefaultScheduleDetailRepository
import com.nlab.reminder.core.data.repository.impl.InMemoryScheduleCompleteMarkRepository
import com.nlab.reminder.core.data.repository.impl.InMemoryScheduleSelectedIdRepository
import com.nlab.reminder.core.data.repository.impl.OfflineFirstLinkMetadataRepository
Expand Down Expand Up @@ -53,6 +58,20 @@ internal class ViewModelScopeDataModule {
initialCache = emptyMap()
)

@ViewModelScoped
@Provides
fun provideScheduleDetailRepository(
scheduleRepository: ScheduleRepository,
tagRepository: TagRepository,
scheduleTagListRepository: ScheduleTagListRepository,
linkMetadataRepository: LinkMetadataRepository
): ScheduleDetailRepository = DefaultScheduleDetailRepository(
scheduleRepository = scheduleRepository,
tagRepository = tagRepository,
scheduleTagListRepository = scheduleTagListRepository,
linkMetadataRepository = linkMetadataRepository
)

@Provides
@ViewModelScoped
fun provideScheduleCompleteMarkRepository(): ScheduleCompleteMarkRepository =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (C) 2025 The N's lab Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.nlab.reminder.core.data.repository.impl

import com.nlab.reminder.core.data.model.Link
import com.nlab.reminder.core.data.model.LinkMetadata
import com.nlab.reminder.core.data.model.Schedule
import com.nlab.reminder.core.data.model.ScheduleDetail
import com.nlab.reminder.core.data.model.ScheduleId
import com.nlab.reminder.core.data.model.Tag
import com.nlab.reminder.core.data.repository.GetScheduleQuery
import com.nlab.reminder.core.data.repository.GetTagQuery
import com.nlab.reminder.core.data.repository.LinkMetadataRepository
import com.nlab.reminder.core.data.repository.ScheduleDetailRepository
import com.nlab.reminder.core.data.repository.ScheduleRepository
import com.nlab.reminder.core.data.repository.ScheduleTagListRepository
import com.nlab.reminder.core.data.repository.TagRepository
import com.nlab.reminder.core.kotlin.collections.toSet
import com.nlab.reminder.core.kotlin.collections.toSetNotNull
import com.nlab.reminder.core.kotlinx.coroutine.flow.combine
import com.nlab.reminder.core.kotlinx.coroutine.flow.flatMapLatest
import com.nlab.reminder.core.kotlinx.coroutine.flow.map
import kotlinx.coroutines.flow.Flow

/**
* @author Thalys
*/
class DefaultScheduleDetailRepository(
private val scheduleRepository: ScheduleRepository,
private val tagRepository: TagRepository,
private val scheduleTagListRepository: ScheduleTagListRepository,
private val linkMetadataRepository: LinkMetadataRepository
) : ScheduleDetailRepository {
override suspend fun getScheduleDetailsAsStream(
query: GetScheduleQuery
): Flow<Set<ScheduleDetail>> = scheduleRepository
.getSchedulesAsStream(query)
.flatMapLatest(::createScheduleDetailsFlow)

private fun createScheduleDetailsFlow(schedules: Set<Schedule>): Flow<Set<ScheduleDetail>> {
val scheduleIds = mutableSetOf<ScheduleId>()
val links = mutableSetOf<Link>()
schedules.forEach { schedule ->
schedule.id.let(scheduleIds::add)
schedule.content.link?.let(links::add)
}
return combine(
createScheduleIdsToTagListTableFlow(scheduleIds = scheduleIds),
linkMetadataRepository.getAsStream(links = links),
) { scheduleIdToTagsTable, linkToMetadataTable ->
createScheduleDetails(
schedules = schedules,
scheduleIdToTagsTable = scheduleIdToTagsTable,
linkToMetadataTable = linkToMetadataTable
)
}
}

private fun createScheduleIdsToTagListTableFlow(
scheduleIds: Set<ScheduleId>,
): Flow<Map<ScheduleId, Set<Tag>>> = scheduleTagListRepository
.getScheduleTagListAsStream(scheduleIds)
.flatMapLatest { scheduleIdToTagIds ->
tagRepository
.getTagsAsStream(GetTagQuery.ByIds(tagIds = scheduleIdToTagIds.values.flatten().toSet()))
.map { tags ->
val idToTable = tags.associateBy { it.id }
scheduleIdToTagIds.mapValues { entry -> entry.value.toSetNotNull { idToTable[it] } }
}
}

private fun createScheduleDetails(
schedules: Set<Schedule>,
scheduleIdToTagsTable: Map<ScheduleId, Set<Tag>>,
linkToMetadataTable: Map<Link, LinkMetadata>
): Set<ScheduleDetail> = schedules.toSet { schedule ->
ScheduleDetail(
schedule = schedule,
tags = scheduleIdToTagsTable[schedule.id] ?: emptySet(),
linkMetadata = linkToMetadataTable[schedule.content.link]
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.nlab.reminder.core.data.repository.SaveScheduleQuery
import com.nlab.reminder.core.data.repository.ScheduleRepository
import com.nlab.reminder.core.data.repository.UpdateSchedulesQuery
import com.nlab.reminder.core.kotlin.NonNegativeLong
import com.nlab.reminder.core.kotlin.collections.toSet
import com.nlab.reminder.core.kotlin.tryToNonNegativeLongOrZero
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
Expand Down Expand Up @@ -84,17 +85,13 @@ class LocalScheduleRepository(
}
}

override fun getSchedulesAsStream(request: GetScheduleQuery): Flow<Collection<Schedule>> {
override fun getSchedulesAsStream(request: GetScheduleQuery): Flow<Set<Schedule>> {
val scheduleEntitiesFlow = when (request) {
is GetScheduleQuery.All -> scheduleDAO.getAsStream()
is GetScheduleQuery.ByComplete -> scheduleDAO.findByCompleteAsStream(request.isComplete)
}

return scheduleEntitiesFlow.distinctUntilChanged().map { entities ->
ArrayList<Schedule>(entities.size).apply {
entities.mapTo(destination = this, ::Schedule)
}
}
return scheduleEntitiesFlow.distinctUntilChanged().map { entities -> entities.toSet(::Schedule) }
}

@ExcludeFromGeneratedTestReport
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2025 The N's lab Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.nlab.reminder.core.data.repository.impl

import com.nlab.reminder.core.data.model.ScheduleId
import com.nlab.reminder.core.data.model.TagId
import com.nlab.reminder.core.data.repository.ScheduleTagListRepository
import com.nlab.reminder.core.kotlin.NonNegativeLong
import com.nlab.reminder.core.kotlin.Result
import com.nlab.reminder.core.kotlin.catching
import com.nlab.reminder.core.kotlin.collections.toSet
import com.nlab.reminder.core.kotlin.toNonNegativeLong
import com.nlab.reminder.core.kotlinx.coroutine.flow.map
import com.nlab.reminder.core.local.database.dao.ScheduleTagListDAO
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged

/**
* @author Thalys
*/
class LocalScheduleTagListRepository(
private val scheduleTagListDAO: ScheduleTagListDAO,
) : ScheduleTagListRepository {
override fun getScheduleTagListAsStream(scheduleIds: Set<ScheduleId>): Flow<Map<ScheduleId, Set<TagId>>> =
scheduleTagListDAO
.findByScheduleIdsAsStream(scheduleIds.toSet(ScheduleId::rawId))
.distinctUntilChanged()
.map { entities ->
entities
.groupBy(keySelector = { ScheduleId(it.scheduleId) }, valueTransform = { TagId(it.tagId) })
.mapValues { (_, value) -> value.toSet() }
}

override suspend fun getTagUsageCount(tagId: TagId): Result<NonNegativeLong> = catching {
scheduleTagListDAO
.findTagUsageCount(tagId = tagId.rawId)
.toNonNegativeLong()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@ import com.nlab.reminder.core.data.model.TagId
import com.nlab.reminder.core.data.repository.GetTagQuery
import com.nlab.reminder.core.data.repository.SaveTagQuery
import com.nlab.reminder.core.data.repository.TagRepository
import com.nlab.reminder.core.kotlin.NonNegativeLong
import com.nlab.reminder.core.kotlinx.coroutine.flow.map
import com.nlab.reminder.core.kotlin.Result
import com.nlab.reminder.core.kotlin.catching
import com.nlab.reminder.core.kotlin.collections.toSet
import com.nlab.reminder.core.kotlin.map
import com.nlab.reminder.core.kotlin.toNonNegativeLong
import com.nlab.reminder.core.local.database.dao.ScheduleTagListDAO
import com.nlab.reminder.core.local.database.dao.TagRelationDAO
import com.nlab.reminder.core.local.database.dao.TagDAO
import com.nlab.reminder.core.local.database.model.TagEntity
Expand All @@ -40,7 +38,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged
class LocalTagRepository(
private val tagDAO: TagDAO,
private val tagRelationDAO: TagRelationDAO,
private val scheduleTagListDAO: ScheduleTagListDAO,
) : TagRepository {
override suspend fun save(query: SaveTagQuery): Result<Tag> {
val entityResult = catching {
Expand All @@ -64,21 +61,16 @@ class LocalTagRepository(
tagDAO.deleteById(id.rawId)
}

override suspend fun getUsageCount(id: TagId): Result<NonNegativeLong> = catching {
scheduleTagListDAO
.findTagUsageCount(tagId = id.rawId)
.toNonNegativeLong()
}

override fun getTagsAsStream(query: GetTagQuery): Flow<Collection<Tag>> {
override fun getTagsAsStream(query: GetTagQuery): Flow<Set<Tag>> {
val entitiesFlow: Flow<Array<TagEntity>> = when (query) {
is GetTagQuery.All -> tagDAO.getAsStream()
is GetTagQuery.ByIds -> tagDAO.findByIdsAsStream(
buildSet { query.tagIds.mapTo(destination = this, transform = TagId::rawId) }
)
}
return entitiesFlow.distinctUntilChanged().map { entities ->
ArrayList<Tag>(entities.size).apply { entities.mapTo(destination = this, ::Tag) }
is GetTagQuery.All -> {
tagDAO.getAsStream()
}

is GetTagQuery.ByIds -> {
tagDAO.findByIdsAsStream(query.tagIds.toSet(TagId::rawId))
}
}
return entitiesFlow.distinctUntilChanged().map { entities -> entities.toSet(::Tag) }
}
}
Loading

0 comments on commit b88b5a1

Please sign in to comment.