diff --git a/auth/assets/custom-icons/icons/IDme.svg b/auth/assets/custom-icons/icons/IDme.svg index 5761405962..dacc2d7970 100644 --- a/auth/assets/custom-icons/icons/IDme.svg +++ b/auth/assets/custom-icons/icons/IDme.svg @@ -1,6 +1,8 @@ - - - - - - + + + + + + + + \ No newline at end of file diff --git a/auth/assets/custom-icons/icons/coinbase.svg b/auth/assets/custom-icons/icons/coinbase.svg index 9ae1bb7fca..b0f3078541 100644 --- a/auth/assets/custom-icons/icons/coinbase.svg +++ b/auth/assets/custom-icons/icons/coinbase.svg @@ -2,13 +2,9 @@ - - - + + C473.2,351.4,440,380.6,399.9,380.6z" style="fill:#FFFFFF;"/> diff --git a/auth/assets/custom-icons/icons/forusall.svg b/auth/assets/custom-icons/icons/forusall.svg index 0590642318..4b81ec1993 100644 --- a/auth/assets/custom-icons/icons/forusall.svg +++ b/auth/assets/custom-icons/icons/forusall.svg @@ -1,3 +1,4 @@ - - - + + + + \ No newline at end of file diff --git a/auth/assets/custom-icons/icons/guideline.svg b/auth/assets/custom-icons/icons/guideline.svg index 9a0252aa3c..f75f8beaab 100644 --- a/auth/assets/custom-icons/icons/guideline.svg +++ b/auth/assets/custom-icons/icons/guideline.svg @@ -1 +1,5 @@ - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/auth/assets/custom-icons/icons/gusto.svg b/auth/assets/custom-icons/icons/gusto.svg index 9d1b4d7380..65c4453d45 100644 --- a/auth/assets/custom-icons/icons/gusto.svg +++ b/auth/assets/custom-icons/icons/gusto.svg @@ -1,9 +1,5 @@ - - - + c10.9-0.2,17.6-6.1,20.4-8.5l0.4-0.3l-10.7-13.2C248.2,71.8,247.5,72.4,247,72.7L247,72.7z M247,72.7" style="fill:#F45D48;"> - \ No newline at end of file diff --git a/auth/assets/custom-icons/icons/login_gov.svg b/auth/assets/custom-icons/icons/login_gov.svg index 8f632955fc..619c049403 100644 --- a/auth/assets/custom-icons/icons/login_gov.svg +++ b/auth/assets/custom-icons/icons/login_gov.svg @@ -1 +1,6 @@ - \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/auth/assets/custom-icons/icons/t-mobile.svg b/auth/assets/custom-icons/icons/t-mobile.svg index 5beb1f5aa2..171143e3b5 100644 --- a/auth/assets/custom-icons/icons/t-mobile.svg +++ b/auth/assets/custom-icons/icons/t-mobile.svg @@ -1 +1,17 @@ - \ No newline at end of file + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/auth/assets/custom-icons/icons/titan.svg b/auth/assets/custom-icons/icons/titan.svg index 38f8325dd3..993d8ef315 100644 --- a/auth/assets/custom-icons/icons/titan.svg +++ b/auth/assets/custom-icons/icons/titan.svg @@ -1 +1,19 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/desktop/CHANGELOG.md b/desktop/CHANGELOG.md index b96b49f6b2..11a4314f95 100644 --- a/desktop/CHANGELOG.md +++ b/desktop/CHANGELOG.md @@ -3,6 +3,7 @@ ## v1.7.6 (Unreleased) - Parse description from metadata JSON. +- Support Italian and Lithuanian translations. - . ## v1.7.5 diff --git a/docs/docs/self-hosting/index.md b/docs/docs/self-hosting/index.md index 03e31226dd..ebcd8872db 100644 --- a/docs/docs/self-hosting/index.md +++ b/docs/docs/self-hosting/index.md @@ -16,7 +16,11 @@ the same code we use for our own cloud service. ## Getting started -Start the server +#### Installing Docker + +Refer to [How to install Docker from the APT repository](https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository) for detailed instructions. + +#### Start the server ```sh git clone https://github.com/ente-io/ente @@ -29,6 +33,16 @@ docker compose up --build > You can also use a pre-built Docker image from `ghcr.io/ente-io/server` > ([More info](https://github.com/ente-io/ente/blob/main/server/docs/docker.md)) +Install the necessary dependencies for running the web client + +```sh +# installing npm and yarn + +sudo apt update +sudo apt install nodejs npm +sudo npm install -g yarn // to install yarn globally +``` + Then in a separate terminal, you can run (e.g) the web client ```sh diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index a4b4a0d7c4..259d2f169e 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -164,12 +164,12 @@ PODS: - Flutter - onnxruntime (0.0.1): - Flutter - - onnxruntime-objc (= 1.15.1) - - onnxruntime-c (1.15.1) - - onnxruntime-objc (1.15.1): - - onnxruntime-objc/Core (= 1.15.1) - - onnxruntime-objc/Core (1.15.1): - - onnxruntime-c (= 1.15.1) + - onnxruntime-objc (= 1.18.0) + - onnxruntime-c (1.18.0) + - onnxruntime-objc (1.18.0): + - onnxruntime-objc/Core (= 1.18.0) + - onnxruntime-objc/Core (1.18.0): + - onnxruntime-c (= 1.18.0) - open_mail_app (0.0.1): - Flutter - OrderedSet (6.0.3) @@ -206,7 +206,7 @@ PODS: - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS - - sqflite (0.0.3): + - sqflite_darwin (0.0.4): - Flutter - FlutterMacOS - "sqlite3 (3.46.1+1)": @@ -292,7 +292,7 @@ DEPENDENCIES: - sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - - sqflite (from `.symlinks/plugins/sqflite/darwin`) + - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - 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`) @@ -421,8 +421,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/share_plus/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" - sqflite: - :path: ".symlinks/plugins/sqflite/darwin" + sqflite_darwin: + :path: ".symlinks/plugins/sqflite_darwin/darwin" sqlite3_flutter_libs: :path: ".symlinks/plugins/sqlite3_flutter_libs/ios" uni_links: @@ -486,9 +486,9 @@ SPEC CHECKSUMS: move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 native_video_player: d12af78a1a4a8cf09775a5177d5b392def6fd23c - onnxruntime: e9346181d75b8dea8733bdae512a22c298962e00 - onnxruntime-c: ebdcfd8650bcbd10121c125262f99dea681b92a3 - onnxruntime-objc: ae7acec7a3d03eaf072d340afed7a35635c1c2a6 + onnxruntime: e7c2ae44385191eaad5ae64c935a72debaddc997 + onnxruntime-c: a909204639a1f035f575127ac406f781ac797c9c + onnxruntime-objc: b6fab0f1787aa6f7190c2013f03037df4718bd8b open_mail_app: 794172f6a22cd16319d3ddaf45e945b2f74952b0 OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 @@ -505,7 +505,7 @@ SPEC CHECKSUMS: sentry_flutter: 0eb93e5279eb41e2392212afe1ccd2fecb4f8cbe share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 - sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec + sqflite_darwin: a553b1fd6fe66f53bbb0fe5b4f5bab93f08d7a13 sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index e1eb3b275b..110131b69a 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -333,7 +333,7 @@ "${BUILT_PRODUCTS_DIR}/sentry_flutter/sentry_flutter.framework", "${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}/sqflite_darwin/sqflite_darwin.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", @@ -428,7 +428,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sentry_flutter.framework", "${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}/sqflite_darwin.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", diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 5fb9333e97..e0059a3c6f 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -227,7 +227,7 @@ Future _init(bool isBackground, {String via = ''}) async { CryptoUtil.init(); _logger.info("Lockscreen init"); - LockScreenSettings.instance.init(preferences); + unawaited(LockScreenSettings.instance.init(preferences)); _logger.info("Configuration init"); await Configuration.instance.init(); diff --git a/mobile/lib/ui/account/delete_account_page.dart b/mobile/lib/ui/account/delete_account_page.dart index 8aa15abece..b40d71933d 100644 --- a/mobile/lib/ui/account/delete_account_page.dart +++ b/mobile/lib/ui/account/delete_account_page.dart @@ -347,6 +347,7 @@ class _DeleteAccountPageState extends State { // ignore: unawaited_futures showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return alert; diff --git a/mobile/lib/ui/account/sessions_page.dart b/mobile/lib/ui/account/sessions_page.dart index 603d951353..eae4dcd2fa 100644 --- a/mobile/lib/ui/account/sessions_page.dart +++ b/mobile/lib/ui/account/sessions_page.dart @@ -209,6 +209,7 @@ class _SessionsPageState extends State { ); showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return alert; diff --git a/mobile/lib/ui/actions/collection/collection_sharing_actions.dart b/mobile/lib/ui/actions/collection/collection_sharing_actions.dart index 55bec3ad45..0e427b2532 100644 --- a/mobile/lib/ui/actions/collection/collection_sharing_actions.dart +++ b/mobile/lib/ui/actions/collection/collection_sharing_actions.dart @@ -695,6 +695,7 @@ class CollectionActions { ); return showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return alert; diff --git a/mobile/lib/ui/common/progress_dialog.dart b/mobile/lib/ui/common/progress_dialog.dart index f08d7cdbcf..b6f2efbcb6 100644 --- a/mobile/lib/ui/common/progress_dialog.dart +++ b/mobile/lib/ui/common/progress_dialog.dart @@ -153,6 +153,7 @@ class ProgressDialog { context: _context!, barrierDismissible: _barrierDismissible, barrierColor: _barrierColor, + useRootNavigator: false, builder: (BuildContext context) { _dismissingContext = context; return PopScope( diff --git a/mobile/lib/ui/components/action_sheet_widget.dart b/mobile/lib/ui/components/action_sheet_widget.dart index 4e23e91b4f..1015c86f34 100644 --- a/mobile/lib/ui/components/action_sheet_widget.dart +++ b/mobile/lib/ui/components/action_sheet_widget.dart @@ -25,11 +25,12 @@ Future showActionSheet({ Widget? bodyWidget, String? body, String? bodyHighlight, + bool useRootNavigator = false, }) { return showMaterialModalBottomSheet( backgroundColor: Colors.transparent, barrierColor: backdropFaintDark, - useRootNavigator: true, + useRootNavigator: useRootNavigator, context: context, isDismissible: isDismissible, enableDrag: enableDrag, diff --git a/mobile/lib/ui/components/dialog_widget.dart b/mobile/lib/ui/components/dialog_widget.dart index 867fd59e5c..1567151401 100644 --- a/mobile/lib/ui/components/dialog_widget.dart +++ b/mobile/lib/ui/components/dialog_widget.dart @@ -22,11 +22,13 @@ Future showDialogWidget({ required List buttons, IconData? icon, bool isDismissible = true, + bool useRootNavigator = false, }) { return showDialog( barrierDismissible: isDismissible, barrierColor: backdropFaintDark, context: context, + useRootNavigator: useRootNavigator, builder: (context) { final widthOfScreen = MediaQuery.of(context).size.width; final isMobileSmall = widthOfScreen <= mobileSmallThreshold; diff --git a/mobile/lib/ui/home/grant_permissions_widget.dart b/mobile/lib/ui/home/grant_permissions_widget.dart index 453ccb95cf..2773f0771e 100644 --- a/mobile/lib/ui/home/grant_permissions_widget.dart +++ b/mobile/lib/ui/home/grant_permissions_widget.dart @@ -138,6 +138,7 @@ class _GrantPermissionsWidgetState extends State { ); // ignore: unawaited_futures showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return alert; diff --git a/mobile/lib/ui/payment/payment_web_page.dart b/mobile/lib/ui/payment/payment_web_page.dart index 3f781a98ff..e3bf5cccd0 100644 --- a/mobile/lib/ui/payment/payment_web_page.dart +++ b/mobile/lib/ui/payment/payment_web_page.dart @@ -143,6 +143,7 @@ class _PaymentWebPageState extends State { // show dialog to handle accidental back press. Future _buildPageExitWidget(BuildContext context) async { final result = await showDialog( + useRootNavigator: false, context: context, builder: (context) => AlertDialog( title: Text(S.of(context).areYouSureYouWantToExit), @@ -199,6 +200,7 @@ class _PaymentWebPageState extends State { Future _handlePaymentFailure(String reason) async { await showDialog( + useRootNavigator: false, context: context, barrierDismissible: false, builder: (context) => AlertDialog( @@ -251,6 +253,7 @@ class _PaymentWebPageState extends State { // warn the user to wait for sometime before trying another payment Future _showExitPageDialog({String? title, String? content}) { return showDialog( + useRootNavigator: false, context: context, barrierDismissible: false, builder: (context) => AlertDialog( diff --git a/mobile/lib/ui/settings/about_section_widget.dart b/mobile/lib/ui/settings/about_section_widget.dart index 2d00908dbd..01df5544a9 100644 --- a/mobile/lib/ui/settings/about_section_widget.dart +++ b/mobile/lib/ui/settings/about_section_widget.dart @@ -71,6 +71,7 @@ class AboutSectionWidget extends StatelessWidget { if (shouldUpdate) { // ignore: unawaited_futures showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return AppUpdateDialog( diff --git a/mobile/lib/ui/settings/account_section_widget.dart b/mobile/lib/ui/settings/account_section_widget.dart index ec4cac4f91..48193dc3b9 100644 --- a/mobile/lib/ui/settings/account_section_widget.dart +++ b/mobile/lib/ui/settings/account_section_widget.dart @@ -64,6 +64,7 @@ class AccountSectionWidget extends StatelessWidget { if (hasAuthenticated) { // ignore: unawaited_futures showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return const ChangeEmailDialog(); diff --git a/mobile/lib/ui/settings/debug/debug_section_widget.dart b/mobile/lib/ui/settings/debug/debug_section_widget.dart index 56070c214e..6a3b887524 100644 --- a/mobile/lib/ui/settings/debug/debug_section_widget.dart +++ b/mobile/lib/ui/settings/debug/debug_section_widget.dart @@ -116,6 +116,7 @@ class DebugSectionWidget extends StatelessWidget { ); showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return alert; diff --git a/mobile/lib/ui/settings/security_section_widget.dart b/mobile/lib/ui/settings/security_section_widget.dart index 1348bae9de..52962b097a 100644 --- a/mobile/lib/ui/settings/security_section_widget.dart +++ b/mobile/lib/ui/settings/security_section_widget.dart @@ -248,6 +248,7 @@ class _SecuritySectionWidgetState extends State { ); await showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return alert; diff --git a/mobile/lib/ui/settings_page.dart b/mobile/lib/ui/settings_page.dart index de3882cfae..88251fc635 100644 --- a/mobile/lib/ui/settings_page.dart +++ b/mobile/lib/ui/settings_page.dart @@ -176,6 +176,7 @@ class SettingsPage extends StatelessWidget { Future _showVerifyIdentityDialog(BuildContext context) async { await showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return VerifyIdentifyDialog(self: true); diff --git a/mobile/lib/ui/sharing/add_participant_page.dart b/mobile/lib/ui/sharing/add_participant_page.dart index 91378b3a5b..98963bd974 100644 --- a/mobile/lib/ui/sharing/add_participant_page.dart +++ b/mobile/lib/ui/sharing/add_participant_page.dart @@ -168,6 +168,7 @@ class _AddParticipantPage extends State { }, onLongPress: () { showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return VerifyIdentifyDialog( diff --git a/mobile/lib/ui/tabs/home_widget.dart b/mobile/lib/ui/tabs/home_widget.dart index 1cd4312ffe..1922227316 100644 --- a/mobile/lib/ui/tabs/home_widget.dart +++ b/mobile/lib/ui/tabs/home_widget.dart @@ -198,6 +198,7 @@ class _HomeWidgetState extends State { Future.delayed(Duration.zero, () { if (value) { showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return AppUpdateDialog( @@ -252,6 +253,7 @@ class _HomeWidgetState extends State { ); await showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return alert; diff --git a/mobile/lib/ui/viewer/file_details/exif_item_widgets.dart b/mobile/lib/ui/viewer/file_details/exif_item_widgets.dart index 0d231cd912..25b1e5e11e 100644 --- a/mobile/lib/ui/viewer/file_details/exif_item_widgets.dart +++ b/mobile/lib/ui/viewer/file_details/exif_item_widgets.dart @@ -89,6 +89,7 @@ class _AllExifItemWidgetState extends State { } else if (exif.isNotEmpty) { label = S.of(context).viewAllExifData; onTap = () => showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return ExifInfoDialog(file); diff --git a/mobile/lib/ui/viewer/gallery/gallery_app_bar_widget.dart b/mobile/lib/ui/viewer/gallery/gallery_app_bar_widget.dart index 3be5b5f8f4..fb9c05cf0a 100644 --- a/mobile/lib/ui/viewer/gallery/gallery_app_bar_widget.dart +++ b/mobile/lib/ui/viewer/gallery/gallery_app_bar_widget.dart @@ -781,6 +781,7 @@ class _GalleryAppBarWidgetState extends State { final result = await showDialog( context: context, barrierDismissible: true, + useRootNavigator: false, builder: (BuildContext context) { return const CastChooseDialog(); }, @@ -792,6 +793,7 @@ class _GalleryAppBarWidgetState extends State { await Future.delayed(const Duration(milliseconds: 100)); if (result == ButtonAction.first) { await showDialog( + useRootNavigator: false, context: context, barrierDismissible: true, builder: (BuildContext bContext) { diff --git a/mobile/lib/utils/delete_file_util.dart b/mobile/lib/utils/delete_file_util.dart index 6af67ff1ee..0e54be145b 100644 --- a/mobile/lib/utils/delete_file_util.dart +++ b/mobile/lib/utils/delete_file_util.dart @@ -424,6 +424,7 @@ Future> deleteLocalFilesInBatches( ); // ignore: unawaited_futures showDialog( + useRootNavigator: false, context: context, builder: (context) { return dialog; diff --git a/mobile/lib/utils/dialog_util.dart b/mobile/lib/utils/dialog_util.dart index c79b6e5c9e..1274d6d8b0 100644 --- a/mobile/lib/utils/dialog_util.dart +++ b/mobile/lib/utils/dialog_util.dart @@ -48,12 +48,14 @@ Future showErrorDialog( String title, String? body, { bool isDismissable = true, + bool useRootNavigator = false, }) async { return showDialogWidget( context: context, title: title, body: body, isDismissible: isDismissable, + useRootNavigator: useRootNavigator, buttons: [ ButtonWidget( buttonType: ButtonType.secondary, @@ -354,10 +356,12 @@ Future showTextInputDialog( TextEditingController? textEditingController, List? textInputFormatter, TextInputType? textInputType, + bool useRootNavigator = false, }) { return showDialog( barrierColor: backdropFaintDark, context: context, + useRootNavigator: useRootNavigator, builder: (context) { final bottomInset = MediaQuery.of(context).viewInsets.bottom; final isKeyboardUp = bottomInset > 100; diff --git a/mobile/lib/utils/email_util.dart b/mobile/lib/utils/email_util.dart index 639e870672..3486027e1a 100644 --- a/mobile/lib/utils/email_util.dart +++ b/mobile/lib/utils/email_util.dart @@ -72,6 +72,7 @@ Future sendLogs( onTap: () async { // ignore: unawaited_futures showDialog( + useRootNavigator: false, context: context, builder: (BuildContext context) { return LogFileViewer(SuperLogging.logFile!); diff --git a/mobile/lib/utils/lock_screen_settings.dart b/mobile/lib/utils/lock_screen_settings.dart index b08f77f4c8..2a42b1d930 100644 --- a/mobile/lib/utils/lock_screen_settings.dart +++ b/mobile/lib/utils/lock_screen_settings.dart @@ -29,12 +29,16 @@ class LockScreenSettings { Duration(minutes: 5), Duration(minutes: 30), ]; - void init(SharedPreferences prefs) async { + Future init(SharedPreferences prefs) async { _secureStorage = const FlutterSecureStorage(); _preferences = prefs; - ///Workaround for privacyScreen not working when app is killed and opened. - await setHideAppContent(getShouldHideAppContent()); + /// Workaround to check if "lateinit property activity has not been + /// initialized" PlatformException goes away. + await Future.delayed(const Duration(milliseconds: 500), () { + ///Workaround for privacyScreen not working when app is killed and opened. + setHideAppContent(getShouldHideAppContent()); + }); } Future setHideAppContent(bool hideContent) async { diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 6bcba72f52..3dde9823df 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -1657,7 +1657,7 @@ packages: description: path: "." ref: ios_only - resolved-ref: "70e163745e082b81235d7330b9d5e8c110ed68e4" + resolved-ref: "212b2cab85647c6d2ffee213dc3b875ef2bf6504" url: "https://github.com/ente-io/onnxruntime.git" source: git version: "1.4.1" diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 40b5a4a69c..0030a61e5c 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -12,7 +12,7 @@ description: ente photos application # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 0.9.48+948 +version: 0.9.49+949 publish_to: none environment: diff --git a/web/apps/photos/src/components/Sidebar/Preferences.tsx b/web/apps/photos/src/components/Sidebar/Preferences.tsx index dec117fe7e..17c5da18a3 100644 --- a/web/apps/photos/src/components/Sidebar/Preferences.tsx +++ b/web/apps/photos/src/components/Sidebar/Preferences.tsx @@ -156,10 +156,14 @@ const localeName = (locale: SupportedLocale) => { case "es-ES": return "Español"; case "pt-BR": - return "Brazilian Portuguese"; + return "Português Brasileiro"; case "ru-RU": - return "Russian"; + return "Русский"; case "pl-PL": - return "Polish"; + return "Polski"; + case "it-IT": + return "Italiano"; + case "lt-LT": + return "Lietuvių kalba"; } }; diff --git a/web/packages/base/i18n.ts b/web/packages/base/i18n.ts index 5067e5fc95..9c842829b7 100644 --- a/web/packages/base/i18n.ts +++ b/web/packages/base/i18n.ts @@ -28,6 +28,8 @@ export const supportedLocales = [ "pt-BR" /* Portuguese, Brazilian */, "ru-RU" /* Russian */, "pl-PL" /* Polish */, + "it-IT" /* Italian */, + "lt-LT" /* Lithuanian */, ] as const; /** The type of {@link supportedLocales}. */ @@ -177,6 +179,10 @@ const closestSupportedLocale = ( return "ru-RU"; } else if (ls.startsWith("pl")) { return "pl-PL"; + } else if (ls.startsWith("it")) { + return "it-IT"; + } else if (ls.startsWith("lt")) { + return "lt-LT"; } } diff --git a/web/packages/base/locales/en-US/translation.json b/web/packages/base/locales/en-US/translation.json index f51d54168b..53e75234fc 100644 --- a/web/packages/base/locales/en-US/translation.json +++ b/web/packages/base/locales/en-US/translation.json @@ -410,7 +410,7 @@ "folder": "Folder", "google_takeout": "Google takeout", "DEDUPLICATE_FILES": "Deduplicate files", - "NO_DUPLICATES_FOUND": "You've no duplicate files that can be cleared", + "NO_DUPLICATES_FOUND": "You have no duplicate files that can be cleared", "FILES": "files", "EACH": "each", "DEDUPLICATE_BASED_ON_SIZE": "The following files were clubbed based on their sizes, please review and delete items you believe are duplicates", diff --git a/web/packages/base/locales/it-IT/translation.json b/web/packages/base/locales/it-IT/translation.json index f8a0597afb..d6174c9fbf 100644 --- a/web/packages/base/locales/it-IT/translation.json +++ b/web/packages/base/locales/it-IT/translation.json @@ -19,7 +19,7 @@ "ENTER_OTT": "Codice di verifica", "RESEND_MAIL": "Reinvia codice", "VERIFY": "Verifica", - "generic_error": "", + "generic_error": "Qualcosa è andato storto", "generic_error_retry": "Qualcosa è andato storto, per favore riprova", "INVALID_CODE": "Codice di verifica non valido", "EXPIRED_CODE": "Il tuo codice di verifica è scaduto", diff --git a/web/packages/base/locales/lt-LT/translation.json b/web/packages/base/locales/lt-LT/translation.json index 08ecc00403..36edac467b 100644 --- a/web/packages/base/locales/lt-LT/translation.json +++ b/web/packages/base/locales/lt-LT/translation.json @@ -10,7 +10,7 @@ "NEW_USER": "Naujas platformoje „Ente“", "EXISTING_USER": "Esamas naudotojas", "enter_name": "Įveskite vardą", - "PUBLIC_UPLOADER_NAME_MESSAGE": "Pridėkite vardą, kad draugai žinotų, kam padėkoti už šias puikias nuotraukas!", + "PUBLIC_UPLOADER_NAME_MESSAGE": "Pridėkite vardą, kad draugai žinotų, kam padėkoti už šias puikias nuotraukas.", "ENTER_EMAIL": "Įveskite el. pašto adresą", "EMAIL_ERROR": "Įveskite tinkamą el. paštą.", "required": "Privaloma", @@ -37,7 +37,7 @@ "PASSPHRASE_HINT": "Slaptažodis", "CONFIRM_PASSPHRASE": "Patvirtinti slaptažodį", "REFERRAL_CODE_HINT": "Kaip išgirdote apie „Ente“? (nebūtina)", - "REFERRAL_INFO": "Mes nesekame programų diegimų. Mums padėtų, jei pasakytumėte, kur mus radote!", + "REFERRAL_INFO": "Mes nesekame programų diegimų. Mums padėtų, jei pasakytumėte, kur mus radote.", "PASSPHRASE_MATCH_ERROR": "Slaptažodžiai nesutampa.", "welcome_to_ente_title": "Sveiki atvykę į „“", "welcome_to_ente_subtitle": "Visapusiškai užšifruota nuotraukų saugykla ir bendrinimas", @@ -83,7 +83,7 @@ "previous_key": "Ankstesnis (←)", "next_key": "Sekantis (→)", "title_photos": "„Ente“ nuotraukos", - "title_auth": "„Ente“ tap. nust.", + "title_auth": "„Ente Auth“", "title_accounts": "„Ente“ paskyros", "UPLOAD_FIRST_PHOTO": "Įkelkite pirmąją nuotrauką", "IMPORT_YOUR_FOLDERS": "Importuokite aplankus", @@ -286,7 +286,7 @@ "SEND_OTT": "Siųsti OTP", "EMAIl_ALREADY_OWNED": "El. paštas jau paimtas.", "ETAGS_BLOCKED": "

