diff --git a/mobile/lib/events/magic_cache_updated_event.dart b/mobile/lib/events/magic_cache_updated_event.dart new file mode 100644 index 0000000000..028c0ba59c --- /dev/null +++ b/mobile/lib/events/magic_cache_updated_event.dart @@ -0,0 +1,3 @@ +import "package:photos/events/event.dart"; + +class MagicCacheUpdatedEvent extends Event {} diff --git a/mobile/lib/models/search/search_types.dart b/mobile/lib/models/search/search_types.dart index 0674a28aac..1ea0b3a7af 100644 --- a/mobile/lib/models/search/search_types.dart +++ b/mobile/lib/models/search/search_types.dart @@ -6,6 +6,7 @@ import "package:photos/core/event_bus.dart"; import "package:photos/events/collection_updated_event.dart"; import "package:photos/events/event.dart"; import "package:photos/events/location_tag_updated_event.dart"; +import "package:photos/events/magic_cache_updated_event.dart"; import "package:photos/events/people_changed_event.dart"; import "package:photos/generated/l10n.dart"; import "package:photos/models/collection/collection.dart"; @@ -291,6 +292,8 @@ extension SectionTypeExtensions on SectionType { switch (this) { case SectionType.location: return [Bus.instance.on()]; + case SectionType.magic: + return [Bus.instance.on()]; default: return []; } diff --git a/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart b/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart index 8eb60cc211..8885829743 100644 --- a/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart +++ b/mobile/lib/services/machine_learning/semantic_search/semantic_search_service.dart @@ -178,7 +178,7 @@ class SemanticSearchService { return results; } - Future> getMatchingFileIDs( + Future> getMatchingFileIDs( String query, double minimumSimilarity, ) async { @@ -187,7 +187,7 @@ class SemanticSearchService { textEmbedding, minimumSimilarity: minimumSimilarity, ); - final result = {}; + final result = []; for (final r in queryResults) { result.add(r.id); } diff --git a/mobile/lib/services/magic_cache_service.dart b/mobile/lib/services/magic_cache_service.dart index 3f7254d453..7766d8e6e9 100644 --- a/mobile/lib/services/magic_cache_service.dart +++ b/mobile/lib/services/magic_cache_service.dart @@ -8,6 +8,7 @@ import "package:logging/logging.dart"; import "package:path_provider/path_provider.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/events/file_uploaded_event.dart"; +import "package:photos/events/magic_cache_updated_event.dart"; import "package:photos/extensions/stop_watch.dart"; import "package:photos/models/file/extensions/file_props.dart"; import "package:photos/models/file/file.dart"; @@ -24,13 +25,26 @@ import "package:shared_preferences/shared_preferences.dart"; class MagicCache { final String title; - final Set fileUploadedIDs; + final List fileUploadedIDs; + Map? _fileIdToPositionMap; + MagicCache(this.title, this.fileUploadedIDs); + // Get map of uploadID to index in fileUploadedIDs + Map get fileIdToPositionMap { + if (_fileIdToPositionMap == null) { + _fileIdToPositionMap = {}; + for (int i = 0; i < fileUploadedIDs.length; i++) { + _fileIdToPositionMap![fileUploadedIDs[i]] = i; + } + } + return _fileIdToPositionMap!; + } + factory MagicCache.fromJson(Map json) { return MagicCache( json['title'], - Set.from(json['fileUploadedIDs']), + List.from(json['fileUploadedIDs']), ); } Map toJson() { @@ -54,14 +68,25 @@ class MagicCache { GenericSearchResult? toGenericSearchResult( Prompt prompt, List enteFilesInMagicCache, + Map fileIdToPositionMap, ) { if (enteFilesInMagicCache.isEmpty) { return null; } + if (!prompt.recentFirst) { + enteFilesInMagicCache.sort((a, b) { + return fileIdToPositionMap[a.uploadedFileID!]! + .compareTo(fileIdToPositionMap[b.uploadedFileID!]!); + }); + } return GenericSearchResult( ResultType.magic, prompt.title, enteFilesInMagicCache, + params: { + "enableGrouping": prompt.recentFirst, + "fileIdToPosMap": fileIdToPositionMap + }, onResultTap: (ctx) { routeToPage( ctx, @@ -69,6 +94,7 @@ GenericSearchResult? toGenericSearchResult( enteFilesInMagicCache, name: prompt.title, enableGrouping: prompt.recentFirst, + fileIdToPosMap: fileIdToPositionMap, heroTag: GenericSearchResult( ResultType.magic, prompt.title, @@ -122,8 +148,7 @@ class MagicCacheService { return _prefs.getInt(_lastMagicCacheUpdateTime) ?? 0; } - bool get enableDiscover => - localSettings.isMLIndexingEnabled && flagService.internalUser; + bool get enableDiscover => localSettings.isMLIndexingEnabled; Future _updateCacheIfTheTimeHasCome() async { if (!enableDiscover) { @@ -133,13 +158,11 @@ class MagicCacheService { .getAssetIfUpdated(_kMagicPromptsDataUrl); if (updatedJSONFile != null) { _pendingUpdateReason.add("Prompts data updated"); - } else { - if (lastMagicCacheUpdateTime < - DateTime.now() - .subtract(const Duration(days: 3)) - .millisecondsSinceEpoch) { - _pendingUpdateReason.add("Cache is old"); - } + } else if (lastMagicCacheUpdateTime < + DateTime.now() + .subtract(const Duration(days: 1)) + .millisecondsSinceEpoch) { + _pendingUpdateReason.add("Cache is old"); } } @@ -147,10 +170,13 @@ class MagicCacheService { return (await getApplicationSupportDirectory()).path + "/cache/magic_cache"; } - Future updateCache() async { + Future updateCache({bool forced = false}) async { if (!enableDiscover) { return; } + if (forced) { + _pendingUpdateReason.add("Forced update"); + } try { if (_pendingUpdateReason.isEmpty || _isUpdateInProgress) { _logger.info( @@ -178,10 +204,12 @@ class MagicCacheService { w?.log("cacheWritten"); await _resetLastMagicCacheUpdateTime(); w?.logAndReset('done'); + Bus.instance.fire(MagicCacheUpdatedEvent()); } catch (e, s) { _logger.info("Error updating magic cache", e, s); } finally { _isUpdateInProgress = false; + Bus.instance.fire(MagicCacheUpdatedEvent()); } } @@ -241,9 +269,12 @@ class MagicCacheService { w?.log("cacheFound"); } final Map> magicIdToFiles = {}; + final Map promptMap = {}; + final Map> promptFileOrder = {}; for (MagicCache c in magicCaches) { magicIdToFiles[c.title] = []; + promptFileOrder[c.title] = c.fileIdToPositionMap; } for (final p in prompts) { promptMap[p.title] = p; @@ -253,7 +284,8 @@ class MagicCacheService { for (EnteFile file in files) { if (!file.isUploaded) continue; for (MagicCache magicCache in magicCaches) { - if (magicCache.fileUploadedIDs.contains(file.uploadedFileID!)) { + if (magicCache.fileIdToPositionMap + .containsKey(file.uploadedFileID!)) { if (file.isVideo && (promptMap[magicCache.title]?.showVideo ?? true) == false) { continue; @@ -266,6 +298,7 @@ class MagicCacheService { final genericSearchResult = toGenericSearchResult( p, magicIdToFiles[p.title] ?? [], + promptFileOrder[p.title] ?? {}, ); if (genericSearchResult != null) { genericSearchResults.add(genericSearchResult); diff --git a/mobile/lib/services/search_service.dart b/mobile/lib/services/search_service.dart index e9366c1cfb..1c45b1ab73 100644 --- a/mobile/lib/services/search_service.dart +++ b/mobile/lib/services/search_service.dart @@ -1039,6 +1039,7 @@ class SearchService { MagicResultScreen( files, name: query, + enableGrouping: false, heroTag: GenericSearchResult(ResultType.magic, query, files) .heroTag(), ), diff --git a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart index 0f0a6f26f0..f5ffa336b2 100644 --- a/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/ml_debug_section_widget.dart @@ -222,7 +222,7 @@ class _MLDebugSectionWidgetState extends State { trailingIconIsMuted: true, onTap: () async { try { - await MagicCacheService.instance.updateCache(); + await MagicCacheService.instance.updateCache(forced: true); } catch (e, s) { logger.warning('Update discover failed', e, s); await showGenericErrorDialog(context: context, error: e); diff --git a/mobile/lib/ui/viewer/search/result/magic_result_screen.dart b/mobile/lib/ui/viewer/search/result/magic_result_screen.dart index 2c58e9a5e7..a44dec62aa 100644 --- a/mobile/lib/ui/viewer/search/result/magic_result_screen.dart +++ b/mobile/lib/ui/viewer/search/result/magic_result_screen.dart @@ -21,6 +21,7 @@ class MagicResultScreen extends StatefulWidget { final String name; final String heroTag; final bool enableGrouping; + final Map fileIdToPosMap; static const GalleryType appBarType = GalleryType.magic; static const GalleryType overlayType = GalleryType.magic; @@ -29,6 +30,7 @@ class MagicResultScreen extends StatefulWidget { this.files, { required this.name, this.enableGrouping = false, + this.fileIdToPosMap = const {}, this.heroTag = "", super.key, }); @@ -65,10 +67,20 @@ class _MagicResultScreenState extends State { _magicSortChangeEvent = Bus.instance.on().listen((event) { if (event.sortType == MagicSortType.mostRelevant) { + if (_enableGrouping) { + files.sort( + (a, b) => + widget.fileIdToPosMap[a.uploadedFileID]! - + widget.fileIdToPosMap[b.uploadedFileID]!, + ); + } setState(() { _enableGrouping = false; }); } else if (event.sortType == MagicSortType.mostRecent) { + if (!_enableGrouping) { + files.sort((a, b) => b.creationTime!.compareTo(a.creationTime!)); + } setState(() { _enableGrouping = true; }); diff --git a/mobile/lib/ui/viewer/search/result/search_section_all_page.dart b/mobile/lib/ui/viewer/search/result/search_section_all_page.dart index 587049801b..c7585ed51c 100644 --- a/mobile/lib/ui/viewer/search/result/search_section_all_page.dart +++ b/mobile/lib/ui/viewer/search/result/search_section_all_page.dart @@ -170,12 +170,13 @@ class _SearchSectionAllPageState extends State { MagicResultScreen( magicSectionResult.resultFiles(), name: magicSectionResult.name(), + enableGrouping: magicSectionResult + .params["enableGrouping"]! as bool, + fileIdToPosMap: magicSectionResult + .params["fileIdToPosMap"] + as Map, heroTag: "searchable_item" + - GenericSearchResult( - ResultType.magic, - magicSectionResult.name(), - magicSectionResult.resultFiles(), - ).heroTag(), + magicSectionResult.heroTag(), ), ); }, diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 2484940865..b484deab3a 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.9.39+939 +version: 0.9.40+940 publish_to: none environment: