From 5e35b7060493fc21f3df26b3556a15fe9f9736c7 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Mon, 29 Sep 2025 12:39:39 -0400 Subject: [PATCH] chore: on course search, always load course IDs from server (#4174) --- .../course_search_provider.dart | 12 +-- .../course_plans/course_plans_repo.dart | 85 +++++++++---------- lib/pangea/login/pages/new_trip_page.dart | 4 +- lib/pangea/payload_client/payload_client.dart | 2 + 4 files changed, 50 insertions(+), 53 deletions(-) diff --git a/lib/pangea/course_creation/course_search_provider.dart b/lib/pangea/course_creation/course_search_provider.dart index ae30743f8..5a5dba546 100644 --- a/lib/pangea/course_creation/course_search_provider.dart +++ b/lib/pangea/course_creation/course_search_provider.dart @@ -31,19 +31,19 @@ mixin CourseSearchProvider on State { ); } - void setLanguageLevelFilter(LanguageLevelTypeEnum? level) { + void setLanguageLevelFilter(LanguageLevelTypeEnum? level, {reload = true}) { languageLevelFilter = level; - _loadCourses(); + if (reload) _loadCourses(); } - void setInstructionLanguageFilter(LanguageModel? language) { + void setInstructionLanguageFilter(LanguageModel? language, {reload = true}) { instructionLanguageFilter = language; - _loadCourses(); + if (reload) _loadCourses(); } - void setTargetLanguageFilter(LanguageModel? language) { + void setTargetLanguageFilter(LanguageModel? language, {reload = true}) { targetLanguageFilter = language; - _loadCourses(); + if (reload) _loadCourses(); } Future _loadCourses() async { diff --git a/lib/pangea/course_plans/course_plans_repo.dart b/lib/pangea/course_plans/course_plans_repo.dart index a19223065..c90b42efb 100644 --- a/lib/pangea/course_plans/course_plans_repo.dart +++ b/lib/pangea/course_plans/course_plans_repo.dart @@ -58,41 +58,6 @@ class CoursePlansRepo { await _courseStorage.write(coursePlan.uuid, coursePlan.toJson()); } - static String _searchKey(CourseFilter filter) { - return "search_${filter.hashCode.toString()}"; - } - - static List? _getCachedSearchResults( - CourseFilter filter, - ) { - final jsonList = _courseStorage.read(_searchKey(filter)); - if (jsonList != null) { - try { - final ids = List.from(jsonList); - final coursePlans = ids - .map((id) => _getCached(id)) - .whereType() - .toList(); - - return coursePlans; - } catch (e) { - _courseStorage.remove(_searchKey(filter)); - } - } - return null; - } - - static Future _setCachedSearchResults( - CourseFilter filter, - List coursePlans, - ) async { - final jsonList = coursePlans.map((e) => e.uuid).toList(); - for (final plan in coursePlans) { - _setCached(plan); - } - await _courseStorage.write(_searchKey(filter), jsonList); - } - static Future get(String id) async { await _courseStorage.initStorage; final cached = _getCached(id); @@ -133,10 +98,6 @@ class CoursePlansRepo { static Future> search({CourseFilter? filter}) async { await _courseStorage.initStorage; - final cached = _getCachedSearchResults(filter ?? CourseFilter()); - if (cached != null && cached.isNotEmpty) { - return cached; - } final Map where = {}; if (filter != null) { @@ -188,7 +149,38 @@ class CoursePlansRepo { baseUrl: Environment.cmsApi, accessToken: MatrixState.pangeaController.userController.accessToken, ); + + // Run the search for the given filter, selecting only the course IDs final result = await payload.find( + CmsCoursePlan.slug, + (json) => json["id"] as String, + page: 1, + limit: 10, + where: where, + select: {"id": true}, + ); + + final missingIds = result.docs + .where( + (id) => _courseStorage.read(id) == null, + ) + .toList(); + + // If all of the returned IDs are in the cached list, ensure all of the course details have been cached, and return + if (missingIds.isEmpty) { + return result.docs + .map((id) => _getCached(id)) + .whereType() + .toList(); + } + + // Else, take the list of returned course IDs minus the list of cached course IDs and + // fetch/cache the course details for each. Cache the newly returned list with all the IDs. + where["id"] = { + "in": missingIds, + }; + + final searchResult = await payload.find( CmsCoursePlan.slug, CmsCoursePlan.fromJson, page: 1, @@ -196,19 +188,22 @@ class CoursePlansRepo { where: where, ); - final coursePlans = result.docs + final coursePlans = searchResult.docs .map( (cmsCoursePlan) => cmsCoursePlan.toCoursePlanModel(), ) .toList(); - await _setCachedSearchResults( - filter ?? CourseFilter(), - coursePlans, - ); + for (final plan in coursePlans) { + await _setCached(plan); + } final futures = coursePlans.map((c) => c.init()); await Future.wait(futures); - return coursePlans; + + return result.docs + .map((id) => _getCached(id)) + .whereType() + .toList(); } } diff --git a/lib/pangea/login/pages/new_trip_page.dart b/lib/pangea/login/pages/new_trip_page.dart index 2f3716ec6..83a8ab807 100644 --- a/lib/pangea/login/pages/new_trip_page.dart +++ b/lib/pangea/login/pages/new_trip_page.dart @@ -33,12 +33,12 @@ class NewTripPageState extends State with CourseSearchProvider { final target = MatrixState.pangeaController.languageController.userL2; if (target != null) { - setTargetLanguageFilter(target); + setTargetLanguageFilter(target, reload: false); } final base = MatrixState.pangeaController.languageController.systemLanguage; if (base != null) { - setInstructionLanguageFilter(base); + setInstructionLanguageFilter(base, reload: false); } } diff --git a/lib/pangea/payload_client/payload_client.dart b/lib/pangea/payload_client/payload_client.dart index 8b5be8df5..d5865298f 100644 --- a/lib/pangea/payload_client/payload_client.dart +++ b/lib/pangea/payload_client/payload_client.dart @@ -112,6 +112,7 @@ class PayloadClient { int? page, int? limit, Map? where, + Map? select, String? sort, }) async { final Map queryParams = {}; @@ -119,6 +120,7 @@ class PayloadClient { if (page != null) queryParams['page'] = page.toString(); if (limit != null) queryParams['limit'] = limit.toString(); if (where != null && where.isNotEmpty) queryParams['where'] = where; + if (select != null && select.isNotEmpty) queryParams['select'] = select; if (sort != null) queryParams['sort'] = sort; final endpoint =