Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 8 additions & 18 deletions Radix/App/RadixCommands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ struct RadixCommands: Commands {
@FocusedValue(\.chartViewportAction) private var chartViewportAction

var body: some Commands {
let selectionIsInDiscardPile = appModel.selectionIsInDiscardPile
let discardPileSelectionTitle: LocalizedStringKey = selectionIsInDiscardPile
? "Review Discard Pile"
: "Add to Discard Pile"
let selectedActionAvailability = FileNodeActionAvailability(
nodes: navigation.selectedNodes,
activeTarget: scanState.selectedTarget,
Expand Down Expand Up @@ -174,8 +178,8 @@ struct RadixCommands: Commands {

Divider()

Button(addSelectionToDiscardPileTitle, systemImage: "checklist") {
if appModel.selectionIncludesHiddenNodes {
Button(discardPileSelectionTitle, systemImage: "checklist") {
if selectionIsInDiscardPile {
appModel.presentDiscardPileReview()
} else {
appModel.addSelectedNodesToDiscardPile()
Expand All @@ -184,7 +188,7 @@ struct RadixCommands: Commands {
.keyboardShortcut("l", modifiers: [.command, .shift])
.disabled(
!canUseWorkspaceCommands ||
(!appModel.selectionIncludesHiddenNodes
(!selectionIsInDiscardPile
&& !selectedActionAvailability.canMoveToTrash)
)

Expand Down Expand Up @@ -268,20 +272,6 @@ struct RadixCommands: Commands {
&& appModel.presentationCoordinator.activeDestination == nil
}

private var addSelectionToDiscardPileTitle: String {
if appModel.selectionIncludesHiddenNodes {
return String(
localized: "Review Discard Pile",
comment: "Command for reviewing the Discard Pile when the selection is already included."
)
}
let count = navigation.selectedNodeIDs.count
guard count > 1 else {
return String(localized: "Add to Discard Pile", comment: "Action for marking one selected item for possible deletion.")
}
return String(localized: "Add \(count) Items to Discard Pile", comment: "Action for marking multiple selected items for possible deletion.")
}

private var commandSelectedFileActions: SelectedFileActions {
SelectedFileActions(
quickLook: { appModel.toggleQuickLookForSelected() },
Expand All @@ -307,7 +297,7 @@ struct RadixCommands: Commands {
!canUseWorkspaceCommands ||
!action.isEnabled(in: availability) ||
(action == .moveToTrash &&
(appModel.selectionIncludesHiddenNodes || isFileListSearchActive == true))
(appModel.directTrashOverlapsProtectedNodes(navigation.selectedNodes) || isFileListSearchActive == true))
)
}
}
7 changes: 2 additions & 5 deletions Radix/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ struct ContentView: View {
),
maxRenderedDepth: appModel.maxRenderedDepth,
showFreeSpaceInDiskMaps: appModel.showFreeSpaceInDiskMaps,
workspaceHiddenNodeIDs: appModel.workspaceHiddenNodeIDs,
discardPileRootNodeIDs: appModel.discardPileRootNodeIDs,
movingToTrashRootNodeIDs: appModel.movingToTrashRootNodeIDs,
startupDiskTarget: appModel.startupDiskTarget,
Expand Down Expand Up @@ -172,7 +171,7 @@ struct ContentView: View {
GoToPathSheet(
resolver: ScanPathResolver(
treeStore: snapshot.treeStore,
hiddenNodeIDs: appModel.workspaceHiddenNodeIDs,
hiddenNodeIDs: appModel.movingToTrashRootNodeIDs,
baseDirectoryID: request.directoryID
),
onCancel: { appModel.dismissGoToPath() },
Expand Down Expand Up @@ -660,7 +659,6 @@ private struct WorkspaceDetailView: View {

let maxRenderedDepth: Int
let showFreeSpaceInDiskMaps: Bool
let workspaceHiddenNodeIDs: Set<FileNodeRecord.ID>
let discardPileRootNodeIDs: Set<FileNodeRecord.ID>
let movingToTrashRootNodeIDs: Set<FileNodeRecord.ID>
let startupDiskTarget: ScanTarget?
Expand All @@ -686,7 +684,6 @@ private struct WorkspaceDetailView: View {
visualizationMode: $visualizationMode,
maxRenderedDepth: maxRenderedDepth,
showFreeSpaceInDiskMaps: showFreeSpaceInDiskMaps,
workspaceHiddenNodeIDs: workspaceHiddenNodeIDs,
discardPileRootNodeIDs: discardPileRootNodeIDs,
movingToTrashRootNodeIDs: movingToTrashRootNodeIDs,
startupDiskTarget: startupDiskTarget,
Expand Down Expand Up @@ -814,7 +811,6 @@ private extension ContentView {
selectAndFocusNodeAfterViewUpdate: { appModel.selectAndFocusAfterViewUpdate(nodeID: $0) },
expandSummarizedNode: { appModel.expandSummarizedNode($0) {} },
selectedFileActions: primarySelectedFileActions,
addPrimarySelectionToDiscardPile: { appModel.addPrimarySelectionToDiscardPileAfterViewUpdate() },
removeDiscardPileNode: { appModel.removeDiscardPileNode(id: $0) },
presentDiscardPileReview: { appModel.presentDiscardPileReview() },
bulkFileActions: bulkFileActions,
Expand Down Expand Up @@ -849,6 +845,7 @@ private extension ContentView {
revealInFinder: { appModel.revealNodesInFinder($0) },
copyPaths: { appModel.copyPaths(for: $0) },
addToDiscardPile: { appModel.addNodesToDiscardPile($0) },
directTrashOverlapsProtectedNodes: { appModel.directTrashOverlapsProtectedNodes($0) },
moveToTrash: { appModel.requestMoveNodesToTrash($0) }
)
}
Expand Down
25 changes: 23 additions & 2 deletions Radix/Features/FileList/FileBrowserNameCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import SwiftUI
struct FileBrowserNameCell: View {
let node: FileNodeRecord
let subtitleOverride: String?
let isInDiscardPile: Bool
let isExpanding: Bool
let expandAction: () -> Void
@Binding var presentedSharedStorageNodeID: FileNodeRecord.ID?
Expand All @@ -25,8 +26,15 @@ struct FileBrowserNameCell: View {
.foregroundStyle(node.isDirectory || node.isSynthetic ? Color.accentColor : Color.secondary)

VStack(alignment: .leading, spacing: 2) {
Text(node.displayName)
.lineLimit(1)
if isInDiscardPile {
ViewThatFits(in: .horizontal) {
nameWithDiscardPileBadge(style: TitleAndIconLabelStyle())
nameWithDiscardPileBadge(style: IconOnlyLabelStyle())
}
} else {
Text(node.displayName)
.lineLimit(1)
}

if let statusText = subtitleOverride ?? node.secondaryStatusText {
Text(statusText)
Expand Down Expand Up @@ -59,6 +67,19 @@ struct FileBrowserNameCell: View {
.accessibilityElement(children: .contain)
}

private func nameWithDiscardPileBadge(style: some LabelStyle) -> some View {
HStack(spacing: 6) {
Text(node.displayName)
.lineLimit(1)
Label("In Discard Pile", systemImage: "checklist")
.labelStyle(style)
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize()
.help("In Discard Pile")
}
}

private var fileBrowserSystemImageName: String {
node.cloneIdentity == nil ? node.systemImageName : "doc.on.doc"
}
Expand Down
49 changes: 38 additions & 11 deletions Radix/Features/FileList/FileBrowserTableView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct FileBrowserTableView: View {
@ObservedObject var navigation: WorkspaceNavigationModel
@FocusState.Binding var focusedWorkspaceTarget: WorkspaceFocusTarget?
let hiddenNodeIDs: Set<FileNodeRecord.ID>
let discardPileRootNodeIDs: Set<FileNodeRecord.ID>
let fileDragController: FileDragController
let actions: FileBrowserActions

Expand All @@ -34,6 +35,7 @@ struct FileBrowserTableView: View {
navigation: WorkspaceNavigationModel,
focusedWorkspaceTarget: FocusState<WorkspaceFocusTarget?>.Binding,
hiddenNodeIDs: Set<FileNodeRecord.ID>,
discardPileRootNodeIDs: Set<FileNodeRecord.ID>,
fileDragController: FileDragController,
actions: FileBrowserActions,
model: @autoclosure @escaping () -> FileBrowserModel = FileBrowserModel()
Expand All @@ -42,6 +44,7 @@ struct FileBrowserTableView: View {
self.navigation = navigation
self._focusedWorkspaceTarget = focusedWorkspaceTarget
self.hiddenNodeIDs = hiddenNodeIDs
self.discardPileRootNodeIDs = discardPileRootNodeIDs
self.fileDragController = fileDragController
self.actions = actions
_model = StateObject(wrappedValue: model())
Expand Down Expand Up @@ -176,12 +179,19 @@ struct FileBrowserTableView: View {
let tourFolderID = tourFolder?.id
let tour = self.tour
let tourPresentation = self.tourPresentation
let tree = scanState.fileTreeStore
let markedNodes = discardPileRootNodeIDs.isEmpty ? nil : tree?.preparedNodeSet(for: discardPileRootNodeIDs)
func isInDiscardPile(_ id: FileNodeRecord.ID) -> Bool {
guard let markedNodes, let tree else { return false }
return tree.isNodeOrDescendant(id, of: markedNodes)
}

return Table(of: FileNodeRecord.self, selection: tableSelection, sortOrder: sortOrderBinding) {
TableColumn("Name", sortUsing: FileNodeTableComparator(field: .name)) { node in
FileBrowserNameCell(
node: node,
subtitleOverride: subtitle(for: node),
isInDiscardPile: isInDiscardPile(node.id),
isExpanding: isExpanding(node),
expandAction: { expandSummarizedNode(node) },
presentedSharedStorageNodeID: $presentedSharedStorageNodeID
Expand Down Expand Up @@ -243,7 +253,7 @@ struct FileBrowserTableView: View {
.accessibilityLabel("Contents table")
.accessibilityHint("Select a row to inspect it. Double-click a folder to zoom in, or a summarized folder to expand it. Press Space for Quick Look.")
.contextMenu(forSelectionType: FileNodeRecord.ID.self) { selectedIDs in
fileContextMenu(for: selectedIDs)
fileContextMenu(for: selectedIDs, isInDiscardPile: isInDiscardPile)
} primaryAction: { selectedIDs in
performPrimaryAction(for: selectedIDs)
}
Expand Down Expand Up @@ -336,18 +346,26 @@ struct FileBrowserTableView: View {
}

@ViewBuilder
private func fileContextMenu(for selectedIDs: Set<FileNodeRecord.ID>) -> some View {
private func fileContextMenu(
for selectedIDs: Set<FileNodeRecord.ID>,
isInDiscardPile: (FileNodeRecord.ID) -> Bool
) -> some View {
if let selection = selectionContext(for: selectedIDs) {
let canAddToDiscardPile = selection.actionAvailability.canMoveToTrash
&& !selection.ids.allSatisfy(isInDiscardPile)
if selection.nodes.count > 1 {
bulkFileContextMenu(for: selection)
bulkFileContextMenu(for: selection, canAddToDiscardPile: canAddToDiscardPile)
} else {
singleFileContextMenu(for: selection)
singleFileContextMenu(for: selection, canAddToDiscardPile: canAddToDiscardPile)
}
}
}

@ViewBuilder
private func singleFileContextMenu(for selection: FileBrowserSelectionContext) -> some View {
private func singleFileContextMenu(
for selection: FileBrowserSelectionContext,
canAddToDiscardPile: Bool
) -> some View {
if let node = selection.primaryNode,
let id = selection.primaryID {
fileActionButton(.quickLook, availability: selection.actionAvailability, selectedID: selection.id)
Expand Down Expand Up @@ -391,7 +409,7 @@ struct FileBrowserTableView: View {
actions.selectNode(id)
actions.bulkFileActions.addToDiscardPile([node])
}
.disabled(!selection.actionAvailability.canMoveToTrash)
.disabled(!canAddToDiscardPile)

Button(
FileNodeAction.moveToTrash.title,
Expand All @@ -401,15 +419,21 @@ struct FileBrowserTableView: View {
actions.selectNode(id)
actions.bulkFileActions.moveToTrash([node])
}
.disabled(!FileNodeAction.moveToTrash.isEnabled(in: selection.actionAvailability))
.disabled(
!selection.actionAvailability.canMoveToTrash
|| actions.bulkFileActions.directTrashOverlapsProtectedNodes(selection.nodes)
)

fileActionButton(.copyPath, availability: selection.actionAvailability, selectedID: selection.id)
.labelStyle(.titleOnly)
}
}

@ViewBuilder
private func bulkFileContextMenu(for selection: FileBrowserSelectionContext) -> some View {
private func bulkFileContextMenu(
for selection: FileBrowserSelectionContext,
canAddToDiscardPile: Bool
) -> some View {
Button(FileNodeAction.quickLook.title, systemImage: FileNodeAction.quickLook.systemImageName) {
actions.selectNodes(selection.ids, selection.primaryID)
actions.selectedFileActions.quickLook()
Expand All @@ -430,17 +454,20 @@ struct FileBrowserTableView: View {

Divider()

Button("Add \(selection.nodes.count) Items to Discard Pile", systemImage: "checklist") {
Button("Add to Discard Pile", systemImage: "checklist") {
actions.selectNodes(selection.ids, selection.primaryID)
actions.bulkFileActions.addToDiscardPile(selection.nodes)
}
.disabled(!selection.actionAvailability.canMoveToTrash)
.disabled(!canAddToDiscardPile)

Button("Move \(selection.nodes.count) Items to Trash", systemImage: FileNodeAction.moveToTrash.systemImageName, role: .destructive) {
actions.selectNodes(selection.ids, selection.primaryID)
actions.bulkFileActions.moveToTrash(selection.nodes)
}
.disabled(!selection.actionAvailability.canMoveToTrash)
.disabled(
!selection.actionAvailability.canMoveToTrash
|| actions.bulkFileActions.directTrashOverlapsProtectedNodes(selection.nodes)
)
}

private func performPrimaryAction(for selectedIDs: Set<FileNodeRecord.ID>) {
Expand Down
14 changes: 4 additions & 10 deletions Radix/Features/Inspector/InspectorMultiSelectionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ struct InspectorMultiSelectionView: View {
let summary: InspectorSelectionSummary
let percentOfScan: String
let availability: FileNodeActionAvailability
let canAddSelectionToDiscardPile: Bool
let canMoveSelectionToTrash: Bool
let availabilityNotice: InspectorAvailabilityNoticeKind?
let warnings: [ScanWarning]
Expand Down Expand Up @@ -49,15 +50,15 @@ struct InspectorMultiSelectionView: View {
.formStyle(.grouped)
.contentMargins(.horizontal, 0, for: .scrollContent)

if availability.canRevealInFinder || canMoveSelectionToTrash {
if availability.canRevealInFinder || canAddSelectionToDiscardPile {
InspectorActionBar(
revealAction: availability.canRevealInFinder
? { actions.revealInFinder(summary.selectedNodes) }
: nil,
discardPileAction: canMoveSelectionToTrash
discardPileAction: canAddSelectionToDiscardPile
? { actions.addToDiscardPile(summary.topLevelSelectedNodes) }
: nil,
discardPileTitle: addToDiscardPileTitle,
discardPileTitle: String(localized: "Add to Discard Pile"),
discardPileSystemImageName: "checklist"
)
}
Expand Down Expand Up @@ -178,13 +179,6 @@ struct InspectorMultiSelectionView: View {
)
}

private var addToDiscardPileTitle: String {
String(
localized: "Add \(summary.topLevelSelectedCount) Items to Discard Pile",
comment: "Action for marking one or more effective top-level selected items for possible deletion."
)
}

private var moveToTrashTitle: String {
String(
localized: "Move \(summary.topLevelSelectedCount) Items to Trash",
Expand Down
Loading