Some ugly code..., it works

This commit is contained in:
Joren 2024-04-30 01:04:22 +02:00
parent 93b4d04587
commit e0a40a548e
Signed by untrusted user who does not match committer: Joren
GPG Key ID: 280E33DFBC0F1B55
9 changed files with 124 additions and 17 deletions

View File

@ -1,11 +1,11 @@
package com.ti.mobpo package com.ti.mobpo
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory import androidx.lifecycle.viewmodel.viewModelFactory
import com.ti.mobpo.ui.pokesearch.PokeSearchViewModel import com.ti.mobpo.ui.viewmodels.FavouritesViewModel
import com.ti.mobpo.ui.viewmodels.PokeSearchViewModel
object AppViewModelProvider { object AppViewModelProvider {
val Factory = viewModelFactory { val Factory = viewModelFactory {
@ -13,9 +13,17 @@ object AppViewModelProvider {
PokeSearchViewModel( PokeSearchViewModel(
pokesearchApplication().appContainer.favouritesRepository pokesearchApplication().appContainer.favouritesRepository
) )
}
initializer {
FavouritesViewModel(
pokesearchApplication().appContainer.favouritesRepository, pokesearchApplication().featureManager
)
} }
} }
} }
fun CreationExtras.pokesearchApplication(): PokeSearch = fun CreationExtras.pokesearchApplication(): PokeSearch =
(this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as PokeSearch) (this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as PokeSearch)

View File

@ -3,17 +3,26 @@ package com.ti.mobpo
import android.app.Application import android.app.Application
import com.ti.mobpo.data.AppContainer import com.ti.mobpo.data.AppContainer
import com.ti.mobpo.data.AppDataContainer import com.ti.mobpo.data.AppDataContainer
import com.ti.mobpo.ui.util.FeatureManager
class PokeSearch : Application() { class PokeSearch : Application() {
lateinit var appContainer: AppContainer lateinit var appContainer: AppContainer
private set private set
lateinit var featureManager: FeatureManager
private set
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
appContainer = createAppContainer() appContainer = createAppContainer()
featureManager = createFeatrureManager()
} }
private fun createAppContainer(): AppContainer { private fun createAppContainer(): AppContainer {
return AppDataContainer(this) return AppDataContainer(this)
} }
private fun createFeatrureManager(): FeatureManager {
return FeatureManager(this)
}
} }

View File

@ -20,18 +20,16 @@ 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.ui.screens.Favourites import com.ti.mobpo.ui.screens.FavoritesScreen
import com.ti.mobpo.ui.screens.PokeSearchScreen import com.ti.mobpo.ui.screens.PokeSearchScreen
@Composable @Composable
@ -108,7 +106,7 @@ fun Navigation() {
} }
composable( composable(
route = Screen.Favourites.route) { route = Screen.Favourites.route) {
Favourites() FavoritesScreen()
} }
} }
} }

View File

@ -1,3 +0,0 @@
package com.ti.mobpo.ui.pokesearch
data class PokeSearchUiState(val searchQuery: String = "")

View File

