mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 07:19:04 +00:00
v3.7.0
This commit is contained in:
@@ -33,16 +33,7 @@ struct AppCommands: Commands {
|
||||
|
||||
Button {
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
// Refresh Apps list
|
||||
updateOnMain {
|
||||
appState.reload.toggle()
|
||||
}
|
||||
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
appState.sortedApps = []
|
||||
appState.sortedApps = sortedApps
|
||||
appState.reload.toggle()
|
||||
}
|
||||
reloadAppsList(appState: appState, fsm: fsm)
|
||||
}
|
||||
} label: {
|
||||
Text("Refresh Apps")
|
||||
@@ -65,12 +56,9 @@ struct AppCommands: Commands {
|
||||
Button
|
||||
{
|
||||
undoTrash(appState: appState) {
|
||||
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
appState.sortedApps = sortedApps
|
||||
for app in appState.trashedFiles {
|
||||
AppPathFinder(appInfo: app, appState: appState, locations: locations, undo: true).findPaths()
|
||||
}
|
||||
reloadAppsList(appState: appState, fsm: fsm)
|
||||
for app in appState.trashedFiles {
|
||||
AppPathFinder(appInfo: app, appState: appState, locations: locations, undo: true).findPaths()
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
|
||||
@@ -46,22 +46,19 @@ class AppInfoFetcher {
|
||||
return nil
|
||||
}
|
||||
|
||||
// let appName = bundle.infoDictionary?["CFBundleDisplayName"] as? String ?? bundle.infoDictionary?["CFBundleName"] as? String
|
||||
// ?? bundle.localizedInfoDictionary?[kCFBundleNameKey as String] as? String ?? path.deletingPathExtension().lastPathComponent
|
||||
|
||||
let appName = wrapped ? path.deletingLastPathComponent().deletingLastPathComponent().deletingPathExtension().lastPathComponent.capitalizingFirstLetter() : path.deletingPathExtension().lastPathComponent.capitalizingFirstLetter()
|
||||
|
||||
let appVersion = bundle.infoDictionary?["CFBundleShortVersionString"] as? String
|
||||
?? bundle.infoDictionary?["CFBundleVersion"] as? String
|
||||
?? ""
|
||||
let appVersion = (bundle.infoDictionary?["CFBundleShortVersionString"] as? String)?.isEmpty ?? true
|
||||
? bundle.infoDictionary?["CFBundleVersion"] as? String ?? ""
|
||||
: bundle.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
|
||||
|
||||
let appIcon = fetchAppIcon(for: path, wrapped: wrapped)
|
||||
let webApp = (bundle.infoDictionary?["LSTemplateApplication"] as? Bool ?? false || bundle.infoDictionary?["CFBundleExecutable"] as? String == "app_mode_loader")
|
||||
|
||||
let webApp = bundle.infoDictionary?["LSTemplateApplication"] as? Bool ?? false
|
||||
let system = !path.path.contains(NSHomeDirectory())
|
||||
|
||||
return AppInfo(id: UUID(), path: path, bundleIdentifier: bundleIdentifier, appName: appName, appVersion: appVersion, appIcon: appIcon,
|
||||
webApp: webApp, wrapped: wrapped, system: system, files: [], fileSize: [:], fileSizeLogical: [:], fileIcon: [:])
|
||||
webApp: webApp, wrapped: wrapped, system: system, bundleSize: 0, files: [], fileSize: [:], fileSizeLogical: [:], fileIcon: [:])
|
||||
}
|
||||
|
||||
private static func fetchAppIcon(for path: URL, wrapped: Bool) -> NSImage? {
|
||||
|
||||
@@ -47,6 +47,7 @@ class AppState: ObservableObject {
|
||||
webApp: false,
|
||||
wrapped: false,
|
||||
system: false,
|
||||
bundleSize: 0,
|
||||
files: [],
|
||||
fileSize: [:],
|
||||
fileSizeLogical: [:],
|
||||
@@ -98,6 +99,7 @@ struct AppInfo: Identifiable, Equatable, Hashable {
|
||||
let webApp: Bool
|
||||
let wrapped: Bool
|
||||
let system: Bool
|
||||
var bundleSize: Int64
|
||||
var files: [URL]
|
||||
var fileSize: [URL:Int64]
|
||||
var fileSizeLogical: [URL:Int64]
|
||||
@@ -112,7 +114,7 @@ struct AppInfo: Identifiable, Equatable, Hashable {
|
||||
}
|
||||
|
||||
|
||||
static let empty = AppInfo(id: UUID(), path: URL(fileURLWithPath: ""), bundleIdentifier: "", appName: "", appVersion: "", appIcon: nil, webApp: false, wrapped: false, system: false, files: [], fileSize: [:], fileSizeLogical: [:], fileIcon: [:])
|
||||
static let empty = AppInfo(id: UUID(), path: URL(fileURLWithPath: ""), bundleIdentifier: "", appName: "", appVersion: "", appIcon: nil, webApp: false, wrapped: false, system: false, bundleSize: 0, files: [], fileSize: [:], fileSizeLogical: [:], fileIcon: [:])
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,6 @@ func getSortedApps(paths: [String], appState: AppState) -> [AppInfo] {
|
||||
// Get app info and sort
|
||||
let sortedApps = apps
|
||||
.compactMap { AppInfoFetcher.getAppInfo(atPath: $0) }
|
||||
.sorted { $0.appName.replacingOccurrences(of: ".", with: "").lowercased() < $1.appName.replacingOccurrences(of: ".", with: "").lowercased() }
|
||||
|
||||
|
||||
return sortedApps
|
||||
}
|
||||
|
||||
@@ -570,13 +570,7 @@ struct AnimatedSearchStyle: TextFieldStyle {
|
||||
.foregroundStyle(isHovered ? Color("mode").opacity(0.8) : Color("mode").opacity(0.5))
|
||||
.onTapGesture {
|
||||
withAnimation {
|
||||
// Refresh Apps list
|
||||
appState.reload.toggle()
|
||||
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
appState.sortedApps = sortedApps
|
||||
appState.reload.toggle()
|
||||
}
|
||||
reloadAppsList(appState: appState, fsm: fsm)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,19 +27,25 @@ func updateOnMain(after delay: Double? = nil, _ updates: @escaping () -> Void) {
|
||||
|
||||
// Execute functions on background thread
|
||||
func updateOnBackground(_ updates: @escaping () -> Void) {
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
updates()
|
||||
}
|
||||
}
|
||||
|
||||
// Reload apps list
|
||||
func reloadAppsList(appState: AppState, fsm: FolderSettingsManager) {
|
||||
appState.reload = true
|
||||
updateOnBackground {
|
||||
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
|
||||
// Update UI on the main thread
|
||||
updateOnMain {
|
||||
appState.sortedApps = sortedApps
|
||||
appState.reload = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Resize window
|
||||
//func resizeWindow(width: CGFloat, height: CGFloat) {
|
||||
// if let window = NSApplication.shared.windows.first {
|
||||
// let newSize = NSSize(width: width, height: height)
|
||||
// window.setContentSize(newSize)
|
||||
// }
|
||||
//}
|
||||
|
||||
func resizeWindowAuto(windowSettings: WindowSettings, title: String) {
|
||||
if let window = NSApplication.shared.windows.first(where: { $0.title == title }) {
|
||||
@@ -160,16 +166,26 @@ func caskCleanup(app: String) {
|
||||
#elseif arch(arm64)
|
||||
let cmd = "/opt/homebrew/bin/brew"
|
||||
#endif
|
||||
let formattedApp = app.lowercased().replacingOccurrences(of: " ", with: "-")
|
||||
process.executableURL = URL(fileURLWithPath: "/bin/zsh")
|
||||
process.arguments = ["-c", "\(cmd) uninstall --cask \(app) --force; \(cmd) cleanup"]
|
||||
// let pipe = Pipe()
|
||||
process.standardOutput = FileHandle.nullDevice//pipe
|
||||
process.standardError = FileHandle.nullDevice//pipe
|
||||
// process.arguments = ["-c", "\(cmd) uninstall --cask \"\(formattedApp)\" --force; \(cmd) cleanup"]
|
||||
process.arguments = ["-c", """
|
||||
found_cask=$(\(cmd) list --cask | grep "\(formattedApp)")
|
||||
if [ -n "$found_cask" ]; then
|
||||
\(cmd) uninstall --cask "$found_cask" --force;
|
||||
\(cmd) cleanup;
|
||||
else
|
||||
echo "Cask not found for \(formattedApp)";
|
||||
fi
|
||||
"""]
|
||||
let pipe = Pipe()
|
||||
process.standardOutput = pipe
|
||||
process.standardError = pipe
|
||||
try? process.run()
|
||||
process.waitUntilExit() // Ensure the process completes
|
||||
// let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||
// let output = (String(data: data, encoding: .utf8) ?? "") as String
|
||||
// print(output)
|
||||
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||
let output = (String(data: data, encoding: .utf8) ?? "") as String
|
||||
print(output)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,6 +515,7 @@ func formatByte(size: Int64) -> (human: String, byte: String) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Only process supported files
|
||||
func isSupportedFileType(at path: String) -> Bool {
|
||||
let fileManager = FileManager.default
|
||||
|
||||
@@ -88,9 +88,9 @@ struct PearcleanerApp: App {
|
||||
// Disable tabbing
|
||||
NSWindow.allowsAutomaticWindowTabbing = false
|
||||
|
||||
// Get Apps
|
||||
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
|
||||
appState.sortedApps = sortedApps
|
||||
// Load apps list on startup
|
||||
reloadAppsList(appState: appState, fsm: fsm)
|
||||
|
||||
|
||||
// Enable menubar item
|
||||
if menubarEnabled {
|
||||
|
||||
@@ -24,10 +24,10 @@ struct AppListItems: View {
|
||||
@EnvironmentObject var locations: Locations
|
||||
let itemId = UUID()
|
||||
let appInfo: AppInfo
|
||||
var isSelected: Bool {
|
||||
appState.appInfo.path == appInfo.path
|
||||
}
|
||||
var isSelected: Bool { appState.appInfo.path == appInfo.path }
|
||||
@State private var hoveredItemPath: URL? = nil
|
||||
@State private var bundleSize: Int64 = 0
|
||||
|
||||
var body: some View {
|
||||
|
||||
ZStack() {
|
||||
@@ -40,9 +40,7 @@ struct AppListItems: View {
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 30)
|
||||
// .frame(width: (isHovered || isSelected) ? 32 : 30, height: (isHovered || isSelected) ? 32 : 30)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
// .shadow(color: Color("pop").opacity(0.8), radius: isSelected ? 2 : 0)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -80,17 +78,13 @@ struct AppListItems: View {
|
||||
)
|
||||
}
|
||||
|
||||
Text(appInfo.appVersion)
|
||||
.font(.system(size: (isHovered || isSelected) ? 12 : 10))
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
|
||||
// if (isHovered || isSelected) {
|
||||
// RoundedRectangle(cornerRadius: 50)
|
||||
// .fill(isSelected ? Color("pear") : Color("mode").opacity(0.5))
|
||||
// .frame(width: isSelected ? 4 : 2, height: 25)
|
||||
// .padding(.trailing, 5)
|
||||
// }
|
||||
|
||||
if bundleSize == 0 {
|
||||
ProgressView().controlSize(.mini)
|
||||
} else {
|
||||
Text("\(isHovered ? "v\(appInfo.appVersion)" : formatByte(size: bundleSize).human)")
|
||||
.font(.system(size: (isHovered || isSelected) ? 12 : 10))
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -99,7 +93,7 @@ struct AppListItems: View {
|
||||
.padding(.vertical, 5)
|
||||
.help(appInfo.appName)
|
||||
.onHover { hovering in
|
||||
withAnimation(Animation.easeIn(duration: 0.2)) {
|
||||
withAnimation(Animation.easeIn(duration: 0.3)) {
|
||||
self.isHovered = hovering
|
||||
self.hoveredItemPath = isHovered ? appInfo.path : nil
|
||||
}
|
||||
@@ -132,6 +126,19 @@ struct AppListItems: View {
|
||||
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
let size = totalSizeOnDisk(for: appInfo.path).logical
|
||||
DispatchQueue.main.async {
|
||||
self.bundleSize = size
|
||||
|
||||
// Optionally, update the appInfo in the appState array
|
||||
if let index = appState.sortedApps.firstIndex(where: { $0.path == appInfo.path }) {
|
||||
appState.sortedApps[index].bundleSize = size
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ struct AppSearchView: View {
|
||||
@State private var showSys: Bool = true
|
||||
@State private var showUsr: Bool = true
|
||||
@Binding var isMenuBar: Bool
|
||||
@AppStorage("settings.general.selectedSortAppsList") var selectedSortAlpha: Bool = true
|
||||
@State private var progress: Double = 0.0
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .center, spacing: 0) {
|
||||
@@ -53,17 +56,11 @@ struct AppSearchView: View {
|
||||
if search.isEmpty {
|
||||
Button("Refresh") {
|
||||
withAnimation {
|
||||
// Refresh Apps list
|
||||
appState.reload.toggle()
|
||||
showPopover = false
|
||||
let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
appState.sortedApps = sortedApps
|
||||
appState.reload.toggle()
|
||||
}
|
||||
reloadAppsList(appState: appState, fsm: fsm)
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "arrow.uturn.backward.circle", help: "Refresh apps (⌘+R)", size: 16))
|
||||
.buttonStyle(SimpleButtonStyle(icon: "arrow.counterclockwise.circle", help: "Refresh apps (⌘+R)", size: 16))
|
||||
}
|
||||
|
||||
SearchBar(search: $search, darker: (mini || menubarEnabled) ? false : true, glass: glass)
|
||||
@@ -77,6 +74,13 @@ struct AppSearchView: View {
|
||||
.popover(isPresented: $showMenu) {
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
Button("") {
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
selectedSortAlpha.toggle()
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "circle.fill", label: "App List Sorting", help: "Can also click on User/System headers to toggle this", size: 5))
|
||||
|
||||
if mini && !menubarEnabled {
|
||||
Button("") {
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
@@ -142,12 +146,21 @@ struct AppSearchView: View {
|
||||
}
|
||||
|
||||
private var filteredApps: [AppInfo] {
|
||||
let apps: [AppInfo]
|
||||
if search.isEmpty {
|
||||
return appState.sortedApps
|
||||
apps = appState.sortedApps
|
||||
} else {
|
||||
return appState.sortedApps.filter { $0.appName.localizedCaseInsensitiveContains(search) }
|
||||
apps = appState.sortedApps.filter { $0.appName.localizedCaseInsensitiveContains(search) }
|
||||
}
|
||||
|
||||
switch selectedSortAlpha {
|
||||
case true:
|
||||
return apps.sorted { $0.appName.replacingOccurrences(of: ".", with: "").lowercased() < $1.appName.replacingOccurrences(of: ".", with: "").lowercased() }
|
||||
case false:
|
||||
return apps.sorted { $0.bundleSize > $1.bundleSize }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ struct AppsListView: View {
|
||||
@Binding var search: String
|
||||
@Binding var showPopover: Bool
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
@AppStorage("settings.general.selectedSort") var selectedSortAlpha: Bool = true
|
||||
|
||||
var filteredApps: [AppInfo]
|
||||
|
||||
@@ -62,12 +63,12 @@ struct SectionView: View {
|
||||
struct Header: View {
|
||||
let title: String
|
||||
let count: Int
|
||||
// @State private var hovered = false
|
||||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var locations: Locations
|
||||
@EnvironmentObject var fsm: FolderSettingsManager
|
||||
@Binding var showPopover: Bool
|
||||
@AppStorage("settings.general.glass") private var glass: Bool = true
|
||||
@AppStorage("settings.general.selectedSortAppsList") var selectedSortAlpha: Bool = true
|
||||
|
||||
|
||||
var body: some View {
|
||||
@@ -87,6 +88,10 @@ struct Header: View {
|
||||
}
|
||||
.frame(minHeight: 20)
|
||||
.padding(5)
|
||||
.help("Click header to change sorting order")
|
||||
.onTapGesture {
|
||||
selectedSortAlpha.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ struct FilesView: View {
|
||||
HStack {
|
||||
Text("\(appState.appInfo.appName)").font(.title).fontWeight(.bold).lineLimit(1)
|
||||
Text("•").foregroundStyle(Color("AccentColor"))
|
||||
Text("\(appState.appInfo.appVersion)").font(.title3)
|
||||
Text("v\(appState.appInfo.appVersion)").font(.title3)
|
||||
if appState.appInfo.appName.count < 5 {
|
||||
InfoButton(text: "Pearcleaner searches for files via a combination of bundle id and app name. \(appState.appInfo.appName) has a common or short app name so there might be unrelated files found. Please check the list thoroughly before uninstalling.")
|
||||
}
|
||||
|
||||
@@ -173,7 +173,12 @@ struct MiniAppView: View {
|
||||
if appState.reload {
|
||||
VStack {
|
||||
Spacer()
|
||||
ProgressView("Refreshing app list")
|
||||
ProgressView() {
|
||||
Text("Gathering app details")
|
||||
.font(.callout)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
.padding(5)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical)
|
||||
|
||||
@@ -33,7 +33,12 @@ struct RegularMode: View {
|
||||
if appState.reload {
|
||||
VStack {
|
||||
Spacer()
|
||||
ProgressView("Refreshing app list")
|
||||
ProgressView() {
|
||||
Text("Gathering app details")
|
||||
.font(.callout)
|
||||
.foregroundStyle(Color("mode").opacity(0.5))
|
||||
.padding(5)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.frame(width: sidebarWidth)
|
||||
|
||||
@@ -13,6 +13,8 @@ struct ZombieView: View {
|
||||
@EnvironmentObject var locations: Locations
|
||||
@EnvironmentObject var fsm: FolderSettingsManager
|
||||
@State private var showPop: Bool = false
|
||||
@AppStorage("settings.general.leftoverWarning") private var warning: Bool = false
|
||||
@State private var showAlert = false
|
||||
@AppStorage("settings.general.mini") private var mini: Bool = false
|
||||
@AppStorage("settings.general.glass") private var glass: Bool = false
|
||||
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
|
||||
@@ -36,45 +38,6 @@ struct ZombieView: View {
|
||||
|
||||
var body: some View {
|
||||
|
||||
// let totalSelectedZombieSize: (real: String, logical: String, finder: String) = {
|
||||
// var totalReal: Int64 = 0
|
||||
// var totalLogical: Int64 = 0
|
||||
//
|
||||
// for url in selectedZombieItemsLocal {
|
||||
// let realSize = appState.zombieFile.fileSize[url] ?? 0
|
||||
// let logicalSize = appState.zombieFile.fileSizeLogical[url] ?? 0
|
||||
// totalReal += realSize
|
||||
// totalLogical += logicalSize
|
||||
// }
|
||||
// return (formatByte(size: totalReal).human, formatByte(size:totalLogical).human, "\(formatByte(size: totalLogical).byte) (\(formatByte(size: totalReal).human))")
|
||||
// }()
|
||||
|
||||
// let filteredAndSortedFiles: ([URL], Int64, Int64) = {
|
||||
// let fileSizeReal = appState.zombieFile.fileSize
|
||||
// let fileSizeLogical = appState.zombieFile.fileSizeLogical
|
||||
// let filteredFilesReal = fileSizeReal.filter { (url, _) in
|
||||
// searchZ.isEmpty || url.lastPathComponent.localizedCaseInsensitiveContains(searchZ)
|
||||
// }
|
||||
// let filteredFilesLogical = fileSizeLogical.filter { (url, _) in
|
||||
// searchZ.isEmpty || url.lastPathComponent.localizedCaseInsensitiveContains(searchZ)
|
||||
// }
|
||||
// let filesToSort = (sizeType == "Real" || sizeType == "Finder" ? filteredFilesReal : filteredFilesLogical)
|
||||
// let sortedFilteredFiles = filesToSort.sorted(by: {
|
||||
// if selectedSortAlpha {
|
||||
// return $0.key.lastPathComponent.pearFormat() < $1.key.lastPathComponent.pearFormat()
|
||||
// } else {
|
||||
// return $0.value > $1.value
|
||||
// }
|
||||
// }).map { $0.key }
|
||||
// let totalSize = filteredFilesReal.values.reduce(0, +)
|
||||
// let totalSizeL = filteredFilesLogical.values.reduce(0, +)
|
||||
// return (sortedFilteredFiles, totalSize, totalSizeL)
|
||||
// }()
|
||||
|
||||
// let displaySizeTotal = sizeType == "Real" ? formatByte(size: filteredAndSortedFiles.1).human :
|
||||
// sizeType == "Logical" ? formatByte(size: filteredAndSortedFiles.2).human :
|
||||
// "\(formatByte(size: filteredAndSortedFiles.2).byte) (\(formatByte(size: filteredAndSortedFiles.1).human))"
|
||||
|
||||
VStack(alignment: .center) {
|
||||
if appState.showProgress {
|
||||
VStack {
|
||||
@@ -150,7 +113,7 @@ struct ZombieView: View {
|
||||
VStack(alignment: .leading, spacing: 10){
|
||||
HStack {
|
||||
Text("Leftover Files").font(.title).fontWeight(.bold)
|
||||
InfoButton(text: "Leftover file search is not and can never be 100% accurate as it doesn't have any old/uninstalled app bundles to check against for file exclusion. This does a best guess search for files/folders and excludes the ones that have overlap with your currently installed applications. Please confirm files marked for deletion really do belong to old/uninstalled applications.", color: .orange, warning: true)
|
||||
InfoButton(text: "Leftover file search is not 100% accurate as it doesn't have any uninstalled app bundles to check against for file exclusion. This does a best guess search for files/folders and excludes the ones that have overlap with your currently installed applications. Please confirm files marked for deletion really do belong to uninstalled applications.", color: .orange, warning: true)
|
||||
Spacer()
|
||||
|
||||
}
|
||||
@@ -326,9 +289,34 @@ struct ZombieView: View {
|
||||
.onAppear {
|
||||
updateMemoizedFiles(for: searchZ, sizeType: sizeType, selectedSortAlpha: selectedSortAlpha, force: true)
|
||||
}
|
||||
.sheet(isPresented: $showAlert, content: {
|
||||
VStack(spacing: 10) {
|
||||
Text("Important")
|
||||
.font(.headline)
|
||||
Spacer()
|
||||
Text("Leftover file search is not 100% accurate as it doesn't have any uninstalled app bundles to check against for file exclusion. This does a best guess search for files/folders and excludes the ones that have overlap with your currently installed applications. Please confirm files marked for deletion really do belong to uninstalled applications.")
|
||||
.font(.subheadline)
|
||||
Spacer()
|
||||
Button("Close") {
|
||||
warning = true
|
||||
showAlert = false
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", label: "Close", help: "Dismiss"))
|
||||
Spacer()
|
||||
}
|
||||
.padding(15)
|
||||
.frame(width: 400, height: 250)
|
||||
.background(GlassEffect(material: .hudWindow, blendingMode: .behindWindow))
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if !warning {
|
||||
showAlert = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func binding(for file: URL) -> Binding<Bool> {
|
||||
|
||||
Reference in New Issue
Block a user