Mums nepavyko įkelti toliau esančių failų dėl jūsų naršyklės konfigūracijos.

Išjunkite visus priedus, kurie gali trukdyti „Ente“ naudoti „eTags“ dideliems failams įkelti, arba naudokite mūsų darbalaukio programą, kad galėtumėte patikimiau importuoti.

", - "LIVE_PHOTOS_DETECTED": "", + "LIVE_PHOTOS_DETECTED": "Nuotraukų ir vaizdo įrašų failai iš Gyvosios nuotraukos buvo sujungti į vieną failą", "RETRY_FAILED": "Pakartoti nepavykusius įkėlimus", "FAILED_UPLOADS": "Nepavykę įkėlimai ", "failed_uploads_hint": "Kai baigsis įkėlimas, bus parinktis bandyti tai pakartoti", @@ -302,13 +302,13 @@ "TOO_LARGE_UPLOADS": "Dideli failai", "LARGER_THAN_AVAILABLE_STORAGE_UPLOADS": "Nepakankama saugykla", "LARGER_THAN_AVAILABLE_STORAGE_INFO": "Šie failai nebuvo įkelti, nes jie viršija jūsų saugyklos plano didžiausio dydžio ribą.", - "TOO_LARGE_INFO": "", - "THUMBNAIL_GENERATION_FAILED_INFO": "", - "upload_to_album": "", - "add_to_album": "", + "TOO_LARGE_INFO": "Šie failai nebuvo įkelti, nes jie viršija didžiausio failo dydžio ribą.", + "THUMBNAIL_GENERATION_FAILED_INFO": "Šie failai buvo įkelti, bet, deja, negalėjome sugeneruoti jų miniatiūrų.", + "upload_to_album": "Įkelti į albumą", + "add_to_album": "Pridėti į albumą", "move_to_album": "Perkelti į albumą", - "unhide_to_album": "", - "restore_to_album": "", + "unhide_to_album": "Rodyti į albumą", + "restore_to_album": "Atkurti į albumą", "section_all": "Viskas", "section_uncategorized": "Nekategorizuoti", "section_archive": "Archyvas", @@ -320,87 +320,87 @@ "unarchive": "Išarchyvuoti", "unarchive_album": "Išarchyvuoti albumą", "hide_collection": "Slėpti albumą", - "unhide_collection": "", - "MOVE": "", - "add": "", - "REMOVE": "", - "YES_REMOVE": "", - "REMOVE_FROM_COLLECTION": "", - "MOVE_TO_TRASH": "", - "TRASH_FILES_MESSAGE": "", - "TRASH_FILE_MESSAGE": "", + "unhide_collection": "Rodyti albumą", + "MOVE": "Perkelti", + "add": "Pridėti", + "REMOVE": "Pašalinti", + "YES_REMOVE": "Taip, pašalinti", + "REMOVE_FROM_COLLECTION": "Pašalinti iš albumo", + "MOVE_TO_TRASH": "Perkelti į šiukšlinę", + "TRASH_FILES_MESSAGE": "Pasirinkti failai bus pašalinti iš visų albumų ir perkelti į šiukšlinę.", + "TRASH_FILE_MESSAGE": "Failas bus pašalintas iš visų albumų ir perkeltas į šiukšlinę.", "DELETE_PERMANENTLY": "Ištrinti negrįžtamai", "RESTORE": "Atkurti", - "empty_trash": "", - "empty_trash_title": "", - "empty_trash_message": "", - "leave_album": "", - "leave_shared_album_title": "", - "leave_shared_album_message": "", - "leave_shared_album": "", - "NOT_FILE_OWNER": "", - "CONFIRM_SELF_REMOVE_MESSAGE": "", - "CONFIRM_SELF_AND_OTHER_REMOVE_MESSAGE": "", + "empty_trash": "Ištuštinti šiukšlinę", + "empty_trash_title": "Ištuštinti šiukšlinę?", + "empty_trash_message": "Šie failai bus negrįžtamai ištrinti iš jūsų „Ente“ paskyros.", + "leave_album": "Palikti albumą", + "leave_shared_album_title": "Palikti bendrinamą albumą?", + "leave_shared_album_message": "Paliksite albumą ir jis nebebus jums matomas.", + "leave_shared_album": "Taip, palikti", + "NOT_FILE_OWNER": "Negalite ištrinti failų bendrinamame albume.", + "CONFIRM_SELF_REMOVE_MESSAGE": "Pasirinkti elementai bus pašalinti iš šio albumo. Elementai, kurie yra tik šiame albume, bus perkelti į Nekategorizuoti.", + "CONFIRM_SELF_AND_OTHER_REMOVE_MESSAGE": "Kai kuriuos elementus, kuriuos šalinate, pridėjo kiti žmonės, todėl prarasite prieigą prie jų.", "sort_by_creation_time_ascending": "Seniausią", "sort_by_updation_time_descending": "Paskutinį kartą atnaujintą", "sort_by_name": "Pavadinimą", - "FIX_CREATION_TIME": "", - "FIX_CREATION_TIME_IN_PROGRESS": "", - "CREATION_TIME_UPDATED": "", - "UPDATE_CREATION_TIME_NOT_STARTED": "", - "UPDATE_CREATION_TIME_COMPLETED": "", - "UPDATE_CREATION_TIME_COMPLETED_WITH_ERROR": "", + "FIX_CREATION_TIME": "Taisyti laiką", + "FIX_CREATION_TIME_IN_PROGRESS": "Pataisomas laikas", + "CREATION_TIME_UPDATED": "Atnaujintas failo laikas", + "UPDATE_CREATION_TIME_NOT_STARTED": "Pasirinkite parinktį, kurią norite naudoti", + "UPDATE_CREATION_TIME_COMPLETED": "Sėkmingai atnaujinti visi failai", + "UPDATE_CREATION_TIME_COMPLETED_WITH_ERROR": "Nepavyko atnaujinti kai kurių failų laiko. Pakartokite.", "CAPTION_CHARACTER_LIMIT": "Ne daugiau kaip 5000 simbolių", "DATE_TIME_ORIGINAL": "Exif: originali datos laikas", "DATE_TIME_DIGITIZED": "Exif: skaitmeninta datos laikas", "METADATA_DATE": "Exif: metaduomenų data", "CUSTOM_TIME": "Pasirinktinis laikas", - "REOPEN_PLAN_SELECTOR_MODAL": "", - "OPEN_PLAN_SELECTOR_MODAL_FAILED": "", + "REOPEN_PLAN_SELECTOR_MODAL": "Iš naujo atidaryti planus", + "OPEN_PLAN_SELECTOR_MODAL_FAILED": "Nepavyko atidaryti planų.", "sharing_details": "Bendrinimo išsami informacija", - "modify_sharing": "", - "ADD_COLLABORATORS": "", - "ADD_NEW_EMAIL": "", - "shared_with_people_count_zero": "", - "shared_with_people_count_one": "", - "shared_with_people_count": "", - "participants_count_zero": "", - "participants_count_one": "", - "participants_count": "", - "ADD_VIEWERS": "", - "CHANGE_PERMISSIONS_TO_VIEWER": "", - "CHANGE_PERMISSIONS_TO_COLLABORATOR": "", - "CONVERT_TO_VIEWER": "", - "CONVERT_TO_COLLABORATOR": "", - "CHANGE_PERMISSION": "", - "REMOVE_PARTICIPANT": "", - "CONFIRM_REMOVE": "", - "MANAGE": "", - "ADDED_AS": "", - "COLLABORATOR_RIGHTS": "", - "REMOVE_PARTICIPANT_HEAD": "", - "OWNER": "", + "modify_sharing": "Modifikuoti bendrinimą", + "ADD_COLLABORATORS": "Pridėti bendradarbių", + "ADD_NEW_EMAIL": "Pridėti naują el. paštą", + "shared_with_people_count_zero": "Bendrinti su konkrečiais žmonėmis", + "shared_with_people_count_one": "Bendrinta su žmogumi", + "shared_with_people_count": "Bendrinta su {{count, number}} žmonėmis (-ių)", + "participants_count_zero": "Nėra dalyvių", + "participants_count_one": "Dalyvis", + "participants_count": "{count, number} dalyviai (-ų)", + "ADD_VIEWERS": "Pridėti žiūrėtojus", + "CHANGE_PERMISSIONS_TO_VIEWER": "

