[mob][photos] Write a function that returns relevant emails, which will then be used as the base list to further filter suggestions in the 'Link Email' screen and the 'Add Participants to Album' screen.

The number of emails suggested in the 'Add Participants to Album' screen will now be higher than before, yet still useful.
This commit is contained in:
ashilkn 2025-01-31 17:38:18 +05:30
parent e0b9b27537
commit eed50f753b
4 changed files with 115 additions and 104 deletions

View File

@ -18,6 +18,7 @@ import 'package:photos/events/user_details_changed_event.dart';
import "package:photos/generated/l10n.dart";
import "package:photos/l10n/l10n.dart";
import "package:photos/models/account/two_factor.dart";
import "package:photos/models/api/collection/user.dart";
import "package:photos/models/api/user/srp.dart";
import 'package:photos/models/delete_account.dart';
import 'package:photos/models/key_attributes.dart';
@ -26,6 +27,8 @@ import 'package:photos/models/sessions.dart';
import 'package:photos/models/set_keys_request.dart';
import 'package:photos/models/set_recovery_key_request.dart';
import 'package:photos/models/user_details.dart';
import "package:photos/services/collections_service.dart";
import "package:photos/services/machine_learning/face_ml/person/person_service.dart";
import 'package:photos/ui/account/login_page.dart';
import 'package:photos/ui/account/ott_verification_page.dart';
import "package:photos/ui/account/passkey_page.dart";
@ -1284,4 +1287,85 @@ class UserService {
rethrow;
}
}
/// Returns Contacts(Users) that are relevant to the account owner.
/// Note: "User" refers to the account owner in the points below.
/// This includes:
/// - Collaborators and viewers of collections owned by user
/// - Owners of collections shared to user.
/// - All collaborators of collections in which user is a collaborator or
/// a viewer.
/// - All family members of user.
/// - All contacts linked to a person.
List<User> getRelevantContacts() {
final List<User> relevantUsers = [];
final existingEmails = <String>{};
final int ownerID = Configuration.instance.getUserID()!;
final String ownerEmail = Configuration.instance.getEmail()!;
for (final c in CollectionsService.instance.getActiveCollections()) {
// Add collaborators and viewers of collections owned by user
if (c.owner?.id == ownerID) {
for (final User? u in c.sharees ?? []) {
if (u != null && u.id != null && u.email.isNotEmpty) {
if (!existingEmails.contains(u.email)) {
relevantUsers.add(u);
existingEmails.add(u.email);
}
}
}
} else if (c.owner?.id != null && c.owner!.email.isNotEmpty) {
// Add owners of collections shared with user
if (!existingEmails.contains(c.owner!.email)) {
relevantUsers.add(c.owner!);
existingEmails.add(c.owner!.email);
}
// Add collaborators of collections shared with user where user is a
// viewer or a collaborator
for (final User? u in c.sharees ?? []) {
if (u != null &&
u.id != null &&
u.email.isNotEmpty &&
u.email == ownerEmail &&
(u.isCollaborator || u.isViewer)) {
for (final User? u in c.sharees ?? []) {
if (u != null &&
u.id != null &&
u.email.isNotEmpty &&
u.isCollaborator) {
if (!existingEmails.contains(u.email)) {
relevantUsers.add(u);
existingEmails.add(u.email);
}
}
}
break;
}
}
}
}
// Add user's family members
final cachedUserDetails = getCachedUserDetails();
if (cachedUserDetails?.familyData?.members?.isNotEmpty ?? false) {
for (final member in cachedUserDetails!.familyData!.members!) {
if (!existingEmails.contains(member.email)) {
relevantUsers.add(User(email: member.email));
existingEmails.add(member.email);
}
}
}
// Add contacts linked to people
final cachedEmailToPartialPersonData =
PersonService.instance.emailToPartialPersonDataMapCache;
for (final email in cachedEmailToPartialPersonData.keys) {
if (!existingEmails.contains(email)) {
relevantUsers.add(User(email: email));
existingEmails.add(email);
}
}
return relevantUsers;
}
}

View File

