[mob][photos] add hash check for people widget (#6341)

## Description

- [x] Add hash functionality to PeopleHomeWidget to track updation
- [x] Clear hash on albums widget clear

## Tests

- [x] Test if updating different people trigger it or not
This commit is contained in:
Prateek Sunal
2025-06-24 15:33:40 +05:30
committed by GitHub
7 changed files with 83 additions and 19 deletions

View File

@@ -75,7 +75,7 @@ class _EnteAppState extends State<EnteApp> with WidgetsBindingObserver {
(event) async {
_changeCallbackDebouncer.run(
() async =>
unawaited(PeopleHomeWidgetService.instance.peopleChanged()),
unawaited(PeopleHomeWidgetService.instance.checkPeopleChanged()),
);
},
);

View File

@@ -99,4 +99,19 @@ extension EntitiesDB on FilesDB {
}
return LocalEntityData.fromJson(maps.first);
}
Future<String?> getPreHashForEntities(
EntityType type,
List<String> ids,
) async {
final db = await sqliteAsyncDB;
final maps = await db.get(
'SELECT GROUP_CONCAT(id || \':\' || updatedAt, \',\') FROM entities WHERE type = ? AND id IN (${List.filled(ids.length, '?').join(',')})',
[type.name, ...ids],
);
if (maps.isEmpty) {
return null;
}
return maps.values.first as String?;
}
}

View File

