diff --git a/Pearcleaner.xcodeproj/project.pbxproj b/Pearcleaner.xcodeproj/project.pbxproj index 01ac025..58e31e3 100644 --- a/Pearcleaner.xcodeproj/project.pbxproj +++ b/Pearcleaner.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ 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 */; }; @@ -135,6 +136,7 @@ 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; }; @@ -181,6 +183,7 @@ children = ( C7045A312B068AD700376976 /* NewWin.swift */, C7045A332B068B2900376976 /* UpdateView.swift */, + C7D15BBD2BE85F4D007D57CF /* FeatureView.swift */, C7045A352B068DD700376976 /* PermView.swift */, C7DE672A2BA6343D00EB1633 /* MenuBarItem.swift */, C7CF47232B3B3F1700979C5F /* WindowSettings.swift */, @@ -492,6 +495,7 @@ 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 */, diff --git a/Pearcleaner/Logic/AppCommands.swift b/Pearcleaner/Logic/AppCommands.swift index 8762282..3189709 100644 --- a/Pearcleaner/Logic/AppCommands.swift +++ b/Pearcleaner/Logic/AppCommands.swift @@ -50,7 +50,7 @@ struct AppCommands: Commands { .keyboardShortcut("r", modifiers: .command) Button { - uninstallPearcleaner(appState: appState, locations: locations) + appState.triggerUninstallAlert() } label: { Text("Uninstall Pearcleaner") } diff --git a/Pearcleaner/Logic/AppPathsFetch.swift b/Pearcleaner/Logic/AppPathsFetch.swift index 446595e..99e77fb 100644 --- a/Pearcleaner/Logic/AppPathsFetch.swift +++ b/Pearcleaner/Logic/AppPathsFetch.swift @@ -310,7 +310,6 @@ class AppPathFinder { self.appState.appInfo = self.appInfo if !self.undo { self.appState.selectedItems = Set(updatedCollection) - } } diff --git a/Pearcleaner/Logic/AppState.swift b/Pearcleaner/Logic/AppState.swift index 5c3003b..c0c0a1d 100644 --- a/Pearcleaner/Logic/AppState.swift +++ b/Pearcleaner/Logic/AppState.swift @@ -11,8 +11,7 @@ import FinderSync let home = FileManager.default.homeDirectoryForCurrentUser.path -class AppState: ObservableObject -{ +class AppState: ObservableObject { @Published var appInfo: AppInfo @Published var appInfoStore: [AppInfo] = [] @Published var trashedFiles: [AppInfo] = [] @@ -29,8 +28,10 @@ class AppState: ObservableObject @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 @@ -74,6 +75,11 @@ class AppState: ObservableObject self.finderExtensionEnabled = extensionStatus } } + + func triggerUninstallAlert() { + self.showUninstallAlert = true + } + } @@ -166,6 +172,7 @@ enum NewWindow:Int case update case no_update case perm + case feature } diff --git a/Pearcleaner/Logic/Conditions.swift b/Pearcleaner/Logic/Conditions.swift index 5346ee4..90d3aa1 100644 --- a/Pearcleaner/Logic/Conditions.swift +++ b/Pearcleaner/Logic/Conditions.swift @@ -110,8 +110,8 @@ let skipConditions: [SkipCondition] = [ ] -// Skip files/folders during reverse file search -let skipReverse = ["apple", "temporary", "btserver", "proapps", "scripteditor", "ilife", "livefsd", "siritoday", "addressbook", "animoji", "appstore", "askpermission", "callhistory", "clouddocs", "diskimages", "dock", "facetime", "fileprovider", "instruments", "knowledge", "mobilesync", "syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy", "familycircle", "geoservices", "installation", "passkit", "sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband", "coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts", "ipod", "globalpreferences", "apmanalytics", "apmexperiment", "avatarcache", "byhost", "contextstoreagent", "mobilemeaccounts", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandboxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter", "trash", "pearcleaner", "amsdatamigratortool", "arfilecache", "assistant", "chromium", "cloudkit", "webkit", "databases", "diagnostic", "cache", "gamekit", "homebrew", "logi", "microsoft", "mozilla", "sync", "google", "sentinel", "hexnode", "sentry", "tvappservices"] +// Skip files/folders during leftover file search +let skipReverse = ["apple", "temporary", "btserver", "proapps", "scripteditor", "ilife", "livefsd", "siritoday", "addressbook", "animoji", "appstore", "askpermission", "callhistory", "clouddocs", "diskimages", "dock", "facetime", "fileprovider", "instruments", "knowledge", "mobilesync", "syncservices", "homeenergyd", "icloud", "icdd", "networkserviceproxy", "familycircle", "geoservices", "installation", "passkit", "sharedimagecache", "desktop", "mbuseragent", "swiftpm", "baseband", "coresimulator", "photoslegacyupgrade", "photosupgrade", "siritts", "ipod", "globalpreferences", "apmanalytics", "apmexperiment", "avatarcache", "byhost", "contextstoreagent", "mobilemeaccounts", "intentbuilderc", "loginwindow", "momc", "replayd", "sharedfilelistd", "clang", "audiocomponent", "csexattrcryptoservice", "livetranscriptionagent", "sandboxhelper", "statuskitagent", "betaenrollmentd", "contentlinkingd", "diagnosticextensionsd", "gamed", "heard", "homed", "itunescloudd", "lldb", "mds", "mediaanalysisd", "metrickitd", "mobiletimerd", "proactived", "ptpcamerad", "studentd", "talagent", "watchlistd", "apptranslocation", "xcrun", "ds_store", "caches", "crashreporter", "trash", "pearcleaner", "amsdatamigratortool", "arfilecache", "assistant", "chromium", "cloudkit", "webkit", "databases", "diagnostic", "cache", "gamekit", "homebrew", "logi", "microsoft", "mozilla", "sync", "google", "sentinel", "hexnode", "sentry", "tvappservices", "reminders"] // Function to load additional conditions from a GitHub JSON file diff --git a/Pearcleaner/Logic/Features.swift b/Pearcleaner/Logic/Features.swift index 4d8e46e..d48f95a 100644 --- a/Pearcleaner/Logic/Features.swift +++ b/Pearcleaner/Logic/Features.swift @@ -8,8 +8,7 @@ import Foundation import SwiftUI -func getFeatures(appState: AppState, show: Binding, features: Binding) { - +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") @@ -25,25 +24,88 @@ func getFeatures(appState: AppState, show: Binding, features: Binding Void) { - let eventStore = EKEventStore() - eventStore.requestAccess(to: .reminder) { granted, error in - DispatchQueue.main.async { - if error != nil { - completion(false) - } else { - completion(granted) - } - } - } -} - - - struct PermissionsNotificationView: View { diff --git a/Pearcleaner/Logic/Styles.swift b/Pearcleaner/Logic/Styles.swift index b8fc55a..9c4401a 100644 --- a/Pearcleaner/Logic/Styles.swift +++ b/Pearcleaner/Logic/Styles.swift @@ -260,7 +260,7 @@ struct InfoButtonPerms: View { } } .popover(isPresented: $isPopoverPresented, arrowEdge: .bottom) { - VStack(alignment: .leading, spacing: 10) { + VStack(alignment: .leading, spacing: 15) { HStack(alignment: .top, spacing: 20) { Image(systemName: "externaldrive") @@ -268,7 +268,7 @@ struct InfoButtonPerms: View { .scaledToFit() .frame(width: 20, height: 20) .foregroundStyle(Color("mode").opacity(0.5)) - Text("Full Disk Access permission to find and delete files in system paths") + Text("Full Disk permission to access files/folders in system paths") .font(.callout) .foregroundStyle(Color("mode").opacity(0.5)) } @@ -279,7 +279,7 @@ struct InfoButtonPerms: View { .scaledToFit() .frame(width: 20, height: 20) .foregroundStyle(Color("mode").opacity(0.5)) - Text("Accessibility permission to delete files via Finder") + Text("Accessibility permission to allow execution of AppleScript") .font(.callout) .foregroundStyle(Color("mode").opacity(0.5)) } @@ -290,21 +290,11 @@ struct InfoButtonPerms: View { .scaledToFit() .frame(width: 20, height: 20) .foregroundStyle(Color("mode").opacity(0.5)) - Text("Automation permission to perform extension actions via Finder") + Text("Automation permission to perform delete actions via Finder") .font(.callout) .foregroundStyle(Color("mode").opacity(0.5)) } - HStack(alignment: .top, spacing: 20) { - Image(systemName: "calendar") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .foregroundStyle(Color("mode").opacity(0.5)) - Text("Reminders permission to find and delete some reminder files that certain apps cache") - .font(.callout) - .foregroundStyle(Color("mode").opacity(0.5)) - } } .padding() } diff --git a/Pearcleaner/Logic/Utilities.swift b/Pearcleaner/Logic/Utilities.swift index 01018df..f0e216a 100644 --- a/Pearcleaner/Logic/Utilities.swift +++ b/Pearcleaner/Logic/Utilities.swift @@ -123,7 +123,7 @@ func findAndHideWindows(named titles: [String]) { func findAndSetWindowFrame(named titles: [String], windowSettings: WindowSettings) { for title in titles { if let window = NSApp.windows.first(where: { $0.title == title }) { -// window.isRestorable = false // Doing this via view + window.isRestorable = false let frame = windowSettings.loadWindowSettings() window.setFrame(frame, display: true) } @@ -561,10 +561,8 @@ func uninstallPearcleaner(appState: AppState, locations: Locations) { let appInfo = AppInfoFetcher.getAppInfo(atPath: Bundle.main.bundleURL) // Find application files for Pearcleaner - AppPathFinder(appInfo: appInfo!, appState: appState, locations: locations).findPaths() - - // Kill Pearcleaner and tell Finder to trash the files - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + AppPathFinder(appInfo: appInfo!, appState: appState, locations: locations, completion: { + // Kill Pearcleaner and tell Finder to trash the files let selectedItemsArray = Array(appState.selectedItems).filter { !$0.path.contains(".Trash") } let posixFiles = selectedItemsArray.map { "POSIX file \"\($0.path)\", " }.joined().dropLast(3) let scriptSource = """ @@ -577,8 +575,7 @@ func uninstallPearcleaner(appState: AppState, locations: Locations) { NSApp.terminate(nil) exit(0) - } - + }).findPaths() } diff --git a/Pearcleaner/PearcleanerApp.swift b/Pearcleaner/PearcleanerApp.swift index 44c5dc5..c013ce0 100644 --- a/Pearcleaner/PearcleanerApp.swift +++ b/Pearcleaner/PearcleanerApp.swift @@ -26,10 +26,8 @@ struct PearcleanerApp: App { @AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false @AppStorage("settings.menubar.mainWin") private var mainWinEnabled: Bool = false @AppStorage("settings.interface.selectedMenubarIcon") var selectedMenubarIcon: String = "trash" - @State private var search = "" @State private var showPopover: Bool = false - @State private var showFeature: Bool = false @@ -37,21 +35,11 @@ struct PearcleanerApp: App { WindowGroup { Group { - ZStack() { if !mini { RegularMode(search: $search, showPopover: $showPopover) } else { MiniMode(search: $search, showPopover: $showPopover) } - - if showFeature { - NewFeatureView(text: features, mini: mini, showFeature: $showFeature) - .transition(.opacity) - } - - } - - } .environmentObject(appState) .environmentObject(locations) @@ -80,6 +68,16 @@ struct PearcleanerApp: App { windowSettings.saveWindowSettings(frame: newFrame) } } + .alert(isPresented: $appState.showUninstallAlert) { + Alert( + title: Text("Warning!"), + message: Text("Pearcleaner and all of its files will be cleanly removed, are you sure?"), + primaryButton: .destructive(Text("Uninstall")) { + uninstallPearcleaner(appState: appState, locations: locations) + }, + secondaryButton: .cancel() + ) + } .onAppear { if miniView { @@ -91,10 +89,6 @@ struct PearcleanerApp: App { // Disable tabbing NSWindow.allowsAutomaticWindowTabbing = false - - // Set window size on load -// findAndSetWindowFrame(named: ["Pearcleaner"], windowSettings: windowSettings) - // Get Apps let sortedApps = getSortedApps(paths: fsm.folderPaths, appState: appState) appState.sortedApps = sortedApps @@ -112,7 +106,7 @@ struct PearcleanerApp: App { } -#if DEBUG +#if !DEBUG Task { @@ -124,7 +118,7 @@ struct PearcleanerApp: App { appState.permissionResults = results if results.allPermissionsGranted { loadGithubReleases(appState: appState) - getFeatures(appState: appState, show: $showFeature, features: $features) + getFeatures(appState: appState, features: $features) } } @@ -159,14 +153,13 @@ struct PearcleanerApp: App { Settings { - SettingsView(showPopover: $showPopover, search: $search, showFeature: $showFeature) + SettingsView(showPopover: $showPopover, search: $search) .environmentObject(appState) .environmentObject(locations) .environmentObject(fsm) .environmentObject(ThemeSettings.shared) .toolbarBackground(.clear) .preferredColorScheme(displayMode.colorScheme) - .willRestore() } } } diff --git a/Pearcleaner/Settings/General.swift b/Pearcleaner/Settings/General.swift index 3829cec..dbcb052 100644 --- a/Pearcleaner/Settings/General.swift +++ b/Pearcleaner/Settings/General.swift @@ -28,7 +28,6 @@ struct GeneralSettingsTab: View { @State private var diskStatus: Bool = false @State private var accessStatus: Bool = false @State private var autoStatus: Bool = false - @State private var remStatus: Bool = false @Binding var showPopover: Bool @Binding var search: String @State var selectedIndex: Int? @@ -182,6 +181,8 @@ struct GeneralSettingsTab: View { InfoButtonPerms() + Spacer() + Button("Refresh") { checkAllPermissions(appState: appState) { results in updateOnMain { @@ -190,7 +191,6 @@ struct GeneralSettingsTab: View { diskStatus = results.fullDiskAccess accessStatus = results.accessibility autoStatus = results.automation - remStatus = results.reminders if results.allPermissionsGranted { updateOnMain { appState.permissionsOkay = true @@ -199,142 +199,91 @@ struct GeneralSettingsTab: View { } } .buttonStyle(SimpleButtonStyle(icon: "arrow.triangle.2.circlepath", help: "Refresh permissions")) + .padding(.trailing, 5) - - Spacer() } .padding(.leading) - HStack { - HStack(spacing: 0) { - Image(systemName: "externaldrive") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(diskStatus ? .green : .red) - .saturation(displayMode.colorScheme == .dark ? 0.5 : 1) - Text("Full Disk") - .font(.callout) - .foregroundStyle(Color("mode").opacity(0.5)) - .frame(width: 100) - - Spacer() - - Button("") { - if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") { - NSWorkspace.shared.open(url) - } - } - .buttonStyle(SimpleButtonStyle(icon: "arrow.right.circle.fill", help: "View disk permissions pane", size: 14)) - - Spacer() - - } - .padding(5) - .padding(.leading) - .frame(width: 200) + HStack(spacing: 0) { + Image(systemName: "externaldrive") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(diskStatus ? .green : .red) + .saturation(displayMode.colorScheme == .dark ? 0.5 : 1) + Text(diskStatus ? "Full Disk permission granted" : "Full Disk permission not granted") + .font(.callout) + .foregroundStyle(Color("mode").opacity(0.5)) Spacer() - - HStack(spacing: 0) { - Image(systemName: "accessibility") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(accessStatus ? .green : .red) - .saturation(displayMode.colorScheme == .dark ? 0.5 : 1) - Text("Accessibility") - .font(.callout) - .foregroundStyle(Color("mode").opacity(0.5)) - .frame(width: 100) - - Spacer() - - Button("") { - if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") { - NSWorkspace.shared.open(url) - } + Button("") { + if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles") { + NSWorkspace.shared.open(url) } - .buttonStyle(SimpleButtonStyle(icon: "arrow.right.circle.fill", help: "View accessibility permissions pane", size: 14)) - - Spacer() - } - .padding(5) - .padding(.leading) - .frame(width: 200) + .buttonStyle(SimpleButtonStyle(icon: "folder", help: "View disk permissions pane")) } + .padding(5) + .padding(.leading) + + Spacer() - HStack { - 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.5 : 1) - Text("Automation") - .font(.callout) - .foregroundStyle(Color("mode").opacity(0.5)) - .frame(width: 100) - - Spacer() - - Button("") { - if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Automation") { - NSWorkspace.shared.open(url) - } - } - .buttonStyle(SimpleButtonStyle(icon: "arrow.right.circle.fill", help: "View automation permissions pane", size: 14)) - - Spacer() - - } - .padding(5) - .padding(.leading) - .frame(width: 200) - + HStack(spacing: 0) { + Image(systemName: "accessibility") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(accessStatus ? .green : .red) + .saturation(displayMode.colorScheme == .dark ? 0.5 : 1) + Text(accessStatus ? "Accessibility permission granted" : "Accessibility permission not granted") + .font(.callout) + .foregroundStyle(Color("mode").opacity(0.5)) Spacer() - HStack(spacing: 0) { - Image(systemName: "calendar") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(remStatus ? .green : .red) - .saturation(displayMode.colorScheme == .dark ? 0.5 : 1) - Text("Reminders") - .font(.callout) - .foregroundStyle(Color("mode").opacity(0.5)) - .frame(width: 100) - - Spacer() - - Button("") { - if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Reminders") { - NSWorkspace.shared.open(url) - } + Button("") { + if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") { + NSWorkspace.shared.open(url) } - .buttonStyle(SimpleButtonStyle(icon: "arrow.right.circle.fill", help: "View reminders permissions pane", size: 14)) - - Spacer() - } - .padding(5) - .padding(.leading) - .frame(width: 200) + .buttonStyle(SimpleButtonStyle(icon: "folder", help: "View accessibility permissions pane")) } + .padding(5) + .padding(.leading) + Spacer() + + 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.5 : 1) + Text(autoStatus ? "Automation permission granted" : "Automation permission not granted") + .font(.callout) + .foregroundStyle(Color("mode").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) @@ -445,7 +394,6 @@ struct GeneralSettingsTab: View { diskStatus = results.fullDiskAccess accessStatus = results.accessibility autoStatus = results.automation - remStatus = results.reminders if results.allPermissionsGranted { updateOnMain { appState.permissionsOkay = true @@ -457,23 +405,17 @@ struct GeneralSettingsTab: View { } .padding(20) - .frame(width: 500, height: 650) + .frame(width: 500, height: 670) } - private var keysToIgnore: Set { - ["windowWidthKey", "windowHeightKey", "windowWidthKeyMini", "windowHeightKeyMini", "windowXKey", "windowYKey"] - } - private func resetUserDefaults() { isResetting = true DispatchQueue.global(qos: .background).async { let defaults = UserDefaults.standard let dictionary = defaults.dictionaryRepresentation() dictionary.keys.forEach { key in - if !keysToIgnore.contains(key) { - defaults.removeObject(forKey: key) - } + defaults.removeObject(forKey: key) } DispatchQueue.main.async { isResetting = false diff --git a/Pearcleaner/Settings/Interface.swift b/Pearcleaner/Settings/Interface.swift index c22f3ec..1f22bd0 100644 --- a/Pearcleaner/Settings/Interface.swift +++ b/Pearcleaner/Settings/Interface.swift @@ -306,26 +306,23 @@ struct InterfaceSettingsTab: View { if mini { appState.currentView = miniView ? .apps : .empty showPopover = false - findAndHideWindows(named: ["Pearcleaner"]) - windowSettings.newWindow { + windowSettings.newWindow(withView: { MiniMode(search: $search, showPopover: $showPopover) .environmentObject(locations) .environmentObject(appState) .environmentObject(fsm) .environmentObject(ThemeSettings.shared) .preferredColorScheme(displayMode.colorScheme) - } - updateOnMain(after: 0.1, { + }) { resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner") - }) + } } else { if appState.appInfo.appName.isEmpty { appState.currentView = .empty } else { appState.currentView = .files } - findAndHideWindows(named: ["Pearcleaner"]) - windowSettings.newWindow { + windowSettings.newWindow(withView: { RegularMode(search: $search, showPopover: $showPopover) .environmentObject(locations) .environmentObject(appState) @@ -333,9 +330,9 @@ struct InterfaceSettingsTab: View { .environmentObject(ThemeSettings.shared) .preferredColorScheme(displayMode.colorScheme) } - updateOnMain(after: 0.1, { + ) { resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner") - }) + } } @@ -443,25 +440,27 @@ struct InterfaceSettingsTab: View { NSApplication.shared.setActivationPolicy(.regular) if !hasWindowOpen() { if mini { - windowSettings.newWindow { + windowSettings.newWindow(withView: { MiniMode(search: $search, showPopover: $showPopover) .environmentObject(locations) .environmentObject(appState) .environmentObject(fsm) .environmentObject(ThemeSettings.shared) .preferredColorScheme(displayMode.colorScheme) + }) { + resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner") } - resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner") } else { - windowSettings.newWindow { + windowSettings.newWindow(withView: { RegularMode(search: $search, showPopover: $showPopover) .environmentObject(locations) .environmentObject(appState) .environmentObject(fsm) .environmentObject(ThemeSettings.shared) .preferredColorScheme(displayMode.colorScheme) + }) { + resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner") } - resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner") } } diff --git a/Pearcleaner/Settings/SettingsWindow.swift b/Pearcleaner/Settings/SettingsWindow.swift index 8d7080f..c209edb 100644 --- a/Pearcleaner/Settings/SettingsWindow.swift +++ b/Pearcleaner/Settings/SettingsWindow.swift @@ -13,7 +13,6 @@ struct SettingsView: View { @EnvironmentObject var themeSettings: ThemeSettings @Binding var showPopover: Bool @Binding var search: String - @Binding var showFeature: Bool @AppStorage("settings.general.glass") private var glass: Bool = false @AppStorage("settings.general.selectedTab") private var selectedTab: CurrentTabView = .general @@ -38,7 +37,7 @@ struct SettingsView: View { } .tag(CurrentTabView.folders) - UpdateSettingsTab(showFeature: $showFeature) + UpdateSettingsTab() .tabItem { Label(CurrentTabView.update.title, systemImage: "cloud") } diff --git a/Pearcleaner/Settings/Update.swift b/Pearcleaner/Settings/Update.swift index 6bb509f..1c46a98 100644 --- a/Pearcleaner/Settings/Update.swift +++ b/Pearcleaner/Settings/Update.swift @@ -14,7 +14,6 @@ struct UpdateSettingsTab: View { @State private var showAlert = false @State private var showDone = false @AppStorage("settings.updater.updateTimeframe") private var updateTimeframe: Int = 1 - @Binding var showFeature: Bool var body: some View { VStack { @@ -61,7 +60,7 @@ struct UpdateSettingsTab: View { Spacer() Button(""){ - showFeature.toggle() + NewWin.show(appState: appState, width: 500, height: 440, newWin: .feature) } .buttonStyle(SimpleButtonStyle(icon: "star", help: "Show last feature alert")) diff --git a/Pearcleaner/Views/AppSearchView.swift b/Pearcleaner/Views/AppSearchView.swift index 55ca11e..8314810 100644 --- a/Pearcleaner/Views/AppSearchView.swift +++ b/Pearcleaner/Views/AppSearchView.swift @@ -32,6 +32,8 @@ struct AppSearchView: View { UpdateNotificationView(appState: appState) } else if !appState.permissionsOkay { PermissionsNotificationView(appState: appState) + } else if appState.featureAvailable { + FeatureNotificationView(appState: appState) } AppsListView(search: $search, showPopover: $showPopover, filteredApps: filteredApps) diff --git a/Pearcleaner/Windows/FeatureView.swift b/Pearcleaner/Windows/FeatureView.swift new file mode 100644 index 0000000..c806291 --- /dev/null +++ b/Pearcleaner/Windows/FeatureView.swift @@ -0,0 +1,59 @@ +// +// FeatureView.swift +// Pearcleaner +// +// Created by Alin Lupascu on 5/5/24. +// + +import SwiftUI + + +struct FeatureView: View { + @EnvironmentObject var appState: AppState + @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(.vertical) + + Spacer() + + } + + Divider() + .padding([.horizontal]) + + Text(features) + .font(.body) + .lineLimit(3) + .multilineTextAlignment(.leading) + .padding() + + + + Spacer() + + HStack(alignment: .center, spacing: 20) { + Button(action: { + NewWin.close() + }) { + Text("Okay") + } + .buttonStyle(SimpleButtonBrightStyle(icon: "checkmark.circle", label: "Ok", help: "Ok", color: .accentColor)) + } + .padding(.bottom) + + } + .padding(EdgeInsets(top: -25, leading: 0, bottom: 25, trailing: 0)) + + } + +} + + diff --git a/Pearcleaner/Windows/NewWin.swift b/Pearcleaner/Windows/NewWin.swift index f2e2c81..3fd9478 100644 --- a/Pearcleaner/Windows/NewWin.swift +++ b/Pearcleaner/Windows/NewWin.swift @@ -80,7 +80,11 @@ class NewWin: NSWindowController { 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/WindowSettings.swift b/Pearcleaner/Windows/WindowSettings.swift index 7302273..9c5a074 100644 --- a/Pearcleaner/Windows/WindowSettings.swift +++ b/Pearcleaner/Windows/WindowSettings.swift @@ -44,7 +44,7 @@ class WindowSettings { return NSRect(x: x, y: y, width: width, height: height) } - func newWindow(withView view: @escaping () -> V) { + func newWindow(withView view: @escaping () -> V, completion: @escaping () -> Void = {}) { findAndHideWindows(named: ["Pearcleaner"]) let contentView = view let frame = self.loadWindowSettings() @@ -56,30 +56,32 @@ class WindowSettings { newWindow.isMovableByWindowBackground = true newWindow.center() newWindow.title = "Pearcleaner" + newWindow.isRestorable = false newWindow.titleVisibility = .hidden newWindow.setFrameAutosaveName("Pearcleaner") newWindow.contentView = NSHostingView(rootView: contentView()) self.windows.append(newWindow) newWindow.makeKeyAndOrderFront(nil) + completion() } } -struct WillRestore: ViewModifier { - let restore: Bool - - func body(content: Content) -> some View { - content - .onReceive(NotificationCenter.default.publisher(for: NSWindow.didBecomeKeyNotification), perform: { output in - let window = output.object as! NSWindow - window.isRestorable = false - }) - } -} - -extension View { - func willRestore(_ restoreState: Bool = true) -> some View { - modifier(WillRestore(restore: restoreState)) - } -} +//struct WillRestore: ViewModifier { +// let restore: Bool +// +// func body(content: Content) -> some View { +// content +// .onReceive(NotificationCenter.default.publisher(for: NSWindow.didBecomeKeyNotification), perform: { output in +// let window = output.object as! NSWindow +// window.isRestorable = false +// }) +// } +//} +// +//extension View { +// func willRestore(_ restoreState: Bool = true) -> some View { +// modifier(WillRestore(restore: restoreState)) +// } +//}