3.0.2 fixes

This commit is contained in:
Alin
2024-03-04 15:32:40 -07:00
parent 7895996118
commit 583f0d40a5
18 changed files with 346 additions and 299 deletions
+19 -19
View File
@@ -15,7 +15,6 @@
C71848442B8D2D600046CB13 /* ZombieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C71848432B8D2D600046CB13 /* ZombieView.swift */; };
C72893052AFD42E600C8C1CD /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72893042AFD42E600C8C1CD /* DeepLink.swift */; };
C72893122AFD51EA00C8C1CD /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C72893112AFD51EA00C8C1CD /* main.swift */; };
C7575D382B0181FC006A600A /* FileWatcher in Frameworks */ = {isa = PBXBuildFile; productRef = C7575D372B0181FC006A600A /* FileWatcher */; };
C7575D392B0182DE006A600A /* PearcleanerSentinel in CopyFiles */ = {isa = PBXBuildFile; fileRef = C728930F2AFD51EA00C8C1CD /* PearcleanerSentinel */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
C76D08482AF83C3F00D07867 /* Update.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08472AF83C3F00D07867 /* Update.swift */; };
C76D084A2AF83C6E00D07867 /* General.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08492AF83C6E00D07867 /* General.swift */; };
@@ -30,6 +29,7 @@
C77B901E2AF1A9DB009CC655 /* SettingsWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B901D2AF1A9DB009CC655 /* SettingsWindow.swift */; };
C77B90232AF2D616009CC655 /* FilesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90222AF2D616009CC655 /* FilesView.swift */; };
C7A27E812AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist in Resources */ = {isa = PBXBuildFile; fileRef = C7A27E802AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist */; };
C7A2AE5D2B9644F300161540 /* FileWatcher in Frameworks */ = {isa = PBXBuildFile; productRef = C7A2AE5C2B9644F300161540 /* FileWatcher */; };
C7A9CE472B89164700EB6E78 /* Authorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A9CE462B89164700EB6E78 /* Authorization.swift */; };
C7ACADE62B92A737000B5845 /* Features.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ACADE52B92A737000B5845 /* Features.swift */; };
C7CF47242B3B3F1700979C5F /* WindowSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7CF47232B3B3F1700979C5F /* WindowSettings.swift */; };
@@ -37,6 +37,7 @@
C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D492AFEB26700C7ED9E /* AppListItems.swift */; };
C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D502AFF00F300C7ED9E /* Locations.swift */; };
C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F539372AF60865007DF1B2 /* Utilities.swift */; };
C7FB173B2B96321300B96F9A /* AppsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7FB173A2B96321300B96F9A /* AppsListView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -95,6 +96,7 @@
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListItems.swift; sourceTree = "<group>"; };
C7D31D502AFF00F300C7ED9E /* Locations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Locations.swift; sourceTree = "<group>"; };
C7F539372AF60865007DF1B2 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
C7FB173A2B96321300B96F9A /* AppsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppsListView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -102,7 +104,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C7575D382B0181FC006A600A /* FileWatcher in Frameworks */,
C7A2AE5D2B9644F300161540 /* FileWatcher in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -146,7 +148,6 @@
C72893102AFD51EA00C8C1CD /* PearcleanerSentinel */ = {
isa = PBXGroup;
children = (
C7A27E822AFD996100166168 /* Resources */,
C7A27E7F2AFD70DD00166168 /* PearcleanerSentinel.entitlements */,
C72893112AFD51EA00C8C1CD /* main.swift */,
);
@@ -224,17 +225,11 @@
C7D31D492AFEB26700C7ED9E /* AppListItems.swift */,
C7045A272B03E71D00376976 /* MiniMode.swift */,
C71848432B8D2D600046CB13 /* ZombieView.swift */,
C7FB173A2B96321300B96F9A /* AppsListView.swift */,
);
path = Views;
sourceTree = "<group>";
};
C7A27E822AFD996100166168 /* Resources */ = {
isa = PBXGroup;
children = (
);
path = Resources;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -252,7 +247,7 @@
);
name = PearcleanerSentinel;
packageProductDependencies = (
C7575D372B0181FC006A600A /* FileWatcher */,
C7A2AE5C2B9644F300161540 /* FileWatcher */,
);
productName = PeakPurgeMonitor;
productReference = C728930F2AFD51EA00C8C1CD /* PearcleanerSentinel */;
@@ -306,7 +301,7 @@
);
mainGroup = C77B8FF72AF18E2E009CC655;
packageReferences = (
C7575D362B0181F3006A600A /* XCRemoteSwiftPackageReference "FileWatcher" */,
C7A2AE5B2B9644DA00161540 /* XCRemoteSwiftPackageReference "FileWatcher" */,
);
productRefGroup = C77B90012AF18E2E009CC655 /* Products */;
projectDirPath = "";
@@ -344,6 +339,7 @@
buildActionMask = 2147483647;
files = (
C76D08482AF83C3F00D07867 /* Update.swift in Sources */,
C7FB173B2B96321300B96F9A /* AppsListView.swift in Sources */,
C7045A342B068B2900376976 /* UpdateView.swift in Sources */,
C77B90162AF19377009CC655 /* AppState.swift in Sources */,
C7045A322B068AD700376976 /* NewWin.swift in Sources */,
@@ -383,6 +379,7 @@
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = BK8443AXLU;
ENABLE_HARDENED_RUNTIME = YES;
FRAMEWORK_SEARCH_PATHS = "";
MACOSX_DEPLOYMENT_TARGET = 13.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.PearcleanerSentinel;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -400,6 +397,7 @@
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = BK8443AXLU;
ENABLE_HARDENED_RUNTIME = YES;
FRAMEWORK_SEARCH_PATHS = "";
MACOSX_DEPLOYMENT_TARGET = 13.0;
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.PearcleanerSentinel;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -448,6 +446,7 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
FRAMEWORK_SEARCH_PATHS = "";
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@@ -513,6 +512,7 @@
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
FRAMEWORK_SEARCH_PATHS = "";
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -539,7 +539,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 20;
CURRENT_PROJECT_VERSION = 21;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BK8443AXLU;
@@ -557,7 +557,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 3.0.1;
MARKETING_VERSION = 3.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -574,7 +574,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 20;
CURRENT_PROJECT_VERSION = 21;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BK8443AXLU;
@@ -592,7 +592,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 3.0.1;
MARKETING_VERSION = 3.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -633,7 +633,7 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
C7575D362B0181F3006A600A /* XCRemoteSwiftPackageReference "FileWatcher" */ = {
C7A2AE5B2B9644DA00161540 /* XCRemoteSwiftPackageReference "FileWatcher" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/eonist/FileWatcher.git";
requirement = {
@@ -644,9 +644,9 @@
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
C7575D372B0181FC006A600A /* FileWatcher */ = {
C7A2AE5C2B9644F300161540 /* FileWatcher */ = {
isa = XCSwiftPackageProductDependency;
package = C7575D362B0181F3006A600A /* XCRemoteSwiftPackageReference "FileWatcher" */;
package = C7A2AE5B2B9644DA00161540 /* XCRemoteSwiftPackageReference "FileWatcher" */;
productName = FileWatcher;
};
/* End XCSwiftPackageProductDependency section */
+17 -12
View File
@@ -8,7 +8,8 @@
import SwiftUI
struct AppCommands: Commands {
@AppStorage("settings.general.instant") private var instantSearch: Bool = true
let appState: AppState
let locations: Locations
@@ -32,18 +33,20 @@ struct AppCommands: Commands {
Button {
withAnimation(.easeInOut(duration: 0.5)) {
// Refresh Apps list
appState.reload.toggle()
let sortedApps = getSortedApps()
updateOnMain {
appState.reload.toggle()
}
let sortedApps = getSortedApps()
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
appState.sortedApps.userApps = []
appState.sortedApps.systemApps = []
appState.sortedApps.userApps = sortedApps.userApps
appState.sortedApps.systemApps = sortedApps.systemApps
}
Task(priority: .high){
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
}
updateOnMain {
if instantSearch {
Task(priority: .high){
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
}
}
appState.reload.toggle()
}
}
@@ -64,16 +67,18 @@ struct AppCommands: Commands {
// Edit Menu
CommandGroup(replacing: .undoRedo) {
Button
{
undoTrash(appState: appState) {
let sortedApps = getSortedApps()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
appState.sortedApps.userApps = []
appState.sortedApps.systemApps = []
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
appState.sortedApps.userApps = sortedApps.userApps
appState.sortedApps.systemApps = sortedApps.systemApps
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
// if instantSearch {
// loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
// }
}
}
} label: {
+12 -6
View File
@@ -321,7 +321,14 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L
var collection: [URL] = []
if let url = URL(string: appInfo.path.absoluteString) {
collection.insert(url, at: 0)
if !url.path.contains(".Trash") {
if url.path.contains("Wrapper") {
let modifiedUrl = url.deletingLastPathComponent().deletingLastPathComponent()
collection.insert(modifiedUrl, at: 0)
} else {
collection.insert(url, at: 0)
}
}
}
@@ -329,7 +336,6 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L
let dispatchGroup = DispatchGroup()
var bundleComponents = appInfo.bundleIdentifier.components(separatedBy: ".")
if let lastComponent = bundleComponents.last, let rangeOfDash = lastComponent.range(of: "-") {
// Remove the dash and everything after it
let updatedLastComponent = String(lastComponent[..<rangeOfDash.lowerBound])
bundleComponents[bundleComponents.count - 1] = updatedLastComponent
}
@@ -370,6 +376,7 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L
if collection.contains(itemURL) {
continue
}
// Catch web app plist files
if appInfo.webApp {
if itemL.contains(bundleIdentifierL) {
@@ -417,7 +424,6 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L
collection.append(contentsOf: groupContainers)
var sortedCollection = collection.sorted(by: { $0.absoluteString < $1.absoluteString })
// Calculate file details (sizes and icons)
var fileSize: [URL: Int64] = [:]
var fileIcon: [URL: NSImage?] = [:]
@@ -438,6 +444,7 @@ func findPathsForApp(appInfo: AppInfo = .empty, appState: AppState, locations: L
sortedCollection = []
}
}
// Save to appState
dispatchGroup.notify(queue: .main) {
@@ -593,7 +600,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"] // Skip system folders
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
// Skip locations that might not exist
@@ -654,7 +661,6 @@ func reversePathsSearch(appState: AppState, locations: Locations, completion: @e
updateOnMain {
updatedZombieFile.fileSize = fileSize
updatedZombieFile.fileIcon = fileIcon
// appState.selectedZombieItems = Set(sortedCollection)
appState.zombieFile = updatedZombieFile
appState.showProgress = false
}
@@ -683,7 +689,7 @@ func moveFilesToTrash(at fileURLs: [URL], completion: @escaping () -> Void = {})
}
}
}
if !filesSudo.isEmpty {
// Remove socket files with rm
let filesSudoPaths = filesSudo.map { $0.path }
+34
View File
@@ -504,6 +504,40 @@ struct WindowActionButton: ButtonStyle {
}
struct SimpleButtonBrightStyle: ButtonStyle {
@State private var hovered = false
let icon: String
let help: String
let color: Color
let shield: Bool?
init(icon: String, help: String, color: Color, shield: Bool? = nil) {
self.icon = icon
self.help = help
self.color = color
self.shield = shield
}
func makeBody(configuration: Self.Configuration) -> some View {
HStack {
Image(systemName: icon)
.resizable()
.scaledToFit()
.frame(width: 20)
.foregroundColor(hovered ? color.opacity(0.5) : color)
}
.padding(5)
.onHover { hovering in
withAnimation() {
hovered = hovering
}
}
.scaleEffect(configuration.isPressed ? 0.95 : 1)
.help(help)
}
}
struct FilesViewActionButton: ButtonStyle {
enum UserAction {
+4 -2
View File
@@ -427,7 +427,7 @@ func ensureApplicationSupportFolderExists(appState: AppState) {
let fileManager = FileManager.default
let supportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("Pearcleaner")
// Check to make sure Application Support/Support Admin folder exists
// Check to make sure Application Support/Pearcleaner folder exists
if !fileManager.fileExists(atPath: supportURL.path) {
try! fileManager.createDirectory(at: supportURL, withIntermediateDirectories: true)
printOS("Created Application Support/Pearcleaner folder")
@@ -463,7 +463,7 @@ func writeLog(string: String) {
// --- Load Plist file with launchctl ---
func launchctl(load: Bool) {
func launchctl(load: Bool, completion: @escaping () -> Void = {}) {
let cmd = load ? "load" : "unload"
if let plistPath = Bundle.main.path(forResource: "com.alienator88.PearcleanerSentinel", ofType: "plist") {
var plistContent = try! String(contentsOfFile: plistPath)
@@ -491,6 +491,8 @@ func launchctl(load: Bool) {
task.launch()
completion()
}
}
+2 -5
View File
@@ -29,8 +29,6 @@ struct PearcleanerApp: App {
var body: some Scene {
WindowGroup {
Group {
ZStack() {
@@ -53,7 +51,7 @@ struct PearcleanerApp: App {
}
.environmentObject(appState)
.preferredColorScheme(displayMode.colorScheme)
.alert(isPresented: $appState.showAlert) { presentAlert(appState: appState) }
// .alert(isPresented: $appState.showAlert) { presentAlert(appState: appState) }
.handlesExternalEvents(preferring: Set(arrayLiteral: "pear"), allowing: Set(arrayLiteral: "*"))
.onOpenURL(perform: { url in
let deeplinkManager = DeeplinkManager(showPopover: $showPopover)
@@ -101,8 +99,7 @@ struct PearcleanerApp: App {
if instantSearch {
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
}
#if !DEBUG
Task {
@@ -5,9 +5,9 @@
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xFF",
"red" : "0xFF"
"blue" : "0x50",
"green" : "0xC3",
"red" : "0x79"
}
},
"idiom" : "universal"
@@ -23,9 +23,9 @@
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xFF",
"red" : "0xFF"
"blue" : "0x50",
"green" : "0xC3",
"red" : "0x79"
}
},
"idiom" : "universal"
+1 -1
View File
@@ -48,7 +48,7 @@ struct GeneralSettingsTab: View {
.font(.callout)
.foregroundStyle(.gray)
}
InfoButton(text: "When transparent material is enabled, sticky section headers (User/System) in app list are disabled to keep from showing app name text overlayed under the section header text with no background to separate the two.", color: nil)
// InfoButton(text: "When transparent material is enabled, sticky section headers (User/System) in app list are disabled to keep from showing app name text overlayed under the section header text with no background to separate the two.", color: nil)
Spacer()
Toggle(isOn: $glass, label: {
})
+1 -1
View File
@@ -24,7 +24,7 @@ struct Asset: Codable {
extension Release {
var modifiedBody: String {
return body.replacingOccurrences(of: "- [x]", with: "􀆊").replacingOccurrences(of: "###", with: "")
return body.replacingOccurrences(of: "- [x]", with: ">").replacingOccurrences(of: "###", with: "")
}
}
+2 -46
View File
@@ -64,53 +64,9 @@ struct AppListView: View {
}
}
.padding(.top, 20)
// .padding(.bottom)
ScrollView {
LazyVStack(alignment: .leading, spacing: 0, pinnedViews: [glass ? [] : .sectionHeaders]) {
if filteredUserApps.count > 0 {
// VStack {
// Header(title: "User", count: filteredUserApps.count, showPopover: $showPopover)
ForEach(filteredUserApps, id: \.self) { appInfo in
Section(header: Header(title: "User", count: filteredUserApps.count, showPopover: $showPopover)) {
AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
.padding(.vertical,5)
if appInfo != filteredUserApps.last {
Divider().padding(.horizontal, 5)
}
}
// AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
// if appInfo != filteredUserApps.last {
// Divider().padding(.horizontal, 5)
// }
}
// }
}
if filteredSystemApps.count > 0 {
// VStack {
// Header(title: "System", count: filteredSystemApps.count, showPopover: $showPopover)
Section(header: Header(title: "System", count: filteredSystemApps.count, showPopover: $showPopover)) {
ForEach(filteredSystemApps, id: \.self) { appInfo in
AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
.padding(.vertical,5)
if appInfo != filteredSystemApps.last {
Divider().padding(.horizontal, 5)
}
}
}
// }
}
}
.padding(.horizontal)
}
.scrollIndicators(.never)
AppsListView(search: $search, showPopover: $showPopover, filteredUserApps: filteredUserApps, filteredSystemApps: filteredSystemApps)
}
.frame(width: sidebarWidth)
@@ -267,6 +223,6 @@ struct Header: View {
}
.padding(.vertical, 9)
.padding(.leading, 6)
.background(!glass ? Color("windowBG") : .clear)
// .background(!glass ? Color("windowBG") : .clear)
}
}
+79
View File
@@ -0,0 +1,79 @@
//
// AppsListView.swift
// Pearcleaner
//
// Created by Alin Lupascu on 3/4/24.
//
import Foundation
import SwiftUI
struct AppsListView: View {
@Binding var search: String
@Binding var showPopover: Bool
var filteredUserApps: [AppInfo]
var filteredSystemApps: [AppInfo]
var body: some View {
ScrollView {
LazyVStack(alignment: .leading, spacing: 0, pinnedViews: [.sectionHeaders]) {
if filteredUserApps.count > 0 {
SectionView(title: "User", count: filteredUserApps.count, apps: filteredUserApps, search: $search, showPopover: $showPopover)
}
if filteredSystemApps.count > 0 {
SectionView(title: "System", count: filteredSystemApps.count, apps: filteredSystemApps, search: $search, showPopover: $showPopover)
}
}
.padding(.horizontal)
}
.scrollIndicators(.never)
}
}
struct SectionView: View {
var title: String
var count: Int
var apps: [AppInfo]
@Binding var search: String
@Binding var showPopover: Bool
var body: some View {
VStack(spacing: 0) {
Header(title: title, count: count, showPopover: $showPopover)
ForEach(apps, id: \.self) { appInfo in
AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
.padding(.vertical, 5)
if appInfo != apps.last {
Divider().padding(.horizontal, 5)
}
}
}
}
}
//List {
// Section {
// ForEach(filteredUserApps, id:\.self) { element in
// Text(element.appName)
// }
// .listRowBackground(Color.clear)
// } header: {
// Text("User Apps")
// }
//
// Section {
// ForEach(filteredSystemApps, id:\.self) { element in
// Text(element.appName)
// }
// .listRowBackground(Color.clear)
// } header: {
// Text("System Apps")
// }
//
//}
//.scrollContentBackground(.hidden)
//.background(.clear)
//.scrollIndicators(.never)
+31 -35
View File
@@ -14,6 +14,7 @@ struct FilesView: View {
@State private var showPop: Bool = false
@AppStorage("settings.general.mini") private var mini: Bool = false
@AppStorage("settings.sentinel.enable") private var sentinel: Bool = false
@AppStorage("settings.general.instant") var instantSearch: Bool = true
@Environment(\.colorScheme) var colorScheme
@Binding var showPopover: Bool
@Binding var search: String
@@ -35,8 +36,24 @@ struct FilesView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity)
.transition(.opacity)
} else {
VStack() {
// Titlebar
if mini {
HStack() {
Spacer()
Button("Close") {
updateOnMain {
appState.appInfo = AppInfo.empty
search = ""
appState.currentView = .apps
showPopover = false
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "x.circle.fill", help: "Close"))
}
.padding([.horizontal, .top], 5)
}
VStack(spacing: 0) {
// Main Group
HStack(alignment: .center) {
@@ -112,7 +129,7 @@ struct FilesView: View {
}
.padding(.horizontal, 20)
.padding(.top, 10)
.padding(.top, 0)
}
Divider()
@@ -153,8 +170,8 @@ struct FilesView: View {
}
}
)) {
Text("􀃲").tag(true)
Text("􀂒").tag(false)
Image(systemName: "checkmark.square").tag(true)
Image(systemName: "square").tag(false)
}
.pickerStyle(SegmentedPickerStyle())
.frame(width: 100)
@@ -163,20 +180,6 @@ struct FilesView: View {
Spacer()
if mini {
Button("Close") {
updateOnMain {
appState.appInfo = AppInfo.empty
search = ""
appState.currentView = .apps
showPopover = false
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "x.circle.fill", help: "Close"))
}
if !appState.selectedItems.isEmpty {
Button("Uninstall") {
Task {
@@ -191,13 +194,9 @@ struct FilesView: View {
}
}
var selectedItemsArray = Array(appState.selectedItems)
.filter { !$0.path.contains(".Trash") }
.map { path in
return path.path.contains("Wrapper") ? path.deletingLastPathComponent().deletingLastPathComponent() : path
}
if let url = URL(string: appState.appInfo.path.absoluteString) {
let appFolderURL = url.deletingLastPathComponent() // Get the immediate parent directory
let appFolderURL = appState.appInfo.path.absoluteString.contains("Wrapper") ? url.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() : url.deletingLastPathComponent() // Get the immediate parent directory of regular and wrapped apps
if appFolderURL.path == "/Applications" || appFolderURL.path == "\(home)/Applications" {
// Do nothing, skip insertion
@@ -207,7 +206,6 @@ struct FilesView: View {
}
}
killApp(appId: appState.appInfo.bundleIdentifier) {
moveFilesToTrash(at: selectedItemsArray) {
withAnimation {
@@ -226,7 +224,7 @@ struct FilesView: View {
.buttonStyle(NavButtonBottomBarStyle(image: "trash.fill", help: "Uninstall"))
} else {
Text("No files selected to clean").font(.title).foregroundStyle(Color("mode")).opacity(0.2)
Text("No files selected to remove").font(.title).foregroundStyle(Color("mode")).opacity(0.2)
.padding(5)
}
@@ -234,17 +232,17 @@ struct FilesView: View {
Spacer()
Picker("", selection: $selectedOption) {
Text("􀅐").tag("Default")
Text("􀆃").tag("Size")
Image(systemName: "textformat.abc").tag("Default")
Image(systemName: "number").tag("Size")
}
.pickerStyle(SegmentedPickerStyle())
.frame(width: 100)
.help("Sorting Selection")
.help("Sorting alphabetically or by size")
}
}
.transition(.opacity)
.padding(20)
.padding([.horizontal, .bottom], 20)
}
@@ -254,14 +252,12 @@ struct FilesView: View {
func refreshAppList(_ appInfo: AppInfo) {
showPopover = false
let sortedApps = getSortedApps()
updateOnMain {
appState.sortedApps.userApps = []
appState.sortedApps.systemApps = []
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
appState.sortedApps.userApps = sortedApps.userApps
appState.sortedApps.systemApps = sortedApps.systemApps
}
Task(priority: .high){
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
// if instantSearch {
// loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
// }
}
}
}
+1 -43
View File
@@ -186,49 +186,7 @@ struct MiniAppView: View {
} else {
VStack(alignment: .center) {
ScrollView {
LazyVStack(alignment: .leading, spacing: 0, pinnedViews: [glass ? [] : .sectionHeaders]) {
if filteredUserApps.count > 0 {
// VStack {
// Header(title: "User", count: filteredUserApps.count, showPopover: $showPopover)
Section(header: Header(title: "User", count: filteredUserApps.count, showPopover: $showPopover)) {
ForEach(filteredUserApps, id: \.self) { appInfo in
AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
// .padding(.vertical,5)
if appInfo != filteredUserApps.last {
Divider().padding(.horizontal, 5)
}
}
}
// }
}
if filteredSystemApps.count > 0 {
// VStack {
// Header(title: "System", count: filteredSystemApps.count, showPopover: $showPopover)
Section(header: Header(title: "System", count: filteredSystemApps.count, showPopover: $showPopover)) {
ForEach(filteredSystemApps, id: \.self) { appInfo in
AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo)
.padding(.vertical,5)
if appInfo != filteredSystemApps.last {
Divider().padding(.horizontal, 5)
}
}
}
// }
}
}
.padding(.horizontal)
}
.scrollIndicators(.never)
AppsListView(search: $search, showPopover: $showPopover, filteredUserApps: filteredUserApps, filteredSystemApps: filteredSystemApps)
if appState.currentView != .empty {
SearchBarMiniBottom(search: $search).background(.ultraThinMaterial)
+38 -57
View File
@@ -37,8 +37,38 @@ struct ZombieView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity)
.transition(.opacity)
} else {
VStack() {
// Titlebar
if mini {
HStack(spacing: 0) {
Spacer()
Button("Rescan") {
updateOnMain {
appState.zombieFile = .empty
appState.showProgress.toggle()
if instantSearch {
reversePathsSearch(appState: appState, locations: locations)
} else {
loadAllPaths(allApps: appState.sortedApps.userApps + appState.sortedApps.systemApps, appState: appState, locations: locations, reverseAddon: true)
}
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
Button("Close") {
updateOnMain {
appState.appInfo = AppInfo.empty
search = ""
appState.currentView = .apps
showPopover = false
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "x.circle.fill", help: "Close"))
}
.padding([.horizontal, .top], 5)
}
VStack() {
// Main Group
HStack() {
@@ -74,7 +104,7 @@ struct ZombieView: View {
}
.padding(.horizontal, 20)
.padding(.top, 10)
.padding(.top, 0)
}
Divider()
@@ -116,8 +146,8 @@ struct ZombieView: View {
}
}
)) {
Text("􀃲").tag(true)
Text("􀂒").tag(false)
Image(systemName: "checkmark.square").tag(true)
Image(systemName: "square").tag(false)
}
.pickerStyle(SegmentedPickerStyle())
.frame(width: 100)
@@ -126,23 +156,6 @@ struct ZombieView: View {
Spacer()
if mini {
Button("Close") {
updateOnMain {
appState.appInfo = AppInfo.empty
search = ""
appState.currentView = .apps
showPopover = false
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "x.circle.fill", help: "Close"))
}
if !appState.selectedZombieItems.isEmpty {
Button("Remove") {
Task {
@@ -158,7 +171,6 @@ struct ZombieView: View {
}
let selectedItemsArray = Array(appState.selectedZombieItems)
.filter { !$0.path.contains(".Trash") }
killApp(appId: appState.appInfo.bundleIdentifier) {
moveFilesToTrash(at: selectedItemsArray) {
@@ -166,9 +178,6 @@ struct ZombieView: View {
showPopover = false
updateOnMain {
appState.isReminderVisible.toggle()
if sentinel {
launchctl(load: true)
}
}
}
}
@@ -183,53 +192,25 @@ struct ZombieView: View {
.padding(5)
}
Button("Rescan") {
updateOnMain {
appState.zombieFile = .empty
appState.showProgress.toggle()
if instantSearch {
reversePathsSearch(appState: appState, locations: locations)
} else {
loadAllPaths(allApps: appState.sortedApps.userApps + appState.sortedApps.systemApps, appState: appState, locations: locations, reverseAddon: true)
}
}
}
.buttonStyle(NavButtonBottomBarStyle(image: "arrow.counterclockwise.circle.fill", help: "Rescan files"))
Spacer()
Picker("", selection: $selectedOption) {
Text("􀅐").tag("Default")
Text("􀆃").tag("Size")
Image(systemName: "textformat.abc").tag("Default")
Image(systemName: "number").tag("Size")
}
.pickerStyle(SegmentedPickerStyle())
.frame(width: 100)
.help("Sorting Selection")
.help("Sorting alphabetically or by size")
}
}
.transition(.opacity)
.padding(20)
.padding([.horizontal, .bottom], 20)
}
}
}
func refreshAppList(_ appInfo: AppInfo) {
showPopover = false
let sortedApps = getSortedApps()
updateOnMain {
appState.sortedApps.userApps = []
appState.sortedApps.systemApps = []
appState.sortedApps.userApps = sortedApps.userApps
appState.sortedApps.systemApps = sortedApps.systemApps
}
Task(priority: .high){
loadAllPaths(allApps: sortedApps.userApps + sortedApps.systemApps, appState: appState, locations: locations)
}
}
}
+6 -2
View File
@@ -27,7 +27,7 @@ class NewWin: NSWindowController {
let window = Self.makeWindow(width: width, height: height)
window.backgroundColor = NSColor.controlBackgroundColor
self.init(window: window)
// Using Visual Effect to make titlebar fully transparent
@@ -47,7 +47,7 @@ class NewWin: NSWindowController {
window.titleVisibility = .hidden
window.titlebarAppearsTransparent = true
window.center()
window.title = "Support Admin"
window.title = "Pearcleaner"
}
@@ -65,10 +65,12 @@ class NewWin: NSWindowController {
}
private func makeNewView(appState: AppState, newWin: NewWindow) -> some View {
switch newWin {
case .update:
return AnyView(UpdateView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.black.opacity(0.2))
.environmentObject(appState))
case .no_update:
return AnyView(NoUpdateView()
@@ -77,6 +79,8 @@ class NewWin: NSWindowController {
case .perm:
return AnyView(PermView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.black.opacity(0.2))
.environmentObject(appState))
}
}
+54 -24
View File
@@ -17,34 +17,69 @@ struct PermView: View {
Spacer()
Text("Attention")
Text("Pearcleaner Permissions")
.font(.title)
.bold()
.padding(.vertical)
.padding(.top, 5)
Spacer()
}
.background(.red)
Text("Pearcleaner requires the following permissions:")
.font(.title3)
.fontWeight(.semibold)
.padding(.vertical)
ScrollView {
Text(" - Full Disk Access permission to find and delete files in non-user locations.\n - Accessibility permission to delete files via Finder which allows for the Undo function.\n\nAdd Pearcleaner in both Privacy panes via the + or by dragging the app over the pane. If the app is already pre-populated in the list, just toggle On. Restart app when both permissions are granted.")
.font(.body)
.multilineTextAlignment(.leading)
.padding(.horizontal, 20)
Divider()
.padding(.bottom)
Spacer()
VStack(alignment: .leading, spacing: 20) {
HStack(alignment: .top, spacing: 20) {
Image(systemName: "externaldrive")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(.gray)
Text("Full Disk Access permission to find and delete files in system paths")
.font(.callout)
.foregroundStyle(.gray)
}
HStack(alignment: .top, spacing: 20) {
Image(systemName: "accessibility")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(.gray)
Text("Accessibility permission to delete files via Finder")
.font(.callout)
.foregroundStyle(.gray)
}
HStack(alignment: .top, spacing: 20) {
Image(systemName: "info.circle")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(.gray)
Text("Add Pearcleaner in both Privacy panes via the + or by dragging the app over the pane. If the app is already pre-populated in the list, just toggle On if neeeded. Restart app when both permissions are granted")
.font(.callout)
.foregroundStyle(.gray)
}
}
.frame(height: 130)
.padding(.horizontal)
.padding(.horizontal, 20)
Spacer()
HStack {
Button(action: {
relaunchApp(afterDelay: 1)
}) {
Text("Restart")
}
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", help: "Restart", color: .red))
.padding()
Button(action: {
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") {
NSWorkspace.shared.open(url)
@@ -52,7 +87,8 @@ struct PermView: View {
}) {
Text("Full Disk Access")
}
.buttonStyle(WindowActionButton(action: .accept))
.buttonStyle(SimpleButtonBrightStyle(icon: "externaldrive", help: "Full Disk", color: .accentColor))
.padding()
Button(action: {
@@ -62,16 +98,10 @@ struct PermView: View {
}) {
Text("Accessibility")
}
.buttonStyle(WindowActionButton(action: .accept))
.buttonStyle(SimpleButtonBrightStyle(icon: "accessibility", help: "Accessibility", color: .accentColor))
.padding()
Button(action: {
relaunchApp(afterDelay: 1)
}) {
Text("Restart")
}
.buttonStyle(WindowActionButton(action: .cancel))
.padding()
}
+28 -37
View File
@@ -13,7 +13,7 @@ struct UpdateView: View {
let isAppInAppsDir = isAppInApplicationsDir()
var body: some View {
VStack {
VStack(spacing: 5) {
HStack {
Spacer()
@@ -29,36 +29,28 @@ struct UpdateView: View {
Text("v\(appState.releases.first?.tag_name ?? "")")
.font(.title3)
.fontWeight(.semibold)
.foregroundStyle(Color.white.opacity(0.5))
.opacity(0.5)
.padding()
}
.background(.blue)
Spacer()
Divider()
.padding([.horizontal])
ScrollView {
Text("\(appState.releases.first?.body ?? "No release information")")
Text("\(appState.releases.first?.modifiedBody ?? "No release information")")
.font(.body)
.multilineTextAlignment(.leading)
.padding(.horizontal, 20)
.padding(20)
}
.frame(height: 160)
.padding()
Spacer()
if !isAppInAppsDir {
HStack {
Text("Pearcleaner not in /Applications directory. Updater could have permission issues updating files.")
.font(.footnote)
.foregroundStyle(.red)
.padding()
.lineLimit(2)
.fixedSize(horizontal: false, vertical: true)
}
Text("Please move Pearcleaner to the **Applications** folder before updating!")
.font(.callout)
.foregroundStyle(.red)
.padding(.top, 0)
}
VStack() {
@@ -73,7 +65,7 @@ struct UpdateView: View {
Button(action: NewWin.close) {
Text("Cancel")
}
.buttonStyle(WindowActionButton(action: .cancel))
.buttonStyle(SimpleButtonBrightStyle(icon: "x.circle", help: "Close", color: .red))
if appState.progressBar.1 != 1.0 {
Button(action: {
@@ -81,7 +73,7 @@ struct UpdateView: View {
}) {
Text("Update")
}
.buttonStyle(WindowActionButton(action: .accept))
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.down.circle", help: "Update", color: .accentColor))
} else {
Button(action: {
NewWin.close()
@@ -89,7 +81,7 @@ struct UpdateView: View {
}) {
Text("Restart")
}
.buttonStyle(WindowActionButton(action: .accept))
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", help: "Restart", color: .accentColor))
}
}
@@ -99,7 +91,7 @@ struct UpdateView: View {
}
.padding(EdgeInsets(top: -25, leading: 0, bottom: 25, trailing: 0))
}
@@ -110,9 +102,9 @@ struct UpdateView: View {
struct NoUpdateView: View {
@EnvironmentObject var appState: AppState
var body: some View {
VStack {
VStack(spacing: 5) {
HStack {
Spacer()
@@ -128,19 +120,19 @@ struct NoUpdateView: View {
Text("v\(appState.releases.first?.tag_name ?? "")")
.font(.title3)
.fontWeight(.semibold)
.foregroundStyle(Color.white.opacity(0.5))
.opacity(0.5)
.padding()
}
.background(.blue)
Spacer()
Divider()
.padding([.horizontal])
Text("Pearcleaner is on the most current release available, but you may force a re-download of the same version below.")
.font(.body)
.lineLimit(3)
.multilineTextAlignment(.leading)
.padding(20)
.padding()
@@ -160,15 +152,15 @@ struct NoUpdateView: View {
}) {
Text("Force Update")
}
.buttonStyle(WindowActionButton(action: .cancel))
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.down.circle", help: "Force-Update", color: .red))
Button(action: {
NewWin.close()
}) {
Text("Okay")
}
.buttonStyle(WindowActionButton(action: .accept))
.buttonStyle(SimpleButtonBrightStyle(icon: "checkmark.circle", help: "Ok", color: .accentColor))
}
@@ -179,7 +171,7 @@ struct NoUpdateView: View {
}) {
Text("Restart")
}
.buttonStyle(WindowActionButton(action: .accept))
.buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", help: "Restart", color: .accentColor))
}
}
.padding(.bottom)
@@ -190,7 +182,6 @@ struct NoUpdateView: View {
.padding(EdgeInsets(top: -25, leading: 0, bottom: 25, trailing: 0))
}
+11 -3
View File
@@ -6,8 +6,8 @@
//
import Foundation
import FileWatcher
import AppKit
import FileWatcher
// Start Trash monitoring
fileWatcher()
@@ -31,8 +31,16 @@ func checkApp(file: String) {
let app = URL(fileURLWithPath: file)
let appExt = app.pathExtension
if appExt == "app" {
if FileManager.default.isInTrash(app) {
NSWorkspace.shared.open(URL(string: "pear://com.alienator88.Pearcleaner?path=\(file)")!)
if let appBundle = Bundle(url: app) {
if appBundle.bundleIdentifier == "com.alienator88.Pearcleaner" {
return
} else {
if FileManager.default.isInTrash(app) {
NSWorkspace.shared.open(URL(string: "pear://com.alienator88.Pearcleaner?path=\(file)")!)
}
}
} else {
print("Error: Unable to get bundle information for \(file)")
}
}
}