Add support for a tags data.

This commit is contained in:
Thomas Andres Gomez 2023-07-31 18:29:01 +02:00
parent 82738a8f03
commit dc5e52cf18
9 changed files with 92 additions and 28 deletions

View file

@ -6,6 +6,7 @@ import androidx.compose.runtime.Stable
@Stable
data class Lexicon(
val id: Int,
val sheetIndex: Int,
val name: String,
val diminutive: String?,
val gender: Gender,
@ -13,6 +14,7 @@ data class Lexicon(
val portrait: List<Uri>,
val description: String?,
val history: String?,
val tags: String?,
) {
@Stable

View file

@ -55,6 +55,7 @@ class LexiconRepository @Inject constructor(
private fun updateData(data: ValueRange?) {
val sheet = data?.values?.sheet()
var sheetStructure: Map<String, Int>? = null
var id = 0
val lexicon: List<Lexicon> = sheet?.mapIndexedNotNull { index, row ->
when {
index == 0 -> {
@ -64,9 +65,13 @@ class LexiconRepository @Inject constructor(
row is List<*> -> parseCharacterRow(
sheetStructure = sheetStructure,
index = index - 1,
id = id,
sheetIndex = index,
row = row,
)
)?.also {
// update next id if parsing is successful.
id = it.id + 1
}
else -> null
}
@ -82,24 +87,19 @@ class LexiconRepository @Inject constructor(
}
// 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
if (cell is String && Sheet.COLUMNS.contains(cell)) {
sheetStructure[cell] = index
}
}
// check if we found everything we need.
when {
sheetStructure.size < Sheet.KNOWN_COLUMN -> throw IncompatibleSheetStructure(
sheetStructure.size < Sheet.COLUMNS.size -> throw IncompatibleSheetStructure(
message = "Sheet header row does not have enough column: ${firstRow.size}.\nstructure: $firstRow\nheader: $sheetStructure"
)
sheetStructure.size > Sheet.KNOWN_COLUMN -> throw IncompatibleSheetStructure(
sheetStructure.size > Sheet.COLUMNS.size -> throw IncompatibleSheetStructure(
message = "Sheet header row does have too mush columns: ${firstRow.size}.\nstructure: $firstRow\nheader: $sheetStructure"
)
}
@ -109,7 +109,8 @@ class LexiconRepository @Inject constructor(
private fun parseCharacterRow(
sheetStructure: Map<String, Int>?,
index: Int,
id: Int,
sheetIndex: Int,
row: List<*>?,
): Lexicon? {
val name = row?.getOrNull(sheetStructure.name) as? String
@ -119,10 +120,12 @@ class LexiconRepository @Inject constructor(
val portrait = row?.getOrNull(sheetStructure.portrait) as? String?
val description = row?.getOrNull(sheetStructure.description) as? String?
val history = row?.getOrNull(sheetStructure.history) as? String?
val tags = row?.getOrNull(sheetStructure.tags) as? String?
return if (name != null) {
Lexicon(
id = index,
id = id,
sheetIndex = sheetIndex,
name = name,
diminutive = diminutive?.takeIf { it.isNotBlank() },
gender = when (gender?.takeIf { it.isNotBlank() }) {
@ -149,6 +152,7 @@ class LexiconRepository @Inject constructor(
portrait = portrait?.split("\n")?.mapNotNull { it.toUriOrNull() } ?: emptyList(),
description = description?.takeIf { it.isNotBlank() },
history = history?.takeIf { it.isNotBlank() },
tags = tags?.takeIf { it.isNotBlank() },
)
} else {
null
@ -175,6 +179,7 @@ class LexiconRepository @Inject constructor(
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
private val Map<String, Int>?.tags: Int get() = this?.getValue(Sheet.TAGS) ?: 7
class ServiceNotReady : Exception()
@ -186,15 +191,29 @@ class LexiconRepository @Inject constructor(
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"
const val META = "MetaData"
val COLUMNS = listOf(
"Nom",
"Diminutif",
"Sexe",
"Race",
"Portrait",
"Description",
"Histoire",
"Mots clés",
)
val NAME = COLUMNS[0]
val DIMINUTIVE = COLUMNS[1]
val GENDER = COLUMNS[2]
val RACE = COLUMNS[3]
val PORTRAIT = COLUMNS[4]
val DESCRIPTION = COLUMNS[5]
val HISTORY = COLUMNS[6]
val TAGS = COLUMNS[7]
}
private object Gender {

View file

@ -80,6 +80,7 @@ data class CharacterDetailUio(
val search: String?,
val highlightGender: Boolean?,
val highlightRace: Boolean?,
val tags: String?,
)
@Stable
@ -91,6 +92,7 @@ data class AnnotatedCharacterDetailUio(
val portrait: List<Uri>,
val description: AnnotatedString?,
val history: AnnotatedString?,
val tags: AnnotatedString?
)
@Composable
@ -104,6 +106,7 @@ fun CharacterDetailUio.annotated(): AnnotatedCharacterDetailUio {
return remember(search, race, highlightRace, gender, highlightGender) {
AnnotatedCharacterDetailUio(
portrait = portrait,
name = AnnotatedString(
text = name,
spanStyles = highlightRegex?.annotatedSpan(
@ -137,7 +140,9 @@ fun CharacterDetailUio.annotated(): AnnotatedCharacterDetailUio {
history = history?.let { history ->
highlightRegex?.annotatedString(history, spanStyle = highlight)
},
portrait = portrait,
tags = tags?.let { tags ->
highlightRegex?.annotatedString(tags, spanStyle = highlight)
},
)
}
}
@ -244,6 +249,7 @@ private fun CharacterDetailScreenContent(
)
}
}
Row(
modifier = Modifier.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp)
@ -257,6 +263,7 @@ private fun CharacterDetailScreenContent(
text = annotatedItem.race,
)
}
annotatedItem.description?.let {
Text(
modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp),
@ -276,6 +283,7 @@ private fun CharacterDetailScreenContent(
text = it,
)
}
annotatedItem.history?.let {
Text(
modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp),
@ -288,6 +296,7 @@ private fun CharacterDetailScreenContent(
text = it,
)
}
if (annotatedItem.portrait.isNotEmpty()) {
val maxSize = rememberPortraitWidth()
Text(
@ -316,6 +325,14 @@ private fun CharacterDetailScreenContent(
}
}
}
annotatedItem.tags?.let {
Text(
modifier = Modifier.padding(start = 16.dp, top = 24.dp, end = 16.dp),
style = remember { typography.labelSmall.copy(fontStyle = FontStyle.Italic) },
text = it,
)
}
}
}
}
@ -374,6 +391,7 @@ private fun CharacterDetailScreenContentPreview() {
),
description = "Brulkhai, ou plus simplement Bru, est solidement bâti. Elle mesure 192 cm pour 110 kg de muscles lorsquelle est en bonne santé. Elle a les cheveux châtains, les yeux noisettes et la peau couleur gris-vert typique de son espèce. Dun 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 quelle considère plus faibles quelle. Dune 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 dun manque déducation et dune capacité limitée à gérer ses émotions quà une débilité congénitale. Elle voue à la force un culte car cest par son expression quelle se sent vraiment vivante et éprouve de grandes difficultés vis à vis de ceux quelle nomme foshnu (bébé, chouineur en commun).",
history = null,
tags = null,
search = "Bru",
highlightGender = true,
highlightRace = true,

View file

@ -29,6 +29,7 @@ class CharacterDetailViewModel @Inject constructor(
portrait = source.portrait,
description = source.description,
history = source.history,
tags = source.tags,
search = argument.highlight,
highlightGender = argument.highlightGender && argument.gender == source.gender,
highlightRace = argument.highlightRace && argument.race == source.race,

View file

@ -48,6 +48,7 @@ class SearchItemUio(
val race: Lexicon.Race,
val description: String?,
val history: String?,
val tags: String?,
val search: String,
val highlightGender: Boolean,
val highlightRace: Boolean,
@ -75,6 +76,7 @@ class SearchItemUio(
race = race,
description = description,
history = history,
tags = null,
search = search,
highlightGender = highlightGender,
highlightRace = highlightRace,
@ -92,6 +94,7 @@ class AnnotatedSearchItemUio(
val race: AnnotatedString,
val description: AnnotatedString?,
val history: AnnotatedString?,
val tags: AnnotatedString?,
)
@Composable
@ -143,6 +146,9 @@ private fun SearchItemUio.annotate(): AnnotatedSearchItemUio {
history = finderRegex?.foldAll(history)?.let { history ->
highlightRegex?.annotatedString(history, spanStyle = highlight)
},
tags = finderRegex?.foldAll(tags)?.let { tags ->
highlightRegex?.annotatedString(tags, spanStyle = highlight)
}
)
}
}
@ -244,6 +250,20 @@ fun SearchItem(
)
}
}
annotatedItem.tags?.let {
Column(
modifier = Modifier.padding(start = 8.dp),
) {
Text(
style = remember { typography.labelSmall.copy(fontWeight = FontWeight.Bold) },
text = stringResource(id = R.string.search_item_tags),
)
Text(
style = typography.labelSmall,
text = it,
)
}
}
}
}
}

View file

@ -215,28 +215,28 @@ private fun SearchScreenContentPreview() {
race = Lexicon.Race.HALF_ORC,
),
SearchItemUio.preview(
id = 0,
id = 1,
name = "Léandre",
diminutive = null,
gender = Lexicon.Gender.MALE,
race = Lexicon.Race.HUMAN,
),
SearchItemUio.preview(
id = 0,
id = 2,
name = "Nélia",
diminutive = null,
gender = Lexicon.Gender.FEMALE,
race = Lexicon.Race.ELF,
),
SearchItemUio.preview(
id = 0,
id = 3,
name = "Tigrane",
diminutive = null,
gender = Lexicon.Gender.MALE,
race = Lexicon.Race.TIEFLING,
),
SearchItemUio.preview(
id = 0,
id = 4,
name = "Unathana",
diminutive = "Una",
gender = Lexicon.Gender.FEMALE,

View file

@ -57,7 +57,8 @@ class SearchViewModel @Inject constructor(
val diminutive = item.diminutive?.contains(search, true) == true
val description = item.description?.contains(search, true) == true
val history = item.history?.contains(search, true) == true
name || diminutive || description || history
val tag = item.tags?.contains(search, true) == true
name || diminutive || description || history || tag
}
(gender == null || gender) && (race == null || race) && (search == null || search)
}.map {
@ -82,5 +83,6 @@ class SearchViewModel @Inject constructor(
search = search,
highlightGender = highlightGender,
highlightRace = highlightRace,
tags = tags,
)
}

View file

@ -41,4 +41,5 @@
<string name="search_field_gender">Sexe</string>
<string name="search_item_description">Description :</string>
<string name="search_item_history">Histoire :</string>
<string name="search_item_tags">Mots clés :</string>
</resources>

View file

@ -41,4 +41,5 @@
<string name="search_field_gender">Gender</string>
<string name="search_item_description">Description:</string>
<string name="search_item_history">History:</string>
<string name="search_item_tags">Tags:</string>
</resources>