Skip to content

Commit

Permalink
Simplifications and updates
Browse files Browse the repository at this point in the history
  • Loading branch information
kihaki committed Dec 12, 2023
1 parent 4087c8a commit b5ffdee
Show file tree
Hide file tree
Showing 12 changed files with 159 additions and 116 deletions.
8 changes: 8 additions & 0 deletions .idea/deploymentTargetSelector.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ junitVersion = "1.1.5"
espressoCore = "3.5.1"
kotlinxCollectionsImmutable = "0.3.6"
kotlinxSerializationJson = "1.6.1"
ktorClient = "2.3.5"
ktorClient = "2.3.6"
lifecycleRuntimeKtx = "2.6.2"
activityCompose = "1.8.1"
composeBom = "2023.10.01"
Expand Down
11 changes: 9 additions & 2 deletions kruiser/src/main/java/de/gaw/kruiser/backstack/Backstack.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@ package de.gaw.kruiser.backstack
import de.gaw.kruiser.destination.Destination
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.StateFlow
import java.io.Serializable
import java.util.UUID

typealias ImmutableEntries = ImmutableList<Destination>
typealias BackstackEntries = List<Destination>
typealias ImmutableEntries = ImmutableList<BackstackEntry>
typealias BackstackEntries = List<BackstackEntry>

data class BackstackEntry(
val destination: Destination,
val id: String = UUID.randomUUID().toString(),
) : Serializable

interface Backstack {
val entries: StateFlow<BackstackEntries>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ package de.gaw.kruiser.backstack

import de.gaw.kruiser.destination.Destination


fun MutableBackstack.pop() = mutate { dropLast(1) }

fun MutableBackstack.push(destination: Destination) = mutate { this + destination }

fun MutableBackstack.pushAvoidDuplicates(destination: Destination) = mutate {
val currentItem = lastOrNull()
when {
currentItem != destination -> this + destination
currentItem?.destination != destination -> this + destination
else -> this
}
}
}

