1.0
This commit is contained in:
parent
133e06db1e
commit
82ecbb2caa
1
.gitignore
vendored
1
.gitignore
vendored
@ -112,3 +112,4 @@ fabric.properties
|
|||||||
# Android studio 3.1+ serialized cache file
|
# Android studio 3.1+ serialized cache file
|
||||||
.idea/caches/build_file_checksums.ser
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
GameLibraryApp.zip
|
||||||
|
@ -3,7 +3,7 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
|
|
||||||
id("org.jetbrains.kotlin.plugin.serialization") version "1.9.10"
|
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 {
|
android {
|
||||||
|
@ -4,5 +4,7 @@ data class Game(
|
|||||||
val id: Int,
|
val id: Int,
|
||||||
val name: String,
|
val name: String,
|
||||||
val details: String,
|
val details: String,
|
||||||
val note: String
|
val note: String,
|
||||||
|
val imageUrl :String,
|
||||||
|
val inLibrary: Boolean
|
||||||
)
|
)
|
||||||
|
@ -1,43 +1,56 @@
|
|||||||
package at.xaxa.gamelibraryapp.data
|
package at.xaxa.gamelibraryapp.data
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import at.xaxa.gamelibraryapp.data.db.LibraryDao
|
import at.xaxa.gamelibraryapp.data.db.LibraryDao
|
||||||
import at.xaxa.gamelibraryapp.data.db.LibraryEntity
|
import at.xaxa.gamelibraryapp.data.db.LibraryEntity
|
||||||
|
import at.xaxa.gamelibraryapp.data.remote.GameDto
|
||||||
import at.xaxa.gamelibraryapp.data.remote.GameRemoteService
|
import at.xaxa.gamelibraryapp.data.remote.GameRemoteService
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlin.math.log
|
||||||
|
|
||||||
class GameRepository(private val libraryDao: LibraryDao, private val gameRemoteService: GameRemoteService) {
|
class GameRepository(private val libraryDao: LibraryDao, private val gameRemoteService: GameRemoteService) {
|
||||||
private val apiKey = "7a2a800a085d41e88621a8a59dc5ea82"
|
private val apiKey = "7a2a800a085d41e88621a8a59dc5ea82"
|
||||||
|
|
||||||
/*suspend fun loadInitialContacts() {
|
suspend fun searchGames(searchText: String): List<GameDto> {
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val contactDtoList = gameRemoteService.getAllContacts()
|
print(gameRemoteService.searchGames(apiKey, searchText).toString())
|
||||||
contactDtoList.map {
|
gameRemoteService.searchGames(apiKey, searchText).results
|
||||||
Game(0, it., "${it.telephoneNumber}", it.age)
|
|
||||||
}.forEach {
|
|
||||||
insertContact(it)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("Repository", "Something went wrong! ${e.message}", e)
|
Log.e("GameRepository", "Error searching games from API: ${e.message}", e)
|
||||||
|
emptyList<GameDto>()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
|
|
||||||
fun getAllGamesInLibrary(): Flow<List<Game>> {
|
fun getAllGamesInLibrary(): Flow<List<Game>> {
|
||||||
return libraryDao.getAllEntries().map {
|
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<List<Game>>{
|
||||||
|
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 {
|
suspend fun findGameById(id: Int): LibraryEntity {
|
||||||
val item = libraryDao.findEntryById(id)
|
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) {
|
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) {
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,9 +22,9 @@ interface LibraryDao {
|
|||||||
@Query("SELECT * FROM library WHERE _id = :id")
|
@Query("SELECT * FROM library WHERE _id = :id")
|
||||||
suspend fun findEntryById(id: Int): LibraryEntity
|
suspend fun findEntryById(id: Int): LibraryEntity
|
||||||
|
|
||||||
@Query("SELECT * FROM library")
|
@Query("SELECT * FROM library WHERE inLibrary = 1")
|
||||||
fun getAllEntries(): Flow<List<LibraryEntity>>
|
fun getAllEntries(): Flow<List<LibraryEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM library WHERE name = :entryName")
|
@Query("SELECT * FROM library WHERE name LIKE :entryName AND inLibrary = 1")
|
||||||
fun getEntriesWithName(entryName: String): Flow<List<LibraryEntity>>
|
fun getEntriesWithName(entryName: String): Flow<List<LibraryEntity>>
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import androidx.room.Room
|
|||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import at.xaxa.gamelibraryapp.LibraryApplication
|
import at.xaxa.gamelibraryapp.LibraryApplication
|
||||||
|
|
||||||
@Database(entities = [LibraryEntity::class], version = 2)
|
@Database(entities = [LibraryEntity::class], version = 3)
|
||||||
abstract class LibraryDatabase : RoomDatabase() {
|
abstract class LibraryDatabase : RoomDatabase() {
|
||||||
abstract fun libraryDao(): LibraryDao
|
abstract fun libraryDao(): LibraryDao
|
||||||
|
|
||||||
|
@ -9,5 +9,7 @@ data class LibraryEntity(
|
|||||||
val _id: Int,
|
val _id: Int,
|
||||||
val name: String,
|
val name: String,
|
||||||
val details: String,
|
val details: String,
|
||||||
val note: String
|
val note: String,
|
||||||
|
val imageUrl :String,
|
||||||
|
val inLibrary: Boolean
|
||||||
)
|
)
|
@ -9,14 +9,5 @@ import retrofit2.http.Query
|
|||||||
// TODO Implement Remote Service
|
// TODO Implement Remote Service
|
||||||
interface GameRemoteService {
|
interface GameRemoteService {
|
||||||
@GET("games")
|
@GET("games")
|
||||||
suspend fun getAllGames(@Query("key") apiKey: String): List<ApiResponse>
|
suspend fun searchGames(@Query("key") apiKey: String, @Query("search") searchText: String): ApiResponse
|
||||||
|
|
||||||
@GET("contacts/{contactId}")
|
|
||||||
suspend fun getContactById(@Path("contactId") contactId: Int)
|
|
||||||
|
|
||||||
@GET("contacts/search") // becomes contacts/search?filter=<content of filterText>
|
|
||||||
suspend fun findContacts(@Query("filter") filterText : String): List<ApiResponse>
|
|
||||||
|
|
||||||
@POST("contacts")
|
|
||||||
suspend fun addContact(@Body contactDto: ApiResponse)
|
|
||||||
}
|
}
|
@ -6,6 +6,8 @@ import androidx.lifecycle.viewmodel.initializer
|
|||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
import at.xaxa.gamelibraryapp.LibraryApplication
|
import at.xaxa.gamelibraryapp.LibraryApplication
|
||||||
import at.xaxa.gamelibraryapp.ui.Details.DetailsViewModel
|
import at.xaxa.gamelibraryapp.ui.Details.DetailsViewModel
|
||||||
|
import at.xaxa.gamelibraryapp.ui.GameList.GameListViewModel
|
||||||
|
import at.xaxa.gamelibraryapp.ui.Search.SearchListViewModel
|
||||||
|
|
||||||
object AppViewModelProvider {
|
object AppViewModelProvider {
|
||||||
val Factory = viewModelFactory {
|
val Factory = viewModelFactory {
|
||||||
@ -13,10 +15,14 @@ object AppViewModelProvider {
|
|||||||
DetailsViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as LibraryApplication).libraryRepository)
|
DetailsViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as LibraryApplication).libraryRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*initializer {
|
initializer {
|
||||||
ContactsViewModel((this[APPLICATION_KEY] as ContactsApplication).contactsRepository)
|
GameListViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as LibraryApplication).libraryRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initializer {
|
||||||
|
SearchListViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as LibraryApplication).libraryRepository)
|
||||||
|
}
|
||||||
|
/*
|
||||||
initializer {
|
initializer {
|
||||||
ContactDetailsViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as ContactsApplication).contactsRepository)
|
ContactDetailsViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as ContactsApplication).contactsRepository)
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,66 @@
|
|||||||
package at.xaxa.gamelibraryapp.ui.Details
|
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.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
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.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavBackStackEntry
|
|
||||||
import at.xaxa.gamelibraryapp.ui.AppViewModelProvider
|
import at.xaxa.gamelibraryapp.ui.AppViewModelProvider
|
||||||
import at.xaxa.gamelibraryapp.ui.DetailCard
|
import at.xaxa.gamelibraryapp.ui.DetailCard
|
||||||
|
import at.xaxa.gamelibraryapp.ui.LayoutMediaText
|
||||||
|
import at.xaxa.gamelibraryapp.ui.ShowPicture
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun DetailView(
|
fun DetailView(
|
||||||
navBackStackEntry: NavBackStackEntry,
|
|
||||||
viewModel: DetailsViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
viewModel: DetailsViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
// Extract arguments here
|
val libraryEntity = viewModel.editUiState.libraryEntity
|
||||||
val gameId = navBackStackEntry.arguments?.getInt("gameId") // Or `Int` if needed
|
|
||||||
|
|
||||||
|
val buttonText = if (libraryEntity.inLibrary) "Remove from library" else "Add to library"
|
||||||
|
|
||||||
|
Column {
|
||||||
DetailCard(modifier,
|
DetailCard(modifier,
|
||||||
title = "Game Title $gameId", // Use the extracted gameId
|
title = libraryEntity.name,
|
||||||
details = "details",
|
details = libraryEntity.details,
|
||||||
imageUrl = "https://media.rawg.io/media/games/b7f/b7ffc4c4776e61eca19d36d3c227f89a.jpg",
|
imageUrl = libraryEntity.imageUrl,
|
||||||
note = "note"
|
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") }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,13 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
data class GameEditUi(
|
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"])
|
private val gameId: Int = checkNotNull(savedStateHandle["gameId"])
|
||||||
|
|
||||||
@ -33,13 +36,12 @@ class DetailsViewModel(private val savedStateHandle: SavedStateHandle, private v
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateGame(libraryEntity: LibraryEntity) {
|
fun updateGame(libraryEntity: LibraryEntity) {
|
||||||
editUiState = editUiState.copy(libraryEntity=libraryEntity)
|
editUiState = editUiState.copy(libraryEntity = libraryEntity)
|
||||||
}
|
|
||||||
|
|
||||||
fun saveLibrary() {
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
gameRepository.updateEntry(editUiState.libraryEntity)
|
withContext(Dispatchers.IO) {
|
||||||
|
gameRepository.updateEntry(libraryEntity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -14,7 +14,7 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
data class GameDetailUi(
|
data class GameDetailUi(
|
||||||
val game: LibraryEntity = LibraryEntity(0, "", "", "")
|
val game: LibraryEntity = LibraryEntity(0, "", "", "", "", false)
|
||||||
)
|
)
|
||||||
|
|
||||||
class GameDetailsViewModel(savedStateHandle: SavedStateHandle, private val gameRepository: GameRepository): ViewModel() {
|
class GameDetailsViewModel(savedStateHandle: SavedStateHandle, private val gameRepository: GameRepository): ViewModel() {
|
||||||
|
@ -5,20 +5,36 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
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.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
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 at.xaxa.gamelibraryapp.ui.HorizontalCard
|
||||||
|
import kotlinx.coroutines.flow.count
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@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("") }
|
var searchText by remember { mutableStateOf("") }
|
||||||
|
viewModel.searchGamesInDB(searchText)
|
||||||
|
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
// Search Bar
|
// Search Bar
|
||||||
@ -28,19 +44,22 @@ fun GameList(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit) {
|
|||||||
label = { Text("Search games") },
|
label = { Text("Search games") },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.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
|
// Game List
|
||||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||||
items(16) { index ->
|
items(gameListState) { game ->
|
||||||
HorizontalCard(
|
HorizontalCard(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
title = "Game $index",
|
title = game.name,
|
||||||
details = "Details about game $index",
|
details = game.details,
|
||||||
imageUrl = "https://media.rawg.io/media/games/b7f/b7ffc4c4776e61eca19d36d3c227f89a.jpg",
|
imageUrl = game.imageUrl,
|
||||||
onClick = {
|
onClick = {
|
||||||
onCardClick(index)
|
onCardClick(game.id)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,47 @@
|
|||||||
package at.xaxa.gamelibraryapp.ui.GameList
|
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<List<Game>> = 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,6 +16,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.List
|
import androidx.compose.material.icons.automirrored.filled.List
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material3.FilledTonalButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.NavigationBarItem
|
import androidx.compose.material3.NavigationBarItem
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
@ -99,7 +100,6 @@ fun LibraryApp(modifier: Modifier = Modifier) {
|
|||||||
) {
|
) {
|
||||||
backStackEntry ->
|
backStackEntry ->
|
||||||
DetailView(
|
DetailView(
|
||||||
navBackStackEntry = backStackEntry,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -114,7 +114,9 @@ fun DetailCard(
|
|||||||
title: String,
|
title: String,
|
||||||
details: String,
|
details: String,
|
||||||
note: String,
|
note: String,
|
||||||
imageUrl: String) {
|
imageUrl: String,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
buttonText: String) {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@ -125,6 +127,13 @@ fun DetailCard(
|
|||||||
Column(modifier) {
|
Column(modifier) {
|
||||||
ShowPicture(modifier = Modifier.height(200.dp), imageUrl)
|
ShowPicture(modifier = Modifier.height(200.dp), imageUrl)
|
||||||
LayoutMediaText(modifier = Modifier, title, details)
|
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
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun DetailCardPreview() {
|
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")
|
||||||
}
|
}
|
@ -1,22 +1,70 @@
|
|||||||
package at.xaxa.gamelibraryapp.ui.Search
|
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.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.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.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 at.xaxa.gamelibraryapp.ui.HorizontalCard
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SearchList(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit){
|
fun SearchList(
|
||||||
LazyColumn{
|
modifier: Modifier = Modifier,
|
||||||
items(16) { index ->
|
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,
|
HorizontalCard(modifier,
|
||||||
"test $index",
|
game.name,
|
||||||
"details $index",
|
game.details,
|
||||||
"https://media.rawg.io/media/games/6e0/6e0c19bb111bd4fa20cf0eb72a049519.jpg",
|
game.imageUrl,//"https://media.rawg.io/media/games/6e0/6e0c19bb111bd4fa20cf0eb72a049519.jpg",
|
||||||
onClick = {
|
onClick = {
|
||||||
onCardClick(index)
|
viewModel.addGameToDB(game)
|
||||||
|
onCardClick(game.id)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,2 +1,80 @@
|
|||||||
package at.xaxa.gamelibraryapp.ui.Search
|
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<List<Game>> = 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user