From 1ffa58efd3cc4a246527df33befd79cb93360cfa Mon Sep 17 00:00:00 2001 From: Alin Date: Tue, 4 Jun 2024 10:41:16 -0600 Subject: [PATCH] v3.6.5 --- Pearcleaner.xcodeproj/project.pbxproj | 8 +- Pearcleaner/Logic/AppState.swift | 1 + Pearcleaner/Logic/DeepLink.swift | 14 +++- Pearcleaner/Settings/General.swift | 27 +++++- Pearcleaner/Settings/Interface.swift | 60 +++++++------- Pearcleaner/Views/FilesView.swift | 8 ++ Pearcleaner/Views/MiniMode.swift | 115 ++++++++++++++------------ Pearcleaner/Views/RegularMode.swift | 47 +++++------ 8 files changed, 169 insertions(+), 111 deletions(-) diff --git a/Pearcleaner.xcodeproj/project.pbxproj b/Pearcleaner.xcodeproj/project.pbxproj index 6fd60e5..5939bc4 100644 --- a/Pearcleaner.xcodeproj/project.pbxproj +++ b/Pearcleaner.xcodeproj/project.pbxproj @@ -568,8 +568,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 43; - APP_VERSION = 3.6.4; + APP_BUILD = 44; + APP_VERSION = 3.6.5; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -638,8 +638,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 43; - APP_VERSION = 3.6.4; + APP_BUILD = 44; + APP_VERSION = 3.6.5; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; diff --git a/Pearcleaner/Logic/AppState.swift b/Pearcleaner/Logic/AppState.swift index c0c0a1d..6de4655 100644 --- a/Pearcleaner/Logic/AppState.swift +++ b/Pearcleaner/Logic/AppState.swift @@ -32,6 +32,7 @@ class AppState: ObservableObject { @Published var permissionsOkay: Bool = true @Published var permissionResults: PermissionsCheckResults? @Published var showUninstallAlert: Bool = false + @Published var oneShotMode: Bool = false diff --git a/Pearcleaner/Logic/DeepLink.swift b/Pearcleaner/Logic/DeepLink.swift index efac51a..afc87eb 100644 --- a/Pearcleaner/Logic/DeepLink.swift +++ b/Pearcleaner/Logic/DeepLink.swift @@ -11,6 +11,7 @@ import SwiftUI class DeeplinkManager { @Binding var showPopover: Bool @AppStorage("settings.general.mini") private var mini: Bool = false + @AppStorage("settings.general.oneshot") private var oneShotMode: Bool = false init(showPopover: Binding) { _showPopover = showPopover @@ -27,7 +28,7 @@ class DeeplinkManager { if url.pathExtension == "app" { handleAppBundle(url: url, appState: appState, locations: locations) } else if url.scheme == DeepLinkConstants.scheme, - // This handles SentinelMonitor FileWatcher + // This handles SentinelMonitor FileWatcher and FinderOpen extension url.host == DeepLinkConstants.host, let components = URLComponents(url: url, resolvingAgainstBaseURL: true), let queryItems = components.queryItems { @@ -35,6 +36,11 @@ class DeeplinkManager { let pathURL = URL(fileURLWithPath: path) let appInfo = AppInfoFetcher.getAppInfo(atPath: pathURL) showAppInFiles(appInfo: appInfo!, appState: appState, locations: locations, showPopover: $showPopover) + if oneShotMode { + updateOnMain { + appState.oneShotMode = true + } + } } else { printOS("No path query parameter found in the URL") } @@ -47,6 +53,12 @@ class DeeplinkManager { func handleAppBundle(url: URL, appState: AppState, locations: Locations) { let appInfo = AppInfoFetcher.getAppInfo(atPath: url) showAppInFiles(appInfo: appInfo!, appState: appState, locations: locations, showPopover: $showPopover) + // Even if enabled, disable one-shot mode for app drops + if oneShotMode { + updateOnMain { + appState.oneShotMode = false + } + } } } diff --git a/Pearcleaner/Settings/General.swift b/Pearcleaner/Settings/General.swift index 7e78d8d..b3e8cf3 100644 --- a/Pearcleaner/Settings/General.swift +++ b/Pearcleaner/Settings/General.swift @@ -23,6 +23,7 @@ struct GeneralSettingsTab: View { @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 @@ -68,6 +69,30 @@ struct GeneralSettingsTab: View { .padding(.leading) + HStack(spacing: 0) { + Image(systemName: oneShotMode ? "scope" : "circlebadge") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(Color("mode").opacity(0.5)) + VStack(alignment: .leading, spacing: 5) { + Text("\(oneShotMode ? "One-Shot Mode is enabled" : "One-Shot Mode is disabled")") + .font(.callout) + .foregroundStyle(Color("mode").opacity(0.5)) + } + + InfoButton(text: "When one-shot mode is enabled, clicking the Uninstall button to remove an app will also close Pearcleaner right after. This only affects Pearcleaner when it is opened via external means, like Sentinel Trash Monitor or the Finder extension. This allows for single use of the app, AKA one-shot mode. When Pearcleaner is opened normally, this setting is ignored and will work as usual.") + + Spacer() + Toggle(isOn: $oneShotMode, label: { + }) + .toggleStyle(.switch) + } + .padding(5) + .padding(.leading) + + HStack(spacing: 0) { Image(systemName: selectedSortAlpha ? "textformat.abc" : "textformat.123") .resizable() @@ -405,7 +430,7 @@ struct GeneralSettingsTab: View { } .padding(20) - .frame(width: 500, height: 670) + .frame(width: 500, height: 710) } diff --git a/Pearcleaner/Settings/Interface.swift b/Pearcleaner/Settings/Interface.swift index bc58c87..4a3fb6b 100644 --- a/Pearcleaner/Settings/Interface.swift +++ b/Pearcleaner/Settings/Interface.swift @@ -25,7 +25,7 @@ struct InterfaceSettingsTab: View { @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.animateLogo") private var animateLogo: Bool = true +// @AppStorage("settings.general.animateLogo") private var animateLogo: 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 @@ -76,34 +76,34 @@ struct InterfaceSettingsTab: View { .padding(.leading) - HStack(spacing: 0) { - Image(systemName: animateLogo ? "play.fill" : "pause") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(Color("mode").opacity(0.5)) - VStack(alignment: .leading, spacing: 5) { - Text("\(animateLogo ? "Logo animation enabled" : "Logo animation disabled")") - .font(.callout) - .foregroundStyle(Color("mode").opacity(0.5)) - - } - if !isMacOS14OrHigher { - Text("(macOS 14+)") - .font(.footnote) - .foregroundStyle(Color("mode").opacity(0.3)) - .padding(.leading, 5) - } -// InfoButton(text: "The logo animation is only available in macOS 14 or higher") - Spacer() - Toggle(isOn: $animateLogo, label: { - }) - .toggleStyle(.switch) - .disabled(!isMacOS14OrHigher) - } - .padding(5) - .padding(.leading) +// HStack(spacing: 0) { +// Image(systemName: animateLogo ? "play.fill" : "pause") +// .resizable() +// .scaledToFit() +// .frame(width: 20, height: 20) +// .padding(.trailing) +// .foregroundStyle(Color("mode").opacity(0.5)) +// VStack(alignment: .leading, spacing: 5) { +// Text("\(animateLogo ? "Logo animation enabled" : "Logo animation disabled")") +// .font(.callout) +// .foregroundStyle(Color("mode").opacity(0.5)) +// +// } +// if !isMacOS14OrHigher { +// Text("(macOS 14+)") +// .font(.footnote) +// .foregroundStyle(Color("mode").opacity(0.3)) +// .padding(.leading, 5) +// } +//// InfoButton(text: "The logo animation is only available in macOS 14 or higher") +// Spacer() +// Toggle(isOn: $animateLogo, label: { +// }) +// .toggleStyle(.switch) +// .disabled(!isMacOS14OrHigher) +// } +// .padding(5) +// .padding(.leading) @@ -557,7 +557,7 @@ struct InterfaceSettingsTab: View { } .padding(20) - .frame(width: 500, height: 600) + .frame(width: 500, height: 580) } diff --git a/Pearcleaner/Views/FilesView.swift b/Pearcleaner/Views/FilesView.swift index 6989089..a702e99 100644 --- a/Pearcleaner/Views/FilesView.swift +++ b/Pearcleaner/Views/FilesView.swift @@ -317,6 +317,11 @@ struct FilesView: View { } // Match found, remove the app removeApp(appState: appState, withPath: appState.appInfo.path) + + if appState.oneShotMode { + NSApp.terminate(nil) + } + } else { // Add deleted appInfo object to trashed array appState.trashedFiles.append(appState.appInfo) @@ -331,6 +336,9 @@ struct FilesView: View { appState.appInfo.fileSize = appState.appInfo.fileSize.filter { !appState.selectedItems.contains($0.key) } // Update the selectedFiles to remove references that are no longer present appState.selectedItems.removeAll() + if appState.oneShotMode { + NSApp.terminate(nil) + } } } } diff --git a/Pearcleaner/Views/MiniMode.swift b/Pearcleaner/Views/MiniMode.swift index c55528d..ea2f188 100644 --- a/Pearcleaner/Views/MiniMode.swift +++ b/Pearcleaner/Views/MiniMode.swift @@ -52,59 +52,70 @@ struct MiniEmptyView: View { @Environment(\.colorScheme) var colorScheme @EnvironmentObject var appState: AppState @EnvironmentObject var locations: Locations - @State private var animateGradient: Bool = false +// @State private var animateGradient: Bool = false @AppStorage("settings.general.mini") private var mini: Bool = false - @AppStorage("settings.general.animateLogo") private var animateLogo: Bool = true +// @AppStorage("settings.general.animateLogo") private var animateLogo: Bool = true @Binding var showPopover: Bool - @State private var animationStart = false +// @State private var animationStart = false var body: some View { VStack(alignment: .center) { Spacer() - - 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) - ) - } + + 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) + ) + +// 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) @@ -124,11 +135,11 @@ struct MiniEmptyView: View { appState.currentView = .apps } } - .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - animationStart = true - } - } +// .onAppear { +// DispatchQueue.main.asyncAfter(deadline: .now() + 1) { +// animationStart = true +// } +// } } } @@ -141,7 +152,7 @@ struct MiniAppView: View { @EnvironmentObject var appState: AppState @EnvironmentObject var locations: Locations @EnvironmentObject var themeSettings: ThemeSettings - @State private var animateGradient: Bool = false +// @State private var animateGradient: Bool = false @Binding var search: String @State private var showSys: Bool = true @State private var showUsr: Bool = true diff --git a/Pearcleaner/Views/RegularMode.swift b/Pearcleaner/Views/RegularMode.swift index 2607c9b..f80073a 100644 --- a/Pearcleaner/Views/RegularMode.swift +++ b/Pearcleaner/Views/RegularMode.swift @@ -86,30 +86,31 @@ struct AppDetailsEmptyView: View { @Environment(\.colorScheme) var colorScheme @EnvironmentObject var appState: AppState @EnvironmentObject var locations: Locations - @AppStorage("settings.general.animateLogo") private var animateLogo: Bool = true +// @AppStorage("settings.general.animateLogo") private var animateLogo: Bool = true @Binding var showPopover: Bool - @State private var animationStart = false +// @State private var animationStart = false var body: some View { VStack(alignment: .center) { Spacer() - if #available(macOS 14, *) { - if animateLogo && animationStart { - PearDropView() - .phaseAnimator([false, true]) { wwdc24, chromaRotate in - wwdc24 - .hueRotation(.degrees(chromaRotate ? 420 : 0)) - } animation: { chromaRotate in - .easeInOut(duration: 6) - } - } else { - PearDropView() - } - - } else { - PearDropView() - } + PearDropView() +// if #available(macOS 14, *) { +// if animateLogo && animationStart { +// PearDropView() +// .phaseAnimator([false, true]) { wwdc24, chromaRotate in +// wwdc24 +// .hueRotation(.degrees(chromaRotate ? 420 : 0)) +// } animation: { chromaRotate in +// .easeInOut(duration: 6) +// } +// } else { +// PearDropView() +// } +// +// } else { +// PearDropView() +// } Spacer() @@ -118,10 +119,10 @@ struct AppDetailsEmptyView: View { .padding(.bottom, 25) .opacity(0.5) } - .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - animationStart = true - } - } +// .onAppear { +// DispatchQueue.main.asyncAfter(deadline: .now() + 1) { +// animationStart = true +// } +// } } }