operator fun BackstackEntries.plus(destination: Destination) =
this + BackstackEntry(destination = destination)
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.SaveableStateHolder
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import de.gaw.kruiser.backstack.Backstack
import de.gaw.kruiser.backstack.BackstackEntry
import de.gaw.kruiser.backstack.ui.util.LocalBackstack
import de.gaw.kruiser.backstack.ui.util.LocalSaveableStateHolder
import de.gaw.kruiser.destination.Destination
Expand All @@ -16,16 +17,15 @@ import de.gaw.kruiser.viewmodel.viewModelStoreOwner
*/
@Composable
fun ScreenContent(
destination: Destination,
savedStateKey: Any = destination,
backstackEntry: BackstackEntry,
stateHolder: SaveableStateHolder = LocalSaveableStateHolder.current,
backstack: Backstack = LocalBackstack.current,
) {
stateHolder.SaveableStateProvider(savedStateKey) {
val destinationViewModelStoreOwner = backstack.viewModelStoreOwner(destination)
stateHolder.SaveableStateProvider(backstackEntry.id) {
val screen = remember(backstackEntry) { backstackEntry.destination.build() }
val destinationViewModelStoreOwner = backstack.viewModelStoreOwner(backstackEntry)

CompositionLocalProvider(LocalViewModelStoreOwner provides destinationViewModelStoreOwner) {
val screen = remember(destination) { destination.build() }
screen.Content()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ fun NoAnimation(
) {
val entriesState = backstack.collectEntries()
val entries by entriesState
val currentEntry = entries.last()
ScreenContent(currentEntry)
val entry = entries.last()
ScreenContent(entry)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import de.gaw.kruiser.backstack.Backstack
import de.gaw.kruiser.backstack.BackstackEntries
import de.gaw.kruiser.backstack.BackstackEntry
import de.gaw.kruiser.backstack.ui.ScreenContent
import de.gaw.kruiser.backstack.ui.util.collectEntries
import de.gaw.kruiser.backstack.util.rememberPreviousBackstackOf
import de.gaw.kruiser.destination.Destination

interface ScreenAnimationContext {
val backstack: Backstack
Expand All @@ -25,7 +25,7 @@ private data class ScreenAnimationContextImpl(
override val previousEntries: BackstackEntries,
) : ScreenAnimationContext

typealias ScreenAnimationSpec = AnimatedContentTransitionScope<Destination?>.(ScreenAnimationContext) -> ContentTransform
typealias ScreenAnimationSpec = AnimatedContentTransitionScope<BackstackEntry?>.(ScreenAnimationContext) -> ContentTransform

@Composable
fun ScreenAnimation(
Expand Down Expand Up @@ -53,10 +53,10 @@ fun ScreenAnimation(
)
},
label = label,
) { destination ->
when (destination) {
) { entry ->
when (entry) {
null -> Spacer(modifier = modifier.fillMaxSize())
else -> ScreenContent(destination)
else -> ScreenContent(entry)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import de.gaw.kruiser.backstack.BackstackEntries
import de.gaw.kruiser.backstack.BackstackEntry
import de.gaw.kruiser.backstack.MutableBackstack
import de.gaw.kruiser.destination.Destination

Expand All @@ -16,12 +17,13 @@ fun rememberSaveableBackstack(initial: BackstackEntries = emptyList()) = remembe
}

@Composable
fun rememberSaveableBackstack(initial: Destination) = rememberSaveableBackstack(listOf(initial))
fun rememberSaveableBackstack(initial: Destination) =
rememberSaveableBackstack(listOf(BackstackEntry(initial)))

fun mutableBackstackSaver(): Saver<MutableBackstack, List<Destination>> =
fun mutableBackstackSaver(): Saver<MutableBackstack, List<BackstackEntry>> =
MutableBackstackSaver

private val MutableBackstackSaver = Saver<MutableBackstack, List<Destination>>(
private val MutableBackstackSaver = Saver<MutableBackstack, List<BackstackEntry>>(
save = { it.entries.value.toList() },
restore = { entries ->
MutableBackstack(initial = entries)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package de.gaw.kruiser.destination

import androidx.compose.runtime.Composable
import java.io.Serializable

/**
* May potentially live forever
*/
interface Destination {
interface Destination : Serializable {
fun build(): Screen
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package de.gaw.kruiser.viewmodel

import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import de.gaw.kruiser.backstack.Backstack
import de.gaw.kruiser.backstack.BackstackEntry
import de.gaw.kruiser.backstack.currentEntries
import de.gaw.kruiser.destination.Destination

/**
* Holds the [ViewModelStoreOwner]s for [Destination]s, allowing [ViewModel]s to be scoped to
* [BackstackEntry]s.
*/
private object BackstackViewModelStoreOwner {
private val viewModelStoreOwners: MutableMap<BackstackEntry, ViewModelStoreOwner> = mutableMapOf()

fun remove(entry: BackstackEntry) = viewModelStoreOwners.remove(entry)

operator fun get(entry: BackstackEntry): ViewModelStoreOwner {
return when (val owner = viewModelStoreOwners[entry]) {
null -> object : ViewModelStoreOwner {
override val viewModelStore: ViewModelStore = ViewModelStore()
}.also {
viewModelStoreOwners[entry] = it
}

else -> owner
}
}
}

/**
* Creates a [ViewModelStoreOwner] for the current [BackstackEntry]
*
* @param entry: The [BackstackEntry] to scope the [ViewModelStoreOwner] to.
* @param disposeWhen: Checks whether the [ViewModel]s should be disposed, can be called whenever the system requires it.
* This enables arbitrary scoping of [ViewModel]s, for Android Default behavior see the implementation of [Backstack.viewModelStoreOwner].
*/
@Composable
fun backstackEntryViewModelStoreOwner(
entry: BackstackEntry,
disposeWhen: (BackstackEntry) -> Boolean,
): ViewModelStoreOwner {
val backstackEntryViewModelStoreOwner = BackstackViewModelStoreOwner[entry]

val currentCanDispose by rememberUpdatedState(disposeWhen)
DisposableEffect(entry, backstackEntryViewModelStoreOwner) {
onDispose {
if (currentCanDispose(entry)) {
backstackEntryViewModelStoreOwner.viewModelStore.clear()
BackstackViewModelStoreOwner.remove(entry)
}
}
}

return backstackEntryViewModelStoreOwner
}

/**
* Creates a [ViewModelStoreOwner] for the current [BackstackEntry] using Android Default behavior
* of keeping the [ViewModel] around for as long as the [entry] is on the [Backstack].
*
* @param entry: The [BackstackEntry] to scope the [ViewModelStoreOwner] to.
*/
@Composable
fun Backstack.viewModelStoreOwner(entry: BackstackEntry): ViewModelStoreOwner {
val currentBackstack by rememberUpdatedState(this)
return backstackEntryViewModelStoreOwner(
entry = entry,
disposeWhen = { !currentBackstack.currentEntries().contains(it) },
)
}

This file was deleted.

Loading

0 comments on commit b5ffdee

Please sign in to comment.