Start implementing search

This commit is contained in:
FirephoenixX02 2024-04-16 14:54:24 +02:00
parent e5a8586560
commit 72fe8363b8
4 changed files with 1096 additions and 10 deletions

View file

@ -7,13 +7,23 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
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.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.TextFieldColors
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
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.geometry.CornerRadius import androidx.compose.ui.geometry.CornerRadius
@ -28,8 +38,12 @@ import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import io.kamel.image.KamelImage import io.kamel.image.KamelImage
import io.kamel.image.asyncPainterResource import io.kamel.image.asyncPainterResource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.painterResource
import java.util.Locale
class HomeScreen: Screen { class HomeScreen: Screen {
@ -38,6 +52,8 @@ class HomeScreen: Screen {
override fun Content() { override fun Content() {
val orange = Color(0xFFffa500) val orange = Color(0xFFffa500)
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val coroutineScope = rememberCoroutineScope()
var searchQuery by remember { mutableStateOf("") }
Box(modifier = Modifier.background(color = orange).fillMaxSize()) { Box(modifier = Modifier.background(color = orange).fillMaxSize()) {
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 256.dp), columns = GridCells.Adaptive(minSize = 256.dp),
@ -88,6 +104,20 @@ class HomeScreen: Screen {
} }
} }
} }
OutlinedTextField(
value = searchQuery,
onValueChange = { newName -> searchQuery = newName;
if (PokemonNames.names.contains(searchQuery.lowercase(Locale.getDefault()))) {
coroutineScope.launch {
withContext(Dispatchers.IO) {
val newMap = loadPokemonDataFromName(searchQuery);
newMap?.let { pokemap.clear(); pokemap.addAll(it) }
}
}
}
},
modifier = Modifier.background(color = Color.White),
)
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -37,6 +37,7 @@ import java.util.Locale
var pokemap = ArrayList<Pokemon>() var pokemap = ArrayList<Pokemon>()
val apiString = "https://pokeapi.co/api/v2/pokemon/" val apiString = "https://pokeapi.co/api/v2/pokemon/"
@OptIn(ExperimentalResourceApi::class) @OptIn(ExperimentalResourceApi::class)
val pokemonTypeDrawableMap = hashMapOf( val pokemonTypeDrawableMap = hashMapOf(
"normal" to Res.drawable.normal, "normal" to Res.drawable.normal,
@ -59,7 +60,6 @@ val pokemonTypeDrawableMap = hashMapOf(
"fairy" to Res.drawable.fairy "fairy" to Res.drawable.fairy
) )
@OptIn(ExperimentalResourceApi::class)
@Composable @Composable
@Preview @Preview
fun App() { fun App() {
@ -67,14 +67,14 @@ fun App() {
MaterialTheme { MaterialTheme {
if (!dataLoaded) { if (!dataLoaded) {
Navigator(screen = LoadingScreen()) { Navigator(screen = LoadingScreen()) { navigator ->
navigator -> FadeTransition(navigator) FadeTransition(navigator)
} }
// Load data asynchronously // Load data asynchronously
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
pokemap = withContext(Dispatchers.IO) { pokemap = withContext(Dispatchers.IO) {
loadPokemonData(apiString, 1, 10) loadPokemonData(1, 10)
} as ArrayList<Pokemon> } as ArrayList<Pokemon>
pokemap.forEach { pokemon -> pokemap.forEach { pokemon ->
@ -87,25 +87,26 @@ fun App() {
} else { } else {
println("Pokemon should show up any second!") println("Pokemon should show up any second!")
//Display Pokemon Grid //Display Pokemon Grid
Navigator(screen = HomeScreen()) { Navigator(screen = HomeScreen()) { navigator ->
navigator -> SlideTransition(navigator) SlideTransition(navigator)
} }
} }
} }
} }
// Function to load Pokemon data asynchronously, leave suspend even if IDE complains // Function to load Pokemon data asynchronously, leave suspend even if IDE complains
suspend fun loadPokemonData(apiString: String, startId: Int, endId: Int): List<Pokemon> { suspend fun loadPokemonData(startId: Int, endId: Int): List<Pokemon> {
val pokemap = ArrayList<Pokemon>() val pokemap = ArrayList<Pokemon>()
for (i in startId..endId) { for (i in startId..endId) {
val json = JSONObject(URL(apiString + i).readText()); val json = JSONObject(URL(apiString + i).readText());
val sprites = json.optJSONObject("sprites") val sprites = json.optJSONObject("sprites")
val type: String = json.getJSONArray("types").optJSONObject(0).optJSONObject("type").optString("name") val type: String =
json.getJSONArray("types").optJSONObject(0).optJSONObject("type")?.optString("name") ?: "null"
val name: String = json.optString("name") val name: String = json.optString("name")
.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } .replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
val pokemon = Pokemon( val pokemon = Pokemon(
name, URL(sprites.optString("front_default")), type, json.optInt("id") name, URL(sprites?.optString("front_default") ?: "null"), type, json.optInt("id")
) )
pokemap.add(pokemon) pokemap.add(pokemon)
@ -113,6 +114,31 @@ suspend fun loadPokemonData(apiString: String, startId: Int, endId: Int): List<P
return pokemap return pokemap
} }
suspend fun loadPokemonDataFromName(name: String): ArrayList<Pokemon>? {
if (name.equals("")) return null
var json: JSONObject = JSONObject("{}")
try {
json = JSONObject(URL(apiString + name).readText());
} catch (e: Exception) {
return null
}
val sprites = json.optJSONObject("sprites")
val type: String =
json.getJSONArray("types").optJSONObject(0).optJSONObject("type")?.optString("name") ?: "null"
val name: String = json.optString("name")
.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
val pokemon = Pokemon(
name, URL(sprites?.optString("front_default") ?: "null"), type, json.optInt("id")
)
val pokemap = ArrayList<Pokemon>()
pokemap.add(pokemon)
return pokemap
}
fun getPokeMap(): ArrayList<Pokemon> { fun getPokeMap(): ArrayList<Pokemon> {
return pokemap return pokemap
} }

View file

@ -0,0 +1,5 @@
object PokemonNames {
val names: ArrayList<String> = arrayListOf(
)
}