Skip to content

Conversation

vargaat
Copy link
Collaborator

@vargaat vargaat commented Oct 1, 2025

What's new?

  • Re-introduced the new Todo list behind a local opt-in feature flag (for now).
  • Fixed app and tab badges not updating.
  • Limited item fetching interval from -28 to +28 days.
  • Updated cell design by re-using the widget cell with increased sizes and line counts not limited to one.
  • Added sticky day headers. I didn't re-use the widget one here.
  • Updated course name lookup logic to display the course's nickname and fall back to course code and original name if one of them is not available. This required new API and CoreData fields.
  • Updated empty state with a new panda. Also updated the panda previewer with pandas not yet added to it.
  • Updated date display on the todo cell not to display the day, only the hour because day information is already available in the sticky header.
  • Added a tap action to the sticky header that routes to the appropriate day on the calendar tab.
  • I also updated the widget preview to present better mock todo items.

refs: MBL-19373
builds: Student
affects: Student
release note: none

test plan:

  • Go to developer menu and enable the new_student_todo_screen feature flag.
  • Compare design with figma.
  • Check accessibility.
  • Check if everything is implemented that is in the ticket.

Screenshots

BeforeAfter

Checklist

  • Follow-up e2e test ticket created
  • A11y checked
  • Tested on phone
  • Tested on tablet
  • Tested in dark mode
  • Tested in light mode
  • Approve from product

@vargaat vargaat self-assigned this Oct 1, 2025
@inst-danger
Copy link
Contributor

inst-danger commented Oct 1, 2025

Warnings
⚠️ This pull request will not generate a release note.
⚠️ One or more files are below the minimum test coverage 50%
⚠️ The total test coverage is below the minimum 90%

Affected Apps: Student

Builds: Student

MBL-19373

Coverage New % Master % Delta
Canvas iOS 79.76% 79.8% -0.04%
Core/Core/Common/Extensions/Combine/SubjectExtensions.swift 0% 0% 0%
Core/Core/Features/Planner/Model/API/APICalendarEventSubAssignment.swift 0% 0% 0%
Core/Core/Features/Enrollments/GetEnrollments.swift 48.81% 48.81% 0%
Horizon/Horizon/Sources/Features/LearningObjects/Assignment/AssignmentDetails/View/LTIQuiz/OnAppear.swift 0% 0% 0%
Horizon/Horizon/Sources/Common/View/EmbeddedWebPage/HEmbeddedWebPageContainerViewModel.swift 0% 0% 0%
Horizon/Horizon/Sources/Features/LearningObjects/Assignment/AssignmentDetails/View/LTIQuiz/DidSubmitQuiz.swift 0% 0% 0%
Horizon/Horizon/Sources/Features/Inbox/Attachment/AttachmentViewModel.swift 0% 0% 0%
Horizon/Horizon/Sources/Features/Notebook/Common/Data/UpdateRedwoodNote.swift 0% 0% 0%
Horizon/Horizon/Sources/Features/LearningObjects/Assignment/AssignmentDetails/Data/SubmissionProperties.swift 0% 0% 0%
Horizon/Horizon/Sources/Common/Data/LearningObjectType.swift 0% 0% 0%

Generated by 🚫 dangerJS against 92cc847

@inst-danger
Copy link
Contributor

inst-danger commented Oct 1, 2025

Builds

Commit: Implement code review suggestions. Improve a11y. (92cc847)
Build Number: 560
Built At: Oct 15 12:50 CEST (10/15 04:50 AM MDT)

Student

Copy link
Contributor

@suhaibabsi-inst suhaibabsi-inst left a comment

Choose a reason for hiding this comment

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

Code +1

Comment on lines 72 to 73
// Squeeze height to 0 so day badge goes next to cell.
.frame(height: 0, alignment: .top)
Copy link
Contributor

Choose a reason for hiding this comment

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

That's an interesting approach !

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Kind of a hack that made this type of header implementation quite easy. The drawback is that when scrolling the next section up, it wont push out the previous section (since its height is 0) but just overlap it.

@suhaibabsi-inst
Copy link
Contributor

QA +1 for all UI related features.

Though there is one performance issue while scrolling. See below

ScreenRecording_10-02-2025.11-10-32_1.MP4

Copy link

@Copilot Copilot AI left a comment

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 introduces a redesigned Todo list screen for the Student app, featuring improved visual design and functionality while maintaining the existing screen as a fallback behind a feature flag. The new design includes grouped todo items by day with sticky headers, enhanced visual elements using shared widget components, and improved course name handling logic.

