-
Notifications
You must be signed in to change notification settings - Fork 121
[MBL-19532][S] Learner Dashboard - Course Invitations #3873
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
Merged
Merged
Changes from 47 commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
70bd109
Make some properties public on SessionDefaults to allow external exte…
vargaat 37a43e7
Add skeleton architecture.
vargaat bce4cae
Style updates.
vargaat 901b44e
Add state to widgets. Add example transitions. Fix animations and pad…
vargaat 281a8b7
Animation fixes. Renaming.
vargaat 22442de
Layout updates
vargaat 8e0097e
Add tests.
vargaat 325c7f2
Merge branch 'master' into feature/MBL-19528-dashboard-widget-foundat…
vargaat f7da8c0
Add empty protocol file to code coverage exclusion list.
vargaat fb666ce
Merge branch 'master' into feature/MBL-19528-dashboard-widget-foundat…
vargaat e9ce333
Add carousel widget and course invitations skeleton.
vargaat 563a72a
Merge branch 'master' into feature/MBL-19532-Course-Invitations
vargaat 0031e87
Add thread safe course fetch interactor.
vargaat e82ebe2
Update profile button color to use textDarkest
github-actions[bot] 1f9ebd2
Merge branch 'master' into feature/MBL-19528-dashboard-widget-foundat…
vargaat f2941ce
Rename files.
vargaat 1737e5c
Implement code review suggestions.
vargaat 34dbb60
- Remove example horizontal widget.
vargaat 2c9a266
Merge branch 'feature/MBL-19528-dashboard-widget-foundations' into fe…
vargaat cebd361
Add previews and animations.
vargaat 703d4ec
Improve error and success feedback on ui.
vargaat cf63c3c
- Fix menu button color on iOS 26.
vargaat 89630b7
Fix widget title not getting resized on font size change.
vargaat c15fc51
Fix swiftlint.
vargaat ffcc50a
Merge branch 'feature/MBL-19528-dashboard-widget-foundations' into fe…
vargaat bf318cb
Use shared animation.
vargaat d3651da
Merge branch 'master' into feature/MBL-19532-Course-Invitations
vargaat 9910d83
Use pill buttons.
vargaat f7ec2e7
Remove offline warnings from view model.
vargaat b32afc9
Improve accessibility.
vargaat df94706
Merge branch 'master' into feature/MBL-19532-Course-Invitations
vargaat 9578e46
Optimize example widget refresh animation.
vargaat f925b17
Update interactor to support ignore cache.
vargaat a39dc9e
Add enrollment fetch to courses. Fix deadlock in interactor.
vargaat 23355ce
Sort course invitations by invitation date.
vargaat 69e9b7b
Move CoreData fetch to a background queue.
vargaat 9f7966c
Add more unit tests.
vargaat b873b6b
Fix swiftui deprecations.
vargaat fd4bed9
Add page indicator.
vargaat 04e5519
Move helper to a better place.
vargaat fa3dd52
Improve preview.
vargaat d4e5800
Work around animation glitches.
vargaat 25f566b
Properly update database after invitation status changes.
vargaat 32c0aa3
Fix retain cycle.
vargaat ebea358
Update unit test.
vargaat 0aa5a52
Merge branch 'master' into feature/MBL-19532-Course-Invitations
vargaat 727064b
Improve empty section name handling.
vargaat 38d01a0
Remove auto widget refresh.
vargaat 7c43add
Merge branch 'master' into feature/MBL-19532-Course-Invitations
vargaat 6332477
Implement review findings. Revert accept and decline button order.
vargaat 09bb788
Remove extra new line.
vargaat File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
130 changes: 130 additions & 0 deletions
130
Core/Core/Features/Enrollments/AcceptCourseInvitation.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| // | ||
| // This file is part of Canvas. | ||
| // Copyright (C) 2026-present Instructure, Inc. | ||
| // | ||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU Affero General Public License as | ||
| // published by the Free Software Foundation, either version 3 of the | ||
| // License, or (at your option) any later version. | ||
| // | ||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU Affero General Public License for more details. | ||
| // | ||
| // You should have received a copy of the GNU Affero General Public License | ||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
| // | ||
|
|
||
| import Combine | ||
| import CoreData | ||
| import Foundation | ||
|
|
||
| public struct InvitationAcceptResponse: Codable { | ||
| let success: Bool | ||
| let course: APICourse? | ||
| let enrollments: [APIEnrollment]? | ||
| } | ||
|
|
||
| public class AcceptCourseInvitation: UseCase { | ||
| public typealias Model = Enrollment | ||
| public typealias Response = InvitationAcceptResponse | ||
|
|
||
| public var scope: Scope { | ||
| .where(#keyPath(Enrollment.id), equals: enrollmentID) | ||
| } | ||
| public let cacheKey: String? = nil | ||
| public let ttl: TimeInterval = 0 | ||
|
|
||
| private let courseID: String | ||
| private let enrollmentID: String | ||
| private var subscriptions = Set<AnyCancellable>() | ||
|
|
||
| public init(courseID: String, enrollmentID: String) { | ||
| self.courseID = courseID | ||
| self.enrollmentID = enrollmentID | ||
| } | ||
|
|
||
| public func makeRequest(environment: AppEnvironment, completionHandler: @escaping RequestCallback) { | ||
| let handleRequest = HandleCourseInvitationRequest( | ||
| courseID: courseID, | ||
| enrollmentID: enrollmentID, | ||
| isAccepted: true | ||
| ) | ||
| let courseID = courseID | ||
|
|
||
| environment.api.makeRequest(handleRequest) | ||
| .flatMap { handleResponse, handleURLResponse -> AnyPublisher<(InvitationAcceptResponse, URLResponse?), Error> in | ||
| guard handleResponse.success else { | ||
| return Fail(error: NSError.internalError()) | ||
| .eraseToAnyPublisher() | ||
| } | ||
|
|
||
| let getCoursePublisher = environment.api.makeRequest(GetCourseRequest(courseID: courseID)) | ||
| .map { $0.body } | ||
| .catch { _ -> Just<APICourse?> in | ||
| return Just(nil) | ||
| } | ||
| .setFailureType(to: Error.self) | ||
|
|
||
| let getEnrollmentsPublisher = environment.api.makeRequest(GetEnrollmentsRequest(context: .course(courseID))) | ||
| .map { $0.body } | ||
| .catch { _ -> Just<[APIEnrollment]?> in | ||
| return Just(nil) | ||
| } | ||
| .setFailureType(to: Error.self) | ||
vargaat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return Publishers.Zip(getCoursePublisher, getEnrollmentsPublisher) | ||
| .map { courseResponse, enrollmentsResponse in | ||
| let response = InvitationAcceptResponse( | ||
| success: true, | ||
| course: courseResponse, | ||
| enrollments: enrollmentsResponse | ||
| ) | ||
| return (response, handleURLResponse) | ||
| } | ||
| .eraseToAnyPublisher() | ||
| } | ||
| .sink( | ||
| receiveCompletion: { completion in | ||
| if case .failure(let error) = completion { | ||
| completionHandler(nil, nil, error) | ||
| } | ||
| }, | ||
| receiveValue: { response, urlResponse in | ||
| completionHandler(response, urlResponse, nil) | ||
| } | ||
| ) | ||
| .store(in: &subscriptions) | ||
| } | ||
|
|
||
| public func write(response: InvitationAcceptResponse?, urlResponse: URLResponse?, to context: NSManagedObjectContext) { | ||
| guard let response else { return } | ||
|
|
||
vargaat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if let apiCourse = response.course { | ||
| Course.save(apiCourse, in: context) | ||
| } | ||
|
|
||
| guard let apiEnrollments = response.enrollments else { | ||
| return | ||
| } | ||
|
|
||
| for apiEnrollment in apiEnrollments { | ||
| guard let enrollmentId = apiEnrollment.id?.value else { continue } | ||
| let enrollment: Enrollment = context.first( | ||
| where: #keyPath(Enrollment.id), | ||
| equals: enrollmentId | ||
| ) ?? context.insert() | ||
|
|
||
| enrollment.update( | ||
| fromApiModel: apiEnrollment, | ||
| course: nil, | ||
| in: context | ||
| ) | ||
|
|
||
| if enrollmentId == self.enrollmentID { | ||
| enrollment.isFromInvitation = false | ||
| } | ||
| } | ||
| } | ||
| } | ||
60 changes: 60 additions & 0 deletions
60
Core/Core/Features/Enrollments/DeclineCourseInvitation.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| // | ||
| // This file is part of Canvas. | ||
| // Copyright (C) 2026-present Instructure, Inc. | ||
| // | ||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU Affero General Public License as | ||
| // published by the Free Software Foundation, either version 3 of the | ||
| // License, or (at your option) any later version. | ||
| // | ||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU Affero General Public License for more details. | ||
| // | ||
| // You should have received a copy of the GNU Affero General Public License | ||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
| // | ||
|
|
||
| import CoreData | ||
| import Foundation | ||
|
|
||
| public class DeclineCourseInvitation: UseCase { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting way of using UseCase!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just realized I could have used |
||
| public typealias Model = Enrollment | ||
| public typealias Response = HandleCourseInvitationRequest.Response | ||
|
|
||
| public var scope: Scope { | ||
| .where(#keyPath(Enrollment.id), equals: enrollmentID) | ||
| } | ||
| public let cacheKey: String? = nil | ||
| public let ttl: TimeInterval = 0 | ||
|
|
||
| private let courseID: String | ||
| private let enrollmentID: String | ||
|
|
||
| public init(courseID: String, enrollmentID: String) { | ||
| self.courseID = courseID | ||
| self.enrollmentID = enrollmentID | ||
| } | ||
|
|
||
| public func makeRequest(environment: AppEnvironment, completionHandler: @escaping RequestCallback) { | ||
| let request = HandleCourseInvitationRequest( | ||
| courseID: courseID, | ||
| enrollmentID: enrollmentID, | ||
| isAccepted: false | ||
| ) | ||
|
|
||
| environment.api.makeRequest(request, callback: completionHandler) | ||
| } | ||
|
|
||
| public func write(response: Response?, urlResponse: URLResponse?, to context: NSManagedObjectContext) { | ||
| guard response?.success == true else { return } | ||
|
|
||
| if let enrollment: Enrollment = context.first( | ||
| where: #keyPath(Enrollment.id), | ||
| equals: enrollmentID | ||
| ) { | ||
| context.delete([enrollment]) | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.