From 63471eea85cec7279c15f27fedb2b39dc54d4eac Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:44:07 +0530 Subject: [PATCH 001/177] [mob] Add attributes for person email & userID --- mobile/lib/models/ml/face/person.dart | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mobile/lib/models/ml/face/person.dart b/mobile/lib/models/ml/face/person.dart index 2e5191c485..678c3117c1 100644 --- a/mobile/lib/models/ml/face/person.dart +++ b/mobile/lib/models/ml/face/person.dart @@ -53,6 +53,10 @@ class PersonData { List? assigned = List.empty(); List? rejected = List.empty(); final String? birthDate; + // email should be always looked via userID as user might have changed + // their email ids. + final String? email; + final int? userID; bool hasAvatar() => avatarFaceID != null; @@ -66,6 +70,8 @@ class PersonData { this.avatarFaceID, this.isHidden = false, this.birthDate, + this.email, + this.userID, }); // copyWith PersonData copyWith({ @@ -75,6 +81,8 @@ class PersonData { bool? isHidden, int? version, String? birthDate, + String? email, + int? userID, }) { return PersonData( name: name ?? this.name, @@ -82,6 +90,8 @@ class PersonData { avatarFaceID: avatarFaceId ?? this.avatarFaceID, isHidden: isHidden ?? this.isHidden, birthDate: birthDate ?? this.birthDate, + email: email ?? this.email, + userID: userID ?? this.userID, ); } @@ -112,6 +122,8 @@ class PersonData { 'avatarFaceID': avatarFaceID, 'isHidden': isHidden, 'birthDate': birthDate, + 'email': email, + 'userID': userID, }; // fromJson @@ -134,6 +146,8 @@ class PersonData { avatarFaceID: json['avatarFaceID'] as String?, isHidden: json['isHidden'] as bool? ?? false, birthDate: json['birthDate'] as String?, + userID: json['userID'] as int?, + email: json['email'] as String?, ); } } From fb4c1b206dc8bc7a6cf067ad2f0aadd8c503e117 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:47:23 +0530 Subject: [PATCH 002/177] [mob] Basic skeleton to save person --- mobile/ios/Podfile.lock | 6 + mobile/ios/Runner.xcodeproj/project.pbxproj | 2 + mobile/lib/ui/common/email_input.dart | 129 ++++++++++++++++++ mobile/lib/ui/viewer/people/cluster_page.dart | 47 ++++--- mobile/lib/ui/viewer/people/save_person.dart | 82 +++++++++++ 5 files changed, 248 insertions(+), 18 deletions(-) create mode 100644 mobile/lib/ui/common/email_input.dart create mode 100644 mobile/lib/ui/viewer/people/save_person.dart diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock index 3e4684855a..05b45bbd44 100644 --- a/mobile/ios/Podfile.lock +++ b/mobile/ios/Podfile.lock @@ -223,6 +223,8 @@ PODS: - sqlite3/fts5 - sqlite3/perf-threadsafe - sqlite3/rtree + - system_info_plus (0.0.1): + - Flutter - Toast (4.1.1) - ua_client_hints (1.4.0): - Flutter @@ -290,6 +292,7 @@ DEPENDENCIES: - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) + - system_info_plus (from `.symlinks/plugins/system_info_plus/ios`) - ua_client_hints (from `.symlinks/plugins/ua_client_hints/ios`) - uni_links (from `.symlinks/plugins/uni_links/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) @@ -418,6 +421,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/sqflite_darwin/darwin" sqlite3_flutter_libs: :path: ".symlinks/plugins/sqlite3_flutter_libs/ios" + system_info_plus: + :path: ".symlinks/plugins/system_info_plus/ios" ua_client_hints: :path: ".symlinks/plugins/ua_client_hints/ios" uni_links: @@ -501,6 +506,7 @@ SPEC CHECKSUMS: sqflite_darwin: a553b1fd6fe66f53bbb0fe5b4f5bab93f08d7a13 sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b + system_info_plus: 5393c8da281d899950d751713575fbf91c7709aa Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e ua_client_hints: 46bb5817a868f9e397c0ba7e3f2f5c5d90c35156 uni_links: d97da20c7701486ba192624d99bffaaffcfc298a diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index 142efc1059..86d7aa2535 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -334,6 +334,7 @@ "${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}/system_info_plus/system_info_plus.framework", "${BUILT_PRODUCTS_DIR}/ua_client_hints/ua_client_hints.framework", "${BUILT_PRODUCTS_DIR}/uni_links/uni_links.framework", "${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework", @@ -428,6 +429,7 @@ "${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}/system_info_plus.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ua_client_hints.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/uni_links.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework", diff --git a/mobile/lib/ui/common/email_input.dart b/mobile/lib/ui/common/email_input.dart new file mode 100644 index 0000000000..9fab4d44c5 --- /dev/null +++ b/mobile/lib/ui/common/email_input.dart @@ -0,0 +1,129 @@ +import 'package:flutter/material.dart'; +import "package:photos/generated/l10n.dart"; +import "package:photos/models/api/collection/user.dart"; +import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/components/captioned_text_widget.dart"; +import "package:photos/ui/components/divider_widget.dart"; +import "package:photos/ui/components/menu_item_widget/menu_item_widget.dart"; +import "package:photos/ui/sharing/user_avator_widget.dart"; + +class EmailInputField extends StatelessWidget { + final List suggestions; + final ValueChanged? onChanged; + final ValueChanged? onSelected; + final String? initialValue; + + const EmailInputField({ + super.key, + required this.suggestions, + this.onChanged, + this.onSelected, + this.initialValue, + }); + + @override + Widget build(BuildContext context) { + return Autocomplete( + initialValue: + initialValue != null ? TextEditingValue(text: initialValue!) : null, + optionsBuilder: (TextEditingValue textEditingValue) { + if (textEditingValue.text == '') { + return const Iterable.empty(); + } + return suggestions.where((String option) { + return option + .toLowerCase() + .contains(textEditingValue.text.toLowerCase()); + }); + }, + onSelected: onSelected, + fieldViewBuilder: ( + BuildContext context, + TextEditingController controller, + FocusNode focusNode, + VoidCallback onFieldSubmitted, + ) { + return TextFormField( + controller: controller, + focusNode: focusNode, + onChanged: onChanged, + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8.0)), + borderSide: + BorderSide(color: getEnteColorScheme(context).strokeMuted), + ), + fillColor: getEnteColorScheme(context).fillFaint, + filled: true, + hintText: S.of(context).enterEmail, + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 14, + ), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(8), + ), + ), + ); + }, + optionsViewBuilder: ( + BuildContext context, + AutocompleteOnSelected onSelected, + Iterable options, + ) { + return Align( + alignment: Alignment.topLeft, + child: Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Material( + elevation: 4, + borderRadius: BorderRadius.circular(8), + child: Container( + constraints: const BoxConstraints(maxHeight: 160), + width: MediaQuery.of(context).size.width - + 16, // Adjust padding as needed + child: ListView.builder( + padding: EdgeInsets.zero, + physics: const ClampingScrollPhysics(), + shrinkWrap: true, + itemCount: options.length, + itemBuilder: (BuildContext context, int index) { + final String option = options.elementAt(index); + return Column( + children: [ + MenuItemWidget( + captionedTextWidget: CaptionedTextWidget( + title: option, + ), + leadingIconSize: 24.0, + leadingIconWidget: UserAvatarWidget( + User(email: option, id: option.hashCode), + ), + menuItemColor: getEnteColorScheme(context).fillFaint, + pressedColor: getEnteColorScheme(context).fillFaint, + trailingIcon: null, + onTap: () async { + onSelected(option); + }, + isTopBorderRadiusRemoved: index > 0, + isBottomBorderRadiusRemoved: index < (options.length), + ), + (index == (options.length - 1)) + ? const SizedBox.shrink() + : DividerWidget( + dividerType: DividerType.menu, + bgColor: getEnteColorScheme(context).fillFaint, + ), + ], + ); + }, + ), + ), + ), + ), + ); + }, + ); + } +} diff --git a/mobile/lib/ui/viewer/people/cluster_page.dart b/mobile/lib/ui/viewer/people/cluster_page.dart index 8185228c64..33798a5b75 100644 --- a/mobile/lib/ui/viewer/people/cluster_page.dart +++ b/mobile/lib/ui/viewer/people/cluster_page.dart @@ -17,12 +17,13 @@ import 'package:photos/ui/viewer/actions/file_selection_overlay_bar.dart'; import 'package:photos/ui/viewer/gallery/gallery.dart'; import "package:photos/ui/viewer/gallery/state/gallery_files_inherited_widget.dart"; import "package:photos/ui/viewer/gallery/state/selection_state.dart"; -import "package:photos/ui/viewer/people/add_person_action_sheet.dart"; import "package:photos/ui/viewer/people/cluster_app_bar.dart"; import "package:photos/ui/viewer/people/people_banner.dart"; import "package:photos/ui/viewer/people/people_page.dart"; +import "package:photos/ui/viewer/people/save_person.dart"; import "package:photos/ui/viewer/search/result/person_face_widget.dart"; import "package:photos/ui/viewer/search/result/search_result_page.dart"; +import "package:photos/utils/dialog_util.dart"; import "package:photos/utils/navigation_util.dart"; import "package:photos/utils/toast_util.dart"; @@ -172,26 +173,36 @@ class _ClusterPageState extends State { text: S.of(context).addAName, subText: S.of(context).findPeopleByName, onTap: () async { - if (widget.personID == null) { - final result = await showAssignPersonAction( - context, - clusterID: widget.clusterID, - ); - if (result != null && - result is (PersonEntity, EnteFile)) { - Navigator.pop(context); - // ignore: unawaited_futures - routeToPage( + try { + if (widget.personID == null) { + final result = await routeToPage( context, - PeoplePage(person: result.$1), + SavePerson(widget.clusterID), ); - } else if (result != null && result is PersonEntity) { - Navigator.pop(context); - // ignore: unawaited_futures - routeToPage(context, PeoplePage(person: result)); + // final result = await showAssignPersonAction( + // context, + // clusterID: widget.clusterID, + // ); + if (result != null && + result is (PersonEntity, EnteFile)) { + Navigator.pop(context); + // ignore: unawaited_futures + routeToPage( + context, + PeoplePage(person: result.$1), + ); + } else if (result != null && + result is PersonEntity) { + Navigator.pop(context); + // ignore: unawaited_futures + routeToPage(context, PeoplePage(person: result)); + } + } else { + showShortToast(context, "No personID or clusterID"); } - } else { - showShortToast(context, "No personID or clusterID"); + } catch (e) { + showGenericErrorDialog(context: context, error: e) + .ignore(); } }, ), diff --git a/mobile/lib/ui/viewer/people/save_person.dart b/mobile/lib/ui/viewer/people/save_person.dart new file mode 100644 index 0000000000..907f4a2850 --- /dev/null +++ b/mobile/lib/ui/viewer/people/save_person.dart @@ -0,0 +1,82 @@ +import "package:flutter/material.dart"; +import "package:photos/generated/l10n.dart"; +import "package:photos/theme/ente_theme.dart"; +import "package:photos/ui/common/email_input.dart"; + +class SavePerson extends StatefulWidget { + final String clusterID; + final bool isEditing; + + const SavePerson(this.clusterID, {super.key, this.isEditing = false}); + + @override + State createState() => _SavePersonState(); +} + +class _SavePersonState extends State { + bool isKeypadOpen = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: isKeypadOpen, + appBar: AppBar( + title: Text( + widget.isEditing + ? S.of(context).addViewer + : S.of(context).addCollaborator, + ), + ), + body: Padding( + padding: EdgeInsets.symmetric(horizontal: 8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 12), + TextFormField( + // controller: controller, + // focusNode: focusNode, + // onChanged: onChanged, + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8.0)), + borderSide: BorderSide( + color: getEnteColorScheme(context).strokeMuted, + ), + ), + fillColor: getEnteColorScheme(context).fillFaint, + filled: true, + hintText: "Enter name", + hintStyle: getEnteTextTheme(context).bodyFaint, + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 14, + ), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(8), + ), + ), + ), + const SizedBox(height: 8), + EmailInputField( + suggestions: [ + "a@example.com", + "b@example.com", + "c@example.com", + "dc@example.com", + "ec@example.com", + "fc@example.com", + ], + ), + const SizedBox(height: 8), + Text( + "aasdas", + style: getEnteTextTheme(context).body, + ), + ], + ), + ), + ); + } +} From 908a55bb20db116ba484d694a9677e49e5f59601 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 11 Nov 2024 15:42:24 +0530 Subject: [PATCH 003/177] [mob][photos] Push not person feedback --- mobile/lib/models/ml/face/person.dart | 23 ++++++++++--------- .../face_ml/person/person_service.dart | 6 +++++ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/mobile/lib/models/ml/face/person.dart b/mobile/lib/models/ml/face/person.dart index 2e5191c485..348915f017 100644 --- a/mobile/lib/models/ml/face/person.dart +++ b/mobile/lib/models/ml/face/person.dart @@ -51,7 +51,7 @@ class PersonData { final bool isHidden; String? avatarFaceID; List? assigned = List.empty(); - List? rejected = List.empty(); + List? rejectedFaceIDs = List.empty(); final String? birthDate; bool hasAvatar() => avatarFaceID != null; @@ -62,7 +62,7 @@ class PersonData { PersonData({ required this.name, this.assigned, - this.rejected, + this.rejectedFaceIDs, this.avatarFaceID, this.isHidden = false, this.birthDate, @@ -79,7 +79,7 @@ class PersonData { return PersonData( name: name ?? this.name, assigned: assigned ?? this.assigned, - avatarFaceID: avatarFaceId ?? this.avatarFaceID, + avatarFaceID: avatarFaceId ?? avatarFaceID, isHidden: isHidden ?? this.isHidden, birthDate: birthDate ?? this.birthDate, ); @@ -95,7 +95,7 @@ class PersonData { assignedCount += a.faces.length; } sb.writeln('Assigned: ${assigned?.length} withFaces $assignedCount'); - sb.writeln('Rejected: ${rejected?.length}'); + sb.writeln('Rejected faceIDs: ${rejectedFaceIDs?.length}'); if (assigned != null) { for (var cluster in assigned!) { sb.writeln('Cluster: ${cluster.id} - ${cluster.faces.length}'); @@ -108,7 +108,7 @@ class PersonData { Map toJson() => { 'name': name, 'assigned': assigned?.map((e) => e.toJson()).toList(), - 'rejected': rejected?.map((e) => e.toJson()).toList(), + 'rejectedFaceIDs': rejectedFaceIDs, 'avatarFaceID': avatarFaceID, 'isHidden': isHidden, 'birthDate': birthDate, @@ -122,15 +122,16 @@ class PersonData { json['assigned'].map((x) => ClusterInfo.fromJson(x)), ); - final rejected = (json['rejected'] == null || json['rejected'].length == 0) - ? [] - : List.from( - json['rejected'].map((x) => ClusterInfo.fromJson(x)), - ); + final List rejectedFaceIDs = + (json['rejectedFaceIDs'] == null || json['rejectedFaceIDs'].length == 0) + ? [] + : List.from( + json['rejectedFaceIDs'], + ); return PersonData( name: json['name'] as String, assigned: assigned, - rejected: rejected, + rejectedFaceIDs: rejectedFaceIDs, avatarFaceID: json['avatarFaceID'] as String?, isHidden: json['isHidden'] as bool? ?? false, birthDate: json['birthDate'] as String?, diff --git a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart index 91c0c8d087..f540897db5 100644 --- a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart @@ -201,6 +201,8 @@ class PersonService { required Set faceIDs, }) async { final personData = person.data; + + // Remove faces from clusters final List emptiedClusters = []; for (final cluster in personData.assigned!) { cluster.faces.removeWhere((faceID) => faceIDs.contains(faceID)); @@ -219,6 +221,10 @@ class PersonService { ); } + // Add removed faces to rejected faces + personData.rejectedFaceIDs ??= []; + personData.rejectedFaceIDs!.addAll(faceIDs); + await entityService.addOrUpdate( EntityType.cgroup, personData.toJson(), From 015790874ed547d011a447ffa3fc99ce420b31be Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 12 Nov 2024 09:26:04 +0530 Subject: [PATCH 004/177] [mob][photos] Pull rejected faces for person from remote --- mobile/lib/db/ml/db.dart | 19 +++++++++ .../face_ml/person/person_service.dart | 39 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/mobile/lib/db/ml/db.dart b/mobile/lib/db/ml/db.dart index 6ed5b85e3c..1196bf5689 100644 --- a/mobile/lib/db/ml/db.dart +++ b/mobile/lib/db/ml/db.dart @@ -477,6 +477,25 @@ class MLDataDB { return result; } + Future>> getClusterIdToFaceIdsForPerson( + String personID, + ) async { + final db = await instance.asyncDB; + final List> maps = await db.getAll( + 'SELECT $faceClustersTable.$clusterIDColumn, $faceIDColumn FROM $clusterPersonTable ' + 'INNER JOIN $faceClustersTable ON $clusterPersonTable.$clusterIDColumn = $faceClustersTable.$clusterIDColumn ' + 'WHERE $personIdColumn = ?', + [personID], + ); + final Map> result = {}; + for (final map in maps) { + final clusterID = map[clusterIDColumn] as String; + final faceID = map[faceIDColumn] as String; + result.putIfAbsent(clusterID, () => {}).add(faceID); + } + return result; + } + Future> getFaceIDsForPerson(String personID) async { final db = await instance.asyncDB; final faceIdsResult = await db.getAll( diff --git a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart index f540897db5..cf8513a613 100644 --- a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart @@ -8,6 +8,7 @@ import "package:photos/db/ml/db.dart"; import "package:photos/events/people_changed_event.dart"; import "package:photos/extensions/stop_watch.dart"; import "package:photos/models/api/entity/type.dart"; +import "package:photos/models/base/id.dart"; import "package:photos/models/file/file.dart"; import 'package:photos/models/ml/face/face.dart'; import "package:photos/models/ml/face/person.dart"; @@ -280,6 +281,8 @@ class PersonService { for (var e in entities) { final personData = PersonData.fromJson(json.decode(e.data)); int faceCount = 0; + + // Locally store the assignment of faces to clusters and people for (var cluster in personData.assigned!) { faceCount += cluster.faces.length; for (var faceId in cluster.faces) { @@ -305,6 +308,42 @@ class PersonService { "Person ${e.id} ${personData.name} has ${personData.assigned!.length} clusters with $faceCount faces", ); } + + // Locally store the rejection of faces to a person + if (personData.rejectedFaceIDs != null) { + final personFaceIDs = await faceMLDataDB.getFaceIDsForPerson(e.id); + final rejectedFaceIDsSet = personData.rejectedFaceIDs!.toSet(); + final remotelyRejectedFaceIDs = + rejectedFaceIDsSet.intersection(personFaceIDs); + if (remotelyRejectedFaceIDs.isNotEmpty) { + logger.info( + "Person ${e.id} ${personData.name} has ${remotelyRejectedFaceIDs.length} rejected faces", + ); + // Assign rejected faces to new clusters + for (final faceId in remotelyRejectedFaceIDs) { + faceIdToClusterID[faceId] = newClusterID(); + } + // Check that we don't have any empty clusters now + final dbPersonClusterInfo = + await faceMLDataDB.getClusterIdToFaceIdsForPerson(e.id); + for (final clusterIdToFaceIDs in dbPersonClusterInfo.entries) { + final clusterID = clusterIdToFaceIDs.key; + final faceIDs = clusterIdToFaceIDs.value; + faceIDs.removeWhere( + (faceID) => remotelyRejectedFaceIDs.contains(faceID), + ); + if (faceIDs.isEmpty) { + logger.info( + "Cluster $clusterID for person ${e.id} ${personData.name} is empty due to rejected faces from remote, removing the cluster from person", + ); + await faceMLDataDB.removeClusterToPerson( + personID: e.id, + clusterID: clusterID, + ); + } + } + } + } } logger.info("Storing feedback for ${faceIdToClusterID.length} faces"); await faceMLDataDB.updateFaceIdToClusterId(faceIdToClusterID); From b9c63426fc0b07dab222d6336ab675c5c68fb9ed Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 12 Nov 2024 14:54:25 +0530 Subject: [PATCH 005/177] [mob][photos] Assign not person feedback for completely rejected remote cluster --- .../face_ml/person/person_service.dart | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart index cf8513a613..a562fea997 100644 --- a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart @@ -319,19 +319,20 @@ class PersonService { logger.info( "Person ${e.id} ${personData.name} has ${remotelyRejectedFaceIDs.length} rejected faces", ); - // Assign rejected faces to new clusters - for (final faceId in remotelyRejectedFaceIDs) { - faceIdToClusterID[faceId] = newClusterID(); - } + // Check that we don't have any empty clusters now final dbPersonClusterInfo = await faceMLDataDB.getClusterIdToFaceIdsForPerson(e.id); for (final clusterIdToFaceIDs in dbPersonClusterInfo.entries) { final clusterID = clusterIdToFaceIDs.key; final faceIDs = clusterIdToFaceIDs.value; - faceIDs.removeWhere( - (faceID) => remotelyRejectedFaceIDs.contains(faceID), - ); + final foundRejectedFaces = []; + for (final faceID in faceIDs) { + if (remotelyRejectedFaceIDs.contains(faceID)) { + faceIDs.remove(faceID); + foundRejectedFaces.add(faceID); + } + } if (faceIDs.isEmpty) { logger.info( "Cluster $clusterID for person ${e.id} ${personData.name} is empty due to rejected faces from remote, removing the cluster from person", @@ -340,8 +341,17 @@ class PersonService { personID: e.id, clusterID: clusterID, ); + await faceMLDataDB.captureNotPersonFeedback( + personID: e.id, + clusterID: clusterID, + ); + remotelyRejectedFaceIDs.removeAll(foundRejectedFaces); } } + // Assign rejected faces to new clusters + for (final faceId in remotelyRejectedFaceIDs) { + faceIdToClusterID[faceId] = newClusterID(); + } } } } From 4c222f2cd72892d831902576da3edc415ac5220f Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 12 Nov 2024 21:36:49 +0530 Subject: [PATCH 006/177] [mob][photos] Get rid of remotely rejected faces from local person --- mobile/lib/db/ml/db.dart | 20 +++- .../face_ml/person/person_service.dart | 99 ++++++++++--------- 2 files changed, 70 insertions(+), 49 deletions(-) diff --git a/mobile/lib/db/ml/db.dart b/mobile/lib/db/ml/db.dart index 1196bf5689..b3e3fa232a 100644 --- a/mobile/lib/db/ml/db.dart +++ b/mobile/lib/db/ml/db.dart @@ -17,14 +17,17 @@ import "package:photos/services/machine_learning/ml_result.dart"; import "package:photos/utils/ml_util.dart"; import 'package:sqlite_async/sqlite_async.dart'; -/// Stores all data for the FacesML-related features. The database can be accessed by `MLDataDB.instance.database`. +/// Stores all data for the ML related features. The database can be accessed by `MLDataDB.instance.database`. /// /// This includes: /// [facesTable] - Stores all the detected faces and its embeddings in the images. -/// [createFaceClustersTable] - Stores all the mappings from the faces (faceID) to the clusters (clusterID). +/// [faceClustersTable] - Stores all the mappings from the faces (faceID) to the clusters (clusterID). /// [clusterPersonTable] - Stores all the clusters that are mapped to a certain person. /// [clusterSummaryTable] - Stores a summary of each cluster, containg the mean embedding and the number of faces in the cluster. /// [notPersonFeedback] - Stores the clusters that are confirmed not to belong to a certain person by the user +/// +/// [clipTable] - Stores the embeddings of the CLIP model +/// [fileDataTable] - Stores data about the files that are already processed by the ML models class MLDataDB { static final Logger _logger = Logger("MLDataDB"); @@ -572,6 +575,19 @@ class MLDataDB { await db.executeBatch(sql, parameterSets); } + Future removeFaceIdToClusterId( + Map faceIDToClusterID, + ) async { + final db = await instance.asyncDB; + const String sql = ''' + DELETE FROM $faceClustersTable + WHERE $faceIDColumn = ? AND $clusterIDColumn = ? + '''; + final parameterSets = + faceIDToClusterID.entries.map((e) => [e.key, e.value]).toList(); + await db.executeBatch(sql, parameterSets); + } + Future removePerson(String personID) async { final db = await instance.asyncDB; diff --git a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart index a562fea997..7a9b7ee7b5 100644 --- a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart @@ -8,7 +8,6 @@ import "package:photos/db/ml/db.dart"; import "package:photos/events/people_changed_event.dart"; import "package:photos/extensions/stop_watch.dart"; import "package:photos/models/api/entity/type.dart"; -import "package:photos/models/base/id.dart"; import "package:photos/models/file/file.dart"; import 'package:photos/models/ml/face/face.dart'; import "package:photos/models/ml/face/person.dart"; @@ -278,8 +277,10 @@ class PersonService { entities.sort((a, b) => a.updatedAt.compareTo(b.updatedAt)); final Map faceIdToClusterID = {}; final Map clusterToPersonID = {}; + bool shouldCheckRejectedFaces = false; for (var e in entities) { final personData = PersonData.fromJson(json.decode(e.data)); + if (personData.rejectedFaceIDs != null) shouldCheckRejectedFaces = true; int faceCount = 0; // Locally store the assignment of faces to clusters and people @@ -308,56 +309,60 @@ class PersonService { "Person ${e.id} ${personData.name} has ${personData.assigned!.length} clusters with $faceCount faces", ); } - - // Locally store the rejection of faces to a person - if (personData.rejectedFaceIDs != null) { - final personFaceIDs = await faceMLDataDB.getFaceIDsForPerson(e.id); - final rejectedFaceIDsSet = personData.rejectedFaceIDs!.toSet(); - final remotelyRejectedFaceIDs = - rejectedFaceIDsSet.intersection(personFaceIDs); - if (remotelyRejectedFaceIDs.isNotEmpty) { - logger.info( - "Person ${e.id} ${personData.name} has ${remotelyRejectedFaceIDs.length} rejected faces", - ); - - // Check that we don't have any empty clusters now - final dbPersonClusterInfo = - await faceMLDataDB.getClusterIdToFaceIdsForPerson(e.id); - for (final clusterIdToFaceIDs in dbPersonClusterInfo.entries) { - final clusterID = clusterIdToFaceIDs.key; - final faceIDs = clusterIdToFaceIDs.value; - final foundRejectedFaces = []; - for (final faceID in faceIDs) { - if (remotelyRejectedFaceIDs.contains(faceID)) { - faceIDs.remove(faceID); - foundRejectedFaces.add(faceID); - } - } - if (faceIDs.isEmpty) { - logger.info( - "Cluster $clusterID for person ${e.id} ${personData.name} is empty due to rejected faces from remote, removing the cluster from person", - ); - await faceMLDataDB.removeClusterToPerson( - personID: e.id, - clusterID: clusterID, - ); - await faceMLDataDB.captureNotPersonFeedback( - personID: e.id, - clusterID: clusterID, - ); - remotelyRejectedFaceIDs.removeAll(foundRejectedFaces); - } - } - // Assign rejected faces to new clusters - for (final faceId in remotelyRejectedFaceIDs) { - faceIdToClusterID[faceId] = newClusterID(); - } - } - } } logger.info("Storing feedback for ${faceIdToClusterID.length} faces"); await faceMLDataDB.updateFaceIdToClusterId(faceIdToClusterID); await faceMLDataDB.bulkAssignClusterToPersonID(clusterToPersonID); + + if (shouldCheckRejectedFaces) { + final dbPeopleClusterInfo = + await faceMLDataDB.getPersonToClusterIdToFaceIds(); + for (var e in entities) { + final personData = PersonData.fromJson(json.decode(e.data)); + if (personData.rejectedFaceIDs != null) { + final personFaceIDs = + dbPeopleClusterInfo[e.id]!.values.expand((e) => e).toSet(); + final rejectedFaceIDsSet = personData.rejectedFaceIDs!.toSet(); + final assignedAndRejectedFaceIDs = + rejectedFaceIDsSet.intersection(personFaceIDs); + + if (assignedAndRejectedFaceIDs.isNotEmpty) { + // Check that we don't have any empty clusters now + final dbPersonClusterInfo = dbPeopleClusterInfo[e.id]!; + final faceToClusterToRemove = {}; + for (final clusterIdToFaceIDs in dbPersonClusterInfo.entries) { + final clusterID = clusterIdToFaceIDs.key; + final faceIDs = clusterIdToFaceIDs.value; + final foundRejectedFacesToCluster = {}; + for (final faceID in faceIDs) { + if (assignedAndRejectedFaceIDs.contains(faceID)) { + faceIDs.remove(faceID); + foundRejectedFacesToCluster[faceID] = clusterID; + } + } + if (faceIDs.isEmpty) { + logger.info( + "Cluster $clusterID for person ${e.id} ${personData.name} is empty due to rejected faces from remote, removing the cluster from person", + ); + await faceMLDataDB.removeClusterToPerson( + personID: e.id, + clusterID: clusterID, + ); + await faceMLDataDB.captureNotPersonFeedback( + personID: e.id, + clusterID: clusterID, + ); + } else { + faceToClusterToRemove.addAll(foundRejectedFacesToCluster); + } + } + // Remove the clusterID for the remaining conflicting faces + await faceMLDataDB.removeFaceIdToClusterId(faceToClusterToRemove); + } + } + } + } + return changed; } From 5d99a7c757076a6ca46137e675a7779c0b5ca7e0 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Nov 2024 11:21:43 +0530 Subject: [PATCH 007/177] [mob][photos] Dont recluster rejected face to cluster mapping --- .../face_clustering/face_clustering_service.dart | 12 +++++++++++- .../face_db_info_for_clustering.dart | 1 + .../services/machine_learning/ml_service.dart | 16 ++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart b/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart index ebb16f2252..f2c53771cd 100644 --- a/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart @@ -22,6 +22,7 @@ class FaceInfo { final bool? badFace; final Vector? vEmbedding; String? clusterId; + final List? rejectedClusterIds; String? closestFaceId; int? closestDist; int? fileCreationTime; @@ -32,6 +33,7 @@ class FaceInfo { this.badFace, this.vEmbedding, this.clusterId, + this.rejectedClusterIds, this.fileCreationTime, }); } @@ -328,6 +330,7 @@ ClusteringResult runLinearClustering(Map args) { dtype: DType.float32, ), clusterId: face.clusterId, + rejectedClusterIds: face.rejectedClusterIds, fileCreationTime: fileIDToCreationTime?[getFileIdFromFaceId(face.faceID)], ), @@ -372,7 +375,6 @@ ClusteringResult runLinearClustering(Map args) { _logger.info( "[ClusterIsolate] ${DateTime.now()} Processing $totalFaces faces ($newToClusterCount new, $alreadyClusteredCount already done) in total in this round ${offset != null ? "on top of ${offset + facesWithClusterID.length} earlier processed faces" : ""}", ); - // set current epoch time as clusterID String clusterID = newClusterID(); if (facesWithClusterID.isEmpty) { // assign a clusterID to the first face @@ -398,6 +400,7 @@ ClusteringResult runLinearClustering(Map args) { } else { thresholdValue = distanceThreshold; } + final bool faceHasBeenRejectedBefore = sortedFaceInfos[i].rejectedClusterIds != null; if (i % 250 == 0) { _logger.info("Processed ${offset != null ? i + offset : i} faces"); } @@ -410,6 +413,13 @@ ClusteringResult runLinearClustering(Map args) { distance > conservativeDistanceThreshold) { continue; } + if (faceHasBeenRejectedBefore && + sortedFaceInfos[j].clusterId != null && + sortedFaceInfos[i].rejectedClusterIds!.contains( + sortedFaceInfos[j].clusterId!, + )) { + continue; + } closestDistance = distance; closestIdx = j; } diff --git a/mobile/lib/services/machine_learning/face_ml/face_clustering/face_db_info_for_clustering.dart b/mobile/lib/services/machine_learning/face_ml/face_clustering/face_db_info_for_clustering.dart index 1822d3fb55..6726761415 100644 --- a/mobile/lib/services/machine_learning/face_ml/face_clustering/face_db_info_for_clustering.dart +++ b/mobile/lib/services/machine_learning/face_ml/face_clustering/face_db_info_for_clustering.dart @@ -3,6 +3,7 @@ import "dart:typed_data" show Uint8List; class FaceDbInfoForClustering { final String faceID; String? clusterId; + List? rejectedClusterIds; final Uint8List embeddingBytes; final double faceScore; final double blurValue; diff --git a/mobile/lib/services/machine_learning/ml_service.dart b/mobile/lib/services/machine_learning/ml_service.dart index a9f300d74c..929730b3a6 100644 --- a/mobile/lib/services/machine_learning/ml_service.dart +++ b/mobile/lib/services/machine_learning/ml_service.dart @@ -250,6 +250,19 @@ class MLService { _logger.info('Pulling remote feedback before actually clustering'); await PersonService.instance.fetchRemoteClusterFeedback(); + final persons = await PersonService.instance.getPersons(); + final faceIdNotToCluster = >{}; + for (final person in persons) { + if (person.data.rejectedFaceIDs != null && + person.data.rejectedFaceIDs!.isNotEmpty) { + final personClusters = person.data.assigned?.map((e) => e.id).toList(); + if (personClusters != null) { + for (final faceID in person.data.rejectedFaceIDs!) { + faceIdNotToCluster[faceID] = personClusters; + } + } + } + } try { _showClusteringIsHappening = true; @@ -271,6 +284,9 @@ class MLService { if (!fileIDToCreationTime.containsKey(faceInfo.fileID)) { missingFileIDs.add(faceInfo.fileID); } else { + if (faceIdNotToCluster.containsKey(faceInfo.faceID)) { + faceInfo.rejectedClusterIds = faceIdNotToCluster[faceInfo.faceID]; + } allFaceInfoForClustering.add(faceInfo); } } From 4a495e58064b0fce04d10c0f7d1fc1b76fbc095d Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Nov 2024 11:22:38 +0530 Subject: [PATCH 008/177] [mob][photos] Make other clustering methods private --- .../face_ml/face_clustering/face_clustering_service.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart b/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart index f2c53771cd..9ad92ebb5f 100644 --- a/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart @@ -163,7 +163,7 @@ class FaceClusteringService extends SuperIsolate { _logger.info( 'Running complete clustering on ${input.length} faces with distance threshold $mergeThreshold', ); - final ClusteringResult clusterResult = await predictCompleteComputer( + final ClusteringResult clusterResult = await _predictCompleteComputer( input, fileIDToCreationTime: fileIDToCreationTime, oldClusterSummaries: oldClusterSummaries, @@ -175,7 +175,7 @@ class FaceClusteringService extends SuperIsolate { _logger.info( 'Running linear clustering on ${input.length} faces with distance threshold $distanceThreshold', ); - final ClusteringResult clusterResult = await predictLinearComputer( + final ClusteringResult clusterResult = await _predictLinearComputer( input, fileIDToCreationTime: fileIDToCreationTime, oldClusterSummaries: oldClusterSummaries, @@ -190,7 +190,7 @@ class FaceClusteringService extends SuperIsolate { } /// Runs the clustering algorithm [runLinearClustering] on the given [input], in computer, without any dynamic thresholding - Future predictLinearComputer( + Future _predictLinearComputer( Map input, { Map? fileIDToCreationTime, required Map oldClusterSummaries, @@ -250,7 +250,7 @@ class FaceClusteringService extends SuperIsolate { /// Runs the clustering algorithm [_runCompleteClustering] on the given [input], in computer. /// /// WARNING: Only use on small datasets, as it is not optimized for large datasets. - Future predictCompleteComputer( + Future _predictCompleteComputer( Map input, { Map? fileIDToCreationTime, required Map oldClusterSummaries, From 822aa7fcd477400756613da95626613e62da10db Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Nov 2024 14:38:51 +0530 Subject: [PATCH 009/177] [mob][photos] Make sure user can add back rejected faces --- .../face_ml/feedback/cluster_feedback.dart | 25 +++++++++++++++++++ .../face_ml/person/person_service.dart | 6 ++--- .../people/add_person_action_sheet.dart | 6 ++--- .../people/person_cluster_suggestion.dart | 4 +-- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/mobile/lib/services/machine_learning/face_ml/feedback/cluster_feedback.dart b/mobile/lib/services/machine_learning/face_ml/feedback/cluster_feedback.dart index 2f17010180..8e9171611c 100644 --- a/mobile/lib/services/machine_learning/face_ml/feedback/cluster_feedback.dart +++ b/mobile/lib/services/machine_learning/face_ml/feedback/cluster_feedback.dart @@ -334,6 +334,31 @@ class ClusterFeedbackService { return true; } + Future addClusterToExistingPerson({ + required PersonEntity person, + required String clusterID, + }) async { + if (person.data.rejectedFaceIDs != null && + person.data.rejectedFaceIDs!.isNotEmpty) { + final clusterFaceIDs = + await MLDataDB.instance.getFaceIDsForCluster(clusterID); + final rejectedLengthBefore = person.data.rejectedFaceIDs!.length; + person.data.rejectedFaceIDs! + .removeWhere((faceID) => clusterFaceIDs.contains(faceID)); + final rejectedLengthAfter = person.data.rejectedFaceIDs!.length; + if (rejectedLengthBefore != rejectedLengthAfter) { + _logger.info( + 'Removed ${rejectedLengthBefore - rejectedLengthAfter} rejected faces from person ${person.data.name} due to adding cluster $clusterID', + ); + await PersonService.instance.updatePerson(person); + } + } + await MLDataDB.instance.assignClusterToPerson( + personID: person.remoteID, + clusterID: clusterID, + ); + } + Future ignoreCluster(String clusterID) async { await PersonService.instance.addPerson('', clusterID, isHidden: true); Bus.instance.fire(PeopleChangedEvent()); diff --git a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart index 7a9b7ee7b5..07c96a066f 100644 --- a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart @@ -381,7 +381,7 @@ class PersonService { final updatedPerson = person.copyWith( data: person.data.copyWith(avatarFaceId: face.faceID), ); - await _updatePerson(updatedPerson); + await updatePerson(updatedPerson); } Future updateAttributes( @@ -402,10 +402,10 @@ class PersonService { birthDate: birthDate, ), ); - await _updatePerson(updatedPerson); + await updatePerson(updatedPerson); } - Future _updatePerson(PersonEntity updatePerson) async { + Future updatePerson(PersonEntity updatePerson) async { await entityService.addOrUpdate( EntityType.cgroup, updatePerson.data.toJson(), diff --git a/mobile/lib/ui/viewer/people/add_person_action_sheet.dart b/mobile/lib/ui/viewer/people/add_person_action_sheet.dart index 9aa05b16e4..0039d477fb 100644 --- a/mobile/lib/ui/viewer/people/add_person_action_sheet.dart +++ b/mobile/lib/ui/viewer/people/add_person_action_sheet.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import "package:logging/logging.dart"; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import "package:photos/core/event_bus.dart"; -import "package:photos/db/ml/db.dart"; import "package:photos/events/people_changed_event.dart"; import "package:photos/generated/l10n.dart"; import "package:photos/models/file/file.dart"; @@ -255,8 +254,9 @@ class _PersonActionSheetState extends State { return; } userAlreadyAssigned = true; - await MLDataDB.instance.assignClusterToPerson( - personID: person.$1.remoteID, + await ClusterFeedbackService.instance + .addClusterToExistingPerson( + person: person.$1, clusterID: widget.cluserID, ); Bus.instance.fire(PeopleChangedEvent()); diff --git a/mobile/lib/ui/viewer/people/person_cluster_suggestion.dart b/mobile/lib/ui/viewer/people/person_cluster_suggestion.dart index 54ff9975e8..47857ce82b 100644 --- a/mobile/lib/ui/viewer/people/person_cluster_suggestion.dart +++ b/mobile/lib/ui/viewer/people/person_cluster_suggestion.dart @@ -199,8 +199,8 @@ class _PersonClustersState extends State { ); if (yesOrNo) { canGiveFeedback = false; - await MLDataDB.instance.assignClusterToPerson( - personID: widget.person.remoteID, + await ClusterFeedbackService.instance.addClusterToExistingPerson( + person: widget.person, clusterID: clusterID, ); Bus.instance.fire(PeopleChangedEvent()); From 1ed03c59424c2b58bb3b0ef9d2941bb11335d564 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Nov 2024 15:45:59 +0530 Subject: [PATCH 010/177] [mob][photos] Safe removed clusters in rejected faces --- .../machine_learning/face_ml/person/person_service.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart index 07c96a066f..0fa86e174f 100644 --- a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart @@ -183,6 +183,11 @@ class PersonService { }) async { final person = (await getPerson(personID))!; final personData = person.data; + final clusterInfo = personData.assigned!.firstWhere( + (element) => element.id == clusterID, + ); + personData.rejectedFaceIDs ??= []; + personData.rejectedFaceIDs!.addAll(clusterInfo.faces); personData.assigned!.removeWhere((element) => element.id != clusterID); await entityService.addOrUpdate( EntityType.cgroup, From 3d6106c1c1179055dcd2d90145d1de76e1f35645 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Nov 2024 16:06:50 +0530 Subject: [PATCH 011/177] [mob][photos] Add empty check --- .../machine_learning/face_ml/person/person_service.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart index 0fa86e174f..8dff6c99a0 100644 --- a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart @@ -285,7 +285,10 @@ class PersonService { bool shouldCheckRejectedFaces = false; for (var e in entities) { final personData = PersonData.fromJson(json.decode(e.data)); - if (personData.rejectedFaceIDs != null) shouldCheckRejectedFaces = true; + if (personData.rejectedFaceIDs != null && + personData.rejectedFaceIDs!.isNotEmpty) { + shouldCheckRejectedFaces = true; + } int faceCount = 0; // Locally store the assignment of faces to clusters and people @@ -324,7 +327,8 @@ class PersonService { await faceMLDataDB.getPersonToClusterIdToFaceIds(); for (var e in entities) { final personData = PersonData.fromJson(json.decode(e.data)); - if (personData.rejectedFaceIDs != null) { + if (personData.rejectedFaceIDs != null && + personData.rejectedFaceIDs!.isNotEmpty) { final personFaceIDs = dbPeopleClusterInfo[e.id]!.values.expand((e) => e).toSet(); final rejectedFaceIDsSet = personData.rejectedFaceIDs!.toSet(); From 92f6d027db76796ffcd4eedf3e9f5765e60c248b Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:13:23 +0530 Subject: [PATCH 012/177] [mob] Clean up --- .../people/person_cluster_suggestion.dart | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/mobile/lib/ui/viewer/people/person_cluster_suggestion.dart b/mobile/lib/ui/viewer/people/person_cluster_suggestion.dart index 54ff9975e8..7ca68596da 100644 --- a/mobile/lib/ui/viewer/people/person_cluster_suggestion.dart +++ b/mobile/lib/ui/viewer/people/person_cluster_suggestion.dart @@ -319,26 +319,6 @@ class _PersonClustersState extends State { ], ), ), - // const SizedBox( - // height: 24.0, - // ), - // ButtonWidget( - // shouldSurfaceExecutionStates: false, - // buttonType: ButtonType.neutral, - // labelText: 'Assign different person', - // buttonSize: ButtonSize.small, - // onTap: () async { - // final result = await showAssignPersonAction( - // context, - // clusterID: clusterID, - // ); - // if (result != null && - // (result is (PersonEntity, EnteFile) || - // result is PersonEntity)) { - // await _rejectSuggestion(clusterID, numberOfSuggestions); - // } - // }, - // ), ], ); // Precompute face thumbnails for next suggestions, in case there are From 179e5866717b78cd0586a50c2ab306eefa6ce49a Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 15 Nov 2024 15:18:29 +0530 Subject: [PATCH 013/177] [mob] Refactor --- .../gallery/hierarchical_search_gallery.dart | 34 +++++++------------ mobile/lib/ui/viewer/people/cluster_page.dart | 16 +++------ .../lib/ui/viewer/people/people_app_bar.dart | 14 ++------ .../ui/viewer/search_tab/people_section.dart | 17 +++------- 4 files changed, 25 insertions(+), 56 deletions(-) diff --git a/mobile/lib/ui/viewer/gallery/hierarchical_search_gallery.dart b/mobile/lib/ui/viewer/gallery/hierarchical_search_gallery.dart index 106130af98..234848549b 100644 --- a/mobile/lib/ui/viewer/gallery/hierarchical_search_gallery.dart +++ b/mobile/lib/ui/viewer/gallery/hierarchical_search_gallery.dart @@ -1,4 +1,5 @@ import "dart:async"; + import "package:flutter/material.dart"; import "package:logging/logging.dart"; import "package:photos/core/event_bus.dart"; @@ -184,28 +185,17 @@ class _HierarchicalSearchGalleryState extends State { clusterID: _firstUnnamedAppliedFaceFilter!.clusterId!, ); - if (result != null && - result is (PersonEntity, EnteFile)) { - Navigator.of(context).pop(); - unawaited( - routeToPage( - context, - PeoplePage( - person: result.$1, - searchResult: null, - ), - ), - ); - } else if (result != null && - result is PersonEntity) { - Navigator.of(context).pop(); - unawaited( - routeToPage( - context, - PeoplePage( - person: result, - searchResult: null, - ), + Navigator.of(context).pop(); + if (result != null) { + final person = result is (PersonEntity, EnteFile) + ? result.$1 + : result; + // ignore: unawaited_futures + routeToPage( + context, + PeoplePage( + person: person, + searchResult: null, ), ); } diff --git a/mobile/lib/ui/viewer/people/cluster_page.dart b/mobile/lib/ui/viewer/people/cluster_page.dart index cca4e2b6d2..38f629d6e7 100644 --- a/mobile/lib/ui/viewer/people/cluster_page.dart +++ b/mobile/lib/ui/viewer/people/cluster_page.dart @@ -149,23 +149,17 @@ class _ClusterPageState extends State { context, clusterID: widget.clusterID, ); - if (result != null && result is (PersonEntity, EnteFile)) { + if (result != null) { Navigator.pop(context); - // ignore: unawaited_futures - routeToPage( - context, - PeoplePage(person: result.$1, searchResult: null), - ); - } else if (result != null && result is PersonEntity) { - Navigator.pop(context); - // ignore: unawaited_futures + final person = + result is (PersonEntity, EnteFile) ? result.$1 : result; routeToPage( context, PeoplePage( - person: result, + person: person, searchResult: null, ), - ); + ).ignore(); } } else { showShortToast(context, "No personID or clusterID"); diff --git a/mobile/lib/ui/viewer/people/people_app_bar.dart b/mobile/lib/ui/viewer/people/people_app_bar.dart index 05da13c8b2..44e5d66e0d 100644 --- a/mobile/lib/ui/viewer/people/people_app_bar.dart +++ b/mobile/lib/ui/viewer/people/people_app_bar.dart @@ -336,21 +336,13 @@ class _AppBarWidgetState extends State { clusterID: widget.person.data.assigned!.first.id, ); Navigator.pop(context); - if (result != null && result is (PersonEntity, EnteFile)) { + if (result != null) { + final person = result is (PersonEntity, EnteFile) ? result.$1 : result; // ignore: unawaited_futures routeToPage( context, PeoplePage( - person: result.$1, - searchResult: null, - ), - ); - } else if (result != null && result is PersonEntity) { - // ignore: unawaited_futures - routeToPage( - context, - PeoplePage( - person: result, + person: person, searchResult: null, ), ); diff --git a/mobile/lib/ui/viewer/search_tab/people_section.dart b/mobile/lib/ui/viewer/search_tab/people_section.dart index f118e101c3..673886c588 100644 --- a/mobile/lib/ui/viewer/search_tab/people_section.dart +++ b/mobile/lib/ui/viewer/search_tab/people_section.dart @@ -262,22 +262,15 @@ class SearchExample extends StatelessWidget { context, clusterID: searchResult.name(), ); - if (result != null && - result is (PersonEntity, EnteFile)) { + if (result != null) { + final person = result is (PersonEntity, EnteFile) + ? result.$1 + : result; // ignore: unawaited_futures routeToPage( context, PeoplePage( - person: result.$1, - searchResult: null, - ), - ); - } else if (result != null && result is PersonEntity) { - // ignore: unawaited_futures - routeToPage( - context, - PeoplePage( - person: result, + person: person, searchResult: null, ), ); From a88586c437fe54937a6a321f62309c72737f6fcf Mon Sep 17 00:00:00 2001 From: Simon Dubrulle Date: Fri, 15 Nov 2024 13:40:29 +0100 Subject: [PATCH 014/177] Added (draft) doc for mobile translations + added 2 new source strings for "add_participant_page" --- mobile/docs/translations.md | 49 +++++++++++++++++++ mobile/lib/generated/l10n.dart | 26 ++++++++++ mobile/lib/l10n/intl_en.arb | 22 ++++++++- .../lib/ui/sharing/add_participant_page.dart | 2 +- 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 mobile/docs/translations.md diff --git a/mobile/docs/translations.md b/mobile/docs/translations.md new file mode 100644 index 0000000000..104bd482ce --- /dev/null +++ b/mobile/docs/translations.md @@ -0,0 +1,49 @@ +# Translations + +We use Crowdin for translations, and the `intl` package to load these at +runtime. + +Within our project we have the _source_ strings - these are the key value pairs +in the `lib/l10n/intl_en.arb` file. + +Volunteers can add a new _translation_ in their language corresponding to each +such source key-value to our +[Crowdin project](https://crowdin.com/project/ente-photos-app). + +When a new source string is added, we run a [GitHub workflow](../../.github/workflows/mobile-crowdin-push.yml) +that + +- Uploads sources to Crowdin - So any new key value pair we add in the source + `intl_en.arb` becomes available to translators to translate. + +Every monday, we run a [GitHub workflow](../../.github/workflows/mobile-crowdin-sync.yml) +that + +- Downloads translations from Crowdin - So any new translations that + translators have made on the Crowdin dashboard (for existing sources) will + be added to the corresponding `intl_XX.arb`. + +The workflow also uploads existing translations and also downloads new sources +from Crowdin, but these two should be no-ops. + +## Adding a new string + +- Add a new entry in `lib/l10n/intl_en.arb` (the + **source `intl_en.arb`**). +- Use the new key in code with the `S` class + (`import "package:photos/generated/l10n.dart"`). +- During the next sync, the workflow will upload this source item to Crowdin's + dashboard, allowing translators to translate it. + +## Updating an existing string + +- Update the existing value for the key in the source `intl_en.arb`. +- During the next sync, the workflow will clear out all the existing + translations so that they can be translated afresh. + +## Deleting an existing string + +- Remove the key value pair from the source `intl_en.arb`. +- During the next sync, the workflow will delete that source item from all + existing translations (both in the Crowdin project and also from the + other `intl_XX.arb` files in the repository). diff --git a/mobile/lib/generated/l10n.dart b/mobile/lib/generated/l10n.dart index a76cc59628..b1aedae08c 100644 --- a/mobile/lib/generated/l10n.dart +++ b/mobile/lib/generated/l10n.dart @@ -9945,6 +9945,32 @@ class S { args: [], ); } + + /// `{count, plural, =0 {Added 0 viewer} =1 {Added 1 viewer} other {Added {count} viewers}}` + String viewersSuccessfullyAdded(int count) { + return Intl.plural( + count, + zero: 'Added 0 viewer', + one: 'Added 1 viewer', + other: 'Added $count viewers', + name: 'viewersSuccessfullyAdded', + desc: 'Number of viewers that were successfully added to an album.', + args: [count], + ); + } + + /// `{count, plural, =0 {Added 0 collaborator} =1 {Added 1 collaborator} other {Added {count} collaborators}}` + String collaboratorsSuccessfullyAdded(int count) { + return Intl.plural( + count, + zero: 'Added 0 collaborator', + one: 'Added 1 collaborator', + other: 'Added $count collaborators', + name: 'collaboratorsSuccessfullyAdded', + desc: 'Number of collaborators that were successfully added to an album.', + args: [count], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/mobile/lib/l10n/intl_en.arb b/mobile/lib/l10n/intl_en.arb index 2c7b8701fd..eab99b9c9b 100644 --- a/mobile/lib/l10n/intl_en.arb +++ b/mobile/lib/l10n/intl_en.arb @@ -1363,5 +1363,25 @@ "checkingModels": "Checking models...", "enableMachineLearningBanner": "Enable machine learning for magic search and face recognition", "searchDiscoverEmptySection": "Images will be shown here once processing is complete", - "searchPersonsEmptySection": "People will be shown here once processing is complete" + "searchPersonsEmptySection": "People will be shown here once processing is complete", + "viewersSuccessfullyAdded": "{count, plural, =0 {Added 0 viewer} =1 {Added 1 viewer} other {Added {count} viewers}}", + "@viewersSuccessfullyAdded": { + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + }, + "description": "Number of viewers that were successfully added to an album." + }, + "collaboratorsSuccessfullyAdded": "{count, plural, =0 {Added 0 collaborator} =1 {Added 1 collaborator} other {Added {count} collaborators}}", + "@collaboratorsSuccessfullyAdded": { + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + }, + "description": "Number of collaborators that were successfully added to an album." + } } \ No newline at end of file diff --git a/mobile/lib/ui/sharing/add_participant_page.dart b/mobile/lib/ui/sharing/add_participant_page.dart index 98963bd974..4fadaf1e78 100644 --- a/mobile/lib/ui/sharing/add_participant_page.dart +++ b/mobile/lib/ui/sharing/add_participant_page.dart @@ -239,7 +239,7 @@ class _AddParticipantPage extends State { results.where((e) => e).length; showToast( context, - "Added $noOfSuccessfullAdds ${widget.isAddingViewer ? "viewers" : "collaborators"}", + widget.isAddingViewer ? S.of(context).viewersSuccessfullyAdded(noOfSuccessfullAdds) : S.of(context).collaboratorsSuccessfullyAdded(noOfSuccessfullAdds), ); if (!results.any((e) => e == false) && mounted) { From 17bcf212166df799da2a47cac80e2f636f543356 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Mon, 18 Nov 2024 17:54:24 +0530 Subject: [PATCH 015/177] Start deprecating ensure ensure: Error: Required value was undefined Built in undefined property access (note how the error message has more info): TypeError: Cannot read properties of undefined (reading 'length') --- web/apps/accounts/src/pages/passkeys/index.tsx | 2 +- web/packages/base/log.ts | 2 -- web/packages/build-config/eslintrc-base.js | 13 +++++++++++++ web/packages/new/photos/services/ml/cluster.ts | 1 - web/packages/new/photos/services/ml/face.ts | 9 --------- web/packages/new/photos/services/ml/image.ts | 3 --- web/packages/new/photos/services/ml/math.ts | 1 - web/packages/new/photos/services/ml/people.ts | 1 - web/packages/new/photos/utils/file.ts | 3 +-- 9 files changed, 15 insertions(+), 20 deletions(-) diff --git a/web/apps/accounts/src/pages/passkeys/index.tsx b/web/apps/accounts/src/pages/passkeys/index.tsx index ac741f06d7..4304d8975b 100644 --- a/web/apps/accounts/src/pages/passkeys/index.tsx +++ b/web/apps/accounts/src/pages/passkeys/index.tsx @@ -64,7 +64,7 @@ const Page: React.FC = () => { const refreshPasskeys = useCallback(async () => { try { - setPasskeys(await getPasskeys(ensure(token))); + setPasskeys(await getPasskeys(token!)); } catch (e) { log.error("Failed to fetch passkeys", e); showPasskeyFetchFailedErrorDialog(); diff --git a/web/packages/base/log.ts b/web/packages/base/log.ts index 0be2b24512..f79022fa6b 100644 --- a/web/packages/base/log.ts +++ b/web/packages/base/log.ts @@ -30,8 +30,6 @@ export const logToDisk = (message: string) => { }; const workerLogToDisk = (message: string) => { - // We checked that we're `inWorker` prior to calling this function. - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion workerBridge!.logToDisk(message).catch((e: unknown) => { console.error( "Failed to log a message from worker", diff --git a/web/packages/build-config/eslintrc-base.js b/web/packages/build-config/eslintrc-base.js index 0b54770457..e5d43938b0 100644 --- a/web/packages/build-config/eslintrc-base.js +++ b/web/packages/build-config/eslintrc-base.js @@ -41,5 +41,18 @@ module.exports = { }, }, ], + /* Allow force unwrapping potentially optional values. + + It is best if these can be avoided by restructuring the code, but + there do arise legitimate scenarios where we know from code logic + that the value should be present. Of course, the surrounding code + might change causing that expectation to be falsified, but in certain + cases there isn't much we can do other than throwing an exception. + + Instead of rolling our own such exception (which we in fact used to + do at one point), rely on the JS's native undefined property access + exception since that conveys more information in the logs. + */ + "@typescript-eslint/no-non-null-assertion": "off", }, }; diff --git a/web/packages/new/photos/services/ml/cluster.ts b/web/packages/new/photos/services/ml/cluster.ts index cfd2e6dcd5..e0a2003218 100644 --- a/web/packages/new/photos/services/ml/cluster.ts +++ b/web/packages/new/photos/services/ml/cluster.ts @@ -306,7 +306,6 @@ const clusterBatchLinear = async ( for (let j = i - 1; j >= 0; j--) { // ! This is an O(n^2) loop, be careful when adding more code here. - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const fj = faces[j]!; // The vectors are already normalized, so we can directly use their diff --git a/web/packages/new/photos/services/ml/face.ts b/web/packages/new/photos/services/ml/face.ts index c93241cb23..098c0ed4d4 100644 --- a/web/packages/new/photos/services/ml/face.ts +++ b/web/packages/new/photos/services/ml/face.ts @@ -1,12 +1,3 @@ -// [Note: Allowing non-null assertions selectively] -// -// The code in this file involves a lot of imperative array processing and -// indexing, and allowing non-null assertions ("!") is the easiest way to get -// TypeScript to accept it in the presence of noUncheckedIndexedAccess without -// obfuscating the original algorithms. -// -/* eslint-disable @typescript-eslint/no-non-null-assertion */ - import { assertionFailed } from "@/base/assert"; import type { ElectronMLWorker } from "@/base/types/ipc"; import type { EnteFile } from "@/media/file"; diff --git a/web/packages/new/photos/services/ml/image.ts b/web/packages/new/photos/services/ml/image.ts index 8494eb961c..80f80f0c7f 100644 --- a/web/packages/new/photos/services/ml/image.ts +++ b/web/packages/new/photos/services/ml/image.ts @@ -1,6 +1,3 @@ -// See: [Note: Allowing non-null assertions selectively] -/* eslint-disable @typescript-eslint/no-non-null-assertion */ - import { ensure } from "@/utils/ensure"; import { Matrix, inverse } from "ml-matrix"; import { clamp } from "./math"; diff --git a/web/packages/new/photos/services/ml/math.ts b/web/packages/new/photos/services/ml/math.ts index 9e78dcd822..afdb18b03a 100644 --- a/web/packages/new/photos/services/ml/math.ts +++ b/web/packages/new/photos/services/ml/math.ts @@ -61,7 +61,6 @@ export const dotProduct = (v1: Float32Array, v2: Float32Array) => { if (v1.length != v2.length) throw new Error(`Length mismatch ${v1.length} ${v2.length}`); let d = 0; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion for (let i = 0; i < v1.length; i++) d += v1[i]! * v2[i]!; return d; }; diff --git a/web/packages/new/photos/services/ml/people.ts b/web/packages/new/photos/services/ml/people.ts index 30f24e390b..fae1c189ef 100644 --- a/web/packages/new/photos/services/ml/people.ts +++ b/web/packages/new/photos/services/ml/people.ts @@ -611,7 +611,6 @@ const randomSample = (items: T[], n: number) => { while (ix.size < n) { ix.add(Math.floor(Math.random() * items.length)); } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return [...ix].map((i) => items[i]!); }; diff --git a/web/packages/new/photos/utils/file.ts b/web/packages/new/photos/utils/file.ts index d5948bf735..6aab6e7017 100644 --- a/web/packages/new/photos/utils/file.ts +++ b/web/packages/new/photos/utils/file.ts @@ -127,8 +127,7 @@ const nativeConvertToJPEG = async (imageBlob: Blob) => { // thus, to the `window.electron`) object. const jpegData = electron ? await electron.convertToJPEG(imageData) - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await workerBridge!.convertToJPEG(imageData); + : await workerBridge!.convertToJPEG(imageData); log.debug(() => `Native JPEG conversion took ${Date.now() - startTime} ms`); return new Blob([jpegData], { type: "image/jpeg" }); }; From a261d1b3a225a184adcd6116857ff5422e76cc50 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Mon, 18 Nov 2024 18:03:00 +0530 Subject: [PATCH 016/177] Continue 17bcf212166df799da2a47cac80e2f636f543356 --- .../accounts/src/pages/passkeys/index.tsx | 5 ++--- .../accounts/src/pages/passkeys/verify.tsx | 5 ++--- web/apps/accounts/src/services/passkey.ts | 3 +-- web/apps/accounts/src/types/context.ts | 3 +-- web/apps/auth/src/pages/_app.tsx | 3 +-- web/apps/auth/src/pages/auth.tsx | 12 +++++------- web/apps/auth/src/services/code.ts | 7 +++---- web/apps/cast/src/pages/slideshow.tsx | 3 +-- web/apps/cast/src/services/render.ts | 3 +-- web/packages/accounts/pages/credentials.tsx | 3 +-- web/packages/accounts/pages/generate.tsx | 3 +-- web/packages/accounts/pages/recover.tsx | 3 +-- .../accounts/pages/two-factor/recover.tsx | 5 ++--- .../accounts/pages/two-factor/setup.tsx | 3 +-- .../accounts/pages/two-factor/verify.tsx | 3 +-- web/packages/accounts/pages/verify.tsx | 3 +-- web/packages/media/file-metadata.ts | 3 +-- .../components/gallery/PeopleHeader.tsx | 3 +-- .../new/photos/services/ffmpeg/worker.ts | 3 +-- .../new/photos/services/ml/cluster.ts | 19 +++++++++---------- web/packages/new/photos/services/ml/crop.ts | 3 +-- web/packages/new/photos/services/ml/image.ts | 9 ++++----- web/packages/new/photos/services/ml/index.ts | 3 +-- web/packages/new/photos/services/ml/people.ts | 9 ++++----- web/packages/new/photos/services/ml/worker.ts | 13 +++++-------- .../new/photos/services/search/worker.ts | 4 +--- .../new/photos/services/user-entity/remote.ts | 3 +-- web/packages/new/photos/types/context.ts | 3 +-- web/packages/shared/utils/index.ts | 3 +-- web/packages/shared/utils/queueProcessor.ts | 4 +--- 30 files changed, 57 insertions(+), 92 deletions(-) diff --git a/web/apps/accounts/src/pages/passkeys/index.tsx b/web/apps/accounts/src/pages/passkeys/index.tsx index 4304d8975b..5e5b78dfff 100644 --- a/web/apps/accounts/src/pages/passkeys/index.tsx +++ b/web/apps/accounts/src/pages/passkeys/index.tsx @@ -4,7 +4,6 @@ import { SidebarDrawer } from "@/base/components/mui/SidebarDrawer"; import { Titlebar } from "@/base/components/Titlebar"; import { errorDialogAttributes } from "@/base/components/utils/dialog"; import log from "@/base/log"; -import { ensure } from "@/utils/ensure"; import { CenteredFlex } from "@ente/shared/components/Container"; import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem"; import SingleInputForm from "@ente/shared/components/SingleInputForm"; @@ -103,7 +102,7 @@ const Page: React.FC = () => { resetForm: () => void, ) => { try { - await registerPasskey(ensure(token), inputValue); + await registerPasskey(token!, inputValue); } catch (e) { log.error("Failed to register a new passkey", e); // If the user cancels the operation, then an error with name @@ -273,7 +272,7 @@ const ManagePasskeyDrawer: React.FC = ({ text: t("delete"), color: "critical", action: async () => { - await deletePasskey(ensure(token), ensure(passkey).id); + await deletePasskey(token!, passkey!.id); onUpdateOrDeletePasskey(); }, }, diff --git a/web/apps/accounts/src/pages/passkeys/verify.tsx b/web/apps/accounts/src/pages/passkeys/verify.tsx index 196682be28..06e0cdbc8d 100644 --- a/web/apps/accounts/src/pages/passkeys/verify.tsx +++ b/web/apps/accounts/src/pages/passkeys/verify.tsx @@ -2,7 +2,6 @@ import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator"; import { FocusVisibleButton } from "@/base/components/mui/FocusVisibleButton"; import log from "@/base/log"; import type { TwoFactorAuthorizationResponse } from "@/base/types/credentials"; -import { ensure } from "@/utils/ensure"; import { nullToUndefined } from "@/utils/transform"; import { VerticallyCentered } from "@ente/shared/components/Container"; import InfoIcon from "@mui/icons-material/Info"; @@ -212,7 +211,7 @@ const Page = () => { if (successRedirectURL) redirectToURL(successRedirectURL); }, [successRedirectURL]); - const handleVerify = () => void authenticateContinue(ensure(continuation)); + const handleVerify = () => void authenticateContinue(continuation!); const handleRetry = () => void authenticate(); @@ -232,7 +231,7 @@ const Page = () => { return () => redirectToPasskeyRecoverPage(new URL(recover)); })(); - const handleRedirectAgain = () => redirectToURL(ensure(successRedirectURL)); + const handleRedirectAgain = () => redirectToURL(successRedirectURL!); const components: Record = { loading: , diff --git a/web/apps/accounts/src/services/passkey.ts b/web/apps/accounts/src/services/passkey.ts index 3c12e31d79..632cd590cc 100644 --- a/web/apps/accounts/src/services/passkey.ts +++ b/web/apps/accounts/src/services/passkey.ts @@ -8,7 +8,6 @@ import { isDevBuild } from "@/base/env"; import { clientPackageHeader, ensureOk, HTTPError } from "@/base/http"; import { apiURL } from "@/base/origins"; import { TwoFactorAuthorizationResponse } from "@/base/types/credentials"; -import { ensure } from "@/utils/ensure"; import { nullToUndefined } from "@/utils/transform"; import { z } from "zod"; @@ -116,7 +115,7 @@ export const registerPasskey = async (token: string, name: string) => { const { sessionID, options } = await beginPasskeyRegistration(token); // Ask the browser to new (public key) credentials using these options. - const credential = ensure(await navigator.credentials.create(options)); + const credential = (await navigator.credentials.create(options))!; // Finish by letting the backend know about these credentials so that it can // save the public key for future authentication. diff --git a/web/apps/accounts/src/types/context.ts b/web/apps/accounts/src/types/context.ts index dece58358d..416ff1fcf1 100644 --- a/web/apps/accounts/src/types/context.ts +++ b/web/apps/accounts/src/types/context.ts @@ -1,5 +1,4 @@ import type { AccountsContextT } from "@/accounts/types/context"; -import { ensure } from "@/utils/ensure"; import { createContext, useContext } from "react"; /** @@ -16,4 +15,4 @@ export const AppContext = createContext(undefined); * Utility hook to get the {@link AppContextT}, throwing an exception if it is * not defined. */ -export const useAppContext = (): AppContextT => ensure(useContext(AppContext)); +export const useAppContext = (): AppContextT => useContext(AppContext)!; diff --git a/web/apps/auth/src/pages/_app.tsx b/web/apps/auth/src/pages/_app.tsx index 41e61d200c..e1bb9353f0 100644 --- a/web/apps/auth/src/pages/_app.tsx +++ b/web/apps/auth/src/pages/_app.tsx @@ -12,7 +12,6 @@ import { logStartupBanner, logUnhandledErrorsAndRejections, } from "@/base/log-web"; -import { ensure } from "@/utils/ensure"; import { MessageContainer } from "@ente/shared/components/MessageContainer"; import { useLocalState } from "@ente/shared/hooks/useLocalState"; import HTTPService from "@ente/shared/network/HTTPService"; @@ -51,7 +50,7 @@ type AppContextT = AccountsContextT & { export const AppContext = createContext(undefined); /** Utility hook to reduce amount of boilerplate in account related pages. */ -export const useAppContext = () => ensure(useContext(AppContext)); +export const useAppContext = () => useContext(AppContext)!; const App: React.FC = ({ Component, pageProps }) => { const router = useRouter(); diff --git a/web/apps/auth/src/pages/auth.tsx b/web/apps/auth/src/pages/auth.tsx index 6fb996cf3f..0032777ff9 100644 --- a/web/apps/auth/src/pages/auth.tsx +++ b/web/apps/auth/src/pages/auth.tsx @@ -3,7 +3,6 @@ import { stashRedirect } from "@/accounts/services/redirect"; import { EnteLogo } from "@/base/components/EnteLogo"; import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator"; import { NavbarBase } from "@/base/components/Navbar"; -import { ensure } from "@/utils/ensure"; import { HorizontalFlex, VerticallyCentered, @@ -17,15 +16,14 @@ import MoreHoriz from "@mui/icons-material/MoreHoriz"; import { Button, ButtonBase, Snackbar, TextField, styled } from "@mui/material"; import { t } from "i18next"; import { useRouter } from "next/router"; -import React, { useContext, useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import { generateOTPs, type Code } from "services/code"; import { getAuthCodes } from "services/remote"; -import { AppContext } from "./_app"; +import { useAppContext } from "./_app"; const Page: React.FC = () => { - const { logout, showNavBar, showMiniDialog } = ensure( - useContext(AppContext), - ); + const { logout, showNavBar, showMiniDialog } = useAppContext(); + const router = useRouter(); const [codes, setCodes] = useState([]); const [hasFetched, setHasFetched] = useState(false); @@ -141,7 +139,7 @@ const Page: React.FC = () => { export default Page; const AuthNavbar: React.FC = () => { - const { logout } = ensure(useContext(AppContext)); + const { logout } = useAppContext(); return ( diff --git a/web/apps/auth/src/services/code.ts b/web/apps/auth/src/services/code.ts index c604bae0ce..a7b1662b5d 100644 --- a/web/apps/auth/src/services/code.ts +++ b/web/apps/auth/src/services/code.ts @@ -1,4 +1,3 @@ -import { ensure } from "@/utils/ensure"; import { HOTP, TOTP } from "otpauth"; import { Steam } from "./steam"; @@ -168,8 +167,8 @@ const parseIssuer = (url: URL, path: string): string => { let p = decodeURIComponent(path); if (p.startsWith("/")) p = p.slice(1); - if (p.includes(":")) p = ensure(p.split(":")[0]); - else if (p.includes("-")) p = ensure(p.split("-")[0]); + if (p.includes(":")) p = p.split(":")[0]!; + else if (p.includes("-")) p = p.split("-")[0]!; return p; }; @@ -206,7 +205,7 @@ const parseCounter = (url: URL): number | undefined => { }; const parseSecret = (url: URL): string => - ensure(url.searchParams.get("secret")).replaceAll(" ", "").toUpperCase(); + url.searchParams.get("secret")!.replaceAll(" ", "").toUpperCase(); /** * Generate a pair of OTPs (one time passwords) from the given {@link code}. diff --git a/web/apps/cast/src/pages/slideshow.tsx b/web/apps/cast/src/pages/slideshow.tsx index 1d206867c4..8f17a38abb 100644 --- a/web/apps/cast/src/pages/slideshow.tsx +++ b/web/apps/cast/src/pages/slideshow.tsx @@ -1,5 +1,4 @@ import log from "@/base/log"; -import { ensure } from "@/utils/ensure"; import { styled } from "@mui/material"; import { FilledCircleCheck } from "components/FilledCircleCheck"; import { useRouter } from "next/router"; @@ -22,7 +21,7 @@ export default function Slideshow() { const loop = async () => { try { - const urlGenerator = imageURLGenerator(ensure(readCastData())); + const urlGenerator = imageURLGenerator(readCastData()!); while (!stop) { const { value: url, done } = await urlGenerator.next(); if (done == true || !url) { diff --git a/web/apps/cast/src/services/render.ts b/web/apps/cast/src/services/render.ts index 0d9723511c..b9f9c589d0 100644 --- a/web/apps/cast/src/services/render.ts +++ b/web/apps/cast/src/services/render.ts @@ -21,7 +21,6 @@ import { isHEICExtension, needsJPEGConversion } from "@/media/formats"; import { heicToJPEG } from "@/media/heic-convert"; import { decodeLivePhoto } from "@/media/live-photo"; import { shuffled } from "@/utils/array"; -import { ensure } from "@/utils/ensure"; import { wait } from "@/utils/promise"; import { ApiError } from "@ente/shared/error"; import HTTPService from "@ente/shared/network/HTTPService"; @@ -134,7 +133,7 @@ export const imageURLGenerator = async function* (castData: CastData) { // The last to last element is the one that was shown prior to that, // and now can be safely revoked. if (previousURLs.length > 1) - URL.revokeObjectURL(ensure(previousURLs.shift())); + URL.revokeObjectURL(previousURLs.shift()!); previousURLs.push(url); diff --git a/web/packages/accounts/pages/credentials.tsx b/web/packages/accounts/pages/credentials.tsx index 188ae07f21..01f9174a0b 100644 --- a/web/packages/accounts/pages/credentials.tsx +++ b/web/packages/accounts/pages/credentials.tsx @@ -4,7 +4,6 @@ import { sharedCryptoWorker } from "@/base/crypto"; import type { B64EncryptionResult } from "@/base/crypto/libsodium"; import { clearLocalStorage } from "@/base/local-storage"; import log from "@/base/log"; -import { ensure } from "@/utils/ensure"; import { VerticallyCentered } from "@ente/shared/components/Container"; import LinkButton from "@ente/shared/components/LinkButton"; import VerifyMasterPasswordForm, { @@ -218,7 +217,7 @@ const Page: React.FC = ({ appContext }) => { id, twoFactorSessionID, passkeySessionID, - } = await loginViaSRP(ensure(srpAttributes), kek); + } = await loginViaSRP(srpAttributes!, kek); setIsFirstLogin(true); if (passkeySessionID) { const sessionKeyAttributes = diff --git a/web/packages/accounts/pages/generate.tsx b/web/packages/accounts/pages/generate.tsx index 42c01adb5c..02e4c5747a 100644 --- a/web/packages/accounts/pages/generate.tsx +++ b/web/packages/accounts/pages/generate.tsx @@ -13,7 +13,6 @@ import { } from "@/base/components/FormPaper"; import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator"; import log from "@/base/log"; -import { ensure } from "@/utils/ensure"; import { VerticallyCentered } from "@ente/shared/components/Container"; import LinkButton from "@ente/shared/components/LinkButton"; import { @@ -80,7 +79,7 @@ const Page: React.FC = ({ appContext }) => { await generateKeyAndSRPAttributes(passphrase); // TODO: Refactor the code to not require this ensure - await putAttributes(ensure(token), keyAttributes); + await putAttributes(token!, keyAttributes); await configureSRP(srpSetupAttributes); await generateAndSaveIntermediateKeyAttributes( passphrase, diff --git a/web/packages/accounts/pages/recover.tsx b/web/packages/accounts/pages/recover.tsx index 38106a2980..411c76a7ce 100644 --- a/web/packages/accounts/pages/recover.tsx +++ b/web/packages/accounts/pages/recover.tsx @@ -7,7 +7,6 @@ import { } from "@/base/components/FormPaper"; import { sharedCryptoWorker } from "@/base/crypto"; import log from "@/base/log"; -import { ensure } from "@/utils/ensure"; import { VerticallyCentered } from "@ente/shared/components/Container"; import LinkButton from "@ente/shared/components/LinkButton"; import SingleInputForm, { @@ -82,7 +81,7 @@ const Page: React.FC = ({ appContext }) => { recoveryKey = bip39.mnemonicToEntropy(recoveryKey); } const cryptoWorker = await sharedCryptoWorker(); - const keyAttr = ensure(keyAttributes); + const keyAttr = keyAttributes!; const masterKey = await cryptoWorker.decryptB64( keyAttr.masterKeyEncryptedWithRecoveryKey, keyAttr.masterKeyDecryptionNonce, diff --git a/web/packages/accounts/pages/two-factor/recover.tsx b/web/packages/accounts/pages/two-factor/recover.tsx index c8d5a08508..9718902ce1 100644 --- a/web/packages/accounts/pages/two-factor/recover.tsx +++ b/web/packages/accounts/pages/two-factor/recover.tsx @@ -14,7 +14,6 @@ import type { MiniDialogAttributes } from "@/base/components/MiniDialog"; import { sharedCryptoWorker } from "@/base/crypto"; import type { B64EncryptionResult } from "@/base/crypto/libsodium"; import log from "@/base/log"; -import { ensure } from "@/utils/ensure"; import { VerticallyCentered } from "@ente/shared/components/Container"; import LinkButton from "@ente/shared/components/LinkButton"; import SingleInputForm, { @@ -114,14 +113,14 @@ const Page: React.FC = ({ appContext, twoFactorType }) => { recoveryKey = bip39.mnemonicToEntropy(recoveryKey); } const cryptoWorker = await sharedCryptoWorker(); - const { encryptedData, nonce } = ensure(encryptedTwoFactorSecret); + const { encryptedData, nonce } = encryptedTwoFactorSecret!; const twoFactorSecret = await cryptoWorker.decryptB64( encryptedData, nonce, await cryptoWorker.fromHex(recoveryKey), ); const resp = await removeTwoFactor( - ensure(sessionID), + sessionID!, twoFactorSecret, twoFactorType, ); diff --git a/web/packages/accounts/pages/two-factor/setup.tsx b/web/packages/accounts/pages/two-factor/setup.tsx index 2f1cc66bd4..794a2faebe 100644 --- a/web/packages/accounts/pages/two-factor/setup.tsx +++ b/web/packages/accounts/pages/two-factor/setup.tsx @@ -5,7 +5,6 @@ import VerifyTwoFactor, { import { TwoFactorSetup } from "@/accounts/components/two-factor/setup"; import type { TwoFactorSecret } from "@/accounts/types/user"; import log from "@/base/log"; -import { ensure } from "@/utils/ensure"; import { VerticallyCentered } from "@ente/shared/components/Container"; import LinkButton from "@ente/shared/components/LinkButton"; import { encryptWithRecoveryKey } from "@ente/shared/crypto/helpers"; @@ -50,7 +49,7 @@ const Page: React.FC = () => { markSuccessful, ) => { const recoveryEncryptedTwoFactorSecret = await encryptWithRecoveryKey( - ensure(twoFactorSecret).secretCode, + twoFactorSecret!.secretCode, ); await enableTwoFactor(otp, recoveryEncryptedTwoFactorSecret); await markSuccessful(); diff --git a/web/packages/accounts/pages/two-factor/verify.tsx b/web/packages/accounts/pages/two-factor/verify.tsx index 033d1e61dc..0db7eb9fee 100644 --- a/web/packages/accounts/pages/two-factor/verify.tsx +++ b/web/packages/accounts/pages/two-factor/verify.tsx @@ -8,7 +8,6 @@ import { FormPaperFooter, FormPaperTitle, } from "@/base/components/FormPaper"; -import { ensure } from "@/utils/ensure"; import { VerticallyCentered } from "@ente/shared/components/Container"; import LinkButton from "@ente/shared/components/LinkButton"; import { ApiError } from "@ente/shared/error"; @@ -60,7 +59,7 @@ const Page: React.FC = ({ appContext }) => { encryptedToken, id, }); - setData(LS_KEYS.KEY_ATTRIBUTES, ensure(keyAttributes)); + setData(LS_KEYS.KEY_ATTRIBUTES, keyAttributes!); router.push(unstashRedirect() ?? PAGES.CREDENTIALS); } catch (e) { if ( diff --git a/web/packages/accounts/pages/verify.tsx b/web/packages/accounts/pages/verify.tsx index 6f0e410ccc..868d3e47b7 100644 --- a/web/packages/accounts/pages/verify.tsx +++ b/web/packages/accounts/pages/verify.tsx @@ -2,7 +2,6 @@ import type { UserVerificationResponse } from "@/accounts/types/user"; import { FormPaper, FormPaperTitle } from "@/base/components/FormPaper"; import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator"; import log from "@/base/log"; -import { ensure } from "@/utils/ensure"; import { VerticallyCentered } from "@ente/shared/components/Container"; import LinkButton from "@ente/shared/components/LinkButton"; import SingleInputForm, { @@ -124,7 +123,7 @@ const Page: React.FC = ({ appContext }) => { } else { if (getData(LS_KEYS.ORIGINAL_KEY_ATTRIBUTES)) { await putAttributes( - ensure(token), + token!, getData(LS_KEYS.ORIGINAL_KEY_ATTRIBUTES), ); } diff --git a/web/packages/media/file-metadata.ts b/web/packages/media/file-metadata.ts index 7ee4defb40..cd460f46a5 100644 --- a/web/packages/media/file-metadata.ts +++ b/web/packages/media/file-metadata.ts @@ -3,7 +3,6 @@ import { authenticatedRequestHeaders, ensureOk } from "@/base/http"; import { apiURL } from "@/base/origins"; import { type Location } from "@/base/types"; import { type EnteFile, type FilePublicMagicMetadata } from "@/media/file"; -import { ensure } from "@/utils/ensure"; import { nullToUndefined } from "@/utils/transform"; import { z } from "zod"; import { mergeMetadata1 } from "./file"; @@ -374,7 +373,7 @@ export const updateRemotePublicMagicMetadata = async ( metadataVersion, ); - const updatedEnvelope = ensure(updateRequest.metadataList[0]).magicMetadata; + const updatedEnvelope = updateRequest.metadataList[0]!.magicMetadata; await putFilesPublicMagicMetadata(updateRequest); diff --git a/web/packages/new/photos/components/gallery/PeopleHeader.tsx b/web/packages/new/photos/components/gallery/PeopleHeader.tsx index 37e1fe0cae..8ef463d306 100644 --- a/web/packages/new/photos/components/gallery/PeopleHeader.tsx +++ b/web/packages/new/photos/components/gallery/PeopleHeader.tsx @@ -26,7 +26,6 @@ import { type PersonSuggestionUpdates, type PreviewableCluster, } from "@/new/photos/services/ml/people"; -import { ensure } from "@/utils/ensure"; import OverflowMenu from "@ente/shared/components/OverflowMenu/menu"; import { OverflowMenuOption } from "@ente/shared/components/OverflowMenu/option"; import AddIcon from "@mui/icons-material/Add"; @@ -329,7 +328,7 @@ const AddPersonDialog: React.FC = ({ const handleAddPersonBySelect = useWrapAsyncOperation( async (personID: string) => { onClose(); - const person = ensure(cgroupPeople.find((p) => p.id == personID)); + const person = cgroupPeople.find((p) => p.id == personID)!; await addClusterToCGroup(person.cgroup, cluster); onSelectPerson(personID); }, diff --git a/web/packages/new/photos/services/ffmpeg/worker.ts b/web/packages/new/photos/services/ffmpeg/worker.ts index c8bdabb7a0..64574b23c7 100644 --- a/web/packages/new/photos/services/ffmpeg/worker.ts +++ b/web/packages/new/photos/services/ffmpeg/worker.ts @@ -1,5 +1,4 @@ import log from "@/base/log"; -import { ensure } from "@/utils/ensure"; import QueueProcessor from "@ente/shared/utils/queueProcessor"; import { expose } from "comlink"; import { @@ -107,7 +106,7 @@ const randomPrefix = () => { let result = ""; for (let i = 0; i < 10; i++) - result += ensure(alphabet[Math.floor(Math.random() * alphabet.length)]); + result += alphabet[Math.floor(Math.random() * alphabet.length)]!; return result; }; diff --git a/web/packages/new/photos/services/ml/cluster.ts b/web/packages/new/photos/services/ml/cluster.ts index e0a2003218..3d123286a8 100644 --- a/web/packages/new/photos/services/ml/cluster.ts +++ b/web/packages/new/photos/services/ml/cluster.ts @@ -2,7 +2,6 @@ import { assertionFailed } from "@/base/assert"; import { newNonSecureID } from "@/base/id-worker"; import log from "@/base/log"; import type { EnteFile } from "@/media/file"; -import { ensure } from "@/utils/ensure"; import { wait } from "@/utils/promise"; import { pullUserEntities, @@ -216,7 +215,7 @@ const sortFacesNewestOnesFirst = ( const fileForFaceID = new Map( faces.map(({ faceID }) => [ faceID, - localFileByID.get(ensure(fileIDFromFaceID(faceID))), + localFileByID.get(fileIDFromFaceID(faceID)!), ]), ); @@ -320,7 +319,7 @@ const clusterBatchLinear = async ( if (rejectedClusters) { const cjx = state.faceIDToClusterIndex.get(fj.faceID); if (cjx !== undefined) { - const cj = ensure(state.clusters[cjx]); + const cj = state.clusters[cjx]!; if (rejectedClusters.has(cj.id)) { continue; } @@ -333,11 +332,11 @@ const clusterBatchLinear = async ( if (nnIndex !== undefined) { // Found a neighbour close enough, add ourselves to its cluster. - const nnFace = ensure(faces[nnIndex]); - const nnClusterIndex = ensure( - state.faceIDToClusterIndex.get(nnFace.faceID), - ); - const nnCluster = ensure(state.clusters[nnClusterIndex]); + const nnFace = faces[nnIndex]!; + const nnClusterIndex = state.faceIDToClusterIndex.get( + nnFace.faceID, + )!; + const nnCluster = state.clusters[nnClusterIndex]!; state.faceIDToClusterID.set(fi.faceID, nnCluster.id); state.faceIDToClusterIndex.set(fi.faceID, nnClusterIndex); @@ -384,8 +383,8 @@ export const reconcileClusters = async ( ...cgroup, data: { ...cgroup.data, - assigned: cgroup.data.assigned.map(({ id }) => - ensure(clusterByID.get(id)), + assigned: cgroup.data.assigned.map( + ({ id }) => clusterByID.get(id)!, ), }, }; diff --git a/web/packages/new/photos/services/ml/crop.ts b/web/packages/new/photos/services/ml/crop.ts index 4c2def7002..a913baa81b 100644 --- a/web/packages/new/photos/services/ml/crop.ts +++ b/web/packages/new/photos/services/ml/crop.ts @@ -1,6 +1,5 @@ import { blobCache } from "@/base/blob-cache"; import type { EnteFile } from "@/media/file"; -import { ensure } from "@/utils/ensure"; import { fetchRenderableEnteFileBlob } from "./blob"; import { type Box, type FaceIndex } from "./face"; import { clamp } from "./math"; @@ -106,7 +105,7 @@ export const extractFaceCrop = (imageBitmap: ImageBitmap, faceBox: Box) => { const height = clamp(heightCrop, 0, imageHeight - y); const canvas = new OffscreenCanvas(width, height); - const ctx = ensure(canvas.getContext("2d")); + const ctx = canvas.getContext("2d")!; ctx.imageSmoothingQuality = "high"; ctx.drawImage(imageBitmap, x, y, width, height, 0, 0, width, height); diff --git a/web/packages/new/photos/services/ml/image.ts b/web/packages/new/photos/services/ml/image.ts index 80f80f0c7f..8d91842922 100644 --- a/web/packages/new/photos/services/ml/image.ts +++ b/web/packages/new/photos/services/ml/image.ts @@ -1,4 +1,3 @@ -import { ensure } from "@/utils/ensure"; import { Matrix, inverse } from "ml-matrix"; import { clamp } from "./math"; @@ -14,10 +13,10 @@ const pixelRGBA = ( } const index = (y * width + x) * 4; return { - r: ensure(imageData[index]), - g: ensure(imageData[index + 1]), - b: ensure(imageData[index + 2]), - a: ensure(imageData[index + 3]), + r: imageData[index]!, + g: imageData[index + 1]!, + b: imageData[index + 2]!, + a: imageData[index + 3]!, }; }; diff --git a/web/packages/new/photos/services/ml/index.ts b/web/packages/new/photos/services/ml/index.ts index 5aea3e396b..f79c4eefff 100644 --- a/web/packages/new/photos/services/ml/index.ts +++ b/web/packages/new/photos/services/ml/index.ts @@ -11,7 +11,6 @@ import type { Electron } from "@/base/types/ipc"; import { ComlinkWorker } from "@/base/worker/comlink-worker"; import type { EnteFile } from "@/media/file"; import { FileType } from "@/media/file-type"; -import { ensure } from "@/utils/ensure"; import { throttled } from "@/utils/promise"; import { proxy, transfer } from "comlink"; import { getRemoteFlag, updateRemoteFlag } from "../remote-store"; @@ -170,7 +169,7 @@ const createMLWorker = (electron: Electron): Promise => { // preload script. The data is the message that was posted. if (source == window && data == "createMLWorker/port") { window.removeEventListener("message", l); - resolve(ensure(ports[0])); + resolve(ports[0]!); } }; window.addEventListener("message", l); diff --git a/web/packages/new/photos/services/ml/people.ts b/web/packages/new/photos/services/ml/people.ts index fae1c189ef..f7592c4a52 100644 --- a/web/packages/new/photos/services/ml/people.ts +++ b/web/packages/new/photos/services/ml/people.ts @@ -2,7 +2,6 @@ import { assertionFailed } from "@/base/assert"; import log from "@/base/log"; import type { EnteFile } from "@/media/file"; import { shuffled } from "@/utils/array"; -import { ensure } from "@/utils/ensure"; import { getLocalFiles } from "../files"; import { savedCGroups, @@ -509,7 +508,7 @@ export const _suggestionsAndChoicesForPerson = async ( if (csims.length == 0) continue; - const medianSim = ensure(csims[Math.floor(csims.length / 2)]); + const medianSim = csims[Math.floor(csims.length / 2)]!; if (medianSim > 0.48) { candidateClustersAndSimilarity.push([cluster, medianSim]); } @@ -572,7 +571,7 @@ export const _suggestionsAndChoicesForPerson = async ( // 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. - const firstChoice = { ...ensure(assignedChoices[0]), fixed: true }; + const firstChoice = { ...assignedChoices[0]!, fixed: true }; const restChoices = assignedChoices.slice(1).concat(rejectedChoices); sortBySize(restChoices); @@ -662,7 +661,7 @@ export const _applyPersonSuggestionUpdates = async ( let rejectUpdateCount = 0; const clusterWithID = (clusterID: string) => - ensure(localClusters.find((c) => c.id == clusterID)); + localClusters.find((c) => c.id == clusterID)!; // Add cluster with `clusterID` to the list of assigned clusters. const assign = (clusterID: string) => { @@ -692,7 +691,7 @@ export const _applyPersonSuggestionUpdates = async ( // part of the remote data. Since we're removing it from the remote // state, add it to the local state instead so that the user can see // it in their saved choices (local only). - localClusters.push(ensure(cluster)); + localClusters.push(cluster!); } }; diff --git a/web/packages/new/photos/services/ml/worker.ts b/web/packages/new/photos/services/ml/worker.ts index d9270ce911..1e91c974b6 100644 --- a/web/packages/new/photos/services/ml/worker.ts +++ b/web/packages/new/photos/services/ml/worker.ts @@ -6,7 +6,6 @@ import { ensureAuthToken } from "@/base/local-user"; import log from "@/base/log"; import type { ElectronMLWorker } from "@/base/types/ipc"; import { fileLogID, type EnteFile } from "@/media/file"; -import { ensure } from "@/utils/ensure"; import { wait } from "@/utils/promise"; import { expose, wrap } from "comlink"; import downloadManager from "../download"; @@ -207,7 +206,7 @@ export class MLWorker { * Find {@link CLIPMatches} for a given normalized {@link searchPhrase}. */ async clipMatches(searchPhrase: string): Promise { - return _clipMatches(searchPhrase, ensure(this.electron)); + return _clipMatches(searchPhrase, this.electron!); } private async tick() { @@ -241,7 +240,7 @@ export class MLWorker { // Index them. const allSuccess = await indexNextBatch( items, - ensure(this.electron), + this.electron!, this.delegate, ); if (allSuccess) { @@ -284,7 +283,7 @@ export class MLWorker { /** Return the next batch of items to backfill (if any). */ private async backfillQ() { - const userID = ensure(await getKVN("userID")); + const userID = (await getKVN("userID"))!; // Find files that our local DB thinks need syncing. const fileByID = await syncWithLocalFilesAndGetFilesToIndex( userID, @@ -392,7 +391,7 @@ const indexNextBatch = async ( .catch(() => { allSuccess = false; tasks[j] = undefined; - }))(ensure(items[i++]), j); + }))(items[i++]!, j); } } @@ -450,9 +449,7 @@ const syncWithLocalFilesAndGetFilesToIndex = async ( ); const fileIDsToIndex = await getIndexableFileIDs(count); - return new Map( - fileIDsToIndex.map((id) => [id, ensure(localFileByID.get(id))]), - ); + return new Map(fileIDsToIndex.map((id) => [id, localFileByID.get(id)!])); }; /** diff --git a/web/packages/new/photos/services/search/worker.ts b/web/packages/new/photos/services/search/worker.ts index d4d4667062..323d50d33f 100644 --- a/web/packages/new/photos/services/search/worker.ts +++ b/web/packages/new/photos/services/search/worker.ts @@ -3,7 +3,6 @@ import type { Location } from "@/base/types"; import type { Collection } from "@/media/collection"; import type { EnteFile } from "@/media/file"; import { fileCreationPhotoDate, fileLocation } from "@/media/file-metadata"; -import { ensure } from "@/utils/ensure"; import { nullToUndefined } from "@/utils/transform"; import { getPublicMagicMetadataSync } from "@ente/shared/file-metadata"; import type { Component } from "chrono-node"; @@ -470,7 +469,6 @@ const sortMatchesIfNeeded = ( ) => { if (suggestion.type != "clip") return files; // Sort CLIP matches by their corresponding scores. - const score = ({ id }: EnteFile) => - ensure(suggestion.clipScoreForFileID.get(id)); + const score = ({ id }: EnteFile) => suggestion.clipScoreForFileID.get(id)!; return files.sort((a, b) => score(b) - score(a)); }; diff --git a/web/packages/new/photos/services/user-entity/remote.ts b/web/packages/new/photos/services/user-entity/remote.ts index c7c02c170f..59715218f8 100644 --- a/web/packages/new/photos/services/user-entity/remote.ts +++ b/web/packages/new/photos/services/user-entity/remote.ts @@ -2,7 +2,6 @@ import { decryptBlob } from "@/base/crypto"; import type { EncryptedBlobB64 } from "@/base/crypto/types"; import { authenticatedRequestHeaders, ensureOk, HTTPError } from "@/base/http"; import { apiURL } from "@/base/origins"; -import { ensure } from "@/utils/ensure"; import { z } from "zod"; import type { EntityType } from "."; @@ -140,7 +139,7 @@ export const userEntityDiff = async ( async ({ id, encryptedData, header, isDeleted, updatedAt }) => ({ id, data: !isDeleted - ? await decrypt(ensure(encryptedData), ensure(header)) + ? await decrypt(encryptedData!, header!) : undefined, updatedAt, }), diff --git a/web/packages/new/photos/types/context.ts b/web/packages/new/photos/types/context.ts index dfc2638286..903eee593f 100644 --- a/web/packages/new/photos/types/context.ts +++ b/web/packages/new/photos/types/context.ts @@ -1,5 +1,4 @@ import type { AccountsContextT } from "@/accounts/types/context"; -import { ensure } from "@/utils/ensure"; import { THEME_COLOR } from "@ente/shared/themes/constants"; import { createContext, useContext } from "react"; import type { SetNotificationAttributes } from "./notification"; @@ -42,4 +41,4 @@ export const AppContext = createContext(undefined); * This context is provided at the top level _app component for the photos app, * and thus is available to all React components in the Photos app's React tree. */ -export const useAppContext = (): AppContextT => ensure(useContext(AppContext)); +export const useAppContext = (): AppContextT => useContext(AppContext)!; diff --git a/web/packages/shared/utils/index.ts b/web/packages/shared/utils/index.ts index fd372fd7cf..878f2d66f8 100644 --- a/web/packages/shared/utils/index.ts +++ b/web/packages/shared/utils/index.ts @@ -1,4 +1,3 @@ -import { ensure } from "@/utils/ensure"; import { wait } from "@/utils/promise"; export async function retryAsyncFunction( @@ -25,7 +24,7 @@ export async function retryAsyncFunction( if (attemptNumber === waitTimeBeforeNextTry.length) { throw e; } - await wait(ensure(waitTimeBeforeNextTry[attemptNumber])); + await wait(waitTimeBeforeNextTry[attemptNumber]!); } } } diff --git a/web/packages/shared/utils/queueProcessor.ts b/web/packages/shared/utils/queueProcessor.ts index 15daf069d6..0661e34533 100644 --- a/web/packages/shared/utils/queueProcessor.ts +++ b/web/packages/shared/utils/queueProcessor.ts @@ -1,5 +1,3 @@ -import { ensure } from "@/utils/ensure"; - interface RequestQueueItem { request: (canceller?: RequestCanceller) => Promise; successCallback: (response: any) => void; @@ -50,7 +48,7 @@ export default class QueueProcessor { this.isProcessingRequest = true; while (this.requestQueue.length > 0) { - const queueItem = ensure(this.requestQueue.shift()); + const queueItem = this.requestQueue.shift()!; let response = null; if (queueItem.isCanceled.status) { From 24b9e629c1c0c0d44d892a3b0b7f2b10ca339797 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Mon, 18 Nov 2024 18:18:18 +0530 Subject: [PATCH 017/177] Cont --- .../photos/src/components/FixCreationTime.tsx | 5 ++--- .../PhotoViewer/ImageEditorOverlay/index.tsx | 9 ++++---- .../photos/src/components/Upload/Uploader.tsx | 21 +++++++------------ .../photos/src/components/WatchFolder.tsx | 3 +-- web/apps/photos/src/pages/gallery.tsx | 3 +-- .../photos/src/services/upload/thumbnail.ts | 7 +++---- .../src/services/upload/uploadManager.ts | 19 +++++++---------- web/apps/photos/src/utils/photoFrame/index.ts | 9 ++++---- .../accounts/components/RecoveryKey.tsx | 3 +-- web/packages/accounts/pages/change-email.tsx | 3 +-- .../accounts/pages/change-password.tsx | 5 ++--- web/packages/accounts/services/passkey.ts | 3 +-- web/packages/accounts/services/session.ts | 3 +-- .../photos/components/CollectionSelector.tsx | 5 ++--- .../new/photos/components/PlanSelector.tsx | 8 ++----- .../new/photos/components/gallery/BarImpl.tsx | 11 +++++----- .../new/photos/components/gallery/reducer.ts | 19 ++++++++--------- web/packages/new/photos/services/download.ts | 18 +++++++--------- web/packages/new/photos/services/ml/blob.ts | 7 +++---- .../new/photos/services/user-details.ts | 3 +-- 20 files changed, 65 insertions(+), 99 deletions(-) diff --git a/web/apps/photos/src/components/FixCreationTime.tsx b/web/apps/photos/src/components/FixCreationTime.tsx index 91ea1ecb90..6bb9b16d43 100644 --- a/web/apps/photos/src/components/FixCreationTime.tsx +++ b/web/apps/photos/src/components/FixCreationTime.tsx @@ -12,7 +12,6 @@ import { FileType } from "@/media/file-type"; import { PhotoDateTimePicker } from "@/new/photos/components/PhotoDateTimePicker"; import downloadManager from "@/new/photos/services/download"; import { extractExifDates } from "@/new/photos/services/exif"; -import { ensure } from "@/utils/ensure"; import { Box, Dialog, @@ -318,11 +317,11 @@ const updateEnteFileDate = async ( if (fixOption == "custom") { newDate = { - dateTime: ensure(customDate).dateTime, + dateTime: customDate!.dateTime, // See [Note: Don't modify offsetTime when editing date via picker] // for why we don't also set the offset here. offset: undefined, - timestamp: ensure(customDate).timestamp, + timestamp: customDate!.timestamp, }; } else if (enteFile.metadata.fileType == FileType.image) { const stream = await downloadManager.getFile(enteFile); diff --git a/web/apps/photos/src/components/PhotoViewer/ImageEditorOverlay/index.tsx b/web/apps/photos/src/components/PhotoViewer/ImageEditorOverlay/index.tsx index 248976a57b..c5aab81f81 100644 --- a/web/apps/photos/src/components/PhotoViewer/ImageEditorOverlay/index.tsx +++ b/web/apps/photos/src/components/PhotoViewer/ImageEditorOverlay/index.tsx @@ -12,7 +12,6 @@ import { EnteFile } from "@/media/file"; import { photosDialogZIndex } from "@/new/photos/components/utils/z-index"; import downloadManager from "@/new/photos/services/download"; import { AppContext } from "@/new/photos/types/context"; -import { ensure } from "@/utils/ensure"; import { CenteredFlex, HorizontalFlex, @@ -453,7 +452,7 @@ const ImageEditorOverlay = (props: IProps) => { } const getEditedFile = async () => { - const originalSizeCanvas = ensure(originalSizeCanvasRef.current); + const originalSizeCanvas = originalSizeCanvasRef.current!; const originalFileName = props.file.metadata.title; return canvasToFile(originalSizeCanvas, originalFileName, mimeType); }; @@ -777,9 +776,9 @@ const canvasToFile = async ( break; } - const blob = ensure( - await new Promise((resolve) => canvas.toBlob(resolve, mimeType)), - ); + const blob = (await new Promise((resolve) => + canvas.toBlob(resolve, mimeType), + ))!; const [originalName] = nameAndExtension(originalFileName); const fileName = `${originalName}-edited.${extension}`; diff --git a/web/apps/photos/src/components/Upload/Uploader.tsx b/web/apps/photos/src/components/Upload/Uploader.tsx index 7b0e0e2378..e5acb2f6d4 100644 --- a/web/apps/photos/src/components/Upload/Uploader.tsx +++ b/web/apps/photos/src/components/Upload/Uploader.tsx @@ -18,7 +18,6 @@ import { redirectToCustomerPortal } from "@/new/photos/services/user-details"; import { useAppContext } from "@/new/photos/types/context"; import { NotificationAttributes } from "@/new/photos/types/notification"; import { firstNonEmpty } from "@/utils/array"; -import { ensure } from "@/utils/ensure"; import { CustomError } from "@ente/shared/error"; import DiscFullIcon from "@mui/icons-material/DiscFull"; import InfoOutlined from "@mui/icons-material/InfoRounded"; @@ -853,17 +852,13 @@ const desktopFilesAndZipItems = async (electron: Electron, files: File[]) => { * https://github.com/react-dropzone/file-selector/blob/master/src/file.ts#L1214 */ const pathLikeForWebFile = (file: File): string => - ensure( - firstNonEmpty([ - // We need to check first, since path is not a property of - // the standard File objects. - "path" in file && typeof file.path == "string" - ? file.path - : undefined, - file.webkitRelativePath, - file.name, - ]), - ); + firstNonEmpty([ + // We need to check first, since path is not a property of + // the standard File objects. + "path" in file && typeof file.path == "string" ? file.path : undefined, + file.webkitRelativePath, + file.name, + ])!; // This is used to prompt the user the make upload strategy choice interface ImportSuggestion { @@ -889,7 +884,7 @@ function getImportSuggestion( const separatorCounts = new Map( paths.map((s) => [s, s.match(/\//g)?.length ?? 0]), ); - const separatorCount = (s: string) => ensure(separatorCounts.get(s)); + const separatorCount = (s: string) => separatorCounts.get(s)!; paths.sort((path1, path2) => separatorCount(path1) - separatorCount(path2)); const firstPath = paths[0]; const lastPath = paths[paths.length - 1]; diff --git a/web/apps/photos/src/components/WatchFolder.tsx b/web/apps/photos/src/components/WatchFolder.tsx index 0446509b2e..f8b75086ee 100644 --- a/web/apps/photos/src/components/WatchFolder.tsx +++ b/web/apps/photos/src/components/WatchFolder.tsx @@ -9,7 +9,6 @@ import type { CollectionMapping, FolderWatch } from "@/base/types/ipc"; import { CollectionMappingChoiceDialog } from "@/new/photos/components/CollectionMappingChoiceDialog"; import { DialogCloseIconButton } from "@/new/photos/components/mui/Dialog"; import { AppContext, useAppContext } from "@/new/photos/types/context"; -import { ensure } from "@/utils/ensure"; import { FlexWrapper, HorizontalFlex, @@ -109,7 +108,7 @@ export const WatchFolder: React.FC = ({ const handleCollectionMappingSelect = (mapping: CollectionMapping) => { setSavedFolderPath(undefined); - addWatch(ensure(savedFolderPath), mapping); + addWatch(savedFolderPath!, mapping); }; return ( diff --git a/web/apps/photos/src/pages/gallery.tsx b/web/apps/photos/src/pages/gallery.tsx index f6deba1397..e929aa6707 100644 --- a/web/apps/photos/src/pages/gallery.tsx +++ b/web/apps/photos/src/pages/gallery.tsx @@ -55,7 +55,6 @@ import { } from "@/new/photos/services/user-details"; import { useAppContext } from "@/new/photos/types/context"; import { splitByPredicate } from "@/utils/array"; -import { ensure } from "@/utils/ensure"; import { CenteredFlex, FlexWrapper, @@ -512,7 +511,7 @@ export default function Gallery() { } : { mode: barMode as "albums" | "hidden-albums", - collectionID: ensure(activeCollectionID), + collectionID: activeCollectionID!, }, }; diff --git a/web/apps/photos/src/services/upload/thumbnail.ts b/web/apps/photos/src/services/upload/thumbnail.ts index a768a04622..281a91fa52 100644 --- a/web/apps/photos/src/services/upload/thumbnail.ts +++ b/web/apps/photos/src/services/upload/thumbnail.ts @@ -8,7 +8,6 @@ import { toDataOrPathOrZipEntry, type DesktopUploadItem, } from "@/new/photos/services/upload/types"; -import { ensure } from "@/utils/ensure"; import { withTimeout } from "@/utils/promise"; /** Maximum width or height of the generated thumbnail */ @@ -70,7 +69,7 @@ const generateImageThumbnailWeb = async ( const generateImageThumbnailUsingCanvas = async (blob: Blob) => { const canvas = document.createElement("canvas"); - const canvasCtx = ensure(canvas.getContext("2d")); + const canvasCtx = canvas.getContext("2d")!; const imageURL = URL.createObjectURL(blob); await withTimeout( @@ -118,7 +117,7 @@ const compressedJPEGData = async (canvas: HTMLCanvasElement) => { percentageSizeDiff(blob.size, prevSize) >= 10 ); - return new Uint8Array(await ensure(blob).arrayBuffer()); + return new Uint8Array(await blob!.arrayBuffer()); }; const percentageSizeDiff = ( @@ -140,7 +139,7 @@ const generateVideoThumbnailWeb = async (blob: Blob) => { export const generateVideoThumbnailUsingCanvas = async (blob: Blob) => { const canvas = document.createElement("canvas"); - const canvasCtx = ensure(canvas.getContext("2d")); + const canvasCtx = canvas.getContext("2d")!; const videoURL = URL.createObjectURL(blob); await withTimeout( diff --git a/web/apps/photos/src/services/upload/uploadManager.ts b/web/apps/photos/src/services/upload/uploadManager.ts index 12ddef9ee2..cb733f7a5c 100644 --- a/web/apps/photos/src/services/upload/uploadManager.ts +++ b/web/apps/photos/src/services/upload/uploadManager.ts @@ -18,7 +18,6 @@ import { UPLOAD_RESULT, type UploadPhase, } from "@/new/photos/services/upload/types"; -import { ensure } from "@/utils/ensure"; import { wait } from "@/utils/promise"; import { CustomError } from "@ente/shared/error"; import { Canceler } from "axios"; @@ -514,9 +513,7 @@ class UploadManager { this.abortIfCancelled(); log.info(`Parsing metadata JSON ${fileName}`); - const metadataJSON = await tryParseTakeoutMetadataJSON( - ensure(uploadItem), - ); + const metadataJSON = await tryParseTakeoutMetadataJSON(uploadItem!); if (metadataJSON) { this.parsedMetadataJSONMap.set( getMetadataJSONMapKeyForJSON(collectionID, fileName), @@ -757,13 +754,11 @@ type UploadItemWithCollectionIDAndName = UploadAsset & { const makeUploadItemWithCollectionIDAndName = ( f: UploadItemWithCollection, ): UploadItemWithCollectionIDAndName => ({ - localID: ensure(f.localID), - collectionID: ensure(f.collectionID), - fileName: ensure( - f.isLivePhoto - ? uploadItemFileName(f.livePhotoAssets.image) - : uploadItemFileName(f.uploadItem), - ), + localID: f.localID!, + collectionID: f.collectionID!, + fileName: (f.isLivePhoto + ? uploadItemFileName(f.livePhotoAssets.image) + : uploadItemFileName(f.uploadItem))!, isLivePhoto: f.isLivePhoto, uploadItem: f.uploadItem, livePhotoAssets: f.livePhotoAssets, @@ -835,7 +830,7 @@ const markUploaded = async (electron: Electron, item: ClusteredUploadItem) => { ); } } else { - const p = ensure(item.uploadItem); + const p = item.uploadItem!; if (Array.isArray(p)) { electron.markUploadedZipItems([p]); } else if (typeof p == "string") { diff --git a/web/apps/photos/src/utils/photoFrame/index.ts b/web/apps/photos/src/utils/photoFrame/index.ts index 651848f1d9..3665478a05 100644 --- a/web/apps/photos/src/utils/photoFrame/index.ts +++ b/web/apps/photos/src/utils/photoFrame/index.ts @@ -4,7 +4,6 @@ import { EnteFile } from "@/media/file"; import { FileType } from "@/media/file-type"; import type { SelectionContext } from "@/new/photos/components/gallery"; import type { GalleryBarMode } from "@/new/photos/components/gallery/reducer"; -import { ensure } from "@/utils/ensure"; import { SetSelectedState } from "types/gallery"; export async function playVideo(livePhotoVideo, livePhotoImage) { @@ -137,10 +136,10 @@ export const handleSelectCreator = ...selected, context: mode == "people" - ? { mode, personID: ensure(activePersonID) } + ? { mode, personID: activePersonID! } : { mode, - collectionID: ensure(activeCollectionID), + collectionID: activeCollectionID!, }, }; } else { @@ -153,10 +152,10 @@ export const handleSelectCreator = collectionID: 0, context: mode == "people" - ? { mode, personID: ensure(activePersonID) } + ? { mode, personID: activePersonID! } : { mode, - collectionID: ensure(activeCollectionID), + collectionID: activeCollectionID!, }, }; } else { diff --git a/web/packages/accounts/components/RecoveryKey.tsx b/web/packages/accounts/components/RecoveryKey.tsx index 3b26ff969c..ba5e4f4e64 100644 --- a/web/packages/accounts/components/RecoveryKey.tsx +++ b/web/packages/accounts/components/RecoveryKey.tsx @@ -7,7 +7,6 @@ import { useIsSmallWidth } from "@/base/hooks"; import log from "@/base/log"; import { downloadString } from "@/base/utils/web"; import { DialogCloseIconButton } from "@/new/photos/components/mui/Dialog"; -import { ensure } from "@/utils/ensure"; import CodeBlock from "@ente/shared/components/CodeBlock"; import { getRecoveryKey } from "@ente/shared/crypto/helpers"; import { @@ -58,7 +57,7 @@ export const RecoveryKey: React.FC = ({ }, [open, handleLoadError]); const handleSaveClick = () => { - downloadRecoveryKeyMnemonic(ensure(recoveryKey)); + downloadRecoveryKeyMnemonic(recoveryKey!); onClose(); }; diff --git a/web/packages/accounts/pages/change-email.tsx b/web/packages/accounts/pages/change-email.tsx index f60f96f6d0..94b182e01a 100644 --- a/web/packages/accounts/pages/change-email.tsx +++ b/web/packages/accounts/pages/change-email.tsx @@ -5,7 +5,6 @@ import { FormPaperTitle, } from "@/base/components/FormPaper"; import { LoadingButton } from "@/base/components/mui/LoadingButton"; -import { ensure } from "@/utils/ensure"; import { VerticallyCentered } from "@ente/shared/components/Container"; import LinkButton from "@ente/shared/components/LinkButton"; import { LS_KEYS, getData, setLSUser } from "@ente/shared/storage/localStorage"; @@ -81,7 +80,7 @@ const ChangeEmailForm: React.FC = () => { ) => { try { setLoading(true); - await changeEmail(email, ensure(ott)); + await changeEmail(email, ott!); await setLSUser({ ...getData(LS_KEYS.USER), email }); setLoading(false); goToApp(); diff --git a/web/packages/accounts/pages/change-password.tsx b/web/packages/accounts/pages/change-password.tsx index a8750be29f..54870dcd3c 100644 --- a/web/packages/accounts/pages/change-password.tsx +++ b/web/packages/accounts/pages/change-password.tsx @@ -19,7 +19,6 @@ import { FormPaperTitle, } from "@/base/components/FormPaper"; import { sharedCryptoWorker } from "@/base/crypto"; -import { ensure } from "@/utils/ensure"; import { VerticallyCentered } from "@ente/shared/components/Container"; import LinkButton from "@ente/shared/components/LinkButton"; import { @@ -94,7 +93,7 @@ const Page: React.FC = () => { const srpA = convertBufferToBase64(srpClient.computeA()); - const { setupID, srpB } = await startSRPSetup(ensure(token), { + const { setupID, srpB } = await startSRPSetup(token!, { srpUserID, srpSalt, srpVerifier, @@ -105,7 +104,7 @@ const Page: React.FC = () => { const srpM1 = convertBufferToBase64(srpClient.computeM1()); - await updateSRPAndKeys(ensure(token), { + await updateSRPAndKeys(token!, { setupID, srpM1, updatedKeyAttr: updatedKey, diff --git a/web/packages/accounts/services/passkey.ts b/web/packages/accounts/services/passkey.ts index ac68f470b0..a15b5ec0a9 100644 --- a/web/packages/accounts/services/passkey.ts +++ b/web/packages/accounts/services/passkey.ts @@ -5,7 +5,6 @@ import { clientPackageHeader, HTTPError } from "@/base/http"; import log from "@/base/log"; import { accountsAppOrigin, apiURL } from "@/base/origins"; import { TwoFactorAuthorizationResponse } from "@/base/types/credentials"; -import { ensure } from "@/utils/ensure"; import { getRecoveryKey } from "@ente/shared/crypto/helpers"; import HTTPService from "@ente/shared/network/HTTPService"; import { @@ -263,7 +262,7 @@ export const saveCredentialsAndNavigateTo = async ( encryptedToken, id, }); - setData(LS_KEYS.KEY_ATTRIBUTES, ensure(keyAttributes)); + setData(LS_KEYS.KEY_ATTRIBUTES, keyAttributes!); return unstashRedirect() ?? "/credentials"; }; diff --git a/web/packages/accounts/services/session.ts b/web/packages/accounts/services/session.ts index 77389e6d02..0830ab923b 100644 --- a/web/packages/accounts/services/session.ts +++ b/web/packages/accounts/services/session.ts @@ -1,7 +1,6 @@ import { authenticatedRequestHeaders, HTTPError } from "@/base/http"; import { ensureLocalUser } from "@/base/local-user"; import { apiURL } from "@/base/origins"; -import { ensure } from "@/utils/ensure"; import { getData, LS_KEYS } from "@ente/shared/storage/localStorage"; import type { KeyAttributes } from "@ente/shared/user/types"; import type { SRPAttributes } from "../api/srp"; @@ -86,7 +85,7 @@ export const checkSessionValidity = async (): Promise => { // We should have these values locally if we reach here. const email = ensureLocalUser().email; - const localSRPAttributes = ensure(getData(LS_KEYS.SRP_ATTRIBUTES)); + const localSRPAttributes = getData(LS_KEYS.SRP_ATTRIBUTES)!; // Fetch the remote SRP attributes. // diff --git a/web/packages/new/photos/components/CollectionSelector.tsx b/web/packages/new/photos/components/CollectionSelector.tsx index 3619b3ab0a..cbbdec0311 100644 --- a/web/packages/new/photos/components/CollectionSelector.tsx +++ b/web/packages/new/photos/components/CollectionSelector.tsx @@ -14,7 +14,6 @@ import { type CollectionSummaries, type CollectionSummary, } from "@/new/photos/services/collection/ui"; -import { ensure } from "@/utils/ensure"; import { Dialog, DialogContent, @@ -126,8 +125,8 @@ export const CollectionSelector: React.FC = ({ }) .sort((a, b) => { return ( - ensure(CollectionSummaryOrder.get(a.type)) - - ensure(CollectionSummaryOrder.get(b.type)) + CollectionSummaryOrder.get(a.type)! - + CollectionSummaryOrder.get(b.type)! ); }); diff --git a/web/packages/new/photos/components/PlanSelector.tsx b/web/packages/new/photos/components/PlanSelector.tsx index 5f8061897f..5d43857d60 100644 --- a/web/packages/new/photos/components/PlanSelector.tsx +++ b/web/packages/new/photos/components/PlanSelector.tsx @@ -32,7 +32,6 @@ import { import { useAppContext } from "@/new/photos/types/context"; import { bytesInGB, formattedStorageByteSize } from "@/new/photos/utils/units"; import { openURL } from "@/new/photos/utils/web"; -import { ensure } from "@/utils/ensure"; import { FlexWrapper, FluidContainer, @@ -158,7 +157,7 @@ const PlanSelectorCard: React.FC = ({ case "buyPlan": try { setLoading(true); - await redirectToPaymentsApp(ensure(plan.stripeID), "buy"); + await redirectToPaymentsApp(plan.stripeID!, "buy"); } catch (e) { setLoading(false); showMiniDialog( @@ -176,10 +175,7 @@ const PlanSelectorCard: React.FC = ({ continue: { text: t("update_subscription"), action: () => - redirectToPaymentsApp( - ensure(plan.stripeID), - "update", - ), + redirectToPaymentsApp(plan.stripeID!, "update"), }, }); break; diff --git a/web/packages/new/photos/components/gallery/BarImpl.tsx b/web/packages/new/photos/components/gallery/BarImpl.tsx index 29c9d7987c..7d4875f32c 100644 --- a/web/packages/new/photos/components/gallery/BarImpl.tsx +++ b/web/packages/new/photos/components/gallery/BarImpl.tsx @@ -18,7 +18,6 @@ import type { CollectionsSortBy, } from "@/new/photos/services/collection/ui"; import type { Person } from "@/new/photos/services/ml/people"; -import { ensure } from "@/utils/ensure"; import ArchiveIcon from "@mui/icons-material/Archive"; import ExpandMore from "@mui/icons-material/ExpandMore"; import Favorite from "@mui/icons-material/FavoriteRounded"; @@ -201,7 +200,7 @@ export const GalleryBarImpl: React.FC = ({ ? { type: "collections", collectionSummaries, - activeCollectionID: ensure(activeCollectionID), + activeCollectionID: activeCollectionID!, onSelectCollectionID, } : { @@ -439,11 +438,11 @@ const getItemCount = (data: ItemData) => { const getItemKey = (index: number, data: ItemData) => { switch (data.type) { case "collections": { - const collectionSummary = ensure(data.collectionSummaries[index]); + const collectionSummary = data.collectionSummaries[index]!; return `${data.type}-${collectionSummary.id}-${collectionSummary.coverFile?.id}`; } case "people": { - const person = ensure(data.people[index]); + const person = data.people[index]!; return `${data.type}-${person.id}-${person.displayFaceID}`; } } @@ -461,7 +460,7 @@ const ListItem = memo((props: ListChildComponentProps) => { activeCollectionID, onSelectCollectionID, } = data; - const collectionSummary = ensure(collectionSummaries[index]); + const collectionSummary = collectionSummaries[index]!; card = ( ) => { case "people": { const { people, activePerson, onSelectPerson } = data; - const person = ensure(people[index]); + const person = people[index]!; card = ( = ( action.collections.concat(state.hiddenCollections), ), collectionSummaries: deriveCollectionSummaries( - ensure(state.user), + state.user!, action.collections, state.files, state.trashedFiles, @@ -464,14 +463,14 @@ const galleryReducer: React.Reducer = ( action.collections.concat(action.hiddenCollections), ), collectionSummaries: deriveCollectionSummaries( - ensure(state.user), + state.user!, action.collections, state.files, state.trashedFiles, archivedCollectionIDs, ), hiddenCollectionSummaries: deriveHiddenCollectionSummaries( - ensure(state.user), + state.user!, action.hiddenCollections, state.hiddenFiles, ), @@ -488,7 +487,7 @@ const galleryReducer: React.Reducer = ( ), fileCollectionIDs: createFileCollectionIDs(action.files), collectionSummaries: deriveCollectionSummaries( - ensure(state.user), + state.user!, state.collections, files, state.trashedFiles, @@ -511,7 +510,7 @@ const galleryReducer: React.Reducer = ( ), fileCollectionIDs: createFileCollectionIDs(action.files), collectionSummaries: deriveCollectionSummaries( - ensure(state.user), + state.user!, state.collections, files, state.trashedFiles, @@ -532,7 +531,7 @@ const galleryReducer: React.Reducer = ( // TODO: Consider batching this instead of doing it per file // upload to speed up uploads. Perf test first though. collectionSummaries: deriveCollectionSummaries( - ensure(state.user), + state.user!, state.collections, files, state.trashedFiles, @@ -547,7 +546,7 @@ const galleryReducer: React.Reducer = ( hiddenFiles, hiddenFileIDs: deriveHiddenFileIDs(hiddenFiles), hiddenCollectionSummaries: deriveHiddenCollectionSummaries( - ensure(state.user), + state.user!, state.hiddenCollections, hiddenFiles, ), @@ -567,7 +566,7 @@ const galleryReducer: React.Reducer = ( hiddenFiles, hiddenFileIDs: deriveHiddenFileIDs(hiddenFiles), hiddenCollectionSummaries: deriveHiddenCollectionSummaries( - ensure(state.user), + state.user!, state.hiddenCollections, hiddenFiles, ), @@ -578,7 +577,7 @@ const galleryReducer: React.Reducer = ( ...state, trashedFiles: action.trashedFiles, collectionSummaries: deriveCollectionSummaries( - ensure(state.user), + state.user!, state.collections, state.files, action.trashedFiles, diff --git a/web/packages/new/photos/services/download.ts b/web/packages/new/photos/services/download.ts index 2da7a4c5e7..5973c5f0ff 100644 --- a/web/packages/new/photos/services/download.ts +++ b/web/packages/new/photos/services/download.ts @@ -12,7 +12,6 @@ import { FileType } from "@/media/file-type"; import { decodeLivePhoto } from "@/media/live-photo"; import * as ffmpeg from "@/new/photos/services/ffmpeg"; import { renderableImageBlob } from "@/new/photos/utils/file"; -import { ensure } from "@/utils/ensure"; import { CustomError } from "@ente/shared/error"; import HTTPService from "@ente/shared/network/HTTPService"; import { retryAsyncFunction } from "@ente/shared/utils"; @@ -90,8 +89,8 @@ class DownloadManagerImpl { ); return { - downloadClient: ensure(this.downloadClient), - cryptoWorker: ensure(this.cryptoWorker), + downloadClient: this.downloadClient!, + cryptoWorker: this.cryptoWorker!, }; } @@ -200,9 +199,8 @@ class DownloadManagerImpl { // TODO: Is this ensure valid? // The existing code was already dereferencing, so it shouldn't // affect behaviour. - const { url: originalFileURL } = ensure( - await this.fileObjectURLPromises.get(file.id), - ); + const { url: originalFileURL } = + (await this.fileObjectURLPromises.get(file.id))!; const converted = await getRenderableFileURL( file, @@ -255,9 +253,7 @@ class DownloadManagerImpl { // TODO: Is this ensure valid? // The existing code was already dereferencing, so it shouldn't // affect behaviour. - const fileURLs = ensure( - await this.fileObjectURLPromises.get(file.id), - ); + const fileURLs = (await this.fileObjectURLPromises.get(file.id))!; if (fileURLs.isOriginal) { const fileStream = (await fetch(fileURLs.url as string)).body; return fileStream; @@ -515,8 +511,8 @@ async function getRenderableFileURL( } } - // TODO: Can we remove this ensure and reflect it in the types? - return { url: ensure(url), isOriginal, isRenderable, type, mimeType }; + // TODO: Can we remove this non-null assertion and reflect it in the types? + return { url: url!, isOriginal, isRenderable, type, mimeType }; } async function getRenderableLivePhotoURL( diff --git a/web/packages/new/photos/services/ml/blob.ts b/web/packages/new/photos/services/ml/blob.ts index 234a030172..380c7bfa0d 100644 --- a/web/packages/new/photos/services/ml/blob.ts +++ b/web/packages/new/photos/services/ml/blob.ts @@ -3,7 +3,6 @@ import type { ElectronMLWorker } from "@/base/types/ipc"; import type { EnteFile } from "@/media/file"; import { FileType } from "@/media/file-type"; import { decodeLivePhoto } from "@/media/live-photo"; -import { ensure } from "@/utils/ensure"; import { renderableImageBlob } from "../../utils/file"; import { readStream } from "../../utils/native-stream"; import DownloadManager from "../download"; @@ -44,7 +43,7 @@ export const createImageBitmapAndData = async ( // Use an OffscreenCanvas to get the bitmap's data. const offscreenCanvas = new OffscreenCanvas(width, height); - const ctx = ensure(offscreenCanvas.getContext("2d")); + const ctx = offscreenCanvas.getContext("2d")!; ctx.drawImage(imageBitmap, 0, 0, width, height); const imageData = ctx.getImageData(0, 0, width, height); @@ -93,7 +92,7 @@ const fetchRenderableUploadItemBlob = async ( const fileType = file.metadata.fileType; if (fileType == FileType.video) { const thumbnailData = await DownloadManager.getThumbnail(file); - return new Blob([ensure(thumbnailData)]); + return new Blob([thumbnailData!]); } else { const blob = await readNonVideoUploadItem(uploadItem, electron); return renderableImageBlob(file.metadata.title, blob); @@ -148,7 +147,7 @@ export const fetchRenderableEnteFileBlob = async ( const fileType = file.metadata.fileType; if (fileType == FileType.video) { const thumbnailData = await DownloadManager.getThumbnail(file); - return new Blob([ensure(thumbnailData)]); + return new Blob([thumbnailData!]); } const fileStream = await DownloadManager.getFile(file); diff --git a/web/packages/new/photos/services/user-details.ts b/web/packages/new/photos/services/user-details.ts index f3d9dfc13f..29716d1b03 100644 --- a/web/packages/new/photos/services/user-details.ts +++ b/web/packages/new/photos/services/user-details.ts @@ -1,7 +1,6 @@ import { authenticatedRequestHeaders, ensureOk } from "@/base/http"; import { getKV, setKV } from "@/base/kv"; import { apiURL, familyAppOrigin, paymentsAppOrigin } from "@/base/origins"; -import { ensure } from "@/utils/ensure"; import { nullishToEmpty, nullishToZero, @@ -336,7 +335,7 @@ export const verifyStripeSubscription = async ( }), ); await syncUserDetails(); - return ensure(userDetailsSnapshot()?.subscription); + return userDetailsSnapshot()!.subscription; }; /** From ebd550505fd8819a74ff9f577f5490d815f93741 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Mon, 18 Nov 2024 18:32:51 +0530 Subject: [PATCH 018/177] Cont --- web/packages/shared/themes/palette.tsx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/web/packages/shared/themes/palette.tsx b/web/packages/shared/themes/palette.tsx index 004d06b4cb..f7c6a7d9bb 100644 --- a/web/packages/shared/themes/palette.tsx +++ b/web/packages/shared/themes/palette.tsx @@ -1,4 +1,3 @@ -import { ensure } from "@/utils/ensure"; import type { PaletteOptions, ThemeColorsOptions } from "@mui/material"; import { THEME_COLOR } from "./constants"; @@ -21,24 +20,33 @@ export const getPalletteOptions = ( ): PaletteOptions => { return { primary: { - // TODO: Refactor this code to not require this ensure - main: ensure(colors.fill?.base), + // See: [Note: strict mode migration] + // + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + main: colors.fill.base, dark: colors.fill?.basePressed, contrastText: themeColor === "dark" ? colors.black?.base : colors.white?.base, }, secondary: { - main: ensure(colors.fill?.faint), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + main: colors.fill.faint, dark: colors.fill?.faintPressed, contrastText: colors.text?.base, }, accent: { - main: ensure(colors.accent?.A500), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + main: colors.accent.A500, dark: colors.accent?.A700, contrastText: colors.white?.base, }, critical: { - main: ensure(colors.danger?.A700), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + main: colors.danger.A700, dark: colors.danger?.A800, contrastText: colors.white?.base, }, From ebbca2b609f751840ab55458300f0791b2398c8f Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Mon, 18 Nov 2024 18:37:30 +0530 Subject: [PATCH 019/177] Fin --- .../photos/src/services/upload/upload-service.ts | 4 ++-- web/apps/photos/src/utils/photoFrame/index.ts | 8 ++++---- web/packages/base/local-user.ts | 7 +++++-- web/packages/utils/ensure.ts | 14 -------------- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/web/apps/photos/src/services/upload/upload-service.ts b/web/apps/photos/src/services/upload/upload-service.ts index c08a08ad60..6afe1e02db 100644 --- a/web/apps/photos/src/services/upload/upload-service.ts +++ b/web/apps/photos/src/services/upload/upload-service.ts @@ -38,7 +38,7 @@ import { import { detectFileTypeInfoFromChunk } from "@/new/photos/utils/detect-type"; import { readStream } from "@/new/photos/utils/native-stream"; import { mergeUint8Arrays } from "@/utils/array"; -import { ensure, ensureInteger, ensureNumber } from "@/utils/ensure"; +import { ensureInteger, ensureNumber } from "@/utils/ensure"; import { CustomError, handleUploadError } from "@ente/shared/error"; import { addToCollection } from "services/collectionService"; import { @@ -857,7 +857,7 @@ const readImageOrVideoDetails = async (uploadItem: UploadItem) => { const fileTypeInfo = await detectFileTypeInfoFromChunk(async () => { const reader = stream.getReader(); - const chunk = ensure((await reader.read()).value); + const chunk = (await reader.read())!.value; await reader.cancel(); return chunk; }, uploadItemFileName(uploadItem)); diff --git a/web/apps/photos/src/utils/photoFrame/index.ts b/web/apps/photos/src/utils/photoFrame/index.ts index 3665478a05..8d752d1bd8 100644 --- a/web/apps/photos/src/utils/photoFrame/index.ts +++ b/web/apps/photos/src/utils/photoFrame/index.ts @@ -168,7 +168,7 @@ export const handleSelectCreator = collectionID: 0, context: { mode: selected.context?.mode, - personID: ensure(activePersonID), + personID: activePersonID!, }, }; } @@ -183,7 +183,7 @@ export const handleSelectCreator = collectionID: 0, context: { mode: selected.context?.mode, - collectionID: ensure(activeCollectionID), + collectionID: activeCollectionID!, }, }; } @@ -194,8 +194,8 @@ export const handleSelectCreator = const newContext: SelectionContext | undefined = !mode ? undefined : mode == "people" - ? { mode, personID: ensure(activePersonID) } - : { mode, collectionID: ensure(activeCollectionID) }; + ? { mode, personID: activePersonID! } + : { mode, collectionID: activeCollectionID! }; const handleCounterChange = (count: number) => { if (selected[id] === checked) { diff --git a/web/packages/base/local-user.ts b/web/packages/base/local-user.ts index 5a3eeafaac..c2a446eb3c 100644 --- a/web/packages/base/local-user.ts +++ b/web/packages/base/local-user.ts @@ -1,6 +1,5 @@ // TODO: This file belongs to the accounts package -import { ensure } from "@/utils/ensure"; import { z } from "zod"; import { getKVS } from "./kv"; @@ -57,4 +56,8 @@ export const ensureLocalUser = (): LocalUser => { * The underlying data is stored in IndexedDB, and can be accessed from web * workers. */ -export const ensureAuthToken = async () => ensure(await getKVS("token")); +export const ensureAuthToken = async () => { + const token = await getKVS("token"); + if (!token) throw new Error("Not logged in"); + return token; +}; diff --git a/web/packages/utils/ensure.ts b/web/packages/utils/ensure.ts index a7508eb8ad..ec31167a68 100644 --- a/web/packages/utils/ensure.ts +++ b/web/packages/utils/ensure.ts @@ -1,17 +1,3 @@ -/** - * Throw an exception if the given value is `null` or `undefined`. - * - * This is different from TypeScript's built in null assertion operator `!` in - * that `ensure` involves a runtime check, and will throw if the given value is - * null-ish. On the other hand the TypeScript null assertion is only an - * indication to the type system and does not involve any runtime checks. - */ -export const ensure = (v: T | null | undefined): T => { - if (v === null) throw new Error("Required value was null"); - if (v === undefined) throw new Error("Required value was undefined"); - return v; -}; - /** * Throw an exception if the given value is not a string. */ From ed5c4dfc7edaca92ba566198c7b36a0e13a970f2 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Mon, 18 Nov 2024 18:58:48 +0530 Subject: [PATCH 020/177] Unlab --- .../src/components/Sidebar/Preferences.tsx | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/web/apps/photos/src/components/Sidebar/Preferences.tsx b/web/apps/photos/src/components/Sidebar/Preferences.tsx index 459ed53d77..c09b812c93 100644 --- a/web/apps/photos/src/components/Sidebar/Preferences.tsx +++ b/web/apps/photos/src/components/Sidebar/Preferences.tsx @@ -26,8 +26,7 @@ import { import { useAppContext } from "@/new/photos/types/context"; import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem"; import ChevronRight from "@mui/icons-material/ChevronRight"; -import ScienceIcon from "@mui/icons-material/Science"; -import { Box, Stack } from "@mui/material"; +import { Stack } from "@mui/material"; import DropdownInput from "components/DropdownInput"; import { t } from "i18next"; import React, { useCallback, useEffect } from "react"; @@ -65,6 +64,15 @@ export const Preferences: React.FC = ({ /> + {isMLSupported && ( + + } + onClick={showMLSettings} + label={t("ml_search")} + /> + + )} } @@ -75,21 +83,6 @@ export const Preferences: React.FC = ({ endIcon={} label={t("advanced")} /> - {isMLSupported && ( - - } - /> - - } - onClick={showMLSettings} - label={t("ml_search")} - /> - - - )} Date: Mon, 18 Nov 2024 19:02:14 +0530 Subject: [PATCH 021/177] Always show people section btn on destkop --- .../Collections/GalleryBarAndListHeader.tsx | 2 -- web/apps/photos/src/pages/gallery.tsx | 5 ---- .../new/photos/components/gallery/BarImpl.tsx | 25 ++++++------------- 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx b/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx index 28bc207407..7b1e9bce2c 100644 --- a/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx +++ b/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx @@ -78,7 +78,6 @@ type CollectionsProps = Omit< */ export const GalleryBarAndListHeader: React.FC = ({ shouldHide, - showPeopleSectionButton, mode, onChangeMode, collectionSummaries, @@ -193,7 +192,6 @@ export const GalleryBarAndListHeader: React.FC = ({ <> ; } - // `peopleState` will be undefined only when ML is disabled, otherwise it'll - // be present, with empty arrays, even if people data is still syncing. - const showPeopleSectionButton = peopleState !== undefined; - return ( = ({ - showPeopleSectionButton, mode, onChangeMode, collectionSummaries, @@ -255,9 +251,7 @@ export const GalleryBarImpl: React.FC = ({ sx={people.length ? {} : { borderBlockEndColor: "transparent" }} > - + {controls1} @@ -314,20 +308,17 @@ export const Row2 = styled(Box)` `; const ModeIndicator: React.FC< - Pick< - GalleryBarImplProps, - "showPeopleSectionButton" | "mode" | "onChangeMode" - > -> = ({ showPeopleSectionButton, mode, onChangeMode }) => { + Pick +> = ({ mode, onChangeMode }) => { // Mode switcher is not shown in the hidden albums section. if (mode == "hidden-albums") { return {t("hidden_albums")}; } - // Show the static mode indicator with only the "Albums" title if we have - // not been asked to show the people button (there are no other sections to - // switch to in such a case). - if (!showPeopleSectionButton) { + // Show the static mode indicator with only the "Albums" title if ML is not + // supported on this client (web), since there are no other sections to + // switch to in such a case. + if (!isMLSupported) { return {t("albums")}; } From d5d97d3d6e20a1798b830e88ffb47e5e17d244d1 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Mon, 18 Nov 2024 19:58:30 +0530 Subject: [PATCH 022/177] Prep --- .../new/photos/components/gallery/index.tsx | 75 +++++++++++++------ .../photos/components/sidebar/MLSettings.tsx | 2 +- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/web/packages/new/photos/components/gallery/index.tsx b/web/packages/new/photos/components/gallery/index.tsx index 2d828fd62f..3209f9da37 100644 --- a/web/packages/new/photos/components/gallery/index.tsx +++ b/web/packages/new/photos/components/gallery/index.tsx @@ -7,8 +7,8 @@ * there. */ +import { CenteredBox } from "@/base/components/mui/Container"; import type { SearchOption } from "@/new/photos/services/search/types"; -import { VerticallyCentered } from "@ente/shared/components/Container"; import { Typography } from "@mui/material"; import { t } from "i18next"; import React from "react"; @@ -46,25 +46,56 @@ export const SearchResultsHeader: React.FC = ({ export const PeopleEmptyState: React.FC = () => { const mlStatus = useMLStatusSnapshot(); - const message = - mlStatus?.phase == "done" - ? t("people_empty_too_few") - : t("syncing_wait"); - - return ( - - - - ); + switch (mlStatus?.phase) { + case "disabled": + return ; + case "done": + return ( + + {t("people_empty_too_few")} + + ); + default: + return ( + + {t("syncing_wait")} + + ); + } }; + +export const PeopleEmptyStateDisabled: React.FC = () => ( + + + +); + +export const PeopleEmptyStateMessage: React.FC = ({ + children, +}) => ( + + + +); diff --git a/web/packages/new/photos/components/sidebar/MLSettings.tsx b/web/packages/new/photos/components/sidebar/MLSettings.tsx index d67b1e028f..cc034cab2b 100644 --- a/web/packages/new/photos/components/sidebar/MLSettings.tsx +++ b/web/packages/new/photos/components/sidebar/MLSettings.tsx @@ -100,7 +100,7 @@ interface EnableMLProps { onEnable: () => void; } -const EnableML: React.FC = ({ onEnable }) => { +export const EnableML: React.FC = ({ onEnable }) => { const moreDetails = () => openURL("https://help.ente.io/photos/features/machine-learning"); From 0024ee5b77af5bf8ab2fbd1e69b70b728b47fc3f Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Mon, 18 Nov 2024 20:10:48 +0530 Subject: [PATCH 023/177] wip checkpoint --- .../new/photos/components/gallery/index.tsx | 55 +++++++++++++------ .../photos/components/sidebar/MLSettings.tsx | 21 +++++-- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/web/packages/new/photos/components/gallery/index.tsx b/web/packages/new/photos/components/gallery/index.tsx index 3209f9da37..bcf9314789 100644 --- a/web/packages/new/photos/components/gallery/index.tsx +++ b/web/packages/new/photos/components/gallery/index.tsx @@ -9,10 +9,12 @@ import { CenteredBox } from "@/base/components/mui/Container"; import type { SearchOption } from "@/new/photos/services/search/types"; -import { Typography } from "@mui/material"; +import { Paper, Stack, Typography } from "@mui/material"; import { t } from "i18next"; -import React from "react"; +import React, { useState } from "react"; +import { EnableML, FaceConsent } from "../sidebar/MLSettings"; import { useMLStatusSnapshot } from "../utils/use-snapshot"; +import { useWrapAsyncOperation } from "../utils/use-wrap-async"; import { GalleryItemsHeaderAdapter, GalleryItemsSummary } from "./ListHeader"; /** @@ -64,22 +66,39 @@ export const PeopleEmptyState: React.FC = () => { } }; -export const PeopleEmptyStateDisabled: React.FC = () => ( - - - -); +// import { FormPaper } from "@/base/components/FormPaper"; + +export const PeopleEmptyStateDisabled: React.FC = () => { + const [openFaceConsent, setOpenFaceConsent] = useState(false); + + const handleEnableML = () => setOpenFaceConsent(true); + + const handleConsent = useWrapAsyncOperation(async () => { + await enableML(); + // Close the FaceConsent drawer, come back to ourselves. + setOpenFaceConsent(false); + }); + + return ( + + + + + + setOpenFaceConsent(false)} + onRootClose={() => {}} + onConsent={handleConsent} + /> + + ); +}; export const PeopleEmptyStateMessage: React.FC = ({ children, diff --git a/web/packages/new/photos/components/sidebar/MLSettings.tsx b/web/packages/new/photos/components/sidebar/MLSettings.tsx index cc034cab2b..108eee09f5 100644 --- a/web/packages/new/photos/components/sidebar/MLSettings.tsx +++ b/web/packages/new/photos/components/sidebar/MLSettings.tsx @@ -54,7 +54,7 @@ export const MLSettings: React.FC = ({ if (!mlStatus) { component = ; } else if (mlStatus.phase == "disabled") { - component = ; + component = ; } else { component = ( @@ -98,9 +98,16 @@ const Loading: React.FC = () => { interface EnableMLProps { /** Called when the user enables ML. */ onEnable: () => void; + /** + * If true, a footnote describing the magic search feature will be shown. + */ + showMagicSearchHint?: boolean; } -export const EnableML: React.FC = ({ onEnable }) => { +export const EnableML: React.FC = ({ + onEnable, + showMagicSearchHint, +}) => { const moreDetails = () => openURL("https://help.ente.io/photos/features/machine-learning"); @@ -118,9 +125,11 @@ export const EnableML: React.FC = ({ onEnable }) => { {t("more_details")} - - {t("ml_search_footnote")} - + {showMagicSearchHint && ( + + {t("ml_search_footnote")} + + )} ); }; @@ -130,7 +139,7 @@ type FaceConsentProps = NestedSidebarDrawerVisibilityProps & { onConsent: () => void; }; -const FaceConsent: React.FC = ({ +export const FaceConsent: React.FC = ({ open, onClose, onRootClose, From 07600c60185e9426313f1d8ea6e99e8f5dd8f326 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Mon, 18 Nov 2024 20:23:57 +0530 Subject: [PATCH 024/177] Inline consent contents --- .../new/photos/components/gallery/index.tsx | 58 +++----- .../photos/components/sidebar/MLSettings.tsx | 138 +++++++++--------- 2 files changed, 97 insertions(+), 99 deletions(-) diff --git a/web/packages/new/photos/components/gallery/index.tsx b/web/packages/new/photos/components/gallery/index.tsx index bcf9314789..b487dd512e 100644 --- a/web/packages/new/photos/components/gallery/index.tsx +++ b/web/packages/new/photos/components/gallery/index.tsx @@ -12,6 +12,7 @@ import type { SearchOption } from "@/new/photos/services/search/types"; import { Paper, Stack, Typography } from "@mui/material"; import { t } from "i18next"; import React, { useState } from "react"; +import { enableML } from "../../services/ml"; import { EnableML, FaceConsent } from "../sidebar/MLSettings"; import { useMLStatusSnapshot } from "../utils/use-snapshot"; import { useWrapAsyncOperation } from "../utils/use-wrap-async"; @@ -66,40 +67,6 @@ export const PeopleEmptyState: React.FC = () => { } }; -// import { FormPaper } from "@/base/components/FormPaper"; - -export const PeopleEmptyStateDisabled: React.FC = () => { - const [openFaceConsent, setOpenFaceConsent] = useState(false); - - const handleEnableML = () => setOpenFaceConsent(true); - - const handleConsent = useWrapAsyncOperation(async () => { - await enableML(); - // Close the FaceConsent drawer, come back to ourselves. - setOpenFaceConsent(false); - }); - - return ( - - - - - - setOpenFaceConsent(false)} - onRootClose={() => {}} - onConsent={handleConsent} - /> - - ); -}; - export const PeopleEmptyStateMessage: React.FC = ({ children, }) => ( @@ -118,3 +85,26 @@ export const PeopleEmptyStateMessage: React.FC = ({ ); + +export const PeopleEmptyStateDisabled: React.FC = () => { + const [showConsent, setShowConsent] = useState(false); + + const handleConsent = useWrapAsyncOperation(async () => { + await enableML(); + }); + + return ( + + + {!showConsent ? ( + setShowConsent(true)} /> + ) : ( + setShowConsent(false)} + /> + )} + + + ); +}; diff --git a/web/packages/new/photos/components/sidebar/MLSettings.tsx b/web/packages/new/photos/components/sidebar/MLSettings.tsx index 108eee09f5..315aa21774 100644 --- a/web/packages/new/photos/components/sidebar/MLSettings.tsx +++ b/web/packages/new/photos/components/sidebar/MLSettings.tsx @@ -77,7 +77,7 @@ export const MLSettings: React.FC = ({ - setOpenFaceConsent(false)} onRootClose={handleRootClose} @@ -134,28 +134,54 @@ export const EnableML: React.FC = ({ ); }; -type FaceConsentProps = NestedSidebarDrawerVisibilityProps & { - /** Called when the user provides their consent. */ - onConsent: () => void; -}; +type FaceConsentDrawerProps = NestedSidebarDrawerVisibilityProps & + Pick; -export const FaceConsent: React.FC = ({ +const FaceConsentDrawer: React.FC = ({ open, onClose, onRootClose, onConsent, }) => { - const [acceptTerms, setAcceptTerms] = useState(false); - - useEffect(() => { - setAcceptTerms(false); - }, [open]); - const handleRootClose = () => { onClose(); onRootClose(); }; + return ( + + + + + + + ); +}; + +interface FaceConsentProps { + /** Called when the user provides their consent. */ + onConsent: () => void; + /** Called when the user cancels out. */ + onCancel: () => void; +} + +export const FaceConsent: React.FC = ({ + onConsent, + onCancel, +}) => { + const [acceptTerms, setAcceptTerms] = useState(false); + + useEffect(() => { + setAcceptTerms(false); + }, []); + const privacyPolicyLink = ( = ({ ); return ( - - - + + - - - + + setAcceptTerms(e.target.checked)} /> - - - - setAcceptTerms(e.target.checked) - } - /> - } - label={t("ml_consent_confirmation")} - /> - - - - - - + } + label={t("ml_consent_confirmation")} + /> + + + + - + ); }; From a2b9126c88c5241c86214f1b44a93061be8c8731 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 06:34:24 +0530 Subject: [PATCH 025/177] Rename --- web/packages/base/components/mui/Container.tsx | 2 +- .../photos/components/gallery/PeopleHeader.tsx | 17 ++++++++++------- .../new/photos/components/gallery/index.tsx | 6 +++--- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/web/packages/base/components/mui/Container.tsx b/web/packages/base/components/mui/Container.tsx index abaa0e2dce..78e8657f1b 100644 --- a/web/packages/base/components/mui/Container.tsx +++ b/web/packages/base/components/mui/Container.tsx @@ -18,7 +18,7 @@ export const SpaceBetweenFlex = styled("div")` * A flex child that fills the entire flex direction, and shows its children * after centering them both vertically and horizontally. */ -export const CenteredBox = styled("div")` +export const CenteredFill = styled("div")` flex: 1; display: flex; justify-content: center; diff --git a/web/packages/new/photos/components/gallery/PeopleHeader.tsx b/web/packages/new/photos/components/gallery/PeopleHeader.tsx index 8ef463d306..63ae4f2ca9 100644 --- a/web/packages/new/photos/components/gallery/PeopleHeader.tsx +++ b/web/packages/new/photos/components/gallery/PeopleHeader.tsx @@ -1,6 +1,9 @@ import { ActivityErrorIndicator } from "@/base/components/ErrorIndicator"; import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator"; -import { CenteredBox, SpaceBetweenFlex } from "@/base/components/mui/Container"; +import { + CenteredFill, + SpaceBetweenFlex, +} from "@/base/components/mui/Container"; import { FocusVisibleButton } from "@/base/components/mui/FocusVisibleButton"; import { LoadingButton } from "@/base/components/mui/LoadingButton"; import { @@ -691,15 +694,15 @@ const SuggestionsDialog: React.FC = ({ sx={{ display: "flex", "&&&": { pt: 0 } }} > {state.activity == "fetching" ? ( - + {t("people_suggestions_finding")} - + ) : state.fetchFailed ? ( - + - + ) : state.showChoices ? ( = ({ onUpdateItem={handleUpdateItem} /> ) : state.suggestions.length == 0 ? ( - + t{"people_suggestions_empty"} - + ) : ( { export const PeopleEmptyStateMessage: React.FC = ({ children, }) => ( - + = ({ > {children} - + ); export const PeopleEmptyStateDisabled: React.FC = () => { From 68c230dae931d408ba478d358243dcb819bcb1ed Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 08:10:47 +0530 Subject: [PATCH 026/177] Fix scroll (partially) We want the consent message to be scrollable. The current changes are a partial solution, the navbar still shows through at times. --- web/apps/photos/src/components/FullScreenDropZone.tsx | 1 + web/apps/photos/src/styles/global.css | 1 + web/packages/new/photos/components/gallery/index.tsx | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/web/apps/photos/src/components/FullScreenDropZone.tsx b/web/apps/photos/src/components/FullScreenDropZone.tsx index 9de95d8f5c..d02b0da2bc 100644 --- a/web/apps/photos/src/components/FullScreenDropZone.tsx +++ b/web/apps/photos/src/components/FullScreenDropZone.tsx @@ -14,6 +14,7 @@ const DropDiv = styled("div")` flex: 1; display: flex; flex-direction: column; + height: 100%; `; const Overlay = styled("div")` border-width: 8px; diff --git a/web/apps/photos/src/styles/global.css b/web/apps/photos/src/styles/global.css index e60b1944f4..7a06257c3c 100644 --- a/web/apps/photos/src/styles/global.css +++ b/web/apps/photos/src/styles/global.css @@ -49,6 +49,7 @@ body { flex: 1; display: flex; flex-direction: column; + height: 100%; } .pswp__button--custom { diff --git a/web/packages/new/photos/components/gallery/index.tsx b/web/packages/new/photos/components/gallery/index.tsx index 37b31a3b54..db1afadf39 100644 --- a/web/packages/new/photos/components/gallery/index.tsx +++ b/web/packages/new/photos/components/gallery/index.tsx @@ -94,7 +94,7 @@ export const PeopleEmptyStateDisabled: React.FC = () => { }); return ( - + {!showConsent ? ( setShowConsent(true)} /> From 641a73c1011e57e22cb904f48d6bd856bde08da3 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 19 Nov 2024 10:24:58 +0530 Subject: [PATCH 027/177] [server] validate bonus reversal --- server/pkg/repo/storagebonus/bf_addon.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/pkg/repo/storagebonus/bf_addon.go b/server/pkg/repo/storagebonus/bf_addon.go index 5e012f01fc..00326b4777 100644 --- a/server/pkg/repo/storagebonus/bf_addon.go +++ b/server/pkg/repo/storagebonus/bf_addon.go @@ -3,6 +3,8 @@ package storagebonus import ( "context" "fmt" + + "github.com/ente-io/museum/ente" "github.com/ente-io/museum/ente/storagebonus" ) @@ -27,7 +29,15 @@ func (r *Repository) RemoveAddOnBonus(ctx context.Context, bonusType storagebonu if err != nil { return 0, err } - return res.RowsAffected() + // verify if the bonus was removed + rowsAffected, err := res.RowsAffected() + if err != nil { + return 0, err + } + if rowsAffected == int64(0) { + return 0, ente.NewBadRequestWithMessage(fmt.Sprintf("bonus not found for user %d with bonusID %s", userID, bonusID)) + } + return rowsAffected, nil } func (r *Repository) UpdateAddOnBonus(ctx context.Context, bonusType storagebonus.BonusType, userID int64, validTill int64, storage int64) error { From 05f5c7f439bb2038aee96ac22b06e54d8c28e2bc Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 19 Nov 2024 10:28:02 +0530 Subject: [PATCH 028/177] [server] Wrap inside db transaction --- server/pkg/repo/storagebonus/bf_addon.go | 26 ++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/server/pkg/repo/storagebonus/bf_addon.go b/server/pkg/repo/storagebonus/bf_addon.go index 00326b4777..2c5a759c28 100644 --- a/server/pkg/repo/storagebonus/bf_addon.go +++ b/server/pkg/repo/storagebonus/bf_addon.go @@ -24,20 +24,34 @@ func (r *Repository) RemoveAddOnBonus(ctx context.Context, bonusType storagebonu if err := _validate(bonusType); err != nil { return 0, err } + bonusID := fmt.Sprintf("%s-%d", bonusType, userID) - res, err := r.DB.ExecContext(ctx, "DELETE FROM storage_bonus WHERE bonus_id = $1", bonusID) + + tx, err := r.DB.BeginTx(ctx, nil) if err != nil { - return 0, err + return 0, fmt.Errorf("failed to begin transaction: %w", err) + } + defer tx.Rollback() // Will be no-op if transaction is committed + res, err := tx.ExecContext(ctx, "DELETE FROM storage_bonus WHERE bonus_id = $1", bonusID) + if err != nil { + return 0, fmt.Errorf("failed to execute delete query: %w", err) } - // verify if the bonus was removed rowsAffected, err := res.RowsAffected() if err != nil { - return 0, err + return 0, fmt.Errorf("failed to get affected rows: %w", err) } - if rowsAffected == int64(0) { + switch { + case rowsAffected == 0: return 0, ente.NewBadRequestWithMessage(fmt.Sprintf("bonus not found for user %d with bonusID %s", userID, bonusID)) + case rowsAffected > 1: + return 0, fmt.Errorf("more than one (%d) bonus found for user %d with bonusID %s", rowsAffected, userID, bonusID) } - return rowsAffected, nil + + if err := tx.Commit(); err != nil { + return 0, fmt.Errorf("failed to commit transaction: %w", err) + } + + return 1, nil // We know exactly one row was affected at this point } func (r *Repository) UpdateAddOnBonus(ctx context.Context, bonusType storagebonus.BonusType, userID int64, validTill int64, storage int64) error { From 14adea59bfcc5761b536bfd1faf85280c9ad348e Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 19 Nov 2024 10:57:40 +0530 Subject: [PATCH 029/177] Use super --- mobile/lib/ui/viewer/people/person_row_item.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/lib/ui/viewer/people/person_row_item.dart b/mobile/lib/ui/viewer/people/person_row_item.dart index 95e2453eaa..fa3dc38e23 100644 --- a/mobile/lib/ui/viewer/people/person_row_item.dart +++ b/mobile/lib/ui/viewer/people/person_row_item.dart @@ -9,11 +9,11 @@ class PersonRowItem extends StatelessWidget { final VoidCallback onTap; const PersonRowItem({ - Key? key, + super.key, required this.person, required this.personFile, required this.onTap, - }) : super(key: key); + }); @override Widget build(BuildContext context) { From 18cf3f2e60bdfaeace90c4910ca556f68b5876ab Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 19 Nov 2024 11:53:06 +0530 Subject: [PATCH 030/177] [mob] Add new screen for save person --- mobile/lib/generated/intl/messages_en.dart | 7 + mobile/lib/generated/intl/messages_pl.dart | 4 +- mobile/lib/generated/l10n.dart | 50 ++++ mobile/lib/l10n/intl_en.arb | 5 + mobile/lib/l10n/intl_pl.arb | 4 +- mobile/lib/ui/common/date_input.dart | 171 +++++++++++ .../people/add_person_action_sheet.dart | 81 ++---- mobile/lib/ui/viewer/people/cluster_page.dart | 1 + .../lib/ui/viewer/people/person_row_item.dart | 44 +++ mobile/lib/ui/viewer/people/save_person.dart | 265 ++++++++++++++++-- 10 files changed, 550 insertions(+), 82 deletions(-) create mode 100644 mobile/lib/ui/common/date_input.dart diff --git a/mobile/lib/generated/intl/messages_en.dart b/mobile/lib/generated/intl/messages_en.dart index 940af4877f..d8417292d1 100644 --- a/mobile/lib/generated/intl/messages_en.dart +++ b/mobile/lib/generated/intl/messages_en.dart @@ -408,6 +408,7 @@ class MessageLookup extends MessageLookupByLibrary { "backupStatusDescription": MessageLookupByLibrary.simpleMessage( "Items that have been backed up will show up here"), "backupVideos": MessageLookupByLibrary.simpleMessage("Backup videos"), + "birthday": MessageLookupByLibrary.simpleMessage("Birthday"), "blackFridaySale": MessageLookupByLibrary.simpleMessage("Black Friday Sale"), "blog": MessageLookupByLibrary.simpleMessage("Blog"), @@ -739,9 +740,12 @@ class MessageLookup extends MessageLookupByLibrary { "enterCode": MessageLookupByLibrary.simpleMessage("Enter code"), "enterCodeDescription": MessageLookupByLibrary.simpleMessage( "Enter the code provided by your friend to claim free storage for both of you"), + "enterDateOfBirth": + MessageLookupByLibrary.simpleMessage("Birthday (optional)"), "enterEmail": MessageLookupByLibrary.simpleMessage("Enter email"), "enterFileName": MessageLookupByLibrary.simpleMessage("Enter file name"), + "enterName": MessageLookupByLibrary.simpleMessage("Enter name"), "enterNewPasswordToEncrypt": MessageLookupByLibrary.simpleMessage( "Enter a new password we can use to encrypt your data"), "enterPassword": MessageLookupByLibrary.simpleMessage("Enter password"), @@ -1131,6 +1135,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("OpenStreetMap contributors"), "optionalAsShortAsYouLike": MessageLookupByLibrary.simpleMessage( "Optional, as short as you like..."), + "orMergeWithExistingPerson": MessageLookupByLibrary.simpleMessage( + "Or merge with existing person"), "orPickAnExistingOne": MessageLookupByLibrary.simpleMessage("Or pick an existing one"), "pair": MessageLookupByLibrary.simpleMessage("Pair"), @@ -1349,6 +1355,7 @@ class MessageLookup extends MessageLookupByLibrary { "saveCollage": MessageLookupByLibrary.simpleMessage("Save collage"), "saveCopy": MessageLookupByLibrary.simpleMessage("Save copy"), "saveKey": MessageLookupByLibrary.simpleMessage("Save key"), + "savePerson": MessageLookupByLibrary.simpleMessage("Save person"), "saveYourRecoveryKeyIfYouHaventAlready": MessageLookupByLibrary.simpleMessage( "Save your recovery key if you haven\'t already"), diff --git a/mobile/lib/generated/intl/messages_pl.dart b/mobile/lib/generated/intl/messages_pl.dart index d446bb852b..2fcbcfe497 100644 --- a/mobile/lib/generated/intl/messages_pl.dart +++ b/mobile/lib/generated/intl/messages_pl.dart @@ -21,7 +21,7 @@ class MessageLookup extends MessageLookupByLibrary { String get localeName => 'pl'; static String m6(count) => - "${Intl.plural(count, one: 'Dodaj współuczestnika', few: 'Dodaj współuczestników', many: 'Dodaj współuczestników', other: 'Dodaj współuczestników')}"; + "${Intl.plural(count, one: 'Dodaj współuczestnika', few: 'Dodaj współuczestników', other: 'Dodaj współuczestników')}"; static String m7(count) => "${Intl.plural(count, one: 'Dodaj element', few: 'Dodaj elementy', other: 'Dodaj elementów')}"; @@ -30,7 +30,7 @@ class MessageLookup extends MessageLookupByLibrary { "Twój dodatek ${storageAmount} jest ważny do ${endDate}"; static String m9(count) => - "${Intl.plural(count, one: 'Dodaj widza', few: 'Dodaj widzów', many: 'Dodaj widzów', other: 'Dodaj widzów')}"; + "${Intl.plural(count, one: 'Dodaj widza', few: 'Dodaj widzów', other: 'Dodaj widzów')}"; static String m10(emailOrName) => "Dodane przez ${emailOrName}"; diff --git a/mobile/lib/generated/l10n.dart b/mobile/lib/generated/l10n.dart index a33c0511ea..c8cf88c93a 100644 --- a/mobile/lib/generated/l10n.dart +++ b/mobile/lib/generated/l10n.dart @@ -8995,6 +8995,56 @@ class S { ); } + /// `Enter name` + String get enterName { + return Intl.message( + 'Enter name', + name: 'enterName', + desc: '', + args: [], + ); + } + + /// `Save person` + String get savePerson { + return Intl.message( + 'Save person', + name: 'savePerson', + desc: '', + args: [], + ); + } + + /// `Or merge with existing person` + String get orMergeWithExistingPerson { + return Intl.message( + 'Or merge with existing person', + name: 'orMergeWithExistingPerson', + desc: '', + args: [], + ); + } + + /// `Birthday (optional)` + String get enterDateOfBirth { + return Intl.message( + 'Birthday (optional)', + name: 'enterDateOfBirth', + desc: '', + args: [], + ); + } + + /// `Birthday` + String get birthday { + return Intl.message( + 'Birthday', + name: 'birthday', + desc: '', + args: [], + ); + } + /// `Remove person label` String get removePersonLabel { return Intl.message( diff --git a/mobile/lib/l10n/intl_en.arb b/mobile/lib/l10n/intl_en.arb index 88629cf49f..e7d445aaef 100644 --- a/mobile/lib/l10n/intl_en.arb +++ b/mobile/lib/l10n/intl_en.arb @@ -1260,6 +1260,11 @@ "createCollaborativeLink": "Create collaborative link", "search": "Search", "enterPersonName": "Enter person name", + "enterName": "Enter name", + "savePerson": "Save person", + "orMergeWithExistingPerson" : "Or merge with existing person", + "enterDateOfBirth": "Birthday (optional)", + "birthday": "Birthday", "removePersonLabel": "Remove person label", "autoPairDesc": "Auto pair works only with devices that support Chromecast.", "manualPairDesc": "Pair with PIN works with any screen you wish to view your album on.", diff --git a/mobile/lib/l10n/intl_pl.arb b/mobile/lib/l10n/intl_pl.arb index 182563ad0c..1dfd9ef9df 100644 --- a/mobile/lib/l10n/intl_pl.arb +++ b/mobile/lib/l10n/intl_pl.arb @@ -1247,8 +1247,8 @@ "descriptions": "Opisy", "addAName": "Dodaj nazwę", "findPeopleByName": "Szybko szukaj osób po imieniu", - "addViewers": "{count, plural, one {Dodaj widza} few {Dodaj widzów} many {Dodaj widzów} other {Dodaj widzów}}", - "addCollaborators": "{count, plural, one {Dodaj współuczestnika} few {Dodaj współuczestników} many {Dodaj współuczestników} other {Dodaj współuczestników}}", + "addViewers": "{count, plural, one {Dodaj widza} few {Dodaj widzów} other {Dodaj widzów}}", + "addCollaborators": "{count, plural, one {Dodaj współuczestnika} few {Dodaj współuczestników} other {Dodaj współuczestników}}", "longPressAnEmailToVerifyEndToEndEncryption": "Naciśnij i przytrzymaj e-mail, aby zweryfikować szyfrowanie end-to-end.", "developerSettingsWarning": "Czy na pewno chcesz zmodyfikować ustawienia programisty?", "developerSettings": "Ustawienia dla programistów", diff --git a/mobile/lib/ui/common/date_input.dart b/mobile/lib/ui/common/date_input.dart new file mode 100644 index 0000000000..a1b7e32806 --- /dev/null +++ b/mobile/lib/ui/common/date_input.dart @@ -0,0 +1,171 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import "package:photos/theme/ente_theme.dart"; + +class DatePickerField extends StatefulWidget { + final String? initialValue; + final String? hintText; + final void Function(DateTime?)? onChanged; + final DateTime? firstDate; + final DateTime? lastDate; + final bool isRequired; // New parameter for optional/required state + + const DatePickerField({ + super.key, + this.initialValue, + this.hintText, + this.onChanged, + this.firstDate, + this.lastDate, + this.isRequired = true, // Default to required for backward compatibility + }); + + @override + State createState() => _DatePickerFieldState(); +} + +class _DatePickerFieldState extends State { + final TextEditingController _controller = TextEditingController(); + DateTime? _selectedDate; + bool _hasError = false; + + @override + void initState() { + super.initState(); + if (widget.initialValue != null) { + _controller.text = widget.initialValue!; + _tryParseDate(widget.initialValue!); + } + } + + void _tryParseDate(String value) { + // If the field is empty and not required, reset error state and clear date + if (value.isEmpty && !widget.isRequired) { + setState(() { + _selectedDate = null; + _hasError = false; + }); + widget.onChanged?.call(null); + return; + } + + // Skip validation for empty optional fields + if (value.isEmpty) { + return; + } + + try { + // Try parsing different date formats + DateTime? parsed; + final List formats = ['MM/dd/yyyy', 'MM-dd-yyyy', 'yyyy-MM-dd']; + + for (String format in formats) { + try { + parsed = DateFormat(format).parseStrict(value); + break; + } catch (_) { + continue; + } + } + + if (parsed != null) { + // Validate date range if specified + bool isValid = true; + if (widget.firstDate != null && parsed.isBefore(widget.firstDate!)) { + isValid = false; + } + if (widget.lastDate != null && parsed.isAfter(widget.lastDate!)) { + isValid = false; + } + + setState(() { + _selectedDate = isValid ? parsed : null; + _hasError = !isValid; + }); + + if (isValid) { + widget.onChanged?.call(parsed); + } + } else { + setState(() { + _selectedDate = null; + _hasError = true; + }); + } + } catch (e) { + setState(() { + _selectedDate = null; + _hasError = true; + }); + } + } + + Future _showDatePicker() async { + final DateTime? picked = await showDatePicker( + context: context, + initialDate: _selectedDate ?? DateTime.now(), + firstDate: widget.firstDate ?? DateTime(1900), + lastDate: widget.lastDate ?? DateTime(2100), + ); + + if (picked != null) { + setState(() { + _selectedDate = picked; + _hasError = false; + _controller.text = DateFormat('MM/dd/yyyy').format(picked); + }); + widget.onChanged?.call(picked); + } + } + + @override + Widget build(BuildContext context) { + return TextFormField( + controller: _controller, + onChanged: (value) => _tryParseDate(value), + decoration: InputDecoration( + focusedBorder: OutlineInputBorder( + borderRadius: const BorderRadius.all(Radius.circular(8.0)), + borderSide: BorderSide( + color: getEnteColorScheme(context).strokeMuted, + ), + ), + fillColor: getEnteColorScheme(context).fillFaint, + filled: true, + hintText: widget.hintText ?? + "Enter date (DD/MM/YYYY)${widget.isRequired ? '' : ' (optional)'}", + hintStyle: getEnteTextTheme(context).bodyFaint, + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 14, + ), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(8), + ), + error: _hasError + ? Align( + alignment: Alignment.centerLeft, + child: Text( + 'Use format (MM-dd-YYYY or MM/dd/YYYY)', + style: getEnteTextTheme(context).miniMuted, + ), + ) + : null, + suffixIcon: IconButton( + icon: const Icon(Icons.calendar_today), + onPressed: _showDatePicker, + color: _hasError + ? getEnteColorScheme(context).warning500 + : getEnteColorScheme(context).strokeMuted, + ), + ), + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/mobile/lib/ui/viewer/people/add_person_action_sheet.dart b/mobile/lib/ui/viewer/people/add_person_action_sheet.dart index 9aa05b16e4..bfdc61cb9f 100644 --- a/mobile/lib/ui/viewer/people/add_person_action_sheet.dart +++ b/mobile/lib/ui/viewer/people/add_person_action_sheet.dart @@ -23,9 +23,10 @@ import 'package:photos/ui/components/buttons/button_widget.dart'; import 'package:photos/ui/components/models/button_type.dart'; import "package:photos/ui/components/text_input_widget.dart"; import 'package:photos/ui/components/title_bar_title_widget.dart'; -import "package:photos/ui/viewer/people/new_person_item_widget.dart"; import "package:photos/ui/viewer/people/person_row_item.dart"; +import "package:photos/ui/viewer/people/save_person.dart"; import "package:photos/utils/dialog_util.dart"; +import "package:photos/utils/navigation_util.dart"; import "package:photos/utils/toast_util.dart"; enum PersonActionType { @@ -48,29 +49,37 @@ String _actionName( Future showAssignPersonAction( BuildContext context, { required String clusterID, + EnteFile? file, PersonActionType actionType = PersonActionType.assignPerson, bool showOptionToAddNewPerson = true, -}) { - return showBarModalBottomSheet( - context: context, - builder: (context) { - return PersonActionSheet( - actionType: actionType, - showOptionToCreateNewPerson: showOptionToAddNewPerson, - cluserID: clusterID, - ); - }, - shape: const RoundedRectangleBorder( - side: BorderSide(width: 0), - borderRadius: BorderRadius.vertical( - top: Radius.circular(5), - ), +}) async { + return routeToPage( + context, + SavePerson( + clusterID, + file: file, ), - topControl: const SizedBox.shrink(), - backgroundColor: getEnteColorScheme(context).backgroundElevated, - barrierColor: backdropFaintDark, - enableDrag: false, ); + // return showBarModalBottomSheet( + // context: context, + // builder: (context) { + // return PersonActionSheet( + // actionType: actionType, + // showOptionToCreateNewPerson: showOptionToAddNewPerson, + // cluserID: clusterID, + // ); + // }, + // shape: const RoundedRectangleBorder( + // side: BorderSide(width: 0), + // borderRadius: BorderRadius.vertical( + // top: Radius.circular(5), + // ), + // ), + // topControl: const SizedBox.shrink(), + // backgroundColor: getEnteColorScheme(context).backgroundElevated, + // barrierColor: backdropFaintDark, + // enableDrag: false, + // ); } class PersonActionSheet extends StatefulWidget { @@ -176,7 +185,7 @@ class _PersonActionSheetState extends State { Flexible _getPersonItems() { return Flexible( child: Padding( - padding: const EdgeInsets.fromLTRB(16, 24, 4, 0), + padding: const EdgeInsets.symmetric(vertical: 16), child: FutureBuilder>( future: _getPersonsWithRecentFile(), builder: (context, snapshot) { @@ -208,8 +217,6 @@ class _PersonActionSheetState extends State { searchResults.sort( (a, b) => a.$1.data.name.compareTo(b.$1.data.name), ); - final shouldShowAddPerson = widget.showOptionToCreateNewPerson && - (_searchQuery.isEmpty || searchResults.isEmpty); return Scrollbar( thumbVisibility: true, @@ -219,34 +226,8 @@ class _PersonActionSheetState extends State { child: ListView.separated( itemCount: searchResults.length + 1, itemBuilder: (context, index) { - if (index == 0 && shouldShowAddPerson) { - return GestureDetector( - behavior: HitTestBehavior.opaque, - child: const NewPersonItemWidget(), - onTap: () async => { - addNewPerson( - context, - initValue: _searchQuery.trim(), - clusterID: widget.cluserID, - ), - }, - ); - } - if (index == 0 && !shouldShowAddPerson) { - return Padding( - padding: const EdgeInsets.fromLTRB(0, 8, 0, 16), - child: Center( - child: Text( - S.of(context).mergeWithExisting, - style: getEnteTextTheme(context).body.copyWith( - color: - getEnteColorScheme(context).textMuted, - ), - ), - ), - ); - } final person = searchResults[index - 1]; + return PersonRowItem( person: person.$1, personFile: person.$2, diff --git a/mobile/lib/ui/viewer/people/cluster_page.dart b/mobile/lib/ui/viewer/people/cluster_page.dart index 38f629d6e7..1565482b33 100644 --- a/mobile/lib/ui/viewer/people/cluster_page.dart +++ b/mobile/lib/ui/viewer/people/cluster_page.dart @@ -148,6 +148,7 @@ class _ClusterPageState extends State { final result = await showAssignPersonAction( context, clusterID: widget.clusterID, + file: files.isEmpty ? null : files.first, ); if (result != null) { Navigator.pop(context); diff --git a/mobile/lib/ui/viewer/people/person_row_item.dart b/mobile/lib/ui/viewer/people/person_row_item.dart index fa3dc38e23..9718364ca0 100644 --- a/mobile/lib/ui/viewer/people/person_row_item.dart +++ b/mobile/lib/ui/viewer/people/person_row_item.dart @@ -36,3 +36,47 @@ class PersonRowItem extends StatelessWidget { ); } } + +class PersonGridItem extends StatelessWidget { + final PersonEntity person; + final EnteFile personFile; + final VoidCallback onTap; + + const PersonGridItem({ + super.key, + required this.person, + required this.personFile, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 112, + height: 112, + child: ClipRRect( + borderRadius: const BorderRadius.all( + Radius.elliptical(16, 12), + ), + child: PersonFaceWidget(personFile, personId: person.remoteID), + ), + ), + const SizedBox(height: 4), + Text( + person.data.name, + style: Theme.of(context).textTheme.bodyMedium, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ); + } +} diff --git a/mobile/lib/ui/viewer/people/save_person.dart b/mobile/lib/ui/viewer/people/save_person.dart index 907f4a2850..1b5db110d6 100644 --- a/mobile/lib/ui/viewer/people/save_person.dart +++ b/mobile/lib/ui/viewer/people/save_person.dart @@ -1,13 +1,40 @@ +import "dart:developer"; + +import "package:flutter/foundation.dart"; import "package:flutter/material.dart"; +import "package:logging/logging.dart"; +import "package:photos/core/event_bus.dart"; +import "package:photos/db/ml/db.dart"; +import "package:photos/events/people_changed_event.dart"; import "package:photos/generated/l10n.dart"; +import "package:photos/l10n/l10n.dart"; +import "package:photos/models/file/file.dart"; +import "package:photos/models/ml/face/person.dart"; +import "package:photos/services/machine_learning/face_ml/feedback/cluster_feedback.dart"; +import "package:photos/services/machine_learning/face_ml/person/person_service.dart"; +import "package:photos/services/search_service.dart"; import "package:photos/theme/ente_theme.dart"; -import "package:photos/ui/common/email_input.dart"; +import "package:photos/ui/common/date_input.dart"; +import "package:photos/ui/common/loading_widget.dart"; +import "package:photos/ui/components/buttons/button_widget.dart"; +import "package:photos/ui/components/models/button_type.dart"; +import "package:photos/ui/viewer/file/no_thumbnail_widget.dart"; +import "package:photos/ui/viewer/people/person_row_item.dart"; +import "package:photos/ui/viewer/search/result/person_face_widget.dart"; +import "package:photos/utils/dialog_util.dart"; +import "package:photos/utils/toast_util.dart"; class SavePerson extends StatefulWidget { final String clusterID; + final EnteFile? file; final bool isEditing; - const SavePerson(this.clusterID, {super.key, this.isEditing = false}); + const SavePerson( + this.clusterID, { + super.key, + this.file, + this.isEditing = false, + }); @override State createState() => _SavePersonState(); @@ -15,28 +42,56 @@ class SavePerson extends StatefulWidget { class _SavePersonState extends State { bool isKeypadOpen = false; + String _inputName = ""; + bool userAlreadyAssigned = false; + late final Logger _logger = Logger("_SavePersonState"); @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: isKeypadOpen, appBar: AppBar( - title: Text( - widget.isEditing - ? S.of(context).addViewer - : S.of(context).addCollaborator, + title: Align( + alignment: Alignment.centerLeft, + child: Text( + context.l10n.savePerson, + ), ), ), - body: Padding( - padding: EdgeInsets.symmetric(horizontal: 8.0), + body: SingleChildScrollView( + padding: const EdgeInsets.only(bottom: 16.0, left: 16.0, right: 16.0), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 12), + const SizedBox(height: 48), + SizedBox( + height: 110, + width: 110, + child: widget.file != null + ? ClipRRect( + borderRadius: const BorderRadius.all( + Radius.elliptical(16, 12), + ), + child: PersonFaceWidget( + widget.file!, + clusterID: widget.clusterID, + ), + ) + : const ClipRRect( + borderRadius: BorderRadius.all( + Radius.elliptical(16, 12), + ), + child: NoThumbnailWidget( + addBorder: false, + ), + ), + ), + const SizedBox(height: 36), TextFormField( - // controller: controller, - // focusNode: focusNode, - // onChanged: onChanged, + onChanged: (value) { + setState(() { + _inputName = value; + }); + }, decoration: InputDecoration( focusedBorder: OutlineInputBorder( borderRadius: const BorderRadius.all(Radius.circular(8.0)), @@ -46,7 +101,7 @@ class _SavePersonState extends State { ), fillColor: getEnteColorScheme(context).fillFaint, filled: true, - hintText: "Enter name", + hintText: context.l10n.enterName, hintStyle: getEnteTextTheme(context).bodyFaint, contentPadding: const EdgeInsets.symmetric( horizontal: 16, @@ -58,25 +113,179 @@ class _SavePersonState extends State { ), ), ), - const SizedBox(height: 8), - EmailInputField( - suggestions: [ - "a@example.com", - "b@example.com", - "c@example.com", - "dc@example.com", - "ec@example.com", - "fc@example.com", - ], + const SizedBox(height: 16), + DatePickerField( + hintText: context.l10n.enterDateOfBirth, + firstDate: DateTime(1900), + lastDate: DateTime.now(), + isRequired: false, ), - const SizedBox(height: 8), - Text( - "aasdas", - style: getEnteTextTheme(context).body, + const SizedBox(height: 32), + ButtonWidget( + buttonType: ButtonType.primary, + labelText: context.l10n.save, + isDisabled: _inputName.isEmpty, + onTap: () async { + await addNewPerson( + context, + text: _inputName, + clusterID: widget.clusterID, + ); + }, ), + const SizedBox(height: 32), + _getPersonItems(), ], ), ), ); } + + Widget _getPersonItems() { + return Padding( + padding: const EdgeInsets.fromLTRB(0, 12, 4, 0), + child: FutureBuilder>( + future: _getPersonsWithRecentFile(), + builder: (context, snapshot) { + if (snapshot.hasError) { + log("Error: ${snapshot.error} ${snapshot.stackTrace}}"); + if (kDebugMode) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text('${snapshot.error}'), + Text('${snapshot.stackTrace}'), + ], + ); + } else { + return const SizedBox.shrink(); + } + } else if (snapshot.hasData) { + final persons = snapshot.data!; + final searchResults = _inputName.isNotEmpty + ? persons + .where( + (element) => element.$1.data.name + .toLowerCase() + .contains(_inputName.toLowerCase()), + ) + .toList() + : persons; + searchResults.sort( + (a, b) => a.$1.data.name.compareTo(b.$1.data.name), + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // left align + Padding( + padding: const EdgeInsets.only(top: 12, bottom: 12), + child: Text( + context.l10n.orMergeWithExistingPerson, + style: getEnteTextTheme(context).largeBold, + ), + ), + + SizedBox( + height: 160, // Adjust this height based on your needs + child: ScrollConfiguration( + behavior: ScrollConfiguration.of(context).copyWith( + scrollbars: true, + ), + child: ListView.separated( + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.only(right: 8), + itemCount: searchResults.length, + itemBuilder: (context, index) { + final person = searchResults[index]; + return PersonGridItem( + person: person.$1, + personFile: person.$2, + onTap: () async { + if (userAlreadyAssigned) { + return; + } + userAlreadyAssigned = true; + await MLDataDB.instance.assignClusterToPerson( + personID: person.$1.remoteID, + clusterID: widget.clusterID, + ); + Bus.instance.fire(PeopleChangedEvent()); + + Navigator.pop(context, person); + }, + ); + }, + separatorBuilder: (context, index) { + return const SizedBox(width: 6); + }, + ), + ), + ), + ]); + } else { + return const EnteLoadingWidget(); + } + }, + ), + ); + } + + Future addNewPerson( + BuildContext context, { + String text = '', + required String clusterID, + }) async { + try { + if (userAlreadyAssigned) { + return; + } + if (text.trim() == "") { + return; + } + userAlreadyAssigned = true; + final personEntity = + await PersonService.instance.addPerson(text, clusterID); + final bool extraPhotosFound = + await ClusterFeedbackService.instance.checkAndDoAutomaticMerges( + personEntity, + personClusterID: clusterID, + ); + if (extraPhotosFound) { + showShortToast(context, S.of(context).extraPhotosFound); + } + Bus.instance.fire(PeopleChangedEvent()); + Navigator.pop(context, personEntity); + } catch (e) { + _logger.severe("Error adding new person", e); + userAlreadyAssigned = false; + await showGenericErrorDialog(context: context, error: e); + } + } + + Future> _getPersonsWithRecentFile({ + bool excludeHidden = true, + }) async { + final persons = await PersonService.instance.getPersons(); + if (excludeHidden) { + persons.removeWhere((person) => person.data.isIgnored); + } + final List<(PersonEntity, EnteFile)> personAndFileID = []; + for (final person in persons) { + final clustersToFiles = + await SearchService.instance.getClusterFilesForPersonID( + person.remoteID, + ); + final files = clustersToFiles.values.expand((e) => e).toList(); + if (files.isEmpty) { + debugPrint( + "Person ${kDebugMode ? person.data.name : person.remoteID} has no files", + ); + continue; + } + personAndFileID.add((person, files.first)); + } + return personAndFileID; + } } From 3a3f9fbd942bea7b991381a3b1893d1c03f69a4c Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 12:20:21 +0530 Subject: [PATCH 031/177] Swap to newer --- web/packages/shared/.eslintrc.js | 35 +++++++++++++++++++++++--------- web/packages/shared/package.json | 2 +- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/web/packages/shared/.eslintrc.js b/web/packages/shared/.eslintrc.js index 1fc7db29af..227fc276cb 100644 --- a/web/packages/shared/.eslintrc.js +++ b/web/packages/shared/.eslintrc.js @@ -1,13 +1,28 @@ module.exports = { - // When root is set to true, ESLint will stop looking for configuration files in parent directories. - // This is required here to ensure desktop picks the right eslint config, where this app is - // packaged as a submodule. - root: true, - extends: ["@ente/eslint-config"], - parser: "@typescript-eslint/parser", - parserOptions: { - tsconfigRootDir: __dirname, - project: "./tsconfig.json", + extends: ["@/build-config/eslintrc-react"], + ignorePatterns: ["next/utils/headers.js"], + rules: { + /* TODO: + * "This rule requires the `strictNullChecks` compiler option to be + * turned on to function correctly" + */ + "@typescript-eslint/prefer-nullish-coalescing": "off", + "@typescript-eslint/no-unnecessary-condition": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-argument": "off", + /** TODO: Disabled as we migrate, try to prune these again */ + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-unsafe-enum-comparison": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "off", + "@typescript-eslint/array-type": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unnecessary-template-expression": "off", + "@typescript-eslint/consistent-indexed-object-style": "off", + "@typescript-eslint/prefer-promise-reject-errors": "off", + "@typescript-eslint/no-useless-constructor": "off", + "react-hooks/exhaustive-deps": "off", }, - ignorePatterns: [".eslintrc.js", "next/utils/headers.js"], }; diff --git a/web/packages/shared/package.json b/web/packages/shared/package.json index 6e268322a5..0d8a08b061 100644 --- a/web/packages/shared/package.json +++ b/web/packages/shared/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@/base": "*", - "@ente/eslint-config": "*", + "@/build-config": "*", "axios": "^1.6.7" } } From e2636edbb9ba8b7f32860c87c6357f1fa80ae4de Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 12:25:14 +0530 Subject: [PATCH 032/177] Swap --- web/apps/auth/.eslintrc.js | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/web/apps/auth/.eslintrc.js b/web/apps/auth/.eslintrc.js index a407643d8a..5e036185ce 100644 --- a/web/apps/auth/.eslintrc.js +++ b/web/apps/auth/.eslintrc.js @@ -1,13 +1,28 @@ module.exports = { - // When root is set to true, ESLint will stop looking for configuration files in parent directories. - // This is required here to ensure desktop picks the right eslint config, where this app is - // packaged as a submodule. - root: true, - extends: ["@ente/eslint-config"], - parser: "@typescript-eslint/parser", - parserOptions: { - tsconfigRootDir: __dirname, - project: "./tsconfig.json", + extends: ["@/build-config/eslintrc-next"], + rules: { + /* TODO: + * "This rule requires the `strictNullChecks` compiler option to be + * turned on to function correctly" + */ + "@typescript-eslint/prefer-nullish-coalescing": "off", + "@typescript-eslint/no-unnecessary-condition": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-argument": "off", + /** TODO: Disabled as we migrate, try to prune these again */ + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-unsafe-enum-comparison": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "off", + "@typescript-eslint/array-type": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unnecessary-template-expression": "off", + "@typescript-eslint/consistent-indexed-object-style": "off", + "@typescript-eslint/prefer-promise-reject-errors": "off", + "@typescript-eslint/no-useless-constructor": "off", + "react-hooks/exhaustive-deps": "off", + "react-refresh/only-export-components": "off", }, - ignorePatterns: [".eslintrc.js", "next.config.js", "out"], }; From 27c722148303cb2ddad04a1b30443110364c6f56 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 12:28:18 +0530 Subject: [PATCH 033/177] Swap --- web/packages/accounts/.eslintrc.js | 43 +++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/web/packages/accounts/.eslintrc.js b/web/packages/accounts/.eslintrc.js index 556f3b6392..38562301c4 100644 --- a/web/packages/accounts/.eslintrc.js +++ b/web/packages/accounts/.eslintrc.js @@ -1,13 +1,36 @@ module.exports = { - // When root is set to true, ESLint will stop looking for configuration files in parent directories. - // This is required here to ensure desktop picks the right eslint config, where this app is - // packaged as a submodule. - root: true, - extends: ["@ente/eslint-config"], - parser: "@typescript-eslint/parser", - parserOptions: { - tsconfigRootDir: __dirname, - project: "./tsconfig.json", + extends: ["@/build-config/eslintrc-react"], + rules: { + /* TODO: + * "This rule requires the `strictNullChecks` compiler option to be + * turned on to function correctly" + */ + "@typescript-eslint/prefer-nullish-coalescing": "off", + "@typescript-eslint/no-unnecessary-condition": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-call": "off", + /** TODO: Disabled as we migrate, try to prune these again */ + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-unsafe-enum-comparison": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "off", + "@typescript-eslint/array-type": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unnecessary-template-expression": "off", + "@typescript-eslint/consistent-indexed-object-style": "off", + "@typescript-eslint/prefer-promise-reject-errors": "off", + "@typescript-eslint/no-useless-constructor": "off", + "@typescript-eslint/dot-notation": "off", + "@typescript-eslint/require-await": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/prefer-optional-chain": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/no-misused-promises": "off", + "react-hooks/exhaustive-deps": "off", + "react-hooks/rules-of-hooks": "off", + "react-refresh/only-export-components": "off", }, - ignorePatterns: [".eslintrc.js"], }; From 28d6a525db5b5c4d7814b0d8d35fe8487ce4a4af Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 12:32:54 +0530 Subject: [PATCH 034/177] Swap --- web/apps/photos/.eslintrc.js | 56 +++++++++++++++++++++++++++++------- web/apps/photos/package.json | 2 +- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/web/apps/photos/.eslintrc.js b/web/apps/photos/.eslintrc.js index 4eabc44fff..f97cf1e20a 100644 --- a/web/apps/photos/.eslintrc.js +++ b/web/apps/photos/.eslintrc.js @@ -1,14 +1,5 @@ module.exports = { - // When root is set to true, ESLint will stop looking for configuration files in parent directories. - // This is required here to ensure desktop picks the right eslint config, where this app is - // packaged as a submodule. - root: true, - extends: ["@ente/eslint-config"], - parser: "@typescript-eslint/parser", - parserOptions: { - tsconfigRootDir: __dirname, - project: "./tsconfig.json", - }, + extends: ["@/build-config/eslintrc-next"], ignorePatterns: [ ".eslintrc.js", "out", @@ -16,4 +7,49 @@ module.exports = { "public", "next.config.js", ], + rules: { + /* TODO: + * "This rule requires the `strictNullChecks` compiler option to be + * turned on to function correctly" + */ + "@typescript-eslint/prefer-nullish-coalescing": "off", + "@typescript-eslint/no-unnecessary-condition": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-call": "off", + /** TODO: Disabled as we migrate, try to prune these again */ + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-unsafe-enum-comparison": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "off", + "@typescript-eslint/array-type": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unnecessary-template-expression": "off", + "@typescript-eslint/consistent-indexed-object-style": "off", + "@typescript-eslint/prefer-promise-reject-errors": "off", + "@typescript-eslint/no-useless-constructor": "off", + "@typescript-eslint/dot-notation": "off", + "@typescript-eslint/require-await": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/prefer-optional-chain": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/no-misused-promises": "off", + "@typescript-eslint/restrict-template-expressions": "off", + "@typescript-eslint/consistent-type-definitions": "off", + "@typescript-eslint/consistent-generic-constructors": "off", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/no-base-to-string": "off", + "@typescript-eslint/no-unnecessary-type-arguments": "off", + "@typescript-eslint/prefer-for-of": "off", + "@typescript-eslint/no-confusing-void-expression": "off", + "@typescript-eslint/use-unknown-in-catch-callback-variable": "off", + "@typescript-eslint/only-throw-error": "off", + "@typescript-eslint/restrict-plus-operands": "off", + "@typescript-eslint/no-meaningless-void-operator": "off", + "react-hooks/exhaustive-deps": "off", + "react-hooks/rules-of-hooks": "off", + "react-refresh/only-export-components": "off", + }, }; diff --git a/web/apps/photos/package.json b/web/apps/photos/package.json index ad2f3323d2..89bcaca5d1 100644 --- a/web/apps/photos/package.json +++ b/web/apps/photos/package.json @@ -5,10 +5,10 @@ "dependencies": { "@/accounts": "*", "@/base": "*", + "@/build-config": "*", "@/gallery": "*", "@/media": "*", "@/new": "*", - "@ente/eslint-config": "*", "@ente/shared": "*", "@stripe/stripe-js": "^1.13.2", "bip39": "^3.0.4", From cda9f17bcb28bdac8cdfa4d5755085d744a616b9 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 12:36:23 +0530 Subject: [PATCH 035/177] Inline --- web/packages/shared/tsconfig.json | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/web/packages/shared/tsconfig.json b/web/packages/shared/tsconfig.json index 1eddcee1e7..2a10b272a8 100644 --- a/web/packages/shared/tsconfig.json +++ b/web/packages/shared/tsconfig.json @@ -1,6 +1,25 @@ { - "extends": "../../tsconfig.base.json", "compilerOptions": { + "allowJs": true, + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "incremental": true, + "isolatedModules": true, + // "jsx": "react-jsx", + // "lib": ["dom", "dom.iterable", "esnext"], + "module": "esnext", + "moduleResolution": "node", + "noEmit": true, + "noFallthroughCasesInSwitch": true, + // "noImplicitAny": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "es5", "downlevelIteration": true, "jsx": "preserve", "lib": ["dom", "dom.iterable", "esnext", "webworker"], @@ -8,8 +27,11 @@ "noUnusedLocals": false, "noUnusedParameters": false, "strictNullChecks": false, - "target": "es5", "useUnknownInCatchVariables": false + // "paths": { + // "@ente/eslint-config/*": ["./packages/eslint-config/*"], + // "@ente/shared/*": ["./packages/shared/*"] + // } }, "include": [ "**/*.ts", @@ -18,5 +40,6 @@ "themes/mui-theme.d.ts", "../base/log-web.ts", "../base/global-electron.d.ts" - ] + ], + "exclude": ["**/node_modules", "**/.*/"] } From cbf48df272bc97823464eedc00460b07c61fbe9f Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 12:44:45 +0530 Subject: [PATCH 036/177] Inline base config --- web/apps/photos/tsconfig.json | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/web/apps/photos/tsconfig.json b/web/apps/photos/tsconfig.json index 9ccccd5cbe..07f7f2efcb 100644 --- a/web/apps/photos/tsconfig.json +++ b/web/apps/photos/tsconfig.json @@ -1,6 +1,25 @@ { - "extends": "../../tsconfig.base.json", "compilerOptions": { + "allowJs": true, + "allowSyntheticDefaultImports": true, + // "baseUrl": ".", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "incremental": true, + "isolatedModules": true, + // "jsx": "react-jsx", + // "lib": ["dom", "dom.iterable", "esnext"], + "module": "esnext", + // "moduleResolution": "node", + "noEmit": true, + "noFallthroughCasesInSwitch": true, + // "noImplicitAny": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + // "target": "es2021", "baseUrl": "./src", "downlevelIteration": true, "jsx": "preserve", @@ -11,9 +30,13 @@ "strictNullChecks": false, "target": "es5", "useUnknownInCatchVariables": false, - /* TODO(MR): Add to auth */ "moduleResolution": "bundler" + + // "paths": { + // "@ente/eslint-config/*": ["./packages/eslint-config/*"], + // "@ente/shared/*": ["./packages/shared/*"] + // } }, "include": [ "next-env.d.ts", From dee8f8af6cc3a30b51223425bc183c6e0298bb0c Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 12:49:58 +0530 Subject: [PATCH 037/177] Prune --- web/apps/auth/package.json | 5 +- web/apps/cast/package.json | 2 +- web/apps/photos/package.json | 3 +- web/packages/accounts/package.json | 4 +- web/packages/eslint-config/.eslintrc.js | 9 - web/packages/eslint-config/README.md | 1 - web/packages/eslint-config/empty.js | 0 web/packages/eslint-config/index.js | 61 --- web/packages/eslint-config/package.json | 15 - web/packages/eslint-config/tsconfig.json | 3 - web/packages/gallery/package.json | 5 +- web/packages/media/package.json | 1 + web/packages/new/package.json | 4 +- web/packages/shared/package.json | 4 +- web/tsconfig.base.json | 29 -- web/yarn.lock | 609 +---------------------- 16 files changed, 43 insertions(+), 712 deletions(-) delete mode 100644 web/packages/eslint-config/.eslintrc.js delete mode 100644 web/packages/eslint-config/README.md delete mode 100644 web/packages/eslint-config/empty.js delete mode 100644 web/packages/eslint-config/index.js delete mode 100644 web/packages/eslint-config/package.json delete mode 100644 web/packages/eslint-config/tsconfig.json delete mode 100644 web/tsconfig.base.json diff --git a/web/apps/auth/package.json b/web/apps/auth/package.json index f43932ccdb..7ec5749d3c 100644 --- a/web/apps/auth/package.json +++ b/web/apps/auth/package.json @@ -5,10 +5,11 @@ "dependencies": { "@/accounts": "*", "@/base": "*", - "@/build-config": "*", - "@ente/eslint-config": "*", "@ente/shared": "*", "jssha": "~3.3.1", "otpauth": "9.2.4" + }, + "devDependencies": { + "@/build-config": "*" } } diff --git a/web/apps/cast/package.json b/web/apps/cast/package.json index a6438eadf4..6e93f92c37 100644 --- a/web/apps/cast/package.json +++ b/web/apps/cast/package.json @@ -5,11 +5,11 @@ "dependencies": { "@/accounts": "*", "@/base": "*", - "@/build-config": "*", "@/media": "*", "@ente/shared": "*" }, "devDependencies": { + "@/build-config": "*", "@types/chromecast-caf-receiver": "^6.0.17" } } diff --git a/web/apps/photos/package.json b/web/apps/photos/package.json index 89bcaca5d1..76c100e46a 100644 --- a/web/apps/photos/package.json +++ b/web/apps/photos/package.json @@ -5,7 +5,6 @@ "dependencies": { "@/accounts": "*", "@/base": "*", - "@/build-config": "*", "@/gallery": "*", "@/media": "*", "@/new": "*", @@ -38,7 +37,7 @@ "zxcvbn": "^4.4.2" }, "devDependencies": { - "@ente/eslint-config": "*", + "@/build-config": "*", "@next/bundle-analyzer": "^14.1", "@types/bs58": "^4.0.1", "@types/leaflet": "^1.9", diff --git a/web/packages/accounts/package.json b/web/packages/accounts/package.json index c5f7b0b881..544d43f374 100644 --- a/web/packages/accounts/package.json +++ b/web/packages/accounts/package.json @@ -4,8 +4,10 @@ "private": true, "dependencies": { "@/base": "*", - "@ente/eslint-config": "*", "@ente/shared": "*", "react-otp-input": "^3.1.1" + }, + "devDependencies": { + "@/build-config": "*" } } diff --git a/web/packages/eslint-config/.eslintrc.js b/web/packages/eslint-config/.eslintrc.js deleted file mode 100644 index d8576680e3..0000000000 --- a/web/packages/eslint-config/.eslintrc.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - extends: ["@ente/eslint-config"], - parser: "@typescript-eslint/parser", - parserOptions: { - tsconfigRootDir: __dirname, - project: "./tsconfig.json", - }, - ignorePatterns: [".eslintrc.js", "index.js"], -}; diff --git a/web/packages/eslint-config/README.md b/web/packages/eslint-config/README.md deleted file mode 100644 index 5f28c59c4e..0000000000 --- a/web/packages/eslint-config/README.md +++ /dev/null @@ -1 +0,0 @@ -Deprecated in favor of [@/build-config](../build-config/README.md). diff --git a/web/packages/eslint-config/empty.js b/web/packages/eslint-config/empty.js deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/web/packages/eslint-config/index.js b/web/packages/eslint-config/index.js deleted file mode 100644 index aa4d1b2f9d..0000000000 --- a/web/packages/eslint-config/index.js +++ /dev/null @@ -1,61 +0,0 @@ -module.exports = { - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking", - ], - parserOptions: { - tsconfigRootDir: __dirname, - project: "./tsconfig.json", - }, - plugins: ["@typescript-eslint"], - rules: { - indent: "off", - "class-methods-use-this": "off", - "react/prop-types": "off", - "react/display-name": "off", - "react/no-unescaped-entities": "off", - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": ["error"], - "require-jsdoc": "off", - "valid-jsdoc": "off", - "max-len": "off", - "new-cap": "off", - "no-invalid-this": "off", - "no-throw-literal": "error", - // TODO(MR): We want this off anyway, for now forcing it here - eqeqeq: "off", - "object-curly-spacing": ["error", "always"], - "space-before-function-paren": "off", - "operator-linebreak": [ - "error", - "after", - { overrides: { "?": "before", ":": "before" } }, - ], - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-inferrable-types": "off", - "@typescript-eslint/restrict-template-expressions": "off", - "@typescript-eslint/ban-types": "off", - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-unsafe-call": "off", - "@typescript-eslint/require-await": "off", - "@typescript-eslint/restrict-plus-operands": "off", - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/no-empty-interface": "off", - "@typescript-eslint/no-misused-promises": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-unnecessary-type-assertion": "off", - "react-hooks/rules-of-hooks": "off", - "react-hooks/exhaustive-deps": "off", - "@typescript-eslint/no-unsafe-argument": "off", - "jsx-a11y/alt-text": "off", - // Temporarily turn these off to allow existing code. - // TODO (MR): Remove me (and those above). - "@typescript-eslint/no-unsafe-enum-comparison": "off", - "@typescript-eslint/no-base-to-string": "off", - }, -}; diff --git a/web/packages/eslint-config/package.json b/web/packages/eslint-config/package.json deleted file mode 100644 index fe1006ca94..0000000000 --- a/web/packages/eslint-config/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@ente/eslint-config", - "version": "0.0.0", - "private": "true", - "main": "index.js", - "dependencies": {}, - "devDependencies": { - "@typescript-eslint/eslint-plugin": "^7", - "@typescript-eslint/parser": "^7", - "eslint": "^8", - "eslint-config-next": "latest", - "eslint-config-prettier": "latest", - "eslint-plugin-react": "^7.34" - } -} diff --git a/web/packages/eslint-config/tsconfig.json b/web/packages/eslint-config/tsconfig.json deleted file mode 100644 index 02ec15ac42..0000000000 --- a/web/packages/eslint-config/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../tsconfig.base.json" -} diff --git a/web/packages/gallery/package.json b/web/packages/gallery/package.json index 4281b87fd9..f019ef1b28 100644 --- a/web/packages/gallery/package.json +++ b/web/packages/gallery/package.json @@ -2,5 +2,8 @@ "name": "@/gallery", "version": "0.0.0", "private": true, - "dependencies": {} + "dependencies": {}, + "devDependencies": { + "@/build-config": "*" + } } diff --git a/web/packages/media/package.json b/web/packages/media/package.json index 135b576e92..f64bb2b4c5 100644 --- a/web/packages/media/package.json +++ b/web/packages/media/package.json @@ -9,6 +9,7 @@ "jszip": "^3.10.1" }, "devDependencies": { + "@/build-config": "*", "@types/heic-convert": "^2.1.0" } } diff --git a/web/packages/new/package.json b/web/packages/new/package.json index f870dd4a6f..9ee39d6554 100644 --- a/web/packages/new/package.json +++ b/web/packages/new/package.json @@ -12,5 +12,7 @@ "idb": "^8.0.0", "zod": "^3.23.8" }, - "devDependencies": {} + "devDependencies": { + "@/build-config": "*" + } } diff --git a/web/packages/shared/package.json b/web/packages/shared/package.json index 0d8a08b061..0f24c8d925 100644 --- a/web/packages/shared/package.json +++ b/web/packages/shared/package.json @@ -4,7 +4,9 @@ "private": true, "dependencies": { "@/base": "*", - "@/build-config": "*", "axios": "^1.6.7" + }, + "devDependencies": { + "@/build-config": "*" } } diff --git a/web/tsconfig.base.json b/web/tsconfig.base.json deleted file mode 100644 index d38c99560f..0000000000 --- a/web/tsconfig.base.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "allowSyntheticDefaultImports": true, - "baseUrl": ".", - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "incremental": true, - "isolatedModules": true, - "jsx": "react-jsx", - "lib": ["dom", "dom.iterable", "esnext"], - "module": "esnext", - "moduleResolution": "node", - "noEmit": true, - "noFallthroughCasesInSwitch": true, - "noImplicitAny": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "strict": true, - "target": "es2021", - "paths": { - "@ente/eslint-config/*": ["./packages/eslint-config/*"], - "@ente/shared/*": ["./packages/shared/*"] - } - }, - "exclude": ["**/node_modules", "**/.*/"] -} diff --git a/web/yarn.lock b/web/yarn.lock index ebe44ccfda..da104df57d 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -429,7 +429,7 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": version "4.11.0" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== @@ -493,18 +493,6 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== -"@isaacs/cliui@^8.0.2": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" - integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== - dependencies: - string-width "^5.1.2" - string-width-cjs "npm:string-width@^4.2.0" - strip-ansi "^7.0.1" - strip-ansi-cjs "npm:strip-ansi@^6.0.1" - wrap-ansi "^8.1.0" - wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" - "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" @@ -650,13 +638,6 @@ resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.9.tgz#f7fed48efa51b069cfc611082ad0101756df4c6a" integrity sha512-hnDAoDPMii31V0ivibI8p6b023jOF1XblWTVjsDUoZKwnZlaBtJFZKDwFqi22R8r9i6W08dThUWU7Bsh2Rg8Ww== -"@next/eslint-plugin-next@14.2.9": - version "14.2.9" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.9.tgz#13a3c48758e86fe2af7d05d08d557533368689fa" - integrity sha512-tmLXuDNfPTqoFuSfsd9Q4R96SS/UCKTPtBnnR+cKDcbh8xZU+126vZnRWH1WEpOmS4Vl2Hy/X6SPmgOGZzn+hA== - dependencies: - glob "10.3.10" - "@next/swc-darwin-arm64@14.2.9": version "14.2.9" resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.9.tgz#6d6880b580a0cb8d71be929d5399f0904d867e0a" @@ -728,16 +709,6 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nolyfill/is-core-module@1.0.39": - version "1.0.39" - resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e" - integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA== - -"@pkgjs/parseargs@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" - integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== - "@pkgr/core@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" @@ -833,16 +804,6 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.2.tgz#e4291e3c1bc637083f87936c333cdbcad22af63b" integrity sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA== -"@rtsao/scc@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" - integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== - -"@rushstack/eslint-patch@^1.3.3": - version "1.10.4" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz#427d5549943a9c6fce808e39ea64dbe60d4047f1" - integrity sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA== - "@stripe/stripe-js@^1.13.2", "@stripe/stripe-js@^1.17.0": version "1.54.2" resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.54.2.tgz#0665848e22cbda936cfd05256facdfbba121438d" @@ -935,16 +896,6 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" -"@types/json-schema@^7.0.12": - version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" - integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== - -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== - "@types/leaflet@^1.9": version "1.9.12" resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.9.12.tgz#a6626a0b3fba36fd34723d6e95b22e8024781ad6" @@ -1022,11 +973,6 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/semver@^7.5.0": - version "7.5.8" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" - integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== - "@types/uuid@^9.0.2": version "9.0.8" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" @@ -1037,23 +983,6 @@ resolved "https://registry.yarnpkg.com/@types/zxcvbn/-/zxcvbn-4.4.5.tgz#8ce8623ed7a36e3a76d1c0b539708dfb2e859bc0" integrity sha512-FZJgC5Bxuqg7Rhsm/bx6gAruHHhDQ55r+s0JhDh8CQ16fD7NsJJ+p8YMMQDhSQoIrSmjpqqYWA96oQVMNkjRyA== -"@typescript-eslint/eslint-plugin@^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.2.0.tgz#5a5fcad1a7baed85c10080d71ad901f98c38d5b7" - integrity sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw== - dependencies: - "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "7.2.0" - "@typescript-eslint/type-utils" "7.2.0" - "@typescript-eslint/utils" "7.2.0" - "@typescript-eslint/visitor-keys" "7.2.0" - debug "^4.3.4" - graphemer "^1.4.0" - ignore "^5.2.4" - natural-compare "^1.4.0" - semver "^7.5.4" - ts-api-utils "^1.0.1" - "@typescript-eslint/eslint-plugin@^7": version "7.18.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz#b16d3cf3ee76bf572fdf511e79c248bdec619ea3" @@ -1069,17 +998,6 @@ natural-compare "^1.4.0" ts-api-utils "^1.3.0" -"@typescript-eslint/parser@^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.2.0.tgz#44356312aea8852a3a82deebdacd52ba614ec07a" - integrity sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg== - dependencies: - "@typescript-eslint/scope-manager" "7.2.0" - "@typescript-eslint/types" "7.2.0" - "@typescript-eslint/typescript-estree" "7.2.0" - "@typescript-eslint/visitor-keys" "7.2.0" - debug "^4.3.4" - "@typescript-eslint/parser@^7": version "7.18.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.18.0.tgz#83928d0f1b7f4afa974098c64b5ce6f9051f96a0" @@ -1099,14 +1017,6 @@ "@typescript-eslint/types" "7.18.0" "@typescript-eslint/visitor-keys" "7.18.0" -"@typescript-eslint/scope-manager@7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz#cfb437b09a84f95a0930a76b066e89e35d94e3da" - integrity sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg== - dependencies: - "@typescript-eslint/types" "7.2.0" - "@typescript-eslint/visitor-keys" "7.2.0" - "@typescript-eslint/type-utils@7.18.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz#2165ffaee00b1fbbdd2d40aa85232dab6998f53b" @@ -1117,26 +1027,11 @@ debug "^4.3.4" ts-api-utils "^1.3.0" -"@typescript-eslint/type-utils@7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.2.0.tgz#7be5c30e9b4d49971b79095a1181324ef6089a19" - integrity sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA== - dependencies: - "@typescript-eslint/typescript-estree" "7.2.0" - "@typescript-eslint/utils" "7.2.0" - debug "^4.3.4" - ts-api-utils "^1.0.1" - "@typescript-eslint/types@7.18.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.18.0.tgz#b90a57ccdea71797ffffa0321e744f379ec838c9" integrity sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ== -"@typescript-eslint/types@7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.2.0.tgz#0feb685f16de320e8520f13cca30779c8b7c403f" - integrity sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA== - "@typescript-eslint/typescript-estree@7.18.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz#b5868d486c51ce8f312309ba79bdb9f331b37931" @@ -1151,20 +1046,6 @@ semver "^7.6.0" ts-api-utils "^1.3.0" -"@typescript-eslint/typescript-estree@7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz#5beda2876c4137f8440c5a84b4f0370828682556" - integrity sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA== - dependencies: - "@typescript-eslint/types" "7.2.0" - "@typescript-eslint/visitor-keys" "7.2.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - minimatch "9.0.3" - semver "^7.5.4" - ts-api-utils "^1.0.1" - "@typescript-eslint/utils@7.18.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.18.0.tgz#bca01cde77f95fc6a8d5b0dbcbfb3d6ca4be451f" @@ -1175,19 +1056,6 @@ "@typescript-eslint/types" "7.18.0" "@typescript-eslint/typescript-estree" "7.18.0" -"@typescript-eslint/utils@7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.2.0.tgz#fc8164be2f2a7068debb4556881acddbf0b7ce2a" - integrity sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA== - dependencies: - "@eslint-community/eslint-utils" "^4.4.0" - "@types/json-schema" "^7.0.12" - "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "7.2.0" - "@typescript-eslint/types" "7.2.0" - "@typescript-eslint/typescript-estree" "7.2.0" - semver "^7.5.4" - "@typescript-eslint/visitor-keys@7.18.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz#0564629b6124d67607378d0f0332a0495b25e7d7" @@ -1196,14 +1064,6 @@ "@typescript-eslint/types" "7.18.0" eslint-visitor-keys "^3.4.3" -"@typescript-eslint/visitor-keys@7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz#5035f177752538a5750cca1af6044b633610bf9e" - integrity sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A== - dependencies: - "@typescript-eslint/types" "7.2.0" - eslint-visitor-keys "^3.4.1" - "@ungap/structured-clone@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" @@ -1257,11 +1117,6 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-regex@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" - integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== - ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1276,24 +1131,12 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@~5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" - integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== - dependencies: - deep-equal "^2.0.5" - -array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1: +array-buffer-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== @@ -1330,19 +1173,7 @@ array.prototype.findlast@^1.2.5: es-object-atoms "^1.0.0" es-shim-unscopables "^1.0.2" -array.prototype.findlastindex@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" - integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-errors "^1.3.0" - es-object-atoms "^1.0.0" - es-shim-unscopables "^1.0.2" - -array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: +array.prototype.flat@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== @@ -1387,11 +1218,6 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" -ast-types-flow@^0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" - integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1409,11 +1235,6 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" -axe-core@^4.10.0: - version "4.10.0" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.0.tgz#d9e56ab0147278272739a000880196cdfe113b59" - integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g== - axios@^1.6.7: version "1.7.7" resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" @@ -1423,11 +1244,6 @@ axios@^1.6.7: form-data "^4.0.0" proxy-from-env "^1.1.0" -axobject-query@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" - integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== - babel-plugin-macros@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" @@ -1656,7 +1472,7 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" -cross-spawn@^7.0.0, cross-spawn@^7.0.2: +cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1670,11 +1486,6 @@ csstype@^3.0.2, csstype@^3.1.3: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== -damerau-levenshtein@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" - integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== - data-view-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" @@ -1717,44 +1528,13 @@ debounce@^2.1.1: resolved "https://registry.yarnpkg.com/debounce/-/debounce-2.1.1.tgz#8ae1d5233ec5abd1c8edf3e994a9286a73d0f4ff" integrity sha512-+xRWxgel9LgTC4PwKlm7TJUK6B6qsEK77NaiNvXmeQ7Y3e6OVVsBC4a9BSptS/mAYceyAz37Oa8JTTuPRft7uQ== -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: +debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.7" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== dependencies: ms "^2.1.3" -deep-equal@^2.0.5: - version "2.2.3" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" - integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== - dependencies: - array-buffer-byte-length "^1.0.0" - call-bind "^1.0.5" - es-get-iterator "^1.1.3" - get-intrinsic "^1.2.2" - is-arguments "^1.1.1" - is-array-buffer "^3.0.2" - is-date-object "^1.0.5" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - isarray "^2.0.5" - object-is "^1.1.5" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.5.1" - side-channel "^1.0.4" - which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.13" - deep-freeze@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84" @@ -1837,11 +1617,6 @@ duplexer@^0.1.2: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - electron-to-chromium@^1.5.4: version "1.5.19" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.19.tgz#aeaa0a076f3f0f0e8db2c57fd10158508f00725a" @@ -1852,19 +1627,6 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -enhanced-resolve@^5.15.0: - version "5.17.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" - integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - equals@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/equals/-/equals-1.0.5.tgz#212062dde5e1a510d955f13598efcc6a621b6ace" @@ -1943,21 +1705,6 @@ es-errors@^1.2.1, es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-get-iterator@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" - integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - has-symbols "^1.0.3" - is-arguments "^1.1.1" - is-map "^2.0.2" - is-set "^2.0.2" - is-string "^1.0.7" - isarray "^2.0.5" - stop-iteration-iterator "^1.0.0" - es-iterator-helpers@^1.0.19: version "1.0.19" resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz#117003d0e5fec237b4b5c08aded722e0c6d50ca8" @@ -2054,104 +1801,7 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-config-next@latest: - version "14.2.9" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-14.2.9.tgz#7230dbf285ff7c377be13e8f05febcdbfc6276fc" - integrity sha512-aNgGqWBp2KFcuEf9zNqmv+8dBkOrdyOlCIbdtyw7fiCQioLqXNcXmalAyeNtVyE95Kwgg11bgXvuVqdxpbR79g== - dependencies: - "@next/eslint-plugin-next" "14.2.9" - "@rushstack/eslint-patch" "^1.3.3" - "@typescript-eslint/eslint-plugin" "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0" - "@typescript-eslint/parser" "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0" - eslint-import-resolver-node "^0.3.6" - eslint-import-resolver-typescript "^3.5.2" - eslint-plugin-import "^2.28.1" - eslint-plugin-jsx-a11y "^6.7.1" - eslint-plugin-react "^7.33.2" - eslint-plugin-react-hooks "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" - -eslint-config-prettier@latest: - version "9.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" - integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== - -eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9: - version "0.3.9" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" - integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== - dependencies: - debug "^3.2.7" - is-core-module "^2.13.0" - resolve "^1.22.4" - -eslint-import-resolver-typescript@^3.5.2: - version "3.6.3" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz#bb8e388f6afc0f940ce5d2c5fd4a3d147f038d9e" - integrity sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA== - dependencies: - "@nolyfill/is-core-module" "1.0.39" - debug "^4.3.5" - enhanced-resolve "^5.15.0" - eslint-module-utils "^2.8.1" - fast-glob "^3.3.2" - get-tsconfig "^4.7.5" - is-bun-module "^1.0.2" - is-glob "^4.0.3" - -eslint-module-utils@^2.8.1, eslint-module-utils@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.11.0.tgz#b99b211ca4318243f09661fae088f373ad5243c4" - integrity sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ== - dependencies: - debug "^3.2.7" - -eslint-plugin-import@^2.28.1: - version "2.30.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz#21ceea0fc462657195989dd780e50c92fe95f449" - integrity sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw== - dependencies: - "@rtsao/scc" "^1.1.0" - array-includes "^3.1.8" - array.prototype.findlastindex "^1.2.5" - array.prototype.flat "^1.3.2" - array.prototype.flatmap "^1.3.2" - debug "^3.2.7" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.9" - eslint-module-utils "^2.9.0" - hasown "^2.0.2" - is-core-module "^2.15.1" - is-glob "^4.0.3" - minimatch "^3.1.2" - object.fromentries "^2.0.8" - object.groupby "^1.0.3" - object.values "^1.2.0" - semver "^6.3.1" - tsconfig-paths "^3.15.0" - -eslint-plugin-jsx-a11y@^6.7.1: - version "6.10.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.0.tgz#36fb9dead91cafd085ddbe3829602fb10ef28339" - integrity sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg== - dependencies: - aria-query "~5.1.3" - array-includes "^3.1.8" - array.prototype.flatmap "^1.3.2" - ast-types-flow "^0.0.8" - axe-core "^4.10.0" - axobject-query "^4.1.0" - damerau-levenshtein "^1.0.8" - emoji-regex "^9.2.2" - es-iterator-helpers "^1.0.19" - hasown "^2.0.2" - jsx-ast-utils "^3.3.5" - language-tags "^1.0.9" - minimatch "^3.1.2" - object.fromentries "^2.0.8" - safe-regex-test "^1.0.3" - string.prototype.includes "^2.0.0" - -"eslint-plugin-react-hooks@^4.5.0 || 5.0.0-canary-7118f5dd7-20230705", eslint-plugin-react-hooks@^4.6: +eslint-plugin-react-hooks@^4.6: version "4.6.2" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596" integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== @@ -2161,7 +1811,7 @@ eslint-plugin-react-refresh@^0.4.7: resolved "https://registry.yarnpkg.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.11.tgz#e450761a2bdb260aa10cfb73f846209a737827cb" integrity sha512-wrAKxMbVr8qhXTtIKfXqAn5SAtRZt0aXxe5P23Fh4pUAdC6XEsybGLB8P0PI4j1yYqOgUEUlzKAGDfo7rJOjcw== -eslint-plugin-react@^7.33.2, eslint-plugin-react@^7.34: +eslint-plugin-react@^7.34: version "7.35.2" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.35.2.tgz#d32500d3ec268656d5071918bfec78cfd8b070ed" integrity sha512-Rbj2R9zwP2GYNcIak4xoAMV57hrBh3hTaR0k7hVjwCQgryE/pw5px4b13EYjduOI0hfXyZhwBxaGpOTbWSGzKQ== @@ -2287,7 +1937,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.2: +fast-glob@^3.2.9, fast-glob@^3.3.0: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -2396,14 +2046,6 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" -foreground-child@^3.1.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" - integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== - dependencies: - cross-spawn "^7.0.0" - signal-exit "^4.0.1" - form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -2467,7 +2109,7 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== @@ -2492,13 +2134,6 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" -get-tsconfig@^4.7.5: - version "4.8.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.0.tgz#125dc13a316f61650a12b20c97c11b8fd996fedd" - integrity sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw== - dependencies: - resolve-pkg-maps "^1.0.0" - get-user-locale@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/get-user-locale/-/get-user-locale-2.3.2.tgz#d37ae6e670c2b57d23a96fb4d91e04b2059d52cf" @@ -2525,17 +2160,6 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@10.3.10: - version "10.3.10" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" - integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== - dependencies: - foreground-child "^3.1.0" - jackspeak "^2.3.5" - minimatch "^9.0.1" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-scurry "^1.10.1" - glob@^7.1.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -2598,7 +2222,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.2.11, graceful-fs@^4.2.4: +graceful-fs@^4.2.11: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -2756,7 +2380,7 @@ inherits@2, inherits@^2.0.3, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -internal-slot@^1.0.4, internal-slot@^1.0.7: +internal-slot@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== @@ -2770,15 +2394,7 @@ is-any-array@^2.0.0, is-any-array@^2.0.1: resolved "https://registry.yarnpkg.com/is-any-array/-/is-any-array-2.0.1.tgz#9233242a9c098220290aa2ec28f82ca7fa79899e" integrity sha512-UtilS7hLRu++wb/WBAw9bNuP1Eg04Ivn1vERJck8zJthEvXCBEBpGR/33u/xLKWEQf95803oalHrVDptcAvFdQ== -is-arguments@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: +is-array-buffer@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== @@ -2813,19 +2429,12 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-bun-module@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-bun-module/-/is-bun-module-1.2.1.tgz#495e706f42e29f086fd5fe1ac3c51f106062b9fc" - integrity sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q== - dependencies: - semver "^7.6.3" - is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.13.0, is-core-module@^2.15.1: +is-core-module@^2.13.0: version "2.15.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== @@ -2882,7 +2491,7 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" -is-map@^2.0.2, is-map@^2.0.3: +is-map@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== @@ -2927,7 +2536,7 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-set@^2.0.2, is-set@^2.0.3: +is-set@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== @@ -3011,15 +2620,6 @@ iterator.prototype@^1.1.2: reflect.getprototypeof "^1.0.4" set-function-name "^2.0.1" -jackspeak@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" - integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - jkroso-type@1: version "1.1.1" resolved "https://registry.yarnpkg.com/jkroso-type/-/jkroso-type-1.1.1.tgz#bc4ced6d6c45fe0745282bafc86a9f8c4fc9ce61" @@ -3067,13 +2667,6 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json5@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" - integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== - dependencies: - minimist "^1.2.0" - json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" @@ -3084,7 +2677,7 @@ jssha@~3.3.1: resolved "https://registry.yarnpkg.com/jssha/-/jssha-3.3.1.tgz#c5b7fc7fb9aa745461923b87df0e247dd59c7ea8" integrity sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ== -"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: +"jsx-ast-utils@^2.4.1 || ^3.0.0": version "3.3.5" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== @@ -3111,18 +2704,6 @@ keyv@^4.5.3: dependencies: json-buffer "3.0.1" -language-subtag-registry@^0.3.20: - version "0.3.23" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" - integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== - -language-tags@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.9.tgz#1ffdcd0ec0fafb4b1be7f8b11f306ad0f9c08777" - integrity sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== - dependencies: - language-subtag-registry "^0.3.20" - leaflet-defaulticon-compatibility@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/leaflet-defaulticon-compatibility/-/leaflet-defaulticon-compatibility-0.1.2.tgz#f5e1a5841aeab9d1682d17887348855a741b3c2a" @@ -3213,11 +2794,6 @@ loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -lru-cache@^10.2.0: - version "10.4.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" - integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== - lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -3280,13 +2856,6 @@ mimic-fn@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74" integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ== -minimatch@9.0.3: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -3294,23 +2863,13 @@ minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^9.0.1, minimatch@^9.0.4: +minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": - version "7.1.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" - integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== - ml-array-max@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/ml-array-max/-/ml-array-max-1.2.4.tgz#2373e2b7e51c8807e456cc0ef364c5863713623b" @@ -3347,7 +2906,7 @@ mrmime@^2.0.0: resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== -ms@^2.1.1, ms@^2.1.3: +ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -3412,14 +2971,6 @@ object-inspect@^1.13.1: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== -object-is@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" - integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -3454,15 +3005,6 @@ object.fromentries@^2.0.8: es-abstract "^1.23.2" es-object-atoms "^1.0.0" -object.groupby@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" - integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - es-abstract "^1.23.2" - object.values@^1.1.6, object.values@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" @@ -3569,14 +3111,6 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.10.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" - integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== - dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -3848,7 +3382,7 @@ regenerator-runtime@^0.14.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== -regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: +regexp.prototype.flags@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== @@ -3868,12 +3402,7 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve-pkg-maps@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" - integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== - -resolve@^1.19.0, resolve@^1.22.4: +resolve@^1.19.0: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -3995,7 +3524,7 @@ semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: +semver@^7.6.0: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -4054,11 +3583,6 @@ side-channel@^1.0.4, side-channel@^1.0.6: get-intrinsic "^1.2.4" object-inspect "^1.13.1" -signal-exit@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - similarity-transformation@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/similarity-transformation/-/similarity-transformation-0.0.1.tgz#7b40266150be1ac593827b08a06a458df4fc9238" @@ -4112,27 +3636,11 @@ source-map@^0.5.7: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -stop-iteration-iterator@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" - integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== - dependencies: - internal-slot "^1.0.4" - streamsearch@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -4142,23 +3650,6 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - -string.prototype.includes@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz#8986d57aee66d5460c144620a6d873778ad7289f" - integrity sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - string.prototype.matchall@^4.0.11: version "4.0.11" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" @@ -4227,13 +3718,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -4241,18 +3725,6 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== - strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -4312,11 +3784,6 @@ synckit@0.9.2: "@pkgr/core" "^0.1.0" tslib "^2.6.2" -tapable@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" - integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== - text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -4379,21 +3846,11 @@ truncate-utf8-bytes@^1.0.0: dependencies: utf8-byte-length "^1.0.1" -ts-api-utils@^1.0.1, ts-api-utils@^1.3.0: +ts-api-utils@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== -tsconfig-paths@^3.15.0: - version "3.15.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" - integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.2" - minimist "^1.2.6" - strip-bom "^3.0.0" - tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.2: version "2.7.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" @@ -4592,7 +4049,7 @@ which-builtin-type@^1.1.3: which-collection "^1.0.2" which-typed-array "^1.1.15" -which-collection@^1.0.1, which-collection@^1.0.2: +which-collection@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== @@ -4602,7 +4059,7 @@ which-collection@^1.0.1, which-collection@^1.0.2: is-weakmap "^2.0.2" is-weakset "^2.0.3" -which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15: +which-typed-array@^1.1.14, which-typed-array@^1.1.15: version "1.1.15" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== @@ -4625,15 +4082,6 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -4643,15 +4091,6 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" From 4536486daac58a23e345ee214c49717679522870 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 12:57:26 +0530 Subject: [PATCH 038/177] Prep --- web/apps/photos/.eslintrc.js | 1 - web/apps/photos/src/pages/_app.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/web/apps/photos/.eslintrc.js b/web/apps/photos/.eslintrc.js index f97cf1e20a..3ae4ddd8df 100644 --- a/web/apps/photos/.eslintrc.js +++ b/web/apps/photos/.eslintrc.js @@ -45,7 +45,6 @@ module.exports = { "@typescript-eslint/prefer-for-of": "off", "@typescript-eslint/no-confusing-void-expression": "off", "@typescript-eslint/use-unknown-in-catch-callback-variable": "off", - "@typescript-eslint/only-throw-error": "off", "@typescript-eslint/restrict-plus-operands": "off", "@typescript-eslint/no-meaningless-void-operator": "off", "react-hooks/exhaustive-deps": "off", diff --git a/web/apps/photos/src/pages/_app.tsx b/web/apps/photos/src/pages/_app.tsx index 3a4e638e2e..e92f092a76 100644 --- a/web/apps/photos/src/pages/_app.tsx +++ b/web/apps/photos/src/pages/_app.tsx @@ -159,7 +159,7 @@ export default function App({ Component, pageProps }: AppProps) { redirectToFamilyPortal(); // https://github.com/vercel/next.js/issues/2476#issuecomment-573460710 - // eslint-disable-next-line no-throw-literal + // eslint-disable-next-line @typescript-eslint/only-throw-error throw "Aborting route change, redirection in process...."; } }); From f848f64dc57f50c219f28bff5b6906a9cc37430d Mon Sep 17 00:00:00 2001 From: Ashil <77285023+ashilkn@users.noreply.github.com> Date: Tue, 19 Nov 2024 14:09:33 +0530 Subject: [PATCH 039/177] Update apple-app-site-association for opening public link album in-app. --- .../.well-known/apple-app-site-association | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/web/apps/photos/public/.well-known/apple-app-site-association b/web/apps/photos/public/.well-known/apple-app-site-association index e05abb216b..ebab4a54da 100644 --- a/web/apps/photos/public/.well-known/apple-app-site-association +++ b/web/apps/photos/public/.well-known/apple-app-site-association @@ -1,4 +1,22 @@ { + "applinks": { + "apps": [], + "details": [ + { + "appIDs": [ + "6Z68YJY9Q2.io.ente.frame" + ], + "paths": [ + "*" + ], + "components": [ + { + "/": "/*" + } + ] + } + ] + }, "webcredentials": { "apps": [ "6Z68YJY9Q2.io.ente.frame", @@ -7,3 +25,4 @@ ] } } + From 55044b18186a72d1959d154a723c652955776914 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Tue, 19 Nov 2024 17:00:09 +0530 Subject: [PATCH 040/177] [mob][photos] Fix ConcurrentModificationError --- .../machine_learning/face_ml/person/person_service.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart index 8dff6c99a0..593a6628d1 100644 --- a/mobile/lib/services/machine_learning/face_ml/person/person_service.dart +++ b/mobile/lib/services/machine_learning/face_ml/person/person_service.dart @@ -343,13 +343,14 @@ class PersonService { final clusterID = clusterIdToFaceIDs.key; final faceIDs = clusterIdToFaceIDs.value; final foundRejectedFacesToCluster = {}; + final removeFaceIDs = {}; for (final faceID in faceIDs) { if (assignedAndRejectedFaceIDs.contains(faceID)) { - faceIDs.remove(faceID); + removeFaceIDs.add(faceID); foundRejectedFacesToCluster[faceID] = clusterID; } } - if (faceIDs.isEmpty) { + if (faceIDs.length == removeFaceIDs.length) { logger.info( "Cluster $clusterID for person ${e.id} ${personData.name} is empty due to rejected faces from remote, removing the cluster from person", ); From 7e7d53fbaf791b6a6a267f014c3c4d09058c9575 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 19 Nov 2024 17:22:20 +0530 Subject: [PATCH 041/177] [mob] Rename icons and action name for people bar --- mobile/lib/generated/intl/messages_en.dart | 3 +- mobile/lib/generated/l10n.dart | 14 +++++- mobile/lib/l10n/intl_en.arb | 3 +- .../lib/ui/viewer/people/people_app_bar.dart | 43 +++++++------------ 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/mobile/lib/generated/intl/messages_en.dart b/mobile/lib/generated/intl/messages_en.dart index d3471c3d36..d9e5696eb0 100644 --- a/mobile/lib/generated/intl/messages_en.dart +++ b/mobile/lib/generated/intl/messages_en.dart @@ -1333,7 +1333,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Reset ignored files"), "resetPasswordTitle": MessageLookupByLibrary.simpleMessage("Reset password"), - "resetPerson": MessageLookupByLibrary.simpleMessage("Reset person"), + "resetPerson": MessageLookupByLibrary.simpleMessage("Remove"), "resetToDefault": MessageLookupByLibrary.simpleMessage("Reset to default"), "restore": MessageLookupByLibrary.simpleMessage("Restore"), @@ -1344,6 +1344,7 @@ class MessageLookup extends MessageLookupByLibrary { "resumableUploads": MessageLookupByLibrary.simpleMessage("Resumable uploads"), "retry": MessageLookupByLibrary.simpleMessage("Retry"), + "review": MessageLookupByLibrary.simpleMessage("Review"), "reviewDeduplicateItems": MessageLookupByLibrary.simpleMessage( "Please review and delete the items you believe are duplicates."), "reviewSuggestions": diff --git a/mobile/lib/generated/l10n.dart b/mobile/lib/generated/l10n.dart index abd2f16a80..d1ae7f88cf 100644 --- a/mobile/lib/generated/l10n.dart +++ b/mobile/lib/generated/l10n.dart @@ -9305,6 +9305,16 @@ class S { ); } + /// `Review` + String get review { + return Intl.message( + 'Review', + name: 'review', + desc: '', + args: [], + ); + } + /// `Use as cover` String get useAsCover { return Intl.message( @@ -9906,10 +9916,10 @@ class S { ); } - /// `Reset person` + /// `Remove` String get resetPerson { return Intl.message( - 'Reset person', + 'Remove', name: 'resetPerson', desc: '', args: [], diff --git a/mobile/lib/l10n/intl_en.arb b/mobile/lib/l10n/intl_en.arb index 8f2276cc64..8c6edcdb7f 100644 --- a/mobile/lib/l10n/intl_en.arb +++ b/mobile/lib/l10n/intl_en.arb @@ -1291,6 +1291,7 @@ "right": "Right", "whatsNew": "What's new", "reviewSuggestions": "Review suggestions", + "review": "Review", "useAsCover": "Use as cover", "notPersonLabel": "Not {name}?", "@notPersonLabel": { @@ -1360,7 +1361,7 @@ "extraPhotosFound": "Extra photos found", "configuration": "Configuration", "localIndexing": "Local indexing", - "resetPerson": "Reset person", + "resetPerson": "Remove", "areYouSureYouWantToResetThisPerson": "Are you sure you want to reset this person?", "allPersonGroupingWillReset": "All groupings for this person will be reset, and you will lose all suggestions made for this person", "yesResetPerson": "Yes, reset person", diff --git a/mobile/lib/ui/viewer/people/people_app_bar.dart b/mobile/lib/ui/viewer/people/people_app_bar.dart index 44e5d66e0d..cf88e60b59 100644 --- a/mobile/lib/ui/viewer/people/people_app_bar.dart +++ b/mobile/lib/ui/viewer/people/people_app_bar.dart @@ -198,44 +198,31 @@ class _AppBarWidgetState extends State { const Padding( padding: EdgeInsets.all(8), ), - Text(S.of(context).rename), + Text(S.of(context).edit), + ], + ), + ), + PopupMenuItem( + value: PeoplePopupAction.reviewSuggestions, + child: Row( + children: [ + const Icon(Icons.search_outlined), + const Padding( + padding: EdgeInsets.all(8), + ), + Text(S.of(context).review), ], ), ), - // PopupMenuItem( - // value: PeoplPopupAction.setCover, - // child: Row( - // children: [ - // const Icon(Icons.image_outlined), - // const Padding( - // padding: EdgeInsets.all(8), - // ), - // Text(S.of(context).setCover), - // ], - // ), - // ), - PopupMenuItem( value: PeoplePopupAction.removeLabel, child: Row( children: [ - const Icon(Icons.remove_circle_outline), + const Icon(Icons.delete_outline), const Padding( padding: EdgeInsets.all(8), ), - Text(S.of(context).resetPerson), - ], - ), - ), - const PopupMenuItem( - value: PeoplePopupAction.reviewSuggestions, - child: Row( - children: [ - Icon(CupertinoIcons.square_stack_3d_down_right), - Padding( - padding: EdgeInsets.all(8), - ), - Text('Review suggestions'), + Text(S.of(context).remove), ], ), ), From 143d346ed4b28151847001d32e121a5f3d8a2b14 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 17:49:37 +0530 Subject: [PATCH 042/177] eslint migration wip checkpoint --- web/package.json | 2 +- web/packages/build-config/.eslintrc.js | 12 +- web/packages/build-config/eslint.config.mjs | 25 ++ web/packages/build-config/package.json | 11 +- web/yarn.lock | 474 +++++++++----------- 5 files changed, 253 insertions(+), 271 deletions(-) create mode 100644 web/packages/build-config/eslint.config.mjs diff --git a/web/package.json b/web/package.json index 86d4d2e215..b4dcf1116e 100644 --- a/web/package.json +++ b/web/package.json @@ -36,7 +36,7 @@ }, "devDependencies": { "concurrently": "^9.1.0", - "eslint": "^8", + "eslint": "^9.15.0", "prettier": "^3.3.3", "typescript": "^5.6.3" }, diff --git a/web/packages/build-config/.eslintrc.js b/web/packages/build-config/.eslintrc.js index ef82ed7187..7496b8d983 100644 --- a/web/packages/build-config/.eslintrc.js +++ b/web/packages/build-config/.eslintrc.js @@ -1,6 +1,6 @@ -/* eslint-env node */ -module.exports = { - extends: ["eslint:recommended"], - ignorePatterns: [".eslintrc.js"], - root: true, -}; +// /* eslint-env node */ +// module.exports = { +// extends: ["eslint:recommended"], +// ignorePatterns: [".eslintrc.js"], +// root: true, +// }; diff --git a/web/packages/build-config/eslint.config.mjs b/web/packages/build-config/eslint.config.mjs new file mode 100644 index 0000000000..69691eface --- /dev/null +++ b/web/packages/build-config/eslint.config.mjs @@ -0,0 +1,25 @@ +import js from "@eslint/js"; + + +export default [ + js.configs.recommended, + { + ignores: ["eslintrc-*.js"] + } +] +// import path from "node:path"; +// import { fileURLToPath } from "node:url"; +// import js from "@eslint/js"; +// import { FlatCompat } from "@eslint/eslintrc"; + +// const __filename = fileURLToPath(import.meta.url); +// const __dirname = path.dirname(__filename); +// const compat = new FlatCompat({ +// baseDirectory: __dirname, +// recommendedConfig: js.configs.recommended, +// allConfig: js.configs.all +// }); + +// export default [{ +// ignores: ["**/.eslintrc.js"], +// }, ...compat.extends("eslint:recommended")]; diff --git a/web/packages/build-config/package.json b/web/packages/build-config/package.json index 81c5c84b15..11338df6b9 100644 --- a/web/packages/build-config/package.json +++ b/web/packages/build-config/package.json @@ -3,11 +3,12 @@ "version": "0.0.0", "private": true, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^7", - "@typescript-eslint/parser": "^7", - "eslint-plugin-react": "^7.34", - "eslint-plugin-react-hooks": "^4.6", - "eslint-plugin-react-refresh": "^0.4.7", + "@eslint/js": "^9.15.0", + "@typescript-eslint/eslint-plugin": "^8.15.0", + "@typescript-eslint/parser": "^8.15.0", + "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-react-refresh": "^0.4.14", "prettier-plugin-organize-imports": "^4.1.0", "prettier-plugin-packagejson": "^2.5.3" } diff --git a/web/yarn.lock b/web/yarn.lock index da104df57d..b92166b8b3 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -429,30 +429,61 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": +"@eslint-community/regexpp@^4.10.0": version "4.11.0" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== -"@eslint/eslintrc@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" - integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== +"@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/config-array@^0.19.0": + version "0.19.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.0.tgz#3251a528998de914d59bb21ba4c11767cf1b3519" + integrity sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ== + dependencies: + "@eslint/object-schema" "^2.1.4" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/core@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.9.0.tgz#168ee076f94b152c01ca416c3e5cf82290ab4fcd" + integrity sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg== + +"@eslint/eslintrc@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.2.0.tgz#57470ac4e2e283a6bf76044d63281196e370542c" + integrity sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.6.0" - globals "^13.19.0" + espree "^10.0.1" + globals "^14.0.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.57.0": - version "8.57.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" - integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== +"@eslint/js@9.15.0", "@eslint/js@^9.15.0": + version "9.15.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.15.0.tgz#df0e24fe869143b59731942128c19938fdbadfb5" + integrity sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg== + +"@eslint/object-schema@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" + integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== + +"@eslint/plugin-kit@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz#812980a6a41ecf3a8341719f92a6d1e784a2e0e8" + integrity sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA== + dependencies: + levn "^0.4.1" "@floating-ui/core@^1.6.0": version "1.6.7" @@ -474,24 +505,33 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.7.tgz#d0ece53ce99ab5a8e37ebdfe5e32452a2bfc073e" integrity sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA== -"@humanwhocodes/config-array@^0.11.14": - version "0.11.14" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" - integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== dependencies: - "@humanwhocodes/object-schema" "^2.0.2" - debug "^4.3.1" - minimatch "^3.0.5" + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^2.0.2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" - integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@humanwhocodes/retry@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== + +"@humanwhocodes/retry@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" + integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" @@ -701,7 +741,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": +"@nodelib/fs.walk@^1.2.3": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -878,6 +918,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== +"@types/estree@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + "@types/geojson@*": version "7946.0.14" resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.14.tgz#319b63ad6df705ee2a65a73ef042c8271e696613" @@ -896,6 +941,11 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + "@types/leaflet@^1.9": version "1.9.12" resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.9.12.tgz#a6626a0b3fba36fd34723d6e95b22e8024781ad6" @@ -983,91 +1033,86 @@ resolved "https://registry.yarnpkg.com/@types/zxcvbn/-/zxcvbn-4.4.5.tgz#8ce8623ed7a36e3a76d1c0b539708dfb2e859bc0" integrity sha512-FZJgC5Bxuqg7Rhsm/bx6gAruHHhDQ55r+s0JhDh8CQ16fD7NsJJ+p8YMMQDhSQoIrSmjpqqYWA96oQVMNkjRyA== -"@typescript-eslint/eslint-plugin@^7": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz#b16d3cf3ee76bf572fdf511e79c248bdec619ea3" - integrity sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw== +"@typescript-eslint/eslint-plugin@^8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.15.0.tgz#c95c6521e70c8b095a684d884d96c0c1c63747d2" + integrity sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg== dependencies: "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "7.18.0" - "@typescript-eslint/type-utils" "7.18.0" - "@typescript-eslint/utils" "7.18.0" - "@typescript-eslint/visitor-keys" "7.18.0" + "@typescript-eslint/scope-manager" "8.15.0" + "@typescript-eslint/type-utils" "8.15.0" + "@typescript-eslint/utils" "8.15.0" + "@typescript-eslint/visitor-keys" "8.15.0" graphemer "^1.4.0" ignore "^5.3.1" natural-compare "^1.4.0" ts-api-utils "^1.3.0" -"@typescript-eslint/parser@^7": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.18.0.tgz#83928d0f1b7f4afa974098c64b5ce6f9051f96a0" - integrity sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg== +"@typescript-eslint/parser@^8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.15.0.tgz#92610da2b3af702cfbc02a46e2a2daa6260a9045" + integrity sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A== dependencies: - "@typescript-eslint/scope-manager" "7.18.0" - "@typescript-eslint/types" "7.18.0" - "@typescript-eslint/typescript-estree" "7.18.0" - "@typescript-eslint/visitor-keys" "7.18.0" + "@typescript-eslint/scope-manager" "8.15.0" + "@typescript-eslint/types" "8.15.0" + "@typescript-eslint/typescript-estree" "8.15.0" + "@typescript-eslint/visitor-keys" "8.15.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz#c928e7a9fc2c0b3ed92ab3112c614d6bd9951c83" - integrity sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA== +"@typescript-eslint/scope-manager@8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.15.0.tgz#28a1a0f13038f382424f45a988961acaca38f7c6" + integrity sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA== dependencies: - "@typescript-eslint/types" "7.18.0" - "@typescript-eslint/visitor-keys" "7.18.0" + "@typescript-eslint/types" "8.15.0" + "@typescript-eslint/visitor-keys" "8.15.0" -"@typescript-eslint/type-utils@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz#2165ffaee00b1fbbdd2d40aa85232dab6998f53b" - integrity sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA== +"@typescript-eslint/type-utils@8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.15.0.tgz#a6da0f93aef879a68cc66c73fe42256cb7426c72" + integrity sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw== dependencies: - "@typescript-eslint/typescript-estree" "7.18.0" - "@typescript-eslint/utils" "7.18.0" + "@typescript-eslint/typescript-estree" "8.15.0" + "@typescript-eslint/utils" "8.15.0" debug "^4.3.4" ts-api-utils "^1.3.0" -"@typescript-eslint/types@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.18.0.tgz#b90a57ccdea71797ffffa0321e744f379ec838c9" - integrity sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ== +"@typescript-eslint/types@8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.15.0.tgz#4958edf3d83e97f77005f794452e595aaf6430fc" + integrity sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ== -"@typescript-eslint/typescript-estree@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz#b5868d486c51ce8f312309ba79bdb9f331b37931" - integrity sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA== +"@typescript-eslint/typescript-estree@8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.15.0.tgz#915c94e387892b114a2a2cc0df2d7f19412c8ba7" + integrity sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg== dependencies: - "@typescript-eslint/types" "7.18.0" - "@typescript-eslint/visitor-keys" "7.18.0" + "@typescript-eslint/types" "8.15.0" + "@typescript-eslint/visitor-keys" "8.15.0" debug "^4.3.4" - globby "^11.1.0" + fast-glob "^3.3.2" is-glob "^4.0.3" minimatch "^9.0.4" semver "^7.6.0" ts-api-utils "^1.3.0" -"@typescript-eslint/utils@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.18.0.tgz#bca01cde77f95fc6a8d5b0dbcbfb3d6ca4be451f" - integrity sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw== +"@typescript-eslint/utils@8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.15.0.tgz#ac04679ad19252776b38b81954b8e5a65567cef6" + integrity sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ== dependencies: "@eslint-community/eslint-utils" "^4.4.0" - "@typescript-eslint/scope-manager" "7.18.0" - "@typescript-eslint/types" "7.18.0" - "@typescript-eslint/typescript-estree" "7.18.0" + "@typescript-eslint/scope-manager" "8.15.0" + "@typescript-eslint/types" "8.15.0" + "@typescript-eslint/typescript-estree" "8.15.0" -"@typescript-eslint/visitor-keys@7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz#0564629b6124d67607378d0f0332a0495b25e7d7" - integrity sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg== +"@typescript-eslint/visitor-keys@8.15.0": + version "8.15.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.15.0.tgz#9ea5a85eb25401d2aa74ec8a478af4e97899ea12" + integrity sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q== dependencies: - "@typescript-eslint/types" "7.18.0" - eslint-visitor-keys "^3.4.3" - -"@ungap/structured-clone@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" - integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + "@typescript-eslint/types" "8.15.0" + eslint-visitor-keys "^4.2.0" "@vitejs/plugin-react@^4.3": version "4.3.1" @@ -1097,11 +1142,16 @@ acorn-walk@^8.0.0: dependencies: acorn "^8.11.0" -acorn@^8.0.4, acorn@^8.11.0, acorn@^8.9.0: +acorn@^8.0.4, acorn@^8.11.0: version "8.12.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== +acorn@^8.14.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -1156,11 +1206,6 @@ array-includes@^3.1.6, array-includes@^3.1.8: get-intrinsic "^1.2.4" is-string "^1.0.7" -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - array.prototype.findlast@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" @@ -1472,10 +1517,10 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" -cross-spawn@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== +cross-spawn@^7.0.5: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" @@ -1597,13 +1642,6 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - dom-helpers@^5.0.1: version "5.2.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" @@ -1705,10 +1743,10 @@ es-errors@^1.2.1, es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-iterator-helpers@^1.0.19: - version "1.0.19" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz#117003d0e5fec237b4b5c08aded722e0c6d50ca8" - integrity sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw== +es-iterator-helpers@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz#2f1a3ab998b30cb2d10b195b587c6d9ebdebf152" + integrity sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q== dependencies: call-bind "^1.0.7" define-properties "^1.2.1" @@ -1717,12 +1755,13 @@ es-iterator-helpers@^1.0.19: es-set-tostringtag "^2.0.3" function-bind "^1.1.2" get-intrinsic "^1.2.4" - globalthis "^1.0.3" + globalthis "^1.0.4" + gopd "^1.0.1" has-property-descriptors "^1.0.2" has-proto "^1.0.3" has-symbols "^1.0.3" internal-slot "^1.0.7" - iterator.prototype "^1.1.2" + iterator.prototype "^1.1.3" safe-array-concat "^1.1.2" es-object-atoms@^1.0.0: @@ -1801,27 +1840,27 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-plugin-react-hooks@^4.6: - version "4.6.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596" - integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== +eslint-plugin-react-hooks@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz#72e2eefbac4b694f5324154619fee44f5f60f101" + integrity sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw== -eslint-plugin-react-refresh@^0.4.7: - version "0.4.11" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.11.tgz#e450761a2bdb260aa10cfb73f846209a737827cb" - integrity sha512-wrAKxMbVr8qhXTtIKfXqAn5SAtRZt0aXxe5P23Fh4pUAdC6XEsybGLB8P0PI4j1yYqOgUEUlzKAGDfo7rJOjcw== +eslint-plugin-react-refresh@^0.4.14: + version "0.4.14" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.14.tgz#e3c611ead69bbf7436d01295c853d4abb8c59f68" + integrity sha512-aXvzCTK7ZBv1e7fahFuR3Z/fyQQSIQ711yPgYRj+Oj64tyTgO4iQIDmYXDBqvSWQ/FA4OSCsXOStlF+noU0/NA== -eslint-plugin-react@^7.34: - version "7.35.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.35.2.tgz#d32500d3ec268656d5071918bfec78cfd8b070ed" - integrity sha512-Rbj2R9zwP2GYNcIak4xoAMV57hrBh3hTaR0k7hVjwCQgryE/pw5px4b13EYjduOI0hfXyZhwBxaGpOTbWSGzKQ== +eslint-plugin-react@^7.37.2: + version "7.37.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz#cd0935987876ba2900df2f58339f6d92305acc7a" + integrity sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w== dependencies: array-includes "^3.1.8" array.prototype.findlast "^1.2.5" array.prototype.flatmap "^1.3.2" array.prototype.tosorted "^1.1.4" doctrine "^2.1.0" - es-iterator-helpers "^1.0.19" + es-iterator-helpers "^1.1.0" estraverse "^5.3.0" hasown "^2.0.2" jsx-ast-utils "^2.4.1 || ^3.0.0" @@ -1835,73 +1874,74 @@ eslint-plugin-react@^7.34: string.prototype.matchall "^4.0.11" string.prototype.repeat "^1.0.0" -eslint-scope@^7.2.2: - version "7.2.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" - integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.3.0: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8: - version "8.57.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" - integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^9.15.0: + version "9.15.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.15.0.tgz#77c684a4e980e82135ebff8ee8f0a9106ce6b8a6" + integrity sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.4" - "@eslint/js" "8.57.0" - "@humanwhocodes/config-array" "^0.11.14" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.19.0" + "@eslint/core" "^0.9.0" + "@eslint/eslintrc" "^3.2.0" + "@eslint/js" "9.15.0" + "@eslint/plugin-kit" "^0.2.3" + "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - "@ungap/structured-clone" "^1.2.0" + "@humanwhocodes/retry" "^0.4.1" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" - cross-spawn "^7.0.2" + cross-spawn "^7.0.5" debug "^4.3.2" - doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.2.2" - eslint-visitor-keys "^3.4.3" - espree "^9.6.1" - esquery "^1.4.2" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + esquery "^1.5.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" + file-entry-cache "^8.0.0" find-up "^5.0.0" glob-parent "^6.0.2" - globals "^13.19.0" - graphemer "^1.4.0" ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" -espree@^9.6.0, espree@^9.6.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" - integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== +espree@^10.0.1, espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== dependencies: - acorn "^8.9.0" + acorn "^8.14.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.1" + eslint-visitor-keys "^4.2.0" -esquery@^1.4.2: +esquery@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== @@ -1937,7 +1977,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.9, fast-glob@^3.3.0: +fast-glob@^3.3.0, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -1977,12 +2017,12 @@ fastq@^1.6.0: node-fetch "^2.6.1" regenerator-runtime "^0.13.7" -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== dependencies: - flat-cache "^3.0.4" + flat-cache "^4.0.0" file-selector@^0.6.0: version "0.6.0" @@ -2020,14 +2060,13 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -flat-cache@^3.0.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" - integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== dependencies: flatted "^3.2.9" - keyv "^4.5.3" - rimraf "^3.0.2" + keyv "^4.5.4" flatted@^3.2.9: version "3.3.1" @@ -2069,11 +2108,6 @@ formik@^2.4.6: tiny-warning "^1.0.2" tslib "^2.0.0" -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" @@ -2160,31 +2194,17 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^7.1.3: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.19.0: - version "13.24.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" - integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== - dependencies: - type-fest "^0.20.2" +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== -globalthis@^1.0.3: +globalthis@^1.0.3, globalthis@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== @@ -2192,18 +2212,6 @@ globalthis@^1.0.3: define-properties "^1.2.1" gopd "^1.0.1" -globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - globby@^13.1.2: version "13.2.2" resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" @@ -2367,15 +2375,7 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.3, inherits@~2.0.3: +inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2513,11 +2513,6 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - is-plain-obj@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" @@ -2609,10 +2604,10 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -iterator.prototype@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" - integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== +iterator.prototype@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.3.tgz#016c2abe0be3bbdb8319852884f60908ac62bf9c" + integrity sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ== dependencies: define-properties "^1.2.1" get-intrinsic "^1.2.1" @@ -2697,7 +2692,7 @@ jszip@^3.10.1: readable-stream "~2.3.6" setimmediate "^1.0.5" -keyv@^4.5.3: +keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== @@ -2856,7 +2851,7 @@ mimic-fn@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74" integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ== -minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -3014,13 +3009,6 @@ object.values@^1.1.6, object.values@^1.2.0: define-properties "^1.2.1" es-object-atoms "^1.0.0" -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" @@ -3096,11 +3084,6 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -3425,13 +3408,6 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - rollup@^4.20.0: version "4.21.2" resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.21.2.tgz#f41f277a448d6264e923dd1ea179f0a926aaf9b7" @@ -3597,11 +3573,6 @@ sirv@^2.0.3: mrmime "^2.0.0" totalist "^3.0.0" -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - slash@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" @@ -3784,11 +3755,6 @@ synckit@0.9.2: "@pkgr/core" "^0.1.0" tslib "^2.6.2" -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - tiny-case@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-case/-/tiny-case-1.0.3.tgz#d980d66bc72b5d5a9ca86fb7c9ffdb9c898ddd03" @@ -3863,11 +3829,6 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - type-fest@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" @@ -4091,11 +4052,6 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - ws@^7.3.1: version "7.5.10" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" From a2d022f7517ec220437a1ee5b27f40b670b156f9 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 19 Nov 2024 17:53:23 +0530 Subject: [PATCH 043/177] [mob] UI Changes --- mobile/lib/generated/intl/messages_en.dart | 2 ++ mobile/lib/generated/l10n.dart | 11 ++++++++++ mobile/lib/l10n/intl_en.arb | 4 ++++ .../gallery/hierarchical_search_gallery.dart | 4 ++-- mobile/lib/ui/viewer/people/cluster_page.dart | 4 ++-- .../lib/ui/viewer/people/people_banner.dart | 21 ++++++++++++------- 6 files changed, 34 insertions(+), 12 deletions(-) diff --git a/mobile/lib/generated/intl/messages_en.dart b/mobile/lib/generated/intl/messages_en.dart index d9e5696eb0..370a855bdb 100644 --- a/mobile/lib/generated/intl/messages_en.dart +++ b/mobile/lib/generated/intl/messages_en.dart @@ -825,6 +825,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Files saved to gallery"), "findPeopleByName": MessageLookupByLibrary.simpleMessage("Find people quickly by name"), + "findThemQuickly": + MessageLookupByLibrary.simpleMessage("Find them quickly"), "flip": MessageLookupByLibrary.simpleMessage("Flip"), "forYourMemories": MessageLookupByLibrary.simpleMessage("for your memories"), diff --git a/mobile/lib/generated/l10n.dart b/mobile/lib/generated/l10n.dart index d1ae7f88cf..99d63d56bf 100644 --- a/mobile/lib/generated/l10n.dart +++ b/mobile/lib/generated/l10n.dart @@ -8849,6 +8849,17 @@ class S { ); } + /// `Find them quickly` + String get findThemQuickly { + return Intl.message( + 'Find them quickly', + name: 'findThemQuickly', + desc: + 'Subtitle to indicate that the user can find people quickly by name', + args: [], + ); + } + /// `Find people quickly by name` String get findPeopleByName { return Intl.message( diff --git a/mobile/lib/l10n/intl_en.arb b/mobile/lib/l10n/intl_en.arb index 8c6edcdb7f..4c1cc42249 100644 --- a/mobile/lib/l10n/intl_en.arb +++ b/mobile/lib/l10n/intl_en.arb @@ -1246,6 +1246,10 @@ "locations": "Locations", "descriptions": "Descriptions", "addAName": "Add a name", + "findThemQuickly": "Find them quickly", + "@findThemQuickly": { + "description": "Subtitle to indicate that the user can find people quickly by name" + }, "findPeopleByName": "Find people quickly by name", "addViewers": "{count, plural, zero {Add viewer} one {Add viewer} other {Add viewers}}", "addCollaborators": "{count, plural, zero {Add collaborator} one {Add collaborator} other {Add collaborators}}", diff --git a/mobile/lib/ui/viewer/gallery/hierarchical_search_gallery.dart b/mobile/lib/ui/viewer/gallery/hierarchical_search_gallery.dart index 234848549b..4f88ac0256 100644 --- a/mobile/lib/ui/viewer/gallery/hierarchical_search_gallery.dart +++ b/mobile/lib/ui/viewer/gallery/hierarchical_search_gallery.dart @@ -177,8 +177,8 @@ class _HierarchicalSearchGalleryState extends State { thumbnailFallback: false, ), actionIcon: Icons.add_outlined, - text: S.of(context).addAName, - subText: S.of(context).findPeopleByName, + text: S.of(context).savePerson, + subText: S.of(context).findThemQuickly, onTap: () async { final result = await showAssignPersonAction( context, diff --git a/mobile/lib/ui/viewer/people/cluster_page.dart b/mobile/lib/ui/viewer/people/cluster_page.dart index 1565482b33..625c3b40a3 100644 --- a/mobile/lib/ui/viewer/people/cluster_page.dart +++ b/mobile/lib/ui/viewer/people/cluster_page.dart @@ -141,8 +141,8 @@ class _ClusterPageState extends State { clusterID: widget.clusterID, ), actionIcon: Icons.add_outlined, - text: S.of(context).addAName, - subText: S.of(context).findPeopleByName, + text: S.of(context).savePerson, + subText: S.of(context).findThemQuickly, onTap: () async { if (widget.personID == null) { final result = await showAssignPersonAction( diff --git a/mobile/lib/ui/viewer/people/people_banner.dart b/mobile/lib/ui/viewer/people/people_banner.dart index 033a35eb55..8380853739 100644 --- a/mobile/lib/ui/viewer/people/people_banner.dart +++ b/mobile/lib/ui/viewer/people/people_banner.dart @@ -17,7 +17,7 @@ class PeopleBanner extends StatelessWidget { final String? subText; final GestureTapCallback onTap; -const PeopleBanner({ + const PeopleBanner({ super.key, required this.type, this.startIcon, @@ -54,14 +54,19 @@ const PeopleBanner({ case PeopleBannerType.addName: assert(faceWidget != null); backgroundColor = colorScheme.backgroundElevated; - startWidget = SizedBox( - width: 56, - height: 56, - child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(4), + startWidget = Padding( + padding: const EdgeInsets.all(4.0), + child: SizedBox( + width: 56, + height: 56, + child: ClipPath( + clipper: ShapeBorderClipper( + shape: ContinuousRectangleBorder( + borderRadius: BorderRadius.circular(40), + ), + ), + child: faceWidget!, ), - child: faceWidget!, ), ); roundedActionIcon = false; From 572f2aff88b90050e350420b6a2dad3efd900300 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 19 Nov 2024 17:55:41 +0530 Subject: [PATCH 044/177] [mob] Hide merge section if no suggestion --- mobile/lib/ui/viewer/people/save_person.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mobile/lib/ui/viewer/people/save_person.dart b/mobile/lib/ui/viewer/people/save_person.dart index 25bc953104..ba760781de 100644 --- a/mobile/lib/ui/viewer/people/save_person.dart +++ b/mobile/lib/ui/viewer/people/save_person.dart @@ -73,8 +73,10 @@ class _SavePersonState extends State { ), ), child: widget.file != null - ? PersonFaceWidget(widget.file!, - clusterID: widget.clusterID) + ? PersonFaceWidget( + widget.file!, + clusterID: widget.clusterID, + ) : const NoThumbnailWidget( addBorder: false, ), @@ -169,6 +171,9 @@ class _SavePersonState extends State { searchResults.sort( (a, b) => a.$1.data.name.compareTo(b.$1.data.name), ); + if (searchResults.isEmpty) { + return const SizedBox.shrink(); + } return Column( crossAxisAlignment: CrossAxisAlignment.start, From dbeb95d438028683922592c65b61c920d80b541b Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 17:57:59 +0530 Subject: [PATCH 045/177] wip checkpoint --- web/packages/build-config/eslint.config.mjs | 1 - web/packages/build-config/eslintrc-base.js | 58 ------------------ web/packages/build-config/eslintrc-base.mjs | 67 +++++++++++++++++++++ web/packages/build-config/package.json | 5 +- web/packages/build-config/tsconfig.json | 5 +- web/yarn.lock | 39 +++++++----- 6 files changed, 97 insertions(+), 78 deletions(-) delete mode 100644 web/packages/build-config/eslintrc-base.js create mode 100644 web/packages/build-config/eslintrc-base.mjs diff --git a/web/packages/build-config/eslint.config.mjs b/web/packages/build-config/eslint.config.mjs index 69691eface..879df38ad2 100644 --- a/web/packages/build-config/eslint.config.mjs +++ b/web/packages/build-config/eslint.config.mjs @@ -1,6 +1,5 @@ import js from "@eslint/js"; - export default [ js.configs.recommended, { diff --git a/web/packages/build-config/eslintrc-base.js b/web/packages/build-config/eslintrc-base.js deleted file mode 100644 index e5d43938b0..0000000000 --- a/web/packages/build-config/eslintrc-base.js +++ /dev/null @@ -1,58 +0,0 @@ -/* eslint-env node */ -module.exports = { - root: true, - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/strict-type-checked", - "plugin:@typescript-eslint/stylistic-type-checked", - ], - plugins: ["@typescript-eslint"], - parserOptions: { project: true }, - parser: "@typescript-eslint/parser", - ignorePatterns: [".eslintrc.js"], - rules: { - /* Allow numbers to be used in template literals */ - "@typescript-eslint/restrict-template-expressions": [ - "error", - { - allowNumber: true, - }, - ], - /* Allow void expressions as the entire body of an arrow function */ - "@typescript-eslint/no-confusing-void-expression": [ - "error", - { - ignoreArrowShorthand: true, - }, - ], - /* - Allow async functions to be passed as JSX attributes expected to be - functions that return void (typically onFoo event handlers). - - This should be safe since we have registered global unhandled Promise - handlers. - */ - "@typescript-eslint/no-misused-promises": [ - "error", - { - checksVoidReturn: { - arguments: false, - attributes: false, - }, - }, - ], - /* Allow force unwrapping potentially optional values. - - It is best if these can be avoided by restructuring the code, but - there do arise legitimate scenarios where we know from code logic - that the value should be present. Of course, the surrounding code - might change causing that expectation to be falsified, but in certain - cases there isn't much we can do other than throwing an exception. - - Instead of rolling our own such exception (which we in fact used to - do at one point), rely on the JS's native undefined property access - exception since that conveys more information in the logs. - */ - "@typescript-eslint/no-non-null-assertion": "off", - }, -}; diff --git a/web/packages/build-config/eslintrc-base.mjs b/web/packages/build-config/eslintrc-base.mjs new file mode 100644 index 0000000000..e8d86f0b17 --- /dev/null +++ b/web/packages/build-config/eslintrc-base.mjs @@ -0,0 +1,67 @@ +// @ts-check + +import eslint from "@eslint/js"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + eslint.configs.recommended, + tseslint.configs.recommended, +); +// /* eslint-env node */ +// module.exports = { +// root: true, +// extends: [ +// "eslint:recommended", +// "plugin:@typescript-eslint/strict-type-checked", +// "plugin:@typescript-eslint/stylistic-type-checked", +// ], +// plugins: ["@typescript-eslint"], +// parserOptions: { project: true }, +// parser: "@typescript-eslint/parser", +// ignorePatterns: [".eslintrc.js"], +// rules: { +// /* Allow numbers to be used in template literals */ +// "@typescript-eslint/restrict-template-expressions": [ +// "error", +// { +// allowNumber: true, +// }, +// ], +// /* Allow void expressions as the entire body of an arrow function */ +// "@typescript-eslint/no-confusing-void-expression": [ +// "error", +// { +// ignoreArrowShorthand: true, +// }, +// ], +// /* +// Allow async functions to be passed as JSX attributes expected to be +// functions that return void (typically onFoo event handlers). + +// This should be safe since we have registered global unhandled Promise +// handlers. +// */ +// "@typescript-eslint/no-misused-promises": [ +// "error", +// { +// checksVoidReturn: { +// arguments: false, +// attributes: false, +// }, +// }, +// ], +// /* Allow force unwrapping potentially optional values. + +// It is best if these can be avoided by restructuring the code, but +// there do arise legitimate scenarios where we know from code logic +// that the value should be present. Of course, the surrounding code +// might change causing that expectation to be falsified, but in certain +// cases there isn't much we can do other than throwing an exception. + +// Instead of rolling our own such exception (which we in fact used to +// do at one point), rely on the JS's native undefined property access +// exception since that conveys more information in the logs. +// */ +// "@typescript-eslint/no-non-null-assertion": "off", +// }, +// }; diff --git a/web/packages/build-config/package.json b/web/packages/build-config/package.json index 11338df6b9..c01ed90fb5 100644 --- a/web/packages/build-config/package.json +++ b/web/packages/build-config/package.json @@ -4,12 +4,11 @@ "private": true, "devDependencies": { "@eslint/js": "^9.15.0", - "@typescript-eslint/eslint-plugin": "^8.15.0", - "@typescript-eslint/parser": "^8.15.0", "eslint-plugin-react": "^7.37.2", "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.14", "prettier-plugin-organize-imports": "^4.1.0", - "prettier-plugin-packagejson": "^2.5.3" + "prettier-plugin-packagejson": "^2.5.3", + "typescript-eslint": "^8.15.0" } } diff --git a/web/packages/build-config/tsconfig.json b/web/packages/build-config/tsconfig.json index 6fb762d4f9..2f2ab5c97f 100644 --- a/web/packages/build-config/tsconfig.json +++ b/web/packages/build-config/tsconfig.json @@ -3,7 +3,8 @@ itself */ "compilerOptions": { "noEmit": true, - "checkJs": true + "checkJs": true, + "esModuleInterop": true }, - "include": ["*.js"] + "include": ["*.js", "eslintrc-base.mjs"] } diff --git a/web/yarn.lock b/web/yarn.lock index b92166b8b3..0fd4d9642a 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -217,7 +217,7 @@ source-map "^0.5.7" stylis "4.2.0" -"@emotion/cache@11.13.1", "@emotion/cache@^11.11.0", "@emotion/cache@^11.13.0", "@emotion/cache@^11.4.0": +"@emotion/cache@^11.11.0", "@emotion/cache@^11.13.0", "@emotion/cache@^11.4.0": version "11.13.1" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.13.1.tgz#fecfc54d51810beebf05bf2a161271a1a91895d7" integrity sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw== @@ -245,7 +245,7 @@ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== -"@emotion/react@11.13.3", "@emotion/react@^11.13.3", "@emotion/react@^11.8.1": +"@emotion/react@11.13.3", "@emotion/react@^11.8.1": version "11.13.3" resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.13.3.tgz#a69d0de2a23f5b48e0acf210416638010e4bd2e4" integrity sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg== @@ -422,19 +422,21 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== -"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": +"@eslint-community/eslint-utils@^4.2.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.10.0": - version "4.11.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" - integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== +"@eslint-community/eslint-utils@^4.4.0": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" + integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== + dependencies: + eslint-visitor-keys "^3.4.3" -"@eslint-community/regexpp@^4.12.1": +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": version "4.12.1" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== @@ -1033,7 +1035,7 @@ resolved "https://registry.yarnpkg.com/@types/zxcvbn/-/zxcvbn-4.4.5.tgz#8ce8623ed7a36e3a76d1c0b539708dfb2e859bc0" integrity sha512-FZJgC5Bxuqg7Rhsm/bx6gAruHHhDQ55r+s0JhDh8CQ16fD7NsJJ+p8YMMQDhSQoIrSmjpqqYWA96oQVMNkjRyA== -"@typescript-eslint/eslint-plugin@^8.15.0": +"@typescript-eslint/eslint-plugin@8.15.0": version "8.15.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.15.0.tgz#c95c6521e70c8b095a684d884d96c0c1c63747d2" integrity sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg== @@ -1048,7 +1050,7 @@ natural-compare "^1.4.0" ts-api-utils "^1.3.0" -"@typescript-eslint/parser@^8.15.0": +"@typescript-eslint/parser@8.15.0": version "8.15.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.15.0.tgz#92610da2b3af702cfbc02a46e2a2daa6260a9045" integrity sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A== @@ -1882,7 +1884,7 @@ eslint-scope@^8.2.0: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0: +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== @@ -3813,9 +3815,9 @@ truncate-utf8-bytes@^1.0.0: utf8-byte-length "^1.0.1" ts-api-utils@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" - integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + version "1.4.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.0.tgz#709c6f2076e511a81557f3d07a0cbd566ae8195c" + integrity sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ== tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6.2: version "2.7.0" @@ -3878,6 +3880,15 @@ typed-array-length@^1.0.6: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" +typescript-eslint@^8.15.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.15.0.tgz#c8a2a0d183c3eb48ae176aa078c1b9daa584cf9d" + integrity sha512-wY4FRGl0ZI+ZU4Jo/yjdBu0lVTSML58pu6PgGtJmCufvzfV565pUF6iACQt092uFOd49iLOTX/sEVmHtbSrS+w== + dependencies: + "@typescript-eslint/eslint-plugin" "8.15.0" + "@typescript-eslint/parser" "8.15.0" + "@typescript-eslint/utils" "8.15.0" + typescript@^5.6.3: version "5.6.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" From 56b72bd55b79d3a8e063d9a8d7eaa99441d4871b Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 18:05:45 +0530 Subject: [PATCH 046/177] Chain --- web/apps/payments/.eslintrc.cjs | 3 -- web/apps/payments/eslint.config.mjs | 7 +++++ web/packages/build-config/eslintrc-react.js | 29 ----------------- web/packages/build-config/eslintrc-react.mjs | 33 ++++++++++++++++++++ web/packages/build-config/eslintrc-vite.js | 5 --- web/packages/build-config/eslintrc-vite.mjs | 9 ++++++ web/packages/build-config/tsconfig.json | 2 +- 7 files changed, 50 insertions(+), 38 deletions(-) delete mode 100644 web/apps/payments/.eslintrc.cjs create mode 100644 web/apps/payments/eslint.config.mjs delete mode 100644 web/packages/build-config/eslintrc-react.js create mode 100644 web/packages/build-config/eslintrc-react.mjs delete mode 100644 web/packages/build-config/eslintrc-vite.js create mode 100644 web/packages/build-config/eslintrc-vite.mjs diff --git a/web/apps/payments/.eslintrc.cjs b/web/apps/payments/.eslintrc.cjs deleted file mode 100644 index 99b4b9226c..0000000000 --- a/web/apps/payments/.eslintrc.cjs +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ["@/build-config/eslintrc-vite"], -}; diff --git a/web/apps/payments/eslint.config.mjs b/web/apps/payments/eslint.config.mjs new file mode 100644 index 0000000000..33ed94f25d --- /dev/null +++ b/web/apps/payments/eslint.config.mjs @@ -0,0 +1,7 @@ +// module.exports = { +// extends: ["@/build-config/eslintrc-vite"], +// }; + +import config from "@/build-config/eslintrc-vite.mjs"; + +export default config; diff --git a/web/packages/build-config/eslintrc-react.js b/web/packages/build-config/eslintrc-react.js deleted file mode 100644 index a275ad9f74..0000000000 --- a/web/packages/build-config/eslintrc-react.js +++ /dev/null @@ -1,29 +0,0 @@ -/* eslint-env node */ -module.exports = { - extends: [ - "./eslintrc-base.js", - "plugin:react/recommended", - "plugin:react-hooks/recommended", - ], - plugins: ["react-refresh"], - settings: { react: { version: "18.2" } }, - rules: { - /* The rule is misguided - only the opener should be omitted, not the - referrer. */ - "react/jsx-no-target-blank": ["warn", { allowReferrer: true }], - /* Otherwise we need to do unnecessary boilerplating when using memo. */ - "react/display-name": "off", - /* Apparently Fast refresh only works if a file only exports components, - and this rule warns about that. Constants are okay though (otherwise - we'll need to create unnecessary helper files). */ - "react-refresh/only-export-components": [ - "warn", - { allowConstantExport: true }, - ], - /* Next.js supports the JSX transform introduced in React 17 */ - "react/react-in-jsx-scope": "off", - /* Without React in scope, this rule starts causing false positives (We - don't use prop types in our own code anyways). */ - "react/prop-types": "off", - }, -}; diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs new file mode 100644 index 0000000000..270dafaf57 --- /dev/null +++ b/web/packages/build-config/eslintrc-react.mjs @@ -0,0 +1,33 @@ +import config from "./eslintrc-base.mjs"; + +export default config; + +// /* eslint-env node */ +// module.exports = { +// extends: [ +// "./eslintrc-base.js", +// "plugin:react/recommended", +// "plugin:react-hooks/recommended", +// ], +// plugins: ["react-refresh"], +// settings: { react: { version: "18.2" } }, +// rules: { +// /* The rule is misguided - only the opener should be omitted, not the +// referrer. */ +// "react/jsx-no-target-blank": ["warn", { allowReferrer: true }], +// /* Otherwise we need to do unnecessary boilerplating when using memo. */ +// "react/display-name": "off", +// /* Apparently Fast refresh only works if a file only exports components, +// and this rule warns about that. Constants are okay though (otherwise +// we'll need to create unnecessary helper files). */ +// "react-refresh/only-export-components": [ +// "warn", +// { allowConstantExport: true }, +// ], +// /* Next.js supports the JSX transform introduced in React 17 */ +// "react/react-in-jsx-scope": "off", +// /* Without React in scope, this rule starts causing false positives (We +// don't use prop types in our own code anyways). */ +// "react/prop-types": "off", +// }, +// }; diff --git a/web/packages/build-config/eslintrc-vite.js b/web/packages/build-config/eslintrc-vite.js deleted file mode 100644 index 37032546bd..0000000000 --- a/web/packages/build-config/eslintrc-vite.js +++ /dev/null @@ -1,5 +0,0 @@ -/* eslint-env node */ -module.exports = { - extends: ["./eslintrc-react.js", "plugin:react/jsx-runtime"], - ignorePatterns: [".eslintrc.cjs", "vite.config.ts", "dist"], -}; diff --git a/web/packages/build-config/eslintrc-vite.mjs b/web/packages/build-config/eslintrc-vite.mjs new file mode 100644 index 0000000000..73e64c1fcd --- /dev/null +++ b/web/packages/build-config/eslintrc-vite.mjs @@ -0,0 +1,9 @@ +import config from "./eslintrc-react.mjs"; + +export default config; + +// /* eslint-env node */ +// module.exports = { +// extends: ["./eslintrc-react.js", "plugin:react/jsx-runtime"], +// ignorePatterns: [".eslintrc.cjs", "vite.config.ts", "dist"], +// }; diff --git a/web/packages/build-config/tsconfig.json b/web/packages/build-config/tsconfig.json index 2f2ab5c97f..25e05fc8ad 100644 --- a/web/packages/build-config/tsconfig.json +++ b/web/packages/build-config/tsconfig.json @@ -6,5 +6,5 @@ "checkJs": true, "esModuleInterop": true }, - "include": ["*.js", "eslintrc-base.mjs"] + "include": ["*.js", "eslintrc-base.mjs", "eslintrc-react.mjs", "eslintrc-vite.mjs"] } From 2bdf85403b04c4e5167f53af03fc5b46cf6e901e Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 18:09:47 +0530 Subject: [PATCH 047/177] Direct --- web/apps/payments/eslint.config.mjs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/web/apps/payments/eslint.config.mjs b/web/apps/payments/eslint.config.mjs index 33ed94f25d..dda85e151c 100644 --- a/web/apps/payments/eslint.config.mjs +++ b/web/apps/payments/eslint.config.mjs @@ -1,7 +1 @@ -// module.exports = { -// extends: ["@/build-config/eslintrc-vite"], -// }; - -import config from "@/build-config/eslintrc-vite.mjs"; - -export default config; +export { default } from "@/build-config/eslintrc-vite.mjs"; From b0b527d2ea1c0b69dc0b238d086fbc75b1d03410 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 19 Nov 2024 18:12:58 +0530 Subject: [PATCH 048/177] [mob] Clean up --- .../people/add_person_action_sheet.dart | 317 ------------------ mobile/lib/ui/viewer/people/save_person.dart | 28 +- 2 files changed, 24 insertions(+), 321 deletions(-) diff --git a/mobile/lib/ui/viewer/people/add_person_action_sheet.dart b/mobile/lib/ui/viewer/people/add_person_action_sheet.dart index bfdc61cb9f..5fa1cf97ad 100644 --- a/mobile/lib/ui/viewer/people/add_person_action_sheet.dart +++ b/mobile/lib/ui/viewer/people/add_person_action_sheet.dart @@ -1,56 +1,14 @@ import "dart:async"; -import "dart:developer"; -import "dart:math" as math; -import "package:flutter/foundation.dart"; import 'package:flutter/material.dart'; -import "package:logging/logging.dart"; -import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; -import "package:photos/core/event_bus.dart"; -import "package:photos/db/ml/db.dart"; -import "package:photos/events/people_changed_event.dart"; -import "package:photos/generated/l10n.dart"; import "package:photos/models/file/file.dart"; -import "package:photos/models/ml/face/person.dart"; -import 'package:photos/services/machine_learning/face_ml/feedback/cluster_feedback.dart'; -import "package:photos/services/machine_learning/face_ml/person/person_service.dart"; -import "package:photos/services/search_service.dart"; -import 'package:photos/theme/colors.dart'; -import 'package:photos/theme/ente_theme.dart'; -import 'package:photos/ui/common/loading_widget.dart'; -import 'package:photos/ui/components/bottom_of_title_bar_widget.dart'; -import 'package:photos/ui/components/buttons/button_widget.dart'; -import 'package:photos/ui/components/models/button_type.dart'; -import "package:photos/ui/components/text_input_widget.dart"; -import 'package:photos/ui/components/title_bar_title_widget.dart'; -import "package:photos/ui/viewer/people/person_row_item.dart"; import "package:photos/ui/viewer/people/save_person.dart"; -import "package:photos/utils/dialog_util.dart"; import "package:photos/utils/navigation_util.dart"; -import "package:photos/utils/toast_util.dart"; - -enum PersonActionType { - assignPerson, -} - -String _actionName( - BuildContext context, - PersonActionType type, -) { - String text = ""; - switch (type) { - case PersonActionType.assignPerson: - text = S.of(context).addNameOrMerge; - break; - } - return text; -} Future showAssignPersonAction( BuildContext context, { required String clusterID, EnteFile? file, - PersonActionType actionType = PersonActionType.assignPerson, bool showOptionToAddNewPerson = true, }) async { return routeToPage( @@ -60,279 +18,4 @@ Future showAssignPersonAction( file: file, ), ); - // return showBarModalBottomSheet( - // context: context, - // builder: (context) { - // return PersonActionSheet( - // actionType: actionType, - // showOptionToCreateNewPerson: showOptionToAddNewPerson, - // cluserID: clusterID, - // ); - // }, - // shape: const RoundedRectangleBorder( - // side: BorderSide(width: 0), - // borderRadius: BorderRadius.vertical( - // top: Radius.circular(5), - // ), - // ), - // topControl: const SizedBox.shrink(), - // backgroundColor: getEnteColorScheme(context).backgroundElevated, - // barrierColor: backdropFaintDark, - // enableDrag: false, - // ); -} - -class PersonActionSheet extends StatefulWidget { - final PersonActionType actionType; - final String cluserID; - final bool showOptionToCreateNewPerson; - const PersonActionSheet({ - required this.actionType, - required this.cluserID, - required this.showOptionToCreateNewPerson, - super.key, - }); - - @override - State createState() => _PersonActionSheetState(); -} - -class _PersonActionSheetState extends State { - static const int cancelButtonSize = 80; - String _searchQuery = ""; - bool userAlreadyAssigned = false; - - @override - Widget build(BuildContext context) { - final bottomInset = MediaQuery.of(context).viewInsets.bottom; - final isKeyboardUp = bottomInset > 100; - return Padding( - padding: EdgeInsets.only( - bottom: isKeyboardUp ? bottomInset - cancelButtonSize : 0, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ConstrainedBox( - constraints: BoxConstraints( - maxWidth: math.min(428, MediaQuery.of(context).size.width), - ), - child: Padding( - padding: const EdgeInsets.fromLTRB(0, 32, 0, 8), - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: Column( - children: [ - BottomOfTitleBarWidget( - title: TitleBarTitleWidget( - title: _actionName(context, widget.actionType), - ), - // caption: 'Select or create a ', - ), - Padding( - padding: const EdgeInsets.only( - top: 16, - left: 16, - right: 16, - ), - child: TextInputWidget( - hintText: S.of(context).personName, - prefixIcon: Icons.search_rounded, - onChange: (value) { - setState(() { - _searchQuery = value; - }); - }, - isClearable: true, - shouldUnfocusOnClearOrSubmit: true, - borderRadius: 2, - ), - ), - _getPersonItems(), - ], - ), - ), - SafeArea( - child: Container( - //inner stroke of 1pt + 15 pts of top padding = 16 pts - padding: const EdgeInsets.fromLTRB(16, 15, 16, 8), - decoration: BoxDecoration( - border: Border( - top: BorderSide( - color: getEnteColorScheme(context).strokeFaint, - ), - ), - ), - child: ButtonWidget( - buttonType: ButtonType.secondary, - buttonAction: ButtonAction.cancel, - isInAlert: true, - labelText: S.of(context).cancel, - ), - ), - ), - ], - ), - ), - ), - ], - ), - ); - } - - Flexible _getPersonItems() { - return Flexible( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 16), - child: FutureBuilder>( - future: _getPersonsWithRecentFile(), - builder: (context, snapshot) { - if (snapshot.hasError) { - log("Error: ${snapshot.error} ${snapshot.stackTrace}}"); - //Need to show an error on the UI here - if (kDebugMode) { - return Column( - children: [ - Text('${snapshot.error}'), - Text('${snapshot.stackTrace}'), - ], - ); - } else { - return const SizedBox.shrink(); - } - } else if (snapshot.hasData) { - final persons = snapshot.data!; - final searchResults = _searchQuery.isNotEmpty - ? persons - .where( - (element) => element.$1.data.name - .toLowerCase() - .contains(_searchQuery), - ) - .toList() - : persons; - // sort searchResults alphabetically by name - searchResults.sort( - (a, b) => a.$1.data.name.compareTo(b.$1.data.name), - ); - - return Scrollbar( - thumbVisibility: true, - radius: const Radius.circular(2), - child: Padding( - padding: const EdgeInsets.only(right: 12), - child: ListView.separated( - itemCount: searchResults.length + 1, - itemBuilder: (context, index) { - final person = searchResults[index - 1]; - - return PersonRowItem( - person: person.$1, - personFile: person.$2, - onTap: () async { - if (userAlreadyAssigned) { - return; - } - userAlreadyAssigned = true; - await MLDataDB.instance.assignClusterToPerson( - personID: person.$1.remoteID, - clusterID: widget.cluserID, - ); - Bus.instance.fire(PeopleChangedEvent()); - - Navigator.pop(context, person); - }, - ); - }, - separatorBuilder: (context, index) { - return const SizedBox(height: 6); - }, - ), - ), - ); - } else { - return const EnteLoadingWidget(); - } - }, - ), - ), - ); - } - - Future addNewPerson( - BuildContext context, { - String initValue = '', - required String clusterID, - }) async { - PersonEntity? personEntity; - final result = await showTextInputDialog( - context, - title: S.of(context).newPerson, - submitButtonLabel: S.of(context).add, - hintText: S.of(context).addName, - alwaysShowSuccessState: false, - initialValue: initValue, - textCapitalization: TextCapitalization.words, - onSubmit: (String text) async { - if (userAlreadyAssigned) { - return; - } - // indicates user cancelled the rename request - if (text.trim() == "") { - return; - } - try { - userAlreadyAssigned = true; - personEntity = - await PersonService.instance.addPerson(text, clusterID); - final bool extraPhotosFound = - await ClusterFeedbackService.instance.checkAndDoAutomaticMerges( - personEntity!, - personClusterID: clusterID, - ); - if (extraPhotosFound) { - showShortToast(context, S.of(context).extraPhotosFound); - } - } catch (e, s) { - Logger("_PersonActionSheetState") - .severe("Failed to add person", e, s); - rethrow; - } - }, - ); - if (result is Exception) { - await showGenericErrorDialog(context: context, error: result); - } - if (personEntity != null) { - Bus.instance.fire(PeopleChangedEvent()); - Navigator.pop(context, personEntity); - } - } - - Future> _getPersonsWithRecentFile({ - bool excludeHidden = true, - }) async { - final persons = await PersonService.instance.getPersons(); - if (excludeHidden) { - persons.removeWhere((person) => person.data.isIgnored); - } - final List<(PersonEntity, EnteFile)> personAndFileID = []; - for (final person in persons) { - final clustersToFiles = - await SearchService.instance.getClusterFilesForPersonID( - person.remoteID, - ); - final files = clustersToFiles.values.expand((e) => e).toList(); - if (files.isEmpty) { - debugPrint( - "Person ${kDebugMode ? person.data.name : person.remoteID} has no files", - ); - continue; - } - personAndFileID.add((person, files.first)); - } - return personAndFileID; - } } diff --git a/mobile/lib/ui/viewer/people/save_person.dart b/mobile/lib/ui/viewer/people/save_person.dart index ba760781de..f222145143 100644 --- a/mobile/lib/ui/viewer/people/save_person.dart +++ b/mobile/lib/ui/viewer/people/save_person.dart @@ -1,4 +1,5 @@ import "dart:developer"; +import 'dart:async'; import "package:flutter/foundation.dart"; import "package:flutter/material.dart"; @@ -45,6 +46,14 @@ class _SavePersonState extends State { String _inputName = ""; bool userAlreadyAssigned = false; late final Logger _logger = Logger("_SavePersonState"); + Timer? _debounce; + List<(PersonEntity, EnteFile)> _cachedPersons = []; + + @override + void dispose() { + _debounce?.cancel(); + super.dispose(); + } @override Widget build(BuildContext context) { @@ -85,8 +94,11 @@ class _SavePersonState extends State { const SizedBox(height: 36), TextFormField( onChanged: (value) { - setState(() { - _inputName = value; + if (_debounce?.isActive ?? false) _debounce?.cancel(); + _debounce = Timer(const Duration(milliseconds: 300), () { + setState(() { + _inputName = value; + }); }); }, decoration: InputDecoration( @@ -141,8 +153,8 @@ class _SavePersonState extends State { Widget _getPersonItems() { return Padding( padding: const EdgeInsets.fromLTRB(0, 12, 4, 0), - child: FutureBuilder>( - future: _getPersonsWithRecentFile(), + child: StreamBuilder>( + stream: _getPersonsWithRecentFileStream(), builder: (context, snapshot) { if (snapshot.hasError) { log("Error: ${snapshot.error} ${snapshot.stackTrace}}"); @@ -232,6 +244,14 @@ class _SavePersonState extends State { ); } + Stream> + _getPersonsWithRecentFileStream() async* { + if (_cachedPersons.isEmpty) { + _cachedPersons = await _getPersonsWithRecentFile(); + } + yield _cachedPersons; + } + Future addNewPerson( BuildContext context, { String text = '', From da71a34f75a7fdf2c0fb5aaf6640631728f9414b Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 18:15:51 +0530 Subject: [PATCH 049/177] TC --- web/packages/build-config/eslintrc-base.mjs | 10 +++++++++- web/packages/build-config/package.json | 1 + web/packages/build-config/tsconfig.json | 4 +++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/web/packages/build-config/eslintrc-base.mjs b/web/packages/build-config/eslintrc-base.mjs index e8d86f0b17..aff809e3de 100644 --- a/web/packages/build-config/eslintrc-base.mjs +++ b/web/packages/build-config/eslintrc-base.mjs @@ -5,7 +5,15 @@ import tseslint from "typescript-eslint"; export default tseslint.config( eslint.configs.recommended, - tseslint.configs.recommended, + tseslint.configs.recommendedTypeChecked, + { + languageOptions: { + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, ); // /* eslint-env node */ // module.exports = { diff --git a/web/packages/build-config/package.json b/web/packages/build-config/package.json index c01ed90fb5..2580f855be 100644 --- a/web/packages/build-config/package.json +++ b/web/packages/build-config/package.json @@ -2,6 +2,7 @@ "name": "@/build-config", "version": "0.0.0", "private": true, + "type": "module", "devDependencies": { "@eslint/js": "^9.15.0", "eslint-plugin-react": "^7.37.2", diff --git a/web/packages/build-config/tsconfig.json b/web/packages/build-config/tsconfig.json index 25e05fc8ad..395176496b 100644 --- a/web/packages/build-config/tsconfig.json +++ b/web/packages/build-config/tsconfig.json @@ -2,9 +2,11 @@ /* A minimal tsconfig so that we can run tsc on the build-config package itself */ "compilerOptions": { + "module": "ESNext", + "moduleResolution": "bundler", "noEmit": true, "checkJs": true, "esModuleInterop": true }, - "include": ["*.js", "eslintrc-base.mjs", "eslintrc-react.mjs", "eslintrc-vite.mjs"] + "include": ["*.js", "eslintrc-*.mjs"] } From 41cdb73382da3ad5e8658f41c60c9ee3762b395c Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 18:22:23 +0530 Subject: [PATCH 050/177] Upd --- web/packages/build-config/eslintrc-base.mjs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/packages/build-config/eslintrc-base.mjs b/web/packages/build-config/eslintrc-base.mjs index aff809e3de..6da6e46511 100644 --- a/web/packages/build-config/eslintrc-base.mjs +++ b/web/packages/build-config/eslintrc-base.mjs @@ -5,6 +5,7 @@ import tseslint from "typescript-eslint"; export default tseslint.config( eslint.configs.recommended, + tseslint.configs.strictTypeChecked, tseslint.configs.recommendedTypeChecked, { languageOptions: { @@ -14,6 +15,9 @@ export default tseslint.config( }, }, }, + { + ignores: ["eslint.config.mjs"], + }, ); // /* eslint-env node */ // module.exports = { From ebcabe5bfe7357395f9e26fb1ed3e177f1cf5b52 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 18:25:24 +0530 Subject: [PATCH 051/177] Restore rules --- web/packages/build-config/eslintrc-base.mjs | 105 +++++++++----------- 1 file changed, 46 insertions(+), 59 deletions(-) diff --git a/web/packages/build-config/eslintrc-base.mjs b/web/packages/build-config/eslintrc-base.mjs index 6da6e46511..e042c1d110 100644 --- a/web/packages/build-config/eslintrc-base.mjs +++ b/web/packages/build-config/eslintrc-base.mjs @@ -6,7 +6,7 @@ import tseslint from "typescript-eslint"; export default tseslint.config( eslint.configs.recommended, tseslint.configs.strictTypeChecked, - tseslint.configs.recommendedTypeChecked, + tseslint.configs.stylisticTypeChecked, { languageOptions: { parserOptions: { @@ -18,62 +18,49 @@ export default tseslint.config( { ignores: ["eslint.config.mjs"], }, + { + rules: { + // Allow numbers to be used in template literals. + "@typescript-eslint/restrict-template-expressions": [ + "error", + { + allowNumber: true, + }, + ], + // Allow void expressions as the entire body of an arrow function. + "@typescript-eslint/no-confusing-void-expression": [ + "error", + { + ignoreArrowShorthand: true, + }, + ], + // Allow async functions to be passed as JSX attributes expected to + // be functions that return void (typically onFoo event handlers). + // + // This should be safe since we have registered global unhandled + // Promise handlers. + "@typescript-eslint/no-misused-promises": [ + "error", + { + checksVoidReturn: { + arguments: false, + attributes: false, + }, + }, + ], + // Allow force unwrapping potentially optional values. + // + // It is best if these can be avoided by restructuring the code, but + // there do arise legitimate scenarios where we know from code logic + // that the value should be present. Of course, the surrounding code + // might change causing that expectation to be falsified, but in + // certain cases there isn't much we can do other than throwing an + // exception. + // + // Instead of rolling our own such exception (which we in fact used + // to do at one point), rely on the JS's native undefined property + // access exception since that conveys more information in the logs. + "@typescript-eslint/no-non-null-assertion": "off", + }, + }, ); -// /* eslint-env node */ -// module.exports = { -// root: true, -// extends: [ -// "eslint:recommended", -// "plugin:@typescript-eslint/strict-type-checked", -// "plugin:@typescript-eslint/stylistic-type-checked", -// ], -// plugins: ["@typescript-eslint"], -// parserOptions: { project: true }, -// parser: "@typescript-eslint/parser", -// ignorePatterns: [".eslintrc.js"], -// rules: { -// /* Allow numbers to be used in template literals */ -// "@typescript-eslint/restrict-template-expressions": [ -// "error", -// { -// allowNumber: true, -// }, -// ], -// /* Allow void expressions as the entire body of an arrow function */ -// "@typescript-eslint/no-confusing-void-expression": [ -// "error", -// { -// ignoreArrowShorthand: true, -// }, -// ], -// /* -// Allow async functions to be passed as JSX attributes expected to be -// functions that return void (typically onFoo event handlers). - -// This should be safe since we have registered global unhandled Promise -// handlers. -// */ -// "@typescript-eslint/no-misused-promises": [ -// "error", -// { -// checksVoidReturn: { -// arguments: false, -// attributes: false, -// }, -// }, -// ], -// /* Allow force unwrapping potentially optional values. - -// It is best if these can be avoided by restructuring the code, but -// there do arise legitimate scenarios where we know from code logic -// that the value should be present. Of course, the surrounding code -// might change causing that expectation to be falsified, but in certain -// cases there isn't much we can do other than throwing an exception. - -// Instead of rolling our own such exception (which we in fact used to -// do at one point), rely on the JS's native undefined property access -// exception since that conveys more information in the logs. -// */ -// "@typescript-eslint/no-non-null-assertion": "off", -// }, -// }; From b5be13df2c7ab284c8787970fff9db32dec6f4e7 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 18:27:24 +0530 Subject: [PATCH 052/177] Cleanup --- web/packages/build-config/.eslintrc.js | 6 ------ web/packages/build-config/eslint.config.mjs | 23 +-------------------- web/packages/build-config/eslintrc-next.js | 5 ----- web/packages/build-config/eslintrc-next.mjs | 5 +++++ 4 files changed, 6 insertions(+), 33 deletions(-) delete mode 100644 web/packages/build-config/.eslintrc.js delete mode 100644 web/packages/build-config/eslintrc-next.js create mode 100644 web/packages/build-config/eslintrc-next.mjs diff --git a/web/packages/build-config/.eslintrc.js b/web/packages/build-config/.eslintrc.js deleted file mode 100644 index 7496b8d983..0000000000 --- a/web/packages/build-config/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -// /* eslint-env node */ -// module.exports = { -// extends: ["eslint:recommended"], -// ignorePatterns: [".eslintrc.js"], -// root: true, -// }; diff --git a/web/packages/build-config/eslint.config.mjs b/web/packages/build-config/eslint.config.mjs index 879df38ad2..29fb59c881 100644 --- a/web/packages/build-config/eslint.config.mjs +++ b/web/packages/build-config/eslint.config.mjs @@ -1,24 +1,3 @@ import js from "@eslint/js"; -export default [ - js.configs.recommended, - { - ignores: ["eslintrc-*.js"] - } -] -// import path from "node:path"; -// import { fileURLToPath } from "node:url"; -// import js from "@eslint/js"; -// import { FlatCompat } from "@eslint/eslintrc"; - -// const __filename = fileURLToPath(import.meta.url); -// const __dirname = path.dirname(__filename); -// const compat = new FlatCompat({ -// baseDirectory: __dirname, -// recommendedConfig: js.configs.recommended, -// allConfig: js.configs.all -// }); - -// export default [{ -// ignores: ["**/.eslintrc.js"], -// }, ...compat.extends("eslint:recommended")]; +export default [js.configs.recommended]; diff --git a/web/packages/build-config/eslintrc-next.js b/web/packages/build-config/eslintrc-next.js deleted file mode 100644 index be87b8c059..0000000000 --- a/web/packages/build-config/eslintrc-next.js +++ /dev/null @@ -1,5 +0,0 @@ -/* eslint-env node */ -module.exports = { - extends: ["./eslintrc-react.js"], - ignorePatterns: [".eslintrc.js", "next.config.js", "out"], -}; diff --git a/web/packages/build-config/eslintrc-next.mjs b/web/packages/build-config/eslintrc-next.mjs new file mode 100644 index 0000000000..e2d2adb558 --- /dev/null +++ b/web/packages/build-config/eslintrc-next.mjs @@ -0,0 +1,5 @@ +// /* eslint-env node */ +// module.exports = { +// extends: ["./eslintrc-react.js"], +// ignorePatterns: [".eslintrc.js", "next.config.js", "out"], +// }; From 69d4e632f4985c6f49dd1b8d905e3dfe1fcbbf99 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 18:40:49 +0530 Subject: [PATCH 053/177] rp --- web/packages/build-config/eslintrc-react.mjs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index 270dafaf57..f6c8064ba9 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -1,6 +1,11 @@ +import reactPlugin from "eslint-plugin-react"; import config from "./eslintrc-base.mjs"; -export default config; +export default [ + ...config, + reactPlugin.configs.recommended, + reactPlugin.configs["jsx-runtime"], +]; // /* eslint-env node */ // module.exports = { From 5209562bba99ea585d7c8397c4a491fafc242e39 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 18:47:24 +0530 Subject: [PATCH 054/177] rv --- web/packages/build-config/eslintrc-react.mjs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index f6c8064ba9..e55df1fd7a 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -3,8 +3,15 @@ import config from "./eslintrc-base.mjs"; export default [ ...config, - reactPlugin.configs.recommended, - reactPlugin.configs["jsx-runtime"], + reactPlugin.configs.flat.recommended, + reactPlugin.configs.flat["jsx-runtime"], + { + settings: { + react: { + version: "detect", + }, + }, + }, ]; // /* eslint-env node */ From dbfb5e5da3fd5e608661193ec2cfcff155ce90df Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 18:49:40 +0530 Subject: [PATCH 055/177] hp --- web/packages/build-config/eslintrc-react.mjs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index e55df1fd7a..aab8b2908d 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -1,4 +1,5 @@ import reactPlugin from "eslint-plugin-react"; +import hooksPlugin from "eslint-plugin-react-hooks"; import config from "./eslintrc-base.mjs"; export default [ @@ -12,6 +13,14 @@ export default [ }, }, }, + { + plugins: { + "react-hooks": hooksPlugin, + }, + rules: { + ...hooksPlugin.configs.recommended.rules, + }, + }, ]; // /* eslint-env node */ From 0f93c48e10777f89e97624a57e475cd7294350fd Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 18:57:40 +0530 Subject: [PATCH 056/177] Scope --- web/packages/build-config/eslintrc-react.mjs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index aab8b2908d..da152c10f0 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -4,9 +4,10 @@ import config from "./eslintrc-base.mjs"; export default [ ...config, - reactPlugin.configs.flat.recommended, - reactPlugin.configs.flat["jsx-runtime"], { + files: ["**/*.{jsx,tsx}"], + ...reactPlugin.configs.flat.recommended, + ...reactPlugin.configs.flat["jsx-runtime"], settings: { react: { version: "detect", @@ -14,6 +15,7 @@ export default [ }, }, { + files: ["**/*.{jsx,tsx}"], plugins: { "react-hooks": hooksPlugin, }, From 0338313f73073836bf2f4724ae428c0433ee7881 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:00:10 +0530 Subject: [PATCH 057/177] rr --- web/packages/build-config/eslintrc-react.mjs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index da152c10f0..c83c6b03e1 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -1,5 +1,6 @@ import reactPlugin from "eslint-plugin-react"; import hooksPlugin from "eslint-plugin-react-hooks"; +import reactRefreshPlugin from "eslint-plugin-react-refresh"; import config from "./eslintrc-base.mjs"; export default [ @@ -23,6 +24,22 @@ export default [ ...hooksPlugin.configs.recommended.rules, }, }, + { + files: ["**/*.{jsx,tsx}"], + plugins: { + "react-refresh": reactRefreshPlugin, + }, + rules: { + // Apparently Fast refresh only works if a file only exports components, + // and this rule warns about that. + // + // Constants are okay though (otherwise we'll need to create unnecessary helper files). + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + }, + }, ]; // /* eslint-env node */ From 32cd241982403e35cf5425ff185c98ac6870b9bd Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:01:00 +0530 Subject: [PATCH 058/177] Merge --- web/packages/build-config/eslintrc-react.mjs | 28 +++++--------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index c83c6b03e1..156ffaf9e3 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -1,3 +1,5 @@ +// @ts-check + import reactPlugin from "eslint-plugin-react"; import hooksPlugin from "eslint-plugin-react-hooks"; import reactRefreshPlugin from "eslint-plugin-react-refresh"; @@ -14,26 +16,17 @@ export default [ version: "detect", }, }, - }, - { - files: ["**/*.{jsx,tsx}"], plugins: { "react-hooks": hooksPlugin, - }, - rules: { - ...hooksPlugin.configs.recommended.rules, - }, - }, - { - files: ["**/*.{jsx,tsx}"], - plugins: { "react-refresh": reactRefreshPlugin, }, rules: { - // Apparently Fast refresh only works if a file only exports components, - // and this rule warns about that. + ...hooksPlugin.configs.recommended.rules, + // Apparently Fast refresh only works if a file only exports + // components, and this rule warns about that. // - // Constants are okay though (otherwise we'll need to create unnecessary helper files). + // Constants are okay though (otherwise we'll need to create + // unnecessary helper files). "react-refresh/only-export-components": [ "warn", { allowConstantExport: true }, @@ -57,13 +50,6 @@ export default [ // "react/jsx-no-target-blank": ["warn", { allowReferrer: true }], // /* Otherwise we need to do unnecessary boilerplating when using memo. */ // "react/display-name": "off", -// /* Apparently Fast refresh only works if a file only exports components, -// and this rule warns about that. Constants are okay though (otherwise -// we'll need to create unnecessary helper files). */ -// "react-refresh/only-export-components": [ -// "warn", -// { allowConstantExport: true }, -// ], // /* Next.js supports the JSX transform introduced in React 17 */ // "react/react-in-jsx-scope": "off", // /* Without React in scope, this rule starts causing false positives (We From db5049b801dfce017d5404f5e944fa7cf5469908 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:06:17 +0530 Subject: [PATCH 059/177] Swap --- web/packages/utils/.eslintrc.js | 3 --- web/packages/utils/eslint.config.mjs | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 web/packages/utils/.eslintrc.js create mode 100644 web/packages/utils/eslint.config.mjs diff --git a/web/packages/utils/.eslintrc.js b/web/packages/utils/.eslintrc.js deleted file mode 100644 index 4123f0cae3..0000000000 --- a/web/packages/utils/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ["@/build-config/eslintrc-base"], -}; diff --git a/web/packages/utils/eslint.config.mjs b/web/packages/utils/eslint.config.mjs new file mode 100644 index 0000000000..d42e644661 --- /dev/null +++ b/web/packages/utils/eslint.config.mjs @@ -0,0 +1 @@ +export { default } from "@/build-config/eslintrc-base.mjs"; From 2bde3fb0d769e289b68420f49c3b5aeeeb91c047 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:12:22 +0530 Subject: [PATCH 060/177] Squash --- web/apps/payments/eslint.config.mjs | 2 +- web/packages/build-config/eslintrc-next.mjs | 5 ----- web/packages/build-config/eslintrc-react.mjs | 16 ++++++++-------- web/packages/build-config/eslintrc-vite.mjs | 9 --------- web/packages/build-config/tsconfig.json | 3 +-- 5 files changed, 10 insertions(+), 25 deletions(-) delete mode 100644 web/packages/build-config/eslintrc-next.mjs delete mode 100644 web/packages/build-config/eslintrc-vite.mjs diff --git a/web/apps/payments/eslint.config.mjs b/web/apps/payments/eslint.config.mjs index dda85e151c..0934aeee27 100644 --- a/web/apps/payments/eslint.config.mjs +++ b/web/apps/payments/eslint.config.mjs @@ -1 +1 @@ -export { default } from "@/build-config/eslintrc-vite.mjs"; +export { default } from "@/build-config/eslintrc-react.mjs"; diff --git a/web/packages/build-config/eslintrc-next.mjs b/web/packages/build-config/eslintrc-next.mjs deleted file mode 100644 index e2d2adb558..0000000000 --- a/web/packages/build-config/eslintrc-next.mjs +++ /dev/null @@ -1,5 +0,0 @@ -// /* eslint-env node */ -// module.exports = { -// extends: ["./eslintrc-react.js"], -// ignorePatterns: [".eslintrc.js", "next.config.js", "out"], -// }; diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index 156ffaf9e3..d204cbd008 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -33,17 +33,17 @@ export default [ ], }, }, + { + ignores: [ + // Next.js + "out", + // Vite + "dist", + ], + }, ]; -// /* eslint-env node */ // module.exports = { -// extends: [ -// "./eslintrc-base.js", -// "plugin:react/recommended", -// "plugin:react-hooks/recommended", -// ], -// plugins: ["react-refresh"], -// settings: { react: { version: "18.2" } }, // rules: { // /* The rule is misguided - only the opener should be omitted, not the // referrer. */ diff --git a/web/packages/build-config/eslintrc-vite.mjs b/web/packages/build-config/eslintrc-vite.mjs deleted file mode 100644 index 73e64c1fcd..0000000000 --- a/web/packages/build-config/eslintrc-vite.mjs +++ /dev/null @@ -1,9 +0,0 @@ -import config from "./eslintrc-react.mjs"; - -export default config; - -// /* eslint-env node */ -// module.exports = { -// extends: ["./eslintrc-react.js", "plugin:react/jsx-runtime"], -// ignorePatterns: [".eslintrc.cjs", "vite.config.ts", "dist"], -// }; diff --git a/web/packages/build-config/tsconfig.json b/web/packages/build-config/tsconfig.json index 395176496b..88d20f5430 100644 --- a/web/packages/build-config/tsconfig.json +++ b/web/packages/build-config/tsconfig.json @@ -1,6 +1,5 @@ { - /* A minimal tsconfig so that we can run tsc on the build-config package - itself */ + /* A minimal tsconfig so that we can run tsc on the build-config itself. */ "compilerOptions": { "module": "ESNext", "moduleResolution": "bundler", From 1b772b644a41493d83f581cd875740df1727d38d Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:14:12 +0530 Subject: [PATCH 061/177] Move to config --- web/package.json | 4 ++-- web/packages/build-config/eslintrc-base.mjs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/web/package.json b/web/package.json index b4dcf1116e..7a8ab23be3 100644 --- a/web/package.json +++ b/web/package.json @@ -20,8 +20,8 @@ "dev:cast": "yarn workspace cast next dev -p 3001", "dev:payments": "yarn workspace payments dev", "dev:photos": "yarn workspace photos next dev -p 3000", - "lint": "concurrently --names 'prettier,eslint,tsc' \"yarn prettier --check --log-level warn .\" \"yarn workspaces run eslint --report-unused-disable-directives .\" \"yarn workspaces run tsc\"", - "lint-fix": "concurrently --names 'prettier,eslint,tsc' \"yarn prettier --write --log-level warn .\" \"yarn workspaces run eslint --report-unused-disable-directives --fix .\" \"yarn workspaces run tsc\"", + "lint": "concurrently --names 'prettier,eslint,tsc' \"yarn prettier --check --log-level warn .\" \"yarn workspaces run eslint .\" \"yarn workspaces run tsc\"", + "lint-fix": "concurrently --names 'prettier,eslint,tsc' \"yarn prettier --write --log-level warn .\" \"yarn workspaces run eslint --fix .\" \"yarn workspaces run tsc\"", "preview": "yarn preview:photos", "preview:accounts": "yarn build:accounts && python3 -m http.server -d apps/accounts/out 3001", "preview:auth": "yarn build:auth && python3 -m http.server -d apps/auth/out 3000", diff --git a/web/packages/build-config/eslintrc-base.mjs b/web/packages/build-config/eslintrc-base.mjs index e042c1d110..78f2d1363f 100644 --- a/web/packages/build-config/eslintrc-base.mjs +++ b/web/packages/build-config/eslintrc-base.mjs @@ -14,6 +14,9 @@ export default tseslint.config( tsconfigRootDir: import.meta.dirname, }, }, + linterOptions: { + reportUnusedDisableDirectives: "error", + }, }, { ignores: ["eslint.config.mjs"], From 0271e53c2663172298fcbd9bce585e5af6b0a316 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:15:07 +0530 Subject: [PATCH 062/177] Swap --- web/packages/media/.eslintrc.js | 3 --- web/packages/media/eslint.config.mjs | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 web/packages/media/.eslintrc.js create mode 100644 web/packages/media/eslint.config.mjs diff --git a/web/packages/media/.eslintrc.js b/web/packages/media/.eslintrc.js deleted file mode 100644 index 348075cd4f..0000000000 --- a/web/packages/media/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ["@/build-config/eslintrc-next"], -}; diff --git a/web/packages/media/eslint.config.mjs b/web/packages/media/eslint.config.mjs new file mode 100644 index 0000000000..0934aeee27 --- /dev/null +++ b/web/packages/media/eslint.config.mjs @@ -0,0 +1 @@ +export { default } from "@/build-config/eslintrc-react.mjs"; From 4a3260f627f8bd7c6e036186593a685caf65e027 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:19:31 +0530 Subject: [PATCH 063/177] Auto fixer for new lint rule --- web/packages/media/file-metadata.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/packages/media/file-metadata.ts b/web/packages/media/file-metadata.ts index cd460f46a5..e31f482990 100644 --- a/web/packages/media/file-metadata.ts +++ b/web/packages/media/file-metadata.ts @@ -674,7 +674,7 @@ export const parseMetadataDate = ( // Check to see if there is a time-zone descriptor of the form "Z" or // "±05:30" or "±0530" at the end of s. - const m = s.match(/Z|[+-]\d\d:?\d\d$/); + const m = /Z|[+-]\d\d:?\d\d$/.exec(s); if (m?.index) { sWithoutOffset = s.substring(0, m.index); offset = s.substring(m.index); From ea19cd08c6f054acae4fba471ca4aa2e4482998a Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:20:31 +0530 Subject: [PATCH 064/177] Swap --- web/packages/gallery/.eslintrc.js | 3 --- web/packages/gallery/eslint.config.mjs | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 web/packages/gallery/.eslintrc.js create mode 100644 web/packages/gallery/eslint.config.mjs diff --git a/web/packages/gallery/.eslintrc.js b/web/packages/gallery/.eslintrc.js deleted file mode 100644 index 348075cd4f..0000000000 --- a/web/packages/gallery/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: ["@/build-config/eslintrc-next"], -}; diff --git a/web/packages/gallery/eslint.config.mjs b/web/packages/gallery/eslint.config.mjs new file mode 100644 index 0000000000..0934aeee27 --- /dev/null +++ b/web/packages/gallery/eslint.config.mjs @@ -0,0 +1 @@ +export { default } from "@/build-config/eslintrc-react.mjs"; From 453c825cc647667d87ea3ed7e4326464d0d434a3 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:22:25 +0530 Subject: [PATCH 065/177] Autofixer no-duplicate-type-constituents --- web/packages/gallery/upload.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/packages/gallery/upload.ts b/web/packages/gallery/upload.ts index a2f830d8b8..868b9caf3d 100644 --- a/web/packages/gallery/upload.ts +++ b/web/packages/gallery/upload.ts @@ -61,7 +61,7 @@ export const shouldDisableCFUploadProxy = () => * expressed to disable the proxy. */ export const updateShouldDisableCFUploadProxy = async ( - savedPreference?: boolean | undefined, + savedPreference?: boolean, ) => { _state.shouldDisableCFUploadProxy = // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing From 02c9f9f61536a6de00566060bc4b5466a0d8a51a Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:25:18 +0530 Subject: [PATCH 066/177] Swap --- web/packages/base/.eslintrc.js | 4 ---- web/packages/base/eslint.config.mjs | 8 ++++++++ 2 files changed, 8 insertions(+), 4 deletions(-) delete mode 100644 web/packages/base/.eslintrc.js create mode 100644 web/packages/base/eslint.config.mjs diff --git a/web/packages/base/.eslintrc.js b/web/packages/base/.eslintrc.js deleted file mode 100644 index 4a4e4d15dc..0000000000 --- a/web/packages/base/.eslintrc.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - extends: ["@/build-config/eslintrc-next"], - ignorePatterns: ["next.config.base.js"], -}; diff --git a/web/packages/base/eslint.config.mjs b/web/packages/base/eslint.config.mjs new file mode 100644 index 0000000000..b5636ffc56 --- /dev/null +++ b/web/packages/base/eslint.config.mjs @@ -0,0 +1,8 @@ +import config from "@/build-config/eslintrc-react.mjs"; + +export default [ + ...config, + { + ignores: ["next.config.base.js"], + }, +]; From e6cbd75a52c3bfa3d5cebe54ffe7e9e7a503e8df Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:26:23 +0530 Subject: [PATCH 067/177] Fix as per new rules --- web/packages/base/components/Menu.tsx | 2 +- web/packages/base/components/Titlebar.tsx | 2 +- web/packages/base/crypto/libsodium.ts | 2 +- web/packages/base/log.ts | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/web/packages/base/components/Menu.tsx b/web/packages/base/components/Menu.tsx index 00381aea67..f63ee77478 100644 --- a/web/packages/base/components/Menu.tsx +++ b/web/packages/base/components/Menu.tsx @@ -4,7 +4,7 @@ import React from "react"; interface MenuSectionTitleProps { title: string; - icon?: JSX.Element; + icon?: React.JSX.Element; } export const MenuSectionTitle: React.FC = ({ diff --git a/web/packages/base/components/Titlebar.tsx b/web/packages/base/components/Titlebar.tsx index 1029dff467..8cec0763c0 100644 --- a/web/packages/base/components/Titlebar.tsx +++ b/web/packages/base/components/Titlebar.tsx @@ -10,7 +10,7 @@ interface TitlebarProps { onClose: () => void; backIsClose?: boolean; onRootClose?: () => void; - actionButton?: JSX.Element; + actionButton?: React.JSX.Element; } // TODO: Deprecated in favor of SidebarDrawerTitlebarProps where possible (will diff --git a/web/packages/base/crypto/libsodium.ts b/web/packages/base/crypto/libsodium.ts index ff470b1c9e..0ceb28f85e 100644 --- a/web/packages/base/crypto/libsodium.ts +++ b/web/packages/base/crypto/libsodium.ts @@ -617,7 +617,7 @@ export async function deriveSensitiveKey(passphrase: string, salt: string) { opsLimit, memLimit, }; - } catch (e) { + } catch { opsLimit *= 2; memLimit /= 2; } diff --git a/web/packages/base/log.ts b/web/packages/base/log.ts index f79022fa6b..07cfeed52d 100644 --- a/web/packages/base/log.ts +++ b/web/packages/base/log.ts @@ -59,6 +59,7 @@ const messageWithError = (message: string, e?: unknown) => { } } else { // For the rest rare cases, use the default string serialization of e. + // eslint-disable-next-line @typescript-eslint/no-base-to-string es = String(e); } From 83adb94fc94899cc3d3489b4e95b3fe61325a6ba Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:29:52 +0530 Subject: [PATCH 068/177] Swap --- web/packages/accounts/.eslintrc.js | 36 ---------------------- web/packages/accounts/eslint.config.mjs | 40 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 36 deletions(-) delete mode 100644 web/packages/accounts/.eslintrc.js create mode 100644 web/packages/accounts/eslint.config.mjs diff --git a/web/packages/accounts/.eslintrc.js b/web/packages/accounts/.eslintrc.js deleted file mode 100644 index 38562301c4..0000000000 --- a/web/packages/accounts/.eslintrc.js +++ /dev/null @@ -1,36 +0,0 @@ -module.exports = { - extends: ["@/build-config/eslintrc-react"], - rules: { - /* TODO: - * "This rule requires the `strictNullChecks` compiler option to be - * turned on to function correctly" - */ - "@typescript-eslint/prefer-nullish-coalescing": "off", - "@typescript-eslint/no-unnecessary-condition": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-call": "off", - /** TODO: Disabled as we migrate, try to prune these again */ - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-unsafe-enum-comparison": "off", - "@typescript-eslint/no-unnecessary-type-assertion": "off", - "@typescript-eslint/array-type": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-unnecessary-template-expression": "off", - "@typescript-eslint/consistent-indexed-object-style": "off", - "@typescript-eslint/prefer-promise-reject-errors": "off", - "@typescript-eslint/no-useless-constructor": "off", - "@typescript-eslint/dot-notation": "off", - "@typescript-eslint/require-await": "off", - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/prefer-optional-chain": "off", - "@typescript-eslint/ban-types": "off", - "@typescript-eslint/no-misused-promises": "off", - "react-hooks/exhaustive-deps": "off", - "react-hooks/rules-of-hooks": "off", - "react-refresh/only-export-components": "off", - }, -}; diff --git a/web/packages/accounts/eslint.config.mjs b/web/packages/accounts/eslint.config.mjs new file mode 100644 index 0000000000..b6ef7f05d1 --- /dev/null +++ b/web/packages/accounts/eslint.config.mjs @@ -0,0 +1,40 @@ +import config from "@/build-config/eslintrc-react.mjs"; + +export default [ + ...config, + { + rules: { + /* TODO: + * "This rule requires the `strictNullChecks` compiler option to be + * turned on to function correctly" + */ + "@typescript-eslint/prefer-nullish-coalescing": "off", + "@typescript-eslint/no-unnecessary-condition": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-call": "off", + /** TODO: Disabled as we migrate, try to prune these again */ + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-unsafe-enum-comparison": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "off", + "@typescript-eslint/array-type": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unnecessary-template-expression": "off", + "@typescript-eslint/consistent-indexed-object-style": "off", + "@typescript-eslint/prefer-promise-reject-errors": "off", + "@typescript-eslint/no-useless-constructor": "off", + "@typescript-eslint/dot-notation": "off", + "@typescript-eslint/require-await": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/prefer-optional-chain": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/no-misused-promises": "off", + "react-hooks/exhaustive-deps": "off", + "react-hooks/rules-of-hooks": "off", + "react-refresh/only-export-components": "off", + }, + }, +]; From 097b90eeda684f28e5688e1a344bf82002e536e8 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:31:33 +0530 Subject: [PATCH 069/177] Get it to run --- web/packages/accounts/components/SignUp.tsx | 2 +- web/packages/accounts/eslint.config.mjs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/web/packages/accounts/components/SignUp.tsx b/web/packages/accounts/components/SignUp.tsx index 35da852730..b5c98f5b27 100644 --- a/web/packages/accounts/components/SignUp.tsx +++ b/web/packages/accounts/components/SignUp.tsx @@ -146,7 +146,7 @@ export const SignUp: React.FC = ({ router, login, host }) => { errors, handleChange, handleSubmit, - }): JSX.Element => ( + }): React.JSX.Element => (
9 migration */ + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-require-imports": "off", + "@typescript-eslint/no-unsafe-function-type": "off", }, }, ]; From a3586fed7e3b68f195f7ff79855ed12cb5912de9 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:32:52 +0530 Subject: [PATCH 070/177] Swap --- web/packages/shared/.eslintrc.js | 28 ---------------------- web/packages/shared/eslint.config.mjs | 34 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 28 deletions(-) delete mode 100644 web/packages/shared/.eslintrc.js create mode 100644 web/packages/shared/eslint.config.mjs diff --git a/web/packages/shared/.eslintrc.js b/web/packages/shared/.eslintrc.js deleted file mode 100644 index 227fc276cb..0000000000 --- a/web/packages/shared/.eslintrc.js +++ /dev/null @@ -1,28 +0,0 @@ -module.exports = { - extends: ["@/build-config/eslintrc-react"], - ignorePatterns: ["next/utils/headers.js"], - rules: { - /* TODO: - * "This rule requires the `strictNullChecks` compiler option to be - * turned on to function correctly" - */ - "@typescript-eslint/prefer-nullish-coalescing": "off", - "@typescript-eslint/no-unnecessary-condition": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unsafe-argument": "off", - /** TODO: Disabled as we migrate, try to prune these again */ - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-unsafe-enum-comparison": "off", - "@typescript-eslint/no-unnecessary-type-assertion": "off", - "@typescript-eslint/array-type": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-unnecessary-template-expression": "off", - "@typescript-eslint/consistent-indexed-object-style": "off", - "@typescript-eslint/prefer-promise-reject-errors": "off", - "@typescript-eslint/no-useless-constructor": "off", - "react-hooks/exhaustive-deps": "off", - }, -}; diff --git a/web/packages/shared/eslint.config.mjs b/web/packages/shared/eslint.config.mjs new file mode 100644 index 0000000000..61c51485fa --- /dev/null +++ b/web/packages/shared/eslint.config.mjs @@ -0,0 +1,34 @@ +import config from "@/build-config/eslintrc-react.mjs"; + +export default [ + ...config, + { + ignores: ["next/utils/headers.js"], + }, + { + rules: { + /* TODO: + * "This rule requires the `strictNullChecks` compiler option to be + * turned on to function correctly" + */ + "@typescript-eslint/prefer-nullish-coalescing": "off", + "@typescript-eslint/no-unnecessary-condition": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-argument": "off", + /** TODO: Disabled as we migrate, try to prune these again */ + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-unsafe-enum-comparison": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "off", + "@typescript-eslint/array-type": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unnecessary-template-expression": "off", + "@typescript-eslint/consistent-indexed-object-style": "off", + "@typescript-eslint/prefer-promise-reject-errors": "off", + "@typescript-eslint/no-useless-constructor": "off", + "react-hooks/exhaustive-deps": "off", + }, + }, +]; From 1aac1ae3c3fd740ba7907e28cc846c2e4b86c9a6 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:33:33 +0530 Subject: [PATCH 071/177] Fix for now --- web/packages/shared/eslint.config.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/packages/shared/eslint.config.mjs b/web/packages/shared/eslint.config.mjs index 61c51485fa..5897b2d378 100644 --- a/web/packages/shared/eslint.config.mjs +++ b/web/packages/shared/eslint.config.mjs @@ -29,6 +29,9 @@ export default [ "@typescript-eslint/prefer-promise-reject-errors": "off", "@typescript-eslint/no-useless-constructor": "off", "react-hooks/exhaustive-deps": "off", + /** TODO: New during eslint 8=>9 migration */ + "@typescript-eslint/no-unused-expressions": "off", + "@typescript-eslint/no-unused-vars": "off", }, }, ]; From 938679922c76410b936a4076d2b879b3063d459b Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:35:14 +0530 Subject: [PATCH 072/177] Swap --- web/packages/new/.eslintrc.js | 8 -------- web/packages/new/eslint.config.mjs | 12 ++++++++++++ 2 files changed, 12 insertions(+), 8 deletions(-) delete mode 100644 web/packages/new/.eslintrc.js create mode 100644 web/packages/new/eslint.config.mjs diff --git a/web/packages/new/.eslintrc.js b/web/packages/new/.eslintrc.js deleted file mode 100644 index 37111028d5..0000000000 --- a/web/packages/new/.eslintrc.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - extends: ["@/build-config/eslintrc-react"], - // TODO: These can be removed when we start using ffmpeg upstream. For an reason - // I haven't investigated much, when we run eslint on our CI, it seems to behave - // differently than locally and give a lot of warnings that possibly arise from - // it not being able to locate ffmpeg-wasm. - ignorePatterns: ["**/ffmpeg/worker.ts"], -}; diff --git a/web/packages/new/eslint.config.mjs b/web/packages/new/eslint.config.mjs new file mode 100644 index 0000000000..0679b564ea --- /dev/null +++ b/web/packages/new/eslint.config.mjs @@ -0,0 +1,12 @@ +import config from "@/build-config/eslintrc-react.mjs"; + +export default [ + ...config, + { + // TODO: These can be removed when we start using ffmpeg upstream. For + // reasons I haven't investigated much, when we run eslint on our CI, it + // seems to behave differently than locally and give a lot of warnings + // that possibly arise from it not being able to locate ffmpeg-wasm. + ignores: ["**/ffmpeg/worker.ts"], + }, +]; From c506eec544c95f6ef281e9011dc6bbfe95d041b6 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:38:42 +0530 Subject: [PATCH 073/177] Fix --- web/packages/new/eslint.config.mjs | 7 +++++++ .../new/photos/components/PhotoDateTimePicker.tsx | 2 +- web/packages/new/photos/services/magic-metadata.ts | 1 - web/packages/new/photos/services/user-entity/index.ts | 2 +- web/packages/new/photos/types/notification.tsx | 8 ++++---- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/web/packages/new/eslint.config.mjs b/web/packages/new/eslint.config.mjs index 0679b564ea..8b441fa3fe 100644 --- a/web/packages/new/eslint.config.mjs +++ b/web/packages/new/eslint.config.mjs @@ -9,4 +9,11 @@ export default [ // that possibly arise from it not being able to locate ffmpeg-wasm. ignores: ["**/ffmpeg/worker.ts"], }, + { + rules: { + /** TODO: New during eslint 8=>9 migration */ + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-unused-expressions": "off", + }, + }, ]; diff --git a/web/packages/new/photos/components/PhotoDateTimePicker.tsx b/web/packages/new/photos/components/PhotoDateTimePicker.tsx index b6e6ec4b93..b78a1140ad 100644 --- a/web/packages/new/photos/components/PhotoDateTimePicker.tsx +++ b/web/packages/new/photos/components/PhotoDateTimePicker.tsx @@ -151,7 +151,7 @@ const parseMetadataDateFromDayjs = (d: Dayjs): ParsedMetadataDate => { // Check to see if there is a time-zone descriptor of the form "Z" or // "±05:30" or "±0530" at the end of s. - const m = s.match(/Z|[+-]\d\d:?\d\d$/); + const m = /Z|[+-]\d\d:?\d\d$/.exec(s); if (m?.index) { dateTime = s.substring(0, m.index); offset = s.substring(m.index); diff --git a/web/packages/new/photos/services/magic-metadata.ts b/web/packages/new/photos/services/magic-metadata.ts index d7b78f8964..faf98a8bce 100644 --- a/web/packages/new/photos/services/magic-metadata.ts +++ b/web/packages/new/photos/services/magic-metadata.ts @@ -114,7 +114,6 @@ export const getNonEmptyMagicMetadataProps = (magicMetadataProps: T): T => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore Object.entries(magicMetadataProps).filter( - // eslint-disable-next-line @typescript-eslint/no-unused-vars ([_, v]) => v !== null && v !== undefined, ), ) as T; diff --git a/web/packages/new/photos/services/user-entity/index.ts b/web/packages/new/photos/services/user-entity/index.ts index ad7fb76eb9..ea6dcb57e3 100644 --- a/web/packages/new/photos/services/user-entity/index.ts +++ b/web/packages/new/photos/services/user-entity/index.ts @@ -134,7 +134,7 @@ export const pullUserEntities = async ( const entityKeyB64 = await getOrCreateEntityKeyB64(type, masterKey); let sinceTime = (await savedLatestUpdatedAt(type)) ?? 0; - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, no-constant-condition + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while (true) { const diff = await userEntityDiff(type, sinceTime, entityKeyB64); if (diff.length == 0) break; diff --git a/web/packages/new/photos/types/notification.tsx b/web/packages/new/photos/types/notification.tsx index 43ea111911..b4080ce257 100644 --- a/web/packages/new/photos/types/notification.tsx +++ b/web/packages/new/photos/types/notification.tsx @@ -8,8 +8,8 @@ export type NotificationAttributes = interface MessageSubTextNotificationAttributes { startIcon?: ReactNode; variant: ButtonProps["color"]; - message?: JSX.Element | string; - subtext?: JSX.Element | string; + message?: React.JSX.Element | string; + subtext?: React.JSX.Element | string; title?: never; caption?: never; onClick: () => void; @@ -19,8 +19,8 @@ interface MessageSubTextNotificationAttributes { interface TitleCaptionNotificationAttributes { startIcon?: ReactNode; variant: ButtonProps["color"]; - title?: JSX.Element | string; - caption?: JSX.Element | string; + title?: React.JSX.Element | string; + caption?: React.JSX.Element | string; message?: never; subtext?: never; onClick: () => void; From 31b50852b62c30086a2662472def23c3e8abaf77 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:39:59 +0530 Subject: [PATCH 074/177] Swap --- web/apps/accounts/.eslintrc.js | 4 ---- web/apps/accounts/eslint.config.mjs | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 web/apps/accounts/.eslintrc.js create mode 100644 web/apps/accounts/eslint.config.mjs diff --git a/web/apps/accounts/.eslintrc.js b/web/apps/accounts/.eslintrc.js deleted file mode 100644 index b1aff972c9..0000000000 --- a/web/apps/accounts/.eslintrc.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - extends: ["@/build-config/eslintrc-next"], - ignorePatterns: ["next.config.js", "next-env.d.ts"], -}; diff --git a/web/apps/accounts/eslint.config.mjs b/web/apps/accounts/eslint.config.mjs new file mode 100644 index 0000000000..0934aeee27 --- /dev/null +++ b/web/apps/accounts/eslint.config.mjs @@ -0,0 +1 @@ +export { default } from "@/build-config/eslintrc-react.mjs"; From 87ac2360eb9ae815165a6b140686a65704fd07ef Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:44:24 +0530 Subject: [PATCH 075/177] Swap --- web/apps/accounts/eslint.config.mjs | 2 +- web/apps/payments/eslint.config.mjs | 2 +- web/packages/build-config/eslintrc-next-app.mjs | 9 +++++++++ web/packages/build-config/eslintrc-react.mjs | 8 -------- web/packages/build-config/eslintrc-vite-app.mjs | 6 ++++++ 5 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 web/packages/build-config/eslintrc-next-app.mjs create mode 100644 web/packages/build-config/eslintrc-vite-app.mjs diff --git a/web/apps/accounts/eslint.config.mjs b/web/apps/accounts/eslint.config.mjs index 0934aeee27..06dd5eefb9 100644 --- a/web/apps/accounts/eslint.config.mjs +++ b/web/apps/accounts/eslint.config.mjs @@ -1 +1 @@ -export { default } from "@/build-config/eslintrc-react.mjs"; +export { default } from "@/build-config/eslintrc-next-app.mjs"; diff --git a/web/apps/payments/eslint.config.mjs b/web/apps/payments/eslint.config.mjs index 0934aeee27..badf668bf1 100644 --- a/web/apps/payments/eslint.config.mjs +++ b/web/apps/payments/eslint.config.mjs @@ -1 +1 @@ -export { default } from "@/build-config/eslintrc-react.mjs"; +export { default } from "@/build-config/eslintrc-vite-app.mjs"; diff --git a/web/packages/build-config/eslintrc-next-app.mjs b/web/packages/build-config/eslintrc-next-app.mjs new file mode 100644 index 0000000000..32e17fca5a --- /dev/null +++ b/web/packages/build-config/eslintrc-next-app.mjs @@ -0,0 +1,9 @@ +// @ts-check + +import config from "./eslintrc-react.mjs"; + +// A base config for Next.js apps. +export default [ + ...config, + { ignores: ["out", ".next", "next.config.js", "next-env.d.ts"] }, +]; diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index d204cbd008..b1193b78ad 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -33,14 +33,6 @@ export default [ ], }, }, - { - ignores: [ - // Next.js - "out", - // Vite - "dist", - ], - }, ]; // module.exports = { diff --git a/web/packages/build-config/eslintrc-vite-app.mjs b/web/packages/build-config/eslintrc-vite-app.mjs new file mode 100644 index 0000000000..8421b84060 --- /dev/null +++ b/web/packages/build-config/eslintrc-vite-app.mjs @@ -0,0 +1,6 @@ +// @ts-check + +import config from "./eslintrc-react.mjs"; + +// A base config for Vite apps. +export default [...config, { ignores: ["dist"] }]; From 01f117238250a8a5a791b98ebabe05dfac4117e9 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:46:32 +0530 Subject: [PATCH 076/177] Swap --- web/apps/auth/.eslintrc.js | 28 ---------------------------- web/apps/auth/eslint.config.mjs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 28 deletions(-) delete mode 100644 web/apps/auth/.eslintrc.js create mode 100644 web/apps/auth/eslint.config.mjs diff --git a/web/apps/auth/.eslintrc.js b/web/apps/auth/.eslintrc.js deleted file mode 100644 index 5e036185ce..0000000000 --- a/web/apps/auth/.eslintrc.js +++ /dev/null @@ -1,28 +0,0 @@ -module.exports = { - extends: ["@/build-config/eslintrc-next"], - rules: { - /* TODO: - * "This rule requires the `strictNullChecks` compiler option to be - * turned on to function correctly" - */ - "@typescript-eslint/prefer-nullish-coalescing": "off", - "@typescript-eslint/no-unnecessary-condition": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unsafe-argument": "off", - /** TODO: Disabled as we migrate, try to prune these again */ - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-unsafe-enum-comparison": "off", - "@typescript-eslint/no-unnecessary-type-assertion": "off", - "@typescript-eslint/array-type": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-unnecessary-template-expression": "off", - "@typescript-eslint/consistent-indexed-object-style": "off", - "@typescript-eslint/prefer-promise-reject-errors": "off", - "@typescript-eslint/no-useless-constructor": "off", - "react-hooks/exhaustive-deps": "off", - "react-refresh/only-export-components": "off", - }, -}; diff --git a/web/apps/auth/eslint.config.mjs b/web/apps/auth/eslint.config.mjs new file mode 100644 index 0000000000..c61e4cd750 --- /dev/null +++ b/web/apps/auth/eslint.config.mjs @@ -0,0 +1,32 @@ +import config from "@/build-config/eslintrc-next-app.mjs"; + +export default [ + ...config, + { + rules: { + /* TODO: + * "This rule requires the `strictNullChecks` compiler option to be + * turned on to function correctly" + */ + "@typescript-eslint/prefer-nullish-coalescing": "off", + "@typescript-eslint/no-unnecessary-condition": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-argument": "off", + /** TODO: Disabled as we migrate, try to prune these again */ + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-unsafe-enum-comparison": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "off", + "@typescript-eslint/array-type": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unnecessary-template-expression": "off", + "@typescript-eslint/consistent-indexed-object-style": "off", + "@typescript-eslint/prefer-promise-reject-errors": "off", + "@typescript-eslint/no-useless-constructor": "off", + "react-hooks/exhaustive-deps": "off", + "react-refresh/only-export-components": "off", + }, + }, +]; From 5c6784c549d5806603adcdbffaed4ad99caca8ea Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:47:28 +0530 Subject: [PATCH 077/177] Swap --- web/apps/cast/.eslintrc.js | 4 ---- web/apps/cast/eslint.config.mjs | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 web/apps/cast/.eslintrc.js create mode 100644 web/apps/cast/eslint.config.mjs diff --git a/web/apps/cast/.eslintrc.js b/web/apps/cast/.eslintrc.js deleted file mode 100644 index b1aff972c9..0000000000 --- a/web/apps/cast/.eslintrc.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - extends: ["@/build-config/eslintrc-next"], - ignorePatterns: ["next.config.js", "next-env.d.ts"], -}; diff --git a/web/apps/cast/eslint.config.mjs b/web/apps/cast/eslint.config.mjs new file mode 100644 index 0000000000..06dd5eefb9 --- /dev/null +++ b/web/apps/cast/eslint.config.mjs @@ -0,0 +1 @@ +export { default } from "@/build-config/eslintrc-next-app.mjs"; From 91ddce142591fd185e8ab1d23aed16e6e79eaeb4 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:48:59 +0530 Subject: [PATCH 078/177] Fix --- web/apps/cast/src/services/cast-data.ts | 8 +++++--- web/apps/cast/src/services/pair.ts | 1 - 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/web/apps/cast/src/services/cast-data.ts b/web/apps/cast/src/services/cast-data.ts index 367b3e4dbd..85e9dd8451 100644 --- a/web/apps/cast/src/services/cast-data.ts +++ b/web/apps/cast/src/services/cast-data.ts @@ -20,9 +20,11 @@ export const storeCastData = (payload: unknown) => { // localStorage. We don't validate here, we'll validate when we read these // values back in `readCastData`. for (const [key, value] of Object.entries(payload)) { - typeof value == "string" || typeof value == "number" - ? localStorage.setItem(key, value.toString()) - : localStorage.removeItem(key); + if (typeof value == "string" || typeof value == "number") { + localStorage.setItem(key, value.toString()); + } else { + localStorage.removeItem(key); + } } }; diff --git a/web/apps/cast/src/services/pair.ts b/web/apps/cast/src/services/pair.ts index 378165eb2b..ae0a8fd98c 100644 --- a/web/apps/cast/src/services/pair.ts +++ b/web/apps/cast/src/services/pair.ts @@ -83,7 +83,6 @@ export const register = async (): Promise => { // eslint has fixed this spurious warning, but we're not on the latest // version yet, so add a disable. // https://github.com/eslint/eslint/pull/18286 - /* eslint-disable no-constant-condition */ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while (true) { try { From 2bda469176a97337e3b7e3aa10df5fa075327e6c Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:51:11 +0530 Subject: [PATCH 079/177] Swap --- web/apps/photos/.eslintrc.js | 54 ------------------------------- web/apps/photos/eslint.config.mjs | 54 +++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 54 deletions(-) delete mode 100644 web/apps/photos/.eslintrc.js create mode 100644 web/apps/photos/eslint.config.mjs diff --git a/web/apps/photos/.eslintrc.js b/web/apps/photos/.eslintrc.js deleted file mode 100644 index 3ae4ddd8df..0000000000 --- a/web/apps/photos/.eslintrc.js +++ /dev/null @@ -1,54 +0,0 @@ -module.exports = { - extends: ["@/build-config/eslintrc-next"], - ignorePatterns: [ - ".eslintrc.js", - "out", - "thirdparty", - "public", - "next.config.js", - ], - rules: { - /* TODO: - * "This rule requires the `strictNullChecks` compiler option to be - * turned on to function correctly" - */ - "@typescript-eslint/prefer-nullish-coalescing": "off", - "@typescript-eslint/no-unnecessary-condition": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-call": "off", - /** TODO: Disabled as we migrate, try to prune these again */ - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-unsafe-enum-comparison": "off", - "@typescript-eslint/no-unnecessary-type-assertion": "off", - "@typescript-eslint/array-type": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-unnecessary-template-expression": "off", - "@typescript-eslint/consistent-indexed-object-style": "off", - "@typescript-eslint/prefer-promise-reject-errors": "off", - "@typescript-eslint/no-useless-constructor": "off", - "@typescript-eslint/dot-notation": "off", - "@typescript-eslint/require-await": "off", - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/prefer-optional-chain": "off", - "@typescript-eslint/ban-types": "off", - "@typescript-eslint/no-misused-promises": "off", - "@typescript-eslint/restrict-template-expressions": "off", - "@typescript-eslint/consistent-type-definitions": "off", - "@typescript-eslint/consistent-generic-constructors": "off", - "@typescript-eslint/no-inferrable-types": "off", - "@typescript-eslint/no-base-to-string": "off", - "@typescript-eslint/no-unnecessary-type-arguments": "off", - "@typescript-eslint/prefer-for-of": "off", - "@typescript-eslint/no-confusing-void-expression": "off", - "@typescript-eslint/use-unknown-in-catch-callback-variable": "off", - "@typescript-eslint/restrict-plus-operands": "off", - "@typescript-eslint/no-meaningless-void-operator": "off", - "react-hooks/exhaustive-deps": "off", - "react-hooks/rules-of-hooks": "off", - "react-refresh/only-export-components": "off", - }, -}; diff --git a/web/apps/photos/eslint.config.mjs b/web/apps/photos/eslint.config.mjs new file mode 100644 index 0000000000..1cd39b9d11 --- /dev/null +++ b/web/apps/photos/eslint.config.mjs @@ -0,0 +1,54 @@ +import config from "@/build-config/eslintrc-next-app.mjs"; + +export default [ + ...config, + { + ignores: ["thirdparty", "public"], + }, + { + rules: { + /* TODO: + * "This rule requires the `strictNullChecks` compiler option to be + * turned on to function correctly" + */ + "@typescript-eslint/prefer-nullish-coalescing": "off", + "@typescript-eslint/no-unnecessary-condition": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-call": "off", + /** TODO: Disabled as we migrate, try to prune these again */ + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-unsafe-enum-comparison": "off", + "@typescript-eslint/no-unnecessary-type-assertion": "off", + "@typescript-eslint/array-type": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unnecessary-template-expression": "off", + "@typescript-eslint/consistent-indexed-object-style": "off", + "@typescript-eslint/prefer-promise-reject-errors": "off", + "@typescript-eslint/no-useless-constructor": "off", + "@typescript-eslint/dot-notation": "off", + "@typescript-eslint/require-await": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/prefer-optional-chain": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/no-misused-promises": "off", + "@typescript-eslint/restrict-template-expressions": "off", + "@typescript-eslint/consistent-type-definitions": "off", + "@typescript-eslint/consistent-generic-constructors": "off", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/no-base-to-string": "off", + "@typescript-eslint/no-unnecessary-type-arguments": "off", + "@typescript-eslint/prefer-for-of": "off", + "@typescript-eslint/no-confusing-void-expression": "off", + "@typescript-eslint/use-unknown-in-catch-callback-variable": "off", + "@typescript-eslint/restrict-plus-operands": "off", + "@typescript-eslint/no-meaningless-void-operator": "off", + "react-hooks/exhaustive-deps": "off", + "react-hooks/rules-of-hooks": "off", + "react-refresh/only-export-components": "off", + }, + }, +]; From e668c8618532a1cf51465b4aa0cf8d5c0307671b Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 19:59:17 +0530 Subject: [PATCH 080/177] Fix --- web/apps/photos/eslint.config.mjs | 6 ++++++ .../photos/src/components/Collections/CollectionNamer.tsx | 2 ++ web/apps/photos/src/components/DeleteAccountModal.tsx | 2 +- web/apps/photos/src/components/ItemList.tsx | 6 +++--- web/apps/photos/src/components/PhotoList/dedupe.tsx | 6 +++--- web/apps/photos/src/components/PhotoList/index.tsx | 6 +++--- .../PhotoViewer/FileInfo/FileNameEditDialog.tsx | 2 ++ web/apps/photos/src/components/PhotoViewer/index.tsx | 5 +++-- web/apps/photos/src/pages/gallery.tsx | 2 +- web/apps/photos/src/services/collectionService.ts | 2 ++ web/apps/photos/src/services/export/index.ts | 2 +- web/apps/photos/src/types/Notification/index.tsx | 8 ++++---- 12 files changed, 31 insertions(+), 18 deletions(-) diff --git a/web/apps/photos/eslint.config.mjs b/web/apps/photos/eslint.config.mjs index 1cd39b9d11..5ca76ecd4d 100644 --- a/web/apps/photos/eslint.config.mjs +++ b/web/apps/photos/eslint.config.mjs @@ -49,6 +49,12 @@ export default [ "react-hooks/exhaustive-deps": "off", "react-hooks/rules-of-hooks": "off", "react-refresh/only-export-components": "off", + /** TODO: New during eslint 8=>9 migration */ + "@typescript-eslint/no-unused-expressions": "off", + // "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/return-await": "off", + "@typescript-eslint/prefer-regexp-exec": "off", + "@typescript-eslint/no-require-imports": "off", }, }, ]; diff --git a/web/apps/photos/src/components/Collections/CollectionNamer.tsx b/web/apps/photos/src/components/Collections/CollectionNamer.tsx index bf64267499..c9429801ba 100644 --- a/web/apps/photos/src/components/Collections/CollectionNamer.tsx +++ b/web/apps/photos/src/components/Collections/CollectionNamer.tsx @@ -1,4 +1,5 @@ import { TitledMiniDialog } from "@/base/components/MiniDialog"; +import log from "@/base/log"; import SingleInputForm, { type SingleInputFormProps, } from "@ente/shared/components/SingleInputForm"; @@ -34,6 +35,7 @@ export default function CollectionNamer({ attributes, ...props }: Props) { attributes.callback(albumName); props.onHide(); } catch (e) { + log.error(e); setFieldError(t("generic_error_retry")); } }; diff --git a/web/apps/photos/src/components/DeleteAccountModal.tsx b/web/apps/photos/src/components/DeleteAccountModal.tsx index 63dcd4d6a3..3af2eb1c26 100644 --- a/web/apps/photos/src/components/DeleteAccountModal.tsx +++ b/web/apps/photos/src/components/DeleteAccountModal.tsx @@ -144,7 +144,7 @@ const DeleteAccountModal = ({ open, onClose }: Iprops) => { errors, handleChange, handleSubmit, - }): JSX.Element => ( + }): React.JSX.Element => ( { items: T[]; generateItemKey: (item: T) => string | number; getItemTitle: (item: T) => string; - renderListItem: (item: T) => JSX.Element; + renderListItem: (item: T) => React.JSX.Element; maxHeight?: number; itemSize?: number; } interface ItemData { - renderListItem: (item: T) => JSX.Element; + renderListItem: (item: T) => React.JSX.Element; getItemTitle: (item: T) => string; items: T[]; } const createItemData: ( - renderListItem: (item: T) => JSX.Element, + renderListItem: (item: T) => React.JSX.Element, getItemTitle: (item: T) => string, items: T[], ) => ItemData = memoize((renderListItem, getItemTitle, items) => ({ diff --git a/web/apps/photos/src/components/PhotoList/dedupe.tsx b/web/apps/photos/src/components/PhotoList/dedupe.tsx index 6cc550f43e..46a0b53bf5 100644 --- a/web/apps/photos/src/components/PhotoList/dedupe.tsx +++ b/web/apps/photos/src/components/PhotoList/dedupe.tsx @@ -147,7 +147,7 @@ interface Props { file: EnteFile, index: number, isScrolling?: boolean, - ) => JSX.Element; + ) => React.JSX.Element; activeCollectionID: number; } @@ -158,7 +158,7 @@ interface ItemData { renderListItem: ( timeStampListItem: TimeStampListItem, isScrolling?: boolean, - ) => JSX.Element; + ) => React.JSX.Element; } const createItemData = memoize( @@ -169,7 +169,7 @@ const createItemData = memoize( renderListItem: ( timeStampListItem: TimeStampListItem, isScrolling?: boolean, - ) => JSX.Element, + ) => React.JSX.Element, ): ItemData => ({ timeStampList, columns, diff --git a/web/apps/photos/src/components/PhotoList/index.tsx b/web/apps/photos/src/components/PhotoList/index.tsx index cdb55fa85c..27dfcda6d9 100644 --- a/web/apps/photos/src/components/PhotoList/index.tsx +++ b/web/apps/photos/src/components/PhotoList/index.tsx @@ -197,7 +197,7 @@ type Props = Pick & { file: EnteFile, index: number, isScrolling?: boolean, - ) => JSX.Element; + ) => React.JSX.Element; activeCollectionID: number; activePersonID?: string; }; @@ -209,7 +209,7 @@ interface ItemData { renderListItem: ( timeStampListItem: TimeStampListItem, isScrolling?: boolean, - ) => JSX.Element; + ) => React.JSX.Element; } const createItemData = memoize( @@ -220,7 +220,7 @@ const createItemData = memoize( renderListItem: ( timeStampListItem: TimeStampListItem, isScrolling?: boolean, - ) => JSX.Element, + ) => React.JSX.Element, ): ItemData => ({ timeStampList, columns, diff --git a/web/apps/photos/src/components/PhotoViewer/FileInfo/FileNameEditDialog.tsx b/web/apps/photos/src/components/PhotoViewer/FileInfo/FileNameEditDialog.tsx index 8dbc28dedd..578a80172c 100644 --- a/web/apps/photos/src/components/PhotoViewer/FileInfo/FileNameEditDialog.tsx +++ b/web/apps/photos/src/components/PhotoViewer/FileInfo/FileNameEditDialog.tsx @@ -1,4 +1,5 @@ import { TitledMiniDialog } from "@/base/components/MiniDialog"; +import log from "@/base/log"; import { photosDialogZIndex } from "@/new/photos/components/utils/z-index"; import SingleInputForm, { type SingleInputFormProps, @@ -20,6 +21,7 @@ export const FileNameEditDialog = ({ await saveEdits(filename); closeEditMode(); } catch (e) { + log.error(e); setFieldError(t("generic_error_retry")); } }; diff --git a/web/apps/photos/src/components/PhotoViewer/index.tsx b/web/apps/photos/src/components/PhotoViewer/index.tsx index 0c8d6c9a41..beff8d012b 100644 --- a/web/apps/photos/src/components/PhotoViewer/index.tsx +++ b/web/apps/photos/src/components/PhotoViewer/index.tsx @@ -426,6 +426,7 @@ function PhotoViewer(props: PhotoViewerProps) { if (ele) { const rect = ele.getBoundingClientRect(); const pageYScroll = + // eslint-disable-next-line @typescript-eslint/no-deprecated window.pageYOffset || document.documentElement.scrollTop; return { @@ -435,7 +436,7 @@ function PhotoViewer(props: PhotoViewerProps) { }; } return null; - } catch (e) { + } catch { return null; } }, @@ -694,7 +695,7 @@ function PhotoViewer(props: PhotoViewerProps) { file.metadata.title, ); await downloadSingleFile(file, setSingleFileDownloadProgress); - } catch (e) { + } catch { // do nothing } } diff --git a/web/apps/photos/src/pages/gallery.tsx b/web/apps/photos/src/pages/gallery.tsx index c74fb6d1bc..19428b1563 100644 --- a/web/apps/photos/src/pages/gallery.tsx +++ b/web/apps/photos/src/pages/gallery.tsx @@ -327,7 +327,7 @@ export default function Gallery() { try { await getRecoveryKey(); return true; - } catch (e) { + } catch { logout(); return false; } diff --git a/web/apps/photos/src/services/collectionService.ts b/web/apps/photos/src/services/collectionService.ts index ac8e02f5d5..a4eda9232c 100644 --- a/web/apps/photos/src/services/collectionService.ts +++ b/web/apps/photos/src/services/collectionService.ts @@ -1005,6 +1005,8 @@ export const sortCollectionSummaries = ( return (b.updationTime ?? 0) - (a.updationTime ?? 0); } }) + // TODO: + // eslint-disable-next-line no-constant-binary-expression .sort((a, b) => b.order ?? 0 - a.order ?? 0) .sort( (a, b) => diff --git a/web/apps/photos/src/services/export/index.ts b/web/apps/photos/src/services/export/index.ts index 27a28d456e..c68593eff3 100644 --- a/web/apps/photos/src/services/export/index.ts +++ b/web/apps/photos/src/services/export/index.ts @@ -1407,7 +1407,7 @@ export const isLivePhotoExportName = (exportName: string) => { try { JSON.parse(exportName); return true; - } catch (e) { + } catch { return false; } }; diff --git a/web/apps/photos/src/types/Notification/index.tsx b/web/apps/photos/src/types/Notification/index.tsx index b38fff9537..6883c6bb92 100644 --- a/web/apps/photos/src/types/Notification/index.tsx +++ b/web/apps/photos/src/types/Notification/index.tsx @@ -8,8 +8,8 @@ export type NotificationAttributes = interface MessageSubTextNotificationAttributes { startIcon?: ReactNode; variant: ButtonProps["color"]; - message?: JSX.Element | string; - subtext?: JSX.Element | string; + message?: React.JSX.Element | string; + subtext?: React.JSX.Element | string; title?: never; caption?: never; onClick: () => void; @@ -19,8 +19,8 @@ interface MessageSubTextNotificationAttributes { interface TitleCaptionNotificationAttributes { startIcon?: ReactNode; variant: ButtonProps["color"]; - title?: JSX.Element | string; - caption?: JSX.Element | string; + title?: React.JSX.Element | string; + caption?: React.JSX.Element | string; message?: never; subtext?: never; onClick: () => void; From 9e2a70e469b9b284f8e5888e5ea49e0a12de8086 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 20:04:52 +0530 Subject: [PATCH 081/177] tsc on build-config --- web/packages/build-config/eslintrc-react.mjs | 2 -- web/packages/build-config/tsconfig.json | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index b1193b78ad..22b634d940 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -1,5 +1,3 @@ -// @ts-check - import reactPlugin from "eslint-plugin-react"; import hooksPlugin from "eslint-plugin-react-hooks"; import reactRefreshPlugin from "eslint-plugin-react-refresh"; diff --git a/web/packages/build-config/tsconfig.json b/web/packages/build-config/tsconfig.json index 88d20f5430..3828ee1917 100644 --- a/web/packages/build-config/tsconfig.json +++ b/web/packages/build-config/tsconfig.json @@ -1,11 +1,12 @@ { /* A minimal tsconfig so that we can run tsc on the build-config itself. */ "compilerOptions": { + "target": "ES2023", "module": "ESNext", "moduleResolution": "bundler", "noEmit": true, "checkJs": true, "esModuleInterop": true }, - "include": ["*.js", "eslintrc-*.mjs"] + "include": ["eslintrc-base.mjs"] } From 886a0ddb259a27153056714665729ef87d3de8f4 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 19 Nov 2024 20:10:24 +0530 Subject: [PATCH 082/177] Update --- web/yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/yarn.lock b/web/yarn.lock index 0fd4d9642a..cc27ea6ae4 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -217,7 +217,7 @@ source-map "^0.5.7" stylis "4.2.0" -"@emotion/cache@^11.11.0", "@emotion/cache@^11.13.0", "@emotion/cache@^11.4.0": +"@emotion/cache@11.13.1", "@emotion/cache@^11.11.0", "@emotion/cache@^11.13.0", "@emotion/cache@^11.4.0": version "11.13.1" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.13.1.tgz#fecfc54d51810beebf05bf2a161271a1a91895d7" integrity sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw== @@ -245,7 +245,7 @@ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== -"@emotion/react@11.13.3", "@emotion/react@^11.8.1": +"@emotion/react@11.13.3", "@emotion/react@^11.13.3", "@emotion/react@^11.8.1": version "11.13.3" resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.13.3.tgz#a69d0de2a23f5b48e0acf210416638010e4bd2e4" integrity sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg== From 7c2b8422a8e83ba4134e457fa67098228d76e502 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 07:45:25 +0530 Subject: [PATCH 083/177] Dot's no longer needed From docs: > If you are using a flat configuration file(eslint.config.js), you can also omit the file arguments and ESLint will use . --- web/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/package.json b/web/package.json index 7a8ab23be3..8fed07bf19 100644 --- a/web/package.json +++ b/web/package.json @@ -20,8 +20,8 @@ "dev:cast": "yarn workspace cast next dev -p 3001", "dev:payments": "yarn workspace payments dev", "dev:photos": "yarn workspace photos next dev -p 3000", - "lint": "concurrently --names 'prettier,eslint,tsc' \"yarn prettier --check --log-level warn .\" \"yarn workspaces run eslint .\" \"yarn workspaces run tsc\"", - "lint-fix": "concurrently --names 'prettier,eslint,tsc' \"yarn prettier --write --log-level warn .\" \"yarn workspaces run eslint --fix .\" \"yarn workspaces run tsc\"", + "lint": "concurrently --names 'prettier,eslint,tsc' \"yarn prettier --check --log-level warn .\" \"yarn workspaces run eslint\" \"yarn workspaces run tsc\"", + "lint-fix": "concurrently --names 'prettier,eslint,tsc' \"yarn prettier --write --log-level warn .\" \"yarn workspaces run eslint --fix\" \"yarn workspaces run tsc\"", "preview": "yarn preview:photos", "preview:accounts": "yarn build:accounts && python3 -m http.server -d apps/accounts/out 3001", "preview:auth": "yarn build:auth && python3 -m http.server -d apps/auth/out 3000", From 89b90c93323ac9f080e1284b0be2fb7467a6c30d Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 07:53:02 +0530 Subject: [PATCH 084/177] Fix overwriting --- web/packages/build-config/eslintrc-react.mjs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index 22b634d940..6cae8c620e 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -5,15 +5,16 @@ import config from "./eslintrc-base.mjs"; export default [ ...config, + reactPlugin.configs.flat.recommended, + reactPlugin.configs.flat["jsx-runtime"], { - files: ["**/*.{jsx,tsx}"], - ...reactPlugin.configs.flat.recommended, - ...reactPlugin.configs.flat["jsx-runtime"], settings: { react: { version: "detect", }, }, + }, + { plugins: { "react-hooks": hooksPlugin, "react-refresh": reactRefreshPlugin, From c211a829f86960da2b592f3343255d6c5bfd77cc Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 07:56:54 +0530 Subject: [PATCH 085/177] Bring back overrides --- web/packages/build-config/eslintrc-react.mjs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index 6cae8c620e..2aa1b49c18 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -13,6 +13,11 @@ export default [ version: "detect", }, }, + rules: { + // The rule is misguided - only the opener should be omitted, not + // the referrer. + "react/jsx-no-target-blank": ["warn", { allowReferrer: true }], + }, }, { plugins: { @@ -36,9 +41,6 @@ export default [ // module.exports = { // rules: { -// /* The rule is misguided - only the opener should be omitted, not the -// referrer. */ -// "react/jsx-no-target-blank": ["warn", { allowReferrer: true }], // /* Otherwise we need to do unnecessary boilerplating when using memo. */ // "react/display-name": "off", // /* Next.js supports the JSX transform introduced in React 17 */ From 916b56fa65d4b3eb067e4ff799545b3e05649cd6 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 07:59:57 +0530 Subject: [PATCH 086/177] The files filter has a big impact on speed --- web/packages/build-config/eslintrc-react.mjs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index 2aa1b49c18..3b02f53d1c 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -5,9 +5,16 @@ import config from "./eslintrc-base.mjs"; export default [ ...config, - reactPlugin.configs.flat.recommended, - reactPlugin.configs.flat["jsx-runtime"], { + files: ["**/*.{jsx,tsx}"], + ...reactPlugin.configs.flat.recommended, + }, + { + files: ["**/*.{jsx,tsx}"], + ...reactPlugin.configs.flat["jsx-runtime"], + }, + { + files: ["**/*.{jsx,tsx}"], settings: { react: { version: "detect", From 8f43f27fcfdbdfe26f7cdcabc922d89fcea7d783 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 08:03:56 +0530 Subject: [PATCH 087/177] Tweak ignores --- web/apps/photos/eslint.config.mjs | 2 +- web/packages/build-config/eslintrc-next-app.mjs | 11 ++++++++++- web/packages/build-config/eslintrc-react.mjs | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/web/apps/photos/eslint.config.mjs b/web/apps/photos/eslint.config.mjs index 5ca76ecd4d..fe6f0ab98b 100644 --- a/web/apps/photos/eslint.config.mjs +++ b/web/apps/photos/eslint.config.mjs @@ -3,7 +3,7 @@ import config from "@/build-config/eslintrc-next-app.mjs"; export default [ ...config, { - ignores: ["thirdparty", "public"], + ignores: ["thirdparty"], }, { rules: { diff --git a/web/packages/build-config/eslintrc-next-app.mjs b/web/packages/build-config/eslintrc-next-app.mjs index 32e17fca5a..5ad5e91554 100644 --- a/web/packages/build-config/eslintrc-next-app.mjs +++ b/web/packages/build-config/eslintrc-next-app.mjs @@ -5,5 +5,14 @@ import config from "./eslintrc-react.mjs"; // A base config for Next.js apps. export default [ ...config, - { ignores: ["out", ".next", "next.config.js", "next-env.d.ts"] }, + { + ignores: [ + "out", + ".next", + "public", + ".env*", + "next.config.js", + "next-env.d.ts", + ], + }, ]; diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index 3b02f53d1c..fd88b7caaf 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -27,6 +27,7 @@ export default [ }, }, { + files: ["**/*.{jsx,tsx}"], plugins: { "react-hooks": hooksPlugin, "react-refresh": reactRefreshPlugin, From e55181a9beb8e9c6bd36c28abe0a935a6c13d1d3 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 08:07:04 +0530 Subject: [PATCH 088/177] Bring back the rest that are needed --- web/packages/build-config/eslintrc-react.mjs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/web/packages/build-config/eslintrc-react.mjs b/web/packages/build-config/eslintrc-react.mjs index fd88b7caaf..92a848a92c 100644 --- a/web/packages/build-config/eslintrc-react.mjs +++ b/web/packages/build-config/eslintrc-react.mjs @@ -24,6 +24,11 @@ export default [ // The rule is misguided - only the opener should be omitted, not // the referrer. "react/jsx-no-target-blank": ["warn", { allowReferrer: true }], + // Otherwise we need to do unnecessary boilerplating when using memo. + "react/display-name": "off", + // Without React in scope, this rule starts causing false positives + // (We don't use prop types in our own code anyways). + "react/prop-types": "off", }, }, { @@ -46,15 +51,3 @@ export default [ }, }, ]; - -// module.exports = { -// rules: { -// /* Otherwise we need to do unnecessary boilerplating when using memo. */ -// "react/display-name": "off", -// /* Next.js supports the JSX transform introduced in React 17 */ -// "react/react-in-jsx-scope": "off", -// /* Without React in scope, this rule starts causing false positives (We -// don't use prop types in our own code anyways). */ -// "react/prop-types": "off", -// }, -// }; From f69832ea165b4b60d1740e67d2b5c91bf742b5a3 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 08:13:39 +0530 Subject: [PATCH 089/177] Fix updated rule --- web/apps/cast/src/services/pair.ts | 5 ----- web/packages/build-config/eslintrc-base.mjs | 5 +++++ web/packages/new/photos/services/user-entity/index.ts | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/web/apps/cast/src/services/pair.ts b/web/apps/cast/src/services/pair.ts index ae0a8fd98c..cc30ef1d1c 100644 --- a/web/apps/cast/src/services/pair.ts +++ b/web/apps/cast/src/services/pair.ts @@ -79,11 +79,6 @@ export const register = async (): Promise => { // Register keypair with museum to get a pairing code. let pairingCode: string | undefined; - // [TODO: spurious while(true) eslint warning]. - // eslint has fixed this spurious warning, but we're not on the latest - // version yet, so add a disable. - // https://github.com/eslint/eslint/pull/18286 - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while (true) { try { pairingCode = await castGateway.registerDevice(publicKeyB64); diff --git a/web/packages/build-config/eslintrc-base.mjs b/web/packages/build-config/eslintrc-base.mjs index 78f2d1363f..779cfe46b2 100644 --- a/web/packages/build-config/eslintrc-base.mjs +++ b/web/packages/build-config/eslintrc-base.mjs @@ -64,6 +64,11 @@ export default tseslint.config( // to do at one point), rely on the JS's native undefined property // access exception since that conveys more information in the logs. "@typescript-eslint/no-non-null-assertion": "off", + // Allow `while(true)` etc. + "@typescript-eslint/no-unnecessary-condition": [ + "error", + { allowConstantLoopConditions: true }, + ], }, }, ); diff --git a/web/packages/new/photos/services/user-entity/index.ts b/web/packages/new/photos/services/user-entity/index.ts index ea6dcb57e3..5321a2c642 100644 --- a/web/packages/new/photos/services/user-entity/index.ts +++ b/web/packages/new/photos/services/user-entity/index.ts @@ -134,7 +134,6 @@ export const pullUserEntities = async ( const entityKeyB64 = await getOrCreateEntityKeyB64(type, masterKey); let sinceTime = (await savedLatestUpdatedAt(type)) ?? 0; - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while (true) { const diff = await userEntityDiff(type, sinceTime, entityKeyB64); if (diff.length == 0) break; From 3dd136bdf399a5139c2476ef4c17789d114012aa Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 08:19:46 +0530 Subject: [PATCH 090/177] Tweaks --- web/apps/payments/package.json | 2 +- web/packages/base/package.json | 2 +- web/packages/build-config/eslintrc-vite-app.mjs | 2 +- web/yarn.lock | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/web/apps/payments/package.json b/web/apps/payments/package.json index 412da2d265..02e87ceaa1 100644 --- a/web/apps/payments/package.json +++ b/web/apps/payments/package.json @@ -18,6 +18,6 @@ "@types/react": "^18", "@types/react-dom": "^18", "@vitejs/plugin-react": "^4.3", - "vite": "^5.4" + "vite": "^5.4.11" } } diff --git a/web/packages/base/package.json b/web/packages/base/package.json index 014006e6a8..2bbf7ead27 100644 --- a/web/packages/base/package.json +++ b/web/packages/base/package.json @@ -9,7 +9,7 @@ "@mui/material": "^5.16.6", "comlink": "^4.4.2", "get-user-locale": "^2.3.2", - "i18next": "^23.16.5", + "i18next": "^23.16.6", "i18next-resources-to-backend": "^1.2.1", "is-electron": "^2.2.2", "libsodium-wrappers-sumo": "^0.7.15", diff --git a/web/packages/build-config/eslintrc-vite-app.mjs b/web/packages/build-config/eslintrc-vite-app.mjs index 8421b84060..d3ceea7b50 100644 --- a/web/packages/build-config/eslintrc-vite-app.mjs +++ b/web/packages/build-config/eslintrc-vite-app.mjs @@ -3,4 +3,4 @@ import config from "./eslintrc-react.mjs"; // A base config for Vite apps. -export default [...config, { ignores: ["dist"] }]; +export default [...config, { ignores: ["dist", ".env*"] }]; diff --git a/web/yarn.lock b/web/yarn.lock index cc27ea6ae4..fe0a018809 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -2337,10 +2337,10 @@ i18next-resources-to-backend@^1.2.1: dependencies: "@babel/runtime" "^7.23.2" -i18next@^23.16.5: - version "23.16.5" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.16.5.tgz#53d48ae9f985fd73fc1fcb96e6c7d90ababf0831" - integrity sha512-KTlhE3EP9x6pPTAW7dy0WKIhoCpfOGhRQlO+jttQLgzVaoOjWwBWramu7Pp0i+8wDNduuzXfe3kkVbzrKyrbTA== +i18next@^23.16.6: + version "23.16.6" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.16.6.tgz#85d09d66ec1334fdcc73ca11aa28409ea45df3ab" + integrity sha512-wGdE5rUfkZtrL5k6MCptxbpjmgwku4rBRVU/YOJ7Xfd841fgaZjlxHpVJ5NIz8sfSvAJhEhJrvJ8qE7AWXE4Xg== dependencies: "@babel/runtime" "^7.23.2" @@ -3944,7 +3944,7 @@ uuid@^9.0.1: resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== -vite@^5.4: +vite@^5.4.11: version "5.4.11" resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.11.tgz#3b415cd4aed781a356c1de5a9ebafb837715f6e5" integrity sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q== From 80146522ad21b106577878e0e31074720c8704d3 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 08:28:17 +0530 Subject: [PATCH 091/177] Start fixing --- web/apps/auth/eslint.config.mjs | 2 +- web/apps/auth/src/pages/_app.tsx | 2 +- web/apps/auth/src/pages/auth.tsx | 12 ++++++------ web/apps/auth/src/pages/index.tsx | 5 +---- web/apps/auth/src/pages/share.tsx | 2 +- web/apps/photos/src/pages/_app.tsx | 2 +- 6 files changed, 11 insertions(+), 14 deletions(-) diff --git a/web/apps/auth/eslint.config.mjs b/web/apps/auth/eslint.config.mjs index c61e4cd750..619424e396 100644 --- a/web/apps/auth/eslint.config.mjs +++ b/web/apps/auth/eslint.config.mjs @@ -16,7 +16,7 @@ export default [ "@typescript-eslint/no-unsafe-member-access": "off", "@typescript-eslint/no-unsafe-argument": "off", /** TODO: Disabled as we migrate, try to prune these again */ - "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-unsafe-enum-comparison": "off", "@typescript-eslint/no-unnecessary-type-assertion": "off", "@typescript-eslint/array-type": "off", diff --git a/web/apps/auth/src/pages/_app.tsx b/web/apps/auth/src/pages/_app.tsx index e1bb9353f0..772311ce95 100644 --- a/web/apps/auth/src/pages/_app.tsx +++ b/web/apps/auth/src/pages/_app.tsx @@ -70,7 +70,7 @@ const App: React.FC = ({ Component, pageProps }) => { useEffect(() => { void setupI18n().finally(() => setIsI18nReady(true)); const user = getData(LS_KEYS.USER) as User | undefined | null; - migrateKVToken(user); + void migrateKVToken(user); logStartupBanner(user?.id); HTTPService.setHeaders({ "X-Client-Package": clientPackageName }); logUnhandledErrorsAndRejections(true); diff --git a/web/apps/auth/src/pages/auth.tsx b/web/apps/auth/src/pages/auth.tsx index 0032777ff9..a254d151c9 100644 --- a/web/apps/auth/src/pages/auth.tsx +++ b/web/apps/auth/src/pages/auth.tsx @@ -42,7 +42,7 @@ const Page: React.FC = () => { e.message == CustomError.KEY_MISSING ) { stashRedirect(PAGES.AUTH); - router.push("/"); + void router.push("/"); } else if (e instanceof ApiError && e.httpStatusCode == 401) { // We get back a 401 Unauthorized if the token is not valid. showSessionExpiredDialog(); @@ -184,11 +184,11 @@ const CodeDisplay: React.FC = ({ code }) => { } }; - const copyCode = () => { - navigator.clipboard.writeText(otp); - setHasCopied(true); - setTimeout(() => setHasCopied(false), 2000); - }; + const copyCode = () => + void navigator.clipboard.writeText(otp).then(() => { + setHasCopied(true); + setTimeout(() => setHasCopied(false), 2000); + }); useEffect(() => { // Generate to set the initial otp and nextOTP on component mount. diff --git a/web/apps/auth/src/pages/index.tsx b/web/apps/auth/src/pages/index.tsx index e5540896e3..81dd02b769 100644 --- a/web/apps/auth/src/pages/index.tsx +++ b/web/apps/auth/src/pages/index.tsx @@ -4,10 +4,7 @@ import React, { useEffect } from "react"; const Page: React.FC = () => { const router = useRouter(); - useEffect(() => { - router.push(PAGES.LOGIN); - }, []); - + useEffect(() => void router.push(PAGES.LOGIN), []); return <>; }; diff --git a/web/apps/auth/src/pages/share.tsx b/web/apps/auth/src/pages/share.tsx index 97d32e415d..adb7cf37e0 100644 --- a/web/apps/auth/src/pages/share.tsx +++ b/web/apps/auth/src/pages/share.tsx @@ -89,7 +89,7 @@ const Share: React.FC = () => { ); } }; - decryptCode(); + void decryptCode(); }, []); useEffect(() => { diff --git a/web/apps/photos/src/pages/_app.tsx b/web/apps/photos/src/pages/_app.tsx index e92f092a76..640f0fa5b1 100644 --- a/web/apps/photos/src/pages/_app.tsx +++ b/web/apps/photos/src/pages/_app.tsx @@ -79,7 +79,7 @@ export default function App({ Component, pageProps }: AppProps) { useEffect(() => { void setupI18n().finally(() => setIsI18nReady(true)); const user = getData(LS_KEYS.USER) as User | undefined | null; - migrateKVToken(user); + void migrateKVToken(user); logStartupBanner(user?.id); HTTPService.setHeaders({ "X-Client-Package": clientPackageName }); logUnhandledErrorsAndRejections(true); From 1ec67c6bafb4ec97582be3063c01268be79fe8ec Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 09:07:20 +0530 Subject: [PATCH 092/177] Prune --- web/apps/auth/eslint.config.mjs | 5 ----- web/apps/auth/src/services/remote.ts | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/web/apps/auth/eslint.config.mjs b/web/apps/auth/eslint.config.mjs index 619424e396..9405448dcf 100644 --- a/web/apps/auth/eslint.config.mjs +++ b/web/apps/auth/eslint.config.mjs @@ -16,12 +16,7 @@ export default [ "@typescript-eslint/no-unsafe-member-access": "off", "@typescript-eslint/no-unsafe-argument": "off", /** TODO: Disabled as we migrate, try to prune these again */ - - "@typescript-eslint/no-unsafe-enum-comparison": "off", "@typescript-eslint/no-unnecessary-type-assertion": "off", - "@typescript-eslint/array-type": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-unnecessary-template-expression": "off", "@typescript-eslint/consistent-indexed-object-style": "off", "@typescript-eslint/prefer-promise-reject-errors": "off", "@typescript-eslint/no-useless-constructor": "off", diff --git a/web/apps/auth/src/services/remote.ts b/web/apps/auth/src/services/remote.ts index 23cf643be8..c8e15d2f1d 100644 --- a/web/apps/auth/src/services/remote.ts +++ b/web/apps/auth/src/services/remote.ts @@ -94,6 +94,7 @@ export const getAuthKey = async (): Promise => { } catch (e) { if ( e instanceof ApiError && + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison e.httpStatusCode == HttpStatusCode.NotFound ) { throw Error(CustomError.AUTH_KEY_NOT_FOUND); From 1ddd143b2ef3d3421527b975774b213d54afbe77 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 09:15:18 +0530 Subject: [PATCH 093/177] Context --- web/apps/accounts/src/types/context.ts | 4 ++-- web/apps/auth/eslint.config.mjs | 5 ---- web/apps/auth/src/pages/_app.tsx | 24 ++----------------- web/apps/auth/src/pages/auth.tsx | 2 +- web/apps/auth/src/pages/change-email.tsx | 2 +- web/apps/auth/src/pages/change-password.tsx | 2 +- web/apps/auth/src/pages/credentials.tsx | 2 +- web/apps/auth/src/pages/generate.tsx | 2 +- web/apps/auth/src/pages/login.tsx | 2 +- web/apps/auth/src/pages/passkeys/finish.tsx | 2 +- web/apps/auth/src/pages/passkeys/recover.tsx | 2 +- web/apps/auth/src/pages/recover.tsx | 2 +- web/apps/auth/src/pages/signup.tsx | 2 +- .../auth/src/pages/two-factor/recover.tsx | 2 +- web/apps/auth/src/pages/two-factor/setup.tsx | 2 +- web/apps/auth/src/pages/two-factor/verify.tsx | 2 +- web/apps/auth/src/pages/verify.tsx | 2 +- web/apps/auth/src/types/context.ts | 17 +++++++++++++ web/packages/new/photos/types/context.ts | 3 +-- 19 files changed, 36 insertions(+), 45 deletions(-) create mode 100644 web/apps/auth/src/types/context.ts diff --git a/web/apps/accounts/src/types/context.ts b/web/apps/accounts/src/types/context.ts index 416ff1fcf1..5218ae033f 100644 --- a/web/apps/accounts/src/types/context.ts +++ b/web/apps/accounts/src/types/context.ts @@ -12,7 +12,7 @@ type AppContextT = Omit; export const AppContext = createContext(undefined); /** - * Utility hook to get the {@link AppContextT}, throwing an exception if it is - * not defined. + * Utility hook to get the {@link AppContextT} expected to be available to all + * React components in the Accounts app's React tree. */ export const useAppContext = (): AppContextT => useContext(AppContext)!; diff --git a/web/apps/auth/eslint.config.mjs b/web/apps/auth/eslint.config.mjs index 9405448dcf..d9bbd0401c 100644 --- a/web/apps/auth/eslint.config.mjs +++ b/web/apps/auth/eslint.config.mjs @@ -16,12 +16,7 @@ export default [ "@typescript-eslint/no-unsafe-member-access": "off", "@typescript-eslint/no-unsafe-argument": "off", /** TODO: Disabled as we migrate, try to prune these again */ - "@typescript-eslint/no-unnecessary-type-assertion": "off", - "@typescript-eslint/consistent-indexed-object-style": "off", - "@typescript-eslint/prefer-promise-reject-errors": "off", - "@typescript-eslint/no-useless-constructor": "off", "react-hooks/exhaustive-deps": "off", - "react-refresh/only-export-components": "off", }, }, ]; diff --git a/web/apps/auth/src/pages/_app.tsx b/web/apps/auth/src/pages/_app.tsx index 772311ce95..98feccc295 100644 --- a/web/apps/auth/src/pages/_app.tsx +++ b/web/apps/auth/src/pages/_app.tsx @@ -1,5 +1,4 @@ import { accountLogout } from "@/accounts/services/logout"; -import type { AccountsContextT } from "@/accounts/types/context"; import { clientPackageName, staticAppTitle } from "@/base/app"; import { CustomHead } from "@/base/components/Head"; import { AttributedMiniDialog } from "@/base/components/MiniDialog"; @@ -28,30 +27,11 @@ import { ThemeProvider } from "@mui/material/styles"; import { t } from "i18next"; import type { AppProps } from "next/app"; import { useRouter } from "next/router"; -import React, { - createContext, - useCallback, - useContext, - useEffect, - useState, -} from "react"; +import React, { useCallback, useEffect, useState } from "react"; +import { AppContext } from "types/context"; import "../../public/css/global.css"; -/** - * Properties available via {@link AppContext} to the Auth app's React tree. - */ -type AppContextT = AccountsContextT & { - themeColor: THEME_COLOR; - setThemeColor: (themeColor: THEME_COLOR) => void; -}; - -/** The React {@link Context} available to all pages. */ -export const AppContext = createContext(undefined); - -/** Utility hook to reduce amount of boilerplate in account related pages. */ -export const useAppContext = () => useContext(AppContext)!; - const App: React.FC = ({ Component, pageProps }) => { const router = useRouter(); const [isI18nReady, setIsI18nReady] = useState(false); diff --git a/web/apps/auth/src/pages/auth.tsx b/web/apps/auth/src/pages/auth.tsx index a254d151c9..ee872a3a1f 100644 --- a/web/apps/auth/src/pages/auth.tsx +++ b/web/apps/auth/src/pages/auth.tsx @@ -19,7 +19,7 @@ import { useRouter } from "next/router"; import React, { useEffect, useState } from "react"; import { generateOTPs, type Code } from "services/code"; import { getAuthCodes } from "services/remote"; -import { useAppContext } from "./_app"; +import { useAppContext } from "types/context"; const Page: React.FC = () => { const { logout, showNavBar, showMiniDialog } = useAppContext(); diff --git a/web/apps/auth/src/pages/change-email.tsx b/web/apps/auth/src/pages/change-email.tsx index cef4716203..74b094aeed 100644 --- a/web/apps/auth/src/pages/change-email.tsx +++ b/web/apps/auth/src/pages/change-email.tsx @@ -1,5 +1,5 @@ import Page_ from "@/accounts/pages/change-email"; -import { useAppContext } from "./_app"; +import { useAppContext } from "types/context"; const Page = () => ; diff --git a/web/apps/auth/src/pages/change-password.tsx b/web/apps/auth/src/pages/change-password.tsx index 2232edc6bc..3ce2c6267d 100644 --- a/web/apps/auth/src/pages/change-password.tsx +++ b/web/apps/auth/src/pages/change-password.tsx @@ -1,5 +1,5 @@ import Page_ from "@/accounts/pages/change-password"; -import { useAppContext } from "./_app"; +import { useAppContext } from "types/context"; const Page = () => ; diff --git a/web/apps/auth/src/pages/credentials.tsx b/web/apps/auth/src/pages/credentials.tsx index fa3cc8fad6..17dd9fef70 100644 --- a/web/apps/auth/src/pages/credentials.tsx +++ b/web/apps/auth/src/pages/credentials.tsx @@ -1,5 +1,5 @@ import Page_ from "@/accounts/pages/credentials"; -import { useAppContext } from "./_app"; +import { useAppContext } from "types/context"; const Page = () => ; diff --git a/web/apps/auth/src/pages/generate.tsx b/web/apps/auth/src/pages/generate.tsx index a82d0a46f3..12723d0b0a 100644 --- a/web/apps/auth/src/pages/generate.tsx +++ b/web/apps/auth/src/pages/generate.tsx @@ -1,5 +1,5 @@ import Page_ from "@/accounts/pages/generate"; -import { useAppContext } from "./_app"; +import { useAppContext } from "types/context"; const Page = () => ; diff --git a/web/apps/auth/src/pages/login.tsx b/web/apps/auth/src/pages/login.tsx index a61718a309..c373229f00 100644 --- a/web/apps/auth/src/pages/login.tsx +++ b/web/apps/auth/src/pages/login.tsx @@ -1,5 +1,5 @@ import Page_ from "@/accounts/pages/login"; -import { useAppContext } from "./_app"; +import { useAppContext } from "types/context"; const Page = () => ; diff --git a/web/apps/auth/src/pages/passkeys/finish.tsx b/web/apps/auth/src/pages/passkeys/finish.tsx index e75df9677b..e7c08cae2c 100644 --- a/web/apps/auth/src/pages/passkeys/finish.tsx +++ b/web/apps/auth/src/pages/passkeys/finish.tsx @@ -1,5 +1,5 @@ import Page_ from "@/accounts/pages/passkeys/finish"; -import { useAppContext } from "../_app"; +import { useAppContext } from "types/context"; const Page = () => ; diff --git a/web/apps/auth/src/pages/passkeys/recover.tsx b/web/apps/auth/src/pages/passkeys/recover.tsx index d7ac1c30df..a156314ed5 100644 --- a/web/apps/auth/src/pages/passkeys/recover.tsx +++ b/web/apps/auth/src/pages/passkeys/recover.tsx @@ -1,5 +1,5 @@ import Page_ from "@/accounts/pages/two-factor/recover"; -import { useAppContext } from "../_app"; +import { useAppContext } from "types/context"; const Page = () => ( diff --git a/web/apps/auth/src/pages/recover.tsx b/web/apps/auth/src/pages/recover.tsx index 3fb3866a8a..62ceb6a63a 100644 --- a/web/apps/auth/src/pages/recover.tsx +++ b/web/apps/auth/src/pages/recover.tsx @@ -1,5 +1,5 @@ import Page_ from "@/accounts/pages/recover"; -import { useAppContext } from "./_app"; +import { useAppContext } from "types/context"; const Page = () => ; diff --git a/web/apps/auth/src/pages/signup.tsx b/web/apps/auth/src/pages/signup.tsx index 37386b9b4e..e1bb54b598 100644 --- a/web/apps/auth/src/pages/signup.tsx +++ b/web/apps/auth/src/pages/signup.tsx @@ -1,5 +1,5 @@ import Page_ from "@/accounts/pages/signup"; -import { useAppContext } from "./_app"; +import { useAppContext } from "types/context"; const Page = () => ; diff --git a/web/apps/auth/src/pages/two-factor/recover.tsx b/web/apps/auth/src/pages/two-factor/recover.tsx index 1c36e691ec..dbf10270fb 100644 --- a/web/apps/auth/src/pages/two-factor/recover.tsx +++ b/web/apps/auth/src/pages/two-factor/recover.tsx @@ -1,5 +1,5 @@ import Page_ from "@/accounts/pages/two-factor/recover"; -import { useAppContext } from "../_app"; +import { useAppContext } from "types/context"; const Page = () => ; diff --git a/web/apps/auth/src/pages/two-factor/setup.tsx b/web/apps/auth/src/pages/two-factor/setup.tsx index 404fbb271a..7e6691e98e 100644 --- a/web/apps/auth/src/pages/two-factor/setup.tsx +++ b/web/apps/auth/src/pages/two-factor/setup.tsx @@ -1,5 +1,5 @@ import Page_ from "@/accounts/pages/two-factor/setup"; -import { useAppContext } from "../_app"; +import { useAppContext } from "types/context"; const Page = () => ; diff --git a/web/apps/auth/src/pages/two-factor/verify.tsx b/web/apps/auth/src/pages/two-factor/verify.tsx index b6fc679f8c..0a449ad470 100644 --- a/web/apps/auth/src/pages/two-factor/verify.tsx +++ b/web/apps/auth/src/pages/two-factor/verify.tsx @@ -1,5 +1,5 @@ import Page_ from "@/accounts/pages/two-factor/verify"; -import { useAppContext } from "../_app"; +import { useAppContext } from "types/context"; const Page = () => ; diff --git a/web/apps/auth/src/pages/verify.tsx b/web/apps/auth/src/pages/verify.tsx index 64aeb883a9..572f40aa99 100644 --- a/web/apps/auth/src/pages/verify.tsx +++ b/web/apps/auth/src/pages/verify.tsx @@ -1,5 +1,5 @@ import Page_ from "@/accounts/pages/verify"; -import { useAppContext } from "./_app"; +import { useAppContext } from "types/context"; const Page = () => ; diff --git a/web/apps/auth/src/types/context.ts b/web/apps/auth/src/types/context.ts new file mode 100644 index 0000000000..c5f95ba5c7 --- /dev/null +++ b/web/apps/auth/src/types/context.ts @@ -0,0 +1,17 @@ +import type { AccountsContextT } from "@/accounts/types/context"; +import { THEME_COLOR } from "@ente/shared/themes/constants"; +import { createContext, useContext } from "react"; + +/** + * Properties available via {@link AppContext} to the Auth app's React tree. + */ +type AppContextT = AccountsContextT & { + themeColor: THEME_COLOR; + setThemeColor: (themeColor: THEME_COLOR) => void; +}; + +/** The React {@link Context} available to all pages. */ +export const AppContext = createContext(undefined); + +/** Utility hook to reduce amount of boilerplate in account related pages. */ +export const useAppContext = () => useContext(AppContext)!; diff --git a/web/packages/new/photos/types/context.ts b/web/packages/new/photos/types/context.ts index 903eee593f..65dd7ad76e 100644 --- a/web/packages/new/photos/types/context.ts +++ b/web/packages/new/photos/types/context.ts @@ -35,8 +35,7 @@ export type AppContextT = AccountsContextT & { export const AppContext = createContext(undefined); /** - * Utility hook to get the photos {@link AppContextT}, throwing an exception if - * it is not defined. + * Utility hook to get the photos {@link AppContextT}. * * This context is provided at the top level _app component for the photos app, * and thus is available to all React components in the Photos app's React tree. From af82ef194d51e543762585d32c5003e42b692ea4 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 09:28:55 +0530 Subject: [PATCH 094/177] Not awaiting promises to retain existing behavior --- web/packages/accounts/api/srp.ts | 1 + web/packages/accounts/api/user.ts | 1 + web/packages/accounts/components/Login.tsx | 4 ++-- .../accounts/components/LoginComponents.tsx | 4 ++-- web/packages/accounts/components/SignUp.tsx | 2 +- web/packages/accounts/eslint.config.mjs | 2 -- web/packages/accounts/pages/change-email.tsx | 4 ++-- web/packages/accounts/pages/change-password.tsx | 4 ++-- web/packages/accounts/pages/credentials.tsx | 14 +++++++------- web/packages/accounts/pages/generate.tsx | 10 +++++----- web/packages/accounts/pages/login.tsx | 4 ++-- web/packages/accounts/pages/passkeys/finish.tsx | 6 ++---- web/packages/accounts/pages/recover.tsx | 12 ++++++------ web/packages/accounts/pages/signup.tsx | 4 ++-- web/packages/accounts/pages/two-factor/recover.tsx | 9 +++++---- web/packages/accounts/pages/two-factor/setup.tsx | 4 ++-- web/packages/accounts/pages/two-factor/verify.tsx | 9 +++++---- web/packages/accounts/pages/verify.tsx | 14 ++++++++------ 18 files changed, 55 insertions(+), 53 deletions(-) diff --git a/web/packages/accounts/api/srp.ts b/web/packages/accounts/api/srp.ts index 40be7b00e3..cec06e837b 100644 --- a/web/packages/accounts/api/srp.ts +++ b/web/packages/accounts/api/srp.ts @@ -166,6 +166,7 @@ export const verifySRPSession = async ( log.error("verifySRPSession failed", e); if ( e instanceof ApiError && + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison e.httpStatusCode === HttpStatusCode.Unauthorized ) { throw Error(CustomError.INCORRECT_PASSWORD); diff --git a/web/packages/accounts/api/user.ts b/web/packages/accounts/api/user.ts index 763dbb190a..02148bf41a 100644 --- a/web/packages/accounts/api/user.ts +++ b/web/packages/accounts/api/user.ts @@ -61,6 +61,7 @@ export const logout = async () => { // ignore if unauthorized, can be triggered during on token expiry. else if ( e instanceof ApiError && + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison e.httpStatusCode === HttpStatusCode.Unauthorized ) { return; diff --git a/web/packages/accounts/components/Login.tsx b/web/packages/accounts/components/Login.tsx index f8cc69d957..e41576af67 100644 --- a/web/packages/accounts/components/Login.tsx +++ b/web/packages/accounts/components/Login.tsx @@ -31,10 +31,10 @@ export const Login: React.FC = ({ signUp, host }) => { log.debug(() => ["srpAttributes", JSON.stringify(srpAttributes)]); if (!srpAttributes || srpAttributes.isEmailMFAEnabled) { await sendOtt(email); - router.push(PAGES.VERIFY); + void router.push(PAGES.VERIFY); } else { setData(LS_KEYS.SRP_ATTRIBUTES, srpAttributes); - router.push(PAGES.CREDENTIALS); + void router.push(PAGES.CREDENTIALS); } } catch (e) { if (e instanceof Error) { diff --git a/web/packages/accounts/components/LoginComponents.tsx b/web/packages/accounts/components/LoginComponents.tsx index 3063910a2d..178ae5df35 100644 --- a/web/packages/accounts/components/LoginComponents.tsx +++ b/web/packages/accounts/components/LoginComponents.tsx @@ -101,7 +101,7 @@ export const VerifyingPasskey: React.FC = ({ const response = await checkPasskeyVerificationStatus(passkeySessionID); if (!response) setVerificationStatus("pending"); - else router.push(await saveCredentialsAndNavigateTo(response)); + else void router.push(await saveCredentialsAndNavigateTo(response)); } catch (e) { log.error("Passkey verification status check failed", e); showMiniDialog( @@ -115,7 +115,7 @@ export const VerifyingPasskey: React.FC = ({ }; const handleRecover = () => { - router.push("/passkeys/recover"); + void router.push("/passkeys/recover"); }; return ( diff --git a/web/packages/accounts/components/SignUp.tsx b/web/packages/accounts/components/SignUp.tsx index b5c98f5b27..0d2efef62c 100644 --- a/web/packages/accounts/components/SignUp.tsx +++ b/web/packages/accounts/components/SignUp.tsx @@ -109,7 +109,7 @@ export const SignUp: React.FC = ({ router, login, host }) => { masterKey, ); setJustSignedUp(true); - router.push(PAGES.VERIFY); + void router.push(PAGES.VERIFY); } catch (e) { setFieldError("confirm", t("PASSWORD_GENERATION_FAILED")); throw e; diff --git a/web/packages/accounts/eslint.config.mjs b/web/packages/accounts/eslint.config.mjs index 823d196bed..08d3b6bc4d 100644 --- a/web/packages/accounts/eslint.config.mjs +++ b/web/packages/accounts/eslint.config.mjs @@ -17,8 +17,6 @@ export default [ "@typescript-eslint/no-unsafe-argument": "off", "@typescript-eslint/no-unsafe-call": "off", /** TODO: Disabled as we migrate, try to prune these again */ - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-unsafe-enum-comparison": "off", "@typescript-eslint/no-unnecessary-type-assertion": "off", "@typescript-eslint/array-type": "off", "@typescript-eslint/no-empty-function": "off", diff --git a/web/packages/accounts/pages/change-email.tsx b/web/packages/accounts/pages/change-email.tsx index 94b182e01a..019ee92556 100644 --- a/web/packages/accounts/pages/change-email.tsx +++ b/web/packages/accounts/pages/change-email.tsx @@ -24,7 +24,7 @@ const Page: React.FC = () => { useEffect(() => { const user = getData(LS_KEYS.USER); if (!user?.token) { - router.push("/"); + void router.push("/"); } }, []); @@ -83,7 +83,7 @@ const ChangeEmailForm: React.FC = () => { await changeEmail(email, ott!); await setLSUser({ ...getData(LS_KEYS.USER), email }); setLoading(false); - goToApp(); + void goToApp(); } catch (e) { setLoading(false); setFieldError("ott", t("INCORRECT_CODE")); diff --git a/web/packages/accounts/pages/change-password.tsx b/web/packages/accounts/pages/change-password.tsx index 54870dcd3c..dc9f92d2b7 100644 --- a/web/packages/accounts/pages/change-password.tsx +++ b/web/packages/accounts/pages/change-password.tsx @@ -47,7 +47,7 @@ const Page: React.FC = () => { setUser(user); if (!user?.token) { stashRedirect(PAGES.CHANGE_PASSWORD); - router.push("/"); + void router.push("/"); } else { setToken(user.token); } @@ -132,7 +132,7 @@ const Page: React.FC = () => { const redirectToAppHome = () => { setData(LS_KEYS.SHOW_BACK_BUTTON, { value: true }); - router.push(appHomeRoute); + void router.push(appHomeRoute); }; // TODO: Handle the case where user is not loaded yet. diff --git a/web/packages/accounts/pages/credentials.tsx b/web/packages/accounts/pages/credentials.tsx index 01f9174a0b..7887a57453 100644 --- a/web/packages/accounts/pages/credentials.tsx +++ b/web/packages/accounts/pages/credentials.tsx @@ -119,7 +119,7 @@ const Page: React.FC = ({ appContext }) => { const main = async () => { const user: User = getData(LS_KEYS.USER); if (!user?.email) { - router.push("/"); + void router.push("/"); return; } setUser(user); @@ -141,7 +141,7 @@ const Page: React.FC = ({ appContext }) => { } const token = getToken(); if (key && token) { - router.push(appHomeRoute); + void router.push(appHomeRoute); return; } const kekEncryptedAttributes: B64EncryptionResult = getKey( @@ -180,7 +180,7 @@ const Page: React.FC = ({ appContext }) => { (keyAttributes && !keyAttributes.memLimit) ) { clearLocalStorage(); - router.push("/"); + void router.push("/"); return; } setKeyAttributes(keyAttributes); @@ -190,10 +190,10 @@ const Page: React.FC = ({ appContext }) => { if (srpAttributes) { setSrpAttributes(srpAttributes); } else { - router.push("/"); + void router.push("/"); } }; - main(); + void main(); showNavBar(true); }, []); // TODO: ^ validateSession is a dependency, but add that only after we've @@ -252,7 +252,7 @@ const Page: React.FC = ({ appContext }) => { twoFactorSessionID, isTwoFactorEnabled: true, }); - router.push(PAGES.TWO_FACTOR_VERIFY); + void router.push(PAGES.TWO_FACTOR_VERIFY); throw Error(CustomError.TWO_FACTOR_ENABLED); } else { const user = getData(LS_KEYS.USER); @@ -314,7 +314,7 @@ const Page: React.FC = ({ appContext }) => { } catch (e) { log.error("migrate to srp failed", e); } - router.push(unstashRedirect() ?? appHomeRoute); + void router.push(unstashRedirect() ?? appHomeRoute); } catch (e) { log.error("useMasterPassword failed", e); } diff --git a/web/packages/accounts/pages/generate.tsx b/web/packages/accounts/pages/generate.tsx index 02e4c5747a..bea4416139 100644 --- a/web/packages/accounts/pages/generate.tsx +++ b/web/packages/accounts/pages/generate.tsx @@ -51,22 +51,22 @@ const Page: React.FC = ({ appContext }) => { const user: User = getData(LS_KEYS.USER); setUser(user); if (!user?.token) { - router.push("/"); + void router.push("/"); } else if (key) { if (justSignedUp()) { setOpenRecoveryKey(true); setLoading(false); } else { - router.push(appHomeRoute); + void router.push(appHomeRoute); } } else if (keyAttributes?.encryptedKey) { - router.push(PAGES.CREDENTIALS); + void router.push(PAGES.CREDENTIALS); } else { setToken(user.token); setLoading(false); } }; - main(); + void main(); appContext.showNavBar(true); }, []); @@ -106,7 +106,7 @@ const Page: React.FC = ({ appContext }) => { open={openRecoveryKey} onClose={() => { setOpenRecoveryKey(false); - router.push(appHomeRoute); + void router.push(appHomeRoute); }} showMiniDialog={showMiniDialog} /> diff --git a/web/packages/accounts/pages/login.tsx b/web/packages/accounts/pages/login.tsx index 7d297f303c..d1005bdcd3 100644 --- a/web/packages/accounts/pages/login.tsx +++ b/web/packages/accounts/pages/login.tsx @@ -21,14 +21,14 @@ const Page: React.FC = ({ appContext }) => { void customAPIHost().then(setHost); const user = getData(LS_KEYS.USER); if (user?.email) { - router.push(PAGES.VERIFY); + void router.push(PAGES.VERIFY); } setLoading(false); showNavBar(true); }, []); const signUp = () => { - router.push(PAGES.SIGNUP); + void router.push(PAGES.SIGNUP); }; return loading ? ( diff --git a/web/packages/accounts/pages/passkeys/finish.tsx b/web/packages/accounts/pages/passkeys/finish.tsx index 9c074bcfa3..a1cda8b548 100644 --- a/web/packages/accounts/pages/passkeys/finish.tsx +++ b/web/packages/accounts/pages/passkeys/finish.tsx @@ -32,10 +32,8 @@ const Page: React.FC = () => { const response = searchParams.get("response"); if (!passkeySessionID || !response) return; - saveCredentialsAndNavigateTo(passkeySessionID, response).then( - (slug: string) => { - router.push(slug); - }, + void saveCredentialsAndNavigateTo(passkeySessionID, response).then( + (slug: string) => router.push(slug), ); }, []); diff --git a/web/packages/accounts/pages/recover.tsx b/web/packages/accounts/pages/recover.tsx index 411c76a7ce..9897d83b84 100644 --- a/web/packages/accounts/pages/recover.tsx +++ b/web/packages/accounts/pages/recover.tsx @@ -43,19 +43,19 @@ const Page: React.FC = ({ appContext }) => { const keyAttributes: KeyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES); const key = getKey(SESSION_KEYS.ENCRYPTION_KEY); if (!user?.email) { - router.push("/"); + void router.push("/"); return; } if (!user?.encryptedToken && !user?.token) { - sendOtt(user.email); + void sendOtt(user.email); stashRedirect(PAGES.RECOVER); - router.push(PAGES.VERIFY); + void router.push(PAGES.VERIFY); return; } if (!keyAttributes) { - router.push(PAGES.GENERATE); + void router.push(PAGES.GENERATE); } else if (key) { - router.push(appHomeRoute); + void router.push(appHomeRoute); } else { setKeyAttributes(keyAttributes); } @@ -91,7 +91,7 @@ const Page: React.FC = ({ appContext }) => { await decryptAndStoreToken(keyAttr, masterKey); setData(LS_KEYS.SHOW_BACK_BUTTON, { value: false }); - router.push(PAGES.CHANGE_PASSWORD); + void router.push(PAGES.CHANGE_PASSWORD); } catch (e) { log.error("password recovery failed", e); setFieldError(t("INCORRECT_RECOVERY_KEY")); diff --git a/web/packages/accounts/pages/signup.tsx b/web/packages/accounts/pages/signup.tsx index 2aca89c261..8f60d9986c 100644 --- a/web/packages/accounts/pages/signup.tsx +++ b/web/packages/accounts/pages/signup.tsx @@ -21,14 +21,14 @@ const Page: React.FC = ({ appContext }) => { void customAPIHost().then(setHost); const user = getData(LS_KEYS.USER); if (user?.email) { - router.push(PAGES.VERIFY); + void router.push(PAGES.VERIFY); } setLoading(false); showNavBar(true); }, []); const login = () => { - router.push(PAGES.LOGIN); + void router.push(PAGES.LOGIN); }; return ( diff --git a/web/packages/accounts/pages/two-factor/recover.tsx b/web/packages/accounts/pages/two-factor/recover.tsx index 9718902ce1..1c88773deb 100644 --- a/web/packages/accounts/pages/two-factor/recover.tsx +++ b/web/packages/accounts/pages/two-factor/recover.tsx @@ -57,12 +57,12 @@ const Page: React.FC = ({ appContext, twoFactorType }) => { const user = getData(LS_KEYS.USER); const sid = user.passkeySessionID || user.twoFactorSessionID; if (!user || !user.email || !sid) { - router.push("/"); + void router.push("/"); } else if ( !(user.isTwoFactorEnabled || user.isTwoFactorEnabledPasskey) && (user.encryptedToken || user.token) ) { - router.push(PAGES.GENERATE); + void router.push(PAGES.GENERATE); } else { setSessionID(sid); } @@ -81,6 +81,7 @@ const Page: React.FC = ({ appContext, twoFactorType }) => { } catch (e) { if ( e instanceof ApiError && + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison e.httpStatusCode === HttpStatusCode.NotFound ) { logout(); @@ -91,7 +92,7 @@ const Page: React.FC = ({ appContext, twoFactorType }) => { } } }; - main(); + void main(); }, []); const recover: SingleInputFormProps["callback"] = async ( @@ -133,7 +134,7 @@ const Page: React.FC = ({ appContext, twoFactorType }) => { isTwoFactorEnabled: false, }); setData(LS_KEYS.KEY_ATTRIBUTES, keyAttributes); - router.push(PAGES.CREDENTIALS); + void router.push(PAGES.CREDENTIALS); } catch (e) { log.error("two factor recovery failed", e); setFieldError(t("INCORRECT_RECOVERY_KEY")); diff --git a/web/packages/accounts/pages/two-factor/setup.tsx b/web/packages/accounts/pages/two-factor/setup.tsx index 794a2faebe..289c9f95b6 100644 --- a/web/packages/accounts/pages/two-factor/setup.tsx +++ b/web/packages/accounts/pages/two-factor/setup.tsx @@ -41,7 +41,7 @@ const Page: React.FC = () => { log.error("failed to get two factor setup code", e); } }; - main(); + void main(); }, []); const onSubmit: VerifyTwoFactorCallback = async ( @@ -57,7 +57,7 @@ const Page: React.FC = () => { ...getData(LS_KEYS.USER), isTwoFactorEnabled: true, }); - router.push(appHomeRoute); + void router.push(appHomeRoute); }; return ( diff --git a/web/packages/accounts/pages/two-factor/verify.tsx b/web/packages/accounts/pages/two-factor/verify.tsx index 0db7eb9fee..0e609be0d9 100644 --- a/web/packages/accounts/pages/two-factor/verify.tsx +++ b/web/packages/accounts/pages/two-factor/verify.tsx @@ -36,17 +36,17 @@ const Page: React.FC = ({ appContext }) => { const main = async () => { const user: User = getData(LS_KEYS.USER); if (!user?.email || !user.twoFactorSessionID) { - router.push("/"); + void router.push("/"); } else if ( !user.isTwoFactorEnabled && (user.encryptedToken || user.token) ) { - router.push(PAGES.CREDENTIALS); + void router.push(PAGES.CREDENTIALS); } else { setSessionID(user.twoFactorSessionID); } }; - main(); + void main(); }, []); const onSubmit: VerifyTwoFactorCallback = async (otp) => { @@ -60,10 +60,11 @@ const Page: React.FC = ({ appContext }) => { id, }); setData(LS_KEYS.KEY_ATTRIBUTES, keyAttributes!); - router.push(unstashRedirect() ?? PAGES.CREDENTIALS); + void router.push(unstashRedirect() ?? PAGES.CREDENTIALS); } catch (e) { if ( e instanceof ApiError && + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison e.httpStatusCode === HttpStatusCode.NotFound ) { logout(); diff --git a/web/packages/accounts/pages/verify.tsx b/web/packages/accounts/pages/verify.tsx index 868d3e47b7..5479e11873 100644 --- a/web/packages/accounts/pages/verify.tsx +++ b/web/packages/accounts/pages/verify.tsx @@ -60,12 +60,12 @@ const Page: React.FC = ({ appContext }) => { const redirect = await redirectionIfNeeded(user); if (redirect) { - router.push(redirect); + void router.push(redirect); } else { setEmail(user.email); } }; - main(); + void main(); showNavBar(true); }, []); @@ -108,7 +108,7 @@ const Page: React.FC = ({ appContext }) => { isTwoFactorEnabled: true, }); setIsFirstLogin(true); - router.push(PAGES.TWO_FACTOR_VERIFY); + void router.push(PAGES.TWO_FACTOR_VERIFY); } else { await setLSUser({ email, @@ -134,20 +134,22 @@ const Page: React.FC = ({ appContext }) => { await configureSRP(srpSetupAttributes); } } - localForage.clear(); + await localForage.clear(); setIsFirstLogin(true); const redirectURL = unstashRedirect(); if (keyAttributes?.encryptedKey) { clearKeys(); - router.push(redirectURL ?? PAGES.CREDENTIALS); + void router.push(redirectURL ?? PAGES.CREDENTIALS); } else { - router.push(redirectURL ?? PAGES.GENERATE); + void router.push(redirectURL ?? PAGES.GENERATE); } } } catch (e) { if (e instanceof ApiError) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison if (e?.httpStatusCode === HttpStatusCode.Unauthorized) { setFieldError(t("INVALID_CODE")); + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison } else if (e?.httpStatusCode === HttpStatusCode.Gone) { setFieldError(t("EXPIRED_CODE")); } From 3d3ee1bb6f812faf4fe1391d5f1c40ce6b840a9e Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 09:35:56 +0530 Subject: [PATCH 095/177] Apply lints --- .../components/two-factor/VerifyForm.tsx | 6 ++--- web/packages/accounts/eslint.config.mjs | 4 --- .../accounts/pages/two-factor/setup.tsx | 2 +- .../accounts/pages/two-factor/verify.tsx | 25 ++++++++----------- 4 files changed, 14 insertions(+), 23 deletions(-) diff --git a/web/packages/accounts/components/two-factor/VerifyForm.tsx b/web/packages/accounts/components/two-factor/VerifyForm.tsx index 765b8aa235..59332055d8 100644 --- a/web/packages/accounts/components/two-factor/VerifyForm.tsx +++ b/web/packages/accounts/components/two-factor/VerifyForm.tsx @@ -20,16 +20,14 @@ interface Props { export type VerifyTwoFactorCallback = ( otp: string, - markSuccessful: () => Promise, + markSuccessful: () => void, ) => Promise; export default function VerifyTwoFactor(props: Props) { const [waiting, setWaiting] = useState(false); const [shouldAutoFocus, setShouldAutoFocus] = useState(true); - const markSuccessful = async () => { - setWaiting(false); - }; + const markSuccessful = () => setWaiting(false); const submitForm = async ( { otp }: formValues, diff --git a/web/packages/accounts/eslint.config.mjs b/web/packages/accounts/eslint.config.mjs index 08d3b6bc4d..43c778a7b3 100644 --- a/web/packages/accounts/eslint.config.mjs +++ b/web/packages/accounts/eslint.config.mjs @@ -22,10 +22,6 @@ export default [ "@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-unnecessary-template-expression": "off", "@typescript-eslint/consistent-indexed-object-style": "off", - "@typescript-eslint/prefer-promise-reject-errors": "off", - "@typescript-eslint/no-useless-constructor": "off", - "@typescript-eslint/dot-notation": "off", - "@typescript-eslint/require-await": "off", "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/prefer-optional-chain": "off", "@typescript-eslint/ban-types": "off", diff --git a/web/packages/accounts/pages/two-factor/setup.tsx b/web/packages/accounts/pages/two-factor/setup.tsx index 289c9f95b6..c01ac811c8 100644 --- a/web/packages/accounts/pages/two-factor/setup.tsx +++ b/web/packages/accounts/pages/two-factor/setup.tsx @@ -52,7 +52,7 @@ const Page: React.FC = () => { twoFactorSecret!.secretCode, ); await enableTwoFactor(otp, recoveryEncryptedTwoFactorSecret); - await markSuccessful(); + markSuccessful(); await setLSUser({ ...getData(LS_KEYS.USER), isTwoFactorEnabled: true, diff --git a/web/packages/accounts/pages/two-factor/verify.tsx b/web/packages/accounts/pages/two-factor/verify.tsx index 0e609be0d9..9ee835172d 100644 --- a/web/packages/accounts/pages/two-factor/verify.tsx +++ b/web/packages/accounts/pages/two-factor/verify.tsx @@ -33,20 +33,17 @@ const Page: React.FC = ({ appContext }) => { const router = useRouter(); useEffect(() => { - const main = async () => { - const user: User = getData(LS_KEYS.USER); - if (!user?.email || !user.twoFactorSessionID) { - void router.push("/"); - } else if ( - !user.isTwoFactorEnabled && - (user.encryptedToken || user.token) - ) { - void router.push(PAGES.CREDENTIALS); - } else { - setSessionID(user.twoFactorSessionID); - } - }; - void main(); + const user: User = getData(LS_KEYS.USER); + if (!user?.email || !user.twoFactorSessionID) { + void router.push("/"); + } else if ( + !user.isTwoFactorEnabled && + (user.encryptedToken || user.token) + ) { + void router.push(PAGES.CREDENTIALS); + } else { + setSessionID(user.twoFactorSessionID); + } }, []); const onSubmit: VerifyTwoFactorCallback = async (otp) => { From 04a8bd6209302c1a55ff90aa547649608be517e6 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 09:38:35 +0530 Subject: [PATCH 096/177] Apply lints --- web/packages/accounts/pages/generate.tsx | 41 +++++++++++------------ web/packages/accounts/services/passkey.ts | 4 +-- web/packages/accounts/services/srp.ts | 1 + 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/web/packages/accounts/pages/generate.tsx b/web/packages/accounts/pages/generate.tsx index bea4416139..9e7544b4d9 100644 --- a/web/packages/accounts/pages/generate.tsx +++ b/web/packages/accounts/pages/generate.tsx @@ -43,30 +43,27 @@ const Page: React.FC = ({ appContext }) => { const router = useRouter(); useEffect(() => { - const main = async () => { - const key: string = getKey(SESSION_KEYS.ENCRYPTION_KEY); - const keyAttributes: KeyAttributes = getData( - LS_KEYS.ORIGINAL_KEY_ATTRIBUTES, - ); - const user: User = getData(LS_KEYS.USER); - setUser(user); - if (!user?.token) { - void router.push("/"); - } else if (key) { - if (justSignedUp()) { - setOpenRecoveryKey(true); - setLoading(false); - } else { - void router.push(appHomeRoute); - } - } else if (keyAttributes?.encryptedKey) { - void router.push(PAGES.CREDENTIALS); - } else { - setToken(user.token); + const key: string = getKey(SESSION_KEYS.ENCRYPTION_KEY); + const keyAttributes: KeyAttributes = getData( + LS_KEYS.ORIGINAL_KEY_ATTRIBUTES, + ); + const user: User = getData(LS_KEYS.USER); + setUser(user); + if (!user?.token) { + void router.push("/"); + } else if (key) { + if (justSignedUp()) { + setOpenRecoveryKey(true); setLoading(false); + } else { + void router.push(appHomeRoute); } - }; - void main(); + } else if (keyAttributes?.encryptedKey) { + void router.push(PAGES.CREDENTIALS); + } else { + setToken(user.token); + setLoading(false); + } appContext.showNavBar(true); }, []); diff --git a/web/packages/accounts/services/passkey.ts b/web/packages/accounts/services/passkey.ts index a15b5ec0a9..8269f95036 100644 --- a/web/packages/accounts/services/passkey.ts +++ b/web/packages/accounts/services/passkey.ts @@ -142,7 +142,7 @@ export const isPasskeyRecoveryEnabled = async () => { throw Error("request failed"); } - return resp.data["isPasskeyRecoveryEnabled"] as boolean; + return resp.data.isPasskeyRecoveryEnabled as boolean; } catch (e) { log.error("failed to get passkey recovery status", e); throw e; @@ -194,7 +194,7 @@ const getAccountsToken = async () => { "X-Auth-Token": token, }, ); - return resp.data["accountsToken"]; + return resp.data.accountsToken; }; /** diff --git a/web/packages/accounts/services/srp.ts b/web/packages/accounts/services/srp.ts index 9840332b38..319e3e8ef1 100644 --- a/web/packages/accounts/services/srp.ts +++ b/web/packages/accounts/services/srp.ts @@ -154,6 +154,7 @@ export const generateSRPClient = async ( resolve(srpClient); } catch (e) { + // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors reject(e); } }); From 09f76599b2ad91ee18cba337dc0b727ec441e4fe Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 09:39:57 +0530 Subject: [PATCH 097/177] Prune --- web/packages/accounts/eslint.config.mjs | 4 ---- web/packages/accounts/pages/recover.tsx | 1 + web/packages/accounts/pages/two-factor/recover.tsx | 1 + 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/web/packages/accounts/eslint.config.mjs b/web/packages/accounts/eslint.config.mjs index 43c778a7b3..99671aa294 100644 --- a/web/packages/accounts/eslint.config.mjs +++ b/web/packages/accounts/eslint.config.mjs @@ -20,9 +20,6 @@ export default [ "@typescript-eslint/no-unnecessary-type-assertion": "off", "@typescript-eslint/array-type": "off", "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-unnecessary-template-expression": "off", - "@typescript-eslint/consistent-indexed-object-style": "off", - "@typescript-eslint/no-var-requires": "off", "@typescript-eslint/prefer-optional-chain": "off", "@typescript-eslint/ban-types": "off", "@typescript-eslint/no-misused-promises": "off", @@ -31,7 +28,6 @@ export default [ "react-refresh/only-export-components": "off", /** TODO: New during eslint 8=>9 migration */ "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-require-imports": "off", "@typescript-eslint/no-unsafe-function-type": "off", }, }, diff --git a/web/packages/accounts/pages/recover.tsx b/web/packages/accounts/pages/recover.tsx index 9897d83b84..87ee69b468 100644 --- a/web/packages/accounts/pages/recover.tsx +++ b/web/packages/accounts/pages/recover.tsx @@ -25,6 +25,7 @@ import { useEffect, useState } from "react"; import { appHomeRoute, stashRedirect } from "../services/redirect"; import type { PageProps } from "../types/page"; +// eslint-disable-next-line @typescript-eslint/no-require-imports const bip39 = require("bip39"); // mobile client library only supports english. bip39.setDefaultWordlist("english"); diff --git a/web/packages/accounts/pages/two-factor/recover.tsx b/web/packages/accounts/pages/two-factor/recover.tsx index 1c88773deb..ece422aca9 100644 --- a/web/packages/accounts/pages/two-factor/recover.tsx +++ b/web/packages/accounts/pages/two-factor/recover.tsx @@ -33,6 +33,7 @@ import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { Trans } from "react-i18next"; +// eslint-disable-next-line @typescript-eslint/no-require-imports const bip39 = require("bip39"); // mobile client library only supports english. bip39.setDefaultWordlist("english"); From 6fe4201eb8e50b21f1e701c6e3ea51192f94a984 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 09:42:37 +0530 Subject: [PATCH 098/177] Prune --- web/packages/accounts/components/two-factor/VerifyForm.tsx | 1 + web/packages/accounts/eslint.config.mjs | 6 +++--- web/packages/accounts/pages/credentials.tsx | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/web/packages/accounts/components/two-factor/VerifyForm.tsx b/web/packages/accounts/components/two-factor/VerifyForm.tsx index 59332055d8..3a028cc980 100644 --- a/web/packages/accounts/components/two-factor/VerifyForm.tsx +++ b/web/packages/accounts/components/two-factor/VerifyForm.tsx @@ -50,6 +50,7 @@ export default function VerifyTwoFactor(props: Props) { }; const onChange = + // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type (callback: Function, triggerSubmit: Function) => (otp: string) => { callback(otp); if (otp.length === 6) { diff --git a/web/packages/accounts/eslint.config.mjs b/web/packages/accounts/eslint.config.mjs index 99671aa294..b23b6c9dd4 100644 --- a/web/packages/accounts/eslint.config.mjs +++ b/web/packages/accounts/eslint.config.mjs @@ -21,14 +21,14 @@ export default [ "@typescript-eslint/array-type": "off", "@typescript-eslint/no-empty-function": "off", "@typescript-eslint/prefer-optional-chain": "off", - "@typescript-eslint/ban-types": "off", - "@typescript-eslint/no-misused-promises": "off", + + "react-hooks/exhaustive-deps": "off", "react-hooks/rules-of-hooks": "off", "react-refresh/only-export-components": "off", /** TODO: New during eslint 8=>9 migration */ "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-unsafe-function-type": "off", + }, }, ]; diff --git a/web/packages/accounts/pages/credentials.tsx b/web/packages/accounts/pages/credentials.tsx index 7887a57453..45e46897ec 100644 --- a/web/packages/accounts/pages/credentials.tsx +++ b/web/packages/accounts/pages/credentials.tsx @@ -278,6 +278,7 @@ const Page: React.FC = ({ appContext }) => { } }; + // eslint-disable-next-line @typescript-eslint/no-misused-promises const useMasterPassword: VerifyMasterPasswordFormProps["callback"] = async ( key, kek, From 6533883eaac04721f2f60f77f1e2b59d87bf27a4 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 09:43:50 +0530 Subject: [PATCH 099/177] Prune --- web/packages/accounts/eslint.config.mjs | 6 ------ web/packages/accounts/pages/two-factor/recover.tsx | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/web/packages/accounts/eslint.config.mjs b/web/packages/accounts/eslint.config.mjs index b23b6c9dd4..1dee2987bf 100644 --- a/web/packages/accounts/eslint.config.mjs +++ b/web/packages/accounts/eslint.config.mjs @@ -17,12 +17,6 @@ export default [ "@typescript-eslint/no-unsafe-argument": "off", "@typescript-eslint/no-unsafe-call": "off", /** TODO: Disabled as we migrate, try to prune these again */ - "@typescript-eslint/no-unnecessary-type-assertion": "off", - "@typescript-eslint/array-type": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/prefer-optional-chain": "off", - - "react-hooks/exhaustive-deps": "off", "react-hooks/rules-of-hooks": "off", "react-refresh/only-export-components": "off", diff --git a/web/packages/accounts/pages/two-factor/recover.tsx b/web/packages/accounts/pages/two-factor/recover.tsx index ece422aca9..08fd7a2d8f 100644 --- a/web/packages/accounts/pages/two-factor/recover.tsx +++ b/web/packages/accounts/pages/two-factor/recover.tsx @@ -57,7 +57,7 @@ const Page: React.FC = ({ appContext, twoFactorType }) => { useEffect(() => { const user = getData(LS_KEYS.USER); const sid = user.passkeySessionID || user.twoFactorSessionID; - if (!user || !user.email || !sid) { + if (!user?.email || !sid) { void router.push("/"); } else if ( !(user.isTwoFactorEnabled || user.isTwoFactorEnabledPasskey) && From cd6c3d915828ed0b83e300660a97bffcdbaab203 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 09:45:04 +0530 Subject: [PATCH 100/177] Prune --- web/packages/accounts/eslint.config.mjs | 3 --- web/packages/accounts/pages/change-email.tsx | 3 +++ web/packages/accounts/pages/change-password.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/packages/accounts/eslint.config.mjs b/web/packages/accounts/eslint.config.mjs index 1dee2987bf..6a9ee79213 100644 --- a/web/packages/accounts/eslint.config.mjs +++ b/web/packages/accounts/eslint.config.mjs @@ -20,9 +20,6 @@ export default [ "react-hooks/exhaustive-deps": "off", "react-hooks/rules-of-hooks": "off", "react-refresh/only-export-components": "off", - /** TODO: New during eslint 8=>9 migration */ - "@typescript-eslint/no-unused-vars": "off", - }, }, ]; diff --git a/web/packages/accounts/pages/change-email.tsx b/web/packages/accounts/pages/change-email.tsx index 019ee92556..82f74e3c60 100644 --- a/web/packages/accounts/pages/change-email.tsx +++ b/web/packages/accounts/pages/change-email.tsx @@ -5,6 +5,7 @@ import { FormPaperTitle, } from "@/base/components/FormPaper"; import { LoadingButton } from "@/base/components/mui/LoadingButton"; +import log from "@/base/log"; import { VerticallyCentered } from "@ente/shared/components/Container"; import LinkButton from "@ente/shared/components/LinkButton"; import { LS_KEYS, getData, setLSUser } from "@ente/shared/storage/localStorage"; @@ -69,6 +70,7 @@ const ChangeEmailForm: React.FC = () => { // ottInputRef.current?.focus(); // }, 250); } catch (e) { + log.error(e); setFieldError("email", t("email_already_taken")); } setLoading(false); @@ -85,6 +87,7 @@ const ChangeEmailForm: React.FC = () => { setLoading(false); void goToApp(); } catch (e) { + log.error(e); setLoading(false); setFieldError("ott", t("INCORRECT_CODE")); } diff --git a/web/packages/accounts/pages/change-password.tsx b/web/packages/accounts/pages/change-password.tsx index dc9f92d2b7..4325352193 100644 --- a/web/packages/accounts/pages/change-password.tsx +++ b/web/packages/accounts/pages/change-password.tsx @@ -64,7 +64,7 @@ const Page: React.FC = () => { let kek: KEK; try { kek = await cryptoWorker.deriveSensitiveKey(passphrase, kekSalt); - } catch (e) { + } catch { setFieldError("confirm", t("PASSWORD_GENERATION_FAILED")); return; } From 98bfb8b5f5e5e91dab26abb6d4e11cc0de9045de Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 09:47:14 +0530 Subject: [PATCH 101/177] This shouldn't be named useFoo, leaving that refactoring for later --- web/packages/accounts/eslint.config.mjs | 1 - web/packages/accounts/pages/credentials.tsx | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/web/packages/accounts/eslint.config.mjs b/web/packages/accounts/eslint.config.mjs index 6a9ee79213..ea97f05ffd 100644 --- a/web/packages/accounts/eslint.config.mjs +++ b/web/packages/accounts/eslint.config.mjs @@ -18,7 +18,6 @@ export default [ "@typescript-eslint/no-unsafe-call": "off", /** TODO: Disabled as we migrate, try to prune these again */ "react-hooks/exhaustive-deps": "off", - "react-hooks/rules-of-hooks": "off", "react-refresh/only-export-components": "off", }, }, diff --git a/web/packages/accounts/pages/credentials.tsx b/web/packages/accounts/pages/credentials.tsx index 45e46897ec..f41a09258e 100644 --- a/web/packages/accounts/pages/credentials.tsx +++ b/web/packages/accounts/pages/credentials.tsx @@ -171,6 +171,7 @@ const Page: React.FC = ({ appContext }) => { keyAttributes.keyDecryptionNonce, kek, ); + // eslint-disable-next-line react-hooks/rules-of-hooks useMasterPassword(key, kek, keyAttributes); return; } From fdf0e3f0ad187f0591d708caf2239422f40dcae4 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 09:50:11 +0530 Subject: [PATCH 102/177] Fix --- web/apps/auth/src/pages/auth.tsx | 2 +- web/apps/photos/src/pages/gallery.tsx | 2 +- .../accounts/components/LoginComponents.tsx | 23 +----------------- .../components/two-factor/setup/index.tsx | 10 ++++---- .../accounts/components/utils/dialog.ts | 24 +++++++++++++++++++ web/packages/accounts/eslint.config.mjs | 1 - web/packages/accounts/pages/credentials.tsx | 2 +- .../accounts/pages/two-factor/setup.tsx | 5 +--- 8 files changed, 34 insertions(+), 35 deletions(-) create mode 100644 web/packages/accounts/components/utils/dialog.ts diff --git a/web/apps/auth/src/pages/auth.tsx b/web/apps/auth/src/pages/auth.tsx index ee872a3a1f..2fd032d5c2 100644 --- a/web/apps/auth/src/pages/auth.tsx +++ b/web/apps/auth/src/pages/auth.tsx @@ -1,4 +1,4 @@ -import { sessionExpiredDialogAttributes } from "@/accounts/components/LoginComponents"; +import { sessionExpiredDialogAttributes } from "@/accounts/components/utils/dialog"; import { stashRedirect } from "@/accounts/services/redirect"; import { EnteLogo } from "@/base/components/EnteLogo"; import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator"; diff --git a/web/apps/photos/src/pages/gallery.tsx b/web/apps/photos/src/pages/gallery.tsx index 19428b1563..22bc298d0b 100644 --- a/web/apps/photos/src/pages/gallery.tsx +++ b/web/apps/photos/src/pages/gallery.tsx @@ -1,4 +1,4 @@ -import { sessionExpiredDialogAttributes } from "@/accounts/components/LoginComponents"; +import { sessionExpiredDialogAttributes } from "@/accounts/components/utils/dialog"; import { stashRedirect } from "@/accounts/services/redirect"; import type { MiniDialogAttributes } from "@/base/components/MiniDialog"; import { NavbarBase } from "@/base/components/Navbar"; diff --git a/web/packages/accounts/components/LoginComponents.tsx b/web/packages/accounts/components/LoginComponents.tsx index 178ae5df35..8644b5440c 100644 --- a/web/packages/accounts/components/LoginComponents.tsx +++ b/web/packages/accounts/components/LoginComponents.tsx @@ -15,6 +15,7 @@ import { CircularProgress, Stack, Typography, styled } from "@mui/material"; import { t } from "i18next"; import { useRouter } from "next/router"; import React, { useEffect, useState } from "react"; +import { sessionExpiredDialogAttributes } from "@/accounts/components/utils/dialog"; export const PasswordHeader: React.FC = ({ children, @@ -192,25 +193,3 @@ const ButtonStack = styled("div")` flex-direction: column; gap: 1rem; `; - -/** - * {@link MiniDialogAttributes} for showing asking the user to login again when - * their session has expired. - * - * There is one button, which allows them to logout. - * - * @param onLogin Called when the user presses the "Login" button on the error - * dialog. - */ -export const sessionExpiredDialogAttributes = ( - onLogin: () => void, -): MiniDialogAttributes => ({ - title: t("session_expired"), - message: t("session_expired_message"), - nonClosable: true, - continue: { - text: t("login"), - action: onLogin, - }, - cancel: false, -}); diff --git a/web/packages/accounts/components/two-factor/setup/index.tsx b/web/packages/accounts/components/two-factor/setup/index.tsx index e5716abc62..87e3ce404b 100644 --- a/web/packages/accounts/components/two-factor/setup/index.tsx +++ b/web/packages/accounts/components/two-factor/setup/index.tsx @@ -1,6 +1,6 @@ import SetupManualMode from "@/accounts/components/two-factor/setup/ManualMode"; import SetupQRMode from "@/accounts/components/two-factor/setup/QRMode"; -import { SetupMode } from "@/accounts/pages/two-factor/setup"; +import { type SetupMode } from "@/accounts/pages/two-factor/setup"; import type { TwoFactorSecret } from "@/accounts/types/user"; import { VerticallyCentered } from "@ente/shared/components/Container"; import { useState } from "react"; @@ -9,15 +9,15 @@ interface Iprops { twoFactorSecret?: TwoFactorSecret; } export function TwoFactorSetup({ twoFactorSecret }: Iprops) { - const [setupMode, setSetupMode] = useState(SetupMode.QR_CODE); + const [setupMode, setSetupMode] = useState("qrCode"); - const changeToManualMode = () => setSetupMode(SetupMode.MANUAL_CODE); + const changeToManualMode = () => setSetupMode("manualCode"); - const changeToQRMode = () => setSetupMode(SetupMode.QR_CODE); + const changeToQRMode = () => setSetupMode("qrCode"); return ( - {setupMode === SetupMode.QR_CODE ? ( + {setupMode == "qrCode" ? ( void, +): MiniDialogAttributes => ({ + title: t("session_expired"), + message: t("session_expired_message"), + nonClosable: true, + continue: { + text: t("login"), + action: onLogin, + }, + cancel: false, +}); diff --git a/web/packages/accounts/eslint.config.mjs b/web/packages/accounts/eslint.config.mjs index ea97f05ffd..447b0f36d4 100644 --- a/web/packages/accounts/eslint.config.mjs +++ b/web/packages/accounts/eslint.config.mjs @@ -18,7 +18,6 @@ export default [ "@typescript-eslint/no-unsafe-call": "off", /** TODO: Disabled as we migrate, try to prune these again */ "react-hooks/exhaustive-deps": "off", - "react-refresh/only-export-components": "off", }, }, ]; diff --git a/web/packages/accounts/pages/credentials.tsx b/web/packages/accounts/pages/credentials.tsx index f41a09258e..13e21fde20 100644 --- a/web/packages/accounts/pages/credentials.tsx +++ b/web/packages/accounts/pages/credentials.tsx @@ -1,3 +1,4 @@ +import { sessionExpiredDialogAttributes } from "@/accounts/components/utils/dialog"; import { FormPaper } from "@/base/components/FormPaper"; import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator"; import { sharedCryptoWorker } from "@/base/crypto"; @@ -44,7 +45,6 @@ import { LoginFlowFormFooter, PasswordHeader, VerifyingPasskey, - sessionExpiredDialogAttributes, } from "../components/LoginComponents"; import { PAGES } from "../constants/pages"; import { diff --git a/web/packages/accounts/pages/two-factor/setup.tsx b/web/packages/accounts/pages/two-factor/setup.tsx index c01ac811c8..9cf0077d67 100644 --- a/web/packages/accounts/pages/two-factor/setup.tsx +++ b/web/packages/accounts/pages/two-factor/setup.tsx @@ -17,10 +17,7 @@ import { useEffect, useState } from "react"; import { appHomeRoute } from "../../services/redirect"; import type { PageProps } from "../../types/page"; -export enum SetupMode { - QR_CODE, - MANUAL_CODE, -} +export type SetupMode = "qrCode" | "manualCode"; const Page: React.FC = () => { const [twoFactorSecret, setTwoFactorSecret] = useState< From 4e5c99aafeadc2ea6dd785f093beaed4ea41583f Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 09:57:16 +0530 Subject: [PATCH 103/177] Prune --- web/packages/shared/components/OverflowMenu/context.tsx | 1 + web/packages/shared/eslint.config.mjs | 8 -------- web/packages/shared/network/HTTPService.ts | 8 ++------ web/packages/shared/network/cast.ts | 3 +-- web/packages/shared/storage/localStorage/index.ts | 2 ++ web/packages/shared/themes/mui-theme.d.ts | 2 +- web/packages/shared/user/index.ts | 2 +- 7 files changed, 8 insertions(+), 18 deletions(-) diff --git a/web/packages/shared/components/OverflowMenu/context.tsx b/web/packages/shared/components/OverflowMenu/context.tsx index 79e289852e..2908703e3d 100644 --- a/web/packages/shared/components/OverflowMenu/context.tsx +++ b/web/packages/shared/components/OverflowMenu/context.tsx @@ -1,5 +1,6 @@ import { createContext } from "react"; export const OverflowMenuContext = createContext({ + // eslint-disable-next-line @typescript-eslint/no-empty-function close: () => {}, }); diff --git a/web/packages/shared/eslint.config.mjs b/web/packages/shared/eslint.config.mjs index 5897b2d378..d074d7e3c8 100644 --- a/web/packages/shared/eslint.config.mjs +++ b/web/packages/shared/eslint.config.mjs @@ -22,16 +22,8 @@ export default [ "@typescript-eslint/no-floating-promises": "off", "@typescript-eslint/no-unsafe-enum-comparison": "off", "@typescript-eslint/no-unnecessary-type-assertion": "off", - "@typescript-eslint/array-type": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-unnecessary-template-expression": "off", - "@typescript-eslint/consistent-indexed-object-style": "off", "@typescript-eslint/prefer-promise-reject-errors": "off", - "@typescript-eslint/no-useless-constructor": "off", "react-hooks/exhaustive-deps": "off", - /** TODO: New during eslint 8=>9 migration */ - "@typescript-eslint/no-unused-expressions": "off", - "@typescript-eslint/no-unused-vars": "off", }, }, ]; diff --git a/web/packages/shared/network/HTTPService.ts b/web/packages/shared/network/HTTPService.ts index 42124af65f..474728de08 100644 --- a/web/packages/shared/network/HTTPService.ts +++ b/web/packages/shared/network/HTTPService.ts @@ -2,13 +2,9 @@ import log from "@/base/log"; import axios, { type AxiosRequestConfig, type AxiosResponse } from "axios"; import { ApiError, isApiErrorResponse } from "../error"; -interface IHTTPHeaders { - [headerKey: string]: any; -} +type IHTTPHeaders = Record; -interface IQueryPrams { - [paramName: string]: any; -} +type IQueryPrams = Record; /** * Service to manage all HTTP calls. diff --git a/web/packages/shared/network/cast.ts b/web/packages/shared/network/cast.ts index b1b1ae7099..226696371f 100644 --- a/web/packages/shared/network/cast.ts +++ b/web/packages/shared/network/cast.ts @@ -5,8 +5,6 @@ import { getToken } from "../storage/localStorage/helpers"; import HTTPService from "./HTTPService"; class CastGateway { - constructor() {} - public async getCastData(code: string): Promise { let resp; try { @@ -78,6 +76,7 @@ class CastGateway { await HTTPService.post( await apiURL("/cast/cast-data/"), { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-template-expression deviceCode: `${code}`, encPayload: castPayload, collectionID: collectionID, diff --git a/web/packages/shared/storage/localStorage/index.ts b/web/packages/shared/storage/localStorage/index.ts index 31553908f1..83c3379930 100644 --- a/web/packages/shared/storage/localStorage/index.ts +++ b/web/packages/shared/storage/localStorage/index.ts @@ -75,6 +75,7 @@ export const migrateKVToken = async (user: unknown) => { typeof oldLSUser.token == "string" && !(await getKVS("token")); + // eslint-disable-next-line @typescript-eslint/no-unused-expressions user && typeof user == "object" && "id" in user && @@ -82,6 +83,7 @@ export const migrateKVToken = async (user: unknown) => { ? await setKV("userID", user.id) : await removeKV("userID"); + // eslint-disable-next-line @typescript-eslint/no-unused-expressions user && typeof user == "object" && "token" in user && diff --git a/web/packages/shared/themes/mui-theme.d.ts b/web/packages/shared/themes/mui-theme.d.ts index 639ee286b2..e852b6b309 100644 --- a/web/packages/shared/themes/mui-theme.d.ts +++ b/web/packages/shared/themes/mui-theme.d.ts @@ -200,6 +200,6 @@ declare module "@mui/material/styles" { faint: number; } - type AvatarColors = Array; + type AvatarColors = string[]; } export {}; diff --git a/web/packages/shared/user/index.ts b/web/packages/shared/user/index.ts index d23370f2c0..a5a4a79070 100644 --- a/web/packages/shared/user/index.ts +++ b/web/packages/shared/user/index.ts @@ -16,7 +16,7 @@ export const getActualKey = async () => { encryptionKeyAttributes.key, ); return key; - } catch (e) { + } catch { throw new Error(CustomError.KEY_MISSING); } }; From 07b1709308a4cfe7cbaf38930264f83e71b5e476 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 10:01:46 +0530 Subject: [PATCH 104/177] Prune --- web/packages/new/eslint.config.mjs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/web/packages/new/eslint.config.mjs b/web/packages/new/eslint.config.mjs index 8b441fa3fe..0679b564ea 100644 --- a/web/packages/new/eslint.config.mjs +++ b/web/packages/new/eslint.config.mjs @@ -9,11 +9,4 @@ export default [ // that possibly arise from it not being able to locate ffmpeg-wasm. ignores: ["**/ffmpeg/worker.ts"], }, - { - rules: { - /** TODO: New during eslint 8=>9 migration */ - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-unused-expressions": "off", - }, - }, ]; From a370188504673c3379f47f669188b136a4c25d6a Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 10:04:27 +0530 Subject: [PATCH 105/177] Fix awaited returns --- web/apps/photos/eslint.config.mjs | 2 +- web/apps/photos/src/services/export/index.ts | 2 +- web/apps/photos/src/services/upload/upload-service.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/apps/photos/eslint.config.mjs b/web/apps/photos/eslint.config.mjs index fe6f0ab98b..43a57b1159 100644 --- a/web/apps/photos/eslint.config.mjs +++ b/web/apps/photos/eslint.config.mjs @@ -52,7 +52,7 @@ export default [ /** TODO: New during eslint 8=>9 migration */ "@typescript-eslint/no-unused-expressions": "off", // "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/return-await": "off", + "@typescript-eslint/prefer-regexp-exec": "off", "@typescript-eslint/no-require-imports": "off", }, diff --git a/web/apps/photos/src/services/export/index.ts b/web/apps/photos/src/services/export/index.ts index c68593eff3..7b956c17e9 100644 --- a/web/apps/photos/src/services/export/index.ts +++ b/web/apps/photos/src/services/export/index.ts @@ -883,7 +883,7 @@ class ExportService { await this.verifyExportFolderExists(folder); const exportRecordJSONPath = `${folder}/${exportRecordFileName}`; if (!(await fs.exists(exportRecordJSONPath))) { - return this.createEmptyExportRecord(exportRecordJSONPath); + return await this.createEmptyExportRecord(exportRecordJSONPath); } const recordFile = await fs.readTextFile(exportRecordJSONPath); return JSON.parse(recordFile); diff --git a/web/apps/photos/src/services/upload/upload-service.ts b/web/apps/photos/src/services/upload/upload-service.ts index 6afe1e02db..9cce67df6e 100644 --- a/web/apps/photos/src/services/upload/upload-service.ts +++ b/web/apps/photos/src/services/upload/upload-service.ts @@ -1072,7 +1072,7 @@ const tryExtractImageMetadata = async ( } try { - return extractExif(file); + return await extractExif(file); } catch (e) { log.error(`Failed to extract image metadata for ${uploadItem}`, e); return undefined; From 3b0544d779f7ec1c4d8b2fdde350a6777ee30d34 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 10:05:54 +0530 Subject: [PATCH 106/177] Prune --- web/apps/photos/eslint.config.mjs | 5 +---- .../photos/src/components/PhotoViewer/FileInfo/MapBox.tsx | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/web/apps/photos/eslint.config.mjs b/web/apps/photos/eslint.config.mjs index 43a57b1159..159a00a6f0 100644 --- a/web/apps/photos/eslint.config.mjs +++ b/web/apps/photos/eslint.config.mjs @@ -47,14 +47,11 @@ export default [ "@typescript-eslint/restrict-plus-operands": "off", "@typescript-eslint/no-meaningless-void-operator": "off", "react-hooks/exhaustive-deps": "off", - "react-hooks/rules-of-hooks": "off", + "react-refresh/only-export-components": "off", /** TODO: New during eslint 8=>9 migration */ "@typescript-eslint/no-unused-expressions": "off", - // "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/prefer-regexp-exec": "off", - "@typescript-eslint/no-require-imports": "off", }, }, ]; diff --git a/web/apps/photos/src/components/PhotoViewer/FileInfo/MapBox.tsx b/web/apps/photos/src/components/PhotoViewer/FileInfo/MapBox.tsx index b6a6bf79b7..25d3cc25d3 100644 --- a/web/apps/photos/src/components/PhotoViewer/FileInfo/MapBox.tsx +++ b/web/apps/photos/src/components/PhotoViewer/FileInfo/MapBox.tsx @@ -7,8 +7,10 @@ import { useEffect, useRef } from "react"; import "leaflet-defaulticon-compatibility/dist/leaflet-defaulticon-compatibility.webpack.css"; // Re-uses images from ~leaflet package import "leaflet/dist/leaflet.css"; +// eslint-disable-next-line @typescript-eslint/no-require-imports haveWindow() && require("leaflet-defaulticon-compatibility"); const L = haveWindow() + // eslint-disable-next-line @typescript-eslint/no-require-imports ? (require("leaflet") as typeof import("leaflet")) : null; From f5512d3cd8c23bb561058be04ae5375c04e7554e Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 10:07:42 +0530 Subject: [PATCH 107/177] Apply autofixes prefer-regexp-exec --- web/apps/photos/eslint.config.mjs | 2 +- web/apps/photos/src/services/export/index.ts | 2 +- web/apps/photos/src/services/upload/takeout.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/apps/photos/eslint.config.mjs b/web/apps/photos/eslint.config.mjs index 159a00a6f0..31733fe465 100644 --- a/web/apps/photos/eslint.config.mjs +++ b/web/apps/photos/eslint.config.mjs @@ -51,7 +51,7 @@ export default [ "react-refresh/only-export-components": "off", /** TODO: New during eslint 8=>9 migration */ "@typescript-eslint/no-unused-expressions": "off", - "@typescript-eslint/prefer-regexp-exec": "off", + }, }, ]; diff --git a/web/apps/photos/src/services/export/index.ts b/web/apps/photos/src/services/export/index.ts index 7b956c17e9..5f91e6163a 100644 --- a/web/apps/photos/src/services/export/index.ts +++ b/web/apps/photos/src/services/export/index.ts @@ -1181,7 +1181,7 @@ const getRenamedExportedCollections = ( if (currentExportName === collectionExportName) { return false; } - const hasNumberedSuffix = currentExportName.match(/\(\d+\)$/); + const hasNumberedSuffix = /\(\d+\)$/.exec(currentExportName); const currentExportNameWithoutNumberedSuffix = hasNumberedSuffix ? currentExportName.replace(/\(\d+\)$/, "") : currentExportName; diff --git a/web/apps/photos/src/services/upload/takeout.ts b/web/apps/photos/src/services/upload/takeout.ts index 22c24d7110..bb30ed11d7 100644 --- a/web/apps/photos/src/services/upload/takeout.ts +++ b/web/apps/photos/src/services/upload/takeout.ts @@ -29,7 +29,7 @@ export const getMetadataJSONMapKeyForJSON = ( jsonFileName: string, ) => { let title = jsonFileName.slice(0, -1 * ".json".length); - const endsWithNumberedSuffixWithBrackets = title.match(/\(\d+\)$/); + const endsWithNumberedSuffixWithBrackets = /\(\d+\)$/.exec(title); if (endsWithNumberedSuffixWithBrackets) { title = title.slice( 0, From ba8ccbf06531479512a7b5d54bacd738b2379ae1 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 20 Nov 2024 10:08:41 +0530 Subject: [PATCH 108/177] Prune --- web/apps/photos/eslint.config.mjs | 3 --- web/apps/photos/src/components/FixCreationTime.tsx | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/web/apps/photos/eslint.config.mjs b/web/apps/photos/eslint.config.mjs index 31733fe465..ad8ab00dfa 100644 --- a/web/apps/photos/eslint.config.mjs +++ b/web/apps/photos/eslint.config.mjs @@ -45,13 +45,10 @@ export default [ "@typescript-eslint/no-confusing-void-expression": "off", "@typescript-eslint/use-unknown-in-catch-callback-variable": "off", "@typescript-eslint/restrict-plus-operands": "off", - "@typescript-eslint/no-meaningless-void-operator": "off", "react-hooks/exhaustive-deps": "off", - "react-refresh/only-export-components": "off", /** TODO: New during eslint 8=>9 migration */ "@typescript-eslint/no-unused-expressions": "off", - }, }, ]; diff --git a/web/apps/photos/src/components/FixCreationTime.tsx b/web/apps/photos/src/components/FixCreationTime.tsx index 6bb9b16d43..35aa71f87f 100644 --- a/web/apps/photos/src/components/FixCreationTime.tsx +++ b/web/apps/photos/src/components/FixCreationTime.tsx @@ -216,7 +216,7 @@ const OptionsForm: React.FC = ({ )}