diff --git a/Pearcleaner.xcodeproj/project.pbxproj b/Pearcleaner.xcodeproj/project.pbxproj index e0f7ca6..a79f83c 100644 --- a/Pearcleaner.xcodeproj/project.pbxproj +++ b/Pearcleaner.xcodeproj/project.pbxproj @@ -8,8 +8,6 @@ /* Begin PBXBuildFile section */ C7045A282B03E71D00376976 /* MiniMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A272B03E71D00376976 /* MiniMode.swift */; }; - C7045A322B068AD700376976 /* NewWin.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A312B068AD700376976 /* NewWin.swift */; }; - C7045A362B068DD700376976 /* PermView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7045A352B068DD700376976 /* PermView.swift */; }; 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 */; }; @@ -22,7 +20,6 @@ C76D08512AF850F300D07867 /* About.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08502AF850F300D07867 /* About.swift */; }; C76D08552AF89CDE00D07867 /* RegularMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76D08542AF89CDE00D07867 /* RegularMode.swift */; }; C76EDCD62C45DC82000CF29D /* AlinFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = C76EDCD52C45DC82000CF29D /* AlinFoundation */; }; - C771CB762BD028AF00C33A18 /* SegmentedPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C771CB752BD028AF00C33A18 /* SegmentedPicker.swift */; }; C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90032AF18E2E009CC655 /* PearcleanerApp.swift */; }; C77B90082AF18E2F009CC655 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C77B90072AF18E2F009CC655 /* Assets.xcassets */; }; C77B90162AF19377009CC655 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C77B90152AF19377009CC655 /* AppState.swift */; }; @@ -36,20 +33,15 @@ C79947962C34A794007279CC /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = C79947952C34A794007279CC /* Tips.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 */; }; - C7D15BBE2BE85F4D007D57CF /* FeatureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D15BBD2BE85F4D007D57CF /* FeatureView.swift */; }; C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D492AFEB26700C7ED9E /* AppListItems.swift */; }; C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7D31D502AFF00F300C7ED9E /* Locations.swift */; }; - C7DC1FEE2B9F7D3D009AC317 /* features.json in Resources */ = {isa = PBXBuildFile; fileRef = C7DC1FED2B9F7D3D009AC317 /* features.json */; }; C7DD49EA2BAB4D8100CCBA16 /* AppInfoFetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DD49E92BAB4D8100CCBA16 /* AppInfoFetch.swift */; }; C7DD49EE2BAB7F6000CCBA16 /* ReversePathsFetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DD49ED2BAB7F6000CCBA16 /* ReversePathsFetch.swift */; }; C7DD49F02BABA14400CCBA16 /* Folders.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DD49EF2BABA14400CCBA16 /* Folders.swift */; }; C7DE672B2BA6343D00EB1633 /* MenuBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */; }; C7EA06332C1CBE2000F872FC /* ConditionBuilderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7EA06322C1CBE2000F872FC /* ConditionBuilderView.swift */; }; C7ED86B12BA8A35500F13DE4 /* Interface.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ED86B02BA8A35500F13DE4 /* Interface.swift */; }; - C7ED86B32BA8ED8800F13DE4 /* Trash.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7ED86B22BA8ED8800F13DE4 /* Trash.swift */; }; C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7F539372AF60865007DF1B2 /* Utilities.swift */; }; C7FB173B2B96321300B96F9A /* AppsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7FB173A2B96321300B96F9A /* AppsListView.swift */; }; C7FEBA112BDC422200AE195F /* AppSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7FEBA102BDC422200AE195F /* AppSearchView.swift */; }; @@ -100,8 +92,6 @@ /* Begin PBXFileReference section */ C7045A272B03E71D00376976 /* MiniMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiniMode.swift; sourceTree = ""; }; - C7045A312B068AD700376976 /* NewWin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewWin.swift; sourceTree = ""; }; - C7045A352B068DD700376976 /* PermView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermView.swift; sourceTree = ""; }; C71848432B8D2D600046CB13 /* ZombieView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZombieView.swift; sourceTree = ""; }; C72893042AFD42E600C8C1CD /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = ""; }; C728930F2AFD51EA00C8C1CD /* PearcleanerSentinel */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = PearcleanerSentinel; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -113,7 +103,6 @@ C76D08492AF83C6E00D07867 /* General.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = General.swift; sourceTree = ""; }; C76D08502AF850F300D07867 /* About.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = About.swift; sourceTree = ""; }; C76D08542AF89CDE00D07867 /* RegularMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegularMode.swift; sourceTree = ""; }; - C771CB752BD028AF00C33A18 /* SegmentedPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedPicker.swift; sourceTree = ""; }; C77B90002AF18E2E009CC655 /* Pearcleaner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Pearcleaner.app; sourceTree = BUILT_PRODUCTS_DIR; }; C77B90032AF18E2E009CC655 /* PearcleanerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PearcleanerApp.swift; sourceTree = ""; }; C77B90072AF18E2F009CC655 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -133,20 +122,15 @@ C79947952C34A794007279CC /* Tips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tips.swift; sourceTree = ""; }; C7A27E7F2AFD70DD00166168 /* PearcleanerSentinel.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PearcleanerSentinel.entitlements; sourceTree = ""; }; C7A27E802AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.alienator88.PearcleanerSentinel.plist; sourceTree = ""; }; - C7A9CE462B89164700EB6E78 /* Authorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Authorization.swift; sourceTree = ""; }; - C7ACADE52B92A737000B5845 /* Features.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Features.swift; sourceTree = ""; }; C7CF47232B3B3F1700979C5F /* WindowSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowSettings.swift; sourceTree = ""; }; - C7D15BBD2BE85F4D007D57CF /* FeatureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureView.swift; sourceTree = ""; }; C7D31D492AFEB26700C7ED9E /* AppListItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppListItems.swift; sourceTree = ""; }; C7D31D502AFF00F300C7ED9E /* Locations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Locations.swift; sourceTree = ""; }; - C7DC1FED2B9F7D3D009AC317 /* features.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = features.json; sourceTree = SOURCE_ROOT; }; C7DD49E92BAB4D8100CCBA16 /* AppInfoFetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppInfoFetch.swift; sourceTree = ""; }; C7DD49ED2BAB7F6000CCBA16 /* ReversePathsFetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReversePathsFetch.swift; sourceTree = ""; }; C7DD49EF2BABA14400CCBA16 /* Folders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Folders.swift; sourceTree = ""; }; C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarItem.swift; sourceTree = ""; }; C7EA06322C1CBE2000F872FC /* ConditionBuilderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionBuilderView.swift; sourceTree = ""; }; C7ED86B02BA8A35500F13DE4 /* Interface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interface.swift; sourceTree = ""; }; - C7ED86B22BA8ED8800F13DE4 /* Trash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Trash.swift; sourceTree = ""; }; C7F539372AF60865007DF1B2 /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; }; C7FB173A2B96321300B96F9A /* AppsListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppsListView.swift; sourceTree = ""; }; C7FEBA102BDC422200AE195F /* AppSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSearchView.swift; sourceTree = ""; }; @@ -179,18 +163,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - C7045A302B068ABE00376976 /* Windows */ = { - isa = PBXGroup; - children = ( - C7045A312B068AD700376976 /* NewWin.swift */, - C7D15BBD2BE85F4D007D57CF /* FeatureView.swift */, - C7045A352B068DD700376976 /* PermView.swift */, - C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */, - C7CF47232B3B3F1700979C5F /* WindowSettings.swift */, - ); - path = Windows; - sourceTree = ""; - }; C72893072AFD442600C8C1CD /* Logic */ = { isa = PBXGroup; children = ( @@ -206,9 +178,6 @@ C77B901B2AF193A3009CC655 /* Styles.swift */, C7D31D502AFF00F300C7ED9E /* Locations.swift */, C74FDC622BCD833D00B8960F /* Conditions.swift */, - C7A9CE462B89164700EB6E78 /* Authorization.swift */, - C7ACADE52B92A737000B5845 /* Features.swift */, - C7ED86B22BA8ED8800F13DE4 /* Trash.swift */, ); path = Logic; sourceTree = ""; @@ -246,7 +215,6 @@ C76D084F2AF84E0100D07867 /* Resources */ = { isa = PBXGroup; children = ( - C7DC1FED2B9F7D3D009AC317 /* features.json */, C7A27E802AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist */, C77B90142AF18E7C009CC655 /* Info.plist */, C77B90072AF18E2F009CC655 /* Assets.xcassets */, @@ -255,14 +223,6 @@ path = Resources; sourceTree = ""; }; - C771CB742BD028A000C33A18 /* Picker */ = { - isa = PBXGroup; - children = ( - C771CB752BD028AF00C33A18 /* SegmentedPicker.swift */, - ); - path = Picker; - sourceTree = ""; - }; C77B8FF72AF18E2E009CC655 = { isa = PBXGroup; children = ( @@ -292,7 +252,6 @@ C76D084F2AF84E0100D07867 /* Resources */, C76D08462AF83C2F00D07867 /* Settings */, C77B901F2AF1B390009CC655 /* Views */, - C7045A302B068ABE00376976 /* Windows */, C72893072AFD442600C8C1CD /* Logic */, ); path = Pearcleaner; @@ -301,7 +260,6 @@ C77B901F2AF1B390009CC655 /* Views */ = { isa = PBXGroup; children = ( - C771CB742BD028A000C33A18 /* Picker */, C77B90222AF2D616009CC655 /* FilesView.swift */, C71848432B8D2D600046CB13 /* ZombieView.swift */, C76D08542AF89CDE00D07867 /* RegularMode.swift */, @@ -310,6 +268,8 @@ C7FEBA102BDC422200AE195F /* AppSearchView.swift */, C7D31D492AFEB26700C7ED9E /* AppListItems.swift */, C7EA06322C1CBE2000F872FC /* ConditionBuilderView.swift */, + C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */, + C7CF47232B3B3F1700979C5F /* WindowSettings.swift */, ); path = Views; sourceTree = ""; @@ -439,7 +399,6 @@ files = ( C77B90082AF18E2F009CC655 /* Assets.xcassets in Resources */, C7A27E812AFD7C4600166168 /* com.alienator88.PearcleanerSentinel.plist in Resources */, - C7DC1FEE2B9F7D3D009AC317 /* features.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -471,10 +430,8 @@ C7FB173B2B96321300B96F9A /* AppsListView.swift in Sources */, C77B90162AF19377009CC655 /* AppState.swift in Sources */, C7DD49EE2BAB7F6000CCBA16 /* ReversePathsFetch.swift in Sources */, - C7045A322B068AD700376976 /* NewWin.swift in Sources */, C7EA06332C1CBE2000F872FC /* ConditionBuilderView.swift in Sources */, C7DD49F02BABA14400CCBA16 /* Folders.swift in Sources */, - C7ED86B32BA8ED8800F13DE4 /* Trash.swift in Sources */, C7D31D512AFF00F300C7ED9E /* Locations.swift in Sources */, C77B901A2AF1938F009CC655 /* Logic.swift in Sources */, C77B901C2AF193A3009CC655 /* Styles.swift in Sources */, @@ -486,19 +443,14 @@ C7DE672B2BA6343D00EB1633 /* MenuBarItem.swift in Sources */, C76D084A2AF83C6E00D07867 /* General.swift in Sources */, C77B90042AF18E2E009CC655 /* PearcleanerApp.swift in Sources */, - C7045A362B068DD700376976 /* PermView.swift in Sources */, C76D08512AF850F300D07867 /* About.swift in Sources */, C77B90182AF19382009CC655 /* AppCommands.swift in Sources */, C7D31D4A2AFEB26700C7ED9E /* AppListItems.swift in Sources */, C77B901E2AF1A9DB009CC655 /* SettingsWindow.swift in Sources */, - C771CB762BD028AF00C33A18 /* SegmentedPicker.swift in Sources */, C74412C72BBF249000DDFCA8 /* AppPathsFetch.swift in Sources */, C74FDC632BCD833D00B8960F /* Conditions.swift in Sources */, - C7ACADE62B92A737000B5845 /* Features.swift in Sources */, C7F539382AF60865007DF1B2 /* Utilities.swift in Sources */, C76D08552AF89CDE00D07867 /* RegularMode.swift in Sources */, - C7D15BBE2BE85F4D007D57CF /* FeatureView.swift in Sources */, - C7A9CE472B89164700EB6E78 /* Authorization.swift in Sources */, C7FEBA112BDC422200AE195F /* AppSearchView.swift in Sources */, C7CF47242B3B3F1700979C5F /* WindowSettings.swift in Sources */, C77B90232AF2D616009CC655 /* FilesView.swift in Sources */, @@ -699,6 +651,7 @@ MTL_FAST_MATH = YES; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; @@ -714,6 +667,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = ""; @@ -753,6 +707,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = ""; diff --git a/Pearcleaner/Logic/AppState.swift b/Pearcleaner/Logic/AppState.swift index d9010ac..cbaf64b 100644 --- a/Pearcleaner/Logic/AppState.swift +++ b/Pearcleaner/Logic/AppState.swift @@ -18,18 +18,12 @@ class AppState: ObservableObject { @Published var zombieFile: ZombieFile @Published var sortedApps: [AppInfo] = [] @Published var selectedItems = Set() - @Published var alertType = AlertType.off @Published var currentView = CurrentDetailsView.empty @Published var showAlert: Bool = false @Published var sidebar: Bool = true - @Published var progressBar: (String, Double) = ("Ready", 0.0) @Published var reload: Bool = false @Published var showProgress: Bool = false @Published var finderExtensionEnabled: Bool = false -// @Published var updateAvailable: Bool = false - @Published var featureAvailable: Bool = false -// @Published var permissionsOkay: Bool = true -// @Published var permissionResults: PermissionsCheckResults? @Published var showUninstallAlert: Bool = false @Published var oneShotMode: Bool = false @Published var showConditionBuilder: Bool = false @@ -178,55 +172,3 @@ enum CurrentDetailsView:Int case apps case zombie } - -enum NewWindow:Int -{ - case perm - case feature -} - - -enum AlertType:Int -{ - case diskAccess - case update - case no_update - case restartApp - case off -} - - - -enum DisplayMode: Int, CaseIterable { - case system, dark, light - - var colorScheme: ColorScheme? { - get { - switch self { - case .system: return nil - case .dark: return ColorScheme.dark - case .light: return ColorScheme.light - } - } - set { - switch newValue { - case .none: - self = .system - case .dark?: - self = .dark - case .light?: - self = .light - default: - break - } - } - } - - var description: String { - switch self { - case .system: return "System" - case .dark: return "Dark" - case .light: return "Light" - } - } -} diff --git a/Pearcleaner/Logic/Authorization.swift b/Pearcleaner/Logic/Authorization.swift deleted file mode 100644 index 6203d1d..0000000 --- a/Pearcleaner/Logic/Authorization.swift +++ /dev/null @@ -1,113 +0,0 @@ -// -// Authorization.swift -// Pearcleaner -// -// Created by Alin Lupascu on 2/23/24. -// - -//import Foundation -//import Swift -// -//// https://github.com/x13a/authorization-swift -// -//public struct Authorization { -// -// public enum Error: Swift.Error { -// case create(OSStatus) -// case copyRights(OSStatus) -// case exec(OSStatus) -// } -// -// public static func executeWithPrivileges( -// _ command: String -// ) -> Result { -// -// let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2) -// var fn: @convention(c) ( -// AuthorizationRef, -// UnsafePointer, // path -// AuthorizationFlags, -// UnsafePointer?>, // args -// UnsafeMutablePointer>? -// ) -> OSStatus -// fn = unsafeBitCast( -// dlsym(RTLD_DEFAULT, "AuthorizationExecuteWithPrivileges"), -// to: type(of: fn) -// ) -// -// var authorizationRef: AuthorizationRef? = nil -// var err = AuthorizationCreate(nil, nil, [], &authorizationRef) -// guard err == errAuthorizationSuccess else { -// return .failure(.create(err)) -// } -// defer { AuthorizationFree(authorizationRef!, [.destroyRights]) } -// -// var components = command.components(separatedBy: " ") -// var path = components.remove(at: 0).cString(using: .utf8)! -// let name = kAuthorizationRightExecute.cString(using: .utf8)! -// -// var items: AuthorizationItem = name.withUnsafeBufferPointer { nameBuf in -// path.withUnsafeBufferPointer { pathBuf in -// let pathPtr = -// UnsafeMutableRawPointer(mutating: pathBuf.baseAddress!) -// return AuthorizationItem( -// name: nameBuf.baseAddress!, -// valueLength: path.count, -// value: pathPtr, -// flags: 0 -// ) -// } -// } -// -// var rights: AuthorizationRights = -// withUnsafeMutablePointer(to: &items) { items in -// return AuthorizationRights(count: 1, items: items) -// } -// -// let flags: AuthorizationFlags = [ -// .interactionAllowed, -// .preAuthorize, -// .extendRights, -// ] -// -// err = AuthorizationCopyRights( -// authorizationRef!, -// &rights, -// nil, -// flags, -// nil -// ) -// guard err == errAuthorizationSuccess else { -// return .failure(.copyRights(err)) -// } -// -// let rest = components.map { $0.cString(using: .utf8)! } -// var args = Array?>( -// repeating: nil, -// count: rest.count + 1 -// ) -// for (idx, arg) in rest.enumerated() { -// args[idx] = UnsafePointer?(arg) -// } -// -// var file = FILE() -// let fh: FileHandle? -// -// (err, fh) = withUnsafeMutablePointer(to: &file) { file in -// var pipe = file -// let err = fn(authorizationRef!, &path, [], &args, &pipe) -// guard err == errAuthorizationSuccess else { -// return (err, nil) -// } -// let fh = FileHandle( -// fileDescriptor: fileno(pipe), -// closeOnDealloc: true -// ) -// return (err, fh) -// } -// guard err == errAuthorizationSuccess else { -// return .failure(.exec(err)) -// } -// return .success(fh!) -// } -//} diff --git a/Pearcleaner/Logic/Features.swift b/Pearcleaner/Logic/Features.swift deleted file mode 100644 index bc5d846..0000000 --- a/Pearcleaner/Logic/Features.swift +++ /dev/null @@ -1,113 +0,0 @@ -// -// Features.swift -// Pearcleaner -// -// Created by Alin Lupascu on 3/1/24. -// - -import Foundation -import SwiftUI -import AlinFoundation - - -func getFeatures(appState: AppState, features: Binding) { - let url = URL(string: "https://api.github.com/repos/alienator88/Pearcleaner/contents/features.json")! - var request = URLRequest(url: url) - request.setValue("application/vnd.github.VERSION.raw", forHTTPHeaderField: "Accept") - request.setValue("2022-11-28", forHTTPHeaderField: "X-GitHub-Api-Version") - - URLSession.shared.dataTask(with: request) { data, response, error in - if let data = data { - do { - let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) - if let jsonDict = jsonObject as? [String: String], - let bundleVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String, - let featureText = jsonDict[bundleVersion] { - if features.wrappedValue != featureText.featureFormat() { - updateOnMain { - features.wrappedValue = featureText.featureFormat() - appState.featureAvailable = true -// show.wrappedValue = true - } - } else { - updateOnMain { - appState.featureAvailable = false - } -// show.wrappedValue = false -// print("Same version") - } - - } else { - updateOnMain { - appState.featureAvailable = false - } -// show.wrappedValue = false -// print("No features for version found") - } - } catch { - printOS("Error reading features JSON from GitHub: \(error.localizedDescription)") - } - } else { - printOS("Error reading features JSON from GitHub: \(error?.localizedDescription ?? "Unknown error")") - } - }.resume() -} - - - - - -struct FeatureNotificationView: View { - let appState: AppState - @State private var hovered: Bool = false - - var body: some View { - HStack { - - Text("New Features!") - .font(.callout) - .opacity(0.5) - .padding(.leading, 7) - - Spacer() - - HStack(alignment: .center) { - Image(systemName: !hovered ? "star" : "star.fill") - .resizable() - .scaledToFit() - .frame(width: 14, height: 14) - .animation(.easeInOut(duration: 0.2), value: hovered) - .foregroundStyle(.white) - - - Text("Check") - .foregroundStyle(.white) - - } - .padding(3) - .onHover { hovering in - withAnimation() { - hovered = hovering - } - } - .onTapGesture { - NewWin.show(appState: appState, width: 500, height: 400, newWin: .feature) - updateOnMain { - appState.featureAvailable = false - } - } - .help("View latest features") - .padding(.horizontal, 5) - .padding(.vertical, 4) - .background(Color("pear")) - .clipShape(RoundedRectangle(cornerRadius: 6)) - - } - .frame(height: 30) - .padding(5) - .background(.primary.opacity(0.05)) - .clipShape(RoundedRectangle(cornerRadius: 6)) - .padding(.horizontal) - .padding(.bottom) - } -} diff --git a/Pearcleaner/Logic/Styles.swift b/Pearcleaner/Logic/Styles.swift index 2a7dde9..26dd34f 100644 --- a/Pearcleaner/Logic/Styles.swift +++ b/Pearcleaner/Logic/Styles.swift @@ -146,59 +146,6 @@ struct RescanButton: ButtonStyle { -public struct NewFeatureView: View { - var text: String - var mini: Bool - @Binding var showFeature: Bool - - public var body: some View { - VStack { - HStack { - Image(systemName: "star") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 20, height: 20) - Text("New features for v\((Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String)!)!") - .font(.headline).bold() - Spacer() - Button("") { - withAnimation(Animation.easeInOut(duration: 0.5)) { - showFeature = false - } - } - .buttonStyle(SimpleButtonStyle(icon: "x.circle.fill", help: "Close", color: (.primary.opacity(0.5)))) - .onHover { isHovered in - if isHovered { - NSCursor.pointingHand.push() - } else { - NSCursor.pop() - } - } - } - .padding() - - ScrollView() { - Text(text) - .font(.callout) - .multilineTextAlignment(.leading) - .padding(.trailing) - } - .padding(.horizontal) - .padding(.bottom) - - } - .frame(maxWidth: mini ? 200 : 400, maxHeight: mini ? 300 : 250) - .background(.ultraThinMaterial) - .cornerRadius(10) - .overlay( - RoundedRectangle(cornerRadius: 10) - .strokeBorder(.primary.opacity(0.2), lineWidth: 0.6) - ) - .padding() - } -} - - public struct SlideableDivider: View { @EnvironmentObject private var themeManager: ThemeManager @Binding var dimension: Double diff --git a/Pearcleaner/Logic/Trash.swift b/Pearcleaner/Logic/Trash.swift deleted file mode 100644 index 4d746e5..0000000 --- a/Pearcleaner/Logic/Trash.swift +++ /dev/null @@ -1,100 +0,0 @@ -// -// Trash.swift -// Pearcleaner -// -// Created by Alin Lupascu on 3/18/24. -// - - - -import Foundation - -//class TrashManager { -// private var fileLocations: [URL: URL] = [:] // Maps original location to trash location -// -// // Function to move files to Trash and track their original locations -// func delete(fileURLs: [URL]) { -// let fileManager = FileManager.default -// let trashURL = try! fileManager.url(for: .trashDirectory, in: .allDomainsMask, appropriateFor: nil, create: false) -// -// for originalURL in fileURLs { -// let destinationURL = trashURL.appendingPathComponent(originalURL.lastPathComponent) -// -// do { -// // The resultingItemURL is the new location of the item in the trash. -// var resultingItemURL: NSURL? -// try fileManager.trashItem(at: originalURL, resultingItemURL: &resultingItemURL) -// if let trashPath = resultingItemURL as URL? { -// // Track the original URL and its corresponding location in the Trash -// fileLocations[originalURL] = trashPath -// } -// } catch { -// print("Error moving file to Trash: \(error)") -// } -// } -// } -// -// // Function to undo the deletion of files, moving them back from Trash to their original locations -// func undoDelete() { -// let fileManager = FileManager.default -// -// for (originalURL, trashURL) in fileLocations { -// do { -// // Attempt to move the item back to its original location -// try fileManager.moveItem(at: trashURL, to: originalURL) -// } catch { -// print("Error moving file back from Trash: \(error)") -// } -// } -// -// // Clear the tracking dictionary after undoing the deletions -// fileLocations.removeAll() -// } -//} - - - - - - -//import Foundation -// -//func moveToTrash(fileURLs: [URL], completion: @escaping () -> Void = {}) { -// for fileURL in fileURLs { -// do { -// try _ = fileURL.checkResourceIsReachable() -// } catch { -// printOS(error.localizedDescription) -// } -// } -// let target: NSAppleEventDescriptor = .init(bundleIdentifier: "com.apple.finder") -// let event: NSAppleEventDescriptor = .init(eventClass: kAECoreSuite, -// eventID: AEEventID(kAEDelete), -// targetDescriptor: target, -// returnID: AEReturnID(kAutoGenerateReturnID), -// transactionID: AETransactionID(kAnyTransactionID)) -// let fileList: NSAppleEventDescriptor = fileURLs.enumerated().reduce(into: .init(listDescriptor: ())) { -// (result: inout NSAppleEventDescriptor, element: (offset: Int, element: URL)) in -// if let nativePath: NSAppleEventDescriptor = .init( -// descriptorType: typeFileURL, -// data: element.element.absoluteString.data(using: .utf8) -// ) { -// result.insert(nativePath, at: element.offset + 1) -// } -// } -// -// event.setParam(fileList, forKeyword: keyDirectObject) -// -// do { -// try event.sendEvent(options: .noReply, timeout: TimeInterval(kAEDefaultTimeout)) -// } catch let error as NSError { -// if case -600 = error.code { -// printOS("Finder is not running.") -// } else { -// printOS(error.description) -// } -// } -// -// completion() -//} - diff --git a/Pearcleaner/Logic/Utilities.swift b/Pearcleaner/Logic/Utilities.swift index 3fe56fe..a95961a 100644 --- a/Pearcleaner/Logic/Utilities.swift +++ b/Pearcleaner/Logic/Utilities.swift @@ -10,27 +10,6 @@ import SwiftUI import AlinFoundation import AppKit -// Make updates on main thread -//func updateOnMain(after delay: Double? = nil, _ updates: @escaping () -> Void) { -// if let delay = delay { -// DispatchQueue.main.asyncAfter(deadline: .now() + delay) { -// updates() -// } -// } else { -// DispatchQueue.main.async { -// updates() -// } -// } -//} -// -// -//// Execute functions on background thread -//func updateOnBackground(_ updates: @escaping () -> Void) { -// DispatchQueue.global(qos: .userInitiated).async { -// updates() -// } -//} - // Reload apps list func reloadAppsList(appState: AppState, fsm: FolderSettingsManager) { appState.reload = true @@ -54,60 +33,6 @@ func resizeWindowAuto(windowSettings: WindowSettings, title: String) { } - -// Check app directory based on user permission -//func checkAppDirectoryAndUserRole(completion: @escaping ((isInCorrectDirectory: Bool, isAdmin: Bool)) -> Void) { -// isCurrentUserAdmin { isAdmin in -// let bundlePath = Bundle.main.bundlePath as NSString -// let applicationsDir = "/Applications" -// let userApplicationsDir = "\(home)/Applications" -// -// var isInCorrectDirectory = false -// -// if isAdmin { -// // Admins can have the app in either /Applications or ~/Applications -// isInCorrectDirectory = bundlePath.deletingLastPathComponent == applicationsDir || -// bundlePath.deletingLastPathComponent == userApplicationsDir -// } else { -// // Standard users should only have the app in ~/Applications -// isInCorrectDirectory = bundlePath.deletingLastPathComponent == userApplicationsDir -// } -// -// // Return both conditions: if the app is in the correct directory and if the user is an admin -// completion((isInCorrectDirectory, isAdmin)) -// } -//} -// -// -//// Check if user is admin or standard user -//func isCurrentUserAdmin(completion: @escaping (Bool) -> Void) { -// let process = Process() -// process.executableURL = URL(fileURLWithPath: "/bin/zsh") // Using zsh, macOS default shell -// process.arguments = ["-c", "groups $(whoami) | grep -q ' admin '"] -// -// process.terminationHandler = { process in -// // On macOS, a process's exit status of 0 indicates success (admin group found in this context) -// completion(process.terminationStatus == 0) -// } -// -// do { -// try process.run() -// } catch { -// print("Failed to execute command: \(error)") -// completion(false) -// } -//} - -// Check if appearance is dark mode -//func isDarkMode() -> Bool { -// return NSApp.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua -//} -// -//// Set app color mode -//func setAppearance(mode: NSAppearance.Name) { -// NSApp.appearance = NSAppearance(named: mode) -//} - // Check if Pearcleaner has any windows open func hasWindowOpen() -> Bool { for window in NSApp.windows where window.title == "Pearcleaner" { @@ -116,14 +41,7 @@ func hasWindowOpen() -> Bool { return false } -// Find and hide/show main app window when using menubar item -//func findAndHideWindows(named titles: [String]) { -// for title in titles { -// if let window = NSApp.windows.first(where: { $0.title == title }) { -// window.close() -// } -// } -//} + func findAndSetWindowFrame(named titles: [String], windowSettings: WindowSettings) { windowSettings.registerDefaultWindowSettings() { @@ -138,22 +56,6 @@ func findAndSetWindowFrame(named titles: [String], windowSettings: WindowSetting } -//func findAndShowWindows(named titles: [String]) { -// for title in titles { -// if let window = NSApp.windows.first(where: { $0.title == title }) { -// window.makeKeyAndOrderFront(nil) -// } -// } -//} - -// Copy to clipboard -//func copyToClipboard(text: String) { -// let pasteboard = NSPasteboard.general -// pasteboard.clearContents() -// pasteboard.setString(text, forType: .string) -//} - - // Brew cleanup @@ -222,17 +124,6 @@ func saveURLsToFile(urls: Set, appState: AppState) { } -// Check if symlink -//func isSymlink(atPath path: URL) -> Bool { -// do { -// let _ = try path.checkResourceIsReachable() -// let resourceValues = try path.resourceValues(forKeys: [.isSymbolicLinkKey]) -// return resourceValues.isSymbolicLink == true -// } catch { -// return false -// } -//} - // Open trash folder func openTrash() { @@ -295,64 +186,6 @@ func checkAppBundleArchitecture(at appBundlePath: String) -> Arch { -// Convert icon to png so colors render correctly -//func convertICNSToPNG(icon: NSImage, size: NSSize) -> NSImage? { -// // Resize the icon to the specified size -// let resizedIcon = NSImage(size: size) -// resizedIcon.lockFocus() -// icon.draw(in: NSRect(x: 0, y: 0, width: size.width, height: size.height)) -// resizedIcon.unlockFocus() -// -// // Convert the resized icon to PNG format -// if let resizedImageData = resizedIcon.tiffRepresentation, -// let resizedBitmap = NSBitmapImageRep(data: resizedImageData), -// let pngData = resizedBitmap.representation(using: .png, properties: [:]) { -// return NSImage(data: pngData) -// } -// -// return nil -//} - -// Get icon for files and folders -//func getIconForFileOrFolder(atPath path: URL) -> Image? { -// return Image(nsImage: NSWorkspace.shared.icon(forFile: path.path)) -//} -// -//func getIconForFileOrFolderNS(atPath path: URL) -> NSImage? { -// return NSWorkspace.shared.icon(forFile: path.path) -//} - - -// Get average color from image -//extension NSImage { -// var averageColor: NSColor? { -// guard let tiffData = self.tiffRepresentation, let bitmapImage = NSBitmapImageRep(data: tiffData), let inputImage = CIImage(bitmapImageRep: bitmapImage) else { return nil } -// -// let extentVector = CIVector(x: inputImage.extent.origin.x, y: inputImage.extent.origin.y, z: inputImage.extent.size.width, w: inputImage.extent.size.height) -// -// guard let filter = CIFilter(name: "CIAreaAverage", parameters: [kCIInputImageKey: inputImage, kCIInputExtentKey: extentVector]) else { return nil } -// guard let outputImage = filter.outputImage else { return nil } -// -// var bitmap = [UInt8](repeating: 0, count: 4) -// let context = CIContext(options: [.workingColorSpace: NSNull()]) -// context.render(outputImage, toBitmap: &bitmap, rowBytes: 4, bounds: CGRect(x: 0, y: 0, width: 1, height: 1), format: CIFormat.RGBA8, colorSpace: nil) -// -// return NSColor(red: CGFloat(bitmap[0]) / 255, green: CGFloat(bitmap[1]) / 255, blue: CGFloat(bitmap[2]) / 255, alpha: CGFloat(bitmap[3]) / 255) -// } -//} - - -// Relaunch app -//func relaunchApp(afterDelay seconds: TimeInterval = 0.5) -> Never { -// let task = Process() -// task.launchPath = "/bin/sh" -// task.arguments = ["-c", "sleep \(seconds); open \"\(Bundle.main.bundlePath)\""] -// task.launch() -// -// NSApp.terminate(nil) -// exit(0) -//} - // Check if app is running before deleting app files func killApp(appId: String, completion: @escaping () -> Void = {}) { let runningApps = NSWorkspace.shared.runningApplications @@ -459,23 +292,6 @@ func isNested(path: URL) -> Bool { return parentDirectory != applicationsPath && parentDirectory != homeApplicationsPath } -// Check if macOS is 14.0 or higher -//func isMacOS14OrHigher() -> Bool { -// if #available(macOS 14, *) { -// return true -// } else { -// return false -// } -//} - - - -// --- Extend Int to convert hours to seconds --- -//extension Int { -// var daysToSeconds: Double { -// return Double(self) * 24 * 60 * 60 -// } -//} // --- Extend String to remove periods, spaces and lowercase the string @@ -486,23 +302,6 @@ extension String { } } -// --- Extend string to replace - and | with custom characters -extension String { - func featureFormat() -> String { - return self.replacingOccurrences(of: "- ", with: "• ").replacingOccurrences(of: "|", with: "\n\n") - } -} - -// --- Capitalize first letter of string only -//extension String { -// func capitalizingFirstLetter() -> String { -// return prefix(1).capitalized + dropFirst() -// } -// -// mutating func capitalizeFirstLetter() { -// self = self.capitalizingFirstLetter() -// } -//} // --- Returns comma separated string as array of strings extension String { @@ -515,130 +314,6 @@ extension String { } -// --- Overload the greater than operator ">" to do a semantic check on the string versions -//extension String { -// func versionStringToTuple() -> (Int, Int, Int) { -// let components = self.split(separator: ".").compactMap { Int($0) } -// return (components[0], components[1], components[2]) -// } -// -// static func > (lhs: String, rhs: String) -> Bool { -// let lhsVersion = lhs.versionStringToTuple() -// let rhsVersion = rhs.versionStringToTuple() -// return lhsVersion > rhsVersion -// } -//} - - -// --- Trash Relationship --- -//extension FileManager { -// public func isInTrash(_ file: URL) -> Bool { -// var relationship: URLRelationship = .other -// try? getRelationship(&relationship, of: .trashDirectory, in: .userDomainMask, toItemAt: file) -// return relationship == .contains -// } -//} - -// --- Extend print command to also output to the Console --- -//func printOS(_ items: Any..., separator: String = " ", terminator: String = "\n") { -// let message = items.map { "\($0)" }.joined(separator: separator) -// let log = OSLog(subsystem: "pearcleaner", category: "Application") -// os_log("%@", log: log, type: .default, message) -//} - - -// Get size of files -//func totalSizeOnDisk(for paths: [URL]) -> (real: Int64, logical: Int64) { -// let fileManager = FileManager.default -// var totalAllocatedSize: Int64 = 0 -// var totalFileSize: Int64 = 0 -// -// for url in paths { -// var isDirectory: ObjCBool = false -// if fileManager.fileExists(atPath: url.path, isDirectory: &isDirectory) { -// let keys: [URLResourceKey] = [.totalFileAllocatedSizeKey, .fileSizeKey] -// if isDirectory.boolValue { -// // It's a directory, recurse into it -// if let enumerator = fileManager.enumerator(at: url, includingPropertiesForKeys: keys, errorHandler: nil) { -// for case let fileURL as URL in enumerator { -// do { -// let fileAttributes = try fileURL.resourceValues(forKeys: Set(keys)) -// if let allocatedSize = fileAttributes.totalFileAllocatedSize { -// totalAllocatedSize += Int64(allocatedSize) -// } -// if let fileSize = fileAttributes.fileSize { -// totalFileSize += Int64(fileSize) -// } -// } catch { -// print("Error getting file attributes for \(fileURL): \(error)") -// } -// } -// } -// } else { -// // It's a file -// do { -// let fileAttributes = try url.resourceValues(forKeys: Set(keys)) -// if let allocatedSize = fileAttributes.totalFileAllocatedSize { -// totalAllocatedSize += Int64(allocatedSize) -// } -// if let fileSize = fileAttributes.fileSize { -// totalFileSize += Int64(fileSize) -// } -// } catch { -// print("Error getting file attributes for \(url): \(error)") -// } -// } -// } -// } -// -// return (real: totalAllocatedSize, logical: totalFileSize) -//} -// -// -// -//func totalSizeOnDisk(for path: URL) -> (real: Int64, logical: Int64) { -// return totalSizeOnDisk(for: [path]) -//} -// -//// ByteFormatter -//func formatByte(size: Int64) -> (human: String, byte: String) { -// let byteCountFormatter = ByteCountFormatter() -// byteCountFormatter.countStyle = .file -// byteCountFormatter.allowedUnits = [.useAll] -// let human = byteCountFormatter.string(fromByteCount: size) -// -// let numberformatter = NumberFormatter() -// numberformatter.numberStyle = .decimal -// let formattedNumber = numberformatter.string(from: NSNumber(value: size)) ?? "\(size)" -// let byte = "\(formattedNumber)" -// -// return (human: human, byte: byte) -// -//} - - -// Only process supported files -//func isSupportedFileType(at path: String) -> Bool { -// let fileManager = FileManager.default -// do { -// let attributes = try fileManager.attributesOfItem(atPath: path) -// if let fileType = attributes[FileAttributeKey.type] as? FileAttributeType { -// switch fileType { -// case .typeRegular, .typeDirectory, .typeSymbolicLink: -// // The file is a regular file, directory, or symbolic link -// return true -// default: -// // The file is a socket, pipe, or another type not supported -// return false -// } -// } -// } catch { -// printOS("Error getting file attributes: \(error)") -// } -// return false -//} - - // --- Pearcleaner Uninstall -- @@ -668,47 +343,6 @@ func uninstallPearcleaner(appState: AppState, locations: Locations) { } - -// --- Create Application Support folder if it doesn't exist --- -//func ensureApplicationSupportFolderExists(appState: AppState) { -// let fileManager = FileManager.default -// let supportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("com.alienator88.Pearcleaner") -// -// // 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/com.alienator88.Pearcleaner folder") -// } -//} - - -// --- Write Log to File for troubleshooting --- -//func writeLog(string: String) { -// let fileManager = FileManager.default -// let home = fileManager.homeDirectoryForCurrentUser.path -// let logFilePath = "\(home)/Downloads/log.txt" -// -// // Check if the log file exists, and create it if it doesn't -// if !fileManager.fileExists(atPath: logFilePath) { -// if !fileManager.createFile(atPath: logFilePath, contents: nil, attributes: nil) { -// printOS("Failed to create the log file.") -// return -// } -// } -// -// do { -// if let fileHandle = FileHandle(forWritingAtPath: logFilePath) { -// let ns = "\(string)\n" -// fileHandle.seekToEndOfFile() -// fileHandle.write(ns.data(using: .utf8)!) -// fileHandle.closeFile() -// } else { -// printOS("Error opening file for appending") -// } -// } -//} - - // --- Load Plist file with launchctl --- func launchctl(load: Bool, completion: @escaping () -> Void = {}) { let cmd = load ? "load" : "unload" @@ -749,22 +383,3 @@ func sendStartNotificationFW() { func sendStopNotificationFW() { DistributedNotificationCenter.default().postNotificationName(Notification.Name("Pearcleaner.StopFileWatcher"), object: nil, userInfo: nil, deliverImmediately: true) } - - -//func getCurrentTimestamp() -> String { -// let dateFormatter = DateFormatter() -// dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" -// return dateFormatter.string(from: Date()) -//} - - -//func formattedDate(_ date: Date?) -> String { -// guard let date = date else { return "N/A" } -// let formatter = DateFormatter() -// formatter.dateStyle = .short -// formatter.timeStyle = .none -// return formatter.string(from: date) -//} - - - diff --git a/Pearcleaner/PearcleanerApp.swift b/Pearcleaner/PearcleanerApp.swift index 1f27871..7865527 100644 --- a/Pearcleaner/PearcleanerApp.swift +++ b/Pearcleaner/PearcleanerApp.swift @@ -22,7 +22,6 @@ struct PearcleanerApp: App { @AppStorage("settings.permissions.hasLaunched") private var hasLaunched: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false @AppStorage("settings.general.miniview") private var miniView: Bool = true - @AppStorage("settings.general.features") private var features: String = "" @AppStorage("settings.general.brew") private var brew: Bool = false @AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false @AppStorage("settings.menubar.mainWin") private var mainWinEnabled: Bool = false @@ -112,7 +111,7 @@ struct PearcleanerApp: App { .preferredColorScheme(themeManager.displayMode.colorScheme) }, icon: selectedMenubarIcon) } - + #if !DEBUG Task { @@ -121,10 +120,9 @@ struct PearcleanerApp: App { updater.checkAndUpdateIfNeeded() /// Get new features - getFeatures(appState: appState, features: $features) + updater.checkForAnnouncement() // Make sure App Support folder exists in the future if needed for storage - //MARK: This is not needed any longer as the update file is stored in /tmp directory. Use in the future for any local db functions the app might need // ensureApplicationSupportFolderExists(appState: appState) } diff --git a/Pearcleaner/Resources/Assets.xcassets/buttonFiles.colorset/Contents.json b/Pearcleaner/Resources/Assets.xcassets/buttonFiles.colorset/Contents.json deleted file mode 100644 index 305826d..0000000 --- a/Pearcleaner/Resources/Assets.xcassets/buttonFiles.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0xCF", - "green" : "0xCF", - "red" : "0xCF" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0x59", - "green" : "0x59", - "red" : "0x59" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Pearcleaner/Resources/Assets.xcassets/pear.colorset/Contents.json b/Pearcleaner/Resources/Assets.xcassets/pear.colorset/Contents.json deleted file mode 100644 index 8e9a1c7..0000000 --- a/Pearcleaner/Resources/Assets.xcassets/pear.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0x50", - "green" : "0xC3", - "red" : "0x79" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0x50", - "green" : "0xC3", - "red" : "0x79" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Pearcleaner/Resources/Assets.xcassets/windowBG.colorset/Contents.json b/Pearcleaner/Resources/Assets.xcassets/windowBG.colorset/Contents.json deleted file mode 100644 index fdcb29e..0000000 --- a/Pearcleaner/Resources/Assets.xcassets/windowBG.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0xF6", - "green" : "0xF6", - "red" : "0xF6" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0x29", - "green" : "0x28", - "red" : "0x29" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Pearcleaner/Settings/About.swift b/Pearcleaner/Settings/About.swift index b8dc515..0937a71 100644 --- a/Pearcleaner/Settings/About.swift +++ b/Pearcleaner/Settings/About.swift @@ -141,7 +141,7 @@ struct AboutSettingsTab: View { .padding(5) .padding(.leading) - Text("Made with ❤️ by Alin Lupascu").font(.footnote).padding(.bottom) + Text("Made with ❤️ by Alin Lupascu").font(.footnote)//.padding(.bottom) } .padding(20) .frame(width: 500) @@ -151,27 +151,6 @@ struct AboutSettingsTab: View { -extension Bundle { - - var name: String { - func string(for key: String) -> String? { - object(forInfoDictionaryKey: key) as? String - } - return string(for: "CFBundleDisplayName") - ?? string(for: "CFBundleName") - ?? "N/A" - } - - var version: String { - infoDictionary?["CFBundleShortVersionString"] as? String ?? "N/A" - } - - var buildVersion: String { - infoDictionary?["CFBundleVersion"] as? String ?? "N/A" - } - -} - //MARK: Sponsors struct Sponsor: Identifiable { let id = UUID() diff --git a/Pearcleaner/Settings/Folders.swift b/Pearcleaner/Settings/Folders.swift index 62e3a66..6efe027 100644 --- a/Pearcleaner/Settings/Folders.swift +++ b/Pearcleaner/Settings/Folders.swift @@ -5,12 +5,6 @@ // Created by Alin Lupascu on 3/20/24. // -// -// General.swift -// Pearcleaner -// -// Created by Alin Lupascu on 11/5/23. -// import Foundation import SwiftUI diff --git a/Pearcleaner/Settings/General.swift b/Pearcleaner/Settings/General.swift index 3a5b22a..86c29db 100644 --- a/Pearcleaner/Settings/General.swift +++ b/Pearcleaner/Settings/General.swift @@ -15,24 +15,11 @@ struct GeneralSettingsTab: View { @EnvironmentObject var locations: Locations @EnvironmentObject var themeManager: ThemeManager @State private var windowSettings = WindowSettings() - @AppStorage("settings.general.glass") private var glass: Bool = true - @AppStorage("settings.general.mini") private var mini: Bool = false - @AppStorage("settings.general.dark") var isDark: Bool = true - @AppStorage("settings.general.popover") private var popoverStay: Bool = true - @AppStorage("settings.general.miniview") private var miniView: Bool = true - @AppStorage("settings.general.sidebarWidth") private var sidebarWidth: Double = 280 - @AppStorage("displayMode") var displayMode: DisplayMode = .system @AppStorage("settings.sentinel.enable") private var sentinel: Bool = false @AppStorage("settings.general.brew") private var brew: Bool = false @AppStorage("settings.general.oneshot") private var oneShotMode: Bool = false @AppStorage("settings.general.selectedSort") var selectedSortAlpha: Bool = true @AppStorage("settings.general.sizeType") var sizeType: String = "Real" - @State private var diskStatus: Bool = false - @State private var accessStatus: Bool = false - @State private var autoStatus: Bool = false - @Binding var showPopover: Bool - @Binding var search: String - @State var selectedIndex: Int? @State private var isResetting = false var body: some View { @@ -149,121 +136,6 @@ struct GeneralSettingsTab: View { .padding(.leading) - // === Perms ================================================================================================ - - -// Divider() -// .padding() - - -// HStack() { -// Text("Permissions").font(.title2) -// -//// InfoButtonPerms() -// -// Spacer() -// -// Button("Refresh") { -// checkAllPermissions(appState: appState) { results in -// updateOnMain { -// appState.permissionResults = results -// } -// diskStatus = results.fullDiskAccess -// accessStatus = results.accessibility -// autoStatus = results.automation -// if results.allPermissionsGranted { -// updateOnMain { -// appState.permissionsOkay = true -// } -// } -// } -// } -// .buttonStyle(SimpleButtonStyle(icon: "arrow.triangle.2.circlepath", label: "Refresh", help: "Refresh permissions")) -// .padding(.trailing, 5) -// -// } -// .padding(.leading) - - -// HStack(spacing: 0) { -// Image(systemName: "externaldrive") -// .resizable() -// .scaledToFit() -// .frame(width: 20, height: 20) -// .padding(.trailing) -// .foregroundStyle(diskStatus ? .green : .red) -// .saturation(displayMode.colorScheme == .dark ? 0.8 : 1) -// Text(diskStatus ? "Full Disk permission granted" : "Full Disk permission not granted") -// .font(.callout) -// .foregroundStyle(.primary.opacity(0.5)) -// -// Spacer() -// -// Button("") { -// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") { -// NSWorkspace.shared.open(url) -// } -// } -// .buttonStyle(SimpleButtonStyle(icon: "folder", help: "View disk permissions pane")) -// -// } -// .padding(5) -// .padding(.leading) -// -// -// -// HStack(spacing: 0) { -// Image(systemName: isVersionOrHigher(version: 14) ? "accessibility" : "figure.arms.open") -// .resizable() -// .scaledToFit() -// .frame(width: 20, height: 20) -// .padding(.trailing) -// .foregroundStyle(accessStatus ? .green : .red) -// .saturation(displayMode.colorScheme == .dark ? 0.8 : 1) -// Text(accessStatus ? "Accessibility permission granted" : "Accessibility permission not granted") -// .font(.callout) -// .foregroundStyle(.primary.opacity(0.5)) -// -// Spacer() -// -// Button("") { -// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") { -// NSWorkspace.shared.open(url) -// } -// } -// .buttonStyle(SimpleButtonStyle(icon: "folder", help: "View accessibility permissions pane")) -// -// } -// .padding(5) -// .padding(.leading) -// -// -// HStack(spacing: 0) { -// Image(systemName: "gearshape.2") -// .resizable() -// .scaledToFit() -// .frame(width: 20, height: 20) -// .padding(.trailing) -// .foregroundStyle(autoStatus ? .green : .red) -// .saturation(displayMode.colorScheme == .dark ? 0.8 : 1) -// Text(autoStatus ? "Automation permission granted" : "Automation permission not granted") -// .font(.callout) -// .foregroundStyle(.primary.opacity(0.5)) -// -// Spacer() -// -// Button("") { -// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Automation") { -// NSWorkspace.shared.open(url) -// } -// } -// .buttonStyle(SimpleButtonStyle(icon: "folder", help: "View automation permissions pane")) -// -// } -// .padding(5) -// .padding(.leading) - - // === Sentinel ============================================================================================= @@ -283,7 +155,7 @@ struct GeneralSettingsTab: View { .frame(width: 20, height: 20) .padding(.trailing) .foregroundStyle(sentinel ? .green : .red) - .saturation(displayMode.colorScheme == .dark ? 0.8 : 1) + .saturation(themeManager.displayMode.colorScheme == .dark ? 0.8 : 1) Text(sentinel ? "Detecting when apps are moved to Trash" : "**NOT** detecting when apps are moved to Trash") .font(.callout) .foregroundStyle(.primary.opacity(0.5)) @@ -324,7 +196,7 @@ struct GeneralSettingsTab: View { .frame(width: 20, height: 20) .padding(.trailing) .foregroundStyle(appState.finderExtensionEnabled ? .green : .red) - .saturation(displayMode.colorScheme == .dark ? 0.8 : 1) + .saturation(themeManager.displayMode.colorScheme == .dark ? 0.8 : 1) Text(appState.finderExtensionEnabled ? "Context menu extension for Finder is enabled" : "Context menu extension for Finder is disabled") .font(.callout) .foregroundStyle(.primary.opacity(0.5)) diff --git a/Pearcleaner/Settings/Interface.swift b/Pearcleaner/Settings/Interface.swift index 63d25ca..7af1a88 100644 --- a/Pearcleaner/Settings/Interface.swift +++ b/Pearcleaner/Settings/Interface.swift @@ -17,17 +17,11 @@ struct InterfaceSettingsTab: View { @EnvironmentObject var fsm: FolderSettingsManager @EnvironmentObject var themeManager: ThemeManager @State private var windowSettings = WindowSettings() - @Environment(\.colorScheme) var colorScheme: ColorScheme @AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false -// @AppStorage("displayMode") var displayMode: DisplayMode = .system -// @AppStorage("settings.general.selectedTab") private var selectedTab: CurrentTabView = .general @AppStorage("settings.general.glass") private var glass: Bool = false - @AppStorage("settings.general.dark") var isDark: Bool = true -// @AppStorage("settings.interface.themesEnabled") var themesEnabled: Bool = false @AppStorage("settings.general.popover") private var popoverStay: Bool = true @AppStorage("settings.general.miniview") private var miniView: Bool = true -// @AppStorage("settings.general.selectedTheme") var selectedTheme: String = "Auto" @AppStorage("settings.interface.selectedMenubarIcon") var selectedMenubarIcon: String = "pear-4" @State private var isLaunchAtLoginEnabled: Bool = false @Binding var showPopover: Bool diff --git a/Pearcleaner/Settings/SettingsWindow.swift b/Pearcleaner/Settings/SettingsWindow.swift index 6f2fd4d..1ecfabe 100644 --- a/Pearcleaner/Settings/SettingsWindow.swift +++ b/Pearcleaner/Settings/SettingsWindow.swift @@ -21,7 +21,7 @@ struct SettingsView: View { var body: some View { TabView(selection: $selectedTab) { - GeneralSettingsTab(showPopover: $showPopover, search: $search) + GeneralSettingsTab() .tabItem { Label(CurrentTabView.general.title, systemImage: "gear") } diff --git a/Pearcleaner/Settings/Tips.swift b/Pearcleaner/Settings/Tips.swift index cfac4f3..bc9fa77 100644 --- a/Pearcleaner/Settings/Tips.swift +++ b/Pearcleaner/Settings/Tips.swift @@ -38,8 +38,8 @@ struct TipsSettingsTab: View { .padding() } .scrollIndicators(.visible) - .frame(minHeight: 0, maxHeight: 500) - .frame(minWidth: 0, maxWidth: .infinity) + .frame(height: 500) + .frame(maxWidth: .infinity) .background(backgroundView(themeManager: themeManager, darker: true)) .clipShape(RoundedRectangle(cornerRadius: 8)) .padding(.bottom) @@ -47,7 +47,7 @@ struct TipsSettingsTab: View { } .padding(20) - .frame(width: 500)//, height: 520) + .frame(width: 500) } } diff --git a/Pearcleaner/Settings/Update.swift b/Pearcleaner/Settings/Update.swift index dfdbc5e..a7b7814 100644 --- a/Pearcleaner/Settings/Update.swift +++ b/Pearcleaner/Settings/Update.swift @@ -14,8 +14,6 @@ struct UpdateSettingsTab: View { @EnvironmentObject var appState: AppState @EnvironmentObject var themeManager: ThemeManager @EnvironmentObject var updater: Updater - @State private var showAlert = false - @State private var showDone = false var body: some View { VStack { @@ -25,46 +23,36 @@ struct UpdateSettingsTab: View { .padding(.horizontal) ReleasesView(updater: updater) - .frame(maxWidth: .infinity, maxHeight: 400) + .frame(height: 400) + .frame(maxWidth: .infinity) .backgroundAF(opacity: 0.5) HStack(alignment: .center, spacing: 20) { - Spacer() - + Button(""){ updater.checkForUpdates(showSheet: false) } - .buttonStyle(SimpleButtonStyle(icon: "arrow.uturn.left.circle", help: "Refresh updater")) + .buttonStyle(SimpleButtonStyle(icon: "arrow.uturn.left.circle", label: "Refresh", help: "Refresh updater")) - Spacer() Button(""){ - NewWin.show(appState: appState, width: 500, height: 440, newWin: .feature) + updater.resetAnnouncementAlert() } - .buttonStyle(SimpleButtonStyle(icon: "star", help: "Show last feature alert")) + .buttonStyle(SimpleButtonStyle(icon: "star", label: "Announcement", help: "Show announcements badge again")) - Spacer() - -// Button(""){ -// updater.checkForUpdates(showSheet: true) -// } -// .buttonStyle(SimpleButtonStyle(icon: "arrow.down.square", help: "Check for updates")) -// -// Spacer() Button(""){ NSWorkspace.shared.open(URL(string: "https://github.com/alienator88/Pearcleaner/releases")!) } - .buttonStyle(SimpleButtonStyle(icon: "link", help: "View releases on GitHub")) - - Spacer() + .buttonStyle(SimpleButtonStyle(icon: "link", label: "Releases", help: "View releases on GitHub")) + } .padding() } .padding(20) - .frame(width: 500)//, height: 520) + .frame(width: 500) } } diff --git a/Pearcleaner/Views/AppListItems.swift b/Pearcleaner/Views/AppListItems.swift index af576a8..26d9fa6 100644 --- a/Pearcleaner/Views/AppListItems.swift +++ b/Pearcleaner/Views/AppListItems.swift @@ -120,7 +120,7 @@ struct AppListItems: View { HStack { Spacer() RoundedRectangle(cornerRadius: 50) - .fill(isSelected ? Color("pear") : .primary.opacity(0.5)) + .fill(isSelected ? Color("AccentColor") : .primary.opacity(0.5)) .frame(width: isSelected ? 4 : 2, height: 25) .padding(.trailing, 5) } diff --git a/Pearcleaner/Views/AppSearchView.swift b/Pearcleaner/Views/AppSearchView.swift index ae60001..f43742e 100644 --- a/Pearcleaner/Views/AppSearchView.swift +++ b/Pearcleaner/Views/AppSearchView.swift @@ -16,18 +16,13 @@ struct AppSearchView: View { @EnvironmentObject var updater: Updater @EnvironmentObject var permissionManager: PermissionManager var glass: Bool - var sidebarWidth: Double var menubarEnabled: Bool var mini: Bool @Binding var search: String @Binding var showPopover: Bool @State private var showMenu = false - @State private var showSys: Bool = true - @State private var showUsr: Bool = true - @State private var showFeatures: Bool = false @Binding var isMenuBar: Bool @AppStorage("settings.general.selectedSortAppsList") var selectedSortAlpha: Bool = true - @State private var progress: Double = 0.0 var body: some View { @@ -37,24 +32,17 @@ struct AppSearchView: View { .padding(.top, !isMenuBar ? 25 : 0) if updater.updateAvailable { - UpdateButton(updater: updater, dark: false, opacity: 1) + UpdateBadge(updater: updater) .padding(.horizontal) } else if let _ = permissionManager.results, !permissionManager.allPermissionsGranted { - PermissionsView(dark: false, opacity: 1) + PermissionsBadge() + .padding(.horizontal) + } else if updater.announcementAvailable { + FeatureBadge(updater: updater) .padding(.horizontal) - } else if appState.featureAvailable { - AlertNotification(label: "New Message!", icon: "star", buttonAction: { - showFeatures.toggle() - }, btnColor: Color.blue, opacity: 1, themeManager: themeManager) - .padding(.horizontal) - .sheet(isPresented: $showFeatures, content: { - FeatureView() - .environmentObject(themeManager) - }) } - AppsListView(search: $search, showPopover: $showPopover, filteredApps: filteredApps) Divider() diff --git a/Pearcleaner/Windows/MenuBarItem.swift b/Pearcleaner/Views/MenuBarItem.swift similarity index 100% rename from Pearcleaner/Windows/MenuBarItem.swift rename to Pearcleaner/Views/MenuBarItem.swift diff --git a/Pearcleaner/Views/MiniMode.swift b/Pearcleaner/Views/MiniMode.swift index 7f6b3e5..35c11ac 100644 --- a/Pearcleaner/Views/MiniMode.swift +++ b/Pearcleaner/Views/MiniMode.swift @@ -53,11 +53,8 @@ struct MiniEmptyView: View { @Environment(\.colorScheme) var colorScheme @EnvironmentObject var appState: AppState @EnvironmentObject var locations: Locations -// @State private var animateGradient: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false -// @AppStorage("settings.general.animateLogo") private var animateLogo: Bool = true @Binding var showPopover: Bool -// @State private var animationStart = false var body: some View { VStack(alignment: .center) { @@ -75,49 +72,6 @@ struct MiniEmptyView: View { .offset(x: 5, y: 5) ) -// if #available(macOS 14, *) { -// if animateLogo && animationStart { -// LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing) -// .mask( -// Image(systemName: "plus.square.dashed") -// .resizable() -// .scaledToFit() -// .frame(width: 120, height: 120, alignment: .center) -// .padding() -// .fontWeight(.ultraLight) -// .offset(x: 5, y: 5) -// ) -// .phaseAnimator([false, true]) { wwdc24, chromaRotate in -// wwdc24 -// .hueRotation(.degrees(chromaRotate ? 420 : 0)) -// } animation: { chromaRotate in -// .easeInOut(duration: 6) -// } -// } else { -// LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing) -// .mask( -// Image(systemName: "plus.square.dashed") -// .resizable() -// .scaledToFit() -// .frame(width: 120, height: 120, alignment: .center) -// .padding() -// .fontWeight(.ultraLight) -// .offset(x: 5, y: 5) -// ) -// } -// } else { -// LinearGradient(gradient: Gradient(colors: [.green, .orange]), startPoint: .leading, endPoint: .trailing) -// .mask( -// Image(systemName: "plus.square.dashed") -// .resizable() -// .scaledToFit() -// .frame(width: 120, height: 120, alignment: .center) -// .padding() -// .fontWeight(.ultraLight) -// .offset(x: 5, y: 5) -// ) -// } - Text("Drop an app here") .font(.title3) .opacity(0.7) @@ -136,11 +90,6 @@ struct MiniEmptyView: View { appState.currentView = .apps } } -// .onAppear { -// DispatchQueue.main.asyncAfter(deadline: .now() + 1) { -// animationStart = true -// } -// } } } @@ -153,7 +102,6 @@ struct MiniAppView: View { @EnvironmentObject var appState: AppState @EnvironmentObject var locations: Locations @EnvironmentObject var themeManager: ThemeManager -// @State private var animateGradient: Bool = false @Binding var search: String @State private var showSys: Bool = true @State private var showUsr: Bool = true @@ -184,7 +132,7 @@ struct MiniAppView: View { } .padding(.vertical) } else { - AppSearchView(glass: glass, sidebarWidth: sidebarWidth, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover, isMenuBar: $isMenuBar) + AppSearchView(glass: glass, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover, isMenuBar: $isMenuBar) } } diff --git a/Pearcleaner/Views/Picker/SegmentedPicker.swift b/Pearcleaner/Views/Picker/SegmentedPicker.swift deleted file mode 100644 index 23d5a3a..0000000 --- a/Pearcleaner/Views/Picker/SegmentedPicker.swift +++ /dev/null @@ -1,211 +0,0 @@ -// -// SegmentedPicker.swift -// Pearcleaner -// -// Created by Alin Lupascu on 4/17/24. -// - -import SwiftUI - -public struct SegmentedPicker: View -where -Content: View, -Selection: View { - - public typealias Data = [Element] - - @State private var frames: [CGRect] - @Binding private var selectedIndex: Data.Index? - - private let data: Data - private let selection: () -> Selection - private let content: (Data.Element, Bool) -> Content - private let selectionAlignment: VerticalAlignment - - public init(_ data: Data, - selectedIndex: Binding, - selectionAlignment: VerticalAlignment = .center, - @ViewBuilder content: @escaping (Data.Element, Bool) -> Content, - @ViewBuilder selection: @escaping () -> Selection) { - - self.data = data - self.content = content - self.selection = selection - self._selectedIndex = selectedIndex - self._frames = State(wrappedValue: Array(repeating: .zero, - count: data.count)) - self.selectionAlignment = selectionAlignment - } - - public var body: some View { - ZStack(alignment: Alignment(horizontal: .horizontalCenterAlignment, - vertical: selectionAlignment)) { - - if let selectedIndex = selectedIndex { - selection() - .frame(width: frames[selectedIndex].width, - height: frames[selectedIndex].height) - .alignmentGuide(.horizontalCenterAlignment) { dimensions in - dimensions[HorizontalAlignment.center] - } - } - - HStack(spacing: 0) { - ForEach(data.indices, id: \.self) { index in - Button(action: { selectedIndex = index }, - label: { content(data[index], selectedIndex == index) } - ) - .buttonStyle(PlainButtonStyle()) - .background(GeometryReader { proxy in - Color.clear.onAppear { frames[index] = proxy.frame(in: .global) } - }) - .alignmentGuide(.horizontalCenterAlignment, - isActive: selectedIndex == index) { dimensions in - dimensions[HorizontalAlignment.center] - } - } - } - } - } -} - - -extension HorizontalAlignment { - private enum CenterAlignmentID: AlignmentID { - static func defaultValue(in dimension: ViewDimensions) -> CGFloat { - return dimension[HorizontalAlignment.center] - } - } - - static var horizontalCenterAlignment: HorizontalAlignment { - HorizontalAlignment(CenterAlignmentID.self) - } -} - - -extension View { - @ViewBuilder - @inlinable func alignmentGuide(_ alignment: HorizontalAlignment, - isActive: Bool, - computeValue: @escaping (ViewDimensions) -> CGFloat) -> some View { - if isActive { - alignmentGuide(alignment, computeValue: computeValue) - } else { - self - } - } - - @ViewBuilder - @inlinable func alignmentGuide(_ alignment: VerticalAlignment, - isActive: Bool, - computeValue: @escaping (ViewDimensions) -> CGFloat) -> some View { - - if isActive { - alignmentGuide(alignment, computeValue: computeValue) - } else { - self - } - } -} - - -// SegmentedPicker( -// ["Alpha", "Size"], -// selectedIndex: Binding( -// get: { selectedSortAlpha ? 0 : 1 }, -// set: { newIndex in -// withAnimation(.easeInOut(duration: 0.3)) { -// selectedSortAlpha = (newIndex == 0) -// } -// }), -// selectionAlignment: .bottom, -// content: { item, isSelected in -// Text(item) -// .font(.callout) -// .foregroundColor(isSelected ? .primary : .primary.opacity(0.5)) -// .padding(.horizontal) -// .padding(.bottom, 5) -// .frame(width: 75) -// -// }, -// selection: { -// VStack(spacing: 0) { -// Spacer() -// Color("pear").frame(height: 1) -// } -// }) - - -// SegmentedPicker( -// ["Real", "Logical", "Finder"], -// selectedIndex: Binding( -// get: { -// switch sizeType { -// case "Real": return 0 -// case "Logical": return 1 -// case "Finder": return 2 -// default: return 0 -// } -// }, -// set: { newIndex in -// withAnimation(.easeInOut(duration: 0.3)) { -// switch newIndex { -// case 0: sizeType = "Real" -// case 1: sizeType = "Logical" -// case 2: sizeType = "Finder" -// default: sizeType = "Real" -// } -// } -// }), -// selectionAlignment: .bottom, -// content: { item, isSelected in -// Text(item) -// .font(.callout) -// .foregroundColor(isSelected ? .primary : .primary.opacity(0.5) ) -// .padding(.horizontal) -// .padding(.bottom, 5) -// .frame(width: 75) -// }, -// selection: { -// VStack(spacing: 0) { -// Spacer() -// Color("pear").frame(height: 1) -// } -// }) - - - -// SegmentedPicker( -// ["Auto", "Dark", "Light"], -// selectedIndex: Binding( -// get: { -// switch selectedTheme { -// case "Dark": return 1 -// case "Light": return 2 -// default: return 0 -// } -// }, -// set: { newIndex in -// withAnimation(.easeInOut(duration: 0.3)) { -// switch newIndex { -// case 1: selectedTheme = "Dark" -// case 2: selectedTheme = "Light" -// default: selectedTheme = "Auto" -// } -// } -// }), -// selectionAlignment: .bottom, -// content: { item, isSelected in -// Text(item) -// .font(.callout) -// .foregroundColor(isSelected ? .primary : .primary.opacity(0.5) ) -// .padding(.horizontal) -// .padding(.bottom, 5) -// .frame(width: 65) -// }, -// selection: { -// VStack(spacing: 0) { -// Spacer() -// Color("pear").frame(height: 1) -// } -// }) diff --git a/Pearcleaner/Views/RegularMode.swift b/Pearcleaner/Views/RegularMode.swift index 5ddcba5..4819ab9 100644 --- a/Pearcleaner/Views/RegularMode.swift +++ b/Pearcleaner/Views/RegularMode.swift @@ -45,7 +45,7 @@ struct RegularMode: View { .frame(width: sidebarWidth) .padding(.vertical) } else { - AppSearchView(glass: glass, sidebarWidth: sidebarWidth, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover, isMenuBar: $isMenuBar) + AppSearchView(glass: glass, menubarEnabled: menubarEnabled, mini: mini, search: $search, showPopover: $showPopover, isMenuBar: $isMenuBar) .frame(width: sidebarWidth) } diff --git a/Pearcleaner/Windows/WindowSettings.swift b/Pearcleaner/Views/WindowSettings.swift similarity index 100% rename from Pearcleaner/Windows/WindowSettings.swift rename to Pearcleaner/Views/WindowSettings.swift diff --git a/Pearcleaner/Windows/FeatureView.swift b/Pearcleaner/Windows/FeatureView.swift deleted file mode 100644 index 9354982..0000000 --- a/Pearcleaner/Windows/FeatureView.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// FeatureView.swift -// Pearcleaner -// -// Created by Alin Lupascu on 5/5/24. -// - -import SwiftUI -import AlinFoundation - -struct FeatureView: View { - @EnvironmentObject var themeManager: ThemeManager - @Environment(\.dismiss) var dismiss - @AppStorage("settings.general.features") private var features: String = "" - - var body: some View { - VStack(spacing: 5) { - HStack { - - Spacer() - - Text("New features for v\((Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String)!)!") .font(.title) - .bold() -// .padding(.bottom) - - Spacer() - - } - - Divider() - .padding([.vertical]) - - Text(features) - .font(.body) - .multilineTextAlignment(.leading) - .padding(.horizontal) - - Spacer() - - HStack(alignment: .center, spacing: 20) { - Button(action: { - dismiss() -// NewWin.close() - }) { - Text("Close") - } -// .buttonStyle(SimpleButtonBrightStyle(icon: "checkmark.circle", label: "Ok", help: "Ok", color: .primary)) - } - .padding(.vertical) - - } - .padding() - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(themeManager.pickerColor) - -// .padding(EdgeInsets(top: -25, leading: 0, bottom: 25, trailing: 0)) - - } - -} - - diff --git a/Pearcleaner/Windows/NewWin.swift b/Pearcleaner/Windows/NewWin.swift deleted file mode 100644 index 934fd4a..0000000 --- a/Pearcleaner/Windows/NewWin.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// NewWin.swift -// Pearcleaner -// -// Created by Alin Lupascu on 11/16/23. -// - - -import AppKit -import SwiftUI - -class NewWin: NSWindowController { - - static var shared = NewWin() - - static func show(appState: AppState, width: CGFloat, height: CGFloat, newWin: NewWindow) { - shared = NewWin(appState: appState, width: width, height: height, newWin: newWin) - shared.window?.makeKeyAndOrderFront(nil) - } - - static func close() { - shared.window?.close() - } - - convenience init(appState: AppState, width: CGFloat, height: CGFloat, newWin: NewWindow) { - - let window = Self.makeWindow(width: width, height: height) - - window.backgroundColor = NSColor.controlBackgroundColor - - self.init(window: window) - - // Using Visual Effect to make titlebar fully transparent - let visualEffect = NSVisualEffectView() - visualEffect.blendingMode = .behindWindow - visualEffect.state = .active - visualEffect.material = .underWindowBackground - - let contentView = makeNewView(appState: appState, newWin: newWin) - - let hostView = NSHostingView(rootView: contentView) - - window.contentView = visualEffect - - visualEffect.addSubview(hostView) - hostView.frame = visualEffect.frame - window.titleVisibility = .hidden - window.titlebarAppearsTransparent = true - window.center() - window.title = "Pearcleaner" - - } - - private static func makeWindow(width: CGFloat, height: CGFloat) -> NSWindow { - let contentRect = NSRect(x: 0, y: 0, width: width, height: height) - let styleMask: NSWindow.StyleMask = [ - .titled, - .fullSizeContentView, - // .closable, - ] - return NSWindow(contentRect: contentRect, - styleMask: styleMask, - backing: .buffered, - defer: false) - } - - private func makeNewView(appState: AppState, newWin: NewWindow) -> some View { - - switch newWin { - case .perm: - return AnyView(PermView() - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(.black.opacity(0.2)) - .environmentObject(appState)) - case .feature: - return AnyView(FeatureView() - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(.black.opacity(0.2)) - .environmentObject(appState)) - } - } -} - - diff --git a/Pearcleaner/Windows/PermView.swift b/Pearcleaner/Windows/PermView.swift deleted file mode 100644 index 9dddceb..0000000 --- a/Pearcleaner/Windows/PermView.swift +++ /dev/null @@ -1,182 +0,0 @@ -// -// PermView.swift -// Pearcleaner -// -// Created by Alin Lupascu on 11/16/23. -// - - -import SwiftUI - -struct PermView: View { - @EnvironmentObject var appState: AppState - @AppStorage("settings.general.selectedTab") private var selectedTab: CurrentTabView = .general - - var body: some View { - VStack { - HStack { - - Spacer() - - Text("Pearcleaner Permissions") - .font(.title) - .bold() - .padding(.vertical) - .padding(.top, 5) - - Spacer() - - } - - 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(.primary.opacity(0.5)) - Text("Full Disk Access permission to find and delete files in system paths") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - - HStack(alignment: .top, spacing: 20) { - Image(systemName: "accessibility") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .foregroundStyle(.primary.opacity(0.5)) - Text("Accessibility permission to delete files via Finder") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - - HStack(alignment: .top, spacing: 20) { - Image(systemName: "gearshape.2") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .foregroundStyle(.primary.opacity(0.5)) - Text("Automation permission to perform extension actions via Finder") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - - HStack(alignment: .top, spacing: 20) { - Image(systemName: "calendar") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .foregroundStyle(.primary.opacity(0.5)) - Text("Reminders permission to find and delete some reminder files that certain apps cache") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - - HStack(alignment: .top, spacing: 20) { - Image(systemName: "info.circle") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .foregroundStyle(.primary.opacity(0.5)) - 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(.primary.opacity(0.5)) - } - } - .padding(.horizontal, 20) - - Spacer() - - HStack { - - if #available(macOS 14.0, *) { - SettingsLink {} - .buttonStyle(SimpleButtonBrightStyle(icon: "gear", label: "Settings", help: "Check permissions in Settings", color: .primary)) - } else { - Button("Settings") { - if #available(macOS 13.0, *) { - NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil) - } - else { - NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil) - } - } - .buttonStyle(SimpleButtonBrightStyle(icon: "gear", label: "Settings", help: "Check permissions in Settings", color: .red)) - } - - -// Button(action: { -// relaunchApp(afterDelay: 1) -// }) { -// Text("Restart") -// } -// .buttonStyle(SimpleButtonBrightStyle(icon: "arrow.uturn.left.circle", label: "Restart", help: "Restart", color: .red)) -// .padding() - - Button(action: { - NewWin.close() - }) { - Text("Close") - } - .buttonStyle(SimpleButtonBrightStyle(icon: "checkmark.circle", label: "Ok", help: "Ok", color: .blue)) - .padding() - -// Button(action: { -// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") { -// NSWorkspace.shared.open(url) -// } -// }) { -// Text("Full Disk Access") -// } -// .buttonStyle(SimpleButtonBrightStyle(icon: "externaldrive", label: "Full Disk", help: "Full Disk", color: .accentColor)) -// .padding() -// -// Button(action: { -// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") { -// NSWorkspace.shared.open(url) -// } -// }) { -// Text("Accessibility") -// } -// .buttonStyle(SimpleButtonBrightStyle(icon: "accessibility", label: "Accessibility", help: "Accessibility", color: .accentColor)) -// .padding() -// -// Button("") { -// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Automation") { -// NSWorkspace.shared.open(url) -// } -// } -// .buttonStyle(SimpleButtonBrightStyle(icon: "gearshape.2", label: "Automation", help: "Automation", color: .accentColor)) -// .padding() -// -// Button("") { -// if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Reminders") { -// NSWorkspace.shared.open(url) -// } -// } -// .buttonStyle(SimpleButtonBrightStyle(icon: "calendar", label: "Reminders", help: "Reminders", color: .accentColor)) - - - } - .onAppear { - selectedTab = .general - } - - - } - .padding(EdgeInsets(top: -25, leading: 0, bottom: 25, trailing: 0)) - - - - } - - -} - - diff --git a/README.md b/README.md index dcdffeb..688099b 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ This project was born out of wanting to learn more on how macOS deals with app i ## Features - Signed/notarized - Swift/SwiftUI -- Small app size (~3MB) +- Small app size (~4MB) - Leftover file search for finding remaining files from previously uninstalled applications - Sentinel monitor helper that can be enabled to watch Trash folder for deleted apps to cleanup after the fact(Extremely small (210KB) and uses ~2mb of ram to run in the background and file watch) - Mini mode which can be enabled from Settings