Skip to content

Commit d4d69ad

Browse files
committed
Add cache for support
1 parent 6343fa1 commit d4d69ad

28 files changed

+1405
-366
lines changed

Modules/Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ let package = Package(
136136
.target(
137137
name: "Support",
138138
dependencies: [
139-
"AsyncImageKit"
139+
"AsyncImageKit",
140+
"WordPressCore",
140141
]
141142
),
142143
.target(name: "TextBundle"),

Modules/Sources/Support/Extensions/Foundation.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,17 @@ func convertMarkdownTextToAttributedString(_ text: String) -> AttributedString {
8080
return AttributedString(text)
8181
}
8282
}
83+
84+
extension Task where Failure == Error {
85+
static func delayedAndRunOnMainActor<C>(
86+
for duration: C.Instant.Duration,
87+
priority: TaskPriority? = nil,
88+
operation: @MainActor @escaping @Sendable () throws -> Success,
89+
clock: C = .continuous
90+
) -> Task where C: Clock {
91+
Task(priority: priority) {
92+
try await clock.sleep(for: duration)
93+
return try await MainActor.run(body: operation)
94+
}
95+
}
96+
}

Modules/Sources/Support/InternalDataProvider.swift

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Foundation
2+
import WordPressCore
23

34
// This file is all module-internal and provides sample data for UI development
45

