-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Ability to shift courses dates if deadlines have been missed #288
feat: Ability to shift courses dates if deadlines have been missed #288
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Getting response validation failed error for course
course-v1:USMx+PGPM661.3x+2T2022
while getting course dates status. - It would be nice if dates status info view appear with animation like resume course.
- Please remove all the temp files pushed like
[.mocky3C3F2BD4-1ED2-461C-96E3-33CAE5082F47/.config.yml.tmp](https://github.com/openedx/openedx-app-ios/pull/288/files#diff-ae688431c3fdb1778a03a28b5d0af9281947494db8d4c254fe74bcce1cf97637)
import Combine | ||
import Theme | ||
|
||
public struct SuccessViewWithButton: View { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about renaming it to DatesShiftedSuccessView
Thoughts?
"COURSE_DATES.TOAST_SUCCESS_MESSAGE" = "Your due dates have been successfully shifted to help you stay on track."; | ||
"COURSE_DATES.VIEW_ALL_DATES" = "View all dates"; | ||
|
||
"COURSE_DATES.RESET_DATE.RESET_DATE_BANNER.BODY" = "Don't worry - shift our suggested schedule to complete past due assignments without losing any progress."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if they can move these to course module as they are related to course.
@@ -81,6 +84,7 @@ public class CourseContainerViewModel: BaseCourseViewModel { | |||
super.init(manager: manager) | |||
|
|||
addObservers() | |||
addNotificationObserver() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to define new method, new observers can be added to addObservers
.
@@ -47,6 +49,7 @@ public class CourseContainerViewModel: BaseCourseViewModel { | |||
private let authInteractor: AuthInteractorProtocol | |||
private let analytics: CourseAnalytics | |||
private(set) var storage: CourseStorage | |||
private var shiftCourseDatesObserver: NSObjectProtocol? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this observer declaration isn't needed and work can be done by NotificationCenter.default.addObserver(.....
let center = NotificationCenter.default | ||
guard let shiftCourseDatesObserver = self.shiftCourseDatesObserver else { return } | ||
center.removeObserver(shiftCourseDatesObserver) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to remove a specific observer, you can simply call NotificationCenter.default.removeObserver(self)
.
guard let strongSelf = self else { | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it can be updated to guard let self
.
guard let strongSelf = self else { | ||
return | ||
} | ||
Task { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we can define addNotificationObserver
as MainActor so we don't have to define tasks here and even we can omit DispatchQueue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we designate it with @MainActor
and eliminate the Task
closure, we would have to utilize an async
function here. However, this change leads to certain exceptions. To resolve this issue, we may consider employing addObserver(forName:object:queue:using:)
. Currently, this method is not being utilized for notifications in the application.
Task { | ||
await strongSelf.getCourseBlocks(courseID: courseID, withProgress: true) | ||
await strongSelf.getCourseDeadlineInfo(courseID: courseID, withProgress: true) | ||
DispatchQueue.main.async { | ||
strongSelf.dueDatesShifted = true | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about defining addNotificationObserver
as MainActor
so we don't have to define Task here and even can omit DispatchQueue
|
||
@State private var openCertificateView: Bool = false | ||
private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom } | ||
|
||
@State private var showingDownloads: Bool = false | ||
@State private var showingVideoDownloadQuality: Bool = false | ||
@State private var showingNoWifiMessage: Bool = false | ||
@Binding private var selection: Int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about renaming it to some more meaning name like selectedTabIndex or something similar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ScrollSlidingTabBar
also employs an identical naming convention and logic for consistency purposes.
Podfile.lock
Outdated
@@ -182,4 +182,4 @@ SPEC CHECKSUMS: | |||
|
|||
PODFILE CHECKSUM: 544edab2f9ecc4ac18973fb8865f1d0613ec8a28 | |||
|
|||
COCOAPODS: 1.15.0 | |||
COCOAPODS: 1.14.3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to push files generated with a lower version.
Done with 2nd and 3rd point. |
I haven't finished the review yet, but most of the code looks good. I have a slight concern about this view. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -224,9 +237,6 @@ public class CourseRepository: CourseRepositoryProtocol { | |||
} | |||
} | |||
|
|||
// Mark - For testing and SwiftUI preview | |||
// swiftlint:disable all | |||
#if DEBUG |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shafqat-muneer I don't think we need to remove this condition for Mock purposes
} | ||
} | ||
|
||
struct SuccessViewWithButton_Previews: PreviewProvider { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add #if DEBUG
} | ||
|
||
#if DEBUG | ||
struct AdjustScheduleView_Previews: PreviewProvider { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if you can rename the preview class as per the class name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for highlighting it; It was skipped during struct rename.
} | ||
|
||
#if DEBUG | ||
struct SuccessViewWithButton_Previews: PreviewProvider { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if you could rename the preview as per the class name.
"COURSE_DATES.TOMORROW" = "Tomorrow"; | ||
"COURSE_DATES.YESTERDAY" = "Yesterday"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to duplicate these keys, we can use them from the Core.
@volodymyr-chekyrta Regarding the above message, we are trying to display a message in the form of a snack bar similar to Adroid's native snack bar. Currently, it's replicating what the live application shows and I don't think such a component exists in the schema design system. Let me reach out to marco and sam on slack to finalize the approach for this. |
@moiz994 We have this type of view in the profile and a few other screens. |
Thanks for sharing, @volodymyr-chekyrta! I can't find these components in the figma design file but I think the team is updating the snack bar to match this styling. |
@sdaitzman I have implemented the Regarding second point of inline upgrade button, it's intentional because:
|
Thanks, @shafqat-muneer. Makes sense about mobile payments. Unfortunately, using light text on the info background actually has a worse contrast ratio. I've created an early rough draft of this bottom dialog in Figma this morning, screenshots attached below. I want to run this by the mobile design weekly group tomorrow morning, then will follow up with any design updates to get this unblocked quickly. |
@shafqat-muneer, at the Mobile Design Weekly meeting, we discussed and agreed on a design for the "Due Dates Shifted" view/snackbar. |
@shafqat-muneer here is the proposed design, attached and viewable in Figma. When the user shifts due dates from within the Course tab, the dialog should include the button to View All Dates. When the user shifts due dates from within the CTA inside the Dates tab, the dialog should not include a View All Dates button. Also want to make sure to note the wider dialog, which helps to distinguish this snack bar view from the content underneath. With View All Dates, for within the Course tab Without View All Dates, for within the Dates tab
|
@sdaitzman Thank you for the design review and for providing all necessary details. 👏 I will address the change and share details. |
@sdaitzman @volodymyr-chekyrta snackbar design has been updated.
|
Thanks @shafqat-muneer, this looks great! |
func resetDueDatesShiftedFlag() { | ||
dueDatesShifted = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need a method just to change one variable? Can we directly change it's value as the variable isn't private.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This variable cannot be set as private since it is a @Published
property and is being displayed in various views. The reason for creating a method to set its default value is to ensure that the view model is responsible for updating the property value, maintaining a single source of truth.
@MainActor | ||
func shiftDueDates(courseID: String, withProgress: Bool = true) async { | ||
isShowProgress = withProgress | ||
dueDatesShifted = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need to set it to false, as it will be false by default and even after dates shift?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I utilized it specifically for generating mock data. The initializer won't be invoked again if we remain on the same screen and trigger the shift due dates request within a time span of 5
seconds. The snackbar will not be hidden before the next API call. Indeed, you're correct, in a real-world scenario, this situation is unlikely to occur. Therefore, I am just removing it.
await self.getCourseDeadlineInfo(courseID: courseID, withProgress: true) | ||
} | ||
DispatchQueue.main.async { | ||
self.dueDatesShifted = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about using weak self here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would even ask you not to use Concurrency and GCD frameworks at the same time.
Please use MainActor.run or similar approach to switch to the main thread.
@objc private func handleShiftDueDates(_ notification: Notification) { | ||
if let courseID = notification.object as? String { | ||
Task { | ||
await self.getCourseDates(courseID: courseID) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self
is redundant.
guard let self else { return } | ||
self.dueDatesShifted = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to check wether the self
exists or not, can't self?.dueDatesShifted = true
be ignored if the self will be nil?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, please do not combine Swift Concurrency and GCD frameworks.
Use the Main Actor instead.
do { | ||
let courseDates = try | ||
await getCourseDates(courseID: courseID) | ||
return CourseDateBanner( | ||
datesBannerInfo: courseDates.datesBannerInfo, | ||
hasEnded: courseDates.hasEnded | ||
) | ||
} catch { | ||
throw error | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to catch an error just to throw it further?
Seems like we can simplify by removing the do-catch
block.
} | ||
|
||
// Cases are defied according to this link | ||
// https://2u-internal.atlassian.net/browse/LEARNER-7724?focusedCommentId=479226 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This private link does not make sense for the Open edX repo and community.
Better to describe the entire flow here or move it to the documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cases are self explanatory and I believe we can remove this reference link.
@@ -37,6 +39,10 @@ enum CourseEndpoint: EndPointType { | |||
return url | |||
case .getCourseDates(let courseID): | |||
return "/api/course_home/v1/dates/\(courseID)" | |||
case .getCourseDeadlineInfo(courseID: let courseID): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
case .getCourseDeadlineInfo(courseID: let courseID):
can be simplified:
case .getCourseDeadlineInfo(let courseID):
|
||
NotificationCenter.default.addObserver( | ||
self, | ||
selector: #selector(handleShiftDueDates(_:)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm not mistaken, just #selector(handleShiftDueDates)
is enough.
private func addObservers() { | ||
NotificationCenter.default.addObserver( | ||
self, | ||
selector: #selector(handleShiftDueDates(_:)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#selector(handleShiftDueDates)
👍 🚀 🔥 |
@volodymyr-chekyrta @saeedbashir Feedback addressed 🎉 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
Jira Ticket: LEARNER-9781: iOS - Ability to shift courses dates if deadlines have been missed
Github Issues: #161