Change the parsing mechanist to allow more flexibility.
This commit is contained in:
parent
f5c10c5154
commit
fb31de8130
19 changed files with 577 additions and 140 deletions
|
|
@ -24,8 +24,8 @@ android {
|
|||
applicationId = "com.pixelized.rplexicon"
|
||||
minSdk = 26
|
||||
targetSdk = 33
|
||||
versionCode = 1
|
||||
versionName = "0.1.0"
|
||||
versionCode = 2
|
||||
versionName = "0.1.1"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 1,
|
||||
"versionName": "0.1.0",
|
||||
"versionCode": 2,
|
||||
"versionName": "0.1.1",
|
||||
"outputFile": "app-release.apk"
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -6,13 +6,32 @@ data class Lexicon(
|
|||
val id: Int,
|
||||
val name: String,
|
||||
val diminutive: String?,
|
||||
val gender: Gender = Gender.UNDETERMINED,
|
||||
val race: String?,
|
||||
val gender: Gender,
|
||||
val race: Race,
|
||||
val portrait: List<Uri>,
|
||||
val description: String?,
|
||||
val history: String?,
|
||||
) {
|
||||
enum class Gender {
|
||||
MALE, FEMALE, UNDETERMINED
|
||||
MALE,
|
||||
FEMALE,
|
||||
UNDETERMINED
|
||||
}
|
||||
|
||||
enum class Race {
|
||||
ELF,
|
||||
HALFLING,
|
||||
HUMAN,
|
||||
DWARF,
|
||||
HALF_ELF,
|
||||
HALF_ORC,
|
||||
DRAGONBORN,
|
||||
GNOME,
|
||||
TIEFLING,
|
||||
AARAKOCRA,
|
||||
GENASI,
|
||||
DEEP_GNOME,
|
||||
GOLIATH,
|
||||
UNDETERMINED
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ class AuthenticationRepository @Inject constructor(
|
|||
.setBackOff(ExponentialBackOff())
|
||||
|
||||
credential.selectedAccount = signInCredential.value?.let {
|
||||
Account(it.id, "google")
|
||||
Account(it.id, ACCOUNT_TYPE)
|
||||
}
|
||||
|
||||
return credential
|
||||
|
|
@ -40,8 +40,8 @@ class AuthenticationRepository @Inject constructor(
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val ACCOUNT_TYPE = "google"
|
||||
private val capabilities = listOf(
|
||||
// SheetsScopes.SPREADSHEETS,
|
||||
SheetsScopes.SPREADSHEETS_READONLY,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ class LexiconRepository @Inject constructor(
|
|||
GsonFactory(),
|
||||
authenticationRepository.credential,
|
||||
)
|
||||
.setApplicationName("RP-Lexique")
|
||||
.build()
|
||||
|
||||
else -> null
|
||||
|
|
@ -45,7 +44,7 @@ class LexiconRepository @Inject constructor(
|
|||
throw ServiceNotReady()
|
||||
} else {
|
||||
withContext(Dispatchers.IO) {
|
||||
val request = service.spreadsheets().values().get(ID, LEXIQUE)
|
||||
val request = service.spreadsheets().values().get(Sheet.ID, Sheet.LEXIQUE)
|
||||
val data = request.execute()
|
||||
updateData(data = data)
|
||||
}
|
||||
|
|
@ -55,53 +54,98 @@ class LexiconRepository @Inject constructor(
|
|||
@Throws(IncompatibleSheetStructure::class)
|
||||
private fun updateData(data: ValueRange?) {
|
||||
val sheet = data?.values?.sheet()
|
||||
var sheetStructure: Map<String, Int>? = null
|
||||
val lexicon: List<Lexicon> = sheet?.mapIndexedNotNull { index, row ->
|
||||
if (index == 0) {
|
||||
checkSheetStructure(firstRow = row)
|
||||
null
|
||||
} else {
|
||||
parseCharacterRow(index = index - 1, row = row as? List<Any>?)
|
||||
when {
|
||||
index == 0 -> {
|
||||
sheetStructure = checkSheetStructure(firstRow = row)
|
||||
null
|
||||
}
|
||||
|
||||
row is List<*> -> parseCharacterRow(
|
||||
sheetStructure = sheetStructure,
|
||||
index = index - 1,
|
||||
row = row,
|
||||
)
|
||||
|
||||
else -> null
|
||||
}
|
||||
} ?: emptyList()
|
||||
_data.tryEmit(lexicon)
|
||||
}
|
||||
|
||||
@Throws(IncompatibleSheetStructure::class)
|
||||
private fun checkSheetStructure(firstRow: Any?) {
|
||||
when {
|
||||
firstRow !is ArrayList<*> -> throw IncompatibleSheetStructure("First row is not a List: $firstRow")
|
||||
firstRow.size < 7 -> throw IncompatibleSheetStructure("First row have not enough column: ${firstRow.size}, $firstRow")
|
||||
firstRow.size > 7 -> throw IncompatibleSheetStructure("First row have too mush columns: ${firstRow.size}, $firstRow")
|
||||
else -> {
|
||||
for (index in 0..6) {
|
||||
if (columns[index] != firstRow[index]) {
|
||||
throw IncompatibleSheetStructure("Column at index:$index should be ${columns[index]} but was ${firstRow[index]}")
|
||||
}
|
||||
}
|
||||
private fun checkSheetStructure(firstRow: Any?): HashMap<String, Int> {
|
||||
// check if the row is a list
|
||||
if (firstRow !is ArrayList<*>) {
|
||||
throw IncompatibleSheetStructure("First row is not a List: $firstRow")
|
||||
}
|
||||
// parse the first line to find element that we recognize.
|
||||
val sheetStructure = hashMapOf<String, Int>()
|
||||
firstRow.forEachIndexed { index, cell ->
|
||||
when (cell as? String) {
|
||||
Sheet.NAME,
|
||||
Sheet.DIMINUTIVE,
|
||||
Sheet.GENDER,
|
||||
Sheet.RACE,
|
||||
Sheet.PORTRAIT,
|
||||
Sheet.DESCRIPTION,
|
||||
Sheet.HISTORY -> sheetStructure[cell] = index
|
||||
}
|
||||
}
|
||||
// check if we found everything we need.
|
||||
when {
|
||||
sheetStructure.size < Sheet.KNOWN_COLUMN -> throw IncompatibleSheetStructure(
|
||||
message = "Sheet header row does not have enough column: ${firstRow.size}.\nstructure: $firstRow\nheader: $sheetStructure"
|
||||
)
|
||||
|
||||
sheetStructure.size > Sheet.KNOWN_COLUMN -> throw IncompatibleSheetStructure(
|
||||
message = "Sheet header row does have too mush columns: ${firstRow.size}.\nstructure: $firstRow\nheader: $sheetStructure"
|
||||
)
|
||||
}
|
||||
|
||||
return sheetStructure
|
||||
}
|
||||
|
||||
private fun parseCharacterRow(index: Int, row: List<Any>?): Lexicon? {
|
||||
val name = row?.getOrNull(0) as? String
|
||||
val diminutive = row?.getOrNull(1) as? String?
|
||||
val gender = row?.getOrNull(2) as? String?
|
||||
val race = row?.getOrNull(3) as? String?
|
||||
val portrait = row?.getOrNull(4) as? String?
|
||||
val description = row?.getOrNull(5) as? String?
|
||||
val history = row?.getOrNull(6) as? String?
|
||||
private fun parseCharacterRow(
|
||||
sheetStructure: Map<String, Int>?,
|
||||
index: Int,
|
||||
row: List<*>?,
|
||||
): Lexicon? {
|
||||
val name = row?.getOrNull(sheetStructure.name) as? String
|
||||
val diminutive = row?.getOrNull(sheetStructure.diminutive) as? String?
|
||||
val gender = row?.getOrNull(sheetStructure.gender) as? String?
|
||||
val race = row?.getOrNull(sheetStructure.race) as? String?
|
||||
val portrait = row?.getOrNull(sheetStructure.portrait) as? String?
|
||||
val description = row?.getOrNull(sheetStructure.description) as? String?
|
||||
val history = row?.getOrNull(sheetStructure.history) as? String?
|
||||
|
||||
return if (name != null) {
|
||||
Lexicon(
|
||||
id = index,
|
||||
name = name,
|
||||
diminutive = diminutive?.takeIf { it.isNotBlank() },
|
||||
gender = when (gender) {
|
||||
"Male" -> Lexicon.Gender.MALE
|
||||
"Femelle" -> Lexicon.Gender.FEMALE
|
||||
gender = when (gender?.takeIf { it.isNotBlank() }) {
|
||||
Gender.MALE -> Lexicon.Gender.MALE
|
||||
Gender.FEMALE -> Lexicon.Gender.FEMALE
|
||||
else -> Lexicon.Gender.UNDETERMINED
|
||||
},
|
||||
race = race?.takeIf { it.isNotBlank() },
|
||||
race = when (race?.takeIf { it.isNotBlank() }) {
|
||||
Race.ELF -> Lexicon.Race.ELF
|
||||
Race.HALFLING -> Lexicon.Race.HALFLING
|
||||
Race.HUMAN -> Lexicon.Race.HUMAN
|
||||
Race.DWARF -> Lexicon.Race.DWARF
|
||||
Race.HALF_ELF -> Lexicon.Race.HALF_ELF
|
||||
Race.HALF_ORC -> Lexicon.Race.HALF_ORC
|
||||
Race.DRAGONBORN -> Lexicon.Race.DRAGONBORN
|
||||
Race.GNOME -> Lexicon.Race.GNOME
|
||||
Race.TIEFLING -> Lexicon.Race.TIEFLING
|
||||
Race.AARAKOCRA -> Lexicon.Race.AARAKOCRA
|
||||
Race.GENASI -> Lexicon.Race.GENASI
|
||||
Race.DEEP_GNOME -> Lexicon.Race.DEEP_GNOME
|
||||
Race.GOLIATH -> Lexicon.Race.GOLIATH
|
||||
else -> Lexicon.Race.UNDETERMINED
|
||||
},
|
||||
portrait = portrait?.split("\n")?.mapNotNull { it.toUriOrNull() } ?: emptyList(),
|
||||
description = description?.takeIf { it.isNotBlank() },
|
||||
history = history?.takeIf { it.isNotBlank() },
|
||||
|
|
@ -124,16 +168,53 @@ class LexiconRepository @Inject constructor(
|
|||
null
|
||||
}
|
||||
|
||||
private val Map<String, Int>?.name: Int get() = this?.getValue(Sheet.NAME) ?: 0
|
||||
private val Map<String, Int>?.diminutive: Int get() = this?.getValue(Sheet.DIMINUTIVE) ?: 1
|
||||
private val Map<String, Int>?.gender: Int get() = this?.getValue(Sheet.GENDER) ?: 2
|
||||
private val Map<String, Int>?.race: Int get() = this?.getValue(Sheet.RACE) ?: 3
|
||||
private val Map<String, Int>?.portrait: Int get() = this?.getValue(Sheet.PORTRAIT) ?: 4
|
||||
private val Map<String, Int>?.description: Int get() = this?.getValue(Sheet.DESCRIPTION) ?: 5
|
||||
private val Map<String, Int>?.history: Int get() = this?.getValue(Sheet.HISTORY) ?: 6
|
||||
|
||||
class ServiceNotReady : Exception()
|
||||
|
||||
class IncompatibleSheetStructure(message: String?) : Exception(message)
|
||||
|
||||
companion object {
|
||||
const val TAG = "LexiconRepository"
|
||||
}
|
||||
|
||||
private object Sheet {
|
||||
const val ID = "1oL9Nu5y37BPEbKxHre4TN9o8nrgy2JQoON4RRkdAHMs"
|
||||
const val LEXIQUE = "Lexique"
|
||||
const val KNOWN_COLUMN = 7
|
||||
const val NAME = "Nom"
|
||||
const val DIMINUTIVE = "Diminutif"
|
||||
const val GENDER = "Sexe"
|
||||
const val RACE = "Race"
|
||||
const val PORTRAIT = "Portrait"
|
||||
const val DESCRIPTION = "Description"
|
||||
const val HISTORY = "Histoire"
|
||||
}
|
||||
|
||||
val columns =
|
||||
listOf("Nom", "Diminutif", "Sexe", "Race", "Portrait", "Description", "Histoire")
|
||||
private object Gender {
|
||||
const val MALE = "Male"
|
||||
const val FEMALE = "Femelle"
|
||||
}
|
||||
|
||||
private object Race {
|
||||
const val ELF = "Elfe"
|
||||
const val HALFLING = "Halfelin"
|
||||
const val HUMAN = "Humain"
|
||||
const val DWARF = "Nain"
|
||||
const val HALF_ELF = "Demi-Elfe"
|
||||
const val HALF_ORC = "Demi-Orc"
|
||||
const val DRAGONBORN = "Drakéide"
|
||||
const val GNOME = "Gnome"
|
||||
const val TIEFLING = "Tieffelin"
|
||||
const val AARAKOCRA = "Aarakocra"
|
||||
const val GENASI = "Génasi"
|
||||
const val DEEP_GNOME = "Gnome des Profondeurs"
|
||||
const val GOLIATH = "Goliath"
|
||||
}
|
||||
}
|
||||
|
|
@ -2,11 +2,13 @@ package com.pixelized.rplexicon.ui.screens.authentication
|
|||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.shape.CircleShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
|
|
@ -22,6 +24,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -81,23 +84,28 @@ private fun AuthenticationScreenContent(
|
|||
version: VersionViewModel.Version,
|
||||
onGoogleSignIn: () -> Unit,
|
||||
) {
|
||||
val typography = MaterialTheme.typography
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp, Alignment.Bottom),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.Bottom),
|
||||
horizontalAlignment = Alignment.End,
|
||||
) {
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.padding(all = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
colors = ButtonDefaults.buttonColors(),
|
||||
.fillMaxWidth()
|
||||
.border(
|
||||
width = 2.dp,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
shape = CircleShape,
|
||||
),
|
||||
colors = ButtonDefaults.outlinedButtonColors(),
|
||||
onClick = onGoogleSignIn,
|
||||
) {
|
||||
Text(text = rememeberGoogleStringResource())
|
||||
}
|
||||
|
||||
Text(
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
style = remember { typography.labelSmall.copy(fontStyle = FontStyle.Italic) },
|
||||
text = version.toText(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.pixelized.rplexicon.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.annotation.StringRes
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
|
|
@ -48,6 +49,7 @@ 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.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
|
|
@ -63,8 +65,8 @@ import com.skydoves.landscapist.glide.GlideImage
|
|||
data class CharacterDetailUio(
|
||||
val name: String?,
|
||||
val diminutive: String?,
|
||||
val gender: String?,
|
||||
val race: String?,
|
||||
@StringRes val gender: Int,
|
||||
@StringRes val race: Int,
|
||||
val portrait: List<Uri>,
|
||||
val description: String?,
|
||||
val history: String?,
|
||||
|
|
@ -111,7 +113,9 @@ private fun CharacterDetailScreenContent(
|
|||
)
|
||||
}
|
||||
},
|
||||
title = { },
|
||||
title = {
|
||||
Text(text = stringResource(id = R.string.detail_title))
|
||||
},
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
|
|
@ -175,24 +179,20 @@ private fun CharacterDetailScreenContent(
|
|||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
item.value.gender?.let {
|
||||
Text(
|
||||
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||
text = it,
|
||||
)
|
||||
}
|
||||
item.value.race?.let {
|
||||
Text(
|
||||
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||
text = it,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||
text = stringResource(id = item.value.gender),
|
||||
)
|
||||
Text(
|
||||
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||
text = stringResource(id = item.value.race),
|
||||
)
|
||||
}
|
||||
item.value.description?.let {
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp),
|
||||
style = typography.titleMedium,
|
||||
text = "Description",
|
||||
text = stringResource(id = R.string.detail_description),
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
|
|
@ -211,7 +211,7 @@ private fun CharacterDetailScreenContent(
|
|||
Text(
|
||||
modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp),
|
||||
style = typography.titleMedium,
|
||||
text = "Histoire",
|
||||
text = stringResource(id = R.string.detail_history),
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
|
|
@ -223,7 +223,7 @@ private fun CharacterDetailScreenContent(
|
|||
Text(
|
||||
modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp),
|
||||
style = typography.titleMedium,
|
||||
text = "Portrait",
|
||||
text = stringResource(id = R.string.detail_portrait),
|
||||
)
|
||||
LazyRow(
|
||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||
|
|
@ -281,8 +281,8 @@ private fun CharacterDetailScreenContentPreview() {
|
|||
CharacterDetailUio(
|
||||
name = "Brulkhai",
|
||||
diminutive = "./ Bru",
|
||||
gender = "female",
|
||||
race = "Demi-Orc",
|
||||
gender = R.string.gender_female,
|
||||
race = R.string.race_half_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"),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import androidx.compose.runtime.State
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.model.Lexicon
|
||||
import com.pixelized.rplexicon.repository.LexiconRepository
|
||||
import com.pixelized.rplexicon.ui.navigation.screens.characterDetailArgument
|
||||
|
|
@ -23,11 +24,26 @@ class CharacterDetailViewModel @Inject constructor(
|
|||
name = source.name,
|
||||
diminutive = source.diminutive?.let { "./ $it" },
|
||||
gender = when (source.gender) {
|
||||
Lexicon.Gender.MALE -> "Male"
|
||||
Lexicon.Gender.FEMALE -> "Femelle"
|
||||
Lexicon.Gender.UNDETERMINED -> "Inconnu"
|
||||
Lexicon.Gender.MALE -> R.string.gender_male
|
||||
Lexicon.Gender.FEMALE -> R.string.gender_female
|
||||
Lexicon.Gender.UNDETERMINED -> R.string.gender_undetermined
|
||||
},
|
||||
race = when (source.race) {
|
||||
Lexicon.Race.ELF -> R.string.race_elf
|
||||
Lexicon.Race.HALFLING -> R.string.race_halfling
|
||||
Lexicon.Race.HUMAN -> R.string.race_human
|
||||
Lexicon.Race.DWARF -> R.string.race_dwarf
|
||||
Lexicon.Race.HALF_ELF -> R.string.race_half_elf
|
||||
Lexicon.Race.HALF_ORC -> R.string.race_half_orc
|
||||
Lexicon.Race.DRAGONBORN -> R.string.race_dragonborn
|
||||
Lexicon.Race.GNOME -> R.string.race_gnome
|
||||
Lexicon.Race.TIEFLING -> R.string.race_tiefling
|
||||
Lexicon.Race.AARAKOCRA -> R.string.race_aarakocra
|
||||
Lexicon.Race.GENASI -> R.string.race_genasi
|
||||
Lexicon.Race.DEEP_GNOME -> R.string.race_deep_gnome
|
||||
Lexicon.Race.GOLIATH -> R.string.race_goliath
|
||||
Lexicon.Race.UNDETERMINED -> R.string.race_undetermined
|
||||
},
|
||||
race = source.race,
|
||||
portrait = source.portrait,
|
||||
description = source.description,
|
||||
history = source.history,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,14 @@ package com.pixelized.rplexicon.ui.screens.lexicon
|
|||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -13,21 +17,37 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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.rplexicon.R
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
import com.pixelized.rplexicon.utilitary.extentions.placeholder
|
||||
|
||||
@Stable
|
||||
data class LexiconItemUio(
|
||||
val id: Int,
|
||||
val name: String,
|
||||
val diminutive: String?,
|
||||
val gender: String?,
|
||||
val race: String?,
|
||||
)
|
||||
@StringRes val gender: Int,
|
||||
@StringRes val race: Int,
|
||||
val placeholder: Boolean = false,
|
||||
) {
|
||||
companion object {
|
||||
val Brulkhai = LexiconItemUio(
|
||||
id = 0,
|
||||
name = "Brulkhai",
|
||||
diminutive = "Bru",
|
||||
gender = R.string.gender_female_short,
|
||||
race = R.string.race_half_orc,
|
||||
placeholder = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun LexiconItem(
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -39,17 +59,23 @@ fun LexiconItem(
|
|||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Row(
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
modifier = Modifier
|
||||
.alignByBaseline()
|
||||
.placeholder { item.placeholder },
|
||||
style = remember { typography.bodyLarge.copy(fontWeight = FontWeight.Bold) },
|
||||
maxLines = 1,
|
||||
text = item.name,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.alignByBaseline(),
|
||||
modifier = Modifier
|
||||
.alignByBaseline()
|
||||
.placeholder { item.placeholder },
|
||||
style = typography.labelMedium,
|
||||
maxLines = 1,
|
||||
text = item.diminutive ?: ""
|
||||
)
|
||||
}
|
||||
|
|
@ -57,12 +83,20 @@ fun LexiconItem(
|
|||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.alignByBaseline()
|
||||
.placeholder { item.placeholder },
|
||||
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||
text = item.gender ?: ""
|
||||
maxLines = 1,
|
||||
text = stringResource(id = item.gender)
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.alignByBaseline()
|
||||
.placeholder { item.placeholder },
|
||||
style = remember { typography.labelMedium.copy(fontStyle = FontStyle.Italic) },
|
||||
text = item.race ?: ""
|
||||
maxLines = 1,
|
||||
text = stringResource(id = item.race)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -76,13 +110,15 @@ private fun LexiconItemContentPreview() {
|
|||
LexiconTheme {
|
||||
Surface {
|
||||
LexiconItem(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
item = LexiconItemUio(
|
||||
id = 0,
|
||||
name = "Brulkhai",
|
||||
diminutive = "Bru",
|
||||
gender = "f.",
|
||||
race = "Demi-Orc",
|
||||
gender = R.string.gender_female_short,
|
||||
race = R.string.race_half_orc,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
|||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
|
|
@ -16,14 +18,14 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.LinearProgressIndicator
|
||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||
import androidx.compose.material.pullrefresh.PullRefreshState
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -38,20 +40,20 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.rplexicon.LocalSnack
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.ui.composable.FloatingActionButton
|
||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||
import com.pixelized.rplexicon.ui.navigation.screens.navigateToCharacterDetail
|
||||
import com.pixelized.rplexicon.ui.navigation.screens.navigateToSearch
|
||||
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconErrorUio.Default
|
||||
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconErrorUio.Permission
|
||||
import com.pixelized.rplexicon.ui.screens.lexicon.LexiconErrorUio.Structure
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -61,6 +63,9 @@ sealed class LexiconErrorUio {
|
|||
@Stable
|
||||
data class Permission(val intent: Intent) : LexiconErrorUio()
|
||||
|
||||
@Stable
|
||||
object Structure : LexiconErrorUio()
|
||||
|
||||
@Stable
|
||||
object Default : LexiconErrorUio()
|
||||
}
|
||||
|
|
@ -75,7 +80,7 @@ fun LexiconScreen(
|
|||
val screen = LocalScreenNavHost.current
|
||||
|
||||
val refresh = rememberPullRefreshState(
|
||||
refreshing = viewModel.isLoading.value,
|
||||
refreshing = false,
|
||||
onRefresh = {
|
||||
scope.launch {
|
||||
viewModel.fetchLexicon()
|
||||
|
|
@ -113,7 +118,9 @@ fun LexiconScreen(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class,
|
||||
ExperimentalFoundationApi::class
|
||||
)
|
||||
@Composable
|
||||
private fun LexiconScreenContent(
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -156,9 +163,9 @@ private fun LexiconScreenContent(
|
|||
// },
|
||||
// )
|
||||
}
|
||||
) {
|
||||
) { padding ->
|
||||
Box(
|
||||
modifier = Modifier.padding(paddingValues = it),
|
||||
modifier = Modifier.padding(paddingValues = padding),
|
||||
contentAlignment = Alignment.TopCenter,
|
||||
) {
|
||||
LazyColumn(
|
||||
|
|
@ -171,30 +178,72 @@ private fun LexiconScreenContent(
|
|||
bottom = 8.dp + 16.dp // + 56.dp + 16.dp,
|
||||
),
|
||||
) {
|
||||
items(items = items.value) { item ->
|
||||
LexiconItem(
|
||||
modifier = Modifier
|
||||
.clickable { onItem(item) }
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
item = item,
|
||||
)
|
||||
if (items.value.isEmpty()) {
|
||||
items(
|
||||
count = 6,
|
||||
key = { it },
|
||||
contentType = { "Lexicon" },
|
||||
) {
|
||||
LexiconItem(
|
||||
modifier = Modifier
|
||||
.animateItemPlacement()
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
item = LexiconItemUio.Brulkhai,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
items(
|
||||
items = items.value,
|
||||
key = { it.id },
|
||||
contentType = { "Lexicon" },
|
||||
) {
|
||||
LexiconItem(
|
||||
modifier = Modifier
|
||||
.animateItemPlacement()
|
||||
.clickable { onItem(it) }
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
item = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PullRefreshIndicator(
|
||||
refreshing = refreshing.value,
|
||||
state = refreshState,
|
||||
Loader(
|
||||
refreshState = refreshState,
|
||||
refreshing = refreshing,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun Loader(
|
||||
refreshState: PullRefreshState,
|
||||
refreshing: State<Boolean>,
|
||||
) {
|
||||
if (refreshing.value) {
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(shape = CircleShape)
|
||||
)
|
||||
}
|
||||
|
||||
PullRefreshIndicator(
|
||||
refreshing = false,
|
||||
state = refreshState,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HandleError(
|
||||
errors: SharedFlow<LexiconErrorUio>,
|
||||
onLexiconPermissionGranted: suspend () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val snack = LocalSnack.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
|
|
@ -212,7 +261,8 @@ fun HandleError(
|
|||
errors.collect { error ->
|
||||
when (error) {
|
||||
is Permission -> launcher.launch(error.intent)
|
||||
is Default -> snack.showSnackbar(message = "Oops")
|
||||
is Structure -> snack.showSnackbar(message = context.getString(R.string.error_structure))
|
||||
is Default -> snack.showSnackbar(message = context.getString(R.string.error_generic))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -238,11 +288,11 @@ private fun LexiconScreenContentPreview() {
|
|||
mutableStateOf(
|
||||
listOf(
|
||||
LexiconItemUio(
|
||||
id = 2,
|
||||
id = 0,
|
||||
name = "Brulkhai",
|
||||
diminutive = "Bru",
|
||||
gender = "f.",
|
||||
race = "Demi-Orc",
|
||||
gender = R.string.gender_female_short,
|
||||
race = R.string.race_half_orc,
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.model.Lexicon
|
||||
import com.pixelized.rplexicon.repository.LexiconRepository
|
||||
import com.pixelized.rplexicon.repository.LexiconRepository.IncompatibleSheetStructure
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
|
@ -37,11 +39,26 @@ class LexiconViewModel @Inject constructor(
|
|||
name = item.name,
|
||||
diminutive = item.diminutive?.takeIf { it.isNotBlank() }?.let { "./ $it" },
|
||||
gender = when (item.gender) {
|
||||
Lexicon.Gender.MALE -> "m."
|
||||
Lexicon.Gender.FEMALE -> "f."
|
||||
Lexicon.Gender.UNDETERMINED -> "u."
|
||||
Lexicon.Gender.MALE -> R.string.gender_male_short
|
||||
Lexicon.Gender.FEMALE -> R.string.gender_female_short
|
||||
Lexicon.Gender.UNDETERMINED -> R.string.gender_undetermined_short
|
||||
},
|
||||
race = when (item.race) {
|
||||
Lexicon.Race.ELF -> R.string.race_elf
|
||||
Lexicon.Race.HALFLING -> R.string.race_halfling
|
||||
Lexicon.Race.HUMAN -> R.string.race_human
|
||||
Lexicon.Race.DWARF -> R.string.race_dwarf
|
||||
Lexicon.Race.HALF_ELF -> R.string.race_half_elf
|
||||
Lexicon.Race.HALF_ORC -> R.string.race_half_orc
|
||||
Lexicon.Race.DRAGONBORN -> R.string.race_dragonborn
|
||||
Lexicon.Race.GNOME -> R.string.race_gnome
|
||||
Lexicon.Race.TIEFLING -> R.string.race_tiefling
|
||||
Lexicon.Race.AARAKOCRA -> R.string.race_aarakocra
|
||||
Lexicon.Race.GENASI -> R.string.race_genasi
|
||||
Lexicon.Race.DEEP_GNOME -> R.string.race_deep_gnome
|
||||
Lexicon.Race.GOLIATH -> R.string.race_goliath
|
||||
Lexicon.Race.UNDETERMINED -> R.string.race_undetermined
|
||||
},
|
||||
race = item.race,
|
||||
)
|
||||
}.sortedBy { it.name }
|
||||
}
|
||||
|
|
@ -61,6 +78,9 @@ class LexiconViewModel @Inject constructor(
|
|||
catch (exception: UserRecoverableAuthIOException) {
|
||||
Log.e(TAG, exception.message, exception)
|
||||
_error.emit(LexiconErrorUio.Permission(intent = exception.intent))
|
||||
} catch (exception: IncompatibleSheetStructure) {
|
||||
Log.e(TAG, exception.message, exception)
|
||||
_error.emit(LexiconErrorUio.Structure)
|
||||
}
|
||||
// default exception
|
||||
catch (exception: Exception) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,45 @@
|
|||
package com.pixelized.rplexicon.ui.theme
|
||||
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.pixelized.rplexicon.ui.theme.colors.BaseDark
|
||||
import com.pixelized.rplexicon.ui.theme.colors.BaseLight
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
@Stable
|
||||
@Immutable
|
||||
class LexiconColors(
|
||||
val base: ColorScheme,
|
||||
val placeholder: Color,
|
||||
)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
@Stable
|
||||
fun darkColorScheme(
|
||||
base: ColorScheme = darkColorScheme(
|
||||
primary = BaseDark.Purple80,
|
||||
secondary = BaseDark.PurpleGrey80,
|
||||
tertiary = BaseDark.Pink80,
|
||||
onPrimary = Color.White,
|
||||
),
|
||||
placeholder: Color = Color(red = 49, green = 48, blue = 51),
|
||||
) = LexiconColors(
|
||||
base = base,
|
||||
placeholder = placeholder,
|
||||
)
|
||||
|
||||
@Stable
|
||||
fun lightColorScheme(
|
||||
base: ColorScheme = lightColorScheme(
|
||||
primary = BaseLight.Purple40,
|
||||
secondary = BaseLight.PurpleGrey40,
|
||||
tertiary = BaseLight.Pink40,
|
||||
onPrimary = Color.White,
|
||||
),
|
||||
placeholder: Color = Color(red = 230, green = 225, blue = 229),
|
||||
) = LexiconColors(
|
||||
base = base,
|
||||
placeholder = placeholder,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,27 +3,23 @@ package com.pixelized.rplexicon.ui.theme
|
|||
import android.app.Activity
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80,
|
||||
onPrimary = Color.White,
|
||||
)
|
||||
val LocalLexiconTheme = compositionLocalOf<LexiconTheme> {
|
||||
error("LocalLexiconTheme not ready yet.")
|
||||
}
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40,
|
||||
onPrimary = Color.White,
|
||||
@Stable
|
||||
data class LexiconTheme(
|
||||
val colorScheme: LexiconColors,
|
||||
)
|
||||
|
||||
@Composable
|
||||
|
|
@ -31,16 +27,20 @@ fun LexiconTheme(
|
|||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
val lexiconTheme = remember {
|
||||
LexiconTheme(
|
||||
colorScheme = when (darkTheme) {
|
||||
true -> darkColorScheme()
|
||||
else -> lightColorScheme()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
colorScheme.background.toArgb().let {
|
||||
lexiconTheme.colorScheme.base.background.toArgb().let {
|
||||
window.statusBarColor = it
|
||||
window.navigationBarColor = it
|
||||
}
|
||||
|
|
@ -51,9 +51,13 @@ fun LexiconTheme(
|
|||
}
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
CompositionLocalProvider(
|
||||
LocalLexiconTheme provides lexiconTheme,
|
||||
) {
|
||||
MaterialTheme(
|
||||
colorScheme = lexiconTheme.colorScheme.base,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package com.pixelized.rplexicon.ui.theme.colors
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
@Immutable
|
||||
object BaseDark {
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
object BaseLight {
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
object BaseColorPalette {
|
||||
val VeryDarkBlue: Color = Color(0xFF09179D)
|
||||
val DarkBlue: Color = Color(0xFF1A2BDB)
|
||||
val Blue: Color = Color(0xFF2970F2)
|
||||
val LightBlue: Color = Color(0xFF1A91DB)
|
||||
val VeryLightBlue: Color = Color(0xFF1EDDEF)
|
||||
val VeryDarkPurple: Color = Color(0xFF5F0E9E)
|
||||
val DarkPurple: Color = Color(0xFF8330DB)
|
||||
val Purple: Color = Color(0xFF9B54C3)
|
||||
val LightPurple: Color = Color(0xFFBC52D9)
|
||||
val VeryLightPurple: Color = Color(0xFFC856D1)
|
||||
val VeryDarkGreen: Color = Color(0xFF16544A)
|
||||
val DarkGreen: Color = Color(0xFF207A6B)
|
||||
val Green: Color = Color(0xFF269482)
|
||||
val LightGreen: Color = Color(0xFF2AA18D)
|
||||
val VeryLightGreen: Color = Color(0xFF3AE0C5)
|
||||
val VeryDarkRed: Color = Color(0xFF631221)
|
||||
val DarkRed: Color = Color(0xFFA21D36)
|
||||
val Red: Color = Color(0xFFC92443)
|
||||
val LightRed: Color = Color(0xFFE32849)
|
||||
val VeryLightRed: Color = Color(0xFFF02B4F)
|
||||
val VeryDarkPink: Color = Color(0xFF960064)
|
||||
val DarkPink: Color = Color(0xFFBD007E)
|
||||
val Pink: Color = Color(0xFFD6008F)
|
||||
val LightPink: Color = Color(0xFFE35BB5)
|
||||
val VeryLightPink: Color = Color(0xFFFF66CC)
|
||||
val VeryDarkYellow: Color = Color(0xFFB76036)
|
||||
val DarkYellow: Color = Color(0xFFD48341)
|
||||
val Yellow: Color = Color(0xFFF3A850)
|
||||
val LightYellow: Color = Color(0xFFF5BF63)
|
||||
val VeryLightYellow: Color = Color(0xFFF9D679)
|
||||
val VeryDarkGrey: Color = Color(0xFF1D1D1D)
|
||||
val DarkGrey: Color = Color(0xFF424242)
|
||||
val Grey: Color = Color(0xFF919195)
|
||||
val LightGrey: Color = Color(0xFFDFDFDF)
|
||||
val VeryLightGrey: Color = Color(0xFFF9F9F9)
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.pixelized.rplexicon.ui.theme.colors
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
@Immutable
|
||||
object ShadowPalette {
|
||||
val system: Color = Color.Black.copy(alpha = 0.37f)
|
||||
val scrim: Color = Color.Black.copy(alpha = 0.37f)
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.pixelized.rplexicon.utilitary.extentions
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
import com.pixelized.rplexicon.ui.theme.LocalLexiconTheme
|
||||
|
||||
val MaterialTheme.lexicon: LexiconTheme
|
||||
@Composable
|
||||
@Stable
|
||||
get() = LocalLexiconTheme.current
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.pixelized.rplexicon.utilitary.extentions
|
||||
|
||||
import androidx.compose.animation.core.FiniteAnimationSpec
|
||||
import androidx.compose.animation.core.Transition
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import com.google.accompanist.placeholder.PlaceholderHighlight
|
||||
import com.google.accompanist.placeholder.placeholder
|
||||
|
||||
@Composable
|
||||
fun Modifier.placeholder(
|
||||
color: Color = MaterialTheme.lexicon.colorScheme.placeholder,
|
||||
shape: Shape = CircleShape,
|
||||
highlight: PlaceholderHighlight? = null,
|
||||
placeholderFadeTransitionSpec: @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<Float> = { spring() },
|
||||
contentFadeTransitionSpec: @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<Float> = { spring() },
|
||||
visible: () -> Boolean,
|
||||
): Modifier = placeholder(
|
||||
visible = visible(),
|
||||
color = color,
|
||||
shape = shape,
|
||||
highlight = highlight,
|
||||
placeholderFadeTransitionSpec = placeholderFadeTransitionSpec,
|
||||
contentFadeTransitionSpec = contentFadeTransitionSpec,
|
||||
)
|
||||
|
|
@ -1,5 +1,35 @@
|
|||
<resources>
|
||||
<string name="app_name">Lexique</string>
|
||||
<string name="app_name">Rp-Lexique</string>
|
||||
|
||||
<string name="error_generic">Ah !? y\'a un truc qui foire quelque part.</string>
|
||||
<string name="error_structure">La structure du fichier semble avoir changé et n\'est plus compatible avec cette application.</string>
|
||||
|
||||
<string name="gender_male">Mâle</string>
|
||||
<string name="gender_female">Femelle</string>
|
||||
<string name="gender_undetermined">Indéterminé</string>
|
||||
<string name="gender_male_short">m.</string>
|
||||
<string name="gender_female_short">f.</string>
|
||||
<string name="gender_undetermined_short">i.</string>
|
||||
|
||||
<string name="race_elf">Elfe</string>
|
||||
<string name="race_halfling">Halfelin</string>
|
||||
<string name="race_human">Humain</string>
|
||||
<string name="race_dwarf">Nain</string>
|
||||
<string name="race_half_elf">Demi-Elfe</string>
|
||||
<string name="race_half_orc">Demi-Orc</string>
|
||||
<string name="race_dragonborn">Drakéide</string>
|
||||
<string name="race_gnome">Gnome</string>
|
||||
<string name="race_tiefling">Tieffelin</string>
|
||||
<string name="race_aarakocra">Aarakocra</string>
|
||||
<string name="race_genasi">Génasi</string>
|
||||
<string name="race_deep_gnome">Gnome des Profondeurs</string>
|
||||
<string name="race_goliath">Goliath</string>
|
||||
<string name="race_undetermined">Indéterminé</string>
|
||||
|
||||
<string name="action_google_sign_in">Se connecter avec</string>
|
||||
|
||||
<string name="detail_title">Détails du personnage</string>
|
||||
<string name="detail_description">Description</string>
|
||||
<string name="detail_history">History</string>
|
||||
<string name="detail_portrait">Portrait</string>
|
||||
</resources>
|
||||
|
|
@ -1,5 +1,35 @@
|
|||
<resources>
|
||||
<string name="app_name">Lexique</string>
|
||||
<string name="app_name">Rp-Lexicon</string>
|
||||
|
||||
<string name="error_generic">Oups, it should not be rocket science.</string>
|
||||
<string name="error_structure">The file structure appears to have changed and is no longer compatible with this application</string>
|
||||
|
||||
<string name="gender_male">Male</string>
|
||||
<string name="gender_female">Female</string>
|
||||
<string name="gender_undetermined">Undetermined</string>
|
||||
<string name="gender_male_short">m.</string>
|
||||
<string name="gender_female_short">f.</string>
|
||||
<string name="gender_undetermined_short">u.</string>
|
||||
|
||||
<string name="race_elf">Elf</string>
|
||||
<string name="race_halfling">Halfling</string>
|
||||
<string name="race_human">Human</string>
|
||||
<string name="race_dwarf">Dwarf</string>
|
||||
<string name="race_half_elf">Half-elf</string>
|
||||
<string name="race_half_orc">Half-Orc</string>
|
||||
<string name="race_dragonborn">Dragonborn</string>
|
||||
<string name="race_gnome">Gnome</string>
|
||||
<string name="race_tiefling">Tiefling</string>
|
||||
<string name="race_aarakocra">Aarakocra</string>
|
||||
<string name="race_genasi">Genasi</string>
|
||||
<string name="race_deep_gnome">Deep Gnome</string>
|
||||
<string name="race_goliath">Goliath</string>
|
||||
<string name="race_undetermined">Undetermined</string>
|
||||
|
||||
<string name="action_google_sign_in">Sign in with</string>
|
||||
|
||||
<string name="detail_title">Character\'s details</string>
|
||||
<string name="detail_description">Description</string>
|
||||
<string name="detail_history">Histoire</string>
|
||||
<string name="detail_portrait">Portrait</string>
|
||||
</resources>
|
||||
Loading…
Add table
Add a link
Reference in a new issue