Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove SavedStateHandle API #90

Merged
merged 2 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions .idea/runConfigurations/xcodebuild.xml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.github.xxfast.decompose.router.app.screens.stack.list

import androidx.compose.runtime.Composable
import androidx.lifecycle.ViewModel
import io.github.xxfast.decompose.router.rememberOnRoute

class ListViewModel: ViewModel()

@Composable
fun ListScreen() {
val viewModel: ListViewModel = rememberOnRoute(type = ListViewModel::class) { ListViewModel() }
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,26 @@
package io.github.xxfast.decompose.router.screens.stack.list

import com.arkivanov.essenty.instancekeeper.InstanceKeeper.Instance
import io.github.xxfast.decompose.SavedStateHandle
import com.arkivanov.decompose.ComponentContext
import io.github.xxfast.decompose.router.RouterContext
import io.github.xxfast.decompose.router.state
import io.github.xxfast.decompose.router.screens.stack.Item
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlin.coroutines.CoroutineContext

class ListInstance(private val savedStateHandle: SavedStateHandle) : Instance, CoroutineScope {
private val initialState: ListState = savedStateHandle.get() ?: ListState()

class ListComponent(context: RouterContext) : ComponentContext by context, CoroutineScope {
private val initialState: ListState = context.state(ListState()) { states.value }
private val _state: MutableStateFlow<ListState> = MutableStateFlow(initialState)
val state: StateFlow<ListState> = _state
val states: StateFlow<ListState> = _state

override val coroutineContext: CoroutineContext = Dispatchers.Main

fun add(item: Item = Item(_state.value.screens.size)) = launch {
val previous: ListState = _state.value
val updated: ListState = previous.copy(screens = previous.screens.plus(item))
_state.emit(updated)
savedStateHandle.set(updated)
}

override fun onDestroy() {
coroutineContext.cancel()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,17 @@ import io.github.xxfast.decompose.router.screens.TOOLBAR_TAG
import io.github.xxfast.decompose.router.screens.stack.Item
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.serializer

@OptIn(ExperimentalMaterial3Api::class, InternalSerializationApi::class)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ListScreen(
onSelect: (screen: Item) -> Unit,
) {
val instance: ListInstance = rememberOnRoute(
type = ListInstance::class,
strategy = ListState::class.serializer()
) { savedState ->
ListInstance(savedState)
val listComponent: ListComponent = rememberOnRoute(ListComponent::class) { context ->
ListComponent(context)
}

val state: ListState by instance.state.collectAsState()
val state: ListState by listComponent.states.collectAsState()
val listState: LazyListState = rememberLazyListState()
val coroutineScope: CoroutineScope = rememberCoroutineScope()

Expand All @@ -74,7 +69,7 @@ fun ListScreen(
floatingActionButton = {
FloatingActionButton(
onClick = {
instance.add()
listComponent.add()
coroutineScope.launch { listState.animateScrollToItem(state.screens.lastIndex) }
},
content = { Icon(Icons.Rounded.Add, null) },
Expand Down
19 changes: 5 additions & 14 deletions decompose-router/api/android/decompose-router.api
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
public final class io/github/xxfast/decompose/KeyKt {
public static final fun getKey (Lkotlin/reflect/KClass;)Ljava/lang/String;
}

public final class io/github/xxfast/decompose/SavedStateHandle : com/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance {
public static final field $stable I
public fun <init> (Ljava/lang/Object;)V
public final fun get ()Ljava/lang/Object;
public final fun getValue ()Ljava/lang/Object;
public fun onDestroy ()V
public final fun set (Ljava/lang/Object;)V
}

public final class io/github/xxfast/decompose/router/DefaultRouterContextKt {
public static final fun defaultRouterContext (Landroidx/activity/ComponentActivity;)Lio/github/xxfast/decompose/router/RouterContext;
public static final fun defaultRouterContext (Landroidx/fragment/app/Fragment;Landroidx/activity/OnBackPressedDispatcher;)Lio/github/xxfast/decompose/router/RouterContext;
}

public final class io/github/xxfast/decompose/router/KeyKt {
public static final fun getKey (Lkotlin/reflect/KClass;)Ljava/lang/String;
}

public final class io/github/xxfast/decompose/router/RememberOnRouteKt {
public static final fun rememberOnRoute (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;
public static final fun rememberOnRoute (Lkotlin/reflect/KClass;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Ljava/lang/Object;
}

public final class io/github/xxfast/decompose/router/RouterContext : com/arkivanov/decompose/ComponentContext {
Expand Down
19 changes: 5 additions & 14 deletions decompose-router/api/desktop/decompose-router.api
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
public final class io/github/xxfast/decompose/KeyKt {
public static final fun getKey (Lkotlin/reflect/KClass;)Ljava/lang/String;
}

public final class io/github/xxfast/decompose/SavedStateHandle : com/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance {
public static final field $stable I
public fun <init> (Ljava/lang/Object;)V
public final fun get ()Ljava/lang/Object;
public final fun getValue ()Ljava/lang/Object;
public fun onDestroy ()V
public final fun set (Ljava/lang/Object;)V
}

public final class io/github/xxfast/decompose/router/DefaultRouterContextKt {
public static final fun defaultRouterContext (Lcom/arkivanov/essenty/backhandler/BackDispatcher;Lcom/arkivanov/essenty/lifecycle/LifecycleRegistry;Landroidx/compose/ui/window/WindowState;Landroidx/compose/runtime/Composer;II)Lio/github/xxfast/decompose/router/RouterContext;
}

public final class io/github/xxfast/decompose/router/KeyKt {
public static final fun getKey (Lkotlin/reflect/KClass;)Ljava/lang/String;
}

public final class io/github/xxfast/decompose/router/RememberOnRouteKt {
public static final fun rememberOnRoute (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Lcom/arkivanov/essenty/instancekeeper/InstanceKeeper$Instance;
public static final fun rememberOnRoute (Lkotlin/reflect/KClass;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)Ljava/lang/Object;
}

public final class io/github/xxfast/decompose/router/RouterContext : com/arkivanov/decompose/ComponentContext {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.github.xxfast.decompose
package io.github.xxfast.decompose.router

import kotlin.reflect.KClass

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.github.xxfast.decompose
package io.github.xxfast.decompose.router

import kotlin.reflect.KClass

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,33 @@ package io.github.xxfast.decompose.router

import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisallowComposableCalls
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
import com.arkivanov.essenty.instancekeeper.getOrCreate
import com.arkivanov.essenty.statekeeper.StateKeeper
import io.github.xxfast.decompose.SavedStateHandle
import io.github.xxfast.decompose.key
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlin.reflect.KClass

/***
* Creates an instance of [T] that is scoped to the current route
* Scopes instance of [T] to the current route
*
* @param type class of [T] instance
* @param key key to remember the instance with. Defaults to [type]'s key
* @param block lambda to create an instance of [T] with a given [SavedStateHandle]
* @param block lambda to create an instance of [T] with a given [RouterContext]
*/
@Composable
fun <T : InstanceKeeper.Instance, C: @Serializable Any> rememberOnRoute(
fun <T: Any> rememberOnRoute(
type: KClass<T>,
strategy: KSerializer<C>,
key: Any = type.key,
block: @DisallowComposableCalls (savedState: SavedStateHandle) -> T
block: @DisallowComposableCalls (context: RouterContext) -> T
): T {
val component: RouterContext = LocalRouterContext.current
val stateKeeper: StateKeeper = component.stateKeeper
val instanceKeeper: InstanceKeeper = component.instanceKeeper
val instanceKey = "$key.instance"
val stateKey = "$key.state"
val (instance, savedState) = remember(key) {
val savedState: SavedStateHandle = instanceKeeper
.getOrCreate(stateKey) { SavedStateHandle(stateKeeper.consume(stateKey, strategy)) }
var instance: T? = instanceKeeper.get(instanceKey) as T?
class RouteInstance(val instance: T): InstanceKeeper.Instance
val routerContext: RouterContext = LocalRouterContext.current
val instanceKeeper: InstanceKeeper = routerContext.instanceKeeper
val routeInstance: RouteInstance = remember(key) {
var instance: RouteInstance? = instanceKeeper.get(key) as RouteInstance?
if (instance == null) {
instance = block(savedState)
instanceKeeper.put(instanceKey, instance)
instance = RouteInstance(block(routerContext))
instanceKeeper.put(key, instance)
}
instance to savedState
instance
}

LaunchedEffect(Unit) {
if (!stateKeeper.isRegistered(stateKey))
stateKeeper.register(stateKey, strategy) { savedState.value as C? }
}

return instance
return routeInstance.instance
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import com.arkivanov.essenty.backhandler.BackHandler
import com.arkivanov.essenty.instancekeeper.InstanceKeeper
import com.arkivanov.essenty.lifecycle.Lifecycle
import com.arkivanov.essenty.statekeeper.StateKeeper
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.serializer

class RouterContext(
private val delegate: ComponentContext,
Expand All @@ -23,7 +27,7 @@ class RouterContext(
val storage: MutableMap<Any, Any> = HashMap()
}

inline fun <reified T : Any> RouterContext.getOrCreate(key: Any, factory: () -> T) : T {
inline fun <reified T : Any> RouterContext.getOrCreate(key: Any, factory: () -> T): T {
var instance: T? = storage[key] as T?
if (instance == null) {
instance = factory()
Expand All @@ -41,3 +45,15 @@ inline fun <reified T : Any> RouterContext.getOrCreate(key: Any, factory: () ->
*/
val LocalRouterContext: ProvidableCompositionLocal<RouterContext> =
staticCompositionLocalOf { error("Root RouterContext was not provided") }

@OptIn(InternalSerializationApi::class)
inline fun <reified T : @Serializable Any> RouterContext.state(
initial: T,
key: String = T::class.key,
serializer: KSerializer<T> = T::class.serializer(),
noinline supplier: () -> T,
): T {
val state: T = stateKeeper.consume(key, serializer) ?: initial
if (!stateKeeper.isRegistered(key)) stateKeeper.register(key, serializer, supplier)
return state
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.github.xxfast.decompose
package io.github.xxfast.decompose.router

import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import com.arkivanov.decompose.router.pages.PagesNavigation
import com.arkivanov.decompose.router.pages.childPages
import io.github.xxfast.decompose.router.LocalRouterContext
import io.github.xxfast.decompose.router.RouterContext
import io.github.xxfast.decompose.asState
import io.github.xxfast.decompose.router.asState
import io.github.xxfast.decompose.router.getOrCreate
import io.github.xxfast.decompose.key
import io.github.xxfast.decompose.router.key
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import androidx.compose.runtime.remember
import com.arkivanov.decompose.router.slot.ChildSlot
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.childSlot
import io.github.xxfast.decompose.asState
import io.github.xxfast.decompose.key
import io.github.xxfast.decompose.router.asState
import io.github.xxfast.decompose.router.key
import io.github.xxfast.decompose.router.LocalRouterContext
import io.github.xxfast.decompose.router.RouterContext
import io.github.xxfast.decompose.router.getOrCreate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.childStack
import io.github.xxfast.decompose.router.LocalRouterContext
import io.github.xxfast.decompose.router.RouterContext
import io.github.xxfast.decompose.asState
import io.github.xxfast.decompose.router.asState
import io.github.xxfast.decompose.router.getOrCreate
import io.github.xxfast.decompose.key
import io.github.xxfast.decompose.router.key
import kotlinx.serialization.*
import kotlin.reflect.KClass

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.github.xxfast.decompose
package io.github.xxfast.decompose.router

import kotlin.reflect.KClass

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.github.xxfast.decompose
package io.github.xxfast.decompose.router

import kotlin.reflect.KClass

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.github.xxfast.decompose
package io.github.xxfast.decompose.router

import kotlin.reflect.KClass

Expand Down
2 changes: 1 addition & 1 deletion docs/cfg/buildprofiles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<variables>
<noindex-content>false</noindex-content>
<color-preset>soft</color-preset>
<primary-color>blue</primary-color>
<primary-color>red</primary-color>
<header-logo>decompose_router.svg</header-logo>
<custom-favicons>fav.svg</custom-favicons>
</variables>
Expand Down
2 changes: 1 addition & 1 deletion docs/decompose-router.tree
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
<toc-element topic="using-stack-navigation.md"/>
<toc-element topic="using-pages-navigation.md"/>
<toc-element topic="using-slot-navigation.md"/>
</toc-element><toc-element topic="Scoping-Instances-to-Screens.md"/>
</toc-element><toc-element topic="managing-screen-state.md"/>
</instance-profile>
3 changes: 0 additions & 3 deletions docs/topics/Scoping-Instances-to-Screens.md

This file was deleted.

4 changes: 1 addition & 3 deletions docs/topics/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@ decompose-router = { module = "io.github.xxfast:decompose-router", version.ref =
# For Compose Wear
decompose-router-wear = { module = "io.github.xxfast:decompose-router-wear", version.ref = "decompose-router" }

# You will need to also bring in decompose and essenty
decompose = { module = "com.arkivanov.decompose:decompose", version.ref = "decompose" }
# You will need to also bring in decompose extensions for compose-multiplatform
decompose-compose-multiplatform = { module = "com.arkivanov.decompose:extensions-compose-jetbrains", version.ref = "decompose" }
essenty-parcelable = { module = "com.arkivanov.essenty:parcelable", version.ref = "essenty" }
```

**build.gradle.kts**
Expand Down
Loading
Loading