Skip to content

Fix/learn guest mode ux and dashboard stability#3720

Merged
Baalmart merged 9 commits into
stagingfrom
fix/learn-guest-mode-ux-and-dashboard-stability
Jul 1, 2026
Merged

Fix/learn guest mode ux and dashboard stability#3720
Baalmart merged 9 commits into
stagingfrom
fix/learn-guest-mode-ux-and-dashboard-stability

Conversation

@Mozart299

@Mozart299 Mozart299 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Learning content now loads from the updated catalog experience, with improved lesson visuals and course cover images.
    • Lesson completion can now sync in the background, helping progress stay saved across devices and offline sessions.
    • Country selection shows loading placeholders while data is being fetched.
  • Bug Fixes

    • Shortened location lookup time for faster country detection.
    • Improved handling of missing location data when filtering nearby items.
    • Guests no longer trigger survey response syncing.

Mozart299 added 3 commits July 1, 2026 19:50
… shimmer

- Remove pilot demo seed from production builds; add one-time migration
  to clear previously seeded fake progress for all users
- Stop auto-showing completion sheet when reopening an already-complete
  lesson; completed lessons now replay from step 0
- Guard getSurveyResponses API sync behind auth check to avoid noisy
  auth errors and wasted requests for guest users
- Reduce country detection location timeout from 10s to 5s
- Downgrade nearby-view early-return log from warning to info
- Show shimmer placeholders in the country tab row while dashboard data
  is loading instead of rendering an empty row
Replaces GET /api/v2/devices/kya/lessons with GET /api/v2/devices/learn/catalog.
Adds LearnV2CatalogResponse model, LearnRepositoryImpl, and LearnSyncService for
guest session management and offline progress buffering. Real lesson activities from
the v2 API are now used in the experience widget when available, falling back to the
demo script for any lessons without v2 content. Guest progress is reported fire-and-
forget and buffered locally when offline for sync on reconnect.
- LSP/DIP: Add getCachedCatalog() to LearnRepository interface so KyaBloc
  no longer needs a downcast to LearnRepositoryImpl to access it.
- DRP: Consolidate LearnQuizFormat <-> API string mapping onto the enum
  itself (apiKey getter + fromApiKey factory). Removes duplicate switch
  statements from LearnLessonExperienceService and LearnLessonExperience.
- SRP: Extract _ProgressBuffer from LearnSyncService to separate offline
  buffering from session management and progress reporting.
@Mozart299 Mozart299 self-assigned this Jul 1, 2026
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Mozart299, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 57 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0471a7d9-fab7-4861-b761-1a135a768667

📥 Commits

Reviewing files that changed from the base of the PR and between dea10cd and 4d31bf0.

📒 Files selected for processing (17)
  • src/mobile/lib/main.dart
  • src/mobile/lib/src/app/dashboard/widgets/analytics_specifics.dart
  • src/mobile/lib/src/app/learn/bloc/kya_bloc.dart
  • src/mobile/lib/src/app/learn/models/learn_course_structure.dart
  • src/mobile/lib/src/app/learn/models/lesson_response_model.dart
  • src/mobile/lib/src/app/learn/pages/kya_page.dart
  • src/mobile/lib/src/app/learn/pages/lesson_page.dart
  • src/mobile/lib/src/app/learn/repository/kya_repository.dart
  • src/mobile/lib/src/app/learn/repository/learn_repository.dart
  • src/mobile/lib/src/app/learn/services/learn_lesson_experience_service.dart
  • src/mobile/lib/src/app/learn/services/learn_progress_service.dart
  • src/mobile/lib/src/app/learn/services/learn_sync_service.dart
  • src/mobile/lib/src/app/learn/widgets/experience/learn_lesson_experience.dart
  • src/mobile/lib/src/app/learn/widgets/kya_lesson_container.dart
  • src/mobile/lib/src/app/learn/widgets/learn_bottom_sheets.dart
  • src/mobile/test/app/learn/bloc/kya_bloc_test.dart
  • src/mobile/test/widget_test.dart
📝 Walkthrough

Walkthrough

This PR migrates the Learn (formerly KYA) feature from a legacy lesson-response model to a new "Learn V2" catalog data model, adding LearnRepository/LearnRepositoryImpl with caching, a LearnSyncService for guest session and progress sync, updated bloc/state/page/view-model code, plus minor unrelated dashboard, survey, and location tweaks.

