Skip to content

Commit 4050960

Browse files
authored
Merge pull request #6152 from woocommerce/issue/6126-integrate-storage-inbox-notes-in-yosemite-layer
Integrated the storage layer of inbox Notes in the Yosemite layer
2 parents 3922d2c + fc897ad commit 4050960

File tree

9 files changed

+555
-8
lines changed

9 files changed

+555
-8
lines changed

Storage/Storage/Tools/StorageType+Deletions.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,23 @@ public extension StorageType {
159159
deleteObject($0)
160160
}
161161
}
162+
163+
// MARK: - InboxNotes
164+
165+
/// Deletes all of the stored Inbox Notes for the provided siteID.
166+
///
167+
func deleteInboxNotes(siteID: Int64) {
168+
let inboxNotes = loadAllInboxNotes(siteID: siteID)
169+
for inboxNote in inboxNotes {
170+
deleteObject(inboxNote)
171+
}
172+
}
173+
174+
/// Deletes the stored InboxNote with the given id for the provided siteID.
175+
///
176+
func deleteInboxNote(siteID: Int64, id: Int64) {
177+
if let inboxNote = loadInboxNote(siteID: siteID, id: id) {
178+
deleteObject(inboxNote)
179+
}
180+
}
162181
}

Yosemite/Yosemite.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@
171171
45010695239A6CDE00E24722 /* TaxClassAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45010694239A6CDE00E24722 /* TaxClassAction.swift */; };
172172
45151A8F27B156E40080845F /* InboxNotesAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45151A8E27B156E40080845F /* InboxNotesAction.swift */; };
173173
45151A9127B158A10080845F /* InboxNotesStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45151A9027B158A10080845F /* InboxNotesStore.swift */; };
174+
45182D1F27B54D3000B4C05C /* InboxNote+ReadOnlyConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45182D1E27B54D3000B4C05C /* InboxNote+ReadOnlyConvertible.swift */; };
175+
45182D2127B5533400B4C05C /* InboxAction+ReadOnlyConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45182D2027B5533400B4C05C /* InboxAction+ReadOnlyConvertible.swift */; };
176+
45182D2327B55F9C00B4C05C /* InboxNotesStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45182D2227B55F9C00B4C05C /* InboxNotesStoreTests.swift */; };
174177
453305F7245AE68C00264E50 /* SitePostStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 453305F6245AE68C00264E50 /* SitePostStore.swift */; };
175178
453305F9245AE6B200264E50 /* SitePostAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 453305F8245AE6B200264E50 /* SitePostAction.swift */; };
176179
453305FB245AEDCB00264E50 /* SitePostStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 453305FA245AEDCB00264E50 /* SitePostStoreTests.swift */; };
@@ -558,6 +561,9 @@
558561
45010694239A6CDE00E24722 /* TaxClassAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaxClassAction.swift; sourceTree = "<group>"; };
559562
45151A8E27B156E40080845F /* InboxNotesAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InboxNotesAction.swift; sourceTree = "<group>"; };
560563
45151A9027B158A10080845F /* InboxNotesStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InboxNotesStore.swift; sourceTree = "<group>"; };
564+
45182D1E27B54D3000B4C05C /* InboxNote+ReadOnlyConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "InboxNote+ReadOnlyConvertible.swift"; sourceTree = "<group>"; };
565+
45182D2027B5533400B4C05C /* InboxAction+ReadOnlyConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "InboxAction+ReadOnlyConvertible.swift"; sourceTree = "<group>"; };
566+
45182D2227B55F9C00B4C05C /* InboxNotesStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InboxNotesStoreTests.swift; sourceTree = "<group>"; };
561567
453305F6245AE68C00264E50 /* SitePostStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SitePostStore.swift; sourceTree = "<group>"; };
562568
453305F8245AE6B200264E50 /* SitePostAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SitePostAction.swift; sourceTree = "<group>"; };
563569
453305FA245AEDCB00264E50 /* SitePostStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SitePostStoreTests.swift; sourceTree = "<group>"; };
@@ -1152,6 +1158,8 @@
11521158
03FBDA3D2632E29600ACE257 /* Coupon+ReadOnlyConvertible.swift */,
11531159
2685C10C263C900500D9EE97 /* AddOnGroup+ReadOnlyConvertible.swift */,
11541160
45E4620D2684C45500011BF2 /* Country+ReadOnlyConvertible.swift */,
1161+
45182D1E27B54D3000B4C05C /* InboxNote+ReadOnlyConvertible.swift */,
1162+
45182D2027B5533400B4C05C /* InboxAction+ReadOnlyConvertible.swift */,
11551163
74B2601E2188A92A0041793A /* Note+ReadOnlyConvertible.swift */,
11561164
74D7F29A20F6A7FB0058B2F0 /* Order+ReadOnlyConvertible.swift */,
11571165
74685D4F20F7F3CE008958C1 /* OrderCoupon+ReadOnlyConvertible.swift */,
@@ -1332,6 +1340,7 @@
13321340
741F34832195F752005F5BD9 /* CommentStoreTests.swift */,
13331341
03FBDA29263296C400ACE257 /* CouponStoreTests.swift */,
13341342
45E462152684D9C000011BF2 /* DataStoreTests.swift */,
1343+
45182D2227B55F9C00B4C05C /* InboxNotesStoreTests.swift */,
13351344
748525AB218A45360036DF75 /* NotificationStoreTests.swift */,
13361345
74A7688D20D45ED400F9D437 /* OrderStoreTests.swift */,
13371346
573B448A2424082B00E71ADC /* OrderStoreTests+FetchFilteredAndAllOrders.swift */,
@@ -1815,6 +1824,7 @@
18151824
74685D4E20F7EFA7008958C1 /* OrderItem+ReadOnlyConvertible.swift in Sources */,
18161825
7471401121877668009A11CC /* NotificationAction.swift in Sources */,
18171826
266503512620E2EB0079A159 /* ProductAddOn+ReadOnlyConvertible.swift in Sources */,
1827+
45182D2127B5533400B4C05C /* InboxAction+ReadOnlyConvertible.swift in Sources */,
18181828
453305F9245AE6B200264E50 /* SitePostAction.swift in Sources */,
18191829
CE4FD4522350FB5400A16B31 /* OrderItemTaxRefund+ReadOnlyConvertible.swift in Sources */,
18201830
31A89EE6278CC38F002A588E /* StripeAccount+PaymentGatewayAccount.swift in Sources */,
@@ -1984,6 +1994,7 @@
19841994
247CE7C02582DC7200F9D9D1 /* ProductImage+Mocks.swift in Sources */,
19851995
7492FADF217FB11D00ED2C69 /* SettingAction.swift in Sources */,
19861996
450106872399AB3F00E24722 /* TaxClass+ReadOnlyConvertible.swift in Sources */,
1997+
45182D1F27B54D3000B4C05C /* InboxNote+ReadOnlyConvertible.swift in Sources */,
19871998
022F00C224726090008CD97F /* SiteNotificationCountFileContents.swift in Sources */,
19881999
247CE7BC2582DC1E00F9D9D1 /* MockCustomer.swift in Sources */,
19892000
D87F614C22657B150031A13B /* AppSettingsStore.swift in Sources */,
@@ -2066,6 +2077,7 @@
20662077
02E262C0238CE80100B79588 /* StorageShippingSettingsServiceTests.swift in Sources */,
20672078
45AB8B1E24AB363D00B5B36E /* ProductTagStoreTests.swift in Sources */,
20682079
B5C9DE222087FF20006B910A /* DispatcherTests.swift in Sources */,
2080+
45182D2327B55F9C00B4C05C /* InboxNotesStoreTests.swift in Sources */,
20692081
03FBDA2A263296C400ACE257 /* CouponStoreTests.swift in Sources */,
20702082
02E7FFD92562234F00C53030 /* MockShippingLabelRemote.swift in Sources */,
20712083
D4CBAE5C26D401BC00BBE6D1 /* AnnouncementsStoreTests.swift in Sources */,

