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
3 changes: 3 additions & 0 deletions CleanMac/Models/CleanMacModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ enum CleanMacSection: String, CaseIterable, Identifiable {
case results
case diskAnalysis
case duplicates
case shredder
case applications
case settings

Expand All @@ -19,6 +20,7 @@ enum CleanMacSection: String, CaseIterable, Identifiable {
case .results: L.t("section.results")
case .diskAnalysis: L.t("section.diskAnalysis")
case .duplicates: L.t("section.duplicates")
case .shredder: L.t("section.shredder")
case .applications: L.t("section.applications")
case .settings: L.t("section.settings")
}
Expand All @@ -31,6 +33,7 @@ enum CleanMacSection: String, CaseIterable, Identifiable {
case .results: "checklist"
case .diskAnalysis: "chart.pie.fill"
case .duplicates: "square.on.square"
case .shredder: "terminal.fill"
case .applications: "app.badge.checkmark"
case .settings: "gearshape"
}
Expand Down
20 changes: 20 additions & 0 deletions CleanMac/Support/ShredderWorkspaceService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import AppKit

@MainActor
enum ShredderWorkspaceService {
static func chooseFiles() -> [URL] {
let panel = NSOpenPanel()
panel.canChooseFiles = true
panel.canChooseDirectories = false
panel.allowsMultipleSelection = true
panel.canCreateDirectories = false
panel.treatsFilePackagesAsDirectories = false
panel.prompt = L.t("shredder.picker.confirm")
panel.message = L.t("shredder.picker.message")

guard panel.runModal() == .OK else {
return []
}
return panel.urls.map(\.standardizedFileURL)
}
}
62 changes: 20 additions & 42 deletions CleanMac/Views/DiskAnalysisProgressIndicator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,33 @@ struct DiskAnalysisProgressIndicator: View {
@Environment(\.accessibilityReduceMotion) private var reduceMotion

var body: some View {
TimelineView(.animation(minimumInterval: 1 / 30, paused: reduceMotion)) { timeline in
let phase = reduceMotion ? 0.15 : timeline.date.timeIntervalSinceReferenceDate
let rotation = Angle.degrees(phase.truncatingRemainder(dividingBy: 2.4) / 2.4 * 360)
let pulse = reduceMotion ? 1.0 : 0.92 + (sin(phase * 3.2) + 1) * 0.04

ZStack {
Circle()
.fill(
RadialGradient(
colors: [
Color.accentColor.opacity(0.2),
Color.cyan.opacity(0.08),
.clear
],
center: .center,
startRadius: 2,
endRadius: 30
)
ZStack {
Circle()
.fill(
RadialGradient(
colors: [
Color.accentColor.opacity(0.2),
Color.cyan.opacity(0.08),
.clear
],
center: .center,
startRadius: 2,
endRadius: 30
)
.scaleEffect(pulse)

Circle()
.stroke(Color.accentColor.opacity(0.14), lineWidth: 5)
)

Circle()
.trim(from: 0.06, to: 0.7)
.stroke(
AngularGradient(
colors: [.blue, .cyan, .purple, .blue],
center: .center
),
style: StrokeStyle(lineWidth: 5, lineCap: .round)
)
.rotationEffect(rotation)
.shadow(color: Color.accentColor.opacity(0.32), radius: 5)

Circle()
.trim(from: 0.08, to: 0.38)
.stroke(
Color.white.opacity(0.8),
style: StrokeStyle(lineWidth: 2, lineCap: .round)
)
.rotationEffect(.degrees(-rotation.degrees * 0.72))
Circle()
.stroke(Color.accentColor.opacity(0.18), lineWidth: 5)

if reduceMotion {
Image(systemName: "chart.pie.fill")
.font(.system(size: 16, weight: .semibold))
.symbolRenderingMode(.hierarchical)
.foregroundStyle(.tint)
.scaleEffect(pulse)
} else {
ProgressView()
.controlSize(.regular)
.tint(.accentColor)
}
}
.frame(width: 58, height: 58)
Expand Down
7 changes: 5 additions & 2 deletions CleanMac/Views/DiskAnalysisView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -644,8 +644,11 @@ struct DiskAnalysisView: View {
problemMessage = nil

let minimumLargeFileSize = LargeFileThreshold.megabytes50.bytes
let progressChannel = AsyncStream.makeStream(of: DiskAnalysisProgress.self)
let worker = Task.detached(priority: .userInitiated) {
let progressChannel = AsyncStream.makeStream(
of: DiskAnalysisProgress.self,
bufferingPolicy: .bufferingNewest(1)
)
let worker = Task.detached(priority: .utility) {
defer { progressChannel.continuation.finish() }
return try DiskAnalyzer().scan(
root: rootURL,
Expand Down
7 changes: 5 additions & 2 deletions CleanMac/Views/DuplicateFinderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -490,8 +490,11 @@ struct DuplicateFinderView: View {
)

let mode: DuplicateScanMode = includeLargeFiles ? .includeLargeFiles : .standard
let progressChannel = AsyncStream.makeStream(of: DuplicateScanProgress.self)
let worker = Task.detached(priority: .userInitiated) {
let progressChannel = AsyncStream.makeStream(
of: DuplicateScanProgress.self,
bufferingPolicy: .bufferingNewest(1)
)
let worker = Task.detached(priority: .utility) {
defer { progressChannel.continuation.finish() }
return try await DuplicateFinder().scan(
root: rootURL,
Expand Down
11 changes: 9 additions & 2 deletions CleanMac/Views/MainWindowView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ struct MainWindowView: View {
.navigationTitle(selectedSection.title)
.toolbar {
ToolbarItemGroup {
if selectedSection != .diskAnalysis, selectedSection != .duplicates {
if selectedSection != .diskAnalysis,
selectedSection != .duplicates,
selectedSection != .shredder {
Button {
runScan()
} label: {
Expand Down Expand Up @@ -132,6 +134,8 @@ struct MainWindowView: View {
DiskAnalysisView()
case .duplicates:
DuplicateFinderView()
case .shredder:
ShredderView()
case .applications:
ApplicationsView()
case .settings:
Expand Down Expand Up @@ -196,7 +200,10 @@ struct MainWindowView: View {
let scanStartedAt = Date()

Task {
let progressChannel = AsyncStream.makeStream(of: CleanupScanProgress.self)
let progressChannel = AsyncStream.makeStream(
of: CleanupScanProgress.self,
bufferingPolicy: .bufferingNewest(1)
)
let scanTask = Task.detached(priority: .userInitiated) {
let report = CleanupScanner().scan(categories: categories) { progress in
progressChannel.continuation.yield(progress)
Expand Down
Loading