Changes

Learn V2 catalog migration

Layer / File(s) Summary
LearnV2 catalog models
src/mobile/lib/src/app/learn/models/learn_v2_catalog.dart, src/mobile/lib/src/app/learn/models/learn_lesson_activity.dart
New JSON models for catalog/course/unit/lesson/activity; LearnQuizFormat gains apiKey/fromApiKey for API key mapping.
Repository fetch/cache
src/mobile/lib/src/app/learn/repository/learn_repository.dart, .../kya_repository.dart, src/mobile/lib/src/meta/utils/api_utils.dart
LearnRepositoryImpl.fetchCatalog fetches/caches the catalog with offline fallback and background refresh; new learn* API endpoint constants replace fetchLessons.
Bloc/event/state retyping
src/mobile/lib/src/app/learn/bloc/kya_bloc.dart, kya_event.dart, kya_state.dart
KyaBloc now depends on LearnRepository and V2 catalog types; events/states retyped from LessonResponseModel to LearnV2CatalogResponse.
Course view-model building
src/mobile/lib/src/app/learn/models/learn_course_structure.dart, learn_lesson_continuation.dart
LearnLessonSlot/LearnCourseViewModel gain v2Lesson/coverImageUrl; new buildFromV2Catalog mapper; nextLesson getter removed.
Kya page rendering
src/mobile/lib/src/app/learn/pages/kya_page.dart
Courses and cover images derived from the V2 catalog; pilot-demo seeding gated by kDebugMode.
Lesson experience & activities
src/mobile/lib/src/app/learn/services/learn_lesson_experience_service.dart, .../widgets/experience/learn_lesson_experience.dart
New buildFromV2Lesson converts V2 activities to lesson activities; lesson experience widget builds scripts from v2 data and records quiz attempts.
Guest sync service & progress cleanup
src/mobile/lib/src/app/learn/services/learn_sync_service.dart, learn_progress_service.dart
New LearnSyncService handles guest session, account linking, completion reporting with local buffering, and pending sync; LearnProgressService gains one-time pilot-seed cleanup.
App wiring
src/mobile/lib/main.dart
Wires LearnRepositoryImpl into AirqoMobile and initializes guest session/sync on first frame.

Unrelated dashboard and survey tweaks

Layer / File(s) Summary
Minor dashboard/survey adjustments
.../location_service_mananger.dart, .../nearby_view.dart, .../view_selector.dart, .../survey_repository.dart
Reduced geolocation timeout, log-level change, shimmer placeholders for unknown active countries, and guest-user early return in survey sync.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested reviewers: Baalmart

Poem

A rabbit hopped through catalogs deep,
Swapped old lessons for V2 to keep,
Guest sessions bloom, progress synced with care,
Shimmer pills glow while data's laid bare,
🐇 Hop, cache, sync — the Learn stack's complete! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main themes of the PR: learn guest-mode UX updates and dashboard stability improvements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/learn-guest-mode-ux-and-dashboard-stability

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • JIRA integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (5)
src/mobile/lib/src/app/learn/models/learn_v2_catalog.dart (1)

9-173: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider value equality on catalog models.

None of LearnV2CatalogResponse/LearnV2Course/LearnV2Unit/LearnV2Lesson/LearnV2Activity override ==/hashCode. These are embedded in KyaState subclasses' props (see kya_state.dart), which Equatable-based Bloc states use both for its own emit dedup and for BlocBuilder rebuild comparisons. Two structurally-identical catalogs from separate JSON decodes will never be ==, so every refresh — even a no-op one returning the same data — is treated as a distinct state, causing extra rebuilds/emits.

