Add basic UI element as POC.
This commit is contained in:
parent
1afd3bc02b
commit
6876ad7052
12 changed files with 606 additions and 9 deletions
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
android:allowBackup="true"
|
||||
|
|
|
|||
17
app/src/main/java/com/pixelized/lexique/model/Lexicon.kt
Normal file
17
app/src/main/java/com/pixelized/lexique/model/Lexicon.kt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package com.pixelized.lexique.model
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
data class Lexicon(
|
||||
val name: String?,
|
||||
val diminutive: String?,
|
||||
val gender: Gender = Gender.UNDETERMINED,
|
||||
val race: String?,
|
||||
val portrait: List<Uri>,
|
||||
val description: String?,
|
||||
val history: String?,
|
||||
) {
|
||||
enum class Gender {
|
||||
MALE, FEMALE, UNDETERMINED
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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<List<Lexicon>>(emptyList())
|
||||
val data: StateFlow<List<Lexicon>> get() = _data
|
||||
|
||||
init {
|
||||
scope.launch {
|
||||
_data.emit(sample())
|
||||
}
|
||||
}
|
||||
|
||||
private fun sample(): List<Lexicon> {
|
||||
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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Uri>,
|
||||
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 = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<List<LexiconItemUio>>,
|
||||
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 = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
class LexiconViewModel @Inject constructor(
|
||||
private val repository: LexiconRepository,
|
||||
) : ViewModel() {
|
||||
|
||||
// TODO : link it to a paginated DataSource
|
||||
private val _items = mutableStateOf<List<LexiconItemUio>>(emptyList())
|
||||
val items: State<List<LexiconItemUio>> 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
5
app/src/main/res/drawable/ic_arrow_back_ios_new_24.xml
Normal file
5
app/src/main/res/drawable/ic_arrow_back_ios_new_24.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#000000"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M17.77,3.77l-1.77,-1.77l-10,10l10,10l1.77,-1.77l-8.23,-8.23z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_empty.xml
Normal file
9
app/src/main/res/drawable/ic_empty.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M0.0,0.0z" />
|
||||
</vector>
|
||||
Loading…
Add table
Add a link
Reference in a new issue