mirror of
https://github.com/exituser/Pearcleaner.git
synced 2026-09-17 02:38:54 +00:00
v3.9.1
This commit is contained in:
@@ -537,8 +537,8 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APP_BUILD = 62;
|
||||
APP_VERSION = 3.9.0;
|
||||
APP_BUILD = 63;
|
||||
APP_VERSION = 3.9.1;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
@@ -607,8 +607,8 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
APP_BUILD = 62;
|
||||
APP_VERSION = 3.9.0;
|
||||
APP_BUILD = 63;
|
||||
APP_VERSION = 3.9.1;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
|
||||
@@ -14,7 +14,8 @@ import AlinFoundation
|
||||
class MetadataAppInfoFetcher {
|
||||
static func getAppInfo(fromMetadata metadata: [String: Any], atPath path: URL) -> AppInfo? {
|
||||
// Extract metadata attributes for known fields
|
||||
let displayName = metadata["kMDItemDisplayName"] as? String ?? ""
|
||||
var displayName = metadata["kMDItemDisplayName"] as? String ?? ""
|
||||
displayName = displayName.replacingOccurrences(of: ".app", with: "").capitalizingFirstLetter()
|
||||
let fsName = metadata["kMDItemFSName"] as? String ?? path.lastPathComponent
|
||||
let appName = displayName.isEmpty ? fsName : displayName
|
||||
|
||||
@@ -152,7 +153,7 @@ class AppInfoUtils {
|
||||
static func fetchAppIcon(for path: URL, wrapped: Bool, md: Bool = false) -> NSImage? {
|
||||
let iconPath = wrapped ? (md ? path : path.deletingLastPathComponent().deletingLastPathComponent()) : path
|
||||
if let appIcon = getIconForFileOrFolderNS(atPath: iconPath) {
|
||||
return convertICNSToPNG(icon: appIcon, size: NSSize(width: 100, height: 100))
|
||||
return convertICNSToPNG(icon: appIcon, size: NSSize(width: 50, height: 50))
|
||||
} else {
|
||||
printOS("App Icon not found for app at path: \(path)")
|
||||
return nil
|
||||
|
||||
@@ -208,6 +208,21 @@ enum CurrentPage:Int, CaseIterable, Identifiable
|
||||
}
|
||||
}
|
||||
|
||||
enum SortOption:Int, CaseIterable, Identifiable {
|
||||
case alphabetical
|
||||
case size
|
||||
case creationDate
|
||||
case contentChangeDate
|
||||
case lastUsedDate
|
||||
|
||||
var id: Int { rawValue }
|
||||
|
||||
var title: String {
|
||||
let titles: [String] = ["App Name", "App Size", "Install Date", "Modified Date", "Last Used Date"]
|
||||
return titles[rawValue]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum CurrentTabView:Int
|
||||
{
|
||||
|
||||
@@ -186,6 +186,7 @@ struct Sponsor: Identifiable {
|
||||
let url: URL
|
||||
|
||||
static let sponsors: [Sponsor] = [
|
||||
Sponsor(name: "mzdr (monthly)", url: URL(string: "https://github.com/mzdr")!),
|
||||
Sponsor(name: "chris3ware", url: URL(string: "https://github.com/chris3ware")!),
|
||||
Sponsor(name: "fpuhan", url: URL(string: "https://github.com/fpuhan")!),
|
||||
Sponsor(name: "HungThinhIT", url: URL(string: "https://github.com/HungThinhIT")!),
|
||||
|
||||
@@ -19,7 +19,7 @@ struct GeneralSettingsTab: View {
|
||||
@AppStorage("settings.general.brew") private var brew: Bool = false
|
||||
@AppStorage("settings.general.oneshot") private var oneShotMode: Bool = false
|
||||
@AppStorage("settings.general.confirmAlert") private var confirmAlert: Bool = false
|
||||
@AppStorage("settings.general.selectedSort") var selectedSortAlpha: Bool = true
|
||||
// @AppStorage("settings.general.selectedSort") var selectedSortAlpha: Bool = true
|
||||
@AppStorage("settings.general.sizeType") var sizeType: String = "Real"
|
||||
@State private var isCLISymlinked = false
|
||||
|
||||
@@ -107,28 +107,28 @@ struct GeneralSettingsTab: View {
|
||||
|
||||
|
||||
|
||||
HStack(spacing: 0) {
|
||||
Image(systemName: selectedSortAlpha ? "textformat.abc" : "textformat.123")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(.trailing)
|
||||
.foregroundStyle(.primary)
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text("File list sorting order")
|
||||
.font(.callout)
|
||||
.foregroundStyle(.primary)
|
||||
}
|
||||
Spacer()
|
||||
Picker("", selection: $selectedSortAlpha) {
|
||||
Text("Alphabetical")
|
||||
.tag(true)
|
||||
Text("File Size")
|
||||
.tag(false)
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
}
|
||||
.padding(5)
|
||||
// HStack(spacing: 0) {
|
||||
// Image(systemName: selectedSortAlpha ? "textformat.abc" : "textformat.123")
|
||||
// .resizable()
|
||||
// .scaledToFit()
|
||||
// .frame(width: 20, height: 20)
|
||||
// .padding(.trailing)
|
||||
// .foregroundStyle(.primary)
|
||||
// VStack(alignment: .leading, spacing: 5) {
|
||||
// Text("File list sorting order")
|
||||
// .font(.callout)
|
||||
// .foregroundStyle(.primary)
|
||||
// }
|
||||
// Spacer()
|
||||
// Picker("", selection: $selectedSortAlpha) {
|
||||
// Text("Alphabetical")
|
||||
// .tag(true)
|
||||
// Text("File Size")
|
||||
// .tag(false)
|
||||
// }
|
||||
// .buttonStyle(.borderless)
|
||||
// }
|
||||
// .padding(5)
|
||||
|
||||
|
||||
|
||||
@@ -151,8 +151,8 @@ struct GeneralSettingsTab: View {
|
||||
.tag("Real")
|
||||
Text("Logical")
|
||||
.tag("Logical")
|
||||
Text("Finder")
|
||||
.tag("Finder")
|
||||
// Text("Finder")
|
||||
// .tag("Finder")
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ struct AppSearchView: View {
|
||||
@Binding var showPopover: Bool
|
||||
@State private var showMenu = false
|
||||
@Binding var isMenuBar: Bool
|
||||
@AppStorage("settings.general.selectedSortAppsList") var selectedSortAlpha: Bool = true
|
||||
@AppStorage("settings.general.selectedSortAppsList") var selectedSortOption: SortOption = .alphabetical
|
||||
@AppStorage("settings.interface.animationEnabled") private var animationEnabled: Bool = true
|
||||
|
||||
|
||||
@@ -81,10 +81,10 @@ struct AppSearchView: View {
|
||||
.buttonStyle(SimpleButtonStyle(icon: "arrow.counterclockwise.circle", help: "Refresh apps (⌘+R)", size: 16))
|
||||
}
|
||||
|
||||
SearchBar(search: $search, darker: (mini || menubarEnabled) ? false : true, glass: glass)
|
||||
SearchBar(search: $search, darker: (mini || menubarEnabled) ? false : true, glass: glass, sidebar: false)
|
||||
|
||||
|
||||
if search.isEmpty {
|
||||
if search.isEmpty && (mini || menubarEnabled) {
|
||||
Button("More") {
|
||||
self.showMenu.toggle()
|
||||
}
|
||||
@@ -94,10 +94,19 @@ struct AppSearchView: View {
|
||||
|
||||
Button("") {
|
||||
withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.35 : 0)) {
|
||||
selectedSortAlpha.toggle()
|
||||
// Cycle through all enum cases using `CaseIterable`
|
||||
if let nextSortOption = SortOption(rawValue: selectedSortOption.rawValue + 1) {
|
||||
selectedSortOption = nextSortOption
|
||||
showPopover = false
|
||||
showMenu = false
|
||||
} else {
|
||||
selectedSortOption = .alphabetical
|
||||
showPopover = false
|
||||
showMenu = false
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "circle.fill", label: "Sorting: \(selectedSortAlpha ? "Name" : "Size")", help: "Sort app list alphabetically by name or by size", size: 5))
|
||||
.buttonStyle(SimpleButtonStyle(icon: "circle.fill", label: "Sorting: \(selectedSortOption.title)", help: "Sort app list alphabetically by name or by size", size: 5))
|
||||
|
||||
if mini && !menubarEnabled {
|
||||
Button("") {
|
||||
@@ -149,15 +158,48 @@ struct AppSearchView: View {
|
||||
Button("Quit") {
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "circle.fill", label: "Quit Pearcleaner", help: "Quit Pearcleaner", size: 5))
|
||||
.buttonStyle(SimpleButtonStyle(icon: "circle.fill", label: "Quit", help: "Quit Pearcleaner", size: 5))
|
||||
}
|
||||
|
||||
}
|
||||
.padding()
|
||||
.background(backgroundView(themeManager: themeManager, glass: glass).padding(-80))
|
||||
.frame(width: 150)
|
||||
// .frame(width: 200)
|
||||
|
||||
}
|
||||
} else if search.isEmpty && (!mini || !menubarEnabled) {
|
||||
|
||||
Button("") {
|
||||
withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.35 : 0)) {
|
||||
showMenu.toggle()
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: "line.3.horizontal.decrease.circle", help: selectedSortOption.title, size: 16))
|
||||
.popover(isPresented: $showMenu, arrowEdge: .bottom) {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
HStack {
|
||||
Spacer()
|
||||
Text("Sorting Options").font(.subheadline).foregroundStyle(.secondary)
|
||||
Spacer()
|
||||
}
|
||||
Divider()
|
||||
ForEach(SortOption.allCases) { option in
|
||||
Button("") {
|
||||
withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.35 : 0)) {
|
||||
selectedSortOption = option
|
||||
showMenu = false
|
||||
}
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: selectedSortOption == option ? "circle.inset.filled" : "circle", label: option.title, help: "", size: 5))
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(backgroundView(themeManager: themeManager, glass: glass).padding(-80))
|
||||
// .frame(width: 160)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -174,12 +216,28 @@ struct AppSearchView: View {
|
||||
apps = appState.sortedApps.filter { $0.appName.localizedCaseInsensitiveContains(search) }
|
||||
}
|
||||
|
||||
switch selectedSortAlpha {
|
||||
case true:
|
||||
return apps.sorted { $0.appName.replacingOccurrences(of: ".", with: "").lowercased() < $1.appName.replacingOccurrences(of: ".", with: "").lowercased() }
|
||||
case false:
|
||||
// Sort based on the selected option
|
||||
switch selectedSortOption {
|
||||
case .alphabetical:
|
||||
return apps.sorted {
|
||||
$0.appName.replacingOccurrences(of: ".", with: "").lowercased() < $1.appName.replacingOccurrences(of: ".", with: "").lowercased()
|
||||
}
|
||||
case .size:
|
||||
return apps.sorted { $0.bundleSize > $1.bundleSize }
|
||||
case .creationDate:
|
||||
return apps.sorted { ($0.creationDate ?? Date.distantPast) > ($1.creationDate ?? Date.distantPast) }
|
||||
case .contentChangeDate:
|
||||
return apps.sorted { ($0.contentChangeDate ?? Date.distantPast) > ($1.contentChangeDate ?? Date.distantPast) }
|
||||
case .lastUsedDate:
|
||||
return apps.sorted { ($0.lastUsedDate ?? Date.distantPast) > ($1.lastUsedDate ?? Date.distantPast) }
|
||||
}
|
||||
|
||||
// switch selectedSortAlpha {
|
||||
// case true:
|
||||
// return apps.sorted { $0.appName.replacingOccurrences(of: ".", with: "").lowercased() < $1.appName.replacingOccurrences(of: ".", with: "").lowercased() }
|
||||
// case false:
|
||||
// return apps.sorted { $0.bundleSize > $1.bundleSize }
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -218,7 +276,7 @@ struct SimpleSearchStyle: TextFieldStyle {
|
||||
Spacer()
|
||||
Text(isFocused ? "Type to search" : "Click to search")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.primary.opacity(0.2))
|
||||
.foregroundColor(.secondary)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ struct FilesView: View {
|
||||
@AppStorage("settings.general.filesWarning") private var warning: Bool = false
|
||||
@AppStorage("settings.interface.animationEnabled") private var animationEnabled: Bool = true
|
||||
@AppStorage("settings.general.confirmAlert") private var confirmAlert: Bool = false
|
||||
@AppStorage("settings.interface.details") private var detailsEnabled: Bool = true
|
||||
@State private var showAlert = false
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@Binding var showPopover: Bool
|
||||
@@ -30,7 +31,7 @@ struct FilesView: View {
|
||||
var body: some View {
|
||||
|
||||
|
||||
var totalSelectedSize: (real: String, logical: String, finder: String) {
|
||||
var totalSelectedSize: (real: String, logical: String) {
|
||||
var totalReal: Int64 = 0
|
||||
var totalLogical: Int64 = 0
|
||||
for url in appState.selectedItems {
|
||||
@@ -39,12 +40,11 @@ struct FilesView: View {
|
||||
totalReal += realSize
|
||||
totalLogical += logicalSize
|
||||
}
|
||||
return (real: formatByte(size: totalReal).human, logical: formatByte(size: totalLogical).human, finder: "\(formatByte(size: totalLogical).byte) (\(formatByte(size: totalReal).human))")
|
||||
return (real: formatByte(size: totalReal).human, logical: formatByte(size: totalLogical).human)
|
||||
}
|
||||
|
||||
let displaySizeTotal = sizeType == "Real" ? formatByte(size: appState.appInfo.totalSize).human :
|
||||
sizeType == "Logical" ? formatByte(size: appState.appInfo.totalSizeLogical).human :
|
||||
"\(formatByte(size: appState.appInfo.totalSizeLogical).byte) (\(formatByte(size: appState.appInfo.totalSize).human))"
|
||||
formatByte(size: appState.appInfo.totalSizeLogical).human
|
||||
|
||||
VStack(alignment: .center) {
|
||||
if appState.showProgress {
|
||||
@@ -91,65 +91,112 @@ struct FilesView: View {
|
||||
HStack(alignment: .center) {
|
||||
|
||||
//app icon, title, size and items
|
||||
VStack(alignment: .center) {
|
||||
HStack(alignment: .center) {
|
||||
if let appIcon = appState.appInfo.appIcon {
|
||||
Image(nsImage: appIcon)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 50, height: 50)
|
||||
.padding()
|
||||
.background{
|
||||
RoundedRectangle(cornerRadius: 16)
|
||||
.fill(Color((appState.appInfo.appIcon?.averageColor)!))
|
||||
VStack(alignment: .leading, spacing: 5){
|
||||
PearGroupBox(header: {
|
||||
HStack(alignment: .center, spacing: 15) {
|
||||
if let appIcon = appState.appInfo.appIcon {
|
||||
Image(nsImage: appIcon)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 50, height: 50)
|
||||
.padding(5)
|
||||
.background{
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.fill(Color((appState.appInfo.appIcon?.averageColor)!))
|
||||
|
||||
}
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
HStack(alignment: .center) {
|
||||
Text("\(appState.appInfo.appName)").font(.title).fontWeight(.bold).lineLimit(1)
|
||||
Image(systemName: "circle.fill").foregroundStyle(Color("AccentColor")).font(.system(size: 5))
|
||||
Text("\(appState.appInfo.appVersion)").font(.title3)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 5){
|
||||
HStack {
|
||||
Text("\(appState.appInfo.appName)").font(.title).fontWeight(.bold).lineLimit(1)
|
||||
Text("•").foregroundStyle(Color("AccentColor"))
|
||||
Text("v\(appState.appInfo.appVersion)").font(.title3)
|
||||
|
||||
Text("\(appState.appInfo.bundleIdentifier)")
|
||||
.lineLimit(1)
|
||||
.font(.title3)
|
||||
.foregroundStyle((.primary.opacity(0.5)))
|
||||
}
|
||||
Text("\(appState.appInfo.bundleIdentifier)")
|
||||
.lineLimit(1)
|
||||
.font(.title3)
|
||||
.foregroundStyle((.primary.opacity(0.5)))
|
||||
Spacer()
|
||||
|
||||
if let creationDate = appState.appInfo.creationDate {
|
||||
Text("Installed: \(formattedMDDate(from: creationDate))")
|
||||
.font(.footnote)
|
||||
}
|
||||
if let modificationDate = appState.appInfo.contentChangeDate {
|
||||
Text("Modified: \(formattedMDDate(from: modificationDate))")
|
||||
.font(.footnote)
|
||||
}
|
||||
if let lastUsedDate = appState.appInfo.lastUsedDate {
|
||||
Text("Last Used: \(formattedMDDate(from: lastUsedDate))")
|
||||
.font(.footnote)
|
||||
Button("\(detailsEnabled ? "Hide Details" : "Show Details")") {
|
||||
withAnimation(Animation.easeInOut(duration: animationEnabled ? 0.35 : 0)) {
|
||||
detailsEnabled.toggle()
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.leading)
|
||||
|
||||
Spacer()
|
||||
}, content: {
|
||||
|
||||
VStack(alignment: .trailing, spacing: 5) {
|
||||
if detailsEnabled {
|
||||
HStack(spacing: 20) {
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text("Total size of files:")
|
||||
.font(.callout).fontWeight(.bold)
|
||||
Text("")
|
||||
.font(.footnote)
|
||||
Text("Installed Date:")
|
||||
.font(.footnote)
|
||||
Text("Modified Date:")
|
||||
.font(.footnote)
|
||||
Text("Last Used Date:")
|
||||
.font(.footnote)
|
||||
|
||||
Text("\(displaySizeTotal)").font(.title).fontWeight(.bold).help("Total size on disk")
|
||||
Text("\(appState.appInfo.fileSize.count == 1 ? "\(appState.selectedItems.count) / \(appState.appInfo.fileSize.count) item" : "\(appState.selectedItems.count) / \(appState.appInfo.fileSize.count) items")")
|
||||
.font(.callout).foregroundStyle((.primary.opacity(0.5)))
|
||||
}
|
||||
Spacer()
|
||||
VStack(alignment: .trailing, spacing: 5) {
|
||||
Text("\(displaySizeTotal)").font(.callout).fontWeight(.bold)//.help("Total size on disk")
|
||||
|
||||
Text("\(appState.appInfo.fileSize.count == 1 ? "\(appState.selectedItems.count) of \(appState.appInfo.fileSize.count) item" : "\(appState.selectedItems.count) of \(appState.appInfo.fileSize.count) items")")
|
||||
.font(.footnote)
|
||||
|
||||
if let creationDate = appState.appInfo.creationDate {
|
||||
Text(formattedMDDate(from: creationDate))
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
Text("Not available")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
if let modificationDate = appState.appInfo.contentChangeDate {
|
||||
Text(formattedMDDate(from: modificationDate))
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
Text("Not available")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
if let lastUsedDate = appState.appInfo.lastUsedDate {
|
||||
Text(formattedMDDate(from: lastUsedDate))
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
} else {
|
||||
Text("Not available")
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.top, 0)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +213,6 @@ struct FilesView: View {
|
||||
.toggleStyle(SimpleCheckboxToggleStyle())
|
||||
.help("All checkboxes")
|
||||
|
||||
|
||||
Spacer()
|
||||
|
||||
|
||||
@@ -228,7 +274,7 @@ struct FilesView: View {
|
||||
Button("") {
|
||||
selectedSortAlpha.toggle()
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: selectedSortAlpha ? "textformat.abc" : "textformat.123", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size"))
|
||||
.buttonStyle(SimpleButtonStyle(icon: "line.3.horizontal.decrease.circle", label: selectedSortAlpha ? "Name" : "Size", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size", size: 16))
|
||||
|
||||
}
|
||||
.padding()
|
||||
@@ -290,11 +336,11 @@ struct FilesView: View {
|
||||
|
||||
Spacer()
|
||||
|
||||
InfoButton(text: "Always double-check the files/folders marked for removal. In some rare cases, Pearcleaner may find some unrelated files when app names are too similar.", color: .primary.opacity(0.5), warning: true, edge: .top)
|
||||
.padding(.top)
|
||||
// InfoButton(text: "Always double-check the files/folders marked for removal. In some rare cases, Pearcleaner may find some unrelated files when app names are too similar.", color: .primary.opacity(0.5), warning: true, edge: .top)
|
||||
// .padding(.top)
|
||||
|
||||
|
||||
Button("\(sizeType == "Logical" ? totalSelectedSize.logical : sizeType == "Finder" ? totalSelectedSize.finder : totalSelectedSize.real)") {
|
||||
Button("\(sizeType == "Logical" ? totalSelectedSize.logical : totalSelectedSize.real)") {
|
||||
showCustomAlert(enabled: confirmAlert, title: "Warning", message: "Are you sure you want to remove these files?", style: .warning, onOk: {
|
||||
Task {
|
||||
|
||||
@@ -377,7 +423,7 @@ struct FilesView: View {
|
||||
}
|
||||
.buttonStyle(UninstallButton(isEnabled: !appState.selectedItems.isEmpty))
|
||||
.disabled(appState.selectedItems.isEmpty)
|
||||
.padding(.top)
|
||||
.padding(.top, 5)
|
||||
|
||||
|
||||
}
|
||||
@@ -509,9 +555,7 @@ struct FileDetailsItem: View {
|
||||
Spacer()
|
||||
|
||||
let displaySize = sizeType == "Real" ? formatByte(size: size!).human :
|
||||
sizeType == "Logical" ? formatByte(size: sizeL!).human :
|
||||
"\(formatByte(size: sizeL!).byte) (\(formatByte(size: size!).human))"
|
||||
|
||||
formatByte(size: sizeL!).human
|
||||
Text("\(displaySize)")
|
||||
|
||||
}
|
||||
|
||||
@@ -114,10 +114,10 @@ struct RegularMode: View {
|
||||
.buttonStyle(.borderless)
|
||||
.padding(2)
|
||||
.padding(.vertical, 2)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.strokeBorder(.secondary.opacity(0.5), lineWidth: 1)
|
||||
}
|
||||
// .background {
|
||||
// RoundedRectangle(cornerRadius: 8)
|
||||
// .strokeBorder(.secondary.opacity(0.5), lineWidth: 1)
|
||||
// }
|
||||
}
|
||||
.padding(6)
|
||||
|
||||
@@ -126,7 +126,7 @@ struct RegularMode: View {
|
||||
}
|
||||
|
||||
}
|
||||
.frame(minWidth: 900, minHeight: 600)
|
||||
.frame(minWidth: appState.currentPage == .orphans ? 700 : 900, minHeight: 600)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ struct ZombieView: View {
|
||||
@State private var totalLogicalSize: Int64 = 0
|
||||
@State private var totalRealSizeUninstallBtn: String = ""
|
||||
@State private var totalLogicalSizeUninstallBtn: String = ""
|
||||
@State private var totalFinderSizeUninstallBtn: String = ""
|
||||
|
||||
var body: some View {
|
||||
|
||||
@@ -80,39 +79,45 @@ struct ZombieView: View {
|
||||
.padding(.trailing, (mini || menubarEnabled) ? 6 : 0)
|
||||
|
||||
|
||||
VStack() {
|
||||
VStack(spacing: 0) {
|
||||
|
||||
// Main Group
|
||||
HStack() {
|
||||
HStack(alignment: .center) {
|
||||
|
||||
VStack(alignment: .center) {
|
||||
VStack(alignment: .leading, spacing: 5){
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Image(systemName: "doc.badge.clock.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 50, height: 50)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.padding(.trailing)
|
||||
PearGroupBox(header: {
|
||||
HStack(alignment: .center, spacing: 15) {
|
||||
Image(systemName: "doc.badge.clock.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 50, height: 50)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
|
||||
VStack(alignment: .leading, spacing: 10){
|
||||
HStack {
|
||||
VStack(alignment: .leading){
|
||||
Text("Orphaned Files").font(.title).fontWeight(.bold)
|
||||
Spacer()
|
||||
Text("Remaining files and folders from previous applications")
|
||||
.font(.callout).foregroundStyle(.primary.opacity(0.5))
|
||||
}
|
||||
Text("Remaining files and folders from previous applications")
|
||||
.font(.callout).foregroundStyle(.primary.opacity(0.5))
|
||||
}
|
||||
}, content: {
|
||||
HStack(spacing: 20) {
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text("Total size of files:")
|
||||
.font(.callout).fontWeight(.bold)
|
||||
Text("")
|
||||
.font(.footnote)
|
||||
}
|
||||
Spacer()
|
||||
VStack(alignment: .trailing, spacing: 5) {
|
||||
Text("\(displaySizeTotal)").font(.callout).fontWeight(.bold)//.help("Total size on disk")
|
||||
|
||||
Spacer()
|
||||
Text("\(selectedZombieItemsLocal.count) of \(searchZ.isEmpty ? appState.zombieFile.fileSize.count : memoizedFiles.count) \(appState.zombieFile.fileSize.count == 1 ? "item" : "items")")
|
||||
.font(.footnote).foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
VStack(alignment: .trailing, spacing: 5) {
|
||||
Text("\(displaySizeTotal)").font(.title).fontWeight(.bold).help("Total size on disk")
|
||||
|
||||
Text("\(selectedZombieItemsLocal.count) / \(searchZ.isEmpty ? appState.zombieFile.fileSize.count : memoizedFiles.count) \(appState.zombieFile.fileSize.count == 1 ? "item" : "items")")
|
||||
.font(.callout).foregroundStyle(.primary.opacity(0.5))
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
@@ -157,7 +162,6 @@ struct ZombieView: View {
|
||||
.toggleStyle(SimpleCheckboxToggleStyle())
|
||||
.help("All checkboxes")
|
||||
|
||||
|
||||
SearchBar(search: $searchZ, darker: true, glass: glass, sidebar: false)
|
||||
.padding(.horizontal)
|
||||
.onChange(of: searchZ) { newValue in
|
||||
@@ -170,7 +174,7 @@ struct ZombieView: View {
|
||||
selectedSortAlpha.toggle()
|
||||
updateMemoizedFiles(for: searchZ, sizeType: sizeType, selectedSortAlpha: selectedSortAlpha, force: true)
|
||||
}
|
||||
.buttonStyle(SimpleButtonStyle(icon: selectedSortAlpha ? "textformat.abc" : "textformat.123", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size"))
|
||||
.buttonStyle(SimpleButtonStyle(icon: "line.3.horizontal.decrease.circle", label: selectedSortAlpha ? "Name" : "Size", help: selectedSortAlpha ? "Sorted alphabetically" : "Sorted by size", size: 16))
|
||||
|
||||
|
||||
}
|
||||
@@ -203,7 +207,7 @@ struct ZombieView: View {
|
||||
|
||||
Spacer()
|
||||
|
||||
InfoButton(text: "Leftover file search is not 100% accurate as it doesn't have any uninstalled app bundles to check against for file exclusion. This does a best guess search for files/folders and excludes the ones that have overlap with your currently installed applications. Please confirm files marked for deletion really do belong to uninstalled applications.", color: .orange, warning: true, edge: .top)
|
||||
// InfoButton(text: "Leftover file search is not 100% accurate as it doesn't have any uninstalled app bundles to check against for file exclusion. This does a best guess search for files/folders and excludes the ones that have overlap with your currently installed applications. Please confirm files marked for deletion really do belong to uninstalled applications.", color: .orange, warning: true, edge: .top)
|
||||
|
||||
Button("Rescan") {
|
||||
updateOnMain {
|
||||
@@ -214,7 +218,7 @@ struct ZombieView: View {
|
||||
}
|
||||
.buttonStyle(RescanButton())
|
||||
|
||||
Button("\(sizeType == "Logical" ? totalLogicalSizeUninstallBtn : sizeType == "Finder" ? totalFinderSizeUninstallBtn : totalRealSizeUninstallBtn)") {
|
||||
Button("\(sizeType == "Logical" ? totalLogicalSizeUninstallBtn : totalRealSizeUninstallBtn)") {
|
||||
showCustomAlert(enabled: confirmAlert, title: "Warning", message: "Are you sure you want to remove these files?", style: .warning, onOk: {
|
||||
Task {
|
||||
|
||||
@@ -342,7 +346,7 @@ struct ZombieView: View {
|
||||
let filteredFilesReal = fileSizeReal.filter { url, _ in searchTerm.isEmpty || url.lastPathComponent.localizedCaseInsensitiveContains(searchTerm) }
|
||||
let filteredFilesLogical = fileSizeLogical.filter { url, _ in searchTerm.isEmpty || url.lastPathComponent.localizedCaseInsensitiveContains(searchTerm) }
|
||||
|
||||
let filesToSort = sizeType == "Real" || sizeType == "Finder" ? filteredFilesReal : filteredFilesLogical
|
||||
let filesToSort = sizeType == "Real" ? filteredFilesReal : filteredFilesLogical
|
||||
let sortedFilteredFiles = filesToSort.sorted { (left, right) -> Bool in
|
||||
if selectedSortAlpha {
|
||||
return left.key.lastPathComponent.pearFormat() < right.key.lastPathComponent.pearFormat()
|
||||
@@ -377,15 +381,14 @@ struct ZombieView: View {
|
||||
let sizes = calculateTotalSelectedZombieSize()
|
||||
totalRealSizeUninstallBtn = sizes.real
|
||||
totalLogicalSizeUninstallBtn = sizes.logical
|
||||
totalFinderSizeUninstallBtn = "\(sizes.logical) (\(sizes.real))"
|
||||
}
|
||||
|
||||
private var displaySizeText: String {
|
||||
switch sizeType {
|
||||
case "Logical":
|
||||
return totalLogicalSizeUninstallBtn
|
||||
case "Finder":
|
||||
return totalFinderSizeUninstallBtn
|
||||
case "Real":
|
||||
return totalRealSizeUninstallBtn
|
||||
default:
|
||||
return totalRealSizeUninstallBtn
|
||||
}
|
||||
@@ -481,8 +484,7 @@ struct ZombieFileDetailsItem: View {
|
||||
Spacer()
|
||||
|
||||
let displaySize = sizeType == "Real" ? formatByte(size: size!).human :
|
||||
sizeType == "Logical" ? formatByte(size: sizeL!).human :
|
||||
"\(formatByte(size: sizeL!).byte) (\(formatByte(size: size!).human))"
|
||||
formatByte(size: sizeL!).human
|
||||
|
||||
Text("\(displaySize)")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user