diff --git a/mobile/lib/ui/viewer/people/link_email_screen.dart b/mobile/lib/ui/viewer/people/link_email_screen.dart index 5f08f64dc2..1645dc15f1 100644 --- a/mobile/lib/ui/viewer/people/link_email_screen.dart +++ b/mobile/lib/ui/viewer/people/link_email_screen.dart @@ -24,6 +24,7 @@ import 'package:photos/ui/components/models/button_type.dart'; import "package:photos/ui/components/text_input_widget.dart"; import 'package:photos/ui/sharing/user_avator_widget.dart'; import "package:photos/utils/dialog_util.dart"; +import "package:photos/utils/person_contact_linking_util.dart"; import "package:photos/utils/share_util.dart"; class LinkEmailScreen extends StatefulWidget { @@ -214,17 +215,21 @@ class _LinkEmailScreen extends State { } }); } else { - final result = await linkEmailToPerson( - newEmail, - widget.personID!, - context, - ); - if (!result) { - _textController.clear(); - return; - } + try { + final result = await linkEmailToPerson( + newEmail, + widget.personID!, + context, + ); + if (!result) { + _textController.clear(); + return; + } - Navigator.of(context).pop(newEmail); + Navigator.of(context).pop(newEmail); + } catch (e) { + _logger.severe("Failed to link email to person", e); + } } }, ), @@ -325,6 +330,10 @@ class _LinkEmailScreen extends State { String personID, BuildContext context, ) async { + if (await checkIfEmailAlreadyAssignedToAPerson(context, email)) { + throw Exception("Email already linked to a person"); + } + String? publicKey; try { diff --git a/mobile/lib/ui/viewer/people/person_selection_action_widgets.dart b/mobile/lib/ui/viewer/people/person_selection_action_widgets.dart index 86c50715e0..c626529447 100644 --- a/mobile/lib/ui/viewer/people/person_selection_action_widgets.dart +++ b/mobile/lib/ui/viewer/people/person_selection_action_widgets.dart @@ -17,6 +17,7 @@ import "package:photos/ui/components/dialog_widget.dart"; import "package:photos/ui/components/models/button_type.dart"; import "package:photos/ui/viewer/search/result/person_face_widget.dart"; import "package:photos/utils/dialog_util.dart"; +import "package:photos/utils/person_contact_linking_util.dart"; import "package:photos/utils/toast_util.dart"; class PersonEntityWithThumbnailFile { @@ -45,6 +46,7 @@ class _LinkContactToPersonSelectionPageState extends State { late Future> _personEntitiesWithThumnailFile; + final _logger = Logger('LinkContactToPersonSelectionPage'); @override void initState() { @@ -115,15 +117,21 @@ class _LinkContactToPersonSelectionPageState itemBuilder: (context, index) { return _RoundedPersonFaceWidget( onTap: () async { - await linkPersonToContact( - context, - emailToLink: widget.emailToLink!, - personEntity: results[index].person, - ).then((updatedPerson) { - if (updatedPerson != null) { - Navigator.of(context).pop(updatedPerson); - } - }); + try { + unawaited( + linkPersonToContact( + context, + emailToLink: widget.emailToLink!, + personEntity: results[index].person, + ).then((updatedPerson) { + if (updatedPerson != null) { + Navigator.of(context).pop(updatedPerson); + } + }), + ); + } catch (e) { + _logger.severe("Failed to link person to contact", e); + } }, itemSize: itemSize, personEntitiesWithThumbnailFile: results[index], @@ -141,6 +149,10 @@ class _LinkContactToPersonSelectionPageState required String emailToLink, required PersonEntity personEntity, }) async { + if (await checkIfEmailAlreadyAssignedToAPerson(context, emailToLink)) { + throw Exception("Email already linked"); + } + final personName = personEntity.data.name; PersonEntity? updatedPerson; final result = await showDialogWidget( diff --git a/mobile/lib/ui/viewer/people/save_or_edit_person.dart b/mobile/lib/ui/viewer/people/save_or_edit_person.dart index 3eea94a378..515ddb92cb 100644 --- a/mobile/lib/ui/viewer/people/save_or_edit_person.dart +++ b/mobile/lib/ui/viewer/people/save_or_edit_person.dart @@ -31,6 +31,7 @@ import "package:photos/ui/viewer/people/person_row_item.dart"; import "package:photos/ui/viewer/search/result/person_face_widget.dart"; import "package:photos/utils/dialog_util.dart"; import "package:photos/utils/navigation_util.dart"; +import "package:photos/utils/person_contact_linking_util.dart"; import "package:photos/utils/toast_util.dart"; class SaveOrEditPerson extends StatefulWidget { @@ -379,15 +380,23 @@ class _SaveOrEditPersonState extends State { shouldStickToDarkTheme: true, onTap: () async { if (widget.isEditing) { - updatedPersonEntity = await updatePerson(context); + try { + updatedPersonEntity = await updatePerson(context); + } catch (e) { + _logger.severe("Error updating person", e); + } } else { - updatedPersonEntity = await addNewPerson( - context, - text: _inputName, - clusterID: widget.clusterID!, - birthdate: _selectedDate, - email: _email, - ); + try { + updatedPersonEntity = await addNewPerson( + context, + text: _inputName, + clusterID: widget.clusterID!, + birthdate: _selectedDate, + email: _email, + ); + } catch (e) { + _logger.severe("Error updating person", e); + } } }, ), @@ -530,6 +539,11 @@ class _SaveOrEditPersonState extends State { String? birthdate, String? email, }) async { + if (email != null && + email.isNotEmpty && + await checkIfEmailAlreadyAssignedToAPerson(context, email)) { + throw Exception("Email already assigned to a person"); + } try { if (userAlreadyAssigned) { return null; @@ -576,6 +590,12 @@ class _SaveOrEditPersonState extends State { Future updatePerson(BuildContext context) async { try { + if (_email != null && + _email!.isNotEmpty && + _email != person!.data.email && + await checkIfEmailAlreadyAssignedToAPerson(context, _email!)) { + throw Exception("Email already assigned to a person"); + } final String name = _inputName.trim(); final String? birthDate = _selectedDate; final personEntity = await PersonService.instance.updateAttributes( diff --git a/mobile/lib/utils/person_contact_linking_util.dart b/mobile/lib/utils/person_contact_linking_util.dart new file mode 100644 index 0000000000..0c7b6542a5 --- /dev/null +++ b/mobile/lib/utils/person_contact_linking_util.dart @@ -0,0 +1,25 @@ +import "dart:io"; + +import "package:flutter/material.dart"; +import "package:photos/services/machine_learning/face_ml/person/person_service.dart"; +import "package:photos/utils/dialog_util.dart"; + +Future checkIfEmailAlreadyAssignedToAPerson( + BuildContext context, + String email, +) async { + final persons = await PersonService.instance.getPersons(); + for (var person in persons) { + if (person.data.email == email) { + await showErrorDialog( + context, + "Email already linked", + "This email is already linked to a person", + useRootNavigator: Platform.isIOS, + ); + + return true; + } + } + return false; +}