From eb4d8d3c509e6fe1c6ef998caaa58f6bd9a55a53 Mon Sep 17 00:00:00 2001 From: Chen Asraf Date: Fri, 15 May 2026 23:22:58 +0300 Subject: [PATCH] feat: add trash view --- lib/messages.i18n.dart | 93 +++++ lib/messages.i18n.yaml | 15 + lib/messages_de.i18n.dart | 101 ++++++ lib/messages_de.i18n.yaml | 15 + lib/messages_es.i18n.dart | 97 +++++ lib/messages_es.i18n.yaml | 15 + lib/messages_fr.i18n.dart | 98 +++++ lib/messages_fr.i18n.yaml | 15 + lib/messages_he.i18n.dart | 92 +++++ lib/messages_he.i18n.yaml | 15 + lib/models/checklist.dart | 5 + lib/services/checklist_service.dart | 38 ++ lib/views/checklists/checklist_item_tile.dart | 115 ++++-- .../checklists/checklists_controller.dart | 97 ++++- lib/views/checklists/checklists_view.dart | 338 +++++++++++++++--- lib/views/notes/notes_wall_view.dart | 1 + 16 files changed, 1075 insertions(+), 75 deletions(-) diff --git a/lib/messages.i18n.dart b/lib/messages.i18n.dart index 32dfd1b..3352feb 100644 --- a/lib/messages.i18n.dart +++ b/lib/messages.i18n.dart @@ -717,6 +717,82 @@ class ChecklistsMessages { /// ``` String get undo => """Undo"""; + /// ```dart + /// "View trash" + /// ``` + String get viewTrash => """View trash"""; + + /// ```dart + /// "Exit trash" + /// ``` + String get exitTrash => """Exit trash"""; + + /// ```dart + /// "Trash" + /// ``` + String get trashTitle => """Trash"""; + + /// ```dart + /// "Trash is empty." + /// ``` + String get noTrashedItems => """Trash is empty."""; + + /// ```dart + /// "Empty trash" + /// ``` + String get emptyTrash => """Empty trash"""; + + /// ```dart + /// "Empty the trash?" + /// ``` + String get emptyTrashConfirm => """Empty the trash?"""; + + /// ```dart + /// "All items in the trash will be permanently deleted. This cannot be undone." + /// ``` + String get emptyTrashConfirmBody => + """All items in the trash will be permanently deleted. This cannot be undone."""; + + /// ```dart + /// "Failed to empty trash." + /// ``` + String get emptyTrashFailed => """Failed to empty trash."""; + + /// ```dart + /// "Restore" + /// ``` + String get restoreItem => """Restore"""; + + /// ```dart + /// "Delete permanently" + /// ``` + String get permanentlyDeleteItem => """Delete permanently"""; + + /// ```dart + /// "Permanently delete this item?" + /// ``` + String get permanentlyDeleteConfirm => """Permanently delete this item?"""; + + /// ```dart + /// "This cannot be undone." + /// ``` + String get permanentlyDeleteConfirmBody => """This cannot be undone."""; + + /// ```dart + /// "Failed to restore item." + /// ``` + String get restoreFailed => """Failed to restore item."""; + + /// ```dart + /// "Failed to delete item." + /// ``` + String get permanentlyDeleteFailed => """Failed to delete item."""; + + /// ```dart + /// "Item restored" + /// ``` + String get itemRestored => """Item restored"""; + /// ```dart /// "New list" /// ``` @@ -1586,6 +1662,23 @@ Please complete login in your browser.""", """checklists.moveFailed""": """Failed to move item.""", """checklists.itemMarkedDone""": """Item marked as done""", """checklists.undo""": """Undo""", + """checklists.viewTrash""": """View trash""", + """checklists.exitTrash""": """Exit trash""", + """checklists.trashTitle""": """Trash""", + """checklists.noTrashedItems""": """Trash is empty.""", + """checklists.emptyTrash""": """Empty trash""", + """checklists.emptyTrashConfirm""": """Empty the trash?""", + """checklists.emptyTrashConfirmBody""": + """All items in the trash will be permanently deleted. This cannot be undone.""", + """checklists.emptyTrashFailed""": """Failed to empty trash.""", + """checklists.restoreItem""": """Restore""", + """checklists.permanentlyDeleteItem""": """Delete permanently""", + """checklists.permanentlyDeleteConfirm""": + """Permanently delete this item?""", + """checklists.permanentlyDeleteConfirmBody""": """This cannot be undone.""", + """checklists.restoreFailed""": """Failed to restore item.""", + """checklists.permanentlyDeleteFailed""": """Failed to delete item.""", + """checklists.itemRestored""": """Item restored""", """checklists.createList""": """New list""", """checklists.listName""": """List name""", """checklists.listDescription""": """Description (optional)""", diff --git a/lib/messages.i18n.yaml b/lib/messages.i18n.yaml index 4b826c2..bd25b53 100644 --- a/lib/messages.i18n.yaml +++ b/lib/messages.i18n.yaml @@ -131,6 +131,21 @@ checklists: moveFailed: Failed to move item. itemMarkedDone: Item marked as done undo: Undo + viewTrash: View trash + exitTrash: Exit trash + trashTitle: Trash + noTrashedItems: Trash is empty. + emptyTrash: Empty trash + emptyTrashConfirm: Empty the trash? + emptyTrashConfirmBody: All items in the trash will be permanently deleted. This cannot be undone. + emptyTrashFailed: Failed to empty trash. + restoreItem: Restore + permanentlyDeleteItem: Delete permanently + permanentlyDeleteConfirm: Permanently delete this item? + permanentlyDeleteConfirmBody: This cannot be undone. + restoreFailed: Failed to restore item. + permanentlyDeleteFailed: Failed to delete item. + itemRestored: Item restored createList: New list listName: List name listDescription: Description (optional) diff --git a/lib/messages_de.i18n.dart b/lib/messages_de.i18n.dart index 45035ac..b9e3582 100644 --- a/lib/messages_de.i18n.dart +++ b/lib/messages_de.i18n.dart @@ -723,6 +723,86 @@ class ChecklistsMessagesDe extends ChecklistsMessages { /// ``` String get undo => """Rückgängig"""; + /// ```dart + /// "Papierkorb anzeigen" + /// ``` + String get viewTrash => """Papierkorb anzeigen"""; + + /// ```dart + /// "Papierkorb verlassen" + /// ``` + String get exitTrash => """Papierkorb verlassen"""; + + /// ```dart + /// "Papierkorb" + /// ``` + String get trashTitle => """Papierkorb"""; + + /// ```dart + /// "Der Papierkorb ist leer." + /// ``` + String get noTrashedItems => """Der Papierkorb ist leer."""; + + /// ```dart + /// "Papierkorb leeren" + /// ``` + String get emptyTrash => """Papierkorb leeren"""; + + /// ```dart + /// "Papierkorb leeren?" + /// ``` + String get emptyTrashConfirm => """Papierkorb leeren?"""; + + /// ```dart + /// "Alle Einträge im Papierkorb werden endgültig gelöscht. Dies kann nicht rückgängig gemacht werden." + /// ``` + String get emptyTrashConfirmBody => + """Alle Einträge im Papierkorb werden endgültig gelöscht. Dies kann nicht rückgängig gemacht werden."""; + + /// ```dart + /// "Papierkorb konnte nicht geleert werden." + /// ``` + String get emptyTrashFailed => """Papierkorb konnte nicht geleert werden."""; + + /// ```dart + /// "Wiederherstellen" + /// ``` + String get restoreItem => """Wiederherstellen"""; + + /// ```dart + /// "Endgültig löschen" + /// ``` + String get permanentlyDeleteItem => """Endgültig löschen"""; + + /// ```dart + /// "Diesen Eintrag endgültig löschen?" + /// ``` + String get permanentlyDeleteConfirm => + """Diesen Eintrag endgültig löschen?"""; + + /// ```dart + /// "Dies kann nicht rückgängig gemacht werden." + /// ``` + String get permanentlyDeleteConfirmBody => + """Dies kann nicht rückgängig gemacht werden."""; + + /// ```dart + /// "Eintrag konnte nicht wiederhergestellt werden." + /// ``` + String get restoreFailed => + """Eintrag konnte nicht wiederhergestellt werden."""; + + /// ```dart + /// "Eintrag konnte nicht gelöscht werden." + /// ``` + String get permanentlyDeleteFailed => + """Eintrag konnte nicht gelöscht werden."""; + + /// ```dart + /// "Eintrag wiederhergestellt" + /// ``` + String get itemRestored => """Eintrag wiederhergestellt"""; + /// ```dart /// "Neue Liste" /// ``` @@ -1607,6 +1687,27 @@ Bitte melde dich in deinem Browser an.""", """checklists.moveFailed""": """Eintrag konnte nicht verschoben werden.""", """checklists.itemMarkedDone""": """Eintrag als erledigt markiert""", """checklists.undo""": """Rückgängig""", + """checklists.viewTrash""": """Papierkorb anzeigen""", + """checklists.exitTrash""": """Papierkorb verlassen""", + """checklists.trashTitle""": """Papierkorb""", + """checklists.noTrashedItems""": """Der Papierkorb ist leer.""", + """checklists.emptyTrash""": """Papierkorb leeren""", + """checklists.emptyTrashConfirm""": """Papierkorb leeren?""", + """checklists.emptyTrashConfirmBody""": + """Alle Einträge im Papierkorb werden endgültig gelöscht. Dies kann nicht rückgängig gemacht werden.""", + """checklists.emptyTrashFailed""": + """Papierkorb konnte nicht geleert werden.""", + """checklists.restoreItem""": """Wiederherstellen""", + """checklists.permanentlyDeleteItem""": """Endgültig löschen""", + """checklists.permanentlyDeleteConfirm""": + """Diesen Eintrag endgültig löschen?""", + """checklists.permanentlyDeleteConfirmBody""": + """Dies kann nicht rückgängig gemacht werden.""", + """checklists.restoreFailed""": + """Eintrag konnte nicht wiederhergestellt werden.""", + """checklists.permanentlyDeleteFailed""": + """Eintrag konnte nicht gelöscht werden.""", + """checklists.itemRestored""": """Eintrag wiederhergestellt""", """checklists.createList""": """Neue Liste""", """checklists.listName""": """Listenname""", """checklists.listDescription""": """Beschreibung (optional)""", diff --git a/lib/messages_de.i18n.yaml b/lib/messages_de.i18n.yaml index 915efc9..2ec4b5a 100644 --- a/lib/messages_de.i18n.yaml +++ b/lib/messages_de.i18n.yaml @@ -131,6 +131,21 @@ checklists: moveFailed: Eintrag konnte nicht verschoben werden. itemMarkedDone: Eintrag als erledigt markiert undo: "Rückgängig" + viewTrash: Papierkorb anzeigen + exitTrash: Papierkorb verlassen + trashTitle: Papierkorb + noTrashedItems: Der Papierkorb ist leer. + emptyTrash: Papierkorb leeren + emptyTrashConfirm: Papierkorb leeren? + emptyTrashConfirmBody: "Alle Einträge im Papierkorb werden endgültig gelöscht. Dies kann nicht rückgängig gemacht werden." + emptyTrashFailed: Papierkorb konnte nicht geleert werden. + restoreItem: Wiederherstellen + permanentlyDeleteItem: Endgültig löschen + permanentlyDeleteConfirm: Diesen Eintrag endgültig löschen? + permanentlyDeleteConfirmBody: "Dies kann nicht rückgängig gemacht werden." + restoreFailed: Eintrag konnte nicht wiederhergestellt werden. + permanentlyDeleteFailed: Eintrag konnte nicht gelöscht werden. + itemRestored: Eintrag wiederhergestellt createList: Neue Liste listName: Listenname listDescription: Beschreibung (optional) diff --git a/lib/messages_es.i18n.dart b/lib/messages_es.i18n.dart index db0c920..152b8a2 100644 --- a/lib/messages_es.i18n.dart +++ b/lib/messages_es.i18n.dart @@ -721,6 +721,84 @@ class ChecklistsMessagesEs extends ChecklistsMessages { /// ``` String get undo => """Deshacer"""; + /// ```dart + /// "Ver papelera" + /// ``` + String get viewTrash => """Ver papelera"""; + + /// ```dart + /// "Salir de la papelera" + /// ``` + String get exitTrash => """Salir de la papelera"""; + + /// ```dart + /// "Papelera" + /// ``` + String get trashTitle => """Papelera"""; + + /// ```dart + /// "La papelera está vacía." + /// ``` + String get noTrashedItems => """La papelera está vacía."""; + + /// ```dart + /// "Vaciar papelera" + /// ``` + String get emptyTrash => """Vaciar papelera"""; + + /// ```dart + /// "¿Vaciar la papelera?" + /// ``` + String get emptyTrashConfirm => """¿Vaciar la papelera?"""; + + /// ```dart + /// "Todos los artículos de la papelera se eliminarán permanentemente. Esta acción no se puede deshacer." + /// ``` + String get emptyTrashConfirmBody => + """Todos los artículos de la papelera se eliminarán permanentemente. Esta acción no se puede deshacer."""; + + /// ```dart + /// "No se pudo vaciar la papelera." + /// ``` + String get emptyTrashFailed => """No se pudo vaciar la papelera."""; + + /// ```dart + /// "Restaurar" + /// ``` + String get restoreItem => """Restaurar"""; + + /// ```dart + /// "Eliminar permanentemente" + /// ``` + String get permanentlyDeleteItem => """Eliminar permanentemente"""; + + /// ```dart + /// "¿Eliminar este artículo permanentemente?" + /// ``` + String get permanentlyDeleteConfirm => + """¿Eliminar este artículo permanentemente?"""; + + /// ```dart + /// "Esta acción no se puede deshacer." + /// ``` + String get permanentlyDeleteConfirmBody => + """Esta acción no se puede deshacer."""; + + /// ```dart + /// "No se pudo restaurar el artículo." + /// ``` + String get restoreFailed => """No se pudo restaurar el artículo."""; + + /// ```dart + /// "No se pudo eliminar el artículo." + /// ``` + String get permanentlyDeleteFailed => """No se pudo eliminar el artículo."""; + + /// ```dart + /// "Artículo restaurado" + /// ``` + String get itemRestored => """Artículo restaurado"""; + /// ```dart /// "Nueva lista" /// ``` @@ -1599,6 +1677,25 @@ Por favor, completa el inicio de sesión en tu navegador.""", """checklists.moveFailed""": """No se pudo mover el artículo.""", """checklists.itemMarkedDone""": """Artículo marcado como hecho""", """checklists.undo""": """Deshacer""", + """checklists.viewTrash""": """Ver papelera""", + """checklists.exitTrash""": """Salir de la papelera""", + """checklists.trashTitle""": """Papelera""", + """checklists.noTrashedItems""": """La papelera está vacía.""", + """checklists.emptyTrash""": """Vaciar papelera""", + """checklists.emptyTrashConfirm""": """¿Vaciar la papelera?""", + """checklists.emptyTrashConfirmBody""": + """Todos los artículos de la papelera se eliminarán permanentemente. Esta acción no se puede deshacer.""", + """checklists.emptyTrashFailed""": """No se pudo vaciar la papelera.""", + """checklists.restoreItem""": """Restaurar""", + """checklists.permanentlyDeleteItem""": """Eliminar permanentemente""", + """checklists.permanentlyDeleteConfirm""": + """¿Eliminar este artículo permanentemente?""", + """checklists.permanentlyDeleteConfirmBody""": + """Esta acción no se puede deshacer.""", + """checklists.restoreFailed""": """No se pudo restaurar el artículo.""", + """checklists.permanentlyDeleteFailed""": + """No se pudo eliminar el artículo.""", + """checklists.itemRestored""": """Artículo restaurado""", """checklists.createList""": """Nueva lista""", """checklists.listName""": """Nombre de la lista""", """checklists.listDescription""": """Descripción (opcional)""", diff --git a/lib/messages_es.i18n.yaml b/lib/messages_es.i18n.yaml index b525316..2c658ec 100644 --- a/lib/messages_es.i18n.yaml +++ b/lib/messages_es.i18n.yaml @@ -131,6 +131,21 @@ checklists: moveFailed: "No se pudo mover el artículo." itemMarkedDone: "Artículo marcado como hecho" undo: Deshacer + viewTrash: Ver papelera + exitTrash: Salir de la papelera + trashTitle: Papelera + noTrashedItems: La papelera está vacía. + emptyTrash: Vaciar papelera + emptyTrashConfirm: ¿Vaciar la papelera? + emptyTrashConfirmBody: "Todos los artículos de la papelera se eliminarán permanentemente. Esta acción no se puede deshacer." + emptyTrashFailed: No se pudo vaciar la papelera. + restoreItem: Restaurar + permanentlyDeleteItem: Eliminar permanentemente + permanentlyDeleteConfirm: "¿Eliminar este artículo permanentemente?" + permanentlyDeleteConfirmBody: Esta acción no se puede deshacer. + restoreFailed: No se pudo restaurar el artículo. + permanentlyDeleteFailed: No se pudo eliminar el artículo. + itemRestored: Artículo restaurado createList: Nueva lista listName: Nombre de la lista listDescription: "Descripción (opcional)" diff --git a/lib/messages_fr.i18n.dart b/lib/messages_fr.i18n.dart index 65aa4a4..d605859 100644 --- a/lib/messages_fr.i18n.dart +++ b/lib/messages_fr.i18n.dart @@ -724,6 +724,85 @@ class ChecklistsMessagesFr extends ChecklistsMessages { /// ``` String get undo => """Annuler"""; + /// ```dart + /// "Afficher la corbeille" + /// ``` + String get viewTrash => """Afficher la corbeille"""; + + /// ```dart + /// "Quitter la corbeille" + /// ``` + String get exitTrash => """Quitter la corbeille"""; + + /// ```dart + /// "Corbeille" + /// ``` + String get trashTitle => """Corbeille"""; + + /// ```dart + /// "La corbeille est vide." + /// ``` + String get noTrashedItems => """La corbeille est vide."""; + + /// ```dart + /// "Vider la corbeille" + /// ``` + String get emptyTrash => """Vider la corbeille"""; + + /// ```dart + /// "Vider la corbeille ?" + /// ``` + String get emptyTrashConfirm => """Vider la corbeille ?"""; + + /// ```dart + /// "Tous les articles de la corbeille seront supprimés définitivement. Cette action est irréversible." + /// ``` + String get emptyTrashConfirmBody => + """Tous les articles de la corbeille seront supprimés définitivement. Cette action est irréversible."""; + + /// ```dart + /// "Impossible de vider la corbeille." + /// ``` + String get emptyTrashFailed => """Impossible de vider la corbeille."""; + + /// ```dart + /// "Restaurer" + /// ``` + String get restoreItem => """Restaurer"""; + + /// ```dart + /// "Supprimer définitivement" + /// ``` + String get permanentlyDeleteItem => """Supprimer définitivement"""; + + /// ```dart + /// "Supprimer définitivement cet article ?" + /// ``` + String get permanentlyDeleteConfirm => + """Supprimer définitivement cet article ?"""; + + /// ```dart + /// "Cette action est irréversible." + /// ``` + String get permanentlyDeleteConfirmBody => + """Cette action est irréversible."""; + + /// ```dart + /// "Impossible de restaurer l'article." + /// ``` + String get restoreFailed => """Impossible de restaurer l'article."""; + + /// ```dart + /// "Impossible de supprimer l'article." + /// ``` + String get permanentlyDeleteFailed => + """Impossible de supprimer l'article."""; + + /// ```dart + /// "Article restauré" + /// ``` + String get itemRestored => """Article restauré"""; + /// ```dart /// "Nouvelle liste" /// ``` @@ -1604,6 +1683,25 @@ Veuillez terminer la connexion dans votre navigateur.""", """checklists.moveFailed""": """Impossible de déplacer l'article.""", """checklists.itemMarkedDone""": """Article marqué comme fait""", """checklists.undo""": """Annuler""", + """checklists.viewTrash""": """Afficher la corbeille""", + """checklists.exitTrash""": """Quitter la corbeille""", + """checklists.trashTitle""": """Corbeille""", + """checklists.noTrashedItems""": """La corbeille est vide.""", + """checklists.emptyTrash""": """Vider la corbeille""", + """checklists.emptyTrashConfirm""": """Vider la corbeille ?""", + """checklists.emptyTrashConfirmBody""": + """Tous les articles de la corbeille seront supprimés définitivement. Cette action est irréversible.""", + """checklists.emptyTrashFailed""": """Impossible de vider la corbeille.""", + """checklists.restoreItem""": """Restaurer""", + """checklists.permanentlyDeleteItem""": """Supprimer définitivement""", + """checklists.permanentlyDeleteConfirm""": + """Supprimer définitivement cet article ?""", + """checklists.permanentlyDeleteConfirmBody""": + """Cette action est irréversible.""", + """checklists.restoreFailed""": """Impossible de restaurer l'article.""", + """checklists.permanentlyDeleteFailed""": + """Impossible de supprimer l'article.""", + """checklists.itemRestored""": """Article restauré""", """checklists.createList""": """Nouvelle liste""", """checklists.listName""": """Nom de la liste""", """checklists.listDescription""": """Description (facultatif)""", diff --git a/lib/messages_fr.i18n.yaml b/lib/messages_fr.i18n.yaml index a72d3cc..b87368b 100644 --- a/lib/messages_fr.i18n.yaml +++ b/lib/messages_fr.i18n.yaml @@ -131,6 +131,21 @@ checklists: moveFailed: "Impossible de déplacer l'article." itemMarkedDone: "Article marqué comme fait" undo: Annuler + viewTrash: "Afficher la corbeille" + exitTrash: "Quitter la corbeille" + trashTitle: Corbeille + noTrashedItems: "La corbeille est vide." + emptyTrash: "Vider la corbeille" + emptyTrashConfirm: "Vider la corbeille ?" + emptyTrashConfirmBody: "Tous les articles de la corbeille seront supprimés définitivement. Cette action est irréversible." + emptyTrashFailed: "Impossible de vider la corbeille." + restoreItem: Restaurer + permanentlyDeleteItem: "Supprimer définitivement" + permanentlyDeleteConfirm: "Supprimer définitivement cet article ?" + permanentlyDeleteConfirmBody: "Cette action est irréversible." + restoreFailed: "Impossible de restaurer l'article." + permanentlyDeleteFailed: "Impossible de supprimer l'article." + itemRestored: "Article restauré" createList: Nouvelle liste listName: Nom de la liste listDescription: Description (facultatif) diff --git a/lib/messages_he.i18n.dart b/lib/messages_he.i18n.dart index bbf6036..ca00e8b 100644 --- a/lib/messages_he.i18n.dart +++ b/lib/messages_he.i18n.dart @@ -718,6 +718,82 @@ class ChecklistsMessagesHe extends ChecklistsMessages { /// ``` String get undo => """בטל"""; + /// ```dart + /// "הצג אשפה" + /// ``` + String get viewTrash => """הצג אשפה"""; + + /// ```dart + /// "צא מהאשפה" + /// ``` + String get exitTrash => """צא מהאשפה"""; + + /// ```dart + /// "אשפה" + /// ``` + String get trashTitle => """אשפה"""; + + /// ```dart + /// "האשפה ריקה." + /// ``` + String get noTrashedItems => """האשפה ריקה."""; + + /// ```dart + /// "רוקן אשפה" + /// ``` + String get emptyTrash => """רוקן אשפה"""; + + /// ```dart + /// "לרוקן את האשפה?" + /// ``` + String get emptyTrashConfirm => """לרוקן את האשפה?"""; + + /// ```dart + /// "כל הפריטים באשפה יימחקו לצמיתות. לא ניתן לבטל פעולה זו." + /// ``` + String get emptyTrashConfirmBody => + """כל הפריטים באשפה יימחקו לצמיתות. לא ניתן לבטל פעולה זו."""; + + /// ```dart + /// "ריקון האשפה נכשל." + /// ``` + String get emptyTrashFailed => """ריקון האשפה נכשל."""; + + /// ```dart + /// "שחזר" + /// ``` + String get restoreItem => """שחזר"""; + + /// ```dart + /// "מחק לצמיתות" + /// ``` + String get permanentlyDeleteItem => """מחק לצמיתות"""; + + /// ```dart + /// "למחוק את הפריט לצמיתות?" + /// ``` + String get permanentlyDeleteConfirm => """למחוק את הפריט לצמיתות?"""; + + /// ```dart + /// "לא ניתן לבטל פעולה זו." + /// ``` + String get permanentlyDeleteConfirmBody => """לא ניתן לבטל פעולה זו."""; + + /// ```dart + /// "שחזור הפריט נכשל." + /// ``` + String get restoreFailed => """שחזור הפריט נכשל."""; + + /// ```dart + /// "מחיקת הפריט נכשלה." + /// ``` + String get permanentlyDeleteFailed => """מחיקת הפריט נכשלה."""; + + /// ```dart + /// "הפריט שוחזר" + /// ``` + String get itemRestored => """הפריט שוחזר"""; + /// ```dart /// "רשימה חדשה" /// ``` @@ -1585,6 +1661,22 @@ Map get messagesHeMap => { """checklists.moveFailed""": """העברת הפריט נכשלה.""", """checklists.itemMarkedDone""": """הפריט סומן כהושלם""", """checklists.undo""": """בטל""", + """checklists.viewTrash""": """הצג אשפה""", + """checklists.exitTrash""": """צא מהאשפה""", + """checklists.trashTitle""": """אשפה""", + """checklists.noTrashedItems""": """האשפה ריקה.""", + """checklists.emptyTrash""": """רוקן אשפה""", + """checklists.emptyTrashConfirm""": """לרוקן את האשפה?""", + """checklists.emptyTrashConfirmBody""": + """כל הפריטים באשפה יימחקו לצמיתות. לא ניתן לבטל פעולה זו.""", + """checklists.emptyTrashFailed""": """ריקון האשפה נכשל.""", + """checklists.restoreItem""": """שחזר""", + """checklists.permanentlyDeleteItem""": """מחק לצמיתות""", + """checklists.permanentlyDeleteConfirm""": """למחוק את הפריט לצמיתות?""", + """checklists.permanentlyDeleteConfirmBody""": """לא ניתן לבטל פעולה זו.""", + """checklists.restoreFailed""": """שחזור הפריט נכשל.""", + """checklists.permanentlyDeleteFailed""": """מחיקת הפריט נכשלה.""", + """checklists.itemRestored""": """הפריט שוחזר""", """checklists.createList""": """רשימה חדשה""", """checklists.listName""": """שם הרשימה""", """checklists.listDescription""": """תיאור (אופציונלי)""", diff --git a/lib/messages_he.i18n.yaml b/lib/messages_he.i18n.yaml index 3b53677..6be1518 100644 --- a/lib/messages_he.i18n.yaml +++ b/lib/messages_he.i18n.yaml @@ -131,6 +131,21 @@ checklists: moveFailed: העברת הפריט נכשלה. itemMarkedDone: הפריט סומן כהושלם undo: בטל + viewTrash: הצג אשפה + exitTrash: צא מהאשפה + trashTitle: אשפה + noTrashedItems: האשפה ריקה. + emptyTrash: רוקן אשפה + emptyTrashConfirm: לרוקן את האשפה? + emptyTrashConfirmBody: כל הפריטים באשפה יימחקו לצמיתות. לא ניתן לבטל פעולה זו. + emptyTrashFailed: ריקון האשפה נכשל. + restoreItem: שחזר + permanentlyDeleteItem: מחק לצמיתות + permanentlyDeleteConfirm: למחוק את הפריט לצמיתות? + permanentlyDeleteConfirmBody: לא ניתן לבטל פעולה זו. + restoreFailed: שחזור הפריט נכשל. + permanentlyDeleteFailed: מחיקת הפריט נכשלה. + itemRestored: הפריט שוחזר createList: רשימה חדשה listName: שם הרשימה listDescription: תיאור (אופציונלי) diff --git a/lib/models/checklist.dart b/lib/models/checklist.dart index 7ac75fd..6fbb121 100644 --- a/lib/models/checklist.dart +++ b/lib/models/checklist.dart @@ -61,6 +61,7 @@ class ListItem { final int sortOrder; final int createdAt; final int updatedAt; + final int? deletedAt; const ListItem({ required this.id, @@ -81,6 +82,7 @@ class ListItem { required this.sortOrder, required this.createdAt, required this.updatedAt, + this.deletedAt, }); factory ListItem.fromJson(Map json) => ListItem( @@ -102,6 +104,7 @@ class ListItem { sortOrder: json['sortOrder'] as int, createdAt: json['createdAt'] as int, updatedAt: json['updatedAt'] as int, + deletedAt: json['deletedAt'] as int?, ); Map toJson() => { @@ -123,6 +126,7 @@ class ListItem { 'sortOrder': sortOrder, 'createdAt': createdAt, 'updatedAt': updatedAt, + 'deletedAt': deletedAt, }; ListItem copyWith({bool? done, int? doneAt, String? doneBy}) => ListItem( @@ -144,5 +148,6 @@ class ListItem { sortOrder: sortOrder, createdAt: createdAt, updatedAt: updatedAt, + deletedAt: deletedAt, ); } diff --git a/lib/services/checklist_service.dart b/lib/services/checklist_service.dart index b3ceb3f..3a88cc6 100644 --- a/lib/services/checklist_service.dart +++ b/lib/services/checklist_service.dart @@ -186,6 +186,44 @@ class ChecklistService { ); } + Future> getDeletedItems( + int houseId, + int listId, { + int limit = 200, + int offset = 0, + }) async { + return ApiClient.instance.get>( + '/houses/$houseId/lists/$listId/items/trash', + query: {'limit': limit.toString(), 'offset': offset.toString()}, + fromJson: (data) => data + .map((e) => ListItem.fromJson(e as Map)) + .toList(), + ); + } + + Future restoreItem(int houseId, int listId, int itemId) async { + return ApiClient.instance.post, ListItem>( + '/houses/$houseId/lists/$listId/items/$itemId/restore', + fromJson: (data) => ListItem.fromJson(data), + ); + } + + Future permanentlyDeleteItem( + int houseId, + int listId, + int itemId, + ) async { + await ApiClient.instance.delete( + '/houses/$houseId/lists/$listId/items/$itemId/permanent', + ); + } + + Future emptyTrash(int houseId, int listId) async { + await ApiClient.instance.delete( + '/houses/$houseId/lists/$listId/items/trash', + ); + } + Future toggleItem(int houseId, int listId, int itemId) async { return ApiClient.instance.post, ListItem>( '/houses/$houseId/lists/$listId/items/$itemId/toggle', diff --git a/lib/views/checklists/checklist_item_tile.dart b/lib/views/checklists/checklist_item_tile.dart index 4367fe0..eb0fdf2 100644 --- a/lib/views/checklists/checklist_item_tile.dart +++ b/lib/views/checklists/checklist_item_tile.dart @@ -16,22 +16,28 @@ class ChecklistItemTile extends StatelessWidget { final ListItem item; final models.Category? category; final int houseId; + final bool trashMode; final ValueChanged onToggle; final ValueChanged onView; final ValueChanged onEdit; final ValueChanged onMove; final ValueChanged onDelete; + final ValueChanged? onRestore; + final ValueChanged? onPermanentDelete; const ChecklistItemTile({ super.key, required this.item, this.category, required this.houseId, + this.trashMode = false, required this.onToggle, required this.onView, required this.onEdit, required this.onMove, required this.onDelete, + this.onRestore, + this.onPermanentDelete, }); @override @@ -50,12 +56,29 @@ class ChecklistItemTile extends StatelessWidget { itemBuilder: _menuItems, onSelected: (value) => _onMenuSelected(value), child: InkWell( - onTap: tapRowToToggle ? () => onToggle(item) : null, + onTap: trashMode + ? () => onView(item) + : (tapRowToToggle ? () => onToggle(item) : null), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), child: Row( children: [ - Checkbox(value: item.done, onChanged: (_) => onToggle(item)), + if (trashMode) + Padding( + padding: const EdgeInsetsDirectional.only( + start: 12, + end: 12, + ), + child: Icon( + Icons.delete_outline, + color: theme.colorScheme.onSurfaceVariant, + ), + ) + else + Checkbox( + value: item.done, + onChanged: (_) => onToggle(item), + ), if (item.imageFileId != null) ...[ GestureDetector( onTap: () => _showImagePreview(context), @@ -124,38 +147,64 @@ class ChecklistItemTile extends StatelessWidget { ); } - List> _menuItems() => [ - PopupMenuItem( - value: 'edit', - child: Row( - children: [ - const Icon(Icons.edit, size: 18), - const SizedBox(width: 8), - Text(m.checklists.editItem), - ], + List> _menuItems() { + if (trashMode) { + return [ + PopupMenuItem( + value: 'restore', + child: Row( + children: [ + const Icon(Icons.restore_from_trash, size: 18), + const SizedBox(width: 8), + Text(m.checklists.restoreItem), + ], + ), + ), + PopupMenuItem( + value: 'permanent', + child: Row( + children: [ + const Icon(Icons.delete_forever, size: 18), + const SizedBox(width: 8), + Text(m.checklists.permanentlyDeleteItem), + ], + ), + ), + ]; + } + return [ + PopupMenuItem( + value: 'edit', + child: Row( + children: [ + const Icon(Icons.edit, size: 18), + const SizedBox(width: 8), + Text(m.checklists.editItem), + ], + ), ), - ), - PopupMenuItem( - value: 'move', - child: Row( - children: [ - const Icon(Icons.drive_file_move_outlined, size: 18), - const SizedBox(width: 8), - Text(m.checklists.moveItem), - ], + PopupMenuItem( + value: 'move', + child: Row( + children: [ + const Icon(Icons.drive_file_move_outlined, size: 18), + const SizedBox(width: 8), + Text(m.checklists.moveItem), + ], + ), ), - ), - PopupMenuItem( - value: 'remove', - child: Row( - children: [ - const Icon(Icons.delete, size: 18), - const SizedBox(width: 8), - Text(m.checklists.removeItem), - ], + PopupMenuItem( + value: 'remove', + child: Row( + children: [ + const Icon(Icons.delete, size: 18), + const SizedBox(width: 8), + Text(m.checklists.removeItem), + ], + ), ), - ), - ]; + ]; + } void _onMenuSelected(String value) { switch (value) { @@ -165,6 +214,10 @@ class ChecklistItemTile extends StatelessWidget { onMove(item); case 'remove': onDelete(item); + case 'restore': + onRestore?.call(item); + case 'permanent': + onPermanentDelete?.call(item); } } diff --git a/lib/views/checklists/checklists_controller.dart b/lib/views/checklists/checklists_controller.dart index 172bf5c..861bb2d 100644 --- a/lib/views/checklists/checklists_controller.dart +++ b/lib/views/checklists/checklists_controller.dart @@ -41,6 +41,9 @@ class ChecklistsController extends ChangeNotifier { String _sortBy = 'custom'; String get sortBy => _sortBy; + bool _isTrashMode = false; + bool get isTrashMode => _isTrashMode; + bool _isLoading = true; bool get isLoading => _isLoading; @@ -142,6 +145,14 @@ class ChecklistsController extends ChangeNotifier { _currentList = list; _checklistService.selectedListId = list.id; + if (_isTrashMode) { + _items = []; + _isLoading = true; + notifyListeners(); + await _loadTrashItems(list); + return; + } + // Show cached items immediately, or spinner if no cache for this list final cached = _checklistService.getCachedItems(list.id); if (cached != null) { @@ -162,7 +173,7 @@ class ChecklistsController extends ChangeNotifier { sortBy: _sortBy, ); _checklistService.cacheItems(list.id, freshItems); - if (_currentList?.id == list.id) { + if (_currentList?.id == list.id && !_isTrashMode) { _items = freshItems; _isLoading = false; notifyListeners(); @@ -177,6 +188,38 @@ class ChecklistsController extends ChangeNotifier { } } + Future _loadTrashItems(ChecklistList list) async { + try { + final trashItems = await _checklistService.getDeletedItems( + houseId, + list.id, + ); + if (_currentList?.id == list.id && _isTrashMode) { + _items = trashItems; + _error = null; + _isLoading = false; + notifyListeners(); + } + } catch (e) { + debugPrint('[ChecklistsController] Failed to load trash: $e'); + if (_currentList?.id == list.id && _isTrashMode) { + _error = m.checklists.failedToLoadItems; + _isLoading = false; + notifyListeners(); + } + } + } + + Future setTrashMode(bool enabled) async { + if (_isTrashMode == enabled) return; + _isTrashMode = enabled; + if (_currentList != null) { + await selectList(_currentList!); + } else { + notifyListeners(); + } + } + Future setSortBy(String sort) async { if (sort == _sortBy) return; _sortBy = sort; @@ -389,6 +432,43 @@ class ChecklistsController extends ChangeNotifier { notifyListeners(); } + Future restoreItem(ListItem item) async { + final restored = await _checklistService.restoreItem( + houseId, + item.listId, + item.id, + ); + _items.removeWhere((i) => i.id == item.id); + if (!_isTrashMode) { + _items.add(restored); + _checklistService.cacheItems(_currentList!.id, List.of(_items)); + } + notifyListeners(); + return restored; + } + + Future permanentlyDeleteItem(ListItem item) async { + await _checklistService.permanentlyDeleteItem( + houseId, + item.listId, + item.id, + ); + _items.removeWhere((i) => i.id == item.id); + if (!_isTrashMode) { + _checklistService.cacheItems(_currentList!.id, List.of(_items)); + } + notifyListeners(); + } + + Future emptyTrash() async { + if (_currentList == null) return; + await _checklistService.emptyTrash(houseId, _currentList!.id); + if (_isTrashMode) { + _items = []; + notifyListeners(); + } + } + Future toggleItem(ListItem item) async { final index = _items.indexWhere((i) => i.id == item.id); if (index == -1) return; @@ -404,12 +484,23 @@ class ChecklistsController extends ChangeNotifier { item.listId, item.id, ); - _items[index] = updated; + // If toggling caused a soft-delete (deleteOnDone), drop it from active list. + if (updated.deletedAt != null) { + _items.removeWhere((i) => i.id == item.id); + } else { + final i = _items.indexWhere((x) => x.id == item.id); + if (i != -1) _items[i] = updated; + } _checklistService.cacheItems(item.listId, List.of(_items)); notifyListeners(); } catch (e) { // Revert on failure - _items[index] = item; + final i = _items.indexWhere((x) => x.id == item.id); + if (i != -1) { + _items[i] = item; + } else { + _items.insert(index.clamp(0, _items.length), item); + } _checklistService.cacheItems(item.listId, List.of(_items)); notifyListeners(); } diff --git a/lib/views/checklists/checklists_view.dart b/lib/views/checklists/checklists_view.dart index 63da8c7..9ef02ad 100644 --- a/lib/views/checklists/checklists_view.dart +++ b/lib/views/checklists/checklists_view.dart @@ -180,21 +180,36 @@ class _ChecklistsBodyState extends State<_ChecklistsBody> { onCreateNew: () => _createList(context, controller), ), ), - IconButton( - icon: Icon(_searchOpen ? Icons.search_off : Icons.search), - onPressed: _toggleSearch, - ), - ChecklistSortButton( - currentSort: controller.sortBy, - onSelected: controller.setSortBy, + if (!controller.isTrashMode) + IconButton( + icon: Icon(_searchOpen ? Icons.search_off : Icons.search), + onPressed: _toggleSearch, + ), + if (!controller.isTrashMode) + ChecklistSortButton( + currentSort: controller.sortBy, + onSelected: controller.setSortBy, + ), + PopupMenuButton( + icon: Icon( + controller.isTrashMode ? Icons.delete : Icons.more_vert, + ), + tooltip: controller.isTrashMode + ? m.checklists.trashTitle + : null, + onSelected: (value) => + _onListMenuSelected(context, controller, value), + itemBuilder: (_) => _listMenuItems(controller), ), ], ), + if (controller.isTrashMode && controller.currentList != null) + _TrashBanner(onExit: () => controller.setTrashMode(false)), AnimatedSize( duration: const Duration(milliseconds: 250), curve: Curves.easeInOut, alignment: Alignment.topCenter, - child: _searchOpen + child: (_searchOpen && !controller.isTrashMode) ? _SearchPanel( searchController: _searchController, selectedCategoryIds: _selectedCategoryIds, @@ -207,11 +222,12 @@ class _ChecklistsBodyState extends State<_ChecklistsBody> { Expanded(child: itemsArea), ], ), - if (controller.currentList != null) + if (controller.currentList != null && !controller.isTrashMode) PositionedDirectional( end: 16, bottom: 16, child: FloatingActionButton( + heroTag: 'checklists-fab', onPressed: () async { final added = await Navigator.of(context).push( MaterialPageRoute( @@ -238,6 +254,139 @@ class _ChecklistsBodyState extends State<_ChecklistsBody> { await controller.selectList(created); } } + + List> _listMenuItems(ChecklistsController controller) { + if (controller.isTrashMode) { + return [ + PopupMenuItem( + value: 'exit_trash', + child: Row( + children: [ + const Icon(Icons.arrow_back, size: 18), + const SizedBox(width: 8), + Text(m.checklists.exitTrash), + ], + ), + ), + PopupMenuItem( + value: 'empty_trash', + child: Row( + children: [ + const Icon(Icons.delete_forever, size: 18), + const SizedBox(width: 8), + Text(m.checklists.emptyTrash), + ], + ), + ), + ]; + } + return [ + PopupMenuItem( + value: 'view_trash', + child: Row( + children: [ + const Icon(Icons.delete_outline, size: 18), + const SizedBox(width: 8), + Text(m.checklists.viewTrash), + ], + ), + ), + ]; + } + + Future _onListMenuSelected( + BuildContext context, + ChecklistsController controller, + String value, + ) async { + switch (value) { + case 'view_trash': + if (_searchOpen) { + setState(() { + _searchOpen = false; + _searchController.clear(); + _selectedCategoryIds.clear(); + }); + } + await controller.setTrashMode(true); + case 'exit_trash': + await controller.setTrashMode(false); + case 'empty_trash': + await _confirmEmptyTrash(context, controller); + } + } + + Future _confirmEmptyTrash( + BuildContext context, + ChecklistsController controller, + ) async { + final confirmed = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: Text(m.checklists.emptyTrashConfirm), + content: Text(m.checklists.emptyTrashConfirmBody), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx, false), + child: Text(m.common.cancel), + ), + FilledButton( + onPressed: () => Navigator.pop(ctx, true), + child: Text(m.checklists.emptyTrash), + ), + ], + ), + ); + if (confirmed != true) return; + try { + await controller.emptyTrash(); + } catch (e) { + if (context.mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(m.checklists.emptyTrashFailed))); + } + } + } +} + +class _TrashBanner extends StatelessWidget { + final VoidCallback onExit; + + const _TrashBanner({required this.onExit}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return Container( + width: double.infinity, + color: theme.colorScheme.surfaceContainerHighest, + padding: const EdgeInsetsDirectional.fromSTEB(16, 8, 8, 8), + child: Row( + children: [ + Icon( + Icons.delete_outline, + size: 18, + color: theme.colorScheme.onSurfaceVariant, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + m.checklists.trashTitle, + style: theme.textTheme.labelLarge?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), + ), + ), + TextButton.icon( + onPressed: onExit, + icon: const Icon(Icons.close, size: 16), + label: Text(m.checklists.exitTrash), + ), + ], + ), + ); + } } class _SearchPanel extends StatelessWidget { @@ -453,22 +602,40 @@ class _ItemList extends StatelessWidget { @override Widget build(BuildContext context) { - final unchecked = items.where((i) => !i.done).toList(); - final checked = items.where((i) => i.done).toList(); - if (items.isEmpty) { return ListView( children: [ const SizedBox(height: 100), Center( child: Text( - isFiltering ? m.checklists.noSearchResults : m.checklists.noItems, + controller.isTrashMode + ? m.checklists.noTrashedItems + : (isFiltering + ? m.checklists.noSearchResults + : m.checklists.noItems), ), ), ], ); } + if (controller.isTrashMode) { + return CustomScrollView( + slivers: [ + _ReorderablePartition( + items: items, + controller: controller, + categorySpacing: 'disabled', + allowReorder: false, + ), + const SliverToBoxAdapter(child: SizedBox(height: 88)), + ], + ); + } + + final unchecked = items.where((i) => !i.done).toList(); + final checked = items.where((i) => i.done).toList(); + final spacingPref = context.watch().checklistCategorySpacing; final categorySpacing = controller.sortBy == 'category' ? spacingPref @@ -518,11 +685,13 @@ class _ReorderablePartition extends StatelessWidget { final List items; final ChecklistsController controller; final String categorySpacing; + final bool allowReorder; const _ReorderablePartition({ required this.items, required this.controller, this.categorySpacing = 'disabled', + this.allowReorder = true, }); void _toggleItem( @@ -531,6 +700,7 @@ class _ReorderablePartition extends StatelessWidget { ListItem item, ) { final wasDone = item.done; + final wasDeleteOnDone = item.deleteOnDone; controller.toggleItem(item); if (wasDone) return; @@ -541,7 +711,19 @@ class _ReorderablePartition extends StatelessWidget { content: Text(m.checklists.itemMarkedDone), action: SnackBarAction( label: m.checklists.undo, - onPressed: () { + onPressed: () async { + if (wasDeleteOnDone) { + try { + await controller.restoreItem(item); + } catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(m.checklists.restoreFailed)), + ); + } + } + return; + } final current = controller.items.firstWhere( (i) => i.id == item.id, orElse: () => item.copyWith(done: true), @@ -553,6 +735,62 @@ class _ReorderablePartition extends StatelessWidget { ); } + Future _restoreItem( + BuildContext context, + ChecklistsController controller, + ListItem item, + ) async { + try { + await controller.restoreItem(item); + if (context.mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(m.checklists.itemRestored))); + } + } catch (e) { + if (context.mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(m.checklists.restoreFailed))); + } + } + } + + void _permanentlyDelete( + BuildContext context, + ChecklistsController controller, + ListItem item, + ) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: Text(m.checklists.permanentlyDeleteConfirm), + content: Text(m.checklists.permanentlyDeleteConfirmBody), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx, false), + child: Text(m.common.cancel), + ), + FilledButton( + onPressed: () => Navigator.pop(ctx, true), + child: Text(m.common.delete), + ), + ], + ), + ).then((confirmed) async { + if (confirmed != true) return; + try { + await controller.permanentlyDeleteItem(item); + } catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(m.checklists.permanentlyDeleteFailed)), + ); + } + } + }); + } + void _viewItem( BuildContext context, ChecklistsController controller, @@ -692,8 +930,52 @@ class _ReorderablePartition extends StatelessWidget { }); } + Widget _tileFor(BuildContext context, int index) { + final item = items[index]; + final showSeparator = + categorySpacing != 'disabled' && + index > 0 && + items[index - 1].categoryId != item.categoryId; + final tile = ChecklistItemTile( + key: allowReorder ? null : ValueKey(item.id), + item: item, + category: item.categoryId != null + ? controller.categories[item.categoryId] + : null, + houseId: controller.houseId, + trashMode: controller.isTrashMode, + onToggle: (item) => _toggleItem(context, controller, item), + onView: (item) => _viewItem(context, controller, item), + onEdit: (item) => _editItem(context, controller, item), + onMove: (item) => _moveItem(context, controller, item), + onDelete: (item) => _deleteItem(context, controller, item), + onRestore: (item) => _restoreItem(context, controller, item), + onPermanentDelete: (item) => + _permanentlyDelete(context, controller, item), + ); + return showSeparator + ? Column( + children: [ + if (categorySpacing == 'divider') + const Divider(height: 25) + else + const SizedBox(height: 20), + tile, + ], + ) + : tile; + } + @override Widget build(BuildContext context) { + if (!allowReorder) { + return SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) => _tileFor(context, index), + childCount: items.length, + ), + ); + } return SliverReorderableList( itemCount: items.length, onReorder: (oldIndex, newIndex) { @@ -702,36 +984,10 @@ class _ReorderablePartition extends StatelessWidget { }, itemBuilder: (context, index) { final item = items[index]; - final showSeparator = - categorySpacing != 'disabled' && - index > 0 && - items[index - 1].categoryId != item.categoryId; - final tile = ChecklistItemTile( - item: item, - category: item.categoryId != null - ? controller.categories[item.categoryId] - : null, - houseId: controller.houseId, - onToggle: (item) => _toggleItem(context, controller, item), - onView: (item) => _viewItem(context, controller, item), - onEdit: (item) => _editItem(context, controller, item), - onMove: (item) => _moveItem(context, controller, item), - onDelete: (item) => _deleteItem(context, controller, item), - ); return ReorderableDelayedDragStartListener( key: ValueKey(item.id), index: index, - child: showSeparator - ? Column( - children: [ - if (categorySpacing == 'divider') - const Divider(height: 25) - else - const SizedBox(height: 20), - tile, - ], - ) - : tile, + child: _tileFor(context, index), ); }, ); diff --git a/lib/views/notes/notes_wall_view.dart b/lib/views/notes/notes_wall_view.dart index 2b8d690..7a77962 100644 --- a/lib/views/notes/notes_wall_view.dart +++ b/lib/views/notes/notes_wall_view.dart @@ -125,6 +125,7 @@ class _NotesWallBody extends StatelessWidget { end: 16, bottom: 16, child: FloatingActionButton( + heroTag: 'notes-fab', onPressed: () => _createNote(context, controller), child: const Icon(Icons.add), ),