mirror of
https://github.com/chenasraf/pantry-flutter.git
synced 2026-05-18 01:28:58 +00:00
feat: add trash view
This commit is contained in:
@@ -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)""",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)""",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)""",
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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)""",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<String, String> 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""": """תיאור (אופציונלי)""",
|
||||
|
||||
@@ -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: תיאור (אופציונלי)
|
||||
|
||||
@@ -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<String, dynamic> 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<String, dynamic> 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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -186,6 +186,44 @@ class ChecklistService {
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<ListItem>> getDeletedItems(
|
||||
int houseId,
|
||||
int listId, {
|
||||
int limit = 200,
|
||||
int offset = 0,
|
||||
}) async {
|
||||
return ApiClient.instance.get<List, List<ListItem>>(
|
||||
'/houses/$houseId/lists/$listId/items/trash',
|
||||
query: {'limit': limit.toString(), 'offset': offset.toString()},
|
||||
fromJson: (data) => data
|
||||
.map((e) => ListItem.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<ListItem> restoreItem(int houseId, int listId, int itemId) async {
|
||||
return ApiClient.instance.post<Map<String, dynamic>, ListItem>(
|
||||
'/houses/$houseId/lists/$listId/items/$itemId/restore',
|
||||
fromJson: (data) => ListItem.fromJson(data),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> permanentlyDeleteItem(
|
||||
int houseId,
|
||||
int listId,
|
||||
int itemId,
|
||||
) async {
|
||||
await ApiClient.instance.delete(
|
||||
'/houses/$houseId/lists/$listId/items/$itemId/permanent',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> emptyTrash(int houseId, int listId) async {
|
||||
await ApiClient.instance.delete(
|
||||
'/houses/$houseId/lists/$listId/items/trash',
|
||||
);
|
||||
}
|
||||
|
||||
Future<ListItem> toggleItem(int houseId, int listId, int itemId) async {
|
||||
return ApiClient.instance.post<Map<String, dynamic>, ListItem>(
|
||||
'/houses/$houseId/lists/$listId/items/$itemId/toggle',
|
||||
|
||||
@@ -16,22 +16,28 @@ class ChecklistItemTile extends StatelessWidget {
|
||||
final ListItem item;
|
||||
final models.Category? category;
|
||||
final int houseId;
|
||||
final bool trashMode;
|
||||
final ValueChanged<ListItem> onToggle;
|
||||
final ValueChanged<ListItem> onView;
|
||||
final ValueChanged<ListItem> onEdit;
|
||||
final ValueChanged<ListItem> onMove;
|
||||
final ValueChanged<ListItem> onDelete;
|
||||
final ValueChanged<ListItem>? onRestore;
|
||||
final ValueChanged<ListItem>? 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<PopupMenuEntry<String>> _menuItems() => [
|
||||
PopupMenuItem(
|
||||
value: 'edit',
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.edit, size: 18),
|
||||
const SizedBox(width: 8),
|
||||
Text(m.checklists.editItem),
|
||||
],
|
||||
List<PopupMenuEntry<String>> _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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<void> _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<void> setTrashMode(bool enabled) async {
|
||||
if (_isTrashMode == enabled) return;
|
||||
_isTrashMode = enabled;
|
||||
if (_currentList != null) {
|
||||
await selectList(_currentList!);
|
||||
} else {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setSortBy(String sort) async {
|
||||
if (sort == _sortBy) return;
|
||||
_sortBy = sort;
|
||||
@@ -389,6 +432,43 @@ class ChecklistsController extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<ListItem> 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<void> 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<void> emptyTrash() async {
|
||||
if (_currentList == null) return;
|
||||
await _checklistService.emptyTrash(houseId, _currentList!.id);
|
||||
if (_isTrashMode) {
|
||||
_items = [];
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> 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();
|
||||
}
|
||||
|
||||
@@ -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<String>(
|
||||
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<bool>(
|
||||
MaterialPageRoute(
|
||||
@@ -238,6 +254,139 @@ class _ChecklistsBodyState extends State<_ChecklistsBody> {
|
||||
await controller.selectList(created);
|
||||
}
|
||||
}
|
||||
|
||||
List<PopupMenuEntry<String>> _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<void> _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<void> _confirmEmptyTrash(
|
||||
BuildContext context,
|
||||
ChecklistsController controller,
|
||||
) async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
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<PrefsService>().checklistCategorySpacing;
|
||||
final categorySpacing = controller.sortBy == 'category'
|
||||
? spacingPref
|
||||
@@ -518,11 +685,13 @@ class _ReorderablePartition extends StatelessWidget {
|
||||
final List<ListItem> 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<void> _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<bool>(
|
||||
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),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user