Skip to content

Conversation

@kristofnemere
Copy link
Contributor

@kristofnemere kristofnemere commented Jan 27, 2026

Summary

Implements Part 2 of the My Courses Widget for the new dashboard, including:

  • Course Reordering: Drag and drop functionality with backend sync and cache invalidation
  • Announcement Actions: Tapping announcement icon opens announcement details or list
  • Group Messaging: Tapping message icon on group cards opens compose with all members pre-selected
  • Course Customization: 3-dots menu for editing nickname, changing color, and managing offline content
  • Offline Mode Support: Display sync indicators and disable unsynced course interactions
  • Architecture Improvements: Separated announcement and course nickname repositories for better maintainability

Test Plan

Course Reordering

  1. Open the app and navigate to the Dashboard
  2. Long-press on any course card in the widget
  3. Drag the course card to a different position
  4. Release to drop it in the new position
  5. Verify the card moves to the new position immediately
  6. Close and restart the app
  7. Verify the course order is maintained after restart
  8. Test with multiple courses (3+ recommended) in both single column (phone) and multi-column (tablet) layouts

Announcement Actions

  1. Navigate to Dashboard with courses that have announcements
  2. Tap the announcement icon (📢) on a course card with 1 announcement
  3. Verify it opens the announcement details directly
  4. Tap the announcement icon on a course with multiple announcements
  5. Verify it opens the announcement list
  6. Test with courses that have no announcements (icon should not appear)

Group Messaging

  1. Navigate to Dashboard with groups displayed
  2. Tap the message/inbox icon on a group card
  3. Verify the compose inbox screen opens
  4. Verify all group members are pre-populated as recipients
  5. Test sending a message to the group

Course/Group Customization (3-Dots Menu)

  1. Tap the 3-dots menu on a course card
  2. Select "Customize Course"
  3. Change the course nickname - verify it updates and persists
  4. Change the course color - verify it updates across the app
  5. Test "Manage Offline Content" option (if offline feature is enabled)
  6. Verify all changes persist after app restart
  7. Test in both light and dark modes (verify cursor visibility in nickname field)

Offline Mode & Sync Status

  1. Enable offline mode and sync a course
  2. Go offline (disable network)
  3. Navigate to Dashboard
  4. Verify synced courses display the sync indicator icon
  5. Verify synced courses can be opened and browsed
  6. Verify unsynced courses cannot be opened
  7. Verify appropriate feedback when attempting to open unsynced courses
  8. Test with mixed synced/unsynced courses

Navigation Drawer Updates

  1. Open navigation drawer
  2. Verify old dashboard-related switches are hidden when new dashboard is enabled
  3. Test switching between old and new dashboard (if toggle available)

Accessibility

  1. Enable TalkBack
  2. Navigate through course cards, announcement icons, and 3-dots menus
  3. Verify all interactive elements are announced properly
  4. Test course reordering with TalkBack assistance

refs: MBL-19462
affects: Student, Teacher
release note: Improved My Courses widget with drag-and-drop reordering, quick access to announcements and group messaging, course customization options, and enhanced offline support

Technical Details

Course Reordering

  • Added sh.calvin.reorderable library (v2.4.0) for drag and drop support
  • Implemented onCourseMoved callback in CoursesWidgetViewModel to handle reordering
  • Updated UserRepository.updateDashboardPositions to persist order and invalidate cache on success
  • Extracted COURSE_CARD_HEIGHT constant to avoid duplication

Repository Separation

  • Created AnnouncementRepository and AnnouncementRepositoryImpl for announcement operations
  • Created CourseNicknameRepository and CourseNicknameRepositoryImpl for nickname management
  • Added LoadCourseAnnouncementsUseCase for fetching course announcements
  • Added SetCourseColorUseCase and SetCourseNicknameUseCase for customization

UI Components

  • Added CustomizeCourseScreen with nickname editing and color picker
  • Enhanced CourseCard with announcement icon and 3-dots menu
  • Enhanced GroupCard with message/inbox icon
  • Improved color picker accessibility
  • Fixed cursor visibility in dark mode for text fields

Offline Support

  • Added sync indicator display for synced courses
  • Disabled interaction with unsynced courses when offline
  • Added network requirement dialog for actions requiring network

Navigation Updates

  • Updated Student and Teacher routers with new navigation routes
  • Added announcementDetails and announcementList routes
  • Added customizeCourse route
  • Updated InboxComposeRepository to support group recipient pre-population
  • Hidden old dashboard switches in navigation drawer

Testing

  • Added comprehensive unit tests for:
    • AnnouncementRepository and LoadCourseAnnouncementsUseCase
    • CourseNicknameRepository and nickname use cases
    • Course reordering logic and cache invalidation
    • CustomizeCourseViewModel with all customization flows
    • Widget configuration and settings persistence

Files Changed