@ -60,9 +60,10 @@ class _AddParticipantPage extends State<AddParticipantPage> {
Widget build(BuildContext context) {
final filterSuggestedUsers = _suggestedUsers
.where(
(element) => element.email.toLowerCase().contains(
_textController.text.trim().toLowerCase(),
),
(element) =>
(element.displayName ?? element.email).toLowerCase().contains(
_textController.text.trim().toLowerCase(),
),
)
.toList();
isKeypadOpen = MediaQuery.viewInsetsOf(context).bottom > 100;
@ -359,47 +360,22 @@ class _AddParticipantPage extends State<AddParticipantPage> {
}
List<User> _getSuggestedUser() {
final List<User> suggestedUsers = [];
final Set<String> existingEmails = {};
final int ownerID = Configuration.instance.getUserID()!;
existingEmails.add(Configuration.instance.getEmail()!);
for (final User? u in widget.collection.sharees ?? []) {
if (u != null && u.id != null && u.email.isNotEmpty) {
existingEmails.add(u.email);
}
}
for (final c in CollectionsService.instance.getActiveCollections()) {
if (c.owner?.id == ownerID) {
for (final User? u in c.sharees ?? []) {
if (u != null &&
u.id != null &&
u.email.isNotEmpty &&
!existingEmails.contains(u.email)) {
existingEmails.add(u.email);
suggestedUsers.add(u);
}
}
} else if (c.owner != null &&
c.owner!.id != null &&
c.owner!.email.isNotEmpty &&
!existingEmails.contains(c.owner!.email)) {
existingEmails.add(c.owner!.email);
suggestedUsers.add(c.owner!);
}
}
final cachedUserDetails = UserService.instance.getCachedUserDetails();
if (cachedUserDetails != null &&
(cachedUserDetails.familyData?.members?.isNotEmpty ?? false)) {
for (final member in cachedUserDetails.familyData!.members!) {
if (!existingEmails.contains(member.email)) {
existingEmails.add(member.email);
suggestedUsers.add(User(email: member.email));
}
}
}
final List<User> suggestedUsers = UserService.instance.getRelevantContacts()
..removeWhere(
(element) => existingEmails.contains(element.email),
);
if (_textController.text.trim().isNotEmpty) {
suggestedUsers.removeWhere(
(element) => !element.email
(element) => !(element.displayName ?? element.email)
.toLowerCase()
.contains(_textController.text.trim().toLowerCase()),
);

View File

@ -3,13 +3,11 @@ import "dart:async";
import "package:email_validator/email_validator.dart";
import 'package:flutter/material.dart';
import "package:logging/logging.dart";
import 'package:photos/core/configuration.dart';
import "package:photos/core/event_bus.dart";
import "package:photos/events/people_changed_event.dart";
import "package:photos/generated/l10n.dart";
import "package:photos/models/api/collection/user.dart";
import "package:photos/models/ml/face/person.dart";
import 'package:photos/services/collections_service.dart';
import "package:photos/services/machine_learning/face_ml/person/person_service.dart";
import "package:photos/services/user_service.dart";
import 'package:photos/theme/ente_theme.dart';
@ -245,41 +243,18 @@ class _LinkEmailScreen extends State<LinkEmailScreen> {
}
List<User> _getContacts() {
final List<User> suggestedUsers = [];
final int ownerID = Configuration.instance.getUserID()!;
final cachedEmailToPartialPersonDataMap =
PersonService.instance.emailToPartialPersonDataMapCache;
final usersEmailsToAviod =
PersonService.instance.emailToPartialPersonDataMapCache.keys.toSet();
final relevantUsers = UserService.instance.getRelevantContacts()
..removeWhere(
(user) => usersEmailsToAviod.contains(user.email),
);
for (final c in CollectionsService.instance.getActiveCollections()) {
if (c.owner?.id == ownerID) {
for (final User? u in c.sharees ?? []) {
if (u != null && u.id != null && u.email.isNotEmpty) {
if (!suggestedUsers.any((user) => user.email == u.email) &&
cachedEmailToPartialPersonDataMap[u.email] == null) {
suggestedUsers.add(u);
}
}
}
} else if (c.owner?.id != null && c.owner!.email.isNotEmpty) {
if (!suggestedUsers.any((user) => user.email == c.owner!.email) &&
cachedEmailToPartialPersonDataMap[c.owner!.email] == null) {
suggestedUsers.add(c.owner!);
}
}
}
final cachedUserDetails = UserService.instance.getCachedUserDetails();
if (cachedUserDetails?.familyData?.members?.isNotEmpty ?? false) {
for (final member in cachedUserDetails!.familyData!.members!) {
if (!suggestedUsers.any((user) => user.email == member.email) &&
cachedEmailToPartialPersonDataMap[member.email] == null) {
suggestedUsers.add(User(email: member.email));
}
}
}
relevantUsers.sort(
(a, b) => (a.email).compareTo(b.email),
);
suggestedUsers.sort((a, b) => a.email.compareTo(b.email));
return suggestedUsers;
return relevantUsers;
}
Future<bool> _emailHoldsEnteAccount(String email) async {

View File

@ -19,7 +19,6 @@ import "package:photos/l10n/l10n.dart";
import "package:photos/models/api/collection/user.dart";
import "package:photos/models/file/file.dart";
import "package:photos/models/ml/face/person.dart";
import "package:photos/services/collections_service.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/services/machine_learning/ml_result.dart";
@ -986,40 +985,17 @@ class _EmailSectionState extends State<_EmailSection> {
}
List<User> _getContacts() {
final List<User> suggestedUsers = [];
final int ownerID = Configuration.instance.getUserID()!;
final cachedEmailToPartialPersonDataMap =
PersonService.instance.emailToPartialPersonDataMapCache;
final usersEmailsToAviod =
PersonService.instance.emailToPartialPersonDataMapCache.keys;
final relevantUsers = UserService.instance.getRelevantContacts()
..removeWhere(
(user) => usersEmailsToAviod.contains(user.email),
);
for (final c in CollectionsService.instance.getActiveCollections()) {
if (c.owner?.id == ownerID) {
for (final User? u in c.sharees ?? []) {
if (u != null && u.id != null && u.email.isNotEmpty) {
if (!suggestedUsers.any((user) => user.email == u.email) &&
cachedEmailToPartialPersonDataMap[u.email] == null) {
suggestedUsers.add(u);
}
}
}
} else if (c.owner?.id != null && c.owner!.email.isNotEmpty) {
if (!suggestedUsers.any((user) => user.email == c.owner!.email) &&
cachedEmailToPartialPersonDataMap[c.owner!.email] == null) {
suggestedUsers.add(c.owner!);
}
}
}
final cachedUserDetails = UserService.instance.getCachedUserDetails();
if (cachedUserDetails?.familyData?.members?.isNotEmpty ?? false) {
for (final member in cachedUserDetails!.familyData!.members!) {
if (!suggestedUsers.any((user) => user.email == member.email) &&
cachedEmailToPartialPersonDataMap[member.email] == null) {
suggestedUsers.add(User(email: member.email));
}
}
}
relevantUsers.sort(
(a, b) => (a.email).compareTo(b.email),
);
suggestedUsers.sort((a, b) => a.email.compareTo(b.email));
return suggestedUsers;
return relevantUsers;
}
}