mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 06:09:01 +00:00
v3.1.1
This commit is contained in:
@@ -553,7 +553,7 @@ func reversePathsSearch(appState: AppState, locations: Locations, completion: @e
|
||||
let dispatchGroup = DispatchGroup()
|
||||
let allPaths = appState.appInfoStore.flatMap { $0.files.map { $0.path.pearFormat() } }
|
||||
let allNames = appState.appInfoStore.map { $0.appName.pearFormat() }
|
||||
let skipped = ["apple", "comapple", "temporary", "btserver", "proapps", "scripteditor", "ilife", "livefsd", "siritoday", "addressbook", "animoji", "appstore", "askpermission", "callhistory", "clouddocs", "diskimages", "dock", "facetime", "fileprovider", "instruments", "knowledge", "mobilesync", "syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy", "familycircle", "geoservices", "installation", "passkit", "sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband", "coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts", "ipod", "globalpreferences", "apmanalytics", "apmexperiment", "avatarcache", "byhost", "contextstoreagent", "mobilemeaccounts", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandobxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter", "trash"] // Skip system folders
|
||||
let skipped = ["apple", "temporary", "btserver", "proapps", "scripteditor", "ilife", "livefsd", "siritoday", "addressbook", "animoji", "appstore", "askpermission", "callhistory", "clouddocs", "diskimages", "dock", "facetime", "fileprovider", "instruments", "knowledge", "mobilesync", "syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy", "familycircle", "geoservices", "installation", "passkit", "sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband", "coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts", "ipod", "globalpreferences", "apmanalytics", "apmexperiment", "avatarcache", "byhost", "contextstoreagent", "mobilemeaccounts", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandboxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter", "trash", "pearcleaner", "amsdatamigratortool", "arfilecache", "assistant", "chromium", "cloudkit", "webkit", "databases", "diagnostic", "cache", "gamekit", "homebrew", "logi", "microsoft", "mozilla", "sync", "google"] // Skip system folders
|
||||
|
||||
|
||||
// Skip locations that might not exist
|
||||
|
||||
@@ -75,16 +75,26 @@ struct InfoButton: View {
|
||||
@State private var isPopoverPresented: Bool = false
|
||||
let text: String
|
||||
let color: Color?
|
||||
let label: String?
|
||||
|
||||
var body: some View {
|
||||
Button(action: {
|
||||
self.isPopoverPresented.toggle()
|
||||
}) {
|
||||
Image(systemName: "info.circle.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 14, height: 14)
|
||||
.foregroundColor(color?.opacity(0.7) ?? Color("mode").opacity(0.7))
|
||||
HStack {
|
||||
Image(systemName: "info.circle.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 14, height: 14)
|
||||
.foregroundColor(color?.opacity(0.7) ?? Color("mode").opacity(0.7))
|
||||
if !label!.isEmpty {
|
||||
Text(label!)
|
||||
.font(.callout)
|
||||
.foregroundColor(color?.opacity(0.7) ?? Color("mode").opacity(0.7))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.onHover { isHovered in
|
||||
|
||||
@@ -329,7 +329,7 @@ extension Int {
|
||||
// --- Extend String to remove periods, spaces and lowercase the string
|
||||
extension String {
|
||||
func pearFormat() -> String {
|
||||
return self.replacingOccurrences(of: ".", with: "").replacingOccurrences(of: " ", with: "").lowercased()
|
||||
return self.replacingOccurrences(of: ".", with: "").replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "-", with: "").lowercased()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ struct PearcleanerApp: App {
|
||||
loadAllPaths(allApps: sortedApps, appState: appState, locations: locations)
|
||||
}
|
||||
|
||||
|
||||
#if !DEBUG
|
||||
Task {
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ struct GeneralSettingsTab: View {
|
||||
.foregroundStyle(.gray)
|
||||
}
|
||||
|
||||
InfoButton(text: "When instant search is enabled, all application files are gathered and cached for later use on startup instead of on each app click. There might be a slight delay of a few seconds when launching Pearcleaner depending on the amount of apps you have installed.", color: nil)
|
||||
InfoButton(text: "When instant search is enabled, all application files are gathered and cached for later use on startup instead of on each app click. There might be a slight delay of a few seconds when launching Pearcleaner depending on the amount of apps you have installed.", color: nil, label: "")
|
||||
|
||||
Spacer()
|
||||
Toggle(isOn: $instantSearch, label: {
|
||||
@@ -96,7 +96,7 @@ struct GeneralSettingsTab: View {
|
||||
.foregroundStyle(.gray)
|
||||
}
|
||||
|
||||
InfoButton(text: "When homebrew cleanup is enabled, Pearcleaner will check if the app you are removing was installed via homebrew and execute a brew uninstall and brew cleanup command as well to let homebrew know that the app is removed. This way your homebrew list will be synced up correctly and caching will be removed.\n\nNOTE: If you undo the file delete with CMD+Z, the files will be put back but homebrew will not be aware of it. To get the homebrew list back in sync you'd need to run:\n brew install APPNAME --force", color: nil)
|
||||
InfoButton(text: "When homebrew cleanup is enabled, Pearcleaner will check if the app you are removing was installed via homebrew and execute a brew uninstall and brew cleanup command as well to let homebrew know that the app is removed. This way your homebrew list will be synced up correctly and caching will be removed.\n\nNOTE: If you undo the file delete with CMD+Z, the files will be put back but homebrew will not be aware of it. To get the homebrew list back in sync you'd need to run:\n brew install APPNAME --force", color: nil, label: "")
|
||||
|
||||
Spacer()
|
||||
Toggle(isOn: $brew, label: {
|
||||
@@ -209,7 +209,7 @@ struct GeneralSettingsTab: View {
|
||||
.foregroundStyle(.gray)
|
||||
}
|
||||
|
||||
InfoButton(text: "In mini window mode, you can have Pearcleaner startup to the Apps List view or the Drop Target view.", color: nil)
|
||||
InfoButton(text: "In mini window mode, you can have Pearcleaner startup to the Apps List view or the Drop Target view.", color: nil, label: "")
|
||||
|
||||
Spacer()
|
||||
Toggle(isOn: $miniView, label: {
|
||||
@@ -236,7 +236,7 @@ struct GeneralSettingsTab: View {
|
||||
.foregroundStyle(.gray)
|
||||
}
|
||||
|
||||
InfoButton(text: "In mini window mode, if you pin the Files popover on top, clicking away from the window will not dismiss it. Otherwise, it will dismiss by clicking anywhere outside the popover.", color: nil)
|
||||
InfoButton(text: "In mini window mode, if you pin the Files popover on top, clicking away from the window will not dismiss it. Otherwise, it will dismiss by clicking anywhere outside the popover.", color: nil, label: "")
|
||||
|
||||
Spacer()
|
||||
Toggle(isOn: $popoverStay, label: {
|
||||
|
||||
@@ -77,7 +77,7 @@ struct FilesView: View {
|
||||
Text("•").foregroundStyle(Color("AccentColor"))
|
||||
Text("\(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.", color: nil)
|
||||
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.", color: nil, label: "")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ struct TopBar: View {
|
||||
Button("") {
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
updateOnMain {
|
||||
appState.appInfo = .empty
|
||||
if appState.zombieFile.fileSize.keys.count == 0 {
|
||||
appState.currentView = .zombie
|
||||
appState.showProgress.toggle()
|
||||
|
||||
@@ -71,6 +71,7 @@ struct TopBarMini: View {
|
||||
withAnimation(.easeInOut(duration: 0.5)) {
|
||||
showPopover = false
|
||||
updateOnMain {
|
||||
appState.appInfo = .empty
|
||||
if appState.zombieFile.fileSize.keys.count == 0 {
|
||||
appState.currentView = .zombie
|
||||
appState.showProgress.toggle()
|
||||
|
||||
@@ -18,9 +18,36 @@ struct ZombieView: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@Binding var showPopover: Bool
|
||||
@Binding var search: String
|
||||
@State private var searchZ: String = ""
|
||||
@State private var selectedOption = "Default"
|
||||
|
||||
var body: some View {
|
||||
|
||||
// let filteredAndSortedFiles: [URL] = {
|
||||
// let files = appState.zombieFile.fileSize.keys.filter { url in
|
||||
// searchZ.isEmpty || url.lastPathComponent.localizedCaseInsensitiveContains(searchZ)
|
||||
// }
|
||||
//
|
||||
// return selectedOption == "Default" ?
|
||||
// files.sorted(by: { $0.lastPathComponent < $1.lastPathComponent }) :
|
||||
// files.sorted(by: { appState.zombieFile.fileSize[$0, default: 0] > appState.zombieFile.fileSize[$1, default: 0] })
|
||||
// }()
|
||||
let filteredAndSortedFiles: ([URL], Int64) = {
|
||||
let filteredFiles = appState.zombieFile.fileSize.filter { (url, _) in
|
||||
searchZ.isEmpty || url.lastPathComponent.localizedCaseInsensitiveContains(searchZ)
|
||||
}
|
||||
|
||||
let sortedFilteredFiles = filteredFiles.sorted(by: {
|
||||
selectedOption == "Default" ?
|
||||
$0.key.lastPathComponent < $1.key.lastPathComponent :
|
||||
$0.value > $1.value
|
||||
}).map { $0.key }
|
||||
|
||||
let totalSize = filteredFiles.values.reduce(0, +)
|
||||
|
||||
return (sortedFilteredFiles, totalSize)
|
||||
}()
|
||||
|
||||
VStack(alignment: .center) {
|
||||
if appState.showProgress {
|
||||
VStack {
|
||||
@@ -88,7 +115,7 @@ struct ZombieView: View {
|
||||
VStack(alignment: .leading, spacing: 10){
|
||||
HStack {
|
||||
Text("Leftover Files").font(.title).fontWeight(.bold)
|
||||
InfoButton(text: "Leftover file search is not 100% accurate as it doesn't have any app bundles to check against. This searches for files/folders and excludes the ones that have overlap with your currently installed apps. Make sure to confirm files marked for removal are correct.", color: .red)
|
||||
InfoButton(text: "Leftover file search is not 100% accurate as it doesn't have any app bundles to check against. This searches for files/folders and excludes the ones that have overlap with your currently installed apps. Make sure to confirm files marked for removal are correct.", color: .red, label: "READ")
|
||||
Spacer()
|
||||
|
||||
}
|
||||
@@ -99,8 +126,8 @@ struct ZombieView: View {
|
||||
Spacer()
|
||||
|
||||
VStack(alignment: .trailing, spacing: 5) {
|
||||
Text("\(formatByte(size: appState.zombieFile.totalSize))").font(.title).fontWeight(.bold)
|
||||
Text("\(appState.zombieFile.fileSize.count == 1 ? "\(appState.zombieFile.fileSize.count) item" : "\(appState.zombieFile.fileSize.count) items")").font(.callout).underline().foregroundStyle((.gray.opacity(0.8)))
|
||||
Text("\(formatByte(size: filteredAndSortedFiles.1))").font(.title).fontWeight(.bold)
|
||||
Text("\(filteredAndSortedFiles.0.count == 1 ? "\(filteredAndSortedFiles.0.count) item" : "\(filteredAndSortedFiles.0.count) items")").font(.callout).underline().foregroundStyle((.gray.opacity(0.8)))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -110,19 +137,15 @@ struct ZombieView: View {
|
||||
.padding(.top, 0)
|
||||
}
|
||||
|
||||
SearchBarMiniBottom(search: $searchZ)
|
||||
|
||||
Divider()
|
||||
.padding()
|
||||
|
||||
ScrollView() {
|
||||
LazyVStack {
|
||||
let sortedFilesSize = appState.zombieFile.fileSize.keys.sorted(by: { appState.zombieFile.fileSize[$0, default: 0] > appState.zombieFile.fileSize[$1, default: 0] })
|
||||
|
||||
|
||||
let sortedFilesAlpha = appState.zombieFile.fileSize.keys.sorted(by: { $0.lastPathComponent < $1.lastPathComponent })
|
||||
|
||||
let sort = selectedOption == "Default" ? sortedFilesAlpha : sortedFilesSize
|
||||
|
||||
ForEach(sort, id: \.self) { file in
|
||||
ForEach(filteredAndSortedFiles.0, id: \.self) { file in
|
||||
if let fileSize = appState.zombieFile.fileSize[file], let fileIcon = appState.zombieFile.fileIcon[file] {
|
||||
let iconImage = fileIcon.map(Image.init(nsImage:))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user