Skip to content

Commit

Permalink
support removing navigables from LazySetNavigator
Browse files Browse the repository at this point in the history
  • Loading branch information
gerin98 committed Oct 23, 2024
1 parent 5129cf2 commit 8ad37ae
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public open class LazySetNavigator(
private val navigationPropagator: NavigationPropagator = NavigationPropagator
private var ongoingTransition: MagellanTransition? = null

@VisibleForTesting
internal var existingNavigables: MutableSet<NavigableCompat> = mutableSetOf()

@VisibleForTesting
internal var containerView: ScreenContainer? = null
private var currentNavigable: NavigableCompat? = null
Expand All @@ -36,9 +39,39 @@ public open class LazySetNavigator(
}

public fun addNavigable(navigable: NavigableCompat) {
existingNavigables.add(navigable)
lifecycleRegistry.attachToLifecycleWithMaxState(navigable, LifecycleLimit.CREATED)
}

public fun removeNavigables(navigables: Set<NavigableCompat>) {
for (navigable in navigables) {
removeNavigable(navigable)
}
}

public fun removeNavigable(navigable: NavigableCompat) {
existingNavigables.remove(navigable)
lifecycleRegistry.removeFromLifecycle(navigable)
}

public fun safeAddNavigable(navigable: NavigableCompat) {
if (!existingNavigables.contains(navigable)) {
addNavigable(navigable)
}
}

public fun updateNavigables(navigables: Set<NavigableCompat>, handleCurrentTabRemoval: () -> Unit) {
val navigablesToRemove = existingNavigables subtract navigables
val navigablesToAdd = navigables subtract existingNavigables

if (navigablesToRemove.contains(currentNavigable)) {
handleCurrentTabRemoval()
}

removeNavigables(navigablesToRemove)
addNavigables(navigablesToAdd)
}

override fun onShow(context: Context) {
containerView = container()
currentNavigable?.let { currentNavigable ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.wealthfront.magellan.lifecycle.LifecycleState
import com.wealthfront.magellan.lifecycle.transitionToState
import com.wealthfront.magellan.transitions.CrossfadeTransition
import io.mockk.MockKAnnotations.init
import io.mockk.clearAllMocks
import io.mockk.clearMocks
import io.mockk.every
import io.mockk.impl.annotations.MockK
Expand All @@ -28,6 +29,8 @@ class LazySetNavigatorTest {
private lateinit var navigator: LazySetNavigator
private lateinit var step1: DummyStep
private lateinit var step2: DummyStep
private lateinit var step3: DummyStep
private lateinit var step4: DummyStep
@MockK private lateinit var navigableListener: NavigationListener

@Before
Expand All @@ -38,13 +41,16 @@ class LazySetNavigatorTest {
navigator = LazySetNavigator { ScreenContainer(activityController.get()) }
step1 = DummyStep()
step2 = DummyStep()
step3 = DummyStep()
step4 = DummyStep()

NavigationPropagator.addNavigableListener(navigableListener)
}

@After
fun tearDown() {
NavigationPropagator.removeNavigableListener(navigableListener)
clearAllMocks()
}

private fun initMocks() {
Expand Down Expand Up @@ -100,6 +106,7 @@ class LazySetNavigatorTest {
assertThat(navigator.containerView!!.getChildAt(0)).isEqualTo(step2.view)
assertThat(step1.currentState).isInstanceOf(LifecycleState.Shown::class.java)
assertThat(step2.currentState).isInstanceOf(LifecycleState.Resumed::class.java)
assertThat(navigator.existingNavigables).containsExactly(step1, step2)

verify { navigableListener.beforeNavigation() }
verify { navigableListener.onNavigatedFrom(step1) }
Expand Down Expand Up @@ -136,5 +143,123 @@ class LazySetNavigatorTest {
assertThat(navigator.containerView!!.getChildAt(0)).isEqualTo(step1.view)
assertThat(step1.currentState).isInstanceOf(LifecycleState.Resumed::class.java)
assertThat(step2.currentState).isInstanceOf(LifecycleState.Shown::class.java)
assertThat(navigator.existingNavigables).containsExactly(step1, step2)
}

@Test
fun removeNavigables() {
navigator.addNavigables(setOf(step1, step2, step3, step4))
navigator.transitionToState(LifecycleState.Resumed(activityController.get()))

navigator.replace(step1, CrossfadeTransition())
step1.view!!.viewTreeObserver.dispatchOnPreDraw()
shadowOf(Looper.getMainLooper()).idle()
assertThat(navigator.containerView!!.childCount).isEqualTo(1)
assertThat(navigator.containerView!!.getChildAt(0)).isEqualTo(step1.view)
assertThat(step1.currentState).isInstanceOf(LifecycleState.Resumed::class.java)
assertThat(step2.currentState).isInstanceOf(LifecycleState.Created::class.java)
assertThat(step3.currentState).isInstanceOf(LifecycleState.Created::class.java)
assertThat(step4.currentState).isInstanceOf(LifecycleState.Created::class.java)
assertThat(navigator.existingNavigables).containsExactly(step1, step2, step3, step4)

verify { navigableListener.beforeNavigation() }
verify(exactly = 0) { navigableListener.onNavigatedFrom(any()) }
verify { navigableListener.onNavigatedTo(step1) }
verify { navigableListener.afterNavigation() }
clearMocks(navigableListener)
initMocks()

navigator.removeNavigables(setOf(step2, step4))

step1.view!!.viewTreeObserver.dispatchOnPreDraw()
shadowOf(Looper.getMainLooper()).idle()
assertThat(navigator.containerView!!.childCount).isEqualTo(1)
assertThat(navigator.containerView!!.getChildAt(0)).isEqualTo(step1.view)
assertThat(step1.currentState).isInstanceOf(LifecycleState.Resumed::class.java)
assertThat(step3.currentState).isInstanceOf(LifecycleState.Created::class.java)
assertThat(navigator.existingNavigables).containsExactly(step1, step3)

verify(exactly = 0) { navigableListener.onNavigatedTo(any()) }
}

@Test
fun updateMultipleNavigables() {
navigator.updateNavigables(setOf(step1, step2)) {}
navigator.transitionToState(LifecycleState.Resumed(activityController.get()))

navigator.replace(step1, CrossfadeTransition())
step1.view!!.viewTreeObserver.dispatchOnPreDraw()
shadowOf(Looper.getMainLooper()).idle()
assertThat(navigator.containerView!!.childCount).isEqualTo(1)
assertThat(navigator.containerView!!.getChildAt(0)).isEqualTo(step1.view)
assertThat(step1.currentState).isInstanceOf(LifecycleState.Resumed::class.java)
assertThat(step2.currentState).isInstanceOf(LifecycleState.Created::class.java)
assertThat(navigator.existingNavigables).containsExactly(step1, step2)

verify { navigableListener.beforeNavigation() }
verify(exactly = 0) { navigableListener.onNavigatedFrom(any()) }
verify { navigableListener.onNavigatedTo(step1) }
verify { navigableListener.afterNavigation() }
clearMocks(navigableListener)
initMocks()

navigator.updateNavigables(setOf(step1, step3, step4)) {
navigator.replace(step1, CrossfadeTransition())
}
navigator.replace(step3, CrossfadeTransition())

step3.view!!.viewTreeObserver.dispatchOnPreDraw()
shadowOf(Looper.getMainLooper()).idle()
assertThat(navigator.containerView!!.childCount).isEqualTo(1)
assertThat(navigator.containerView!!.getChildAt(0)).isEqualTo(step3.view)
assertThat(step1.currentState).isInstanceOf(LifecycleState.Shown::class.java)
assertThat(step3.currentState).isInstanceOf(LifecycleState.Resumed::class.java)
assertThat(step4.currentState).isInstanceOf(LifecycleState.Created::class.java)
assertThat(navigator.existingNavigables).containsExactly(step1, step3, step4)

verify { navigableListener.beforeNavigation() }
verify { navigableListener.onNavigatedFrom(step1) }
verify { navigableListener.onNavigatedTo(step3) }
verify { navigableListener.afterNavigation() }
}

@Test
fun updateMultipleNavigables_andRemoveCurrentNavigable() {
navigator.updateNavigables(setOf(step1, step2)) {}
navigator.transitionToState(LifecycleState.Resumed(activityController.get()))

navigator.replace(step2, CrossfadeTransition())
step2.view!!.viewTreeObserver.dispatchOnPreDraw()
shadowOf(Looper.getMainLooper()).idle()
assertThat(navigator.containerView!!.childCount).isEqualTo(1)
assertThat(navigator.containerView!!.getChildAt(0)).isEqualTo(step2.view)
assertThat(step1.currentState).isInstanceOf(LifecycleState.Created::class.java)
assertThat(step2.currentState).isInstanceOf(LifecycleState.Resumed::class.java)
assertThat(navigator.existingNavigables).containsExactly(step1, step2)

verify { navigableListener.beforeNavigation() }
verify(exactly = 0) { navigableListener.onNavigatedFrom(any()) }
verify { navigableListener.onNavigatedTo(step2) }
verify { navigableListener.afterNavigation() }
clearMocks(navigableListener)
initMocks()

navigator.updateNavigables(setOf(step1, step3, step4)) {
navigator.replace(step1, CrossfadeTransition())
}

step1.view!!.viewTreeObserver.dispatchOnPreDraw()
shadowOf(Looper.getMainLooper()).idle()
assertThat(navigator.containerView!!.childCount).isEqualTo(1)
assertThat(navigator.containerView!!.getChildAt(0)).isEqualTo(step1.view)
assertThat(step1.currentState).isInstanceOf(LifecycleState.Resumed::class.java)
assertThat(step3.currentState).isInstanceOf(LifecycleState.Created::class.java)
assertThat(step4.currentState).isInstanceOf(LifecycleState.Created::class.java)
assertThat(navigator.existingNavigables).containsExactly(step1, step3, step4)

verify { navigableListener.beforeNavigation() }
verify { navigableListener.onNavigatedFrom(step2) }
verify { navigableListener.onNavigatedTo(step1) }
verify { navigableListener.afterNavigation() }
}
}

0 comments on commit 8ad37ae

Please sign in to comment.