Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ fun InterestsScreen(
uiState = uiState,
followTopic = viewModel::followTopic,
onTopicClick = {
// TODO: this violates SSOT, events should go through the ViewModel
viewModel.onTopicClick(it)
onTopicClick(it)
},
shouldHighlightSelectedTopic = shouldHighlightSelectedTopic,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@

package com.google.samples.apps.nowinandroid.feature.interests.impl

import androidx.lifecycle.SavedStateHandle
import androidx.compose.runtime.snapshotFlow
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase
import com.google.samples.apps.nowinandroid.core.domain.TopicSortField
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
import com.google.samples.apps.nowinandroid.core.navigation.NavigationState
import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsNavKey
import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.TopicNavKey
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
Expand All @@ -36,22 +38,23 @@ import kotlinx.coroutines.launch

@HiltViewModel(assistedFactory = InterestsViewModel.Factory::class)
class InterestsViewModel @AssistedInject constructor(
private val savedStateHandle: SavedStateHandle,
val userDataRepository: UserDataRepository,
getFollowableTopics: GetFollowableTopicsUseCase,
// TODO: see comment below
@Assisted val navigationState: NavigationState,
@Assisted val key: InterestsNavKey,
) : ViewModel() {

// TODO: this should no longer be necessary, the currently selected topic should be
// available through the navigation state
// Key used to save and retrieve the currently selected topic id from saved state.
private val selectedTopicIdKey = "selectedTopicIdKey"

private val selectedTopicId = savedStateHandle.getStateFlow(
key = selectedTopicIdKey,
initialValue = key.initialTopicId,
)
// Derive selected topic from navigation state
private val selectedTopicId: StateFlow<String?> =
snapshotFlow {
navigationState.currentSubStack
.lastOrNull { it is TopicNavKey }
?.let { (it as TopicNavKey).id }
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = key.initialTopicId,
)

val uiState: StateFlow<InterestsUiState> = combine(
selectedTopicId,
Expand All @@ -69,15 +72,9 @@ class InterestsViewModel @AssistedInject constructor(
}
}

fun onTopicClick(topicId: String?) {
// TODO: This should modify the navigation state directly rather than just updating the
// savedStateHandle
savedStateHandle[selectedTopicIdKey] = topicId
}

@AssistedFactory
interface Factory {
fun create(key: InterestsNavKey): InterestsViewModel
fun create(navigationState: NavigationState, key: InterestsNavKey): InterestsViewModel
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fun EntryProviderScope<NavKey>.interestsEntry(navigator: Navigator) {
},
) { key ->
val viewModel = hiltViewModel<InterestsViewModel, InterestsViewModel.Factory> {
it.create(key)
it.create(navigator.state, key)
}
InterestsScreen(
// TODO: This event should either be provided by the ViewModel or by the navigator, not both
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@

package com.google.samples.apps.nowinandroid.interests.impl

import androidx.lifecycle.SavedStateHandle
import androidx.navigation.testing.invoke
import androidx.navigation3.runtime.NavBackStack
import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.navigation.NavigationState
import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule
import com.google.samples.apps.nowinandroid.feature.interests.api.navigation.InterestsNavKey
import com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsUiState
import com.google.samples.apps.nowinandroid.feature.interests.impl.InterestsViewModel
import com.google.samples.apps.nowinandroid.feature.topic.api.navigation.TopicNavKey
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
Expand Down Expand Up @@ -66,13 +67,23 @@ class InterestsViewModelTest {

@Before
fun setup() {
val initialTopicId = testInputTopics[0].topic.id
val interestsKey = InterestsNavKey(initialTopicId = initialTopicId)

viewModel = InterestsViewModel(
savedStateHandle = SavedStateHandle(
route = InterestsNavKey(initialTopicId = testInputTopics[0].topic.id),
),
userDataRepository = userDataRepository,
getFollowableTopics = getFollowableTopicsUseCase,
InterestsNavKey(initialTopicId = testInputTopics[0].topic.id),
navigationState = NavigationState(
startKey = interestsKey,
topLevelStack = NavBackStack(interestsKey),
subStacks = mapOf(
interestsKey to NavBackStack(
interestsKey,
TopicNavKey(id = initialTopicId), // Add TopicNavKey to the sub-stack
),
),
),
key = interestsKey,
)
}

Expand Down