From 68d504850ef67e01d0446b00a16581459644ed78 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 21 Dec 2023 13:04:06 +0530 Subject: [PATCH 001/175] Fix all await warnings --- lib/services/local_file_update_service.dart | 2 +- lib/services/user_service.dart | 33 +++++++++++--- lib/ui/collections/album/vertical_list.dart | 7 ++- .../collections/button/archived_button.dart | 1 + lib/ui/collections/button/hidden_button.dart | 1 + .../button/uncategorized_button.dart | 1 + lib/ui/collections/collection_list_page.dart | 2 +- lib/ui/collections/new_album_icon.dart | 1 + lib/ui/common/progress_dialog.dart | 44 ++++++++++--------- lib/ui/components/home_header_widget.dart | 1 + lib/ui/extents_page_view.dart | 2 +- lib/ui/growth/apply_code_screen.dart | 3 +- lib/ui/growth/code_success_screen.dart | 1 + lib/ui/growth/referral_screen.dart | 2 + lib/ui/home/grant_permissions_widget.dart | 2 +- lib/ui/home/landing_page_widget.dart | 5 ++- lib/ui/home/loading_photos_widget.dart | 1 + lib/ui/home/start_backup_hook_widget.dart | 5 ++- lib/ui/map/map_screen.dart | 4 +- lib/ui/payment/add_on_page.dart | 4 +- lib/ui/payment/skip_subscription_widget.dart | 9 +++- lib/ui/payment/store_subscription_page.dart | 10 ++++- lib/ui/payment/stripe_subscription_page.dart | 1 + .../payment/subscription_common_widgets.dart | 1 + lib/ui/settings/about_section_widget.dart | 3 ++ lib/ui/settings/account_section_widget.dart | 3 ++ lib/ui/settings/advanced_settings_screen.dart | 2 + lib/ui/settings/app_update_dialog.dart | 2 + .../backup/backup_folder_selection_page.dart | 2 +- .../backup/backup_section_widget.dart | 1 + lib/ui/settings/general_section_widget.dart | 6 ++- lib/ui/settings/social_section_widget.dart | 1 + lib/ui/settings/storage_card_widget.dart | 1 + lib/ui/settings/support_section_widget.dart | 1 + lib/ui/settings_page.dart | 1 + lib/ui/sharing/add_partipant_page.dart | 1 + lib/ui/sharing/album_participants_page.dart | 4 ++ lib/ui/sharing/manage_links_widget.dart | 5 +++ lib/ui/sharing/share_collection_page.dart | 4 ++ lib/ui/sharing/verify_identity_dialog.dart | 2 + lib/ui/tabs/home_widget.dart | 3 +- lib/ui/tabs/shared/empty_state.dart | 2 + lib/ui/tabs/shared/quick_link_album_item.dart | 1 + lib/ui/tabs/shared_collections_tab.dart | 1 + lib/ui/tools/editor/image_editor_page.dart | 7 +-- lib/ui/tools/lock_screen.dart | 3 +- lib/ui/viewer/file/detail_page.dart | 3 +- lib/ui/viewer/file/file_app_bar.dart | 8 ++-- lib/ui/viewer/file/thumbnail_widget.dart | 8 ++-- lib/ui/viewer/file/video_widget.dart | 1 + lib/ui/viewer/file/video_widget_new.dart | 1 + .../file_details/upload_icon_widget.dart | 2 +- .../component/gallery_file_widget.dart | 4 +- lib/ui/viewer/gallery/device_folder_page.dart | 1 + lib/utils/delete_file_util.dart | 6 ++- lib/utils/email_util.dart | 4 +- 56 files changed, 173 insertions(+), 64 deletions(-) diff --git a/lib/services/local_file_update_service.dart b/lib/services/local_file_update_service.dart index 5219376b7..591cade67 100644 --- a/lib/services/local_file_update_service.dart +++ b/lib/services/local_file_update_service.dart @@ -221,7 +221,7 @@ class LocalFileUpdateService { if (_prefs.containsKey(_iosLivePhotoSizeMigrationDone)) { return; } - bool hasEntry = await _fileUpdationDB.isExisting( + final hasEntry = await _fileUpdationDB.isExisting( file.localID!, FileUpdationDB.livePhotoCheck, ); diff --git a/lib/services/user_service.dart b/lib/services/user_service.dart index 020047384..2ae079554 100644 --- a/lib/services/user_service.dart +++ b/lib/services/user_service.dart @@ -21,7 +21,7 @@ import "package:photos/models/api/user/srp.dart"; import 'package:photos/models/delete_account.dart'; import 'package:photos/models/key_attributes.dart'; import 'package:photos/models/key_gen_result.dart'; -import 'package:photos/models/public_key.dart' as ePublicKey; +import 'package:photos/models/public_key.dart' as public_key; import 'package:photos/models/sessions.dart'; import 'package:photos/models/set_keys_request.dart'; import 'package:photos/models/set_recovery_key_request.dart'; @@ -160,7 +160,7 @@ class UserService { ); final publicKey = response.data["publicKey"]; await PublicKeysDB.instance.setKey( - ePublicKey.PublicKey( + public_key.PublicKey( email, publicKey, ), @@ -198,7 +198,7 @@ class UserService { await _preferences.setString(keyUserDetails, userDetails.toJson()); // handle email change from different client if (userDetails.email != _config.getEmail()) { - setEmail(userDetails.email); + await setEmail(userDetails.email); } } return userDetails; @@ -377,6 +377,7 @@ class UserService { ); Navigator.of(context).pop(); } else { + // ignore: unawaited_futures showErrorDialog( context, S.of(context).incorrectCode, @@ -386,6 +387,7 @@ class UserService { } catch (e) { await dialog.hide(); _logger.severe(e); + // ignore: unawaited_futures showErrorDialog( context, S.of(context).oops, @@ -430,6 +432,7 @@ class UserService { Bus.instance.fire(UserDetailsChangedEvent()); return; } + // ignore: unawaited_futures showErrorDialog( context, S.of(context).oops, @@ -438,12 +441,14 @@ class UserService { } on DioError catch (e) { await dialog.hide(); if (e.response != null && e.response!.statusCode == 403) { + // ignore: unawaited_futures showErrorDialog( context, S.of(context).oops, S.of(context).thisEmailIsAlreadyInUse, ); } else { + // ignore: unawaited_futures showErrorDialog( context, S.of(context).incorrectCode, @@ -453,6 +458,7 @@ class UserService { } catch (e) { await dialog.hide(); _logger.severe(e); + // ignore: unawaited_futures showErrorDialog( context, S.of(context).oops, @@ -605,7 +611,7 @@ class UserService { final Uint8List identity = Uint8List.fromList( utf8.encode(srpAttributes.srpUserID), ); - _logger.finest('longinKey derivation done'); + _logger.finest('loginKey derivation done'); final Uint8List salt = base64Decode(srpAttributes.srpSalt); final Uint8List password = loginKey; final SecureRandom random = _getSecureRandom(); @@ -644,7 +650,7 @@ class UserService { final String twoFASessionID = response.data["twoFactorSessionID"]; Configuration.instance.setVolatilePassword(userPassword); if (twoFASessionID.isNotEmpty) { - setTwoFactor(value: true); + await setTwoFactor(value: true); page = TwoFactorAuthenticationPage(twoFASessionID); } else { await _saveConfiguration(response); @@ -664,6 +670,7 @@ class UserService { Navigator.of(context).popUntil((route) => route.isFirst); Bus.instance.fire(AccountConfiguredEvent()); } else { + // ignore: unawaited_futures Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { @@ -765,6 +772,7 @@ class UserService { (route) => route.isFirst, ); } else { + // ignore: unawaited_futures showErrorDialog( context, S.of(context).incorrectCode, @@ -774,6 +782,7 @@ class UserService { } catch (e) { await dialog.hide(); _logger.severe(e); + // ignore: unawaited_futures showErrorDialog( context, S.of(context).oops, @@ -793,6 +802,7 @@ class UserService { }, ); if (response.statusCode == 200) { + // ignore: unawaited_futures Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { @@ -810,6 +820,7 @@ class UserService { _logger.severe(e); if (e.response != null && e.response!.statusCode == 404) { showToast(context, S.of(context).sessionExpired); + // ignore: unawaited_futures Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { @@ -819,6 +830,7 @@ class UserService { (route) => route.isFirst, ); } else { + // ignore: unawaited_futures showErrorDialog( context, S.of(context).oops, @@ -827,6 +839,7 @@ class UserService { } } catch (e) { _logger.severe(e); + // ignore: unawaited_futures showErrorDialog( context, S.of(context).oops, @@ -886,6 +899,7 @@ class UserService { S.of(context).twofactorAuthenticationSuccessfullyReset, ); await _saveConfiguration(response); + // ignore: unawaited_futures Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { @@ -899,6 +913,7 @@ class UserService { _logger.severe(e); if (e.response != null && e.response!.statusCode == 404) { showToast(context, "Session expired"); + // ignore: unawaited_futures Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { @@ -908,6 +923,7 @@ class UserService { (route) => route.isFirst, ); } else { + // ignore: unawaited_futures showErrorDialog( context, S.of(context).oops, @@ -916,6 +932,7 @@ class UserService { } } catch (e) { _logger.severe(e); + // ignore: unawaited_futures showErrorDialog( context, S.of(context).oops, @@ -986,6 +1003,7 @@ class UserService { _logger.severe(e, s); if (e is DioError) { if (e.response != null && e.response!.statusCode == 401) { + // ignore: unawaited_futures showErrorDialog( context, S.of(context).incorrectCode, @@ -994,6 +1012,7 @@ class UserService { return false; } } + // ignore: unawaited_futures showErrorDialog( context, S.of(context).somethingWentWrong, @@ -1033,7 +1052,7 @@ class UserService { Future fetchTwoFactorStatus() async { try { final response = await _enteDio.get("/users/two-factor/status"); - setTwoFactor(value: response.data["status"]); + await setTwoFactor(value: response.data["status"]); return response.data["status"]; } catch (e) { _logger.severe("Failed to fetch 2FA status", e); @@ -1109,7 +1128,7 @@ class UserService { if (fetchTwoFactorStatus) { value = await UserService.instance.fetchTwoFactorStatus(); } - _preferences.setBool(keyHasEnabledTwoFactor, value); + await _preferences.setBool(keyHasEnabledTwoFactor, value); } bool hasEnabledTwoFactor() { diff --git a/lib/ui/collections/album/vertical_list.dart b/lib/ui/collections/album/vertical_list.dart index c3634995c..4c65d44f2 100644 --- a/lib/ui/collections/album/vertical_list.dart +++ b/lib/ui/collections/album/vertical_list.dart @@ -302,6 +302,7 @@ class AlbumVerticalListWidget extends StatelessWidget { ShareCollectionPage(collection), ), ); + // ignore: unawaited_futures CollectionsService.instance .updateShareUrl(collection, {'enableCollect': true}).then( (value) => showShortToast( @@ -402,12 +403,13 @@ class AlbumVerticalListWidget extends StatelessWidget { selectedFiles!.files.toList(), ); await dialog.hide(); - RemoteSyncService.instance.sync(silently: true); + unawaited(RemoteSyncService.instance.sync(silently: true)); selectedFiles?.clearAll(); return true; } on AssertionError catch (e) { await dialog.hide(); + // ignore: unawaited_futures showErrorDialog(context, S.of(context).oops, e.message as String?); return false; } catch (e, s) { @@ -431,12 +433,13 @@ class AlbumVerticalListWidget extends StatelessWidget { try { await CollectionsService.instance .restore(toCollectionID, selectedFiles!.files.toList()); - RemoteSyncService.instance.sync(silently: true); + unawaited(RemoteSyncService.instance.sync(silently: true)); selectedFiles?.clearAll(); await dialog.hide(); return true; } on AssertionError catch (e) { await dialog.hide(); + // ignore: unawaited_futures showErrorDialog(context, S.of(context).oops, e.message as String?); return false; } catch (e, s) { diff --git a/lib/ui/collections/button/archived_button.dart b/lib/ui/collections/button/archived_button.dart index 7c705d99a..16957dfe0 100644 --- a/lib/ui/collections/button/archived_button.dart +++ b/lib/ui/collections/button/archived_button.dart @@ -97,6 +97,7 @@ class ArchivedCollectionsButton extends StatelessWidget { ), ), onPressed: () async { + // ignore: unawaited_futures routeToPage( context, ArchivePage(), diff --git a/lib/ui/collections/button/hidden_button.dart b/lib/ui/collections/button/hidden_button.dart index cb1ea8ca1..9de883c51 100644 --- a/lib/ui/collections/button/hidden_button.dart +++ b/lib/ui/collections/button/hidden_button.dart @@ -78,6 +78,7 @@ class HiddenCollectionsButtonWidget extends StatelessWidget { S.of(context).authToViewYourHiddenFiles, ); if (hasAuthenticated) { + // ignore: unawaited_futures routeToPage( context, const HiddenPage(), diff --git a/lib/ui/collections/button/uncategorized_button.dart b/lib/ui/collections/button/uncategorized_button.dart index 0773eb761..f474bc6a1 100644 --- a/lib/ui/collections/button/uncategorized_button.dart +++ b/lib/ui/collections/button/uncategorized_button.dart @@ -101,6 +101,7 @@ class UnCategorizedCollections extends StatelessWidget { ), onPressed: () async { if (collection != null) { + // ignore: unawaited_futures routeToPage( context, UnCategorizedPage(collection), diff --git a/lib/ui/collections/collection_list_page.dart b/lib/ui/collections/collection_list_page.dart index 589b6d77b..f7308e331 100644 --- a/lib/ui/collections/collection_list_page.dart +++ b/lib/ui/collections/collection_list_page.dart @@ -45,7 +45,7 @@ class _CollectionListPageState extends State { collections = widget.collections; _collectionUpdatesSubscription = Bus.instance.on().listen((event) async { - refreshCollections(); + unawaited(refreshCollections()); }); } diff --git a/lib/ui/collections/new_album_icon.dart b/lib/ui/collections/new_album_icon.dart index 9ec818143..01d871210 100644 --- a/lib/ui/collections/new_album_icon.dart +++ b/lib/ui/collections/new_album_icon.dart @@ -43,6 +43,7 @@ class NewAlbumIcon extends StatelessWidget { try { final Collection c = await CollectionsService.instance.createAlbum(text); + // ignore: unawaited_futures routeToPage( context, CollectionPage(CollectionWithThumbnail(c, null)), diff --git a/lib/ui/common/progress_dialog.dart b/lib/ui/common/progress_dialog.dart index 8c9a4e1f1..61f8d4ca1 100644 --- a/lib/ui/common/progress_dialog.dart +++ b/lib/ui/common/progress_dialog.dart @@ -1,3 +1,5 @@ +import "dart:async"; + import 'package:flutter/material.dart'; enum ProgressDialogType { normal, download } @@ -146,27 +148,29 @@ class ProgressDialog { try { if (!_isShowing) { _dialog = _Body(); - showDialog( - context: _context!, - barrierDismissible: _barrierDismissible, - barrierColor: _barrierColor, - builder: (BuildContext context) { - _dismissingContext = context; - return WillPopScope( - onWillPop: () async => _barrierDismissible, - child: Dialog( - backgroundColor: _backgroundColor, - insetAnimationCurve: _insetAnimCurve, - insetAnimationDuration: const Duration(milliseconds: 100), - elevation: _dialogElevation, - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.all(Radius.circular(_borderRadius)), + unawaited( + showDialog( + context: _context!, + barrierDismissible: _barrierDismissible, + barrierColor: _barrierColor, + builder: (BuildContext context) { + _dismissingContext = context; + return WillPopScope( + onWillPop: () async => _barrierDismissible, + child: Dialog( + backgroundColor: _backgroundColor, + insetAnimationCurve: _insetAnimCurve, + insetAnimationDuration: const Duration(milliseconds: 100), + elevation: _dialogElevation, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(_borderRadius)), + ), + child: _dialog, ), - child: _dialog, - ), - ); - }, + ); + }, + ), ); // Delaying the function for 200 milliseconds // [Default transitionDuration of DialogRoute] diff --git a/lib/ui/components/home_header_widget.dart b/lib/ui/components/home_header_widget.dart index aea0aca52..6fb8f0f6d 100644 --- a/lib/ui/components/home_header_widget.dart +++ b/lib/ui/components/home_header_widget.dart @@ -62,6 +62,7 @@ class _HomeHeaderWidgetState extends State { } else { final bool hasGrantedLimit = LocalSyncService.instance.hasGrantedLimitedPermissions(); + // ignore: unawaited_futures showChoiceActionSheet( context, title: S.of(context).preserveMore, diff --git a/lib/ui/extents_page_view.dart b/lib/ui/extents_page_view.dart index 4225efa63..65c711f5d 100644 --- a/lib/ui/extents_page_view.dart +++ b/lib/ui/extents_page_view.dart @@ -190,7 +190,7 @@ class ExtentsPageView extends StatefulWidget { /// } /// ``` /// {@end-tool} - ExtentsPageView.custom({ + const ExtentsPageView.custom({ Key? key, this.scrollDirection = Axis.horizontal, this.reverse = false, diff --git a/lib/ui/growth/apply_code_screen.dart b/lib/ui/growth/apply_code_screen.dart index c0c4128ea..2473479dd 100644 --- a/lib/ui/growth/apply_code_screen.dart +++ b/lib/ui/growth/apply_code_screen.dart @@ -122,7 +122,7 @@ class _ApplyCodeScreenState extends State { await StorageBonusService.instance .getGateway() .claimReferralCode(code.trim().toUpperCase()); - + // ignore: unawaited_futures Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (context) => CodeSuccessScreen( @@ -134,6 +134,7 @@ class _ApplyCodeScreenState extends State { } catch (e) { Logger('$runtimeType') .severe("failed to apply referral", e); + // ignore: unawaited_futures showErrorDialogForException( context: context, exception: e as Exception, diff --git a/lib/ui/growth/code_success_screen.dart b/lib/ui/growth/code_success_screen.dart index d30f05db2..116b0b8d6 100644 --- a/lib/ui/growth/code_success_screen.dart +++ b/lib/ui/growth/code_success_screen.dart @@ -92,6 +92,7 @@ class CodeSuccessScreen extends StatelessWidget { singleBorderRadius: 8, alignCaptionedTextToLeft: true, onTap: () async { + // ignore: unawaited_futures routeToPage( context, StorageDetailsScreen(referralView, userDetails), diff --git a/lib/ui/growth/referral_screen.dart b/lib/ui/growth/referral_screen.dart index 5e403b5dd..32843c192 100644 --- a/lib/ui/growth/referral_screen.dart +++ b/lib/ui/growth/referral_screen.dart @@ -250,6 +250,7 @@ class ReferralWidget extends StatelessWidget { isTopBorderRadiusRemoved: referralView.enableApplyCode, alignCaptionedTextToLeft: true, onTap: () async { + // ignore: unawaited_futures routeToPage( context, WebPage( @@ -287,6 +288,7 @@ class ReferralWidget extends StatelessWidget { singleBorderRadius: 8, alignCaptionedTextToLeft: true, onTap: () async { + // ignore: unawaited_futures routeToPage( context, StorageDetailsScreen(referralView, userDetails), diff --git a/lib/ui/home/grant_permissions_widget.dart b/lib/ui/home/grant_permissions_widget.dart index 7fbeec1d8..29c8e5e1b 100644 --- a/lib/ui/home/grant_permissions_widget.dart +++ b/lib/ui/home/grant_permissions_widget.dart @@ -119,7 +119,7 @@ class GrantPermissionsWidget extends StatelessWidget { ), ], ); - + // ignore: unawaited_futures showDialog( context: context, builder: (BuildContext context) { diff --git a/lib/ui/home/landing_page_widget.dart b/lib/ui/home/landing_page_widget.dart index 1e8d98e41..119610ffa 100644 --- a/lib/ui/home/landing_page_widget.dart +++ b/lib/ui/home/landing_page_widget.dart @@ -1,3 +1,4 @@ +import "dart:async"; import 'dart:io'; import 'package:dots_indicator/dots_indicator.dart'; @@ -55,6 +56,7 @@ class _LandingPageWidgetState extends State { ), onTap: () async { final locale = await getLocale(); + // ignore: unawaited_futures routeToPage( context, LanguageSelectorPage( @@ -62,7 +64,7 @@ class _LandingPageWidgetState extends State { (locale) async { await setLocale(locale); EnteApp.setLocale(context, locale); - S.delegate.load(locale); + unawaited(S.delegate.load(locale)); }, locale, ), @@ -202,6 +204,7 @@ class _LandingPageWidgetState extends State { page = getSubscriptionPage(isOnBoarding: true); } } + // ignore: unawaited_futures Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { diff --git a/lib/ui/home/loading_photos_widget.dart b/lib/ui/home/loading_photos_widget.dart index fb74ce4df..1eeddc4b8 100644 --- a/lib/ui/home/loading_photos_widget.dart +++ b/lib/ui/home/loading_photos_widget.dart @@ -39,6 +39,7 @@ class _LoadingPhotosWidgetState extends State { if (LocalSyncService.instance.hasGrantedLimitedPermissions()) { // Do nothing, let HomeWidget refresh } else { + // ignore: unawaited_futures routeToPage( context, BackupFolderSelectionPage( diff --git a/lib/ui/home/start_backup_hook_widget.dart b/lib/ui/home/start_backup_hook_widget.dart index 1db891371..f331cd586 100644 --- a/lib/ui/home/start_backup_hook_widget.dart +++ b/lib/ui/home/start_backup_hook_widget.dart @@ -1,3 +1,5 @@ +import "dart:async"; + import 'package:flutter/material.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:photos/generated/l10n.dart'; @@ -42,8 +44,9 @@ class StartBackupHookWidget extends StatelessWidget { onTap: () async { if (LocalSyncService.instance .hasGrantedLimitedPermissions()) { - PhotoManager.presentLimited(); + unawaited(PhotoManager.presentLimited()); } else { + // ignore: unawaited_futures routeToPage( context, BackupFolderSelectionPage( diff --git a/lib/ui/map/map_screen.dart b/lib/ui/map/map_screen.dart index df692a2fe..0a021c045 100644 --- a/lib/ui/map/map_screen.dart +++ b/lib/ui/map/map_screen.dart @@ -68,7 +68,7 @@ class _MapScreenState extends State { Future initialize() async { try { allImages = await widget.filesFutureFn(); - processFiles(allImages); + unawaited(processFiles(allImages)); } catch (e, s) { _logger.severe("Error initializing map screen", e, s); } @@ -157,7 +157,7 @@ class _MapScreenState extends State { prevMessage = message; } else { - _mapMoveSubscription?.cancel(); + await _mapMoveSubscription?.cancel(); isolate?.kill(); } }); diff --git a/lib/ui/payment/add_on_page.dart b/lib/ui/payment/add_on_page.dart index 6272bc75c..fa7b3aba7 100644 --- a/lib/ui/payment/add_on_page.dart +++ b/lib/ui/payment/add_on_page.dart @@ -39,7 +39,7 @@ class AddOnPage extends StatelessWidget { sliver: SliverList( delegate: SliverChildBuilderDelegate( (delegateBuildContext, index) { - Bonus bonus = bonusData.getAddOnBonuses()[index]; + final bonus = bonusData.getAddOnBonuses()[index]; return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: AddOnViewSection( @@ -50,7 +50,7 @@ class AddOnPage extends StatelessWidget { ), ); }, - childCount: bonusData?.getAddOnBonuses().length ?? 0, + childCount: bonusData.getAddOnBonuses().length, ), ), ), diff --git a/lib/ui/payment/skip_subscription_widget.dart b/lib/ui/payment/skip_subscription_widget.dart index 2add30f7a..c2949a238 100644 --- a/lib/ui/payment/skip_subscription_widget.dart +++ b/lib/ui/payment/skip_subscription_widget.dart @@ -1,3 +1,5 @@ +import "dart:async"; + import 'package:flutter/material.dart'; import 'package:photos/core/event_bus.dart'; import 'package:photos/events/subscription_purchased_event.dart'; @@ -32,6 +34,7 @@ class SkipSubscriptionWidget extends StatelessWidget { ), onPressed: () async { Bus.instance.fire(SubscriptionPurchasedEvent()); + // ignore: unawaited_futures Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { @@ -40,8 +43,10 @@ class SkipSubscriptionWidget extends StatelessWidget { ), (route) => false, ); - BillingService.instance - .verifySubscription(freeProductID, "", paymentProvider: "ente"); + unawaited( + BillingService.instance + .verifySubscription(freeProductID, "", paymentProvider: "ente"), + ); }, child: Text(S.of(context).continueOnFreeTrial), ), diff --git a/lib/ui/payment/store_subscription_page.dart b/lib/ui/payment/store_subscription_page.dart index d0eca6c93..4bcdd4d5b 100644 --- a/lib/ui/payment/store_subscription_page.dart +++ b/lib/ui/payment/store_subscription_page.dart @@ -119,11 +119,13 @@ class _StoreSubscriptionPageState extends State { ? S.of(context).googlePlayId : S.of(context).appleId; final String message = S.of(context).subAlreadyLinkedErrMessage(id); + // ignore: unawaited_futures showErrorDialog(context, title, message); return; } catch (e) { _logger.warning("Could not complete payment ", e); await _dialog.hide(); + // ignore: unawaited_futures showErrorDialog( context, S.of(context).paymentFailed, @@ -175,6 +177,7 @@ class _StoreSubscriptionPageState extends State { } Future _fetchSubData() async { + // ignore: unawaited_futures _userService.getUserDetailsV2(memoryCount: false).then((userDetails) async { _userDetails = userDetails; _currentSubscription = userDetails.subscription; @@ -316,7 +319,9 @@ class _StoreSubscriptionPageState extends State { singleBorderRadius: 4, alignCaptionedTextToLeft: true, onTap: () async { - _billingService.launchFamilyPortal(context, _userDetails); + unawaited( + _billingService.launchFamilyPortal(context, _userDetails), + ); }, ), ), @@ -452,6 +457,7 @@ class _StoreSubscriptionPageState extends State { if (isActive) { return; } + // ignore: unawaited_futures showErrorDialog( context, S.of(context).sorry, @@ -513,6 +519,7 @@ class _StoreSubscriptionPageState extends State { "addOnBonus ${convertBytesToReadableFormat(addOnBonus)}," "overshooting by ${convertBytesToReadableFormat(_userDetails.getFamilyOrPersonalUsage() - (plan.storage + addOnBonus))}", ); + // ignore: unawaited_futures showErrorDialog( context, S.of(context).sorry, @@ -540,6 +547,7 @@ class _StoreSubscriptionPageState extends State { _currentSubscription!.productID != plan.androidID; if (isCrossGradingOnAndroid) { await _dialog.hide(); + // ignore: unawaited_futures showErrorDialog( context, S.of(context).couldNotUpdateSubscription, diff --git a/lib/ui/payment/stripe_subscription_page.dart b/lib/ui/payment/stripe_subscription_page.dart index 9772f4ed4..51a80a67e 100644 --- a/lib/ui/payment/stripe_subscription_page.dart +++ b/lib/ui/payment/stripe_subscription_page.dart @@ -277,6 +277,7 @@ class _StripeSubscriptionPageState extends State { singleBorderRadius: 4, alignCaptionedTextToLeft: true, onTap: () async { + // ignore: unawaited_futures _billingService.launchFamilyPortal(context, _userDetails); }, ), diff --git a/lib/ui/payment/subscription_common_widgets.dart b/lib/ui/payment/subscription_common_widgets.dart index c33f26d79..8c743e2f6 100644 --- a/lib/ui/payment/subscription_common_widgets.dart +++ b/lib/ui/payment/subscription_common_widgets.dart @@ -187,6 +187,7 @@ class SubFaqWidget extends StatelessWidget { singleBorderRadius: 4, alignCaptionedTextToLeft: true, onTap: () async { + // ignore: unawaited_futures showModalBottomSheet( backgroundColor: Theme.of(context).colorScheme.bgColorForQuestions, barrierColor: Colors.black87, diff --git a/lib/ui/settings/about_section_widget.dart b/lib/ui/settings/about_section_widget.dart index e5de7a663..8102b5d52 100644 --- a/lib/ui/settings/about_section_widget.dart +++ b/lib/ui/settings/about_section_widget.dart @@ -36,6 +36,7 @@ class AboutSectionWidget extends StatelessWidget { trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, onTap: () async { + // ignore: unawaited_futures launchUrl(Uri.parse("https://github.com/ente-io/photos-app")); }, ), @@ -68,6 +69,7 @@ class AboutSectionWidget extends StatelessWidget { await UpdateService.instance.shouldUpdate(); await dialog.hide(); if (shouldUpdate) { + // ignore: unawaited_futures showDialog( context: context, builder: (BuildContext context) { @@ -115,6 +117,7 @@ class AboutMenuItemWidget extends StatelessWidget { trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, onTap: () async { + // ignore: unawaited_futures Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { diff --git a/lib/ui/settings/account_section_widget.dart b/lib/ui/settings/account_section_widget.dart index 3f5615e69..c184d4b89 100644 --- a/lib/ui/settings/account_section_widget.dart +++ b/lib/ui/settings/account_section_widget.dart @@ -59,6 +59,7 @@ class AccountSectionWidget extends StatelessWidget { S.of(context).authToChangeYourEmail, ); if (hasAuthenticated) { + // ignore: unawaited_futures showDialog( context: context, builder: (BuildContext context) { @@ -86,6 +87,7 @@ class AccountSectionWidget extends StatelessWidget { S.of(context).authToChangeYourPassword, ); if (hasAuthenticated) { + // ignore: unawaited_futures Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { @@ -107,6 +109,7 @@ class AccountSectionWidget extends StatelessWidget { trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, onTap: () async { + // ignore: unawaited_futures launchUrlString("https://ente.io/faq/migration/out-of-ente/"); }, ), diff --git a/lib/ui/settings/advanced_settings_screen.dart b/lib/ui/settings/advanced_settings_screen.dart index 3804e6105..2a8733bc0 100644 --- a/lib/ui/settings/advanced_settings_screen.dart +++ b/lib/ui/settings/advanced_settings_screen.dart @@ -82,6 +82,7 @@ class _AdvancedSettingsScreenState extends State { singleBorderRadius: 8, alignCaptionedTextToLeft: true, onTap: () async { + // ignore: unawaited_futures routeToPage( context, const MachineLearningSettingsPage(), @@ -135,6 +136,7 @@ class _AdvancedSettingsScreenState extends State { singleBorderRadius: 8, alignCaptionedTextToLeft: true, onTap: () async { + // ignore: unawaited_futures routeToPage(context, const AppStorageViewer()); }, ), diff --git a/lib/ui/settings/app_update_dialog.dart b/lib/ui/settings/app_update_dialog.dart index 2b087772f..00bf1c948 100644 --- a/lib/ui/settings/app_update_dialog.dart +++ b/lib/ui/settings/app_update_dialog.dart @@ -76,6 +76,7 @@ class _AppUpdateDialogState extends State { ), onPressed: () async { Navigator.pop(context); + // ignore: unawaited_futures showDialog( context: context, builder: (BuildContext context) { @@ -236,6 +237,7 @@ class _ApkDownloaderDialogState extends State { ], ); + // ignore: unawaited_futures showDialog( context: context, builder: (BuildContext context) { diff --git a/lib/ui/settings/backup/backup_folder_selection_page.dart b/lib/ui/settings/backup/backup_folder_selection_page.dart index e55ab9a5b..fda3f9343 100644 --- a/lib/ui/settings/backup/backup_folder_selection_page.dart +++ b/lib/ui/settings/backup/backup_folder_selection_page.dart @@ -227,7 +227,7 @@ class _BackupFolderSelectionPageState extends State { _allDevicePathIDs.length == _selectedDevicePathIDs.length, ); await RemoteSyncService.instance.updateDeviceFolderSyncStatus(syncStatus); - dialog.hide(); + await dialog.hide(); Navigator.of(context).pop(); } catch (e, s) { _logger.severe("Failed to updated backup folder", e, s); diff --git a/lib/ui/settings/backup/backup_section_widget.dart b/lib/ui/settings/backup/backup_section_widget.dart index 56c9f176e..81013f653 100644 --- a/lib/ui/settings/backup/backup_section_widget.dart +++ b/lib/ui/settings/backup/backup_section_widget.dart @@ -100,6 +100,7 @@ class BackupSectionWidgetState extends State { } if (status.localIDs.isEmpty) { + // ignore: unawaited_futures showErrorDialog( context, S.of(context).allClear, diff --git a/lib/ui/settings/general_section_widget.dart b/lib/ui/settings/general_section_widget.dart index 5094d2628..c01db225d 100644 --- a/lib/ui/settings/general_section_widget.dart +++ b/lib/ui/settings/general_section_widget.dart @@ -1,3 +1,5 @@ +import "dart:async"; + import 'package:flutter/material.dart'; import "package:photos/app.dart"; import "package:photos/generated/l10n.dart"; @@ -39,6 +41,7 @@ class GeneralSectionWidget extends StatelessWidget { trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, onTap: () async { + // ignore: unawaited_futures routeToPage( context, const ReferralScreen(), @@ -75,7 +78,7 @@ class GeneralSectionWidget extends StatelessWidget { (locale) async { await setLocale(locale); EnteApp.setLocale(context, locale); - S.load(locale); + unawaited(S.load(locale)); }, locale, ), @@ -114,6 +117,7 @@ class GeneralSectionWidget extends StatelessWidget { Future _onFamilyPlansTapped(BuildContext context) async { final userDetails = await UserService.instance.getUserDetailsV2(memoryCount: false); + // ignore: unawaited_futures BillingService.instance.launchFamilyPortal(context, userDetails); } diff --git a/lib/ui/settings/social_section_widget.dart b/lib/ui/settings/social_section_widget.dart index 95ea433fa..2e04aa723 100644 --- a/lib/ui/settings/social_section_widget.dart +++ b/lib/ui/settings/social_section_widget.dart @@ -99,6 +99,7 @@ class SocialsMenuItemWidget extends StatelessWidget { trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, onTap: () async { + // ignore: unawaited_futures launchUrlString( url, mode: launchInExternalApp diff --git a/lib/ui/settings/storage_card_widget.dart b/lib/ui/settings/storage_card_widget.dart index 543505bd4..0d8924c83 100644 --- a/lib/ui/settings/storage_card_widget.dart +++ b/lib/ui/settings/storage_card_widget.dart @@ -62,6 +62,7 @@ class _StorageCardWidgetState extends State { return GestureDetector( behavior: HitTestBehavior.translucent, onTap: () async { + // ignore: unawaited_futures Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { diff --git a/lib/ui/settings/support_section_widget.dart b/lib/ui/settings/support_section_widget.dart index ef7690be5..580d3fd5e 100644 --- a/lib/ui/settings/support_section_widget.dart +++ b/lib/ui/settings/support_section_widget.dart @@ -55,6 +55,7 @@ class SupportSectionWidget extends StatelessWidget { trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, onTap: () async { + // ignore: unawaited_futures launchUrlString( githubIssuesUrl, mode: LaunchMode.externalApplication, diff --git a/lib/ui/settings_page.dart b/lib/ui/settings_page.dart index ecbb9b08a..85ce16afb 100644 --- a/lib/ui/settings_page.dart +++ b/lib/ui/settings_page.dart @@ -100,6 +100,7 @@ class SettingsPage extends StatelessWidget { type: NotificationType.goldenBanner, onTap: () async { StorageBonusService.instance.markStorageBonusAsDone(); + // ignore: unawaited_futures routeToPage(context, const ReferralScreen()); }, ), diff --git a/lib/ui/sharing/add_partipant_page.dart b/lib/ui/sharing/add_partipant_page.dart index a7e11c356..cf828e1fe 100644 --- a/lib/ui/sharing/add_partipant_page.dart +++ b/lib/ui/sharing/add_partipant_page.dart @@ -219,6 +219,7 @@ class _AddParticipantPage extends State { } final emailToAdd = selectedEmail == '' ? _email : selectedEmail; + // ignore: unawaited_futures showDialog( context: context, builder: (BuildContext context) { diff --git a/lib/ui/sharing/album_participants_page.dart b/lib/ui/sharing/album_participants_page.dart index 893586b89..494df319f 100644 --- a/lib/ui/sharing/album_participants_page.dart +++ b/lib/ui/sharing/album_participants_page.dart @@ -165,6 +165,7 @@ class _AlbumParticipantsPageState extends State { onTap: isOwner ? () async { if (isOwner) { + // ignore: unawaited_futures _navigateToManageUser(currentUser); } } @@ -192,6 +193,7 @@ class _AlbumParticipantsPageState extends State { leadingIcon: Icons.add_outlined, menuItemColor: getEnteColorScheme(context).fillFaint, onTap: () async { + // ignore: unawaited_futures _navigateToAddUser(false); }, isTopBorderRadiusRemoved: collaborators.isNotEmpty, @@ -241,6 +243,7 @@ class _AlbumParticipantsPageState extends State { onTap: isOwner ? () async { if (isOwner) { + // ignore: unawaited_futures _navigateToManageUser(currentUser); } } @@ -268,6 +271,7 @@ class _AlbumParticipantsPageState extends State { leadingIcon: Icons.add_outlined, menuItemColor: getEnteColorScheme(context).fillFaint, onTap: () async { + // ignore: unawaited_futures _navigateToAddUser(true); }, isTopBorderRadiusRemoved: viewers.isNotEmpty, diff --git a/lib/ui/sharing/manage_links_widget.dart b/lib/ui/sharing/manage_links_widget.dart index 846867029..02878735e 100644 --- a/lib/ui/sharing/manage_links_widget.dart +++ b/lib/ui/sharing/manage_links_widget.dart @@ -109,6 +109,7 @@ class _ManageSharedLinkWidgetState extends State { menuItemColor: enteColorScheme.fillFaint, surfaceExecutionStates: false, onTap: () async { + // ignore: unawaited_futures routeToPage( context, LinkExpiryPickerPage(widget.collection!), @@ -144,6 +145,7 @@ class _ManageSharedLinkWidgetState extends State { alignCaptionedTextToLeft: true, isBottomBorderRadiusRemoved: true, onTap: () async { + // ignore: unawaited_futures routeToPage( context, DeviceLimitPickerPage(widget.collection!), @@ -174,6 +176,7 @@ class _ManageSharedLinkWidgetState extends State { {'enableDownload': value}, ); if (!value) { + // ignore: unawaited_futures showErrorDialog( context, S.of(context).disableDownloadWarningTitle, @@ -199,6 +202,7 @@ class _ManageSharedLinkWidgetState extends State { value: isPasswordEnabled, onChanged: (enablePassword) async { if (enablePassword) { + // ignore: unawaited_futures showTextInputDialog( context, title: S.of(context).setAPassword, @@ -275,6 +279,7 @@ class _ManageSharedLinkWidgetState extends State { leadingIcon: Icons.adaptive.share, menuItemColor: getEnteColorScheme(context).fillFaint, onTap: () async { + // ignore: unawaited_futures shareText(urlValue); }, isTopBorderRadiusRemoved: true, diff --git a/lib/ui/sharing/share_collection_page.dart b/lib/ui/sharing/share_collection_page.dart index ff3c8aeb6..e63a39f3e 100644 --- a/lib/ui/sharing/share_collection_page.dart +++ b/lib/ui/sharing/share_collection_page.dart @@ -87,6 +87,7 @@ class _ShareCollectionPageState extends State { isTopBorderRadiusRemoved: _sharees.isNotEmpty, isBottomBorderRadiusRemoved: true, onTap: () async { + // ignore: unawaited_futures routeToPage( context, AddParticipantPage(widget.collection, true), @@ -114,6 +115,7 @@ class _ShareCollectionPageState extends State { menuItemColor: getEnteColorScheme(context).fillFaint, isTopBorderRadiusRemoved: true, onTap: () async { + // ignore: unawaited_futures routeToPage(context, AddParticipantPage(widget.collection, false)) .then( (value) => { @@ -191,6 +193,7 @@ class _ShareCollectionPageState extends State { leadingIcon: Icons.adaptive.share, menuItemColor: getEnteColorScheme(context).fillFaint, onTap: () async { + // ignore: unawaited_futures shareText(url); }, isTopBorderRadiusRemoved: true, @@ -216,6 +219,7 @@ class _ShareCollectionPageState extends State { menuItemColor: getEnteColorScheme(context).fillFaint, trailingIconIsMuted: true, onTap: () async { + // ignore: unawaited_futures routeToPage( context, ManageSharedLinkWidget(collection: widget.collection), diff --git a/lib/ui/sharing/verify_identity_dialog.dart b/lib/ui/sharing/verify_identity_dialog.dart index 3e50b3e0e..eaa725635 100644 --- a/lib/ui/sharing/verify_identity_dialog.dart +++ b/lib/ui/sharing/verify_identity_dialog.dart @@ -79,6 +79,7 @@ class _VerifyIdentifyDialogState extends State { labelText: S.of(context).sendInvite, isInAlert: true, onTap: () async { + // ignore: unawaited_futures shareText( S.of(context).shareTextRecommendUsingEnte, ); @@ -167,6 +168,7 @@ class _VerifyIdentifyDialogState extends State { await Clipboard.setData( ClipboardData(text: verificationID), ); + // ignore: unawaited_futures shareText( widget.self ? S.of(context).shareMyVerificationID(verificationID) diff --git a/lib/ui/tabs/home_widget.dart b/lib/ui/tabs/home_widget.dart index e2ccdee87..1096821a8 100644 --- a/lib/ui/tabs/home_widget.dart +++ b/lib/ui/tabs/home_widget.dart @@ -343,7 +343,7 @@ class _HomeWidgetState extends State { return false; } if (Platform.isAndroid && action == IntentAction.main) { - MoveToBackground.moveTaskToBack(); + unawaited(MoveToBackground.moveTaskToBack()); return false; } else { return true; @@ -570,6 +570,7 @@ class _HomeWidgetState extends State { .getCollectionByID(int.parse(collectionID))!; final thumbnail = await CollectionsService.instance.getCover(collection); + // ignore: unawaited_futures routeToPage( context, CollectionPage( diff --git a/lib/ui/tabs/shared/empty_state.dart b/lib/ui/tabs/shared/empty_state.dart index 9196a52c9..d466e0aa0 100644 --- a/lib/ui/tabs/shared/empty_state.dart +++ b/lib/ui/tabs/shared/empty_state.dart @@ -92,6 +92,7 @@ class SharedEmptyStateWidget extends StatelessWidget { labelText: S.of(context).inviteYourFriends, icon: Icons.ios_share_outlined, onTap: () async { + // ignore: unawaited_futures shareText(S.of(context).shareTextRecommendUsingEnte); }, ), @@ -188,6 +189,7 @@ class IncomingAlbumEmptyState extends StatelessWidget { labelText: S.of(context).inviteYourFriends, icon: Icons.ios_share_outlined, onTap: () async { + // ignore: unawaited_futures shareText(S.of(context).shareTextRecommendUsingEnte); }, ), diff --git a/lib/ui/tabs/shared/quick_link_album_item.dart b/lib/ui/tabs/shared/quick_link_album_item.dart index 0e7e233be..70ed4d3dc 100644 --- a/lib/ui/tabs/shared/quick_link_album_item.dart +++ b/lib/ui/tabs/shared/quick_link_album_item.dart @@ -122,6 +122,7 @@ class QuickLinkAlbumItem extends StatelessWidget { ), tagPrefix: heroTagPrefix, ); + // ignore: unawaited_futures routeToPage(context, page); }, ); diff --git a/lib/ui/tabs/shared_collections_tab.dart b/lib/ui/tabs/shared_collections_tab.dart index d937fa716..450d3062c 100644 --- a/lib/ui/tabs/shared_collections_tab.dart +++ b/lib/ui/tabs/shared_collections_tab.dart @@ -255,6 +255,7 @@ class _SharedCollectionsTabState extends State labelText: S.of(context).inviteYourFriendsToEnte, icon: Icons.ios_share_outlined, onTap: () async { + // ignore: unawaited_futures shareText( S.of(context).shareTextRecommendUsingEnte, ); diff --git a/lib/ui/tools/editor/image_editor_page.dart b/lib/ui/tools/editor/image_editor_page.dart index fee597b2f..ca36db002 100644 --- a/lib/ui/tools/editor/image_editor_page.dart +++ b/lib/ui/tools/editor/image_editor_page.dart @@ -1,3 +1,4 @@ +import "dart:async"; import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; @@ -349,7 +350,7 @@ class _ImageEditorPageState extends State { ".JPEG"; //Disabling notifications for assets changing to insert the file into //files db before triggering a sync. - PhotoManager.stopChangeNotify(); + await PhotoManager.stopChangeNotify(); final AssetEntity? newAsset = await (PhotoManager.editor.saveImage(result, title: fileName)); final newFile = await ente.EnteFile.fromAsset( @@ -372,7 +373,7 @@ class _ImageEditorPageState extends State { } newFile.generatedID = await FilesDB.instance.insert(newFile); Bus.instance.fire(LocalPhotosUpdatedEvent([newFile], source: "editSave")); - SyncService.instance.sync(); + unawaited(SyncService.instance.sync()); showShortToast(context, S.of(context).editsSaved); _logger.info("Original file " + widget.originalFile.toString()); _logger.info("Saved edits to file " + newFile.toString()); @@ -403,7 +404,7 @@ class _ImageEditorPageState extends State { showToast(context, S.of(context).oopsCouldNotSaveEdits); _logger.severe(e, s); } finally { - PhotoManager.startChangeNotify(); + await PhotoManager.startChangeNotify(); } await dialog.hide(); } diff --git a/lib/ui/tools/lock_screen.dart b/lib/ui/tools/lock_screen.dart index 6b34fda4f..f5f0696a3 100644 --- a/lib/ui/tools/lock_screen.dart +++ b/lib/ui/tools/lock_screen.dart @@ -56,6 +56,7 @@ class _LockScreenState extends State with WidgetsBindingObserver { text: context.l10n.unlock, iconData: Icons.lock_open_outlined, onTap: () async { + // ignore: unawaited_futures _showLockScreen(source: "tapUnlock"); }, ), @@ -72,7 +73,7 @@ class _LockScreenState extends State with WidgetsBindingObserver { if (Platform.isAndroid) { return false; } - var shortestSide = MediaQuery.of(context).size.shortestSide; + final shortestSide = MediaQuery.of(context).size.shortestSide; return shortestSide > 600 ? true : false; } diff --git a/lib/ui/viewer/file/detail_page.dart b/lib/ui/viewer/file/detail_page.dart index 19e16f9ca..46db449c5 100644 --- a/lib/ui/viewer/file/detail_page.dart +++ b/lib/ui/viewer/file/detail_page.dart @@ -324,7 +324,7 @@ class _DetailPageState extends State { ? currentPageIndex : currentPageIndex - 1; if (_files!.isNotEmpty) { - _pageController.animateToPage( + await _pageController.animateToPage( targetPageIndex, duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, @@ -340,6 +340,7 @@ class _DetailPageState extends State { UnauthorizedEditError(), StackTrace.current, ); + // ignore: unawaited_futures showErrorDialog( context, S.of(context).sorry, diff --git a/lib/ui/viewer/file/file_app_bar.dart b/lib/ui/viewer/file/file_app_bar.dart index 28a83ca66..0921c88e4 100644 --- a/lib/ui/viewer/file/file_app_bar.dart +++ b/lib/ui/viewer/file/file_app_bar.dart @@ -321,7 +321,7 @@ class FileAppBarState extends State { final File? fileToSave = await getFile(file); //Disabling notifications for assets changing to insert the file into //files db before triggering a sync. - PhotoManager.stopChangeNotify(); + await PhotoManager.stopChangeNotify(); if (type == FileType.image) { savedAsset = await PhotoManager.editor .saveImageWithPath(fileToSave!.path, title: file.title!); @@ -364,7 +364,7 @@ class FileAppBarState extends State { await dialog.hide(); await showGenericErrorDialog(context: context, error: e); } finally { - PhotoManager.startChangeNotify(); + await PhotoManager.startChangeNotify(); LocalSyncService.instance.checkAndSync().ignore(); } } @@ -419,9 +419,9 @@ class FileAppBarState extends State { if (result == false) { showShortToast(context, S.of(context).somethingWentWrong); } - dialog.hide(); + await dialog.hide(); } catch (e) { - dialog.hide(); + await dialog.hide(); _logger.severe("Failed to use as", e); await showGenericErrorDialog(context: context, error: e); } diff --git a/lib/ui/viewer/file/thumbnail_widget.dart b/lib/ui/viewer/file/thumbnail_widget.dart index 93820d4ae..272dc73d3 100644 --- a/lib/ui/viewer/file/thumbnail_widget.dart +++ b/lib/ui/viewer/file/thumbnail_widget.dart @@ -211,7 +211,7 @@ class _ThumbnailWidgetState extends State { } Future _getThumbnailFromDisk() async { - getThumbnailFromLocal( + return getThumbnailFromLocal( widget.file, size: widget.thumbnailSize, ).then((thumbData) async { @@ -220,15 +220,15 @@ class _ThumbnailWidgetState extends State { _logger.fine("Removing localID reference for " + widget.file.tag); widget.file.localID = null; if (widget.file.isTrash) { - TrashDB.instance.update(widget.file as TrashFile); + await TrashDB.instance.update(widget.file as TrashFile); } else { - FilesDB.instance.update(widget.file); + await FilesDB.instance.update(widget.file); } _loadNetworkImage(); } else { if (await doesLocalFileExist(widget.file) == false) { _logger.info("Deleting file " + widget.file.tag); - FilesDB.instance.deleteLocalFile(widget.file); + await FilesDB.instance.deleteLocalFile(widget.file); Bus.instance.fire( LocalPhotosUpdatedEvent( [widget.file], diff --git a/lib/ui/viewer/file/video_widget.dart b/lib/ui/viewer/file/video_widget.dart index 590f0dcb5..c9c07df5c 100644 --- a/lib/ui/viewer/file/video_widget.dart +++ b/lib/ui/viewer/file/video_widget.dart @@ -68,6 +68,7 @@ class _VideoWidgetState extends State { _loadNetworkVideo(); } } else { + // ignore: unawaited_futures asset.getMediaUrl().then((url) { _setVideoPlayerController(url: url); }); diff --git a/lib/ui/viewer/file/video_widget_new.dart b/lib/ui/viewer/file/video_widget_new.dart index 97659d7df..0968f9577 100644 --- a/lib/ui/viewer/file/video_widget_new.dart +++ b/lib/ui/viewer/file/video_widget_new.dart @@ -68,6 +68,7 @@ class _VideoWidgetNewState extends State _loadNetworkVideo(); } } else { + // ignore: unawaited_futures asset.getMediaUrl().then((url) { _setVideoController( url ?? diff --git a/lib/ui/viewer/file_details/upload_icon_widget.dart b/lib/ui/viewer/file_details/upload_icon_widget.dart index de8fffd20..1d724d4b7 100644 --- a/lib/ui/viewer/file_details/upload_icon_widget.dart +++ b/lib/ui/viewer/file_details/upload_icon_widget.dart @@ -123,7 +123,7 @@ class _UpdateIconWidgetState extends State { widget.file.collectionID = (await CollectionsService.instance .getUncategorizedCollection()) .id; - FilesDB.instance.insert(widget.file); + await FilesDB.instance.insert(widget.file); } RemoteSyncService.instance.sync().ignore(); if (mounted) { diff --git a/lib/ui/viewer/gallery/component/gallery_file_widget.dart b/lib/ui/viewer/gallery/component/gallery_file_widget.dart index 425fe0ab3..142f4427c 100644 --- a/lib/ui/viewer/gallery/component/gallery_file_widget.dart +++ b/lib/ui/viewer/gallery/component/gallery_file_widget.dart @@ -124,7 +124,7 @@ class GalleryFileWidget extends StatelessWidget { if (AppLifecycleService.instance.mediaExtensionAction.action == IntentAction.pick) { final ioFile = await getFile(file); - MediaExtension().setResult("file://${ioFile!.path}"); + await MediaExtension().setResult("file://${ioFile!.path}"); } else { _routeToDetailPage(file, context); } @@ -148,7 +148,7 @@ class GalleryFileWidget extends StatelessWidget { if (AppLifecycleService.instance.mediaExtensionAction.action == IntentAction.pick) { final ioFile = await getFile(file); - MediaExtension().setResult("file://${ioFile!.path}"); + await MediaExtension().setResult("file://${ioFile!.path}"); } else { _routeToDetailPage(file, context); } diff --git a/lib/ui/viewer/gallery/device_folder_page.dart b/lib/ui/viewer/gallery/device_folder_page.dart index 0604050f4..42d7b80a6 100644 --- a/lib/ui/viewer/gallery/device_folder_page.dart +++ b/lib/ui/viewer/gallery/device_folder_page.dart @@ -253,6 +253,7 @@ class _ResetIgnoredFilesWidgetState extends State { await _removeFilesFromIgnoredFiles( widget.filesInDeviceCollection, ); + // ignore: unawaited_futures RemoteSyncService.instance.sync(silently: true).then((value) { if (mounted) { widget.parentSetState.call(); diff --git a/lib/utils/delete_file_util.dart b/lib/utils/delete_file_util.dart index bd5f76308..545ea9c27 100644 --- a/lib/utils/delete_file_util.dart +++ b/lib/utils/delete_file_util.dart @@ -133,6 +133,7 @@ Future deleteFilesFromEverywhere( } } if (uploadedFilesToBeTrashed.isNotEmpty) { + // ignore: unawaited_futures RemoteSyncService.instance.sync(silently: true); } } @@ -183,7 +184,9 @@ Future deleteFilesFromRemoteOnly( source: "deleteFromRemoteOnly", ), ); + // ignore: unawaited_futures SyncService.instance.sync(); + // ignore: unawaited_futures RemoteSyncService.instance.sync(silently: true); } @@ -232,7 +235,7 @@ Future deleteFilesOnDeviceOnly( alreadyDeletedIDs.contains(file.localID)) { deletedFiles.add(file); file.localID = null; - FilesDB.instance.update(file); + await FilesDB.instance.update(file); } } if (deletedFiles.isNotEmpty || alreadyDeletedIDs.isNotEmpty) { @@ -411,6 +414,7 @@ Future> deleteLocalFilesInBatches( "Deleting " + localIDs.length.toString() + " backed up files...", key: dialogKey, ); + // ignore: unawaited_futures showDialog( context: context, builder: (context) { diff --git a/lib/utils/email_util.dart b/lib/utils/email_util.dart index 6aca4aea0..f07d37b1c 100644 --- a/lib/utils/email_util.dart +++ b/lib/utils/email_util.dart @@ -42,6 +42,7 @@ Future sendLogs( String? subject, String? body, }) async { + // ignore: unawaited_futures showDialogWidget( context: context, title: S.of(context).reportABug, @@ -68,6 +69,7 @@ Future sendLogs( labelText: S.of(context).viewLogs, buttonAction: ButtonAction.second, onTap: () async { + // ignore: unawaited_futures showDialog( context: context, builder: (BuildContext context) { @@ -130,7 +132,7 @@ Future getZippedLogsFile(BuildContext context) async { tempPath + "/logs-${Configuration.instance.getUserID() ?? 0}.zip"; final encoder = ZipFileEncoder(); encoder.create(zipFilePath); - encoder.addDirectory(logsDirectory); + await encoder.addDirectory(logsDirectory); encoder.close(); await dialog.hide(); return zipFilePath; From 15480b3ecbcc178833315be8b3436fe4f9d6711b Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 21 Dec 2023 14:31:09 +0530 Subject: [PATCH 002/175] Created a method that returns search results as a stream of results, instead of awaiting for all results to be ready and sending it over --- lib/ui/viewer/search/search_widget.dart | 128 +++++++++++++++++++++++- 1 file changed, 127 insertions(+), 1 deletion(-) diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index 133f98e7e..c98cc449f 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -103,13 +103,13 @@ class SearchWidgetState extends State { final List allResults = await getSearchResultsForQuery(context, value); + /*checking if query == value to make sure that the results are from the current query and not from the previous query (race condition).*/ //checking if query == value to make sure that the latest query's result //(allResults) is passed to updateResult. Due to race condition, the previous //query's allResults could be passed to updateResult after the lastest query's //allResults is passed. - if (mounted && query == value) { final inheritedSearchResults = InheritedSearchResults.of(context); inheritedSearchResults.updateResults(allResults); @@ -274,6 +274,132 @@ class SearchWidgetState extends State { completer.complete(allResults); } + Stream> _getSearchResultsStream( + BuildContext context, + String query, + ) { + int resultCount = 0; + final maxResultCount = _isYearValid(query) ? 11 : 10; + final searchResultsStream = StreamController>(); + + if (query.isEmpty) { + searchResultsStream.sink.add([]); + searchResultsStream.close(); + return searchResultsStream.stream; + } + if (_isYearValid(query)) { + _searchService.getYearSearchResults(query).then((yearSearchResults) { + searchResultsStream.sink.add(yearSearchResults); + resultCount++; + if (resultCount == maxResultCount) { + searchResultsStream.close(); + } + }); + } + + _searchService.getHolidaySearchResults(context, query).then( + (holidayResults) { + searchResultsStream.sink.add(holidayResults); + resultCount++; + if (resultCount == maxResultCount) { + searchResultsStream.close(); + } + }, + ); + + _searchService.getFileTypeResults(context, query).then( + (fileTypeSearchResults) { + searchResultsStream.sink.add(fileTypeSearchResults); + resultCount++; + if (resultCount == maxResultCount) { + searchResultsStream.close(); + } + }, + ); + + _searchService.getCaptionAndNameResults(query).then( + (captionAndDisplayNameResult) { + searchResultsStream.sink.add(captionAndDisplayNameResult); + resultCount++; + if (resultCount == maxResultCount) { + searchResultsStream.close(); + } + }, + ); + + _searchService.getFileExtensionResults(query).then( + (fileExtnResult) { + searchResultsStream.sink.add(fileExtnResult); + resultCount++; + if (resultCount == maxResultCount) { + searchResultsStream.close(); + } + }, + ); + + _searchService.getLocationResults(query).then( + (locationResult) { + searchResultsStream.sink.add(locationResult); + resultCount++; + if (resultCount == maxResultCount) { + searchResultsStream.close(); + } + }, + ); + + _searchService.getCollectionSearchResults(query).then( + (collectionResults) { + searchResultsStream.sink.add(collectionResults); + resultCount++; + if (resultCount == maxResultCount) { + searchResultsStream.close(); + } + }, + ); + + _searchService.getMonthSearchResults(context, query).then( + (monthResults) { + searchResultsStream.sink.add(monthResults); + resultCount++; + if (resultCount == maxResultCount) { + searchResultsStream.close(); + } + }, + ); + + _searchService.getDateResults(context, query).then( + (possibleEvents) { + searchResultsStream.sink.add(possibleEvents); + resultCount++; + if (resultCount == maxResultCount) { + searchResultsStream.close(); + } + }, + ); + + _searchService.getMagicSearchResults(context, query).then( + (magicResults) { + searchResultsStream.sink.add(magicResults); + resultCount++; + if (resultCount == maxResultCount) { + searchResultsStream.close(); + } + }, + ); + + _searchService.getContactSearchResults(query).then( + (contactResults) { + searchResultsStream.sink.add(contactResults); + resultCount++; + if (resultCount == maxResultCount) { + searchResultsStream.close(); + } + }, + ); + + return searchResultsStream.stream; + } + bool _isYearValid(String year) { final yearAsInt = int.tryParse(year); //returns null if cannot be parsed return yearAsInt != null && yearAsInt <= currentYear; From b7486f3c3b30202b6dc33f01001bc617f03e5552 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 21 Dec 2023 17:41:55 +0530 Subject: [PATCH 003/175] Create new search results stream in every query change wih debouncer. Made the stream available in SearchSuggestionsWidget --- lib/models/typedefs.dart | 4 ++- lib/states/search_results_state.dart | 18 ++++++------- lib/ui/search_tab.dart | 25 +++++++++--------- lib/ui/viewer/search/search_widget.dart | 34 ++++++++++++++++++++----- 4 files changed, 53 insertions(+), 28 deletions(-) diff --git a/lib/models/typedefs.dart b/lib/models/typedefs.dart index b5624c1bf..d358180da 100644 --- a/lib/models/typedefs.dart +++ b/lib/models/typedefs.dart @@ -11,7 +11,9 @@ typedef VoidCallbackParamDouble = Function(double); typedef VoidCallbackParamBool = void Function(bool); typedef VoidCallbackParamListDouble = void Function(List); typedef VoidCallbackParamLocation = void Function(Location); -typedef VoidCallbackParamSearchResults = void Function(List); +typedef VoidCallbackParamSearchResutlsStream = void Function( + Stream>, +); typedef FutureVoidCallback = Future Function(); typedef FutureOrVoidCallback = FutureOr Function(); diff --git a/lib/states/search_results_state.dart b/lib/states/search_results_state.dart index 042f6f2a8..d7166ae64 100644 --- a/lib/states/search_results_state.dart +++ b/lib/states/search_results_state.dart @@ -14,29 +14,29 @@ class SearchResultsProvider extends StatefulWidget { } class _SearchResultsProviderState extends State { - var searchResults = []; + Stream>? searchResultsStream; @override Widget build(BuildContext context) { return InheritedSearchResults( - searchResults, + searchResultsStream, updateSearchResults, child: widget.child, ); } - void updateSearchResults(List newResult) { + void updateSearchResults(Stream> newStream) { setState(() { - searchResults = newResult; + searchResultsStream = newStream; }); } } class InheritedSearchResults extends InheritedWidget { - final List results; - final VoidCallbackParamSearchResults updateResults; + final Stream>? searchResultsStream; + final VoidCallbackParamSearchResutlsStream updateStream; const InheritedSearchResults( - this.results, - this.updateResults, { + this.searchResultsStream, + this.updateStream, { required super.child, super.key, }); @@ -48,6 +48,6 @@ class InheritedSearchResults extends InheritedWidget { @override bool updateShouldNotify(covariant InheritedSearchResults oldWidget) { - return results != oldWidget.results; + return searchResultsStream != oldWidget.searchResultsStream; } } diff --git a/lib/ui/search_tab.dart b/lib/ui/search_tab.dart index 719dc6e8f..aefe784f0 100644 --- a/lib/ui/search_tab.dart +++ b/lib/ui/search_tab.dart @@ -10,7 +10,6 @@ import "package:photos/ui/common/loading_widget.dart"; import "package:photos/ui/viewer/search/result/no_result_widget.dart"; import "package:photos/ui/viewer/search/search_section.dart"; import "package:photos/ui/viewer/search/search_suggestions.dart"; -import 'package:photos/ui/viewer/search/search_widget.dart'; import "package:photos/ui/viewer/search/tab_empty_state.dart"; class SearchTab extends StatefulWidget { @@ -21,22 +20,24 @@ class SearchTab extends StatefulWidget { } class _SearchTabState extends State { - var _searchResults = []; + // var _searchResults = []; + late Stream>? _searchResults; int index = 0; @override void didChangeDependencies() { super.didChangeDependencies(); - _searchResults = InheritedSearchResults.of(context).results; - if (_searchResults.isEmpty) { - if (isSearchQueryEmpty) { - index = 0; - } else { - index = 2; - } - } else { - index = 1; - } + _searchResults = InheritedSearchResults.of(context).searchResultsStream; + // if (_searchResults.isEmpty) { + // if (isSearchQueryEmpty) { + // index = 0; + // } else { + // index = 2; + // } + // } else { + // index = 1; + // } + index = 1; } @override diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index c98cc449f..2ce050a2a 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -101,8 +101,29 @@ class SearchWidgetState extends State { //latest query in global variable query = textController.text; - final List allResults = - await getSearchResultsForQuery(context, value); + // final List allResults = + // await getSearchResultsForQuery(context, value); + + _debouncer.run(() async { + final Stream> searchResultsStream = + _getSearchResultsStream(context, query); + + if (mounted && query == value) { + final inheritedSearchResults = InheritedSearchResults.of(context); + inheritedSearchResults.updateStream(searchResultsStream); + } + + // await for (final value in searchResultsStream) { + // value.forEach((element) { + // print( + // "----------------" + + // element.name() + + // " " + + // element.type().toString(), + // ); + // }); + // } + }); /*checking if query == value to make sure that the results are from the current query and not from the previous query (race condition).*/ @@ -110,10 +131,11 @@ class SearchWidgetState extends State { //(allResults) is passed to updateResult. Due to race condition, the previous //query's allResults could be passed to updateResult after the lastest query's //allResults is passed. - if (mounted && query == value) { - final inheritedSearchResults = InheritedSearchResults.of(context); - inheritedSearchResults.updateResults(allResults); - } + + // if (mounted && query == value) { + // final inheritedSearchResults = InheritedSearchResults.of(context); + // inheritedSearchResults.updateStream(allResults); + // } } @override From e5e33b0f7ea379a6a4d181cd5547728012805bcf Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 21 Dec 2023 18:25:16 +0530 Subject: [PATCH 004/175] show search results when querying from stream --- lib/ui/viewer/search/search_suggestions.dart | 151 +++++++++++-------- 1 file changed, 87 insertions(+), 64 deletions(-) diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index 22f6f76bd..209d92f39 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -1,19 +1,20 @@ +import "dart:async"; + import 'package:flutter/material.dart'; -import 'package:logging/logging.dart'; +import "package:logging/logging.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/events/clear_and_unfocus_search_bar_event.dart"; -import "package:photos/generated/l10n.dart"; -import 'package:photos/models/search/album_search_result.dart'; -import 'package:photos/models/search/generic_search_result.dart'; +import "package:photos/models/search/album_search_result.dart"; +import "package:photos/models/search/generic_search_result.dart"; import 'package:photos/models/search/search_result.dart'; import "package:photos/services/collections_service.dart"; -import "package:photos/theme/ente_theme.dart"; -import 'package:photos/ui/viewer/gallery/collection_page.dart'; -import 'package:photos/ui/viewer/search/result/search_result_widget.dart'; -import 'package:photos/utils/navigation_util.dart'; +import "package:photos/ui/viewer/gallery/collection_page.dart"; +import "package:photos/ui/viewer/search/result/search_result_widget.dart"; +import "package:photos/utils/navigation_util.dart"; class SearchSuggestionsWidget extends StatelessWidget { - final List results; + // final List results; + final Stream>? results; const SearchSuggestionsWidget( this.results, { @@ -22,9 +23,10 @@ class SearchSuggestionsWidget extends StatelessWidget { @override Widget build(BuildContext context) { - late final String title; - final resultsCount = results.length; - title = S.of(context).searchResultCount(resultsCount); + // late final String title; + // final resultsCount = results.length; + // title = S.of(context).searchResultCount(resultsCount); + final searchResultWidgets = []; return Scaffold( appBar: AppBar( leading: BackButton( @@ -35,62 +37,83 @@ class SearchSuggestionsWidget extends StatelessWidget { ), body: Padding( padding: const EdgeInsets.fromLTRB(12, 0, 12, 0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: getEnteTextTheme(context).largeBold, - ), - const SizedBox(height: 20), - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: ListView.separated( - itemBuilder: (context, index) { - final result = results[index]; - if (result is AlbumSearchResult) { - final AlbumSearchResult albumSearchResult = result; - return SearchResultWidget( - result, - resultCount: CollectionsService.instance.getFileCount( - albumSearchResult.collectionWithThumbnail.collection, - ), - onResultTap: () => routeToPage( - context, - CollectionPage( - albumSearchResult.collectionWithThumbnail, - tagPrefix: result.heroTag(), - ), + child: StreamBuilder( + stream: results, + builder: (context, snapshot) { + if (snapshot.hasData) { + final results = snapshot.data as List; + for (SearchResult result in results) { + searchResultWidgets.add(SearchResultsWidgetGenerator(result)); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // // Text( + // // title, + // // style: getEnteTextTheme(context).largeBold, + // // ), + // const SizedBox(height: 20), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: ListView.separated( + itemBuilder: (context, index) { + return searchResultWidgets[index]; + }, + separatorBuilder: (context, index) { + return const SizedBox(height: 12); + }, + itemCount: searchResultWidgets.length, + physics: const BouncingScrollPhysics(), + padding: EdgeInsets.only( + bottom: (MediaQuery.sizeOf(context).height / 2) + 50, ), - ); - } else if (result is GenericSearchResult) { - return SearchResultWidget( - result, - onResultTap: result.onResultTap != null - ? () => result.onResultTap!(context) - : null, - ); - } else { - Logger('SearchSuggestionsWidget') - .info("Invalid/Unsupported value"); - return const SizedBox.shrink(); - } - }, - padding: EdgeInsets.only( - bottom: (MediaQuery.sizeOf(context).height / 2) + 50, + ), + ), ), - separatorBuilder: (context, index) { - return const SizedBox(height: 12); - }, - itemCount: results.length, - physics: const BouncingScrollPhysics(), - ), - ), - ), - ], + ], + ); + } else { + return const SizedBox.shrink(); + } + }, ), ), ); } } + +class SearchResultsWidgetGenerator extends StatelessWidget { + final SearchResult result; + const SearchResultsWidgetGenerator(this.result, {super.key}); + + @override + Widget build(BuildContext context) { + if (result is AlbumSearchResult) { + final AlbumSearchResult albumSearchResult = result as AlbumSearchResult; + return SearchResultWidget( + result, + resultCount: CollectionsService.instance.getFileCount( + albumSearchResult.collectionWithThumbnail.collection, + ), + onResultTap: () => routeToPage( + context, + CollectionPage( + albumSearchResult.collectionWithThumbnail, + tagPrefix: result.heroTag(), + ), + ), + ); + } else if (result is GenericSearchResult) { + return SearchResultWidget( + result, + onResultTap: (result as GenericSearchResult).onResultTap != null + ? () => (result as GenericSearchResult).onResultTap!(context) + : null, + ); + } else { + Logger('SearchResultsWidgetGenerator').info("Invalid/Unsupported value"); + return const SizedBox.shrink(); + } + } +} From 3de3b77ae05a87176fe1b652d8020f6a5853202b Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 22 Dec 2023 18:44:24 +0530 Subject: [PATCH 005/175] use better variable name --- lib/ui/viewer/search/search_widget.dart | 87 ++++++++++++++++--------- 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index 2ce050a2a..372e8cc5d 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -108,20 +108,27 @@ class SearchWidgetState extends State { final Stream> searchResultsStream = _getSearchResultsStream(context, query); - if (mounted && query == value) { + if (mounted) { + print( + "Updating to new stream (${searchResultsStream.hashCode}) with query: $query", + ); final inheritedSearchResults = InheritedSearchResults.of(context); inheritedSearchResults.updateStream(searchResultsStream); } // await for (final value in searchResultsStream) { + // print( + // "Recieved event from stream ${searchResultsStream.hashCode} -------------- -------------- -------------- -------------- --------------", + // ); + // value.forEach((element) { // print( - // "----------------" + - // element.name() + - // " " + - // element.type().toString(), + // "---------" + element.name() + " " + element.type().toString(), // ); // }); + // print( + // "End -------------- -------------- -------------- -------------- --------------", + // ); // } }); @@ -302,124 +309,140 @@ class SearchWidgetState extends State { ) { int resultCount = 0; final maxResultCount = _isYearValid(query) ? 11 : 10; - final searchResultsStream = StreamController>(); + final streamController = StreamController>(); if (query.isEmpty) { - searchResultsStream.sink.add([]); - searchResultsStream.close(); - return searchResultsStream.stream; + streamController.sink.add([]); + streamController.close(); + return streamController.stream; } if (_isYearValid(query)) { _searchService.getYearSearchResults(query).then((yearSearchResults) { - searchResultsStream.sink.add(yearSearchResults); + streamController.sink.add(yearSearchResults); resultCount++; + if (resultCount == maxResultCount) { - searchResultsStream.close(); + streamController.close(); } }); } _searchService.getHolidaySearchResults(context, query).then( (holidayResults) { - searchResultsStream.sink.add(holidayResults); + streamController.sink.add(holidayResults); resultCount++; + if (resultCount == maxResultCount) { - searchResultsStream.close(); + streamController.close(); } }, ); _searchService.getFileTypeResults(context, query).then( (fileTypeSearchResults) { - searchResultsStream.sink.add(fileTypeSearchResults); + streamController.sink.add(fileTypeSearchResults); resultCount++; + if (resultCount == maxResultCount) { - searchResultsStream.close(); + streamController.close(); } }, ); _searchService.getCaptionAndNameResults(query).then( (captionAndDisplayNameResult) { - searchResultsStream.sink.add(captionAndDisplayNameResult); + streamController.sink.add(captionAndDisplayNameResult); resultCount++; + if (resultCount == maxResultCount) { - searchResultsStream.close(); + streamController.close(); } }, ); _searchService.getFileExtensionResults(query).then( (fileExtnResult) { - searchResultsStream.sink.add(fileExtnResult); + streamController.sink.add(fileExtnResult); resultCount++; + if (resultCount == maxResultCount) { - searchResultsStream.close(); + streamController.close(); } }, ); _searchService.getLocationResults(query).then( (locationResult) { - searchResultsStream.sink.add(locationResult); + streamController.sink.add(locationResult); resultCount++; + if (resultCount == maxResultCount) { - searchResultsStream.close(); + streamController.close(); } }, ); _searchService.getCollectionSearchResults(query).then( (collectionResults) { - searchResultsStream.sink.add(collectionResults); + print("is stream closed : ${streamController.isClosed}"); + + streamController.sink.add(collectionResults); resultCount++; + print("------ $resultCount $maxResultCount"); + print( + "results for collection search: ${collectionResults.length}, query : $query", + ); if (resultCount == maxResultCount) { - searchResultsStream.close(); + streamController.close(); } }, ); _searchService.getMonthSearchResults(context, query).then( (monthResults) { - searchResultsStream.sink.add(monthResults); + streamController.sink.add(monthResults); resultCount++; + if (resultCount == maxResultCount) { - searchResultsStream.close(); + streamController.close(); } }, ); _searchService.getDateResults(context, query).then( (possibleEvents) { - searchResultsStream.sink.add(possibleEvents); + streamController.sink.add(possibleEvents); resultCount++; + if (resultCount == maxResultCount) { - searchResultsStream.close(); + streamController.close(); } }, ); _searchService.getMagicSearchResults(context, query).then( (magicResults) { - searchResultsStream.sink.add(magicResults); + streamController.sink.add(magicResults); resultCount++; + if (resultCount == maxResultCount) { - searchResultsStream.close(); + streamController.close(); } }, ); _searchService.getContactSearchResults(query).then( (contactResults) { - searchResultsStream.sink.add(contactResults); + streamController.sink.add(contactResults); resultCount++; + if (resultCount == maxResultCount) { - searchResultsStream.close(); + streamController.close(); } }, ); - return searchResultsStream.stream; + return streamController.stream; } bool _isYearValid(String year) { From 7f3dd9b691e94583c8519557a89c12eb34d0585f Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 22 Dec 2023 18:45:13 +0530 Subject: [PATCH 006/175] fix: results from pervious stream coming up on UI --- lib/ui/viewer/search/search_suggestions.dart | 44 ++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index 209d92f39..592129f6b 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -12,7 +12,7 @@ import "package:photos/ui/viewer/gallery/collection_page.dart"; import "package:photos/ui/viewer/search/result/search_result_widget.dart"; import "package:photos/utils/navigation_util.dart"; -class SearchSuggestionsWidget extends StatelessWidget { +class SearchSuggestionsWidget extends StatefulWidget { // final List results; final Stream>? results; @@ -21,12 +21,43 @@ class SearchSuggestionsWidget extends StatelessWidget { Key? key, }) : super(key: key); + @override + State createState() => + _SearchSuggestionsWidgetState(); +} + +class _SearchSuggestionsWidgetState extends State { + var searchResultWidgets = []; + late Stream>? resultsStream; + @override + initState() { + super.initState(); + resultsStream = widget.results; + } + + @override + didUpdateWidget(SearchSuggestionsWidget oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.results != oldWidget.results) { + setState(() { + print( + "____ in didUpdateWidget. Updating stream from ${resultsStream.hashCode} to ${widget.results.hashCode}", + ); + searchResultWidgets.clear(); + resultsStream = widget.results!; + }); + } + } + @override Widget build(BuildContext context) { + print( + "_______ rebuiding SearchSuggestionWidget with stream : ${resultsStream.hashCode}", + ); + // return const SizedBox.shrink(); // late final String title; // final resultsCount = results.length; // title = S.of(context).searchResultCount(resultsCount); - final searchResultWidgets = []; return Scaffold( appBar: AppBar( leading: BackButton( @@ -38,9 +69,16 @@ class SearchSuggestionsWidget extends StatelessWidget { body: Padding( padding: const EdgeInsets.fromLTRB(12, 0, 12, 0), child: StreamBuilder( - stream: results, + key: UniqueKey(), + stream: resultsStream, builder: (context, snapshot) { + print("----------- ${snapshot.connectionState}"); if (snapshot.hasData) { + if (snapshot.data!.isNotEmpty) { + print("---------- ${snapshot.data!.first.name()}"); + } else { + print("---------- empty data"); + } final results = snapshot.data as List; for (SearchResult result in results) { searchResultWidgets.add(SearchResultsWidgetGenerator(result)); From e2e240459f6bbff48f05cd559f02e156d6c28b04 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 23 Dec 2023 18:48:28 +0530 Subject: [PATCH 007/175] add logs for debugging --- lib/states/search_results_state.dart | 4 +++ lib/ui/search_tab.dart | 4 +++ lib/ui/viewer/search/search_widget.dart | 40 +++++++++++++++++-------- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/lib/states/search_results_state.dart b/lib/states/search_results_state.dart index d7166ae64..18a4ade4b 100644 --- a/lib/states/search_results_state.dart +++ b/lib/states/search_results_state.dart @@ -26,6 +26,10 @@ class _SearchResultsProviderState extends State { void updateSearchResults(Stream> newStream) { setState(() { + print( + "_____updating stream from ${searchResultsStream.hashCode} to ${newStream.hashCode} in inherited widget", + ); + searchResultsStream = null; searchResultsStream = newStream; }); } diff --git a/lib/ui/search_tab.dart b/lib/ui/search_tab.dart index aefe784f0..75d0de7cb 100644 --- a/lib/ui/search_tab.dart +++ b/lib/ui/search_tab.dart @@ -28,6 +28,9 @@ class _SearchTabState extends State { void didChangeDependencies() { super.didChangeDependencies(); _searchResults = InheritedSearchResults.of(context).searchResultsStream; + print( + "____ Updating dependencies for SearchTabState. New stream : ${_searchResults.hashCode}", + ); // if (_searchResults.isEmpty) { // if (isSearchQueryEmpty) { // index = 0; @@ -42,6 +45,7 @@ class _SearchTabState extends State { @override Widget build(BuildContext context) { + print("_____ rebuilding SearchTab with stream: ${_searchResults.hashCode}"); return AllSectionsExamplesProvider( child: FadeIndexedStack( duration: const Duration(milliseconds: 150), diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index 372e8cc5d..e8a60abc1 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -320,7 +320,7 @@ class SearchWidgetState extends State { _searchService.getYearSearchResults(query).then((yearSearchResults) { streamController.sink.add(yearSearchResults); resultCount++; - + print('-----------yearSearchResults: ${yearSearchResults.length}'); if (resultCount == maxResultCount) { streamController.close(); } @@ -330,9 +330,13 @@ class SearchWidgetState extends State { _searchService.getHolidaySearchResults(context, query).then( (holidayResults) { streamController.sink.add(holidayResults); + resultCount++; + print('------------holidayResults: ${holidayResults.length}'); + if (resultCount == maxResultCount) { + print("------- closing stream from holiday results"); streamController.close(); } }, @@ -342,8 +346,11 @@ class SearchWidgetState extends State { (fileTypeSearchResults) { streamController.sink.add(fileTypeSearchResults); resultCount++; - + print( + '----------fileTypeSearchResults: ${fileTypeSearchResults.length}', + ); if (resultCount == maxResultCount) { + print("------- closing stream from file type results"); streamController.close(); } }, @@ -353,8 +360,11 @@ class SearchWidgetState extends State { (captionAndDisplayNameResult) { streamController.sink.add(captionAndDisplayNameResult); resultCount++; - + print( + '--------------captionAndDisplayNameResult: ${captionAndDisplayNameResult.length}', + ); if (resultCount == maxResultCount) { + print("------- closing stream from caption results"); streamController.close(); } }, @@ -364,8 +374,9 @@ class SearchWidgetState extends State { (fileExtnResult) { streamController.sink.add(fileExtnResult); resultCount++; - + print('-----------fileExtnResult: ${fileExtnResult.length}'); if (resultCount == maxResultCount) { + print("------- closing stream from file extn results"); streamController.close(); } }, @@ -375,8 +386,9 @@ class SearchWidgetState extends State { (locationResult) { streamController.sink.add(locationResult); resultCount++; - + print('-------------locationResult: ${locationResult.length}'); if (resultCount == maxResultCount) { + print("------- closing stream from location results"); streamController.close(); } }, @@ -384,15 +396,15 @@ class SearchWidgetState extends State { _searchService.getCollectionSearchResults(query).then( (collectionResults) { - print("is stream closed : ${streamController.isClosed}"); - streamController.sink.add(collectionResults); resultCount++; - print("------ $resultCount $maxResultCount"); + + print('--------------collectionResults: ${collectionResults.length}'); print( "results for collection search: ${collectionResults.length}, query : $query", ); if (resultCount == maxResultCount) { + print("------- closing stream from collection results"); streamController.close(); } }, @@ -402,8 +414,9 @@ class SearchWidgetState extends State { (monthResults) { streamController.sink.add(monthResults); resultCount++; - + print('-------------monthResults: ${monthResults.length}'); if (resultCount == maxResultCount) { + print("------- closing stream from month results"); streamController.close(); } }, @@ -413,8 +426,9 @@ class SearchWidgetState extends State { (possibleEvents) { streamController.sink.add(possibleEvents); resultCount++; - + print('-------------possibleEvents: ${possibleEvents.length}'); if (resultCount == maxResultCount) { + print("------- closing stream from possible events results"); streamController.close(); } }, @@ -424,8 +438,9 @@ class SearchWidgetState extends State { (magicResults) { streamController.sink.add(magicResults); resultCount++; - + print('------------magicResults: ${magicResults.length}'); if (resultCount == maxResultCount) { + print("------- closing stream from magic results"); streamController.close(); } }, @@ -435,8 +450,9 @@ class SearchWidgetState extends State { (contactResults) { streamController.sink.add(contactResults); resultCount++; - + print('-------------contactResults: ${contactResults.length}'); if (resultCount == maxResultCount) { + print("------- closing stream from contact results"); streamController.close(); } }, From d277d8ca23ce7bfe1e0c9d60a12efe5a87fe1970 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 23 Dec 2023 19:38:21 +0530 Subject: [PATCH 008/175] Use custom lossless widget to rebuild with stream data instead of relying on lossy StreamBuilder --- lib/ui/viewer/search/search_suggestions.dart | 119 +++++++++++-------- 1 file changed, 70 insertions(+), 49 deletions(-) diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index 592129f6b..82255dd8a 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -13,7 +13,6 @@ import "package:photos/ui/viewer/search/result/search_result_widget.dart"; import "package:photos/utils/navigation_util.dart"; class SearchSuggestionsWidget extends StatefulWidget { - // final List results; final Stream>? results; const SearchSuggestionsWidget( @@ -27,12 +26,19 @@ class SearchSuggestionsWidget extends StatefulWidget { } class _SearchSuggestionsWidgetState extends State { - var searchResultWidgets = []; late Stream>? resultsStream; + final queueOfEvents = >[]; + var searchResultWidgets = []; + StreamSubscription>? subscription; + Timer? timer; @override initState() { super.initState(); resultsStream = widget.results; + subscription = resultsStream?.listen((event) { + queueOfEvents.add(event); + }); + //ondone, cancel subscription, get the total number of results and show in UI } @override @@ -44,11 +50,46 @@ class _SearchSuggestionsWidgetState extends State { "____ in didUpdateWidget. Updating stream from ${resultsStream.hashCode} to ${widget.results.hashCode}", ); searchResultWidgets.clear(); - resultsStream = widget.results!; + releaseResources(); + resultsStream = widget.results; + subscription = resultsStream?.listen((event) { + queueOfEvents.add(event); + }); + generateResultWidgetsInIntervalsFromQueue(); }); } } + void releaseResources() { + subscription?.cancel(); + timer?.cancel(); + } + + ///This method generates searchResultsWidgets from the queueOfEvents by checking + ///every 40ms if the queue is empty or not. If the queue is not empty, it + ///generates the widgets and clears the queue and updates the UI. + void generateResultWidgetsInIntervalsFromQueue() { + timer = Timer.periodic(const Duration(milliseconds: 40), (timer) { + if (queueOfEvents.isNotEmpty) { + for (List event in queueOfEvents) { + for (SearchResult result in event) { + searchResultWidgets.add( + SearchResultsWidgetGenerator(result), + ); + } + } + queueOfEvents.clear(); + setState(() {}); + } + }); + } + + @override + void dispose() { + releaseResources(); + super.dispose(); + } + @override Widget build(BuildContext context) { print( @@ -68,53 +109,33 @@ class _SearchSuggestionsWidgetState extends State { ), body: Padding( padding: const EdgeInsets.fromLTRB(12, 0, 12, 0), - child: StreamBuilder( - key: UniqueKey(), - stream: resultsStream, - builder: (context, snapshot) { - print("----------- ${snapshot.connectionState}"); - if (snapshot.hasData) { - if (snapshot.data!.isNotEmpty) { - print("---------- ${snapshot.data!.first.name()}"); - } else { - print("---------- empty data"); - } - final results = snapshot.data as List; - for (SearchResult result in results) { - searchResultWidgets.add(SearchResultsWidgetGenerator(result)); - } - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // // Text( - // // title, - // // style: getEnteTextTheme(context).largeBold, - // // ), - // const SizedBox(height: 20), - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: ListView.separated( - itemBuilder: (context, index) { - return searchResultWidgets[index]; - }, - separatorBuilder: (context, index) { - return const SizedBox(height: 12); - }, - itemCount: searchResultWidgets.length, - physics: const BouncingScrollPhysics(), - padding: EdgeInsets.only( - bottom: (MediaQuery.sizeOf(context).height / 2) + 50, - ), - ), - ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // // Text( + // // title, + // // style: getEnteTextTheme(context).largeBold, + // // ), + // const SizedBox(height: 20), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: ListView.separated( + itemBuilder: (context, index) { + return searchResultWidgets[index]; + }, + separatorBuilder: (context, index) { + return const SizedBox(height: 12); + }, + itemCount: searchResultWidgets.length, + physics: const BouncingScrollPhysics(), + padding: EdgeInsets.only( + bottom: (MediaQuery.sizeOf(context).height / 2) + 50, ), - ], - ); - } else { - return const SizedBox.shrink(); - } - }, + ), + ), + ), + ], ), ), ); From 2192dc6151c80fb769fecac9dcc4fabfaab45920 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 23 Dec 2023 19:41:20 +0530 Subject: [PATCH 009/175] added comments --- lib/ui/viewer/search/search_suggestions.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index 82255dd8a..756cb47c2 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -12,6 +12,10 @@ import "package:photos/ui/viewer/gallery/collection_page.dart"; import "package:photos/ui/viewer/search/result/search_result_widget.dart"; import "package:photos/utils/navigation_util.dart"; +///Not using StreamBuilder in this widget for rebuilding on every new event as +///StreamBuilder is not lossless. It misses some events if the stream fires too +///fast. Instead, we usi a queue to store the events and then generate the +///widgets from the queue at regular intervals. class SearchSuggestionsWidget extends StatefulWidget { final Stream>? results; @@ -66,10 +70,10 @@ class _SearchSuggestionsWidgetState extends State { } ///This method generates searchResultsWidgets from the queueOfEvents by checking - ///every 40ms if the queue is empty or not. If the queue is not empty, it + ///every 60ms if the queue is empty or not. If the queue is not empty, it ///generates the widgets and clears the queue and updates the UI. void generateResultWidgetsInIntervalsFromQueue() { - timer = Timer.periodic(const Duration(milliseconds: 40), (timer) { + timer = Timer.periodic(const Duration(milliseconds: 60), (timer) { if (queueOfEvents.isNotEmpty) { for (List event in queueOfEvents) { for (SearchResult result in event) { From 30262e202b6e53c24788feef5b023f285b5ae1b9 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 23 Dec 2023 19:48:26 +0530 Subject: [PATCH 010/175] added animation to search results when they appear --- lib/ui/viewer/search/search_suggestions.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index 756cb47c2..37fda7a2e 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -1,6 +1,7 @@ import "dart:async"; import 'package:flutter/material.dart'; +import "package:flutter_animate/flutter_animate.dart"; import "package:logging/logging.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/events/clear_and_unfocus_search_bar_event.dart"; @@ -73,12 +74,15 @@ class _SearchSuggestionsWidgetState extends State { ///every 60ms if the queue is empty or not. If the queue is not empty, it ///generates the widgets and clears the queue and updates the UI. void generateResultWidgetsInIntervalsFromQueue() { - timer = Timer.periodic(const Duration(milliseconds: 60), (timer) { + timer = Timer.periodic(const Duration(milliseconds: 50), (timer) { if (queueOfEvents.isNotEmpty) { for (List event in queueOfEvents) { for (SearchResult result in event) { searchResultWidgets.add( - SearchResultsWidgetGenerator(result), + SearchResultsWidgetGenerator(result).animate().fadeIn( + duration: const Duration(milliseconds: 80), + curve: Curves.easeIn, + ), ); } } From 7c94c579eaa64c2434e6656c60ab9e8fa42ccdd1 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 23 Dec 2023 21:26:29 +0530 Subject: [PATCH 011/175] temp step 1 --- lib/ui/search_tab.dart | 35 ++--------- lib/ui/viewer/search/search_suggestions.dart | 62 +++++++++++--------- lib/ui/viewer/search/search_widget.dart | 13 ++-- 3 files changed, 44 insertions(+), 66 deletions(-) diff --git a/lib/ui/search_tab.dart b/lib/ui/search_tab.dart index 75d0de7cb..a784a24e9 100644 --- a/lib/ui/search_tab.dart +++ b/lib/ui/search_tab.dart @@ -2,10 +2,8 @@ import "package:fade_indexed_stack/fade_indexed_stack.dart"; import "package:flutter/material.dart"; import "package:flutter_animate/flutter_animate.dart"; import "package:photos/core/constants.dart"; -import "package:photos/models/search/search_result.dart"; import "package:photos/models/search/search_types.dart"; import "package:photos/states/all_sections_examples_state.dart"; -import "package:photos/states/search_results_state.dart"; import "package:photos/ui/common/loading_widget.dart"; import "package:photos/ui/viewer/search/result/no_result_widget.dart"; import "package:photos/ui/viewer/search/search_section.dart"; @@ -20,40 +18,19 @@ class SearchTab extends StatefulWidget { } class _SearchTabState extends State { - // var _searchResults = []; - late Stream>? _searchResults; - int index = 0; - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - _searchResults = InheritedSearchResults.of(context).searchResultsStream; - print( - "____ Updating dependencies for SearchTabState. New stream : ${_searchResults.hashCode}", - ); - // if (_searchResults.isEmpty) { - // if (isSearchQueryEmpty) { - // index = 0; - // } else { - // index = 2; - // } - // } else { - // index = 1; - // } - index = 1; - } + int index = 1; @override Widget build(BuildContext context) { - print("_____ rebuilding SearchTab with stream: ${_searchResults.hashCode}"); + // print("_____ rebuilding SearchTab with stream: ${_searchResults.hashCode}"); return AllSectionsExamplesProvider( child: FadeIndexedStack( duration: const Duration(milliseconds: 150), index: index, - children: [ - const AllSearchSections(), - SearchSuggestionsWidget(_searchResults), - const NoResultWidget(), + children: const [ + AllSearchSections(), + SearchSuggestionsWidget(), + NoResultWidget(), ], ), ); diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index 37fda7a2e..f8092c62e 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -11,6 +11,7 @@ import 'package:photos/models/search/search_result.dart'; import "package:photos/services/collections_service.dart"; import "package:photos/ui/viewer/gallery/collection_page.dart"; import "package:photos/ui/viewer/search/result/search_result_widget.dart"; +import "package:photos/ui/viewer/search/search_widget.dart"; import "package:photos/utils/navigation_util.dart"; ///Not using StreamBuilder in this widget for rebuilding on every new event as @@ -18,10 +19,7 @@ import "package:photos/utils/navigation_util.dart"; ///fast. Instead, we usi a queue to store the events and then generate the ///widgets from the queue at regular intervals. class SearchSuggestionsWidget extends StatefulWidget { - final Stream>? results; - - const SearchSuggestionsWidget( - this.results, { + const SearchSuggestionsWidget({ Key? key, }) : super(key: key); @@ -31,38 +29,46 @@ class SearchSuggestionsWidget extends StatefulWidget { } class _SearchSuggestionsWidgetState extends State { - late Stream>? resultsStream; + Stream>? resultsStream; final queueOfEvents = >[]; var searchResultWidgets = []; StreamSubscription>? subscription; Timer? timer; - @override - initState() { - super.initState(); - resultsStream = widget.results; - subscription = resultsStream?.listen((event) { - queueOfEvents.add(event); - }); - //ondone, cancel subscription, get the total number of results and show in UI - } + + // @override + // void didChangeDependencies() { + // super.didChangeDependencies(); + + // searchResultWidgets.clear(); + // releaseResources(); + // resultsStream = InheritedSearchResults.of(context).searchResultsStream; + // subscription = resultsStream?.listen((event) { + // if (event.isNotEmpty) { + // //update a index value notifier for indexed stack here and rebuild the indexedStack widget. + // //Also, add dependecy to inherited widget on changing stream in this widget and not on the serach tab. + // queueOfEvents.add(event); + // } + // }); + // generateResultWidgetsInIntervalsFromQueue(); + // } @override - didUpdateWidget(SearchSuggestionsWidget oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.results != oldWidget.results) { - setState(() { - print( - "____ in didUpdateWidget. Updating stream from ${resultsStream.hashCode} to ${widget.results.hashCode}", - ); - searchResultWidgets.clear(); - releaseResources(); - resultsStream = widget.results; - subscription = resultsStream?.listen((event) { + void initState() { + super.initState(); + SearchWidgetState.searchResultsStreamNotifier.addListener(() { + final value = SearchWidgetState.searchResultsStreamNotifier.value; + searchResultWidgets.clear(); + releaseResources(); + resultsStream = value; + subscription = resultsStream?.listen((event) { + if (event.isNotEmpty) { + //update a index value notifier for indexed stack here and rebuild the indexedStack widget. + //Also, add dependecy to inherited widget on changing stream in this widget and not on the serach tab. queueOfEvents.add(event); - }); - generateResultWidgetsInIntervalsFromQueue(); + } }); - } + generateResultWidgetsInIntervalsFromQueue(); + }); } void releaseResources() { diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index e8a60abc1..d8ec50f35 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -8,7 +8,6 @@ import "package:photos/events/clear_and_unfocus_search_bar_event.dart"; import "package:photos/events/tab_changed_event.dart"; import "package:photos/models/search/search_result.dart"; import "package:photos/services/search_service.dart"; -import "package:photos/states/search_results_state.dart"; import "package:photos/theme/ente_theme.dart"; import "package:photos/ui/viewer/search/search_suffix_icon_widget.dart"; import "package:photos/utils/date_time_util.dart"; @@ -24,6 +23,8 @@ class SearchWidget extends StatefulWidget { } class SearchWidgetState extends State { + static ValueNotifier>?> + searchResultsStreamNotifier = ValueNotifier(null); static String query = ""; final _searchService = SearchService.instance; final _debouncer = Debouncer(const Duration(milliseconds: 200)); @@ -105,15 +106,9 @@ class SearchWidgetState extends State { // await getSearchResultsForQuery(context, value); _debouncer.run(() async { - final Stream> searchResultsStream = - _getSearchResultsStream(context, query); - if (mounted) { - print( - "Updating to new stream (${searchResultsStream.hashCode}) with query: $query", - ); - final inheritedSearchResults = InheritedSearchResults.of(context); - inheritedSearchResults.updateStream(searchResultsStream); + searchResultsStreamNotifier.value = + _getSearchResultsStream(context, query); } // await for (final value in searchResultsStream) { From 234c1ad15e711c23e8f73ba98ab847b69225d16d Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 23 Dec 2023 22:18:40 +0530 Subject: [PATCH 012/175] Simplify state management on search suggestion screen to get rid of an edge case bug + add comments + use better variable names --- lib/states/search_results_state.dart | 57 ------- lib/ui/tabs/home_widget.dart | 159 +++++++++---------- lib/ui/viewer/search/search_suggestions.dart | 45 ++---- 3 files changed, 94 insertions(+), 167 deletions(-) delete mode 100644 lib/states/search_results_state.dart diff --git a/lib/states/search_results_state.dart b/lib/states/search_results_state.dart deleted file mode 100644 index 18a4ade4b..000000000 --- a/lib/states/search_results_state.dart +++ /dev/null @@ -1,57 +0,0 @@ -import "package:flutter/cupertino.dart"; -import "package:photos/models/search/search_result.dart"; -import "package:photos/models/typedefs.dart"; - -class SearchResultsProvider extends StatefulWidget { - final Widget child; - const SearchResultsProvider({ - required this.child, - super.key, - }); - - @override - State createState() => _SearchResultsProviderState(); -} - -class _SearchResultsProviderState extends State { - Stream>? searchResultsStream; - @override - Widget build(BuildContext context) { - return InheritedSearchResults( - searchResultsStream, - updateSearchResults, - child: widget.child, - ); - } - - void updateSearchResults(Stream> newStream) { - setState(() { - print( - "_____updating stream from ${searchResultsStream.hashCode} to ${newStream.hashCode} in inherited widget", - ); - searchResultsStream = null; - searchResultsStream = newStream; - }); - } -} - -class InheritedSearchResults extends InheritedWidget { - final Stream>? searchResultsStream; - final VoidCallbackParamSearchResutlsStream updateStream; - const InheritedSearchResults( - this.searchResultsStream, - this.updateStream, { - required super.child, - super.key, - }); - - static InheritedSearchResults of(BuildContext context) { - return context - .dependOnInheritedWidgetOfExactType()!; - } - - @override - bool updateShouldNotify(covariant InheritedSearchResults oldWidget) { - return searchResultsStream != oldWidget.searchResultsStream; - } -} diff --git a/lib/ui/tabs/home_widget.dart b/lib/ui/tabs/home_widget.dart index e2ccdee87..9621578bd 100644 --- a/lib/ui/tabs/home_widget.dart +++ b/lib/ui/tabs/home_widget.dart @@ -33,7 +33,6 @@ import 'package:photos/services/local_sync_service.dart'; import "package:photos/services/notification_service.dart"; import 'package:photos/services/update_service.dart'; import 'package:photos/services/user_service.dart'; -import "package:photos/states/search_results_state.dart"; import 'package:photos/states/user_details_state.dart'; import 'package:photos/theme/colors.dart'; import "package:photos/theme/effects.dart"; @@ -393,90 +392,88 @@ class _HomeWidgetState extends State { !LocalSyncService.instance.hasGrantedLimitedPermissions() && CollectionsService.instance.getActiveCollections().isEmpty; - return SearchResultsProvider( - child: Stack( - children: [ - Builder( - builder: (context) { - return ExtentsPageView( - onPageChanged: (page) { - Bus.instance.fire( - TabChangedEvent( - page, - TabChangedEventSource.pageView, - ), - ); - }, - controller: _pageController, - openDrawer: Scaffold.of(context).openDrawer, - physics: const BouncingScrollPhysics(), - children: [ - _showShowBackupHook - ? const StartBackupHookWidget(headerWidget: _headerWidget) - : HomeGalleryWidget( - header: _headerWidget, - footer: const SizedBox( - height: 160, - ), - selectedFiles: _selectedFiles, - ), - _userCollectionsTab, - _sharedCollectionTab, - _searchTab, - ], - ); - }, - ), - Align( - alignment: Alignment.bottomCenter, - child: ValueListenableBuilder( - valueListenable: isOnSearchTabNotifier, - builder: (context, value, child) { - return Container( - decoration: value - ? BoxDecoration( - color: getEnteColorScheme(context).backgroundElevated, - boxShadow: shadowFloatFaintLight, - ) - : null, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - value - ? const SearchWidget() - .animate() - .fadeIn( - duration: const Duration(milliseconds: 225), - curve: Curves.easeInOutSine, - ) - .scale( - begin: const Offset(0.8, 0.8), - end: const Offset(1, 1), - duration: const Duration( - milliseconds: 225, - ), - curve: Curves.easeInOutSine, - ) - .slide( - begin: const Offset(0, 0.4), - curve: Curves.easeInOutSine, - duration: const Duration( - milliseconds: 225, - ), - ) - : const SizedBox.shrink(), - HomeBottomNavigationBar( - _selectedFiles, - selectedTabIndex: _selectedTabIndex, - ), - ], + return Stack( + children: [ + Builder( + builder: (context) { + return ExtentsPageView( + onPageChanged: (page) { + Bus.instance.fire( + TabChangedEvent( + page, + TabChangedEventSource.pageView, ), ); }, - ), + controller: _pageController, + openDrawer: Scaffold.of(context).openDrawer, + physics: const BouncingScrollPhysics(), + children: [ + _showShowBackupHook + ? const StartBackupHookWidget(headerWidget: _headerWidget) + : HomeGalleryWidget( + header: _headerWidget, + footer: const SizedBox( + height: 160, + ), + selectedFiles: _selectedFiles, + ), + _userCollectionsTab, + _sharedCollectionTab, + _searchTab, + ], + ); + }, + ), + Align( + alignment: Alignment.bottomCenter, + child: ValueListenableBuilder( + valueListenable: isOnSearchTabNotifier, + builder: (context, value, child) { + return Container( + decoration: value + ? BoxDecoration( + color: getEnteColorScheme(context).backgroundElevated, + boxShadow: shadowFloatFaintLight, + ) + : null, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + value + ? const SearchWidget() + .animate() + .fadeIn( + duration: const Duration(milliseconds: 225), + curve: Curves.easeInOutSine, + ) + .scale( + begin: const Offset(0.8, 0.8), + end: const Offset(1, 1), + duration: const Duration( + milliseconds: 225, + ), + curve: Curves.easeInOutSine, + ) + .slide( + begin: const Offset(0, 0.4), + curve: Curves.easeInOutSine, + duration: const Duration( + milliseconds: 225, + ), + ) + : const SizedBox.shrink(), + HomeBottomNavigationBar( + _selectedFiles, + selectedTabIndex: _selectedTabIndex, + ), + ], + ), + ); + }, ), - ], - ), + ), + ], ); } diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index f8092c62e..07ffc549c 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -30,43 +30,30 @@ class SearchSuggestionsWidget extends StatefulWidget { class _SearchSuggestionsWidgetState extends State { Stream>? resultsStream; - final queueOfEvents = >[]; + final queueOfSearchResults = >[]; var searchResultWidgets = []; StreamSubscription>? subscription; Timer? timer; - // @override - // void didChangeDependencies() { - // super.didChangeDependencies(); - - // searchResultWidgets.clear(); - // releaseResources(); - // resultsStream = InheritedSearchResults.of(context).searchResultsStream; - // subscription = resultsStream?.listen((event) { - // if (event.isNotEmpty) { - // //update a index value notifier for indexed stack here and rebuild the indexedStack widget. - // //Also, add dependecy to inherited widget on changing stream in this widget and not on the serach tab. - // queueOfEvents.add(event); - // } - // }); - // generateResultWidgetsInIntervalsFromQueue(); - // } - @override void initState() { super.initState(); SearchWidgetState.searchResultsStreamNotifier.addListener(() { - final value = SearchWidgetState.searchResultsStreamNotifier.value; + final resultsStream = SearchWidgetState.searchResultsStreamNotifier.value; + searchResultWidgets.clear(); releaseResources(); - resultsStream = value; - subscription = resultsStream?.listen((event) { - if (event.isNotEmpty) { - //update a index value notifier for indexed stack here and rebuild the indexedStack widget. - //Also, add dependecy to inherited widget on changing stream in this widget and not on the serach tab. - queueOfEvents.add(event); - } + + subscription = resultsStream!.listen((searchResults) { + //Currently, we add searchResults even if the list is empty. So we are adding + //empty list to the queue, which will trigger rebuilds with no change in UI + //(see [generateResultWidgetsInIntervalsFromQueue]'s setState()). + //This is needed to clear the search results in this widget when the + //search bar is cleared, and the event fired by the stream will be an + //empty list. Can optimize rebuilds if there are performance issues in future. + queueOfSearchResults.add(searchResults); }); + generateResultWidgetsInIntervalsFromQueue(); }); } @@ -81,8 +68,8 @@ class _SearchSuggestionsWidgetState extends State { ///generates the widgets and clears the queue and updates the UI. void generateResultWidgetsInIntervalsFromQueue() { timer = Timer.periodic(const Duration(milliseconds: 50), (timer) { - if (queueOfEvents.isNotEmpty) { - for (List event in queueOfEvents) { + if (queueOfSearchResults.isNotEmpty) { + for (List event in queueOfSearchResults) { for (SearchResult result in event) { searchResultWidgets.add( SearchResultsWidgetGenerator(result).animate().fadeIn( @@ -92,7 +79,7 @@ class _SearchSuggestionsWidgetState extends State { ); } } - queueOfEvents.clear(); + queueOfSearchResults.clear(); setState(() {}); } }); From 860fe74902cef97f98a1027b1dedc517d3a23dbc Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 23 Dec 2023 23:14:31 +0530 Subject: [PATCH 013/175] use ChangeNotifier to notify change in index of IndexedStack to be shown in search tab --- lib/models/search/index_of_indexed_stack.dart | 38 +++++++++++++++++++ lib/ui/search_tab.dart | 24 +++++++++++- lib/ui/viewer/search/search_widget.dart | 5 +-- 3 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 lib/models/search/index_of_indexed_stack.dart diff --git a/lib/models/search/index_of_indexed_stack.dart b/lib/models/search/index_of_indexed_stack.dart new file mode 100644 index 000000000..242e8eb6e --- /dev/null +++ b/lib/models/search/index_of_indexed_stack.dart @@ -0,0 +1,38 @@ +import "package:flutter/material.dart"; + +class IndexOfStackNotifier with ChangeNotifier { + int _index = 0; + bool _isSearchQueryEmpty = true; + bool _isSearchResultsEmpty = true; + + static IndexOfStackNotifier? _instance; + + IndexOfStackNotifier._(); + + factory IndexOfStackNotifier() => _instance ??= IndexOfStackNotifier._(); + + set isSearchQueryEmpty(bool value) { + _isSearchQueryEmpty = value; + setIndex(); + } + + set isSearchResultsEmpty(bool value) { + _isSearchResultsEmpty = value; + setIndex(); + } + + setIndex() { + if (_isSearchResultsEmpty) { + if (_isSearchQueryEmpty) { + _index = 0; + } else { + _index = 2; + } + } else { + _index = 1; + } + notifyListeners(); + } + + get index => _index; +} diff --git a/lib/ui/search_tab.dart b/lib/ui/search_tab.dart index a784a24e9..b4fd67f75 100644 --- a/lib/ui/search_tab.dart +++ b/lib/ui/search_tab.dart @@ -2,6 +2,7 @@ import "package:fade_indexed_stack/fade_indexed_stack.dart"; import "package:flutter/material.dart"; import "package:flutter_animate/flutter_animate.dart"; import "package:photos/core/constants.dart"; +import "package:photos/models/search/index_of_indexed_stack.dart"; import "package:photos/models/search/search_types.dart"; import "package:photos/states/all_sections_examples_state.dart"; import "package:photos/ui/common/loading_widget.dart"; @@ -18,7 +19,28 @@ class SearchTab extends StatefulWidget { } class _SearchTabState extends State { - int index = 1; + late int index; + final indexOfStackNotifier = IndexOfStackNotifier(); + + @override + void initState() { + super.initState(); + index = indexOfStackNotifier.index; + indexOfStackNotifier.addListener(indexNotifierListener); + } + + void indexNotifierListener() { + setState(() { + index = indexOfStackNotifier.index; + }); + } + + @override + void dispose() { + indexOfStackNotifier.removeListener(indexNotifierListener); + indexOfStackNotifier.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index d8ec50f35..10a89628b 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -6,6 +6,7 @@ import "package:logging/logging.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/events/clear_and_unfocus_search_bar_event.dart"; import "package:photos/events/tab_changed_event.dart"; +import "package:photos/models/search/index_of_indexed_stack.dart"; import "package:photos/models/search/search_result.dart"; import "package:photos/services/search_service.dart"; import "package:photos/theme/ente_theme.dart"; @@ -13,8 +14,6 @@ import "package:photos/ui/viewer/search/search_suffix_icon_widget.dart"; import "package:photos/utils/date_time_util.dart"; import "package:photos/utils/debouncer.dart"; -bool isSearchQueryEmpty = true; - class SearchWidget extends StatefulWidget { const SearchWidget({Key? key}) : super(key: key); @@ -98,7 +97,7 @@ class SearchWidgetState extends State { Future textControllerListener() async { //query in local varialbe final value = textController.text; - isSearchQueryEmpty = value.isEmpty; + IndexOfStackNotifier().isSearchResultsEmpty = value.isEmpty; //latest query in global variable query = textController.text; From 3fcc1665af0436792dd742986966c554df7ad1c8 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 23 Dec 2023 23:18:19 +0530 Subject: [PATCH 014/175] minor fix --- lib/ui/viewer/search/search_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index 10a89628b..4fdbc841f 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -97,7 +97,7 @@ class SearchWidgetState extends State { Future textControllerListener() async { //query in local varialbe final value = textController.text; - IndexOfStackNotifier().isSearchResultsEmpty = value.isEmpty; + IndexOfStackNotifier().isSearchQueryEmpty = value.isEmpty; //latest query in global variable query = textController.text; From 397f6756feb0c7e89bb788cd3ef638af38114779 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 26 Dec 2023 11:26:07 +0530 Subject: [PATCH 015/175] fix: show NoResultsWidget if the stream if complete and there aren't any search results --- lib/ui/search_tab.dart | 2 ++ lib/ui/viewer/search/search_suggestions.dart | 29 ++++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/ui/search_tab.dart b/lib/ui/search_tab.dart index b4fd67f75..9e4a1e0ef 100644 --- a/lib/ui/search_tab.dart +++ b/lib/ui/search_tab.dart @@ -47,6 +47,7 @@ class _SearchTabState extends State { // print("_____ rebuilding SearchTab with stream: ${_searchResults.hashCode}"); return AllSectionsExamplesProvider( child: FadeIndexedStack( + lazy: false, duration: const Duration(milliseconds: 150), index: index, children: const [ @@ -69,6 +70,7 @@ class AllSearchSections extends StatefulWidget { class _AllSearchSectionsState extends State { @override Widget build(BuildContext context) { + print("In initState of AllSearchSections -----------------"); final searchTypes = SectionType.values.toList(growable: true); // remove face and content sectionType searchTypes.remove(SectionType.face); diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index 07ffc549c..72d1a8aba 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -7,6 +7,7 @@ import "package:photos/core/event_bus.dart"; import "package:photos/events/clear_and_unfocus_search_bar_event.dart"; import "package:photos/models/search/album_search_result.dart"; import "package:photos/models/search/generic_search_result.dart"; +import "package:photos/models/search/index_of_indexed_stack.dart"; import 'package:photos/models/search/search_result.dart'; import "package:photos/services/collections_service.dart"; import "package:photos/ui/viewer/gallery/collection_page.dart"; @@ -44,15 +45,25 @@ class _SearchSuggestionsWidgetState extends State { searchResultWidgets.clear(); releaseResources(); - subscription = resultsStream!.listen((searchResults) { - //Currently, we add searchResults even if the list is empty. So we are adding - //empty list to the queue, which will trigger rebuilds with no change in UI - //(see [generateResultWidgetsInIntervalsFromQueue]'s setState()). - //This is needed to clear the search results in this widget when the - //search bar is cleared, and the event fired by the stream will be an - //empty list. Can optimize rebuilds if there are performance issues in future. - queueOfSearchResults.add(searchResults); - }); + subscription = resultsStream!.listen( + (searchResults) { + //Currently, we add searchResults even if the list is empty. So we are adding + //empty list to the queue, which will trigger rebuilds with no change in UI + //(see [generateResultWidgetsInIntervalsFromQueue]'s setState()). + //This is needed to clear the search results in this widget when the + //search bar is cleared, and the event fired by the stream will be an + //empty list. Can optimize rebuilds if there are performance issues in future. + if (searchResults.isNotEmpty) { + IndexOfStackNotifier().isSearchResultsEmpty = false; + } + queueOfSearchResults.add(searchResults); + }, + onDone: () { + if (!queueOfSearchResults.any((element) => element.isNotEmpty)) { + IndexOfStackNotifier().isSearchResultsEmpty = true; + } + }, + ); generateResultWidgetsInIntervalsFromQueue(); }); From 6a25ada94bf1800ad6babb26efa570f3f21bf7ff Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 26 Dec 2023 11:57:57 +0530 Subject: [PATCH 016/175] fix: if results are from the very last event, NoResultsWidget was being shown --- lib/ui/viewer/search/search_suggestions.dart | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index 72d1a8aba..e47a09ec9 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -36,6 +36,10 @@ class _SearchSuggestionsWidgetState extends State { StreamSubscription>? subscription; Timer? timer; + ///This is the interval at which the queue is checked for new events and + ///the search result widgets are generated from the queue. + static const _surfaceNewResultsInterval = 50; + @override void initState() { super.initState(); @@ -59,9 +63,13 @@ class _SearchSuggestionsWidgetState extends State { queueOfSearchResults.add(searchResults); }, onDone: () { - if (!queueOfSearchResults.any((element) => element.isNotEmpty)) { - IndexOfStackNotifier().isSearchResultsEmpty = true; - } + Future.delayed( + const Duration(milliseconds: _surfaceNewResultsInterval + 20), + () { + if (searchResultWidgets.isEmpty) { + IndexOfStackNotifier().isSearchResultsEmpty = true; + } + }); }, ); @@ -78,7 +86,8 @@ class _SearchSuggestionsWidgetState extends State { ///every 60ms if the queue is empty or not. If the queue is not empty, it ///generates the widgets and clears the queue and updates the UI. void generateResultWidgetsInIntervalsFromQueue() { - timer = Timer.periodic(const Duration(milliseconds: 50), (timer) { + timer = Timer.periodic( + const Duration(milliseconds: _surfaceNewResultsInterval), (timer) { if (queueOfSearchResults.isNotEmpty) { for (List event in queueOfSearchResults) { for (SearchResult result in event) { From 682459e8009e22d0f180a813d8c2505127bbb636 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 26 Dec 2023 12:35:48 +0530 Subject: [PATCH 017/175] clean up unwanted code and comments --- lib/ui/viewer/search/search_widget.dart | 39 ++----------------------- 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index 4fdbc841f..85b0f40ea 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -24,7 +24,6 @@ class SearchWidget extends StatefulWidget { class SearchWidgetState extends State { static ValueNotifier>?> searchResultsStreamNotifier = ValueNotifier(null); - static String query = ""; final _searchService = SearchService.instance; final _debouncer = Debouncer(const Duration(milliseconds: 200)); final Logger _logger = Logger((SearchWidgetState).toString()); @@ -64,7 +63,6 @@ class SearchWidgetState extends State { textController.addListener(textControllerListener); }); - textController.text = query; _clearAndUnfocusSearchBar = Bus.instance.on().listen((event) { @@ -95,48 +93,15 @@ class SearchWidgetState extends State { } Future textControllerListener() async { - //query in local varialbe - final value = textController.text; - IndexOfStackNotifier().isSearchQueryEmpty = value.isEmpty; - //latest query in global variable - query = textController.text; - - // final List allResults = - // await getSearchResultsForQuery(context, value); + final query = textController.text; + IndexOfStackNotifier().isSearchQueryEmpty = query.isEmpty; _debouncer.run(() async { if (mounted) { searchResultsStreamNotifier.value = _getSearchResultsStream(context, query); } - - // await for (final value in searchResultsStream) { - // print( - // "Recieved event from stream ${searchResultsStream.hashCode} -------------- -------------- -------------- -------------- --------------", - // ); - - // value.forEach((element) { - // print( - // "---------" + element.name() + " " + element.type().toString(), - // ); - // }); - // print( - // "End -------------- -------------- -------------- -------------- --------------", - // ); - // } }); - - /*checking if query == value to make sure that the results are from the current query - and not from the previous query (race condition).*/ - //checking if query == value to make sure that the latest query's result - //(allResults) is passed to updateResult. Due to race condition, the previous - //query's allResults could be passed to updateResult after the lastest query's - //allResults is passed. - - // if (mounted && query == value) { - // final inheritedSearchResults = InheritedSearchResults.of(context); - // inheritedSearchResults.updateStream(allResults); - // } } @override From 608af962b4cf52e1a0bc5984d57698559e4de573 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 26 Dec 2023 14:39:21 +0530 Subject: [PATCH 018/175] fix: 'NoResultsWidget' coming up for a fraction of a second on the first query --- lib/models/search/index_of_indexed_stack.dart | 24 ++++++++++++------- lib/ui/viewer/search/search_suggestions.dart | 5 ++-- lib/ui/viewer/search/search_widget.dart | 5 ++-- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/models/search/index_of_indexed_stack.dart b/lib/models/search/index_of_indexed_stack.dart index 242e8eb6e..49e0cec6b 100644 --- a/lib/models/search/index_of_indexed_stack.dart +++ b/lib/models/search/index_of_indexed_stack.dart @@ -1,9 +1,15 @@ import "package:flutter/material.dart"; +enum SearchState { + empty, + searching, + notEmpty, +} + class IndexOfStackNotifier with ChangeNotifier { int _index = 0; bool _isSearchQueryEmpty = true; - bool _isSearchResultsEmpty = true; + SearchState _searchState = SearchState.empty; static IndexOfStackNotifier? _instance; @@ -16,20 +22,20 @@ class IndexOfStackNotifier with ChangeNotifier { setIndex(); } - set isSearchResultsEmpty(bool value) { - _isSearchResultsEmpty = value; + set searchState(SearchState value) { + _searchState = value; setIndex(); } setIndex() { - if (_isSearchResultsEmpty) { - if (_isSearchQueryEmpty) { - _index = 0; - } else { + if (_isSearchQueryEmpty) { + _index = 0; + } else { + if (_searchState == SearchState.empty) { _index = 2; + } else { + _index = 1; } - } else { - _index = 1; } notifyListeners(); } diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index e47a09ec9..ba80410d7 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -44,6 +44,7 @@ class _SearchSuggestionsWidgetState extends State { void initState() { super.initState(); SearchWidgetState.searchResultsStreamNotifier.addListener(() { + IndexOfStackNotifier().searchState = SearchState.searching; final resultsStream = SearchWidgetState.searchResultsStreamNotifier.value; searchResultWidgets.clear(); @@ -58,7 +59,7 @@ class _SearchSuggestionsWidgetState extends State { //search bar is cleared, and the event fired by the stream will be an //empty list. Can optimize rebuilds if there are performance issues in future. if (searchResults.isNotEmpty) { - IndexOfStackNotifier().isSearchResultsEmpty = false; + IndexOfStackNotifier().searchState = SearchState.notEmpty; } queueOfSearchResults.add(searchResults); }, @@ -67,7 +68,7 @@ class _SearchSuggestionsWidgetState extends State { const Duration(milliseconds: _surfaceNewResultsInterval + 20), () { if (searchResultWidgets.isEmpty) { - IndexOfStackNotifier().isSearchResultsEmpty = true; + IndexOfStackNotifier().searchState = SearchState.empty; } }); }, diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index 85b0f40ea..d683566e7 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -93,11 +93,10 @@ class SearchWidgetState extends State { } Future textControllerListener() async { - final query = textController.text; - IndexOfStackNotifier().isSearchQueryEmpty = query.isEmpty; - _debouncer.run(() async { if (mounted) { + final query = textController.text; + IndexOfStackNotifier().isSearchQueryEmpty = query.isEmpty; searchResultsStreamNotifier.value = _getSearchResultsStream(context, query); } From df5d8b35b2bb1205f3729425e0a2c4e93660beee Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 26 Dec 2023 14:49:57 +0530 Subject: [PATCH 019/175] pref: only notify listeners if index is different from previous one --- lib/models/search/index_of_indexed_stack.dart | 5 ++++- lib/ui/search_tab.dart | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/models/search/index_of_indexed_stack.dart b/lib/models/search/index_of_indexed_stack.dart index 49e0cec6b..6e09b72b7 100644 --- a/lib/models/search/index_of_indexed_stack.dart +++ b/lib/models/search/index_of_indexed_stack.dart @@ -7,6 +7,7 @@ enum SearchState { } class IndexOfStackNotifier with ChangeNotifier { + int _prevIndex = 0; int _index = 0; bool _isSearchQueryEmpty = true; SearchState _searchState = SearchState.empty; @@ -28,6 +29,8 @@ class IndexOfStackNotifier with ChangeNotifier { } setIndex() { + _prevIndex = _index; + if (_isSearchQueryEmpty) { _index = 0; } else { @@ -37,7 +40,7 @@ class IndexOfStackNotifier with ChangeNotifier { _index = 1; } } - notifyListeners(); + _prevIndex != _index ? notifyListeners() : null; } get index => _index; diff --git a/lib/ui/search_tab.dart b/lib/ui/search_tab.dart index 9e4a1e0ef..20ae89fb4 100644 --- a/lib/ui/search_tab.dart +++ b/lib/ui/search_tab.dart @@ -44,7 +44,6 @@ class _SearchTabState extends State { @override Widget build(BuildContext context) { - // print("_____ rebuilding SearchTab with stream: ${_searchResults.hashCode}"); return AllSectionsExamplesProvider( child: FadeIndexedStack( lazy: false, From 6f2b6fc1b04edfc18742d60fcda7a71b79ae0efa Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 26 Dec 2023 15:04:08 +0530 Subject: [PATCH 020/175] fix: keep the latest query in search tab when shifting tabs and coming back to search tab in between searching --- lib/ui/viewer/search/search_widget.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index d683566e7..2589438d8 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -24,6 +24,11 @@ class SearchWidget extends StatefulWidget { class SearchWidgetState extends State { static ValueNotifier>?> searchResultsStreamNotifier = ValueNotifier(null); + + ///This stores the query that is being searched for. When going to other tabs + ///when searching, this state gets disposed and when coming back to the + ///search tab, this query is used to populate the search bar. + static String query = ""; final _searchService = SearchService.instance; final _debouncer = Debouncer(const Duration(milliseconds: 200)); final Logger _logger = Logger((SearchWidgetState).toString()); @@ -64,6 +69,10 @@ class SearchWidgetState extends State { textController.addListener(textControllerListener); }); + //Populate the serach tab with the latest query when coming back + //to the serach tab. + textController.text = query; + _clearAndUnfocusSearchBar = Bus.instance.on().listen((event) { textController.clear(); @@ -95,7 +104,7 @@ class SearchWidgetState extends State { Future textControllerListener() async { _debouncer.run(() async { if (mounted) { - final query = textController.text; + query = textController.text; IndexOfStackNotifier().isSearchQueryEmpty = query.isEmpty; searchResultsStreamNotifier.value = _getSearchResultsStream(context, query); From 2aa712dc94b377552cfceebd995f461e43fa1105 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 26 Dec 2023 18:24:38 +0530 Subject: [PATCH 021/175] when searching, show loading state in search bar when debouncing and querying db --- lib/ui/viewer/search/search_suffix_icon_widget.dart | 3 +-- lib/ui/viewer/search/search_suggestions.dart | 1 + lib/ui/viewer/search/search_widget.dart | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/ui/viewer/search/search_suffix_icon_widget.dart b/lib/ui/viewer/search/search_suffix_icon_widget.dart index f5d36f499..744684422 100644 --- a/lib/ui/viewer/search/search_suffix_icon_widget.dart +++ b/lib/ui/viewer/search/search_suffix_icon_widget.dart @@ -11,8 +11,7 @@ class SearchSuffixIcon extends StatefulWidget { State createState() => _SearchSuffixIconState(); } -class _SearchSuffixIconState extends State - with TickerProviderStateMixin { +class _SearchSuffixIconState extends State { @override Widget build(BuildContext context) { final colorScheme = getEnteColorScheme(context); diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index ba80410d7..bf5899008 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -71,6 +71,7 @@ class _SearchSuggestionsWidgetState extends State { IndexOfStackNotifier().searchState = SearchState.empty; } }); + SearchWidgetState.isLoading.value = false; }, ); diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index 2589438d8..4d4942819 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -29,6 +29,8 @@ class SearchWidgetState extends State { ///when searching, this state gets disposed and when coming back to the ///search tab, this query is used to populate the search bar. static String query = ""; + //Debouncing + querying + static final isLoading = ValueNotifier(false); final _searchService = SearchService.instance; final _debouncer = Debouncer(const Duration(milliseconds: 200)); final Logger _logger = Logger((SearchWidgetState).toString()); @@ -102,6 +104,7 @@ class SearchWidgetState extends State { } Future textControllerListener() async { + isLoading.value = true; _debouncer.run(() async { if (mounted) { query = textController.text; @@ -173,14 +176,14 @@ class SearchWidgetState extends State { /*Using valueListenableBuilder inside a stateful widget because this widget is only rebuild when setState is called when deboucncing is over and the spinner needs to be shown while debouncing */ suffixIcon: ValueListenableBuilder( - valueListenable: _debouncer.debounceActiveNotifier, + valueListenable: isLoading, builder: ( BuildContext context, - bool isDebouncing, + bool isSearching, Widget? child, ) { return SearchSuffixIcon( - isDebouncing, + isSearching, ); }, ), From b89cbd13f4142c0b1e2f3729e658a77a32722af8 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 26 Dec 2023 18:43:14 +0530 Subject: [PATCH 022/175] add final --- lib/ui/viewer/search/search_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index 4d4942819..68431840f 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -22,7 +22,7 @@ class SearchWidget extends StatefulWidget { } class SearchWidgetState extends State { - static ValueNotifier>?> + static final ValueNotifier>?> searchResultsStreamNotifier = ValueNotifier(null); ///This stores the query that is being searched for. When going to other tabs From c18577e28d618d40a030263323307c929dd7edb4 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 26 Dec 2023 20:43:03 +0530 Subject: [PATCH 023/175] remove print statements --- lib/ui/search_tab.dart | 1 - lib/ui/viewer/search/search_widget.dart | 31 ++----------------------- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/lib/ui/search_tab.dart b/lib/ui/search_tab.dart index 20ae89fb4..6bdb6ad7e 100644 --- a/lib/ui/search_tab.dart +++ b/lib/ui/search_tab.dart @@ -69,7 +69,6 @@ class AllSearchSections extends StatefulWidget { class _AllSearchSectionsState extends State { @override Widget build(BuildContext context) { - print("In initState of AllSearchSections -----------------"); final searchTypes = SectionType.values.toList(growable: true); // remove face and content sectionType searchTypes.remove(SectionType.face); diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index 68431840f..ba713716c 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -290,7 +290,6 @@ class SearchWidgetState extends State { _searchService.getYearSearchResults(query).then((yearSearchResults) { streamController.sink.add(yearSearchResults); resultCount++; - print('-----------yearSearchResults: ${yearSearchResults.length}'); if (resultCount == maxResultCount) { streamController.close(); } @@ -303,10 +302,7 @@ class SearchWidgetState extends State { resultCount++; - print('------------holidayResults: ${holidayResults.length}'); - if (resultCount == maxResultCount) { - print("------- closing stream from holiday results"); streamController.close(); } }, @@ -316,11 +312,8 @@ class SearchWidgetState extends State { (fileTypeSearchResults) { streamController.sink.add(fileTypeSearchResults); resultCount++; - print( - '----------fileTypeSearchResults: ${fileTypeSearchResults.length}', - ); + if (resultCount == maxResultCount) { - print("------- closing stream from file type results"); streamController.close(); } }, @@ -330,11 +323,8 @@ class SearchWidgetState extends State { (captionAndDisplayNameResult) { streamController.sink.add(captionAndDisplayNameResult); resultCount++; - print( - '--------------captionAndDisplayNameResult: ${captionAndDisplayNameResult.length}', - ); + if (resultCount == maxResultCount) { - print("------- closing stream from caption results"); streamController.close(); } }, @@ -344,9 +334,7 @@ class SearchWidgetState extends State { (fileExtnResult) { streamController.sink.add(fileExtnResult); resultCount++; - print('-----------fileExtnResult: ${fileExtnResult.length}'); if (resultCount == maxResultCount) { - print("------- closing stream from file extn results"); streamController.close(); } }, @@ -356,9 +344,7 @@ class SearchWidgetState extends State { (locationResult) { streamController.sink.add(locationResult); resultCount++; - print('-------------locationResult: ${locationResult.length}'); if (resultCount == maxResultCount) { - print("------- closing stream from location results"); streamController.close(); } }, @@ -369,12 +355,7 @@ class SearchWidgetState extends State { streamController.sink.add(collectionResults); resultCount++; - print('--------------collectionResults: ${collectionResults.length}'); - print( - "results for collection search: ${collectionResults.length}, query : $query", - ); if (resultCount == maxResultCount) { - print("------- closing stream from collection results"); streamController.close(); } }, @@ -384,9 +365,7 @@ class SearchWidgetState extends State { (monthResults) { streamController.sink.add(monthResults); resultCount++; - print('-------------monthResults: ${monthResults.length}'); if (resultCount == maxResultCount) { - print("------- closing stream from month results"); streamController.close(); } }, @@ -396,9 +375,7 @@ class SearchWidgetState extends State { (possibleEvents) { streamController.sink.add(possibleEvents); resultCount++; - print('-------------possibleEvents: ${possibleEvents.length}'); if (resultCount == maxResultCount) { - print("------- closing stream from possible events results"); streamController.close(); } }, @@ -408,9 +385,7 @@ class SearchWidgetState extends State { (magicResults) { streamController.sink.add(magicResults); resultCount++; - print('------------magicResults: ${magicResults.length}'); if (resultCount == maxResultCount) { - print("------- closing stream from magic results"); streamController.close(); } }, @@ -420,9 +395,7 @@ class SearchWidgetState extends State { (contactResults) { streamController.sink.add(contactResults); resultCount++; - print('-------------contactResults: ${contactResults.length}'); if (resultCount == maxResultCount) { - print("------- closing stream from contact results"); streamController.close(); } }, From 98ca44743afc1b79ca4bdb8415025c3c018cb062 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 26 Dec 2023 21:28:42 +0530 Subject: [PATCH 024/175] show results count on top of search results --- lib/ui/viewer/search/search_suggestions.dart | 36 +++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index bf5899008..6bcb24767 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -5,11 +5,14 @@ import "package:flutter_animate/flutter_animate.dart"; import "package:logging/logging.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/events/clear_and_unfocus_search_bar_event.dart"; +import "package:photos/generated/l10n.dart"; import "package:photos/models/search/album_search_result.dart"; import "package:photos/models/search/generic_search_result.dart"; import "package:photos/models/search/index_of_indexed_stack.dart"; import 'package:photos/models/search/search_result.dart'; import "package:photos/services/collections_service.dart"; +import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/common/loading_widget.dart"; import "package:photos/ui/viewer/gallery/collection_page.dart"; import "package:photos/ui/viewer/search/result/search_result_widget.dart"; import "package:photos/ui/viewer/search/search_widget.dart"; @@ -115,13 +118,9 @@ class _SearchSuggestionsWidgetState extends State { @override Widget build(BuildContext context) { - print( - "_______ rebuiding SearchSuggestionWidget with stream : ${resultsStream.hashCode}", - ); - // return const SizedBox.shrink(); - // late final String title; - // final resultsCount = results.length; - // title = S.of(context).searchResultCount(resultsCount); + String title; + final resultsCount = searchResultWidgets.length; + title = S.of(context).searchResultCount(resultsCount); return Scaffold( appBar: AppBar( leading: BackButton( @@ -135,11 +134,24 @@ class _SearchSuggestionsWidgetState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // // Text( - // // title, - // // style: getEnteTextTheme(context).largeBold, - // // ), - // const SizedBox(height: 20), + AnimatedContainer( + duration: const Duration(seconds: 2), + child: SearchWidgetState.isLoading.value + ? EnteLoadingWidget( + size: 14, + padding: 4, + color: getEnteColorScheme(context).strokeMuted, + alignment: Alignment.centerLeft, + ) + : Text( + title, + style: getEnteTextTheme(context).largeBold, + ).animate().fadeIn( + duration: const Duration(milliseconds: 60), + curve: Curves.easeIn, + ), + ), + const SizedBox(height: 20), Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 4), From c0742295ca03ce0f34563884546486935edfdbe9 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 26 Dec 2023 21:37:16 +0530 Subject: [PATCH 025/175] indentation and comments --- lib/ui/viewer/search/search_suggestions.dart | 5 +++-- lib/ui/viewer/search/search_widget.dart | 5 ----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index 6bcb24767..c9fb647bb 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -88,8 +88,9 @@ class _SearchSuggestionsWidgetState extends State { } ///This method generates searchResultsWidgets from the queueOfEvents by checking - ///every 60ms if the queue is empty or not. If the queue is not empty, it - ///generates the widgets and clears the queue and updates the UI. + ///every [_surfaceNewResultsInterval] if the queue is empty or not. If the + ///queue is not empty, it generates the widgets and clears the queue and + ///updates the UI. void generateResultWidgetsInIntervalsFromQueue() { timer = Timer.periodic( const Duration(milliseconds: _surfaceNewResultsInterval), (timer) { diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index ba713716c..de28c78dc 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -299,9 +299,7 @@ class SearchWidgetState extends State { _searchService.getHolidaySearchResults(context, query).then( (holidayResults) { streamController.sink.add(holidayResults); - resultCount++; - if (resultCount == maxResultCount) { streamController.close(); } @@ -312,7 +310,6 @@ class SearchWidgetState extends State { (fileTypeSearchResults) { streamController.sink.add(fileTypeSearchResults); resultCount++; - if (resultCount == maxResultCount) { streamController.close(); } @@ -323,7 +320,6 @@ class SearchWidgetState extends State { (captionAndDisplayNameResult) { streamController.sink.add(captionAndDisplayNameResult); resultCount++; - if (resultCount == maxResultCount) { streamController.close(); } @@ -354,7 +350,6 @@ class SearchWidgetState extends State { (collectionResults) { streamController.sink.add(collectionResults); resultCount++; - if (resultCount == maxResultCount) { streamController.close(); } From e96085b110c40dd47dbb424844ba1ba69fa3c173 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Tue, 26 Dec 2023 21:55:38 +0530 Subject: [PATCH 026/175] fix popping of search results when search results count is ready --- lib/ui/viewer/search/search_suggestions.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/ui/viewer/search/search_suggestions.dart b/lib/ui/viewer/search/search_suggestions.dart index c9fb647bb..8742cdad3 100644 --- a/lib/ui/viewer/search/search_suggestions.dart +++ b/lib/ui/viewer/search/search_suggestions.dart @@ -135,14 +135,14 @@ class _SearchSuggestionsWidgetState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - AnimatedContainer( - duration: const Duration(seconds: 2), + SizedBox( + height: 44, child: SearchWidgetState.isLoading.value ? EnteLoadingWidget( size: 14, padding: 4, color: getEnteColorScheme(context).strokeMuted, - alignment: Alignment.centerLeft, + alignment: Alignment.topLeft, ) : Text( title, @@ -152,7 +152,6 @@ class _SearchSuggestionsWidgetState extends State { curve: Curves.easeIn, ), ), - const SizedBox(height: 20), Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 4), From 71b62a9b09243b99557d3a096e8a42237fcd84e4 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Tue, 26 Dec 2023 22:33:51 +0530 Subject: [PATCH 027/175] Resolve review comment --- lib/ui/viewer/file/thumbnail_widget.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ui/viewer/file/thumbnail_widget.dart b/lib/ui/viewer/file/thumbnail_widget.dart index 272dc73d3..7ee299b23 100644 --- a/lib/ui/viewer/file/thumbnail_widget.dart +++ b/lib/ui/viewer/file/thumbnail_widget.dart @@ -1,3 +1,5 @@ +import "dart:async"; + import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:photos/core/cache/thumbnail_in_memory_cache.dart'; @@ -220,9 +222,9 @@ class _ThumbnailWidgetState extends State { _logger.fine("Removing localID reference for " + widget.file.tag); widget.file.localID = null; if (widget.file.isTrash) { - await TrashDB.instance.update(widget.file as TrashFile); + unawaited(TrashDB.instance.update(widget.file as TrashFile)); } else { - await FilesDB.instance.update(widget.file); + unawaited(FilesDB.instance.update(widget.file)); } _loadNetworkImage(); } else { From 1f8ea9c2f467abdaf5d7db06c110f5d28e1ce35b Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Tue, 26 Dec 2023 23:21:56 +0530 Subject: [PATCH 028/175] Create a service to load and cache remote assets --- lib/services/remote_assets_service.dart | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 lib/services/remote_assets_service.dart diff --git a/lib/services/remote_assets_service.dart b/lib/services/remote_assets_service.dart new file mode 100644 index 000000000..8a11f84a4 --- /dev/null +++ b/lib/services/remote_assets_service.dart @@ -0,0 +1,57 @@ +import "dart:io"; + +import "package:logging/logging.dart"; +import "package:path_provider/path_provider.dart"; +import "package:photos/core/network/network.dart"; + +class RemoteAssetsService { + static final _logger = Logger("RemoteAssetsService"); + + RemoteAssetsService._privateConstructor(); + + static final RemoteAssetsService instance = + RemoteAssetsService._privateConstructor(); + + Future getAsset(String remotePath) async { + final path = await _getLocalPath(remotePath); + final file = File(path); + if (await file.exists()) { + _logger.info("Returning cached file for $remotePath"); + return file; + } else { + final tempFile = File(path + ".temp"); + await _downloadFile(remotePath, tempFile.path); + await tempFile.rename(path); + return File(path); + } + } + + Future _getLocalPath(String remotePath) async { + return (await getTemporaryDirectory()).path + + "/assets/" + + _urlToFileName(remotePath); + } + + String _urlToFileName(String url) { + // Remove the protocol part (http:// or https://) + String fileName = url + .replaceAll(RegExp(r'https?://'), '') + // Replace all non-alphanumeric characters except for underscores and periods with an underscore + .replaceAll(RegExp(r'[^\w\.]'), '_'); + // Optionally, you might want to trim the resulting string to a certain length + + // Replace periods with underscores for better readability, if desired + fileName = fileName.replaceAll('.', '_'); + + return fileName; + } + + Future _downloadFile(String url, String savePath) async { + _logger.info("Downloading " + url); + final existingFile = File(savePath); + if (await existingFile.exists()) { + await existingFile.delete(); + } + await NetworkClient.instance.getDio().download(url, savePath); + } +} From 5d25785eddfc8e4a93429da13a2eebf58a620480 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Tue, 26 Dec 2023 23:22:09 +0530 Subject: [PATCH 029/175] Load and parse cities --- lib/services/location_service.dart | 49 ++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/lib/services/location_service.dart b/lib/services/location_service.dart index 10e3bfd72..516adff01 100644 --- a/lib/services/location_service.dart +++ b/lib/services/location_service.dart @@ -10,11 +10,13 @@ import "package:photos/models/local_entity_data.dart"; import "package:photos/models/location/location.dart"; import 'package:photos/models/location_tag/location_tag.dart'; import "package:photos/services/entity_service.dart"; +import "package:photos/services/remote_assets_service.dart"; import "package:shared_preferences/shared_preferences.dart"; class LocationService { late SharedPreferences prefs; final Logger _logger = Logger((LocationService).toString()); + final List _cities = []; LocationService._privateConstructor(); @@ -22,6 +24,7 @@ class LocationService { void init(SharedPreferences preferences) { prefs = preferences; + _loadCities(); } Future>> _getStoredLocationTags() async { @@ -203,6 +206,52 @@ class LocationService { rethrow; } } + + Future _loadCities() async { + try { + final data = await RemoteAssetsService.instance + .getAsset("https://assets.ente.io/world_cities.json"); + final citiesJson = json.decode(await data.readAsString()); + final List jsonData = citiesJson['data']; + final cities = + jsonData.map((jsonItem) => City.fromMap(jsonItem)).toList(); + _cities.clear(); + _cities.addAll(cities); + _logger.info("Loaded cities"); + } catch (e, s) { + _logger.severe("Failed to load cities", e, s); + } + } +} + +class City { + final String city; + final String country; + final double lat; + final double lng; + + City({ + required this.city, + required this.country, + required this.lat, + required this.lng, + }); + + factory City.fromMap(Map map) { + return City( + city: map['city'] ?? '', + country: map['country'] ?? '', + lat: map['lat']?.toDouble() ?? 0.0, + lng: map['lng']?.toDouble() ?? 0.0, + ); + } + + factory City.fromJson(String source) => City.fromMap(json.decode(source)); + + @override + String toString() { + return 'City(city: $city, country: $country, lat: $lat, lng: $lng)'; + } } class GPSData { From b16ee22dea069011cc8a944caae18a2ab793cd85 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Tue, 26 Dec 2023 23:23:46 +0530 Subject: [PATCH 030/175] Add attribution for Simple Maps --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index bd750fdef..d6d9fba28 100644 --- a/README.md +++ b/README.md @@ -105,3 +105,7 @@ If you're interested in helping out with translation, please visit our [Crowdin Follow us on [Twitter](https://twitter.com/enteio), join [r/enteio](https://reddit.com/r/enteio) or hang out on our [Discord](https://ente.io/discord) to get regular updates, connect with other customers, and discuss your ideas. An important part of our journey is to build better software by consistently listening to community feedback. Please feel free to [share your thoughts](mailto:feedback@ente.io) with us at any time. + +## 🙇 Attributions + +- [Simple Maps](https://simplemaps.com/data/world-cities) From fe2bc0bc975b80d275582be4204f1af736a770ab Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Tue, 26 Dec 2023 23:52:47 +0530 Subject: [PATCH 031/175] Render city results for internal users --- lib/core/constants.dart | 2 ++ lib/services/location_service.dart | 9 ++++- lib/services/search_service.dart | 48 +++++++++++++++++++++++++ lib/ui/viewer/search/search_widget.dart | 12 ++++++- 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/lib/core/constants.dart b/lib/core/constants.dart index 9ae2e8b66..5c70e218f 100644 --- a/lib/core/constants.dart +++ b/lib/core/constants.dart @@ -61,6 +61,8 @@ const defaultRadiusValues = [1, 2, 10, 20, 40, 80, 200, 400, 1200]; const defaultRadiusValue = 40.0; +const defaultCityRadius = 10.0; + const galleryGridSpacing = 2.0; const searchSectionLimit = 7; diff --git a/lib/services/location_service.dart b/lib/services/location_service.dart index 516adff01..4f61e5f99 100644 --- a/lib/services/location_service.dart +++ b/lib/services/location_service.dart @@ -10,6 +10,7 @@ import "package:photos/models/local_entity_data.dart"; import "package:photos/models/location/location.dart"; import 'package:photos/models/location_tag/location_tag.dart'; import "package:photos/services/entity_service.dart"; +import "package:photos/services/feature_flag_service.dart"; import "package:photos/services/remote_assets_service.dart"; import "package:shared_preferences/shared_preferences.dart"; @@ -24,7 +25,9 @@ class LocationService { void init(SharedPreferences preferences) { prefs = preferences; - _loadCities(); + if (!FeatureFlagService.instance.isInternalUserOrDebugBuild()) { + _loadCities(); + } } Future>> _getStoredLocationTags() async { @@ -34,6 +37,10 @@ class LocationService { ); } + List getAllCities() { + return _cities; + } + Future>> getLocationTags() { return _getStoredLocationTags(); } diff --git a/lib/services/search_service.dart b/lib/services/search_service.dart index 335864840..7b831480a 100644 --- a/lib/services/search_service.dart +++ b/lib/services/search_service.dart @@ -3,6 +3,7 @@ import "dart:math"; import "package:flutter/cupertino.dart"; import "package:intl/intl.dart"; import 'package:logging/logging.dart'; +import "package:photos/core/constants.dart"; import 'package:photos/core/event_bus.dart'; import 'package:photos/data/holidays.dart'; import 'package:photos/data/months.dart'; @@ -17,6 +18,7 @@ import "package:photos/models/file/extensions/file_props.dart"; import 'package:photos/models/file/file.dart'; import 'package:photos/models/file/file_type.dart'; import "package:photos/models/local_entity_data.dart"; +import "package:photos/models/location/location.dart"; import "package:photos/models/location_tag/location_tag.dart"; import 'package:photos/models/search/album_search_result.dart'; import 'package:photos/models/search/generic_search_result.dart'; @@ -681,6 +683,52 @@ class SearchService { return searchResults; } + Future> getCityResults(String query) async { + final startTime = DateTime.now().microsecondsSinceEpoch; + final List searchResults = []; + final cities = LocationService.instance.getAllCities(); + final matchingCities = []; + final queryLower = query.toLowerCase(); + for (City city in cities) { + if (city.city.toLowerCase().startsWith(queryLower)) { + matchingCities.add(city); + } + } + final files = await getAllFiles(); + final Map> results = {}; + for (final city in matchingCities) { + final List matchingFiles = []; + final cityLocation = Location(latitude: city.lat, longitude: city.lng); + for (final file in files) { + if (file.hasLocation) { + if (LocationService.instance.isFileInsideLocationTag( + cityLocation, + file.location!, + defaultCityRadius, + )) { + matchingFiles.add(file); + } + } + } + if (matchingFiles.isNotEmpty) { + results[city] = matchingFiles; + } + } + for (final entry in results.entries) { + searchResults.add( + GenericSearchResult( + ResultType.location, + entry.key.city, + entry.value, + ), + ); + } + final endTime = DateTime.now().microsecondsSinceEpoch; + _logger + .info("Time taken " + ((endTime - startTime) / 1000).toString() + "ms"); + return searchResults; + } + Future> getAllLocationTags(int? limit) async { try { final Map, List> tagToItemsMap = {}; diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index de28c78dc..daf3d9f8b 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -278,7 +278,7 @@ class SearchWidgetState extends State { String query, ) { int resultCount = 0; - final maxResultCount = _isYearValid(query) ? 11 : 10; + final maxResultCount = _isYearValid(query) ? 12 : 11; final streamController = StreamController>(); if (query.isEmpty) { @@ -346,6 +346,16 @@ class SearchWidgetState extends State { }, ); + _searchService.getCityResults(query).then( + (results) { + streamController.sink.add(results); + resultCount++; + if (resultCount == maxResultCount) { + streamController.close(); + } + }, + ); + _searchService.getCollectionSearchResults(query).then( (collectionResults) { streamController.sink.add(collectionResults); From 4b06f58059fbc063cfdecbf698dc301f1e5fe7d2 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Tue, 26 Dec 2023 23:58:06 +0530 Subject: [PATCH 032/175] Flip the flag --- lib/services/location_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/location_service.dart b/lib/services/location_service.dart index 4f61e5f99..6ca82a9b8 100644 --- a/lib/services/location_service.dart +++ b/lib/services/location_service.dart @@ -25,7 +25,7 @@ class LocationService { void init(SharedPreferences preferences) { prefs = preferences; - if (!FeatureFlagService.instance.isInternalUserOrDebugBuild()) { + if (FeatureFlagService.instance.isInternalUserOrDebugBuild()) { _loadCities(); } } From 42bbe05d61ef3f447a2b0e5d446115df8d4d4dc6 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Wed, 27 Dec 2023 00:00:16 +0530 Subject: [PATCH 033/175] Refactor SearchWidget --- lib/ui/viewer/search/search_widget.dart | 81 +++++++------------------ 1 file changed, 21 insertions(+), 60 deletions(-) diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index daf3d9f8b..293025ee8 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -286,123 +286,84 @@ class SearchWidgetState extends State { streamController.close(); return streamController.stream; } + + void onResultsReceived(List results) { + streamController.sink.add(results); + resultCount++; + if (resultCount == maxResultCount) { + streamController.close(); + } + } + if (_isYearValid(query)) { _searchService.getYearSearchResults(query).then((yearSearchResults) { - streamController.sink.add(yearSearchResults); - resultCount++; - if (resultCount == maxResultCount) { - streamController.close(); - } + onResultsReceived(yearSearchResults); }); } _searchService.getHolidaySearchResults(context, query).then( (holidayResults) { - streamController.sink.add(holidayResults); - resultCount++; - if (resultCount == maxResultCount) { - streamController.close(); - } + onResultsReceived(holidayResults); }, ); _searchService.getFileTypeResults(context, query).then( (fileTypeSearchResults) { - streamController.sink.add(fileTypeSearchResults); - resultCount++; - if (resultCount == maxResultCount) { - streamController.close(); - } + onResultsReceived(fileTypeSearchResults); }, ); _searchService.getCaptionAndNameResults(query).then( (captionAndDisplayNameResult) { - streamController.sink.add(captionAndDisplayNameResult); - resultCount++; - if (resultCount == maxResultCount) { - streamController.close(); - } + onResultsReceived(captionAndDisplayNameResult); }, ); _searchService.getFileExtensionResults(query).then( (fileExtnResult) { - streamController.sink.add(fileExtnResult); - resultCount++; - if (resultCount == maxResultCount) { - streamController.close(); - } + onResultsReceived(fileExtnResult); }, ); _searchService.getLocationResults(query).then( (locationResult) { - streamController.sink.add(locationResult); - resultCount++; - if (resultCount == maxResultCount) { - streamController.close(); - } + onResultsReceived(locationResult); }, ); _searchService.getCityResults(query).then( (results) { - streamController.sink.add(results); - resultCount++; - if (resultCount == maxResultCount) { - streamController.close(); - } + onResultsReceived(results); }, ); _searchService.getCollectionSearchResults(query).then( (collectionResults) { - streamController.sink.add(collectionResults); - resultCount++; - if (resultCount == maxResultCount) { - streamController.close(); - } + onResultsReceived(collectionResults); }, ); _searchService.getMonthSearchResults(context, query).then( (monthResults) { - streamController.sink.add(monthResults); - resultCount++; - if (resultCount == maxResultCount) { - streamController.close(); - } + onResultsReceived(monthResults); }, ); _searchService.getDateResults(context, query).then( (possibleEvents) { - streamController.sink.add(possibleEvents); - resultCount++; - if (resultCount == maxResultCount) { - streamController.close(); - } + onResultsReceived(possibleEvents); }, ); _searchService.getMagicSearchResults(context, query).then( (magicResults) { - streamController.sink.add(magicResults); - resultCount++; - if (resultCount == maxResultCount) { - streamController.close(); - } + onResultsReceived(magicResults); }, ); _searchService.getContactSearchResults(query).then( (contactResults) { - streamController.sink.add(contactResults); - resultCount++; - if (resultCount == maxResultCount) { - streamController.close(); - } + onResultsReceived(contactResults); }, ); From ea35b94d794726d09906a31d627184012fb95c35 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Wed, 27 Dec 2023 01:18:39 +0530 Subject: [PATCH 034/175] Extract constant --- lib/services/location_service.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/services/location_service.dart b/lib/services/location_service.dart index 6ca82a9b8..2da7092e4 100644 --- a/lib/services/location_service.dart +++ b/lib/services/location_service.dart @@ -23,6 +23,8 @@ class LocationService { static final LocationService instance = LocationService._privateConstructor(); + static const kCitiesRemotePath = "https://assets.ente.io/world_cities.json"; + void init(SharedPreferences preferences) { prefs = preferences; if (FeatureFlagService.instance.isInternalUserOrDebugBuild()) { @@ -216,8 +218,8 @@ class LocationService { Future _loadCities() async { try { - final data = await RemoteAssetsService.instance - .getAsset("https://assets.ente.io/world_cities.json"); + final data = + await RemoteAssetsService.instance.getAsset(kCitiesRemotePath); final citiesJson = json.decode(await data.readAsString()); final List jsonData = citiesJson['data']; final cities = From ce276bbc07874b7328259fb3a69ca0f50fb4a030 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Wed, 27 Dec 2023 08:29:39 +0530 Subject: [PATCH 035/175] check if Semantic search has been initialized before calling ObjectBox.instance.clearTable() to avoid LateInitializationError of 'store' --- lib/core/configuration.dart | 5 ++++- lib/services/semantic_search/semantic_search_service.dart | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/core/configuration.dart b/lib/core/configuration.dart index fa96f1fd5..a77099dc3 100644 --- a/lib/core/configuration.dart +++ b/lib/core/configuration.dart @@ -27,6 +27,7 @@ import 'package:photos/services/favorites_service.dart'; import 'package:photos/services/ignored_files_service.dart'; import 'package:photos/services/memories_service.dart'; import 'package:photos/services/search_service.dart'; +import "package:photos/services/semantic_search/semantic_search_service.dart"; import 'package:photos/services/sync_service.dart'; import 'package:photos/utils/crypto_util.dart'; import 'package:photos/utils/file_uploader.dart'; @@ -157,7 +158,9 @@ class Configuration { _cachedToken = null; _secretKey = null; await FilesDB.instance.clearTable(); - await ObjectBox.instance.clearTable(); + SemanticSearchService.instance.hasInitialized + ? await ObjectBox.instance.clearTable() + : null; await CollectionsDB.instance.clearTable(); await MemoriesDB.instance.clearTable(); await PublicKeysDB.instance.clearTable(); diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index 8087d28de..23daecd61 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -46,6 +46,8 @@ class SemanticSearchService { Future>? _ongoingRequest; PendingQuery? _nextQuery; + get hasInitialized => _hasInitialized; + Future init() async { if (!LocalSettings.instance.hasEnabledMagicSearch()) { return; From 883363fcc0694ff754d3c75b4d562da4ab1c4743 Mon Sep 17 00:00:00 2001 From: NullPointer <126418158+0nullpointer@users.noreply.github.com> Date: Wed, 27 Dec 2023 20:02:04 +0530 Subject: [PATCH 036/175] Fix reshape order for image tensor --- .../frameworks/onnx/onnx_image_encoder.dart | 61 +------------------ 1 file changed, 2 insertions(+), 59 deletions(-) diff --git a/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart b/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart index 2336ba0c1..9d6dc325f 100644 --- a/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart +++ b/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart @@ -34,64 +34,6 @@ class OnnxImageEncoder { //Check the existence of imagePath locally final rgb = img.decodeImage(File(args["imagePath"]).readAsBytesSync())!; - // dynamic inputImage; - // if (rgb.height >= rgb.width) { - // inputImage = img.copyResize( - // rgb, - // width: 224, - // interpolation: img.Interpolation.linear, - // ); - // inputImage = img.copyCrop( - // inputImage, - // x: 0, - // y: (inputImage.height - 224) ~/ 2, - // width: 224, - // height: 224, - // ); - // } else { - // inputImage = img.copyResize( - // rgb, - // height: 224, - // interpolation: img.Interpolation.linear, - // ); - // inputImage = img.copyCrop( - // inputImage, - // x: (inputImage.width - 224) ~/ 2, - // y: 0, - // width: 224, - // height: 224, - // ); - // } - - // final mean = [0.48145466, 0.4578275, 0.40821073]; - // final std = [0.26862954, 0.26130258, 0.27577711]; - // //final processedImage = imageToByteListFloat32(rgb, 224, mean, std); - // final rgbData = [[], [], []]; // [1, 3, 224, 224 - // rgbData[0] = List.filled(224, List.filled(224, 0.0)); - // rgbData[1] = List.filled(224, List.filled(224, 0.0)); - // rgbData[2] = List.filled(224, List.filled(224, 0.0)); - - // // [3, 224*224] -> [3, 224, 224] - // for (int i = 0; i < 224; i++) { - // for (int j = 0; j < 224; j++) { - // rgbData[0][i][j] = (inputImage.getPixel(i, j).r / 255 - mean[0]) / std[0]; - // rgbData[1][i][j] = (inputImage.getPixel(i, j).g / 255 - mean[1]) / std[1]; - // rgbData[2][i][j] = (inputImage.getPixel(i, j).g / 255 - mean[2]) / std[2]; - // } - // } - - // final flattenedList = [ - // for (final subList in rgbData) - // for (final innerList in subList) - // for (final element in innerList) - // element, - // ]; - - // final floatList = Float32List(flattenedList.length); - // for (int i = 0; i < flattenedList.length; i++) { - // floatList[i] = flattenedList[i]; - // } - final int nx = rgb.width; final int ny = rgb.height; final int inputSize = 3 * nx * ny; @@ -153,7 +95,8 @@ class OnnxImageEncoder { final int v2 = min(max(v.round(), 0), 255); - final int i = 3 * (y * nx3 + x) + c; + // createTensorWithDataList is dump compared to reshape and hence has to be given with one channel after another + final int i = (y * nx3 + x) + (c % 3) * 224 * 224; result[i] = ((v2 / 255) - mean[c]) / std[c]; } From b6b7b326633789be3edb6c11ebcbdea07b4023a4 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Wed, 27 Dec 2023 22:05:47 +0530 Subject: [PATCH 037/175] v0.8.25 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 40f2e3163..94ead37a5 100644 --- a/pubspec.yaml +++ b/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.8.20+540 +version: 0.8.25+545 environment: sdk: ">=3.0.0 <4.0.0" From b1b2fb311d288da1c32045f9c93d0f8ffc8073d8 Mon Sep 17 00:00:00 2001 From: 0nullpointer Date: Thu, 4 Jan 2024 00:28:14 +0530 Subject: [PATCH 038/175] Add mobile image encoder --- .../frameworks/ml_framework.dart | 2 +- .../semantic_search/frameworks/onnx/onnx.dart | 4 ++-- .../frameworks/onnx/onnx_image_encoder.dart | 24 ------------------- .../semantic_search_service.dart | 3 ++- 4 files changed, 5 insertions(+), 28 deletions(-) diff --git a/lib/services/semantic_search/frameworks/ml_framework.dart b/lib/services/semantic_search/frameworks/ml_framework.dart index 4fc0ef7de..dead6ba7d 100644 --- a/lib/services/semantic_search/frameworks/ml_framework.dart +++ b/lib/services/semantic_search/frameworks/ml_framework.dart @@ -7,7 +7,7 @@ import "package:path_provider/path_provider.dart"; import "package:photos/core/network/network.dart"; abstract class MLFramework { - static const kImageEncoderEnabled = false; + static const kImageEncoderEnabled = true; final _logger = Logger("MLFramework"); diff --git a/lib/services/semantic_search/frameworks/onnx/onnx.dart b/lib/services/semantic_search/frameworks/onnx/onnx.dart index 061de2c3a..035004110 100644 --- a/lib/services/semantic_search/frameworks/onnx/onnx.dart +++ b/lib/services/semantic_search/frameworks/onnx/onnx.dart @@ -24,7 +24,7 @@ class ONNX extends MLFramework { @override String getImageModelRemotePath() { - return ""; + return kModelBucketEndpoint + kImageModel; } @override @@ -35,7 +35,7 @@ class ONNX extends MLFramework { @override Future loadImageModel(String path) async { final startTime = DateTime.now(); - await _clipImage.init(); + await _computer.compute(_clipImage.init); _imageEncoderAddress = await _computer.compute( _clipImage.loadModel, param: { diff --git a/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart b/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart index 9d6dc325f..7212eff9f 100644 --- a/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart +++ b/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart @@ -120,28 +120,4 @@ class OnnxImageEncoder { } return embedding; } - - Float32List imageToByteListFloat32( - img.Image image, - int inputSize, - List mean, - List std, - ) { - final convertedBytes = Float32List(1 * inputSize * inputSize * 3); - final buffer = Float32List.view(convertedBytes.buffer); - int pixelIndex = 0; - assert(mean.length == 3); - assert(std.length == 3); - - //TODO: rewrite this part - for (var i = 0; i < inputSize; i++) { - for (var j = 0; j < inputSize; j++) { - final pixel = image.getPixel(i, j); - buffer[pixelIndex++] = ((pixel.r / 255) - mean[0]) / std[0]; - buffer[pixelIndex++] = ((pixel.g / 255) - mean[1]) / std[1]; - buffer[pixelIndex++] = ((pixel.b / 255) - mean[2]) / std[2]; - } - } - return convertedBytes.buffer.asFloat32List(); - } } diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index 23daecd61..25fd082b7 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -75,6 +75,7 @@ class SemanticSearchService { await _getTextEmbedding("warm up text encoder"); _logger.info("Got text embedding"); }); + // Adding to queue only on init? Bus.instance.on().listen((event) async { _addToQueue(event.file); }); @@ -168,7 +169,7 @@ class SemanticSearchService { return; } await _frameworkInitialization.future; - _logger.info("Attempting backfill"); + _logger.info("Attempting backfill for image embeddings"); final fileIDs = await _getFileIDsToBeIndexed(); final files = await FilesDB.instance.getUploadedFiles(fileIDs); _logger.info(files.length.toString() + " to be embedded"); From 4063f75d4a0dd3d7052b97c025bb91c54e04b084 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 4 Jan 2024 10:11:24 +0530 Subject: [PATCH 039/175] created a new minimal FullScreenMemoryWidget with only delete option --- .../home/memories/full_screen_memory_new.dart | 78 +++++++++++++++++++ lib/ui/home/memories/memory_cover_widget.dart | 4 +- 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 lib/ui/home/memories/full_screen_memory_new.dart diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart new file mode 100644 index 000000000..240df4bd6 --- /dev/null +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -0,0 +1,78 @@ +import "dart:io"; + +import "package:flutter/cupertino.dart"; +import "package:flutter/material.dart"; +import "package:photos/models/memory.dart"; +import "package:photos/ui/actions/file/file_actions.dart"; +import "package:photos/ui/viewer/file/file_widget.dart"; + +class FullScreenMemoryNew extends StatefulWidget { + final String title; + final List memories; + final int index; + const FullScreenMemoryNew(this.title, this.memories, this.index, {super.key}); + + @override + State createState() => _FullScreenMemoryNewState(); +} + +class _FullScreenMemoryNewState extends State { + late final ValueNotifier _currentIndex; + + @override + void initState() { + super.initState(); + _currentIndex = ValueNotifier(widget.index); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + extendBodyBehindAppBar: true, + appBar: AppBar(), + body: PageView.builder( + itemBuilder: (context, index) { + return Stack( + alignment: Alignment.bottomCenter, + children: [ + FileWidget( + widget.memories[index].file, + autoPlay: false, + tagPrefix: "memories", + backgroundDecoration: const BoxDecoration( + color: Colors.transparent, + ), + ), + IconButton( + icon: Icon( + Platform.isAndroid + ? Icons.delete_outline + : CupertinoIcons.delete, + color: Colors.white, //same for both themes + ), + onPressed: () async { + await showSingleFileDeleteSheet( + context, + widget.memories[_currentIndex.value].file, + onFileRemoved: (file) => + {onFileDeleted(widget.memories[_currentIndex.value])}, + ); + }, + ), + ], + ); + }, + onPageChanged: (index) { + _currentIndex.value = index; + }, + itemCount: widget.memories.length, + ), + ); + } + + Future onFileDeleted(Memory removedMemory) async { + setState(() { + widget.memories.remove(removedMemory); + }); + } +} diff --git a/lib/ui/home/memories/memory_cover_widget.dart b/lib/ui/home/memories/memory_cover_widget.dart index 91e1159d3..bad08dd20 100644 --- a/lib/ui/home/memories/memory_cover_widget.dart +++ b/lib/ui/home/memories/memory_cover_widget.dart @@ -2,7 +2,7 @@ import "package:flutter/material.dart"; import "package:photos/generated/l10n.dart"; import "package:photos/models/memory.dart"; import "package:photos/theme/ente_theme.dart"; -import "package:photos/ui/home/memories/full_screen_memory.dart"; +import "package:photos/ui/home/memories/full_screen_memory_new.dart"; import "package:photos/ui/viewer/file/thumbnail_widget.dart"; import "package:photos/utils/navigation_util.dart"; @@ -27,7 +27,7 @@ class _MemoryCovertWidgetState extends State { onTap: () async { await routeToPage( context, - FullScreenMemory(title, widget.memories, index), + FullScreenMemoryNew(title, widget.memories, index), forceCustomPageRoute: true, ); setState(() {}); From 5413646a2820f10b9347b50d161be7cc72e36a81 Mon Sep 17 00:00:00 2001 From: 0nullpointer Date: Thu, 4 Jan 2024 11:19:16 +0530 Subject: [PATCH 040/175] fix image encoder --- lib/services/semantic_search/semantic_search_service.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index 25fd082b7..7c4771898 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -17,7 +17,6 @@ import "package:photos/objectbox.g.dart"; import "package:photos/services/semantic_search/embedding_store.dart"; import "package:photos/services/semantic_search/frameworks/ml_framework.dart"; import 'package:photos/services/semantic_search/frameworks/onnx/onnx.dart'; -import "package:photos/utils/file_util.dart"; import "package:photos/utils/local_settings.dart"; import "package:photos/utils/thumbnail_util.dart"; @@ -32,7 +31,7 @@ class SemanticSearchService { static const kModelName = "clip"; static const kEmbeddingLength = 512; static const kScoreThreshold = 0.23; - static const kShouldPushEmbeddings = false; + static const kShouldPushEmbeddings = true; final _logger = Logger("SemanticSearchService"); final _queue = Queue(); @@ -150,7 +149,7 @@ class SemanticSearchService { .getEmbeddingBox() .query( Embedding_.model.equals( - "ggml" + "-" + kModelName, + _mlFramework.getFrameworkName() + "-" + kModelName, ), ) .watch(triggerImmediately: true) @@ -266,7 +265,7 @@ class SemanticSearchService { return; } try { - final filePath = (await getFile(file))!.path; + final filePath = (await getThumbnailForUploadedFile(file))!.path; _logger.info("Running clip over $file"); final result = await _mlFramework.getImageEmbedding(filePath); if (result.length != kEmbeddingLength) { From 83200fbb24f7a7aac28c2f8194a9dfa5848d07b6 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 4 Jan 2024 11:39:06 +0530 Subject: [PATCH 041/175] Initialize ORT only once --- .../semantic_search/frameworks/onnx/onnx.dart | 12 ++++++++++-- .../frameworks/onnx/onnx_image_encoder.dart | 4 ---- .../frameworks/onnx/onnx_text_encoder.dart | 4 ---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/services/semantic_search/frameworks/onnx/onnx.dart b/lib/services/semantic_search/frameworks/onnx/onnx.dart index 035004110..9b12249be 100644 --- a/lib/services/semantic_search/frameworks/onnx/onnx.dart +++ b/lib/services/semantic_search/frameworks/onnx/onnx.dart @@ -32,10 +32,15 @@ class ONNX extends MLFramework { return kModelBucketEndpoint + kTextModel; } + @override + Future init() async { + await _computer.compute(initOrtEnv); + await super.init(); + } + @override Future loadImageModel(String path) async { final startTime = DateTime.now(); - await _computer.compute(_clipImage.init); _imageEncoderAddress = await _computer.compute( _clipImage.loadModel, param: { @@ -51,7 +56,6 @@ class ONNX extends MLFramework { @override Future loadTextModel(String path) async { final startTime = DateTime.now(); - await _computer.compute(_clipText.init); // Doing this from main isolate since `rootBundle` cannot be accessed outside it await _clipText.initTokenizer(); _textEncoderAddress = await _computer.compute( @@ -120,3 +124,7 @@ class ONNX extends MLFramework { _logger.info('Released'); } } + +void initOrtEnv() async { + OrtEnv.instance.init(); +} diff --git a/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart b/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart index 7212eff9f..9391616e9 100644 --- a/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart +++ b/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart @@ -9,10 +9,6 @@ import "package:onnxruntime/onnxruntime.dart"; class OnnxImageEncoder { final _logger = Logger("OnnxImageEncoder"); - Future init() async { - OrtEnv.instance.init(); - } - Future loadModel(Map args) async { final sessionOptions = OrtSessionOptions() ..setInterOpNumThreads(1) diff --git a/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart b/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart index 6a8bb3b74..a27bdb53a 100644 --- a/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart +++ b/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart @@ -12,10 +12,6 @@ class OnnxTextEncoder { final _logger = Logger("OnnxTextEncoder"); final OnnxTextTokenizer _tokenizer = OnnxTextTokenizer(); - Future init() async { - OrtEnv.instance.init(); - } - // Do not run in an isolate since rootBundle can only be accessed in the main isolate Future initTokenizer() async { final vocab = await rootBundle.loadString(kVocabFilePath); From 060b516058b8c8ae59215e0b77e75881d033e54b Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 4 Jan 2024 13:14:56 +0530 Subject: [PATCH 042/175] used an InheritedWidget to hold list of memories and update the InheritedWidget when an item is deleted, which rebuilds widgets that depend on the inheritedWidget --- .../home/memories/full_screen_memory_new.dart | 148 ++++++++++++++---- lib/ui/home/memories/memory_cover_widget.dart | 6 +- 2 files changed, 124 insertions(+), 30 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 240df4bd6..7c718eb54 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -2,31 +2,107 @@ import "dart:io"; import "package:flutter/cupertino.dart"; import "package:flutter/material.dart"; +import "package:photos/core/configuration.dart"; import "package:photos/models/memory.dart"; import "package:photos/ui/actions/file/file_actions.dart"; import "package:photos/ui/viewer/file/file_widget.dart"; +class FullScreenMemoryDataUpdater extends StatefulWidget { + final List memories; + final int initialIndex; + final Widget child; + const FullScreenMemoryDataUpdater({ + required this.memories, + required this.initialIndex, + required this.child, + super.key, + }); + + @override + State createState() => + _FullScreenMemoryDataUpdaterState(); +} + +class _FullScreenMemoryDataUpdaterState + extends State { + late ValueNotifier indexNotifier; + + @override + void initState() { + super.initState(); + indexNotifier = ValueNotifier(widget.initialIndex); + } + + @override + void dispose() { + indexNotifier.dispose(); + super.dispose(); + } + + void removeCurrentMemory() { + setState(() { + widget.memories.removeAt(indexNotifier.value); + }); + } + + @override + Widget build(BuildContext context) { + return FullScreenMemoryData( + memories: widget.memories, + indexNotifier: indexNotifier, + removeCurrentMemory: removeCurrentMemory, + child: widget.child, + ); + } +} + +class FullScreenMemoryData extends InheritedWidget { + final List memories; + final ValueNotifier indexNotifier; + final VoidCallback removeCurrentMemory; + + const FullScreenMemoryData({ + required this.memories, + required this.indexNotifier, + required this.removeCurrentMemory, + required Widget child, + Key? key, + }) : super(child: child, key: key); + + static FullScreenMemoryData? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } + + @override + bool updateShouldNotify(FullScreenMemoryData oldWidget) { + // Checking oldWidget.memories.length != memories.length here doesn't work + //because the old widget and new widget reference the same memories list. + return true; + } +} + class FullScreenMemoryNew extends StatefulWidget { final String title; - final List memories; - final int index; - const FullScreenMemoryNew(this.title, this.memories, this.index, {super.key}); + final int initialIndex; + const FullScreenMemoryNew( + this.title, + this.initialIndex, { + super.key, + }); @override State createState() => _FullScreenMemoryNewState(); } class _FullScreenMemoryNewState extends State { - late final ValueNotifier _currentIndex; - @override void initState() { super.initState(); - _currentIndex = ValueNotifier(widget.index); } @override Widget build(BuildContext context) { + final inheritedData = FullScreenMemoryData.of(context)!; return Scaffold( extendBodyBehindAppBar: true, appBar: AppBar(), @@ -36,43 +112,57 @@ class _FullScreenMemoryNewState extends State { alignment: Alignment.bottomCenter, children: [ FileWidget( - widget.memories[index].file, + inheritedData.memories[index].file, autoPlay: false, tagPrefix: "memories", backgroundDecoration: const BoxDecoration( color: Colors.transparent, ), ), - IconButton( - icon: Icon( - Platform.isAndroid - ? Icons.delete_outline - : CupertinoIcons.delete, - color: Colors.white, //same for both themes - ), - onPressed: () async { - await showSingleFileDeleteSheet( - context, - widget.memories[_currentIndex.value].file, - onFileRemoved: (file) => - {onFileDeleted(widget.memories[_currentIndex.value])}, - ); - }, - ), + Configuration.instance.getUserID() != + inheritedData.memories[index].file.ownerID + ? Padding( + padding: const EdgeInsets.all(64.0), + child: Container( + color: Colors.red, + height: 30, + width: 30, + ), + ) + : const SizedBox.shrink(), + const BottomIcons(), ], ); }, onPageChanged: (index) { - _currentIndex.value = index; + inheritedData.indexNotifier.value = index; }, - itemCount: widget.memories.length, + itemCount: inheritedData.memories.length, ), ); } +} - Future onFileDeleted(Memory removedMemory) async { - setState(() { - widget.memories.remove(removedMemory); - }); +class BottomIcons extends StatelessWidget { + const BottomIcons({super.key}); + + @override + Widget build(BuildContext context) { + final inheritedData = FullScreenMemoryData.of(context)!; + return IconButton( + icon: Icon( + Platform.isAndroid ? Icons.delete_outline : CupertinoIcons.delete, + color: Colors.white, //same for both themes + ), + onPressed: () async { + await showSingleFileDeleteSheet( + context, + inheritedData.memories[inheritedData.indexNotifier.value].file, + onFileRemoved: (file) => { + inheritedData.removeCurrentMemory.call(), + }, + ); + }, + ); } } diff --git a/lib/ui/home/memories/memory_cover_widget.dart b/lib/ui/home/memories/memory_cover_widget.dart index bad08dd20..1be67f8dd 100644 --- a/lib/ui/home/memories/memory_cover_widget.dart +++ b/lib/ui/home/memories/memory_cover_widget.dart @@ -27,7 +27,11 @@ class _MemoryCovertWidgetState extends State { onTap: () async { await routeToPage( context, - FullScreenMemoryNew(title, widget.memories, index), + FullScreenMemoryDataUpdater( + initialIndex: index, + memories: widget.memories, + child: FullScreenMemoryNew(title, index), + ), forceCustomPageRoute: true, ); setState(() {}); From 44fdff07176bd20114d93727f2ca15fa5544d658 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 4 Jan 2024 14:59:42 +0530 Subject: [PATCH 043/175] v0.8.26 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 94ead37a5..de47b0705 100644 --- a/pubspec.yaml +++ b/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.8.25+545 +version: 0.8.26+546 environment: sdk: ">=3.0.0 <4.0.0" From 8e238cbf40919c1e3c2db01081286a5c4662dc2a Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 4 Jan 2024 15:52:41 +0530 Subject: [PATCH 044/175] Fix string --- lib/generated/intl/messages_en.dart | 2 +- lib/l10n/intl_en.arb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index a6803ced5..1872064d5 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -514,7 +514,7 @@ class MessageLookup extends MessageLookupByLibrary { "This will delete all empty albums. This is useful when you want to reduce the clutter in your album list."), "deleteAll": MessageLookupByLibrary.simpleMessage("Delete All"), "deleteConfirmDialogBody": MessageLookupByLibrary.simpleMessage( - "This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."), + "This account is linked to other ente apps, if you use any. Your uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted."), "deleteEmailRequest": MessageLookupByLibrary.simpleMessage( "Please send an email to account-deletion@ente.io from your registered email address."), "deleteEmptyAlbums": diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 9bb80a822..2f26f7da5 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1139,7 +1139,7 @@ "addToHiddenAlbum": "Add to hidden album", "moveToHiddenAlbum": "Move to hidden album", "fileTypes": "File types", - "deleteConfirmDialogBody": "This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted.", + "deleteConfirmDialogBody": "This account is linked to other ente apps, if you use any. Your uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted.", "hearUsWhereTitle": "How did you hear about Ente? (optional)", "hearUsExplanation": "We don't track app installs. It'd help if you told us where you found us!", "viewAddOnButton": "View add-ons", From 6f9d53fc4a7e07b3cd965663861efcfad94ca924 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 4 Jan 2024 15:57:17 +0530 Subject: [PATCH 045/175] Update translation --- lib/generated/l10n.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 5765cf70b..c4b5a40ea 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -7925,10 +7925,10 @@ class S { ); } - /// `This account is linked to other ente apps, if you use any.\n\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted.` + /// `This account is linked to other ente apps, if you use any. Your uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted.` String get deleteConfirmDialogBody { return Intl.message( - 'This account is linked to other ente apps, if you use any.\\n\\nYour uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted.', + 'This account is linked to other ente apps, if you use any. Your uploaded data, across all ente apps, will be scheduled for deletion, and your account will be permanently deleted.', name: 'deleteConfirmDialogBody', desc: '', args: [], From 02263a4d882df1c8433a5e541ae76392000a2145 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 4 Jan 2024 16:00:51 +0530 Subject: [PATCH 046/175] Add bottom icons to memories --- .../home/memories/full_screen_memory_new.dart | 111 ++++++++++++++---- 1 file changed, 86 insertions(+), 25 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 7c718eb54..ba9e61626 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -6,6 +6,8 @@ import "package:photos/core/configuration.dart"; import "package:photos/models/memory.dart"; import "package:photos/ui/actions/file/file_actions.dart"; import "package:photos/ui/viewer/file/file_widget.dart"; +import "package:photos/ui/viewer/file_details/favorite_widget.dart"; +import "package:photos/utils/share_util.dart"; class FullScreenMemoryDataUpdater extends StatefulWidget { final List memories; @@ -95,11 +97,18 @@ class FullScreenMemoryNew extends StatefulWidget { } class _FullScreenMemoryNewState extends State { + PageController? _pageController; @override void initState() { super.initState(); } + @override + void dispose() { + _pageController?.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final inheritedData = FullScreenMemoryData.of(context)!; @@ -107,6 +116,9 @@ class _FullScreenMemoryNewState extends State { extendBodyBehindAppBar: true, appBar: AppBar(), body: PageView.builder( + controller: _pageController ??= PageController( + initialPage: widget.initialIndex, + ), itemBuilder: (context, index) { return Stack( alignment: Alignment.bottomCenter, @@ -119,18 +131,16 @@ class _FullScreenMemoryNewState extends State { color: Colors.transparent, ), ), - Configuration.instance.getUserID() != - inheritedData.memories[index].file.ownerID - ? Padding( - padding: const EdgeInsets.all(64.0), - child: Container( - color: Colors.red, - height: 30, - width: 30, - ), - ) - : const SizedBox.shrink(), - const BottomIcons(), + BottomIcons(index), + Padding( + padding: const EdgeInsets.all(120), + child: Container( + color: Colors.black, + child: Text( + inheritedData.memories[index].file.generatedID.toString(), + ), + ), + ), ], ); }, @@ -144,25 +154,76 @@ class _FullScreenMemoryNewState extends State { } class BottomIcons extends StatelessWidget { - const BottomIcons({super.key}); + final int pageViewIndex; + const BottomIcons(this.pageViewIndex, {super.key}); @override Widget build(BuildContext context) { final inheritedData = FullScreenMemoryData.of(context)!; - return IconButton( - icon: Icon( - Platform.isAndroid ? Icons.delete_outline : CupertinoIcons.delete, - color: Colors.white, //same for both themes + final currentFile = inheritedData.memories[pageViewIndex].file; + + final List rowChildren = [ + IconButton( + icon: Icon( + Platform.isAndroid ? Icons.info_outline : CupertinoIcons.info, + color: Colors.white, //same for both themes + ), + onPressed: () { + showDetailsSheet(context, currentFile); + }, ), - onPressed: () async { - await showSingleFileDeleteSheet( - context, - inheritedData.memories[inheritedData.indexNotifier.value].file, - onFileRemoved: (file) => { - inheritedData.removeCurrentMemory.call(), + ]; + rowChildren.add( + Padding( + padding: const EdgeInsets.all(8), + child: Text(currentFile.generatedID.toString()), + ), + ); + if (currentFile.ownerID == null || + (Configuration.instance.getUserID() ?? 0) == currentFile.ownerID) { + rowChildren.addAll([ + IconButton( + icon: Icon( + Platform.isAndroid ? Icons.delete_outline : CupertinoIcons.delete, + color: Colors.white, //same for both themes + ), + onPressed: () async { + await showSingleFileDeleteSheet( + context, + inheritedData.memories[inheritedData.indexNotifier.value].file, + onFileRemoved: (file) => { + inheritedData.removeCurrentMemory.call(), + }, + ); }, - ); - }, + ), + SizedBox( + height: 32, + child: FavoriteWidget(currentFile), + ), + ]); + } + rowChildren.add( + IconButton( + icon: Icon( + Icons.adaptive.share, + color: Colors.white, //same for both themes + ), + onPressed: () { + share(context, [currentFile]); + }, + ), + ); + + return SafeArea( + child: Container( + alignment: Alignment.bottomCenter, + padding: const EdgeInsets.fromLTRB(12, 0, 12, 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: rowChildren, + ), + ), ); } } From aecd8b3d81b414ba2ca06968c095fcef2d13f603 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 4 Jan 2024 18:34:14 +0530 Subject: [PATCH 047/175] v0.8.28 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index de47b0705..a4dcdfbc5 100644 --- a/pubspec.yaml +++ b/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.8.26+546 +version: 0.8.28+548 environment: sdk: ">=3.0.0 <4.0.0" From 6faa93c90cc968126fe6d413257988e4aa9feef3 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 4 Jan 2024 19:01:32 +0530 Subject: [PATCH 048/175] pop FullScreenMemory screen when last memory is deleted --- lib/ui/home/memories/full_screen_memory_new.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index ba9e61626..e6e97e926 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -193,6 +193,10 @@ class BottomIcons extends StatelessWidget { inheritedData.memories[inheritedData.indexNotifier.value].file, onFileRemoved: (file) => { inheritedData.removeCurrentMemory.call(), + if (inheritedData.memories.isEmpty) + { + Navigator.of(context).pop(), + }, }, ); }, From f143f90ec0e1c78873e443440fea3fd5e63c2b6a Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 4 Jan 2024 19:09:06 +0530 Subject: [PATCH 049/175] fix: handle range error when all memories are deleted and FullScreenMemory screen is popped --- lib/ui/home/memories/memory_cover_widget.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/ui/home/memories/memory_cover_widget.dart b/lib/ui/home/memories/memory_cover_widget.dart index 1be67f8dd..df1b6d5b3 100644 --- a/lib/ui/home/memories/memory_cover_widget.dart +++ b/lib/ui/home/memories/memory_cover_widget.dart @@ -21,6 +21,11 @@ class MemoryCovertWidget extends StatefulWidget { class _MemoryCovertWidgetState extends State { @override Widget build(BuildContext context) { + //memories will be empty if all memories are deleted and setState is called + //after FullScreenMemory screen is popped + if (widget.memories.isEmpty) { + return const SizedBox.shrink(); + } final index = _getNextMemoryIndex(); final title = _getTitle(widget.memories[index]); return GestureDetector( From bcbc8db569935e7dc6675a175cca0d633930b41f Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 4 Jan 2024 19:46:11 +0530 Subject: [PATCH 050/175] Add dependency on Isar --- pubspec.lock | 82 +++++++++++++++++++++++++++++++++++++++++----------- pubspec.yaml | 8 +++-- 2 files changed, 71 insertions(+), 19 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index f6af1e1ed..dcac2c4d2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -141,10 +141,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.7" build_runner_core: dependency: transitive description: @@ -347,6 +347,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.1" + dartx: + dependency: transitive + description: + name: dartx + sha256: "8b25435617027257d43e6508b5fe061012880ddfdaa75a71d607c3de2a13d244" + url: "https://pub.dev" + source: hosted + version: "1.2.0" dbus: dependency: transitive description: @@ -1055,6 +1063,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + isar: + dependency: "direct main" + description: + name: isar + sha256: "99165dadb2cf2329d3140198363a7e7bff9bbd441871898a87e26914d25cf1ea" + url: "https://pub.dev" + source: hosted + version: "3.1.0+1" + isar_flutter_libs: + dependency: "direct main" + description: + name: isar_flutter_libs + sha256: bc6768cc4b9c61aabff77152e7f33b4b17d2fc93134f7af1c3dd51500fe8d5e8 + url: "https://pub.dev" + source: hosted + version: "3.1.0+1" + isar_generator: + dependency: "direct dev" + description: + name: isar_generator + sha256: "76c121e1295a30423604f2f819bc255bc79f852f3bc8743a24017df6068ad133" + url: "https://pub.dev" + source: hosted + version: "3.1.0+1" js: dependency: transitive description: @@ -1470,50 +1502,50 @@ packages: dependency: "direct main" description: name: path_provider - sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.1.1" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" url: "https://pub.dev" source: hosted - version: "2.0.27" + version: "2.2.2" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.1" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.2.1" pedantic: dependency: "direct main" description: @@ -2063,6 +2095,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.3" + time: + dependency: transitive + description: + name: time + sha256: "83427e11d9072e038364a5e4da559e85869b227cf699a541be0da74f14140124" + url: "https://pub.dev" + source: hosted + version: "2.1.3" timezone: dependency: transitive description: @@ -2329,13 +2369,13 @@ packages: source: hosted version: "0.0.2" watcher: - dependency: transitive + dependency: "direct overridden" description: name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.0" web: dependency: transitive description: @@ -2432,6 +2472,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + xxh3: + dependency: transitive + description: + name: xxh3 + sha256: a92b30944a9aeb4e3d4f3c3d4ddb3c7816ca73475cd603682c4f8149690f56d7 + url: "https://pub.dev" + source: hosted + version: "1.0.1" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 40f2e3163..419fd64be 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -94,6 +94,8 @@ dependencies: image_editor: ^1.3.0 in_app_purchase: ^3.0.7 intl: ^0.18.0 + isar: ^3.1.0+1 + isar_flutter_libs: ^3.1.0+1 json_annotation: ^4.8.0 latlong2: ^0.9.0 like_button: ^2.0.2 @@ -124,7 +126,7 @@ dependencies: page_transition: ^2.0.2 password_strength: ^0.2.0 path: #dart - path_provider: ^2.0.1 + path_provider: ^2.1.1 pedantic: ^1.9.2 photo_manager: ^2.5.0 photo_view: ^0.14.0 @@ -175,6 +177,7 @@ dependency_overrides: url: https://github.com/ente-io/packages.git ref: android_video_roation_fix path: packages/video_player/video_player/ + watcher: ^1.1.0 flutter_intl: enabled: true @@ -182,7 +185,7 @@ flutter_intl: dev_dependencies: - build_runner: ^2.4.6 + build_runner: ^2.4.7 flutter_driver: sdk: flutter flutter_lints: ^2.0.1 @@ -191,6 +194,7 @@ dev_dependencies: freezed: ^2.3.2 integration_test: sdk: flutter + isar_generator: ^3.1.0+1 json_serializable: ^6.6.1 objectbox_generator: any test: ^1.22.0 From 3eca6e44a7053c676c232e98e6655937202bf4f4 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 4 Jan 2024 20:21:59 +0530 Subject: [PATCH 051/175] Update iOS config --- ios/Podfile.lock | 8 +++++++- ios/Runner.xcodeproj/project.pbxproj | 6 ++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b77a5b801..878a0627d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -99,6 +99,8 @@ PODS: - FlutterMacOS - integration_test (0.0.1): - Flutter + - isar_flutter_libs (1.0.0): + - Flutter - libwebp (1.3.2): - libwebp/demux (= 1.3.2) - libwebp/mux (= 1.3.2) @@ -218,6 +220,7 @@ DEPENDENCIES: - image_editor_common (from `.symlinks/plugins/image_editor_common/ios`) - in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/darwin`) - integration_test (from `.symlinks/plugins/integration_test/ios`) + - isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`) - local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`) - media_extension (from `.symlinks/plugins/media_extension/ios`) - media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`) @@ -308,6 +311,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/in_app_purchase_storekit/darwin" integration_test: :path: ".symlinks/plugins/integration_test/ios" + isar_flutter_libs: + :path: ".symlinks/plugins/isar_flutter_libs/ios" local_auth_ios: :path: ".symlinks/plugins/local_auth_ios/ios" media_extension: @@ -387,6 +392,7 @@ SPEC CHECKSUMS: image_editor_common: d6f6644ae4a6de80481e89fe6d0a8c49e30b4b43 in_app_purchase_storekit: 4fb7ee9e824b1f09107fbfbbce8c4b276366dc43 integration_test: 13825b8a9334a850581300559b8839134b124670 + isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073 libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009 local_auth_ios: c6cf091ded637a88f24f86a8875d8b0f526e2605 Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d @@ -405,7 +411,7 @@ SPEC CHECKSUMS: open_mail_app: 794172f6a22cd16319d3ddaf45e945b2f74952b0 OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7 - path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8 + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604 PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index d003810dd..4c137dda2 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -16,7 +16,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - DA6BE5E826B3BC8600656280 /* (null) in Resources */ = {isa = PBXBuildFile; }; + DA6BE5E826B3BC8600656280 /* BuildFile in Resources */ = {isa = PBXBuildFile; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -213,7 +213,7 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - DA6BE5E826B3BC8600656280 /* (null) in Resources */, + DA6BE5E826B3BC8600656280 /* BuildFile in Resources */, 277218A0270F596900FFE3CC /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -291,6 +291,7 @@ "${BUILT_PRODUCTS_DIR}/image_editor_common/image_editor_common.framework", "${BUILT_PRODUCTS_DIR}/in_app_purchase_storekit/in_app_purchase_storekit.framework", "${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework", + "${BUILT_PRODUCTS_DIR}/isar_flutter_libs/isar_flutter_libs.framework", "${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework", "${BUILT_PRODUCTS_DIR}/local_auth_ios/local_auth_ios.framework", "${BUILT_PRODUCTS_DIR}/media_extension/media_extension.framework", @@ -371,6 +372,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_editor_common.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/in_app_purchase_storekit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/isar_flutter_libs.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth_ios.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_extension.framework", From 22fd204e11234b66a4e918eb17b84ff40cbab40c Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 4 Jan 2024 20:25:16 +0530 Subject: [PATCH 052/175] Replace ObjectBox with Isar --- lib/core/configuration.dart | 4 +- lib/db/embeddings_db.dart | 55 ++ lib/db/object_box.dart | 27 - lib/models/embedding.dart | 42 +- lib/models/embedding.g.dart | 756 ++++++++++++++++++ lib/objectbox-model.json | 47 -- lib/objectbox.g.dart | 171 ---- .../semantic_search/embedding_store.dart | 22 +- .../semantic_search/frameworks/ggml.dart | 5 - .../frameworks/ml_framework.dart | 3 - .../semantic_search/frameworks/onnx/onnx.dart | 5 - .../semantic_search_service.dart | 47 +- pubspec.lock | 40 - pubspec.yaml | 3 - 14 files changed, 879 insertions(+), 348 deletions(-) create mode 100644 lib/db/embeddings_db.dart delete mode 100644 lib/db/object_box.dart create mode 100644 lib/models/embedding.g.dart delete mode 100644 lib/objectbox-model.json delete mode 100644 lib/objectbox.g.dart diff --git a/lib/core/configuration.dart b/lib/core/configuration.dart index a77099dc3..6b4e1b0ea 100644 --- a/lib/core/configuration.dart +++ b/lib/core/configuration.dart @@ -10,9 +10,9 @@ import 'package:photos/core/constants.dart'; import 'package:photos/core/error-reporting/super_logging.dart'; import 'package:photos/core/event_bus.dart'; import 'package:photos/db/collections_db.dart'; +import "package:photos/db/embeddings_db.dart"; import 'package:photos/db/files_db.dart'; import 'package:photos/db/memories_db.dart'; -import "package:photos/db/object_box.dart"; import 'package:photos/db/public_keys_db.dart'; import 'package:photos/db/trash_db.dart'; import 'package:photos/db/upload_locks_db.dart'; @@ -159,7 +159,7 @@ class Configuration { _secretKey = null; await FilesDB.instance.clearTable(); SemanticSearchService.instance.hasInitialized - ? await ObjectBox.instance.clearTable() + ? await EmbeddingsDB.instance.clearTable() : null; await CollectionsDB.instance.clearTable(); await MemoriesDB.instance.clearTable(); diff --git a/lib/db/embeddings_db.dart b/lib/db/embeddings_db.dart new file mode 100644 index 000000000..4d030a6cf --- /dev/null +++ b/lib/db/embeddings_db.dart @@ -0,0 +1,55 @@ +import "package:isar/isar.dart"; +import 'package:path_provider/path_provider.dart'; +import "package:photos/models/embedding.dart"; + +class EmbeddingsDB { + late final Isar _isar; + + EmbeddingsDB._privateConstructor(); + + static final EmbeddingsDB instance = EmbeddingsDB._privateConstructor(); + + Future init() async { + final dir = await getApplicationDocumentsDirectory(); + _isar = await Isar.open( + [EmbeddingSchema], + directory: dir.path, + ); + } + + Future clearTable() async { + await _isar.clear(); + } + + Stream> getStream(Model model) { + return _isar.embeddings.filter().modelEqualTo(model).watch(); + } + + Future> getAll(Model model) async { + return _isar.embeddings.filter().modelEqualTo(model).findAll(); + } + + Future put(Embedding embedding) { + return _isar.writeTxn(() async { + await _isar.embeddings.put(embedding); + }); + } + + Future putMany(List embeddings) { + return _isar.writeTxn(() async { + await _isar.embeddings.putAll(embeddings); + }); + } + + Future> getUnsyncedEmbeddings() async { + return await _isar.embeddings.filter().updationTimeEqualTo(null).findAll(); + } + + Future deleteAllForModel(Model model) async { + await _isar.writeTxn(() async { + final embeddings = + await _isar.embeddings.filter().modelEqualTo(model).findAll(); + await _isar.embeddings.deleteAll(embeddings.map((e) => e.id).toList()); + }); + } +} diff --git a/lib/db/object_box.dart b/lib/db/object_box.dart deleted file mode 100644 index 635810092..000000000 --- a/lib/db/object_box.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:path/path.dart' as p; -import 'package:path_provider/path_provider.dart'; -import "package:photos/models/embedding.dart"; -import "package:photos/objectbox.g.dart"; // created by `flutter pub run build_runner build` - -class ObjectBox { - /// The Store of this app. - late final Store store; - - ObjectBox._privateConstructor(); - - static final ObjectBox instance = ObjectBox._privateConstructor(); - - Future init() async { - final docsDir = await getApplicationDocumentsDirectory(); - // Future openStore() {...} is defined in the generated objectbox.g.dart - store = await openStore(directory: p.join(docsDir.path, "object-box-store")); - } - - Future clearTable() async { - getEmbeddingBox().removeAll(); - } - - Box getEmbeddingBox() { - return store.box(); - } -} diff --git a/lib/models/embedding.dart b/lib/models/embedding.dart index 4e35bce99..ddf6c33f8 100644 --- a/lib/models/embedding.dart +++ b/lib/models/embedding.dart @@ -1,12 +1,15 @@ import "dart:convert"; -import "package:objectbox/objectbox.dart"; +import "package:isar/isar.dart"; -@Entity() +part 'embedding.g.dart'; + +@collection class Embedding { - @Id(assignable: true) + Id id = Isar.autoIncrement; // you can also use id = null to auto increment final int fileID; - final String model; + @enumerated + final Model model; final List embedding; int? updationTime; @@ -25,3 +28,34 @@ class Embedding { return jsonEncode(embedding); } } + +enum Model { + onnxClip, + ggmlClip, +} + +extension ModelExtension on Model { + String get name => serialize(this); +} + +String serialize(Model model) { + switch (model) { + case Model.onnxClip: + return 'onnx-clip'; + case Model.ggmlClip: + return 'ggml-clip'; + default: + throw Exception('$model is not a valid Model'); + } +} + +Model deserialize(String model) { + switch (model) { + case 'onnx-clip': + return Model.onnxClip; + case 'ggml-clip': + return Model.ggmlClip; + default: + throw Exception('$model is not a valid Model'); + } +} diff --git a/lib/models/embedding.g.dart b/lib/models/embedding.g.dart new file mode 100644 index 000000000..3f8fcfa07 --- /dev/null +++ b/lib/models/embedding.g.dart @@ -0,0 +1,756 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'embedding.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters, always_specify_types + +extension GetEmbeddingCollection on Isar { + IsarCollection get embeddings => this.collection(); +} + +const EmbeddingSchema = CollectionSchema( + name: r'Embedding', + id: -8064100183150254587, + properties: { + r'embedding': PropertySchema( + id: 0, + name: r'embedding', + type: IsarType.doubleList, + ), + r'fileID': PropertySchema( + id: 1, + name: r'fileID', + type: IsarType.long, + ), + r'model': PropertySchema( + id: 2, + name: r'model', + type: IsarType.byte, + enumMap: _EmbeddingmodelEnumValueMap, + ), + r'updationTime': PropertySchema( + id: 3, + name: r'updationTime', + type: IsarType.long, + ) + }, + estimateSize: _embeddingEstimateSize, + serialize: _embeddingSerialize, + deserialize: _embeddingDeserialize, + deserializeProp: _embeddingDeserializeProp, + idName: r'id', + indexes: {}, + links: {}, + embeddedSchemas: {}, + getId: _embeddingGetId, + getLinks: _embeddingGetLinks, + attach: _embeddingAttach, + version: '3.1.0+1', +); + +int _embeddingEstimateSize( + Embedding object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + bytesCount += 3 + object.embedding.length * 8; + return bytesCount; +} + +void _embeddingSerialize( + Embedding object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeDoubleList(offsets[0], object.embedding); + writer.writeLong(offsets[1], object.fileID); + writer.writeByte(offsets[2], object.model.index); + writer.writeLong(offsets[3], object.updationTime); +} + +Embedding _embeddingDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = Embedding( + embedding: reader.readDoubleList(offsets[0]) ?? [], + fileID: reader.readLong(offsets[1]), + model: _EmbeddingmodelValueEnumMap[reader.readByteOrNull(offsets[2])] ?? + Model.onnxClip, + updationTime: reader.readLongOrNull(offsets[3]), + ); + object.id = id; + return object; +} + +P _embeddingDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readDoubleList(offset) ?? []) as P; + case 1: + return (reader.readLong(offset)) as P; + case 2: + return (_EmbeddingmodelValueEnumMap[reader.readByteOrNull(offset)] ?? + Model.onnxClip) as P; + case 3: + return (reader.readLongOrNull(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +const _EmbeddingmodelEnumValueMap = { + 'onnxClip': 0, + 'ggmlClip': 1, +}; +const _EmbeddingmodelValueEnumMap = { + 0: Model.onnxClip, + 1: Model.ggmlClip, +}; + +Id _embeddingGetId(Embedding object) { + return object.id; +} + +List> _embeddingGetLinks(Embedding object) { + return []; +} + +void _embeddingAttach(IsarCollection col, Id id, Embedding object) { + object.id = id; +} + +extension EmbeddingQueryWhereSort + on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension EmbeddingQueryWhere + on QueryBuilder { + QueryBuilder idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder idNotEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder idGreaterThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } +} + +extension EmbeddingQueryFilter + on QueryBuilder { + QueryBuilder + embeddingElementEqualTo( + double value, { + double epsilon = Query.epsilon, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'embedding', + value: value, + epsilon: epsilon, + )); + }); + } + + QueryBuilder + embeddingElementGreaterThan( + double value, { + bool include = false, + double epsilon = Query.epsilon, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'embedding', + value: value, + epsilon: epsilon, + )); + }); + } + + QueryBuilder + embeddingElementLessThan( + double value, { + bool include = false, + double epsilon = Query.epsilon, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'embedding', + value: value, + epsilon: epsilon, + )); + }); + } + + QueryBuilder + embeddingElementBetween( + double lower, + double upper, { + bool includeLower = true, + bool includeUpper = true, + double epsilon = Query.epsilon, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'embedding', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + epsilon: epsilon, + )); + }); + } + + QueryBuilder + embeddingLengthEqualTo(int length) { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'embedding', + length, + true, + length, + true, + ); + }); + } + + QueryBuilder embeddingIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'embedding', + 0, + true, + 0, + true, + ); + }); + } + + QueryBuilder + embeddingIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'embedding', + 0, + false, + 999999, + true, + ); + }); + } + + QueryBuilder + embeddingLengthLessThan( + int length, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'embedding', + 0, + true, + length, + include, + ); + }); + } + + QueryBuilder + embeddingLengthGreaterThan( + int length, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'embedding', + length, + include, + 999999, + true, + ); + }); + } + + QueryBuilder + embeddingLengthBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.listLength( + r'embedding', + lower, + includeLower, + upper, + includeUpper, + ); + }); + } + + QueryBuilder fileIDEqualTo( + int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'fileID', + value: value, + )); + }); + } + + QueryBuilder fileIDGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'fileID', + value: value, + )); + }); + } + + QueryBuilder fileIDLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'fileID', + value: value, + )); + }); + } + + QueryBuilder fileIDBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'fileID', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder idEqualTo( + Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idBetween( + Id lower, + Id upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder modelEqualTo( + Model value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'model', + value: value, + )); + }); + } + + QueryBuilder modelGreaterThan( + Model value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'model', + value: value, + )); + }); + } + + QueryBuilder modelLessThan( + Model value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'model', + value: value, + )); + }); + } + + QueryBuilder modelBetween( + Model lower, + Model upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'model', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + updationTimeIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'updationTime', + )); + }); + } + + QueryBuilder + updationTimeIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'updationTime', + )); + }); + } + + QueryBuilder updationTimeEqualTo( + int? value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'updationTime', + value: value, + )); + }); + } + + QueryBuilder + updationTimeGreaterThan( + int? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'updationTime', + value: value, + )); + }); + } + + QueryBuilder + updationTimeLessThan( + int? value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'updationTime', + value: value, + )); + }); + } + + QueryBuilder updationTimeBetween( + int? lower, + int? upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'updationTime', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } +} + +extension EmbeddingQueryObject + on QueryBuilder {} + +extension EmbeddingQueryLinks + on QueryBuilder {} + +extension EmbeddingQuerySortBy on QueryBuilder { + QueryBuilder sortByFileID() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'fileID', Sort.asc); + }); + } + + QueryBuilder sortByFileIDDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'fileID', Sort.desc); + }); + } + + QueryBuilder sortByModel() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'model', Sort.asc); + }); + } + + QueryBuilder sortByModelDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'model', Sort.desc); + }); + } + + QueryBuilder sortByUpdationTime() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'updationTime', Sort.asc); + }); + } + + QueryBuilder sortByUpdationTimeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'updationTime', Sort.desc); + }); + } +} + +extension EmbeddingQuerySortThenBy + on QueryBuilder { + QueryBuilder thenByFileID() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'fileID', Sort.asc); + }); + } + + QueryBuilder thenByFileIDDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'fileID', Sort.desc); + }); + } + + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder thenByModel() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'model', Sort.asc); + }); + } + + QueryBuilder thenByModelDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'model', Sort.desc); + }); + } + + QueryBuilder thenByUpdationTime() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'updationTime', Sort.asc); + }); + } + + QueryBuilder thenByUpdationTimeDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'updationTime', Sort.desc); + }); + } +} + +extension EmbeddingQueryWhereDistinct + on QueryBuilder { + QueryBuilder distinctByEmbedding() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'embedding'); + }); + } + + QueryBuilder distinctByFileID() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'fileID'); + }); + } + + QueryBuilder distinctByModel() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'model'); + }); + } + + QueryBuilder distinctByUpdationTime() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'updationTime'); + }); + } +} + +extension EmbeddingQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder, QQueryOperations> embeddingProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'embedding'); + }); + } + + QueryBuilder fileIDProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'fileID'); + }); + } + + QueryBuilder modelProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'model'); + }); + } + + QueryBuilder updationTimeProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'updationTime'); + }); + } +} diff --git a/lib/objectbox-model.json b/lib/objectbox-model.json deleted file mode 100644 index 72d32c621..000000000 --- a/lib/objectbox-model.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.", - "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.", - "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.", - "entities": [ - { - "id": "1:4067035246682038114", - "lastPropertyId": "4:7974898435327252398", - "name": "Embedding", - "properties": [ - { - "id": "1:2902120230153008095", - "name": "fileID", - "type": 6, - "flags": 129 - }, - { - "id": "2:5644004076892986076", - "name": "model", - "type": 9 - }, - { - "id": "3:4818114203635230783", - "name": "embedding", - "type": 29 - }, - { - "id": "4:7974898435327252398", - "name": "updationTime", - "type": 6 - } - ], - "relations": [] - } - ], - "lastEntityId": "1:4067035246682038114", - "lastIndexId": "0:0", - "lastRelationId": "0:0", - "lastSequenceId": "0:0", - "modelVersion": 5, - "modelVersionParserMinimum": 5, - "retiredEntityUids": [], - "retiredIndexUids": [], - "retiredPropertyUids": [], - "retiredRelationUids": [], - "version": 1 -} \ No newline at end of file diff --git a/lib/objectbox.g.dart b/lib/objectbox.g.dart deleted file mode 100644 index 9b0fc67c8..000000000 --- a/lib/objectbox.g.dart +++ /dev/null @@ -1,171 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND -// This code was generated by ObjectBox. To update it run the generator again: -// With a Flutter package, run `flutter pub run build_runner build`. -// With a Dart package, run `dart run build_runner build`. -// See also https://docs.objectbox.io/getting-started#generate-objectbox-code - -// ignore_for_file: camel_case_types, depend_on_referenced_packages -// coverage:ignore-file - -import 'dart:typed_data'; - -import 'package:flat_buffers/flat_buffers.dart' as fb; -import 'package:objectbox/internal.dart'; // generated code can access "internal" functionality -import 'package:objectbox/objectbox.dart'; -import 'package:objectbox_flutter_libs/objectbox_flutter_libs.dart'; - -import 'package:photos/models/embedding.dart'; - -export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file - -final _entities = [ - ModelEntity( - id: const IdUid(1, 4067035246682038114), - name: 'Embedding', - lastPropertyId: const IdUid(4, 7974898435327252398), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 2902120230153008095), - name: 'fileID', - type: 6, - flags: 129, - ), - ModelProperty( - id: const IdUid(2, 5644004076892986076), - name: 'model', - type: 9, - flags: 0, - ), - ModelProperty( - id: const IdUid(3, 4818114203635230783), - name: 'embedding', - type: 29, - flags: 0, - ), - ModelProperty( - id: const IdUid(4, 7974898435327252398), - name: 'updationTime', - type: 6, - flags: 0, - ), - ], - relations: [], - backlinks: [], - ), -]; - -/// Shortcut for [Store.new] that passes [getObjectBoxModel] and for Flutter -/// apps by default a [directory] using `defaultStoreDirectory()` from the -/// ObjectBox Flutter library. -/// -/// Note: for desktop apps it is recommended to specify a unique [directory]. -/// -/// See [Store.new] for an explanation of all parameters. -Future openStore({ - String? directory, - int? maxDBSizeInKB, - int? fileMode, - int? maxReaders, - bool queriesCaseSensitiveDefault = true, - String? macosApplicationGroup, -}) async => - Store( - getObjectBoxModel(), - directory: directory ?? (await defaultStoreDirectory()).path, - maxDBSizeInKB: maxDBSizeInKB, - fileMode: fileMode, - maxReaders: maxReaders, - queriesCaseSensitiveDefault: queriesCaseSensitiveDefault, - macosApplicationGroup: macosApplicationGroup, - ); - -/// Returns the ObjectBox model definition for this project for use with -/// [Store.new]. -ModelDefinition getObjectBoxModel() { - final model = ModelInfo( - entities: _entities, - lastEntityId: const IdUid(1, 4067035246682038114), - lastIndexId: const IdUid(0, 0), - lastRelationId: const IdUid(0, 0), - lastSequenceId: const IdUid(0, 0), - retiredEntityUids: const [], - retiredIndexUids: const [], - retiredPropertyUids: const [], - retiredRelationUids: const [], - modelVersion: 5, - modelVersionParserMinimum: 5, - version: 1, - ); - - final bindings = { - Embedding: EntityDefinition( - model: _entities[0], - toOneRelations: (Embedding object) => [], - toManyRelations: (Embedding object) => {}, - getId: (Embedding object) => object.fileID, - setId: (Embedding object, int id) { - if (object.fileID != id) { - throw ArgumentError('Field Embedding.fileID is read-only ' - '(final or getter-only) and it was declared to be self-assigned. ' - 'However, the currently inserted object (.fileID=${object.fileID}) ' - "doesn't match the inserted ID (ID $id). " - 'You must assign an ID before calling [box.put()].'); - } - }, - objectToFB: (Embedding object, fb.Builder fbb) { - final modelOffset = fbb.writeString(object.model); - final embeddingOffset = fbb.writeListFloat64(object.embedding); - fbb.startTable(5); - fbb.addInt64(0, object.fileID); - fbb.addOffset(1, modelOffset); - fbb.addOffset(2, embeddingOffset); - fbb.addInt64(3, object.updationTime); - fbb.finish(fbb.endTable()); - return object.fileID; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - final fileIDParam = - const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0); - final modelParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 6, ''); - final embeddingParam = - const fb.ListReader(fb.Float64Reader(), lazy: false) - .vTableGet(buffer, rootOffset, 8, []); - final updationTimeParam = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 10); - final object = Embedding( - fileID: fileIDParam, - model: modelParam, - embedding: embeddingParam, - updationTime: updationTimeParam, - ); - - return object; - }, - ), - }; - - return ModelDefinition(model, bindings); -} - -/// [Embedding] entity fields to define ObjectBox queries. -class Embedding_ { - /// see [Embedding.fileID] - static final fileID = - QueryIntegerProperty(_entities[0].properties[0]); - - /// see [Embedding.model] - static final model = - QueryStringProperty(_entities[0].properties[1]); - - /// see [Embedding.embedding] - static final embedding = - QueryDoubleVectorProperty(_entities[0].properties[2]); - - /// see [Embedding.updationTime] - static final updationTime = - QueryIntegerProperty(_entities[0].properties[3]); -} diff --git a/lib/services/semantic_search/embedding_store.dart b/lib/services/semantic_search/embedding_store.dart index e269ccd14..a1a9b6821 100644 --- a/lib/services/semantic_search/embedding_store.dart +++ b/lib/services/semantic_search/embedding_store.dart @@ -5,11 +5,10 @@ import "dart:typed_data"; import "package:computer/computer.dart"; import "package:logging/logging.dart"; import "package:photos/core/network/network.dart"; +import "package:photos/db/embeddings_db.dart"; import "package:photos/db/files_db.dart"; -import "package:photos/db/object_box.dart"; import "package:photos/models/embedding.dart"; import "package:photos/models/file/file.dart"; -import "package:photos/objectbox.g.dart"; import "package:photos/services/semantic_search/remote_embedding.dart"; import "package:photos/utils/crypto_util.dart"; import "package:photos/utils/file_download_util.dart"; @@ -20,7 +19,7 @@ class EmbeddingStore { static final EmbeddingStore instance = EmbeddingStore._privateConstructor(); - static const kEmbeddingsSyncTimeKey = "sync_time_embeddings"; + static const kEmbeddingsSyncTimeKey = "sync_time_embeddings_v2"; final _logger = Logger("EmbeddingStore"); final _dio = NetworkClient.instance.enteDio; @@ -50,12 +49,7 @@ class EmbeddingStore { } Future pushEmbeddings() async { - final query = (ObjectBox.instance - .getEmbeddingBox() - .query(Embedding_.updationTime.isNull())) - .build(); - final pendingItems = query.find(); - query.close(); + final pendingItems = await EmbeddingsDB.instance.getUnsyncedEmbeddings(); for (final item in pendingItems) { final file = await FilesDB.instance.getAnyUploadedFile(item.fileID); await _pushEmbedding(file!, item); @@ -63,7 +57,7 @@ class EmbeddingStore { } Future storeEmbedding(EnteFile file, Embedding embedding) async { - ObjectBox.instance.getEmbeddingBox().put(embedding); + await EmbeddingsDB.instance.put(embedding); unawaited(_pushEmbedding(file, embedding)); } @@ -82,14 +76,14 @@ class EmbeddingStore { "/embeddings", data: { "fileID": embedding.fileID, - "model": embedding.model, + "model": embedding.model.name, "encryptedEmbedding": encryptedData, "decryptionHeader": header, }, ); final updationTime = response.data["updationTime"]; embedding.updationTime = updationTime; - ObjectBox.instance.getEmbeddingBox().put(embedding); + await EmbeddingsDB.instance.put(embedding); } catch (e, s) { _logger.severe(e, s); } @@ -148,7 +142,7 @@ class EmbeddingStore { }, ); _logger.info("${embeddings.length} embeddings decoded"); - await ObjectBox.instance.getEmbeddingBox().putManyAsync(embeddings); + await EmbeddingsDB.instance.putMany(embeddings); await _preferences.setInt( kEmbeddingsSyncTimeKey, embeddings.last.updationTime!, @@ -179,7 +173,7 @@ Future> decodeEmbeddings(Map args) async { embeddings.add( Embedding( fileID: input.embedding.fileID, - model: input.embedding.model, + model: deserialize(input.embedding.model), embedding: decodedEmbedding, updationTime: input.embedding.updatedAt, ), diff --git a/lib/services/semantic_search/frameworks/ggml.dart b/lib/services/semantic_search/frameworks/ggml.dart index eaf7b1871..e4903091c 100644 --- a/lib/services/semantic_search/frameworks/ggml.dart +++ b/lib/services/semantic_search/frameworks/ggml.dart @@ -10,11 +10,6 @@ class GGML extends MLFramework { final _computer = Computer.shared(); final _logger = Logger("GGML"); - - @override - String getFrameworkName() { - return "ggml"; - } @override String getImageModelRemotePath() { diff --git a/lib/services/semantic_search/frameworks/ml_framework.dart b/lib/services/semantic_search/frameworks/ml_framework.dart index dead6ba7d..ffb4dbaee 100644 --- a/lib/services/semantic_search/frameworks/ml_framework.dart +++ b/lib/services/semantic_search/frameworks/ml_framework.dart @@ -11,9 +11,6 @@ abstract class MLFramework { final _logger = Logger("MLFramework"); - /// Returns the name of the framework - String getFrameworkName(); - /// Returns the path of the Image Model hosted remotely String getImageModelRemotePath(); diff --git a/lib/services/semantic_search/frameworks/onnx/onnx.dart b/lib/services/semantic_search/frameworks/onnx/onnx.dart index 9b12249be..0a18c66e6 100644 --- a/lib/services/semantic_search/frameworks/onnx/onnx.dart +++ b/lib/services/semantic_search/frameworks/onnx/onnx.dart @@ -17,11 +17,6 @@ class ONNX extends MLFramework { int _textEncoderAddress = 0; int _imageEncoderAddress = 0; - @override - String getFrameworkName() { - return "onnx"; - } - @override String getImageModelRemotePath() { return kModelBucketEndpoint + kImageModel; diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index 7c4771898..5b076e058 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -6,15 +6,15 @@ import "package:logging/logging.dart"; import "package:photos/core/cache/lru_map.dart"; import "package:photos/core/configuration.dart"; import "package:photos/core/event_bus.dart"; +import "package:photos/db/embeddings_db.dart"; import "package:photos/db/files_db.dart"; -import "package:photos/db/object_box.dart"; import "package:photos/events/diff_sync_complete_event.dart"; import 'package:photos/events/embedding_updated_event.dart'; import "package:photos/events/file_uploaded_event.dart"; import "package:photos/models/embedding.dart"; import "package:photos/models/file/file.dart"; -import "package:photos/objectbox.g.dart"; import "package:photos/services/semantic_search/embedding_store.dart"; +import "package:photos/services/semantic_search/frameworks/ggml.dart"; import "package:photos/services/semantic_search/frameworks/ml_framework.dart"; import 'package:photos/services/semantic_search/frameworks/onnx/onnx.dart'; import "package:photos/utils/local_settings.dart"; @@ -32,11 +32,12 @@ class SemanticSearchService { static const kEmbeddingLength = 512; static const kScoreThreshold = 0.23; static const kShouldPushEmbeddings = true; + static const kCurrentModel = Model.onnxClip; final _logger = Logger("SemanticSearchService"); final _queue = Queue(); final _cachedEmbeddings = []; - final _mlFramework = ONNX(); + final _mlFramework = kCurrentModel == Model.onnxClip ? ONNX() : GGML(); final _frameworkInitialization = Completer(); bool _hasInitialized = false; @@ -56,9 +57,9 @@ class SemanticSearchService { return; } _hasInitialized = true; - await ObjectBox.instance.init(); + await EmbeddingsDB.instance.init(); await EmbeddingStore.instance.init(); - _setupCachedEmbeddings(); + await _setupCachedEmbeddings(); Bus.instance.on().listen((event) { // Diff sync is complete, we can now pull embeddings from remote unawaited(sync()); @@ -132,29 +133,21 @@ class SemanticSearchService { } Future clearIndexes() async { - await ObjectBox.instance - .getEmbeddingBox() - .query( - Embedding_.model.equals( - _mlFramework.getFrameworkName() + "-" + kModelName, - ), - ) - .build() - .removeAsync(); - _logger.info("Indexes cleared for ${_mlFramework.getFrameworkName()}"); + await EmbeddingsDB.instance.deleteAllForModel(kCurrentModel); + _logger.info("Indexes cleared for $kCurrentModel"); } - void _setupCachedEmbeddings() { - ObjectBox.instance - .getEmbeddingBox() - .query( - Embedding_.model.equals( - _mlFramework.getFrameworkName() + "-" + kModelName, - ), - ) - .watch(triggerImmediately: true) - .map((query) => query.find()) - .listen((embeddings) { + Future _setupCachedEmbeddings() async { + _logger.info("Setting up cached embeddings"); + final startTime = DateTime.now(); + final cachedEmbeddings = await EmbeddingsDB.instance.getAll(kCurrentModel); + final endTime = DateTime.now(); + _logger.info( + "Loading ${cachedEmbeddings.length} took: ${(endTime.millisecondsSinceEpoch - startTime.millisecondsSinceEpoch)}ms", + ); + _cachedEmbeddings.addAll(cachedEmbeddings); + _logger.info("Cached embeddings: " + _cachedEmbeddings.length.toString()); + EmbeddingsDB.instance.getStream(kCurrentModel).listen((embeddings) { _logger.info("Updated embeddings: " + embeddings.length.toString()); _cachedEmbeddings.clear(); _cachedEmbeddings.addAll(embeddings); @@ -279,7 +272,7 @@ class SemanticSearchService { final embedding = Embedding( fileID: file.uploadedFileID!, - model: _mlFramework.getFrameworkName() + "-" + kModelName, + model: kCurrentModel, embedding: result, ); await EmbeddingStore.instance.storeEmbedding( diff --git a/pubspec.lock b/pubspec.lock index dcac2c4d2..5129dcf80 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -315,14 +315,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" - cryptography: - dependency: transitive - description: - name: cryptography - sha256: df156c5109286340817d21fa7b62f9140f17915077127dd70f8bd7a2a0997a35 - url: "https://pub.dev" - source: hosted - version: "2.5.0" csslib: dependency: transitive description: @@ -596,14 +588,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" - flat_buffers: - dependency: transitive - description: - name: flat_buffers - sha256: "23e2ced0d8e8ecdffbd9f267f49a668c74438393b9acaeac1c724123e3764263" - url: "https://pub.dev" - source: hosted - version: "2.0.5" flutter: dependency: "direct main" description: flutter @@ -1385,30 +1369,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" - objectbox: - dependency: "direct main" - description: - name: objectbox - sha256: "4b645c71771b87188442143a50c55ab238a8e60fe367b6a0968c0842292ffb30" - url: "https://pub.dev" - source: hosted - version: "2.3.1" - objectbox_flutter_libs: - dependency: "direct main" - description: - name: objectbox_flutter_libs - sha256: e9a3d8e3ce0d47d6fc942921ef0444a238cd4258e8fcefe13b994cf984a8bf61 - url: "https://pub.dev" - source: hosted - version: "2.3.1" - objectbox_generator: - dependency: "direct dev" - description: - name: objectbox_generator - sha256: aaffef7eb51b4d911bb00a7c52b19b55fe3e5a69de8ec56552cf35550a1e9beb - url: "https://pub.dev" - source: hosted - version: "2.3.1" octo_image: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 419fd64be..91faedaf1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -117,8 +117,6 @@ dependencies: move_to_background: ^1.0.2 # open_file: ^3.2.1 - objectbox: ^2.3.1 - objectbox_flutter_libs: any onnxruntime: git: "https://github.com/ente-io/onnxruntime.git" open_mail_app: ^0.4.5 @@ -196,7 +194,6 @@ dev_dependencies: sdk: flutter isar_generator: ^3.1.0+1 json_serializable: ^6.6.1 - objectbox_generator: any test: ^1.22.0 flutter_icons: From b756f33f18e992b97c4b03cc724fc142880863ab Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 4 Jan 2024 20:50:18 +0530 Subject: [PATCH 053/175] TODO: Clear deprecated store --- lib/db/embeddings_db.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/db/embeddings_db.dart b/lib/db/embeddings_db.dart index 4d030a6cf..78f3c4420 100644 --- a/lib/db/embeddings_db.dart +++ b/lib/db/embeddings_db.dart @@ -1,3 +1,5 @@ +import "dart:io"; + import "package:isar/isar.dart"; import 'package:path_provider/path_provider.dart'; import "package:photos/models/embedding.dart"; @@ -15,6 +17,7 @@ class EmbeddingsDB { [EmbeddingSchema], directory: dir.path, ); + // TODO: _clearDeprecatedStore(dir); } Future clearTable() async { @@ -52,4 +55,11 @@ class EmbeddingsDB { await _isar.embeddings.deleteAll(embeddings.map((e) => e.id).toList()); }); } + + Future _clearDeprecatedStore(Directory dir) async { + final deprecatedStore = Directory(dir.path + "/object-box-store"); + if (await deprecatedStore.exists()) { + await deprecatedStore.delete(recursive: true); + } + } } From 15b3621ab421e30180d07ce61048b425a9d9aff9 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 4 Jan 2024 20:50:38 +0530 Subject: [PATCH 054/175] v0.8.30 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 94c64e20f..d0a4fd9d1 100644 --- a/pubspec.yaml +++ b/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.8.28+548 +version: 0.8.30+550 environment: sdk: ">=3.0.0 <4.0.0" From 4ffa600da640a9853d68d2a4a7934434dea115aa Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 4 Jan 2024 20:52:07 +0530 Subject: [PATCH 055/175] Enable SSS in the background --- lib/main.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 81982fce5..8e553280b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -193,9 +193,7 @@ Future _init(bool isBackground, {String via = ''}) async { }); } unawaited(FeatureFlagService.instance.init()); - if (!isBackground) { - unawaited(SemanticSearchService.instance.init()); - } + unawaited(SemanticSearchService.instance.init()); // Can not including existing tf/ml binaries as they are not being built // from source. // See https://gitlab.com/fdroid/fdroiddata/-/merge_requests/12671#note_1294346819 From 76520c569cad74da888f220baed1086c13a016ab Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 10:43:34 +0530 Subject: [PATCH 056/175] preload thumbnail and file of next memory --- lib/ui/home/memories/full_screen_memory_new.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index e6e97e926..d76757a88 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -7,6 +7,7 @@ import "package:photos/models/memory.dart"; import "package:photos/ui/actions/file/file_actions.dart"; import "package:photos/ui/viewer/file/file_widget.dart"; import "package:photos/ui/viewer/file_details/favorite_widget.dart"; +import "package:photos/utils/file_util.dart"; import "package:photos/utils/share_util.dart"; class FullScreenMemoryDataUpdater extends StatefulWidget { @@ -120,6 +121,11 @@ class _FullScreenMemoryNewState extends State { initialPage: widget.initialIndex, ), itemBuilder: (context, index) { + if (index < inheritedData.memories.length - 1) { + final nextFile = inheritedData.memories[index + 1].file; + preloadThumbnail(nextFile); + preloadFile(nextFile); + } return Stack( alignment: Alignment.bottomCenter, children: [ From 3554dc382760d152ec45b6bd10be027300028208 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 10:59:25 +0530 Subject: [PATCH 057/175] Remove debug code --- lib/services/semantic_search/semantic_search_service.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index 5b076e058..156924f5e 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -265,10 +265,6 @@ class SemanticSearchService { _logger.severe("Discovered incorrect embedding for $file - $result"); return; } - // dev.log(result.toString()); - // dev.log(computeScore(result, webEmbedding).toString()); - // dev.log(computeScore(result, pyEmbedding).toString()); - // dev.log(computeScore(pyEmbedding, webEmbedding).toString()); final embedding = Embedding( fileID: file.uploadedFileID!, From 30904d0d5988c6a58fc603daf0f271e226305d15 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 10:59:31 +0530 Subject: [PATCH 058/175] Update iOS config files --- ios/Podfile.lock | 10 ---------- ios/Runner.xcodeproj/project.pbxproj | 4 ---- 2 files changed, 14 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 878a0627d..ee3a1d132 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -135,10 +135,6 @@ PODS: - nanopb/encode (= 2.30909.1) - nanopb/decode (2.30909.1) - nanopb/encode (2.30909.1) - - ObjectBox (1.9.0) - - objectbox_flutter_libs (0.0.1): - - Flutter - - ObjectBox (= 1.9.0) - onnxruntime (0.0.1): - Flutter - onnxruntime-objc (= 1.15.1) @@ -228,7 +224,6 @@ DEPENDENCIES: - media_kit_video (from `.symlinks/plugins/media_kit_video/ios`) - motionphoto (from `.symlinks/plugins/motionphoto/ios`) - move_to_background (from `.symlinks/plugins/move_to_background/ios`) - - objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`) - onnxruntime (from `.symlinks/plugins/onnxruntime/ios`) - open_mail_app (from `.symlinks/plugins/open_mail_app/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) @@ -260,7 +255,6 @@ SPEC REPOS: - libwebp - Mantle - nanopb - - ObjectBox - onnxruntime-c - onnxruntime-objc - OrderedSet @@ -327,8 +321,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/motionphoto/ios" move_to_background: :path: ".symlinks/plugins/move_to_background/ios" - objectbox_flutter_libs: - :path: ".symlinks/plugins/objectbox_flutter_libs/ios" onnxruntime: :path: ".symlinks/plugins/onnxruntime/ios" open_mail_app: @@ -403,8 +395,6 @@ SPEC CHECKSUMS: motionphoto: d4a432b8c8f22fb3ad966258597c0103c9c5ff16 move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5 - ObjectBox: e7ff611291a0663380e0736b46786bcd077294ff - objectbox_flutter_libs: 0948d6feb7de4f7edaebc7a898b9e85b7fc2bc89 onnxruntime: e9346181d75b8dea8733bdae512a22c298962e00 onnxruntime-c: ebdcfd8650bcbd10121c125262f99dea681b92a3 onnxruntime-objc: ae7acec7a3d03eaf072d340afed7a35635c1c2a6 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 4c137dda2..03a95b930 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -301,7 +301,6 @@ "${BUILT_PRODUCTS_DIR}/motionphoto/motionphoto.framework", "${BUILT_PRODUCTS_DIR}/move_to_background/move_to_background.framework", "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", - "${BUILT_PRODUCTS_DIR}/objectbox_flutter_libs/objectbox_flutter_libs.framework", "${BUILT_PRODUCTS_DIR}/open_mail_app/open_mail_app.framework", "${BUILT_PRODUCTS_DIR}/package_info_plus/package_info_plus.framework", "${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework", @@ -318,7 +317,6 @@ "${BUILT_PRODUCTS_DIR}/video_thumbnail/video_thumbnail.framework", "${BUILT_PRODUCTS_DIR}/volume_controller/volume_controller.framework", "${BUILT_PRODUCTS_DIR}/wakelock_plus/wakelock_plus.framework", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/ObjectBox/ObjectBox.framework/ObjectBox", "${PODS_XCFRAMEWORKS_BUILD_DIR}/media_kit_libs_ios_video/Ass.framework/Ass", "${PODS_XCFRAMEWORKS_BUILD_DIR}/media_kit_libs_ios_video/Avcodec.framework/Avcodec", "${PODS_XCFRAMEWORKS_BUILD_DIR}/media_kit_libs_ios_video/Avfilter.framework/Avfilter", @@ -382,7 +380,6 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/motionphoto.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/move_to_background.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/objectbox_flutter_libs.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/open_mail_app.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework", @@ -399,7 +396,6 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_thumbnail.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/volume_controller.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock_plus.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ObjectBox.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Ass.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Avcodec.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Avfilter.framework", From 51af506f14bbe8f4df9ec0c319c73aa0a67e44f3 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 11:00:50 +0530 Subject: [PATCH 059/175] Handle cases where we could not fetch the thumbnail --- lib/services/semantic_search/semantic_search_service.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index 156924f5e..edc3d6eab 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -258,7 +258,12 @@ class SemanticSearchService { return; } try { - final filePath = (await getThumbnailForUploadedFile(file))!.path; + final thumbnail = await getThumbnailForUploadedFile(file); + if (thumbnail == null) { + _logger.warning("Could not get thumbnail for $file"); + return; + } + final filePath = thumbnail.path; _logger.info("Running clip over $file"); final result = await _mlFramework.getImageEmbedding(filePath); if (result.length != kEmbeddingLength) { From aed0a2f3dc45d29b488c8db68db4aa3eca9b76da Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 11:21:43 +0530 Subject: [PATCH 060/175] add memory counter on bottom of FullScreenMemory --- .../home/memories/full_screen_memory_new.dart | 109 ++++++++++++------ 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index d76757a88..61743d100 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -4,6 +4,7 @@ import "package:flutter/cupertino.dart"; import "package:flutter/material.dart"; import "package:photos/core/configuration.dart"; import "package:photos/models/memory.dart"; +import "package:photos/theme/text_style.dart"; import "package:photos/ui/actions/file/file_actions.dart"; import "package:photos/ui/viewer/file/file_widget.dart"; import "package:photos/ui/viewer/file_details/favorite_widget.dart"; @@ -99,6 +100,7 @@ class FullScreenMemoryNew extends StatefulWidget { class _FullScreenMemoryNewState extends State { PageController? _pageController; + @override void initState() { super.initState(); @@ -113,47 +115,61 @@ class _FullScreenMemoryNewState extends State { @override Widget build(BuildContext context) { final inheritedData = FullScreenMemoryData.of(context)!; + return Scaffold( extendBodyBehindAppBar: true, appBar: AppBar(), - body: PageView.builder( - controller: _pageController ??= PageController( - initialPage: widget.initialIndex, - ), - itemBuilder: (context, index) { - if (index < inheritedData.memories.length - 1) { - final nextFile = inheritedData.memories[index + 1].file; - preloadThumbnail(nextFile); - preloadFile(nextFile); - } - return Stack( - alignment: Alignment.bottomCenter, - children: [ - FileWidget( - inheritedData.memories[index].file, - autoPlay: false, - tagPrefix: "memories", - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, - ), - ), - BottomIcons(index), - Padding( - padding: const EdgeInsets.all(120), - child: Container( - color: Colors.black, - child: Text( - inheritedData.memories[index].file.generatedID.toString(), + body: Stack( + alignment: Alignment.bottomCenter, + children: [ + PageView.builder( + controller: _pageController ??= PageController( + initialPage: widget.initialIndex, + ), + itemBuilder: (context, index) { + if (index < inheritedData.memories.length - 1) { + final nextFile = inheritedData.memories[index + 1].file; + preloadThumbnail(nextFile); + preloadFile(nextFile); + } + return Stack( + alignment: Alignment.bottomCenter, + children: [ + FileWidget( + inheritedData.memories[index].file, + autoPlay: false, + tagPrefix: "memories", + backgroundDecoration: const BoxDecoration( + color: Colors.transparent, + ), ), - ), - ), - ], - ); - }, - onPageChanged: (index) { - inheritedData.indexNotifier.value = index; - }, - itemCount: inheritedData.memories.length, + BottomIcons(index), + Padding( + padding: const EdgeInsets.only(bottom: 120, right: 240), + child: Container( + color: Colors.black, + child: Text( + inheritedData.memories[index].file.generatedID + .toString(), + ), + ), + ), + ], + ); + }, + onPageChanged: (index) { + inheritedData.indexNotifier.value = index; + }, + itemCount: inheritedData.memories.length, + ), + const SafeArea( + top: false, + child: Padding( + padding: EdgeInsets.only(bottom: 84), + child: MemoryCounter(), + ), + ), + ], ), ); } @@ -226,6 +242,7 @@ class BottomIcons extends StatelessWidget { ); return SafeArea( + top: false, child: Container( alignment: Alignment.bottomCenter, padding: const EdgeInsets.fromLTRB(12, 0, 12, 20), @@ -237,3 +254,21 @@ class BottomIcons extends StatelessWidget { ); } } + +class MemoryCounter extends StatelessWidget { + const MemoryCounter({super.key}); + + @override + Widget build(BuildContext context) { + final inheritedData = FullScreenMemoryData.of(context)!; + return ValueListenableBuilder( + valueListenable: inheritedData.indexNotifier, + builder: (context, value, _) { + return Text( + "${value + 1}/${inheritedData.memories.length}", + style: darkTextTheme.bodyMuted, + ); + }, + ); + } +} From a808831906fecaf208404da923cb27a190c050de Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 11:37:42 +0530 Subject: [PATCH 061/175] show 'x years ago' title when opening memories --- .../home/memories/full_screen_memory_new.dart | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 61743d100..b470209f4 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -100,15 +100,24 @@ class FullScreenMemoryNew extends StatefulWidget { class _FullScreenMemoryNewState extends State { PageController? _pageController; + final _showTitle = ValueNotifier(true); @override void initState() { + Future.delayed(const Duration(seconds: 3), () { + if (mounted) { + setState(() { + _showTitle.value = false; + }); + } + }); super.initState(); } @override void dispose() { _pageController?.dispose(); + _showTitle.dispose(); super.dispose(); } @@ -162,11 +171,26 @@ class _FullScreenMemoryNewState extends State { }, itemCount: inheritedData.memories.length, ), - const SafeArea( + SafeArea( top: false, child: Padding( - padding: EdgeInsets.only(bottom: 84), - child: MemoryCounter(), + padding: const EdgeInsets.only(bottom: 84), + child: ValueListenableBuilder( + builder: (context, value, _) { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 250), + switchInCurve: Curves.easeIn, + switchOutCurve: Curves.easeOut, + child: value + ? Text( + widget.title, + style: darkTextTheme.h2, + ) + : const MemoryCounter(), + ); + }, + valueListenable: _showTitle, + ), ), ), ], From f05f7a86a7161455d5f894f0047ee6c51f17e7b5 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 11:51:45 +0530 Subject: [PATCH 062/175] add bottom gradient --- .../home/memories/full_screen_memory_new.dart | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index b470209f4..7ac57ab43 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -152,6 +152,7 @@ class _FullScreenMemoryNewState extends State { color: Colors.transparent, ), ), + const BottomGradient(), BottomIcons(index), Padding( padding: const EdgeInsets.only(bottom: 120, right: 240), @@ -174,7 +175,7 @@ class _FullScreenMemoryNewState extends State { SafeArea( top: false, child: Padding( - padding: const EdgeInsets.only(bottom: 84), + padding: const EdgeInsets.only(bottom: 72), child: ValueListenableBuilder( builder: (context, value, _) { return AnimatedSwitcher( @@ -296,3 +297,28 @@ class MemoryCounter extends StatelessWidget { ); } } + +class BottomGradient extends StatelessWidget { + const BottomGradient({super.key}); + + @override + Widget build(BuildContext context) { + return IgnorePointer( + child: Container( + height: 124, + width: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Colors.black.withOpacity(0.5), //same for both themes + Colors.transparent, + ], + stops: const [0, 0.8], + ), + ), + ), + ); + } +} From 212423f955de2baceaf9541088f364f3c44a1181 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 14:36:06 +0530 Subject: [PATCH 063/175] show date and step progress indicator on top of FullScreenMemory screen --- .../home/memories/full_screen_memory_new.dart | 61 ++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 7ac57ab43..d41fec416 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -2,6 +2,7 @@ import "dart:io"; import "package:flutter/cupertino.dart"; import "package:flutter/material.dart"; +import "package:intl/intl.dart"; import "package:photos/core/configuration.dart"; import "package:photos/models/memory.dart"; import "package:photos/theme/text_style.dart"; @@ -10,6 +11,7 @@ import "package:photos/ui/viewer/file/file_widget.dart"; import "package:photos/ui/viewer/file_details/favorite_widget.dart"; import "package:photos/utils/file_util.dart"; import "package:photos/utils/share_util.dart"; +import "package:step_progress_indicator/step_progress_indicator.dart"; class FullScreenMemoryDataUpdater extends StatefulWidget { final List memories; @@ -124,10 +126,65 @@ class _FullScreenMemoryNewState extends State { @override Widget build(BuildContext context) { final inheritedData = FullScreenMemoryData.of(context)!; - + final showStepProgressIndicator = inheritedData.memories.length < 60; return Scaffold( extendBodyBehindAppBar: true, - appBar: AppBar(), + appBar: AppBar( + toolbarHeight: 84, + automaticallyImplyLeading: false, + title: ValueListenableBuilder( + valueListenable: inheritedData.indexNotifier, + child: Padding( + padding: const EdgeInsets.only(right: 16), + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: const Icon( + Icons.close, + color: Colors.white, //same for both themes + ), + ), + ), + builder: (context, value, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + showStepProgressIndicator + ? StepProgressIndicator( + totalSteps: inheritedData.memories.length, + currentStep: value + 1, + size: 2, + selectedColor: Colors.white, //same for both themes + unselectedColor: Colors.white.withOpacity(0.4), + ) + : const SizedBox.shrink(), + const SizedBox( + height: 18, + ), + Row( + children: [ + child!, + Text( + DateFormat.yMMMd( + Localizations.localeOf(context).languageCode, + ).format( + DateTime.fromMicrosecondsSinceEpoch( + inheritedData.memories[value].file.creationTime!, + ), + ), + style: Theme.of(context).textTheme.titleMedium!.copyWith( + fontSize: 14, + color: Colors.white, + ), //same for both themes + ), + ], + ), + ], + ); + }, + ), + ), body: Stack( alignment: Alignment.bottomCenter, children: [ From 8730bc5451e6f983db0e5100ebdc2289d8d913cc Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 14:39:34 +0530 Subject: [PATCH 064/175] don't show memory counter if step progress indicator is visible --- lib/ui/home/memories/full_screen_memory_new.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index d41fec416..75460f2e8 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -244,7 +244,9 @@ class _FullScreenMemoryNewState extends State { widget.title, style: darkTextTheme.h2, ) - : const MemoryCounter(), + : showStepProgressIndicator + ? const SizedBox.shrink() + : const MemoryCounter(), ); }, valueListenable: _showTitle, From c8c421e01157d80a535974a9f2c0cf4b6a5c08e9 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 14:55:06 +0530 Subject: [PATCH 065/175] Add tap to go to previous or next story in FullScreenMemory screen --- .../home/memories/full_screen_memory_new.dart | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 75460f2e8..63addcf36 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -201,12 +201,34 @@ class _FullScreenMemoryNewState extends State { return Stack( alignment: Alignment.bottomCenter, children: [ - FileWidget( - inheritedData.memories[index].file, - autoPlay: false, - tagPrefix: "memories", - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, + GestureDetector( + onTapDown: (TapDownDetails details) { + final screenWidth = MediaQuery.of(context).size.width; + final edgeWidth = screenWidth * 0.33; + if (details.localPosition.dx < edgeWidth) { + if (index > 0) { + _pageController!.previousPage( + duration: const Duration(milliseconds: 250), + curve: Curves.ease, + ); + } + } else if (details.localPosition.dx > + screenWidth - edgeWidth) { + if (index < (inheritedData.memories.length - 1)) { + _pageController!.nextPage( + duration: const Duration(milliseconds: 250), + curve: Curves.ease, + ); + } + } + }, + child: FileWidget( + inheritedData.memories[index].file, + autoPlay: false, + tagPrefix: "memories", + backgroundDecoration: const BoxDecoration( + color: Colors.transparent, + ), ), ), const BottomGradient(), From 45f1fd1bd1ca4f45ef7a203ddf4451fb63a938c4 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 15:00:01 +0530 Subject: [PATCH 066/175] mark inital memory as seen on init --- lib/ui/home/memories/full_screen_memory_new.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 63addcf36..2fe59f54c 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -5,6 +5,7 @@ import "package:flutter/material.dart"; import "package:intl/intl.dart"; import "package:photos/core/configuration.dart"; import "package:photos/models/memory.dart"; +import "package:photos/services/memories_service.dart"; import "package:photos/theme/text_style.dart"; import "package:photos/ui/actions/file/file_actions.dart"; import "package:photos/ui/viewer/file/file_widget.dart"; @@ -37,6 +38,8 @@ class _FullScreenMemoryDataUpdaterState void initState() { super.initState(); indexNotifier = ValueNotifier(widget.initialIndex); + MemoriesService.instance + .markMemoryAsSeen(widget.memories[widget.initialIndex]); } @override @@ -106,6 +109,7 @@ class _FullScreenMemoryNewState extends State { @override void initState() { + super.initState(); Future.delayed(const Duration(seconds: 3), () { if (mounted) { setState(() { @@ -113,7 +117,6 @@ class _FullScreenMemoryNewState extends State { }); } }); - super.initState(); } @override From 59add2ed04c097a0f85437d885ab342b7fcac7de Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 15:06:37 +0530 Subject: [PATCH 067/175] add gradient on top of FullScreenMemory for better visibilty of top widgets --- lib/ui/home/memories/full_screen_memory_new.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index 2fe59f54c..ec7747a1e 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -187,6 +187,22 @@ class _FullScreenMemoryNewState extends State { ); }, ), + flexibleSpace: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.black.withOpacity(0.6), + Colors.black.withOpacity(0.5), + Colors.transparent, + ], + stops: const [0, 0.6, 1], + ), + ), + ), + backgroundColor: const Color(0x00000000), + elevation: 0, ), body: Stack( alignment: Alignment.bottomCenter, From e599f2bcec566aff7dc139b0ac60c155f11e323a Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 15:12:49 +0530 Subject: [PATCH 068/175] minor change --- lib/ui/home/memories/full_screen_memory_new.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index ec7747a1e..f67fc8459 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -223,7 +223,7 @@ class _FullScreenMemoryNewState extends State { GestureDetector( onTapDown: (TapDownDetails details) { final screenWidth = MediaQuery.of(context).size.width; - final edgeWidth = screenWidth * 0.33; + final edgeWidth = screenWidth * 0.20; if (details.localPosition.dx < edgeWidth) { if (index > 0) { _pageController!.previousPage( From 09d274778fb450b99a669ed1dde365ed79d2c5e4 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 15:14:50 +0530 Subject: [PATCH 069/175] Fetch only ONNX embeddings --- lib/services/semantic_search/embedding_store.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/services/semantic_search/embedding_store.dart b/lib/services/semantic_search/embedding_store.dart index a1a9b6821..7b80e4153 100644 --- a/lib/services/semantic_search/embedding_store.dart +++ b/lib/services/semantic_search/embedding_store.dart @@ -10,6 +10,7 @@ import "package:photos/db/files_db.dart"; import "package:photos/models/embedding.dart"; import "package:photos/models/file/file.dart"; import "package:photos/services/semantic_search/remote_embedding.dart"; +import "package:photos/services/semantic_search/semantic_search_service.dart"; import "package:photos/utils/crypto_util.dart"; import "package:photos/utils/file_download_util.dart"; import "package:shared_preferences/shared_preferences.dart"; @@ -99,6 +100,7 @@ class EmbeddingStore { final response = await _dio.get( "/embeddings/diff", queryParameters: { + "model": SemanticSearchService.kCurrentModel.name, "sinceTime": sinceTime, "limit": limit, }, From bbed1c052a442940201b8a173356f761a5a9cec9 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 15:16:27 +0530 Subject: [PATCH 070/175] Inject model from upstream --- lib/services/semantic_search/embedding_store.dart | 12 ++++++------ .../semantic_search/semantic_search_service.dart | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/services/semantic_search/embedding_store.dart b/lib/services/semantic_search/embedding_store.dart index 7b80e4153..143cc59d6 100644 --- a/lib/services/semantic_search/embedding_store.dart +++ b/lib/services/semantic_search/embedding_store.dart @@ -10,7 +10,6 @@ import "package:photos/db/files_db.dart"; import "package:photos/models/embedding.dart"; import "package:photos/models/file/file.dart"; import "package:photos/services/semantic_search/remote_embedding.dart"; -import "package:photos/services/semantic_search/semantic_search_service.dart"; import "package:photos/utils/crypto_util.dart"; import "package:photos/utils/file_download_util.dart"; import "package:shared_preferences/shared_preferences.dart"; @@ -34,15 +33,15 @@ class EmbeddingStore { _preferences = await SharedPreferences.getInstance(); } - Future pullEmbeddings() async { + Future pullEmbeddings(Model model) async { if (_syncStatus != null) { return _syncStatus!.future; } _syncStatus = Completer(); - var remoteEmbeddings = await _getRemoteEmbeddings(); + var remoteEmbeddings = await _getRemoteEmbeddings(model); await _storeRemoteEmbeddings(remoteEmbeddings.embeddings); while (remoteEmbeddings.hasMore) { - remoteEmbeddings = await _getRemoteEmbeddings(); + remoteEmbeddings = await _getRemoteEmbeddings(model); await _storeRemoteEmbeddings(remoteEmbeddings.embeddings); } _syncStatus!.complete(); @@ -90,7 +89,8 @@ class EmbeddingStore { } } - Future _getRemoteEmbeddings({ + Future _getRemoteEmbeddings( + Model model, { int limit = 500, }) async { final remoteEmbeddings = []; @@ -100,7 +100,7 @@ class EmbeddingStore { final response = await _dio.get( "/embeddings/diff", queryParameters: { - "model": SemanticSearchService.kCurrentModel.name, + "model": model.name, "sinceTime": sinceTime, "limit": limit, }, diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index edc3d6eab..5f21292fb 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -92,7 +92,7 @@ class SemanticSearchService { return; } _isSyncing = true; - await EmbeddingStore.instance.pullEmbeddings(); + await EmbeddingStore.instance.pullEmbeddings(kCurrentModel); await _backFill(); _isSyncing = false; } From f24faa040476b04ab452cfb5d5e13732bcee87d7 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 15:19:49 +0530 Subject: [PATCH 071/175] Subscribe to DB updates only when app is in foreground --- lib/main.dart | 4 +++- .../semantic_search_service.dart | 20 ++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 8e553280b..13522cc09 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -193,7 +193,9 @@ Future _init(bool isBackground, {String via = ''}) async { }); } unawaited(FeatureFlagService.instance.init()); - unawaited(SemanticSearchService.instance.init()); + unawaited( + SemanticSearchService.instance.init(isAppInForeground: !isBackground), + ); // Can not including existing tf/ml binaries as they are not being built // from source. // See https://gitlab.com/fdroid/fdroiddata/-/merge_requests/12671#note_1294346819 diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index 5f21292fb..cdbfd9e83 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -48,7 +48,7 @@ class SemanticSearchService { get hasInitialized => _hasInitialized; - Future init() async { + Future init({bool isAppInForeground = true}) async { if (!LocalSettings.instance.hasEnabledMagicSearch()) { return; } @@ -59,7 +59,7 @@ class SemanticSearchService { _hasInitialized = true; await EmbeddingsDB.instance.init(); await EmbeddingStore.instance.init(); - await _setupCachedEmbeddings(); + await _setupCachedEmbeddings(isAppInForeground); Bus.instance.on().listen((event) { // Diff sync is complete, we can now pull embeddings from remote unawaited(sync()); @@ -137,7 +137,7 @@ class SemanticSearchService { _logger.info("Indexes cleared for $kCurrentModel"); } - Future _setupCachedEmbeddings() async { + Future _setupCachedEmbeddings(bool shouldListenForUpdates) async { _logger.info("Setting up cached embeddings"); final startTime = DateTime.now(); final cachedEmbeddings = await EmbeddingsDB.instance.getAll(kCurrentModel); @@ -147,12 +147,14 @@ class SemanticSearchService { ); _cachedEmbeddings.addAll(cachedEmbeddings); _logger.info("Cached embeddings: " + _cachedEmbeddings.length.toString()); - EmbeddingsDB.instance.getStream(kCurrentModel).listen((embeddings) { - _logger.info("Updated embeddings: " + embeddings.length.toString()); - _cachedEmbeddings.clear(); - _cachedEmbeddings.addAll(embeddings); - Bus.instance.fire(EmbeddingUpdatedEvent()); - }); + if (shouldListenForUpdates) { + EmbeddingsDB.instance.getStream(kCurrentModel).listen((embeddings) { + _logger.info("Updated embeddings: " + embeddings.length.toString()); + _cachedEmbeddings.clear(); + _cachedEmbeddings.addAll(embeddings); + Bus.instance.fire(EmbeddingUpdatedEvent()); + }); + } } Future _backFill() async { From ae015c7693f135463e367daab130fa7c3fa838b2 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 15:36:49 +0530 Subject: [PATCH 072/175] add hero animation + mark memory as seen when swiping through memories --- lib/ui/home/memories/full_screen_memory_new.dart | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index f67fc8459..af1842b77 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -1,3 +1,4 @@ +import "dart:async"; import "dart:io"; import "package:flutter/cupertino.dart"; @@ -266,6 +267,10 @@ class _FullScreenMemoryNewState extends State { ); }, onPageChanged: (index) { + unawaited( + MemoriesService.instance + .markMemoryAsSeen(inheritedData.memories[index]), + ); inheritedData.indexNotifier.value = index; }, itemCount: inheritedData.memories.length, @@ -281,9 +286,12 @@ class _FullScreenMemoryNewState extends State { switchInCurve: Curves.easeIn, switchOutCurve: Curves.easeOut, child: value - ? Text( - widget.title, - style: darkTextTheme.h2, + ? Hero( + tag: widget.title, + child: Text( + widget.title, + style: darkTextTheme.h2, + ), ) : showStepProgressIndicator ? const SizedBox.shrink() From 6917d5075ce7c102c28e457f62c8a2fbce44d151 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 15:47:00 +0530 Subject: [PATCH 073/175] use BouncingScrollPhysics for list of memories in home tab --- lib/ui/home/memories/memories_widget.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index d5115673f..72f6add5b 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -64,6 +64,7 @@ class _MemoriesWidgetState extends State { } return SingleChildScrollView( scrollDirection: Axis.horizontal, + physics: const BouncingScrollPhysics(), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: memoryWidgets, From 39affa085293329d0852b61228afb5b9a9967015 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 15:59:58 +0530 Subject: [PATCH 074/175] Add retrials for model downloads --- .../frameworks/ml_framework.dart | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/services/semantic_search/frameworks/ml_framework.dart b/lib/services/semantic_search/frameworks/ml_framework.dart index ffb4dbaee..2bf415881 100644 --- a/lib/services/semantic_search/frameworks/ml_framework.dart +++ b/lib/services/semantic_search/frameworks/ml_framework.dart @@ -8,6 +8,7 @@ import "package:photos/core/network/network.dart"; abstract class MLFramework { static const kImageEncoderEnabled = true; + static const kMaximumRetrials = 3; final _logger = Logger("MLFramework"); @@ -97,13 +98,26 @@ abstract class MLFramework { basename(getTextModelRemotePath()); } - Future _downloadFile(String url, String savePath) async { + Future _downloadFile( + String url, + String savePath, { + int trialCount = 1, + }) async { _logger.info("Downloading " + url); final existingFile = File(savePath); if (await existingFile.exists()) { await existingFile.delete(); } - await NetworkClient.instance.getDio().download(url, savePath); + try { + await NetworkClient.instance.getDio().download(url, savePath); + } catch (e, s) { + _logger.severe(e, s); + if (trialCount < kMaximumRetrials) { + return _downloadFile(url, savePath, trialCount: trialCount + 1); + } else { + rethrow; + } + } } Future getAccessiblePathForAsset( From 37de9d97eb619228c094ca8bd6dbb56ca1240a1a Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 16:00:44 +0530 Subject: [PATCH 075/175] remove widgets added for debugging + minor change to animation --- .../home/memories/full_screen_memory_new.dart | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart index af1842b77..970703e85 100644 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ b/lib/ui/home/memories/full_screen_memory_new.dart @@ -253,16 +253,6 @@ class _FullScreenMemoryNewState extends State { ), const BottomGradient(), BottomIcons(index), - Padding( - padding: const EdgeInsets.only(bottom: 120, right: 240), - child: Container( - color: Colors.black, - child: Text( - inheritedData.memories[index].file.generatedID - .toString(), - ), - ), - ), ], ); }, @@ -283,8 +273,8 @@ class _FullScreenMemoryNewState extends State { builder: (context, value, _) { return AnimatedSwitcher( duration: const Duration(milliseconds: 250), - switchInCurve: Curves.easeIn, - switchOutCurve: Curves.easeOut, + switchInCurve: Curves.easeOut, + switchOutCurve: Curves.easeIn, child: value ? Hero( tag: widget.title, @@ -328,12 +318,7 @@ class BottomIcons extends StatelessWidget { }, ), ]; - rowChildren.add( - Padding( - padding: const EdgeInsets.all(8), - child: Text(currentFile.generatedID.toString()), - ), - ); + if (currentFile.ownerID == null || (Configuration.instance.getUserID() ?? 0) == currentFile.ownerID) { rowChildren.addAll([ From 87fd7813040dd528fcd0f9bcf65c4e1e65aa6c36 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 16:05:03 +0530 Subject: [PATCH 076/175] Reassign variable instead of iterating over the list --- .../semantic_search/semantic_search_service.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index cdbfd9e83..5249e6a00 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -36,7 +36,6 @@ class SemanticSearchService { final _logger = Logger("SemanticSearchService"); final _queue = Queue(); - final _cachedEmbeddings = []; final _mlFramework = kCurrentModel == Model.onnxClip ? ONNX() : GGML(); final _frameworkInitialization = Completer(); @@ -44,6 +43,7 @@ class SemanticSearchService { bool _isComputingEmbeddings = false; bool _isSyncing = false; Future>? _ongoingRequest; + List _cachedEmbeddings = []; PendingQuery? _nextQuery; get hasInitialized => _hasInitialized; @@ -140,18 +140,16 @@ class SemanticSearchService { Future _setupCachedEmbeddings(bool shouldListenForUpdates) async { _logger.info("Setting up cached embeddings"); final startTime = DateTime.now(); - final cachedEmbeddings = await EmbeddingsDB.instance.getAll(kCurrentModel); + _cachedEmbeddings = await EmbeddingsDB.instance.getAll(kCurrentModel); final endTime = DateTime.now(); _logger.info( - "Loading ${cachedEmbeddings.length} took: ${(endTime.millisecondsSinceEpoch - startTime.millisecondsSinceEpoch)}ms", + "Loading ${_cachedEmbeddings.length} took: ${(endTime.millisecondsSinceEpoch - startTime.millisecondsSinceEpoch)}ms", ); - _cachedEmbeddings.addAll(cachedEmbeddings); _logger.info("Cached embeddings: " + _cachedEmbeddings.length.toString()); if (shouldListenForUpdates) { EmbeddingsDB.instance.getStream(kCurrentModel).listen((embeddings) { _logger.info("Updated embeddings: " + embeddings.length.toString()); - _cachedEmbeddings.clear(); - _cachedEmbeddings.addAll(embeddings); + _cachedEmbeddings = embeddings; Bus.instance.fire(EmbeddingUpdatedEvent()); }); } From d896160a5da9b8b17bacb1acd6eeffac9ffeac03 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 16:05:09 +0530 Subject: [PATCH 077/175] delete the old FullScreenMemory widget and file and rename new FullScreenMemoryNew widget and file to 'FullScreenMemory' --- lib/ui/home/memories/full_screen_memory.dart | 513 ++++++++++-------- .../home/memories/full_screen_memory_new.dart | 417 -------------- lib/ui/home/memories/memory_cover_widget.dart | 4 +- 3 files changed, 289 insertions(+), 645 deletions(-) delete mode 100644 lib/ui/home/memories/full_screen_memory_new.dart diff --git a/lib/ui/home/memories/full_screen_memory.dart b/lib/ui/home/memories/full_screen_memory.dart index 7fb2a330d..60f00a56b 100644 --- a/lib/ui/home/memories/full_screen_memory.dart +++ b/lib/ui/home/memories/full_screen_memory.dart @@ -5,7 +5,6 @@ import "package:flutter/cupertino.dart"; import "package:flutter/material.dart"; import "package:intl/intl.dart"; import "package:photos/core/configuration.dart"; -import 'package:photos/models/file/file.dart'; import "package:photos/models/memory.dart"; import "package:photos/services/memories_service.dart"; import "package:photos/theme/text_style.dart"; @@ -16,98 +15,178 @@ import "package:photos/utils/file_util.dart"; import "package:photos/utils/share_util.dart"; import "package:step_progress_indicator/step_progress_indicator.dart"; -class FullScreenMemory extends StatefulWidget { - final String title; +class FullScreenMemoryDataUpdater extends StatefulWidget { final List memories; - final int index; + final int initialIndex; + final Widget child; + const FullScreenMemoryDataUpdater({ + required this.memories, + required this.initialIndex, + required this.child, + super.key, + }); + + @override + State createState() => + _FullScreenMemoryDataUpdaterState(); +} + +class _FullScreenMemoryDataUpdaterState + extends State { + late ValueNotifier indexNotifier; + + @override + void initState() { + super.initState(); + indexNotifier = ValueNotifier(widget.initialIndex); + MemoriesService.instance + .markMemoryAsSeen(widget.memories[widget.initialIndex]); + } + + @override + void dispose() { + indexNotifier.dispose(); + super.dispose(); + } + + void removeCurrentMemory() { + setState(() { + widget.memories.removeAt(indexNotifier.value); + }); + } + + @override + Widget build(BuildContext context) { + return FullScreenMemoryData( + memories: widget.memories, + indexNotifier: indexNotifier, + removeCurrentMemory: removeCurrentMemory, + child: widget.child, + ); + } +} + +class FullScreenMemoryData extends InheritedWidget { + final List memories; + final ValueNotifier indexNotifier; + final VoidCallback removeCurrentMemory; + + const FullScreenMemoryData({ + required this.memories, + required this.indexNotifier, + required this.removeCurrentMemory, + required Widget child, + Key? key, + }) : super(child: child, key: key); + + static FullScreenMemoryData? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } + + @override + bool updateShouldNotify(FullScreenMemoryData oldWidget) { + // Checking oldWidget.memories.length != memories.length here doesn't work + //because the old widget and new widget reference the same memories list. + return true; + } +} - const FullScreenMemory(this.title, this.memories, this.index, {Key? key}) - : super(key: key); +class FullScreenMemory extends StatefulWidget { + final String title; + final int initialIndex; + const FullScreenMemory( + this.title, + this.initialIndex, { + super.key, + }); @override State createState() => _FullScreenMemoryState(); } class _FullScreenMemoryState extends State { - int _index = 0; - double _opacity = 1; - // shows memory counter as index+1/totalFiles for large number of memories - // when the top step indicator isn't visible. - bool _showCounter = false; - bool _showStepIndicator = true; PageController? _pageController; - final bool _shouldDisableScroll = false; - late int currentUserID; - final GlobalKey shareButtonKey = GlobalKey(); + final _showTitle = ValueNotifier(true); @override void initState() { super.initState(); - _index = widget.index; - currentUserID = Configuration.instance.getUserID() ?? 0; - _showStepIndicator = widget.memories.length <= 60; Future.delayed(const Duration(seconds: 3), () { if (mounted) { setState(() { - _opacity = 0; - _showCounter = !_showStepIndicator; + _showTitle.value = false; }); } }); - MemoriesService.instance.markMemoryAsSeen(widget.memories[_index]); + } + + @override + void dispose() { + _pageController?.dispose(); + _showTitle.dispose(); + super.dispose(); } @override Widget build(BuildContext context) { - final file = widget.memories[_index].file; + final inheritedData = FullScreenMemoryData.of(context)!; + final showStepProgressIndicator = inheritedData.memories.length < 60; return Scaffold( + extendBodyBehindAppBar: true, appBar: AppBar( toolbarHeight: 84, automaticallyImplyLeading: false, - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _showStepIndicator - ? StepProgressIndicator( - totalSteps: widget.memories.length, - currentStep: _index + 1, - size: 2, - selectedColor: Colors.white, //same for both themes - unselectedColor: Colors.white.withOpacity(0.4), - ) - : const SizedBox.shrink(), - const SizedBox( - height: 18, + title: ValueListenableBuilder( + valueListenable: inheritedData.indexNotifier, + child: Padding( + padding: const EdgeInsets.only(right: 16), + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: const Icon( + Icons.close, + color: Colors.white, //same for both themes + ), ), - Row( + ), + builder: (context, value, child) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.only(right: 16), - child: InkWell( - onTap: () { - Navigator.pop(context); - }, - child: const Icon( - Icons.close, - color: Colors.white, //same for both themes - ), - ), + showStepProgressIndicator + ? StepProgressIndicator( + totalSteps: inheritedData.memories.length, + currentStep: value + 1, + size: 2, + selectedColor: Colors.white, //same for both themes + unselectedColor: Colors.white.withOpacity(0.4), + ) + : const SizedBox.shrink(), + const SizedBox( + height: 18, ), - Text( - DateFormat.yMMMd(Localizations.localeOf(context).languageCode) - .format( - DateTime.fromMicrosecondsSinceEpoch( - file.creationTime!, + Row( + children: [ + child!, + Text( + DateFormat.yMMMd( + Localizations.localeOf(context).languageCode, + ).format( + DateTime.fromMicrosecondsSinceEpoch( + inheritedData.memories[value].file.creationTime!, + ), + ), + style: Theme.of(context).textTheme.titleMedium!.copyWith( + fontSize: 14, + color: Colors.white, + ), //same for both themes ), - ), - style: Theme.of(context).textTheme.titleMedium!.copyWith( - fontSize: 14, - color: Colors.white, - ), //same for both themes + ], ), ], - ), - ], + ); + }, ), flexibleSpace: Container( decoration: BoxDecoration( @@ -126,86 +205,108 @@ class _FullScreenMemoryState extends State { backgroundColor: const Color(0x00000000), elevation: 0, ), - extendBodyBehindAppBar: true, - body: Container( - key: ValueKey(widget.memories.length), - color: Colors.black, - child: Stack( - alignment: Alignment.bottomCenter, - children: [ - _buildSwiper(), - bottomGradient(), - _buildInfoText(), - _buildBottomIcons(), - ], - ), + body: Stack( + alignment: Alignment.bottomCenter, + children: [ + PageView.builder( + controller: _pageController ??= PageController( + initialPage: widget.initialIndex, + ), + itemBuilder: (context, index) { + if (index < inheritedData.memories.length - 1) { + final nextFile = inheritedData.memories[index + 1].file; + preloadThumbnail(nextFile); + preloadFile(nextFile); + } + return Stack( + alignment: Alignment.bottomCenter, + children: [ + GestureDetector( + onTapDown: (TapDownDetails details) { + final screenWidth = MediaQuery.of(context).size.width; + final edgeWidth = screenWidth * 0.20; + if (details.localPosition.dx < edgeWidth) { + if (index > 0) { + _pageController!.previousPage( + duration: const Duration(milliseconds: 250), + curve: Curves.ease, + ); + } + } else if (details.localPosition.dx > + screenWidth - edgeWidth) { + if (index < (inheritedData.memories.length - 1)) { + _pageController!.nextPage( + duration: const Duration(milliseconds: 250), + curve: Curves.ease, + ); + } + } + }, + child: FileWidget( + inheritedData.memories[index].file, + autoPlay: false, + tagPrefix: "memories", + backgroundDecoration: const BoxDecoration( + color: Colors.transparent, + ), + ), + ), + const BottomGradient(), + BottomIcons(index), + ], + ); + }, + onPageChanged: (index) { + unawaited( + MemoriesService.instance + .markMemoryAsSeen(inheritedData.memories[index]), + ); + inheritedData.indexNotifier.value = index; + }, + itemCount: inheritedData.memories.length, + ), + SafeArea( + top: false, + child: Padding( + padding: const EdgeInsets.only(bottom: 72), + child: ValueListenableBuilder( + builder: (context, value, _) { + return AnimatedSwitcher( + duration: const Duration(milliseconds: 250), + switchInCurve: Curves.easeOut, + switchOutCurve: Curves.easeIn, + child: value + ? Hero( + tag: widget.title, + child: Text( + widget.title, + style: darkTextTheme.h2, + ), + ) + : showStepProgressIndicator + ? const SizedBox.shrink() + : const MemoryCounter(), + ); + }, + valueListenable: _showTitle, + ), + ), + ), + ], ), ); } +} - @override - void dispose() { - debugPrint("FullScreenMemoryDisposed"); - // _pageController?.dispose(); - _pageController = null; - super.dispose(); - } +class BottomIcons extends StatelessWidget { + final int pageViewIndex; + const BottomIcons(this.pageViewIndex, {super.key}); - Future onFileDeleted(Memory removedMemory) async { - if (!mounted) { - return; - } - final totalFiles = widget.memories.length; - if (totalFiles == 1) { - // Deleted the only file - Navigator.of(context).pop(); // Close pageview - return; - } - if (_index == totalFiles - 1) { - // Deleted the last file - widget.memories.remove(removedMemory); - await _pageController!.previousPage( - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOut, - ); - setState(() {}); - } else { - widget.memories.remove(removedMemory); - await _pageController!.nextPage( - duration: const Duration(milliseconds: 200), - curve: Curves.easeInOut, - ); - setState(() {}); - } - } - - Hero _buildInfoText() { - return Hero( - tag: widget.title, - child: SafeArea( - child: Container( - alignment: Alignment.bottomCenter, - padding: const EdgeInsets.fromLTRB(0, 0, 0, 72), - child: _showCounter - ? Text( - '${_index + 1}/${widget.memories.length}', - style: darkTextTheme.bodyMuted, - ) - : AnimatedOpacity( - opacity: _opacity, - duration: const Duration(milliseconds: 500), - child: Text( - widget.title, - style: darkTextTheme.h2, - ), - ), - ), - ), - ); - } + @override + Widget build(BuildContext context) { + final inheritedData = FullScreenMemoryData.of(context)!; + final currentFile = inheritedData.memories[pageViewIndex].file; - Widget _buildBottomIcons() { - final EnteFile currentFile = widget.memories[_index].file; final List rowChildren = [ IconButton( icon: Icon( @@ -217,31 +318,35 @@ class _FullScreenMemoryState extends State { }, ), ]; - if (currentFile.ownerID == null || currentUserID == currentFile.ownerID) { - rowChildren.addAll( - [ - IconButton( - icon: Icon( - Platform.isAndroid ? Icons.delete_outline : CupertinoIcons.delete, - color: Colors.white, //same for both themes - ), - onPressed: () async { - await showSingleFileDeleteSheet( - context, - currentFile, - onFileRemoved: (file) => - {onFileDeleted(widget.memories[_index])}, - ); - }, - ), - SizedBox( - height: 32, - child: FavoriteWidget(currentFile), + + if (currentFile.ownerID == null || + (Configuration.instance.getUserID() ?? 0) == currentFile.ownerID) { + rowChildren.addAll([ + IconButton( + icon: Icon( + Platform.isAndroid ? Icons.delete_outline : CupertinoIcons.delete, + color: Colors.white, //same for both themes ), - ], - ); + onPressed: () async { + await showSingleFileDeleteSheet( + context, + inheritedData.memories[inheritedData.indexNotifier.value].file, + onFileRemoved: (file) => { + inheritedData.removeCurrentMemory.call(), + if (inheritedData.memories.isEmpty) + { + Navigator.of(context).pop(), + }, + }, + ); + }, + ), + SizedBox( + height: 32, + child: FavoriteWidget(currentFile), + ), + ]); } - rowChildren.add( IconButton( icon: Icon( @@ -255,6 +360,7 @@ class _FullScreenMemoryState extends State { ); return SafeArea( + top: false, child: Container( alignment: Alignment.bottomCenter, padding: const EdgeInsets.fromLTRB(12, 0, 12, 20), @@ -265,8 +371,31 @@ class _FullScreenMemoryState extends State { ), ); } +} + +class MemoryCounter extends StatelessWidget { + const MemoryCounter({super.key}); + + @override + Widget build(BuildContext context) { + final inheritedData = FullScreenMemoryData.of(context)!; + return ValueListenableBuilder( + valueListenable: inheritedData.indexNotifier, + builder: (context, value, _) { + return Text( + "${value + 1}/${inheritedData.memories.length}", + style: darkTextTheme.bodyMuted, + ); + }, + ); + } +} + +class BottomGradient extends StatelessWidget { + const BottomGradient({super.key}); - Widget bottomGradient() { + @override + Widget build(BuildContext context) { return IgnorePointer( child: Container( height: 124, @@ -285,72 +414,4 @@ class _FullScreenMemoryState extends State { ), ); } - - Widget _buildSwiper() { - debugPrint( - "FullScreenbuildSwiper: $_index and total ${widget.memories.length}", - ); - _pageController ??= PageController(initialPage: _index); - return GestureDetector( - behavior: HitTestBehavior.translucent, - onTapDown: (TapDownDetails details) { - if (_shouldDisableScroll) { - return; - } - final screenWidth = MediaQuery.of(context).size.width; - final edgeWidth = screenWidth * 0.20; // 20% of screen width - if (details.localPosition.dx < edgeWidth) { - if (_index > 0) { - _pageController!.previousPage( - duration: const Duration(milliseconds: 250), - curve: Curves.ease, - ); - } - } else if (details.localPosition.dx > screenWidth - edgeWidth) { - if (_index < (widget.memories.length - 1)) { - _pageController!.nextPage( - duration: const Duration(milliseconds: 250), - curve: Curves.ease, - ); - } - } - }, - child: PageView.builder( - itemBuilder: (BuildContext context, int index) { - if (index < widget.memories.length - 1) { - final nextFile = widget.memories[index + 1].file; - preloadThumbnail(nextFile); - preloadFile(nextFile); - } - final file = widget.memories[index].file; - return FileWidget( - file, - autoPlay: false, - tagPrefix: "memories", - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, - ), - ); - }, - itemCount: widget.memories.length, - controller: _pageController!, - onPageChanged: (index) async { - unawaited( - MemoriesService.instance.markMemoryAsSeen(widget.memories[index]), - ); - if (mounted) { - debugPrint( - "FullScreenonPageChanged: $index and total ${widget.memories.length}", - ); - setState(() { - _index = index; - }); - } - }, - physics: _shouldDisableScroll - ? const NeverScrollableScrollPhysics() - : const PageScrollPhysics(), - ), - ); - } } diff --git a/lib/ui/home/memories/full_screen_memory_new.dart b/lib/ui/home/memories/full_screen_memory_new.dart deleted file mode 100644 index 970703e85..000000000 --- a/lib/ui/home/memories/full_screen_memory_new.dart +++ /dev/null @@ -1,417 +0,0 @@ -import "dart:async"; -import "dart:io"; - -import "package:flutter/cupertino.dart"; -import "package:flutter/material.dart"; -import "package:intl/intl.dart"; -import "package:photos/core/configuration.dart"; -import "package:photos/models/memory.dart"; -import "package:photos/services/memories_service.dart"; -import "package:photos/theme/text_style.dart"; -import "package:photos/ui/actions/file/file_actions.dart"; -import "package:photos/ui/viewer/file/file_widget.dart"; -import "package:photos/ui/viewer/file_details/favorite_widget.dart"; -import "package:photos/utils/file_util.dart"; -import "package:photos/utils/share_util.dart"; -import "package:step_progress_indicator/step_progress_indicator.dart"; - -class FullScreenMemoryDataUpdater extends StatefulWidget { - final List memories; - final int initialIndex; - final Widget child; - const FullScreenMemoryDataUpdater({ - required this.memories, - required this.initialIndex, - required this.child, - super.key, - }); - - @override - State createState() => - _FullScreenMemoryDataUpdaterState(); -} - -class _FullScreenMemoryDataUpdaterState - extends State { - late ValueNotifier indexNotifier; - - @override - void initState() { - super.initState(); - indexNotifier = ValueNotifier(widget.initialIndex); - MemoriesService.instance - .markMemoryAsSeen(widget.memories[widget.initialIndex]); - } - - @override - void dispose() { - indexNotifier.dispose(); - super.dispose(); - } - - void removeCurrentMemory() { - setState(() { - widget.memories.removeAt(indexNotifier.value); - }); - } - - @override - Widget build(BuildContext context) { - return FullScreenMemoryData( - memories: widget.memories, - indexNotifier: indexNotifier, - removeCurrentMemory: removeCurrentMemory, - child: widget.child, - ); - } -} - -class FullScreenMemoryData extends InheritedWidget { - final List memories; - final ValueNotifier indexNotifier; - final VoidCallback removeCurrentMemory; - - const FullScreenMemoryData({ - required this.memories, - required this.indexNotifier, - required this.removeCurrentMemory, - required Widget child, - Key? key, - }) : super(child: child, key: key); - - static FullScreenMemoryData? of(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType(); - } - - @override - bool updateShouldNotify(FullScreenMemoryData oldWidget) { - // Checking oldWidget.memories.length != memories.length here doesn't work - //because the old widget and new widget reference the same memories list. - return true; - } -} - -class FullScreenMemoryNew extends StatefulWidget { - final String title; - final int initialIndex; - const FullScreenMemoryNew( - this.title, - this.initialIndex, { - super.key, - }); - - @override - State createState() => _FullScreenMemoryNewState(); -} - -class _FullScreenMemoryNewState extends State { - PageController? _pageController; - final _showTitle = ValueNotifier(true); - - @override - void initState() { - super.initState(); - Future.delayed(const Duration(seconds: 3), () { - if (mounted) { - setState(() { - _showTitle.value = false; - }); - } - }); - } - - @override - void dispose() { - _pageController?.dispose(); - _showTitle.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final inheritedData = FullScreenMemoryData.of(context)!; - final showStepProgressIndicator = inheritedData.memories.length < 60; - return Scaffold( - extendBodyBehindAppBar: true, - appBar: AppBar( - toolbarHeight: 84, - automaticallyImplyLeading: false, - title: ValueListenableBuilder( - valueListenable: inheritedData.indexNotifier, - child: Padding( - padding: const EdgeInsets.only(right: 16), - child: InkWell( - onTap: () { - Navigator.pop(context); - }, - child: const Icon( - Icons.close, - color: Colors.white, //same for both themes - ), - ), - ), - builder: (context, value, child) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - showStepProgressIndicator - ? StepProgressIndicator( - totalSteps: inheritedData.memories.length, - currentStep: value + 1, - size: 2, - selectedColor: Colors.white, //same for both themes - unselectedColor: Colors.white.withOpacity(0.4), - ) - : const SizedBox.shrink(), - const SizedBox( - height: 18, - ), - Row( - children: [ - child!, - Text( - DateFormat.yMMMd( - Localizations.localeOf(context).languageCode, - ).format( - DateTime.fromMicrosecondsSinceEpoch( - inheritedData.memories[value].file.creationTime!, - ), - ), - style: Theme.of(context).textTheme.titleMedium!.copyWith( - fontSize: 14, - color: Colors.white, - ), //same for both themes - ), - ], - ), - ], - ); - }, - ), - flexibleSpace: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.black.withOpacity(0.6), - Colors.black.withOpacity(0.5), - Colors.transparent, - ], - stops: const [0, 0.6, 1], - ), - ), - ), - backgroundColor: const Color(0x00000000), - elevation: 0, - ), - body: Stack( - alignment: Alignment.bottomCenter, - children: [ - PageView.builder( - controller: _pageController ??= PageController( - initialPage: widget.initialIndex, - ), - itemBuilder: (context, index) { - if (index < inheritedData.memories.length - 1) { - final nextFile = inheritedData.memories[index + 1].file; - preloadThumbnail(nextFile); - preloadFile(nextFile); - } - return Stack( - alignment: Alignment.bottomCenter, - children: [ - GestureDetector( - onTapDown: (TapDownDetails details) { - final screenWidth = MediaQuery.of(context).size.width; - final edgeWidth = screenWidth * 0.20; - if (details.localPosition.dx < edgeWidth) { - if (index > 0) { - _pageController!.previousPage( - duration: const Duration(milliseconds: 250), - curve: Curves.ease, - ); - } - } else if (details.localPosition.dx > - screenWidth - edgeWidth) { - if (index < (inheritedData.memories.length - 1)) { - _pageController!.nextPage( - duration: const Duration(milliseconds: 250), - curve: Curves.ease, - ); - } - } - }, - child: FileWidget( - inheritedData.memories[index].file, - autoPlay: false, - tagPrefix: "memories", - backgroundDecoration: const BoxDecoration( - color: Colors.transparent, - ), - ), - ), - const BottomGradient(), - BottomIcons(index), - ], - ); - }, - onPageChanged: (index) { - unawaited( - MemoriesService.instance - .markMemoryAsSeen(inheritedData.memories[index]), - ); - inheritedData.indexNotifier.value = index; - }, - itemCount: inheritedData.memories.length, - ), - SafeArea( - top: false, - child: Padding( - padding: const EdgeInsets.only(bottom: 72), - child: ValueListenableBuilder( - builder: (context, value, _) { - return AnimatedSwitcher( - duration: const Duration(milliseconds: 250), - switchInCurve: Curves.easeOut, - switchOutCurve: Curves.easeIn, - child: value - ? Hero( - tag: widget.title, - child: Text( - widget.title, - style: darkTextTheme.h2, - ), - ) - : showStepProgressIndicator - ? const SizedBox.shrink() - : const MemoryCounter(), - ); - }, - valueListenable: _showTitle, - ), - ), - ), - ], - ), - ); - } -} - -class BottomIcons extends StatelessWidget { - final int pageViewIndex; - const BottomIcons(this.pageViewIndex, {super.key}); - - @override - Widget build(BuildContext context) { - final inheritedData = FullScreenMemoryData.of(context)!; - final currentFile = inheritedData.memories[pageViewIndex].file; - - final List rowChildren = [ - IconButton( - icon: Icon( - Platform.isAndroid ? Icons.info_outline : CupertinoIcons.info, - color: Colors.white, //same for both themes - ), - onPressed: () { - showDetailsSheet(context, currentFile); - }, - ), - ]; - - if (currentFile.ownerID == null || - (Configuration.instance.getUserID() ?? 0) == currentFile.ownerID) { - rowChildren.addAll([ - IconButton( - icon: Icon( - Platform.isAndroid ? Icons.delete_outline : CupertinoIcons.delete, - color: Colors.white, //same for both themes - ), - onPressed: () async { - await showSingleFileDeleteSheet( - context, - inheritedData.memories[inheritedData.indexNotifier.value].file, - onFileRemoved: (file) => { - inheritedData.removeCurrentMemory.call(), - if (inheritedData.memories.isEmpty) - { - Navigator.of(context).pop(), - }, - }, - ); - }, - ), - SizedBox( - height: 32, - child: FavoriteWidget(currentFile), - ), - ]); - } - rowChildren.add( - IconButton( - icon: Icon( - Icons.adaptive.share, - color: Colors.white, //same for both themes - ), - onPressed: () { - share(context, [currentFile]); - }, - ), - ); - - return SafeArea( - top: false, - child: Container( - alignment: Alignment.bottomCenter, - padding: const EdgeInsets.fromLTRB(12, 0, 12, 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: rowChildren, - ), - ), - ); - } -} - -class MemoryCounter extends StatelessWidget { - const MemoryCounter({super.key}); - - @override - Widget build(BuildContext context) { - final inheritedData = FullScreenMemoryData.of(context)!; - return ValueListenableBuilder( - valueListenable: inheritedData.indexNotifier, - builder: (context, value, _) { - return Text( - "${value + 1}/${inheritedData.memories.length}", - style: darkTextTheme.bodyMuted, - ); - }, - ); - } -} - -class BottomGradient extends StatelessWidget { - const BottomGradient({super.key}); - - @override - Widget build(BuildContext context) { - return IgnorePointer( - child: Container( - height: 124, - width: double.infinity, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - colors: [ - Colors.black.withOpacity(0.5), //same for both themes - Colors.transparent, - ], - stops: const [0, 0.8], - ), - ), - ), - ); - } -} diff --git a/lib/ui/home/memories/memory_cover_widget.dart b/lib/ui/home/memories/memory_cover_widget.dart index df1b6d5b3..5218ba5ff 100644 --- a/lib/ui/home/memories/memory_cover_widget.dart +++ b/lib/ui/home/memories/memory_cover_widget.dart @@ -2,7 +2,7 @@ import "package:flutter/material.dart"; import "package:photos/generated/l10n.dart"; import "package:photos/models/memory.dart"; import "package:photos/theme/ente_theme.dart"; -import "package:photos/ui/home/memories/full_screen_memory_new.dart"; +import 'package:photos/ui/home/memories/full_screen_memory.dart'; import "package:photos/ui/viewer/file/thumbnail_widget.dart"; import "package:photos/utils/navigation_util.dart"; @@ -35,7 +35,7 @@ class _MemoryCovertWidgetState extends State { FullScreenMemoryDataUpdater( initialIndex: index, memories: widget.memories, - child: FullScreenMemoryNew(title, index), + child: FullScreenMemory(title, index), ), forceCustomPageRoute: true, ); From 1d7e609bbe19cddfcf85b152f837c5ad13b91942 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 16:05:22 +0530 Subject: [PATCH 078/175] Remove unused variable --- lib/services/semantic_search/semantic_search_service.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index 5249e6a00..b78951be8 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -28,7 +28,6 @@ class SemanticSearchService { static final Computer _computer = Computer.shared(); static final LRUMap> _queryCache = LRUMap(20); - static const kModelName = "clip"; static const kEmbeddingLength = 512; static const kScoreThreshold = 0.23; static const kShouldPushEmbeddings = true; From e50e6dc013d9252e8adddba36242de982597c129 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 16:18:46 +0530 Subject: [PATCH 079/175] Add log line --- lib/services/semantic_search/semantic_search_service.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index b78951be8..203729990 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -117,6 +117,7 @@ class SemanticSearchService { return _ongoingRequest!; } else { // If there's an ongoing request, create or replace the nextCompleter. + _logger.info("Queuing query $query"); await _nextQuery?.completer.future .timeout(const Duration(seconds: 0)); // Cancels the previous future. _nextQuery = PendingQuery(query, Completer>()); From 7643a29152e6040ce8d30e06084c67b64fb0368f Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 5 Jan 2024 16:26:38 +0530 Subject: [PATCH 080/175] added documentation --- lib/ui/home/memories/full_screen_memory.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/ui/home/memories/full_screen_memory.dart b/lib/ui/home/memories/full_screen_memory.dart index 60f00a56b..bacc6adda 100644 --- a/lib/ui/home/memories/full_screen_memory.dart +++ b/lib/ui/home/memories/full_screen_memory.dart @@ -15,6 +15,22 @@ import "package:photos/utils/file_util.dart"; import "package:photos/utils/share_util.dart"; import "package:step_progress_indicator/step_progress_indicator.dart"; +//There are two states of variables that FullScreenMemory depends on: +//1. The list of memories +//2. The current index of the page view + +//1 +//Only when items are deleted will list of memories change and this requires the +//whole screen to be rebuild. So the InheritedWidget is updated using the Updater +//widget which will then lead to a rebuild of all widgets that call +//InheritedWidget.of(context). + +//2 +//There are widgets that doesn't come inside the PageView that needs to rebuild +//with new state when page index is changed. So the index is stored in a +//ValueNotifier inside the InheritedWidget and the widgets that need to change +//are wrapped in a ValueListenableBuilder. + class FullScreenMemoryDataUpdater extends StatefulWidget { final List memories; final int initialIndex; From 637f3c0f6652d301b3447862f95980b092469d8e Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 16:51:54 +0530 Subject: [PATCH 081/175] Remove reliance on Isar's watcher --- lib/db/embeddings_db.dart | 8 +++---- lib/main.dart | 4 +--- .../semantic_search_service.dart | 24 ++++++++++--------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/db/embeddings_db.dart b/lib/db/embeddings_db.dart index 78f3c4420..1d3ae16f6 100644 --- a/lib/db/embeddings_db.dart +++ b/lib/db/embeddings_db.dart @@ -2,6 +2,8 @@ import "dart:io"; import "package:isar/isar.dart"; import 'package:path_provider/path_provider.dart'; +import "package:photos/core/event_bus.dart"; +import "package:photos/events/embedding_updated_event.dart"; import "package:photos/models/embedding.dart"; class EmbeddingsDB { @@ -24,10 +26,6 @@ class EmbeddingsDB { await _isar.clear(); } - Stream> getStream(Model model) { - return _isar.embeddings.filter().modelEqualTo(model).watch(); - } - Future> getAll(Model model) async { return _isar.embeddings.filter().modelEqualTo(model).findAll(); } @@ -35,12 +33,14 @@ class EmbeddingsDB { Future put(Embedding embedding) { return _isar.writeTxn(() async { await _isar.embeddings.put(embedding); + Bus.instance.fire(EmbeddingUpdatedEvent()); }); } Future putMany(List embeddings) { return _isar.writeTxn(() async { await _isar.embeddings.putAll(embeddings); + Bus.instance.fire(EmbeddingUpdatedEvent()); }); } diff --git a/lib/main.dart b/lib/main.dart index 13522cc09..8e553280b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -193,9 +193,7 @@ Future _init(bool isBackground, {String via = ''}) async { }); } unawaited(FeatureFlagService.instance.init()); - unawaited( - SemanticSearchService.instance.init(isAppInForeground: !isBackground), - ); + unawaited(SemanticSearchService.instance.init()); // Can not including existing tf/ml binaries as they are not being built // from source. // See https://gitlab.com/fdroid/fdroiddata/-/merge_requests/12671#note_1294346819 diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index 203729990..d92f0a11a 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -17,6 +17,7 @@ import "package:photos/services/semantic_search/embedding_store.dart"; import "package:photos/services/semantic_search/frameworks/ggml.dart"; import "package:photos/services/semantic_search/frameworks/ml_framework.dart"; import 'package:photos/services/semantic_search/frameworks/onnx/onnx.dart'; +import "package:photos/utils/debouncer.dart"; import "package:photos/utils/local_settings.dart"; import "package:photos/utils/thumbnail_util.dart"; @@ -32,11 +33,14 @@ class SemanticSearchService { static const kScoreThreshold = 0.23; static const kShouldPushEmbeddings = true; static const kCurrentModel = Model.onnxClip; + static const kDebounceDuration = Duration(milliseconds: 10000); final _logger = Logger("SemanticSearchService"); final _queue = Queue(); final _mlFramework = kCurrentModel == Model.onnxClip ? ONNX() : GGML(); final _frameworkInitialization = Completer(); + final _embeddingLoaderDebouncer = + Debouncer(kDebounceDuration, executionInterval: kDebounceDuration); bool _hasInitialized = false; bool _isComputingEmbeddings = false; @@ -47,7 +51,7 @@ class SemanticSearchService { get hasInitialized => _hasInitialized; - Future init({bool isAppInForeground = true}) async { + Future init() async { if (!LocalSettings.instance.hasEnabledMagicSearch()) { return; } @@ -58,7 +62,12 @@ class SemanticSearchService { _hasInitialized = true; await EmbeddingsDB.instance.init(); await EmbeddingStore.instance.init(); - await _setupCachedEmbeddings(isAppInForeground); + await _loadEmbeddings(); + Bus.instance.on().listen((event) { + _embeddingLoaderDebouncer.run(() async { + await _loadEmbeddings(); + }); + }); Bus.instance.on().listen((event) { // Diff sync is complete, we can now pull embeddings from remote unawaited(sync()); @@ -137,8 +146,8 @@ class SemanticSearchService { _logger.info("Indexes cleared for $kCurrentModel"); } - Future _setupCachedEmbeddings(bool shouldListenForUpdates) async { - _logger.info("Setting up cached embeddings"); + Future _loadEmbeddings() async { + _logger.info("Pulling cached embeddings"); final startTime = DateTime.now(); _cachedEmbeddings = await EmbeddingsDB.instance.getAll(kCurrentModel); final endTime = DateTime.now(); @@ -146,13 +155,6 @@ class SemanticSearchService { "Loading ${_cachedEmbeddings.length} took: ${(endTime.millisecondsSinceEpoch - startTime.millisecondsSinceEpoch)}ms", ); _logger.info("Cached embeddings: " + _cachedEmbeddings.length.toString()); - if (shouldListenForUpdates) { - EmbeddingsDB.instance.getStream(kCurrentModel).listen((embeddings) { - _logger.info("Updated embeddings: " + embeddings.length.toString()); - _cachedEmbeddings = embeddings; - Bus.instance.fire(EmbeddingUpdatedEvent()); - }); - } } Future _backFill() async { From add4ad27004a4316f3f1a10a0a8c4297eee1eeb1 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 16:55:03 +0530 Subject: [PATCH 082/175] Reduce debounce duration --- lib/services/semantic_search/semantic_search_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index d92f0a11a..13cb66f9c 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -33,7 +33,7 @@ class SemanticSearchService { static const kScoreThreshold = 0.23; static const kShouldPushEmbeddings = true; static const kCurrentModel = Model.onnxClip; - static const kDebounceDuration = Duration(milliseconds: 10000); + static const kDebounceDuration = Duration(milliseconds: 4000); final _logger = Logger("SemanticSearchService"); final _queue = Queue(); From 4edebb9501a74fc4e64bb8086dd79a355e39c078 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 16:55:15 +0530 Subject: [PATCH 083/175] Update machine learning settings page --- lib/generated/intl/messages_en.dart | 2 +- lib/generated/l10n.dart | 4 ++-- lib/l10n/intl_en.arb | 2 +- lib/ui/settings/machine_learning_settings_page.dart | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 1872064d5..f0fc982cb 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -856,7 +856,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Machine learning"), "magicSearch": MessageLookupByLibrary.simpleMessage("Magic search"), "magicSearchDescription": MessageLookupByLibrary.simpleMessage( - "Please use our Desktop app to index the pending items in your library."), + "Please note that this will result in a higher bandwidth and battery usage until all items are indexed."), "manage": MessageLookupByLibrary.simpleMessage("Manage"), "manageDeviceStorage": MessageLookupByLibrary.simpleMessage("Manage device storage"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index c4b5a40ea..301641f61 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -2866,10 +2866,10 @@ class S { ); } - /// `Please use our Desktop app to index the pending items in your library.` + /// `Please note that this will result in a higher bandwidth and battery usage until all items are indexed.` String get magicSearchDescription { return Intl.message( - 'Please use our Desktop app to index the pending items in your library.', + 'Please note that this will result in a higher bandwidth and battery usage until all items are indexed.', name: 'magicSearchDescription', desc: '', args: [], diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 2f26f7da5..371801517 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -408,7 +408,7 @@ "manageDeviceStorage": "Manage device storage", "machineLearning": "Machine learning", "magicSearch": "Magic search", - "magicSearchDescription": "Please use our Desktop app to index the pending items in your library.", + "magicSearchDescription": "Please note that this will result in a higher bandwidth and battery usage until all items are indexed.", "status": "Status", "indexedItems": "Indexed items", "pendingItems": "Pending items", diff --git a/lib/ui/settings/machine_learning_settings_page.dart b/lib/ui/settings/machine_learning_settings_page.dart index 158abd0f2..de40c7947 100644 --- a/lib/ui/settings/machine_learning_settings_page.dart +++ b/lib/ui/settings/machine_learning_settings_page.dart @@ -192,12 +192,12 @@ class _MagicSearchIndexStatsWidgetState Row( children: [ MenuSectionTitle(title: S.of(context).status), - // Expanded(child: Container()), - // _status!.pendingItems > 0 - // ? EnteLoadingWidget( - // color: getEnteColorScheme(context).fillMuted, - // ) - // : const SizedBox.shrink(), + Expanded(child: Container()), + _status!.pendingItems > 0 + ? EnteLoadingWidget( + color: getEnteColorScheme(context).fillMuted, + ) + : const SizedBox.shrink(), ], ), MenuItemWidget( From 529ad88a7067389d273f7cc7ed1db375169284ca Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 18:08:23 +0530 Subject: [PATCH 084/175] Render model loading status --- lib/generated/intl/messages_en.dart | 2 + lib/generated/l10n.dart | 10 +++++ lib/l10n/intl_en.arb | 6 +-- .../semantic_search_service.dart | 8 +++- .../machine_learning_settings_page.dart | 37 ++++++++++++++++++- 5 files changed, 57 insertions(+), 6 deletions(-) diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index f0fc982cb..3febd6407 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -832,6 +832,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Loading gallery..."), "loadingMessage": MessageLookupByLibrary.simpleMessage("Loading your photos..."), + "loadingModel": + MessageLookupByLibrary.simpleMessage("Downloading models..."), "localGallery": MessageLookupByLibrary.simpleMessage("Local gallery"), "location": MessageLookupByLibrary.simpleMessage("Location"), "locationName": MessageLookupByLibrary.simpleMessage("Location name"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 301641f61..9f3136f8a 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -2876,6 +2876,16 @@ class S { ); } + /// `Downloading models...` + String get loadingModel { + return Intl.message( + 'Downloading models...', + name: 'loadingModel', + desc: '', + args: [], + ); + } + /// `Status` String get status { return Intl.message( diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 371801517..20571d2e4 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -409,6 +409,7 @@ "machineLearning": "Machine learning", "magicSearch": "Magic search", "magicSearchDescription": "Please note that this will result in a higher bandwidth and battery usage until all items are indexed.", + "loadingModel": "Downloading models...", "status": "Status", "indexedItems": "Indexed items", "pendingItems": "Pending items", @@ -704,7 +705,7 @@ "deleteAlbumsDialogBody": "This will delete all empty albums. This is useful when you want to reduce the clutter in your album list.", "deleteProgress": "Deleting {currentlyDeleting} / {totalCount}", "genericProgress": "Processing {currentlyProcessing} / {totalCount}", - "@genericProgress" : { + "@genericProgress": { "description": "Generic progress text to display when processing multiple items", "type": "text", "placeholders": { @@ -718,7 +719,6 @@ } } }, - "permanentlyDelete": "Permanently delete", "canOnlyCreateLinkForFilesOwnedByYou": "Can only create link for files owned by you", "publicLinkCreated": "Public link created", @@ -1187,4 +1187,4 @@ "changeLocationOfSelectedItems": "Change location of selected items?", "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", "cleanUncategorized": "Clean Uncategorized" -} \ No newline at end of file +} diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index 13cb66f9c..06f56d94e 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -38,7 +38,7 @@ class SemanticSearchService { final _logger = Logger("SemanticSearchService"); final _queue = Queue(); final _mlFramework = kCurrentModel == Model.onnxClip ? ONNX() : GGML(); - final _frameworkInitialization = Completer(); + final _frameworkInitialization = Completer(); final _embeddingLoaderDebouncer = Debouncer(kDebounceDuration, executionInterval: kDebounceDuration); @@ -141,6 +141,10 @@ class SemanticSearchService { ); } + Future getFrameworkInitializationStatus() { + return _frameworkInitialization.future; + } + Future clearIndexes() async { await EmbeddingsDB.instance.deleteAllForModel(kCurrentModel); _logger.info("Indexes cleared for $kCurrentModel"); @@ -232,7 +236,7 @@ class SemanticSearchService { _logger.info("Initializing ML framework"); try { await _mlFramework.init(); - _frameworkInitialization.complete(); + _frameworkInitialization.complete(true); } catch (e, s) { _logger.severe("ML framework initialization failed", e, s); } diff --git a/lib/ui/settings/machine_learning_settings_page.dart b/lib/ui/settings/machine_learning_settings_page.dart index de40c7947..3bb38f874 100644 --- a/lib/ui/settings/machine_learning_settings_page.dart +++ b/lib/ui/settings/machine_learning_settings_page.dart @@ -115,7 +115,17 @@ class _MachineLearningSettingsPageState hasEnabled ? Column( children: [ - const MagicSearchIndexStatsWidget(), + FutureBuilder( + future: SemanticSearchService.instance + .getFrameworkInitializationStatus(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + return const MagicSearchIndexStatsWidget(); + } else { + return const ModelLoadingState(); + } + }, + ), const SizedBox( height: 12, ), @@ -144,6 +154,31 @@ class _MachineLearningSettingsPageState } } +class ModelLoadingState extends StatelessWidget { + const ModelLoadingState({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + MenuSectionTitle(title: S.of(context).status), + MenuItemWidget( + captionedTextWidget: CaptionedTextWidget( + title: S.of(context).loadingModel, + ), + trailingWidget: EnteLoadingWidget( + size: 12, + color: getEnteColorScheme(context).fillMuted, + ), + singleBorderRadius: 8, + alignCaptionedTextToLeft: true, + isGestureDetectorDisabled: true, + ), + ], + ); + } +} + class MagicSearchIndexStatsWidget extends StatefulWidget { const MagicSearchIndexStatsWidget({ super.key, From 3d9126ab48b11205c06c4d23cc0deda0057b2b29 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Fri, 5 Jan 2024 18:09:01 +0530 Subject: [PATCH 085/175] v0.8.31 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index d0a4fd9d1..f2de3c249 100644 --- a/pubspec.yaml +++ b/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.8.30+550 +version: 0.8.31+551 environment: sdk: ">=3.0.0 <4.0.0" From 827003b965b215b9a421ac23b8ca6fbef52ae4aa Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 6 Jan 2024 01:12:15 +0530 Subject: [PATCH 086/175] Do the dew --- lib/db/embeddings_db.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/db/embeddings_db.dart b/lib/db/embeddings_db.dart index 1d3ae16f6..3a6dc541a 100644 --- a/lib/db/embeddings_db.dart +++ b/lib/db/embeddings_db.dart @@ -19,7 +19,7 @@ class EmbeddingsDB { [EmbeddingSchema], directory: dir.path, ); - // TODO: _clearDeprecatedStore(dir); + await _clearDeprecatedStore(dir); } Future clearTable() async { From fff3191f6b8c0a61e135c67b487de013421d8819 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 6 Jan 2024 01:15:49 +0530 Subject: [PATCH 087/175] Fire event in case of deletions --- lib/db/embeddings_db.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/db/embeddings_db.dart b/lib/db/embeddings_db.dart index 3a6dc541a..539137c96 100644 --- a/lib/db/embeddings_db.dart +++ b/lib/db/embeddings_db.dart @@ -53,6 +53,7 @@ class EmbeddingsDB { final embeddings = await _isar.embeddings.filter().modelEqualTo(model).findAll(); await _isar.embeddings.deleteAll(embeddings.map((e) => e.id).toList()); + Bus.instance.fire(EmbeddingUpdatedEvent()); }); } From 81c199d3a20dd0ebed40363e01212d0798527cd1 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 6 Jan 2024 12:17:21 +0530 Subject: [PATCH 088/175] fix: adding a file with path '/storage/emulated/0' shared from outside ente to an album failing --- lib/utils/share_util.dart | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/utils/share_util.dart b/lib/utils/share_util.dart index c8750490f..2fae92bbb 100644 --- a/lib/utils/share_util.dart +++ b/lib/utils/share_util.dart @@ -119,9 +119,33 @@ Future> convertIncomingSharedMediaToFile( // fileName: img_x.jpg enteFile.title = basename(media.path); var ioFile = File(media.path); - ioFile = ioFile.renameSync( - Configuration.instance.getSharedMediaDirectory() + "/" + enteFile.title!, - ); + try { + ioFile = ioFile.renameSync( + Configuration.instance.getSharedMediaDirectory() + + "/" + + enteFile.title!, + ); + } catch (e) { + if (e is FileSystemException) { + //from renameSync docs: + //On some platforms, a rename operation cannot move a file between + //different file systems. If that is the case, instead copySync the + //file to the new location and then deleteSync the original. + _logger.info("Creating new copy of file in path ${ioFile.path}"); + final newIoFile = ioFile.copySync( + Configuration.instance.getSharedMediaDirectory() + + "/" + + enteFile.title!, + ); + if (media.path.contains("io.ente.photos")) { + _logger.info("delete original file in path ${ioFile.path}"); + ioFile.deleteSync(); + } + ioFile = newIoFile; + } else { + rethrow; + } + } enteFile.localID = sharedMediaIdentifier + enteFile.title!; enteFile.collectionID = collectionID; enteFile.fileType = From 9e4e8ef740cd93dcfef0a3553fcef7f55b6118d2 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 6 Jan 2024 17:45:15 +0530 Subject: [PATCH 089/175] remove unused old search related code --- lib/ui/viewer/search/search_widget.dart | 75 ------------------------- 1 file changed, 75 deletions(-) diff --git a/lib/ui/viewer/search/search_widget.dart b/lib/ui/viewer/search/search_widget.dart index 293025ee8..0044697f1 100644 --- a/lib/ui/viewer/search/search_widget.dart +++ b/lib/ui/viewer/search/search_widget.dart @@ -198,81 +198,6 @@ class SearchWidgetState extends State { ); } - Future> getSearchResultsForQuery( - BuildContext context, - String query, - ) async { - final Completer> completer = Completer(); - - _debouncer.run( - () { - return _getSearchResultsFromService(context, query, completer); - }, - ); - - return completer.future; - } - - Future _getSearchResultsFromService( - BuildContext context, - String query, - Completer completer, - ) async { - final List allResults = []; - if (query.isEmpty) { - completer.complete(allResults); - return; - } - try { - if (_isYearValid(query)) { - final yearResults = await _searchService.getYearSearchResults(query); - allResults.addAll(yearResults); - } - - final holidayResults = - await _searchService.getHolidaySearchResults(context, query); - allResults.addAll(holidayResults); - - final fileTypeSearchResults = - await _searchService.getFileTypeResults(context, query); - allResults.addAll(fileTypeSearchResults); - - final captionAndDisplayNameResult = - await _searchService.getCaptionAndNameResults(query); - allResults.addAll(captionAndDisplayNameResult); - - final fileExtnResult = - await _searchService.getFileExtensionResults(query); - allResults.addAll(fileExtnResult); - - final locationResult = await _searchService.getLocationResults(query); - allResults.addAll(locationResult); - - final collectionResults = - await _searchService.getCollectionSearchResults(query); - allResults.addAll(collectionResults); - - final monthResults = - await _searchService.getMonthSearchResults(context, query); - allResults.addAll(monthResults); - - final possibleEvents = - await _searchService.getDateResults(context, query); - allResults.addAll(possibleEvents); - - final magicResults = - await _searchService.getMagicSearchResults(context, query); - allResults.addAll(magicResults); - - final contactResults = - await _searchService.getContactSearchResults(query); - allResults.addAll(contactResults); - } catch (e, s) { - _logger.severe("error during search", e, s); - } - completer.complete(allResults); - } - Stream> _getSearchResultsStream( BuildContext context, String query, From d00ae5742a4f3d0d7c74860b0fef8333e03d71d8 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 6 Jan 2024 19:12:15 +0530 Subject: [PATCH 090/175] Prevent duplicate insertions --- lib/db/embeddings_db.dart | 5 +++-- lib/services/semantic_search/embedding_store.dart | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/db/embeddings_db.dart b/lib/db/embeddings_db.dart index 539137c96..d1a71609e 100644 --- a/lib/db/embeddings_db.dart +++ b/lib/db/embeddings_db.dart @@ -30,10 +30,11 @@ class EmbeddingsDB { return _isar.embeddings.filter().modelEqualTo(model).findAll(); } - Future put(Embedding embedding) { + Future put(Embedding embedding) { return _isar.writeTxn(() async { - await _isar.embeddings.put(embedding); + final id = await _isar.embeddings.put(embedding); Bus.instance.fire(EmbeddingUpdatedEvent()); + return id; }); } diff --git a/lib/services/semantic_search/embedding_store.dart b/lib/services/semantic_search/embedding_store.dart index 143cc59d6..67f4522d7 100644 --- a/lib/services/semantic_search/embedding_store.dart +++ b/lib/services/semantic_search/embedding_store.dart @@ -57,7 +57,7 @@ class EmbeddingStore { } Future storeEmbedding(EnteFile file, Embedding embedding) async { - await EmbeddingsDB.instance.put(embedding); + embedding.id = await EmbeddingsDB.instance.put(embedding); unawaited(_pushEmbedding(file, embedding)); } From 41ae10b4548c2d78f8ffd1a984c687a9408096b1 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 6 Jan 2024 19:31:49 +0530 Subject: [PATCH 091/175] Sync when initialized from the UI --- lib/services/semantic_search/semantic_search_service.dart | 5 ++++- lib/ui/settings/machine_learning_settings_page.dart | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index 06f56d94e..7c2565bff 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -51,7 +51,7 @@ class SemanticSearchService { get hasInitialized => _hasInitialized; - Future init() async { + Future init({bool shouldSyncImmediately = false}) async { if (!LocalSettings.instance.hasEnabledMagicSearch()) { return; } @@ -87,6 +87,9 @@ class SemanticSearchService { Bus.instance.on().listen((event) async { _addToQueue(event.file); }); + if (shouldSyncImmediately) { + unawaited(sync()); + } } Future release() async { diff --git a/lib/ui/settings/machine_learning_settings_page.dart b/lib/ui/settings/machine_learning_settings_page.dart index 3bb38f874..0367cc483 100644 --- a/lib/ui/settings/machine_learning_settings_page.dart +++ b/lib/ui/settings/machine_learning_settings_page.dart @@ -92,7 +92,10 @@ class _MachineLearningSettingsPageState !LocalSettings.instance.hasEnabledMagicSearch(), ); if (LocalSettings.instance.hasEnabledMagicSearch()) { - unawaited(SemanticSearchService.instance.init()); + unawaited( + SemanticSearchService.instance + .init(shouldSyncImmediately: true), + ); } else { await SemanticSearchService.instance.clearQueue(); } From 99575825c09a8ef3d1c06faeb67a18520084a25f Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 6 Jan 2024 20:22:03 +0530 Subject: [PATCH 092/175] Rely on Isar's index to remove duplicates --- lib/db/embeddings_db.dart | 7 +- lib/models/embedding.dart | 5 +- lib/models/embedding.g.dart | 305 +++++++++++++++++- .../semantic_search/embedding_store.dart | 2 +- 4 files changed, 312 insertions(+), 7 deletions(-) diff --git a/lib/db/embeddings_db.dart b/lib/db/embeddings_db.dart index d1a71609e..eeb5b15c6 100644 --- a/lib/db/embeddings_db.dart +++ b/lib/db/embeddings_db.dart @@ -30,17 +30,16 @@ class EmbeddingsDB { return _isar.embeddings.filter().modelEqualTo(model).findAll(); } - Future put(Embedding embedding) { + Future put(Embedding embedding) { return _isar.writeTxn(() async { - final id = await _isar.embeddings.put(embedding); + await _isar.embeddings.putByIndex(Embedding.index, embedding); Bus.instance.fire(EmbeddingUpdatedEvent()); - return id; }); } Future putMany(List embeddings) { return _isar.writeTxn(() async { - await _isar.embeddings.putAll(embeddings); + await _isar.embeddings.putAllByIndex(Embedding.index, embeddings); Bus.instance.fire(EmbeddingUpdatedEvent()); }); } diff --git a/lib/models/embedding.dart b/lib/models/embedding.dart index ddf6c33f8..1f78687b9 100644 --- a/lib/models/embedding.dart +++ b/lib/models/embedding.dart @@ -6,9 +6,12 @@ part 'embedding.g.dart'; @collection class Embedding { - Id id = Isar.autoIncrement; // you can also use id = null to auto increment + static const index = 'unique_file_model_embedding'; + + Id id = Isar.autoIncrement; final int fileID; @enumerated + @Index(name: index, composite: [CompositeIndex('fileID')], unique: true, replace: true) final Model model; final List embedding; int? updationTime; diff --git a/lib/models/embedding.g.dart b/lib/models/embedding.g.dart index 3f8fcfa07..ca041a0d0 100644 --- a/lib/models/embedding.g.dart +++ b/lib/models/embedding.g.dart @@ -44,7 +44,26 @@ const EmbeddingSchema = CollectionSchema( deserialize: _embeddingDeserialize, deserializeProp: _embeddingDeserializeProp, idName: r'id', - indexes: {}, + indexes: { + r'unique_file_model_embedding': IndexSchema( + id: 6248303800853228628, + name: r'unique_file_model_embedding', + unique: true, + replace: true, + properties: [ + IndexPropertySchema( + name: r'model', + type: IndexType.value, + caseSensitive: false, + ), + IndexPropertySchema( + name: r'fileID', + type: IndexType.value, + caseSensitive: false, + ) + ], + ) + }, links: {}, embeddedSchemas: {}, getId: _embeddingGetId, @@ -134,6 +153,95 @@ void _embeddingAttach(IsarCollection col, Id id, Embedding object) { object.id = id; } +extension EmbeddingByIndex on IsarCollection { + Future getByModelFileID(Model model, int fileID) { + return getByIndex(r'unique_file_model_embedding', [model, fileID]); + } + + Embedding? getByModelFileIDSync(Model model, int fileID) { + return getByIndexSync(r'unique_file_model_embedding', [model, fileID]); + } + + Future deleteByModelFileID(Model model, int fileID) { + return deleteByIndex(r'unique_file_model_embedding', [model, fileID]); + } + + bool deleteByModelFileIDSync(Model model, int fileID) { + return deleteByIndexSync(r'unique_file_model_embedding', [model, fileID]); + } + + Future> getAllByModelFileID( + List modelValues, List fileIDValues) { + final len = modelValues.length; + assert(fileIDValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([modelValues[i], fileIDValues[i]]); + } + + return getAllByIndex(r'unique_file_model_embedding', values); + } + + List getAllByModelFileIDSync( + List modelValues, List fileIDValues) { + final len = modelValues.length; + assert(fileIDValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([modelValues[i], fileIDValues[i]]); + } + + return getAllByIndexSync(r'unique_file_model_embedding', values); + } + + Future deleteAllByModelFileID( + List modelValues, List fileIDValues) { + final len = modelValues.length; + assert(fileIDValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([modelValues[i], fileIDValues[i]]); + } + + return deleteAllByIndex(r'unique_file_model_embedding', values); + } + + int deleteAllByModelFileIDSync( + List modelValues, List fileIDValues) { + final len = modelValues.length; + assert(fileIDValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([modelValues[i], fileIDValues[i]]); + } + + return deleteAllByIndexSync(r'unique_file_model_embedding', values); + } + + Future putByModelFileID(Embedding object) { + return putByIndex(r'unique_file_model_embedding', object); + } + + Id putByModelFileIDSync(Embedding object, {bool saveLinks = true}) { + return putByIndexSync(r'unique_file_model_embedding', object, + saveLinks: saveLinks); + } + + Future> putAllByModelFileID(List objects) { + return putAllByIndex(r'unique_file_model_embedding', objects); + } + + List putAllByModelFileIDSync(List objects, + {bool saveLinks = true}) { + return putAllByIndexSync(r'unique_file_model_embedding', objects, + saveLinks: saveLinks); + } +} + extension EmbeddingQueryWhereSort on QueryBuilder { QueryBuilder anyId() { @@ -141,6 +249,14 @@ extension EmbeddingQueryWhereSort return query.addWhereClause(const IdWhereClause.any()); }); } + + QueryBuilder anyModelFileID() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + const IndexWhereClause.any(indexName: r'unique_file_model_embedding'), + ); + }); + } } extension EmbeddingQueryWhere @@ -209,6 +325,193 @@ extension EmbeddingQueryWhere )); }); } + + QueryBuilder modelEqualToAnyFileID( + Model model) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'unique_file_model_embedding', + value: [model], + )); + }); + } + + QueryBuilder + modelNotEqualToAnyFileID(Model model) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [], + upper: [model], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [model], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [model], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [], + upper: [model], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + modelGreaterThanAnyFileID( + Model model, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [model], + includeLower: include, + upper: [], + )); + }); + } + + QueryBuilder modelLessThanAnyFileID( + Model model, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [], + upper: [model], + includeUpper: include, + )); + }); + } + + QueryBuilder modelBetweenAnyFileID( + Model lowerModel, + Model upperModel, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [lowerModel], + includeLower: includeLower, + upper: [upperModel], + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder modelFileIDEqualTo( + Model model, int fileID) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'unique_file_model_embedding', + value: [model, fileID], + )); + }); + } + + QueryBuilder + modelEqualToFileIDNotEqualTo(Model model, int fileID) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [model], + upper: [model, fileID], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [model, fileID], + includeLower: false, + upper: [model], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [model, fileID], + includeLower: false, + upper: [model], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [model], + upper: [model, fileID], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + modelEqualToFileIDGreaterThan( + Model model, + int fileID, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [model, fileID], + includeLower: include, + upper: [model], + )); + }); + } + + QueryBuilder + modelEqualToFileIDLessThan( + Model model, + int fileID, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [model], + upper: [model, fileID], + includeUpper: include, + )); + }); + } + + QueryBuilder + modelEqualToFileIDBetween( + Model model, + int lowerFileID, + int upperFileID, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'unique_file_model_embedding', + lower: [model, lowerFileID], + includeLower: includeLower, + upper: [model, upperFileID], + includeUpper: includeUpper, + )); + }); + } } extension EmbeddingQueryFilter diff --git a/lib/services/semantic_search/embedding_store.dart b/lib/services/semantic_search/embedding_store.dart index 67f4522d7..143cc59d6 100644 --- a/lib/services/semantic_search/embedding_store.dart +++ b/lib/services/semantic_search/embedding_store.dart @@ -57,7 +57,7 @@ class EmbeddingStore { } Future storeEmbedding(EnteFile file, Embedding embedding) async { - embedding.id = await EmbeddingsDB.instance.put(embedding); + await EmbeddingsDB.instance.put(embedding); unawaited(_pushEmbedding(file, embedding)); } From f3bd80d6f01f13e1f9bb4502f2948855df8b9593 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 6 Jan 2024 20:27:42 +0530 Subject: [PATCH 093/175] Provide a way for internal users to clear indexes --- lib/services/semantic_search/embedding_store.dart | 5 +++++ lib/services/semantic_search/semantic_search_service.dart | 2 +- lib/ui/settings/machine_learning_settings_page.dart | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/services/semantic_search/embedding_store.dart b/lib/services/semantic_search/embedding_store.dart index 143cc59d6..de10fec60 100644 --- a/lib/services/semantic_search/embedding_store.dart +++ b/lib/services/semantic_search/embedding_store.dart @@ -61,6 +61,11 @@ class EmbeddingStore { unawaited(_pushEmbedding(file, embedding)); } + Future clearEmbeddings(Model model) async { + await EmbeddingsDB.instance.deleteAllForModel(model); + await _preferences.remove(kEmbeddingsSyncTimeKey); + } + Future _pushEmbedding(EnteFile file, Embedding embedding) async { final encryptionKey = getFileKey(file); final embeddingJSON = jsonEncode(embedding.embedding); diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index 7c2565bff..fed266aef 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -149,7 +149,7 @@ class SemanticSearchService { } Future clearIndexes() async { - await EmbeddingsDB.instance.deleteAllForModel(kCurrentModel); + await EmbeddingStore.instance.clearEmbeddings(kCurrentModel); _logger.info("Indexes cleared for $kCurrentModel"); } diff --git a/lib/ui/settings/machine_learning_settings_page.dart b/lib/ui/settings/machine_learning_settings_page.dart index 0367cc483..c64ae5714 100644 --- a/lib/ui/settings/machine_learning_settings_page.dart +++ b/lib/ui/settings/machine_learning_settings_page.dart @@ -1,11 +1,11 @@ import "dart:async"; -import "package:flutter/foundation.dart"; import "package:flutter/material.dart"; import "package:intl/intl.dart"; import "package:photos/core/event_bus.dart"; import 'package:photos/events/embedding_updated_event.dart'; import "package:photos/generated/l10n.dart"; +import "package:photos/services/feature_flag_service.dart"; import "package:photos/services/semantic_search/semantic_search_service.dart"; import "package:photos/theme/ente_theme.dart"; import "package:photos/ui/common/loading_widget.dart"; @@ -132,7 +132,7 @@ class _MachineLearningSettingsPageState const SizedBox( height: 12, ), - kDebugMode + FeatureFlagService.instance.isInternalUserOrDebugBuild() ? MenuItemWidget( leadingIcon: Icons.delete_sweep_outlined, captionedTextWidget: CaptionedTextWidget( From c60efc6ef927a2f4ebcfef019f78c39b918f30ce Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 6 Jan 2024 20:27:53 +0530 Subject: [PATCH 094/175] v0.8.32 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index f2de3c249..219fb7c85 100644 --- a/pubspec.yaml +++ b/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.8.31+551 +version: 0.8.32+552 environment: sdk: ">=3.0.0 <4.0.0" From 5ffa05aec529f01ff23ffcaec1c424a605402efe Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 8 Jan 2024 20:53:45 +0530 Subject: [PATCH 095/175] Hide hidden items from magic search results --- lib/services/semantic_search/semantic_search_service.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index fed266aef..b233559cf 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -13,6 +13,7 @@ import 'package:photos/events/embedding_updated_event.dart'; import "package:photos/events/file_uploaded_event.dart"; import "package:photos/models/embedding.dart"; import "package:photos/models/file/file.dart"; +import "package:photos/services/collections_service.dart"; import "package:photos/services/semantic_search/embedding_store.dart"; import "package:photos/services/semantic_search/frameworks/ggml.dart"; import "package:photos/services/semantic_search/frameworks/ml_framework.dart"; @@ -215,8 +216,12 @@ class SemanticSearchService { final filesMap = await FilesDB.instance .getFilesFromIDs(queryResults.map((e) => e.id).toList()); final results = []; + + final ignoredCollections = + CollectionsService.instance.getHiddenCollectionIds(); for (final result in queryResults) { - if (filesMap.containsKey(result.id)) { + final file = filesMap[result.id]; + if (file != null && !ignoredCollections.contains(file.collectionID)) { results.add(filesMap[result.id]!); } } From 63cb04d4c1d4d37774205e28f3e5b964bfdd51e3 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 8 Jan 2024 21:10:34 +0530 Subject: [PATCH 096/175] Load cities in a separate isolate --- lib/services/location_service.dart | 31 ++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/services/location_service.dart b/lib/services/location_service.dart index 2da7092e4..b3b6bb4a8 100644 --- a/lib/services/location_service.dart +++ b/lib/services/location_service.dart @@ -1,6 +1,8 @@ import "dart:convert"; +import "dart:io"; import "dart:math"; +import "package:computer/computer.dart"; import "package:logging/logging.dart"; import "package:photos/core/constants.dart"; import "package:photos/core/event_bus.dart"; @@ -17,7 +19,7 @@ import "package:shared_preferences/shared_preferences.dart"; class LocationService { late SharedPreferences prefs; final Logger _logger = Logger((LocationService).toString()); - final List _cities = []; + final Computer _computer = Computer.shared(); LocationService._privateConstructor(); @@ -25,6 +27,8 @@ class LocationService { static const kCitiesRemotePath = "https://assets.ente.io/world_cities.json"; + List _cities = []; + void init(SharedPreferences preferences) { prefs = preferences; if (FeatureFlagService.instance.isInternalUserOrDebugBuild()) { @@ -218,14 +222,15 @@ class LocationService { Future _loadCities() async { try { - final data = + final file = await RemoteAssetsService.instance.getAsset(kCitiesRemotePath); - final citiesJson = json.decode(await data.readAsString()); - final List jsonData = citiesJson['data']; - final cities = - jsonData.map((jsonItem) => City.fromMap(jsonItem)).toList(); - _cities.clear(); - _cities.addAll(cities); + final startTime = DateTime.now(); + _cities = + await _computer.compute(parseCities, param: {"filePath": file.path}); + final endTime = DateTime.now(); + _logger.info( + "Loaded cities in ${(endTime.millisecondsSinceEpoch - startTime.millisecondsSinceEpoch)}ms", + ); _logger.info("Loaded cities"); } catch (e, s) { _logger.severe("Failed to load cities", e, s); @@ -233,6 +238,16 @@ class LocationService { } } +Future> parseCities(Map args) async { + final file = File(args["filePath"]); + final citiesJson = json.decode(await file.readAsString()); + + final List jsonData = citiesJson['data']; + final cities = + jsonData.map((jsonItem) => City.fromMap(jsonItem)).toList(); + return cities; +} + class City { final String city; final String country; From 0645ff89f582b9569cd34b38f044692a1b9ae837 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 8 Jan 2024 21:29:21 +0530 Subject: [PATCH 097/175] Move city search to a separate isolate --- lib/services/location_service.dart | 95 ++++++++++++++----- lib/services/search_service.dart | 42 +------- .../dynamic_location_gallery_widget.dart | 2 +- lib/ui/viewer/location/location_screen.dart | 2 +- 4 files changed, 76 insertions(+), 65 deletions(-) diff --git a/lib/services/location_service.dart b/lib/services/location_service.dart index b3b6bb4a8..1f35000b9 100644 --- a/lib/services/location_service.dart +++ b/lib/services/location_service.dart @@ -8,6 +8,7 @@ import "package:photos/core/constants.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/events/location_tag_updated_event.dart"; import "package:photos/models/api/entity/type.dart"; +import "package:photos/models/file/file.dart"; import "package:photos/models/local_entity_data.dart"; import "package:photos/models/location/location.dart"; import 'package:photos/models/location_tag/location_tag.dart'; @@ -43,8 +44,19 @@ class LocationService { ); } - List getAllCities() { - return _cities; + Future>> getFilesInCity( + List allFiles, + String query, + ) async { + final result = await _computer.compute( + getCityResults, + param: { + "query": query, + "cities": _cities, + "files": allFiles, + }, + ); + return result; } Future>> getLocationTags() { @@ -81,14 +93,6 @@ class LocationService { } } - ///The area bounded by the location tag becomes more elliptical with increase - ///in the magnitude of the latitude on the caritesian plane. When latitude is - ///0 degrees, the ellipse is a circle with a = b = r. When latitude incrases, - ///the major axis (a) has to be scaled by the secant of the latitude. - double _scaleFactor(double lat) { - return 1 / cos(lat * (pi / 180)); - } - Future>> enclosingLocationTags( Location fileCoordinates, ) async { @@ -114,22 +118,6 @@ class LocationService { } } - bool isFileInsideLocationTag( - Location centerPoint, - Location fileCoordinates, - double radius, - ) { - final a = - (radius * _scaleFactor(centerPoint.latitude!)) / kilometersPerDegree; - final b = radius / kilometersPerDegree; - final x = centerPoint.latitude! - fileCoordinates.latitude!; - final y = centerPoint.longitude! - fileCoordinates.longitude!; - if ((x * x) / (a * a) + (y * y) / (b * b) <= 1) { - return true; - } - return false; - } - /// returns [lat, lng] List? convertLocationToDMS(Location centerPoint) { if (centerPoint.latitude == null || centerPoint.longitude == null) { @@ -248,6 +236,61 @@ Future> parseCities(Map args) async { return cities; } +Map> getCityResults(Map args) { + final query = (args["query"] as String).toLowerCase(); + final cities = args["cities"] as List; + final files = args["files"] as List; + + final matchingCities = cities.where( + (city) => city.city.toLowerCase().contains(query), + ); + + final Map> results = {}; + for (final city in matchingCities) { + final List matchingFiles = []; + final cityLocation = Location(latitude: city.lat, longitude: city.lng); + for (final file in files) { + if (file.hasLocation) { + if (isFileInsideLocationTag( + cityLocation, + file.location!, + defaultCityRadius, + )) { + matchingFiles.add(file); + } + } + } + if (matchingFiles.isNotEmpty) { + results[city] = matchingFiles; + } + } + return results; +} + +bool isFileInsideLocationTag( + Location centerPoint, + Location fileCoordinates, + double radius, +) { + final a = + (radius * _scaleFactor(centerPoint.latitude!)) / kilometersPerDegree; + final b = radius / kilometersPerDegree; + final x = centerPoint.latitude! - fileCoordinates.latitude!; + final y = centerPoint.longitude! - fileCoordinates.longitude!; + if ((x * x) / (a * a) + (y * y) / (b * b) <= 1) { + return true; + } + return false; +} + +///The area bounded by the location tag becomes more elliptical with increase +///in the magnitude of the latitude on the caritesian plane. When latitude is +///0 degrees, the ellipse is a circle with a = b = r. When latitude incrases, +///the major axis (a) has to be scaled by the secant of the latitude. +double _scaleFactor(double lat) { + return 1 / cos(lat * (pi / 180)); +} + class City { final String city; final String country; diff --git a/lib/services/search_service.dart b/lib/services/search_service.dart index 7b831480a..97fb16dbe 100644 --- a/lib/services/search_service.dart +++ b/lib/services/search_service.dart @@ -3,7 +3,6 @@ import "dart:math"; import "package:flutter/cupertino.dart"; import "package:intl/intl.dart"; import 'package:logging/logging.dart'; -import "package:photos/core/constants.dart"; import 'package:photos/core/event_bus.dart'; import 'package:photos/data/holidays.dart'; import 'package:photos/data/months.dart'; @@ -18,7 +17,6 @@ import "package:photos/models/file/extensions/file_props.dart"; import 'package:photos/models/file/file.dart'; import 'package:photos/models/file/file_type.dart'; import "package:photos/models/local_entity_data.dart"; -import "package:photos/models/location/location.dart"; import "package:photos/models/location_tag/location_tag.dart"; import 'package:photos/models/search/album_search_result.dart'; import 'package:photos/models/search/generic_search_result.dart'; @@ -620,7 +618,7 @@ class SearchService { for (EnteFile file in allFiles) { if (file.hasLocation) { for (LocalEntity tag in result.keys) { - if (LocationService.instance.isFileInsideLocationTag( + if (isFileInsideLocationTag( tag.item.centerPoint, file.location!, tag.item.radius, @@ -639,7 +637,7 @@ class SearchService { return false; } for (LocalEntity tag in locationTagEntities) { - if (LocationService.instance.isFileInsideLocationTag( + if (isFileInsideLocationTag( tag.item.centerPoint, file.location!, tag.item.radius, @@ -684,36 +682,9 @@ class SearchService { } Future> getCityResults(String query) async { - final startTime = DateTime.now().microsecondsSinceEpoch; - final List searchResults = []; - final cities = LocationService.instance.getAllCities(); - final matchingCities = []; - final queryLower = query.toLowerCase(); - for (City city in cities) { - if (city.city.toLowerCase().startsWith(queryLower)) { - matchingCities.add(city); - } - } final files = await getAllFiles(); - final Map> results = {}; - for (final city in matchingCities) { - final List matchingFiles = []; - final cityLocation = Location(latitude: city.lat, longitude: city.lng); - for (final file in files) { - if (file.hasLocation) { - if (LocationService.instance.isFileInsideLocationTag( - cityLocation, - file.location!, - defaultCityRadius, - )) { - matchingFiles.add(file); - } - } - } - if (matchingFiles.isNotEmpty) { - results[city] = matchingFiles; - } - } + final results = await LocationService.instance.getFilesInCity(files, query); + final List searchResults = []; for (final entry in results.entries) { searchResults.add( GenericSearchResult( @@ -723,9 +694,6 @@ class SearchService { ), ); } - final endTime = DateTime.now().microsecondsSinceEpoch; - _logger - .info("Time taken " + ((endTime - startTime) / 1000).toString() + "ms"); return searchResults; } @@ -745,7 +713,7 @@ class SearchService { for (EnteFile file in allFiles) { if (file.hasLocation) { for (LocalEntity tag in tagToItemsMap.keys) { - if (LocationService.instance.isFileInsideLocationTag( + if (isFileInsideLocationTag( tag.item.centerPoint, file.location!, tag.item.radius, diff --git a/lib/ui/viewer/location/dynamic_location_gallery_widget.dart b/lib/ui/viewer/location/dynamic_location_gallery_widget.dart index 3103fa143..04dbcd6a6 100644 --- a/lib/ui/viewer/location/dynamic_location_gallery_widget.dart +++ b/lib/ui/viewer/location/dynamic_location_gallery_widget.dart @@ -62,7 +62,7 @@ class _DynamicLocationGalleryWidgetState final stopWatch = Stopwatch()..start(); final copyOfFiles = List.from(result.files); copyOfFiles.removeWhere((f) { - return !LocationService.instance.isFileInsideLocationTag( + return !isFileInsideLocationTag( InheritedLocationTagData.of(context).centerPoint, f.location!, selectedRadius, diff --git a/lib/ui/viewer/location/location_screen.dart b/lib/ui/viewer/location/location_screen.dart index cdfa7f92b..374d70cb9 100644 --- a/lib/ui/viewer/location/location_screen.dart +++ b/lib/ui/viewer/location/location_screen.dart @@ -205,7 +205,7 @@ class _LocationGalleryWidgetState extends State { final stopWatch = Stopwatch()..start(); final filesInLocation = allFilesWithLocation; filesInLocation.removeWhere((f) { - return !LocationService.instance.isFileInsideLocationTag( + return !isFileInsideLocationTag( centerPoint, f.location!, selectedRadius, From 20c0d455df2575d467f9cf53c468368d9b17613d Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 8 Jan 2024 22:39:22 +0530 Subject: [PATCH 098/175] v0.8.33 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 219fb7c85..d38ab111e 100644 --- a/pubspec.yaml +++ b/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.8.32+552 +version: 0.8.33+553 environment: sdk: ">=3.0.0 <4.0.0" From 6876e0e85e8564636b5043378719f2be16dad777 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Tue, 9 Jan 2024 00:07:06 +0530 Subject: [PATCH 099/175] Execute a single query to fetch files for fetched embeddings --- lib/services/semantic_search/embedding_store.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/services/semantic_search/embedding_store.dart b/lib/services/semantic_search/embedding_store.dart index de10fec60..6aedf8547 100644 --- a/lib/services/semantic_search/embedding_store.dart +++ b/lib/services/semantic_search/embedding_store.dart @@ -133,12 +133,12 @@ class EmbeddingStore { return; } final inputs = []; + final fileMap = await FilesDB.instance + .getFilesFromIDs(remoteEmbeddings.map((e) => e.fileID).toList()); + for (final embedding in remoteEmbeddings) { - final file = await FilesDB.instance.getAnyUploadedFile(embedding.fileID); - if (file == null) { - continue; - } - final fileKey = getFileKey(file); + final file = fileMap[embedding.fileID]; + final fileKey = getFileKey(file!); final input = EmbeddingsDecoderInput(embedding, fileKey); inputs.add(input); } From 10ef318bbbffd478b3dd06a86209dd7afec4ea35 Mon Sep 17 00:00:00 2001 From: Jaden <97551221+jadenet@users.noreply.github.com> Date: Mon, 8 Jan 2024 14:13:32 -0500 Subject: [PATCH 100/175] Fix typo --- lib/l10n/intl_en.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 20571d2e4..873593fe0 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -497,7 +497,7 @@ }, "remindToEmptyEnteTrash": "Also empty your \"Trash\" to claim the freed up space", "sparkleSuccess": "✨ Success", - "duplicateFileCountWithStorageSaved": "Your have cleaned up {count, plural, one{{count} duplicate file} other{{count} duplicate files}}, saving ({storageSaved}!)", + "duplicateFileCountWithStorageSaved": "You have cleaned up {count, plural, one{{count} duplicate file} other{{count} duplicate files}}, saving ({storageSaved}!)", "@duplicateFileCountWithStorageSaved": { "description": "The text to display when the user has successfully cleaned up duplicate files", "type": "text", From e21b862f53a0d3398f8f7c329e315afe1be776d6 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 9 Jan 2024 15:32:43 +0530 Subject: [PATCH 101/175] MinorPerf: Avoid repeated sqrt computation --- .../frameworks/onnx/onnx_image_encoder.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart b/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart index 9391616e9..5325d8e66 100644 --- a/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart +++ b/lib/services/semantic_search/frameworks/onnx/onnx_image_encoder.dart @@ -43,9 +43,10 @@ class OnnxImageEncoder { for (int y = 0; y < ny; y++) { for (int x = 0; x < nx; x++) { final int i = 3 * (y * nx + x); - inputImage[i] = rgb.getPixel(x, y).r.toDouble(); - inputImage[i + 1] = rgb.getPixel(x, y).g.toDouble(); - inputImage[i + 2] = rgb.getPixel(x, y).b.toDouble(); + final pixel = rgb.getPixel(x, y); + inputImage[i] = pixel.r.toDouble(); + inputImage[i + 1] = pixel.g.toDouble(); + inputImage[i + 2] = pixel.b.toDouble(); } } @@ -111,8 +112,9 @@ class OnnxImageEncoder { for (int i = 0; i < 512; i++) { imageNormalization += embedding[i] * embedding[i]; } + final double sqrtImageNormalization = sqrt(imageNormalization); for (int i = 0; i < 512; i++) { - embedding[i] = embedding[i] / sqrt(imageNormalization); + embedding[i] = embedding[i] / sqrtImageNormalization; } return embedding; } From d40d28cfa27bbde39d315f03326155dbbc708ac7 Mon Sep 17 00:00:00 2001 From: 0nullpointer Date: Tue, 9 Jan 2024 16:18:00 +0530 Subject: [PATCH 102/175] avoid sqrt computation for text encoder --- .../semantic_search/frameworks/onnx/onnx_text_encoder.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart b/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart index a27bdb53a..5c9927085 100644 --- a/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart +++ b/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart @@ -51,8 +51,9 @@ class OnnxTextEncoder { textNormalization += embedding[i] * embedding[i]; } + final double sqrtTexteNormalization = sqrt(textNormalization); for (int i = 0; i < 512; i++) { - embedding[i] = embedding[i] / sqrt(textNormalization); + embedding[i] = embedding[i] / sqrtTexteNormalization; } return (embedding); From ff0eaefd9bacc053e7d68f0a6d4bd3faae4ec7ec Mon Sep 17 00:00:00 2001 From: 0nullpointer Date: Tue, 9 Jan 2024 16:19:30 +0530 Subject: [PATCH 103/175] Revert "avoid sqrt computation for text encoder" This reverts commit d40d28cfa27bbde39d315f03326155dbbc708ac7. --- .../semantic_search/frameworks/onnx/onnx_text_encoder.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart b/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart index 5c9927085..a27bdb53a 100644 --- a/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart +++ b/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart @@ -51,9 +51,8 @@ class OnnxTextEncoder { textNormalization += embedding[i] * embedding[i]; } - final double sqrtTexteNormalization = sqrt(textNormalization); for (int i = 0; i < 512; i++) { - embedding[i] = embedding[i] / sqrtTexteNormalization; + embedding[i] = embedding[i] / sqrt(textNormalization); } return (embedding); From 8186b38bb64eed8838419153f8f137a92c76e61a Mon Sep 17 00:00:00 2001 From: 0nullpointer Date: Tue, 9 Jan 2024 16:22:28 +0530 Subject: [PATCH 104/175] Avoid multiple sqrt computation for text encoder --- .../semantic_search/frameworks/onnx/onnx_text_encoder.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart b/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart index a27bdb53a..664b925e7 100644 --- a/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart +++ b/lib/services/semantic_search/frameworks/onnx/onnx_text_encoder.dart @@ -50,9 +50,10 @@ class OnnxTextEncoder { for (int i = 0; i < 512; i++) { textNormalization += embedding[i] * embedding[i]; } - + + final double sqrtTextNormalization = sqrt(textNormalization); for (int i = 0; i < 512; i++) { - embedding[i] = embedding[i] / sqrt(textNormalization); + embedding[i] = embedding[i] / sqrtTextNormalization; } return (embedding); From 9f5ef640f8df7219306d71cd64efe60379449c67 Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Tue, 9 Jan 2024 12:04:25 +0000 Subject: [PATCH 105/175] New Crowdin translations by GitHub Action --- lib/l10n/intl_zh.arb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index a53329ebe..72c9e991d 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -409,6 +409,7 @@ "machineLearning": "机器学习", "magicSearch": "魔法搜索", "magicSearchDescription": "请使用我们的桌面应用程序来为您库中的待处理项目建立索引。", + "loadingModel": "正在下载模型...", "status": "状态", "indexedItems": "已索引项目", "pendingItems": "待处理项目", From 9dad483bfcb1cf0faf3c0edc2326516f3dbab6b5 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Wed, 10 Jan 2024 18:09:42 +0530 Subject: [PATCH 106/175] create a working model of memories header in hometab where each memory dynamically changes size wrt scroll offset --- lib/ui/home/memories/memories_widget.dart | 57 ++++++++++++++++++++--- pubspec.lock | 8 ++++ pubspec.yaml | 1 + 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 72f6add5b..12f2a6129 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -1,6 +1,8 @@ import "dart:async"; import 'package:flutter/material.dart'; +import "package:flutter/rendering.dart"; +import "package:infinite_carousel/infinite_carousel.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; import 'package:photos/models/memory.dart'; @@ -15,6 +17,8 @@ class MemoriesWidget extends StatefulWidget { } class _MemoriesWidgetState extends State { + final double _itemExtent = 120; + late InfiniteScrollController _controller; late StreamSubscription _subscription; @override @@ -25,11 +29,13 @@ class _MemoriesWidgetState extends State { setState(() {}); } }); + _controller = InfiniteScrollController(); } @override void dispose() { _subscription.cancel(); + _controller.dispose(); super.dispose(); } @@ -60,14 +66,51 @@ class _MemoriesWidgetState extends State { final collatedMemories = _collateMemories(memories); final List memoryWidgets = []; for (final memories in collatedMemories) { - memoryWidgets.add(MemoryCovertWidget(memories: memories)); + memoryWidgets.add( + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + color: Colors.yellow, + child: MemoryCovertWidget(memories: memories), + ), + ), + ); } - return SingleChildScrollView( - scrollDirection: Axis.horizontal, - physics: const BouncingScrollPhysics(), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: memoryWidgets, + return SizedBox( + height: 150, + child: InfiniteCarousel.builder( + controller: _controller, + itemCount: memoryWidgets.length, + itemExtent: _itemExtent, + itemBuilder: (context, itemIndex, realIndex) { + final currentOffset = _itemExtent * realIndex; + return AnimatedBuilder( + animation: _controller, + builder: (context, child) { + final diff = (_controller.offset - currentOffset); + const maxPadding = 10.0; + final carouselRatio = _itemExtent / maxPadding; + + return Padding( + padding: EdgeInsets.only( + top: (diff / carouselRatio).abs(), + bottom: (diff / carouselRatio).abs(), + ), + child: child, + ); + }, + child: Padding( + padding: const EdgeInsets.all(2.0), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + boxShadow: kElevationToShadow[2], + ), + child: memoryWidgets[itemIndex], + ), + ), + ); + }, ), ); } diff --git a/pubspec.lock b/pubspec.lock index 5129dcf80..1cb6a548d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1026,6 +1026,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.6+4" + infinite_carousel: + dependency: "direct main" + description: + name: infinite_carousel + sha256: fe04c3b08adad2ee00c9bf40b46e0ff7944d206081392c4ae0f6b82c89c6e70d + url: "https://pub.dev" + source: hosted + version: "1.0.3" integration_test: dependency: "direct dev" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index d38ab111e..5112dbf09 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -93,6 +93,7 @@ dependencies: image: ^4.0.17 image_editor: ^1.3.0 in_app_purchase: ^3.0.7 + infinite_carousel: ^1.0.3 intl: ^0.18.0 isar: ^3.1.0+1 isar_flutter_libs: ^3.1.0+1 From 7383dbe0c18d53bc7b561ddfaf711655e9d8c0e4 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 11 Jan 2024 06:56:37 +0530 Subject: [PATCH 107/175] Added widgets for showing image and text on memory that works with the dynamic resizing --- lib/ui/home/memories/memories_widget.dart | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 12f2a6129..4c36f6f3c 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -79,6 +79,7 @@ class _MemoriesWidgetState extends State { return SizedBox( height: 150, child: InfiniteCarousel.builder( + loop: false, controller: _controller, itemCount: memoryWidgets.length, itemExtent: _itemExtent, @@ -101,12 +102,19 @@ class _MemoriesWidgetState extends State { }, child: Padding( padding: const EdgeInsets.all(2.0), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - boxShadow: kElevationToShadow[2], - ), - child: memoryWidgets[itemIndex], + child: Stack( + fit: StackFit.expand, + alignment: Alignment.bottomCenter, + children: [ + Image.asset( + "assets/onboarding_safe.png", + fit: BoxFit.cover, + ), + const Positioned( + bottom: 8, + child: Text("1 year ago"), + ), + ], ), ), ); From 463523e09ed1ba50c5b0ac1896ad0b0395bf7240 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 11 Jan 2024 07:01:01 +0530 Subject: [PATCH 108/175] Pass width constrain to title text in memory to that it breaks to next line --- lib/ui/home/memories/memories_widget.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 4c36f6f3c..aee82b546 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -110,9 +110,14 @@ class _MemoriesWidgetState extends State { "assets/onboarding_safe.png", fit: BoxFit.cover, ), - const Positioned( + Positioned( bottom: 8, - child: Text("1 year ago"), + child: SizedBox( + width: _itemExtent - 16, + child: const Text( + "1 year ago", + ), + ), ), ], ), From 845e58d1ba2172f675865de53890ad9f74cd538d Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 11 Jan 2024 10:56:12 +0530 Subject: [PATCH 109/175] use custom implementation for memories and remove use of infinite_carousel --- lib/ui/home/memories/memories_widget.dart | 76 ++++++++++++----------- pubspec.lock | 8 --- pubspec.yaml | 1 - 3 files changed, 41 insertions(+), 44 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index aee82b546..b7806f8ab 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -1,8 +1,8 @@ import "dart:async"; +import "dart:math"; import 'package:flutter/material.dart'; import "package:flutter/rendering.dart"; -import "package:infinite_carousel/infinite_carousel.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; import 'package:photos/models/memory.dart'; @@ -17,10 +17,15 @@ class MemoriesWidget extends StatefulWidget { } class _MemoriesWidgetState extends State { - final double _itemExtent = 120; - late InfiniteScrollController _controller; + final double _widthOfItem = 85; + late ScrollController _controller; late StreamSubscription _subscription; + final _assetPaths = [ + "assets/onboarding_safe.png", + "assets/gallery_locked.png", + ]; + @override void initState() { super.initState(); @@ -29,7 +34,7 @@ class _MemoriesWidgetState extends State { setState(() {}); } }); - _controller = InfiniteScrollController(); + _controller = ScrollController(); } @override @@ -63,6 +68,7 @@ class _MemoriesWidgetState extends State { } Widget _buildMemories(List memories) { + final widthOfScreen = MediaQuery.sizeOf(context).width; final collatedMemories = _collateMemories(memories); final List memoryWidgets = []; for (final memories in collatedMemories) { @@ -78,48 +84,47 @@ class _MemoriesWidgetState extends State { } return SizedBox( height: 150, - child: InfiniteCarousel.builder( - loop: false, + child: ListView.builder( + scrollDirection: Axis.horizontal, controller: _controller, itemCount: memoryWidgets.length, - itemExtent: _itemExtent, - itemBuilder: (context, itemIndex, realIndex) { - final currentOffset = _itemExtent * realIndex; + itemBuilder: (context, itemIndex) { + final offsetOfItem = _widthOfItem * itemIndex; return AnimatedBuilder( animation: _controller, builder: (context, child) { - final diff = (_controller.offset - currentOffset); - const maxPadding = 10.0; - final carouselRatio = _itemExtent / maxPadding; + final diff = + (_controller.offset - offsetOfItem) + widthOfScreen / 7; - return Padding( - padding: EdgeInsets.only( - top: (diff / carouselRatio).abs(), - bottom: (diff / carouselRatio).abs(), - ), + return Transform.scale( + scale: 1 - (diff / widthOfScreen).abs() / 3, child: child, ); }, - child: Padding( - padding: const EdgeInsets.all(2.0), - child: Stack( - fit: StackFit.expand, - alignment: Alignment.bottomCenter, - children: [ - Image.asset( - "assets/onboarding_safe.png", - fit: BoxFit.cover, - ), - Positioned( - bottom: 8, - child: SizedBox( - width: _itemExtent - 16, - child: const Text( - "1 year ago", + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: SizedBox( + width: 85, + height: 125, + child: Stack( + fit: StackFit.expand, + alignment: Alignment.bottomCenter, + children: [ + Image.asset( + _assetPaths[Random().nextInt(_assetPaths.length)], + fit: BoxFit.cover, + ), + Positioned( + bottom: 8, + child: SizedBox( + width: _widthOfItem - 16, + child: const Text( + "1 year ago", + ), ), ), - ), - ], + ], + ), ), ), ); @@ -137,6 +142,7 @@ class _MemoriesWidgetState extends State { final List collatedYearlyMemories = []; collatedYearlyMemories.addAll(yearlyMemories); collatedMemories.add(collatedYearlyMemories); + yearlyMemories.clear(); } yearlyMemories.add(memories[index]); diff --git a/pubspec.lock b/pubspec.lock index 1cb6a548d..5129dcf80 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1026,14 +1026,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.6+4" - infinite_carousel: - dependency: "direct main" - description: - name: infinite_carousel - sha256: fe04c3b08adad2ee00c9bf40b46e0ff7944d206081392c4ae0f6b82c89c6e70d - url: "https://pub.dev" - source: hosted - version: "1.0.3" integration_test: dependency: "direct dev" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 5112dbf09..d38ab111e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -93,7 +93,6 @@ dependencies: image: ^4.0.17 image_editor: ^1.3.0 in_app_purchase: ^3.0.7 - infinite_carousel: ^1.0.3 intl: ^0.18.0 isar: ^3.1.0+1 isar_flutter_libs: ^3.1.0+1 From a66af2f1836257afa8186cb3612254bc4865e8c8 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 11 Jan 2024 13:19:02 +0530 Subject: [PATCH 110/175] chage the size of memory cover widget rather than transforming it to avoid incresing gaps in listView when memory cover widgets get smaller --- lib/ui/home/memories/memories_widget.dart | 64 +++++++++++++---------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index b7806f8ab..0ff7abeb5 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -1,5 +1,4 @@ import "dart:async"; -import "dart:math"; import 'package:flutter/material.dart'; import "package:flutter/rendering.dart"; @@ -7,7 +6,9 @@ import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; import 'package:photos/models/memory.dart'; import 'package:photos/services/memories_service.dart'; +import "package:photos/theme/ente_theme.dart"; import "package:photos/ui/home/memories/memory_cover_widget.dart"; +import "package:photos/ui/viewer/file/thumbnail_widget.dart"; class MemoriesWidget extends StatefulWidget { const MemoriesWidget({Key? key}) : super(key: key); @@ -83,7 +84,7 @@ class _MemoriesWidgetState extends State { ); } return SizedBox( - height: 150, + height: 125, child: ListView.builder( scrollDirection: Axis.horizontal, controller: _controller, @@ -95,37 +96,41 @@ class _MemoriesWidgetState extends State { builder: (context, child) { final diff = (_controller.offset - offsetOfItem) + widthOfScreen / 7; + final scale = 1 - (diff / widthOfScreen).abs() / 3; - return Transform.scale( - scale: 1 - (diff / widthOfScreen).abs() / 3, - child: child, - ); - }, - child: ClipRRect( - borderRadius: BorderRadius.circular(5), - child: SizedBox( - width: 85, - height: 125, - child: Stack( - fit: StackFit.expand, - alignment: Alignment.bottomCenter, - children: [ - Image.asset( - _assetPaths[Random().nextInt(_assetPaths.length)], - fit: BoxFit.cover, - ), - Positioned( - bottom: 8, - child: SizedBox( - width: _widthOfItem - 16, - child: const Text( - "1 year ago", + return SizedBox( + height: 125 * scale, + width: 85 * scale, + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: Stack( + fit: StackFit.expand, + alignment: Alignment.bottomCenter, + children: [ + child!, + Positioned( + bottom: 8, + child: SizedBox( + width: 85 * scale, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8.0), + child: Text( + "1 year ago", + style: getEnteTextTheme(context).miniBold, + textScaleFactor: 1 * scale, + ), + ), ), ), - ), - ], + ], + ), ), - ), + ); + }, + child: ThumbnailWidget( + memories[0].file, + shouldShowArchiveStatus: false, ), ); }, @@ -142,6 +147,7 @@ class _MemoriesWidgetState extends State { final List collatedYearlyMemories = []; collatedYearlyMemories.addAll(yearlyMemories); collatedMemories.add(collatedYearlyMemories); + collatedMemories.add(collatedYearlyMemories); yearlyMemories.clear(); } From 7492f89dfcf7405fd89091e32b7cd47517a00497 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 11 Jan 2024 13:34:05 +0530 Subject: [PATCH 111/175] fix: memory cover widget occuping hight contrain passed by parent, wrapping with row widget is a workaround --- lib/ui/home/memories/memories_widget.dart | 56 +++++++++++++---------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 0ff7abeb5..1b08c3d9f 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -97,35 +97,41 @@ class _MemoriesWidgetState extends State { final diff = (_controller.offset - offsetOfItem) + widthOfScreen / 7; final scale = 1 - (diff / widthOfScreen).abs() / 3; - - return SizedBox( - height: 125 * scale, - width: 85 * scale, - child: ClipRRect( - borderRadius: BorderRadius.circular(5), - child: Stack( - fit: StackFit.expand, - alignment: Alignment.bottomCenter, - children: [ - child!, - Positioned( - bottom: 8, - child: SizedBox( - width: 85 * scale, - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8.0), - child: Text( - "1 year ago", - style: getEnteTextTheme(context).miniBold, - textScaleFactor: 1 * scale, + //Adding this row is a workaround for making height of memory cover + //render as 125 * scale. Without this, height of rendered memory + //cover will be 125 + return Row( + children: [ + SizedBox( + height: 125 * scale, + width: 85 * scale, + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: Stack( + fit: StackFit.expand, + alignment: Alignment.bottomCenter, + children: [ + child!, + Positioned( + bottom: 8, + child: SizedBox( + width: 85 * scale, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 8.0), + child: Text( + "1 year ago", + style: getEnteTextTheme(context).miniBold, + textScaleFactor: 1 * scale, + ), + ), ), ), - ), + ], ), - ], + ), ), - ), + ], ); }, child: ThumbnailWidget( From 0b1bb7acc5ecc4bf4286f30de866b8b92385de40 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 11 Jan 2024 13:55:42 +0530 Subject: [PATCH 112/175] Add states for ML Framework --- .../frameworks/ml_framework.dart | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/services/semantic_search/frameworks/ml_framework.dart b/lib/services/semantic_search/frameworks/ml_framework.dart index 2bf415881..7e71e4917 100644 --- a/lib/services/semantic_search/frameworks/ml_framework.dart +++ b/lib/services/semantic_search/frameworks/ml_framework.dart @@ -4,14 +4,26 @@ import "package:flutter/services.dart"; import "package:logging/logging.dart"; import "package:path/path.dart"; import "package:path_provider/path_provider.dart"; +import "package:photos/core/event_bus.dart"; import "package:photos/core/network/network.dart"; +import "package:photos/events/event.dart"; abstract class MLFramework { static const kImageEncoderEnabled = true; static const kMaximumRetrials = 3; + InitializationState _state = InitializationState.notInitialized; + final _logger = Logger("MLFramework"); + InitializationState get initializationState => _state; + + set _initState(InitializationState state) { + Bus.instance.fire(MLFrameworkInitializationEvent(state)); + _logger.info("Init state is $state"); + _state = state; + } + /// Returns the path of the Image Model hosted remotely String getImageModelRemotePath(); @@ -37,6 +49,7 @@ abstract class MLFramework { Future init() async { await _initImageModel(); await _initTextModel(); + _initState = InitializationState.initialized; } // Releases any resources held by the framework @@ -65,8 +78,10 @@ abstract class MLFramework { } final path = await _getLocalImageModelPath(); if (File(path).existsSync()) { + _initState = InitializationState.initializingImageModel; await loadImageModel(path); } else { + _initState = InitializationState.downloadingImageModel; final tempFile = File(path + ".temp"); await _downloadFile(getImageModelRemotePath(), tempFile.path); await tempFile.rename(path); @@ -77,8 +92,10 @@ abstract class MLFramework { Future _initTextModel() async { final path = await _getLocalTextModelPath(); if (File(path).existsSync()) { + _initState = InitializationState.initializingTextModel; await loadTextModel(path); } else { + _initState = InitializationState.downloadingTextModel; final tempFile = File(path + ".temp"); await _downloadFile(getTextModelRemotePath(), tempFile.path); await tempFile.rename(path); @@ -131,3 +148,19 @@ abstract class MLFramework { return file.path; } } + +class MLFrameworkInitializationEvent extends Event { + final InitializationState state; + + MLFrameworkInitializationEvent(this.state); +} + +enum InitializationState { + notInitialized, + waitingForNetwork, + downloadingImageModel, + downloadingTextModel, + initializingImageModel, + initializingTextModel, + initialized, +} From 860b5d74d8dd787f77b846eea10da5146524386f Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 11 Jan 2024 13:59:17 +0530 Subject: [PATCH 113/175] Parallelize model initialization --- lib/services/semantic_search/frameworks/ml_framework.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/services/semantic_search/frameworks/ml_framework.dart b/lib/services/semantic_search/frameworks/ml_framework.dart index 7e71e4917..5524f63ca 100644 --- a/lib/services/semantic_search/frameworks/ml_framework.dart +++ b/lib/services/semantic_search/frameworks/ml_framework.dart @@ -47,8 +47,7 @@ abstract class MLFramework { /// initialization. For eg. if you wish to load the model from `/assets` /// instead of a CDN. Future init() async { - await _initImageModel(); - await _initTextModel(); + await Future.wait([_initImageModel(), _initTextModel()]); _initState = InitializationState.initialized; } From 32008842c05241730852a7be0498664a697dc11b Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 11 Jan 2024 15:12:25 +0530 Subject: [PATCH 114/175] create a separate widget for the new MemoryCoverWidget --- lib/ui/home/memories/memories_widget.dart | 56 +++++-------------- .../memories/memory_cover_widget_new.dart | 52 +++++++++++++++++ 2 files changed, 65 insertions(+), 43 deletions(-) create mode 100644 lib/ui/home/memories/memory_cover_widget_new.dart diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 1b08c3d9f..69be67352 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -1,13 +1,12 @@ import "dart:async"; import 'package:flutter/material.dart'; -import "package:flutter/rendering.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; import 'package:photos/models/memory.dart'; import 'package:photos/services/memories_service.dart'; -import "package:photos/theme/ente_theme.dart"; import "package:photos/ui/home/memories/memory_cover_widget.dart"; +import "package:photos/ui/home/memories/memory_cover_widget_new.dart"; import "package:photos/ui/viewer/file/thumbnail_widget.dart"; class MemoriesWidget extends StatefulWidget { @@ -22,11 +21,6 @@ class _MemoriesWidgetState extends State { late ScrollController _controller; late StreamSubscription _subscription; - final _assetPaths = [ - "assets/onboarding_safe.png", - "assets/gallery_locked.png", - ]; - @override void initState() { super.initState(); @@ -76,10 +70,7 @@ class _MemoriesWidgetState extends State { memoryWidgets.add( Padding( padding: const EdgeInsets.all(8.0), - child: Container( - color: Colors.yellow, - child: MemoryCovertWidget(memories: memories), - ), + child: MemoryCovertWidget(memories: memories), ), ); } @@ -99,39 +90,18 @@ class _MemoriesWidgetState extends State { final scale = 1 - (diff / widthOfScreen).abs() / 3; //Adding this row is a workaround for making height of memory cover //render as 125 * scale. Without this, height of rendered memory - //cover will be 125 - return Row( - children: [ - SizedBox( - height: 125 * scale, - width: 85 * scale, - child: ClipRRect( - borderRadius: BorderRadius.circular(5), - child: Stack( - fit: StackFit.expand, - alignment: Alignment.bottomCenter, - children: [ - child!, - Positioned( - bottom: 8, - child: SizedBox( - width: 85 * scale, - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8.0), - child: Text( - "1 year ago", - style: getEnteTextTheme(context).miniBold, - textScaleFactor: 1 * scale, - ), - ), - ), - ), - ], - ), + //cover will be 125. + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 2.5), + child: Row( + children: [ + MemoryCoverWidgetNew( + memories: memories, + thumbnailWidget: child!, + scale: scale, ), - ), - ], + ], + ), ); }, child: ThumbnailWidget( diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart new file mode 100644 index 000000000..768398af4 --- /dev/null +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -0,0 +1,52 @@ +import "package:flutter/material.dart"; +import "package:photos/models/memory.dart"; +import "package:photos/theme/ente_theme.dart"; + +class MemoryCoverWidgetNew extends StatelessWidget { + final double scale; + final Widget thumbnailWidget; + final List memories; + + const MemoryCoverWidgetNew({ + required this.memories, + required this.thumbnailWidget, + required this.scale, + super.key, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 125 * scale, + width: 85 * scale, + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: Stack( + fit: StackFit.expand, + alignment: Alignment.bottomCenter, + children: [ + thumbnailWidget, + Positioned( + bottom: 8 * scale, + child: Transform.scale( + scale: scale, + child: SizedBox( + width: 85, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + ), + child: Text( + "1 year ago", + style: getEnteTextTheme(context).miniBold, + ), + ), + ), + ), + ), + ], + ), + ), + ); + } +} From 2fc550b5429a85b8b6a5a80088061f3f9c1366b8 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 11 Jan 2024 15:41:03 +0530 Subject: [PATCH 115/175] Refactor --- lib/ui/home/memories/memories_widget.dart | 32 +------ .../memories/memory_cover_widget_new.dart | 91 ++++++++++++------- 2 files changed, 64 insertions(+), 59 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 69be67352..93445772b 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -7,7 +7,6 @@ import 'package:photos/models/memory.dart'; import 'package:photos/services/memories_service.dart'; import "package:photos/ui/home/memories/memory_cover_widget.dart"; import "package:photos/ui/home/memories/memory_cover_widget_new.dart"; -import "package:photos/ui/viewer/file/thumbnail_widget.dart"; class MemoriesWidget extends StatefulWidget { const MemoriesWidget({Key? key}) : super(key: key); @@ -63,7 +62,6 @@ class _MemoriesWidgetState extends State { } Widget _buildMemories(List memories) { - final widthOfScreen = MediaQuery.sizeOf(context).width; final collatedMemories = _collateMemories(memories); final List memoryWidgets = []; for (final memories in collatedMemories) { @@ -82,32 +80,10 @@ class _MemoriesWidgetState extends State { itemCount: memoryWidgets.length, itemBuilder: (context, itemIndex) { final offsetOfItem = _widthOfItem * itemIndex; - return AnimatedBuilder( - animation: _controller, - builder: (context, child) { - final diff = - (_controller.offset - offsetOfItem) + widthOfScreen / 7; - final scale = 1 - (diff / widthOfScreen).abs() / 3; - //Adding this row is a workaround for making height of memory cover - //render as 125 * scale. Without this, height of rendered memory - //cover will be 125. - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 2.5), - child: Row( - children: [ - MemoryCoverWidgetNew( - memories: memories, - thumbnailWidget: child!, - scale: scale, - ), - ], - ), - ); - }, - child: ThumbnailWidget( - memories[0].file, - shouldShowArchiveStatus: false, - ), + return MemoryCoverWidgetNew( + memories: memories, + controller: _controller, + offsetOfItem: offsetOfItem, ); }, ), diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index 768398af4..6cdd85028 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -1,51 +1,80 @@ import "package:flutter/material.dart"; import "package:photos/models/memory.dart"; import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/viewer/file/thumbnail_widget.dart"; class MemoryCoverWidgetNew extends StatelessWidget { - final double scale; - final Widget thumbnailWidget; final List memories; + final ScrollController controller; + final double offsetOfItem; const MemoryCoverWidgetNew({ required this.memories, - required this.thumbnailWidget, - required this.scale, + required this.controller, + required this.offsetOfItem, super.key, }); @override Widget build(BuildContext context) { - return SizedBox( - height: 125 * scale, - width: 85 * scale, - child: ClipRRect( - borderRadius: BorderRadius.circular(5), - child: Stack( - fit: StackFit.expand, - alignment: Alignment.bottomCenter, - children: [ - thumbnailWidget, - Positioned( - bottom: 8 * scale, - child: Transform.scale( - scale: scale, - child: SizedBox( - width: 85, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - ), - child: Text( - "1 year ago", - style: getEnteTextTheme(context).miniBold, - ), + //memories will be empty if all memories are deleted and setState is called + //after FullScreenMemory screen is popped + if (memories.isEmpty) { + return const SizedBox.shrink(); + } + + final widthOfScreen = MediaQuery.sizeOf(context).width; + return AnimatedBuilder( + animation: controller, + builder: (context, child) { + final diff = (controller.offset - offsetOfItem) + widthOfScreen / 7; + final scale = 1 - (diff / widthOfScreen).abs() / 3; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 2.5), + //Adding this row is a workaround for making height of memory cover + //render as 125 * scale. Without this, height of rendered memory + //cover will be 125. + child: Row( + children: [ + SizedBox( + height: 125 * scale, + width: 85 * scale, + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: Stack( + fit: StackFit.expand, + alignment: Alignment.bottomCenter, + children: [ + child!, + Positioned( + bottom: 8 * scale, + child: Transform.scale( + scale: scale, + child: SizedBox( + width: 85, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + ), + child: Text( + "1 year ago", + style: getEnteTextTheme(context).miniBold, + ), + ), + ), + ), + ), + ], ), ), ), - ), - ], - ), + ], + ), + ); + }, + child: ThumbnailWidget( + memories[0].file, + shouldShowArchiveStatus: false, ), ); } From b701b1665e361cd39a0571f37b8317e4cc92b819 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 11 Jan 2024 18:40:03 +0530 Subject: [PATCH 116/175] Ignore embeddings for files that don't exist --- lib/services/semantic_search/embedding_store.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/services/semantic_search/embedding_store.dart b/lib/services/semantic_search/embedding_store.dart index 6aedf8547..9b9eccdce 100644 --- a/lib/services/semantic_search/embedding_store.dart +++ b/lib/services/semantic_search/embedding_store.dart @@ -138,7 +138,10 @@ class EmbeddingStore { for (final embedding in remoteEmbeddings) { final file = fileMap[embedding.fileID]; - final fileKey = getFileKey(file!); + if (file == null) { + continue; + } + final fileKey = getFileKey(file); final input = EmbeddingsDecoderInput(embedding, fileKey); inputs.add(input); } From 386cdcf3c0a8ca15078fdac3ad181cb669ea8fbb Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 11 Jan 2024 18:53:44 +0530 Subject: [PATCH 117/175] Fix response param --- lib/services/semantic_search/embedding_store.dart | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/services/semantic_search/embedding_store.dart b/lib/services/semantic_search/embedding_store.dart index 9b9eccdce..4b665c8af 100644 --- a/lib/services/semantic_search/embedding_store.dart +++ b/lib/services/semantic_search/embedding_store.dart @@ -50,9 +50,15 @@ class EmbeddingStore { Future pushEmbeddings() async { final pendingItems = await EmbeddingsDB.instance.getUnsyncedEmbeddings(); + final fileMap = await FilesDB.instance + .getFilesFromIDs(pendingItems.map((e) => e.fileID).toList()); + _logger.info("Pushing ${pendingItems.length} embeddings"); for (final item in pendingItems) { - final file = await FilesDB.instance.getAnyUploadedFile(item.fileID); - await _pushEmbedding(file!, item); + try { + await _pushEmbedding(fileMap[item.fileID]!, item); + } catch (e, s) { + _logger.severe(e, s); + } } } @@ -67,6 +73,7 @@ class EmbeddingStore { } Future _pushEmbedding(EnteFile file, Embedding embedding) async { + _logger.info("Pushing embedding for $file"); final encryptionKey = getFileKey(file); final embeddingJSON = jsonEncode(embedding.embedding); final encryptedEmbedding = await CryptoUtil.encryptChaCha( @@ -86,7 +93,7 @@ class EmbeddingStore { "decryptionHeader": header, }, ); - final updationTime = response.data["updationTime"]; + final updationTime = response.data["updatedAt"]; embedding.updationTime = updationTime; await EmbeddingsDB.instance.put(embedding); } catch (e, s) { From d79ad9f02d9adffe1bd4b077a753ca2bc73cdc26 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 11 Jan 2024 19:06:24 +0530 Subject: [PATCH 118/175] Download models on mobile data only if enabled --- .../semantic_search/frameworks/ggml.dart | 2 + .../frameworks/ml_framework.dart | 62 ++++++++++++++++--- .../semantic_search/frameworks/onnx/onnx.dart | 2 + .../semantic_search_service.dart | 7 ++- 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/lib/services/semantic_search/frameworks/ggml.dart b/lib/services/semantic_search/frameworks/ggml.dart index e4903091c..6ff862084 100644 --- a/lib/services/semantic_search/frameworks/ggml.dart +++ b/lib/services/semantic_search/frameworks/ggml.dart @@ -11,6 +11,8 @@ class GGML extends MLFramework { final _computer = Computer.shared(); final _logger = Logger("GGML"); + GGML(super.shouldDownloadOverMobileData); + @override String getImageModelRemotePath() { return kModelBucketEndpoint + kImageModel; diff --git a/lib/services/semantic_search/frameworks/ml_framework.dart b/lib/services/semantic_search/frameworks/ml_framework.dart index 5524f63ca..8cf6538f1 100644 --- a/lib/services/semantic_search/frameworks/ml_framework.dart +++ b/lib/services/semantic_search/frameworks/ml_framework.dart @@ -1,9 +1,13 @@ +import "dart:async"; import "dart:io"; +import "package:connectivity_plus/connectivity_plus.dart"; import "package:flutter/services.dart"; import "package:logging/logging.dart"; import "package:path/path.dart"; import "package:path_provider/path_provider.dart"; +import "package:photos/core/errors.dart"; + import "package:photos/core/event_bus.dart"; import "package:photos/core/network/network.dart"; import "package:photos/events/event.dart"; @@ -12,9 +16,24 @@ abstract class MLFramework { static const kImageEncoderEnabled = true; static const kMaximumRetrials = 3; - InitializationState _state = InitializationState.notInitialized; + static final _logger = Logger("MLFramework"); - final _logger = Logger("MLFramework"); + final bool shouldDownloadOverMobileData; + + InitializationState _state = InitializationState.notInitialized; + final _initializationCompleter = Completer(); + + MLFramework(this.shouldDownloadOverMobileData) { + Connectivity() + .onConnectivityChanged + .listen((ConnectivityResult result) async { + _logger.info("Connectivity changed to $result"); + if (_state == InitializationState.waitingForNetwork && + await _canDownload()) { + unawaited(init()); + } + }); + } InitializationState get initializationState => _state; @@ -47,8 +66,18 @@ abstract class MLFramework { /// initialization. For eg. if you wish to load the model from `/assets` /// instead of a CDN. Future init() async { - await Future.wait([_initImageModel(), _initTextModel()]); + try { + await Future.wait([_initImageModel(), _initTextModel()]); + } catch (e, s) { + _logger.warning(e, s); + if (e is WiFiUnavailableError) { + return _initializationCompleter.future; + } else { + rethrow; + } + } _initState = InitializationState.initialized; + _initializationCompleter.complete(); } // Releases any resources held by the framework @@ -75,9 +104,9 @@ abstract class MLFramework { if (!kImageEncoderEnabled) { return; } + _initState = InitializationState.initializingImageModel; final path = await _getLocalImageModelPath(); - if (File(path).existsSync()) { - _initState = InitializationState.initializingImageModel; + if (await File(path).exists()) { await loadImageModel(path); } else { _initState = InitializationState.downloadingImageModel; @@ -86,12 +115,13 @@ abstract class MLFramework { await tempFile.rename(path); await loadImageModel(path); } + _initState = InitializationState.initializedImageModel; } Future _initTextModel() async { final path = await _getLocalTextModelPath(); - if (File(path).existsSync()) { - _initState = InitializationState.initializingTextModel; + _initState = InitializationState.initializingTextModel; + if (await File(path).exists()) { await loadTextModel(path); } else { _initState = InitializationState.downloadingTextModel; @@ -100,6 +130,7 @@ abstract class MLFramework { await tempFile.rename(path); await loadTextModel(path); } + _initState = InitializationState.initializedTextModel; } Future _getLocalImageModelPath() async { @@ -119,6 +150,10 @@ abstract class MLFramework { String savePath, { int trialCount = 1, }) async { + if (!await _canDownload()) { + _initState = InitializationState.waitingForNetwork; + throw WiFiUnavailableError(); + } _logger.info("Downloading " + url); final existingFile = File(savePath); if (await existingFile.exists()) { @@ -136,6 +171,15 @@ abstract class MLFramework { } } + Future _canDownload() async { + final connectivityResult = await (Connectivity().checkConnectivity()); + bool canDownloadUnderCurrentNetworkConditions = true; + if (connectivityResult == ConnectivityResult.mobile) { + canDownloadUnderCurrentNetworkConditions = shouldDownloadOverMobileData; + } + return canDownloadUnderCurrentNetworkConditions; + } + Future getAccessiblePathForAsset( String assetPath, String tempName, @@ -158,8 +202,10 @@ enum InitializationState { notInitialized, waitingForNetwork, downloadingImageModel, - downloadingTextModel, initializingImageModel, + initializedImageModel, + downloadingTextModel, initializingTextModel, + initializedTextModel, initialized, } diff --git a/lib/services/semantic_search/frameworks/onnx/onnx.dart b/lib/services/semantic_search/frameworks/onnx/onnx.dart index 0a18c66e6..00930ccac 100644 --- a/lib/services/semantic_search/frameworks/onnx/onnx.dart +++ b/lib/services/semantic_search/frameworks/onnx/onnx.dart @@ -17,6 +17,8 @@ class ONNX extends MLFramework { int _textEncoderAddress = 0; int _imageEncoderAddress = 0; + ONNX(super.shouldDownloadOverMobileData); + @override String getImageModelRemotePath() { return kModelBucketEndpoint + kImageModel; diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index b233559cf..121c72f9a 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -38,11 +38,11 @@ class SemanticSearchService { final _logger = Logger("SemanticSearchService"); final _queue = Queue(); - final _mlFramework = kCurrentModel == Model.onnxClip ? ONNX() : GGML(); final _frameworkInitialization = Completer(); final _embeddingLoaderDebouncer = Debouncer(kDebounceDuration, executionInterval: kDebounceDuration); + late MLFramework _mlFramework; bool _hasInitialized = false; bool _isComputingEmbeddings = false; bool _isSyncing = false; @@ -61,6 +61,11 @@ class SemanticSearchService { return; } _hasInitialized = true; + final shouldDownloadOverMobileData = + Configuration.instance.shouldBackupOverMobileData(); + _mlFramework = kCurrentModel == Model.onnxClip + ? ONNX(shouldDownloadOverMobileData) + : GGML(shouldDownloadOverMobileData); await EmbeddingsDB.instance.init(); await EmbeddingStore.instance.init(); await _loadEmbeddings(); From e0d50ba55ef775f4c4a64cbf9b905e7a4c6e77f3 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 11 Jan 2024 19:19:58 +0530 Subject: [PATCH 119/175] Surface framework initialization state on UI --- lib/generated/intl/messages_en.dart | 2 + lib/generated/l10n.dart | 10 ++++ lib/l10n/intl_en.arb | 1 + .../frameworks/ml_framework.dart | 6 +- .../semantic_search_service.dart | 4 +- .../machine_learning_settings_page.dart | 59 +++++++++++++++---- 6 files changed, 64 insertions(+), 18 deletions(-) diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 3febd6407..82ec09e23 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -1451,6 +1451,8 @@ class MessageLookup extends MessageLookupByLibrary { "viewer": MessageLookupByLibrary.simpleMessage("Viewer"), "visitWebToManage": MessageLookupByLibrary.simpleMessage( "Please visit web.ente.io to manage your subscription"), + "waitingForWifi": + MessageLookupByLibrary.simpleMessage("Waiting for WiFi..."), "weAreOpenSource": MessageLookupByLibrary.simpleMessage("We are open source!"), "weDontSupportEditingPhotosAndAlbumsThatYouDont": diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 9f3136f8a..750587f72 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -2886,6 +2886,16 @@ class S { ); } + /// `Waiting for WiFi...` + String get waitingForWifi { + return Intl.message( + 'Waiting for WiFi...', + name: 'waitingForWifi', + desc: '', + args: [], + ); + } + /// `Status` String get status { return Intl.message( diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 20571d2e4..b96327a47 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -410,6 +410,7 @@ "magicSearch": "Magic search", "magicSearchDescription": "Please note that this will result in a higher bandwidth and battery usage until all items are indexed.", "loadingModel": "Downloading models...", + "waitingForWifi": "Waiting for WiFi...", "status": "Status", "indexedItems": "Indexed items", "pendingItems": "Pending items", diff --git a/lib/services/semantic_search/frameworks/ml_framework.dart b/lib/services/semantic_search/frameworks/ml_framework.dart index 8cf6538f1..4c04ee59c 100644 --- a/lib/services/semantic_search/frameworks/ml_framework.dart +++ b/lib/services/semantic_search/frameworks/ml_framework.dart @@ -38,7 +38,7 @@ abstract class MLFramework { InitializationState get initializationState => _state; set _initState(InitializationState state) { - Bus.instance.fire(MLFrameworkInitializationEvent(state)); + Bus.instance.fire(MLFrameworkInitializationUpdateEvent(state)); _logger.info("Init state is $state"); _state = state; } @@ -192,10 +192,10 @@ abstract class MLFramework { } } -class MLFrameworkInitializationEvent extends Event { +class MLFrameworkInitializationUpdateEvent extends Event { final InitializationState state; - MLFrameworkInitializationEvent(this.state); + MLFrameworkInitializationUpdateEvent(this.state); } enum InitializationState { diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index 121c72f9a..ddb03d78b 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -150,8 +150,8 @@ class SemanticSearchService { ); } - Future getFrameworkInitializationStatus() { - return _frameworkInitialization.future; + InitializationState getFrameworkInitializationState() { + return _mlFramework.initializationState; } Future clearIndexes() async { diff --git a/lib/ui/settings/machine_learning_settings_page.dart b/lib/ui/settings/machine_learning_settings_page.dart index c64ae5714..6afb3704a 100644 --- a/lib/ui/settings/machine_learning_settings_page.dart +++ b/lib/ui/settings/machine_learning_settings_page.dart @@ -6,6 +6,7 @@ import "package:photos/core/event_bus.dart"; import 'package:photos/events/embedding_updated_event.dart'; import "package:photos/generated/l10n.dart"; import "package:photos/services/feature_flag_service.dart"; +import "package:photos/services/semantic_search/frameworks/ml_framework.dart"; import "package:photos/services/semantic_search/semantic_search_service.dart"; import "package:photos/theme/ente_theme.dart"; import "package:photos/ui/common/loading_widget.dart"; @@ -29,6 +30,32 @@ class MachineLearningSettingsPage extends StatefulWidget { class _MachineLearningSettingsPageState extends State { + late InitializationState _state; + + late StreamSubscription + _eventSubscription; + + @override + void initState() { + super.initState(); + _eventSubscription = + Bus.instance.on().listen((event) { + _fetchState(); + setState(() {}); + }); + _fetchState(); + } + + void _fetchState() { + _state = SemanticSearchService.instance.getFrameworkInitializationState(); + } + + @override + void dispose() { + super.dispose(); + _eventSubscription.cancel(); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -118,17 +145,9 @@ class _MachineLearningSettingsPageState hasEnabled ? Column( children: [ - FutureBuilder( - future: SemanticSearchService.instance - .getFrameworkInitializationStatus(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - return const MagicSearchIndexStatsWidget(); - } else { - return const ModelLoadingState(); - } - }, - ), + _state == InitializationState.initialized + ? const MagicSearchIndexStatsWidget() + : ModelLoadingState(_state), const SizedBox( height: 12, ), @@ -158,7 +177,12 @@ class _MachineLearningSettingsPageState } class ModelLoadingState extends StatelessWidget { - const ModelLoadingState({super.key}); + final InitializationState state; + + const ModelLoadingState( + this.state, { + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -167,7 +191,7 @@ class ModelLoadingState extends StatelessWidget { MenuSectionTitle(title: S.of(context).status), MenuItemWidget( captionedTextWidget: CaptionedTextWidget( - title: S.of(context).loadingModel, + title: _getTitle(context), ), trailingWidget: EnteLoadingWidget( size: 12, @@ -180,6 +204,15 @@ class ModelLoadingState extends StatelessWidget { ], ); } + + String _getTitle(BuildContext context) { + switch (state) { + case InitializationState.waitingForNetwork: + return S.of(context).waitingForWifi; + default: + return S.of(context).loadingModel; + } + } } class MagicSearchIndexStatsWidget extends StatefulWidget { From c6f623280711fda2ab0d46b2ba6522a84859313a Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 11 Jan 2024 19:20:21 +0530 Subject: [PATCH 120/175] v0.8.34 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index d38ab111e..11e699103 100644 --- a/pubspec.yaml +++ b/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.8.33+553 +version: 0.8.34+554 environment: sdk: ">=3.0.0 <4.0.0" From 9d282707680c70314b03f83155eba68ec59572f9 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 11 Jan 2024 21:42:38 +0530 Subject: [PATCH 121/175] Address review comment --- lib/services/semantic_search/frameworks/ml_framework.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/services/semantic_search/frameworks/ml_framework.dart b/lib/services/semantic_search/frameworks/ml_framework.dart index 4c04ee59c..3523c721e 100644 --- a/lib/services/semantic_search/frameworks/ml_framework.dart +++ b/lib/services/semantic_search/frameworks/ml_framework.dart @@ -173,11 +173,8 @@ abstract class MLFramework { Future _canDownload() async { final connectivityResult = await (Connectivity().checkConnectivity()); - bool canDownloadUnderCurrentNetworkConditions = true; - if (connectivityResult == ConnectivityResult.mobile) { - canDownloadUnderCurrentNetworkConditions = shouldDownloadOverMobileData; - } - return canDownloadUnderCurrentNetworkConditions; + return connectivityResult != ConnectivityResult.mobile || + shouldDownloadOverMobileData; } Future getAccessiblePathForAsset( From d1423ac600b075880e2d48b09b28fbc2eac2c7a1 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 11 Jan 2024 21:43:33 +0530 Subject: [PATCH 122/175] Update strings --- lib/generated/intl/messages_en.dart | 2 +- lib/generated/intl/messages_zh.dart | 1 + lib/generated/l10n.dart | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 82ec09e23..254eacaa9 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -69,7 +69,7 @@ class MessageLookup extends MessageLookupByLibrary { "Please drop an email to ${supportEmail} from your registered email address"; static String m16(count, storageSaved) => - "Your have cleaned up ${Intl.plural(count, one: '${count} duplicate file', other: '${count} duplicate files')}, saving (${storageSaved}!)"; + "You have cleaned up ${Intl.plural(count, one: '${count} duplicate file', other: '${count} duplicate files')}, saving (${storageSaved}!)"; static String m17(count, formattedSize) => "${count} files, ${formattedSize} each"; diff --git a/lib/generated/intl/messages_zh.dart b/lib/generated/intl/messages_zh.dart index ecf65f05e..d04c68521 100644 --- a/lib/generated/intl/messages_zh.dart +++ b/lib/generated/intl/messages_zh.dart @@ -704,6 +704,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("正在加载 EXIF 数据..."), "loadingGallery": MessageLookupByLibrary.simpleMessage("正在加载图库..."), "loadingMessage": MessageLookupByLibrary.simpleMessage("正在加载您的照片..."), + "loadingModel": MessageLookupByLibrary.simpleMessage("正在下载模型..."), "localGallery": MessageLookupByLibrary.simpleMessage("本地相册"), "location": MessageLookupByLibrary.simpleMessage("地理位置"), "locationName": MessageLookupByLibrary.simpleMessage("地点名称"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 750587f72..e882159ed 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -3533,10 +3533,10 @@ class S { ); } - /// `Your have cleaned up {count, plural, one{{count} duplicate file} other{{count} duplicate files}}, saving ({storageSaved}!)` + /// `You have cleaned up {count, plural, one{{count} duplicate file} other{{count} duplicate files}}, saving ({storageSaved}!)` String duplicateFileCountWithStorageSaved(int count, String storageSaved) { return Intl.message( - 'Your have cleaned up ${Intl.plural(count, one: '$count duplicate file', other: '$count duplicate files')}, saving ($storageSaved!)', + 'You have cleaned up ${Intl.plural(count, one: '$count duplicate file', other: '$count duplicate files')}, saving ($storageSaved!)', name: 'duplicateFileCountWithStorageSaved', desc: 'The text to display when the user has successfully cleaned up duplicate files', From e9832d039d0a27f79b211d41e6445caa68bdc4da Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 11 Jan 2024 21:43:35 +0530 Subject: [PATCH 123/175] v0.8.35 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 11e699103..e74135aa7 100644 --- a/pubspec.yaml +++ b/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.8.34+554 +version: 0.8.35+555 environment: sdk: ">=3.0.0 <4.0.0" From 3a24e99003ae6474a343d73fc9a33b03b018b105 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 12 Jan 2024 11:48:59 +0530 Subject: [PATCH 124/175] route to FullScreenMemory screen on tapping the new MemoryCoverWidget --- .../memories/memory_cover_widget_new.dart | 90 ++++++++++++------- 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index 6cdd85028..d2f8b9fb0 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -1,9 +1,11 @@ import "package:flutter/material.dart"; import "package:photos/models/memory.dart"; import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/home/memories/full_screen_memory.dart"; import "package:photos/ui/viewer/file/thumbnail_widget.dart"; +import "package:photos/utils/navigation_util.dart"; -class MemoryCoverWidgetNew extends StatelessWidget { +class MemoryCoverWidgetNew extends StatefulWidget { final List memories; final ScrollController controller; final double offsetOfItem; @@ -15,65 +17,85 @@ class MemoryCoverWidgetNew extends StatelessWidget { super.key, }); + @override + State createState() => _MemoryCoverWidgetNewState(); +} + +class _MemoryCoverWidgetNewState extends State { @override Widget build(BuildContext context) { //memories will be empty if all memories are deleted and setState is called //after FullScreenMemory screen is popped - if (memories.isEmpty) { + if (widget.memories.isEmpty) { return const SizedBox.shrink(); } final widthOfScreen = MediaQuery.sizeOf(context).width; return AnimatedBuilder( - animation: controller, + animation: widget.controller, builder: (context, child) { - final diff = (controller.offset - offsetOfItem) + widthOfScreen / 7; + final diff = (widget.controller.offset - widget.offsetOfItem) + + widthOfScreen / 7; final scale = 1 - (diff / widthOfScreen).abs() / 3; return Padding( padding: const EdgeInsets.symmetric(horizontal: 2.5), //Adding this row is a workaround for making height of memory cover //render as 125 * scale. Without this, height of rendered memory //cover will be 125. - child: Row( - children: [ - SizedBox( - height: 125 * scale, - width: 85 * scale, - child: ClipRRect( - borderRadius: BorderRadius.circular(5), - child: Stack( - fit: StackFit.expand, - alignment: Alignment.bottomCenter, - children: [ - child!, - Positioned( - bottom: 8 * scale, - child: Transform.scale( - scale: scale, - child: SizedBox( - width: 85, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - ), - child: Text( - "1 year ago", - style: getEnteTextTheme(context).miniBold, + child: GestureDetector( + onTap: () async { + await routeToPage( + context, + FullScreenMemoryDataUpdater( + initialIndex: 0, + memories: widget.memories, + child: FullScreenMemory("title", 0), + ), + forceCustomPageRoute: true, + ); + setState(() {}); + }, + child: Row( + children: [ + SizedBox( + height: 125 * scale, + width: 85 * scale, + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: Stack( + fit: StackFit.expand, + alignment: Alignment.bottomCenter, + children: [ + child!, + Positioned( + bottom: 8 * scale, + child: Transform.scale( + scale: scale, + child: SizedBox( + width: 85, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + ), + child: Text( + "1 year ago", + style: getEnteTextTheme(context).miniBold, + ), ), ), ), ), - ), - ], + ], + ), ), ), - ), - ], + ], + ), ), ); }, child: ThumbnailWidget( - memories[0].file, + widget.memories[0].file, shouldShowArchiveStatus: false, ), ); From 590c72c6e0f92b6d48e6aed593eca8dba5fb6731 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 12 Jan 2024 11:52:55 +0530 Subject: [PATCH 125/175] show unseen memory on opening new MemoryCoverWidget and show title --- .../memories/memory_cover_widget_new.dart | 47 +++++++++++++++++-- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index d2f8b9fb0..2fb7e846a 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -1,4 +1,5 @@ import "package:flutter/material.dart"; +import "package:photos/generated/l10n.dart"; import "package:photos/models/memory.dart"; import "package:photos/theme/ente_theme.dart"; import "package:photos/ui/home/memories/full_screen_memory.dart"; @@ -31,6 +32,9 @@ class _MemoryCoverWidgetNewState extends State { } final widthOfScreen = MediaQuery.sizeOf(context).width; + final index = _getNextMemoryIndex(); + final title = _getTitle(widget.memories[index]); + final memory = widget.memories[index]; return AnimatedBuilder( animation: widget.controller, builder: (context, child) { @@ -47,9 +51,9 @@ class _MemoryCoverWidgetNewState extends State { await routeToPage( context, FullScreenMemoryDataUpdater( - initialIndex: 0, + initialIndex: index, memories: widget.memories, - child: FullScreenMemory("title", 0), + child: FullScreenMemory(title, index), ), forceCustomPageRoute: true, ); @@ -94,10 +98,43 @@ class _MemoryCoverWidgetNewState extends State { ), ); }, - child: ThumbnailWidget( - widget.memories[0].file, - shouldShowArchiveStatus: false, + child: Hero( + tag: "memories" + memory.file.tag, + child: ThumbnailWidget( + memory.file, + shouldShowArchiveStatus: false, + key: Key("memories" + memory.file.tag), + ), ), ); } + + // Returns either the first unseen memory or the memory that succeeds the + // last seen memory + int _getNextMemoryIndex() { + int lastSeenIndex = 0; + int lastSeenTimestamp = 0; + for (var index = 0; index < widget.memories.length; index++) { + final memory = widget.memories[index]; + if (!memory.isSeen()) { + return index; + } else { + if (memory.seenTime() > lastSeenTimestamp) { + lastSeenIndex = index; + lastSeenTimestamp = memory.seenTime(); + } + } + } + if (lastSeenIndex == widget.memories.length - 1) { + return 0; + } + return lastSeenIndex + 1; + } + + String _getTitle(Memory memory) { + final present = DateTime.now(); + final then = DateTime.fromMicrosecondsSinceEpoch(memory.file.creationTime!); + final diffInYears = present.year - then.year; + return S.of(context).yearsAgo(diffInYears); + } } From 9466c033978ee90c8c50f14c8ba512417e333da6 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 12 Jan 2024 12:06:21 +0530 Subject: [PATCH 126/175] minor changes --- lib/ui/home/memories/memories_widget.dart | 1 + lib/ui/home/memories/memory_cover_widget_new.dart | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 93445772b..1411d114c 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -75,6 +75,7 @@ class _MemoriesWidgetState extends State { return SizedBox( height: 125, child: ListView.builder( + physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, controller: _controller, itemCount: memoryWidgets.length, diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index 2fb7e846a..45ee7b070 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -82,7 +82,7 @@ class _MemoryCoverWidgetNewState extends State { horizontal: 8.0, ), child: Text( - "1 year ago", + title, style: getEnteTextTheme(context).miniBold, ), ), From cd777f573515d5ce0e9ddd89255f9c78845603c6 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 12 Jan 2024 12:59:56 +0530 Subject: [PATCH 127/175] add linear gradient on MemoryCoverWidget --- .../memories/memory_cover_widget_new.dart | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index 45ee7b070..7223280c4 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -81,9 +81,12 @@ class _MemoryCoverWidgetNewState extends State { padding: const EdgeInsets.symmetric( horizontal: 8.0, ), - child: Text( - title, - style: getEnteTextTheme(context).miniBold, + child: Hero( + tag: title, + child: Text( + title, + style: getEnteTextTheme(context).miniBold, + ), ), ), ), @@ -100,10 +103,26 @@ class _MemoryCoverWidgetNewState extends State { }, child: Hero( tag: "memories" + memory.file.tag, - child: ThumbnailWidget( - memory.file, - shouldShowArchiveStatus: false, - key: Key("memories" + memory.file.tag), + child: Stack( + children: [ + ThumbnailWidget( + memory.file, + shouldShowArchiveStatus: false, + key: Key("memories" + memory.file.tag), + ), + Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + stops: [0, 0.5], + colors: [ + Color(0x6601DE4D), + Color(0x0001DE4D), + ], + transform: GradientRotation(-1.2), + ), + ), + ), + ], ), ), ); From 2417fc017994a91bbae0a6f04df1a286789b7eee Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 12 Jan 2024 14:46:00 +0530 Subject: [PATCH 128/175] add black linear gradient + minor fix to text color --- .../memories/memory_cover_widget_new.dart | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index 7223280c4..ec0fb521a 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -85,7 +85,11 @@ class _MemoryCoverWidgetNewState extends State { tag: title, child: Text( title, - style: getEnteTextTheme(context).miniBold, + style: getEnteTextTheme(context) + .miniBold + .copyWith( + color: Colors.white, + ), ), ), ), @@ -113,14 +117,27 @@ class _MemoryCoverWidgetNewState extends State { Container( decoration: const BoxDecoration( gradient: LinearGradient( - stops: [0, 0.5], + stops: [0, 0.35, 0.5], + // stops: [0, 0.5], + colors: [ - Color(0x6601DE4D), + Color.fromARGB(96, 1, 222, 78), + Color(0x1901DE4D), Color(0x0001DE4D), ], transform: GradientRotation(-1.2), ), ), + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Colors.black.withOpacity(0.5), Colors.transparent], + stops: const [0, 0.85], + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + ), + ), + ), ), ], ), From 01fa417dacaacec565194365909b7b56a3316a70 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 12 Jan 2024 16:11:54 +0530 Subject: [PATCH 129/175] refactor + add green linear gradent on bottom --- .../memories/memory_cover_widget_new.dart | 97 ++++++++++++------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index ec0fb521a..bb6f150b0 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -71,6 +71,63 @@ class _MemoryCoverWidgetNewState extends State { alignment: Alignment.bottomCenter, children: [ child!, + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Colors.black.withOpacity(0.5), + Colors.transparent, + ], + stops: const [0, 0.85], + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + ), + ), + ), + Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + stops: [0, 0.35, 0.5], + // stops: [0, 0.5], + + colors: [ + Color.fromARGB(58, 1, 222, 78), + Color(0x1901DE4D), + Color(0x0001DE4D), + ], + transform: GradientRotation(-1.2), + ), + ), + ), + Stack( + fit: StackFit.expand, + alignment: Alignment.bottomCenter, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Transform.scale( + scale: scale, + child: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + stops: [0, 0.5, 1], + colors: [ + Colors.transparent, + Color(0xFF01DE4D), + Colors.transparent, + ], + ), + ), + height: 1, + width: 68, + ), + ), + ], + ), + ], + ), Positioned( bottom: 8 * scale, child: Transform.scale( @@ -105,42 +162,10 @@ class _MemoryCoverWidgetNewState extends State { ), ); }, - child: Hero( - tag: "memories" + memory.file.tag, - child: Stack( - children: [ - ThumbnailWidget( - memory.file, - shouldShowArchiveStatus: false, - key: Key("memories" + memory.file.tag), - ), - Container( - decoration: const BoxDecoration( - gradient: LinearGradient( - stops: [0, 0.35, 0.5], - // stops: [0, 0.5], - - colors: [ - Color.fromARGB(96, 1, 222, 78), - Color(0x1901DE4D), - Color(0x0001DE4D), - ], - transform: GradientRotation(-1.2), - ), - ), - child: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [Colors.black.withOpacity(0.5), Colors.transparent], - stops: const [0, 0.85], - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - ), - ), - ), - ), - ], - ), + child: ThumbnailWidget( + memory.file, + shouldShowArchiveStatus: false, + key: Key("memories" + memory.file.tag), ), ); } From f77c876071ce2fb849896e271deafb1a40112ece Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 12 Jan 2024 16:16:36 +0530 Subject: [PATCH 130/175] fix render overflow error --- lib/ui/home/memories/memory_cover_widget_new.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index bb6f150b0..601bb70a0 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -120,8 +120,8 @@ class _MemoryCoverWidgetNewState extends State { ], ), ), - height: 1, - width: 68, + height: 1 * scale, + width: 68 * scale, ), ), ], From b6544825d8f95250b071e81d40d2e1e5d9077ba3 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 12 Jan 2024 16:51:11 +0530 Subject: [PATCH 131/175] added border stroke to new MemoryCoverWidget --- lib/ui/home/memories/memories_widget.dart | 2 +- .../memories/memory_cover_widget_new.dart | 23 +++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 1411d114c..5e7cc894f 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -73,7 +73,7 @@ class _MemoriesWidgetState extends State { ); } return SizedBox( - height: 125, + height: 125 + MemoryCoverWidgetNew.centerStrokeWidth * 2, child: ListView.builder( physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index 601bb70a0..30aad187e 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -10,6 +10,7 @@ class MemoryCoverWidgetNew extends StatefulWidget { final List memories; final ScrollController controller; final double offsetOfItem; + static const centerStrokeWidth = 0.5; const MemoryCoverWidgetNew({ required this.memories, @@ -61,9 +62,19 @@ class _MemoryCoverWidgetNewState extends State { }, child: Row( children: [ - SizedBox( + Container( height: 125 * scale, width: 85 * scale, + decoration: BoxDecoration( + boxShadow: const [ + BoxShadow( + color: Color.fromRGBO(1, 222, 77, 0.11), + spreadRadius: MemoryCoverWidgetNew.centerStrokeWidth, + blurRadius: 0, + ), + ], + borderRadius: BorderRadius.circular(5), + ), child: ClipRRect( borderRadius: BorderRadius.circular(5), child: Stack( @@ -71,6 +82,14 @@ class _MemoryCoverWidgetNewState extends State { alignment: Alignment.bottomCenter, children: [ child!, + Container( + decoration: BoxDecoration( + border: Border.all( + color: const Color.fromRGBO(1, 222, 77, 0.11), + width: MemoryCoverWidgetNew.centerStrokeWidth, + ), + ), + ), Container( decoration: BoxDecoration( gradient: LinearGradient( @@ -91,7 +110,7 @@ class _MemoryCoverWidgetNewState extends State { // stops: [0, 0.5], colors: [ - Color.fromARGB(58, 1, 222, 78), + Color.fromARGB(71, 1, 222, 78), Color(0x1901DE4D), Color(0x0001DE4D), ], From 64da59ada0200756249694d93f5b0bcd4ad631ac Mon Sep 17 00:00:00 2001 From: ashilkn Date: Fri, 12 Jan 2024 18:05:55 +0530 Subject: [PATCH 132/175] highlight new MemoryCoverWidget only if memories in it are not seen completely --- .../memories/memory_cover_widget_new.dart | 114 ++++++++++-------- 1 file changed, 62 insertions(+), 52 deletions(-) diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index 30aad187e..4890bee28 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -36,6 +36,8 @@ class _MemoryCoverWidgetNewState extends State { final index = _getNextMemoryIndex(); final title = _getTitle(widget.memories[index]); final memory = widget.memories[index]; + final isSeen = memory.isSeen(); + return AnimatedBuilder( animation: widget.controller, builder: (context, child) { @@ -66,9 +68,11 @@ class _MemoryCoverWidgetNewState extends State { height: 125 * scale, width: 85 * scale, decoration: BoxDecoration( - boxShadow: const [ + boxShadow: [ BoxShadow( - color: Color.fromRGBO(1, 222, 77, 0.11), + color: isSeen + ? Colors.transparent + : const Color.fromRGBO(1, 222, 77, 0.11), spreadRadius: MemoryCoverWidgetNew.centerStrokeWidth, blurRadius: 0, ), @@ -82,14 +86,18 @@ class _MemoryCoverWidgetNewState extends State { alignment: Alignment.bottomCenter, children: [ child!, - Container( - decoration: BoxDecoration( - border: Border.all( - color: const Color.fromRGBO(1, 222, 77, 0.11), - width: MemoryCoverWidgetNew.centerStrokeWidth, - ), - ), - ), + isSeen + ? const SizedBox.shrink() + : Container( + decoration: BoxDecoration( + border: Border.all( + color: + const Color.fromRGBO(1, 222, 77, 0.11), + width: + MemoryCoverWidgetNew.centerStrokeWidth, + ), + ), + ), Container( decoration: BoxDecoration( gradient: LinearGradient( @@ -103,50 +111,52 @@ class _MemoryCoverWidgetNewState extends State { ), ), ), - Container( - decoration: const BoxDecoration( - gradient: LinearGradient( - stops: [0, 0.35, 0.5], - // stops: [0, 0.5], - - colors: [ - Color.fromARGB(71, 1, 222, 78), - Color(0x1901DE4D), - Color(0x0001DE4D), - ], - transform: GradientRotation(-1.2), - ), - ), - ), - Stack( - fit: StackFit.expand, - alignment: Alignment.bottomCenter, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Transform.scale( - scale: scale, - child: Container( - decoration: const BoxDecoration( - gradient: LinearGradient( - stops: [0, 0.5, 1], - colors: [ - Colors.transparent, - Color(0xFF01DE4D), - Colors.transparent, - ], - ), - ), - height: 1 * scale, - width: 68 * scale, + isSeen + ? const SizedBox.shrink() + : Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + stops: [0, 0.35, 0.5], + colors: [ + Color.fromARGB(71, 1, 222, 78), + Color(0x1901DE4D), + Color(0x0001DE4D), + ], + transform: GradientRotation(-1.2), ), ), - ], - ), - ], - ), + ), + isSeen + ? const SizedBox.shrink() + : Stack( + fit: StackFit.expand, + alignment: Alignment.bottomCenter, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Transform.scale( + scale: scale, + child: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + stops: [0, 0.5, 1], + colors: [ + Colors.transparent, + Color(0xFF01DE4D), + Colors.transparent, + ], + ), + ), + height: 1 * scale, + width: 68 * scale, + ), + ), + ], + ), + ], + ), Positioned( bottom: 8 * scale, child: Transform.scale( From 65426cc5b4f548cbf7c48e270a575d296acb7a92 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 13 Jan 2024 15:10:01 +0530 Subject: [PATCH 133/175] remove unwanted code --- lib/ui/home/memories/memories_widget.dart | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 5e7cc894f..19a1a4126 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -5,7 +5,6 @@ import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; import 'package:photos/models/memory.dart'; import 'package:photos/services/memories_service.dart'; -import "package:photos/ui/home/memories/memory_cover_widget.dart"; import "package:photos/ui/home/memories/memory_cover_widget_new.dart"; class MemoriesWidget extends StatefulWidget { @@ -63,26 +62,18 @@ class _MemoriesWidgetState extends State { Widget _buildMemories(List memories) { final collatedMemories = _collateMemories(memories); - final List memoryWidgets = []; - for (final memories in collatedMemories) { - memoryWidgets.add( - Padding( - padding: const EdgeInsets.all(8.0), - child: MemoryCovertWidget(memories: memories), - ), - ); - } + return SizedBox( height: 125 + MemoryCoverWidgetNew.centerStrokeWidth * 2, child: ListView.builder( physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, controller: _controller, - itemCount: memoryWidgets.length, + itemCount: collatedMemories.length, itemBuilder: (context, itemIndex) { final offsetOfItem = _widthOfItem * itemIndex; return MemoryCoverWidgetNew( - memories: memories, + memories: collatedMemories[itemIndex], controller: _controller, offsetOfItem: offsetOfItem, ); @@ -100,7 +91,6 @@ class _MemoriesWidgetState extends State { final List collatedYearlyMemories = []; collatedYearlyMemories.addAll(yearlyMemories); collatedMemories.add(collatedYearlyMemories); - collatedMemories.add(collatedYearlyMemories); yearlyMemories.clear(); } From 0a4910372f84e09bbcdabd2b47448a532b65e21b Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 13 Jan 2024 15:10:35 +0530 Subject: [PATCH 134/175] add hero animation for memory thumbnail --- lib/ui/home/memories/memory_cover_widget_new.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index 4890bee28..c8b158664 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -76,6 +76,7 @@ class _MemoryCoverWidgetNewState extends State { spreadRadius: MemoryCoverWidgetNew.centerStrokeWidth, blurRadius: 0, ), + // ...shadowFloatLight, ], borderRadius: BorderRadius.circular(5), ), @@ -191,10 +192,13 @@ class _MemoryCoverWidgetNewState extends State { ), ); }, - child: ThumbnailWidget( - memory.file, - shouldShowArchiveStatus: false, - key: Key("memories" + memory.file.tag), + child: Hero( + tag: "memories" + memory.file.tag, + child: ThumbnailWidget( + memory.file, + shouldShowArchiveStatus: false, + key: Key("memories" + memory.file.tag), + ), ), ); } From ef206bd3b842b7e12daf5fc5d0427fdccbd038b0 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 13 Jan 2024 16:23:18 +0530 Subject: [PATCH 135/175] add shadow for new memory cover widget --- lib/ui/home/memories/memory_cover_widget_new.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index c8b158664..05b7b2f63 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -76,7 +76,11 @@ class _MemoryCoverWidgetNewState extends State { spreadRadius: MemoryCoverWidgetNew.centerStrokeWidth, blurRadius: 0, ), - // ...shadowFloatLight, + const BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.13), + blurRadius: 3, + offset: Offset(1, 1), + ), ], borderRadius: BorderRadius.circular(5), ), From 81493927b38bfa0c28c0861318826cfa8e06c9f0 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 13 Jan 2024 16:47:28 +0530 Subject: [PATCH 136/175] add border new memory cover widget in dark mode --- lib/ui/home/memories/memories_widget.dart | 2 +- .../memories/memory_cover_widget_new.dart | 40 ++++++++++++------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 19a1a4126..7919471c6 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -64,7 +64,7 @@ class _MemoriesWidgetState extends State { final collatedMemories = _collateMemories(memories); return SizedBox( - height: 125 + MemoryCoverWidgetNew.centerStrokeWidth * 2, + height: 125, child: ListView.builder( physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index 05b7b2f63..37aa3b924 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -10,7 +10,7 @@ class MemoryCoverWidgetNew extends StatefulWidget { final List memories; final ScrollController controller; final double offsetOfItem; - static const centerStrokeWidth = 0.5; + static const centerStrokeWidth = 1.0; const MemoryCoverWidgetNew({ required this.memories, @@ -37,6 +37,7 @@ class _MemoryCoverWidgetNewState extends State { final title = _getTitle(widget.memories[index]); final memory = widget.memories[index]; final isSeen = memory.isSeen(); + final currentTheme = MediaQuery.platformBrightnessOf(context); return AnimatedBuilder( animation: widget.controller, @@ -71,9 +72,12 @@ class _MemoryCoverWidgetNewState extends State { boxShadow: [ BoxShadow( color: isSeen - ? Colors.transparent + ? currentTheme == Brightness.dark + ? const Color.fromRGBO(104, 104, 104, 0.32) + : Colors.transparent : const Color.fromRGBO(1, 222, 77, 0.11), - spreadRadius: MemoryCoverWidgetNew.centerStrokeWidth, + spreadRadius: + MemoryCoverWidgetNew.centerStrokeWidth / 2, blurRadius: 0, ), const BoxShadow( @@ -91,18 +95,24 @@ class _MemoryCoverWidgetNewState extends State { alignment: Alignment.bottomCenter, children: [ child!, - isSeen - ? const SizedBox.shrink() - : Container( - decoration: BoxDecoration( - border: Border.all( - color: - const Color.fromRGBO(1, 222, 77, 0.11), - width: - MemoryCoverWidgetNew.centerStrokeWidth, - ), - ), - ), + Container( + decoration: BoxDecoration( + border: Border.all( + color: isSeen + ? currentTheme == Brightness.dark + ? const Color.fromRGBO( + 104, + 104, + 104, + 0.32, + ) + : Colors.transparent + : const Color.fromRGBO(1, 222, 77, 0.11), + width: MemoryCoverWidgetNew.centerStrokeWidth / 2, + ), + borderRadius: BorderRadius.circular(5), + ), + ), Container( decoration: BoxDecoration( gradient: LinearGradient( From 6ee47cc1bef48dc5229ad01dbac3141df938c584 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 13 Jan 2024 18:57:01 +0530 Subject: [PATCH 137/175] add memories title with a heart icon --- lib/ui/home/memories/memories_widget.dart | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 7919471c6..f187e1e70 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -5,6 +5,7 @@ import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; import 'package:photos/models/memory.dart'; import 'package:photos/services/memories_service.dart'; +import "package:photos/theme/ente_theme.dart"; import "package:photos/ui/home/memories/memory_cover_widget_new.dart"; class MemoriesWidget extends StatefulWidget { @@ -51,6 +52,32 @@ class _MemoriesWidgetState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: Stack( + alignment: Alignment.centerRight, + children: [ + const RotationTransition( + turns: AlwaysStoppedAnimation(20 / 360), + child: Icon( + Icons.favorite_rounded, + color: Color.fromRGBO(0, 179, 60, 0.3), + size: 32, + ), + ), + Padding( + padding: const EdgeInsets.only(right: 16), + child: Text( + "Memories", + style: getEnteTextTheme(context).body, + ), + ), + ], + ), + ), + const SizedBox( + height: 12, + ), _buildMemories(snapshot.data!), const Divider(), ], From 69f8cec4a50d6fd608d6830f933db2d1b968fc15 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 13 Jan 2024 19:15:21 +0530 Subject: [PATCH 138/175] store height and width of MemoryCoverWidget as constants --- lib/ui/home/memories/memories_widget.dart | 2 +- lib/ui/home/memories/memory_cover_widget_new.dart | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index f187e1e70..21ad145bc 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -91,7 +91,7 @@ class _MemoriesWidgetState extends State { final collatedMemories = _collateMemories(memories); return SizedBox( - height: 125, + height: MemoryCoverWidgetNew.height, child: ListView.builder( physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget_new.dart index 37aa3b924..304d77f5a 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget_new.dart @@ -11,6 +11,8 @@ class MemoryCoverWidgetNew extends StatefulWidget { final ScrollController controller; final double offsetOfItem; static const centerStrokeWidth = 1.0; + static const width = 85.0; + static const height = 125.0; const MemoryCoverWidgetNew({ required this.memories, @@ -48,8 +50,8 @@ class _MemoryCoverWidgetNewState extends State { return Padding( padding: const EdgeInsets.symmetric(horizontal: 2.5), //Adding this row is a workaround for making height of memory cover - //render as 125 * scale. Without this, height of rendered memory - //cover will be 125. + //render as [MemoryCoverWidgetNew.height] * scale. Without this, height of rendered memory + //cover will be [MemoryCoverWidgetNew.height]. child: GestureDetector( onTap: () async { await routeToPage( @@ -66,8 +68,8 @@ class _MemoryCoverWidgetNewState extends State { child: Row( children: [ Container( - height: 125 * scale, - width: 85 * scale, + height: MemoryCoverWidgetNew.height * scale, + width: MemoryCoverWidgetNew.width * scale, decoration: BoxDecoration( boxShadow: [ BoxShadow( @@ -177,7 +179,7 @@ class _MemoryCoverWidgetNewState extends State { child: Transform.scale( scale: scale, child: SizedBox( - width: 85, + width: MemoryCoverWidgetNew.width, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, From 179b09992beeb91444d85b4e333b7b1cefd9b2e5 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 13 Jan 2024 19:30:56 +0530 Subject: [PATCH 139/175] remove old MemoryCoverWidget --- lib/ui/home/memories/memory_cover_widget.dart | 131 ------------------ 1 file changed, 131 deletions(-) delete mode 100644 lib/ui/home/memories/memory_cover_widget.dart diff --git a/lib/ui/home/memories/memory_cover_widget.dart b/lib/ui/home/memories/memory_cover_widget.dart deleted file mode 100644 index 5218ba5ff..000000000 --- a/lib/ui/home/memories/memory_cover_widget.dart +++ /dev/null @@ -1,131 +0,0 @@ -import "package:flutter/material.dart"; -import "package:photos/generated/l10n.dart"; -import "package:photos/models/memory.dart"; -import "package:photos/theme/ente_theme.dart"; -import 'package:photos/ui/home/memories/full_screen_memory.dart'; -import "package:photos/ui/viewer/file/thumbnail_widget.dart"; -import "package:photos/utils/navigation_util.dart"; - -class MemoryCovertWidget extends StatefulWidget { - const MemoryCovertWidget({ - Key? key, - required this.memories, - }) : super(key: key); - - final List memories; - - @override - State createState() => _MemoryCovertWidgetState(); -} - -class _MemoryCovertWidgetState extends State { - @override - Widget build(BuildContext context) { - //memories will be empty if all memories are deleted and setState is called - //after FullScreenMemory screen is popped - if (widget.memories.isEmpty) { - return const SizedBox.shrink(); - } - final index = _getNextMemoryIndex(); - final title = _getTitle(widget.memories[index]); - return GestureDetector( - onTap: () async { - await routeToPage( - context, - FullScreenMemoryDataUpdater( - initialIndex: index, - memories: widget.memories, - child: FullScreenMemory(title, index), - ), - forceCustomPageRoute: true, - ); - setState(() {}); - }, - child: Row( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - _buildMemoryItem(context, index), - const Padding(padding: EdgeInsets.all(4)), - Hero( - tag: title, - child: Material( - type: MaterialType.transparency, - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 84), - child: Text( - title, - style: getEnteTextTheme(context).mini, - textAlign: TextAlign.center, - ), - ), - ), - ), - ], - ), - ), - ], - ), - ); - } - - Container _buildMemoryItem(BuildContext context, int index) { - final colorScheme = getEnteColorScheme(context); - final memory = widget.memories[index]; - final isSeen = memory.isSeen(); - return Container( - decoration: BoxDecoration( - border: Border.all( - color: isSeen ? colorScheme.strokeFaint : colorScheme.primary500, - width: 2, - ), - borderRadius: BorderRadius.circular(40), - ), - child: ClipOval( - child: SizedBox( - width: 56, - height: 56, - child: Hero( - tag: "memories" + memory.file.tag, - child: ThumbnailWidget( - memory.file, - shouldShowSyncStatus: false, - key: Key("memories" + memory.file.tag), - ), - ), - ), - ), - ); - } - - // Returns either the first unseen memory or the memory that succeeds the - // last seen memory - int _getNextMemoryIndex() { - int lastSeenIndex = 0; - int lastSeenTimestamp = 0; - for (var index = 0; index < widget.memories.length; index++) { - final memory = widget.memories[index]; - if (!memory.isSeen()) { - return index; - } else { - if (memory.seenTime() > lastSeenTimestamp) { - lastSeenIndex = index; - lastSeenTimestamp = memory.seenTime(); - } - } - } - if (lastSeenIndex == widget.memories.length - 1) { - return 0; - } - return lastSeenIndex + 1; - } - - String _getTitle(Memory memory) { - final present = DateTime.now(); - final then = DateTime.fromMicrosecondsSinceEpoch(memory.file.creationTime!); - final diffInYears = present.year - then.year; - return S.of(context).yearsAgo(diffInYears); - } -} From 7f37cee27d3926186ea12c9a0cf4bbfabf90dcb2 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 13 Jan 2024 19:32:21 +0530 Subject: [PATCH 140/175] rename MemoryCoverWidgetNew to MemoryCoverWidget --- lib/ui/home/memories/memories_widget.dart | 6 +++--- ...dget_new.dart => memory_cover_widget.dart} | 19 +++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) rename lib/ui/home/memories/{memory_cover_widget_new.dart => memory_cover_widget.dart} (94%) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 21ad145bc..068913318 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -6,7 +6,7 @@ import "package:photos/events/memories_setting_changed.dart"; import 'package:photos/models/memory.dart'; import 'package:photos/services/memories_service.dart'; import "package:photos/theme/ente_theme.dart"; -import "package:photos/ui/home/memories/memory_cover_widget_new.dart"; +import 'package:photos/ui/home/memories/memory_cover_widget.dart'; class MemoriesWidget extends StatefulWidget { const MemoriesWidget({Key? key}) : super(key: key); @@ -91,7 +91,7 @@ class _MemoriesWidgetState extends State { final collatedMemories = _collateMemories(memories); return SizedBox( - height: MemoryCoverWidgetNew.height, + height: MemoryCoverWidget.height, child: ListView.builder( physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, @@ -99,7 +99,7 @@ class _MemoriesWidgetState extends State { itemCount: collatedMemories.length, itemBuilder: (context, itemIndex) { final offsetOfItem = _widthOfItem * itemIndex; - return MemoryCoverWidgetNew( + return MemoryCoverWidget( memories: collatedMemories[itemIndex], controller: _controller, offsetOfItem: offsetOfItem, diff --git a/lib/ui/home/memories/memory_cover_widget_new.dart b/lib/ui/home/memories/memory_cover_widget.dart similarity index 94% rename from lib/ui/home/memories/memory_cover_widget_new.dart rename to lib/ui/home/memories/memory_cover_widget.dart index 304d77f5a..76e9a929a 100644 --- a/lib/ui/home/memories/memory_cover_widget_new.dart +++ b/lib/ui/home/memories/memory_cover_widget.dart @@ -6,7 +6,7 @@ import "package:photos/ui/home/memories/full_screen_memory.dart"; import "package:photos/ui/viewer/file/thumbnail_widget.dart"; import "package:photos/utils/navigation_util.dart"; -class MemoryCoverWidgetNew extends StatefulWidget { +class MemoryCoverWidget extends StatefulWidget { final List memories; final ScrollController controller; final double offsetOfItem; @@ -14,7 +14,7 @@ class MemoryCoverWidgetNew extends StatefulWidget { static const width = 85.0; static const height = 125.0; - const MemoryCoverWidgetNew({ + const MemoryCoverWidget({ required this.memories, required this.controller, required this.offsetOfItem, @@ -22,10 +22,10 @@ class MemoryCoverWidgetNew extends StatefulWidget { }); @override - State createState() => _MemoryCoverWidgetNewState(); + State createState() => _MemoryCoverWidgetState(); } -class _MemoryCoverWidgetNewState extends State { +class _MemoryCoverWidgetState extends State { @override Widget build(BuildContext context) { //memories will be empty if all memories are deleted and setState is called @@ -68,8 +68,8 @@ class _MemoryCoverWidgetNewState extends State { child: Row( children: [ Container( - height: MemoryCoverWidgetNew.height * scale, - width: MemoryCoverWidgetNew.width * scale, + height: MemoryCoverWidget.height * scale, + width: MemoryCoverWidget.width * scale, decoration: BoxDecoration( boxShadow: [ BoxShadow( @@ -78,8 +78,7 @@ class _MemoryCoverWidgetNewState extends State { ? const Color.fromRGBO(104, 104, 104, 0.32) : Colors.transparent : const Color.fromRGBO(1, 222, 77, 0.11), - spreadRadius: - MemoryCoverWidgetNew.centerStrokeWidth / 2, + spreadRadius: MemoryCoverWidget.centerStrokeWidth / 2, blurRadius: 0, ), const BoxShadow( @@ -110,7 +109,7 @@ class _MemoryCoverWidgetNewState extends State { ) : Colors.transparent : const Color.fromRGBO(1, 222, 77, 0.11), - width: MemoryCoverWidgetNew.centerStrokeWidth / 2, + width: MemoryCoverWidget.centerStrokeWidth / 2, ), borderRadius: BorderRadius.circular(5), ), @@ -179,7 +178,7 @@ class _MemoryCoverWidgetNewState extends State { child: Transform.scale( scale: scale, child: SizedBox( - width: MemoryCoverWidgetNew.width, + width: MemoryCoverWidget.width, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, From aa126cdba4630896a8312e062d9453a5f6326549 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 13 Jan 2024 19:45:36 +0530 Subject: [PATCH 141/175] extract string --- lib/generated/l10n.dart | 10 ++++++++++ lib/l10n/intl_cs.arb | 3 ++- lib/l10n/intl_de.arb | 3 ++- lib/l10n/intl_en.arb | 5 +++-- lib/l10n/intl_es.arb | 3 ++- lib/l10n/intl_fr.arb | 3 ++- lib/l10n/intl_it.arb | 3 ++- lib/l10n/intl_ko.arb | 3 ++- lib/l10n/intl_nl.arb | 3 ++- lib/l10n/intl_no.arb | 3 ++- lib/l10n/intl_pl.arb | 3 ++- lib/l10n/intl_pt.arb | 3 ++- lib/l10n/intl_zh.arb | 3 ++- lib/ui/home/memories/memories_widget.dart | 3 ++- 14 files changed, 37 insertions(+), 14 deletions(-) diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index e882159ed..3380d8a8f 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -8307,6 +8307,16 @@ class S { args: [], ); } + + /// `Memories` + String get memories { + return Intl.message( + 'Memories', + name: 'memories', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb index 6a71af50f..241fdf15e 100644 --- a/lib/l10n/intl_cs.arb +++ b/lib/l10n/intl_cs.arb @@ -10,5 +10,6 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", + "memories": "Memories" } \ No newline at end of file diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index f64a91832..a0445b7d0 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1178,5 +1178,6 @@ "selectALocationFirst": "Wähle zuerst einen Standort", "changeLocationOfSelectedItems": "Standort der gewählten Elemente ändern?", "editsToLocationWillOnlyBeSeenWithinEnte": "Änderungen des Standorts werden nur in ente sichtbar sein", - "cleanUncategorized": "Unkategorisiert leeren" + "cleanUncategorized": "Unkategorisiert leeren", + "memories": "Memories" } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index ae31c07d6..cef7ac405 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1187,5 +1187,6 @@ "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", - "cleanUncategorized": "Clean Uncategorized" -} + "cleanUncategorized": "Clean Uncategorized", + "memories": "Memories" +} \ No newline at end of file diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 0720f8d3e..41522054b 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -973,5 +973,6 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", + "memories": "Memories" } \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index fe9c4c64c..ddfdb7569 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1154,5 +1154,6 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", + "memories": "Memories" } \ No newline at end of file diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 7df3892f5..1a6735bba 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1116,5 +1116,6 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", + "memories": "Memories" } \ No newline at end of file diff --git a/lib/l10n/intl_ko.arb b/lib/l10n/intl_ko.arb index 6a71af50f..241fdf15e 100644 --- a/lib/l10n/intl_ko.arb +++ b/lib/l10n/intl_ko.arb @@ -10,5 +10,6 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", + "memories": "Memories" } \ No newline at end of file diff --git a/lib/l10n/intl_nl.arb b/lib/l10n/intl_nl.arb index bb8404dc4..009821332 100644 --- a/lib/l10n/intl_nl.arb +++ b/lib/l10n/intl_nl.arb @@ -1163,5 +1163,6 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", + "memories": "Memories" } \ No newline at end of file diff --git a/lib/l10n/intl_no.arb b/lib/l10n/intl_no.arb index d55787f17..58981e01f 100644 --- a/lib/l10n/intl_no.arb +++ b/lib/l10n/intl_no.arb @@ -24,5 +24,6 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", + "memories": "Memories" } \ No newline at end of file diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 7dfb7abc1..16d20a38f 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -111,5 +111,6 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", + "memories": "Memories" } \ No newline at end of file diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index e0232c58e..e403970cc 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -277,5 +277,6 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", + "memories": "Memories" } \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index 72c9e991d..a16a858d4 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -1186,5 +1186,6 @@ "selectALocationFirst": "首先选择一个位置", "changeLocationOfSelectedItems": "确定要更改所选项目的位置吗?", "editsToLocationWillOnlyBeSeenWithinEnte": "对位置的编辑只能在 Ente 内看到", - "cleanUncategorized": "清除未分类的" + "cleanUncategorized": "清除未分类的", + "memories": "Memories" } \ No newline at end of file diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 068913318..da69e6d87 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -3,6 +3,7 @@ import "dart:async"; import 'package:flutter/material.dart'; import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; +import "package:photos/generated/l10n.dart"; import 'package:photos/models/memory.dart'; import 'package:photos/services/memories_service.dart'; import "package:photos/theme/ente_theme.dart"; @@ -68,7 +69,7 @@ class _MemoriesWidgetState extends State { Padding( padding: const EdgeInsets.only(right: 16), child: Text( - "Memories", + S.of(context).memories, style: getEnteTextTheme(context).body, ), ), From 07fa8cd235fe8629917cc91e311fb4f4bff43eb6 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 13 Jan 2024 19:46:52 +0530 Subject: [PATCH 142/175] remove divider below memories --- lib/ui/home/memories/memories_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index da69e6d87..6d1c9aa65 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -80,7 +80,7 @@ class _MemoriesWidgetState extends State { height: 12, ), _buildMemories(snapshot.data!), - const Divider(), + const SizedBox(height: 10), ], ); } From 2b917ded840001e7f2344808f30010b2f95f2a27 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Sat, 13 Jan 2024 19:56:42 +0530 Subject: [PATCH 143/175] bump up version to 0.8.36 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index e74135aa7..3f6f66a63 100644 --- a/pubspec.yaml +++ b/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.8.35+555 +version: 0.8.36+556 environment: sdk: ">=3.0.0 <4.0.0" From ed73e0cb24b9ffd25faf2ecdb9425f9f1b4b7499 Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Mon, 15 Jan 2024 00:09:26 +0000 Subject: [PATCH 144/175] New Crowdin translations by GitHub Action --- lib/l10n/intl_zh.arb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index a16a858d4..b6ba24221 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -410,6 +410,7 @@ "magicSearch": "魔法搜索", "magicSearchDescription": "请使用我们的桌面应用程序来为您库中的待处理项目建立索引。", "loadingModel": "正在下载模型...", + "waitingForWifi": "正在等待 WiFi...", "status": "状态", "indexedItems": "已索引项目", "pendingItems": "待处理项目", @@ -1187,5 +1188,5 @@ "changeLocationOfSelectedItems": "确定要更改所选项目的位置吗?", "editsToLocationWillOnlyBeSeenWithinEnte": "对位置的编辑只能在 Ente 内看到", "cleanUncategorized": "清除未分类的", - "memories": "Memories" + "memories": "回忆" } \ No newline at end of file From 57dc3742caab9ccfe655f3791fe9f692b22aeccb Mon Sep 17 00:00:00 2001 From: ashilkn Date: Mon, 15 Jan 2024 11:17:45 +0530 Subject: [PATCH 145/175] Revert "extract string" This reverts commit aa126cdba4630896a8312e062d9453a5f6326549. --- lib/generated/l10n.dart | 10 ---------- lib/l10n/intl_cs.arb | 3 +-- lib/l10n/intl_de.arb | 3 +-- lib/l10n/intl_en.arb | 5 ++--- lib/l10n/intl_es.arb | 3 +-- lib/l10n/intl_fr.arb | 3 +-- lib/l10n/intl_it.arb | 3 +-- lib/l10n/intl_ko.arb | 3 +-- lib/l10n/intl_nl.arb | 3 +-- lib/l10n/intl_no.arb | 3 +-- lib/l10n/intl_pl.arb | 3 +-- lib/l10n/intl_pt.arb | 3 +-- lib/l10n/intl_zh.arb | 3 +-- lib/ui/home/memories/memories_widget.dart | 3 +-- 14 files changed, 14 insertions(+), 37 deletions(-) diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 3380d8a8f..e882159ed 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -8307,16 +8307,6 @@ class S { args: [], ); } - - /// `Memories` - String get memories { - return Intl.message( - 'Memories', - name: 'memories', - desc: '', - args: [], - ); - } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_cs.arb b/lib/l10n/intl_cs.arb index 241fdf15e..6a71af50f 100644 --- a/lib/l10n/intl_cs.arb +++ b/lib/l10n/intl_cs.arb @@ -10,6 +10,5 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", - "memories": "Memories" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" } \ No newline at end of file diff --git a/lib/l10n/intl_de.arb b/lib/l10n/intl_de.arb index a0445b7d0..f64a91832 100644 --- a/lib/l10n/intl_de.arb +++ b/lib/l10n/intl_de.arb @@ -1178,6 +1178,5 @@ "selectALocationFirst": "Wähle zuerst einen Standort", "changeLocationOfSelectedItems": "Standort der gewählten Elemente ändern?", "editsToLocationWillOnlyBeSeenWithinEnte": "Änderungen des Standorts werden nur in ente sichtbar sein", - "cleanUncategorized": "Unkategorisiert leeren", - "memories": "Memories" + "cleanUncategorized": "Unkategorisiert leeren" } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index cef7ac405..ae31c07d6 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -1187,6 +1187,5 @@ "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", - "cleanUncategorized": "Clean Uncategorized", - "memories": "Memories" -} \ No newline at end of file + "cleanUncategorized": "Clean Uncategorized" +} diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index 41522054b..0720f8d3e 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -973,6 +973,5 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", - "memories": "Memories" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" } \ No newline at end of file diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index ddfdb7569..fe9c4c64c 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -1154,6 +1154,5 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", - "memories": "Memories" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" } \ No newline at end of file diff --git a/lib/l10n/intl_it.arb b/lib/l10n/intl_it.arb index 1a6735bba..7df3892f5 100644 --- a/lib/l10n/intl_it.arb +++ b/lib/l10n/intl_it.arb @@ -1116,6 +1116,5 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", - "memories": "Memories" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" } \ No newline at end of file diff --git a/lib/l10n/intl_ko.arb b/lib/l10n/intl_ko.arb index 241fdf15e..6a71af50f 100644 --- a/lib/l10n/intl_ko.arb +++ b/lib/l10n/intl_ko.arb @@ -10,6 +10,5 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", - "memories": "Memories" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" } \ No newline at end of file diff --git a/lib/l10n/intl_nl.arb b/lib/l10n/intl_nl.arb index 009821332..bb8404dc4 100644 --- a/lib/l10n/intl_nl.arb +++ b/lib/l10n/intl_nl.arb @@ -1163,6 +1163,5 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", - "memories": "Memories" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" } \ No newline at end of file diff --git a/lib/l10n/intl_no.arb b/lib/l10n/intl_no.arb index 58981e01f..d55787f17 100644 --- a/lib/l10n/intl_no.arb +++ b/lib/l10n/intl_no.arb @@ -24,6 +24,5 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", - "memories": "Memories" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" } \ No newline at end of file diff --git a/lib/l10n/intl_pl.arb b/lib/l10n/intl_pl.arb index 16d20a38f..7dfb7abc1 100644 --- a/lib/l10n/intl_pl.arb +++ b/lib/l10n/intl_pl.arb @@ -111,6 +111,5 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", - "memories": "Memories" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" } \ No newline at end of file diff --git a/lib/l10n/intl_pt.arb b/lib/l10n/intl_pt.arb index e403970cc..e0232c58e 100644 --- a/lib/l10n/intl_pt.arb +++ b/lib/l10n/intl_pt.arb @@ -277,6 +277,5 @@ "selectALocation": "Select a location", "selectALocationFirst": "Select a location first", "changeLocationOfSelectedItems": "Change location of selected items?", - "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente", - "memories": "Memories" + "editsToLocationWillOnlyBeSeenWithinEnte": "Edits to location will only be seen within Ente" } \ No newline at end of file diff --git a/lib/l10n/intl_zh.arb b/lib/l10n/intl_zh.arb index a16a858d4..72c9e991d 100644 --- a/lib/l10n/intl_zh.arb +++ b/lib/l10n/intl_zh.arb @@ -1186,6 +1186,5 @@ "selectALocationFirst": "首先选择一个位置", "changeLocationOfSelectedItems": "确定要更改所选项目的位置吗?", "editsToLocationWillOnlyBeSeenWithinEnte": "对位置的编辑只能在 Ente 内看到", - "cleanUncategorized": "清除未分类的", - "memories": "Memories" + "cleanUncategorized": "清除未分类的" } \ No newline at end of file diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 6d1c9aa65..6254cbfec 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -3,7 +3,6 @@ import "dart:async"; import 'package:flutter/material.dart'; import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; -import "package:photos/generated/l10n.dart"; import 'package:photos/models/memory.dart'; import 'package:photos/services/memories_service.dart'; import "package:photos/theme/ente_theme.dart"; @@ -69,7 +68,7 @@ class _MemoriesWidgetState extends State { Padding( padding: const EdgeInsets.only(right: 16), child: Text( - S.of(context).memories, + "Memories", style: getEnteTextTheme(context).body, ), ), From d62d7d07434f5719fe93aa4b524d1a9737ea24f9 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Mon, 15 Jan 2024 11:19:49 +0530 Subject: [PATCH 146/175] remove 'memories' title on header of home tab --- lib/ui/home/memories/memories_widget.dart | 24 ----------------------- 1 file changed, 24 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 6254cbfec..75f2a6f5a 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -5,7 +5,6 @@ import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; import 'package:photos/models/memory.dart'; import 'package:photos/services/memories_service.dart'; -import "package:photos/theme/ente_theme.dart"; import 'package:photos/ui/home/memories/memory_cover_widget.dart'; class MemoriesWidget extends StatefulWidget { @@ -52,29 +51,6 @@ class _MemoriesWidgetState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: Stack( - alignment: Alignment.centerRight, - children: [ - const RotationTransition( - turns: AlwaysStoppedAnimation(20 / 360), - child: Icon( - Icons.favorite_rounded, - color: Color.fromRGBO(0, 179, 60, 0.3), - size: 32, - ), - ), - Padding( - padding: const EdgeInsets.only(right: 16), - child: Text( - "Memories", - style: getEnteTextTheme(context).body, - ), - ), - ], - ), - ), const SizedBox( height: 12, ), From 314814074c71eb1e75f8933631dd1904e0a1f2cb Mon Sep 17 00:00:00 2001 From: ashilkn Date: Mon, 15 Jan 2024 11:57:31 +0530 Subject: [PATCH 147/175] adjust colors and gradients --- lib/ui/home/memories/memory_cover_widget.dart | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/ui/home/memories/memory_cover_widget.dart b/lib/ui/home/memories/memory_cover_widget.dart index 76e9a929a..c93da0ef4 100644 --- a/lib/ui/home/memories/memory_cover_widget.dart +++ b/lib/ui/home/memories/memory_cover_widget.dart @@ -134,11 +134,11 @@ class _MemoryCoverWidgetState extends State { gradient: LinearGradient( stops: [0, 0.35, 0.5], colors: [ - Color.fromARGB(71, 1, 222, 78), - Color(0x1901DE4D), - Color(0x0001DE4D), + Color.fromRGBO(1, 222, 78, 0.392), + Color.fromRGBO(1, 222, 77, 0.1), + Colors.transparent, ], - transform: GradientRotation(-1.2), + transform: GradientRotation(-1.1), ), ), ), @@ -157,10 +157,12 @@ class _MemoryCoverWidgetState extends State { child: Container( decoration: const BoxDecoration( gradient: LinearGradient( - stops: [0, 0.5, 1], + stops: [0, 0.1, 0.5, 0.9, 1], colors: [ Colors.transparent, - Color(0xFF01DE4D), + Color.fromRGBO(1, 222, 77, 0.1), + Color.fromRGBO(1, 222, 77, 1), + Color.fromRGBO(1, 222, 77, 0.1), Colors.transparent, ], ), From ea8c648ce4a9dea2c4e25548758aa2e04d8bc496 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Mon, 15 Jan 2024 12:08:13 +0530 Subject: [PATCH 148/175] tweak again --- lib/ui/home/memories/memory_cover_widget.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ui/home/memories/memory_cover_widget.dart b/lib/ui/home/memories/memory_cover_widget.dart index c93da0ef4..74ffe396c 100644 --- a/lib/ui/home/memories/memory_cover_widget.dart +++ b/lib/ui/home/memories/memory_cover_widget.dart @@ -132,10 +132,10 @@ class _MemoryCoverWidgetState extends State { : Container( decoration: const BoxDecoration( gradient: LinearGradient( - stops: [0, 0.35, 0.5], + stops: [0, 0.27, 0.4], colors: [ - Color.fromRGBO(1, 222, 78, 0.392), - Color.fromRGBO(1, 222, 77, 0.1), + Color.fromRGBO(1, 222, 78, 0.293), + Color.fromRGBO(1, 222, 77, 0.07), Colors.transparent, ], transform: GradientRotation(-1.1), From a7e6c34b13bdd115407fd96e25e474f11b64f327 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Mon, 15 Jan 2024 12:40:05 +0530 Subject: [PATCH 149/175] log time taken to fetch memories --- lib/services/memories_service.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/services/memories_service.dart b/lib/services/memories_service.dart index 2bfefcb2f..646113128 100644 --- a/lib/services/memories_service.dart +++ b/lib/services/memories_service.dart @@ -85,6 +85,7 @@ class MemoriesService extends ChangeNotifier { } Future> _fetchMemories() async { + final stopWatch = Stopwatch()..start(); _logger.info("Fetching memories"); final presentTime = DateTime.now(); final present = presentTime.subtract( @@ -121,6 +122,8 @@ class MemoriesService extends ChangeNotifier { } } _cachedMemories = memories; + stopWatch.stop(); + _logger.info("Fetched memories, duration: ${stopWatch.elapsed}"); return _cachedMemories!; } From 0b2b5689db1cfb76a0b5905238e99bf6c21978f3 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 15 Jan 2024 15:19:07 +0530 Subject: [PATCH 150/175] Ensure framework is initialized before fetching the state --- lib/services/semantic_search/semantic_search_service.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/services/semantic_search/semantic_search_service.dart b/lib/services/semantic_search/semantic_search_service.dart index ddb03d78b..37b1901d5 100644 --- a/lib/services/semantic_search/semantic_search_service.dart +++ b/lib/services/semantic_search/semantic_search_service.dart @@ -151,6 +151,9 @@ class SemanticSearchService { } InitializationState getFrameworkInitializationState() { + if (!_hasInitialized) { + return InitializationState.notInitialized; + } return _mlFramework.initializationState; } From 0ad91da2bda33edae13382427d9988fb406cdd29 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 15 Jan 2024 15:19:10 +0530 Subject: [PATCH 151/175] Update translations --- lib/generated/intl/messages_cs.dart | 1 + lib/generated/intl/messages_de.dart | 1 + lib/generated/intl/messages_en.dart | 1 + lib/generated/intl/messages_es.dart | 1 + lib/generated/intl/messages_fr.dart | 1 + lib/generated/intl/messages_it.dart | 1 + lib/generated/intl/messages_ko.dart | 1 + lib/generated/intl/messages_nl.dart | 1 + lib/generated/intl/messages_no.dart | 1 + lib/generated/intl/messages_pl.dart | 1 + lib/generated/intl/messages_pt.dart | 1 + lib/generated/intl/messages_zh.dart | 1 + 12 files changed, 12 insertions(+) diff --git a/lib/generated/intl/messages_cs.dart b/lib/generated/intl/messages_cs.dart index e91d3502e..9493f4bda 100644 --- a/lib/generated/intl/messages_cs.dart +++ b/lib/generated/intl/messages_cs.dart @@ -34,6 +34,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage( "Edits to location will only be seen within Ente"), "fileTypes": MessageLookupByLibrary.simpleMessage("File types"), + "memories": MessageLookupByLibrary.simpleMessage("Memories"), "modifyYourQueryOrTrySearchingFor": MessageLookupByLibrary.simpleMessage( "Modify your query, or try searching for"), diff --git a/lib/generated/intl/messages_de.dart b/lib/generated/intl/messages_de.dart index 7d208e1ec..cad5f3479 100644 --- a/lib/generated/intl/messages_de.dart +++ b/lib/generated/intl/messages_de.dart @@ -898,6 +898,7 @@ class MessageLookup extends MessageLookupByLibrary { "maps": MessageLookupByLibrary.simpleMessage("Karten"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), + "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("Merchandise"), "mobileWebDesktop": diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 254eacaa9..79dd28c5d 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -871,6 +871,7 @@ class MessageLookup extends MessageLookupByLibrary { "maps": MessageLookupByLibrary.simpleMessage("Maps"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), + "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("Merchandise"), "mobileWebDesktop": diff --git a/lib/generated/intl/messages_es.dart b/lib/generated/intl/messages_es.dart index 95868cb62..c1c6ebe31 100644 --- a/lib/generated/intl/messages_es.dart +++ b/lib/generated/intl/messages_es.dart @@ -790,6 +790,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Administrar tu suscripción"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), + "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("Mercancías"), "mobileWebDesktop": diff --git a/lib/generated/intl/messages_fr.dart b/lib/generated/intl/messages_fr.dart index abeb3c550..182847d07 100644 --- a/lib/generated/intl/messages_fr.dart +++ b/lib/generated/intl/messages_fr.dart @@ -898,6 +898,7 @@ class MessageLookup extends MessageLookupByLibrary { "maps": MessageLookupByLibrary.simpleMessage("Cartes"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), + "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("Marchandise"), "mobileWebDesktop": diff --git a/lib/generated/intl/messages_it.dart b/lib/generated/intl/messages_it.dart index 7e4ef27f4..21f473e41 100644 --- a/lib/generated/intl/messages_it.dart +++ b/lib/generated/intl/messages_it.dart @@ -867,6 +867,7 @@ class MessageLookup extends MessageLookupByLibrary { "maps": MessageLookupByLibrary.simpleMessage("Mappe"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), + "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("Merchandise"), "mobileWebDesktop": diff --git a/lib/generated/intl/messages_ko.dart b/lib/generated/intl/messages_ko.dart index d9c3fcd9e..d06efa0df 100644 --- a/lib/generated/intl/messages_ko.dart +++ b/lib/generated/intl/messages_ko.dart @@ -34,6 +34,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage( "Edits to location will only be seen within Ente"), "fileTypes": MessageLookupByLibrary.simpleMessage("File types"), + "memories": MessageLookupByLibrary.simpleMessage("Memories"), "modifyYourQueryOrTrySearchingFor": MessageLookupByLibrary.simpleMessage( "Modify your query, or try searching for"), diff --git a/lib/generated/intl/messages_nl.dart b/lib/generated/intl/messages_nl.dart index eedf810c1..cff3e1a98 100644 --- a/lib/generated/intl/messages_nl.dart +++ b/lib/generated/intl/messages_nl.dart @@ -889,6 +889,7 @@ class MessageLookup extends MessageLookupByLibrary { "maps": MessageLookupByLibrary.simpleMessage("Kaarten"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), + "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("Merchandise"), "mobileWebDesktop": diff --git a/lib/generated/intl/messages_no.dart b/lib/generated/intl/messages_no.dart index 97bb1d5ea..3ccfea342 100644 --- a/lib/generated/intl/messages_no.dart +++ b/lib/generated/intl/messages_no.dart @@ -56,6 +56,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Ugyldig e-postadresse"), "kindlyHelpUsWithThisInformation": MessageLookupByLibrary.simpleMessage( "Vær vennlig og hjelp oss med denne informasjonen"), + "memories": MessageLookupByLibrary.simpleMessage("Memories"), "modifyYourQueryOrTrySearchingFor": MessageLookupByLibrary.simpleMessage( "Modify your query, or try searching for"), diff --git a/lib/generated/intl/messages_pl.dart b/lib/generated/intl/messages_pl.dart index aecb7c89b..361451508 100644 --- a/lib/generated/intl/messages_pl.dart +++ b/lib/generated/intl/messages_pl.dart @@ -116,6 +116,7 @@ class MessageLookup extends MessageLookupByLibrary { "kindlyHelpUsWithThisInformation": MessageLookupByLibrary.simpleMessage("Pomóż nam z tą informacją"), "logInLabel": MessageLookupByLibrary.simpleMessage("Zaloguj się"), + "memories": MessageLookupByLibrary.simpleMessage("Memories"), "moderateStrength": MessageLookupByLibrary.simpleMessage("Umiarkowana"), "modifyYourQueryOrTrySearchingFor": MessageLookupByLibrary.simpleMessage( diff --git a/lib/generated/intl/messages_pt.dart b/lib/generated/intl/messages_pt.dart index 28bf3ac07..81a28e88e 100644 --- a/lib/generated/intl/messages_pt.dart +++ b/lib/generated/intl/messages_pt.dart @@ -266,6 +266,7 @@ class MessageLookup extends MessageLookupByLibrary { "lostDevice": MessageLookupByLibrary.simpleMessage("Dispositivo perdido?"), "manage": MessageLookupByLibrary.simpleMessage("Gerenciar"), + "memories": MessageLookupByLibrary.simpleMessage("Memories"), "moderateStrength": MessageLookupByLibrary.simpleMessage("Moderada"), "modifyYourQueryOrTrySearchingFor": MessageLookupByLibrary.simpleMessage( diff --git a/lib/generated/intl/messages_zh.dart b/lib/generated/intl/messages_zh.dart index d04c68521..c8a1bf85c 100644 --- a/lib/generated/intl/messages_zh.dart +++ b/lib/generated/intl/messages_zh.dart @@ -738,6 +738,7 @@ class MessageLookup extends MessageLookupByLibrary { "maps": MessageLookupByLibrary.simpleMessage("地图"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), + "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("商品"), "mobileWebDesktop": From 3c3e55856f251e468d91736c3e9577ffac87d411 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Mon, 15 Jan 2024 18:41:02 +0530 Subject: [PATCH 152/175] animate MemoryCoverWidget when it appears --- lib/ui/home/memories/memories_widget.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 75f2a6f5a..90203930a 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -1,6 +1,7 @@ import "dart:async"; import 'package:flutter/material.dart'; +import "package:flutter_animate/flutter_animate.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; import 'package:photos/models/memory.dart'; @@ -79,7 +80,13 @@ class _MemoriesWidgetState extends State { memories: collatedMemories[itemIndex], controller: _controller, offsetOfItem: offsetOfItem, - ); + ) + .animate(delay: Duration(milliseconds: 75 * itemIndex)) + .fadeIn( + duration: const Duration(milliseconds: 250), + curve: Curves.easeInOutCubic, + ) + .slideX(begin: 0.04, end: 0); }, ), ); From 9f73de352b913ac969e9f52571a7059a4eea7f45 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Wed, 17 Jan 2024 11:15:19 +0530 Subject: [PATCH 153/175] size the memory cover wiget according to the width of screen --- lib/ui/home/memories/memories_widget.dart | 17 ++++++++++++++--- lib/ui/home/memories/memory_cover_widget.dart | 15 +++++++++------ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 90203930a..2db6300f6 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -16,9 +16,10 @@ class MemoriesWidget extends StatefulWidget { } class _MemoriesWidgetState extends State { - final double _widthOfItem = 85; late ScrollController _controller; late StreamSubscription _subscription; + double _maxHeight = 0; + double _maxWidth = 0; @override void initState() { @@ -31,6 +32,14 @@ class _MemoriesWidgetState extends State { _controller = ScrollController(); } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + final screenWidth = MediaQuery.sizeOf(context).width; + _maxWidth = screenWidth / 4; + _maxHeight = _maxWidth / MemoryCoverWidget.aspectRatio; + } + @override void dispose() { _subscription.cancel(); @@ -68,18 +77,20 @@ class _MemoriesWidgetState extends State { final collatedMemories = _collateMemories(memories); return SizedBox( - height: MemoryCoverWidget.height, + height: _maxHeight, child: ListView.builder( physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, controller: _controller, itemCount: collatedMemories.length, itemBuilder: (context, itemIndex) { - final offsetOfItem = _widthOfItem * itemIndex; + final offsetOfItem = _maxWidth * itemIndex; return MemoryCoverWidget( memories: collatedMemories[itemIndex], controller: _controller, offsetOfItem: offsetOfItem, + maxHeight: _maxHeight, + maxWidth: _maxWidth, ) .animate(delay: Duration(milliseconds: 75 * itemIndex)) .fadeIn( diff --git a/lib/ui/home/memories/memory_cover_widget.dart b/lib/ui/home/memories/memory_cover_widget.dart index 74ffe396c..45b07f319 100644 --- a/lib/ui/home/memories/memory_cover_widget.dart +++ b/lib/ui/home/memories/memory_cover_widget.dart @@ -10,14 +10,17 @@ class MemoryCoverWidget extends StatefulWidget { final List memories; final ScrollController controller; final double offsetOfItem; + final double maxHeight; + final double maxWidth; static const centerStrokeWidth = 1.0; - static const width = 85.0; - static const height = 125.0; + static const aspectRatio = 0.68; const MemoryCoverWidget({ required this.memories, required this.controller, required this.offsetOfItem, + required this.maxHeight, + required this.maxWidth, super.key, }); @@ -68,8 +71,8 @@ class _MemoryCoverWidgetState extends State { child: Row( children: [ Container( - height: MemoryCoverWidget.height * scale, - width: MemoryCoverWidget.width * scale, + height: widget.maxHeight * scale, + width: widget.maxWidth * scale, decoration: BoxDecoration( boxShadow: [ BoxShadow( @@ -168,7 +171,7 @@ class _MemoryCoverWidgetState extends State { ), ), height: 1 * scale, - width: 68 * scale, + width: (widget.maxWidth - 16) * scale, ), ), ], @@ -180,7 +183,7 @@ class _MemoryCoverWidgetState extends State { child: Transform.scale( scale: scale, child: SizedBox( - width: MemoryCoverWidget.width, + width: widget.maxWidth, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, From 72af4b9598a2c744f5c960c691b2da9cc4750d6f Mon Sep 17 00:00:00 2001 From: ashilkn Date: Wed, 17 Jan 2024 11:39:36 +0530 Subject: [PATCH 154/175] change maxWidth in such a way than there will only be 2 or 4 or 6 or 8 or.... memories in header with increase in width of screen --- lib/ui/home/memories/memories_widget.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 2db6300f6..c86e9c0f9 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -18,8 +18,8 @@ class MemoriesWidget extends StatefulWidget { class _MemoriesWidgetState extends State { late ScrollController _controller; late StreamSubscription _subscription; - double _maxHeight = 0; - double _maxWidth = 0; + late double _maxHeight; + late double _maxWidth; @override void initState() { @@ -36,7 +36,8 @@ class _MemoriesWidgetState extends State { void didChangeDependencies() { super.didChangeDependencies(); final screenWidth = MediaQuery.sizeOf(context).width; - _maxWidth = screenWidth / 4; + final factor = (screenWidth / 220).ceil(); + _maxWidth = screenWidth / (factor * 2); _maxHeight = _maxWidth / MemoryCoverWidget.aspectRatio; } From 67c0912fc73dfa89cc63dc5de7b69f28bb814c8e Mon Sep 17 00:00:00 2001 From: ashilkn Date: Wed, 17 Jan 2024 11:52:05 +0530 Subject: [PATCH 155/175] decrease the degree to which a memory cover widget is scaled when scrolled --- lib/ui/home/memories/memories_widget.dart | 1 + lib/ui/home/memories/memory_cover_widget.dart | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index c86e9c0f9..d6af4949e 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -36,6 +36,7 @@ class _MemoriesWidgetState extends State { void didChangeDependencies() { super.didChangeDependencies(); final screenWidth = MediaQuery.sizeOf(context).width; + //factor will be 2 for most phones in portrait mode final factor = (screenWidth / 220).ceil(); _maxWidth = screenWidth / (factor * 2); _maxHeight = _maxWidth / MemoryCoverWidget.aspectRatio; diff --git a/lib/ui/home/memories/memory_cover_widget.dart b/lib/ui/home/memories/memory_cover_widget.dart index 45b07f319..ba2e4a736 100644 --- a/lib/ui/home/memories/memory_cover_widget.dart +++ b/lib/ui/home/memories/memory_cover_widget.dart @@ -49,7 +49,7 @@ class _MemoryCoverWidgetState extends State { builder: (context, child) { final diff = (widget.controller.offset - widget.offsetOfItem) + widthOfScreen / 7; - final scale = 1 - (diff / widthOfScreen).abs() / 3; + final scale = 1 - (diff / widthOfScreen).abs() / 3.7; return Padding( padding: const EdgeInsets.symmetric(horizontal: 2.5), //Adding this row is a workaround for making height of memory cover From b4705d92ae6f21477eddbd1cf349117a30192ede Mon Sep 17 00:00:00 2001 From: ashilkn Date: Wed, 17 Jan 2024 14:48:42 +0530 Subject: [PATCH 156/175] show loading state when memories are being fetched --- lib/ui/home/memories/memories_widget.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index d6af4949e..09f279ca1 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -6,6 +6,7 @@ import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; import 'package:photos/models/memory.dart'; import 'package:photos/services/memories_service.dart'; +import "package:photos/ui/common/loading_widget.dart"; import 'package:photos/ui/home/memories/memory_cover_widget.dart'; class MemoriesWidget extends StatefulWidget { @@ -58,7 +59,10 @@ class _MemoriesWidgetState extends State { future: MemoriesService.instance.getMemories(), builder: (context, snapshot) { if (snapshot.hasError || !snapshot.hasData || snapshot.data!.isEmpty) { - return const SizedBox.shrink(); + return SizedBox( + height: _maxHeight + 12 + 10, + child: const EnteLoadingWidget(), + ); } else { return Column( crossAxisAlignment: CrossAxisAlignment.start, From 6071884f525ecf12d5af88722b30169605657ffb Mon Sep 17 00:00:00 2001 From: ashilkn Date: Wed, 17 Jan 2024 15:04:41 +0530 Subject: [PATCH 157/175] bump up version to 0.8.38 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 3f6f66a63..fbbf42df5 100644 --- a/pubspec.yaml +++ b/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.8.36+556 +version: 0.8.38+558 environment: sdk: ">=3.0.0 <4.0.0" From 6f9600690b4d176c6cc2d82ba96fa50b751c00f7 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 18 Jan 2024 01:42:54 +0530 Subject: [PATCH 158/175] Use method via FFI to compute memories --- lib/db/files_db.dart | 49 ++++++++++++++++++++++++++++++ lib/services/memories_service.dart | 2 +- pubspec.lock | 12 ++++++-- pubspec.yaml | 1 + 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/lib/db/files_db.dart b/lib/db/files_db.dart index 2e7a760b5..39ff83210 100644 --- a/lib/db/files_db.dart +++ b/lib/db/files_db.dart @@ -15,6 +15,7 @@ import "package:photos/services/filter/db_filters.dart"; import 'package:photos/utils/file_uploader_util.dart'; import 'package:sqflite/sqflite.dart'; import 'package:sqflite_migration/sqflite_migration.dart'; +import 'package:sqlite3/sqlite3.dart' as sqlite3; class FilesDB { /* @@ -100,6 +101,7 @@ class FilesDB { // only have a single app-wide reference to the database static Future? _dbFuture; + static Future? _ffiDBFuture; Future get database async { // lazily instantiate the db the first time it is accessed @@ -107,6 +109,11 @@ class FilesDB { return _dbFuture!; } + Future get ffiDB async { + _ffiDBFuture ??= _initFFIDatabase(); + return _ffiDBFuture!; + } + // this opens the database (and creates it if it doesn't exist) Future _initDatabase() async { final Directory documentsDirectory = @@ -116,6 +123,14 @@ class FilesDB { return await openDatabaseWithMigration(path, dbConfig); } + Future _initFFIDatabase() async { + final Directory documentsDirectory = + await getApplicationDocumentsDirectory(); + final String path = join(documentsDirectory.path, _databaseName); + _logger.info("DB path " + path); + return sqlite3.sqlite3.open(path); + } + // SQL code to create the database table static List createTable(String tableName) { return [ @@ -749,6 +764,40 @@ class FilesDB { ); } + Future> getFilesCreatedWithinDurationsSync( + List> durations, + Set ignoredCollectionIDs, { + int? visibility, + String order = 'ASC', + }) async { + if (durations.isEmpty) { + return []; + } + final db = await instance.ffiDB; + String whereClause = "( "; + for (int index = 0; index < durations.length; index++) { + whereClause += "($columnCreationTime >= " + + durations[index][0].toString() + + " AND $columnCreationTime < " + + durations[index][1].toString() + + ")"; + if (index != durations.length - 1) { + whereClause += " OR "; + } else if (visibility != null) { + whereClause += ' AND $columnMMdVisibility = $visibility'; + } + } + whereClause += ")"; + final results = db.select( + 'select * from $filesTable where $whereClause order by $columnCreationTime $order', + ); + final files = convertToFiles(results); + return applyDBFilters( + files, + DBFilterOptions(ignoredCollectionIDs: ignoredCollectionIDs), + ); + } + // Files which user added to a collection manually but they are not // uploaded yet or files belonging to a collection which is marked for backup Future> getFilesPendingForUpload() async { diff --git a/lib/services/memories_service.dart b/lib/services/memories_service.dart index 646113128..de68e2dab 100644 --- a/lib/services/memories_service.dart +++ b/lib/services/memories_service.dart @@ -107,7 +107,7 @@ class MemoriesService extends ChangeNotifier { } final ignoredCollections = CollectionsService.instance.archivedOrHiddenCollectionIds(); - final files = await _filesDB.getFilesCreatedWithinDurations( + final files = await _filesDB.getFilesCreatedWithinDurationsSync( durations, ignoredCollections, visibility: visibleVisibility, diff --git a/pubspec.lock b/pubspec.lock index 5129dcf80..71a4a3a5a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1931,10 +1931,10 @@ packages: dependency: transitive description: name: sqflite_common - sha256: "8ed044102f3135add97be8653662052838859f5400075ef227f8ad72ae320803" + sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6 url: "https://pub.dev" source: hosted - version: "2.5.0+1" + version: "2.5.0+2" sqflite_migration: dependency: "direct main" description: @@ -1943,6 +1943,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.0" + sqlite3: + dependency: "direct main" + description: + name: sqlite3 + sha256: db65233e6b99e99b2548932f55a987961bc06d82a31a0665451fa0b4fff4c3fb + url: "https://pub.dev" + source: hosted + version: "2.1.0" stack_trace: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index fbbf42df5..341cf3d5f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -140,6 +140,7 @@ dependencies: shared_preferences: ^2.0.5 sqflite: ^2.3.0 sqflite_migration: ^0.3.0 + sqlite3: ^2.1.0 step_progress_indicator: ^1.0.2 styled_text: ^7.0.0 syncfusion_flutter_core: ^19.2.49 From 641699c092169fb6efa93bcad132b0765ee673bb Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 18 Jan 2024 01:44:13 +0530 Subject: [PATCH 159/175] v0.8.39 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 341cf3d5f..55e7bf125 100644 --- a/pubspec.yaml +++ b/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.8.38+558 +version: 0.8.39+559 environment: sdk: ">=3.0.0 <4.0.0" From 28289605a87c4530b8308641342a1e101899300c Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 18 Jan 2024 01:56:24 +0530 Subject: [PATCH 160/175] Add index on uploaded file ID --- lib/db/files_db.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/db/files_db.dart b/lib/db/files_db.dart index 39ff83210..4e6e7d129 100644 --- a/lib/db/files_db.dart +++ b/lib/db/files_db.dart @@ -87,6 +87,7 @@ class FilesDB { ...updateIndexes(), ...createEntityDataTable(), ...addAddedTime(), + ...addIndexOnUploadedID(), ]; final dbConfig = MigrationConfig( @@ -397,6 +398,14 @@ class FilesDB { ]; } + static List addIndexOnUploadedID() { + return [ + ''' + CREATE INDEX IF NOT EXISTS uploaded_file_id_index ON $filesTable($columnUploadedFileID); + ''' + ]; + } + Future clearTable() async { final db = await instance.database; await db.delete(filesTable); From 6e7621b919fbb7ced2d95b5a748e930aa756e9e0 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 18 Jan 2024 01:56:46 +0530 Subject: [PATCH 161/175] v0.8.40 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 55e7bf125..6c2837913 100644 --- a/pubspec.yaml +++ b/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.8.39+559 +version: 0.8.40+560 environment: sdk: ">=3.0.0 <4.0.0" From 3967003d65a59e688918f41329777a54f0876866 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 18 Jan 2024 02:11:02 +0530 Subject: [PATCH 162/175] Add dependency on sqlite3_flutter_libs --- pubspec.lock | 8 ++++++++ pubspec.yaml | 1 + 2 files changed, 9 insertions(+) diff --git a/pubspec.lock b/pubspec.lock index 71a4a3a5a..e97b1e8e5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1951,6 +1951,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + sqlite3_flutter_libs: + dependency: "direct main" + description: + name: sqlite3_flutter_libs + sha256: "90963b515721d6a71e96f438175cf43c979493ed14822860a300b69694c74eb6" + url: "https://pub.dev" + source: hosted + version: "0.5.19+1" stack_trace: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6c2837913..c8d1e8efd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -141,6 +141,7 @@ dependencies: sqflite: ^2.3.0 sqflite_migration: ^0.3.0 sqlite3: ^2.1.0 + sqlite3_flutter_libs: ^0.5.19+1 step_progress_indicator: ^1.0.2 styled_text: ^7.0.0 syncfusion_flutter_core: ^19.2.49 From 5b4477e20925dcb2c86ed01d025c68c19f53e5ce Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 18 Jan 2024 02:12:02 +0530 Subject: [PATCH 163/175] Update strings --- lib/generated/intl/messages_cs.dart | 1 - lib/generated/intl/messages_de.dart | 1 - lib/generated/intl/messages_en.dart | 1 - lib/generated/intl/messages_es.dart | 1 - lib/generated/intl/messages_fr.dart | 1 - lib/generated/intl/messages_it.dart | 1 - lib/generated/intl/messages_ko.dart | 1 - lib/generated/intl/messages_nl.dart | 1 - lib/generated/intl/messages_no.dart | 1 - lib/generated/intl/messages_pl.dart | 1 - lib/generated/intl/messages_pt.dart | 1 - lib/generated/intl/messages_zh.dart | 2 +- 12 files changed, 1 insertion(+), 12 deletions(-) diff --git a/lib/generated/intl/messages_cs.dart b/lib/generated/intl/messages_cs.dart index 9493f4bda..e91d3502e 100644 --- a/lib/generated/intl/messages_cs.dart +++ b/lib/generated/intl/messages_cs.dart @@ -34,7 +34,6 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage( "Edits to location will only be seen within Ente"), "fileTypes": MessageLookupByLibrary.simpleMessage("File types"), - "memories": MessageLookupByLibrary.simpleMessage("Memories"), "modifyYourQueryOrTrySearchingFor": MessageLookupByLibrary.simpleMessage( "Modify your query, or try searching for"), diff --git a/lib/generated/intl/messages_de.dart b/lib/generated/intl/messages_de.dart index cad5f3479..7d208e1ec 100644 --- a/lib/generated/intl/messages_de.dart +++ b/lib/generated/intl/messages_de.dart @@ -898,7 +898,6 @@ class MessageLookup extends MessageLookupByLibrary { "maps": MessageLookupByLibrary.simpleMessage("Karten"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), - "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("Merchandise"), "mobileWebDesktop": diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 79dd28c5d..254eacaa9 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -871,7 +871,6 @@ class MessageLookup extends MessageLookupByLibrary { "maps": MessageLookupByLibrary.simpleMessage("Maps"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), - "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("Merchandise"), "mobileWebDesktop": diff --git a/lib/generated/intl/messages_es.dart b/lib/generated/intl/messages_es.dart index c1c6ebe31..95868cb62 100644 --- a/lib/generated/intl/messages_es.dart +++ b/lib/generated/intl/messages_es.dart @@ -790,7 +790,6 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Administrar tu suscripción"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), - "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("Mercancías"), "mobileWebDesktop": diff --git a/lib/generated/intl/messages_fr.dart b/lib/generated/intl/messages_fr.dart index 182847d07..abeb3c550 100644 --- a/lib/generated/intl/messages_fr.dart +++ b/lib/generated/intl/messages_fr.dart @@ -898,7 +898,6 @@ class MessageLookup extends MessageLookupByLibrary { "maps": MessageLookupByLibrary.simpleMessage("Cartes"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), - "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("Marchandise"), "mobileWebDesktop": diff --git a/lib/generated/intl/messages_it.dart b/lib/generated/intl/messages_it.dart index 21f473e41..7e4ef27f4 100644 --- a/lib/generated/intl/messages_it.dart +++ b/lib/generated/intl/messages_it.dart @@ -867,7 +867,6 @@ class MessageLookup extends MessageLookupByLibrary { "maps": MessageLookupByLibrary.simpleMessage("Mappe"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), - "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("Merchandise"), "mobileWebDesktop": diff --git a/lib/generated/intl/messages_ko.dart b/lib/generated/intl/messages_ko.dart index d06efa0df..d9c3fcd9e 100644 --- a/lib/generated/intl/messages_ko.dart +++ b/lib/generated/intl/messages_ko.dart @@ -34,7 +34,6 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage( "Edits to location will only be seen within Ente"), "fileTypes": MessageLookupByLibrary.simpleMessage("File types"), - "memories": MessageLookupByLibrary.simpleMessage("Memories"), "modifyYourQueryOrTrySearchingFor": MessageLookupByLibrary.simpleMessage( "Modify your query, or try searching for"), diff --git a/lib/generated/intl/messages_nl.dart b/lib/generated/intl/messages_nl.dart index cff3e1a98..eedf810c1 100644 --- a/lib/generated/intl/messages_nl.dart +++ b/lib/generated/intl/messages_nl.dart @@ -889,7 +889,6 @@ class MessageLookup extends MessageLookupByLibrary { "maps": MessageLookupByLibrary.simpleMessage("Kaarten"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), - "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("Merchandise"), "mobileWebDesktop": diff --git a/lib/generated/intl/messages_no.dart b/lib/generated/intl/messages_no.dart index 3ccfea342..97bb1d5ea 100644 --- a/lib/generated/intl/messages_no.dart +++ b/lib/generated/intl/messages_no.dart @@ -56,7 +56,6 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Ugyldig e-postadresse"), "kindlyHelpUsWithThisInformation": MessageLookupByLibrary.simpleMessage( "Vær vennlig og hjelp oss med denne informasjonen"), - "memories": MessageLookupByLibrary.simpleMessage("Memories"), "modifyYourQueryOrTrySearchingFor": MessageLookupByLibrary.simpleMessage( "Modify your query, or try searching for"), diff --git a/lib/generated/intl/messages_pl.dart b/lib/generated/intl/messages_pl.dart index 361451508..aecb7c89b 100644 --- a/lib/generated/intl/messages_pl.dart +++ b/lib/generated/intl/messages_pl.dart @@ -116,7 +116,6 @@ class MessageLookup extends MessageLookupByLibrary { "kindlyHelpUsWithThisInformation": MessageLookupByLibrary.simpleMessage("Pomóż nam z tą informacją"), "logInLabel": MessageLookupByLibrary.simpleMessage("Zaloguj się"), - "memories": MessageLookupByLibrary.simpleMessage("Memories"), "moderateStrength": MessageLookupByLibrary.simpleMessage("Umiarkowana"), "modifyYourQueryOrTrySearchingFor": MessageLookupByLibrary.simpleMessage( diff --git a/lib/generated/intl/messages_pt.dart b/lib/generated/intl/messages_pt.dart index 81a28e88e..28bf3ac07 100644 --- a/lib/generated/intl/messages_pt.dart +++ b/lib/generated/intl/messages_pt.dart @@ -266,7 +266,6 @@ class MessageLookup extends MessageLookupByLibrary { "lostDevice": MessageLookupByLibrary.simpleMessage("Dispositivo perdido?"), "manage": MessageLookupByLibrary.simpleMessage("Gerenciar"), - "memories": MessageLookupByLibrary.simpleMessage("Memories"), "moderateStrength": MessageLookupByLibrary.simpleMessage("Moderada"), "modifyYourQueryOrTrySearchingFor": MessageLookupByLibrary.simpleMessage( diff --git a/lib/generated/intl/messages_zh.dart b/lib/generated/intl/messages_zh.dart index c8a1bf85c..d8e1f1f44 100644 --- a/lib/generated/intl/messages_zh.dart +++ b/lib/generated/intl/messages_zh.dart @@ -738,7 +738,6 @@ class MessageLookup extends MessageLookupByLibrary { "maps": MessageLookupByLibrary.simpleMessage("地图"), "mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"), "matrix": MessageLookupByLibrary.simpleMessage("Matrix"), - "memories": MessageLookupByLibrary.simpleMessage("Memories"), "memoryCount": m31, "merchandise": MessageLookupByLibrary.simpleMessage("商品"), "mobileWebDesktop": @@ -1217,6 +1216,7 @@ class MessageLookup extends MessageLookupByLibrary { "viewer": MessageLookupByLibrary.simpleMessage("查看者"), "visitWebToManage": MessageLookupByLibrary.simpleMessage("请访问 web.ente.io 来管理您的订阅"), + "waitingForWifi": MessageLookupByLibrary.simpleMessage("正在等待 WiFi..."), "weAreOpenSource": MessageLookupByLibrary.simpleMessage("我们是开源的 !"), "weDontSupportEditingPhotosAndAlbumsThatYouDont": MessageLookupByLibrary.simpleMessage("我们不支持编辑您尚未拥有的照片和相册"), From d27fa918332891a30efbc2513db87a46b32b3eab Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 18 Jan 2024 02:12:15 +0530 Subject: [PATCH 164/175] v0.8.41 --- pubspec.yaml | 2 +- thirdparty/isar | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 160000 thirdparty/isar diff --git a/pubspec.yaml b/pubspec.yaml index c8d1e8efd..8a3a6da2f 100644 --- a/pubspec.yaml +++ b/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.8.40+560 +version: 0.8.41+561 environment: sdk: ">=3.0.0 <4.0.0" diff --git a/thirdparty/isar b/thirdparty/isar new file mode 160000 index 000000000..6643d064a --- /dev/null +++ b/thirdparty/isar @@ -0,0 +1 @@ +Subproject commit 6643d064abf22606b6c6a741ea873e4781115ef4 From 93248497674650576179322915f183b14e1bbef8 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 18 Jan 2024 02:16:53 +0530 Subject: [PATCH 165/175] Remove unhelpful index --- lib/db/files_db.dart | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/db/files_db.dart b/lib/db/files_db.dart index 4e6e7d129..39ff83210 100644 --- a/lib/db/files_db.dart +++ b/lib/db/files_db.dart @@ -87,7 +87,6 @@ class FilesDB { ...updateIndexes(), ...createEntityDataTable(), ...addAddedTime(), - ...addIndexOnUploadedID(), ]; final dbConfig = MigrationConfig( @@ -398,14 +397,6 @@ class FilesDB { ]; } - static List addIndexOnUploadedID() { - return [ - ''' - CREATE INDEX IF NOT EXISTS uploaded_file_id_index ON $filesTable($columnUploadedFileID); - ''' - ]; - } - Future clearTable() async { final db = await instance.database; await db.delete(filesTable); From de677f2ceefd3e4265eeccc3077972ff882fd88d Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 18 Jan 2024 02:17:03 +0530 Subject: [PATCH 166/175] Update iOS project files --- ios/Podfile.lock | 21 +++++++++++++++++++++ ios/Runner.xcodeproj/project.pbxproj | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index ee3a1d132..e7046d210 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -181,6 +181,21 @@ PODS: - sqflite (0.0.3): - Flutter - FMDB (>= 2.7.5) + - sqlite3 (3.45.0): + - sqlite3/common (= 3.45.0) + - sqlite3/common (3.45.0) + - sqlite3/fts5 (3.45.0): + - sqlite3/common + - sqlite3/perf-threadsafe (3.45.0): + - sqlite3/common + - sqlite3/rtree (3.45.0): + - sqlite3/common + - sqlite3_flutter_libs (0.0.1): + - Flutter + - sqlite3 (~> 3.45.0) + - sqlite3/fts5 + - sqlite3/perf-threadsafe + - sqlite3/rtree - Toast (4.0.0) - uni_links (0.0.1): - Flutter @@ -235,6 +250,7 @@ DEPENDENCIES: - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite (from `.symlinks/plugins/sqflite/ios`) + - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) - uni_links (from `.symlinks/plugins/uni_links/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) @@ -264,6 +280,7 @@ SPEC REPOS: - SDWebImageWebPCoder - Sentry - SentryPrivate + - sqlite3 - Toast EXTERNAL SOURCES: @@ -343,6 +360,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" sqflite: :path: ".symlinks/plugins/sqflite/ios" + sqlite3_flutter_libs: + :path: ".symlinks/plugins/sqlite3_flutter_libs/ios" uni_links: :path: ".symlinks/plugins/uni_links/ios" url_launcher_ios: @@ -415,6 +434,8 @@ SPEC CHECKSUMS: share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a + sqlite3: f307b6291c4db7b5086c38d6237446b98a738581 + sqlite3_flutter_libs: aeb4d37509853dfa79d9b59386a2dac5dd079428 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 uni_links: d97da20c7701486ba192624d99bffaaffcfc298a url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 03a95b930..ea4cd3588 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -311,6 +311,8 @@ "${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework", "${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework", "${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework", + "${BUILT_PRODUCTS_DIR}/sqlite3/sqlite3.framework", + "${BUILT_PRODUCTS_DIR}/sqlite3_flutter_libs/sqlite3_flutter_libs.framework", "${BUILT_PRODUCTS_DIR}/uni_links/uni_links.framework", "${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework", "${BUILT_PRODUCTS_DIR}/video_player_avfoundation/video_player_avfoundation.framework", @@ -390,6 +392,8 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3_flutter_libs.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/uni_links.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player_avfoundation.framework", From ad5a0eaaa6d63cc2e4287291f85d3cea06247183 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 18 Jan 2024 02:17:13 +0530 Subject: [PATCH 167/175] v0.8.42 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 8a3a6da2f..ae7664277 100644 --- a/pubspec.yaml +++ b/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.8.41+561 +version: 0.8.42+562 environment: sdk: ">=3.0.0 <4.0.0" From b9e715b993de6e6b5642be4912111ec41245b5d7 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 18 Jan 2024 15:09:28 +0530 Subject: [PATCH 168/175] refactor --- lib/ui/home/header_widget.dart | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/ui/home/header_widget.dart b/lib/ui/home/header_widget.dart index f75d57e77..a322382f1 100644 --- a/lib/ui/home/header_widget.dart +++ b/lib/ui/home/header_widget.dart @@ -1,12 +1,9 @@ import 'package:flutter/widgets.dart'; import 'package:logging/logging.dart'; -import 'package:photos/ui/home/memories/memories_widget.dart'; +import "package:photos/ui/home/memories/memories_widget.dart"; import 'package:photos/ui/home/status_bar_widget.dart'; class HeaderWidget extends StatelessWidget { - static const _memoriesWidget = MemoriesWidget(); - static const _statusBarWidget = StatusBarWidget(); - const HeaderWidget({ Key? key, }) : super(key: key); @@ -14,13 +11,12 @@ class HeaderWidget extends StatelessWidget { @override Widget build(BuildContext context) { Logger("Header").info("Building header widget"); - const list = [ - _statusBarWidget, - _memoriesWidget, - ]; return const Column( crossAxisAlignment: CrossAxisAlignment.start, - children: list, + children: [ + StatusBarWidget(), + MemoriesWidget(), + ], ); } } From 1ab8d942cc25c7376c13b32deb6a7e4b3a0001fb Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 18 Jan 2024 15:17:45 +0530 Subject: [PATCH 169/175] rearrange widget tree to avoid re initializing elements of child wiget in DraggableScrollbar when it's enabled property changes. This was causing memories to rebuild when draggableScrollbar is enabled when all files are loaded in gallery --- lib/ui/huge_listview/draggable_scrollbar.dart | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/ui/huge_listview/draggable_scrollbar.dart b/lib/ui/huge_listview/draggable_scrollbar.dart index c943c4940..f3d215c69 100644 --- a/lib/ui/huge_listview/draggable_scrollbar.dart +++ b/lib/ui/huge_listview/draggable_scrollbar.dart @@ -103,16 +103,14 @@ class DraggableScrollbarState extends State @override Widget build(BuildContext context) { - if (widget.isEnabled) { - return Stack( - children: [ - RepaintBoundary(child: widget.child), - RepaintBoundary(child: buildThumb()), - ], - ); - } else { - return widget.child; - } + return Stack( + children: [ + RepaintBoundary(child: widget.child), + widget.isEnabled + ? RepaintBoundary(child: buildThumb()) + : const SizedBox.shrink(), + ], + ); } Widget buildThumb() => Padding( From 2d6bff0da68c3366a4adf2fbdab82ec539c04df3 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 18 Jan 2024 15:44:03 +0530 Subject: [PATCH 170/175] remove animation on MemoryCoverWidget to stop animation of memories on edges when scrolling memories --- lib/ui/home/memories/memories_widget.dart | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 09f279ca1..2100ee342 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -1,7 +1,6 @@ import "dart:async"; import 'package:flutter/material.dart'; -import "package:flutter_animate/flutter_animate.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; import 'package:photos/models/memory.dart'; @@ -97,13 +96,7 @@ class _MemoriesWidgetState extends State { offsetOfItem: offsetOfItem, maxHeight: _maxHeight, maxWidth: _maxWidth, - ) - .animate(delay: Duration(milliseconds: 75 * itemIndex)) - .fadeIn( - duration: const Duration(milliseconds: 250), - curve: Curves.easeInOutCubic, - ) - .slideX(begin: 0.04, end: 0); + ); }, ), ); From 565db6bd99210f8d59ddf9299970976f78cb057c Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 18 Jan 2024 18:13:34 +0530 Subject: [PATCH 171/175] handle layout popping when memories are rendered --- lib/ui/home/memories/memories_widget.dart | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 2100ee342..5c42aff32 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -1,11 +1,11 @@ import "dart:async"; import 'package:flutter/material.dart'; +import "package:flutter_animate/flutter_animate.dart"; import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; import 'package:photos/models/memory.dart'; import 'package:photos/services/memories_service.dart'; -import "package:photos/ui/common/loading_widget.dart"; import 'package:photos/ui/home/memories/memory_cover_widget.dart'; class MemoriesWidget extends StatefulWidget { @@ -57,10 +57,12 @@ class _MemoriesWidgetState extends State { return FutureBuilder>( future: MemoriesService.instance.getMemories(), builder: (context, snapshot) { - if (snapshot.hasError || !snapshot.hasData || snapshot.data!.isEmpty) { + if (snapshot.hasData && snapshot.data!.isEmpty) { + return const SizedBox.shrink(); + } + if (snapshot.hasError || !snapshot.hasData) { return SizedBox( height: _maxHeight + 12 + 10, - child: const EnteLoadingWidget(), ); } else { return Column( @@ -72,7 +74,10 @@ class _MemoriesWidgetState extends State { _buildMemories(snapshot.data!), const SizedBox(height: 10), ], - ); + ).animate().fadeIn( + duration: const Duration(milliseconds: 250), + curve: Curves.easeInOutCirc, + ); } }, ); From bd88c96b0d36a63e452f466fac838ad6715f84dc Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 18 Jan 2024 18:27:18 +0530 Subject: [PATCH 172/175] increase tap area of close button in full screen memory --- lib/ui/home/memories/full_screen_memory.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/ui/home/memories/full_screen_memory.dart b/lib/ui/home/memories/full_screen_memory.dart index bacc6adda..8e8509ba4 100644 --- a/lib/ui/home/memories/full_screen_memory.dart +++ b/lib/ui/home/memories/full_screen_memory.dart @@ -154,13 +154,13 @@ class _FullScreenMemoryState extends State { automaticallyImplyLeading: false, title: ValueListenableBuilder( valueListenable: inheritedData.indexNotifier, - child: Padding( - padding: const EdgeInsets.only(right: 16), - child: InkWell( - onTap: () { - Navigator.pop(context); - }, - child: const Icon( + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: const Padding( + padding: EdgeInsets.fromLTRB(4, 8, 8, 8), + child: Icon( Icons.close, color: Colors.white, //same for both themes ), @@ -180,7 +180,7 @@ class _FullScreenMemoryState extends State { ) : const SizedBox.shrink(), const SizedBox( - height: 18, + height: 10, ), Row( children: [ From 120c717dd20bbe73052d16072d0d6fa0d3320178 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Thu, 18 Jan 2024 18:31:45 +0530 Subject: [PATCH 173/175] bump up version to 0.8.43 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index ae7664277..d022ac619 100644 --- a/pubspec.yaml +++ b/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.8.42+562 +version: 0.8.43+563 environment: sdk: ">=3.0.0 <4.0.0" From 5ac4a03d30ff5a61fdc61b52c73e3aa1f4a357f9 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 18 Jan 2024 20:06:48 +0530 Subject: [PATCH 174/175] Add loader for memories widget --- lib/ui/home/memories/memories_widget.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ui/home/memories/memories_widget.dart b/lib/ui/home/memories/memories_widget.dart index 5c42aff32..059f10b03 100644 --- a/lib/ui/home/memories/memories_widget.dart +++ b/lib/ui/home/memories/memories_widget.dart @@ -6,6 +6,7 @@ import "package:photos/core/event_bus.dart"; import "package:photos/events/memories_setting_changed.dart"; import 'package:photos/models/memory.dart'; import 'package:photos/services/memories_service.dart'; +import "package:photos/ui/common/loading_widget.dart"; import 'package:photos/ui/home/memories/memory_cover_widget.dart'; class MemoriesWidget extends StatefulWidget { @@ -63,6 +64,7 @@ class _MemoriesWidgetState extends State { if (snapshot.hasError || !snapshot.hasData) { return SizedBox( height: _maxHeight + 12 + 10, + child: const EnteLoadingWidget(), ); } else { return Column( From eee97fac6227c84af4015b77d1e6680731c82f5d Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Thu, 18 Jan 2024 20:06:57 +0530 Subject: [PATCH 175/175] v0.8.44 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index d022ac619..e95dc432a 100644 --- a/pubspec.yaml +++ b/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.8.43+563 +version: 0.8.44+564 environment: sdk: ">=3.0.0 <4.0.0"