mirror of
https://github.com/nimbold/Firelink.git
synced 2026-07-27 20:40:12 +00:00
perf: reduce main thread CPU usage and SSD wear from frequent JSON serialization
This commit is contained in:
@@ -135,6 +135,7 @@ final class AppSettings: ObservableObject {
|
||||
|
||||
private let defaults: UserDefaults
|
||||
private let storageKey = "Firelink.AppSettings.v1"
|
||||
private var saveTask: Task<Void, Never>?
|
||||
|
||||
init(defaults: UserDefaults = .standard) {
|
||||
self.defaults = defaults
|
||||
@@ -247,9 +248,17 @@ final class AppSettings: ObservableObject {
|
||||
downloadDirectories: Dictionary(uniqueKeysWithValues: downloadDirectories.map { ($0.key.rawValue, $0.value) }),
|
||||
siteLogins: siteLogins
|
||||
)
|
||||
let defaults = self.defaults
|
||||
let storageKey = self.storageKey
|
||||
|
||||
if let data = try? JSONEncoder().encode(stored) {
|
||||
defaults.set(data, forKey: storageKey)
|
||||
saveTask?.cancel()
|
||||
saveTask = Task { @MainActor [defaults, storageKey] in
|
||||
let data = await Task.detached(priority: .background) {
|
||||
try? JSONEncoder().encode(stored)
|
||||
}.value
|
||||
|
||||
guard !Task.isCancelled, let encoded = data else { return }
|
||||
defaults.set(encoded, forKey: storageKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ final class DownloadController: ObservableObject {
|
||||
let supportDir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first ?? URL(fileURLWithPath: NSHomeDirectory())
|
||||
return supportDir.appendingPathComponent("Firelink").appendingPathComponent("downloads.json")
|
||||
}()
|
||||
private var saveTask: Task<Void, Never>?
|
||||
|
||||
init(settings: AppSettings) {
|
||||
self.settings = settings
|
||||
@@ -674,14 +675,23 @@ final class DownloadController: ObservableObject {
|
||||
}
|
||||
|
||||
private func saveDownloads() {
|
||||
do {
|
||||
let directory = storageURL.deletingLastPathComponent()
|
||||
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
|
||||
let state = StoredDownloadState(queues: queues, downloads: downloads)
|
||||
let data = try JSONEncoder().encode(state)
|
||||
try data.write(to: storageURL, options: .atomic)
|
||||
} catch {
|
||||
print("Failed to save downloads: \(error)")
|
||||
let queuesCopy = queues
|
||||
let downloadsCopy = downloads
|
||||
let storageURL = self.storageURL
|
||||
|
||||
saveTask?.cancel()
|
||||
saveTask = Task.detached(priority: .background) {
|
||||
do {
|
||||
let directory = storageURL.deletingLastPathComponent()
|
||||
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
|
||||
let state = StoredDownloadState(queues: queuesCopy, downloads: downloadsCopy)
|
||||
let data = try JSONEncoder().encode(state)
|
||||
|
||||
guard !Task.isCancelled else { return }
|
||||
try data.write(to: storageURL, options: .atomic)
|
||||
} catch {
|
||||
print("Failed to save downloads: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ final class TableSettings: ObservableObject {
|
||||
|
||||
private let defaults = UserDefaults.standard
|
||||
private let storageKey = "Firelink.TableSettings.v1"
|
||||
private var saveTask: Task<Void, Never>?
|
||||
|
||||
init() {
|
||||
let defaultVisibleColumns: Set<DownloadColumn> = [.fileName, .size, .progress, .speed, .eta, .dateAdded]
|
||||
@@ -90,8 +91,16 @@ final class TableSettings: ObservableObject {
|
||||
sortColumn: sortColumn,
|
||||
sortDirection: sortDirection
|
||||
)
|
||||
if let data = try? JSONEncoder().encode(stored) {
|
||||
defaults.set(data, forKey: storageKey)
|
||||
let storageKey = self.storageKey
|
||||
|
||||
saveTask?.cancel()
|
||||
saveTask = Task { @MainActor in
|
||||
let data = await Task.detached(priority: .background) {
|
||||
try? JSONEncoder().encode(stored)
|
||||
}.value
|
||||
|
||||
guard !Task.isCancelled, let encoded = data else { return }
|
||||
UserDefaults.standard.set(encoded, forKey: storageKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,7 +123,7 @@ struct DownloadTable: View {
|
||||
|
||||
@StateObject private var tableSettings = TableSettings()
|
||||
@State private var pendingDeleteItems: Set<DownloadItem.ID>?
|
||||
@State private var resizeBaseWidths: [DownloadColumn: CGFloat] = [:]
|
||||
@State private var dragOffsets: [DownloadColumn: CGFloat] = [:]
|
||||
@State private var lastSelectedIndex: Int?
|
||||
@State private var draggedItemID: DownloadItem.ID?
|
||||
|
||||
@@ -283,12 +292,12 @@ struct DownloadTable: View {
|
||||
.gesture(
|
||||
DragGesture(minimumDistance: 1)
|
||||
.onChanged { value in
|
||||
let baseWidth = resizeBaseWidths[column] ?? width(for: column)
|
||||
resizeBaseWidths[column] = baseWidth
|
||||
tableSettings.columnWidths[column] = max(70, baseWidth + value.translation.width)
|
||||
dragOffsets[column] = value.translation.width
|
||||
}
|
||||
.onEnded { _ in
|
||||
resizeBaseWidths[column] = nil
|
||||
.onEnded { value in
|
||||
let baseWidth = tableSettings.columnWidths[column] ?? column.width
|
||||
tableSettings.columnWidths[column] = max(70, baseWidth + value.translation.width)
|
||||
dragOffsets[column] = nil
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -445,7 +454,9 @@ struct DownloadTable: View {
|
||||
}
|
||||
|
||||
private func width(for column: DownloadColumn) -> CGFloat {
|
||||
max(70, tableSettings.columnWidths[column] ?? column.width)
|
||||
let baseWidth = tableSettings.columnWidths[column] ?? column.width
|
||||
let offset = dragOffsets[column] ?? 0
|
||||
return max(70, baseWidth + offset)
|
||||
}
|
||||
|
||||
private var sortedItems: [DownloadItem] {
|
||||
|
||||
Reference in New Issue
Block a user