{{selectedEmail}} negalės pridėti daugiau nuotraukų į albumą.

Jie vis tiek galės pašalinti savo pridėtas nuotraukas

", + "CHANGE_PERMISSIONS_TO_COLLABORATOR": "{{selectedEmail}} galės pridėti nuotraukų į albumą", + "CONVERT_TO_VIEWER": "Taip, keisti į žiūrėtoją", + "CONVERT_TO_COLLABORATOR": "Taip, keisti į bendradarbį", + "CHANGE_PERMISSION": "Keisti leidimą?", + "REMOVE_PARTICIPANT": "Pašalinti?", + "CONFIRM_REMOVE": "Taip, pašalinti", + "MANAGE": "Tvarkyti", + "ADDED_AS": "Pridėta kaip", + "COLLABORATOR_RIGHTS": "Bendradarbiai gali pridėti nuotraukų ir vaizdo įrašų į bendrintą albumą", + "REMOVE_PARTICIPANT_HEAD": "Pašalinti dalyvį", + "OWNER": "Savininkas", "COLLABORATORS": "Bendradarbiai", "ADD_MORE": "Pridėti daugiau", "VIEWERS": "Žiūrėtojai", - "OR_ADD_EXISTING": "", - "REMOVE_PARTICIPANT_MESSAGE": "", - "NOT_FOUND": "", - "LINK_EXPIRED": "", - "LINK_EXPIRED_MESSAGE": "", + "OR_ADD_EXISTING": "Arba pasirinkite esamą", + "REMOVE_PARTICIPANT_MESSAGE": "

