diff --git a/app/src/main/java/com/owenlejeune/mydex/api/pokeapi/v2/model/evolution/ChainLink.kt b/app/src/main/java/com/owenlejeune/mydex/api/pokeapi/v2/model/evolution/ChainLink.kt index b01e878..ccf93a4 100644 --- a/app/src/main/java/com/owenlejeune/mydex/api/pokeapi/v2/model/evolution/ChainLink.kt +++ b/app/src/main/java/com/owenlejeune/mydex/api/pokeapi/v2/model/evolution/ChainLink.kt @@ -7,5 +7,5 @@ class ChainLink( @SerializedName("is_baby") val isBaby: Boolean, @SerializedName("species") val species: NameAndUrl, @SerializedName("evolution_details") val evolutionDetails: EvolutionDetails, - @SerializedName("evolves_to") val evolves_to: List? + @SerializedName("evolves_to") val evolves_to: List? ) diff --git a/app/src/main/java/com/owenlejeune/mydex/api/pokeapi/v2/model/evolution/EvolutionChain.kt b/app/src/main/java/com/owenlejeune/mydex/api/pokeapi/v2/model/evolution/EvolutionChain.kt index 8b19e15..de60a2f 100644 --- a/app/src/main/java/com/owenlejeune/mydex/api/pokeapi/v2/model/evolution/EvolutionChain.kt +++ b/app/src/main/java/com/owenlejeune/mydex/api/pokeapi/v2/model/evolution/EvolutionChain.kt @@ -6,5 +6,5 @@ import com.owenlejeune.mydex.api.pokeapi.v2.model.misc.NameAndUrl class EvolutionChain( @SerializedName("id") val id: Int, @SerializedName("baby_trigger_item") val babyTriggerItem: NameAndUrl?, - @SerializedName("chain") val chain: ChainLink + @SerializedName("chain") val chain: ChainLink? ) \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/mydex/extensions/ListExtensions.kt b/app/src/main/java/com/owenlejeune/mydex/extensions/ListExtensions.kt new file mode 100644 index 0000000..3184332 --- /dev/null +++ b/app/src/main/java/com/owenlejeune/mydex/extensions/ListExtensions.kt @@ -0,0 +1,9 @@ +package com.owenlejeune.mydex.extensions + +import androidx.compose.ui.text.intl.Locale +import com.owenlejeune.mydex.api.pokeapi.v2.model.misc.NameAndLanguage + +fun List.getNameForLanguage(): String? { + val lang = Locale.current.language + return find { it.language.name == lang }?.name +} \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/mydex/ui/MainActivity.kt b/app/src/main/java/com/owenlejeune/mydex/ui/MainActivity.kt index f9f511a..a168557 100644 --- a/app/src/main/java/com/owenlejeune/mydex/ui/MainActivity.kt +++ b/app/src/main/java/com/owenlejeune/mydex/ui/MainActivity.kt @@ -61,7 +61,7 @@ class MainActivity : MonetCompatActivity() { ) { val id = it.arguments?.getInt("id") id?.let { - PokemonDetailView(pokemonId = it) + PokemonDetailView(pokemonId = id, appNavController = appNavController) } } } diff --git a/app/src/main/java/com/owenlejeune/mydex/ui/views/DetailView.kt b/app/src/main/java/com/owenlejeune/mydex/ui/views/DetailView.kt index 7c4b5f6..b06ffd4 100644 --- a/app/src/main/java/com/owenlejeune/mydex/ui/views/DetailView.kt +++ b/app/src/main/java/com/owenlejeune/mydex/ui/views/DetailView.kt @@ -1,13 +1,195 @@ package com.owenlejeune.mydex.ui.views -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import coil.compose.AsyncImage +import com.owenlejeune.mydex.R +import com.owenlejeune.mydex.api.pokeapi.v2.PokemonService +import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies +import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType +import com.owenlejeune.mydex.extensions.getIdFromUrl +import com.owenlejeune.mydex.extensions.getNameForLanguage +import com.owenlejeune.mydex.ui.components.PokemonTypeLabel import com.owenlejeune.mydex.utils.AppCache +import com.owenlejeune.mydex.utils.ColorUtils +import org.koin.java.KoinJavaComponent.get +@OptIn(ExperimentalMaterial3Api::class) @Composable fun PokemonDetailView( - pokemonId: Int + pokemonId: Int, + appNavController: NavController ) { - val pokemon = AppCache.cachedSpecies[pokemonId] - Text(text = pokemon.name) + val pokemonSpecies = AppCache.cachedSpecies[pokemonId] + val pokemon = AppCache.cachedPokemon[pokemonId] + + Box( + modifier = Modifier + .fillMaxSize() + .background(color = ColorUtils.pokeColorToComposeColor(color = pokemonSpecies.color.name)) + ) { + AsyncImage( + model = R.drawable.pokeball, + contentDescription = null, + modifier = Modifier + .offset(y = 200.dp) + .rotate(65f) + .size(240.dp) + .align(Alignment.TopCenter), + colorFilter = ColorFilter.tint(Color.White.copy(alpha = 0.25f)) + ) + + Column( + modifier = Modifier + .fillMaxSize() + ) { + Spacer(modifier = Modifier.height(120.dp)) + + Header(pokemonSpecies = pokemonSpecies, modifier = Modifier.padding(horizontal = 36.dp)) + + Box { + Card( + modifier = Modifier + .fillMaxSize() + .padding(top = 140.dp), + shape = RoundedCornerShape(topStart = 32.dp, topEnd = 32.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.background) + ) { + + } + + AsyncImage( + model = pokemon.sprites.frontDefault, + contentDescription = null, + contentScale = ContentScale.FillBounds, + modifier = Modifier + .size(250.dp) + .align(Alignment.TopCenter) + ) + } + } + + SmallTopAppBar( + modifier = Modifier + .statusBarsPadding(), + navigationIcon = { + IconButton(onClick = { appNavController.popBackStack() }) { + Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = null) + } + }, + title = {}, + colors = TopAppBarDefaults + .smallTopAppBarColors( + containerColor = Color.Transparent//.copy(alpha = 0.4f) + ) + ) + } +} + +@Composable +private fun Header( + modifier: Modifier = Modifier, + pokemonSpecies: PokemonSpecies, + service: PokemonService = get(PokemonService::class.java) +) { + val pokemon = AppCache.cachedPokemon.get(pokemonSpecies.id) + + Row( + modifier = modifier + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + val pokemonName = pokemonSpecies.names.getNameForLanguage() ?: pokemonSpecies.name + Text( + text = pokemonName, + style = TextStyle( + fontWeight = FontWeight.Bold, + fontSize = 30.sp, + color = MaterialTheme.colorScheme.onBackground + ) + ) + + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + pokemon.types.forEach { type -> + val id = type.type.url.getIdFromUrl() + val pokemonType = remember { mutableStateOf(AppCache.cachedTypes[id]) } + LaunchedEffect(key1 = pokemonType.value) { + fetchPokemonType(id, pokemonType, service) + } + + pokemonType.value?.let { t -> + val typeName = t.names.getNameForLanguage() ?: "" + PokemonTypeLabel(type = typeName) + } + } + } + } + + Spacer(modifier = Modifier.weight(1f)) + + Column( + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + val dexNumber = pokemon.id.toString().padStart(3, '0') + Text( + modifier = Modifier.align(Alignment.End), + text = "#${dexNumber}", + style = TextStyle( + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + color = MaterialTheme.colorScheme.onBackground + ) + ) + + val genus = pokemonSpecies.genera.find { it.language.name == Locale.current.language }?.genus ?: "" + Text( + text = genus, + modifier = Modifier.align(Alignment.End), + style = TextStyle( + fontSize = 12.sp, + color = MaterialTheme.colorScheme.onBackground + ) + ) + } + } +} + +private suspend fun fetchPokemonType( + id: Int, + pokemonType: MutableState, + service: PokemonService +) { + if (pokemonType.value == null) { + service.getPokemonType(id).apply { + if (isSuccessful) { + body()?.let { + pokemonType.value = it + AppCache.cachedTypes.put(it.id, it) + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/owenlejeune/mydex/ui/views/PokedexViews.kt b/app/src/main/java/com/owenlejeune/mydex/ui/views/PokedexViews.kt index c33239e..a6cad3a 100644 --- a/app/src/main/java/com/owenlejeune/mydex/ui/views/PokedexViews.kt +++ b/app/src/main/java/com/owenlejeune/mydex/ui/views/PokedexViews.kt @@ -32,6 +32,7 @@ import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType import com.owenlejeune.mydex.api.pokeapi.v2.viewmodel.PokemonViewModel import com.owenlejeune.mydex.extensions.getIdFromUrl +import com.owenlejeune.mydex.extensions.getNameForLanguage import com.owenlejeune.mydex.extensions.header import com.owenlejeune.mydex.extensions.lazyPagingItems import com.owenlejeune.mydex.ui.components.PokemonTypeLabel @@ -67,7 +68,7 @@ fun PokedexView( horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier - .padding(start = 36.dp, end = 36.dp) + .padding(horizontal = 36.dp) .fillMaxSize() ) { header { @@ -95,8 +96,7 @@ fun PokedexView( SmallTopAppBar( modifier = Modifier - .statusBarsPadding() - .blur(radius = 10.dp), + .statusBarsPadding(), navigationIcon = { IconButton(onClick = { appNavController.popBackStack() }) { Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = null) @@ -134,7 +134,7 @@ fun PokedexCard( val bgColor = remember { mutableStateOf(defBg) } Card( modifier = Modifier - .height(160.dp) + .height(130.dp) .fillMaxWidth(), shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = bgColor.value), @@ -153,7 +153,7 @@ fun PokedexCard( contentDescription = null, modifier = Modifier .align(Alignment.BottomEnd) - .offset(x = 20.dp, y = 0.dp) + .offset(x = 20.dp, y = 20.dp) .size(width = 96.dp, height = 96.dp), colorFilter = ColorFilter.tint(Color.White.copy(alpha = 0.15f)) ) @@ -166,8 +166,7 @@ fun PokedexCard( species.value?.let { species -> bgColor.value = ColorUtils.pokeColorToComposeColor(color = species.color.name) - val locale = Locale.current.language - val name = species.names.find { it.language.name == locale }?.name ?: species.name + val name = species.names.getNameForLanguage() ?: species.name val dexNumber = pokemon.value!!.id.toString().padStart(3, '0') Text( @@ -191,7 +190,7 @@ fun PokedexCard( } pokemonType.value?.let { t -> - val typeName = t.names.find { it.language.name == locale }?.name ?: ""//type.type.name + val typeName = t.names.getNameForLanguage() ?: ""//type.type.name PokemonTypeLabel(type = typeName) } }