diff --git a/shared/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/screens/DKMPViewModelForAndroid.kt b/shared/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/screens/DKMPViewModelForAndroid.kt index aa8a69f10..6faa86e1f 100644 --- a/shared/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/screens/DKMPViewModelForAndroid.kt +++ b/shared/src/androidMain/kotlin/com/adammcneilly/pocketleague/shared/screens/DKMPViewModelForAndroid.kt @@ -1,12 +1,12 @@ package com.adammcneilly.pocketleague.shared.screens -import com.adammcneilly.pocketleague.shared.data.AppDependencies +import com.adammcneilly.pocketleague.shared.data.Repository /** * Creates an instance of a [DKMPViewModel] to be used within an Android application. */ fun DKMPViewModel.Factory.getAndroidInstance(): DKMPViewModel { return DKMPViewModel( - dependencies = AppDependencies(), + dependencies = Repository(), ) } diff --git a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/data/AppDependencies.kt b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/data/AppDependencies.kt deleted file mode 100644 index 777051d4f..000000000 --- a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/data/AppDependencies.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.adammcneilly.pocketleague.shared.data - -import com.adammcneilly.pocketleague.shared.data.remote.octanegg.services.OctaneGGEventService -import com.adammcneilly.pocketleague.shared.data.remote.octanegg.services.OctaneGGGameService -import com.adammcneilly.pocketleague.shared.data.remote.octanegg.services.OctaneGGMatchService -import com.adammcneilly.pocketleague.shared.data.repositories.EventRepository -import com.adammcneilly.pocketleague.shared.data.repositories.GameRepository -import com.adammcneilly.pocketleague.shared.data.repositories.MatchRepository -import com.adammcneilly.pocketleague.shared.eventlist.GetUpcomingEventsUseCase -import com.adammcneilly.pocketleague.shared.eventlist.GetUpcomingEventsUseCaseImpl -import com.adammcneilly.pocketleague.shared.matchdetail.GetMatchGamesUseCase -import com.adammcneilly.pocketleague.shared.matchdetail.GetMatchGamesUseCaseImpl -import com.adammcneilly.pocketleague.shared.matchlist.GetRecentMatchesUseCase -import com.adammcneilly.pocketleague.shared.matchlist.GetRecentMatchesUseCaseImpl - -/** - * Defines the collection of dependencies used throughout the application. This should - * expose interfaces so that the callers don't have to concern themselves - * with the implementation. - */ -class AppDependencies { - private val eventRepository: EventRepository by lazy { - OctaneGGEventService() - } - - private val matchRepository: MatchRepository by lazy { - OctaneGGMatchService() - } - - private val gameRepository: GameRepository by lazy { - OctaneGGGameService() - } - - internal val getUpcomingEventsUseCase: GetUpcomingEventsUseCase - get() = GetUpcomingEventsUseCaseImpl( - repository = eventRepository, - ) - - internal val getRecentMatchesUseCase: GetRecentMatchesUseCase - get() = GetRecentMatchesUseCaseImpl( - repository = matchRepository, - ) - - internal val getMatchGamesUseCase: GetMatchGamesUseCase - get() = GetMatchGamesUseCaseImpl( - repository = gameRepository, - ) -} diff --git a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/data/Repository.kt b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/data/Repository.kt new file mode 100644 index 000000000..04dd592fd --- /dev/null +++ b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/data/Repository.kt @@ -0,0 +1,46 @@ +package com.adammcneilly.pocketleague.shared.data + +import com.adammcneilly.pocketleague.shared.data.remote.octanegg.services.OctaneGGEventService +import com.adammcneilly.pocketleague.shared.data.remote.octanegg.services.OctaneGGGameService +import com.adammcneilly.pocketleague.shared.data.remote.octanegg.services.OctaneGGMatchService +import com.adammcneilly.pocketleague.shared.data.repositories.EventRepository +import com.adammcneilly.pocketleague.shared.data.repositories.GameRepository +import com.adammcneilly.pocketleague.shared.data.repositories.MatchRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +/** + * The main entry point to various data layers inside the application. + * + * @property[useDefaultDispatcher] Most often true, meaning repo calls should be run on the + * Dispatchers.Default dispatcher. We can toggle this false for tests, though. + */ +class Repository( + private val useDefaultDispatcher: Boolean = true, +) { + internal val eventRepository: EventRepository by lazy { + OctaneGGEventService() + } + + internal val matchRepository: MatchRepository by lazy { + OctaneGGMatchService() + } + + internal val gameRepository: GameRepository by lazy { + OctaneGGGameService() + } + + /** + * Runs the supplied [block] inside the scope for this repo, dependent + * oon the [useDefaultDispatcher] flag. + */ + suspend fun withRepoContext(block: suspend () -> T): T { + return if (useDefaultDispatcher) { + withContext(Dispatchers.Default) { + block() + } + } else { + block() + } + } +} diff --git a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/eventlist/GetUpcomingEventsUseCase.kt b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/eventlist/GetUpcomingEventsUseCase.kt deleted file mode 100644 index cffe18b13..000000000 --- a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/eventlist/GetUpcomingEventsUseCase.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.adammcneilly.pocketleague.shared.eventlist - -import com.adammcneilly.pocketleague.shared.data.DataState -import com.adammcneilly.pocketleague.shared.models.Event -import kotlinx.coroutines.flow.Flow - -/** - * Fetches a list of upcoming [Event] entities. This is not specific to any season, group, tier, etc. - */ -interface GetUpcomingEventsUseCase { - - /** - * @see [GetUpcomingEventsUseCase]. - */ - operator fun invoke(): Flow>> -} diff --git a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/eventlist/GetUpcomingEventsUseCaseImpl.kt b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/eventlist/GetUpcomingEventsUseCaseImpl.kt deleted file mode 100644 index 689505994..000000000 --- a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/eventlist/GetUpcomingEventsUseCaseImpl.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.adammcneilly.pocketleague.shared.eventlist - -import com.adammcneilly.pocketleague.shared.data.DataState -import com.adammcneilly.pocketleague.shared.data.models.EventListRequest -import com.adammcneilly.pocketleague.shared.data.repositories.EventRepository -import com.adammcneilly.pocketleague.shared.models.Event -import kotlinx.coroutines.flow.Flow -import kotlinx.datetime.Clock -import kotlinx.datetime.TimeZone -import kotlinx.datetime.toLocalDateTime - -/** - * A concrete implementation of [GetUpcomingEventsUseCase] that will return information from - * the supplied [repository]. - */ -class GetUpcomingEventsUseCaseImpl( - private val repository: EventRepository, -) : GetUpcomingEventsUseCase { - - override fun invoke(): Flow>> { - val today = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()) - - val request = EventListRequest( - after = today, - ) - - return repository.fetchEvents(request) - } -} diff --git a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/matchdetail/GetMatchGamesUseCase.kt b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/matchdetail/GetMatchGamesUseCase.kt deleted file mode 100644 index 434eb146f..000000000 --- a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/matchdetail/GetMatchGamesUseCase.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.adammcneilly.pocketleague.shared.matchdetail - -import com.adammcneilly.pocketleague.shared.data.DataState -import com.adammcneilly.pocketleague.shared.models.Game -import kotlinx.coroutines.flow.Flow - -/** - * Consumes a match ID and requests a list of [Game] entities for that match. - */ -interface GetMatchGamesUseCase { - - /** - * @see [GetMatchGamesUseCase] - */ - operator fun invoke(matchId: String): Flow>> -} diff --git a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/matchdetail/GetMatchGamesUseCaseImpl.kt b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/matchdetail/GetMatchGamesUseCaseImpl.kt deleted file mode 100644 index 5fc39999a..000000000 --- a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/matchdetail/GetMatchGamesUseCaseImpl.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.adammcneilly.pocketleague.shared.matchdetail - -import com.adammcneilly.pocketleague.shared.data.DataState -import com.adammcneilly.pocketleague.shared.data.models.MatchGamesRequest -import com.adammcneilly.pocketleague.shared.data.repositories.GameRepository -import com.adammcneilly.pocketleague.shared.models.Game -import kotlinx.coroutines.flow.Flow - -/** - * A concrete implementation of [GetMatchGamesUseCase] to request the games from the given [repository]. - */ -class GetMatchGamesUseCaseImpl( - private val repository: GameRepository, -) : GetMatchGamesUseCase { - - override fun invoke(matchId: String): Flow>> { - val request = MatchGamesRequest( - matchId = matchId, - ) - - return repository.fetchGamesForMatch(request) - } -} diff --git a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/matchlist/GetRecentMatchesUseCase.kt b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/matchlist/GetRecentMatchesUseCase.kt deleted file mode 100644 index e96492c29..000000000 --- a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/matchlist/GetRecentMatchesUseCase.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.adammcneilly.pocketleague.shared.matchlist - -import com.adammcneilly.pocketleague.shared.data.DataState -import com.adammcneilly.pocketleague.shared.models.Match -import kotlinx.coroutines.flow.Flow - -/** - * A use case to fetch recent matches. The caller can customize within how many - * days "recent" is. - */ -interface GetRecentMatchesUseCase { - - /** - * @see [GetRecentMatchesUseCase] - * - * @param[numDays] The number of days back to pull recent events for. - */ - operator fun invoke(numDays: Int): Flow>> -} diff --git a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/matchlist/GetRecentMatchesUseCaseImpl.kt b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/matchlist/GetRecentMatchesUseCaseImpl.kt deleted file mode 100644 index 748186332..000000000 --- a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/matchlist/GetRecentMatchesUseCaseImpl.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.adammcneilly.pocketleague.shared.matchlist - -import com.adammcneilly.pocketleague.shared.data.DataState -import com.adammcneilly.pocketleague.shared.data.models.MatchListRequest -import com.adammcneilly.pocketleague.shared.data.repositories.MatchRepository -import com.adammcneilly.pocketleague.shared.models.Match -import kotlinx.coroutines.flow.Flow -import kotlinx.datetime.Clock -import kotlinx.datetime.DateTimeUnit -import kotlinx.datetime.TimeZone -import kotlinx.datetime.atStartOfDayIn -import kotlinx.datetime.minus -import kotlinx.datetime.toLocalDateTime - -/** - * A concrete implementation of a [GetRecentMatchesUseCase] that requests matches - * from the given [repository]. - */ -class GetRecentMatchesUseCaseImpl( - private val repository: MatchRepository, -) : GetRecentMatchesUseCase { - - override fun invoke(numDays: Int): Flow>> { - val today = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()) - - val startDate = today.date.minus(numDays, DateTimeUnit.DAY) - .atStartOfDayIn(TimeZone.currentSystemDefault()) - .toLocalDateTime(TimeZone.currentSystemDefault()) - - val request = MatchListRequest( - after = startDate, - before = today, - ) - - return repository.fetchMatches(request) - } -} diff --git a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/DKMPViewModel.kt b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/DKMPViewModel.kt index 1db02c6f2..c9267dcac 100644 --- a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/DKMPViewModel.kt +++ b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/DKMPViewModel.kt @@ -1,13 +1,13 @@ package com.adammcneilly.pocketleague.shared.screens -import com.adammcneilly.pocketleague.shared.data.AppDependencies +import com.adammcneilly.pocketleague.shared.data.Repository import kotlinx.coroutines.flow.StateFlow /** * Root view model for the application that exposes a [stateFlow] of our application's state. */ class DKMPViewModel( - private val dependencies: AppDependencies + private val dependencies: Repository ) { private val stateManager by lazy { diff --git a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/Events.kt b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/Events.kt index accc89207..d891b2f59 100644 --- a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/Events.kt +++ b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/Events.kt @@ -1,6 +1,6 @@ package com.adammcneilly.pocketleague.shared.screens -import com.adammcneilly.pocketleague.shared.data.AppDependencies +import com.adammcneilly.pocketleague.shared.data.Repository /** * This class manages all of the event handling for various screens, specifically @@ -9,8 +9,8 @@ import com.adammcneilly.pocketleague.shared.data.AppDependencies class Events( val stateManager: StateManager, ) { - val dependencies: AppDependencies - get() = stateManager.dependencies + val repository: Repository + get() = stateManager.repository /** * This will run the supplied [block] inside the coroutine scope of the current diff --git a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/StateManager.kt b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/StateManager.kt index ae5c6f36b..f5011702b 100644 --- a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/StateManager.kt +++ b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/StateManager.kt @@ -1,6 +1,6 @@ package com.adammcneilly.pocketleague.shared.screens -import com.adammcneilly.pocketleague.shared.data.AppDependencies +import com.adammcneilly.pocketleague.shared.data.Repository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -16,7 +16,7 @@ import kotlin.reflect.KClass */ @Suppress("TooManyFunctions") class StateManager( - val dependencies: AppDependencies, + val repository: Repository, ) { internal val mutableStateFlow = MutableStateFlow(AppState()) diff --git a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/feed/LoadFeedEvent.kt b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/feed/LoadFeedEvent.kt index a03b8d970..8dc735c31 100644 --- a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/feed/LoadFeedEvent.kt +++ b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/feed/LoadFeedEvent.kt @@ -1,10 +1,18 @@ package com.adammcneilly.pocketleague.shared.screens.feed import com.adammcneilly.pocketleague.shared.data.DataState +import com.adammcneilly.pocketleague.shared.data.models.EventListRequest +import com.adammcneilly.pocketleague.shared.data.models.MatchListRequest import com.adammcneilly.pocketleague.shared.displaymodels.toSummaryDisplayModel import com.adammcneilly.pocketleague.shared.models.Event import com.adammcneilly.pocketleague.shared.screens.Events import kotlinx.coroutines.flow.collect +import kotlinx.datetime.Clock +import kotlinx.datetime.DateTimeUnit +import kotlinx.datetime.TimeZone +import kotlinx.datetime.atStartOfDayIn +import kotlinx.datetime.minus +import kotlinx.datetime.toLocalDateTime const val NUM_DAYS_RECENT_MATCHES = 3 @@ -12,22 +20,27 @@ const val NUM_DAYS_RECENT_MATCHES = 3 * Loads the information for the feed state. */ fun Events.loadFeed() = screenCoroutine { - val upcomingEventsFlow = dependencies.getUpcomingEventsUseCase.invoke() - val recentMatchesFlow = dependencies.getRecentMatchesUseCase.invoke(NUM_DAYS_RECENT_MATCHES) + val today = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()) - upcomingEventsFlow.collect { useCaseResult -> + val upcomingEventsRequest = EventListRequest( + after = today, + ) + + repository.eventRepository.fetchEvents( + upcomingEventsRequest, + ).collect { repoResult -> stateManager.updateScreen(FeedViewState::class) { - val mappedResult = when (useCaseResult) { + val mappedResult = when (repoResult) { is DataState.Loading -> { DataState.Loading } is DataState.Success -> { DataState.Success( - data = useCaseResult.data.map(Event::toSummaryDisplayModel) + data = repoResult.data.map(Event::toSummaryDisplayModel) ) } is DataState.Error -> { - DataState.Error(useCaseResult.error) + DataState.Error(repoResult.error) } } @@ -37,10 +50,19 @@ fun Events.loadFeed() = screenCoroutine { } } - recentMatchesFlow.collect { useCaseResult -> + val recentMatchesRequest = MatchListRequest( + before = today, + after = today.date.minus(NUM_DAYS_RECENT_MATCHES, DateTimeUnit.DAY) + .atStartOfDayIn(TimeZone.currentSystemDefault()) + .toLocalDateTime(TimeZone.currentSystemDefault()), + ) + + repository.matchRepository.fetchMatches( + request = recentMatchesRequest, + ).collect { repoResult -> stateManager.updateScreen(FeedViewState::class) { it.copy( - recentMatchesState = useCaseResult, + recentMatchesState = repoResult, ) } } diff --git a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/matchdetail/GetGamesForMatchEvent.kt b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/matchdetail/GetGamesForMatchEvent.kt index d3e673320..61ab2ebbc 100644 --- a/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/matchdetail/GetGamesForMatchEvent.kt +++ b/shared/src/commonMain/kotlin/com/adammcneilly/pocketleague/shared/screens/matchdetail/GetGamesForMatchEvent.kt @@ -1,5 +1,6 @@ package com.adammcneilly.pocketleague.shared.screens.matchdetail +import com.adammcneilly.pocketleague.shared.data.models.MatchGamesRequest import com.adammcneilly.pocketleague.shared.screens.Events import kotlinx.coroutines.flow.collect @@ -7,12 +8,14 @@ import kotlinx.coroutines.flow.collect * Requests the games for the given [matchId]. */ fun Events.getGamesForMatch(matchId: String) = screenCoroutine { - val useCase = this.dependencies.getMatchGamesUseCase + val matchRequest = MatchGamesRequest( + matchId = matchId, + ) - useCase.invoke(matchId).collect { useCaseResult -> + repository.gameRepository.fetchGamesForMatch(matchRequest).collect { repoResult -> stateManager.updateScreen(MatchDetailViewState::class) { it.copy( - gamesDataState = useCaseResult, + gamesDataState = repoResult, ) } }