diff --git a/.gitignore b/.gitignore index 1c6dccb..7a28e3e 100644 --- a/.gitignore +++ b/.gitignore @@ -112,3 +112,4 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser +GameLibraryApp.zip diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e32b769..09b0bcc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -3,7 +3,7 @@ plugins { alias(libs.plugins.kotlin.android) id("org.jetbrains.kotlin.plugin.serialization") version "1.9.10" - id("com.google.devtools.ksp") version "1.6.21-1.0.6" + id("com.google.devtools.ksp") version "1.9.0-1.0.12" } android { diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/data/Game.kt b/app/src/main/java/at/xaxa/gamelibraryapp/data/Game.kt index bff3337..799a4a0 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/data/Game.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/data/Game.kt @@ -4,5 +4,7 @@ data class Game( val id: Int, val name: String, val details: String, - val note: String + val note: String, + val imageUrl :String, + val inLibrary: Boolean ) diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/data/GameRepository.kt b/app/src/main/java/at/xaxa/gamelibraryapp/data/GameRepository.kt index 0168f85..6ef573d 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/data/GameRepository.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/data/GameRepository.kt @@ -1,43 +1,56 @@ package at.xaxa.gamelibraryapp.data +import android.util.Log import at.xaxa.gamelibraryapp.data.db.LibraryDao import at.xaxa.gamelibraryapp.data.db.LibraryEntity +import at.xaxa.gamelibraryapp.data.remote.GameDto import at.xaxa.gamelibraryapp.data.remote.GameRemoteService +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.withContext +import kotlin.math.log class GameRepository(private val libraryDao: LibraryDao, private val gameRemoteService: GameRemoteService) { private val apiKey = "7a2a800a085d41e88621a8a59dc5ea82" - /*suspend fun loadInitialContacts() { - try { - val contactDtoList = gameRemoteService.getAllContacts() - contactDtoList.map { - Game(0, it., "${it.telephoneNumber}", it.age) - }.forEach { - insertContact(it) + suspend fun searchGames(searchText: String): List { + return withContext(Dispatchers.IO) { + try { + print(gameRemoteService.searchGames(apiKey, searchText).toString()) + gameRemoteService.searchGames(apiKey, searchText).results + } catch (e: Exception) { + Log.e("GameRepository", "Error searching games from API: ${e.message}", e) + emptyList() } - } catch (e: Exception) { - Log.e("Repository", "Something went wrong! ${e.message}", e) } - }*/ + } fun getAllGamesInLibrary(): Flow> { return libraryDao.getAllEntries().map { - it.map {item -> Game(item._id, item.name, item.details, item.note) } + it.map {item -> Game(item._id, item.name, item.details, item.note, item.imageUrl, item.inLibrary) } } } + + fun searchGameInDB(searchText: String):Flow>{ + return libraryDao.getEntriesWithName(searchText).map { + it.map {item -> Game(item._id, item.name, item.details, item.note, item.imageUrl, item.inLibrary) } + } + } + + suspend fun findGameById(id: Int): LibraryEntity { val item = libraryDao.findEntryById(id) - return LibraryEntity(item._id, item.name, item.details, item.note) + //?: throw GameNotFoundException("Game with id $id not found.") + return item } suspend fun addEntry(libraryEntity: LibraryEntity) { - libraryDao.addEntry(LibraryEntity(libraryEntity._id, libraryEntity.name, libraryEntity.details, libraryEntity.note)) + libraryDao.addEntry(LibraryEntity(libraryEntity._id, libraryEntity.name, libraryEntity.details, libraryEntity.note, libraryEntity.imageUrl, libraryEntity.inLibrary)) } suspend fun updateEntry(game: LibraryEntity) { - libraryDao.updateEntry(LibraryEntity(game._id, game.name, game.details, game.note)) + libraryDao.updateEntry(LibraryEntity(game._id, game.name, game.details, game.note, game.imageUrl, game.inLibrary)) } } \ No newline at end of file diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/data/db/LibraryDao.kt b/app/src/main/java/at/xaxa/gamelibraryapp/data/db/LibraryDao.kt index 36f90fc..fe739e1 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/data/db/LibraryDao.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/data/db/LibraryDao.kt @@ -22,9 +22,9 @@ interface LibraryDao { @Query("SELECT * FROM library WHERE _id = :id") suspend fun findEntryById(id: Int): LibraryEntity - @Query("SELECT * FROM library") + @Query("SELECT * FROM library WHERE inLibrary = 1") fun getAllEntries(): Flow> - @Query("SELECT * FROM library WHERE name = :entryName") + @Query("SELECT * FROM library WHERE name LIKE :entryName AND inLibrary = 1") fun getEntriesWithName(entryName: String): Flow> } diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/data/db/LibraryDatabase.kt b/app/src/main/java/at/xaxa/gamelibraryapp/data/db/LibraryDatabase.kt index e70f19b..4d7ab3e 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/data/db/LibraryDatabase.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/data/db/LibraryDatabase.kt @@ -5,7 +5,7 @@ import androidx.room.Room import androidx.room.RoomDatabase import at.xaxa.gamelibraryapp.LibraryApplication -@Database(entities = [LibraryEntity::class], version = 2) +@Database(entities = [LibraryEntity::class], version = 3) abstract class LibraryDatabase : RoomDatabase() { abstract fun libraryDao(): LibraryDao diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/data/db/LibraryEntity.kt b/app/src/main/java/at/xaxa/gamelibraryapp/data/db/LibraryEntity.kt index 6e2d0a8..75b700c 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/data/db/LibraryEntity.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/data/db/LibraryEntity.kt @@ -9,5 +9,7 @@ data class LibraryEntity( val _id: Int, val name: String, val details: String, - val note: String + val note: String, + val imageUrl :String, + val inLibrary: Boolean ) \ No newline at end of file diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/data/remote/GameRemoteService.kt b/app/src/main/java/at/xaxa/gamelibraryapp/data/remote/GameRemoteService.kt index 324729e..bfe64a9 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/data/remote/GameRemoteService.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/data/remote/GameRemoteService.kt @@ -9,14 +9,5 @@ import retrofit2.http.Query // TODO Implement Remote Service interface GameRemoteService { @GET("games") - suspend fun getAllGames(@Query("key") apiKey: String): List - - @GET("contacts/{contactId}") - suspend fun getContactById(@Path("contactId") contactId: Int) - - @GET("contacts/search") // becomes contacts/search?filter= - suspend fun findContacts(@Query("filter") filterText : String): List - - @POST("contacts") - suspend fun addContact(@Body contactDto: ApiResponse) + suspend fun searchGames(@Query("key") apiKey: String, @Query("search") searchText: String): ApiResponse } \ No newline at end of file diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/ui/AppViewModelProvider.kt b/app/src/main/java/at/xaxa/gamelibraryapp/ui/AppViewModelProvider.kt index dc13023..2cdabe2 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/ui/AppViewModelProvider.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/ui/AppViewModelProvider.kt @@ -6,6 +6,8 @@ import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import at.xaxa.gamelibraryapp.LibraryApplication import at.xaxa.gamelibraryapp.ui.Details.DetailsViewModel +import at.xaxa.gamelibraryapp.ui.GameList.GameListViewModel +import at.xaxa.gamelibraryapp.ui.Search.SearchListViewModel object AppViewModelProvider { val Factory = viewModelFactory { @@ -13,16 +15,20 @@ object AppViewModelProvider { DetailsViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as LibraryApplication).libraryRepository) } - /*initializer { - ContactsViewModel((this[APPLICATION_KEY] as ContactsApplication).contactsRepository) + initializer { + GameListViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as LibraryApplication).libraryRepository) } initializer { - ContactDetailsViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as ContactsApplication).contactsRepository) + SearchListViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as LibraryApplication).libraryRepository) } + /* + initializer { + ContactDetailsViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as ContactsApplication).contactsRepository) + } - initializer { - ContactEditViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as ContactsApplication).contactsRepository) - }*/ + initializer { + ContactEditViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as ContactsApplication).contactsRepository) + }*/ } } \ No newline at end of file diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/ui/Details/Details.kt b/app/src/main/java/at/xaxa/gamelibraryapp/ui/Details/Details.kt index 7d4358b..312eb7d 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/ui/Details/Details.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/ui/Details/Details.kt @@ -1,25 +1,66 @@ package at.xaxa.gamelibraryapp.ui.Details +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FilledTonalButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavBackStackEntry import at.xaxa.gamelibraryapp.ui.AppViewModelProvider import at.xaxa.gamelibraryapp.ui.DetailCard +import at.xaxa.gamelibraryapp.ui.LayoutMediaText +import at.xaxa.gamelibraryapp.ui.ShowPicture +@OptIn(ExperimentalMaterial3Api::class) @Composable fun DetailView( - navBackStackEntry: NavBackStackEntry, viewModel: DetailsViewModel = viewModel(factory = AppViewModelProvider.Factory), modifier: Modifier = Modifier ) { - // Extract arguments here - val gameId = navBackStackEntry.arguments?.getInt("gameId") // Or `Int` if needed + val libraryEntity = viewModel.editUiState.libraryEntity - DetailCard(modifier, - title = "Game Title $gameId", // Use the extracted gameId - details = "details", - imageUrl = "https://media.rawg.io/media/games/b7f/b7ffc4c4776e61eca19d36d3c227f89a.jpg", - note = "note" - ) + val buttonText = if (libraryEntity.inLibrary) "Remove from library" else "Add to library" + + Column { + DetailCard(modifier, + title = libraryEntity.name, + details = libraryEntity.details, + imageUrl = libraryEntity.imageUrl, + note = libraryEntity.note, + onClick = { + viewModel.updateGame(libraryEntity.copy(inLibrary = !libraryEntity.inLibrary)) + }, + buttonText = buttonText + ) + OutlinedTextField( + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 4.dp) + .fillMaxSize(), + shape = RoundedCornerShape(12.dp), + colors = TextFieldDefaults.outlinedTextFieldColors( + focusedBorderColor = Color.DarkGray, + unfocusedBorderColor = Color.LightGray), + value = libraryEntity.note, + onValueChange = { + viewModel.updateGame(libraryEntity.copy(note = it)) + }, + label = { Text("Notes") } + ) + } } diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/ui/Details/DetailsViewModel.kt b/app/src/main/java/at/xaxa/gamelibraryapp/ui/Details/DetailsViewModel.kt index 9292595..41ae437 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/ui/Details/DetailsViewModel.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/ui/Details/DetailsViewModel.kt @@ -13,10 +13,13 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext data class GameEditUi( - val libraryEntity: LibraryEntity = LibraryEntity(0, "", "", "") + val libraryEntity: LibraryEntity = LibraryEntity(0, "", "", "", "",true) ) -class DetailsViewModel(private val savedStateHandle: SavedStateHandle, private val gameRepository: GameRepository) : ViewModel() { +class DetailsViewModel( + private val savedStateHandle: SavedStateHandle, + private val gameRepository: GameRepository +) : ViewModel() { private val gameId: Int = checkNotNull(savedStateHandle["gameId"]) @@ -33,13 +36,12 @@ class DetailsViewModel(private val savedStateHandle: SavedStateHandle, private v } fun updateGame(libraryEntity: LibraryEntity) { - editUiState = editUiState.copy(libraryEntity=libraryEntity) - } + editUiState = editUiState.copy(libraryEntity = libraryEntity) - fun saveLibrary() { viewModelScope.launch { - gameRepository.updateEntry(editUiState.libraryEntity) + withContext(Dispatchers.IO) { + gameRepository.updateEntry(libraryEntity) + } } } - -} \ No newline at end of file +} diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/ui/GameDetailsViewModel.kt b/app/src/main/java/at/xaxa/gamelibraryapp/ui/GameDetailsViewModel.kt index 0cc51f2..6764bea 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/ui/GameDetailsViewModel.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/ui/GameDetailsViewModel.kt @@ -14,7 +14,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext data class GameDetailUi( - val game: LibraryEntity = LibraryEntity(0, "", "", "") + val game: LibraryEntity = LibraryEntity(0, "", "", "", "", false) ) class GameDetailsViewModel(savedStateHandle: SavedStateHandle, private val gameRepository: GameRepository): ViewModel() { diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/ui/GameList/GameList.kt b/app/src/main/java/at/xaxa/gamelibraryapp/ui/GameList/GameList.kt index 01bea5a..3013a83 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/ui/GameList/GameList.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/ui/GameList/GameList.kt @@ -5,20 +5,36 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text +import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import at.xaxa.gamelibraryapp.ui.AppViewModelProvider +import at.xaxa.gamelibraryapp.ui.Details.DetailsViewModel import at.xaxa.gamelibraryapp.ui.HorizontalCard +import kotlinx.coroutines.flow.count +import androidx.compose.foundation.lazy.items + +@OptIn(ExperimentalMaterial3Api::class) @Composable -fun GameList(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit) { +fun GameList(modifier: Modifier = Modifier, + viewModel: GameListViewModel = viewModel(factory = AppViewModelProvider.Factory), + onCardClick: (Int) -> Unit) { + //val gameList = viewModel.editUiState.libraryEntity + val gameListState by viewModel.editUiState.libraryEntity.collectAsState(initial = emptyList()) var searchText by remember { mutableStateOf("") } + viewModel.searchGamesInDB(searchText) Column(modifier = modifier) { // Search Bar @@ -28,19 +44,22 @@ fun GameList(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit) { label = { Text("Search games") }, modifier = Modifier .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 4.dp) + .padding(horizontal = 16.dp, vertical = 4.dp), + colors = TextFieldDefaults.outlinedTextFieldColors( + focusedBorderColor = Color.DarkGray, + unfocusedBorderColor = Color.LightGray) ) // Game List LazyColumn(modifier = Modifier.fillMaxSize()) { - items(16) { index -> + items(gameListState) { game -> HorizontalCard( modifier = modifier, - title = "Game $index", - details = "Details about game $index", - imageUrl = "https://media.rawg.io/media/games/b7f/b7ffc4c4776e61eca19d36d3c227f89a.jpg", + title = game.name, + details = game.details, + imageUrl = game.imageUrl, onClick = { - onCardClick(index) + onCardClick(game.id) } ) } diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/ui/GameList/GameListViewModel.kt b/app/src/main/java/at/xaxa/gamelibraryapp/ui/GameList/GameListViewModel.kt index 115a3b8..7c54fca 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/ui/GameList/GameListViewModel.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/ui/GameList/GameListViewModel.kt @@ -1,2 +1,47 @@ package at.xaxa.gamelibraryapp.ui.GameList +import android.util.Log +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import at.xaxa.gamelibraryapp.data.Game +import at.xaxa.gamelibraryapp.data.GameRepository +import at.xaxa.gamelibraryapp.data.db.LibraryEntity +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +data class GameListUi( + val libraryEntity: Flow> = flowOf(emptyList()) +) + +class GameListViewModel(private val savedStateHandle: SavedStateHandle, private val gameRepository: GameRepository) : ViewModel() { + + var editUiState by mutableStateOf(GameListUi()) + private set + + init { + viewModelScope.launch { + val games = withContext(Dispatchers.IO) { + gameRepository.getAllGamesInLibrary() + } + editUiState = GameListUi(games) + } + } + + fun searchGamesInDB(searchString: String) { + viewModelScope.launch { + val searchResults = withContext(Dispatchers.IO) { + gameRepository.searchGameInDB("%$searchString%") + } + + editUiState = GameListUi(searchResults) + } + } +} diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/ui/LibraryUi.kt b/app/src/main/java/at/xaxa/gamelibraryapp/ui/LibraryUi.kt index fe1e4cd..259fd2d 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/ui/LibraryUi.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/ui/LibraryUi.kt @@ -16,6 +16,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.List import androidx.compose.material.icons.filled.Add +import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Scaffold @@ -99,7 +100,6 @@ fun LibraryApp(modifier: Modifier = Modifier) { ) { backStackEntry -> DetailView( - navBackStackEntry = backStackEntry, modifier = Modifier ) } @@ -114,7 +114,9 @@ fun DetailCard( title: String, details: String, note: String, - imageUrl: String) { + imageUrl: String, + onClick: () -> Unit, + buttonText: String) { Surface( modifier = modifier .fillMaxWidth() @@ -125,6 +127,13 @@ fun DetailCard( Column(modifier) { ShowPicture(modifier = Modifier.height(200.dp), imageUrl) LayoutMediaText(modifier = Modifier, title, details) + FilledTonalButton( + shape = RoundedCornerShape(12.dp), + modifier = Modifier.fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + onClick = { onClick() }) { + Text(buttonText) + } } } } @@ -270,5 +279,7 @@ private fun HorizontalCardPreview() { @Preview @Composable private fun DetailCardPreview() { - DetailCard(Modifier, "Uncharted 3", "Action Adventure, 2011", "test", "https://media.rawg.io/media/games/b7f/b7ffc4c4776e61eca19d36d3c227f89a.jpg") + DetailCard(Modifier, "Uncharted 3", "Action Adventure, 2011", "test", "https://media.rawg.io/media/games/b7f/b7ffc4c4776e61eca19d36d3c227f89a.jpg", onClick = { + println("clicked") + }, "Add to library") } \ No newline at end of file diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/ui/Search/SearchList.kt b/app/src/main/java/at/xaxa/gamelibraryapp/ui/Search/SearchList.kt index 19b74e5..6a2f9e0 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/ui/Search/SearchList.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/ui/Search/SearchList.kt @@ -1,22 +1,70 @@ package at.xaxa.gamelibraryapp.ui.Search +import android.util.Log +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import at.xaxa.gamelibraryapp.ui.AppViewModelProvider import at.xaxa.gamelibraryapp.ui.HorizontalCard +import androidx.compose.foundation.lazy.items +@OptIn(ExperimentalMaterial3Api::class) @Composable -fun SearchList(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit){ - LazyColumn{ - items(16) { index -> - HorizontalCard(modifier, - "test $index", - "details $index", - "https://media.rawg.io/media/games/6e0/6e0c19bb111bd4fa20cf0eb72a049519.jpg", - onClick = { - onCardClick(index) - } +fun SearchList( + modifier: Modifier = Modifier, + viewModel: SearchListViewModel = viewModel(factory = AppViewModelProvider.Factory), + onCardClick: (Int) -> Unit +) { + var searchText by remember { mutableStateOf("") } + val gamesList by viewModel.editUiState.games.collectAsState(initial = emptyList()) + + Column(modifier = modifier) { + // Search field + OutlinedTextField( + value = searchText, + onValueChange = { + searchText = it + // Call searchGames when text changes + viewModel.searchGames(searchText) + }, + label = { Text("Search games") }, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 4.dp), + colors = TextFieldDefaults.outlinedTextFieldColors( + focusedBorderColor = Color.DarkGray, + unfocusedBorderColor = Color.LightGray ) + ) + + LazyColumn(modifier = Modifier.fillMaxSize()) { + items(gamesList) { game -> + HorizontalCard(modifier, + game.name, + game.details, + game.imageUrl,//"https://media.rawg.io/media/games/6e0/6e0c19bb111bd4fa20cf0eb72a049519.jpg", + onClick = { + viewModel.addGameToDB(game) + onCardClick(game.id) + } + ) + } } } } \ No newline at end of file diff --git a/app/src/main/java/at/xaxa/gamelibraryapp/ui/Search/SearchListViewModel.kt b/app/src/main/java/at/xaxa/gamelibraryapp/ui/Search/SearchListViewModel.kt index 38f4dec..0110350 100644 --- a/app/src/main/java/at/xaxa/gamelibraryapp/ui/Search/SearchListViewModel.kt +++ b/app/src/main/java/at/xaxa/gamelibraryapp/ui/Search/SearchListViewModel.kt @@ -1,2 +1,80 @@ package at.xaxa.gamelibraryapp.ui.Search +import android.util.Log +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import at.xaxa.gamelibraryapp.data.Game +import at.xaxa.gamelibraryapp.data.GameRepository +import at.xaxa.gamelibraryapp.data.db.LibraryEntity +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + + +// UI State to hold a list of games +data class GameListUi( + val games: Flow> = flow{emit(emptyList()) } +) + +class SearchListViewModel( + private val savedStateHandle: SavedStateHandle, + private val gameRepository: GameRepository +) : ViewModel() { + + var editUiState by mutableStateOf(GameListUi()) + private set + + // Function to search games based on search text + fun searchGames(searchText: String) { + viewModelScope.launch { + // Launching a background task to fetch the data from the repository + val games = withContext(Dispatchers.IO) { + gameRepository.searchGames(searchText) // Get the list of games + } + + // Now transform the data before emitting + val mappedGames = games.map { game -> + // Transform each game as needed + Game( + id = game.id, + name = game.name, + details = game.genres.firstOrNull()?.name ?: "Genre Unknown", + note = "", + imageUrl = game.background_image, + inLibrary = false + ) + } + + editUiState = GameListUi(games = flow { emit(mappedGames) }) + } + } + + fun addGameToDB(game: Game){ + viewModelScope.launch { + try { + val libraryEntity = LibraryEntity( + _id = game.id, + name = game.name, + details = game.details, + note = game.note, + imageUrl = game.imageUrl, + inLibrary = false + ) + + withContext(Dispatchers.IO) { + gameRepository.addEntry(libraryEntity) // Add game to the database + } + // Optionally, log or update UI state to reflect that the game was added + Log.d("GameLibraryApp", "Game added to DB: ${game.name}") + } catch (e: Exception) { + Log.e("GameLibraryApp", "Error adding game to DB", e) + } + } + } +}