Skip to content

[WIP] Lazy-load course activities per topic to reduce requests#5827

Closed
Copilot wants to merge 1 commit intomainfrom
copilot/lazy-load-activities-per-topic
Closed

[WIP] Lazy-load course activities per topic to reduce requests#5827
Copilot wants to merge 1 commit intomainfrom
copilot/lazy-load-activities-per-topic

Conversation

Copy link

Copilot AI commented Feb 26, 2026

  • Understand the issue and explore key files
  • Add activityRoleCounts: Map<String, int> to CourseTopicModel (fromJson/toJson)
  • Update _hasCompletedTopic() in ActivitySummariesProvider to use activityRoleCounts instead of loadedActivities
  • Update currentTopicId() to remove the activityListComplete guard
  • Remove loadAllActivities() call from _loadCourseInfo() in chat_details.dart, remove loadingActivities state
  • Update CourseSettings widget: add per-topic lazy loading trigger, replace global loadingActivities with per-topic !topic.activityListComplete check
  • Add unit test for CourseTopicModel.fromJson with activity_role_counts
  • Run tests and code review
Original prompt

This section details on the original issue you should resolve

<issue_title>Lazy-load course activities per topic instead of all at once</issue_title>
<issue_description>## Problem

When a user opens the course settings tab, ChatDetailsController._loadCourseInfo() calls loadAllActivities(), which fires one POST /choreo/activity_plan/localize request per topic with all of that topic's activity IDs. For a course with 10 topics × 5 activities each, this creates 10 concurrent HTTP requests to the choreographer, which in turn fans out to CMS — contributing to CMS connection pool exhaustion (Sentry CHOREOGRAPHER-2WX / CMS-J, 502 errors).

Why it exists today

ActivitySummariesProvider.currentTopicId() iterates all topics and checks activityListComplete + _hasCompletedTopic() — which needs numberOfParticipants from loaded activity objects — to determine topic unlock state. This forces all activities to be fetched eagerly just to compute which topic is unlocked.

Proposed change

  1. Remove loadAllActivities() from _loadCourseInfo() in lib/pages/chat_details/chat_details.dart (~line 356).
  2. Delegate activity fetching to each TopicActivitiesList widget — only fetch activities for a topic when its row is visible and unlocked. Locked topics already hide activities in the UI, so those requests are eliminated entirely.
  3. Decouple unlock logic from full activity loading — the simplest approach:
    • Option A: Have the choreo include numberOfParticipants in the topic response schema (ClientTopicSchema) so the client can compute unlock without loading activities.
    • Option B: Have currentTopicId() treat topics with unloaded activities as "not yet determined" and fall back to the first non-completed topic, then refine once activities arrive.
    • Option C: Return a lightweight {activityId: numberOfParticipants} map alongside activity IDs in the topic response.

Key files

  • lib/pages/chat_details/chat_details.dart — sole call site for loadAllActivities()
  • lib/pangea/course_plans/courses/course_plan_builder.dartCoursePlanProvider mixin with loadAllActivities()
  • lib/pangea/course_plans/course_activities/activity_summaries_provider.dartcurrentTopicId() and _hasCompletedTopic() (the reason eager loading exists)
  • lib/pangea/course_plans/courses/course_topic_model.dartactivityListComplete getter
  • lib/pangea/course_settings/course_settings.dartCourseSettings widget, renders topics + activities

Impact

Reduces HTTP requests on course settings open from N (one per topic) to 1–2 (only unlocked/visible topics). Eliminates a major contributor to the CMS 502 errors seen in production.</issue_description>

Comments on the Issue (you are @copilot in this section)

@WilsonLe Choreo tracking issue: https://github.com/pangeachat/2-step-choreographer/issues/1738 @WilsonLe ## Choreo side done ✅

PR merged: pangeachat/2-step-choreographer#1739 (closes pangeachat/2-step-choreographer#1738)

POST /choreo/topics/localize now returns activity_role_counts: {activity_id: number_of_participants} in each topic — this is Option C from the issue description. Example response:

{
  "topics": {
    "799cf868-...": {
      "uuid": "799cf868-...",
      "title": "Global Meetings, Future Plans",
      "activity_ids": ["10b57592-...", "22447483-..."],
      "activity_role_counts": {
        "10b57592-...": 2,
        "22447483-...": 2
      }
    }
  }
}

Client-side next steps

The client can now implement lazy activity loading:

  1. Update ClientTopicSchema model in lib/pangea/course_plans/ to include activityRoleCounts: Map<String, int> (new field from the topic response, defaults to {})
  2. Update _hasCompletedTopic() in ActivitySummariesProvider to use activityRoleCounts[activityId] from the topic data instead of requiring ActivityPlanFull.req.numberOfParticipants
  3. Remove loadAllActivities() call from ChatDetailsController._loadCourseInfo() — topic unlock state can now be computed from topic data alone
  4. Lazy-load activities per topic — fetch activities only when TopicActivitiesList is expanded/visible for an unlocked topic</comment_new>

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lazy-load course activities per topic instead of all at once

2 participants