diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetActions.kt b/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetActions.kt index 902ffde..c85e14f 100644 --- a/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetActions.kt +++ b/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetActions.kt @@ -5,6 +5,7 @@ import androidx.glance.GlanceId import androidx.glance.action.ActionParameters import androidx.glance.appwidget.action.ActionCallback import androidx.glance.appwidget.state.updateAppWidgetState +import dev.dettmer.simplenotes.models.ChecklistSortOption import dev.dettmer.simplenotes.models.SyncStatus import dev.dettmer.simplenotes.storage.NotesStorage import dev.dettmer.simplenotes.utils.Logger @@ -51,14 +52,32 @@ class ToggleChecklistItemAction : ActionCallback { } else item } ?: return + // 🆕 v1.8.1 (IMPL_04): Auto-Sort nach Toggle + // Konsistent mit NoteEditorViewModel.updateChecklistItemChecked + val sortOption = try { + note.checklistSortOption?.let { ChecklistSortOption.valueOf(it) } + } catch (e: IllegalArgumentException) { null } + ?: ChecklistSortOption.MANUAL + + val sortedItems = if (sortOption == ChecklistSortOption.MANUAL || + sortOption == ChecklistSortOption.UNCHECKED_FIRST) { + val unchecked = updatedItems.filter { !it.isChecked } + val checked = updatedItems.filter { it.isChecked } + (unchecked + checked).mapIndexed { index, item -> + item.copy(order = index) + } + } else { + updatedItems.mapIndexed { index, item -> item.copy(order = index) } + } + val updatedNote = note.copy( - checklistItems = updatedItems, + checklistItems = sortedItems, updatedAt = System.currentTimeMillis(), syncStatus = SyncStatus.PENDING ) storage.saveNote(updatedNote) - Logger.d(TAG, "Toggled checklist item '$itemId' in widget") + Logger.d(TAG, "Toggled + auto-sorted checklist item '$itemId' in widget") // 🐛 FIX: Glance-State ändern um Re-Render zu erzwingen updateAppWidgetState(context, glanceId) { prefs -> diff --git a/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetContent.kt b/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetContent.kt index ecb967e..986d0f6 100644 --- a/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetContent.kt +++ b/android/app/src/main/java/dev/dettmer/simplenotes/widget/NoteWidgetContent.kt @@ -38,9 +38,11 @@ import androidx.glance.layout.width import androidx.glance.text.Text import androidx.glance.text.TextStyle import dev.dettmer.simplenotes.R +import dev.dettmer.simplenotes.models.ChecklistSortOption import dev.dettmer.simplenotes.models.Note import dev.dettmer.simplenotes.models.NoteType import dev.dettmer.simplenotes.ui.editor.ComposeNoteEditorActivity +import dev.dettmer.simplenotes.ui.main.components.sortChecklistItemsForPreview /** * 🆕 v1.8.0: Glance Composable Content für das Notiz-Widget @@ -72,6 +74,29 @@ private fun DpSize.toSizeClass(): WidgetSizeClass = when { else -> WidgetSizeClass.WIDE_TALL } +/** + * 🆕 v1.8.1 (IMPL_04): Separator zwischen erledigten und unerledigten Items im Widget. + * Glance-kompatible Version von CheckedItemsSeparator. + */ +@Composable +private fun WidgetCheckedItemsSeparator(checkedCount: Int) { + Row( + modifier = GlanceModifier + .fillMaxWidth() + .padding(vertical = 4.dp, horizontal = 8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "── $checkedCount ✔ ──", + style = TextStyle( + color = GlanceTheme.colors.outline, + fontSize = 11.sp + ) + ) + } +} + @Composable fun NoteWidgetContent( note: Note?, @@ -404,13 +429,35 @@ private fun ChecklistCompactView( isLocked: Boolean, glanceId: GlanceId ) { - val items = note.checklistItems?.sortedBy { it.order } ?: return + // 🆕 v1.8.1 (IMPL_04): Sortierung aus Editor übernehmen + val items = note.checklistItems?.let { rawItems -> + sortChecklistItemsForPreview(rawItems, note.checklistSortOption) + } ?: return + + // 🆕 v1.8.1 (IMPL_04): Separator-Logik + val uncheckedCount = items.count { !it.isChecked } + val checkedCount = items.count { it.isChecked } + val sortOption = try { + note.checklistSortOption?.let { ChecklistSortOption.valueOf(it) } + } catch (e: IllegalArgumentException) { null } + ?: ChecklistSortOption.MANUAL + + val showSeparator = (sortOption == ChecklistSortOption.MANUAL || + sortOption == ChecklistSortOption.UNCHECKED_FIRST) && + uncheckedCount > 0 && checkedCount > 0 + val visibleItems = items.take(maxItems) val remainingCount = items.size - visibleItems.size - val checkedCount = items.count { it.isChecked } Column(modifier = GlanceModifier.padding(horizontal = 8.dp, vertical = 2.dp)) { + var separatorShown = false visibleItems.forEach { item -> + // 🆕 v1.8.1: Separator vor dem ersten checked Item anzeigen + if (showSeparator && !separatorShown && item.isChecked) { + WidgetCheckedItemsSeparator(checkedCount = checkedCount) + separatorShown = true + } + if (isLocked) { Row( modifier = GlanceModifier @@ -419,7 +466,7 @@ private fun ChecklistCompactView( verticalAlignment = Alignment.CenterVertically ) { Text( - text = if (item.isChecked) "✅" else "☐", + text = if (item.isChecked) "☑️" else "☐", // 🆕 v1.8.1 (IMPL_06) style = TextStyle(fontSize = 14.sp) ) Spacer(modifier = GlanceModifier.width(6.dp)) @@ -477,15 +524,41 @@ private fun ChecklistFullView( isLocked: Boolean, glanceId: GlanceId ) { - val items = note.checklistItems?.sortedBy { it.order } ?: return + // 🆕 v1.8.1 (IMPL_04): Sortierung aus Editor übernehmen + val items = note.checklistItems?.let { rawItems -> + sortChecklistItemsForPreview(rawItems, note.checklistSortOption) + } ?: return + + // 🆕 v1.8.1 (IMPL_04): Separator-Logik + val uncheckedCount = items.count { !it.isChecked } + val checkedCount = items.count { it.isChecked } + val sortOption = try { + note.checklistSortOption?.let { ChecklistSortOption.valueOf(it) } + } catch (e: IllegalArgumentException) { null } + ?: ChecklistSortOption.MANUAL + + val showSeparator = (sortOption == ChecklistSortOption.MANUAL || + sortOption == ChecklistSortOption.UNCHECKED_FIRST) && + uncheckedCount > 0 && checkedCount > 0 + + // 🆕 v1.8.1: Berechne die Gesamtanzahl der Elemente inklusive Separator + val totalItems = items.size + if (showSeparator) 1 else 0 LazyColumn( modifier = GlanceModifier .fillMaxSize() .padding(horizontal = 8.dp) ) { - items(items.size) { index -> - val item = items[index] + items(totalItems) { index -> + // 🆕 v1.8.1: Separator an Position uncheckedCount einfügen + if (showSeparator && index == uncheckedCount) { + WidgetCheckedItemsSeparator(checkedCount = checkedCount) + return@items + } + + // Tatsächlichen Item-Index berechnen (nach Separator um 1 verschoben) + val itemIndex = if (showSeparator && index > uncheckedCount) index - 1 else index + val item = items.getOrNull(itemIndex) ?: return@items if (isLocked) { Row( @@ -495,7 +568,7 @@ private fun ChecklistFullView( verticalAlignment = Alignment.CenterVertically ) { Text( - text = if (item.isChecked) "✅" else "☐", + text = if (item.isChecked) "☑️" else "☐", // 🆕 v1.8.1 (IMPL_06) style = TextStyle(fontSize = 16.sp) ) Spacer(modifier = GlanceModifier.width(8.dp))