Key changes:

  • Introduced new Todo screen design with sticky day headers and grouped items
  • Enhanced course name logic to display nicknames vs. course codes appropriately
  • Refactored widget components for reuse between widget and main app screens
  • Updated empty state with new vacation panda illustration

Reviewed Changes

Copilot reviewed 33 out of 33 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
TodoItemView.swift Simplified widget view to use shared content component
TodoModel.swift Updated to use new TodoItemViewModel and enhanced preview data
TodoListViewModel.swift Added day header navigation and empty state configuration
TodoItemViewModel.swift New view model with enhanced course name logic and date formatting
TodoGroupViewModel.swift New view model for day-grouped todo items
TodoListScreen.swift Redesigned screen with sticky headers and grouped layout
TodoItemContentView.swift New shared component for consistent todo item display
TodoDayHeaderView.swift New sticky header component for day grouping
Course.swift Added originalName property and nickname detection
APICourse.swift Added original_name field to API model
Various test files Updated tests to work with new view models and data structures

@instructure instructure deleted a comment from Copilot AI Oct 7, 2025
@instructure instructure deleted a comment from Copilot AI Oct 7, 2025
balintbartok
balintbartok previously approved these changes Oct 7, 2025
Copy link
Contributor

@balintbartok balintbartok left a comment

Choose a reason for hiding this comment

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

QA + 1

Copy link
Contributor

@suhaibabsi-inst suhaibabsi-inst left a comment

Choose a reason for hiding this comment

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

QA +1

The scrolling issue now is fixed 👍

ScreenRecording_10-13-2025.11-21-07_1.MP4

# Conflicts:
#	Core/CoreTests/Features/Todos/Model/TodoInteractorLiveTests.swift
Copy link
Contributor

@rh12 rh12 left a comment

Choose a reason for hiding this comment

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

Thanks for this work, nice solution for the day-headers!

Overall it looks good to me, I have some a11y comments, and some minor code-related comments (some of which are about spreading the word about existing helpers in the codebase :) )


override func setUp() {
super.setUp()
Clock.mockNow(Self.mockDate)
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add Clock.reset() in teardown

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I didn't add because it's in the CoreTestCase's setUp method. Maybe also adding it to the tearDown would be appropriate.

.multilineTextAlignment(.leading)
}

private var contextSection: some View {
Copy link
Contributor

Choose a reason for hiding this comment

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

We could InstUI.JoinedSubtitleLabels instead of the HStack.
It has a divider which matches figma.
(I would say center aligning the icon looks better, but that's doable either way)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

Comment on lines 26 to 27
let dateComponents = DateComponents(year: 2021, month: 8, day: 7, hour: 12)
let date = Calendar.current.date(from: dateComponents)!
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: Date.make() (same for the others)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good idea!


let group = TodoGroupViewModel(date: date, items: items)

XCTAssertTrue(group.accessibilityLabel.contains("Saturday"))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: XCTAssertContains (same for the others)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated.

self.htmlURL = plannable.htmlURL

self.color = plannable.color.asColor
self.icon = Image(uiImage: plannable.icon)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
self.icon = Image(uiImage: plannable.icon)
self.icon = plannable.icon.asImage

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

// Check second group (tomorrow)
let secondGroup = todoGroups[1]
XCTAssertEqual(secondGroup.items.count, 1)
XCTAssertEqual(secondGroup.items[0].title, "Quiz 1")
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: using first/last instead of [0]/[1] to ensure the test doesn't crash, just fails (even though CI ignores the crash, it stops execution locally)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated.

// When
interactor.refreshResult = .success(false)
// When - with non-empty todos
interactor.refreshResult = .success(())
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
interactor.refreshResult = .success(())
interactor.refreshResult = .success

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

.foregroundStyle(item.color)
.accessibilityHidden(true)
.frame(maxHeight: .infinity, alignment: .top)
.padding(.top, isCompactLayout ? 0 : 2)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need this extra padding?

Copy link
Collaborator Author

@vargaat vargaat Oct 14, 2025

Choose a reason for hiding this comment

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

The widget and the in-app todo rows use different font heights and for a one liner todo title this offset gave the best vertical centering effect. I modified the logic to scale this with font scale, now the text is centered with the first line of the text most of the time.

}

private var titleSection: some View {
VStack(alignment: .leading) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should have the same spacing as the main vstack here, which is zero at the moment.
(It is usually 2, maybe figma does not have this by mistake)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Right, the huge spacing felt strange but since I copied I guessed that's the correct. I modified the logic to have 0 spacing for the widget (compact layout) and 2 points for the in-app cell.

@vargaat
Copy link
Collaborator Author

vargaat commented Oct 15, 2025

@rh12 I implemented your suggestions, now the screen properly displays even at the largest a11y size 🥳

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.

5 participants