New Files:

  • AnnouncementRepository.kt / AnnouncementRepositoryImpl.kt
  • CourseNicknameRepository.kt / CourseNicknameRepositoryImpl.kt
  • LoadCourseAnnouncementsUseCase.kt
  • SetCourseColorUseCase.kt / SetCourseNicknameUseCase.kt
  • CustomizeCourseScreen.kt / CustomizeCourseViewModel.kt / CustomizeCourseUiState.kt
  • Comprehensive test files for all new components

Modified Files:

  • CoursesWidgetViewModel.kt - Added reordering and announcement handling
  • CourseCard.kt - Added announcement icon and 3-dots menu
  • GroupCard.kt - Added message icon
  • UserRepositoryImpl.kt - Cache invalidation after position updates
  • Navigation drawer layouts - Hidden old dashboard switches
  • Router classes - Added new navigation routes

Checklist

  • Follow-up e2e test ticket created or not needed
  • Tested in dark mode
  • Tested in light mode
  • Test in landscape mode and/or tablet
  • A11y checked
  • Approve from product

kristofnemere and others added 10 commits January 21, 2026 16:27
Added unit test coverage for the new course customization, announcement,
and group messaging features:

- SetCourseColorUseCaseTest with 4 tests for color change functionality
- 11 new tests in CoursesWidgetViewModelTest for announcement loading,
  click handling, group messaging, and broadcast receiver behavior
- Fixed existing broadcast receiver tests to properly mock Intent.getLongExtra
- Added CustomizeCourseBehavior binding to parent app's DefaultBindingsModule
  to resolve Dagger injection error

Tests verify:
- Use cases correctly delegate to repositories and handle errors
- Announcements are loaded, mapped, and filtered for unread status
- Click handlers properly find courses/groups and delegate to behaviors
- Broadcast receiver handles both COURSE_ID and COURSE_FAVORITES changes
- Error scenarios are handled gracefully with crashlytics logging

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…, announcements, and group messaging

This commit implements the remaining features for the My Courses Widget:

## Course Customization
- Added course customize screen allowing users to change course color and nickname
- Implemented CustomizeCourseViewModel with color picker and nickname editing
- Created CustomizeCourseBehavior interface with Student/Teacher implementations
- Student app: color overlay visibility controlled by StudentPrefs
- Teacher app: color overlay always visible
- Router integration for navigation to customize screen from 3-dots menu
- Added CourseNicknameAPI for setting/deleting course nicknames
- Broadcasts COURSE_ID changes for single course updates

## Announcements
- Implemented LoadCourseAnnouncementsUseCase to fetch unread announcements
- Added announcement count badge to course cards
- Announcement icon click routes to:
  - Single announcement: opens announcement details
  - Multiple announcements: opens announcement list
- Graceful error handling with Crashlytics logging
- Caches announcements in UI state to avoid redundant network calls

## Group Messaging
- Added message icon to group cards
- Click handler routes to inbox compose with group pre-selected
- Integrated with existing inbox compose functionality

## Repository & API Changes
- Extended UserRepository with course nickname and color operations
- Added InboxComposeRepository.getGroups() returning empty list (not implemented)
- Fixed InboxComposeRepository constructors across Student/Teacher/Parent apps
- Enhanced ColorKeeper with cache management and hex conversion utilities

## Broadcast Receiver Improvements
- Enhanced broadcast receiver to handle COURSE_ID for targeted updates
- Supports COURSE_FAVORITES for full refresh scenarios
- Single course reload reloads both course data and announcements

## Testing
- SetCourseColorUseCaseTest: 4 tests for color persistence
- SetCourseNicknameUseCaseTest: 4 tests for nickname operations
- LoadCourseAnnouncementsUseCaseTest: 7 tests for filtering unread announcements
- CustomizeCourseViewModelTest: 18 comprehensive tests for all customization flows
- StudentCustomizeCourseBehaviorTest: 2 tests for color overlay preference
- TeacherCustomizeCourseBehaviorTest: 1 test for always-visible overlay
- CoursesWidgetViewModelTest: 11 new tests for announcement/messaging features
- StudentCoursesWidgetBehaviorTest: 4 tests for routing logic
- Fixed existing broadcast receiver tests with proper Intent mocking

## DI & Module Updates
- Added CustomizeCourseBehavior bindings to Student/Teacher modules
- Added parent app binding (NotImplementedError for unused features)
- Registered CourseNicknameAPI in ApiModule

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added visual indicators for offline sync status:
- Show ic_offline_synced icon when course is synced for offline use
- Icon appears next to course name, before announcements
- Existing alpha fade (0.5f) and disabled clicks already handle unavailable courses

