Skip to content

Commit

Permalink
Combine reducers
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-ramotar committed Apr 25, 2024
1 parent 572e14a commit afa249c
Show file tree
Hide file tree
Showing 14 changed files with 299 additions and 245 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.mobilenativefoundation.market


inline fun <reified S : Market.State, reified A : Market.Action> combineReducers(vararg reducers: Market.Reducer<S, A>): Market.Reducer<S, A> {
return Market.Reducer { state, action ->
reducers.fold(state) { currentState, reducer ->
reducer.reduce(currentState, action)
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import me.tatarka.inject.annotations.Component
import me.tatarka.inject.annotations.Provides
import org.mobilenativefoundation.market.combineReducers
import org.mobilenativefoundation.market.impl.MarketActionFactory
import org.mobilenativefoundation.market.impl.RealMarketSupplier
import org.mobilenativefoundation.market.impl.RealScheduledMarketSupplier
import org.mobilenativefoundation.sample.octonaut.android.app.circuit.OctonautPresenterFactory
import org.mobilenativefoundation.sample.octonaut.android.app.circuit.OctonautScreenFactory
import org.mobilenativefoundation.sample.octonaut.android.app.circuit.OctonautUiFactory
import org.mobilenativefoundation.sample.octonaut.android.app.circuit.ScreenFactory
import org.mobilenativefoundation.sample.octonaut.android.app.market.MutableOctonautMarket
import org.mobilenativefoundation.sample.octonaut.android.app.market.RealMutableOctonautMarket
import org.mobilenativefoundation.sample.octonaut.android.app.market.RealOctonautMarketDispatcher
import org.mobilenativefoundation.sample.octonaut.android.app.market.*
import org.mobilenativefoundation.sample.octonaut.android.app.market.reducers.*
import org.mobilenativefoundation.sample.octonaut.xplat.common.market.*
import org.mobilenativefoundation.sample.octonaut.xplat.domain.feed.api.FeedStore
import org.mobilenativefoundation.sample.octonaut.xplat.domain.feed.api.FeedSupplier
Expand Down Expand Up @@ -86,10 +86,21 @@ abstract class CoreComponent : NetworkingComponent {
@Provides
fun provideMarket(mutableMarket: MutableOctonautMarket): OctonautMarket = mutableMarket

@Provides
fun provideRootReducer(): RootReducer {
return combineReducers(
currentUserReducer,
notificationsReducer,
feedReducer,
usersReducer,
repositoriesReducer
)
}

@UserScope
@Provides
fun provideMarketDispatcher(mutableMarket: MutableOctonautMarket): OctonautMarketDispatcher {
return RealOctonautMarketDispatcher(mutableMarket)
fun provideMarketDispatcher(mutableMarket: MutableOctonautMarket, rootReducer: RootReducer): OctonautMarketDispatcher {
return RealOctonautMarketDispatcher(mutableMarket, rootReducer)
}

@UserScope
Expand Down Expand Up @@ -172,9 +183,10 @@ abstract class CoreComponent : NetworkingComponent {
marketDispatcher: OctonautMarketDispatcher,
): RepositorySupplier {

val marketActionFactory = MarketActionFactory<GetRepositoryQuery.Repository, OctonautMarketAction> { storeOutput ->
OctonautMarketAction.AddRepository(storeOutput)
}
val marketActionFactory =
MarketActionFactory<GetRepositoryQuery.Repository, OctonautMarketAction> { storeOutput ->
OctonautMarketAction.AddRepository(storeOutput)
}

return RealMarketSupplier(
coroutineDispatcher,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,245 +3,19 @@ package org.mobilenativefoundation.sample.octonaut.android.app.market
import me.tatarka.inject.annotations.Inject
import org.mobilenativefoundation.sample.octonaut.xplat.common.market.OctonautMarketAction
import org.mobilenativefoundation.sample.octonaut.xplat.common.market.OctonautMarketDispatcher
import org.mobilenativefoundation.sample.octonaut.xplat.domain.feed.api.*
import org.mobilenativefoundation.sample.octonaut.xplat.domain.notifications.api.Notification
import org.mobilenativefoundation.sample.octonaut.xplat.domain.notifications.api.NotificationsState
import org.mobilenativefoundation.sample.octonaut.xplat.domain.repository.api.RepositoriesState
import org.mobilenativefoundation.sample.octonaut.xplat.domain.repository.api.Repository
import org.mobilenativefoundation.sample.octonaut.xplat.domain.user.api.User
import org.mobilenativefoundation.sample.octonaut.xplat.domain.user.api.UsersState

@Inject
class RealOctonautMarketDispatcher(
private val mutableMarket: MutableOctonautMarket
private val mutableMarket: MutableOctonautMarket,
private val rootReducer: RootReducer
) : OctonautMarketDispatcher {
override fun dispatch(action: OctonautMarketAction) {
val prevState = mutableMarket.state.value
val nextState = when (action) {
is OctonautMarketAction.UpdateCurrentUser -> {
prevState.copy(
currentUser = action.user?.let {
User(
id = it.id,
email = it.email,
name = it.name ?: "",
login = it.login,
avatarUrl = it.avatarUrl.toString(),
repositories = it.repositories.nodes?.mapNotNull { repo -> repo?.id } ?: emptyList(),
starredRepositories = it.starredRepositories.nodes?.mapNotNull { repo -> repo?.id }
?: emptyList(),
organizations = it.organizations.nodes?.mapNotNull { org -> org?.id } ?: emptyList(),
pinnedItems = it.pinnedItems.let { pinnedItems -> List(pinnedItems.totalCount) { "" } },
socialAccounts = it.socialAccounts.nodes?.mapNotNull { socialAccount ->
socialAccount?.let {
User.SocialAccount(
socialAccount.displayName,
socialAccount.provider.name
)
}
} ?: emptyList()
)
}
)
}

is OctonautMarketAction.UpdateNotifications -> {

val notifications = action.notifications.notifications.map {
Notification(
it.id,
it.unread,
it.updatedAt,
it.lastReadAt,
it.url,
it.subscriptionUrl
)
}

val byId = notifications.associateBy { it.id }
val allIds = notifications.map { it.id }

val notificationsState = NotificationsState(
byId = byId,
allIds = allIds
)

prevState.copy(
notifications = notificationsState
)
}

is OctonautMarketAction.UpdateFeed -> {

val id = action.feed.id

val entries = action.feed.entry.map {
Entry(
it.id,
it.published,
it.updated,
it.link.href,
it.title,
it.author.uri,
it.thumbnail.url
)
}

val entriesState = EntriesState(
byId = entries.associateBy { it.id },
allIds = entries.map { it.id }
)

val links = action.feed.link.map {
Link(
it.type,
it.rel,
it.href
)
}

val linksState = LinksState(
byHref = links.associateBy { it.href },
allHrefs = links.map { it.href }
)

val thumbnails = action.feed.entry.map {
Thumbnail(
it.thumbnail.height,
it.thumbnail.width,
it.thumbnail.url
)
}

val thumbnailsState = ThumbnailsState(
byUrl = thumbnails.associateBy { it.url },
allUrls = thumbnails.map { it.url }
)

val feedState = FeedState(
id = id,
entries = entriesState,
links = linksState,
thumbnails = thumbnailsState
)

prevState.copy(
feed = feedState
)
}

is OctonautMarketAction.AddUser -> {
val user = User(
id = action.user.id,
email = action.user.email,
name = action.user.name ?: "",
login = action.user.login,
avatarUrl = action.user.avatarUrl.toString(),
repositories = action.user.repositories.nodes?.mapNotNull { repo -> repo?.id } ?: emptyList(),
starredRepositories = action.user.starredRepositories.nodes?.mapNotNull { repo -> repo?.id }
?: emptyList(),
organizations = action.user.organizations.nodes?.mapNotNull { org -> org?.id } ?: emptyList(),
pinnedItems = action.user.pinnedItems.let { pinnedItems -> List(pinnedItems.totalCount) { "" } },
socialAccounts = action.user.socialAccounts.nodes?.mapNotNull { socialAccount ->
socialAccount?.let {
User.SocialAccount(
socialAccount.displayName,
socialAccount.provider.name
)
}
} ?: emptyList()
)

val allIds = if (user.id !in prevState.users.byId) {
val copy = prevState.users.allIds.toMutableList()
copy.add(action.user.id)
copy
} else {
prevState.users.allIds
}

val byId = prevState.users.byId.toMutableMap()
byId[user.id] = user

val loginToId = allIds.mapNotNull { id -> byId[id] }.associate { it.login to it.id }

val usersState = UsersState(
byId = byId,
allIds = allIds,
loginToId = loginToId
)

prevState.copy(
users = usersState
)
}

is OctonautMarketAction.AddNotifications -> {


val notifications = action.notifications.notifications.map {
Notification(
it.id,
it.unread,
it.updatedAt,
it.lastReadAt,
it.url,
it.subscriptionUrl
)
}

val byId = prevState.notifications.byId.toMutableMap()
val allIds = prevState.notifications.allIds.toMutableList()

notifications.forEach {
if (it.id !in byId) {
allIds.add(it.id)
}

byId[it.id] = it
}

val notificationsState = NotificationsState(
byId = byId,
allIds = allIds
)

prevState.copy(
notifications = notificationsState
)

}

is OctonautMarketAction.AddRepository -> {
val repository = Repository(
action.repository.id,
action.repository.name
)

val allIds = if (repository.id !in prevState.repositories.byId) {
val copy = prevState.repositories.allIds.toMutableList()
copy.add(repository.id)
copy
} else {
prevState.repositories.allIds
}
val nextState = rootReducer.reduce(mutableMarket.state.value, action)
mutableMarket.updateState(nextState)
}
}

val byId = prevState.repositories.byId.toMutableMap()
byId[repository.id] = repository

val repositoriesState = RepositoriesState(
byId = byId,
allIds = allIds,
)

prevState.copy(
repositories = repositoriesState
)
}
}

println("UPDATING STATE: $prevState to $nextState")

mutableMarket.updateState(nextState)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.mobilenativefoundation.sample.octonaut.android.app.market

import org.mobilenativefoundation.market.Market
import org.mobilenativefoundation.sample.octonaut.xplat.common.market.OctonautMarketAction
import org.mobilenativefoundation.sample.octonaut.xplat.common.market.OctonautMarketState

typealias RootReducer = Market.Reducer<OctonautMarketState, OctonautMarketAction>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.mobilenativefoundation.sample.octonaut.android.app.market.reducers

import org.mobilenativefoundation.market.Market
import org.mobilenativefoundation.sample.octonaut.xplat.common.market.OctonautMarketAction
import org.mobilenativefoundation.sample.octonaut.xplat.common.market.OctonautMarketState
import org.mobilenativefoundation.sample.octonaut.xplat.domain.user.api.User

val currentUserReducer: Market.Reducer<OctonautMarketState, OctonautMarketAction> =
Market.Reducer { state, action ->

if (action is OctonautMarketAction.UpdateCurrentUser) {
state.copy(currentUser = state.currentUser.copy(
user = action.user?.let {
User(
id = it.id,
email = it.email,
name = it.name ?: "",
login = it.login,
avatarUrl = it.avatarUrl.toString(),
repositories = it.repositories.nodes?.mapNotNull { repo -> repo?.id } ?: emptyList(),
starredRepositories = it.starredRepositories.nodes?.mapNotNull { repo -> repo?.id }
?: emptyList(),
organizations = it.organizations.nodes?.mapNotNull { org -> org?.id } ?: emptyList(),
pinnedItems = it.pinnedItems.let { pinnedItems -> List(pinnedItems.totalCount) { "" } },
socialAccounts = it.socialAccounts.nodes?.mapNotNull { socialAccount ->
socialAccount?.let {
User.SocialAccount(
socialAccount.displayName,
socialAccount.provider.name
)
}
} ?: emptyList()
)
}
))
} else {
state
}
}
Loading

0 comments on commit afa249c

Please sign in to comment.