Skip to content

Commit

Permalink
feat: compose module improvements
Browse files Browse the repository at this point in the history
feat: compose animation become an extension of compose module
  • Loading branch information
programadorthi committed Jan 20, 2024
1 parent c8aab69 commit 75e533c
Show file tree
Hide file tree
Showing 18 changed files with 382 additions and 519 deletions.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,9 @@ fun MyComposeApp() {
routing.call(uri = "/login")
```

## Compose Animation Routing (compose animation module)
## Compose Animation (compose animation module)

> At the moment Compose Animation has limited targets and is not available to all routing targets
> So, this module is a copy of `compose` module with animation support to specific targets
> Use one or other. NEVER BOTH!
Are you using Jetpack or Multiplatform Compose that requires animation? This module is for you.
Easily route any composable you have just doing:
Expand Down
2 changes: 1 addition & 1 deletion compose-animation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ kotlin {
sourceSets {
commonMain {
dependencies {
api(projects.resources)
api(projects.compose)
implementation(compose.runtime)
implementation(compose.animation)
}
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package dev.programadorthi.routing.compose.animation

import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import dev.programadorthi.routing.core.application.ApplicationCall
import io.ktor.util.AttributeKey
import kotlin.jvm.JvmSuppressWildcards

internal typealias Animation<T> = @JvmSuppressWildcards
AnimatedContentTransitionScope<ApplicationCall>.() -> T

private val ComposeRoutingEnterTransitionAttributeKey: AttributeKey<Animation<EnterTransition>> =
AttributeKey("ComposeRoutingEnterTransitionAttributeKey")

private val ComposeRoutingExitTransitionAttributeKey: AttributeKey<Animation<ExitTransition>> =
AttributeKey("ComposeRoutingExitTransitionAttributeKey")

private val ComposeRoutingPopEnterTransitionAttributeKey: AttributeKey<Animation<EnterTransition>> =
AttributeKey("ComposeRoutingPopEnterTransitionAttributeKey")

private val ComposeRoutingPopExitTransitionAttributeKey: AttributeKey<Animation<ExitTransition>> =
AttributeKey("ComposeRoutingPopExitTransitionAttributeKey")

internal var ApplicationCall.enterTransition: Animation<EnterTransition>?
get() = attributes.getOrNull(ComposeRoutingEnterTransitionAttributeKey)
set(value) {
if (value != null) {
attributes.put(ComposeRoutingEnterTransitionAttributeKey, value)
} else {
attributes.remove(ComposeRoutingEnterTransitionAttributeKey)
}
}

internal var ApplicationCall.exitTransition: Animation<ExitTransition>?
get() = attributes.getOrNull(ComposeRoutingExitTransitionAttributeKey)
set(value) {
if (value != null) {
attributes.put(ComposeRoutingExitTransitionAttributeKey, value)
} else {
attributes.remove(ComposeRoutingExitTransitionAttributeKey)
}
}

internal var ApplicationCall.popEnterTransition: Animation<EnterTransition>?
get() = attributes.getOrNull(ComposeRoutingPopEnterTransitionAttributeKey)
set(value) {
if (value != null) {
attributes.put(ComposeRoutingPopEnterTransitionAttributeKey, value)
} else {
attributes.remove(ComposeRoutingPopEnterTransitionAttributeKey)
}
}

internal var ApplicationCall.popExitTransition: Animation<ExitTransition>?
get() = attributes.getOrNull(ComposeRoutingPopExitTransitionAttributeKey)
set(value) {
if (value != null) {
attributes.put(ComposeRoutingPopExitTransitionAttributeKey, value)
} else {
attributes.remove(ComposeRoutingPopExitTransitionAttributeKey)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package dev.programadorthi.routing.compose
package dev.programadorthi.routing.compose.animation

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
Expand All @@ -12,26 +12,21 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.togetherWith
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import dev.programadorthi.routing.compose.CurrentContent
import dev.programadorthi.routing.compose.Routing
import dev.programadorthi.routing.compose.popped
import dev.programadorthi.routing.core.Route
import dev.programadorthi.routing.core.Routing
import dev.programadorthi.routing.core.application
import dev.programadorthi.routing.core.application.ApplicationCall
import dev.programadorthi.routing.core.routing
import io.ktor.util.logging.KtorSimpleLogger
import io.ktor.util.logging.Logger
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext

public val LocalRouting: ProvidableCompositionLocal<Routing> =
staticCompositionLocalOf {
error("Composition local LocalRouting not found")
}
public typealias ComposeAnimatedContent = @Composable AnimatedVisibilityScope.(ApplicationCall) -> Unit

@Composable
public fun Routing(
Expand All @@ -45,32 +40,20 @@ public fun Routing(
},
popEnterTransition: Animation<EnterTransition> = enterTransition,
popExitTransition: Animation<ExitTransition> = exitTransition,
initial: @Composable AnimatedContentScope.() -> Unit,
initial: ComposeAnimatedContent,
content: ComposeAnimatedContent = { CurrentContent() },
) {
CompositionLocalProvider(LocalRouting provides routing) {
val stateList =
remember(routing) {
mutableStateListOf<ComposeEntry>().apply {
routing.contentList = this
add(
ComposeEntry(
content = initial,
call =
ApplicationCall(
application = routing.application,
uri = routing.toString(),
),
).apply {
this.enterTransition = enterTransition
this.exitTransition = exitTransition
this.popEnterTransition = popEnterTransition
this.popExitTransition = popExitTransition
},
)
}
}
val routingUri =
remember(routing) {
routing.toString()
}

Routing(
routing = routing,
initial = { },
) { stateCall ->
AnimatedContent(
targetState = stateList.last(),
targetState = stateCall,
transitionSpec = {
transitionSpec(
scope = this,
Expand All @@ -80,8 +63,12 @@ public fun Routing(
popExitTransition = popExitTransition,
)
},
content = { entry ->
entry.content(this)
content = { call ->
if (call.uri == routingUri) {
initial(call)
} else {
content(call)
}
},
)
}
Expand All @@ -104,7 +91,8 @@ public fun Routing(
popEnterTransition: Animation<EnterTransition> = enterTransition,
popExitTransition: Animation<ExitTransition> = exitTransition,
configuration: Route.() -> Unit,
initial: @Composable AnimatedContentScope.() -> Unit,
initial: ComposeAnimatedContent,
content: ComposeAnimatedContent = { CurrentContent() },
) {
val routing =
remember {
Expand All @@ -131,11 +119,12 @@ public fun Routing(
popEnterTransition = popEnterTransition,
popExitTransition = popExitTransition,
initial = initial,
content = content,
)
}

private fun transitionSpec(
scope: AnimatedContentTransitionScope<ComposeEntry>,
scope: AnimatedContentTransitionScope<ApplicationCall>,
enterTransition: Animation<EnterTransition>,
exitTransition: Animation<ExitTransition>,
popEnterTransition: Animation<EnterTransition> = enterTransition,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package dev.programadorthi.routing.compose
package dev.programadorthi.routing.compose.animation

import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.runtime.Composable
import dev.programadorthi.routing.compose.ComposeContent
import dev.programadorthi.routing.compose.composable
import dev.programadorthi.routing.core.Route
import dev.programadorthi.routing.core.RouteMethod
import dev.programadorthi.routing.core.Routing
Expand Down Expand Up @@ -131,35 +132,16 @@ public fun <T> PipelineContext<Unit, ApplicationCall>.composable(
exitTransition: Animation<ExitTransition>? = null,
popEnterTransition: Animation<EnterTransition>? = enterTransition,
popExitTransition: Animation<ExitTransition>? = exitTransition,
body: @Composable AnimatedContentScope.() -> Unit,
body: ComposeContent,
) {
routing.poppedEntry = null // Removing last popped entry
call.enterTransition = enterTransition
call.exitTransition = exitTransition
call.popEnterTransition = popEnterTransition
call.popExitTransition = popExitTransition

val stateList = routing.contentList
val composeEntry =
ComposeEntry(
call = call,
content = body,
).apply {
this.resource = resource
this.enterTransition = enterTransition
this.exitTransition = exitTransition
this.popEnterTransition = popEnterTransition
this.popExitTransition = popExitTransition
}

when (call.routeMethod) {
RouteMethod.Push -> stateList.add(composeEntry)
RouteMethod.Replace -> stateList[stateList.lastIndex.coerceAtLeast(0)] = composeEntry
RouteMethod.ReplaceAll -> {
stateList.clear()
stateList.add(composeEntry)
}

else ->
error(
"Compose needs a stack route method to work. You called a composable ${call.uri} " +
"using route method ${call.routeMethod} that is not supported",
)
}
composable(
routing = routing,
resource = resource,
body = body,
)
}
Loading

0 comments on commit 75e533c

Please sign in to comment.