feat(deck): бейдж чек-пунктов «☑ x/total» на карточке (P1 diff — CardBadges)
deckChecklistCounts парсит чекбоксы описания (regex как веб). Green compile+detekt.
This commit is contained in:
@@ -362,7 +362,9 @@ private fun DeckCardRow(card: DeckCard, onClick: () -> Unit, onToggleDone: () ->
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
val due = card.duedate?.let { formatDeckDue(it) }
|
val due = card.duedate?.let { formatDeckDue(it) }
|
||||||
if (due != null || card.assignees.isNotEmpty() || card.commentsCount > 0) {
|
val (checked, total) = deckChecklistCounts(card.description)
|
||||||
|
val hasMeta = listOf(due != null, total > 0, card.commentsCount > 0, card.assignees.isNotEmpty())
|
||||||
|
if (hasMeta.any { it }) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
@@ -371,6 +373,10 @@ private fun DeckCardRow(card: DeckCard, onClick: () -> Unit, onToggleDone: () ->
|
|||||||
if (due != null) {
|
if (due != null) {
|
||||||
Text("🗓 $due", style = MaterialTheme.typography.labelMedium, color = F7Colors.TextSecondary)
|
Text("🗓 $due", style = MaterialTheme.typography.labelMedium, color = F7Colors.TextSecondary)
|
||||||
}
|
}
|
||||||
|
if (total > 0) {
|
||||||
|
val todo = "☑ $checked/$total"
|
||||||
|
Text(todo, style = MaterialTheme.typography.labelMedium, color = F7Colors.TextSecondary)
|
||||||
|
}
|
||||||
if (card.commentsCount > 0) {
|
if (card.commentsCount > 0) {
|
||||||
val comments = "💬 ${card.commentsCount}"
|
val comments = "💬 ${card.commentsCount}"
|
||||||
Text(comments, style = MaterialTheme.typography.labelMedium, color = F7Colors.TextSecondary)
|
Text(comments, style = MaterialTheme.typography.labelMedium, color = F7Colors.TextSecondary)
|
||||||
@@ -403,6 +409,17 @@ internal fun DeckLabelChip(label: DeckLabel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val deckChecklistRe = Regex("(?im)^\\s*([*+-]|\\d\\.)\\s+\\[\\s*(\\s|[xX])\\s*\\].*$")
|
||||||
|
private val deckCheckedRe = Regex("(?im)^\\s*([*+-]|\\d\\.)\\s+\\[\\s*[xX]\\s*\\].*$")
|
||||||
|
|
||||||
|
/** Чек-пункты из описания карточки (как веб CardBadges): (отмечено, всего). */
|
||||||
|
internal fun deckChecklistCounts(description: String): Pair<Int, Int> {
|
||||||
|
if (description.isBlank()) return 0 to 0
|
||||||
|
val total = deckChecklistRe.findAll(description).count()
|
||||||
|
val checked = deckCheckedRe.findAll(description).count()
|
||||||
|
return checked to total
|
||||||
|
}
|
||||||
|
|
||||||
/** Цвет Deck («RRGGBB» без #, иногда с #). */
|
/** Цвет Deck («RRGGBB» без #, иногда с #). */
|
||||||
internal fun parseDeckColor(hex: String?): Color? {
|
internal fun parseDeckColor(hex: String?): Color? {
|
||||||
val h = hex?.trim()?.removePrefix("#")?.takeIf { it.length == 6 } ?: return null
|
val h = hex?.trim()?.removePrefix("#")?.takeIf { it.length == 6 } ?: return null
|
||||||
|
|||||||
Reference in New Issue
Block a user