@@ -110,6 +110,7 @@ class AlbumHomeWidgetService {
await _setTotalAlbums(null);
await updateAlbumsStatus(WidgetStatus.syncedEmpty);
_hasSyncedAlbums = false;
await setAlbumsLastHash("");
await _refreshWidget(message: "AlbumsHomeWidget cleared & updated");
}
@@ -183,7 +184,7 @@ class AlbumHomeWidgetService {
for (final albumId in albumIds) {
final collection = CollectionsService.instance.getCollectionByID(albumId);
if (collection != null) {
updationTimestamps += "${collection.updationTime.toString()}_";
updationTimestamps += "$albumId:${collection.updationTime.toString()}_";
}
}
@@ -386,6 +387,7 @@ class AlbumHomeWidgetService {
}
Future<void> _loadAndRenderAlbums() async {
final selectedAlbumIds = await _getEffectiveSelectedAlbumIds();
final albumsWithFiles = await _getAlbumsWithFiles();
if (albumsWithFiles.isEmpty) {
@@ -465,7 +467,6 @@ class AlbumHomeWidgetService {
}
// Update the hash to track changes
final selectedAlbumIds = await _getEffectiveSelectedAlbumIds();
final hash = _calculateHash(selectedAlbumIds);
await setAlbumsLastHash(hash);

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:math';
import "package:crypto/crypto.dart";
import "package:ente_crypto/ente_crypto.dart";
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
@@ -219,4 +220,22 @@ class EntityService {
rethrow;
}
}
Future<String?> getPreHashForEntities(
EntityType type,
List<String> ids,
) async {
return await _db.getPreHashForEntities(type, ids);
}
Future<String> getHashForIds(List<String> personIds) async {
final preHash = await getPreHashForEntities(EntityType.cgroup, personIds);
if (preHash == null) {
return "";
}
final hash = md5.convert(utf8.encode(preHash)).toString().substring(0, 10);
return hash;
}
}

View File

@@ -75,7 +75,10 @@ class PersonService {
final entities = await entityService.getEntities(EntityType.cgroup);
return entities
.map(
(e) => PersonEntity(e.id, PersonData.fromJson(json.decode(e.data))),
(e) => PersonEntity(
e.id,
PersonData.fromJson(json.decode(e.data)),
),
)
.toList();
}
@@ -85,7 +88,10 @@ class PersonService {
if (e == null) {
return null;
}
return PersonEntity(e.id, PersonData.fromJson(json.decode(e.data)));
return PersonEntity(
e.id,
PersonData.fromJson(json.decode(e.data)),
);
});
}
@@ -93,8 +99,10 @@ class PersonService {
final entities = await entityService.getEntities(EntityType.cgroup);
final Map<String, PersonEntity> map = {};
for (var e in entities) {
final person =
PersonEntity(e.id, PersonData.fromJson(json.decode(e.data)));
final person = PersonEntity(
e.id,
PersonData.fromJson(json.decode(e.data)),
);
map[person.remoteID] = person;
}
return map;

View File

@@ -23,6 +23,7 @@ import 'package:synchronized/synchronized.dart';
class PeopleHomeWidgetService {
// Constants
static const String SELECTED_PEOPLE_KEY = "selectedPeopleHW";
static const String PEOPLE_LAST_HASH_KEY = "peopleLastHash";
static const String ANDROID_CLASS_NAME = "EntePeopleWidgetProvider";
static const String IOS_CLASS_NAME = "EntePeopleWidget";
static const String PEOPLE_STATUS_KEY = "peopleStatusKey.widget";
@@ -39,6 +40,7 @@ class PeopleHomeWidgetService {
final Logger _logger = Logger((PeopleHomeWidgetService).toString());
late final SharedPreferences _prefs;
final _peopleForceRefreshLock = Lock();
final _lock2 = Lock();
bool _hasSyncedPeople = false;
// Initialization
@@ -69,6 +71,14 @@ class PeopleHomeWidgetService {
await updatePeopleChanged(true);
}
String? getPeopleLastHash() {
return _prefs.getString(PEOPLE_LAST_HASH_KEY);
}
Future<void> setPeopleLastHash(String hash) async {
await _prefs.setString(PEOPLE_LAST_HASH_KEY, hash);
}
Future<int> countHomeWidgets() async {
return await HomeWidgetService.instance.countHomeWidgets(
ANDROID_CLASS_NAME,
@@ -112,6 +122,7 @@ class PeopleHomeWidgetService {
await _setTotalPeople(null);
_hasSyncedPeople = false;
await updatePeopleStatus(WidgetStatus.syncedEmpty);
await setPeopleLastHash("");
await _refreshWidget(message: "PeopleHomeWidget cleared & updated");
}
@@ -147,20 +158,22 @@ class PeopleHomeWidgetService {
await HomeWidgetService.instance.initHomeWidget();
}
Future<void> peopleChanged() async {
await updatePeopleChanged(true);
Future<void> checkPeopleChanged() async {
final havePeopleChanged = await _lock2.synchronized(() async {
final peopleIds = await _getEffectiveSelectedPeopleIds();
final currentHash = await _calculateHash(peopleIds);
final lastHash = getPeopleLastHash();
if (peopleIds.isEmpty || currentHash == lastHash) {
return false;
}
return true;
});
final cachedMemories = await _getPeople();
final currentTotal = cachedMemories.length;
final existingTotal = await _getTotalPeople() ?? 0;
if (existingTotal == currentTotal && existingTotal == 0) {
await updatePeopleChanged(false);
_logger.info("People empty, no update needed");
await updatePeopleChanged(havePeopleChanged);
if (!havePeopleChanged) {
_logger.info("No changes detected in people, skipping update");
return;
}
_logger.info("People changed, updating widget");
await initHomeWidget(true);
}
@@ -219,6 +232,10 @@ class PeopleHomeWidgetService {
await updatePeopleChanged(false);
}
Future<String> _calculateHash(List<String> peopleIds) async {
return await entityService.getHashForIds(peopleIds);
}
Future<bool> _hasAnyBlockers() async {
// Check if first import is completed
final hasCompletedFirstImport =
@@ -363,6 +380,7 @@ class PeopleHomeWidgetService {
}
Future<void> _loadAndRenderPeople() async {
final peopleIds = await _getEffectiveSelectedPeopleIds();
final peopleWithFiles = await _getPeople();
if (peopleWithFiles.isEmpty) {
@@ -448,6 +466,9 @@ class PeopleHomeWidgetService {
await updatePeopleStatus(WidgetStatus.syncedAll);
}
final hash = await _calculateHash(peopleIds);
await setPeopleLastHash(hash);
await _refreshWidget(
message: "Switched to next people set, total: $renderedCount",
);

View File

@@ -72,7 +72,7 @@ class _PeopleWidgetSettingsState extends State<PeopleWidgetSettings> {
);
Navigator.pop(context);
await PeopleHomeWidgetService.instance
.peopleChanged();
.checkPeopleChanged();
},
);
},