From a7eb573daa1fa54293d32ab1eccfd9dca38d80fd Mon Sep 17 00:00:00 2001 From: Alin Date: Thu, 25 Apr 2024 16:45:53 -0600 Subject: [PATCH] v3.5.4 --- Pearcleaner.xcodeproj/project.pbxproj | 40 ++++--- Pearcleaner/Logic/AppPathsFetch.swift | 23 +--- Pearcleaner/Logic/AppState.swift | 7 +- Pearcleaner/Logic/Styles.swift | 75 +++++++++++- Pearcleaner/Logic/ThemeManager.swift | 8 +- Pearcleaner/Logic/Utilities.swift | 63 +++++++++- Pearcleaner/PearcleanerApp.swift | 6 +- Pearcleaner/Settings/General.swift | 42 ++++++- Pearcleaner/Settings/Interface.swift | 88 +++++++++++--- Pearcleaner/Settings/SettingsWindow.swift | 2 +- Pearcleaner/Views/AppListItems.swift | 130 +++++++++------------ Pearcleaner/Views/AppsListView.swift | 6 +- Pearcleaner/Views/FilesView.swift | 19 ++- Pearcleaner/Views/MenuBarMiniAppView.swift | 48 +------- Pearcleaner/Views/MiniMode.swift | 92 ++++++++++----- Pearcleaner/Views/RegularMode.swift | 23 +++- Pearcleaner/Views/TopBar.swift | 30 ++--- Pearcleaner/Views/ZombieView.swift | 24 ++-- 18 files changed, 467 insertions(+), 259 deletions(-) diff --git a/Pearcleaner.xcodeproj/project.pbxproj b/Pearcleaner.xcodeproj/project.pbxproj index 0813eca..937152b 100644 --- a/Pearcleaner.xcodeproj/project.pbxproj +++ b/Pearcleaner.xcodeproj/project.pbxproj @@ -521,10 +521,12 @@ C72893142AFD51EA00C8C1CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + APP_BUILD = "$(APP_BUILD)"; + APP_VERSION = "$(APP_VERSION)"; CODE_SIGN_ENTITLEMENTS = PearcleanerSentinel/PearcleanerSentinel.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = ""; + CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = BK8443AXLU; ENABLE_HARDENED_RUNTIME = YES; @@ -540,10 +542,12 @@ C72893152AFD51EA00C8C1CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + APP_BUILD = "$(APP_BUILD)"; + APP_VERSION = "$(APP_VERSION)"; CODE_SIGN_ENTITLEMENTS = PearcleanerSentinel/PearcleanerSentinel.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = ""; + CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = BK8443AXLU; ENABLE_HARDENED_RUNTIME = YES; @@ -560,8 +564,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 36; - APP_VERSION = 3.5.2; + APP_BUILD = 38; + APP_VERSION = 3.5.4; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -593,7 +597,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 33; + CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -616,7 +620,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 3.4.2; + MARKETING_VERSION = "$(APP_VERSION)"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -630,8 +634,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 36; - APP_VERSION = 3.5.2; + APP_BUILD = 38; + APP_VERSION = 3.5.4; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -663,7 +667,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 33; + CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; @@ -680,7 +684,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 3.4.2; + MARKETING_VERSION = "$(APP_VERSION)"; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; @@ -692,15 +696,14 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - APP_BUILD = 36; - APP_VERSION = 3.5.2; + APP_BUILD = "$(APP_BUILD)"; + APP_VERSION = "$(APP_VERSION)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Pearcleaner/Resources/Pearcleaner.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BK8443AXLU; @@ -718,7 +721,6 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = "$(APP_VERSION)"; PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -730,15 +732,14 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - APP_BUILD = 36; - APP_VERSION = 3.5.2; + APP_BUILD = "$(APP_BUILD)"; + APP_VERSION = "$(APP_VERSION)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Pearcleaner/Resources/Pearcleaner.entitlements; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BK8443AXLU; @@ -756,7 +757,6 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = "$(APP_VERSION)"; PRODUCT_BUNDLE_IDENTIFIER = com.alienator88.Pearcleaner; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -767,6 +767,8 @@ C781216D2BC892A000BE06BD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + APP_BUILD = "$(APP_BUILD)"; + APP_VERSION = "$(APP_VERSION)"; CODE_SIGN_ENTITLEMENTS = FinderOpen/FinderOpen.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; @@ -797,6 +799,8 @@ C781216E2BC892A000BE06BD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + APP_BUILD = "$(APP_BUILD)"; + APP_VERSION = "$(APP_VERSION)"; CODE_SIGN_ENTITLEMENTS = FinderOpen/FinderOpen.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; diff --git a/Pearcleaner/Logic/AppPathsFetch.swift b/Pearcleaner/Logic/AppPathsFetch.swift index 0e1852e..d5eaab7 100644 --- a/Pearcleaner/Logic/AppPathsFetch.swift +++ b/Pearcleaner/Logic/AppPathsFetch.swift @@ -177,7 +177,7 @@ class AppPathFinder { private func getAllContainers(bundleURL: URL) -> [URL] { - var containers = [URL]() + var containers: [URL] = [] // Extract bundle identifier from bundleURL let bundleIdentifier = Bundle(url: bundleURL)?.bundleIdentifier @@ -190,7 +190,9 @@ class AppPathFinder { // Get the regular container URL for the extracted bundle identifier if let groupContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: containerBundleIdentifier) { - containers.append(groupContainer) + if FileManager.default.fileExists(atPath: groupContainer.path) { + containers.append(groupContainer) + } } else { printOS("Get Containers: Failed to retrieve container URL for bundle identifier: \(containerBundleIdentifier)") } @@ -221,23 +223,6 @@ class AppPathFinder { printOS("Error accessing the Containers directory: \(error)") } - -// let fileURL = URL(fileURLWithPath: "/Users/alin/Library/Containers/2792352D-95FE-43AE-947D-FF4BF31DE4E6") -// -// do { -// // Retrieve the localized name -// let resourceValues = try fileURL.resourceValues(forKeys: [.localizedNameKey]) -// if let localizedName = resourceValues.localizedName { -// print("Localized Name: \(localizedName)") -// } else { -// print("Localized name not available.") -// } -// } catch { -// print("Error retrieving localized name: \(error)") -// } - - - // Return all found containers return containers } diff --git a/Pearcleaner/Logic/AppState.swift b/Pearcleaner/Logic/AppState.swift index 1f5c6c7..db836b3 100644 --- a/Pearcleaner/Logic/AppState.swift +++ b/Pearcleaner/Logic/AppState.swift @@ -24,7 +24,7 @@ class AppState: ObservableObject @Published var currentView = CurrentDetailsView.empty @Published var showAlert: Bool = false @Published var sidebar: Bool = true - @Published var isReminderVisible: Bool = false +// @Published var isReminderVisible: Bool = false @Published var releases = [Release]() @Published var progressBar: (String, Double) = ("Ready", 0.0) @Published var reload: Bool = false @@ -55,6 +55,7 @@ class AppState: ObservableObject fileSizeLogical: [:], fileIcon: [:] ) + updateExtensionStatus() NotificationCenter.default.addObserver( self, @@ -66,9 +67,9 @@ class AppState: ObservableObject } @objc func updateExtensionStatus() { - let extentionStatus = FIFinderSyncController.isExtensionEnabled + let extensionStatus = FIFinderSyncController.isExtensionEnabled DispatchQueue.main.async { - self.finderExtensionEnabled = extentionStatus + self.finderExtensionEnabled = extensionStatus } } } diff --git a/Pearcleaner/Logic/Styles.swift b/Pearcleaner/Logic/Styles.swift index b23b82e..ac5ec81 100644 --- a/Pearcleaner/Logic/Styles.swift +++ b/Pearcleaner/Logic/Styles.swift @@ -47,6 +47,80 @@ struct SimpleButtonStyle: ButtonStyle { } +struct RegularButtonStyle: ButtonStyle { + let icon: String + let label: String + let help: String + @State private var hovered = false + + func makeBody(configuration: Configuration) -> some View { + + HStack(alignment: .center) { + Image(systemName: icon).foregroundColor(Color("mode")).padding(.leading, 2).padding(.trailing, 0) + Text(label).textCase(.uppercase).foregroundColor(Color("mode")).fontWeight(.bold).padding(.trailing, 2) + + } + .padding(8) + .background(!configuration.isPressed ? hovered ? Color("mode").opacity(0.4) : Color("mode").opacity(0) : Color("mode").opacity(0.5)) + .cornerRadius(6) + .scaleEffect(configuration.isPressed ? 0.95 : 1) + .animation(.easeOut(duration: 0.2), value: configuration.isPressed) + .onHover { hovering in + withAnimation(Animation.easeIn(duration: 0.15)) { + hovered = hovering + } + } + .overlay( + RoundedRectangle(cornerRadius: 6) + .strokeBorder(Color("mode").opacity(0.5), lineWidth: 1) + .scaleEffect(configuration.isPressed ? 0.95 : 1) + ) + .help(help) + + } +} + + +struct ResetSettingsButtonStyle: ButtonStyle { + @Binding var isResetting: Bool + let label: String + let help: String + @State private var hovered = false + + func makeBody(configuration: Configuration) -> some View { + HStack(alignment: .center) { + if isResetting { + ProgressView() + .progressViewStyle(CircularProgressViewStyle()) + .controlSize(.small) + .frame(width: 15) + } else { + Image(systemName: "gear") + .foregroundColor(Color("mode")).padding(.leading, 2).padding(.trailing, 0) + } + Text(label) + .textCase(.uppercase).foregroundColor(Color("mode")).fontWeight(.bold).padding(.trailing, 2) + } + .padding(8) + .background(!configuration.isPressed ? hovered ? Color("mode").opacity(0.4) : Color("mode").opacity(0) : Color("mode").opacity(0.5)) + .cornerRadius(6) + .scaleEffect(configuration.isPressed ? 0.95 : 1) + .animation(.easeOut(duration: 0.2), value: configuration.isPressed) + .onHover { hovering in + withAnimation(Animation.easeIn(duration: 0.15)) { + hovered = hovering + } + } + .overlay( + RoundedRectangle(cornerRadius: 6) + .strokeBorder(Color("mode").opacity(0.5), lineWidth: 1) + .scaleEffect(configuration.isPressed ? 0.95 : 1) + ) + .help(help) + } +} + + struct InfoButton: View { @State private var isPopoverPresented: Bool = false @@ -769,7 +843,6 @@ func backgroundView(themeSettings: ThemeSettings, darker: Bool = false, glass: B } - struct PresetColor: ButtonStyle { var fillColor: Color var label: String diff --git a/Pearcleaner/Logic/ThemeManager.swift b/Pearcleaner/Logic/ThemeManager.swift index 038eff1..9b05acc 100644 --- a/Pearcleaner/Logic/ThemeManager.swift +++ b/Pearcleaner/Logic/ThemeManager.swift @@ -22,13 +22,13 @@ class ThemeSettings: ObservableObject { // light - Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1) // dark - Color(.sRGB, red: 0.149, green: 0.149, blue: 0.149, opacity: 1) + // slate - Color(.sRGB, red: 0.188143, green: 0.208556, blue: 0.262679, opacity: 1) init() { // Initialize color from UserDefaults or use a default value if let components = UserDefaults.standard.object(forKey: colorKey) as? [CGFloat], components.count >= 4 { themeColor = Color(.sRGB, red: components[0], green: components[1], blue: components[2], opacity: components[3]) } else { - themeColor = Color.clear } } @@ -42,7 +42,8 @@ class ThemeSettings: ObservableObject { } func resetToDefault(dark: Bool = true) { - themeColor = dark ? Color(.sRGB, red: 0.149, green: 0.149, blue: 0.149, opacity: 1) : Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1) + themeColor = dark ? Color(.sRGB, red: 0.188143, green: 0.208556, blue: 0.262679, opacity: 1) : Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1) +// themeColor = dark ? Color(.sRGB, red: 0.149, green: 0.149, blue: 0.149, opacity: 1) : Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1) saveThemeColor() } @@ -55,6 +56,7 @@ class ThemeSettings: ObservableObject { let slate = colorScheme.colorScheme == .light ? Color(.sRGB, red: 0.499549, green: 0.545169, blue: 0.682028, opacity: 1) : Color(.sRGB, red: 0.188143, green: 0.208556, blue: 0.262679, opacity: 1) let solarized = colorScheme.colorScheme == .light ? Color(.sRGB, red: 0.554372, green: 0.6557, blue: 0.734336, opacity: 1) : Color(.sRGB, red: 0.117257, green: 0.22506, blue: 0.249171, opacity: 1) let dracula = colorScheme.colorScheme == .light ? Color(.sRGB, red: 0.567094, green: 0.562125, blue: 0.81285, opacity: 1) : Color(.sRGB, red: 0.268614, green: 0.264737, blue: 0.383503, opacity: 1) + let macOS = colorScheme.colorScheme == .light ? Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1) : Color(.sRGB, red: 0.149, green: 0.149, blue: 0.149, opacity: 1) switch preset { case "slate": @@ -63,6 +65,8 @@ class ThemeSettings: ObservableObject { return solarized case "dracula": return dracula + case "macOS": + return macOS default: return themeColor } diff --git a/Pearcleaner/Logic/Utilities.swift b/Pearcleaner/Logic/Utilities.swift index 740f62b..7c2a492 100644 --- a/Pearcleaner/Logic/Utilities.swift +++ b/Pearcleaner/Logic/Utilities.swift @@ -312,17 +312,14 @@ func convertICNSToPNG(icon: NSImage, size: NSSize) -> NSImage? { // Get icon for files and folders func getIconForFileOrFolder(atPath path: URL) -> Image? { - let icon = NSWorkspace.shared.icon(forFile: path.path) - let nsImage = icon - return Image(nsImage: nsImage) + return Image(nsImage: NSWorkspace.shared.icon(forFile: path.path)) } func getIconForFileOrFolderNS(atPath path: URL) -> NSImage? { - let icon = NSWorkspace.shared.icon(forFile: path.path) - let nsImage = icon - return nsImage + return NSWorkspace.shared.icon(forFile: path.path) } + // Get average color from image extension NSImage { var averageColor: NSColor? { @@ -393,6 +390,60 @@ func removeApp(appState: AppState, withId id: UUID) { } +// Check if file/folder name has localized variant +func showLocalized(url: URL) -> String { + guard FileManager.default.fileExists(atPath: url.path) else { + return url.lastPathComponent + } + do { + // Retrieve the localized name + let resourceValues = try url.resourceValues(forKeys: [.localizedNameKey]) + if let localizedName = resourceValues.localizedName { + return localizedName + } + } catch { + printOS("Error retrieving localized name: \(error)") + } + // Return the last path component as a fallback + return url.lastPathComponent +} + +// Return image for different folders +func folderImages(for path: String) -> AnyView? { + if path.contains("/Library/Containers/") || path.contains("/Library/Group Containers/") { + return AnyView( + Image(systemName: "shippingbox.fill") + .resizable() + .scaledToFit() + .frame(width: 13) + .foregroundStyle(Color("mode").opacity(0.5)) + .help("Container") + ) + } else if path.contains("/Library/Application Scripts/") { + return AnyView( + Image(systemName: "applescript.fill") + .resizable() + .scaledToFit() + .frame(width: 13) + .foregroundStyle(Color("mode").opacity(0.5)) + .help("Application Script") + ) + } else if path.contains(".plist") { + return AnyView( + Image(systemName: "doc.badge.gearshape.fill") + .resizable() + .scaledToFit() + .frame(width: 13) + .foregroundStyle(Color("mode").opacity(0.5)) + .help("Plist File") + ) + } + + // Return nil if no conditions are met + return nil +} + + // Check if app bundle is nested func isNested(path: URL) -> Bool { let applicationsPath = "/Applications" diff --git a/Pearcleaner/PearcleanerApp.swift b/Pearcleaner/PearcleanerApp.swift index d0d260c..cbb73f3 100644 --- a/Pearcleaner/PearcleanerApp.swift +++ b/Pearcleaner/PearcleanerApp.swift @@ -7,7 +7,7 @@ import SwiftUI import AppKit -import ServiceManagement +//import ServiceManagement @main struct PearcleanerApp: App { @@ -221,7 +221,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { UserDefaults.standard.set(displayMode?.rawValue, forKey: "displayMode") - ThemeSettings.shared.themeColor = isDarkMode() ? Color(.sRGB, red: 0.149, green: 0.149, blue: 0.149, opacity: 1) : Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1) + // Customize dark mode color to use Slate by default + ThemeSettings.shared.themeColor = isDarkMode() ? Color(.sRGB, red: 0.188143, green: 0.208556, blue: 0.262679, opacity: 1) : Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1) +// ThemeSettings.shared.themeColor = isDarkMode() ? Color(.sRGB, red: 0.149, green: 0.149, blue: 0.149, opacity: 1) : Color(.sRGB, red: 1.0, green: 1.0, blue: 1.0, opacity: 1) ThemeSettings.shared.saveThemeColor() } diff --git a/Pearcleaner/Settings/General.swift b/Pearcleaner/Settings/General.swift index 52b8b5e..c7bb3ba 100644 --- a/Pearcleaner/Settings/General.swift +++ b/Pearcleaner/Settings/General.swift @@ -7,7 +7,7 @@ import Foundation import SwiftUI -import ServiceManagement +//import ServiceManagement import FinderSync struct GeneralSettingsTab: View { @@ -29,8 +29,8 @@ struct GeneralSettingsTab: View { @State private var accessStatus: Bool = false @Binding var showPopover: Bool @Binding var search: String - @State var selectedIndex: Int? + @State private var isResetting = false var body: some View { Form { @@ -290,6 +290,7 @@ struct GeneralSettingsTab: View { .frame(width: 20, height: 20) .padding(.trailing) .foregroundStyle(appState.finderExtensionEnabled ? .green : .red) + .saturation(displayMode.colorScheme == .dark ? 0.5 : 1) Text(appState.finderExtensionEnabled ? "Context menu extension for Finder is enabled" : "Context menu extension for Finder is disabled") .font(.callout) .foregroundStyle(Color("mode").opacity(0.5)) @@ -307,7 +308,20 @@ struct GeneralSettingsTab: View { .padding(5) .padding(.leading) + // === Reset Settings ============================================================================================= + HStack() { + Spacer() + + Button("") { + resetUserDefaults() + } + .buttonStyle(ResetSettingsButtonStyle(isResetting: $isResetting, label: "Reset Settings", help: "Reset all app settings to default")) + .disabled(isResetting) + + Spacer() + } + .padding(.vertical, 5) @@ -324,8 +338,28 @@ struct GeneralSettingsTab: View { } .padding(20) - .frame(width: 500, height: 570) + .frame(width: 500, height: 620) } - + + 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) + } + } + DispatchQueue.main.async { + isResetting = false + } + } + } + } diff --git a/Pearcleaner/Settings/Interface.swift b/Pearcleaner/Settings/Interface.swift index 703f3a5..eaad082 100644 --- a/Pearcleaner/Settings/Interface.swift +++ b/Pearcleaner/Settings/Interface.swift @@ -21,10 +21,11 @@ struct InterfaceSettingsTab: View { @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 = true + @AppStorage("settings.general.glass") private var glass: 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.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 @@ -32,6 +33,14 @@ struct InterfaceSettingsTab: View { @Binding var search: String let icons = ["externaldrive", "trash", "folder", "pear-1", "pear-1.5", "pear-2", "pear-3", "pear-4"] + var isMacOS14OrHigher: Bool { + if #available(macOS 14, *) { + return true + } else { + return false + } + } + var body: some View { Form { @@ -67,6 +76,38 @@ 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", color: nil, label: "") + Spacer() + Toggle(isOn: $animateLogo, label: { + }) + .toggleStyle(.switch) + .disabled(!isMacOS14OrHigher) + } + .padding(5) + .padding(.leading) + + + + HStack(spacing: 0) { Image(systemName: "paintbrush") .resizable() @@ -82,22 +123,33 @@ struct InterfaceSettingsTab: View { InfoButton(text: "When using a custom color, you might need to change the application color mode below to Dark or Light so text is readable", color: nil, label: "") Spacer() - Button("") { - themeSettings.setPreset(preset: "slate", colorScheme: displayMode) - } - .buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "slate", colorScheme: displayMode), label: "Slate")) - Button("") { - themeSettings.setPreset(preset: "dracula", colorScheme: displayMode) - } - .buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "dracula", colorScheme: displayMode), label: "Dracula")) - .padding(.horizontal) + HStack(spacing: 10) { + Button("") { + themeSettings.setPreset(preset: "slate", colorScheme: displayMode) + } + .buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "slate", colorScheme: displayMode), label: "Slate")) - Button("") { - themeSettings.setPreset(preset: "solarized", colorScheme: displayMode) - themeSettings.saveThemeColor() + Button("") { + themeSettings.setPreset(preset: "dracula", colorScheme: displayMode) + } + .buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "dracula", colorScheme: displayMode), label: "Dracula")) + + Button("") { + themeSettings.setPreset(preset: "solarized", colorScheme: displayMode) + themeSettings.saveThemeColor() + } + .buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "solarized", colorScheme: displayMode), label: "Solarized")) + + Button("") { + themeSettings.setPreset(preset: "macOS", colorScheme: displayMode) + themeSettings.saveThemeColor() + } + .buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "macOS", colorScheme: displayMode), label: "macOS")) } - .buttonStyle(PresetColor(fillColor: themeSettings.getColorForPreset(preset: "solarized", colorScheme: displayMode), label: "Solarized")) + + + Spacer() @@ -106,6 +158,7 @@ struct InterfaceSettingsTab: View { themeSettings.saveThemeColor() } .padding(.horizontal, 5) + Button("") { themeSettings.resetToDefault(dark: colorScheme == .dark) } @@ -182,7 +235,6 @@ struct InterfaceSettingsTab: View { } else { displayMode.colorScheme = .light } - // displayMode.colorScheme = nil themeSettings.resetToDefault(dark: isDarkMode()) // Refresh foreground colors DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { @@ -193,7 +245,7 @@ struct InterfaceSettingsTab: View { } case "Dark": displayMode.colorScheme = .dark - // themeSettings.resetToDefault(dark: true) + themeSettings.resetToDefault(dark: true) DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.selectedTab = .interface } @@ -202,7 +254,7 @@ struct InterfaceSettingsTab: View { } case "Light": displayMode.colorScheme = .light - // themeSettings.resetToDefault(dark: false) + themeSettings.resetToDefault(dark: false) DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.selectedTab = .interface } @@ -506,7 +558,7 @@ struct InterfaceSettingsTab: View { } .padding(20) - .frame(width: 500, height: 570) + .frame(width: 500, height: 600) } diff --git a/Pearcleaner/Settings/SettingsWindow.swift b/Pearcleaner/Settings/SettingsWindow.swift index 27cb4cb..add41a2 100644 --- a/Pearcleaner/Settings/SettingsWindow.swift +++ b/Pearcleaner/Settings/SettingsWindow.swift @@ -14,7 +14,7 @@ struct SettingsView: View { @Binding var showPopover: Bool @Binding var search: String @Binding var showFeature: Bool - @AppStorage("settings.general.glass") private var glass: Bool = true + @AppStorage("settings.general.glass") private var glass: Bool = false @AppStorage("settings.general.selectedTab") private var selectedTab: CurrentTabView = .general var body: some View { diff --git a/Pearcleaner/Views/AppListItems.swift b/Pearcleaner/Views/AppListItems.swift index 78085e4..8497113 100644 --- a/Pearcleaner/Views/AppListItems.swift +++ b/Pearcleaner/Views/AppListItems.swift @@ -18,47 +18,44 @@ struct AppListItems: View { @AppStorage("displayMode") var displayMode: DisplayMode = .system @AppStorage("settings.general.miniview") private var miniView: Bool = true @AppStorage("settings.general.mini") private var mini: Bool = false + @AppStorage("settings.general.glass") private var glass: Bool = true + @AppStorage("settings.menubar.enabled") private var menubarEnabled: Bool = false @Binding var showPopover: Bool @EnvironmentObject var locations: Locations let itemId = UUID() let appInfo: AppInfo var isSelected: Bool { - appState.appInfo.path == appInfo.path + appState.appInfo.id == appInfo.id } @State private var hoveredItemPath: URL? = nil var body: some View { - HStack(alignment: .center) { + ZStack() { - if (isHovered || isSelected) && mini { - RoundedRectangle(cornerRadius: 50) - .fill(isSelected ? Color("pear") : Color("mode").opacity(0.5)) - .frame(width: isSelected ? 4 : 2, height: 25) - .padding(.trailing, 5) - } + HStack(alignment: .center) { if let appIcon = appInfo.appIcon { ZStack { Image(nsImage: appIcon) .resizable() .aspectRatio(contentMode: .fit) - .frame(width: 30, height: 30) + .frame(width: (isHovered || isSelected) ? 32 : 30, height: (isHovered || isSelected) ? 32 : 30) .clipShape(RoundedRectangle(cornerRadius: 8)) - .shadow(color: .black.opacity(0.5), radius: 2, y: 2) +// .shadow(color: Color("pop").opacity(0.8), radius: isSelected ? 2 : 0) } } - + VStack(alignment: .leading, spacing: 5) { Text(appInfo.appName) - .font(.system(size: 12)) + .font(.system(size: (isHovered || isSelected) ? 14 : 12)) .lineLimit(1) .truncationMode(.tail) .padding(.horizontal, 5) } - + Spacer() if appInfo.webApp { @@ -83,75 +80,58 @@ struct AppListItems: View { } Text(appInfo.appVersion) - .font(.footnote) + .font(.system(size: (isHovered || isSelected) ? 12 : 10)) .foregroundStyle(Color("mode").opacity(0.5)) - if (isHovered || isSelected) && !mini { - Triangle() - .fill(themeSettings.themeColor) - .frame(width: isSelected ? 16 : 8, height: 30) - .padding(.leading, 5) - .offset(x: 22) - .zIndex(5) +// if (isHovered || isSelected) { +// RoundedRectangle(cornerRadius: 50) +// .fill(isSelected ? Color("pear") : Color("mode").opacity(0.5)) +// .frame(width: isSelected ? 4 : 2, height: 25) +// .padding(.trailing, 5) +// } + + } + + } + .frame(height: 35) + .padding(.horizontal) + .padding(.vertical, 5) + .help(appInfo.appName) + .onHover { hovering in + withAnimation(Animation.easeIn(duration: 0.2)) { + self.isHovered = hovering + self.hoveredItemPath = isHovered ? appInfo.path : nil + } + } + .onTapGesture { + withAnimation(Animation.easeInOut(duration: 0.4)) { + if isSelected { + appState.appInfo = .empty + appState.selectedItems = [] + appState.currentView = miniView ? .apps : .empty + showPopover = false + } else { + showAppInFiles(appInfo: appInfo, appState: appState, locations: locations, showPopover: $showPopover) + } + } + } + .background{ + Rectangle() + .fill(isSelected && !glass ? themeSettings.themeColor : .clear) + } + .overlay{ + if (isHovered || isSelected) { + HStack { + Spacer() + RoundedRectangle(cornerRadius: 50) + .fill(isSelected ? Color("pear") : Color("mode").opacity(0.5)) + .frame(width: isSelected ? 4 : 2, height: 25) + .padding(.trailing, 5) } } - .padding(.horizontal, 5) - .help(appInfo.appName) - .onHover { hovering in - withAnimation(Animation.easeIn(duration: 0.2)) { - self.isHovered = hovering - self.hoveredItemPath = isHovered ? appInfo.path : nil - } - } - .onTapGesture { - withAnimation(Animation.easeInOut(duration: 0.4)) { - if isSelected { - appState.appInfo = .empty - appState.currentView = miniView ? .apps : .empty - showPopover = false - } else { - showAppInFiles(appInfo: appInfo, appState: appState, locations: locations, showPopover: $showPopover) - } - } - } - - } - - func opacityForItem(_ path: URL) -> Double { - // Check if any item is selected - let isAnyItemSelected = appState.sortedApps.contains(where: { $0.path == appState.appInfo.path }) - - let isItemSelected = appState.appInfo.path == path - let isItemHovered = hoveredItemPath == path - - // Logic to determine grayscale level - if isItemSelected || !isAnyItemSelected || isItemHovered { - return 0 // No grayscale - } else { - return 1.0 // Full grayscale } -// return isAnyItemSelected ? (appState.appInfo.path == appInfo.path ? 0 : 1.0) : 0 } } - - - -struct Triangle: Shape { - func path(in rect: CGRect) -> Path { - var path = Path() - - // Start at the top right - path.move(to: CGPoint(x: rect.maxX, y: rect.minY)) - // Add line to the bottom right - path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) - // Add line to the left point - path.addLine(to: CGPoint(x: rect.minX, y: rect.midY)) - // Close the path - path.closeSubpath() - - return path - } -} diff --git a/Pearcleaner/Views/AppsListView.swift b/Pearcleaner/Views/AppsListView.swift index 0c2a7d4..80f5a6f 100644 --- a/Pearcleaner/Views/AppsListView.swift +++ b/Pearcleaner/Views/AppsListView.swift @@ -30,7 +30,7 @@ struct AppsListView: View { SectionView(title: "System", count: filteredSystemApps.count, apps: filteredSystemApps, search: $search, showPopover: $showPopover) } } - .padding(.horizontal) +// .padding(.horizontal) .padding(.top, !mini ? 4 : 0) } .scrollIndicators(.never) @@ -47,11 +47,11 @@ struct SectionView: View { var body: some View { VStack(spacing: 0) { Header(title: title, count: count, showPopover: $showPopover) + .padding(.leading, 5) ForEach(apps, id: \.self) { appInfo in AppListItems(search: $search, showPopover: $showPopover, appInfo: appInfo) - .padding(.vertical, 5) if appInfo != apps.last { - Divider().padding(.horizontal, 5) +// Divider().padding(.horizontal, 5) } } } diff --git a/Pearcleaner/Views/FilesView.swift b/Pearcleaner/Views/FilesView.swift index afae20b..6c0b8dc 100644 --- a/Pearcleaner/Views/FilesView.swift +++ b/Pearcleaner/Views/FilesView.swift @@ -110,8 +110,6 @@ struct FilesView: View { .resizable() .scaledToFit() .frame(width: 50, height: 50) -// .clipShape(RoundedRectangle(cornerRadius: 8)) -// .padding(.trailing) .padding() .background{ RoundedRectangle(cornerRadius: 16) @@ -288,15 +286,17 @@ struct FilesView: View { showPopover = false updateOnMain { appState.currentView = mini ? .apps : .empty - appState.isReminderVisible.toggle() +// appState.isReminderVisible.toggle() } if sentinel { launchctl(load: true) } } - // Remove app from app list if main app bundle is removed - if selectedItemsArray.contains(where: { $0.absoluteString == appState.appInfo.path.absoluteString }) { + // Remove app from app list if main app bundle is removed for regular and wrapped apps + if (appState.appInfo.wrapped && selectedItemsArray.contains(where: { $0.absoluteString == appState.appInfo.path.deletingLastPathComponent().deletingLastPathComponent().absoluteString })) || + (!appState.appInfo.wrapped && selectedItemsArray.contains(where: { $0.absoluteString == appState.appInfo.path.absoluteString })) { + // Match found, remove the app removeApp(appState: appState, withId: appState.appInfo.id) } else { // Add deleted appInfo object to trashed array @@ -373,16 +373,23 @@ struct FileDetailsItem: View { .shadow(color: Color("mode"), radius: isHovered ? 2 : 0) } + + VStack(alignment: .leading, spacing: 5) { HStack(alignment: .center) { - Text(path.lastPathComponent) + Text(showLocalized(url: path)) .font(.title3) .lineLimit(1) .truncationMode(.tail) .help(path.lastPathComponent) + if isNested(path: path) { InfoButton(text: "Application file is nested within subdirectories. To prevent deleting incorrect folders, Pearcleaner will leave these alone. You may manually delete the remaining folders if required.", color: nil, label: "") } + + if let imageView = folderImages(for: path.path) { + imageView + } } Text(path.path) diff --git a/Pearcleaner/Views/MenuBarMiniAppView.swift b/Pearcleaner/Views/MenuBarMiniAppView.swift index f35fc6c..95f6143 100644 --- a/Pearcleaner/Views/MenuBarMiniAppView.swift +++ b/Pearcleaner/Views/MenuBarMiniAppView.swift @@ -74,7 +74,6 @@ struct MenuBarMiniAppView: View { .padding(.leading, 10) SearchBarMiniBottom(search: $search) -// .padding(.leading, 20) Button("More") { self.showMenu.toggle() @@ -105,26 +104,11 @@ struct MenuBarMiniAppView: View { .padding() .background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80)) -// .background( -// Group { -// if glass { -// GlassEffect(material: .sidebar, blendingMode: .behindWindow) -// .edgesIgnoringSafeArea(.all) -// } else { -// Rectangle() -// .fill(Color("pop")) -// .padding(-80) -// -// } -// } -// ) } - - - } -// .padding(.horizontal) + .padding(.top, 5) + } .padding(.vertical, 8) @@ -137,20 +121,6 @@ struct MenuBarMiniAppView: View { .frame(minWidth: 300, minHeight: 370) .edgesIgnoringSafeArea(.all) .background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80)) - -// .background( -// Group { -// if glass { -// GlassEffect(material: .sidebar, blendingMode: .behindWindow) -// .edgesIgnoringSafeArea(.all) -// } else { -// Rectangle() -// .fill(Color("pop")) -// .padding(-80) -// -// } -// } -// ) .transition(.opacity) .popover(isPresented: $showPopover, arrowEdge: .leading) { VStack { @@ -165,20 +135,6 @@ struct MenuBarMiniAppView: View { } .interactiveDismissDisabled(popoverStay) .background(backgroundView(themeSettings: themeSettings, glass: glass).padding(-80)) - -// .background( -// Group { -// if glass { -// GlassEffect(material: .sidebar, blendingMode: .behindWindow) -// .edgesIgnoringSafeArea(.all) -// } else { -// Rectangle() -// .fill(Color("pop")) -// .padding(-80) -// -// } -// } -// ) .frame(width: 650, height: 500) } diff --git a/Pearcleaner/Views/MiniMode.swift b/Pearcleaner/Views/MiniMode.swift index 4b64089..2454ca0 100644 --- a/Pearcleaner/Views/MiniMode.swift +++ b/Pearcleaner/Views/MiniMode.swift @@ -93,6 +93,7 @@ struct MiniEmptyView: View { @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 var body: some View { @@ -100,18 +101,52 @@ struct MiniEmptyView: View { Spacer() - 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 { + 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 your app here") + + + Text("Drop an app here") .font(.title3) .padding(.bottom, 25) .opacity(0.5) @@ -120,23 +155,23 @@ struct MiniEmptyView: View { Spacer() - if appState.isReminderVisible { - Text("CMD + Z to undo") - .font(.title2) - .foregroundStyle(Color("mode").opacity(0.5)) - .fontWeight(.medium) - .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { - withAnimation { - updateOnMain { - appState.isReminderVisible = false - } - } - } - } - } - - Spacer() +// if appState.isReminderVisible { +// Text("CMD + Z to undo") +// .font(.title2) +// .foregroundStyle(Color("mode").opacity(0.5)) +// .fontWeight(.medium) +// .onAppear { +// DispatchQueue.main.asyncAfter(deadline: .now() + 2) { +// withAnimation { +// updateOnMain { +// appState.isReminderVisible = false +// } +// } +// } +// } +// } +// +// Spacer() } @@ -186,6 +221,7 @@ struct MiniAppView: View { if appState.currentView != .empty { SearchBarMiniBottom(search: $search) .padding(.horizontal) + .padding(.top, 5) } } .padding(.bottom) diff --git a/Pearcleaner/Views/RegularMode.swift b/Pearcleaner/Views/RegularMode.swift index be2ad79..a896eb0 100644 --- a/Pearcleaner/Views/RegularMode.swift +++ b/Pearcleaner/Views/RegularMode.swift @@ -59,7 +59,7 @@ struct RegularMode: View { } .frame(width: sidebarWidth) - .padding(.vertical) + .padding(.vertical, 7) } @@ -107,19 +107,34 @@ struct AppDetailsEmptyView: View { @Environment(\.colorScheme) var colorScheme @EnvironmentObject var appState: AppState @EnvironmentObject var locations: Locations - @State private var animateGradient: Bool = false + @AppStorage("settings.general.animateLogo") private var animateLogo: Bool = true @Binding var showPopover: Bool var body: some View { VStack(alignment: .center) { Spacer() + if #available(macOS 14, *) { + if animateLogo { + PearDropView() + .phaseAnimator([false, true]) { wwdc24, chromaRotate in + wwdc24 + .hueRotation(.degrees(chromaRotate ? 420 : 0)) + } animation: { chromaRotate in + .easeInOut(duration: 6) + } + } else { + PearDropView() + } + + } else { + PearDropView() + } - PearDropView() Spacer() - Text("Drop your app here or select one from the list") + Text("Drop an app here") .font(.title3) .padding(.bottom, 25) .opacity(0.5) diff --git a/Pearcleaner/Views/TopBar.swift b/Pearcleaner/Views/TopBar.swift index a3c9260..e6ca8ae 100644 --- a/Pearcleaner/Views/TopBar.swift +++ b/Pearcleaner/Views/TopBar.swift @@ -20,21 +20,21 @@ struct TopBar: View { var body: some View { HStack(alignment: .center, spacing: 0) { - Spacer() - - if appState.isReminderVisible { - Text("CMD + Z to undo") - .font(.title2) - .foregroundStyle(Color("mode").opacity(0.5)) - .fontWeight(.medium) - .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { - withAnimation { - appState.isReminderVisible = false - } - } - } - } +// Spacer() +// +// if appState.isReminderVisible { +// Text("CMD + Z to undo") +// .font(.title2) +// .foregroundStyle(Color("mode").opacity(0.5)) +// .fontWeight(.medium) +// .onAppear { +// DispatchQueue.main.asyncAfter(deadline: .now() + 2) { +// withAnimation { +// appState.isReminderVisible = false +// } +// } +// } +// } Spacer() diff --git a/Pearcleaner/Views/ZombieView.swift b/Pearcleaner/Views/ZombieView.swift index f08ded8..e40dfbe 100644 --- a/Pearcleaner/Views/ZombieView.swift +++ b/Pearcleaner/Views/ZombieView.swift @@ -255,9 +255,9 @@ struct ZombieView: View { moveFilesToTrash(at: selectedItemsArray) { withAnimation { showPopover = false - updateOnMain { - appState.isReminderVisible.toggle() - } +// updateOnMain { +// appState.isReminderVisible.toggle() +// } } } } @@ -321,11 +321,19 @@ struct ZombieFileDetailsItem: View { } VStack(alignment: .leading, spacing: 5) { - Text(path.lastPathComponent) - .font(.title3) - .lineLimit(1) - .truncationMode(.tail) - .help(path.lastPathComponent) + + HStack(alignment: .center) { + Text(showLocalized(url: path)) + .font(.title3) + .lineLimit(1) + .truncationMode(.tail) + .help(path.lastPathComponent) + + if let imageView = folderImages(for: path.path) { + imageView + } + } + Text(path.path) .font(.footnote) .lineLimit(1)