@ -18,6 +18,8 @@ import com.ti.mobpo.ui.viewmodels.FavouritesViewModel
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@Composable @Composable
@ -27,8 +29,11 @@ fun FavoritesScreen(viewModel: FavouritesViewModel = viewModel(factory = AppView
@Composable @Composable
fun Favorites(favoritesViewModel: FavouritesViewModel) { fun Favorites(favoritesViewModel: FavouritesViewModel) {
val favorites by favoritesViewModel.pokemonDetails.collectAsState() LaunchedEffect(Unit) {
favoritesViewModel.loadFavourites() favoritesViewModel.loadFavourites()
}
val favorites by favoritesViewModel.pokemonDetails.collectAsState()
Column( Column(
verticalArrangement = Arrangement.Top, verticalArrangement = Arrangement.Top,

View File

@ -1,6 +1,6 @@
package com.ti.mobpo.ui.screens package com.ti.mobpo.ui.screens
import com.ti.mobpo.ui.pokesearch.PokeSearchViewModel import com.ti.mobpo.ui.viewmodels.PokeSearchViewModel
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -26,9 +26,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
@ -94,7 +91,6 @@ fun PokeSearch(pokeSearchViewModel: PokeSearchViewModel) {
@Composable @Composable
fun PokemonCard(pokemon: PokemonDetails, toggleFavorite: (Int) -> Unit) { fun PokemonCard(pokemon: PokemonDetails, toggleFavorite: (Int) -> Unit) {
var isFavourite by remember { mutableStateOf(false) }
Card( Card(
shape = MaterialTheme.shapes.medium, shape = MaterialTheme.shapes.medium,
elevation = CardDefaults.cardElevation(defaultElevation = 8.dp), elevation = CardDefaults.cardElevation(defaultElevation = 8.dp),

View File

@ -0,0 +1,18 @@
package com.ti.mobpo.ui.util
import android.content.Context
class FeatureManager(private val context: Context) {
private val PREFS_NAME = "FeaturePrefs"
private val KEY_PAID_FEATURE_ENABLED = "paid_feature_enabled"
fun hasAccessToPaidFeature(): Boolean {
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
return prefs.getBoolean(KEY_PAID_FEATURE_ENABLED, false)
}
fun setPaidFeatureEnabled(enabled: Boolean) {
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
prefs.edit().putBoolean(KEY_PAID_FEATURE_ENABLED, enabled).apply()
}
}

View File

@ -0,0 +1,76 @@
package com.ti.mobpo.ui.viewmodels
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.ti.mobpo.data.Favourite
import com.ti.mobpo.data.FavouritesRepository
import com.ti.mobpo.model.PokemonDetails
import com.ti.mobpo.network.PokeApi
import com.ti.mobpo.ui.util.FeatureManager
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import java.io.IOException
class FavouritesViewModel(private val favouritesRepository: FavouritesRepository, featureManager: FeatureManager) : ViewModel() {
private val service = PokeApi.retrofitService;
private val _pokemonDetails = MutableStateFlow<List<PokemonDetails>?>(null)
val pokemonDetails: StateFlow<List<PokemonDetails>?> = _pokemonDetails.asStateFlow()
fun loadFavourites() {
// Ugly workaround to make sure all the favourites are loaded before displaying them
viewModelScope.launch {
try {
val favouritesList: StateFlow<FavouriteUiState> =
favouritesRepository.getAllItems().map { FavouriteUiState(it) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000L),
initialValue = FavouriteUiState()
)
favouritesList.collect { state ->
val detailsList = mutableListOf<PokemonDetails>()
for (favourite in state.favourites) {
val details = service.getPokemonDetails(favourite.id)
detailsList.add(details.copy(isFavorite = true))
}
_pokemonDetails.value = detailsList
}
} catch (e: IOException) {
/* Handle error */
}
}
}
fun toggleFavorite(pokemonId: Int) {
_pokemonDetails.value = _pokemonDetails.value?.map { pokemon ->
if (pokemon.id == pokemonId) {
pokemon.copy(isFavorite = !pokemon.isFavorite)
} else {
pokemon
}
}
viewModelScope.launch {
if (_pokemonDetails.value != null) {
val pokemon = _pokemonDetails.value!!.find { it.id == pokemonId }
pokemon?.let {
if (it.isFavorite) {
favouritesRepository.insertItem(Favourite(it.id, it.name))
} else {
favouritesRepository.deleteItem(Favourite(it.id, it.name))
}
}
}
}
}
}
data class FavouriteUiState(val favourites: List<Favourite> = listOf())

View File

@ -1,4 +1,4 @@
package com.ti.mobpo.ui.pokesearch package com.ti.mobpo.ui.viewmodels
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope