56 lines
2.5 KiB
Swift
56 lines
2.5 KiB
Swift
|
|
import SwiftUI
|
||
|
|
import TVAnarchyCore
|
||
|
|
|
||
|
|
/// Recent storage culls and missing-episode reconcile state.
|
||
|
|
struct OfflineCullPanel: View {
|
||
|
|
@Bindable var offline: OfflineCacheController
|
||
|
|
var maxFileRows = 5
|
||
|
|
|
||
|
|
var body: some View {
|
||
|
|
VStack(alignment: .leading, spacing: 8) {
|
||
|
|
if let summary = offline.lastCullSummary {
|
||
|
|
Label(summary, systemImage: "trash")
|
||
|
|
.font(.callout)
|
||
|
|
.foregroundStyle(.orange)
|
||
|
|
if let at = offline.lastCulledAt {
|
||
|
|
Text(at, style: .relative)
|
||
|
|
.font(.caption2).foregroundStyle(.tertiary)
|
||
|
|
}
|
||
|
|
if !offline.lastCulledFiles.isEmpty {
|
||
|
|
VStack(alignment: .leading, spacing: 2) {
|
||
|
|
ForEach(offline.lastCulledFiles.prefix(maxFileRows), id: \.self) { name in
|
||
|
|
Text(name).font(.caption.monospaced()).foregroundStyle(.secondary)
|
||
|
|
}
|
||
|
|
let extra = offline.lastCulledFiles.count - min(offline.lastCulledFiles.count, maxFileRows)
|
||
|
|
if extra > 0 {
|
||
|
|
Text("+\(extra) more").font(.caption2).foregroundStyle(.tertiary)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if offline.planMissingCount > 0 {
|
||
|
|
Label(
|
||
|
|
"\(offline.planMissingCount) episode\(offline.planMissingCount == 1 ? "" : "s") missing from warmup window",
|
||
|
|
systemImage: "arrow.clockwise"
|
||
|
|
)
|
||
|
|
.font(.caption)
|
||
|
|
.foregroundStyle(.secondary)
|
||
|
|
}
|
||
|
|
if offline.pinnedMissingCount > 0 {
|
||
|
|
Label(
|
||
|
|
"\(offline.pinnedMissingCount) pinned item\(offline.pinnedMissingCount == 1 ? "" : "s") missing — restore prioritized",
|
||
|
|
systemImage: "pin"
|
||
|
|
)
|
||
|
|
.font(.caption)
|
||
|
|
.foregroundStyle(.secondary)
|
||
|
|
}
|
||
|
|
Text("Checks every \(Int(OfflineCacheController.reconcileInterval / 60)) min — culled episodes refetch when space allows. Pinned items (e.g. kept from Adult) are never culled and restored first.")
|
||
|
|
.font(.caption2)
|
||
|
|
.foregroundStyle(.tertiary)
|
||
|
|
.fixedSize(horizontal: false, vertical: true)
|
||
|
|
}
|
||
|
|
.padding(10)
|
||
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||
|
|
.background(.orange.opacity(0.08), in: RoundedRectangle(cornerRadius: 8))
|
||
|
|
}
|
||
|
|
}
|