♻️ Add Equatable to reduce redundant rebuilds
+import 'package:equatable/equatable.dart';
+
-class LearnV2CatalogResponse {
+class LearnV2CatalogResponse extends Equatable {
   final bool success;
   final String catalogVersion;
   final List<LearnV2Course> courses;

   const LearnV2CatalogResponse({
     required this.success,
     required this.catalogVersion,
     required this.courses,
   });
+
+  `@override`
+  List<Object?> get props => [success, catalogVersion, courses];

(repeat similarly for the nested model classes)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mobile/lib/src/app/learn/models/learn_v2_catalog.dart` around lines 9 -
173, The catalog model classes currently rely on reference equality, so
identical JSON-decoded data is treated as different by Equatable-based states.
Update LearnV2CatalogResponse, LearnV2Course, LearnV2Unit, LearnV2Lesson, and
LearnV2Activity to support value equality by extending Equatable or otherwise
overriding == and hashCode, and include all of their fields in equality/props.
Keep the existing fromJson/toJson behavior unchanged, and ensure nested
collections like courses, units, lessons, activities, and payload are part of
the equality comparison so KyaState props can dedupe no-op updates correctly.
src/mobile/lib/src/app/learn/pages/kya_page.dart (1)

166-168: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Silent stale-data fallback on error.

When LessonsLoadingError carries a cachedModel, the error UI is skipped entirely and courses render from the stale cache with no indication to the user that the latest refresh failed. Worth a lightweight "showing cached content" affordance, though not blocking.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mobile/lib/src/app/learn/pages/kya_page.dart` around lines 166 - 168, The
current LessonsLoadingError handling in kya_page.dart silently falls back to
cached content whenever cachedModel is present, so users never see that the
refresh failed. Update the error-path logic around the LessonsLoadingError check
in the KyaPage build flow to keep rendering the cached courses but also surface
a lightweight non-blocking affordance/message that fresh loading failed and
cached content is being shown. Use the existing _buildErrorState and
cachedModel-related state handling in KyaPage to place this notice without
replacing the cached UI.
src/mobile/lib/src/app/learn/repository/learn_repository.dart (1)

20-21: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Learn catalog cache reuses the location Hive box.

_catalogCacheKey is stored under CacheBoxName.location, which conceptually belongs to geolocation data. This mirrors an existing convention in kya_repository.dart, but a new file is a good opportunity to introduce a dedicated CacheBoxName (e.g. learn) instead of perpetuating the conflation — reduces risk of confusion if location-specific cache maintenance (eviction, migrations, "clear on logout", etc.) is ever added and inadvertently wipes/affects unrelated Learn catalog data.

Also applies to: 28-52, 104-117, 144-151

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mobile/lib/src/app/learn/repository/learn_repository.dart` around lines
20 - 21, The Learn catalog cache is being stored in the shared location Hive
box, which mixes unrelated concerns and can cause future maintenance to affect
Learn data. Update LearnRepository to use a dedicated CacheBoxName for Learn
(instead of CacheBoxName.location) and wire _catalogCacheKey through that new
box in the cache read/write paths, following the existing CacheManager usage in
learn_repository.dart and the related methods that touch the catalog cache. Also
align any associated initialization or invalidation logic so Learn catalog data
is isolated from geolocation cache handling.
src/mobile/lib/src/app/learn/bloc/kya_bloc.dart (1)

32-47: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Unreachable catch block around _getCachedCatalog().

_getCachedCatalog() already swallows all errors internally and always returns a value (or null) — it never throws. The try { ... _getCachedCatalog() ... } catch (cacheError) wrapper at lines 32-47 is therefore dead code that can never execute its catch branch.

♻️ Simplification
-      try {
-        final LearnV2CatalogResponse? cachedModel =
-            await _getCachedCatalog();
-
-        emit(LessonsLoadingError(
-          message: e.toString(),
-          cachedModel: cachedModel,
-          isOffline: !_cacheManager.isConnected,
-        ));
-      } catch (cacheError) {
-        loggy.error('Error fetching cached catalog: $cacheError');
-        emit(LessonsLoadingError(
-          message: e.toString(),
-          isOffline: !_cacheManager.isConnected,
-        ));
-      }
+      final LearnV2CatalogResponse? cachedModel = await _getCachedCatalog();
+      emit(LessonsLoadingError(
+        message: e.toString(),
+        cachedModel: cachedModel,
+        isOffline: !_cacheManager.isConnected,
+      ));

Also applies to: 78-85

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mobile/lib/src/app/learn/bloc/kya_bloc.dart` around lines 32 - 47, The
try/catch around _getCachedCatalog() in kya_bloc.dart is dead code because
_getCachedCatalog() already handles its own failures and only returns a model or
null. Remove the unreachable catch branch and keep the error handling in the
surrounding bloc flow, using the cachedModel result from _getCachedCatalog()
directly when emitting LessonsLoadingError. Apply the same cleanup to the
duplicate pattern in the other referenced block so the code paths match and no
unreachable cache-error logging remains.
src/mobile/lib/src/app/learn/repository/kya_repository.dart (1)

22-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the retired KYA repository path
KyaRepository/KyaImpl has no live callers anymore, so keeping the hardcoded /api/v2/devices/kya/lessons here just leaves dead code and a misleading fallback behind. Remove this file and the retired endpoint constant instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mobile/lib/src/app/learn/repository/kya_repository.dart` around lines 22
- 23, The retired KYA repository fallback is dead code and should be removed.
Delete the hardcoded _fetchLessonsPath constant from KyaRepository and remove
the obsolete kya_repository.dart implementation (including KyaImpl) so there is
no lingering /api/v2/devices/kya/lessons endpoint reference.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/mobile/lib/main.dart`:
- Around line 256-259: The guest Learn sync flow currently only creates the
guest session and syncs pending progress in _initLearnGuestSession, but it never
transfers that progress after sign-in. Update the AuthLoaded handling in the
auth-success path to call LearnSyncService.instance.linkProgressToAccount(...)
so any guest progress is attached to the signed-in account, and keep the
existing guest-session initialization logic separate from the account-linking
step.

In `@src/mobile/lib/src/app/learn/models/learn_course_structure.dart`:
- Around line 22-28: The activityCount getter in learn_course_structure.dart is
falling back to the demo constant for every non-v2 lesson, even when apiLesson
has real tasks. Update activityCount to mirror hasContent’s branching in
LearnCourseStructure: when v2Lesson is null, return apiLesson!.tasks.length if
apiLesson exists, and only use LearnLessonExperienceService.demoActivityCount as
the final fallback when no legacy lesson data is available.

In `@src/mobile/lib/src/app/learn/pages/kya_page.dart`:
- Around line 160-172: The course catalog selection in kya_page.dart does not
preserve data during background refresh, so LessonsRefreshing falls through the
switch and clears the grid. Update the catalog resolution logic in the body that
computes catalog/courses to also map LessonsRefreshing to its currentModel (and
keep the existing LessonsLoaded and LessonsLoadingError handling). Use the
LessonsRefreshing symbol from kya_state.dart and ensure the fallback only
returns null when no cached/current model exists, so the course grid stays
populated while refresh is in progress.

In `@src/mobile/lib/src/app/learn/repository/learn_repository.dart`:
- Around line 59-65: The Learn catalog flow is still calling
ApiUtils.learnCatalog when AIRQO_API_TOKEN is absent, which should be handled as
a configuration problem instead of a generic request failure. Update
LearnRepository.fetchCatalog and the background refresh path in
_refreshInBackground to use the same fail-fast token check pattern as
KyaImpl.fetchLessons, and bail out early with the existing configuration error
when dotenv.env['AIRQO_API_TOKEN'] is missing. Keep the request-building logic
in createGetRequest unchanged for the valid-token path.

In `@src/mobile/lib/src/app/learn/services/learn_lesson_experience_service.dart`:
- Around line 209-213: The quiz payload parsing in
learn_lesson_experience_service is too strict when reading correct_index,
correct_indices, and correct_order from the dynamic map. Update the parsing
logic around the correctIndex/correctIndices/correctOrder extraction to validate
each element type and use int.tryParse for string values instead of direct
casts, so mixed or string-based API values don’t throw during lesson experience
parsing. Keep the fix localized to the quiz parsing branch and preserve the
existing data shape handling.

In `@src/mobile/lib/src/app/learn/services/learn_sync_service.dart`:
- Around line 128-149: In linkProgressToAccount in LearnSyncService, pending
guest progress may still exist when _guestIdKey is removed after a successful
link. Update the flow so buffered local completions are flushed via
syncPendingProgress() before clearing the guest ID, or otherwise keep the guest
identifier until the pending queue is empty. Use the existing _guestIdKey,
syncPendingProgress(), and linkProgressToAccount() flow to place the fix at the
account-link success path.
- Line 120: Keep the success log in LearnSyncService but stop interpolating the
persistent guest/session identifier; update the loggy.info call in the guest
session creation flow to emit a generic success message without guestId. Locate
the logging statement in the method that creates the guest Learn session and
remove the identifier value while preserving the event context.
- Line 178: The LearnSyncService request URL is interpolating lessonId directly
into the path, which can break routing when the ID contains reserved characters.
Update the URL निर्माण in the sync/completion request to encode lessonId before
concatenation, using the existing Uri.parse call in LearnSyncService so the path
remains valid for dynamic catalog IDs.

In
`@src/mobile/lib/src/app/learn/widgets/experience/learn_lesson_experience.dart`:
- Around line 64-84: Guard the empty-script case in LearnLessonExperienceState
before _current is accessed: buildFromV2Lesson in LearnLessonExperienceService
can return an empty list, so in the initialization path that sets _script and
_activityIndex, ensure empty _script is handled by short-circuiting or rendering
a fallback before build() indexes into it. Use the LearnLessonExperienceState
initializer and the _current accessor/build path to locate the fix, and make
sure _activityIndex is never used when _script.isEmpty.

---

Nitpick comments:
In `@src/mobile/lib/src/app/learn/bloc/kya_bloc.dart`:
- Around line 32-47: The try/catch around _getCachedCatalog() in kya_bloc.dart
is dead code because _getCachedCatalog() already handles its own failures and
only returns a model or null. Remove the unreachable catch branch and keep the
error handling in the surrounding bloc flow, using the cachedModel result from
_getCachedCatalog() directly when emitting LessonsLoadingError. Apply the same
cleanup to the duplicate pattern in the other referenced block so the code paths
match and no unreachable cache-error logging remains.

In `@src/mobile/lib/src/app/learn/models/learn_v2_catalog.dart`:
- Around line 9-173: The catalog model classes currently rely on reference
equality, so identical JSON-decoded data is treated as different by
Equatable-based states. Update LearnV2CatalogResponse, LearnV2Course,
LearnV2Unit, LearnV2Lesson, and LearnV2Activity to support value equality by
extending Equatable or otherwise overriding == and hashCode, and include all of
their fields in equality/props. Keep the existing fromJson/toJson behavior
unchanged, and ensure nested collections like courses, units, lessons,
activities, and payload are part of the equality comparison so KyaState props
can dedupe no-op updates correctly.

In `@src/mobile/lib/src/app/learn/pages/kya_page.dart`:
- Around line 166-168: The current LessonsLoadingError handling in kya_page.dart
silently falls back to cached content whenever cachedModel is present, so users
never see that the refresh failed. Update the error-path logic around the
LessonsLoadingError check in the KyaPage build flow to keep rendering the cached
courses but also surface a lightweight non-blocking affordance/message that
fresh loading failed and cached content is being shown. Use the existing
_buildErrorState and cachedModel-related state handling in KyaPage to place this
notice without replacing the cached UI.

In `@src/mobile/lib/src/app/learn/repository/kya_repository.dart`:
- Around line 22-23: The retired KYA repository fallback is dead code and should
be removed. Delete the hardcoded _fetchLessonsPath constant from KyaRepository
and remove the obsolete kya_repository.dart implementation (including KyaImpl)
so there is no lingering /api/v2/devices/kya/lessons endpoint reference.

In `@src/mobile/lib/src/app/learn/repository/learn_repository.dart`:
- Around line 20-21: The Learn catalog cache is being stored in the shared
location Hive box, which mixes unrelated concerns and can cause future
maintenance to affect Learn data. Update LearnRepository to use a dedicated
CacheBoxName for Learn (instead of CacheBoxName.location) and wire
_catalogCacheKey through that new box in the cache read/write paths, following
the existing CacheManager usage in learn_repository.dart and the related methods
that touch the catalog cache. Also align any associated initialization or
invalidation logic so Learn catalog data is isolated from geolocation cache
handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 44023e46-dd68-46d4-90fe-6846ea79ea69

📥 Commits

Reviewing files that changed from the base of the PR and between 56af1ac and dea10cd.

📒 Files selected for processing (20)
  • src/mobile/lib/main.dart
  • src/mobile/lib/src/app/dashboard/services/location_service_mananger.dart
  • src/mobile/lib/src/app/dashboard/widgets/nearby_view.dart
  • src/mobile/lib/src/app/dashboard/widgets/view_selector.dart
  • src/mobile/lib/src/app/learn/bloc/kya_bloc.dart
  • src/mobile/lib/src/app/learn/bloc/kya_event.dart
  • src/mobile/lib/src/app/learn/bloc/kya_state.dart
  • src/mobile/lib/src/app/learn/models/learn_course_structure.dart
  • src/mobile/lib/src/app/learn/models/learn_lesson_activity.dart
  • src/mobile/lib/src/app/learn/models/learn_lesson_continuation.dart
  • src/mobile/lib/src/app/learn/models/learn_v2_catalog.dart
  • src/mobile/lib/src/app/learn/pages/kya_page.dart
  • src/mobile/lib/src/app/learn/repository/kya_repository.dart
  • src/mobile/lib/src/app/learn/repository/learn_repository.dart
  • src/mobile/lib/src/app/learn/services/learn_lesson_experience_service.dart
  • src/mobile/lib/src/app/learn/services/learn_progress_service.dart
  • src/mobile/lib/src/app/learn/services/learn_sync_service.dart
  • src/mobile/lib/src/app/learn/widgets/experience/learn_lesson_experience.dart
  • src/mobile/lib/src/app/surveys/repository/survey_repository.dart
  • src/mobile/lib/src/meta/utils/api_utils.dart
💤 Files with no reviewable changes (1)
  • src/mobile/lib/src/app/learn/models/learn_lesson_continuation.dart

Comment thread src/mobile/lib/main.dart
Comment thread src/mobile/lib/src/app/learn/models/learn_course_structure.dart Outdated
Comment thread src/mobile/lib/src/app/learn/pages/kya_page.dart
Comment thread src/mobile/lib/src/app/learn/repository/learn_repository.dart Outdated
Comment thread src/mobile/lib/src/app/learn/services/learn_lesson_experience_service.dart Outdated
Comment thread src/mobile/lib/src/app/learn/services/learn_sync_service.dart Outdated
Comment thread src/mobile/lib/src/app/learn/services/learn_sync_service.dart
Comment thread src/mobile/lib/src/app/learn/services/learn_sync_service.dart Outdated
Comment thread src/mobile/lib/src/app/learn/widgets/experience/learn_lesson_experience.dart Outdated
Mozart299 added 3 commits July 1, 2026 21:56
- Sign-in: call LearnSyncService.linkProgressToAccount on AuthLoaded
  so guest progress transfers to the signed-in account.
- kya_page: map LessonsRefreshing to its currentModel in catalog switch
  so the course grid stays populated during background refresh.
- LearnRepository: fail-fast when AIRQO_API_TOKEN is absent in both
  fetchCatalog and _refreshInBackground instead of sending an empty token.
- Quiz parsing: use _toInt() helper with int.tryParse fallback for
  correct_index/correct_indices/correct_order to tolerate string-typed values.
- LearnSyncService: redact guest session ID from success log.
- LearnSyncService: flush pending buffer before clearing guestId in
  linkProgressToAccount so offline completions are not orphaned.
- LearnSyncService: URI-encode lessonId in the progress PUT path.
- LearnLessonExperience: guard empty _script in build() to prevent
  RangeError when a v2 lesson has no activities.
Delete kya_repository.dart, kya_lesson_container.dart, lesson_response_model.dart.
Strip buildFromLessons, buildDemoScript, courseCoverImage, syncLegacyApiProgress,
ensurePilotLearnDemosV3, showLesson (legacy), and all apiLesson references.
Update bloc tests to use LearnRepository / LearnV2CatalogResponse.
Remove const from non-const ColorFilter.mode in analytics_specifics.dart.
Drop redundant null-coalesce on non-nullable return in feature_flag_service.dart.
@Mozart299
Mozart299 requested a review from Baalmart July 1, 2026 19:23
DIP: add isOffline to LearnRepository interface so KyaBloc no longer
instantiates CacheManager directly.

DRP: extract _fetchAndCache/_getToken helpers in LearnRepositoryImpl
to eliminate duplicated token+request logic between fetchCatalog and
_refreshInBackground.

DIP: introduce abstract LearnSyncService interface; rename impl to
_LearnSyncServiceImpl so callers depend on the abstraction.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the mobile “Learn / KYA” experience from the legacy flat lessons API to a v2 catalog structure, adds guest-mode progress syncing/linking, and includes a few dashboard stability/UX improvements (loading placeholders, faster location lookup, and safer survey syncing for guests).

Changes:

  • Replace legacy KYA lessons API/repo/models with a Learn v2 catalog model + repository and update bloc/tests accordingly.
  • Add Learn guest session + offline progress buffering/syncing, and link guest progress to an authenticated account after login.
  • Improve dashboard UX/stability (country shimmer placeholders, faster geolocation timeLimit, less noisy nearby-position logging) and prevent guest survey sync calls.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/mobile/test/widget_test.dart Updates test wiring/types to use LearnRepository instead of legacy KyaRepository.
src/mobile/test/app/learn/bloc/kya_bloc_test.dart Updates bloc tests to mock LearnRepository and v2 catalog responses.
src/mobile/lib/src/meta/utils/api_utils.dart Replaces legacy lessons endpoint with new Learn v2 catalog/sessions/progress endpoints.
src/mobile/lib/src/app/surveys/repository/survey_repository.dart Skips server sync for guests when fetching survey responses.
src/mobile/lib/src/app/shared/services/feature_flag_service.dart Adjusts PostHog flag fetch assignment logic (but currently introduces a null-safety issue).
src/mobile/lib/src/app/learn/widgets/learn_bottom_sheets.dart Removes legacy lesson entry path and legacy API lesson wiring.
src/mobile/lib/src/app/learn/widgets/kya_lesson_container.dart Deletes legacy flat KYA lesson card widget.
src/mobile/lib/src/app/learn/widgets/experience/learn_lesson_experience.dart Builds lesson script from v2 lesson activities and reports completion via sync service.
src/mobile/lib/src/app/learn/services/learn_sync_service.dart Adds guest session creation, offline progress buffer, sync, and account-linking flow.
src/mobile/lib/src/app/learn/services/learn_progress_service.dart Adds one-time cleanup migration for pilot-seeded progress; removes pilot seeding logic.
src/mobile/lib/src/app/learn/services/learn_lesson_experience_service.dart Replaces demo-script generation with v2 activity parsing (article/video/image/quiz).
src/mobile/lib/src/app/learn/repository/learn_repository.dart New repository for fetching/caching v2 catalog and exposing offline status.
src/mobile/lib/src/app/learn/repository/kya_repository.dart Removes legacy KYA repository implementation.
src/mobile/lib/src/app/learn/pages/lesson_page.dart Removes legacy apiLesson plumb-through to lesson experience.
src/mobile/lib/src/app/learn/pages/kya_page.dart Switches KYA page to v2 catalog-driven courses + cover image usage.
src/mobile/lib/src/app/learn/models/lesson_response_model.dart Removes legacy lessons response model.
src/mobile/lib/src/app/learn/models/learn_v2_catalog.dart Adds v2 catalog model (courses/units/lessons/activities).
src/mobile/lib/src/app/learn/models/learn_lesson_continuation.dart Removes legacy nextLesson accessor that depended on old API lesson.
src/mobile/lib/src/app/learn/models/learn_lesson_activity.dart Adds API-key mapping for quiz formats.
src/mobile/lib/src/app/learn/models/learn_course_structure.dart Updates slot/course models for v2 lesson data + cover image URL; builds catalog from v2 courses.
src/mobile/lib/src/app/learn/bloc/kya_state.dart Updates states to hold LearnV2CatalogResponse instead of legacy lessons model.
src/mobile/lib/src/app/learn/bloc/kya_event.dart Updates refresh event to carry LearnV2CatalogResponse.
src/mobile/lib/src/app/learn/bloc/kya_bloc.dart Switches bloc to LearnRepository, uses cached catalog + repository offline flag.
src/mobile/lib/src/app/dashboard/widgets/view_selector.dart Adds shimmer placeholders when active countries haven’t loaded.
src/mobile/lib/src/app/dashboard/widgets/nearby_view.dart Downgrades missing-position log message and clarifies behavior.
src/mobile/lib/src/app/dashboard/widgets/analytics_specifics.dart Minor const removal on ColorFilter.mode.
src/mobile/lib/src/app/dashboard/services/location_service_mananger.dart Reduces geolocation time limit from 10s to 5s.
src/mobile/lib/main.dart Wires LearnRepositoryImpl, initializes Learn guest session/sync at startup, and links progress after login token refresh.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/mobile/lib/src/app/shared/services/feature_flag_service.dart Outdated
Comment thread src/mobile/lib/src/app/learn/services/learn_sync_service.dart
Mozart299 and others added 2 commits July 1, 2026 23:24
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@Baalmart
Baalmart merged commit 9aecf59 into staging Jul 1, 2026
25 of 27 checks passed
@Baalmart
Baalmart deleted the fix/learn-guest-mode-ux-and-dashboard-stability branch July 1, 2026 22:15
@Baalmart Baalmart mentioned this pull request Jul 1, 2026
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants