feat(preview): IMPL_03 - Add checklist sorting in main preview
Changes: - Note.kt: Add checklistSortOption field (String?, nullable for backward compatibility) - Note.fromJson(): Parse checklistSortOption from JSON - Note.fromMarkdown(): Parse sort field from YAML frontmatter - Note.toMarkdown(): Include sort field in YAML frontmatter for checklists - ChecklistPreviewHelper.kt: New file with sortChecklistItemsForPreview() and generateChecklistPreview() - NoteEditorViewModel.loadNote(): Load and restore checklistSortOption from note - NoteEditorViewModel.saveNote(): Persist checklistSortOption when saving (both new and existing notes) - NoteCardCompact.kt: Use generateChecklistPreview() for sorted preview (includes IMPL_06 emoji change) - NoteCardGrid.kt: Use generateChecklistPreview() for sorted preview (includes IMPL_06 emoji change)
This commit is contained in:
@@ -24,7 +24,9 @@ data class Note(
|
||||
val syncStatus: SyncStatus = SyncStatus.LOCAL_ONLY,
|
||||
// v1.4.0: Checklisten-Felder
|
||||
val noteType: NoteType = NoteType.TEXT,
|
||||
val checklistItems: List<ChecklistItem>? = null
|
||||
val checklistItems: List<ChecklistItem>? = null,
|
||||
// 🆕 v1.8.1 (IMPL_03): Persistierte Sortierung
|
||||
val checklistSortOption: String? = null
|
||||
) {
|
||||
/**
|
||||
* Serialisiert Note zu JSON
|
||||
@@ -71,13 +73,20 @@ data class Note(
|
||||
* v1.4.0: Unterstützt jetzt auch Checklisten-Format
|
||||
*/
|
||||
fun toMarkdown(): String {
|
||||
// 🆕 v1.8.1 (IMPL_03): Sortierung im Frontmatter
|
||||
val sortLine = if (noteType == NoteType.CHECKLIST && checklistSortOption != null) {
|
||||
"\nsort: $checklistSortOption"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
val header = """
|
||||
---
|
||||
id: $id
|
||||
created: ${formatISO8601(createdAt)}
|
||||
updated: ${formatISO8601(updatedAt)}
|
||||
device: $deviceId
|
||||
type: ${noteType.name.lowercase()}
|
||||
type: ${noteType.name.lowercase()}$sortLine
|
||||
---
|
||||
|
||||
# $title
|
||||
@@ -119,6 +128,14 @@ type: ${noteType.name.lowercase()}
|
||||
NoteType.TEXT
|
||||
}
|
||||
|
||||
// 🆕 v1.8.1 (IMPL_03): Gespeicherte Sortierung laden
|
||||
val checklistSortOption = if (jsonObject.has("checklistSortOption") &&
|
||||
!jsonObject.get("checklistSortOption").isJsonNull) {
|
||||
jsonObject.get("checklistSortOption").asString
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
// Parsen der Basis-Note
|
||||
val rawNote = gson.fromJson(json, NoteRaw::class.java)
|
||||
|
||||
@@ -158,7 +175,8 @@ type: ${noteType.name.lowercase()}
|
||||
deviceId = rawNote.deviceId,
|
||||
syncStatus = rawNote.syncStatus ?: SyncStatus.LOCAL_ONLY,
|
||||
noteType = noteType,
|
||||
checklistItems = checklistItems
|
||||
checklistItems = checklistItems,
|
||||
checklistSortOption = checklistSortOption // 🆕 v1.8.1 (IMPL_03)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Logger.w(TAG, "Failed to parse JSON: ${e.message}")
|
||||
@@ -246,6 +264,9 @@ type: ${noteType.name.lowercase()}
|
||||
else -> NoteType.TEXT
|
||||
}
|
||||
|
||||
// 🆕 v1.8.1 (IMPL_03): Gespeicherte Sortierung aus YAML laden
|
||||
val checklistSortOption = metadata["sort"]
|
||||
|
||||
// v1.4.0: Parse Content basierend auf Typ
|
||||
// FIX: Robusteres Parsing - suche nach dem Titel-Header und extrahiere den Rest
|
||||
val titleLineIndex = contentBlock.lines().indexOfFirst { it.startsWith("# ") }
|
||||
@@ -300,7 +321,8 @@ type: ${noteType.name.lowercase()}
|
||||
deviceId = metadata["device"] ?: "desktop",
|
||||
syncStatus = SyncStatus.SYNCED, // Annahme: Vom Server importiert
|
||||
noteType = noteType,
|
||||
checklistItems = checklistItems
|
||||
checklistItems = checklistItems,
|
||||
checklistSortOption = checklistSortOption // 🆕 v1.8.1 (IMPL_03)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Logger.w(TAG, "Failed to parse Markdown: ${e.message}")
|
||||
|
||||
@@ -109,6 +109,16 @@ class NoteEditorViewModel(
|
||||
}
|
||||
|
||||
if (note.noteType == NoteType.CHECKLIST) {
|
||||
// 🆕 v1.8.1 (IMPL_03): Gespeicherte Sortierung laden
|
||||
note.checklistSortOption?.let { sortName ->
|
||||
try {
|
||||
_lastChecklistSortOption.value = ChecklistSortOption.valueOf(sortName)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Logger.w(TAG, "Unknown sort option '$sortName', using MANUAL")
|
||||
_lastChecklistSortOption.value = ChecklistSortOption.MANUAL
|
||||
}
|
||||
}
|
||||
|
||||
val items = note.checklistItems?.sortedBy { it.order }?.map {
|
||||
ChecklistItemState(
|
||||
id = it.id,
|
||||
@@ -351,6 +361,7 @@ class NoteEditorViewModel(
|
||||
content = "", // Empty for checklists
|
||||
noteType = NoteType.CHECKLIST,
|
||||
checklistItems = validItems,
|
||||
checklistSortOption = _lastChecklistSortOption.value.name, // 🆕 v1.8.1 (IMPL_03)
|
||||
updatedAt = System.currentTimeMillis(),
|
||||
syncStatus = SyncStatus.PENDING
|
||||
)
|
||||
@@ -360,6 +371,7 @@ class NoteEditorViewModel(
|
||||
content = "",
|
||||
noteType = NoteType.CHECKLIST,
|
||||
checklistItems = validItems,
|
||||
checklistSortOption = _lastChecklistSortOption.value.name, // 🆕 v1.8.1 (IMPL_03)
|
||||
deviceId = DeviceIdGenerator.getDeviceId(getApplication()),
|
||||
syncStatus = SyncStatus.LOCAL_ONLY
|
||||
)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package dev.dettmer.simplenotes.ui.main.components
|
||||
|
||||
import dev.dettmer.simplenotes.models.ChecklistItem
|
||||
import dev.dettmer.simplenotes.models.ChecklistSortOption
|
||||
|
||||
/**
|
||||
* 🆕 v1.8.1 (IMPL_03): Helper-Funktionen für die Checklisten-Vorschau in Main Activity.
|
||||
*
|
||||
* Stellt sicher, dass die Sortierung aus dem Editor konsistent
|
||||
* in allen Preview-Components (NoteCard, NoteCardCompact, NoteCardGrid)
|
||||
* angezeigt wird.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sortiert Checklist-Items für die Vorschau basierend auf der
|
||||
* gespeicherten Sortier-Option.
|
||||
*/
|
||||
fun sortChecklistItemsForPreview(
|
||||
items: List<ChecklistItem>,
|
||||
sortOptionName: String?
|
||||
): List<ChecklistItem> {
|
||||
val sortOption = try {
|
||||
sortOptionName?.let { ChecklistSortOption.valueOf(it) }
|
||||
} catch (e: IllegalArgumentException) {
|
||||
null
|
||||
} ?: ChecklistSortOption.MANUAL
|
||||
|
||||
return when (sortOption) {
|
||||
ChecklistSortOption.MANUAL,
|
||||
ChecklistSortOption.UNCHECKED_FIRST ->
|
||||
items.sortedBy { it.isChecked }
|
||||
|
||||
ChecklistSortOption.CHECKED_FIRST ->
|
||||
items.sortedByDescending { it.isChecked }
|
||||
|
||||
ChecklistSortOption.ALPHABETICAL_ASC ->
|
||||
items.sortedBy { it.text.lowercase() }
|
||||
|
||||
ChecklistSortOption.ALPHABETICAL_DESC ->
|
||||
items.sortedByDescending { it.text.lowercase() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generiert den Vorschau-Text für eine Checkliste mit korrekter
|
||||
* Sortierung und passenden Emojis.
|
||||
*
|
||||
* @param items Die Checklisten-Items
|
||||
* @param sortOptionName Der Name der ChecklistSortOption (oder null für MANUAL)
|
||||
* @return Formatierter Preview-String mit Emojis und Zeilenumbrüchen
|
||||
*
|
||||
* 🆕 v1.8.1 (IMPL_06): Emoji-Änderung (☑️ statt ✅ für checked items)
|
||||
*/
|
||||
fun generateChecklistPreview(
|
||||
items: List<ChecklistItem>,
|
||||
sortOptionName: String?
|
||||
): String {
|
||||
val sorted = sortChecklistItemsForPreview(items, sortOptionName)
|
||||
return sorted.joinToString("\n") { item ->
|
||||
val prefix = if (item.isChecked) "☑️" else "☐"
|
||||
"$prefix ${item.text}"
|
||||
}
|
||||
}
|
||||
@@ -149,11 +149,10 @@ fun NoteCardCompact(
|
||||
text = when (note.noteType) {
|
||||
NoteType.TEXT -> note.content
|
||||
NoteType.CHECKLIST -> {
|
||||
note.checklistItems
|
||||
?.joinToString("\n") { item ->
|
||||
val prefix = if (item.isChecked) "✅" else "☐"
|
||||
"$prefix ${item.text}"
|
||||
} ?: ""
|
||||
// 🆕 v1.8.1 (IMPL_03 + IMPL_06): Sortierte Preview mit neuen Emojis
|
||||
note.checklistItems?.let { items ->
|
||||
generateChecklistPreview(items, note.checklistSortOption)
|
||||
} ?: ""
|
||||
}
|
||||
},
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
|
||||
@@ -163,11 +163,10 @@ fun NoteCardGrid(
|
||||
text = when (note.noteType) {
|
||||
NoteType.TEXT -> note.content
|
||||
NoteType.CHECKLIST -> {
|
||||
note.checklistItems
|
||||
?.joinToString("\n") { item ->
|
||||
val prefix = if (item.isChecked) "✅" else "☐"
|
||||
"$prefix ${item.text}"
|
||||
} ?: ""
|
||||
// 🆕 v1.8.1 (IMPL_03 + IMPL_06): Sortierte Preview mit neuen Emojis
|
||||
note.checklistItems?.let { items ->
|
||||
generateChecklistPreview(items, note.checklistSortOption)
|
||||
} ?: ""
|
||||
}
|
||||
},
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
|
||||
Reference in New Issue
Block a user