Decouple source access from detail view types

This commit is contained in:
John Burwell 2026-05-29 13:44:25 -05:00
parent 3e7e3016e5
commit dce5af8a89
9 changed files with 23 additions and 22 deletions

View File

@ -0,0 +1,7 @@
import Foundation
struct DirectoryEntry: Identifiable, Hashable, Sendable {
let id = UUID()
let name: String
let isDirectory: Bool
}

View File

@ -187,7 +187,7 @@ final class SourceLibrary: ObservableObject, SourceScanSessionHosting, SourcePer
startScan(for: sourceID, mode: .fullScan) startScan(for: sourceID, mode: .fullScan)
} }
func listContents(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> [DirectoryPreviewEntry] { func listContents(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> [DirectoryEntry] {
try await sourceAccessMethod.listItemContents(for: item, in: source) try await sourceAccessMethod.listItemContents(for: item, in: source)
} }

View File

@ -307,7 +307,7 @@ struct AppleMobileDeviceSourceAccess: ConnectedDeviceSourceAccessMethod {
} }
} }
nonisolated func listItemContents(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> [DirectoryPreviewEntry] { nonisolated func listItemContents(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> [DirectoryEntry] {
guard case .connectedDevice(_, let container) = source.origin else { guard case .connectedDevice(_, let container) = source.origin else {
return [] return []
} }
@ -325,7 +325,7 @@ struct AppleMobileDeviceSourceAccess: ConnectedDeviceSourceAccessMethod {
return entries return entries
.map { entry in .map { entry in
let isDirectory = !NSString(string: entry).pathExtension.isEmpty ? false : true let isDirectory = !NSString(string: entry).pathExtension.isEmpty ? false : true
return DirectoryPreviewEntry(name: entry, isDirectory: isDirectory) return DirectoryEntry(name: entry, isDirectory: isDirectory)
} }
.sorted { lhs, rhs in .sorted { lhs, rhs in
if lhs.isDirectory != rhs.isDirectory { if lhs.isDirectory != rhs.isDirectory {

View File

@ -26,7 +26,7 @@ protocol SourceAccessMethod: Sendable {
nonisolated func loadPreviewAssets(for items: [MinecraftContentItem], in source: MinecraftSource) async -> [MinecraftContentItem] nonisolated func loadPreviewAssets(for items: [MinecraftContentItem], in source: MinecraftSource) async -> [MinecraftContentItem]
nonisolated func loadSize(for item: MinecraftContentItem, in source: MinecraftSource) async -> MinecraftContentItem nonisolated func loadSize(for item: MinecraftContentItem, in source: MinecraftSource) async -> MinecraftContentItem
nonisolated func loadSizeAssets(for items: [MinecraftContentItem], in source: MinecraftSource) async -> [MinecraftContentItem] nonisolated func loadSizeAssets(for items: [MinecraftContentItem], in source: MinecraftSource) async -> [MinecraftContentItem]
nonisolated func listItemContents(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> [DirectoryPreviewEntry] nonisolated func listItemContents(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> [DirectoryEntry]
nonisolated func materializeItem(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> URL nonisolated func materializeItem(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> URL
nonisolated func purgeCachedArtifacts(for source: MinecraftSource) async nonisolated func purgeCachedArtifacts(for source: MinecraftSource) async
} }
@ -96,7 +96,7 @@ extension SourceAccessMethod {
return sizedItems return sizedItems
} }
nonisolated func listItemContents(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> [DirectoryPreviewEntry] { nonisolated func listItemContents(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> [DirectoryEntry] {
_ = source _ = source
_ = item _ = item
return [] return []
@ -191,7 +191,7 @@ struct SourceAccessCoordinator: SourceAccessMethod {
return await accessMethod(for: source).loadSizeAssets(for: items, in: source) return await accessMethod(for: source).loadSizeAssets(for: items, in: source)
} }
nonisolated func listItemContents(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> [DirectoryPreviewEntry] { nonisolated func listItemContents(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> [DirectoryEntry] {
return try await accessMethod(for: source).listItemContents(for: item, in: source) return try await accessMethod(for: source).listItemContents(for: item, in: source)
} }

View File

@ -104,7 +104,7 @@ struct LocalFolderSourceAccess: SourceAccessMethod {
return WorldScanner.loadSize(for: item) return WorldScanner.loadSize(for: item)
} }
nonisolated func listItemContents(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> [DirectoryPreviewEntry] { nonisolated func listItemContents(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> [DirectoryEntry] {
_ = source _ = source
let fileManager = FileManager.default let fileManager = FileManager.default
let urls = try fileManager.contentsOfDirectory( let urls = try fileManager.contentsOfDirectory(
@ -116,7 +116,7 @@ struct LocalFolderSourceAccess: SourceAccessMethod {
return urls return urls
.map { url in .map { url in
let isDirectory = (try? url.resourceValues(forKeys: [.isDirectoryKey]).isDirectory) == true let isDirectory = (try? url.resourceValues(forKeys: [.isDirectoryKey]).isDirectory) == true
return DirectoryPreviewEntry(name: url.lastPathComponent, isDirectory: isDirectory) return DirectoryEntry(name: url.lastPathComponent, isDirectory: isDirectory)
} }
.sorted { lhs, rhs in .sorted { lhs, rhs in
if lhs.isDirectory != rhs.isDirectory { if lhs.isDirectory != rhs.isDirectory {

View File

@ -1,12 +1,6 @@
import AppKit import AppKit
import SwiftUI import SwiftUI
struct DirectoryPreviewEntry: Identifiable {
let id = UUID()
let name: String
let isDirectory: Bool
}
struct ItemDetailColumnView: View { struct ItemDetailColumnView: View {
let item: MinecraftContentItem? let item: MinecraftContentItem?
let source: MinecraftSource? let source: MinecraftSource?
@ -16,7 +10,7 @@ struct ItemDetailColumnView: View {
let worldsUsingPack: [MinecraftContentItem] let worldsUsingPack: [MinecraftContentItem]
let backingPackInstances: [MinecraftContentItem] let backingPackInstances: [MinecraftContentItem]
let isSuspiciousPack: Bool let isSuspiciousPack: Bool
let contents: [DirectoryPreviewEntry] let contents: [DirectoryEntry]
let directoryPreviewLimit: Int let directoryPreviewLimit: Int
let isEmpty: Bool let isEmpty: Bool
let isPerformingItemAction: Bool let isPerformingItemAction: Bool

View File

@ -12,7 +12,7 @@ struct ItemDetailView: View {
let worldsUsingPack: [MinecraftContentItem] let worldsUsingPack: [MinecraftContentItem]
let backingPackInstances: [MinecraftContentItem] let backingPackInstances: [MinecraftContentItem]
let isSuspiciousPack: Bool let isSuspiciousPack: Bool
let contents: [DirectoryPreviewEntry] let contents: [DirectoryEntry]
let directoryPreviewLimit: Int let directoryPreviewLimit: Int
let isPerformingItemAction: Bool let isPerformingItemAction: Bool
let areFileActionsEnabled: Bool let areFileActionsEnabled: Bool

View File

@ -230,11 +230,11 @@ enum PreviewFixtures {
static let allSources = [primarySource, secondarySource] static let allSources = [primarySource, secondarySource]
static let directoryEntries = [ static let directoryEntries = [
DirectoryPreviewEntry(name: "db", isDirectory: true), DirectoryEntry(name: "db", isDirectory: true),
DirectoryPreviewEntry(name: "level.dat", isDirectory: false), DirectoryEntry(name: "level.dat", isDirectory: false),
DirectoryPreviewEntry(name: "levelname.txt", isDirectory: false), DirectoryEntry(name: "levelname.txt", isDirectory: false),
DirectoryPreviewEntry(name: "world_icon.jpeg", isDirectory: false), DirectoryEntry(name: "world_icon.jpeg", isDirectory: false),
DirectoryPreviewEntry(name: "resource_packs", isDirectory: true) DirectoryEntry(name: "resource_packs", isDirectory: true)
] ]
} }

View File

@ -19,7 +19,7 @@ struct ContentView: View {
@State private var isPerformingItemAction = false @State private var isPerformingItemAction = false
@State private var isShowingDeviceSourceSheet = false @State private var isShowingDeviceSourceSheet = false
@State private var sortMode: ItemSortMode = .name @State private var sortMode: ItemSortMode = .name
@State private var directoryPreviewContents: [DirectoryPreviewEntry] = [] @State private var directoryPreviewContents: [DirectoryEntry] = []
private let connectedDeviceAccess: AppleMobileDeviceSourceAccess private let connectedDeviceAccess: AppleMobileDeviceSourceAccess
private let deviceSourceFactory: ConnectedDeviceSourceFactory private let deviceSourceFactory: ConnectedDeviceSourceFactory