Skip to content

Commit

Permalink
Implement a Configuration Class for Compose
Browse files Browse the repository at this point in the history
In #69, we implemented QueryCachingStrategy. To avoid the undesired practice of continually adding more arguments with
future feature additions, we have introduced a separate configuration class specifically for Composable functions.
  • Loading branch information
ogaclejapan committed Aug 25, 2024
1 parent a454031 commit 464b622
Show file tree
Hide file tree
Showing 31 changed files with 383 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import soil.playground.query.data.Post
import soil.playground.query.key.posts.GetPostKey
import soil.query.compose.QueryConfig
import soil.query.compose.QueryObject
import soil.query.compose.rememberQuery

typealias GetPostQueryObject = QueryObject<Post>

@Composable
fun rememberGetPostQuery(postId: Int): GetPostQueryObject {
fun rememberGetPostQuery(
postId: Int,
builderBlock: QueryConfig.Builder.() -> Unit = {}
): GetPostQueryObject {
val key = remember(postId) { GetPostKey(postId) }
return rememberQuery(key)
return rememberQuery(key, QueryConfig(builderBlock))
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import soil.playground.query.data.PageParam
import soil.playground.query.data.Posts
import soil.playground.query.key.posts.GetPostsKey
import soil.query.chunkedData
import soil.query.compose.InfiniteQueryConfig
import soil.query.compose.InfiniteQueryObject
import soil.query.compose.rememberInfiniteQuery

typealias GetPostsQueryObject = InfiniteQueryObject<Posts, PageParam>

@Composable
fun rememberGetPostsQuery(userId: Int? = null): GetPostsQueryObject {
fun rememberGetPostsQuery(
userId: Int? = null,
builderBlock: InfiniteQueryConfig.Builder.() -> Unit = {}
): GetPostsQueryObject {
val key = remember(userId) { GetPostsKey(userId) }
return rememberInfiniteQuery(key, select = { it.chunkedData })
return rememberInfiniteQuery(key, select = { it.chunkedData }, config = InfiniteQueryConfig(builderBlock))
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import soil.playground.query.data.PageParam
import soil.playground.query.data.Posts
import soil.playground.query.key.users.GetUserPostsKey
import soil.query.chunkedData
import soil.query.compose.InfiniteQueryConfig
import soil.query.compose.InfiniteQueryObject
import soil.query.compose.rememberInfiniteQuery

typealias GetUserPostsQueryObject = InfiniteQueryObject<Posts, PageParam>

@Composable
fun rememberGetUserPostsQuery(userId: Int): GetUserPostsQueryObject {
fun rememberGetUserPostsQuery(
userId: Int,
builderBlock: InfiniteQueryConfig.Builder.() -> Unit = {}
): GetUserPostsQueryObject {
val key = remember(userId) { GetUserPostsKey(userId) }
return rememberInfiniteQuery(key = key, select = { it.chunkedData })
return rememberInfiniteQuery(key = key, select = { it.chunkedData }, config = InfiniteQueryConfig(builderBlock))
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import soil.playground.query.data.User
import soil.playground.query.key.users.GetUserKey
import soil.query.compose.QueryConfig
import soil.query.compose.QueryObject
import soil.query.compose.rememberQuery

typealias GetUserQueryObject = QueryObject<User>

@Composable
fun rememberGetUserQuery(userId: Int): GetUserQueryObject {
fun rememberGetUserQuery(
userId: Int,
builderBlock: QueryConfig.Builder.() -> Unit = {}
): GetUserQueryObject {
val key = remember(userId) { GetUserKey(userId) }
return rememberQuery(key)
return rememberQuery(key, config = QueryConfig(builderBlock))
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,19 @@ import soil.query.core.map
* @param T Type of data to retrieve.
* @param S Type of parameter.
* @param key The [InfiniteQueryKey] for managing [query][soil.query.Query] associated with [id][soil.query.InfiniteQueryId].
* @param config The configuration for the query. By default, it uses the [InfiniteQueryConfig.Default].
* @param client The [QueryClient] to resolve [key]. By default, it uses the [LocalQueryClient].
* @return A [InfiniteQueryObject] each the query state changed.
*/
@Composable
fun <T, S> rememberInfiniteQuery(
key: InfiniteQueryKey<T, S>,
strategy: QueryCachingStrategy = QueryCachingStrategy,
config: InfiniteQueryConfig = InfiniteQueryConfig.Default,
client: QueryClient = LocalQueryClient.current
): InfiniteQueryObject<QueryChunks<T, S>, S> {
val scope = rememberCoroutineScope()
val query = remember(key) { client.getInfiniteQuery(key).also { it.launchIn(scope) } }
return strategy.collectAsState(query).toInfiniteObject(query = query, select = { it })
val query = remember(key) { client.getInfiniteQuery(key, config.marker).also { it.launchIn(scope) } }
return config.strategy.collectAsState(query).toInfiniteObject(query = query, select = { it })
}

/**
Expand All @@ -43,19 +44,20 @@ fun <T, S> rememberInfiniteQuery(
* @param S Type of parameter.
* @param key The [InfiniteQueryKey] for managing [query][soil.query.Query] associated with [id][soil.query.InfiniteQueryId].
* @param select A function to select data from [QueryChunks].
* @param config The configuration for the query. By default, it uses the [InfiniteQueryConfig.Default].
* @param client The [QueryClient] to resolve [key]. By default, it uses the [LocalQueryClient].
* @return A [InfiniteQueryObject] with selected data each the query state changed.
*/
@Composable
fun <T, S, U> rememberInfiniteQuery(
key: InfiniteQueryKey<T, S>,
select: (chunks: QueryChunks<T, S>) -> U,
strategy: QueryCachingStrategy = QueryCachingStrategy,
config: InfiniteQueryConfig = InfiniteQueryConfig.Default,
client: QueryClient = LocalQueryClient.current
): InfiniteQueryObject<U, S> {
val scope = rememberCoroutineScope()
val query = remember(key) { client.getInfiniteQuery(key).also { it.launchIn(scope) } }
return strategy.collectAsState(query).toInfiniteObject(query = query, select = select)
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(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2024 Soil Contributors
// SPDX-License-Identifier: Apache-2.0

package soil.query.compose

import androidx.compose.runtime.Immutable
import soil.query.core.Marker

/**
* Configuration for the infinite query.
*
* @property strategy The strategy for caching query data.
* @property marker The marker with additional information based on the caller of a query.
*/
@Immutable
data class InfiniteQueryConfig internal constructor(
val strategy: QueryCachingStrategy,
val marker: Marker
) {

@Suppress("MemberVisibilityCanBePrivate")
class Builder {
var strategy: QueryCachingStrategy = Default.strategy
val marker: Marker = Default.marker

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

companion object {
val Default = InfiniteQueryConfig(
strategy = QueryCachingStrategy.Default,
marker = Marker.None
)
}
}

/**
* Creates a [InfiniteQueryConfig] with the provided [initializer].
*/
fun InfiniteQueryConfig(initializer: InfiniteQueryConfig.Builder.() -> Unit): InfiniteQueryConfig {
return InfiniteQueryConfig.Builder().apply(initializer).build()
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@ import soil.query.MutationStatus
* @param T Type of the return value from the mutation.
* @param S Type of the variable to be mutated.
* @param key The [MutationKey] for managing [mutation][soil.query.Mutation] associated with [id][soil.query.MutationId].
* @param config The configuration for the mutation. By default, it uses the [MutationConfig.Default].
* @param client The [MutationClient] to resolve [key]. By default, it uses the [LocalMutationClient].
* @return A [MutationObject] each the mutation state changed.
*/
@Composable
fun <T, S> rememberMutation(
key: MutationKey<T, S>,
config: MutationConfig = MutationConfig.Default,
client: MutationClient = LocalMutationClient.current
): MutationObject<T, S> {
val scope = rememberCoroutineScope()
val mutation = remember(key) { client.getMutation(key).also { it.launchIn(scope) } }
val mutation = remember(key) { client.getMutation(key, config.marker).also { it.launchIn(scope) } }
val state by mutation.state.collectAsState()
return state.toObject(mutation = mutation)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2024 Soil Contributors
// SPDX-License-Identifier: Apache-2.0

package soil.query.compose

import androidx.compose.runtime.Immutable
import soil.query.core.Marker

/**
* Configuration for the mutation.
*
* @property marker The marker with additional information based on the caller of a mutation.
*/
@Immutable
data class MutationConfig internal constructor(
val marker: Marker
) {

@Suppress("MemberVisibilityCanBePrivate")
class Builder {
val marker: Marker = Default.marker

fun build() = MutationConfig(
marker = marker
)
}

companion object {
val Default = MutationConfig(
marker = Marker.None
)
}
}

/**
* Creates a [MutationConfig] with the provided [initializer].
*/
fun MutationConfig(initializer: MutationConfig.Builder.() -> Unit): MutationConfig {
return MutationConfig.Builder().apply(initializer).build()
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,19 @@ import soil.query.core.map
*
* @param T Type of data to retrieve.
* @param key The [QueryKey] for managing [query][soil.query.Query].
* @param config The configuration for the query. By default, it uses the [QueryConfig.Default].
* @param client The [QueryClient] to resolve [key]. By default, it uses the [LocalQueryClient].
* @return A [QueryObject] each the query state changed.
*/
@Composable
fun <T> rememberQuery(
key: QueryKey<T>,
strategy: QueryCachingStrategy = QueryCachingStrategy,
config: QueryConfig = QueryConfig.Default,
client: QueryClient = LocalQueryClient.current
): QueryObject<T> {
val scope = rememberCoroutineScope()
val query = remember(key) { client.getQuery(key).also { it.launchIn(scope) } }
return strategy.collectAsState(query).toObject(query = query, select = { it })
val query = remember(key) { client.getQuery(key, config.marker).also { it.launchIn(scope) } }
return config.strategy.collectAsState(query).toObject(query = query, select = { it })
}

/**
Expand All @@ -40,19 +41,20 @@ fun <T> rememberQuery(
* @param U Type of selected data.
* @param key The [QueryKey] for managing [query][soil.query.Query].
* @param select A function to select data from [T].
* @param config The configuration for the query. By default, it uses the [QueryConfig.Default].
* @param client The [QueryClient] to resolve [key]. By default, it uses the [LocalQueryClient].
* @return A [QueryObject] with selected data each the query state changed.
*/
@Composable
fun <T, U> rememberQuery(
key: QueryKey<T>,
select: (T) -> U,
strategy: QueryCachingStrategy = QueryCachingStrategy,
config: QueryConfig = QueryConfig.Default,
client: QueryClient = LocalQueryClient.current
): QueryObject<U> {
val scope = rememberCoroutineScope()
val query = remember(key) { client.getQuery(key).also { it.launchIn(scope) } }
return strategy.collectAsState(query).toObject(query = query, select = select)
val query = remember(key) { client.getQuery(key, config.marker).also { it.launchIn(scope) } }
return config.strategy.collectAsState(query).toObject(query = query, select = select)
}

private fun <T, U> QueryState<T>.toObject(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2024 Soil Contributors
// SPDX-License-Identifier: Apache-2.0

package soil.query.compose

import androidx.compose.runtime.Immutable
import soil.query.core.Marker

/**
* Configuration for the query.
*
* @property strategy The strategy for caching query data.
* @property marker The marker with additional information based on the caller of a query.
*/
@Immutable
data class QueryConfig internal constructor(
val strategy: QueryCachingStrategy,
val marker: Marker
) {

@Suppress("MemberVisibilityCanBePrivate")
class Builder {
var strategy: QueryCachingStrategy = Default.strategy
var marker: Marker = Default.marker

fun build() = QueryConfig(
strategy = strategy,
marker = marker
)
}

companion object {
val Default = QueryConfig(
strategy = QueryCachingStrategy.Default,
marker = Marker.None
)
}
}

/**
* Creates a [QueryConfig] with the provided [initializer].
*/
fun QueryConfig(initializer: QueryConfig.Builder.() -> Unit): QueryConfig {
return QueryConfig.Builder().apply(initializer).build()
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import soil.query.MutationKey
import soil.query.MutationOptions
import soil.query.MutationRef
import soil.query.MutationState
import soil.query.core.Marker
import soil.query.core.UniqueId

/**
Expand All @@ -34,15 +35,17 @@ class MutationPreviewClient(
) : MutationClient {

@Suppress("UNCHECKED_CAST")
override fun <T, S> getMutation(key: MutationKey<T, S>): MutationRef<T, S> {
override fun <T, S> getMutation(
key: MutationKey<T, S>,
marker: Marker
): MutationRef<T, S> {
val state = previewData[key.id] as? MutationState<T> ?: MutationState.initial()
val options = key.onConfigureOptions()?.invoke(defaultMutationOptions) ?: defaultMutationOptions
return SnapshotMutation(key, options, MutableStateFlow(state))
return SnapshotMutation(key, marker, MutableStateFlow(state))
}

private class SnapshotMutation<T, S>(
override val key: MutationKey<T, S>,
override val options: MutationOptions,
override val marker: Marker,
override val state: StateFlow<MutationState<T>>
) : MutationRef<T, S> {
override fun launchIn(scope: CoroutineScope): Job = Job()
Expand Down
Loading

0 comments on commit 464b622

Please sign in to comment.