Skip to content

Commit d06cb11

Browse files
authored
[Horizon] Caching of the notebook (#3341)
1 parent c5670de commit d06cb11

26 files changed

+834
-548
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// This file is part of Canvas.
3+
// Copyright (C) 2025-present Instructure, Inc.
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as
7+
// published by the Free Software Foundation, either version 3 of the
8+
// License, or (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
19+
import CoreData
20+
import Foundation
21+
22+
final public class CDNotebookNote: NSManagedObject {
23+
@NSManaged public var after: String? // used for paging in the query used to fetch this object
24+
@NSManaged public var before: String? // used for paging in the query used to fetch this object
25+
@NSManaged public var content: String? // the text of the note
26+
@NSManaged public var courseID: String
27+
@NSManaged public var date: Date // the date the note was created
28+
@NSManaged public var end: NSNumber?
29+
@NSManaged public var endContainer: String?
30+
@NSManaged public var endOffset: NSNumber?
31+
@NSManaged public var id: String
32+
@NSManaged public var labels: String?
33+
@NSManaged public var objectType: String
34+
@NSManaged public var pageID: String
35+
@NSManaged public var selectedText: String?
36+
@NSManaged public var start: NSNumber?
37+
@NSManaged public var startContainer: String?
38+
@NSManaged public var startOffset: NSNumber?
39+
}
40+
41+
extension String? {
42+
public var deserializeLabels: [String]? {
43+
self?.split(separator: ";").map { String($0) }
44+
}
45+
}
46+
47+
extension Array where Element == String {
48+
public var serializeLabels: String? {
49+
self.sorted().joined(separator: ";")
50+
}
51+
}

Core/Core/Resources/Database.xcdatamodeld/Database.xcdatamodel/contents

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23788" systemVersion="24E248" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
2+
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23788" systemVersion="24E263" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
33
<entity name="AccountNotification" representedClassName="Core.AccountNotification" syncable="YES">
44
<attribute name="endAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
55
<attribute name="iconRaw" attributeType="String"/>
@@ -230,6 +230,24 @@
230230
<attribute name="useOutOfOffice" attributeType="Boolean" usesScalarValueType="YES"/>
231231
<attribute name="useSignature" attributeType="Boolean" usesScalarValueType="YES"/>
232232
</entity>
233+
<entity name="CDNotebookNote" representedClassName=".CDNotebookNote" syncable="YES">
234+
<attribute name="after" optional="YES" attributeType="String"/>
235+
<attribute name="before" optional="YES" attributeType="String"/>
236+
<attribute name="content" optional="YES" attributeType="String"/>
237+
<attribute name="courseID" attributeType="String"/>
238+
<attribute name="date" attributeType="Date" usesScalarValueType="NO"/>
239+
<attribute name="end" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
240+
<attribute name="endContainer" optional="YES" attributeType="String"/>
241+
<attribute name="endOffset" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
242+
<attribute name="id" attributeType="String"/>
243+
<attribute name="labels" optional="YES" attributeType="String"/>
244+
<attribute name="objectType" attributeType="String"/>
245+
<attribute name="pageID" attributeType="String"/>
246+
<attribute name="selectedText" optional="YES" attributeType="String"/>
247+
<attribute name="start" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
248+
<attribute name="startContainer" optional="YES" attributeType="String"/>
249+
<attribute name="startOffset" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
250+
</entity>
233251
<entity name="CDRubricCriterion" representedClassName="Core.CDRubricCriterion" syncable="YES">
234252
<attribute name="assignmentID" attributeType="String"/>
235253
<attribute name="criterionUseRange" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>

Horizon/Horizon/Sources/Features/Assist/AssistChat/Domain/AssistChatInteractor.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,3 +528,18 @@ struct AssistChatInteractorPreview: AssistChatInteractor {
528528
func publish(action: AssistChatAction) {}
529529
var listen: AnyPublisher<AssistChatResponse, Error> = Empty().eraseToAnyPublisher()
530530
}
531+
532+
extension API {
533+
func makeRequest<Request: APIRequestable>(_ requestable: Request) -> AnyPublisher<Request.Response?, Error> {
534+
AnyPublisher<Request.Response?, Error> { [weak self] subscriber in
535+
self?.makeRequest(requestable) { response, _, error in
536+
if let error = error {
537+
subscriber.send(completion: .failure(error))
538+
return
539+
}
540+
subscriber.send(response)
541+
}
542+
return AnyCancellable { }
543+
}
544+
}
545+
}

Horizon/Horizon/Sources/Features/Notebook/Common/Data/CourseNotebookNote.swift

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ struct CourseNotebookNote {
2727
var id: String
2828
var date: Date
2929
var courseId: String
30+
var hasNext: Bool
31+
var hasPrevious: Bool
3032
var objectId: String
3133

3234
// MARK: - Optional
3335

3436
var content: String?
3537
var highlightData: NotebookHighlight?
3638
var labels: [CourseNoteLabel]?
37-
var nextCursor: String?
38-
var previousCursor: String?
3939
}
4040

4141
extension CourseNotebookNote {
@@ -45,19 +45,19 @@ extension CourseNotebookNote {
4545
) {
4646
let note = edge.node
4747

48-
self.id = note.id ?? ""
49-
self.date = note.createdAt ?? Date()
48+
self.id = note.id
49+
self.date = note.createdAt
5050
self.courseId = note.courseId
5151
self.objectId = note.objectId
52+
self.hasNext = false
53+
self.hasPrevious = false
5254

5355
self.labels = note.reaction?.compactMap { .init(rawValue: $0) }
5456

5557
self.content = note.userText
5658

57-
self.nextCursor = pageInfo.hasNextPage ? edge.cursor : nil
58-
self.previousCursor = pageInfo.hasPreviousPage ? edge.cursor : nil
59-
6059
self.highlightData = note.highlightData
60+
6161
}
6262

6363
func copy(
@@ -67,34 +67,36 @@ extension CourseNotebookNote {
6767
content: String? = nil,
6868
highlightData: NotebookHighlight? = nil,
6969
labels: [CourseNoteLabel]? = nil,
70-
nextCursor: String? = nil,
71-
previousCursor: String? = nil
70+
hasPrevious: Bool = false,
71+
hasNext: Bool = false
7272
) -> CourseNotebookNote {
7373
CourseNotebookNote(
7474
id: self.id,
7575
date: date ?? self.date,
7676
courseId: courseId ?? self.courseId,
77+
hasNext: hasNext,
78+
hasPrevious: hasPrevious,
7779
objectId: objectId ?? self.objectId,
7880
content: content ?? self.content,
7981
highlightData: highlightData ?? self.highlightData,
80-
labels: labels ?? self.labels,
81-
nextCursor: nextCursor ?? self.nextCursor,
82-
previousCursor: previousCursor ?? self.previousCursor
82+
labels: labels ?? self.labels
8383
)
8484
}
8585
}
8686

8787
extension CourseNotebookNote {
8888
init(from note: RedwoodNote) {
89-
self.id = note.id ?? ""
90-
self.date = note.createdAt ?? Date()
89+
self.id = note.id
90+
self.date = note.createdAt
9191
self.courseId = note.courseId
9292
self.objectId = note.objectId
9393

9494
self.content = note.userText
9595
self.labels = note.reaction?.compactMap { CourseNoteLabel(rawValue: $0) } ?? []
9696

9797
self.highlightData = note.highlightData
98+
self.hasNext = false
99+
self.hasPrevious = false
98100
}
99101
}
100102

@@ -105,6 +107,8 @@ extension CourseNotebookNote {
105107
id: "1",
106108
date: Date(),
107109
courseId: "courseID",
110+
hasNext: false,
111+
hasPrevious: false,
108112
objectId: "objectID",
109113
content: "Good morning",
110114
highlightData: NotebookHighlight(

Horizon/Horizon/Sources/Features/Notebook/Common/Data/GraphQL/CreateNoteMutation.swift

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import Core
2020

2121
class RedwoodCreateNoteMutation: APIGraphQLRequestable {
2222
let variables: NewCourseNoteInput
23-
private let jwt: String
2423

2524
var path: String {
2625
"/graphql"
@@ -29,17 +28,12 @@ class RedwoodCreateNoteMutation: APIGraphQLRequestable {
2928
var headers: [String: String?] {
3029
[
3130
"x-apollo-operation-name": "CreateNote",
32-
HttpHeader.accept: "application/json",
33-
HttpHeader.authorization: "Bearer \(jwt)"
31+
HttpHeader.accept: "application/json"
3432
]
3533
}
3634

37-
public init(
38-
jwt: String,
39-
note: NewRedwoodNote
40-
) {
35+
public init(note: NewRedwoodNote) {
4136
self.variables = NewCourseNoteInput(input: note)
42-
self.jwt = jwt
4337
}
4438

4539
public static let operationName: String = "CreateNote"

Horizon/Horizon/Sources/Features/Notebook/Common/Data/GraphQL/DeleteNoteMutation.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import Core
2020

2121
class RedwoodDeleteNoteMutation: APIGraphQLRequestable {
2222
let variables: Input
23-
private let jwt: String
2423

2524
var path: String {
2625
"/graphql"
@@ -34,11 +33,9 @@ class RedwoodDeleteNoteMutation: APIGraphQLRequestable {
3433
}
3534

3635
public init(
37-
jwt: String,
3836
id: String
3937
) {
4038
self.variables = Input(id: id)
41-
self.jwt = jwt
4239
}
4340

4441
public static let operationName: String = "DeleteNote"

Horizon/Horizon/Sources/Features/Notebook/Common/Data/GraphQL/GetNotesQuery.swift

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,29 +23,23 @@ import Combine
2323
class GetNotesQuery: APIGraphQLRequestable {
2424
public let variables: GetNotesQueryInput
2525

26-
private let jwt: String
27-
2826
var path: String {
2927
"/graphql"
3028
}
3129

3230
var headers: [String: String?] {
3331
[
3432
"x-apollo-operation-name": "FetchNotes",
35-
HttpHeader.accept: "application/json",
36-
HttpHeader.authorization: "Bearer \(jwt)"
33+
HttpHeader.accept: "application/json"
3734
]
3835
}
3936

4037
public init(
41-
jwt: String,
4238
after: String,
4339
reactions: [String]? = nil,
4440
courseId: String? = nil,
4541
objectId: String? = nil
4642
) {
47-
self.jwt = jwt
48-
4943
let filter = GetNotesQuery.NoteFilterInput.build(
5044
courseId: courseId,
5145
objectId: objectId,
@@ -59,14 +53,11 @@ class GetNotesQuery: APIGraphQLRequestable {
5953
}
6054

6155
public init(
62-
jwt: String,
6356
before: String,
6457
reactions: [String]? = nil,
6558
courseId: String? = nil,
6659
objectId: String? = nil
6760
) {
68-
self.jwt = jwt
69-
7061
self.variables = GetNotesQueryInput(
7162
before: before,
7263
filter: GetNotesQuery.NoteFilterInput.build(
@@ -78,12 +69,10 @@ class GetNotesQuery: APIGraphQLRequestable {
7869
}
7970

8071
public init(
81-
jwt: String,
8272
reactions: [String]? = nil,
8373
courseId: String? = nil,
8474
objectId: String? = nil
8575
) {
86-
self.jwt = jwt
8776
self.variables = GetNotesQueryInput(
8877
filter: GetNotesQuery.NoteFilterInput.build(
8978
courseId: courseId,
@@ -123,7 +112,7 @@ class GetNotesQuery: APIGraphQLRequestable {
123112
typealias Response = RedwoodFetchNotesQueryResponse
124113

125114
struct GetNotesQueryInput: Codable, Equatable {
126-
private static let pageSize: Float = 10
115+
private static let pageSize: Float = 1000
127116

128117
let after: String?
129118
let before: String?
@@ -215,7 +204,6 @@ class GetNotesQuery: APIGraphQLRequestable {
215204
// MARK: - Codeables
216205

217206
public struct RedwoodFetchNotesQueryResponse: Codable {
218-
219207
let data: ResponseData
220208

221209
public struct ResponseData: Codable {
@@ -237,14 +225,3 @@ public struct RedwoodFetchNotesQueryResponse: Codable {
237225
let hasPreviousPage: Bool
238226
}
239227
}
240-
241-
extension RedwoodFetchNotesQueryResponse {
242-
var courseNotebookNotes: [CourseNotebookNote] {
243-
data.notes.edges.compactMap { edge in
244-
.init(
245-
from: edge,
246-
pageInfo: data.notes.pageInfo
247-
)
248-
}
249-
}
250-
}

0 commit comments

Comments
 (0)