Yosemite/Yosemite/Actions/InboxNotesAction.swift

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ public enum InboxNotesAction: Action {
88
/// Retrieves all of the `InboxNote`s from the API.
99
///
1010
case loadAllInboxNotes(siteID: Int64,
11-
pageNumber: Int,
12-
pageSize: Int,
13-
orderBy: InboxNotesRemote.OrderBy,
14-
type: [InboxNotesRemote.NoteType]?,
15-
status: [InboxNotesRemote.Status]?,
11+
pageNumber: Int = Default.pageNumber,
12+
pageSize: Int = Default.pageSize,
13+
orderBy: InboxNotesRemote.OrderBy = .date,
14+
type: [InboxNotesRemote.NoteType]? = nil,
15+
status: [InboxNotesRemote.Status]? = nil,
1616
completion: (Result<[InboxNote], Error>) -> ())
1717

1818
/// Dismiss one `InboxNote`.
@@ -30,3 +30,12 @@ public enum InboxNotesAction: Action {
3030
actionID: Int64,
3131
completion: (Result<InboxNote, Error>) -> ())
3232
}
33+
34+
// MARK: - Constants
35+
//
36+
public extension InboxNotesAction {
37+
enum Default {
38+
public static let pageSize = 25
39+
public static let pageNumber = 1
40+
}
41+
}

