Created
February 21, 2025 08:27
-
-
Save Skeptick/7c354c80b9941576e85c3c3367c57f76 to your computer and use it in GitHub Desktop.
Decompose-Router Result API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.Stable | |
import androidx.compose.runtime.mutableStateMapOf | |
import androidx.compose.runtime.remember | |
import androidx.compose.runtime.staticCompositionLocalOf | |
val LocalResultManager = staticCompositionLocalOf<ResultManager> { | |
RouterResultManager | |
} | |
@Composable | |
fun rememberResultManager(): ResultManager { | |
return remember { RouterResultManager } | |
} | |
interface ResultManager { | |
fun register(key: String, result: Any) | |
fun consume(key: String): Any? | |
fun isRegistered(key: String): Boolean | |
} | |
@Stable | |
internal object RouterResultManager : ResultManager { | |
private val results = mutableStateMapOf<String, Any>() | |
override fun register(key: String, result: Any) { | |
results[key] = result | |
} | |
override fun consume(key: String): Any? { | |
return results.remove(key) | |
} | |
override fun isRegistered(key: String): Boolean { | |
return results.containsKey(key) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.NonRestartableComposable | |
import androidx.compose.runtime.remember | |
import com.arkivanov.decompose.router.slot.activate | |
import com.arkivanov.decompose.router.slot.dismiss | |
import com.arkivanov.decompose.router.stack.pop | |
import com.arkivanov.decompose.router.stack.pushNew | |
import io.github.xxfast.decompose.router.slot.Router as SlotRouter | |
import io.github.xxfast.decompose.router.stack.Router as StackRouter | |
inline fun <R : Route, C : R> StackRouter<R>.pushForResult( | |
key: RouteAwareKey<C>, | |
configuration: C, | |
crossinline onComplete: () -> Unit = {} | |
) { | |
pushNew(configuration) { | |
stack.value.active.instance.setResultKey(configuration, key, canReplace = false) | |
onComplete() | |
} | |
} | |
inline fun <R : Route, C : R> SlotRouter<R>.activateForResult( | |
key: RouteAwareKey<C>, | |
configuration: C, | |
crossinline onComplete: () -> Unit = {} | |
) { | |
activate(configuration) { | |
slot.value.child?.instance?.setResultKey(configuration, key, canReplace = true) | |
onComplete() | |
} | |
} | |
inline fun <R : Route> StackRouter<R>.popWithResult( | |
resultManager: ResultManager, | |
result: Any, | |
crossinline onComplete: (isSuccess: Boolean) -> Unit = {} | |
) { | |
getResultKey()?.let { resultManager.register(it.value, result) } | |
pop(onComplete) | |
} | |
inline fun <R : Route> SlotRouter<R>.dismissWithResult( | |
resultManager: ResultManager, | |
result: Any, | |
crossinline onComplete: (isSuccess: Boolean) -> Unit = {} | |
) { | |
getResultKey()?.let { resultManager.register(it.value, result) } | |
dismiss(onComplete) | |
} | |
@Composable | |
@NonRestartableComposable | |
inline fun <R : Route> SlotRouter<R>.setResultOnDismiss(crossinline resultBuilder: () -> Any) { | |
val resultManager = LocalResultManager.current | |
val resultKey = remember { getResultKey()?.value } ?: return | |
NavigationHandler(router = this) { | |
if (!resultManager.isRegistered(resultKey)) { | |
resultManager.register(resultKey, resultBuilder()) | |
} | |
} | |
} | |
@Composable | |
@NonRestartableComposable | |
inline fun <R : Route> StackRouter<R>.setResultOnPop(crossinline resultBuilder: () -> Any) { | |
val resultManager = LocalResultManager.current | |
val resultKey = remember { getResultKey()?.value } ?: return | |
NavigationHandler(router = this) { | |
if (!resultManager.isRegistered(resultKey)) { | |
resultManager.register(resultKey, resultBuilder()) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.DisallowComposableCalls | |
import androidx.compose.runtime.Immutable | |
import androidx.compose.runtime.LaunchedEffect | |
import androidx.compose.runtime.remember | |
import androidx.compose.runtime.saveable.rememberSaveable | |
import com.arkivanov.essenty.instancekeeper.InstanceKeeper | |
import kotlinx.serialization.Serializable | |
import io.github.xxfast.decompose.router.RouterContext | |
import io.github.xxfast.decompose.router.slot.Router as SlotRouter | |
import io.github.xxfast.decompose.router.stack.Router as StackRouter | |
import kotlin.jvm.JvmInline | |
import kotlin.uuid.ExperimentalUuidApi | |
import kotlin.uuid.Uuid | |
/** | |
* Marker interface for all routes | |
*/ | |
@Serializable | |
@Immutable | |
sealed interface Route | |
/** | |
* Marker interface for routes that can return value | |
*/ | |
interface Returnable<T : Any> | |
@JvmInline | |
@Immutable | |
value class RouteAwareKey<T : Route>(val value: String) : InstanceKeeper.Instance | |
@OptIn(ExperimentalUuidApi::class) | |
@Suppress("UNCHECKED_CAST") | |
@Composable | |
inline fun <R : Any, C> ResultManager.rememberResultKey( | |
crossinline onResult: @DisallowComposableCalls (R) -> Unit | |
): RouteAwareKey<C> where C : Route, C : Returnable<R> { | |
val key = rememberSaveable { Uuid.random().toString() } | |
val hasResult = isRegistered(key) | |
LaunchedEffect(hasResult) { | |
if (hasResult) onResult(consume(key) as R) | |
} | |
return remember(key) { RouteAwareKey(key) } | |
} | |
@PublishedApi | |
internal fun <R : Route> StackRouter<R>.getResultKey(): RouteAwareKey<*>? { | |
val active = stack.value.active | |
return active.instance.getResultKey(active.configuration) | |
} | |
@PublishedApi | |
internal fun <R : Route> SlotRouter<R>.getResultKey(): RouteAwareKey<*>? { | |
val child = slot.value.child | |
return child?.instance?.getResultKey(child.configuration) | |
} | |
@PublishedApi | |
internal fun RouterContext.getResultKey(route: Route): RouteAwareKey<*>? { | |
return instanceKeeper.get(route.resultKey) as? RouteAwareKey<*> | |
} | |
@PublishedApi | |
internal fun RouterContext.setResultKey(route: Route, key: RouteAwareKey<*>, canReplace: Boolean = false) { | |
val instanceKey = route.resultKey | |
if (canReplace) instanceKeeper.remove(instanceKey) | |
instanceKeeper.put(instanceKey, instance = key) | |
} | |
private val Route.resultKey: String get() = "$this.result_key" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment