Skip to content
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: Dates Tab integration with Calendar #347

Merged
merged 9 commits into from
Mar 26, 2024

Conversation

shafqat-muneer
Copy link
Contributor

@shafqat-muneer shafqat-muneer commented Mar 17, 2024

Jira Tickets:

  • LEARNER-9665: iOS - Dates Tab integration with Calendar
  • LEARNER-9855: iOS - Calendar Integration deep links implementation in local calendar events

Github Issues:

Light Mode Dark Mode
Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 36 45 Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 37 36
Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 36 51 Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 37 47
Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 36 55 Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 37 52
Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 37 11 Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 38 09
Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 37 18 Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 38 15
Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 37 21 Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 38 19
Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 39 38 Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 40 13
Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 41 29 Simulator Screenshot - iPhone 15 - 2024-03-15 at 12 40 30
Simulator Screenshot - iPhone 15 - 2024-03-18 at 01 06 21 Simulator Screenshot - iPhone 15 - 2024-03-18 at 01 06 54
Simulator Screenshot - iPhone 15 - 2024-03-18 at 01 06 25 Simulator Screenshot - iPhone 15 - 2024-03-18 at 01 06 58
Simulator Screenshot - iPhone 15 - 2024-03-18 at 01 06 31 Simulator Screenshot - iPhone 15 - 2024-03-18 at 01 07 32

Demos

Light Mode: Sync and un-sync events in calendar application:

Simulator.Screen.Recording.-.iPhone.15.-.2024-03-15.at.12.42.44.mp4

Light Mode: Update out dated calendar:

Simulator.Screen.Recording.-.iPhone.15.-.2024-03-15.at.12.49.21.mp4

Light Mode: Deep Linking:

RPReplay_Final1710750802.mp4

Dark Mode: Sync and un-sync events in calendar application:

Simulator.Screen.Recording.-.iPhone.15.-.2024-03-15.at.12.43.27.mp4

Dark Mode: Update out dated calendar:

Simulator.Screen.Recording.-.iPhone.15.-.2024-03-15.at.12.50.38.mp4

@volodymyr-chekyrta
Copy link
Contributor

Hey @shafqat-muneer, thanks, looks great! 🚀
Do you know why the deep link from the calendar app didn't take the user to the specific course page?
I thought it would work after @eyatsenkoperpetio introduced #329
Do we need to develop anything else to make it work like that?

RPReplay_Final1710750802.mp4

Comment on lines 34 to 37
<key>NSCalendarsUsageDescription</key>
<string>edX would like to use your calendar list to subscribe to your personalized edX calendar for this course.</string>
<key>NSCalendarsFullAccessUsageDescription</key>
<string>edX would like to use your calendar list to subscribe to your personalized edX calendar for this course.</string>
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please not hardcode the name of the edX app?
Ideally, we should use platformName or something similar from the config.yaml.

@shafqat-muneer
Copy link
Contributor Author

Hey @shafqat-muneer, thanks, looks great! 🚀 Do you know why the deep link from the calendar app didn't take the user to the specific course page? I thought it would work after @eyatsenkoperpetio introduced #329 Do we need to develop anything else to make it work like that?

RPReplay_Final1710750802.mp4

Hi @volodymyr-chekyrta, thanks for the review.
I've just finished implementing the generation of deep link URLs for the calendar in this pull request. That's why it hasn't been included in #329. I'll proceed to open another pull request to direct the user to the specific course page.

@volodymyr-chekyrta
Copy link
Contributor

Hey @shafqat-muneer, thanks, looks great! 🚀 Do you know why the deep link from the calendar app didn't take the user to the specific course page? I thought it would work after @eyatsenkoperpetio introduced #329 Do we need to develop anything else to make it work like that?
RPReplay_Final1710750802.mp4

Hi @volodymyr-chekyrta, thanks for the review. I've just finished implementing the generation of deep link URLs for the calendar in this pull request. That's why it hasn't been included in #329. I'll proceed to open another pull request to direct the user to the specific course page.

Makes sense to me, thank you! 👍

rnr
rnr previously approved these changes Mar 19, 2024
Copy link
Contributor

@rnr rnr left a comment

Choose a reason for hiding this comment

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

LGTM, some small changes

import Core
import BranchSDK

enum DeepLinkType: String {
Copy link
Contributor

Choose a reason for hiding this comment

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

@shafqat-muneer maybe we could re-use DeepLinkType and DeepLinkKeys defined in DeepLink class to avoid duplication?
Thank you

courseID: courseID,
title: calendarName,
isOn: true,
modalPresented: false)
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you move the bracket to the next line to respect the "parameter on its own line" style?

@@ -41,7 +41,53 @@ public struct CourseDatesView: View {
}