Implementation matches old dashboard pattern from CourseViewHolder.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add Compose instrumentation tests for CourseCard, GroupCard, and CustomizeCourseScreen with semantic test tags
- Fix CoursesWidgetTest compilation errors (CourseCardItem parameter changes)
- Add tests for ObserveWidgetConfigUseCase CoursesConfig support
- Refactor InboxComposeRepository: isolate group messaging to StudentInboxComposeRepository only, remove GroupAPI dependency from base class and Teacher/Parent implementations
- Add comprehensive tests for canSendToAll in StudentInboxComposeRepositoryTest (courses and groups)
- Add tests for UserRepository new methods (setCourseNickname, deleteCourseNickname, setCourseColor)
- Fix CourseBrowserFragment: cache feature flag, fix default values, prevent updateToolbarVisibility crash
- Fix copyright year in RequireNetworkDialog.kt (2025 -> 2026)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ttern

- Add messageGroup string resource for better accessibility
- Update GroupCard icon content description from generic "Inbox" to specific "Message group"
- Add announcement support to CourseRepository (getCourseAnnouncement, getCourseAnnouncements)
- Refactor LoadCourseAnnouncementsUseCase to use CourseRepository instead of direct API dependency
- Update CourseRepositoryImpl to include AnnouncementAPI with pagination support
- Add comprehensive tests for new CourseRepository announcement methods
- Update RepositoryModule DI to provide AnnouncementAPI to CourseRepository
- Update all related tests to use CourseRepository mock

This improves architecture by following proper repository pattern and enhances
accessibility for screen readers with more descriptive content descriptions.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Move Params data classes inside their respective UseCase classes and rename
them to just "Params" following the project convention:

- LoadCourseAnnouncementsUseCase.Params (was LoadCourseAnnouncementsParams)
- SetCourseColorUseCase.Params (was SetCourseColorParams)
- SetCourseNicknameUseCase.Params (was SetCourseNicknameParams)

Updated tests to reference the nested Params classes.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Create AnnouncementRepository for announcement-related operations
- Create CourseNicknameRepository for course nickname operations
- Remove announcement methods from CourseRepository
- Remove course nickname methods from UserRepository
- Update LoadCourseAnnouncementsUseCase to use AnnouncementRepository
- Update SetCourseNicknameUseCase to use CourseNicknameRepository
- Update RepositoryModule DI configuration for new repositories
- Update all repository and use case tests
- Refactor use case Params to be nested classes
- Update all callers to use nested Params pattern
- Fix cursor visibility in CustomizeCourseScreen for dark mode

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add sh.calvin.reorderable library (v2.4.0) dependency
- Convert courses NonLazyGrid to LazyVerticalGrid with reorderable support
- Add long-press drag gesture for course cards
- Implement onCourseMoved callback in ViewModel
- Add updateDashboardPositions method to UserRepository
- Persist course order to backend via UserAPI
- Update CoursesWidgetUiState with onCourseMoved callback

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Implement drag and drop functionality using sh.calvin.reorderable library
- Add onCourseMoved callback to handle reordering in ViewModel
- Update UserRepository to persist dashboard positions and invalidate cache
- Extract COURSE_CARD_HEIGHT constant to avoid duplication
- Add comprehensive tests for reordering and cache invalidation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@kristofnemere kristofnemere changed the title [MBL-19462][Student][Teacher] Add drag and drop reordering for courses widget [MBL-19462][Student][Teacher] My Courses Widget Part 2 - Actions, customization, and reordering Jan 27, 2026
@github-actions
Copy link

github-actions bot commented Jan 27, 2026

🧪 Unit Test Results

✅ 📱 Student App

  • Tests: 1228 total, 0 failed, 0 skipped
  • Duration: 0.000s
  • Success Rate: 100%

✅ 📱 Teacher App

  • Tests: 367 total, 0 failed, 0 skipped
  • Duration: 31.507s
  • Success Rate: 100%

✅ 🌅 Horizon

  • Tests: 531 total, 0 failed, 0 skipped
  • Duration: 39.038s
  • Success Rate: 100%

✅ 📦 Submodules

  • Tests: 2708 total, 0 failed, 0 skipped
  • Duration: 53.356s
  • Success Rate: 100%

📊 Summary

  • Total Tests: 4834
  • Failed: 0
  • Skipped: 0
  • Status: ✅ All tests passed!

Last updated: Tue, 27 Jan 2026 16:40:58 GMT

@github-actions
Copy link

github-actions bot commented Jan 27, 2026

Teacher Install Page

@github-actions
Copy link

github-actions bot commented Jan 27, 2026

Student Install Page

Removed tests for observeGradeVisibility and observeColorOverlay methods that no longer exist in the CoursesWidgetBehavior interface.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@github-actions
Copy link

📊 Code Coverage Report

⚠️ Student

  • PR Coverage: 43.31%
  • Master Coverage: 43.42%
  • Delta: -0.11%

⚠️ Teacher

  • PR Coverage: 25.47%
  • Master Coverage: 25.51%
  • Delta: -0.04%

✅ Pandautils

  • PR Coverage: 23.18%
  • Master Coverage: 22.98%
  • Delta: +0.20%

📈 Overall Average

  • PR Coverage: 30.65%
  • Master Coverage: 30.64%
  • Delta: +0.02%

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.

2 participants