Skip to content

Commit

Permalink
Implements a ObjectMapper class for Compose
Browse files Browse the repository at this point in the history
In #80, we implemented the Configuration class. We introduced a separate mapper class for Composable functions so that
you can customize the object conversion process.
  • Loading branch information
ogaclejapan committed Sep 7, 2024
1 parent a41b0b3 commit 55db2a7
Show file tree
Hide file tree
Showing 30 changed files with 1,159 additions and 263 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import soil.query.InfiniteQueryKey
import soil.query.InfiniteQueryRef
import soil.query.QueryChunks
import soil.query.QueryClient
import soil.query.QueryState
import soil.query.QueryStatus
import soil.query.core.getOrThrow
import soil.query.core.isNone
import soil.query.core.map

/**
* Remember a [InfiniteQueryObject] and subscribes to the query state of [key].
Expand All @@ -34,7 +28,9 @@ fun <T, S> rememberInfiniteQuery(
): InfiniteQueryObject<QueryChunks<T, S>, S> {
val scope = rememberCoroutineScope()
val query = remember(key) { client.getInfiniteQuery(key, config.marker).also { it.launchIn(scope) } }
return config.strategy.collectAsState(query).toInfiniteObject(query = query, select = { it })
return with(config.mapper) {
config.strategy.collectAsState(query).toObject(query = query, select = { it })
}
}

/**
Expand All @@ -57,66 +53,7 @@ fun <T, S, U> rememberInfiniteQuery(
): InfiniteQueryObject<U, S> {
val scope = rememberCoroutineScope()
val query = remember(key) { client.getInfiniteQuery(key, config.marker).also { it.launchIn(scope) } }
return config.strategy.collectAsState(query).toInfiniteObject(query = query, select = select)
}

private fun <T, S, U> QueryState<QueryChunks<T, S>>.toInfiniteObject(
query: InfiniteQueryRef<T, S>,
select: (chunks: QueryChunks<T, S>) -> U
): InfiniteQueryObject<U, S> {
return when (status) {
QueryStatus.Pending -> InfiniteQueryLoadingObject(
reply = reply.map(select),
replyUpdatedAt = replyUpdatedAt,
error = error,
errorUpdatedAt = errorUpdatedAt,
staleAt = staleAt,
fetchStatus = fetchStatus,
isInvalidated = isInvalidated,
refresh = query::invalidate,
loadMore = query::loadMore,
loadMoreParam = null
)

QueryStatus.Success -> InfiniteQuerySuccessObject(
reply = reply.map(select),
replyUpdatedAt = replyUpdatedAt,
error = error,
errorUpdatedAt = errorUpdatedAt,
staleAt = staleAt,
fetchStatus = fetchStatus,
isInvalidated = isInvalidated,
refresh = query::invalidate,
loadMore = query::loadMore,
loadMoreParam = query.key.loadMoreParam(reply.getOrThrow())
)

QueryStatus.Failure -> if (reply.isNone) {
InfiniteQueryLoadingErrorObject(
reply = reply.map(select),
replyUpdatedAt = replyUpdatedAt,
error = checkNotNull(error),
errorUpdatedAt = errorUpdatedAt,
staleAt = staleAt,
fetchStatus = fetchStatus,
isInvalidated = isInvalidated,
refresh = query::invalidate,
loadMore = query::loadMore,
loadMoreParam = null
)
} else {
InfiniteQueryRefreshErrorObject(
reply = reply.map(select),
replyUpdatedAt = replyUpdatedAt,
error = checkNotNull(error),
errorUpdatedAt = errorUpdatedAt,
staleAt = staleAt,
fetchStatus = fetchStatus,
isInvalidated = isInvalidated,
refresh = query::invalidate,
loadMore = query::loadMore,
loadMoreParam = query.key.loadMoreParam(reply.getOrThrow())
)
}
return with(config.mapper) {
config.strategy.collectAsState(query).toObject(query = query, select = select)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,26 @@ import soil.query.core.Marker
@Immutable
data class InfiniteQueryConfig internal constructor(
val strategy: InfiniteQueryStrategy,
val mapper: InfiniteQueryObjectMapper,
val marker: Marker
) {

class Builder {
var strategy: InfiniteQueryStrategy = Default.strategy
var mapper: InfiniteQueryObjectMapper = Default.mapper
var marker: Marker = Default.marker

fun build() = InfiniteQueryConfig(
strategy = strategy,
mapper = mapper,
marker = marker
)
}

companion object {
val Default = InfiniteQueryConfig(
strategy = InfiniteQueryStrategy.Default,
mapper = InfiniteQueryObjectMapper.Default,
marker = Marker.None
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ sealed interface InfiniteQueryObject<out T, S> : QueryModel<T> {
* @constructor Creates a [InfiniteQueryLoadingObject].
*/
@Immutable
data class InfiniteQueryLoadingObject<T, S> internal constructor(
data class InfiniteQueryLoadingObject<T, S>(
override val reply: Reply<T>,
override val replyUpdatedAt: Long,
override val error: Throwable?,
Expand All @@ -74,7 +74,7 @@ data class InfiniteQueryLoadingObject<T, S> internal constructor(
* @constructor Creates a [InfiniteQueryLoadingErrorObject].
*/
@Immutable
data class InfiniteQueryLoadingErrorObject<T, S> internal constructor(
data class InfiniteQueryLoadingErrorObject<T, S>(
override val reply: Reply<T>,
override val replyUpdatedAt: Long,
override val error: Throwable,
Expand All @@ -98,7 +98,7 @@ data class InfiniteQueryLoadingErrorObject<T, S> internal constructor(
* @constructor Creates a [InfiniteQuerySuccessObject].
*/
@Immutable
data class InfiniteQuerySuccessObject<T, S> internal constructor(
data class InfiniteQuerySuccessObject<T, S>(
override val reply: Reply<T>,
override val replyUpdatedAt: Long,
override val error: Throwable?,
Expand All @@ -124,7 +124,7 @@ data class InfiniteQuerySuccessObject<T, S> internal constructor(
* @constructor Creates a [InfiniteQueryRefreshErrorObject].
*/
@Immutable
data class InfiniteQueryRefreshErrorObject<T, S> internal constructor(
data class InfiniteQueryRefreshErrorObject<T, S>(
override val reply: Reply<T>,
override val replyUpdatedAt: Long,
override val error: Throwable,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2024 Soil Contributors
// SPDX-License-Identifier: Apache-2.0

package soil.query.compose

import soil.query.InfiniteQueryRef
import soil.query.QueryChunks
import soil.query.QueryState
import soil.query.QueryStatus
import soil.query.core.getOrThrow
import soil.query.core.isNone
import soil.query.core.map

/**
* A mapper that converts [QueryState] to [InfiniteQueryObject].
*/
interface InfiniteQueryObjectMapper {

/**
* Converts the given [QueryState] to [InfiniteQueryObject].
*
* @param query The query reference.
* @param select A function that selects the object from the chunks.
* @return The converted object.
*/
fun <T, S, U> QueryState<QueryChunks<T, S>>.toObject(
query: InfiniteQueryRef<T, S>,
select: (chunks: QueryChunks<T, S>) -> U
): InfiniteQueryObject<U, S>

companion object
}

/**
* The default [InfiniteQueryObjectMapper].
*/
val InfiniteQueryObjectMapper.Companion.Default: InfiniteQueryObjectMapper
get() = DefaultInfiniteQueryObjectMapper

private object DefaultInfiniteQueryObjectMapper : InfiniteQueryObjectMapper {
override fun <T, S, U> QueryState<QueryChunks<T, S>>.toObject(
query: InfiniteQueryRef<T, S>,
select: (chunks: QueryChunks<T, S>) -> U
): InfiniteQueryObject<U, S> = when (status) {
QueryStatus.Pending -> InfiniteQueryLoadingObject(
reply = reply.map(select),
replyUpdatedAt = replyUpdatedAt,
error = error,
errorUpdatedAt = errorUpdatedAt,
staleAt = staleAt,
fetchStatus = fetchStatus,
isInvalidated = isInvalidated,
refresh = query::invalidate,
loadMore = query::loadMore,
loadMoreParam = null
)

QueryStatus.Success -> InfiniteQuerySuccessObject(
reply = reply.map(select),
replyUpdatedAt = replyUpdatedAt,
error = error,
errorUpdatedAt = errorUpdatedAt,
staleAt = staleAt,
fetchStatus = fetchStatus,
isInvalidated = isInvalidated,
refresh = query::invalidate,
loadMore = query::loadMore,
loadMoreParam = query.key.loadMoreParam(reply.getOrThrow())
)

QueryStatus.Failure -> if (reply.isNone) {
InfiniteQueryLoadingErrorObject(
reply = reply.map(select),
replyUpdatedAt = replyUpdatedAt,
error = checkNotNull(error),
errorUpdatedAt = errorUpdatedAt,
staleAt = staleAt,
fetchStatus = fetchStatus,
isInvalidated = isInvalidated,
refresh = query::invalidate,
loadMore = query::loadMore,
loadMoreParam = null
)
} else {
InfiniteQueryRefreshErrorObject(
reply = reply.map(select),
replyUpdatedAt = replyUpdatedAt,
error = checkNotNull(error),
errorUpdatedAt = errorUpdatedAt,
staleAt = staleAt,
fetchStatus = fetchStatus,
isInvalidated = isInvalidated,
refresh = query::invalidate,
loadMore = query::loadMore,
loadMoreParam = query.key.loadMoreParam(reply.getOrThrow())
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ interface InfiniteQueryStrategy {
* The default built-in strategy for Infinite Query built into the library.
*/
val InfiniteQueryStrategy.Companion.Default: InfiniteQueryStrategy
get() = InfiniteQueryStrategyDefault
get() = DefaultInfiniteQueryStrategy

private object InfiniteQueryStrategyDefault : InfiniteQueryStrategy {
private object DefaultInfiniteQueryStrategy : InfiniteQueryStrategy {
@Composable
override fun <T, S> collectAsState(query: InfiniteQueryRef<T, S>): QueryState<QueryChunks<T, S>> {
val state by query.state.collectAsState()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import soil.query.MutationClient
import soil.query.MutationKey
import soil.query.MutationRef
import soil.query.MutationState
import soil.query.MutationStatus

/**
* Remember a [MutationObject] and subscribes to the mutation state of [key].
Expand All @@ -30,55 +27,7 @@ fun <T, S> rememberMutation(
): MutationObject<T, S> {
val scope = rememberCoroutineScope()
val mutation = remember(key) { client.getMutation(key, config.marker).also { it.launchIn(scope) } }
return config.strategy.collectAsState(mutation).toObject(mutation = mutation)
}

private fun <T, S> MutationState<T>.toObject(
mutation: MutationRef<T, S>,
): MutationObject<T, S> {
return when (status) {
MutationStatus.Idle -> MutationIdleObject(
reply = reply,
replyUpdatedAt = replyUpdatedAt,
error = error,
errorUpdatedAt = errorUpdatedAt,
mutatedCount = mutatedCount,
mutate = mutation::mutate,
mutateAsync = mutation::mutateAsync,
reset = mutation::reset
)

MutationStatus.Pending -> MutationLoadingObject(
reply = reply,
replyUpdatedAt = replyUpdatedAt,
error = error,
errorUpdatedAt = errorUpdatedAt,
mutatedCount = mutatedCount,
mutate = mutation::mutate,
mutateAsync = mutation::mutateAsync,
reset = mutation::reset
)

MutationStatus.Success -> MutationSuccessObject(
reply = reply,
replyUpdatedAt = replyUpdatedAt,
error = error,
errorUpdatedAt = errorUpdatedAt,
mutatedCount = mutatedCount,
mutate = mutation::mutate,
mutateAsync = mutation::mutateAsync,
reset = mutation::reset
)

MutationStatus.Failure -> MutationErrorObject(
reply = reply,
replyUpdatedAt = replyUpdatedAt,
error = checkNotNull(error),
errorUpdatedAt = errorUpdatedAt,
mutatedCount = mutatedCount,
mutate = mutation::mutate,
mutateAsync = mutation::mutateAsync,
reset = mutation::reset
)
return with(config.mapper) {
config.strategy.collectAsState(mutation).toObject(mutation = mutation)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,26 @@ import soil.query.core.Marker
@Immutable
data class MutationConfig internal constructor(
val strategy: MutationStrategy,
val mapper: MutationObjectMapper,
val marker: Marker
) {

class Builder {
var strategy: MutationStrategy = Default.strategy
var mapper: MutationObjectMapper = Default.mapper
var marker: Marker = Default.marker

fun build() = MutationConfig(
strategy = strategy,
mapper = mapper,
marker = marker
)
}

companion object {
val Default = MutationConfig(
strategy = MutationStrategy.Default,
mapper = MutationObjectMapper.Default,
marker = Marker.None
)
}
Expand Down
Loading

0 comments on commit 55db2a7

Please sign in to comment.