From 6876ad7052cc66b4be03014a532a3d536d2ab924 Mon Sep 17 00:00:00 2001 From: "Andres Gomez, Thomas (ITDV CC) - AF (ext)" Date: Thu, 13 Jul 2023 22:55:59 +0200 Subject: [PATCH] Add basic UI element as POC. --- app/build.gradle.kts | 9 +- app/src/main/AndroidManifest.xml | 2 + .../com/pixelized/lexique/model/Lexicon.kt | 17 ++ .../pixelized/lexique/module/NetworkModule.kt | 24 ++ .../lexique/network/IGoogleSpreadSheet.kt | 19 ++ .../lexique/repository/LexiconRepository.kt | 80 ++++++ .../screens/detail/CharacterDetailScreen.kt | 266 +++++++++++++++++- .../lexique/ui/screens/lexicon/LexiconItem.kt | 88 ++++++ .../ui/screens/lexicon/LexiconScreen.kt | 60 +++- .../ui/screens/lexicon/LexiconViewModel.kt | 36 ++- .../res/drawable/ic_arrow_back_ios_new_24.xml | 5 + app/src/main/res/drawable/ic_empty.xml | 9 + 12 files changed, 606 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/pixelized/lexique/model/Lexicon.kt create mode 100644 app/src/main/java/com/pixelized/lexique/module/NetworkModule.kt create mode 100644 app/src/main/java/com/pixelized/lexique/network/IGoogleSpreadSheet.kt create mode 100644 app/src/main/java/com/pixelized/lexique/repository/LexiconRepository.kt create mode 100644 app/src/main/java/com/pixelized/lexique/ui/screens/lexicon/LexiconItem.kt create mode 100644 app/src/main/res/drawable/ic_arrow_back_ios_new_24.xml create mode 100644 app/src/main/res/drawable/ic_empty.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f7cefdc..3dcea9c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -58,7 +58,6 @@ android { dependencies { implementation("androidx.core:core-ktx:1.10.1") -// implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1") implementation("androidx.activity:activity-compose:1.7.2") // Compose @@ -66,9 +65,17 @@ dependencies { implementation("androidx.compose.ui:ui-graphics:1.4.3") implementation("androidx.compose.ui:ui-tooling-preview:1.4.3") implementation("androidx.compose.material3:material3:1.1.1") + debugImplementation("androidx.compose.ui:ui-tooling:1.4.3") // Hilt: Dependency injection implementation("androidx.hilt:hilt-navigation-compose:1.0.0") implementation("com.google.dagger:hilt-android:2.45") kapt("com.google.dagger:hilt-compiler:2.45") + + // Image + implementation("com.github.skydoves:landscapist-glide:2.1.11") + kapt("com.github.bumptech.glide:compiler:4.14.2") // this have to be align with landscapist-glide + + // Retrofit : Network + implementation("com.squareup.retrofit2:retrofit:2.9.0") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e8d9d04..5c1cd19 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + , + val description: String?, + val history: String?, +) { + enum class Gender { + MALE, FEMALE, UNDETERMINED + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/lexique/module/NetworkModule.kt b/app/src/main/java/com/pixelized/lexique/module/NetworkModule.kt new file mode 100644 index 0000000..c201a3e --- /dev/null +++ b/app/src/main/java/com/pixelized/lexique/module/NetworkModule.kt @@ -0,0 +1,24 @@ +package com.pixelized.lexique.module + +import com.pixelized.lexique.network.IGoogleSpreadSheet +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import retrofit2.Retrofit +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +class NetworkModule { + + @Provides + @Singleton + fun provideGoogleSpreadSheet(): IGoogleSpreadSheet { + val retrofit: Retrofit = Retrofit.Builder() + .baseUrl(IGoogleSpreadSheet.HOST) + .build() + + return retrofit.create(IGoogleSpreadSheet::class.java) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/lexique/network/IGoogleSpreadSheet.kt b/app/src/main/java/com/pixelized/lexique/network/IGoogleSpreadSheet.kt new file mode 100644 index 0000000..7829b56 --- /dev/null +++ b/app/src/main/java/com/pixelized/lexique/network/IGoogleSpreadSheet.kt @@ -0,0 +1,19 @@ +package com.pixelized.lexique.network + +import retrofit2.http.GET + +interface IGoogleSpreadSheet { + + @GET("spreadsheets/d/$ID/edit#gid=$LEXICON_GID") + fun getLexicon() + + @GET("spreadsheets/d/$ID/edit#gid=$META_DATA_GID") + fun getMetaData() + + companion object { + const val HOST = "https://docs.google.com/" + const val ID = "1oL9Nu5y37BPEbKxHre4TN9o8nrgy2JQoON4RRkdAHMs" + const val LEXICON_GID = 0 + const val META_DATA_GID = "957635233" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/lexique/repository/LexiconRepository.kt b/app/src/main/java/com/pixelized/lexique/repository/LexiconRepository.kt new file mode 100644 index 0000000..b30d847 --- /dev/null +++ b/app/src/main/java/com/pixelized/lexique/repository/LexiconRepository.kt @@ -0,0 +1,80 @@ +package com.pixelized.lexique.repository + +import android.net.Uri +import com.pixelized.lexique.model.Lexicon +import com.pixelized.lexique.network.IGoogleSpreadSheet +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class LexiconRepository @Inject constructor( + private val spreadSheet: IGoogleSpreadSheet, +) { + private val scope = CoroutineScope(Dispatchers.IO) + + private val _data = MutableStateFlow>(emptyList()) + val data: StateFlow> get() = _data + + init { + scope.launch { + _data.emit(sample()) + } + } + + private fun sample(): List { + return listOf( + Lexicon( + name = "Brulkhai", + diminutive = "Bru", + gender = Lexicon.Gender.FEMALE, + race = "Demi-Orc", + portrait = listOf( + Uri.parse("https://drive.google.com/file/d/1a31xJ6DQnzqmGBndG-uo65HNQHPEUJnI/view?usp=sharing"), + ), + description = "Brulkhai, ou plus simplement Bru, est solidement bâti. Elle mesure 192 cm pour 110 kg de muscles lorsqu’elle est en bonne santé. Elle a les cheveux châtains, les yeux noisettes et la peau couleur gris-vert typique de son espèce. D’un tempérament taciturne, elle parle peu et de façon concise. Elle est parfois brutale, aussi bien physiquement que verbalement, Elle ne prend cependant aucun plaisir à malmener ceux qu’elle considère plus faibles qu’elle. D’une nature simple et honnête, elle ne mâche pas ses mots et ne dissimule généralement pas ses pensées. Son intelligence modeste est plus le reflet d’un manque d’éducation et d’une capacité limitée à gérer ses émotions qu’à une débilité congénitale. Elle voue à la force un culte car c’est par son expression qu’elle se sent vraiment vivante et éprouve de grandes difficultés vis à vis de ceux qu’elle nomme foshnu (bébé, chouineur en commun).", + history = null, + ), + Lexicon( + name = "Léandre", + diminutive = null, + gender = Lexicon.Gender.MALE, + race = "Humain", + portrait = emptyList(), + description = null, + history = null, + ), + Lexicon( + name = "Nelia", + diminutive = null, + gender = Lexicon.Gender.FEMALE, + race = "Elfe", + portrait = emptyList(), + description = null, + history = null, + ), + Lexicon( + name = "Tigrane", + diminutive = null, + gender = Lexicon.Gender.MALE, + race = "Tieffelin", + portrait = emptyList(), + description = null, + history = null, + ), + Lexicon( + name = "Unathana", + diminutive = "Una", + gender = Lexicon.Gender.FEMALE, + race = "Demi-Elfe", + portrait = emptyList(), + description = null, + history = null, + ), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/lexique/ui/screens/detail/CharacterDetailScreen.kt b/app/src/main/java/com/pixelized/lexique/ui/screens/detail/CharacterDetailScreen.kt index dab9743..1cdfe6a 100644 --- a/app/src/main/java/com/pixelized/lexique/ui/screens/detail/CharacterDetailScreen.kt +++ b/app/src/main/java/com/pixelized/lexique/ui/screens/detail/CharacterDetailScreen.kt @@ -2,11 +2,69 @@ package com.pixelized.lexique.ui.screens.detail import android.content.res.Configuration.UI_MODE_NIGHT_NO import android.content.res.Configuration.UI_MODE_NIGHT_YES +import android.net.Uri +import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.ColorMatrix +import androidx.compose.ui.graphics.Shadow +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import com.pixelized.lexique.ui.screens.lexicon.LexiconScreen +import com.pixelized.lexique.R import com.pixelized.lexique.ui.theme.LexiconTheme +import com.skydoves.landscapist.ImageOptions +import com.skydoves.landscapist.glide.GlideImage + +@Stable +data class CharacterDetailUio( + val name: String?, + val diminutive: String?, + val gender: String?, + val race: String?, + val portrait: List, + val description: String?, + val history: String?, +) @Composable fun CharacterDetailScreen( @@ -15,9 +73,186 @@ fun CharacterDetailScreen( } +@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) @Composable -private fun CharacterDetailScreenContent() { +private fun CharacterDetailScreenContent( + modifier: Modifier = Modifier, + state: ScrollState = rememberScrollState(), + item: CharacterDetailUio, + onBack: () -> Unit, +) { + val colorScheme = MaterialTheme.colorScheme + val typography = MaterialTheme.typography + Scaffold( + modifier = modifier, + containerColor = Color.Transparent, + topBar = { + TopAppBar( + navigationIcon = { + IconButton(onClick = onBack) { + Icon( + painter = painterResource(id = R.drawable.ic_arrow_back_ios_new_24), + contentDescription = null + ) + } + }, + title = { }, + ) + }, + ) { paddingValues -> + Box( + modifier = Modifier.padding(paddingValues = paddingValues), + ) { + item.portrait.firstOrNull()?.let { uri -> + Box( + modifier = Modifier + .fillMaxWidth() + .aspectRatio(ratio = 1f) + .scrollOffset(scrollState = state) { -it / 2 }, + ) { + GlideImage( + modifier = Modifier.matchParentSize(), + imageModel = { uri.toString() }, + imageOptions = ImageOptions( + alignment = Alignment.TopCenter, + contentScale = ContentScale.Crop, + colorFilter = remember { + ColorFilter.colorMatrix( + ColorMatrix().also { it.setToSaturation(0f) } + ) + }, + ), + previewPlaceholder = R.drawable.ic_empty, + ) + Box( + modifier = Modifier + .matchParentSize() + .background(brush = rememberBackgroundGradient()) + ) + } + } + Column( + modifier = Modifier + .verticalScroll(state) + .padding(top = 64.dp, bottom = 16.dp), + verticalArrangement = Arrangement.spacedBy(space = 4.dp), + ) { + FlowRow( + modifier = Modifier.padding(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + item.name?.let { + Text( + modifier = Modifier.alignByBaseline(), + style = typography.headlineSmall, + text = it, + ) + } + item.diminutive?.let { + Text( + modifier = Modifier.alignByBaseline(), + style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) }, + text = it, + ) + } + } + Row( + modifier = Modifier.padding(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + item.race?.let { + Text( + style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) }, + text = it, + ) + } + item.gender?.let { + Text( + style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) }, + text = it, + ) + } + } + item.description?.let { + Text( + modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp), + style = typography.titleMedium, + text = "Description", + ) + Text( + modifier = Modifier.padding(horizontal = 16.dp), + style = remember { + typography.bodyMedium.copy( + shadow = Shadow( + color = colorScheme.surface.copy(alpha = 0.5f), + offset = Offset(x = 1f, y = 1f), + ) + ) + }, + text = it, + ) + } + item.history?.let { + Text( + modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp), + style = typography.titleMedium, + text = "Histoire", + ) + Text( + modifier = Modifier.padding(horizontal = 16.dp), + style = typography.bodyMedium, + text = it, + ) + } + if (item.portrait.isNotEmpty()) { + Text( + modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp), + style = typography.titleMedium, + text = "Portrait", + ) + LazyRow( + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + items(items = item.portrait) { + GlideImage( + modifier = Modifier.height(320.dp), + imageModel = { it }, + imageOptions = ImageOptions( + contentScale = ContentScale.FillHeight + ), + previewPlaceholder = R.drawable.ic_empty, + ) + } + } + } + } + } + + } +} + +@Composable +private fun rememberBackgroundGradient(): Brush { + val colorScheme = MaterialTheme.colorScheme + return remember { + Brush.verticalGradient( + colors = listOf( + colorScheme.surface.copy(alpha = 0.5f), + colorScheme.surface.copy(alpha = 1.0f), + ) + ) + } +} + +@Stable +private fun Modifier.scrollOffset( + scrollState: ScrollState, + block: (Dp) -> Dp +): Modifier = composed { + val density = LocalDensity.current + this.offset(y = with(density) { block(scrollState.value.toDp()) }) } @Composable @@ -25,6 +260,31 @@ private fun CharacterDetailScreenContent() { @Preview(uiMode = UI_MODE_NIGHT_YES) private fun CharacterDetailScreenContentPreview() { LexiconTheme { - CharacterDetailScreenContent() + Surface { + CharacterDetailScreenContent( + modifier = Modifier.fillMaxSize(), + item = CharacterDetailUio( + name = "Brulkhai", + diminutive = "./ Bru", + gender = "female", + race = "Demi-Orc", + portrait = listOf( + Uri.parse("https://cdnb.artstation.com/p/assets/images/images/003/024/889/large/bayard-wu-0716.jpg?1468642855"), + Uri.parse("https://cdnb.artstation.com/p/assets/images/images/003/024/877/large/bayard-wu-0714.jpg?1468642665"), + Uri.parse("https://cdnb.artstation.com/p/assets/images/images/003/024/887/large/bayard-wu-0715.jpg?1468642839"), + Uri.parse("https://cdnb.artstation.com/p/assets/images/images/003/024/891/large/bayard-wu-0623-03.jpg?1468642872"), + Uri.parse("https://cdna.artstation.com/p/assets/images/images/002/869/868/large/bayard-wu-0622-03.jpg?1466664135"), + Uri.parse("https://cdnb.artstation.com/p/assets/images/images/002/869/871/large/bayard-wu-0622-04.jpg?1466664153"), + Uri.parse("https://cdnb.artstation.com/p/assets/images/images/004/347/181/large/bayard-wu-1217.jpg?1482770883"), + Uri.parse("https://cdnb.artstation.com/p/assets/images/images/004/297/635/large/bayard-wu-1215.jpg?1482166826"), + Uri.parse("https://cdnb.artstation.com/p/assets/images/images/004/297/631/large/bayard-wu-1209.jpg?1482166803"), + Uri.parse("https://cdnb.artstation.com/p/assets/images/images/004/297/641/large/bayard-wu-1212.jpg?1482166838"), + ), + description = "Brulkhai, ou plus simplement Bru, est solidement bâti. Elle mesure 192 cm pour 110 kg de muscles lorsqu’elle est en bonne santé. Elle a les cheveux châtains, les yeux noisettes et la peau couleur gris-vert typique de son espèce. D’un tempérament taciturne, elle parle peu et de façon concise. Elle est parfois brutale, aussi bien physiquement que verbalement, Elle ne prend cependant aucun plaisir à malmener ceux qu’elle considère plus faibles qu’elle. D’une nature simple et honnête, elle ne mâche pas ses mots et ne dissimule généralement pas ses pensées. Son intelligence modeste est plus le reflet d’un manque d’éducation et d’une capacité limitée à gérer ses émotions qu’à une débilité congénitale. Elle voue à la force un culte car c’est par son expression qu’elle se sent vraiment vivante et éprouve de grandes difficultés vis à vis de ceux qu’elle nomme foshnu (bébé, chouineur en commun).", + history = null, + ), + onBack = { }, + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/lexique/ui/screens/lexicon/LexiconItem.kt b/app/src/main/java/com/pixelized/lexique/ui/screens/lexicon/LexiconItem.kt new file mode 100644 index 0000000..f2f5d46 --- /dev/null +++ b/app/src/main/java/com/pixelized/lexique/ui/screens/lexicon/LexiconItem.kt @@ -0,0 +1,88 @@ +package com.pixelized.lexique.ui.screens.lexicon + +import android.content.res.Configuration.UI_MODE_NIGHT_NO +import android.content.res.Configuration.UI_MODE_NIGHT_YES +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.pixelized.lexique.ui.theme.LexiconTheme + +@Stable +data class LexiconItemUio( + val name: String, + val diminutive: String?, + val gender: String?, + val race: String?, +) + +@Composable +fun LexiconItem( + modifier: Modifier = Modifier, + item: LexiconItemUio, +) { + val typography = MaterialTheme.typography + + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + modifier = Modifier.alignByBaseline(), + style = remember { typography.bodyLarge.copy(fontWeight = FontWeight.Bold) }, + text = item.name, + ) + Text( + modifier = Modifier.alignByBaseline(), + style = typography.labelMedium, + text = item.diminutive ?: "" + ) + } + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) }, + text = item.gender ?: "" + ) + Text( + style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) }, + text = item.race ?: "" + ) + } + } +} + + +@Composable +@Preview(uiMode = UI_MODE_NIGHT_NO) +@Preview(uiMode = UI_MODE_NIGHT_YES) +private fun LexiconItemContentPreview() { + LexiconTheme { + Surface { + LexiconItem( + modifier = Modifier.fillMaxWidth(), + item = LexiconItemUio( + name = "Brulkhai", + diminutive = "Bru", + gender = "f.", + race = "Demi-Orc", + ) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/lexique/ui/screens/lexicon/LexiconScreen.kt b/app/src/main/java/com/pixelized/lexique/ui/screens/lexicon/LexiconScreen.kt index ae42f08..57be3c0 100644 --- a/app/src/main/java/com/pixelized/lexique/ui/screens/lexicon/LexiconScreen.kt +++ b/app/src/main/java/com/pixelized/lexique/ui/screens/lexicon/LexiconScreen.kt @@ -2,8 +2,21 @@ package com.pixelized.lexique.ui.screens.lexicon import android.content.res.Configuration.UI_MODE_NIGHT_NO import android.content.res.Configuration.UI_MODE_NIGHT_YES +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.PaddingValues +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.items +import androidx.compose.material3.Surface import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.pixelized.lexique.ui.theme.LexiconTheme @@ -11,12 +24,34 @@ import com.pixelized.lexique.ui.theme.LexiconTheme fun LexiconScreen( viewModel: LexiconViewModel = hiltViewModel(), ) { - + Surface { + LexiconScreenContent( + items = viewModel.items, + onItem = { }, + ) + } } @Composable -private fun LexiconScreenContent() { - +private fun LexiconScreenContent( + modifier: Modifier = Modifier, + items: State>, + onItem: (LexiconItemUio) -> Unit, +) { + LazyColumn( + modifier = modifier, + contentPadding = PaddingValues(vertical = 8.dp), + ) { + items(items = items.value) { + LexiconItem( + modifier = Modifier + .clickable { onItem(it) } + .fillMaxWidth() + .padding(vertical = 8.dp, horizontal = 16.dp), + item = it, + ) + } + } } @Composable @@ -24,6 +59,23 @@ private fun LexiconScreenContent() { @Preview(uiMode = UI_MODE_NIGHT_YES) private fun LexiconScreenContentPreview() { LexiconTheme { - LexiconScreenContent() + Surface { + LexiconScreenContent( + modifier = Modifier.fillMaxSize(), + items = remember { + mutableStateOf( + listOf( + LexiconItemUio( + name = "Brulkhai", + diminutive = "Bru", + gender = "f.", + race = "Demi-Orc", + ) + ) + ) + }, + onItem = { }, + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/lexique/ui/screens/lexicon/LexiconViewModel.kt b/app/src/main/java/com/pixelized/lexique/ui/screens/lexicon/LexiconViewModel.kt index 901585a..c8eaca0 100644 --- a/app/src/main/java/com/pixelized/lexique/ui/screens/lexicon/LexiconViewModel.kt +++ b/app/src/main/java/com/pixelized/lexique/ui/screens/lexicon/LexiconViewModel.kt @@ -1,8 +1,42 @@ package com.pixelized.lexique.ui.screens.lexicon +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.pixelized.lexique.model.Lexicon +import com.pixelized.lexique.repository.LexiconRepository import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class LexiconViewModel @Inject constructor() : ViewModel() \ No newline at end of file +class LexiconViewModel @Inject constructor( + private val repository: LexiconRepository, +) : ViewModel() { + + // TODO : link it to a paginated DataSource + private val _items = mutableStateOf>(emptyList()) + val items: State> get() = _items + + init { + viewModelScope.launch { + repository.data.collect { items -> + _items.value = items.mapNotNull { item -> + item.name?.let { + LexiconItemUio( + name = item.name, + diminutive = item.diminutive?.let { "./ $it" }, + gender = when (item.gender) { + Lexicon.Gender.MALE -> "m." + Lexicon.Gender.FEMALE -> "f." + Lexicon.Gender.UNDETERMINED -> "u." + }, + race = item.race, + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_arrow_back_ios_new_24.xml b/app/src/main/res/drawable/ic_arrow_back_ios_new_24.xml new file mode 100644 index 0000000..6bd5650 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_back_ios_new_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_empty.xml b/app/src/main/res/drawable/ic_empty.xml new file mode 100644 index 0000000..a4c243f --- /dev/null +++ b/app/src/main/res/drawable/ic_empty.xml @@ -0,0 +1,9 @@ + + +