Yosemite/Yosemite/Model/Model.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ public typealias StorageAddOnGroup = Storage.AddOnGroup
156156
public typealias StorageAnnouncement = Storage.Announcement
157157
public typealias StorageEligibilityErrorInfo = Storage.EligibilityErrorInfo
158158
public typealias StorageFeature = Storage.Feature
159+
public typealias StorageInboxNote = Storage.InboxNote
160+
public typealias StorageInboxAction = Storage.InboxAction
159161
public typealias StorageNote = Storage.Note
160162
public typealias StorageOrder = Storage.Order
161163
public typealias StorageOrderItemAttribute = Storage.OrderItemAttribute
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Foundation
2+
import Storage
3+
4+
5+
// MARK: - Storage.InboxAction: ReadOnlyConvertible
6+
//
7+
extension Storage.InboxAction: ReadOnlyConvertible {
8+
9+
/// Updates the Storage.InboxAction with the ReadOnly.
10+
///
11+
public func update(with inboxAction: Yosemite.InboxAction) {
12+
id = inboxAction.id
13+
name = inboxAction.name
14+
label = inboxAction.label
15+
status = inboxAction.status
16+
url = inboxAction.url
17+
}
18+
19+
/// Returns a ReadOnly version of the receiver.
20+
///
21+
public func toReadOnly() -> Yosemite.InboxAction {
22+
return InboxAction(id: id,
23+
name: name ?? "",
24+
label: label ?? "",
25+
status: status ?? "",
26+
url: url ?? "")
27+
}
28+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import Foundation
2+
import Storage
3+
4+
5+
// MARK: - Storage.InboxNote: ReadOnlyConvertible
6+
//
7+
extension Storage.InboxNote: ReadOnlyConvertible {
8+
9+
/// Updates the `Storage.InboxNote` from the ReadOnly representation (`Networking.InboxNote`)
10+
///
11+
public func update(with inboxNote: Yosemite.InboxNote) {
12+
siteID = inboxNote.siteID
13+
id = inboxNote.id
14+
name = inboxNote.name
15+
type = inboxNote.type
16+
status = inboxNote.status
17+
title = inboxNote.title
18+
content = inboxNote.content
19+
isRemoved = inboxNote.isRemoved
20+
isRead = inboxNote.isRead
21+
dateCreated = inboxNote.dateCreated
22+
}
23+
24+
/// Returns a ReadOnly (`Networking.InboxNote`) version of the `Storage.InboxNote`
25+
///
26+
public func toReadOnly() -> Yosemite.InboxNote {
27+
return Yosemite.InboxNote(siteID: siteID,
28+
id: id,
29+
name: name ?? "",
30+
type: type ?? "",
31+
status: status ?? "",
32+
actions: actions?.map { $0.toReadOnly() } ?? [Yosemite.InboxAction](),
33+
title: title ?? "",
34+
content: content ?? "",
35+
isRemoved: isRemoved,
36+
isRead: isRead,
37+
dateCreated: dateCreated ?? Date())
38+
}
39+
}

Yosemite/Yosemite/Stores/InboxNotesStore.swift

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import Networking
77
public class InboxNotesStore: Store {
88
private let remote: InboxNotesRemote
99

10+
private lazy var sharedDerivedStorage: StorageType = {
11+
return storageManager.writerDerivedStorage
12+
}()
13+
1014
public override init(dispatcher: Dispatcher, storageManager: StorageManagerType, network: Network) {
1115
self.remote = InboxNotesRemote(network: network)
1216
super.init(dispatcher: dispatcher, storageManager: storageManager, network: network)
@@ -52,7 +56,22 @@ private extension InboxNotesStore {
5256
type: [InboxNotesRemote.NoteType]? = nil,
5357
status: [InboxNotesRemote.Status]? = nil,
5458
completion: @escaping (Result<[InboxNote], Error>) -> ()) {
55-
remote.loadAllInboxNotes(for: siteID, pageNumber: pageNumber, pageSize: pageSize, orderBy: orderBy, type: type, status: status, completion: completion)
59+
remote.loadAllInboxNotes(for: siteID, pageNumber: pageNumber, pageSize: pageSize, orderBy: orderBy, type: type, status: status) { [weak self] result in
60+
guard let self = self else { return }
61+
switch result {
62+
case .failure(let error):
63+
completion(.failure(error))
64+
65+
case .success(let inboxNotes):
66+
if pageNumber == Default.pageNumber {
67+
self.deleteStoredInboxNotes(siteID: siteID)
68+
}
69+
70+
self.upsertStoredInboxNotesInBackground(readOnlyInboxNotes: inboxNotes, siteID: siteID) {
71+
completion(.success(inboxNotes))
72+
}
73+
}
74+
}
5675
}
5776

5877
/// Dismiss one `InboxNote`.
@@ -61,7 +80,17 @@ private extension InboxNotesStore {
6180
func dismissInboxNote(for siteID: Int64,
6281
noteID: Int64,
6382
completion: @escaping (Result<InboxNote, Error>) -> ()) {
64-
remote.dismissInboxNote(for: siteID, noteID: noteID, completion: completion)
83+
remote.dismissInboxNote(for: siteID, noteID: noteID) { [weak self] result in
84+
switch result {
85+
case .failure(let error):
86+
completion(.failure(error))
87+
88+
case .success(let inboxNote):
89+
self?.upsertStoredInboxNotesInBackground(readOnlyInboxNotes: [inboxNote], siteID: siteID, onCompletion: {
90+
completion(.success(inboxNote))
91+
})
92+
}
93+
}
6594
}
6695

6796
/// Set an `InboxNote` as `actioned`.
@@ -71,7 +100,79 @@ private extension InboxNotesStore {
71100
noteID: Int64,
72101
actionID: Int64,
73102
completion: @escaping (Result<InboxNote, Error>) -> ()) {
74-
remote.markInboxNoteAsActioned(for: siteID, noteID: noteID, actionID: actionID, completion: completion)
103+
remote.markInboxNoteAsActioned(for: siteID, noteID: noteID, actionID: actionID) { [weak self] result in
104+
switch result {
105+
case .failure(let error):
106+
completion(.failure(error))
107+
108+
case .success(let inboxNote):
109+
self?.upsertStoredInboxNotesInBackground(readOnlyInboxNotes: [inboxNote], siteID: siteID, onCompletion: {
110+
completion(.success(inboxNote))
111+
})
112+
}
113+
}
114+
}
115+
}
116+
117+
// MARK: - Storage: InboxNote
118+
//
119+
private extension InboxNotesStore {
120+
121+
/// Updates or Inserts specified Inbox Notes Entities in a background thread
122+
/// `onCompletion` will be called on the main thread.
123+
///
124+
func upsertStoredInboxNotesInBackground(readOnlyInboxNotes: [Networking.InboxNote],
125+
siteID: Int64,
126+
onCompletion: @escaping () -> Void) {
127+
let derivedStorage = sharedDerivedStorage
128+
derivedStorage.perform { [weak self] in
129+
self?.upsertStoredInboxNotes(readOnlyInboxNotes: readOnlyInboxNotes,
130+
in: derivedStorage,
131+
siteID: siteID)
132+
}
133+
134+
storageManager.saveDerivedType(derivedStorage: derivedStorage) {
135+
DispatchQueue.main.async(execute: onCompletion)
136+
}
137+
}
138+
139+
/// Updates or Inserts the specified Inbox Note entities
140+
///
141+
func upsertStoredInboxNotes(readOnlyInboxNotes: [Networking.InboxNote],
142+
in storage: StorageType,
143+
siteID: Int64) {
144+
for inboxNote in readOnlyInboxNotes {
145+
let storageInboxNote = storage.loadInboxNote(siteID: siteID, id: inboxNote.id) ?? storage.insertNewObject(ofType: Storage.InboxNote.self)
146+
storageInboxNote.update(with: inboxNote)
147+
handleInboxActions(inboxNote, storageInboxNote, storage)
148+
}
149+
}
150+
151+
/// Updates, inserts, or prunes the provided stored inboxnote actions using the provided read-only Inbox's actions
152+
///
153+
func handleInboxActions(_ readOnlyInboxNote: Networking.InboxNote, _ storageInboxNote: Storage.InboxNote, _ storage: StorageType) {
154+
155+
// Removes all the inbox action first.
156+
storageInboxNote.actions?.forEach { existingStorageAction in
157+
storage.deleteObject(existingStorageAction)
158+
}
159+
160+
// Inserts the actions from the read-only inbox note.
161+
var storageInboxActions = [StorageInboxAction]()
162+
for readOnlyInboxAction in readOnlyInboxNote.actions {
163+
let newStorageInboxAction = storage.insertNewObject(ofType: Storage.InboxAction.self)
164+
newStorageInboxAction.update(with: readOnlyInboxAction)
165+
storageInboxActions.append(newStorageInboxAction)
166+
}
167+
storageInboxNote.actions = Set(storageInboxActions)
168+
}
169+
170+
/// Deletes all Storage.InboxNote with the specified `siteID`
171+
///
172+
func deleteStoredInboxNotes(siteID: Int64) {
173+
let storage = storageManager.viewStorage
174+
storage.deleteInboxNotes(siteID: siteID)
175+
storage.saveIfNeeded()
75176
}
76177
}
77178

Yosemite/YosemiteTests/Mocks/MockStorageManager+Sample.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,14 @@ extension MockStorageManager {
200200
}
201201
return storedCountries
202202
}
203+
204+
/// Inserts a new sample InboxNote into the specified context.
205+
///
206+
@discardableResult
207+
func insertSampleInboxNote(readOnlyInboxNote: InboxNote) -> StorageInboxNote {
208+
let newInboxNote = viewStorage.insertNewObject(ofType: StorageInboxNote.self)
209+
newInboxNote.update(with: readOnlyInboxNote)
210+
211+
return newInboxNote
212+
}
203213
}

0 commit comments

Comments
 (0)