Skip to content
11 changes: 9 additions & 2 deletions Modules/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ let package = Package(
.library(name: "WordPressShared", targets: ["WordPressShared"]),
.library(name: "WordPressUI", targets: ["WordPressUI"]),
.library(name: "WordPressReader", targets: ["WordPressReader"]),
.library(name: "WordPressCore", targets: ["WordPressCore"]),
.library(name: "WordPressCoreProtocols", targets: ["WordPressCore"]),
],
dependencies: [
.package(url: "https://github.com/airbnb/lottie-ios", from: "4.4.0"),
Expand Down Expand Up @@ -137,7 +139,7 @@ let package = Package(
name: "Support",
dependencies: [
"AsyncImageKit",
"WordPressCore",
"WordPressCoreProtocols",
]
),
.target(name: "TextBundle"),
Expand All @@ -152,10 +154,15 @@ let package = Package(
], swiftSettings: [.swiftLanguageMode(.v5)]),
.target(name: "WordPressFlux", swiftSettings: [.swiftLanguageMode(.v5)]),
.target(name: "WordPressCore", dependencies: [
"WordPressCoreProtocols",
"WordPressShared",
.product(name: "WordPressAPI", package: "wordpress-rs")
.product(name: "WordPressAPI", package: "wordpress-rs"),
]
),
.target(name: "WordPressCoreProtocols", dependencies: [
// This package should never have dependencies – it exists to expose protocols implemented in WordPressCore
// to UI code, because `wordpress-rs` doesn't work nicely with previews.
]),
.target(name: "WordPressLegacy", dependencies: ["DesignSystem", "WordPressShared"]),
.target(name: "WordPressSharedObjC", resources: [.process("Resources")], swiftSettings: [.swiftLanguageMode(.v5)]),
.target(
Expand Down
16 changes: 16 additions & 0 deletions Modules/Sources/Support/Extensions/Foundation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,20 @@ extension Task where Failure == Error {
return try await MainActor.run(body: operation)
}
}

enum RunForAtLeastResult<T>: Sendable where T: Sendable {
case result(T)
case wait
}

static func runForAtLeast<C>(
_ duration: C.Instant.Duration,
operation: @escaping @Sendable () async throws -> Success,
clock: C = .continuous
) async throws -> Success where C: Clock {
async let waitResult: () = try await clock.sleep(for: duration)
async let performTask = try await operation()

return try await (waitResult, performTask).1
}
}
33 changes: 31 additions & 2 deletions Modules/Sources/Support/InternalDataProvider.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation
import WordPressCore
import WordPressCoreProtocols

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

Expand All @@ -8,7 +8,8 @@ extension SupportDataProvider {
applicationLogProvider: InternalLogDataProvider(),
botConversationDataProvider: InternalBotConversationDataProvider(),
userDataProvider: InternalUserDataProvider(),
supportConversationDataProvider: InternalSupportConversationDataProvider()
supportConversationDataProvider: InternalSupportConversationDataProvider(),
diagnosticsDataProvider: InternalDiagnosticsDataProvider()
)

