Make sure code doesnt get fetched multiple times, share the same vm over the whole session

This commit is contained in:
Joren 2024-04-30 01:40:33 +02:00
parent e0a40a548e
commit 2f1c6604ba
Signed by untrusted user who does not match committer: Joren
GPG Key ID: 280E33DFBC0F1B55
6 changed files with 45 additions and 17 deletions

View File

@ -0,0 +1,5 @@
package com.ti.mobpo.model
import com.ti.mobpo.data.Favourite
data class FavouriteUiState(val favourites: List<Favourite> = listOf())

View File

@ -20,17 +20,22 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.ti.mobpo.AppViewModelProvider
import com.ti.mobpo.ui.screens.FavoritesScreen import com.ti.mobpo.ui.screens.FavoritesScreen
import com.ti.mobpo.ui.screens.PokeSearchScreen import com.ti.mobpo.ui.screens.PokeSearchScreen
import com.ti.mobpo.ui.viewmodels.FavouritesViewModel
import com.ti.mobpo.ui.viewmodels.PokeSearchViewModel
@Composable @Composable
fun Navigation() { fun Navigation() {
@ -59,6 +64,9 @@ fun Navigation() {
val updatedSelectedIndex = items.indexOfFirst { it.route == currentRoute } val updatedSelectedIndex = items.indexOfFirst { it.route == currentRoute }
selectedItemIndex = rememberUpdatedState(updatedSelectedIndex).value selectedItemIndex = rememberUpdatedState(updatedSelectedIndex).value
val pokeSearchVM = viewModel<PokeSearchViewModel>(factory = AppViewModelProvider.Factory)
val favoritesVM = viewModel<FavouritesViewModel>(factory = AppViewModelProvider.Factory)
Scaffold ( Scaffold (
bottomBar = { bottomBar = {
NavigationBar { NavigationBar {
@ -102,11 +110,11 @@ fun Navigation() {
ExitTransition.None ExitTransition.None
}) { }) {
composable(route = Screen.PokeSearch.route) { composable(route = Screen.PokeSearch.route) {
PokeSearchScreen() PokeSearchScreen(pokeSearchVM)
} }
composable( composable(
route = Screen.Favourites.route) { route = Screen.Favourites.route) {
FavoritesScreen() FavoritesScreen(favoritesVM)
} }
} }
} }
@ -120,6 +128,6 @@ fun Navigation() {
@Composable @Composable
fun NavigationPreview() { fun NavigationPreview() {
Surface { Surface {
PokeSearchScreen() PokeSearchScreen(viewModel(factory = AppViewModelProvider.Factory))
} }
} }

View File

@ -23,7 +23,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@Composable @Composable
fun FavoritesScreen(viewModel: FavouritesViewModel = viewModel(factory = AppViewModelProvider.Factory)) { fun FavoritesScreen(viewModel: FavouritesViewModel) {
Favorites(viewModel) Favorites(viewModel)
} }

View File

@ -47,7 +47,7 @@ import com.ti.mobpo.capitalizeFirstLetterAfterHyphens
import com.ti.mobpo.ui.theme.MobileSecurityTheme import com.ti.mobpo.ui.theme.MobileSecurityTheme
@Composable @Composable
fun PokeSearchScreen(viewModel: PokeSearchViewModel = viewModel(factory = AppViewModelProvider.Factory)) { fun PokeSearchScreen(viewModel: PokeSearchViewModel) {
PokeSearch(viewModel) PokeSearch(viewModel)
} }

View File

@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.ti.mobpo.data.Favourite import com.ti.mobpo.data.Favourite
import com.ti.mobpo.data.FavouritesRepository import com.ti.mobpo.data.FavouritesRepository
import com.ti.mobpo.model.FavouriteUiState
import com.ti.mobpo.model.PokemonDetails import com.ti.mobpo.model.PokemonDetails
import com.ti.mobpo.network.PokeApi import com.ti.mobpo.network.PokeApi
import com.ti.mobpo.ui.util.FeatureManager import com.ti.mobpo.ui.util.FeatureManager
@ -47,9 +48,6 @@ class FavouritesViewModel(private val favouritesRepository: FavouritesRepository
} }
} }
fun toggleFavorite(pokemonId: Int) { fun toggleFavorite(pokemonId: Int) {
_pokemonDetails.value = _pokemonDetails.value?.map { pokemon -> _pokemonDetails.value = _pokemonDetails.value?.map { pokemon ->
if (pokemon.id == pokemonId) { if (pokemon.id == pokemonId) {
@ -72,5 +70,3 @@ class FavouritesViewModel(private val favouritesRepository: FavouritesRepository
} }
} }
} }
data class FavouriteUiState(val favourites: List<Favourite> = listOf())

View File

@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope
import com.ti.mobpo.data.Favourite import com.ti.mobpo.data.Favourite
import com.ti.mobpo.data.FavouritesRepository import com.ti.mobpo.data.FavouritesRepository
import com.ti.mobpo.model.PokemonDetails import com.ti.mobpo.model.PokemonDetails
import com.ti.mobpo.model.PokemonSpecies
import com.ti.mobpo.network.PokeApi import com.ti.mobpo.network.PokeApi
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@ -13,23 +14,41 @@ import kotlinx.coroutines.launch
import java.io.IOException import java.io.IOException
private const val SHOW_LIMIT = 20;
class PokeSearchViewModel(private val favouritesRepository: FavouritesRepository) : ViewModel() { class PokeSearchViewModel(private val favouritesRepository: FavouritesRepository) : ViewModel() {
private val service = PokeApi.retrofitService; private val service = PokeApi.retrofitService;
private val _pokemonDetails = MutableStateFlow<List<PokemonDetails>?>(null) private val _pokemonDetails = MutableStateFlow<List<PokemonDetails>?>(null)
val pokemonDetails: StateFlow<List<PokemonDetails>?> = _pokemonDetails.asStateFlow() val pokemonDetails: StateFlow<List<PokemonDetails>?> = _pokemonDetails.asStateFlow()
private val _initialPokemonList = MutableStateFlow<List<PokemonSpecies>?>(null)
init {
fetchPokemonSpecies()
}
private fun fetchPokemonSpecies() {
viewModelScope.launch {
try {
val response = service.getPokemon()
_initialPokemonList.value = response.results
} catch (e: IOException) {
/*TODO*/
}
}
}
fun search(query: String) { fun search(query: String) {
viewModelScope.launch { viewModelScope.launch {
try { try {
val response = service.getPokemon() val filteredList = _initialPokemonList.value?.filter { it.name.contains(query, ignoreCase = true) }
val filteredList = response.results.filter { it.name.contains(query, ignoreCase = true) }
val detailsList = mutableListOf<PokemonDetails>() val detailsList = mutableListOf<PokemonDetails>()
for (pokemonSpecies in filteredList) { if (filteredList != null) {
val details = service.getPokemonDetails(extractPokemonId(pokemonSpecies.url)) for (pokemonSpecies in filteredList.take(SHOW_LIMIT)) {
val isFavorite = favouritesRepository.isFavourite(details.id) val details = service.getPokemonDetails(extractPokemonId(pokemonSpecies.url))
detailsList.add(details.copy(isFavorite = isFavorite)) val isFavorite = favouritesRepository.isFavourite(details.id)
detailsList.add(details.copy(isFavorite = isFavorite))
}
} }
_pokemonDetails.value = detailsList _pokemonDetails.value = detailsList
} catch (e: IOException) { } catch (e: IOException) {