@@ -14,7 +15,8 @@ extension SupportDataProvider {
1415
static let supportUser = SupportUser(
1516
userId: 1234,
1617
username: "demo-user",
17-
18+
19+
permissions: [.createChatConversation, .createSupportRequest]
1820
)
1921
static let botConversation = BotConversation(
2022
id: 1234,
@@ -271,16 +273,20 @@ actor InternalBotConversationDataProvider: BotConversationDataProvider {
271273
await SupportDataProvider.supportUser
272274
}
273275

274-
func loadBotConversations() async throws -> [BotConversation] {
275-
[await SupportDataProvider.botConversation]
276+
func loadBotConversations() async throws -> any CachedAndFetchedResult<[BotConversation]> {
277+
UncachedResult {
278+
[await SupportDataProvider.botConversation]
279+
}
276280
}
277281

278-
func loadBotConversation(id: UInt64) async throws -> BotConversation? {
279-
if id == 5678 {
280-
return await SupportDataProvider.conversationReferredToHuman
281-
}
282+
func loadBotConversation(id: UInt64) async throws -> any CachedAndFetchedResult<BotConversation> {
283+
UncachedResult {
284+
if id == 5678 {
285+
return await SupportDataProvider.conversationReferredToHuman
286+
}
282287

283-
return await SupportDataProvider.botConversation
288+
return await SupportDataProvider.botConversation
289+
}
284290
}
285291

286292
func delete(conversationIds: [UInt64]) async throws {
@@ -309,23 +315,28 @@ actor InternalBotConversationDataProvider: BotConversationDataProvider {
309315
}
310316

311317
actor InternalUserDataProvider: CurrentUserDataProvider {
312-
func fetchCurrentSupportUser() async throws -> SupportUser {
313-
await SupportDataProvider.supportUser
318+
func fetchCurrentSupportUser() async throws -> any CachedAndFetchedResult<SupportUser> {
319+
UncachedResult {
320+
await SupportDataProvider.supportUser
321+
}
314322
}
315323
}
316324

317325
actor InternalSupportConversationDataProvider: SupportConversationDataProvider {
318326
private var conversations: [UInt64: Conversation] = [:]
319327

320-
func loadSupportConversations() async throws -> [ConversationSummary] {
321-
try await Task.sleep(for: .seconds(10))
322-
return await SupportDataProvider.supportConversationSummaries
328+
func loadSupportConversations() async throws -> any CachedAndFetchedResult<[ConversationSummary]> {
329+
UncachedResult {
330+
return await SupportDataProvider.supportConversationSummaries
331+
}
323332
}
324333

325-
func loadSupportConversation(id: UInt64) async throws -> Conversation {
326-
let conversation = await SupportDataProvider.supportConversation
327-
self.conversations[id] = conversation
328-
return conversation
334+
func loadSupportConversation(id: UInt64) async throws -> any CachedAndFetchedResult<Conversation> {
335+
UncachedResult {
336+
let conversation = await SupportDataProvider.supportConversation
337+
await self.cache(conversation)
338+
return conversation
339+
}
329340
}
330341

331342
func replyToSupportConversation(
@@ -335,7 +346,7 @@ actor InternalSupportConversationDataProvider: SupportConversationDataProvider {
335346
attachments: [URL]
336347
) async throws -> Conversation {
337348

338-
let conversation = try await loadSupportConversation(id: id)
349+
let conversation = try await loadSupportConversation(id: id).fetchedResult()
339350

340351
if Bool.random() {
341352
throw CocoaError(.validationInvalidDate)
@@ -375,4 +386,8 @@ actor InternalSupportConversationDataProvider: SupportConversationDataProvider {
375386
)]
376387
)
377388
}
389+
390+
private func cache(_ value: Conversation) {
391+
self.conversations[value.id] = value
392+
}
378393
}

Modules/Sources/Support/Model/SupportConversation.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Foundation
22

3-
public struct ConversationSummary: Identifiable, Sendable {
3+
public struct ConversationSummary: Identifiable, Hashable, Sendable, Codable {
44
public let id: UInt64
55
public let title: String
66
public let description: String
@@ -25,7 +25,7 @@ public struct ConversationSummary: Identifiable, Sendable {
2525
}
2626
}
2727

28-
public struct Conversation: Identifiable, Sendable {
28+
public struct Conversation: Identifiable, Sendable, Codable {
2929
public let id: UInt64
3030
public let title: String
3131
public let description: String
@@ -57,7 +57,7 @@ public struct Conversation: Identifiable, Sendable {
5757
}
5858
}
5959

60-
public struct Message: Identifiable, Sendable {
60+
public struct Message: Identifiable, Sendable, Codable {
6161
public let id: UInt64
6262
public let content: String
6363

@@ -91,7 +91,7 @@ public struct Message: Identifiable, Sendable {
9191
}
9292
}
9393

94-
public struct Attachment: Identifiable, Sendable {
94+
public struct Attachment: Identifiable, Sendable, Codable {
9595
public let id: UInt64
9696

9797
public init(id: UInt64) {

Modules/Sources/Support/Model/SupportUser.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
import Foundation
22
import CryptoKit
33

4-
public struct SupportUser: Sendable {
4+
public struct SupportUser: Sendable, Codable {
55
public let userId: UInt64
66
public let username: String
77
public let email: String
8+
public let permissions: [SupportUserPermission]
89
public let avatarUrl: URL
910

1011
public init(
1112
userId: UInt64,
1213
username: String,
1314
email: String,
15+
permissions: [SupportUserPermission] = [],
1416
avatarUrl: URL? = nil
1517
) {
1618
self.userId = userId
1719
self.username = username
1820
self.email = email
21+
self.permissions = permissions
1922

2023
if let avatarUrl {
2124
self.avatarUrl = avatarUrl

Modules/Sources/Support/SupportDataProvider.swift

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
import Foundation
2+
import WordPressCore
23

34
public enum SupportFormAction {
45
case viewSupportForm
56
}
67

8+
public enum DiagnosticAction {
9+
case clearDiskCache
10+
}
11+
12+
public enum DiagnosticActionStatus {
13+
case running(progress: Float)
14+
case success
15+
case error(Error)
16+
}
17+
718
@MainActor
819
public final class SupportDataProvider: ObservableObject, Sendable {
920

@@ -33,34 +44,38 @@ public final class SupportDataProvider: ObservableObject, Sendable {
3344
self.supportDelegate?.userDid(action)
3445
}
3546

47+
public func userDid(_ action: DiagnosticAction, progress: @escaping (DiagnosticActionStatus) -> Void) {
48+
self.supportDelegate?.userDid(action, progress: progress)
49+
}
50+
3651
// Support Bots Data Source
37-
public func loadSupportIdentity() async throws -> SupportUser {
52+
public func loadSupportIdentity() async throws -> any CachedAndFetchedResult<SupportUser> {
3853
try await self.userDataProvider.fetchCurrentSupportUser()
3954
}
4055

4156
// Bot Conversation Data Source
42-
public func loadConversations() async throws -> [BotConversation] {
57+
public func loadConversations() async throws -> any CachedAndFetchedResult<[BotConversation]> {
4358
try await self.botConversationDataProvider.loadBotConversations()
4459
}
4560

46-
public func loadConversation(id: UInt64) async throws -> BotConversation? {
61+
public func loadConversation(id: UInt64) async throws -> any CachedAndFetchedResult<BotConversation> {
4762
try await self.botConversationDataProvider.loadBotConversation(id: id)
4863
}
4964

5065
public func delete(conversationIds: [UInt64]) async throws {
5166
try await self.botConversationDataProvider.delete(conversationIds: conversationIds)
5267
}
5368

54-
public func sendMessage(message: String, in conversation: BotConversation?) async throws -> BotConversation {
69+
public func sendMessage(message: String, in conversation: BotConversation? = nil) async throws -> BotConversation {
5570
try await self.botConversationDataProvider.sendMessage(message: message, in: conversation)
5671
}
5772

5873
// Support Conversations Data Source
59-
public func loadSupportConversations() async throws -> [ConversationSummary] {
74+
public func loadSupportConversations() async throws -> any CachedAndFetchedResult<[ConversationSummary]> {
6075
try await self.supportConversationDataProvider.loadSupportConversations()
6176
}
6277

63-
public func loadSupportConversation(id: UInt64) async throws -> Conversation {
78+
public func loadSupportConversation(id: UInt64) async throws -> any CachedAndFetchedResult<Conversation> {
6479
try await self.supportConversationDataProvider.loadSupportConversation(id: id)
6580
}
6681

@@ -147,10 +162,16 @@ extension SupportFormDataProvider {
147162

148163
public protocol SupportDelegate: NSObject {
149164
func userDid(_ action: SupportFormAction)
165+
func userDid(_ action: DiagnosticAction, progress: (DiagnosticActionStatus) -> Void)
166+
}
167+
168+
public enum SupportUserPermission: Sendable, Codable {
169+
case createChatConversation
170+
case createSupportRequest
150171
}
151172

152173
public protocol CurrentUserDataProvider: Actor {
153-
func fetchCurrentSupportUser() async throws -> SupportUser
174+
func fetchCurrentSupportUser() async throws -> any CachedAndFetchedResult<SupportUser>
154175
}
155176

156177
public protocol ApplicationLogDataProvider: Actor {
@@ -173,16 +194,16 @@ public extension ApplicationLogDataProvider {
173194
}
174195

175196
public protocol BotConversationDataProvider: Actor {
176-
func loadBotConversations() async throws -> [BotConversation]
177-
func loadBotConversation(id: UInt64) async throws -> BotConversation?
197+
func loadBotConversations() async throws -> any CachedAndFetchedResult<[BotConversation]>
198+
func loadBotConversation(id: UInt64) async throws -> any CachedAndFetchedResult<BotConversation>
178199

179200
func sendMessage(message: String, in conversation: BotConversation?) async throws -> BotConversation
180201
func delete(conversationIds: [UInt64]) async throws
181202
}
182203

183204
public protocol SupportConversationDataProvider: Actor {
184-
func loadSupportConversations() async throws -> [ConversationSummary]
185-
func loadSupportConversation(id: UInt64) async throws -> Conversation
205+
func loadSupportConversations() async throws -> any CachedAndFetchedResult<[ConversationSummary]>
206+
func loadSupportConversation(id: UInt64) async throws -> any CachedAndFetchedResult<Conversation>
186207

187208
func replyToSupportConversation(
188209
id: UInt64,

Modules/Sources/Support/UI/Application Logs/ActivityLogDetailView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ struct ActivityLogDetailView: View {
124124
}
125125

126126
#Preview {
127-
NavigationView {
127+
NavigationStack {
128128
ActivityLogDetailView(
129129
applicationLog: SupportDataProvider.applicationLog ).environmentObject(SupportDataProvider.testing)
130130
}

Modules/Sources/Support/UI/Application Logs/ActivityLogListView.swift

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,7 @@ public struct ActivityLogListView: View {
4343
}
4444
.navigationTitle("Activity Logs")
4545
.overlay {
46-
if case .loaded(let array, let deletionState) = self.state {
47-
if array.isEmpty {
48-
ContentUnavailableView {
49-
Label("No Logs Found", systemImage: "doc.text")
50-
} description: {
51-
Text("There are no activity logs available")
52-
}
53-
}
54-
46+
if case .loaded(_, let deletionState) = self.state {
5547
switch deletionState {
5648
case .none: EmptyView() // Do nothing
5749
case .deleting: ProgressView()
@@ -110,6 +102,12 @@ public struct ActivityLogListView: View {
110102
self.isConfirmingDeletion = true
111103
}
112104
}
105+
} else {
106+
ContentUnavailableView {
107+
Label("No Logs Found", systemImage: "doc.text")
108+
} description: {
109+
Text("There are no activity logs available")
110+
}
113111
}
114112
}
115113

@@ -166,7 +164,7 @@ public struct ActivityLogListView: View {
166164
}
167165

168166
#Preview {
169-
NavigationView {
167+
NavigationStack {
170168
ActivityLogListView() .environmentObject(SupportDataProvider.testing)
171169

172170
}

Modules/Sources/Support/UI/Application Logs/ActivityLogSharingView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ struct ActivityLogSharingView: View {
3333
var destination: () -> AnyView
3434

3535
var body: some View {
36-
NavigationView {
36+
NavigationStack {
3737
VStack(alignment: .leading, spacing: 24) {
3838

3939
VStack(spacing: 12) {

0 commit comments

Comments
 (0)