static let applicationLog = ApplicationLog(path: URL(filePath: #filePath), createdAt: Date(), modifiedAt: Date())
Expand Down Expand Up @@ -391,3 +392,31 @@ actor InternalSupportConversationDataProvider: SupportConversationDataProvider {
self.conversations[value.id] = value
}
}

actor InternalDiagnosticsDataProvider: DiagnosticsDataProvider {

func fetchDiskCacheUsage() async throws -> WordPressCoreProtocols.DiskCacheUsage {
DiskCacheUsage(fileCount: 64, byteCount: 623_423_562)
}

func clearDiskCache(progress: @Sendable (CacheDeletionProgress) async throws -> Void) async throws {
let totalFiles = 12

// Initial progress (0%)
try await progress(CacheDeletionProgress(filesDeleted: 0, totalFileCount: totalFiles))

for i in 1...totalFiles {
// Pretend each file takes a short time to delete
try await Task.sleep(for: .milliseconds(150))

// Report incremental progress
try await progress(CacheDeletionProgress(filesDeleted: i, totalFileCount: totalFiles))
}
}

func resetPluginRecommendations() async throws {
if Bool.random() {
throw CocoaError(.fileNoSuchFile)
}
}
}
27 changes: 26 additions & 1 deletion Modules/Sources/Support/SupportDataProvider.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Foundation
import WordPressCore
import WordPressCoreProtocols

public enum SupportFormAction {
case viewApplicationLogList
Expand All @@ -23,6 +23,7 @@ public enum SupportFormAction {

case viewDiagnostics
case emptyDiskCache(bytesSaved: Int64)
case resetPluginRecommendations
}

@MainActor
Expand All @@ -32,6 +33,7 @@ public final class SupportDataProvider: ObservableObject, Sendable {
private let botConversationDataProvider: BotConversationDataProvider
private let userDataProvider: CurrentUserDataProvider
private let supportConversationDataProvider: SupportConversationDataProvider
private let diagnosticsDataProvider: DiagnosticsDataProvider

private weak var supportDelegate: SupportDelegate?

Expand All @@ -40,12 +42,14 @@ public final class SupportDataProvider: ObservableObject, Sendable {
botConversationDataProvider: BotConversationDataProvider,
userDataProvider: CurrentUserDataProvider,
supportConversationDataProvider: SupportConversationDataProvider,
diagnosticsDataProvider: DiagnosticsDataProvider,
delegate: SupportDelegate? = nil
) {
self.applicationLogProvider = applicationLogProvider
self.botConversationDataProvider = botConversationDataProvider
self.userDataProvider = userDataProvider
self.supportConversationDataProvider = supportConversationDataProvider
self.diagnosticsDataProvider = diagnosticsDataProvider
self.supportDelegate = delegate
}

Expand Down Expand Up @@ -161,6 +165,20 @@ public final class SupportDataProvider: ObservableObject, Sendable {
self.userDid(.deleteAllApplicationLogs)
try await self.applicationLogProvider.deleteAllApplicationLogs()
}

// Diagnostics
public func fetchDiskCacheUsage() async throws -> DiskCacheUsage {
try await self.diagnosticsDataProvider.fetchDiskCacheUsage()
}

public func clearDiskCache(progress: (@Sendable @escaping (CacheDeletionProgress) async throws -> Void)) async throws {
try await self.diagnosticsDataProvider.clearDiskCache(progress: progress)
}

public func resetPluginRecommendations() async throws {
self.userDid(.resetPluginRecommendations)
try await self.diagnosticsDataProvider.resetPluginRecommendations()
}
}

public protocol SupportFormDataProvider {
Expand Down Expand Up @@ -211,6 +229,13 @@ public protocol CurrentUserDataProvider: Actor {
nonisolated func fetchCurrentSupportUser() throws -> any CachedAndFetchedResult<SupportUser>
}

public protocol DiagnosticsDataProvider: Actor {
func fetchDiskCacheUsage() async throws -> DiskCacheUsage
func clearDiskCache(progress: (@Sendable @escaping (CacheDeletionProgress) async throws -> Void)) async throws

func resetPluginRecommendations() async throws
}

public protocol ApplicationLogDataProvider: Actor {
func readApplicationLog(_ log: ApplicationLog) async throws -> String
func fetchApplicationLogs() async throws -> [ApplicationLog]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import SwiftUI
import WordPressCore

public struct DiagnosticsView: View {

Expand All @@ -15,6 +14,7 @@ public struct DiagnosticsView: View {
.foregroundStyle(.secondary)

EmptyDiskCacheView()
ResetPluginRecommendationsView()
}
.padding()
}
Expand Down
28 changes: 10 additions & 18 deletions Modules/Sources/Support/UI/Diagnostics/EmptyDiskCacheView.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import SwiftUI
import WordPressCore
import WordPressCoreProtocols

struct EmptyDiskCacheView: View {

Expand All @@ -8,7 +8,7 @@ struct EmptyDiskCacheView: View {

enum ViewState: Equatable {
case loading
case loaded(usage: DiskCache.DiskCacheUsage)
case loaded(usage: DiskCacheUsage)
case clearing(progress: Double, result: String)
case error(Error)

Expand Down Expand Up @@ -51,8 +51,6 @@ struct EmptyDiskCacheView: View {
@State
var state: ViewState = .loading

private let cache = DiskCache()

var body: some View {
// Clear Disk Cache card
DiagnosticCard(
Expand Down Expand Up @@ -112,7 +110,7 @@ struct EmptyDiskCacheView: View {

private func fetchDiskCacheUsage() async {
do {
let usage = try await cache.diskUsage()
let usage = try await dataProvider.fetchDiskCacheUsage()
await MainActor.run {
self.state = .loaded(usage: usage)
}
Expand All @@ -134,18 +132,12 @@ struct EmptyDiskCacheView: View {
self.state = .clearing(progress: 0, result: "")

do {
try await cache.removeAll { count, total in
let progress: Double

if count > 0 && total > 0 {
progress = Double(count) / Double(total)
} else {
progress = 0
}

await MainActor.run {
withAnimation {
self.state = .clearing(progress: progress, result: "Working")
try await Task.runForAtLeast(.seconds(1.5)) {
try await dataProvider.clearDiskCache { progress in
await MainActor.run {
withAnimation {
self.state = .clearing(progress: progress.progress, result: "Working")
}
}
}
}
Expand All @@ -166,5 +158,5 @@ struct EmptyDiskCacheView: View {
}

#Preview {
EmptyDiskCacheView()
EmptyDiskCacheView().environmentObject(SupportDataProvider.testing)
}
Loading