diff --git a/mobile/lib/face/db.dart b/mobile/lib/face/db.dart index abe4e19227..7ff180efed 100644 --- a/mobile/lib/face/db.dart +++ b/mobile/lib/face/db.dart @@ -841,6 +841,18 @@ class FaceMLDataDB { await db.executeBatch(sql, parameterSets); } + Future removeNotPersonFeedback({ + required String personID, + required int clusterID, + }) async { + final db = await instance.asyncDB; + + const String sql = ''' + DELETE FROM $notPersonFeedback WHERE $personIdColumn = ? AND $clusterIDColumn = ? + '''; + await db.execute(sql, [personID, clusterID]); + } + Future removeClusterToPerson({ required String personID, required int clusterID, diff --git a/mobile/lib/ui/viewer/people/person_cluster_suggestion.dart b/mobile/lib/ui/viewer/people/person_cluster_suggestion.dart index f8ee2e1e0f..c196ceced4 100644 --- a/mobile/lib/ui/viewer/people/person_cluster_suggestion.dart +++ b/mobile/lib/ui/viewer/people/person_cluster_suggestion.dart @@ -10,14 +10,21 @@ import "package:photos/face/db.dart"; import "package:photos/face/model/person.dart"; import "package:photos/models/file/file.dart"; import 'package:photos/services/machine_learning/face_ml/feedback/cluster_feedback.dart'; +import "package:photos/services/machine_learning/face_ml/person/person_service.dart"; import "package:photos/theme/ente_theme.dart"; import "package:photos/ui/components/buttons/button_widget.dart"; import "package:photos/ui/components/models/button_type.dart"; -// import "package:photos/ui/viewer/people/add_person_action_sheet.dart"; import "package:photos/ui/viewer/people/cluster_page.dart"; import "package:photos/ui/viewer/people/person_clusters_page.dart"; import "package:photos/ui/viewer/search/result/person_face_widget.dart"; +class SuggestionUserFeedback { + final bool accepted; + final ClusterSuggestion suggestion; + + SuggestionUserFeedback(this.accepted, this.suggestion); +} + class PersonReviewClusterSuggestion extends StatefulWidget { final PersonEntity person; @@ -36,6 +43,8 @@ class _PersonClustersState extends State { Key futureBuilderKeySuggestions = UniqueKey(); Key futureBuilderKeyFaceThumbnails = UniqueKey(); bool canGiveFeedback = true; + List pastUserFeedback = []; + List allSuggestions = []; // Declare a variable for the future late Future> futureClusterSuggestions; @@ -61,6 +70,13 @@ class _PersonClustersState extends State { appBar: AppBar( title: const Text('Review suggestions'), actions: [ + if (pastUserFeedback.isNotEmpty) + IconButton( + icon: const Icon(Icons.undo_outlined), + onPressed: () async { + await _undoLastFeedback(); + }, + ), IconButton( icon: const Icon(Icons.history_outlined), onPressed: () { @@ -87,7 +103,7 @@ class _PersonClustersState extends State { ); } - final allSuggestions = snapshot.data!; + allSuggestions = snapshot.data!; final numberOfDifferentSuggestions = allSuggestions.length; final currentSuggestion = allSuggestions[currentSuggestionIndex]; final int clusterID = currentSuggestion.clusterIDToMerge; @@ -166,6 +182,13 @@ class _PersonClustersState extends State { if (!canGiveFeedback) { return; } + // Store the feedback in case the user wants to revert + pastUserFeedback.add( + SuggestionUserFeedback( + yesOrNo, + allSuggestions[currentSuggestionIndex], + ), + ); if (yesOrNo) { canGiveFeedback = false; await FaceMLDataDB.instance.assignClusterToPerson( @@ -448,4 +471,36 @@ class _PersonClustersState extends State { } return faceCrops; } + + Future _undoLastFeedback() async { + if (pastUserFeedback.isNotEmpty) { + final SuggestionUserFeedback lastFeedback = pastUserFeedback.removeLast(); + if (lastFeedback.accepted) { + await PersonService.instance.removeClusterToPerson( + personID: widget.person.remoteID, + clusterID: lastFeedback.suggestion.clusterIDToMerge, + ); + } else { + await FaceMLDataDB.instance.removeNotPersonFeedback( + personID: widget.person.remoteID, + clusterID: lastFeedback.suggestion.clusterIDToMerge, + ); + } + + // futureClusterSuggestions = + // pastUserFeedback.map((element) => element.suggestion) + // as Future>; + + fetch = false; + futureClusterSuggestions = futureClusterSuggestions.then((list) { + return list + .sublist(currentSuggestionIndex) + ..insert(0, lastFeedback.suggestion); + }); + currentSuggestionIndex = 0; + futureBuilderKeySuggestions = UniqueKey(); + futureBuilderKeyFaceThumbnails = UniqueKey(); + setState(() {}); + } + } }