{{selectedEmail}} bus pašalintas iš albumo.

Visos jų pridėtos nuotraukos taip pat bus pašalintos iš albumo

", + "NOT_FOUND": "404 – nerasta.", + "LINK_EXPIRED": "Nuoroda nebegalioja", + "LINK_EXPIRED_MESSAGE": "Ši nuoroda nebegalioja arba yra išjungta.", "MANAGE_LINK": "Tvarkyti nuorodą", - "LINK_TOO_MANY_REQUESTS": "", + "LINK_TOO_MANY_REQUESTS": "Atsiprašome. Šis albumas buvo peržiūrėtas per daug įrenginiuose.", "FILE_DOWNLOAD": "Leisti atsisiuntimus", - "link_password_lock": "", - "PUBLIC_COLLECT": "", - "LINK_DEVICE_LIMIT": "", - "NO_DEVICE_LIMIT": "", - "LINK_EXPIRY": "", - "NEVER": "", - "DISABLE_FILE_DOWNLOAD": "", - "DISABLE_FILE_DOWNLOAD_MESSAGE": "", - "SHARED_USING": "", - "SHARING_REFERRAL_CODE": "", + "link_password_lock": "Slaptažodžio užraktas", + "PUBLIC_COLLECT": "Leisti pridėti nuotraukų", + "LINK_DEVICE_LIMIT": "Įrenginių riba", + "NO_DEVICE_LIMIT": "Jokio", + "LINK_EXPIRY": "Nuorodos galiojimo laikas", + "NEVER": "Niekada", + "DISABLE_FILE_DOWNLOAD": "Išjungti atsisiuntimą", + "DISABLE_FILE_DOWNLOAD_MESSAGE": "

