mirror of
https://github.com/owenlejeune/MYDex.git
synced 2025-11-08 08:22:42 -05:00
pokedex view
This commit is contained in:
@@ -2,6 +2,8 @@ package com.owenlejeune.mydex.api.pokeapi.v2
|
||||
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.misc.PaginatedResponse
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.Pokemon
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Path
|
||||
@@ -15,4 +17,10 @@ interface PokemonApi {
|
||||
@GET("pokemon/")
|
||||
suspend fun getPaginatedPokemon(@Query("offset") offset: Int, @Query("limit") limit: Int): Response<PaginatedResponse>
|
||||
|
||||
@GET("pokemon-species/{id}")
|
||||
suspend fun getPokemonSpecies(@Path("id") id: Int): Response<PokemonSpecies>
|
||||
|
||||
@GET("type/{id}")
|
||||
suspend fun getPokemonType(@Path("id") id: Int): Response<PokemonType>
|
||||
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package com.owenlejeune.mydex.api.pokeapi.v2
|
||||
import android.util.Log
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.misc.PaginatedResponse
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.Pokemon
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import retrofit2.Response
|
||||
@@ -10,7 +12,7 @@ import retrofit2.Response
|
||||
class PokemonService: KoinComponent {
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_LIMIT = 20
|
||||
private const val DEFAULT_LIMIT = 60
|
||||
|
||||
val TAG = PokemonService::class.java.simpleName
|
||||
}
|
||||
@@ -23,7 +25,7 @@ class PokemonService: KoinComponent {
|
||||
|
||||
suspend fun getPaginatedPokemon(page: Int = 1): Response<PaginatedResponse> {
|
||||
val limit = DEFAULT_LIMIT
|
||||
val offset = DEFAULT_LIMIT * page
|
||||
val offset = DEFAULT_LIMIT * (page-1)
|
||||
|
||||
Log.d(TAG, "Paginated: page=$page, limit=$limit, offset=$offset")
|
||||
|
||||
@@ -32,4 +34,12 @@ class PokemonService: KoinComponent {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getPokemonSpecies(id: Int): Response<PokemonSpecies> {
|
||||
return service.getPokemonSpecies(id)
|
||||
}
|
||||
|
||||
suspend fun getPokemonType(id: Int): Response<PokemonType> {
|
||||
return service.getPokemonType(id)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,5 @@ import com.google.gson.annotations.SerializedName
|
||||
|
||||
class NameAndLanguage(
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("language.name") val language: String,
|
||||
@SerializedName("language.url") val languageUrl: String
|
||||
@SerializedName("language") val language: NameAndUrl
|
||||
)
|
||||
|
||||
@@ -5,11 +5,13 @@ import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.PokemonService
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.misc.NameAndUrl
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.misc.PaginatedResponse
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.Pokemon
|
||||
import com.owenlejeune.mydex.utils.AppCache
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class PokemonPagingSource: PagingSource<Int, Pokemon>(), KoinComponent {
|
||||
class PokemonPagingSource: PagingSource<Int, NameAndUrl>(), KoinComponent {
|
||||
|
||||
companion object {
|
||||
private val TAG = PokemonPagingSource::class.java.simpleName
|
||||
@@ -17,21 +19,22 @@ class PokemonPagingSource: PagingSource<Int, Pokemon>(), KoinComponent {
|
||||
|
||||
private val pokemonService: PokemonService by inject()
|
||||
|
||||
override fun getRefreshKey(state: PagingState<Int, Pokemon>): Int? {
|
||||
override fun getRefreshKey(state: PagingState<Int, NameAndUrl>): Int? {
|
||||
return state.anchorPosition
|
||||
}
|
||||
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Pokemon> {
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, NameAndUrl> {
|
||||
return try {
|
||||
val nextPage = params.key ?: 1
|
||||
val response = pokemonService.getPokemon(nextPage)
|
||||
val response = pokemonService.getPaginatedPokemon(nextPage)
|
||||
if (response.isSuccessful) {
|
||||
val responseBody = response.body()!!
|
||||
// Log.d(TAG, "${results.map { it.name }}")
|
||||
val responseBody = response.body()
|
||||
val results = responseBody?.results ?: emptyList()
|
||||
Log.d(TAG, "${results.map { it.name }}")
|
||||
LoadResult.Page(
|
||||
data = listOf(responseBody),
|
||||
data = results,
|
||||
prevKey = if (nextPage == 1) null else nextPage - 1,
|
||||
nextKey = if (responseBody == null) null else nextPage + 1
|
||||
nextKey = if (results.isEmpty()) null else nextPage + 1
|
||||
)
|
||||
} else {
|
||||
LoadResult.Invalid()
|
||||
@@ -41,4 +44,23 @@ class PokemonPagingSource: PagingSource<Int, Pokemon>(), KoinComponent {
|
||||
}
|
||||
}
|
||||
|
||||
// fun t() {
|
||||
// val nextPage = params.key ?: 1
|
||||
// var pokemon = AppCache.cachedPokemon[nextPage]
|
||||
// if (pokemon == null) {
|
||||
// val response = pokemonService.getPokemon(nextPage)
|
||||
// if (response.isSuccessful) {
|
||||
// pokemon = response.body()!!
|
||||
// } else {
|
||||
// return LoadResult.Invalid()
|
||||
// }
|
||||
// }
|
||||
// Log.d(TAG, pokemon.name)
|
||||
// LoadResult.Page(
|
||||
// data = listOf(pokemon),
|
||||
// prevKey = if (nextPage == 1) null else nextPage - 1,
|
||||
// nextKey = nextPage + 1
|
||||
// )
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import com.owenlejeune.mydex.api.pokeapi.v2.paging.PokemonPagingSource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class PokemonViewModel: ViewModel() {
|
||||
val pokemon: Flow<PagingData<Pokemon>> = Pager(PagingConfig(pageSize = Int.MAX_VALUE)) {
|
||||
val pokemon: Flow<PagingData<NameAndUrl>> = Pager(PagingConfig(pageSize = Int.MAX_VALUE)) {
|
||||
PokemonPagingSource()
|
||||
}.flow.cachedIn(viewModelScope)
|
||||
}
|
||||
@@ -19,15 +19,6 @@ fun <T: Any> LazyGridScope.lazyPagingItems(
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Any> LazyGridScope.listItems(
|
||||
items: List<T>,
|
||||
itemContent: @Composable (value: T) -> Unit
|
||||
) {
|
||||
items(items.size) { index ->
|
||||
itemContent(items[index])
|
||||
}
|
||||
}
|
||||
|
||||
fun LazyGridScope.header(
|
||||
content: @Composable LazyGridItemScope.() -> Unit
|
||||
) {
|
||||
@@ -37,32 +28,4 @@ fun LazyGridScope.header(
|
||||
},
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
fun <T: Any> LazyListScope.listItems(
|
||||
items: Collection<T>,
|
||||
itemContent: @Composable (value: T) -> Unit
|
||||
) {
|
||||
items(items.size) { index ->
|
||||
itemContent(items.elementAt(index))
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Any?> LazyListScope.listItems(
|
||||
items: List<T?>,
|
||||
key: (T?) -> Any,
|
||||
itemContent: @Composable (value: T?) -> Unit
|
||||
) {
|
||||
items(items.size, key = { key(items[it]) }) { index ->
|
||||
itemContent(items[index])
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: Any> LazyListScope.lazyPagingItems(
|
||||
lazyPagingItems: LazyPagingItems<T>,
|
||||
itemContent: @Composable LazyItemScope.(value: T?) -> Unit
|
||||
) {
|
||||
items(lazyPagingItems.itemCount) { index ->
|
||||
itemContent(lazyPagingItems[index])
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
package com.owenlejeune.mydex.extensions
|
||||
|
||||
fun String.charAtFromEnd(index: Int): Char {
|
||||
return get(length-1-index)
|
||||
fun String.getIdFromUrl(): Int {
|
||||
return split("/").find { it.toIntOrNull() != null }?.toInt() ?: -1
|
||||
}
|
||||
@@ -7,9 +7,11 @@ import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.kieronquinn.monetcompat.app.MonetCompatActivity
|
||||
import com.owenlejeune.mydex.preferences.AppPreferences
|
||||
import com.owenlejeune.mydex.ui.navigation.DataNavItem
|
||||
@@ -17,6 +19,8 @@ import com.owenlejeune.mydex.ui.navigation.MainNavItem
|
||||
import com.owenlejeune.mydex.ui.theme.MYDexTheme
|
||||
import com.owenlejeune.mydex.ui.views.AppScaffold
|
||||
import com.owenlejeune.mydex.ui.views.PokedexView
|
||||
import com.owenlejeune.mydex.ui.views.PokemonDetailView
|
||||
import com.owenlejeune.mydex.utils.AppCache
|
||||
import org.koin.java.KoinJavaComponent
|
||||
|
||||
class MainActivity : MonetCompatActivity() {
|
||||
@@ -49,6 +53,17 @@ class MainActivity : MonetCompatActivity() {
|
||||
composable(DataNavItem.Pokedex.route) {
|
||||
PokedexView(appNavController = appNavController)
|
||||
}
|
||||
composable(
|
||||
MainNavItem.PokemonDetailView.route.plus("/{id}"),
|
||||
arguments = listOf(
|
||||
navArgument("id") { type = NavType.IntType }
|
||||
)
|
||||
) {
|
||||
val id = it.arguments?.getInt("id")
|
||||
id?.let {
|
||||
PokemonDetailView(pokemonId = it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.modifier.modifierLocalConsumer
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.AsyncImage
|
||||
@@ -191,4 +192,24 @@ fun MenuItemButton(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PokemonTypeLabel(
|
||||
type: String
|
||||
) {
|
||||
Card(
|
||||
shape = RoundedCornerShape(24.dp),
|
||||
colors = CardDefaults.cardColors(containerColor = Color(0x38FFFFFF))
|
||||
) {
|
||||
Text(
|
||||
text = type,
|
||||
modifier = Modifier
|
||||
.padding(vertical = 3.dp, horizontal = 8.dp),
|
||||
textAlign = TextAlign.Center,
|
||||
fontSize = 12.sp,
|
||||
color = Color.White
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ sealed class DataNavItem(
|
||||
|
||||
val title = resourceUtils.getString(titleRes)
|
||||
|
||||
object Pokedex: DataNavItem("pokedex_route", PokeTeal, R.string.pokedex_nav_title)
|
||||
object Pokedex: DataNavItem("pokedex_route", PokeGreen, R.string.pokedex_nav_title)
|
||||
object Moves: DataNavItem("moves_route", PokeRed, R.string.moves_nav_title)
|
||||
object Abilities: DataNavItem("abilities_route", PokeLightBlue, R.string.abilities_nav_title)
|
||||
object Items: DataNavItem("items_route", PokeYellow, R.string.items_nav_title)
|
||||
|
||||
@@ -7,5 +7,6 @@ sealed class MainNavItem(val route: String) {
|
||||
}
|
||||
|
||||
object MainView: MainNavItem("main_route")
|
||||
object PokemonDetailView: MainNavItem("pokemon_route")
|
||||
|
||||
}
|
||||
@@ -3,15 +3,17 @@ package com.owenlejeune.mydex.ui.theme
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val PokeBlack = Color(0xFF303943)
|
||||
val PokeGrey = Color(0xFF434C57)
|
||||
val PokeWhite = Color(0xFF898A8B)
|
||||
val PokeBlue = Color(0xFF429BED)
|
||||
val PokeBrown = Color(0xFFB1736C)
|
||||
val PokeLightBlue = Color(0xFF58ABF6)
|
||||
val PokeLightBrown = Color(0xFFCA8179)
|
||||
val PokeLightPurple = Color(0xFF9F5BBA)
|
||||
val PokeLightRed = Color(0xFFF7786B)
|
||||
val PokeLightTeal = Color(0xFF2CDAB1)
|
||||
val PokeLightGreen = Color(0xFF2CDAB1)
|
||||
val PokeLightYellow = Color(0xFFFFCE4B)
|
||||
val PokePurple = Color(0xFF7C538C)
|
||||
val PokeRed = Color(0xFFFA6555)
|
||||
val PokeTeal = Color(0xFF4FC1A6)
|
||||
val PokeGreen = Color(0xFF4FC1A6)
|
||||
val PokeYellow = Color(0xFFF6C747)
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.owenlejeune.mydex.ui.views
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.owenlejeune.mydex.utils.AppCache
|
||||
|
||||
@Composable
|
||||
fun PokemonDetailView(
|
||||
pokemonId: Int
|
||||
) {
|
||||
val pokemon = AppCache.cachedSpecies[pokemonId]
|
||||
Text(text = pokemon.name)
|
||||
}
|
||||
@@ -4,23 +4,21 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
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.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.blur
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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
|
||||
@@ -30,11 +28,16 @@ import com.owenlejeune.mydex.R
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.PokemonService
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.misc.NameAndUrl
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.Pokemon
|
||||
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.charAtFromEnd
|
||||
import com.owenlejeune.mydex.extensions.getIdFromUrl
|
||||
import com.owenlejeune.mydex.extensions.header
|
||||
import com.owenlejeune.mydex.extensions.lazyPagingItems
|
||||
import kotlinx.coroutines.launch
|
||||
import com.owenlejeune.mydex.ui.components.PokemonTypeLabel
|
||||
import com.owenlejeune.mydex.ui.navigation.MainNavItem
|
||||
import com.owenlejeune.mydex.utils.AppCache
|
||||
import com.owenlejeune.mydex.utils.ColorUtils
|
||||
import org.koin.java.KoinJavaComponent.get
|
||||
|
||||
@Composable
|
||||
@@ -57,6 +60,39 @@ fun PokedexView(
|
||||
colorFilter = ColorFilter.tint(tint)
|
||||
)
|
||||
|
||||
val lazyPokemon = PokemonViewModel().pokemon.collectAsLazyPagingItems()
|
||||
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(2),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier
|
||||
.padding(start = 36.dp, end = 36.dp)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
header {
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(160.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.pokedex_nav_title),
|
||||
style = TextStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 30.sp,
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
),
|
||||
modifier = Modifier.padding(bottom = 50.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
lazyPagingItems(lazyPokemon) { item ->
|
||||
item?.let {
|
||||
PokedexCard(appNavController = appNavController, nameAndUrl = item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SmallTopAppBar(
|
||||
modifier = Modifier
|
||||
.statusBarsPadding()
|
||||
@@ -72,54 +108,45 @@ fun PokedexView(
|
||||
containerColor = Color.Transparent.copy(alpha = 0.4f)
|
||||
)
|
||||
)
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
val lazyPokemon = PokemonViewModel().pokemon.collectAsLazyPagingItems()
|
||||
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(2),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier
|
||||
.padding(start = 36.dp, end = 36.dp, top = 160.dp)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
header {
|
||||
Text(
|
||||
text = stringResource(id = R.string.main_screen_title),
|
||||
style = TextStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontSize = 30.sp,
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
),
|
||||
modifier = Modifier.padding(bottom = 50.dp)
|
||||
)
|
||||
}
|
||||
|
||||
lazyPagingItems(lazyPokemon) { item ->
|
||||
item?.let {
|
||||
PokedexCard(pokemon = item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PokedexCard(
|
||||
pokemon: Pokemon
|
||||
appNavController: NavController,
|
||||
nameAndUrl: NameAndUrl,
|
||||
service: PokemonService = get(PokemonService::class.java)
|
||||
) {
|
||||
val pokemonId = nameAndUrl.url.getIdFromUrl()
|
||||
|
||||
val pokemon = remember { mutableStateOf<Pokemon?>(AppCache.cachedPokemon[pokemonId]) }
|
||||
LaunchedEffect(key1 = pokemon.value) {
|
||||
fetchPokemon(nameAndUrl, service, pokemon)
|
||||
}
|
||||
|
||||
val species = remember { mutableStateOf<PokemonSpecies?>(AppCache.cachedSpecies[pokemonId]) }
|
||||
LaunchedEffect(key1 = pokemon.value) {
|
||||
fetchPokemonSpecies(pokemon, species, service)
|
||||
}
|
||||
|
||||
val defBg = ColorUtils.pokeColorToComposeColor(color = "")
|
||||
val bgColor = remember { mutableStateOf(defBg) }
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.height(80.dp)
|
||||
.height(160.dp)
|
||||
.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.secondaryContainer)
|
||||
colors = CardDefaults.cardColors(containerColor = bgColor.value),
|
||||
onClick = {
|
||||
pokemon.value?.let {
|
||||
appNavController.navigate("${MainNavItem.PokemonDetailView.route}/${it.id}")
|
||||
}
|
||||
}
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
AsyncImage(
|
||||
model = R.drawable.pokeball_s,
|
||||
@@ -131,13 +158,115 @@ fun PokedexCard(
|
||||
colorFilter = ColorFilter.tint(Color.White.copy(alpha = 0.15f))
|
||||
)
|
||||
|
||||
Text(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
.padding(start = 16.dp),
|
||||
text = pokemon.name,
|
||||
style = MaterialTheme.typography.bodyLarge.copy(color = Color.White)
|
||||
)
|
||||
.fillMaxSize()
|
||||
.padding(all = 12.dp)
|
||||
) {
|
||||
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 dexNumber = pokemon.value!!.id.toString().padStart(3, '0')
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopStart),
|
||||
text = name,
|
||||
style = MaterialTheme.typography.bodyLarge.copy(color = Color.White, fontWeight = FontWeight.Bold)
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopStart)
|
||||
.padding(top = 35.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
pokemon.value?.types?.forEach { type ->
|
||||
val id = type.type.url.getIdFromUrl()
|
||||
val pokemonType = remember { mutableStateOf<PokemonType?>(AppCache.cachedTypes[id]) }
|
||||
LaunchedEffect(key1 = pokemonType.value) {
|
||||
fetchPokemonType(id, pokemonType, service)
|
||||
}
|
||||
|
||||
pokemonType.value?.let { t ->
|
||||
val typeName = t.names.find { it.language.name == locale }?.name ?: ""//type.type.name
|
||||
PokemonTypeLabel(type = typeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd),
|
||||
text = "#${dexNumber}",
|
||||
style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold),
|
||||
color = Color.Unspecified.copy(alpha = 0.3f)
|
||||
)
|
||||
|
||||
AsyncImage(
|
||||
model = pokemon.value?.sprites?.frontDefault,
|
||||
contentDescription = name,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.size(70.dp),
|
||||
contentScale = ContentScale.FillBounds
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchPokemon(
|
||||
nameAndUrl: NameAndUrl,
|
||||
service: PokemonService,
|
||||
pokemon: MutableState<Pokemon?>
|
||||
) {
|
||||
if (pokemon.value == null) {
|
||||
val id = nameAndUrl.url.getIdFromUrl()
|
||||
service.getPokemon(id).apply {
|
||||
if (isSuccessful) {
|
||||
body()?.let {
|
||||
pokemon.value = it
|
||||
AppCache.cachedPokemon.put(it.id, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchPokemonSpecies(
|
||||
pokemon: MutableState<Pokemon?>,
|
||||
species: MutableState<PokemonSpecies?>,
|
||||
service: PokemonService
|
||||
) {
|
||||
if (species.value == null && pokemon.value != null) {
|
||||
service.getPokemonSpecies(pokemon.value!!.id).apply {
|
||||
if (isSuccessful) {
|
||||
body()?.let {
|
||||
species.value = it
|
||||
AppCache.cachedSpecies.put(it.id, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchPokemonType(
|
||||
id: Int,
|
||||
pokemonType: MutableState<PokemonType?>,
|
||||
service: PokemonService
|
||||
) {
|
||||
if (pokemonType.value == null) {
|
||||
service.getPokemonType(id).apply {
|
||||
if (isSuccessful) {
|
||||
body()?.let {
|
||||
pokemonType.value = it
|
||||
AppCache.cachedTypes.put(it.id, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
app/src/main/java/com/owenlejeune/mydex/utils/AppCache.kt
Normal file
14
app/src/main/java/com/owenlejeune/mydex/utils/AppCache.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
package com.owenlejeune.mydex.utils
|
||||
|
||||
import android.util.SparseArray
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.Pokemon
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies
|
||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType
|
||||
|
||||
object AppCache {
|
||||
|
||||
var cachedSpecies = SparseArray<PokemonSpecies>()
|
||||
var cachedPokemon = SparseArray<Pokemon>()
|
||||
var cachedTypes = SparseArray<PokemonType>()
|
||||
|
||||
}
|
||||
27
app/src/main/java/com/owenlejeune/mydex/utils/ColorUtils.kt
Normal file
27
app/src/main/java/com/owenlejeune/mydex/utils/ColorUtils.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package com.owenlejeune.mydex.utils
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.owenlejeune.mydex.ui.theme.*
|
||||
|
||||
object ColorUtils {
|
||||
|
||||
@Composable
|
||||
fun pokeColorToComposeColor(color: String): Color {
|
||||
return when (color) {
|
||||
"green" -> PokeGreen
|
||||
"red" -> PokeRed
|
||||
"blue" -> PokeBlue
|
||||
"black" -> PokeBlack
|
||||
"brown" -> PokeBrown
|
||||
"purple" -> PokePurple
|
||||
"pink" -> PokeLightBrown
|
||||
"grey" -> PokeGrey
|
||||
"white" -> PokeWhite
|
||||
"yellow" -> PokeYellow
|
||||
else -> MaterialTheme.colorScheme.secondaryContainer
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
<string name="main_search_placeholder">Search Pokémon, Move, Ability, etc.</string>
|
||||
|
||||
<!-- nav titles -->
|
||||
<string name="pokedex_nav_title">Pokedex</string>
|
||||
<string name="pokedex_nav_title">Pokédex</string>
|
||||
<string name="moves_nav_title">Moves</string>
|
||||
<string name="abilities_nav_title">Abilities</string>
|
||||
<string name="items_nav_title">Items</string>
|
||||
|
||||
Reference in New Issue
Block a user