Skip to content

Commit

Permalink
feat: improved hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
adrielcafe committed Aug 23, 2021
1 parent b92b799 commit a0842a6
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package cafe.adriel.voyager.androidx

import androidx.lifecycle.ViewModelStoreOwner
import cafe.adriel.voyager.core.hook.HookableScreen
import cafe.adriel.voyager.core.hook.ScreenHookHandler
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.core.screen.ScreenHook
import cafe.adriel.voyager.core.screen.uniqueScreenKey

public abstract class AndroidScreen : Screen, ViewModelStoreOwner by ScreenViewModelStoreOwner() {
public abstract class AndroidScreen :
Screen,
HookableScreen by ScreenHookHandler(),
ViewModelStoreOwner by ScreenViewModelStoreOwner() {

override val key: String = uniqueScreenKey
init {
addHooks(viewModelScreenHooks)
}

override val hooks: List<ScreenHook> = viewModelScreenHooks
override val key: String = uniqueScreenKey
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package cafe.adriel.voyager.androidx
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import cafe.adriel.voyager.core.screen.ScreenHook
import cafe.adriel.voyager.core.hook.ScreenHook

public val ViewModelStoreOwner.viewModelScreenHooks: List<ScreenHook>
get() = listOf(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cafe.adriel.voyager.core.hook

public inline fun <reified T> Hookable<T>.addHooks(vararg hooks: T) {
addHooks(hooks.toList())
}

public inline fun <reified T> Hookable<T>.removeHooks(vararg hooks: T) {
removeHooks(hooks.toList())
}

public interface Hookable<T> {

public val hooks: List<T>

public fun addHooks(hooks: List<T>)

public fun removeHooks(hooks: List<T>)

public fun clearHooks()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package cafe.adriel.voyager.core.hook

import androidx.compose.runtime.ProvidedValue
import cafe.adriel.voyager.core.screen.Screen

public typealias HookableScreen = Hookable<ScreenHook>

public val Screen.hooks: ScreenHooks
get() = when (this) {
is Hookable<*> -> ScreenHooks(
providers = hooks.filterIsInstance<ScreenHook.OnProvide<*>>(),
disposers = hooks.filterIsInstance<ScreenHook.OnDispose>(),
)
else -> ScreenHooks()
}

public fun Screen.clearHooks() {
if (this is Hookable<*>) {
clearHooks()
}
}

public sealed class ScreenHook {
public data class OnProvide<T>(val provide: () -> ProvidedValue<T>) : ScreenHook()
public data class OnDispose(val dispose: () -> Unit) : ScreenHook()
}

public data class ScreenHooks(
val providers: List<ScreenHook.OnProvide<*>> = emptyList(),
val disposers: List<ScreenHook.OnDispose> = emptyList()
)

public class ScreenHookHandler : HookableScreen {

private val mutableHooks = mutableListOf<ScreenHook>()

override val hooks: List<ScreenHook>
get() = mutableHooks

override fun addHooks(hooks: List<ScreenHook>) {
mutableHooks += hooks
}

override fun removeHooks(hooks: List<ScreenHook>) {
mutableHooks -= hooks
}

override fun clearHooks() {
mutableHooks.clear()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cafe.adriel.voyager.core.screen

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

@Composable
Expand All @@ -16,19 +15,11 @@ public fun Screen.LifecycleEffect(
}
}

public sealed class ScreenHook {
public data class OnProvide<T>(val provide: () -> ProvidedValue<T>) : ScreenHook()
public data class OnDispose(val dispose: () -> Unit) : ScreenHook()
}

public interface Screen : Serializable {

public val key: String
get() = this::class.qualifiedName ?: error("Default key not found, please provide your own key")

public val hooks: List<ScreenHook>
get() = emptyList()

@Composable
public fun Content()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.saveable.SaveableStateHolder
import androidx.compose.runtime.staticCompositionLocalOf
import cafe.adriel.voyager.core.hook.clearHooks
import cafe.adriel.voyager.core.hook.hooks
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.core.screen.ScreenHook
import cafe.adriel.voyager.core.stack.Stack
import cafe.adriel.voyager.core.stack.StackEvent
import cafe.adriel.voyager.core.stack.toMutableStateStack
Expand Down Expand Up @@ -61,21 +62,14 @@ public fun Navigator(
) {
require(screens.isNotEmpty()) { "Navigator must have at least one screen" }

val navigator = rememberNavigator(
screens = screens,
parent = LocalNavigator.current
)

val (onProvideHooks, onDisposeHooks) =
navigator.lastItem.hooks
.run { filterIsInstance<ScreenHook.OnProvide<*>>() to filterIsInstance<ScreenHook.OnDispose>() }
val navigator = rememberNavigator(screens, LocalNavigator.current)
val currentScreen = navigator.lastItem
val hooks = currentScreen.hooks

CompositionLocalProvider(
LocalNavigator provides navigator,
*onProvideHooks.map { it.provide() }.toTypedArray()
*hooks.providers.map { it.provide() }.toTypedArray()
) {
val currentScreen = navigator.lastItem

content(navigator)

NavigatorBackHandler(navigator, onBackPressed)
Expand All @@ -84,7 +78,8 @@ public fun Navigator(
onDispose {
if (navigator.lastEvent in disposableEvents) {
navigator.stateHolder.removeState(currentScreen.key)
onDisposeHooks.forEach { it.dispose() }
hooks.disposers.forEach { it.dispose() }
currentScreen.clearHooks()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package cafe.adriel.voyager.navigator.tab

import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.SaveableStateHolder
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import cafe.adriel.voyager.core.hook.clearHooks
import cafe.adriel.voyager.core.hook.hooks
import cafe.adriel.voyager.navigator.tab.internal.rememberTabNavigator

public typealias TabNavigatorContent = @Composable (tabNavigator: TabNavigator) -> Unit
Expand All @@ -21,12 +24,22 @@ public fun TabNavigator(
content: TabNavigatorContent = { CurrentTab() }
) {
val tabNavigator = rememberTabNavigator(tab)
val currentTab = tabNavigator.current
val hooks = currentTab.hooks

CompositionLocalProvider(
LocalTabNavigator provides tabNavigator
LocalTabNavigator provides tabNavigator,
*hooks.providers.map { it.provide() }.toTypedArray()
) {
content(tabNavigator)
}

DisposableEffect(tabNavigator) {
onDispose {
hooks.disposers.forEach { it.dispose() }
currentTab.clearHooks()
}
}
}

public class TabNavigator internal constructor(
Expand Down

0 comments on commit a0842a6

Please sign in to comment.