Ar tikrai norite išjungti failų atsisiuntimo mygtuką?

Žiūrėtojai vis tiek gali daryti ekrano kopijas arba išsaugoti nuotraukų kopijas naudojant išorinius įrankius.

", + "SHARED_USING": "Bendrinta naudojant ", + "SHARING_REFERRAL_CODE": "Naudokite kodą {{{referralCode}}, kad gautumėte 10 GB nemokamai", "LIVE": "GYVAI", "DISABLE_PASSWORD": "Išjungti slaptažodžio užraktą", "DISABLE_PASSWORD_MESSAGE": "Ar tikrai norite išjungti slaptažodžio užraktą?", @@ -630,7 +630,7 @@ "passkey_login_credential_hint": "Jei slaptaraktai yra kitame įrenginyje, galite atidaryti šį puslapį tame įrenginyje ir patvirtinti.", "passkeys_not_supported": "Šioje naršyklėje nepalaikomi slaptaraktai.", "try_again": "Bandyti dar kartą", - "check_status": "Patikrinti būseną", + "check_status": "Tikrinti būseną", "passkey_login_instructions": "Sekite žingsnius iš naršyklės, kad tęstumėte prisijungimą.", "passkey_login": "Prisijungti su slaptaraktu", "passkey": "Slaptaraktas", diff --git a/web/packages/base/locales/nl-NL/translation.json b/web/packages/base/locales/nl-NL/translation.json index 04bf6b22af..3bfa0f9d9b 100644 --- a/web/packages/base/locales/nl-NL/translation.json +++ b/web/packages/base/locales/nl-NL/translation.json @@ -19,7 +19,7 @@ "ENTER_OTT": "Verificatiecode", "RESEND_MAIL": "Code opnieuw versturen", "VERIFY": "Verifiëren", - "generic_error": "", + "generic_error": "Er ging iets mis", "generic_error_retry": "Er is iets fout gegaan, probeer het opnieuw", "INVALID_CODE": "Ongeldige verificatiecode", "EXPIRED_CODE": "Uw verificatiecode is verlopen", @@ -48,7 +48,7 @@ "enter_file_name": "Bestandsnaam", "close": "Sluiten", "no": "Nee", - "nothing_here": "", + "nothing_here": "Hier is nog niets te zien", "upload": "Uploaden", "import": "Importeren", "add_photos": "Foto's toevoegen", diff --git a/web/packages/new/photos/services/ml/people.ts b/web/packages/new/photos/services/ml/people.ts index 16b36a0232..80f8a5795c 100644 --- a/web/packages/new/photos/services/ml/people.ts +++ b/web/packages/new/photos/services/ml/people.ts @@ -363,6 +363,7 @@ export interface PersonSuggestionsAndChoices { export const _suggestionsAndChoicesForPerson = async ( person: CGroupPerson, ): Promise => { + console.time("prep"); const startTime = Date.now(); const personClusters = person.cgroup.data.assigned; @@ -391,12 +392,13 @@ export const _suggestionsAndChoicesForPerson = async ( .filter((e) => !!e); // Randomly sample faces to limit the O(n^2) cost. - const sampledPersonFaceEmbeddings = shuffled(personFaceEmbeddings).slice( - 0, - 100, - ); + const sampledPersonEmbeddings = randomSample(personFaceEmbeddings, 50); - const suggestedClusters: FaceCluster[] = []; + console.timeEnd("prep"); + + console.time("loop"); + + const candidateClustersAndSimilarity: [FaceCluster, number][] = []; for (const cluster of clusters) { const { id, faces } = cluster; @@ -405,23 +407,53 @@ export const _suggestionsAndChoicesForPerson = async ( if (personClusterIDs.has(id)) continue; if (ignoredClusterIDs.has(id)) continue; - let suggest = false; - for (const fi of faces) { - const ei = embeddingByFaceID.get(fi); - if (!ei) continue; - for (const ej of sampledPersonFaceEmbeddings) { - const csim = dotProduct(ei, ej); - if (csim >= 0.6) { - suggest = true; - break; + if (process.env.NEXT_PUBLIC_ENTE_WIP_CL_TODO) { + /* direct compare method TODO-Cluster remove me */ + let suggest = false; + for (const fi of faces) { + const ei = embeddingByFaceID.get(fi); + if (!ei) continue; + for (const ej of sampledPersonEmbeddings) { + const csim = dotProduct(ei, ej); + if (csim >= 0.6) { + suggest = true; + break; + } + } + if (suggest) break; + } + + if (suggest) candidateClustersAndSimilarity.push([cluster, 0]); + } else { + const sampledOtherEmbeddings = randomSample(faces, 50) + .map((id) => embeddingByFaceID.get(id)) + .filter((e) => !!e); + + /* cosine similarities */ + const csims: number[] = []; + for (const other of sampledOtherEmbeddings) { + for (const embedding of sampledPersonEmbeddings) { + csims.push(dotProduct(embedding, other)); } } - if (suggest) break; - } + csims.sort(); - if (suggest) suggestedClusters.push(cluster); + if (csims.length == 0) continue; + + const medianSim = ensure(csims[Math.floor(csims.length / 2)]); + if (medianSim > 0.48) { + candidateClustersAndSimilarity.push([cluster, medianSim]); + } + } } + console.timeEnd("loop"); + + console.time("post"); + + candidateClustersAndSimilarity.sort(([, a], [, b]) => b - a); + const suggestedClusters = candidateClustersAndSimilarity.map(([c]) => c); + // Annotate the clusters with the information that the UI needs to show its // preview faces. @@ -449,21 +481,28 @@ export const _suggestionsAndChoicesForPerson = async ( if (previewFaces.length == 4) break; } + if (previewFaces.length == 0) return undefined; + return { ...cluster, previewFaces }; }; + const toPreviewableList = (clusters: FaceCluster[]) => + clusters.map(toPreviewable).filter((p) => !!p); + const sortBySize = (entries: { faces: unknown[] }[]) => entries.sort((a, b) => b.faces.length - a.faces.length); - const acceptedChoices = personClusters - .map(toPreviewable) - .map((p) => ({ ...p, accepted: true })); + const acceptedChoices = toPreviewableList(personClusters).map((p) => ({ + ...p, + accepted: true, + })); sortBySize(acceptedChoices); - const ignoredChoices = ignoredClusters - .map(toPreviewable) - .map((p) => ({ ...p, accepted: false })); + const ignoredChoices = toPreviewableList(ignoredClusters).map((p) => ({ + ...p, + accepted: false, + })); // Ensure that the first item in the choices is not an ignored one, even if // that is what we'd have ended up with if we sorted by size. @@ -474,9 +513,11 @@ export const _suggestionsAndChoicesForPerson = async ( const choices = [firstChoice, ...restChoices]; - sortBySize(suggestedClusters); + // sortBySize(suggestedClusters); // Limit to the number of suggestions shown in a single go. - const suggestions = suggestedClusters.slice(0, 80).map(toPreviewable); + const suggestions = toPreviewableList(suggestedClusters.slice(0, 80)); + + console.timeEnd("post"); log.info( `Generated ${suggestions.length} suggestions for ${person.id} (${Date.now() - startTime} ms)`, @@ -484,3 +525,6 @@ export const _suggestionsAndChoicesForPerson = async ( return { choices, suggestions }; }; + +const randomSample = (items: T[], n: number) => + items.length < n ? items : shuffled(items).slice(0, n);