if viewModel.dueDatesShifted {
DatesShiftedSuccessView(selectedTab: .dates, courseDatesViewModel: viewModel)
DatesSuccessView(title: CourseLocalization.CourseDates.toastSuccessTitle,
Copy link
Contributor

Choose a reason for hiding this comment

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

"parameter on its own line" style please

CoreAssets.syncToCalendar.swiftUIImage
Toggle(
CourseLocalization.CourseDates.syncToCalendar,
isOn: $viewModel.isOn)
Copy link
Contributor

Choose a reason for hiding this comment

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

"parameter on its own line" style please

@@ -153,7 +153,10 @@ public struct CourseOutlineView: View {
.accessibilityAction {}

if viewModel.dueDatesShifted && !isVideo {
DatesShiftedSuccessView(selectedTab: .course, courseContainerViewModel: viewModel) {
DatesSuccessView(title: CourseLocalization.CourseDates.toastSuccessTitle,
Copy link
Contributor

Choose a reason for hiding this comment

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

"parameter on its own line" style please

Copy link
Contributor

@saeedbashir saeedbashir left a comment

Choose a reason for hiding this comment

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

  1. On adding the second calendar the syncing calendar keeps on screen and never goes away.
  2. On selecting cancel remove on remove calendar alert, the switch should turns back on.

Comment on lines 53 to 63
DatesSuccessView(
title: CourseLocalization.CourseDates.calendarEvents,
message: CourseLocalization.CourseDates.calendarEventsAdded,
selectedTab: .dates
) {
Task {
await MainActor.run {
viewModel.calendarEventsAdded = false
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This code is being shared between the following three conditions calendarEventsAdded, calendarEventsRemoved, and calendarEventsUpdated. How about moving it to a function and use that. Even that func can be used for dueDatesShifted with an additional param.

Comment on lines 19 to 21
@Published var calendarEventsAdded: Bool = false
@Published var calendarEventsRemoved: Bool = false
@Published var calendarEventsUpdated: Bool = false
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of using a boolean for each condition, how about using an enum for these cases?

},
okTapped: {
self.router.dismiss(animated: true)
self.removeCourseCalendar { [weak self] success in
Copy link
Contributor

Choose a reason for hiding this comment

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

success is unused here, so it can be replaced with _.

positiveAction: CourseLocalization.CourseDates.calendarShiftPromptUpdateNow,
onCloseTapped: {
// Remove course calendar
self.router.dismiss(animated: true)
Copy link
Contributor

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 in closures? if so please update it for all alert functions.

get {
if let calendarEntry = calendarEntry,
let localCalendar = localCalendar {
if calendarEntry.identifier == localCalendar.calendarIdentifier {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to move this if condition to where clause of if let?

@shafqat-muneer
Copy link
Contributor Author

@saeedbashir I have made the recommended adjustments. Kindly review them.

Comment on lines 102 to 103
} else {
if let localCalendar = localCalendar {
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess else and il let can be merged like else if let

),
positiveAction: CoreLocalization.Alert.accept,
onCloseTapped: { [weak self] in
guard let self else { return }
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems strong reference if self isn't required in this type of blocks because if the self will be nill, by using the optional self will not crash the app and following code will automatically be ignored.

@shafqat-muneer
Copy link
Contributor Author

@saeedbashir I have addressed requested changes.

@volodymyr-chekyrta Please also review this pull request. Thank you.

saeedbashir
saeedbashir previously approved these changes Mar 25, 2024
Copy link
Contributor

@saeedbashir saeedbashir left a comment

Choose a reason for hiding this comment

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

LGTM 👍

rnr
rnr previously approved these changes Mar 25, 2024
Copy link
Contributor

@volodymyr-chekyrta volodymyr-chekyrta left a comment

Choose a reason for hiding this comment

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

LGTM 👍
Just one question

Comment on lines 102 to 112
return DatesSuccessView(
title: title,
message: message,
selectedTab: .dates
) {
Task {
await MainActor.run {
viewModel.eventState = CourseDatesViewModel.EventState.none
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can it be simplified just to something like this?

return DatesSuccessView(
                title: title,
                message: message,
                selectedTab: .dates
            ) {
                viewModel.eventState = CourseDatesViewModel.EventState.none
            }

Copy link
Contributor

Choose a reason for hiding this comment

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

This seems a bit odd. We switch to a Task only to switch back to the MainActor.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@volodymyr-chekyrta Thank you for bringing that to my attention. I've streamlined the logic for simplification.

Explanation:
The DatesSuccessView struct was previously invoking action closures asynchronously on a background thread. Upon receiving the closure action, we had to switch back to the main thread to avoid warnings such as:

Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.

I've refactored this logic so that the action closures in DatesSuccessView are no longer asynchronous.

@shafqat-muneer shafqat-muneer merged commit d02b303 into openedx:develop Mar 26, 2024
3 checks passed
@shafqat-muneer shafqat-muneer deleted the Shafqat/LEARNER-9665 branch March 26, 2024 12:39
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.

4 participants